How do I use git cherry to get committer email?

470 views Asked by At

git cherry is superior to git log for getting the difference between two branches. The problem is that its output is limited.

I'm trying to extract the email address (or user) associated with a commit. Here is what I'm doing.

git cherry firstbranch secondbranch | awk '/^+/ {print $2}' | awk '{ system("git show $1"); }'

All I get is the details of one commit. Instead of every commit that I do get with:

git cherry firstbranch secondbranch | awk '/^+/ {print $2}'

Something is going wrong with the second pipe operation.

My question is: How do I use git cherry to get committer email?

2

There are 2 answers

1
torek On BEST ANSWER

Steven Penny's approach of piping to git log --no-walk --stdin is a correct method, and is required in the most complex cases (complex filtering of selected commit-IDs).

There are a couple of tricks that one can use for this particular case though. If you check the (extensive, albeit confusing) documentation for git rev-list you will find the options --left-right, --left-only, --right-only, --cherry-mark, --cherry-pick, and --cherry.

The --left-right option works with any symmetric difference, i.e., commits selected by A...B, which means "all commits that are on branch A or branch B, excluding any commits that are on both branches." This is how git cherry does the first part of its thing: it finds commits that are on the symmetric difference. However, git cherry goes on to throw out all the commits that are "the same"1 on both branches, then marks those on one side with - and those on the other with +. The --left-right option marks all commits as either < (left side) or > (right side).

Since rev-list is the Swiss Army Chainsaw command, it also has the ability to throw out commits that are the same. It has even more abilities too: it can throw out commits on one side entirely, same-or-different. This is what we want in this case: throw out "commits that are the same" (so that we keep only "different" ones), then throw out all "left side" commits (so that we keep only those that are in the right side but not the left). We get the former with --cherry-pick: it keeps only commits that are considered "different". Then we add --right-only to keep only those on the right side, i.e., when we say firstbranch...secondbranch, keep only those that are both different and in secondbranch ... which is exactly what git cherry does.

Hence:

git rev-list --right-only --cherry-pick firstbranch...secondbranch

produces the same commit ID list as:

git cherry firstbranch secondbranch

(with the single difference being that instead of + face0ff..., it prints +face0ff, i.e., no space after the + mark).

This seems pretty silly: instead of just the four words git cherry and two branch names, we need git rev-list and a bunch of options and two branch names and three periods. We've typed in lots more letters to get pretty much the same thing. So we could just use the shorter command and pipe it to git log, but now we get to the tricky bit.

git log and git rev-list are very nearly the same command

In fact, they are built from the same source code, they just set some internal options differently (and git log will pretend you said HEAD if you don't name any other starting points). Since we're about to pipe the output of git rev-list to git log --pretty='%h %ce', maybe we can just do the whole thing in git log directly.

Sure enough, we can. We need all those same options as with git rev-list, but now we can just run:

git log --cherry-pick --right-only --pretty='%h %ce' firstbranch...secondbranch

(you may want --no-merges here as well—I'm not sure offhand what git cherry does about merges!).


1"Sameness", in this case, is determined by the output of git patch-id, which basically strips identifying line numbers off diffs (and also strips certain white space details). Thus, if one commit was cherry-picked into another branch, both such commits will usually have the same patch-ID. (The patch-IDs will differ if someone had to resolve merge conflicts.)

4
Zombo On

Something like this:

git cherry firstbranch secondbranch | awk '$0=$2' | git log --no-walk --stdin --pretty='%h %ce'
  1. --no-walk: only show the given commits, but do not traverse their ancestors

  2. %h: abbreviated commit hash

  3. %ce: committer email

Source