98

I have two branches with the exact same file (incase you are wondering it is a .sql file) and I want to interactively merge it.

Pretty much I want to open up a diff program like I do when there is a conflict (or command line) and select exactly what lines go where.

Is there anyway to do this?

Phil Miller
  • 36,389
  • 13
  • 67
  • 90
Steven
  • 13,250
  • 33
  • 95
  • 147
  • You might argue answers to this question behave more like "--squash" vs the basic merge most people use. The basic merge is great because it preserves git history; squash does not. Is an interactive approach incompatible with preserving things like author, commit message, past changes, etc? – Kay V Sep 16 '21 at 22:34

6 Answers6

95

Yes, but it will mostly be by manually making that happen. You'll tell Git you're merging the two relevant branches, but that it shouldn't try to commit the result on its own, (edited to add: nor fast-forward if it thinks the merge is trivial):

git merge --no-commit --no-ff branch-to-merge

Then you'll ask git for the file as it appeared in the two branches:

git show HEAD:filename >filename.HEAD
git show branch-to-merge:filename >filename.branch

and their merge base,

git show `git merge-base HEAD branch-to-merge`:filename  >filename.base

You'll merge them using whatever tool you want (e.g.)

meld filename.{HEAD,branch,base}

you'll stage that (git add filename), and then commit the merge (git commit).

Phil Miller
  • 36,389
  • 13
  • 67
  • 90
  • 2
    Wondering the same thing Graham... I tried doing `git merge --no-commit branch` and it ended up merging it... What I'd like is to end up with 3 files, or 2 files. That I could use a diff and move over everything I want into that file. – Steven Jun 07 '12 at 15:55
  • If you want the original merge command to leave *everything* alone, to be done manually, you can call it as `git merge -s ours` to keep everything as it is in the current branch, but then it's up to you to ensure that changes in other files make it over. – Phil Miller Jun 07 '12 at 16:00
  • @Steven; I think the idea is you ignore the merged file and use the HEAD and branch files i.e. the "two files" you're after are HEAD:filename and branch-to-merge:filename. – Adrian Mouat Jul 11 '13 at 10:47
  • 7
    Git Gotcha: `git merge --no-commit --no-ff` will not commit anything at all, see @Brad-O answer below. You have to include `--no-ff` – Ron Wertlen Aug 22 '13 at 08:57
  • I made a git alias from the above commands. It is hairy, but it works. You do need to have the config variable "merge.tool" set for it to work. Also, my version doesn't create a new file with ".base" appended; it just overwrites the file in the current branch. You call the alias as `git remerge `. You must be in the git root dir for it to work: `git config --global alias.remerge '!sh -c "git show HEAD:${2} > ${2}.HEAD; git show ${1}:${2} > ${2}.${1}; git show $(git merge-base HEAD ${1}):${2} > ${2}; $(git config --get merge.tool) ${2}.HEAD ${2} ${2}.${1};"'` – eestrada Feb 01 '15 at 21:39
  • Does `git merge --no-commit --no-ff branch-to-merge` keep commit history of branch-to-merge? – Kootoopas May 14 '18 at 07:58
  • 1
    @Kootoopas: yes – Phil Miller May 14 '18 at 12:12
49

From the branch you want to merge into:

git checkout -p branch_to_merge --

This won't checkout the branch_to_merge, but will let you interactively add hunks from the patch (diff).

http://git-scm.com/docs/git-checkout

taj
  • 611
  • 6
  • 5
  • 3
    This was very easy to use for interactive merging - thanks! – cbcoutinho Apr 17 '18 at 19:53
  • 2
    This does exactly what I was looking for. Why isn't this the accepted answer? – iliis Oct 16 '19 at 08:29
  • 2
    This command presents each "hunk" (differences in the files that are grouped close together) and an option of "yes" or "no" to use it or not. Very useful. – Mark Lakata Feb 19 '20 at 23:00
  • 1
    For everything you've done on the branch and which is not on master, this assumes it should be deleted. That is not the smartest merge strategy. – Anne van Rossum Apr 15 '20 at 11:15
  • 1
    @AnnevanRossum - hoping better to understand your warning. Doesn't -p actually compare what was done on each branch and show you the difference, no matter the origin? – Kay V Apr 22 '21 at 18:43
  • @KayV Just don't see it only as interactively adding (c)hunks, see it just as well as interactively discarding changes in your working directory. – Anne van Rossum Apr 23 '21 at 09:35
  • @AnnevanRossum - thanks for the response; I'm interested in understanding but still not following. Are you referring to changes that are not staged at the time you run the command? – Kay V Apr 23 '21 at 15:33
  • @KayV I don't mean anything complicated. Just create a simple git test project for yourself. You'll be interactively asked if staged/committed stuff on that branch can be removed if it doesn't exist on the branch you're getting your patches from. It's perfectly reasonable and to be expected behavior, but might still catch someone by surprise. – Anne van Rossum Apr 24 '21 at 19:39
  • Thanks for the reply, @AnnevanRossum - I actually use this method pretty frequently and have not run into the downside you mention. Naturally if I'm overlooking something I'd like to discover before it's a problem. Unfortunately the details of your warning are unclear to me - hence my questions. Can you describe the setup you recommend so I can see it myself? – Kay V Apr 24 '21 at 20:55
  • 1
    This has been by far the best answer in my opinion, exactly what was initially asked: `An interactive merge approach for git` – Andreas L. Sep 15 '21 at 07:30
  • 1
    It seems this is underrated answer. First time using it on my part, and its beautifully does exactly what I wanted. – v01d Oct 12 '21 at 03:46
  • 1
    While it does what OP wants, it's not an actual merge, is it? If you use `git log --graph` you'll see just another commit rather than a merging of branches. Just a heads-up. – vesperto Nov 02 '22 at 10:55
45

The easiest way is to do git merge <other_branch then git mergetool to graphically resolve the conflicts. See #10935226 for how to set up mergetool.

The hitch is, your changed file may fast-forward merge with with the older one. Then you have to get a bit more clever.

Novelocrat gives a great way to dig a bit deeper, but you will often have to change the initial command to git merge --no-commit --no-ff <other_branch> because --no-commit really means "Don't commit the merge...unless it's a fast-forward merge." It's a bit of a stinger for lots of folks trying to do exactly what you want.

Sometimes the least confusing way is not very stylish: check out the other branch in a different working copy, use your favorite merge tool to get the version you want in the directory you want, then commit it.

Piers Myers
  • 10,611
  • 6
  • 46
  • 61
Brad O
  • 637
  • 6
  • 3
  • 19
    10935226 is this question's number. What did you mean to write there? – Mathieu K. Jan 25 '18 at 20:05
  • The underlying problem (for me) is that even if there are no conflicts, often git will make wrong merge decisions, discarding stuff i wanted. So an interactive merge is useful. – vesperto Nov 02 '22 at 10:57
40

As per this gist, where temp could be an existing branch.

https://gist.github.com/katylava/564416


On master:

git checkout -b temp

On temp:

git merge --no-commit --no-ff refactor

… which stages everything, so:

git reset HEAD

Then begin adding the pieces you want:

git add --interactive
sam
  • 40,318
  • 2
  • 41
  • 37
Hugh
  • 1,431
  • 2
  • 16
  • 30
  • 1
    This worked, but I had to leave off 'head' in 'git reset head', but git reset worked fine. – Micah Oct 28 '14 at 03:59
  • A final `git commit` does not do a merge! – How do I finish the merge in the end? – Robert Siemer Apr 07 '15 at 13:35
  • @RobertSiemer Maybe I guess you can do normal merge in the next steps: git co master; git merge temp – Victor Choy Apr 16 '16 at 11:14
  • The nice part is the `patch` option from `git add --interactive`, which lets you stage parts of a diff, as described in the [git book](https://git-scm.com/book/en/v2/Git-Tools-Interactive-Staging). – djvg Apr 17 '18 at 20:29
0

You could simply use WinMerge, DiffMerge, or any available diff/merge UI tool to do the work manually. If you want to hook it into "git difftool", you can search online to find ways to make those tools work with git.

Pang
  • 9,564
  • 146
  • 81
  • 122
John Fisher
  • 22,355
  • 2
  • 39
  • 64
0

The best way I have found to do this is:

  1. Checkout the branch with your changes
  2. Create a new branch from that point
  3. Reset your new branch to the commit you want to compare to and build upon. The reset will be a "mixed" reset by default, meaning that it will not change the "working tree", i.e. the actual code files
  4. At this point, my text editor (VSCode) shows me what is different between my current files and the commit I reset to. I can edit the code to select which lines I want to commit. What this does is allow me to see everything my branch changed and confirm every line of code I will commit. This is useful such as before merging my changes back into production.
Dustin
  • 71
  • 2
  • 5