2

With git, how can I perform a manual merge of specific files? When I say "manual merge" I mean so that git inserts the <<<<HEAD, and =====, etc. lines and requires me to make a selection (please enlighten me if the term for this is not "manual merge").

I read this Q&A so I'm aware that git checkout <branch> <file> <file> ... lets me pull files from my desired source branch; but is there a way to also manually resolve all diffs?

Attempting to combine information from multiple sources, I tried git merge --no-commit source_branch ../file.c, but git's response was merge: ../file.c - not something we can merge.

Community
  • 1
  • 1
StoneThrow
  • 5,314
  • 4
  • 44
  • 86

2 Answers2

1

The git merge command deals in commits (period, full stop, end of options ... well, there are some options, but they still apply to the commit-wide merge). It:

  • starts with the current (HEAD) commit;
  • takes something to specify the other commit, usually a branch name;
  • automatically finds the merge-base commit using git merge-base --all HEAD other-commit;
  • and (assuming a single merge base) merges all the files resulting from two diffs: diff -M base HEAD, and diff -M base other.

So unless one or both of those two commit-wide git diff commands only pick out one changed file, you cannot use git merge to do this.

You can use git checkout -p to do an interactive patch to one file, as compared to a second file. This is not a merge, although some people refer to this as a "two-way merge" operation.

So, the probable solution

You can also use git checkout or git show to extract specific versions of a single file from particular commits. If you select three such versions—a merge base, the current commit, and some other commit, for instance—you can then instruct Git to perform a three-way merge on that one file, using the git merge-file command. For instance:

git show 1234567:foo.txt > /tmp/foo.txt.base
cp foo.txt /tmp/foo.txt.merge
git show 8888888:foo.txt > /tmp/foo.txt.other

(cd /tmp; git merge-file foo.txt.merge foo.txt.base foo.txt.other)

You now have a merged file in /tmp/foo.txt.merge, complete with any conflict markers. Note that this file is independent of a Git repository, but if you like it, you can replace any of the files in your work-tree (such as foo.txt) with it. Or, of course, you can even just do the merge-file operation directly in your work-tree.

If you replace the first hard-coded commit hash with the other branch name, and the middle (merge base) commit hash with the hash produced by git merge-base --all HEAD otherbranch, you probably get the kind of thing you want.

A possibly-easier alternative

You can also run git merge -n to run a full merge (of all files, automatically finding the merge base commit) but avoid committing the resulting merge even if it all seems to Git to have worked. You can then finish only the part of the merge you care about, save the result, run git merge --abort to terminate the in-progress merge, and copy the saved result back.

torek
  • 448,244
  • 59
  • 642
  • 775
0

I've pulled off something close to what I wanted:

>git checkout source_branch ../file.c
>git reset HEAD ../file.c
Unstaged changes after reset:
M       utils/file.c
>git add -p ../file.c

The above brings me to the interactive hunk editor.

I guess this is sort of, kind of okay, but is there a better answer out there? I.e. one that inserts the <<<<HEAD, and =====, etc. lines and requires my manual resolution on a per-file basis instead of on a per-hunk basis which my workaround requires?

StoneThrow
  • 5,314
  • 4
  • 44
  • 86