Skip to content

Log().ExcludingRemoteTrackingRefs() without ForRevision returns no commits, even when HEAD has unpushed work #161

Description

@matt-edmondson

What's wrong

GitLogBuilder emits --not --remotes --not for ExcludingRemoteTrackingRefs() and, when neither ForRevision nor IncludingAllRefs() was called, no revision at all (GitLogBuilder.cs L180-210). The builder's docs say Log() lists commits "from HEAD" by default (L29, L48).

git only falls back to HEAD when no revision input was given. A pseudo-ref option like --remotes counts as input (git sets rev_input_given), even when it is negated or matches nothing. So git log --not --remotes --not lists nothing.

Why it matters

repository.Log().ExcludingRemoteTrackingRefs().ExecuteAsync() is the natural way to ask "what have I not pushed?", and it always returns an empty list. An empty list reads as "nothing unpushed", which is the data-loss answer the method's own remarks warn about. Adding ForPath(...), FirstParentOnly() or Take(...) makes no difference.

Reproduction (git 2.43.0)

git init -q r && cd r
git -c user.name=a -c user.email=a@b commit -q --allow-empty -m one
git log --format=%s --not --remotes --not          # prints nothing
git log --format=%s --not --remotes --not HEAD     # prints "one"

The same thing happens in a clone that has origin/main plus one local-only commit.

Existing tests cover only the IncludingAllRefs() and ForRevision("HEAD") combinations (GitRemoteSyncTests.cs L118-170, GitLogBuilderTests.cs L147-203). The bare call is untested.

Suggested fix

When _excludingRemoteTrackingRefs && !_includingAllRefs && _revision is null, append HEAD through AppendOperands, so the documented default holds. Alternatively, throw InvalidOperationException asking for a revision, but defaulting to HEAD matches the docs.

Acceptance criteria

  • Log().ExcludingRemoteTrackingRefs() with no revision returns the commits reachable from HEAD that aren't on any remote-tracking ref.
  • Integration tests cover the bare call both with a remote that is behind HEAD and in a repository with no remotes, where every commit should be returned.
  • An argv test pins the emitted HEAD operand.

Activity

  1. matt-edmondson commented on Sep 29, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: High. Log().ExcludingRemoteTrackingRefs() is the natural "what haven't I pushed?" query, and it always comes back empty. An empty list reads as "safe to delete / nothing to push", which is the data-loss answer the method's own remarks warn about. Nothing fails loudly, so callers won't notice.
    • Area / suggested assignee: GitIntegration/Builders/GitLogBuilder.cs (argv assembly, L180-210). Owner: @matt-edmondson
    • Duplicates / in progress: None found. Open PRs Match caller file paths literally, so file[1].txt no longer also matches file1.txt #159 and Pin diff --submodule=short so Patch reports a moved submodule as its gitlink #160 touch path matching and diff --submodule, not Log().
    • Next step: Append HEAD through AppendOperands when ExcludingRemoteTrackingRefs() is set with no revision and no IncludingAllRefs(). Add the argv test and the two integration tests from the acceptance criteria (remote behind HEAD, and no remotes). It's a small, self-contained fix.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions