Josh Brunton
Josh Brunton

Reputation: 527

How should I show only commits that declare a specific footer?

Reading through this question about extracting footers from commits, the answer left something to be desired for my specific use case. Using the suggested command (git log --format="%h %s %(trailers:key=Issue,valueonly)"), all commits are shown, even those that do not have an "Issue" footer.

Continuing with the example footer "Issue": Say that my repository contains some commits that do declare Issue, and some that don't, and I'm looking only for the commits that have the footer in question - what's the best way to go about showing only commits that declare a certain footer?

One option, of course, is to remove ,valueonly and then grep for "Issue:": git log --format="%h %s %(trailers:key=Issue)" | grep Issue:, but this requires the key to be shown. Obviously, in some cases, this can be worked around - following the aforementioned question, for example, we could then use sed to remove "Issue:", but that brings up the following challenges:

Another, perhaps rather convoluted, solution I've considered would be to show just %h %(trailers:key=Issue) while using --grep for Issue:[1], extract %h and git show each of those commits with the desired format. This approach leaves me with the command: git log --format="%h %(trailers:key=Issue)" --grep Issue: | awk '{print $1}' | xargs git --no-pager show, which seems un-neccesarily convoluted and inefficient (in that it runs git-log twice).

Is there a better (syntactically cleaner and/or more programmatically efficient) way of showing only commits which declare a certain footer?

[1] Thanks to Guildenstern for reminding me in the comments that git-log --grep exists and I didn't have to git log | grep.

Upvotes: 1

Views: 79

Answers (1)

Guildenstern
Guildenstern

Reputation: 3841

Unfortunately there seems to be no way to specifically search for trailers/footers directly. As you’ve correctly pointed out just searching for the strings is not reliable since the trailer keys can occur before the trailer section.

But we don’t need to do much:

git log --format="%H %(trailers:key=Issue)%-C()" \
    | sed -e '/^I/Id' \
    | sed -e '/I/!d' \
    | cut -d' ' -f1 \
    | git log --no-walk --stdin

As you’ve sketched we should start with --format:

git log --format="%H %(trailers:key=Issue)"

Next we need to filter out some needless output since a trailer key can occur multiple times in the same commit message:

fedc612314acfebf506e071bf3a941076aa56d10 Issue: 987
Issue: 465

Which means we need:

sed -e '/^I/Id'

We need the I (case-insenstive match) because apparently the trailer key matching is case-insensitive. [1]

The next sed(1) expression filters out commits without this trailer.

sed -e '/I/!d'

Here though I couldn’t get the I switch to work. And I couldn’get [Ii] to work either.

The final line takes all the input (all hashes) and displays them. --no-walk makes sure that ancestors aren’t found since they are irrelevant to your search.

git log --no-walk --stdin

(git-show(1) could also be used instead of git-log(1))

Notes

  1. This switch does not seem to be available in all sed(1) implementations

Upvotes: 0

Related Questions