I want to use git cherry
to show all commits that went to a specific branch but are missing from the master branch and log this using pretty format.
I tried this:
git cherry master branch_name | grep "^+" | sed 's/^+ //' | xargs -I {} git --no-pager log --pretty=format:'%h,%an,%d,%ae,%ad,%s' --date=short -1 {}
What this does is to:
git cherry master branch_name
- list in the terminal line by line all hashes of commits that have code in branch_name and not in master and the other way around with a + or a - sign before the hash.
grep "^+"
- get only those that start with a + (the ones missing in master from branch_name actually)
sed 's/^+ //'
- remove the +
from the line so we can get only the hash.
xargs -I {} git --no-pager log --pretty=format:'%h,%an,%d,%ae,%ad,%s' --date=short -1 {}
- passes each hash as a parameter to git log to show the formatted log of each hash. Here --no-pager
mean to exit git log window and -1
to show only 1 commit.
The problem with this is that it starts concatenating each result for each hash returned from git cherry on the same line. It doesn't execute it line by line so I can copy paste them or so I can save them to a csv file using ... > tmp.csv
.
Not sure if there is anything else I can use apart for xargs
or if that is actually a problem with git log and this won't work.
What am I missing? and what am I doing wrong?
Thank you all and sorry for being a noob.
You used --pretty=format:...
. Note that there is a --pretty=tformat:...
as well, and that it is a default:
Commit Formatting
--pretty[=<format>], --format=<format>
Pretty-print the contents of the commit logs in a given format ... When <format> is [not a predefined format like short, medium, oneline, etc] and has %placeholder in it, it acts as if
--pretty=tformat:<format>
were given....
The
tformat:
format works exactly likeformat:
, except that it provides "terminator" semantics instead of "separator" semantics. In other words, each commit has the message terminator character (usually a newline) appended, rather than a separator placed between entries. This means that the final entry of a single-line format will be properly terminated with a new line ...
The --pretty=tformat:...
form can therefore be abbreviated as --format=...
.
--no-walk
and sed
tricksAside from this, there's a somewhat better (more efficient) way to achieve the desired result. You can still use git cherry
like this, and use sed
to strip off the markers, just as before. You can omit the grep
since sed
can do that as well: use sed -n
and s/pattern/replacement/p
. And, now that you have selected the right commits, you can then give them all as arguments to one git log
command, and use --no-walk
to make git log
look only at the supplied hashes, and not at any of their ancestors.
That is, instead of:
git cherry master branch_name | grep "^+" | sed 's/^+ //' | xargs -I {} \
git --no-pager log --format='%h,%an,%d,%ae,%ad,%s' --date=short -1 {}
you could do:
git log --no-walk --format='%h,%an,%d,%ae,%ad,%s' --date=short \
$(git cherry master branch_name | sed -n 's/^+ //p')
(you can now omit --no-pager
too, as I did, since it's safe enough to let the pager run).
If git log
were not essentially the same command as git rev-list
, this would be the way to go—but in fact, git log
and git rev-list
take all the same commit selection arguments. Furthermore, git cherry
is mostly just a front end for git rev-list
,1 so there's an even more efficient way to do this:
git log --right-only --cherry-pick --format="$format" master...branch
where $format
is your desired format. (I think—getting the left/right only, cherry-pick/cherry-mark arguments correct is a bit tricky; this is based on a quick scan through the man page and somewhat hasty reading of your question.)
1 When using git cherry
with a <limit>
argument, I am not sure you can get the same from git rev-list
, though ^<limit>
might work.