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:

  1. 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.

  2. grep "^+" - get only those that start with a + (the ones missing in master from branch_name actually)

  3. sed 's/^+ //' - remove the + from the line so we can get only the hash.

  4. 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.

Question author Radum | Source



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 like format:, 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=....

Simplifying: --no-walk and sed tricks

Aside 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).

But in fact, it's all built in

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.

Answer author Torek

Ask about this question here!