Skip to content

Commit

Permalink
Have git blame only search back to upstream
Browse files Browse the repository at this point in the history
We need to know if a line is blamed on the topic branch or upstream, but
if it is blamed on upstream we don't care which commit exactly, so we
can save `git blame` some work by telling it that the upstream commit
doesn't have any parents via the `-S <revs-list>` option.

Note that the manpage description of -S is wrong: it accepts a file of
what git calls "grafts". These are described in commit
5da5c8f4cf4fb of the git repo as "fake commit parent records". From that
commit message:

> Each line of this file is a commit ID, followed by parent commit IDs,
> all 40-byte hex SHA1 separated by a single SP in between.  The records
> override the parent information we would normally read from the commit
> objects, allowing both adding "fake" parents (i.e. grafting), and
> pretending as if a commit is not a child of some of its real parents
> (i.e. cauterizing).

Apparently the -S option was intended for internal use, which might
explain why the manpage description is so misleading. From the comment
on read_ancestry() in the git repo:

> Add phony grafts for use with -S; this is primarily to support git's
> cvsserver that wants to give a linear history to its clients.

Using `git blame`'s --reverse option was investigated as a mechanism to
save work, but it requires the path being blamed to exist in the
starting commit, which won't always be the case for us.

Closes #17
  • Loading branch information
torbiak committed Aug 18, 2023
1 parent 1fe4283 commit f6fc16e
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 4 deletions.
4 changes: 4 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Unreleased

- Speed up `git-blame` by only considering commits since the given revision.

# 0.003001

- Fix bug where the index would be left out-of-sync with `HEAD` after
Expand Down
24 changes: 20 additions & 4 deletions git-autofixup
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,15 @@ sub rtrim {
}

sub blame {
my ($hunk, $alias_for) = @_;
my ($hunk, $alias_for, $grafts_file) = @_;
if ($hunk->{count} == 0) {
return {};
}
my @cmd = git_cmd(
'blame', '--porcelain',
'-L' => "$hunk->{start},+$hunk->{count}",
'HEAD', '--',
"$hunk->{file}");
'-S' => $grafts_file, 'HEAD',
'--', "$hunk->{file}");
my %blame;
my ($sha, $line_num);
open(my $fh, '-|', @cmd) or die "git blame: $!\n";
Expand Down Expand Up @@ -480,6 +480,21 @@ sub create_temp_index {
return $tempfile;
}

# Create a grafts file for `git blame -S` that basically says the upstream
# commit doesn't have any parents, resulting in blame only searching back as
# far back as the upstream commit.
sub create_grafts_file {
my $upstream = shift;
my $grafts_file = File::Temp->new(
TEMPLATE => 'git-autofixup_grafts.XXXXXX',
DIR => File::Spec->tmpdir());
my $merge_base = qx(git merge-base $upstream HEAD) or return "/dev/null";
open(my $fh, '>', $grafts_file) or die "Can't open $grafts_file: $!\n";
print $fh $merge_base, "\n";
close($fh) or die "Error closing grafts file: $!\n";
return $grafts_file;
}

sub main {
$VERBOSE = 0;
my $help;
Expand Down Expand Up @@ -537,7 +552,8 @@ sub main {
my $hunks = diff_hunks($num_context_lines);
my $summary_for = summary_for_commits($upstream);
my $alias_for = sha_aliases($summary_for);
my %blame_for = map {$_ => blame($_, $alias_for)} @{$hunks};
my $grafts_file = create_grafts_file($upstream);
my %blame_for = map {$_ => blame($_, $alias_for, $grafts_file)} @{$hunks};
my $hunks_for = fixup_hunks_by_sha({
hunks => $hunks,
blame_for => \%blame_for,
Expand Down

0 comments on commit f6fc16e

Please sign in to comment.