How can I preview a merge in git?
Asked Answered
H

12

509

I have a git branch (the mainline, for example) and I want to merge in another development branch. Or do I?

In order to decide whether I really want to merge this branch in, i'd like to see some sort of preview of what the merge will do. Preferably with the ability to see the list of commits that are being applied.

So far, the best I can come up with is merge --no-ff --no-commit, and then diff HEAD.

Housum answered 28/4, 2011 at 11:4 Comment(9)
I'd just git merge and git reset --keep HEAD@{1} if I don't like the result.Lempira
Note that seeing the list of commits with their diff doesn't necessarily tell the whole story - if the merge is non-trivial, and especially if there are conflicts, the actual result of the merge might be a bit interesting.Schmit
The problem with this is trying to easily see what actual changes have been made.Housum
Your original method does exactly that. The point of my comment is that although looking at individual diffs is all well and good, if you have a complex merge, you may end up with surprising results even if all the merged commits are independently good.Schmit
@Jan: For some reasons, git reset --keep HEAD@{1} returned fatal: Cannot do a keep reset in the middle of a merge. Help?Creamy
@Siku-Siku.Com: The command assumes the merge finished and commited the result. I believe the message about conflicts tells you both how to finish and how to abort the merge.Lempira
possible duplicate of Is there a git-merge --dry-run option?Jehol
Why isn't there a --preview option in git-merge?Parturifacient
I think the most correct answer I've seen for this is here: https://mcmap.net/q/47079/-is-there-a-git-merge-dry-run-optionMaximamaximal
F
348

I've found that the solution the works best for me is to just perform the merge and abort it if there are conflicts. This particular syntax feels clean and simple to me. This is Strategy 2 below.

However, if you want to ensure you don't mess up your current branch, or you're just not ready to merge regardless of the existence of conflicts, simply create a new sub-branch off of it and merge that:

Strategy 1: The safe way – merge off a temporary branch:

git checkout mybranch
git checkout -b mynew-temporary-branch
git merge some-other-branch

That way you can simply throw away the temporary branch if you just want to see what the conflicts are. You don't need to bother "aborting" the merge, and you can go back to your work -- simply checkout 'mybranch' again and you won't have any merged code or merge conflicts in your branch.

This is basically a dry-run.

Strategy 2: When you definitely want to merge, but only if there aren't conflicts

git checkout mybranch
git merge some-other-branch

If git reports conflicts (and ONLY IF THERE ARE conflicts) you can then do:

git merge --abort

If the merge is successful, you cannot abort it (only reset).

If you're not ready to merge, use the safer way above.

[EDIT: 2016-Nov - I swapped strategy 1 for 2, because it seems to be that most people are looking for "the safe way". Strategy 2 is now more of a note that you can simply abort the merge if the merge has conflicts that you're not ready to deal with. Keep in mind if reading comments!]

Froebel answered 28/5, 2015 at 20:49 Comment(7)
+1 for strategy 2. Temporary branch that shows exactly what will go in when merging. Strategy 1 is what I am trying to avoid for this particular case.Caron
I suggest changing the strategies around so the safer one is first (my psych training coming through - most people would assume the best option would be the first one, despite the clear use of the word "safer") but, other than that, excellent job.Footcandle
You could do a git merge --no-ff --no-commit if you do not want to commit the changes directly. This eliminates the "need" for a different branch makes it a tiny bit easier to review the changes, imo.Ulphia
and if you decide you don't want to merge at all and in fact would rather write some more code and then commit to your branch so you can deploy JUST your branch on a test server, wouldn't you still have to revert the git merge --no-ff --no-commit? I guess you can still do a git merge --abort after that if you don't like what you see? Even if the merge doesn't produce conflicts?Froebel
Most people like to copy and paste and not think too much. Therefore, for Strategy 1, perhaps also add how to abort the merge in the temporary local branch git reset --hard HEAD and then checkout a different branch git checkout <different branch name> and delete the temporary branch git delete -b <name of temporary branch>.Ricker
I think you should add at the end of Strategy 1: if I like the result of the merge in the temporary branch, go back to 'mybranch' and do the actual merge of some-other-branch. (Is that correct?)Tonguing
@GerardvanHelden you're right, and after that, you'll have two options: 1. git commit which will conclude the merge, or 2. git merge --abort which will discard the merge.Purehearted
L
520
  • git log ..otherbranch
    • list of changes that will be merged into current branch.
  • git diff ...otherbranch
    • diff from common ancestor (merge base) to the head of what will be merged. Note the three dots, which have a special meaning compared to two dots (see below).
  • gitk ...otherbranch
    • graphical representation of the branches since they were merged last time.

Empty string implies HEAD, so that's why just ..otherbranch instead of HEAD..otherbranch.

The two vs. three dots have slightly different meaning for diff than for the commands that list revisions (log, gitk etc.). For log and others two dots (a..b) means everything that is in b but not a and three dots (a...b) means everything that is in only one of a or b. But diff works with two revisions and there the simpler case represented by two dots (a..b) is simple difference from a to b and three dots (a...b) mean difference between common ancestor and b (git diff $(git merge-base a b)..b).

Lempira answered 28/4, 2011 at 12:2 Comment(15)
The third dot was the part I was missing, thanks! The log approach works well also, log -p --reverse ..otherbranch seems like a good way to see what would be merged in.Housum
I was missing git checkout master before I tried this. I was wondering why it was saying all of my changes were going to be over-written...Undertone
git show ..otherbranch will show a list of changes and diff that will be merged into current branch.Parturifacient
Just wanted to add that if you run 'git difftool ...otherbranch' it will actually run mergetool so it is a 3-way diff -- notice it doesn't use the difftool configuration. The left is the ancestor, center is local and right is otherbranch versions. I use Meld as my mergetool (and difftool), but I am sure it would work with others.Sinkhole
This is not completely accurate, especially for cherry picks.Preside
@Sinkhole I want to do what you explained in your comment (above), but it doesn't work for me. I've opened a new question looking for answers. If you have anything to suggest there, I'd love to hear it!Alisa
@noisygecko, does it? It definitely does not for me.Lempira
@void.pointer, git does not treat cherry-picks specially in normal merge so it does not here either. A merge strategy could be written that would, but as far as I know, it never was.Lempira
@JanHudec the problem is if your current branch already cherry-picked (or somehow included) some changes from otherbranch, this will still show them. You only want to see what will actually change. This is especially relevant now with GitHub's "squash and merge" making this a common workflow.Irrational
@hraban, the middle method will not; git diff ...branch will show exactly the diff that git merge --squash will apply (sans recursive strategy; I think diff isn't able to synthesize the common ancestor in criss-cross in the same way). A git rebase -i will also try to exclude any cherry-picked commits, but I don't know how to just show a list of what git rebase -i would pick.Lempira
@JanHudec I'm not sure if that's true. git diff ...develop == git diff $(git merge-base HEAD develop) develop == "all changes in develop since the last merge base with HEAD". If you just cherry-picked a change from develop into HEAD, that doesn't change anything about develop, so it will still show up. You can try this locally and observe the difference between that solution, and git merge-tree $(git merge-base HEAD develop) HEAD develop.Irrational
@hraban, A three-way merge is equivalent to applying diff from base to remote on local except for conflict handling. Therefore, git merge-tree $(git merge-base HEAD develop) HEAD develop is equivalent to git diff $(git merge-base HEAD develop) develop | git apply, which is git diff ...develop | git apply. If HEAD already has some of the changes, they will do nothing, but they will still be there.Lempira
I can believe that's how it works under the hood. However, if you want to preview a merge, is it practical to have to think at every change: wait was this already cherry-picked? Git apply dropping (or conflicting) those changes is precisely what you'd want to preview. That is if we stick to the original question; if this becomes a question about merging and diffing internals of git, then I am completely with you.Irrational
@hraban, yeah, true, just doing it and discarding it if it was wrong is easiest and accurate.Lempira
If you want to see it from the other branch's point of view put the 3 dots at the end and it'll show you a preview of the merge of "HEAD" into "otherbranch": git diff otherbranch... (useful when you're on a feature branch and you want to know what you'd apply to main). This seems to be what github shows when comparing branches or showing PR diffs.Fyn
F
348

I've found that the solution the works best for me is to just perform the merge and abort it if there are conflicts. This particular syntax feels clean and simple to me. This is Strategy 2 below.

However, if you want to ensure you don't mess up your current branch, or you're just not ready to merge regardless of the existence of conflicts, simply create a new sub-branch off of it and merge that:

Strategy 1: The safe way – merge off a temporary branch:

git checkout mybranch
git checkout -b mynew-temporary-branch
git merge some-other-branch

That way you can simply throw away the temporary branch if you just want to see what the conflicts are. You don't need to bother "aborting" the merge, and you can go back to your work -- simply checkout 'mybranch' again and you won't have any merged code or merge conflicts in your branch.

This is basically a dry-run.

Strategy 2: When you definitely want to merge, but only if there aren't conflicts

git checkout mybranch
git merge some-other-branch

If git reports conflicts (and ONLY IF THERE ARE conflicts) you can then do:

git merge --abort

If the merge is successful, you cannot abort it (only reset).

If you're not ready to merge, use the safer way above.

[EDIT: 2016-Nov - I swapped strategy 1 for 2, because it seems to be that most people are looking for "the safe way". Strategy 2 is now more of a note that you can simply abort the merge if the merge has conflicts that you're not ready to deal with. Keep in mind if reading comments!]

Froebel answered 28/5, 2015 at 20:49 Comment(7)
+1 for strategy 2. Temporary branch that shows exactly what will go in when merging. Strategy 1 is what I am trying to avoid for this particular case.Caron
I suggest changing the strategies around so the safer one is first (my psych training coming through - most people would assume the best option would be the first one, despite the clear use of the word "safer") but, other than that, excellent job.Footcandle
You could do a git merge --no-ff --no-commit if you do not want to commit the changes directly. This eliminates the "need" for a different branch makes it a tiny bit easier to review the changes, imo.Ulphia
and if you decide you don't want to merge at all and in fact would rather write some more code and then commit to your branch so you can deploy JUST your branch on a test server, wouldn't you still have to revert the git merge --no-ff --no-commit? I guess you can still do a git merge --abort after that if you don't like what you see? Even if the merge doesn't produce conflicts?Froebel
Most people like to copy and paste and not think too much. Therefore, for Strategy 1, perhaps also add how to abort the merge in the temporary local branch git reset --hard HEAD and then checkout a different branch git checkout <different branch name> and delete the temporary branch git delete -b <name of temporary branch>.Ricker
I think you should add at the end of Strategy 1: if I like the result of the merge in the temporary branch, go back to 'mybranch' and do the actual merge of some-other-branch. (Is that correct?)Tonguing
@GerardvanHelden you're right, and after that, you'll have two options: 1. git commit which will conclude the merge, or 2. git merge --abort which will discard the merge.Purehearted
I
28

Most answers here either require a clean working directory and multiple interactive steps (bad for scripting), or don't work for all cases, e.g. past merges which already bring some of the outstanding changes into your target branch, or cherry-picks doing the same.

To truly see what would change in the master branch if you merged develop into it, right now:

git merge-tree $(git merge-base master develop) master develop

As it's a plumbing command, it does not guess what you mean, you have to be explicit. It also doesn't colorize the output or use your pager, so the full command would be:

git merge-tree $(git merge-base master develop) master develop | colordiff | less -R

https://git.seveas.net/previewing-a-merge-result.html

(thanks to David Normington for the link)

P.S.:

If you would get merge conflicts, they will show up with the usual conflict markers in the output, e.g.:

$ git merge-tree $(git merge-base a b ) a b 
added in both
  our    100644 78981922613b2afb6025042ff6bd878ac1994e85 a
  their  100644 61780798228d17af2d34fce4cfbdf35556832472 a
@@ -1 +1,5 @@
+<<<<<<< .our
 a
+=======
+b
+>>>>>>> .their

User @dreftymac makes a good point: this makes it unsuitable for scripting, because you can't easily catch that from the status code. The conflict markers can be quite different depending on circumstance (deleted vs modified, etc), which makes it hard to grep, too. Beware.

Irrational answered 28/2, 2019 at 13:35 Comment(7)
@Irrational This looks like the correct answer, however there is still apparently one missing element. This approach requires the user to "eyeball" the output to see if the conflict markers are present. Do you have an approach that simply returns true if there are conflicts and false if there are no conflicts (e.g., a boolean value that does not require the "eyeball" test or any human intervention).Toilsome
@Toilsome can't find anything to that effect. you could use something like git merge-tree ... | grep -q '^[a-z].*in both$' && echo conflict || echo safe to merge but it's finnicky; I'm probably forgetting a case. maybe you want to check for the conflict markers, instead? e.g. this probably doesn't catch "theirs deleted, ours changed" style conflicts. (I just checked and that doesn't even show conflict markers, so you need a meticulous regex to be safe here)Irrational
Use less -R to handle coloured output without modifying your configuration.Adverbial
This is the only right answer. Now sure why other are upvoted.Teaser
“Most answers here either require a clean working directory” Then do git worktree add ../test-merge && cd ../test-merge && git merge […]. The working tree then becomes irrelevant. And you can use the command directly instead of having to think about what git merge does in terms of low-level commands.Jenifferjenilee
Note that before git version 2.38 this command won't detect the case "FILE deleted in A and modified in B". My recommendation is to use git >= 2.40 which introduces --merge-base option, which when used makes the output contain the extra conflict info, e.g.: git merge-tree --merge-base $(git merge-base A B) A B it will give you an output like this: CONFLICT (modify/delete): FILE deleted in A and modified in B. Version B of FILE left in tree.Cosmography
Any pure git solution (I believe) would need to use two steps. The $ ( ) bit in this solution isn't a git command, it's using bash(?) to just do two steps at once. As such I don't think it would work in any shell or as a git alias?Rabb
D
19

If you're like me, you're looking for equivalent to svn update -n. The following appears to do the trick. Note that make sure to do a git fetch first so that your local repo has the appropriate updates to compare against.

$ git fetch origin
$ git diff --name-status origin/master
D       TableAudit/Step0_DeleteOldFiles.sh
D       TableAudit/Step1_PopulateRawTableList.sh
A       manbuild/staff_companies.sql
M       update-all-slave-dbs.sh

or if you want a diff from your head to the remote:

$ git fetch origin
$ git diff origin/master

IMO this solution is much easier and less error prone (and therefore much less risky) than the top solution which proposes "merge then abort".

Diplopia answered 1/11, 2012 at 22:27 Comment(3)
should be $ git checkout target-branch and then $ git diff --name-status ...branch-to-be-merged (three dots are essential so type them as is)Metallophone
One thing this is missing: it doesn't show you which files would have a conflict -- they have the same "M" marker as files that have been modified on the branch but can be merged without any conflicts.Commix
Maybe things were different in 2012, but these days aborting a merge seems quite reliable, so I don't think it's fair to characterize this as "much easier and less error prone" right now.Grumous
B
6

Adding to the existing answers, an alias could be created to show the diff and/or log prior to a merge. Many answers omit the fetch to be done first before "previewing" the merge; this is an alias that combines these two steps into one (emulating something similar to mercurial's hg incoming / outgoing)

So, building on "git log ..otherbranch", you can add the following to ~/.gitconfig :

...
[alias]
    # fetch and show what would be merged (use option "-p" to see patch)
    incoming = "!git remote update -p; git log ..@{u}"

For symmetry, the following alias can be used to show what is committed and would be pushed, prior to pushing:

    # what would be pushed (currently committed)
    outgoing = log @{u}..

And then you can run "git incoming" to show a lot of changes, or "git incoming -p" to show the patch (i.e., the "diff"), "git incoming --pretty=oneline", for a terse summary, etc. You may then (optionally) run "git pull" to actually merge. (Though, since you've already fetched, the merge could be done directly.)

Likewise, "git outgoing" shows what would be pushed if you were to run "git push".

Bullwhip answered 5/9, 2012 at 6:33 Comment(3)
"Many answers omit the fetch" - That is because the question was about merge preview; not pull or push. There is no need to fetch before a merge except in the specific case of pull (although when merging into a branch that exists remotely, it is generally a good idea to pull that branch first, to make sure you have an up-to-date version of it).Disrespectful
If you'll re-read your comment closely, you'll see that you conflate "pull" with "merge" and "fetch", which is common, but confusing when the distinction is important (as it is here). https://mcmap.net/q/12376/-what-is-the-difference-between-39-git-pull-39-and-39-git-fetch-39Bullwhip
=2, "conflate" - cool word! :) I do actually like your answer too, it shows a use case for merge-preview that some may find helpful. Now if you re-read the question, and then my first comment to your answer, you may notice that I fully intended to emphasize that a pull (which in essence is just a fetch followed by a merge) is the only case where you would need to fetch at all before previewing or applying a merge. And the OP asked specifically about the merge part only,which has many use cases apart from updating a local branch with changes on its remote counterpart.Disrespectful
W
6

If you already fetched the changes, my favourite is:

git log ...@{u}

That needs git 1.7.x I believe though. The @{u} notation is a "shorthand" for the upstream branch so it's a little more versatile than git log ...origin/master.

Note: If you use zsh and the extended glog thing on, you likely have to do something like:

git log ...@\{u\}
Whirlabout answered 3/2, 2013 at 18:50 Comment(0)
S
4

Short of actually performing the merge in a throw away fashion (see Kasapo's answer), there doesn't seem to be a reliable way of seeing this.

Having said that, here's a method that comes marginally close:

git log TARGET_BRANCH...SOURCE_BRANCH --cherry

This gives a fair indication of which commits will make it into the merge. To see diffs, add -p. To see file names, add any of --raw, --stat, --name-only, --name-status.

The problem with the git diff TARGET_BRANCH...SOURCE_BRANCH approach (see Jan Hudec's answer) is, you'll see diffs for changes already in your target branch if your source branch contains cross merges.

Schedule answered 19/4, 2016 at 5:0 Comment(0)
F
4

I tried this thing to review the changes in visual studio code.

Create a temporary branch from dev. Then merge the branch in which you have changed the file with --no-ff --no-commit flag.

git checkout dev 
git checkout -b feature_temp 
git merge feature --no-ff --no-commit

The changed file of your feature branch will be reflecting in the feature_temp branch.

Follower answered 15/9, 2021 at 6:55 Comment(1)
No need to checkout to a temporary branch. You can do it directly on dev. After that, you'll have two options: 1. git commit which will conclude the merge, or 2. git merge --abort which will discard the merge.Purehearted
S
3

git log currentbranch..otherbranch will give you the list of commits that will go into the current branch if you do a merge. The usual arguments to log which give details on the commits will give you more information.

git diff currentbranch otherbranch will give you the diff between the two commits that will become one. This will be a diff that gives you everything that will get merged.

Would these help?

Subak answered 28/4, 2011 at 11:46 Comment(2)
Actually, wrong. git log otherbranch..currentbranch gives list of commits on currentbranch. The git diff otherbranch currentbranch gives you diff from the to-be-merged version to current tip, which is about as useless as it can be, because what you want is diff from the merge base to the merge head.Lempira
Thanks. I've switched the names of the treeishes.Subak
T
3

Pull Request - I've used most of the already submitted ideas but one that I also often use is ( especially if its from another dev ) doing a Pull Request which gives a handy way to review all of the changes in a merge before it takes place. I know that is GitHub not git but it sure is handy.

Truitt answered 6/4, 2017 at 13:11 Comment(0)
P
1

Maybe this can help you ? git-diff-tree - Compares the content and mode of blobs found via two tree objects

Planar answered 28/4, 2011 at 11:12 Comment(0)
M
1

I do not want to use the git merge command as the precursor to reviewing conflicting files. I don't want to do a merge, I want to identify potential problems before I merge - problems that auto-merge might hide from me. The solution I have been searching for is how to have git spit out a list of files that have been changed in both branches that will be merged together in the future, relative to some common ancestor. Once I have that list, I can use other file comparison tools to scout things out further. I have searched multiple times, and I still haven't found what I want in a native git command.

Here is my workaround, in case it helps anyone else out there:

In this scenario I have a branch called QA that has many changes in it since the last production release. Our last production release is tagged with "15.20.1". I have another development branch called new_stuff that I want to merge into the QA branch. Both QA and new_stuff point to commits that "follow" (as reported by gitk) the 15.20.1 tag.

git checkout QA
git pull
git diff 15.20.1 --name-only > QA_files
git checkout new_stuff
git pull
git diff 15.20.1 --name-only > new_stuff_files
comm -12 QA_files new_stuff_files

Here are some discussions that hit on why I'm interested in targeting these specific files:

How can I trust Git merge?

https://softwareengineering.stackexchange.com/questions/199780/how-far-do-you-trust-automerge

Monroe answered 21/10, 2015 at 19:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.