0

If I have a specific hash, call it A, I can get to the parent by A^. But is there a way to get to the commit that followed A in the branch history? Something like A+1, such that A is (A+1)^? I want to check out the commit after the one I have the hash for, and then the one after that, and so on, to inspect them. But they're really buried. Any easy way to check them out given a reference hash other than searching git log output?

SwissCodeMen
  • 4,222
  • 8
  • 24
  • 34
temporary_user_name
  • 35,956
  • 47
  • 141
  • 220
  • 2
    There is no single answer to "the commit that follows A". A single commit may be part of multiple branches, and there can be any number of commits that follow it. – larsks Sep 14 '20 at 18:46
  • I figured that was the case but I was hoping there was some kind of way around that fact, like "If you confine it to a linear path from HEAD to A...." Hypothetically that should be possible with a short bash script, basically loop through parents from HEAD until you hit A. – temporary_user_name Sep 14 '20 at 18:50
  • 2
    Related, if not a dup: https://stackoverflow.com/q/2263674/184546 – TTT Sep 14 '20 at 19:00
  • 1
    `A^` is unique; it is (a) parent of the commit A. There could be many commits `B_i` such that `B_i^ == A`. – chepner Sep 14 '20 at 19:18
  • Does this answer your question? [How do I find the next commit in git? (child/children of ref)](https://stackoverflow.com/questions/2263674/how-do-i-find-the-next-commit-in-git-child-children-of-ref) – SwissCodeMen Sep 14 '20 at 21:19

2 Answers2

3

I want to check out the commit after the one I have the hash for, and then the one after that, and so on, to inspect them

git rev-list --first-parent --reverse $thathash..mybranch

will list you all the commits you want to check out, in order. If you don't just want the linear history but all commits descended from that hash in the branch tip history replace --first-parent with --ancestry-path and you can use the sequencing options to decide what order to visit them in.

jthill
  • 55,082
  • 5
  • 77
  • 137
  • There's a quirk with `--first-parent` : if there are two levels of merge between `$thathash` and `mybranch`, the first level of children commits from `$thathash` will not be listed in the `--first-parent` list. The `--ancestry-path` is a good option (and a built in one) – LeGEC Sep 14 '20 at 19:44
  • I wouldn't call that a quirk, that follows from one legitimate meaning of "in the branch history" and the only one that makes sense of OP's use of the singular, "the commit that follows A in the branch history". If that's what OP means and there are "two levels of merge between" then the only commits that follow A "in the branch history" are the ones `--first-parent` will list here. – jthill Sep 14 '20 at 20:20
1

There is no built in command to find a linear path between two commits ; you will have to write an external script for this.

Here is an example, written in perl (from this answer) :

# in file 'git-path.pl' :
#!/usr/bin/perl

use strict;
use warnings;

# This script implements a breadth first search, starting from $target,
# following the 'child -> parent' links, until $source is found or the
# complete history of $target is traversed.

my $source = $ARGV[0];
my $target = $ARGV[1];

my $srcsha = `git rev-parse -q --verify "$source"` || die "'$source' is not a valid source";
chomp($srcsha);
my $tgtsha = `git rev-parse -q --verify "$target"` || die "'$target' is not a valid target";
chomp($tgtsha);

# %seen stores the commits seen so far, linked to the child we are interested in
my %seen = ();
$seen{$tgtsha} = "";

# @stack lists the commits to visit
my @stack = ();
push @stack, $tgtsha;


# print_path : print the path from '$target' down to '$source'
sub print_path {
  my ($source) = @_;

  my @path = ();

  my $sha = $source;
  while ($sha) {
    unshift @path, $sha;
    $sha = $seen{$sha};
  }

  print "$_\n" for @path;
}


# main body :
# as long as there is something to scan, go for it,
# if $source is found along the way, print the path and exit with success
# otherwise, end the loop, and exit with failure

while( scalar(@stack) > 0 ) {
  my $sha = shift @stack;

  # extract parent lines from 'git cat-file -p commit'
  my @parents = `git cat-file -p $sha | grep '^parent ' | cut -c8-`;
  
  foreach my $p (@parents) {
    chomp($p);
    # for each parent, if not seen yet :
    #   * store it as a parent of $p
    #   * put it on the list of commits to explore next
    if (!$seen{$p}) {
      $seen{$p} = $sha;
      push @stack, $p;

      if ($p eq $srcsha) {
        # if we reached the 'source' commit : stop here
        print_path $p;
        exit 0;
      }
    }
  }
}

# no path found
exit 1;

sample usage :

$ perl git-path.pl A B
175015a5a00bbb9ad2ee4de23254ace4dbc645eb # commit B
bc03512e6082badab86a00cd320d89339741bb7b
077f584c10852cbadeef6c48886fd600cab61aa6
cec6734db31a1053b1b71674671512e1fe1592b1
b1dd3c71e42ca421f306640d4f0fdc69a00aa2c7
778a504c718f30d0dc2c72a30c885f10847f46a8 # commit A
LeGEC
  • 46,477
  • 5
  • 57
  • 104