#git-diff #git-commit #git #git-repository #diff #text #utility

bin+lib git-blamediff

A program to automatically annotate changes to a file in git(1)

3 releases

0.1.2 Sep 18, 2024
0.1.1 Jun 21, 2022
0.1.0 May 27, 2022

#286 in Text processing

Download history 162/week @ 2024-09-17 11/week @ 2024-09-24 7/week @ 2024-10-01

100 downloads per month

GPL-3.0-or-later

19KB
72 lines

pipeline crates.io

git-blamediff

Purpose

git-blamediff is a program aiding in annotating changes made in a git repository with the SHA1 hashes the respective lines in the base state were modified in. This process allows for quick lookup of previous commits of interest or creation of fix-up commits. Depending on your workflow the latter might be massively helpful for sorting in changes to the proper previous commits in a set of changes.

Usage

Consider the following example where a developer noticed that he/she forgot a trailing newline on a string to print. The code has not gotten pushed and so fixing up the commit of interest is the best option. The patch looks as follows:

 --- main.c
 +++ main.c
@@ -6,6 +6,6 @@ int main(int argc, char const* argv[])
     fprintf(stderr, "Too many arguments.\n");
     return -1;
   }
-  printf("Hello world!");
+  printf("Hello world!\n");
   return 0;
 }

There can potentially be a couple of changes being made in the repository. How to find the commit that introduced the initial (faulty) line? Usually, one would use git blame or git annotate and scan through the annotated diff to find the line and with it the SHA1 hash of interest. Doing so one either has to manually inspect the entire annotated file for the lines of interest or to tediously figure out the lines in a file that were modified and craft and pass in an -L argument to the blame/annotate invocation.

git-blamediff can be used to automated this process. It reads a patch such as the one above and automatically invokes git on the respective lines to print it in annotated form. For example:

$ git diff --relative --no-prefix | git blamediff
--- main.c
+++ main.c
8d4442c  6)     fprintf(stderr, "Too many arguments.\n");
8d4442c  7)     return -1;
8d4442c  8)   }
bd7ee05  9)   printf("Hello world!");
bd7ee05 10)   return 0;
bd7ee05 11) }

This example also illustrates two important properties a patch must have in order to be annotated correctly: it should contain paths relative to the current working directory (by using the --relative argument) and contain no prefixes (i.e., instead of a/some-path/some-file just use some-path/some-file; produced by providing the --no-prefix option to git).

These requirements exist to keep the program concise and not have to deal with too many special cases. Since under normal circumstances one would create an alias shortening the git diff invocation shown before, this property is not considered a restriction (see section Installation for an example of such aliases).

Continuing the workflow, one could create a fixup commit and then, at some point, perform an interactive rebase in order to automatically have the original changes amended, e.g.:

$ git add --all
$ git commit --fixup bd7ee05
...
$ git rebase --interactive --autosquash bd7ee05^

Installation

git-blamediff is written in Rust and requires the Cargo package manager to be built. It can be installed using cargo install git-blamediff.

Once installed, due to the program's name, git will recognize the command directly, so it can be invoked as git blamediff but also via git-blamediff.

To simplify usage, a git alias should be introduced. Two aliases for annotating the currently unstaged (git bd -- "git blame diff") and staged (git bds -- "git blame diff staged") changes, respectively, could look like this:

[alias]
  bd = "!bd() { git diff --relative --no-prefix | git blamediff; }; bd"
  bds = "!bds() { git diff --relative --no-prefix --staged | git blamediff; }; bds"

Dependencies

~2.7–4MB
~67K SLoC