1

If there are two git commits (or refs) A and B, and if A is known to be an ancestor of B in the history, is there an easy way to compute an expression that shows how A can be reached, starting from B (going backwards through the ancestry path)?

In other words, how to generate a description of A, as a relative path that starts out from B - with results like these:

A = B~1 - i.e. A is the direct parent of B (first parent, if B is a merge)

A = B^2 - i.e. A is the 2nd parent of (merge-commit) B

A = B~22^2~3 - i.e. A is the 3rd ("first") ancestor of the 2nd parent of a merge that is the 22nd ("first") ancestor of B

In practice, this might be useful as an indicator of how far two commits are apart in the history graph - sort of a condensed form of what --ancestry-path can show in git log (but also with fewer information).

Can something like this be generated using git built-in tooling...?

I realize that there can well be multiple valid paths, going from B back to A - so any resulting expression won't necessarily be unique.

weiresr
  • 650
  • 8
  • 13
  • Not a dupe of, but similar to: https://stackoverflow.com/questions/917102/in-git-is-there-a-way-to-get-the-friendly-name-for-an-arbitrary-commit – Joe Aug 31 '23 at 13:23
  • @Joe why isnt it a dupe? – matt Aug 31 '23 at 13:41
  • 1
    @matt - It's indeed similar but not the same question IMO. I'm specifically interested in how to build a path description between *two* pre-given points in the git history here, and express the ancestor in relation to the descendant in the history graph. I don't see this possible by the linked question (or its replies) – weiresr Sep 01 '23 at 07:24

1 Answers1

1

The partly related question In Git, is there a way to get the "friendly" name for an arbitrary commit? linked by @Joe indeed led me to an easy way to allow for this - so thanks for the input :)

This can be answered using git name-rev, although name-rev really just works for named refs as reference to build a path for (so that wouldn't work directly with a SHA as reference).

Creating an intermediate, dummy ref makes it work though, so the following works

# Set sample values for A + B (B: current HEAD, A: root commit)
B=HEAD
A=`git rev-list HEAD | tail -n 1`

git branch tempRefToB ${B}
git name-rev --name-only --refs=tempRefToB ${A}
git branch -d tempRefToB

Sample output of the name-rev command gives me what I was looking for (the temporary ref-name can easily be stripped back out from it, as needed):

tempRefToB~3334
weiresr
  • 650
  • 8
  • 13
  • There's a workaround for the problem you're eventually going to encounter with this, make a scratch bare reference no-tags single-branch clone and run name-rev in that: ``git branch tempRefToB $B; git clone -s --bare --no-tags --single-branch -b tempRefToB . `mktemp -d`; git -C $_ name-rev $A``. That clone is the living definition of dirt cheap, doing it for my full linux repo took eight milliseconds on spinning rust. – jthill Sep 01 '23 at 14:01
  • If you're really going for any-path-length-to-root, you can do much better, no need for anything but ``echo B~$(git rev-list --count --first-parent B)` – jthill Sep 01 '23 at 14:06