Is there a syntax for referring to the commit which followed a commit in branch history?

104 views Asked by At

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?

2

There are 2 answers

2
jthill On BEST ANSWER

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.

0
LeGEC On

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