Using .gitignore - Java

  1. Which approach best meets these requirements?
    Answer: Commit target/ and build/ in the repository-root .gitignore. When Git questions mention "version-controlled" and "every developer automatically," you should immediately think about where ignore rules live and whether they travel with the repository. Git offers three tiers of ignore configuration: repository-level (.gitignore committed to the repo), repository-local (.git/info/exclude), and user-global (~/.gitignore_global). Only the first tier is tracked by Git itself, meaning it gets cloned, pushed, and pulled alongside the source code. Committing target/ and build/ patterns in the repository-root .gitignore — choice D — is exactly right because every developer who clones the repository automatically receives those rules with no extra setup required. Choice A falls short because IDE settings are tool-specific and personal; they don't affect Git's behavior at all, so files could still be accidentally staged and committed. Choice B uses .git/info/exclude, which is stored inside the local .git folder. That folder is never shared or cloned, so this only works on one machine and violates the "every developer automatically" requirement. Choice C relies on each developer's global Git ignore file, which lives on their personal machine. Not only does this require manual setup per developer, but it also affects every repository that person works in — directly contradicting the requirement to avoid impacting unrelated repositories. The key study tip: whenever a question specifies that a rule must be version-controlled and shared across the team, the answer almost always involves committing a .gitignore file. Anything stored outside the repository (global configs, local excludes, IDE settings) is invisible to other developers and cannot be version-controlled.
  2. After both files are generated in the repository root, how are they treated?
    Answer: Both Main.class and Helper.class are ignored. When working with .gitignore rules, the critical principle is last-matching rule wins. Git evaluates every rule in the file from top to bottom, and whichever rule matches a file last determines how that file is treated. Walk through each file against the three rules: For Helper.class: Rule 1 (*.class) matches — ignore it. Rules 2 and 3 don't apply any differently since Helper.class isn't Main.class. The last matching rule is *.class, so Helper.class is ignored. For Main.class: Rule 1 (*.class) matches — ignore it. Rule 2 (!Main.class) matches — un-ignore it (the ! negates the previous ignore). Rule 3 (Main.class) matches — ignore it again. The last matching rule is Main.class on line 3, so Main.class ends up ignored. This makes C the correct answer. A is the classic trap here — it assumes the !Main.class negation is the final word, ignoring that Main.class on line 3 overrides it afterward. B makes the opposite mistake, somehow concluding Helper.class escapes the wildcard, which nothing in the file supports. D imagines that both files bypass all ignore rules entirely, which contradicts the plain presence of *.class. The study takeaway: treat .gitignore evaluation like a CSS cascade — later rules override earlier ones. Whenever you see a negation rule (!), always check whether a subsequent positive rule re-ignores the same pattern. The ! only holds if nothing after it matches again.
  3. Which generated file is ignored by this pattern?
    Answer: Only build/debug/classes/A.class is ignored. When reading .gitignore patterns, three special rules about slashes dramatically change what gets matched. First, a leading slash (/) anchors the pattern to the repository root. Second, a trailing slash matches only directories. Third, a middle slash (between non-slash characters) prevents recursive matching — the pattern is treated as a literal relative path from the anchor point. The pattern /build/*/classes/ breaks down like this: the leading / anchors it to root, * matches exactly one path segment (any single directory name, but not nested paths), and the trailing / means classes must be a directory. This means Git looks for <root>/build/<one-segment>/classes/ and ignores everything inside. Only build/debug/classes/A.class fits — build/ is at root, debug is one segment, and classes/ follows directly. Answer D is correct for this reason. Now look at why the others fail. A is wrong because not all three files match — the pattern is far more restrictive than a blanket rule. B is wrong because build/debug/java/classes/B.class is also under the root build/ directory, but the * wildcard cannot span multiple path segments, so debug/java (two segments) doesn't match */classes. C is wrong because the pattern is anchored to root with a leading slash, so module/build/debug/classes/C.class is excluded — without the leading slash, it might match, but anchoring prevents that. Your study tip: whenever you see a .gitignore pattern question, immediately identify (1) whether there's a leading slash for anchoring, and (2) whether * or ** is used — * matches one segment, ** matches across segments.
  4. Which .gitignore sequence correctly implements this policy?
    Answer: /build/* !/build/reports/ /build/reports/* !/build/reports/summary.txt When working with .gitignore, a critical rule governs negation patterns: you cannot un-ignore a file inside an ignored directory. Git stops descending into a directory once it's fully ignored, so any negation for files inside that directory is silently skipped. This is the core concept this question tests. Option B is correct because it uses a careful three-step strategy. First, /build/* ignores the contents of /build/ without ignoring the directory itself — this is a subtle but crucial distinction from /build/. Then !/build/reports/ re-exposes the reports/ subdirectory so Git will descend into it. Next, /build/reports/* ignores everything inside reports/. Finally, !/build/reports/summary.txt carves out exactly the one file you want tracked. Each layer peels back just enough to allow the next negation to work. Option A fails because /build/ ignores the entire directory as a unit. Git never looks inside it, so the negation !/build/reports/summary.txt has no effect — Git simply never reaches that file. Option C places the negations before the ignore rule, but .gitignore is order-dependent and the final /build/ line overrides everything above it, swallowing the entire directory anyway. Option D puts the file negation first and then /build/* afterward, which re-ignores everything before the directory is properly re-opened. The ordering destroys the intended effect. Study tip: Whenever you need to un-ignore a specific file deep inside an ignored directory, remember the "peel-back" pattern: ignore contents with dir/*, then negate each intermediate directory, then negate the final file — order matters every step of the way.
  5. What does this pattern ignore?
    Answer: The root directory or root file named build, but not module/build. When working with .gitignore patterns, two features control what gets matched: a leading slash and a trailing slash. Understanding both is essential here. A leading slash (like /build) anchors the pattern to the repository root, meaning Git only looks for matches at the top level — not in subdirectories. A trailing slash (like build/) restricts matching to directories only. When you combine neither trailing slash with a leading slash, the pattern matches any file-system entry (file or directory) at the root level. So /build with no trailing slash tells Git: "At the root only, ignore anything named build — whether it's a regular file or a directory." That's exactly what option C describes, making it correct. The root file named build is ignored, the root directory named build is ignored, but module/build is not, because the leading slash prevents the pattern from reaching nested paths. Option A is wrong because it claims a file named build would not be ignored — but the absence of a trailing slash means both files and directories match. Option B makes the opposite error for the same reason: it incorrectly limits matching to directories only, as if a trailing slash were present. Option D completely inverts the meaning of the leading slash — a leading slash anchors to the root, not to a parent directory deeper in the tree, so module/build is actually excluded, not targeted. A handy rule to memorize: leading slash = root only; trailing slash = directories only. When both are absent, the pattern matches files and directories everywhere in the tree.
  6. Which set of paths is ignored by this rule?
    Answer: Only target/classes/App.class is ignored. When working with .gitignore rules, the critical detail to watch for is whether a pattern starts with a leading slash. A leading slash anchors the pattern to the directory containing the .gitignore file — meaning it only matches at that exact location, not recursively throughout the project. Here, the repository-root .gitignore contains /target/. Because of the leading /, Git interprets this as "ignore a target/ directory only at the repository root." This means target/classes/App.class is ignored (it lives inside root-level target/), but module-a/target/classes/Module.class is not ignored — that target/ folder is nested inside module-a/, not at the root. The .java source file is unaffected entirely since no rule targets it. So A is correct. B is wrong because it assumes the rule applies to all target/ directories anywhere in the project. Without the leading slash, a pattern like target/ would match recursively — but /target/ does not. C has it backwards: it incorrectly ignores the nested path while sparing the root-level one. D overclaims by suggesting all three paths are ignored, which would require a much broader rule. A quick study tip: memorize this toggle. target/ (no leading slash) → ignored everywhere in the repo. /target/ (with leading slash) → ignored only at the .gitignore file's directory level. Git questions on this topic almost always hinge on that single character difference, so train yourself to look for the slash immediately.
  7. Which action correctly completes the change?
    Answer: Run git rm -r --cached target, then commit the index change and .gitignore. Whenever you see a Git question about stopping version control on already-tracked files without deleting them from disk, your focus should be on Git's index (staging area) — the layer between your working directory and your commit history. The key insight is that .gitignore only prevents untracked files from being tracked. Once a file is already in Git's history, .gitignore does nothing to stop Git from noticing changes to it. You must explicitly remove it from the index while keeping it on disk. That's exactly what git rm -r --cached target does — the --cached flag removes the files from the index only, leaving them untouched on disk. After that, committing both the index removal and the .gitignore rule ensures the directory stays untracked going forward. That makes A the correct answer. B is wrong because git reset --hard is for resetting your working tree and index to a previous commit state — it can destroy local changes and isn't designed to untrack files. It won't cleanly remove target/ from version control. C is a destructive approach that violates the requirement: the generated files must remain on disk. Deleting target/, committing the deletion, then re-adding target/ to .gitignore would work conceptually but breaks the constraint by wiping local build artifacts. D is wrong because git clean -fd removes untracked files from the working directory — the opposite of what you want. It would delete the files entirely, not just untrack them. Remember this pattern: **--cached = index only, disk untouched**. Any time you need to untrack without deleting, git rm --cached is your tool.
  8. Which binary file is ignored?
    Answer: Only module-a/target/a.bin is ignored. When reading .gitignore rules, the location of the file and the leading slash in the pattern are critical details that completely change what gets ignored. A .gitignore file applies rules relative to its own directory. So module-a/.gitignore governs paths starting from module-a/. The rule /target/ uses a leading slash, which anchors the pattern to the **root of that .gitignore's directory** — meaning it only matches a folder named target directly inside module-a/, not deeper subdirectories. That's exactly why only module-a/target/a.bin is ignored, making A the correct answer. Here's why the other choices miss the mark. B is wrong because module-a/examples/target/example.bin is not ignored — the leading slash /target/ anchors the match to module-a/target/, not to any target/ nested further down like module-a/examples/target/. If the rule were target/ (no leading slash), it would match recursively and B would be correct. C is wrong for two reasons: the rule only applies within module-a/, and the anchoring prevents recursive matching anyway, so module-b/target/b.bin and target/root.bin are completely unaffected. D is wrong because target/root.bin lives at the repository root, which is outside module-a/.gitignore's scope entirely. A useful rule of thumb: always check where the .gitignore lives and whether the pattern has a leading slash. Leading slash = anchored to that directory only; no leading slash = matches at any depth within that scope.
  9. Which single repository-root .gitignore pattern best satisfies the requirement?
    Answer: target/ When working with .gitignore patterns, the key distinction is between directory-only matching, anchoring, and recursive matching. Ask yourself: does the pattern need to match at all depths, and must it ignore only directories (not files)? The pattern target/ (the trailing slash means "directories only") matches any directory named target at any depth in the repository — including target/, service-a/target/, and service-a/integration/target/. Because it has no leading slash, Git doesn't anchor it to the repo root, so it propagates recursively. Crucially, the trailing slash ensures a regular file named target is never matched, satisfying the requirement that such a file remain trackable. This makes B the correct answer. Here's why the other options fail: A (/target/) anchors the pattern to the repository root with the leading slash, meaning it only ignores the top-level target/ directory — service-a/target/ and deeper paths are completely unaffected. C (*/target/) uses a single wildcard that matches exactly one directory level deep, so it catches service-a/target/ but misses service-a/integration/target/, which requires two levels of nesting. D (target) omits the trailing slash entirely, which means Git will match both directories and regular files named target — this would accidentally ignore the regular file that must stay tracked. As a study tip: remember that in .gitignore, a trailing slash restricts matching to directories only, while a leading slash anchors the pattern to the root. Combining these two rules mentally will help you eliminate wrong answers quickly on exam questions about Git ignore behavior.
  10. Which replacement pattern ignores that directory while preserving the literal leading # in its name?
    Answer: \#build/ Whenever you see a question about .gitignore patterns, you need to think about how Git parses that file: it treats any line beginning with # as a comment and ignores it entirely. This means a pattern like #build/ never reaches Git's matching engine — it's silently discarded as a comment before any file path comparison happens. To force Git to treat a leading # as a literal character rather than a comment marker, you must escape it with a backslash. The pattern \#build/ tells Git's parser, "this backslash signals that the # is literal," so the pattern is evaluated against actual file paths. Since the directory is truly named #build, this escaped pattern matches it correctly — making B the right answer. Looking at the distractors: A (#build/) is the trap most teams fall into. It looks correct at a glance, but because it starts with #, Git treats the entire line as a comment and never applies it. C (build#/) rearranges the characters entirely — this would only match a directory literally named build#, which doesn't exist here, so it solves nothing. D (##build/) doubles the #, but a second # doesn't escape the first; the line still begins with # and is still treated as a comment, leaving the problem unsolved. A good rule of thumb: in .gitignore, the backslash is your escape character for special leading symbols. If a filename or directory starts with # or !, always prefix the pattern with \ to prevent misinterpretation.

Start this quiz