0

I am concerned that git checkout other_branch -- file will overwrite the file in my current branch and not do a merge at all.

Say I am on a branch called foo. I want to merge a file in foo called bar.js, with the same filepath on a branch called baz;

so git branch -v looks like:

* foo        adbe2d4 pdev:set
  baz        ae598f5 [ahead 5] sync-msg

The way I know how to do this is to do:

git checkout baz -- bar.js

what I am confused about, is, doesn't this overwrite the file on my branch with the same name?

How can I merge bar.js from baz instead of checking out the file and overwriting the work on the foo branch? Or am I mistaken and a merge does indeed happen?

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 1
    You seem to be confused about how Git works. In Git, each commit represents a snapshot of _every_ file, not individual ones. There is no real concept in Git of merging a single file. With regard to your question, yes checking out the file from another branch would overwrite the version in your working directory. – Tim Biegeleisen Dec 14 '16 at 05:39
  • I think this is what you are looking for. http://stackoverflow.com/questions/449541/how-do-you-merge-selective-files-with-git-merge – Tyagi Akhilesh Dec 14 '16 at 05:46

1 Answers1

1

You are correct, this form of checkout:

git checkout <tree-ish> -- <path> [ <path> ... ]

simply overwrites the work-tree (and index!) version of each path.

Normally you don't want to merge just one file, but rather some other branch's tip commit with your current commit (resulting in a new merge commit, if successful).

But what if you really want to merge just one file?

To do a full three-way merge of just one file you must choose a merge base between your current commit and the commit from which you want to merge the one file. This is a bit tricky, and usually unnecessary anyway (see the simpler checkout -p method below).

If you want to do it, though, you can automate it through git diff --full-index, piped to git apply -3. The merge base is your choice: you may want the merge base of your current branch and the tip of the other branch, or you may simply want the parent of the commit you are, in effect, then cherry-picking. Depending on the change you are obtaining, these may end up choosing the same merge-base file anyway:

git diff --full-index ...other -- path/to/file

(note the three dots here! this has the same meaning as HEAD...other and has special meaning to git diff) or:

git diff --full-index other~1 other -- path/to/file

These will both produce a diff of the one specified file. The index line in the diff will provide the merge base, so now you can add the git apply -3 step:

git diff --full-index ...other -- path/to/file | git apply -3

Your file is now three-way-merged if possible, or left with conflict markers if not (in which case, resolve as usual).

If you don't need a full 3-way merge

If you don't need a full three-way merge, git checkout -p other -- path/to/file is simpler. This effectively diffs your current version of path/to/file with the one from other, then lets you apply each of those changes one "patch" at a time, a la git add -p. Note that this is substantively different from merging. If you take all of these changes, it's just git diff other HEAD -- path/to/file | git apply, which winds up doing the same thing as git checkout other -- path/to/file, which is what you said you don't want, so you must carefully exclude those changes that you don't want.

torek
  • 448,244
  • 59
  • 642
  • 775
  • thanks for this answer @torek, the git wizard..could you possible explain what the following command really does? – Alexander Mills Dec 15 '16 at 10:15
  • git checkout other_branch -- . # check out all files from other branch (?) – Alexander Mills Dec 15 '16 at 10:16
  • 1
    @AlexanderMills: The form of `git checkout` with some path name(s), `git checkout otherbranch -- .`, tells Git: go look at the commit identified by `otherbranch`. For each file in that commit, extract that file into the index/staging-area, then copy it from the index to the work-tree. So, suppose your current index and work-tree has files `A.txt` and `B.txt`, and commit `otherbranch` has `B.txt` and `C.txt`. `checkout` will overwrite the staged and work-tree `B.txt`, and stage and create `C.txt`, leaving `A.txt` alone. – torek Dec 15 '16 at 10:38
  • The `.` at the end is what makes it get *every* file in the commit. If you said `git checkout otherbranch -- B.txt` it would replace only `B.txt`, not extracting `C.txt` at all. `.` means "the current directory" (note: I'm assuming you're at the top level of your repository!). – torek Dec 15 '16 at 10:40
  • Thanks, yeah that makes sense, yep at the root of git repo – Alexander Mills Dec 15 '16 at 20:46