In git log, what does main..feature select, and how does main...feature differ?
answer
- Ranges are set operations, not timelines
- Two dots is subtraction
- Three dots is symmetric difference
- Same punctuation means something else to git diff
- --left-right labels which side each commit came from
basics
~20 sIn git log, main..feature lists commits reachable from feature but not from main. main...feature lists the symmetric difference: commits reachable from either side but not both. In git diff, three dots means something else — merge base against the right side.
solid answer
~40 s`main..feature` is set subtraction over reachability: everything reachable from `feature` minus everything reachable from `main`. It is shorthand for `git log feature ^main`, and it answers "what is on this branch that master doesn't have yet". `main...feature` is the symmetric difference — commits on either side that the other lacks — and `--left-right` marks each with `<` or `>` so you can see which side it came from. Two crucial cautions. First, ranges are about **reachability**, not time, so a fresh commit on `main` never appears in `main..feature`. Second, `git diff` reuses the same punctuation with different meaning: `git diff A..B` is just A versus B, while `git diff A...B` diffs the merge base of A and B against B, which is why it is the usual way to see "what this branch changed".
code
bash · 5 linesgit log --oneline main..feature
git log --oneline --left-right main...feature
git rev-list --count main..feature
git rev-list --left-right --count main...feature
git diff main...featurego deeper
Know that git log main..feature shows the commits your branch adds on top of main, and that the order of the two names matters.
Explain ranges as reachability set operations — two dots as subtraction, three dots as symmetric difference — and state that git diff reinterprets three dots as merge-base versus the right side.
Show how you use ranges operationally: counting divergence with rev-list --left-right --count, checking whether a fix is contained, and picking the right form when reviewing a long-lived branch.
Be ready to define what "contained in the release" means for your organisation in reachability terms, and to standardise the commands people use to answer it.
## Ranges are set operations on reachability A "revision range" in Git is not a slice of time and not a slice of a branch. It is a set of commits defined by reachability over parent edges. The building block is `^X`, meaning "exclude everything reachable from X". Then: - `A..B` is exactly `B ^A` — every commit reachable from B, minus every commit reachable from A. - `A...B` is the **symmetric difference** — reachable from A or B, but not from both. `git log main..feature` therefore prints the commits your branch adds on top of what `main` already contains. If someone pushed new commits to `main` afterwards, those do not show up: they are reachable from `main`, so they are excluded, and they were never reachable from `feature` anyway. ## Reading the two forms The two-dot form is the one you want almost always. Typical uses: `git log --oneline main..feature` before opening a change for review, `git log @{u}..` to see what you have locally that is not pushed yet, and `git rev-list --count main..feature` to count the commits a branch adds. The three-dot form shines when both sides have moved. `git log --left-right --oneline main...feature` marks commits from the left side with `<` and the right side with `>`, giving you a divergence report in one command. `git rev-list --left-right --count main...feature` collapses that to two numbers — how many commits each side has that the other does not. Omitting a side defaults it to HEAD: `..feature` means `HEAD..feature` and `main..` means `main..HEAD`. ## The diff trap The same punctuation means something different to `git diff`, and this catches experienced people out. - `git diff A B` and `git diff A..B` are identical: compare the two snapshots directly. - `git diff A...B` compares the **merge base** of A and B against B. In other words, it shows only what B changed since the branches parted, hiding changes A made in the meantime. So `git log A...B` (symmetric, both sides) and `git diff A...B` (one-sided, from the fork point) do not correspond to each other at all. When someone asks "what does my branch change", `git diff main...feature` is usually the honest answer, because a plain `git diff main feature` also reports, in reverse, every change `main` picked up that your branch lacks. ## Ranges are not linear A range does not have to be an ancestor relationship. If A and B are unrelated, `A..B` is simply everything reachable from B, since nothing is excluded. If B is an ancestor of A, `A..B` is empty — which is the fastest way to test "is this branch fully merged?": if `git log feature..main` is empty, `main` contains nothing new for you; if `git rev-list --count main..feature` is `0`, your branch adds nothing. Also remember that a range can select merge commits whose side branches are excluded, so the output is a set of commits, not a contiguous path. `--first-parent` or `--no-merges` narrow it when you want a simpler list. ## Verifying your intuition When unsure, resolve the set rather than argue about it: `git rev-list --count main..feature` and `git rev-list --left-right --count main...feature` are cheap and unambiguous, and a quick `git log --graph --oneline main...feature` makes the shape obvious.
- Why does git diff main...feature usually show what a branch changed, while git diff main feature does not?The three-dot form in `git diff` compares the merge base of the two revisions against the right-hand side, so it shows only what `feature` added since the branches diverged. The two-revision form compares the tips directly, so anything `main` gained meanwhile appears as a reversed change from your branch.
- How do you tell in one command how far two branches have diverged?`git rev-list --left-right --count main...feature` prints two numbers: commits only in the left revision and commits only in the right one. Adding `--left-right` to `git log` on the same range labels each commit with `<` or `>` if you want the actual list.
- What does an empty result from git log main..feature tell you?That `feature` has nothing reachable that `main` lacks — either the branch has been fully merged or it was never advanced. It says nothing about `main` having moved ahead; check `git log feature..main` for the other direction.
Two dots is "what's in my bag that isn't in yours"; three dots is "what either of us has that the other doesn't".
saying these in an interview costs you the question
- Thinks two dots means a time window of commits
- Assumes three dots behaves the same in log and diff
- Believes A..B includes new commits on A
- Reads an empty range as an error
- Uses git diff main feature to review a branch