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?
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 byA...B
, which means "all commits that are on branchA
or branchB
, excluding any commits that are on both branches." This is howgit 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 sayfirstbranch...secondbranch
, keep only those that are both different and insecondbranch
... which is exactly whatgit cherry
does.Hence:
produces the same commit ID list as:
(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 needgit 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 togit log
, but now we get to the tricky bit.git log
andgit rev-list
are very nearly the same commandIn fact, they are built from the same source code, they just set some internal options differently (and
git log
will pretend you saidHEAD
if you don't name any other starting points). Since we're about to pipe the output ofgit rev-list
togit log --pretty='%h %ce'
, maybe we can just do the whole thing ingit log
directly.Sure enough, we can. We need all those same options as with
git rev-list
, but now we can just run:(you may want
--no-merges
here as well—I'm not sure offhand whatgit 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.)