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

[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.

  • How to use jenkins git plugin to build from a branch include origin or not
  • How to get git parent branch name of current branch?
  • Broken branch in git, fatal: your current branch appears to be broken
  • Few questions about working with Git
  • Git branch structure for Client & Server
  • Pulling a branch from origin merged it into my local branch and I need to revert that
  • git status on master and branches
  • Reproducing git branching model with help of mercurial bookmarks
  • 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.