I execute git log master ^master and expect an empty output, because I list all commits that are present in master and simultaneously exclude them.
However I see all log messages as if I just ran git log master. I wonder why this happens.
My master is fully up-to-date with remote (git pull -> Already up to date.)
I made a couple tests and got the correct (empty) output with some of commands:
git version 2.50.0
git log master ^master -> wrong (WHY?)
git log master ^origin/master -> correct
git log origin/master ^master -> wrong
git log origin/master ^origin/master -> correct
git log master ^refs/heads/master -> correct
git log refs/heads/master ^master -> wrong
git log refs/heads/master ^refs/heads/master -> correct
git log origin/master ^refs/heads/master -> correct (proves that branches are in-sync)
git log refs/heads/master ^origin/master -> correct
git log master..master -> correct
UPD: Thanks to the guys in the comments, I figured out that the problem was in my Zsh shell. Everything works fine with Bash, so the question is: what does Zsh do that breaks in some cases, but works in others? I assume it's some kind of globbing.
To understand how this works on a deeper level, we need to go to first, to understand how git handles the design logic
: https://github.com/git/git/blob/master/revision.c#L3898
git log master ^master
master):Git parses the ref master as a positive inclusion.
It resolves to a commit object (e.g., refs/heads/master).
This object is added to revs->pending with no special flags.
During prepare_revision_walk(), handle_commit() processes it:
The commit is marked with the SEEN flag.
It's added to the traversal queue.
^master):Git sees ^master as a negative revision (an exclusion).
It resolves to the same commit object as before.
This time, it is added to revs->pending with flags:
UNINTERESTING | BOTTOMDuring prepare_revision_walk() , handle_commit() is called again:
https://github.com/git/git/blob/master/revision.c#L3898
But the commit already has the SEEN flag from the earlier inclusion.
Git skips reprocessing it due to:
if (commit->object.flags & SEEN)
return 0; // Already handled
As a result, the commit never gets marked as UNINTERESTING, and is not excluded from output.
The problem is not that Git doesn’t understand you want to exclude master — it’s that the commit was already marked as SEEN during inclusion, so when Git sees it again during exclusion, it skips updating its flags.
So:
First argument (master) includes the commit
Second argument (^master) tries to exclude it — but fails to apply UNINTERESTING
Therefore, the commit still appears in the log output
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With