215

I have two branches devel and next. In devel I have a more or less huge amount of commits. Some of the commits are cherry picked in next. Also I added some commits to next which are merged to devel.

Now I would like to see what is missing in next, so I can test the changes in detail before bringing them to next. My question is now, how can I see which commits are in devel but not in next?

reinierpost
  • 8,425
  • 1
  • 38
  • 70
Sascha Effert
  • 2,352
  • 2
  • 14
  • 10
  • 1
    Possibly related: [Using Git, show all commits that are in one branch, but not the other(s)](http://stackoverflow.com/q/1710894/456814). –  May 28 '14 at 16:54
  • your title is a bit misleading, as what you want to compare is the tip of both branches. and I came here looking for a solution two compare specific (different) commits of two branches – thebugfinder Feb 10 '15 at 07:49
  • Duplicate?: https://stackoverflow.com/questions/1710894/using-git-show-all-commits-that-are-in-one-branch-but-not-the-others – reinierpost Jun 27 '17 at 10:49

6 Answers6

246

The little-used command git cherry (docs) shows you the commits which haven't yet been cherry-picked.

In short:

git cherry <feature-branch> [base-branch]
# Checkout the base branch
git checkout main

# Diff against the target branch
git cherry -v next

# Diff against the target branch, without switching
git cherry -v next main

Example output:

+ 492508acab7b454eee8b805f8ba906056eede0ff feat: make bazzable
- 5ceb5a9077ddb9e78b1e8f24bfc70e674c627949 hotfix: off-by-one
+ b4459544c000f4d51d1ec23f279d9cdb19c1d32b feat: add bar
+ b6ce3b78e938644a293b2dd2a15b2fecb1b54cd9 feat: add foo

+ means the commit is only in next, but not in main
- means the commit is in both branches

Remove the -v if you want the commits without the subject line:

+ 492508acab7b454eee8b805f8ba906056eede0ff
- 5ceb5a9077ddb9e78b1e8f24bfc70e674c627949
+ b4459544c000f4d51d1ec23f279d9cdb19c1d32b
+ b6ce3b78e938644a293b2dd2a15b2fecb1b54cd9
coolaj86
  • 74,004
  • 20
  • 105
  • 125
Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • Great, this what I needed. It would also be nice to get a short description of the tests, but I can script this. – Sascha Effert Sep 28 '11 at 12:54
  • 33
    You don't need to `git checkout devel`, you can just do `git cherry next devel`. – Robin Winslow Oct 21 '13 at 08:21
  • 25
    _“might want to add the `-v`”_?  Cherry without a `-v` is like `ls` without an `-la`. **;-J** – Slipp D. Thompson Sep 14 '14 at 20:23
  • 2
    And you wouldn't know of a way to get `cherry` to mark or exclude equivalent commits, would you?  `cherry` seems like a plumbing command, but doesn't (appear to) offer many options.  For what I'm currently in the middle of, `git cherry` gives me false positives, but @sehe's `git log --cherry-pick` correctly excludes the previously-picked/rebased commits. – Slipp D. Thompson Sep 14 '14 at 20:38
  • The documentation provides a concrete example: https://git-scm.com/docs/git-cherry#_concrete_example – Aaron Swan Jul 23 '19 at 15:01
  • How can I limit the output commits size by number or by dates? – Sazzad Hissain Khan Feb 19 '20 at 10:14
  • How can I filter the output by commit list size and date *(i.e. `--since` like stuff)*? – Sazzad Hissain Khan Feb 19 '20 at 13:48
  • 1
    Shouldn't it be "+ means the commit is only in main, but not in next", assuming the command was `git cherry -v next main`? – Jorge Feb 28 '23 at 21:56
130

Also, you can use this to get a nice list of actual different commits not shared between the branches:

git log --left-right --graph --cherry-pick --oneline main...next

Example output:

> 492508ac (HEAD -> next) feat: make bazzable
> 5ceb5a90 hotfix: off-by-one
> b4459544 feat: add bar
> b6ce3b78 (origin/add-foo, add-foo) feat: add foo

The operative word is --cherry-pick

--cherry-pick

Omit any commit that introduces the same change as another commit on the "other side" when the set of commits are limited with symmetric difference. For example, if you have two branches, A and B, a usual way to list all commits on only one side of them is with --left-right, like the example above in the description of that option. It however shows the commits that were cherry-picked from the other branch (for example, "3rd on b" may be cherry-picked from branch A). With this option, such pairs of commits are excluded from the output.

Update As mentioned in a comment, recent versions of git added --cherry-mark:

--cherry-mark

Like --cherry-pick (see below) but mark equivalent commits with = rather than omitting them, and inequivalent ones with +.

coolaj86
  • 74,004
  • 20
  • 105
  • 125
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 2
    This did not work for me. My version of git does not know --one-line, therefore I removed it. Then I had to exchange devel and next and it worked. Very nice! – Sascha Effert Sep 28 '11 at 13:35
  • @SaschaEffert: you did not have to switch devel and next (notice THREE dots, not TWO). That said, things may be different if you used an _ancient_ version of git (?) but in that case you should have gotten a rev-parse error on the three dots. – sehe Sep 28 '11 at 13:39
  • 3
    For fun, I worked out that the '...' (symmetrical difference) rev-parse syntax **[was added in July 2006](https://github.com/gitster/git/commit/3dd4e7320de037a5b0adf3c53fbf5baf94a6c540)**, and the documentation for it [was updated in June 2008](https://github.com/gitster/git/commit/224712e521c6d4f740045affa4d1ee1454db10d4). The joy of open source! – sehe Sep 28 '11 at 13:59
  • 3
    'gls --first-parent --cherry-mark --left-only develop...next' where gls is my git log alias with all the pretty formatting. cherry-mark shows both cherry picked and not-cherry picked commits on develop, but marks them differently. – angularsen Jun 24 '14 at 07:14
  • 1
    @AndreasLarsen Yeah, I use `cherry-mark` too these days. It was a recent addition. (Mentioned it in the answer) – sehe Jun 24 '14 at 07:29
  • FINALLY, the correct way to view the differences between branches with 2000+ commits! THANK YOU! – eduncan911 Nov 28 '16 at 22:53
  • 2
    add --no-merges maybe better – Mervyn Dec 13 '19 at 03:26
58

You might could try doing git log subsets:

git log --oneline devel ^next
Bryan Buckley
  • 1,201
  • 11
  • 9
  • 4
    This is the best solution in my opinion (@sehe's answer also shows commits in next that are not in devel - in the OP's context, there are none, but in mine there are - using `--left-only` would have been better). However, this one can be improved a bit by adding `--no-merges` to omit any merge commits (e.g. if a feature or hotfix branch was merged (separately) into both devel and next). Strictly speaking, of course, conflict resolution in merges may create other differences, but that is usually not the case. The ``--no-merges`` option can usefully be applied to the other answers as well. – Alex Dupuy Jan 21 '14 at 00:32
  • This was the solution for me. Im curious though, why did I have to use the caret (parent operator)? In this case, ^next – Stobbej Feb 07 '22 at 10:44
  • 1
    @Stobbej in this case ^ means "not reachable by next" instead of "parent of next". Quote from git log:`List commits that are reachable by following the parent links from the given commit(s), but exclude commits that are reachable from the one(s) given with a ^ in front of them. The output is given in reverse chronological order by default.` – Renzhentaxi Baerde Feb 28 '22 at 01:29
35

How about

git log next..devel

Result is similar to Byran's answer (different order of commits) but both of our answers will produce commits that are different between the branches, rather just showing what's in one branch and not in the other.

justingordon
  • 12,553
  • 12
  • 72
  • 116
1

To get the list of commits that were not integrated into the release branch (next) you may use:

git rev-list --reverse --pretty="TO_TEST %h (<%ae>) %s" --cherry-pick --right-only origin/release_branch...origin/development_branch | grep "^TO_TEST " > NotIntegratedYet.txt

Check git-rev-list for more info.

Mihai
  • 89
  • 6
  • this answer's command is missing the actual branches in question, i.e. `next...devel` – Alex Dupuy Jan 21 '14 at 00:28
  • @AlexDupuy Yah, it's a pretty half-assed answer.  Since it's also not the simplest answer and doesn't explain why this approach would be better, I'm **-1**-ing it. – Slipp D. Thompson Sep 14 '14 at 20:40
1

@Mark Longair nailed it in his answer here, but I'd like to add some additional insight.

Related, and answering the question of how to break up a large Pull Request (PR), especially when squashing your commits is impractical due to one or more merges of master into your feature_branch

My situation:
I made a big feature_branch with 30 commits and opened a Pull Request (PR) on GitHub to merge it into master. Branch master changed a ton underneath me, and received 200 commits my feature_branch didn't have. To resolve conflicts I did git checkout feature_branch and git merge master to merge master's changes into my feature_branch. I chose to merge rather than rebase onto latest master so I would have to resolve conflicts only one single time instead of potentially 30 times (once for each of my commits). I didn't want to squash my 30 commits into 1 first and then rebase onto the latest master because that might wipe away GitHub review comment history in the PR. So, I merged master into my feature branch and resolved conflicts 1 single time. All was well. My PR, however, was too big for my colleagues to review. I needed to split it up. I went to squash my 30 commits and OH NO! WHERE ARE THEY? THEY ARE ALL INTERMINGLED WITH master's 200 recent commits now because I merged master into my feature_branch! WHAT DO I DO?

git cherry usage in case you want to try to git cherry-pick individual commits:

git cherry to the rescue (sort of)!

To see all the commits that are in feature_branch but NOT in master I can do:

git checkout feature_branch
git cherry master

OR, I can check commits from ANY branch withOUT ensuring I'm on feature_branch first by doing git cherry [upstream_branch] [feature_branch], like this. Again, this checks to see which commits ARE in feature_branch but are NOT in upstream_branch (master in this case):

git cherry master feature_branch

Adding -v also shows the commit message subject lines:

git cherry -v master

Piping to "word count" "-lines" (wc -l) counts how many commits there are:

git cherry master | wc -l

You can compare this count against the commit number shown in your GithHub PR to feel better about knowing git cherry really is working. You can also compare the git hashes one by one and see they match between git cherry and GitHub. Note that git cherry will NOT count any merge commits where you merged master into feature_branch, but GitHub WILL. So if you see a small discrepancy in the count, search the GitHub PR commit page for the word "merge" and you'll probably see that's the culprit which is not showing up in git cherry. Ex: a commit titled "Merge branch 'master' into feature_branch" will show up in the GitHub PR but not when you run git cherry master feature_branch. This is fine and expected.

So, now I have a means of finding out which diffs I may want to cherry-pick onto a fresh feature branch to split up this diff: I can use git cherry master feature_branch locally, or look at the commits in the GitHub PR.

How squashing could help--if only we could squash:

An alternative, however, to split up my big diff is to squash all 30 of my commits into one, patch that onto a new feature branch, soft reset the patch commit, then use git gui to add pieces file by file, chunk by chunk, or line by line. Once I get one sub-feature, I can commit what I've added then check out a new branch, add some more, commit, check out a new branch, etc, until I have my big feature broken out into several sub-features. The problem is that my 30 commits are intermingled with the other 200 commits from other people due to my git merge master into my feature_branch, so rebasing is therefore impractical, as I'd have to sift through 230 commits to re-order and squash my 30 commits.

How to use a patch file as a much easier replacement for squashing:

A work-around is to simply obtain a patch file containing a "squash-equivalent" of all 30 of my commits, patch it onto a new fork of master (a new sub-feature-branch), and work from there, as follows:

git checkout feature_branch
# ensure I have the latest changes from master merged into feature_branch
git merge master 
# Obtain a patch file, which is the equivalent of a squash of my 30 commits into 1 commit:
git diff master..feature_branch > ~/mypatch.patch
git checkout master
# Create a new, sub-feature branch
git checkout -b feature_branch2
# Patch the 30 commit patch file onto it:
git apply ~/mypatch.patch

Now I have my 30-commit patch all applied locally, but unstaged and uncommitted.

Now use git gui to add files, chunks, and/or lines and break up your big PR or "diff":

Note that if you don't have git gui, you can easily install it in Ubuntu with sudo apt install git-gui.

I can now run git gui and start adding files, chunks, and/or lines (by right-clicking in the git GUI program), and break up the 30 commit feature branch into sub branches as described just above, repeatedly adding, committing, then forking a new feature branch and repeating this cycle until all changes have been added to a sub-feature-branch and my 30-commit feature is successfully broken up into 3 or 4 sub-features. I can open up a separate PR for each of these sub-features now, and they will be easier for my team to review.

References:

  1. Create patch or diff file from git repository and apply it to another different git repository
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265