git: Find the oldest commit of `mine` which does not exist in `theirs`

I’d like to be able to find the longest path in my git-graph which does not exist upstream. One end of that path is obviously HEAD, so that’s easy. The other end is my oldest commit which does not exist upstream. I don’t know how to find this.

Diagram

  • Set up git to pull and push all branches
  • How do I create git tags for legacy gcc releases?
  • Branching Strategies
  • git change origin of a branch (rebase)
  • Multiple Git Branches in Multiple Eclipse Projects
  • git push remote repo
  • [otherguy]   H--I--J   
                /       \
    [master]   A--B--C--M1--D--E--M2--F--G
                  \      \             \
    [mine]         H---I--M3---J---K---M4--M--HEAD
    

    I want a command which will point me to H (or B), given HEAD and master. Note that a simple git merge-base master HEAD gives me F.

  • Tutorial on git using git bash fixing the conflict
  • fatal: Not a git repository: '.git' error
  • Ignore modified (but not committed) files in git?
  • “Too many revisions” git message
  • Git: How do I list only local branches?
  • Locking binary files using git version control system
  • 2 Solutions collect form web for “git: Find the oldest commit of `mine` which does not exist in `theirs`”

    This should work reasonably:

    git rev-list --topo-order --reverse mine ^master | head -1
    

    Tested with a replica of your setup, it prints the commit you marked as “H”. I am not 100% convinced that it finds the “farthest” path in all possible cases, or that such a concept is even well-defined, so you might want to test it on cases that matter to you before relying on it.

    (Note that the -1 option one would usually specify to limit git rev-list output does not combine well with --reverse, so head -1 is needed.)


    UPDATE:

    Some explanation:

    • git rev-list mine ^master requests the commits reachable from mine, but not from master. In your case we are requesting all commits starting with HEAD, excluding those reachable from G, which gives a list of commits (M, M4, K, J, M3, I, H), of which we choose the last one.

    • --topo-order is needed to ensure topological ordering of commits (that a commit always appears before its parent), rather than chronological ordering, which is the default. We also specify --reverse because we need the last one.

    • commit1 ^commit2 can also be spelled commit2..commit1, which is the preferred syntax when commit1 is a descendant of commit2. In this case mine and master have diverged, so it’s clearer to spell out what we want. Multiple exclusions, such as git log branch1 ^branch2 ^branch3, are also allowed. This applies to all git commands that take ranges, and is documented in the git rev-parse manual.

    This is mostly equivalent to the question »Which commits are reachable from (my) HEAD, but not from master?« To answer that question, use the commit range master..HEAD. You want the oldest commit from this list, so pipe the output through tail: git rev-list master..HEAD|tail -n1.

    If your commit timestamps are not ordered chronologically, you might have to specify the --topo-order option to rev-list.

    Git Baby is a git and github fan, let's start git clone.