337

I have 2 git branches:

  1. branch1
  2. branch2

I want to merge all the history (multiple commits) of file.py in branch2 into file.py in branch1 and only that file.

In essence I just want to work on the file.py in branch1 but want to take advantage of the merge command.

What is the best way to do this?

2

16 Answers 16

335

When content is in file.py from branch2 that is no longer applies to branch1, it requires picking some changes and leaving others. For full control do an interactive merge using the --patch switch:

$ git checkout --patch branch2 file.py

The interactive mode section in the man page for git-add(1) explains the keys that are to be used:

y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk nor any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk nor any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help

The split command is particularly useful.

Sign up to request clarification or add additional context in comments.

7 Comments

How can we use patch and at the same time use a merge tool? instead of the key stuff
@Gabriel I have used Git even with patch files and tarballs because it is so easy to create a repo (git init <dir>) and finally throw it away (rm -r <dir>).
A link on how to work when applying patch would be useful as well, talking about + and - starting lines, and how you should perform your change selection.
This is not a merge, history is not retained
I fail to see how this answers the question; Afaict, this method only carries selective changes to file.py over from branch2 to branch1 in one, stand-alone commit; it does nothing to preserve the commit history of file.py in branch2 when seen from branch1.
|
183

Although not a merge per se, sometimes the entire contents of another file on another branch are needed. Jason Rudolph's blog post provides a simple way to copy files from one branch to another. Apply the technique as follows:

$ git checkout branch1 # ensure in branch1 is checked out and active
$ git checkout branch2 file.py

Now file.py is now in branch1.

5 Comments

Easy, but that's not actually a merge. It just overwrites file.py with whatever is in branch 2.
What if you merge back the file from branch1 to branch2? You'll get conflict!
Does this retain commit history?
@Beez no, see R.M's answer
@Evan While R.M.'s answer - in contrast to this and most other answers - allows to keep changes made to file.py in branch1 as well ('will actually "merge"'), it does not preserve the history of the changes made on branch2. jthill's answer is the only one here so far that actually preserves the history of changes in both branches (but it does so by include the complete history of all (unrelated and irrelevant) changes in branch2)
59

None of the other current answers will actually "merge" the files, as if you were using the merge command. (At best they'll require you to manually pick diffs.) If you actually want to take advantage of merging using the information from a common ancestor, you can follow a procedure based on one found in the "Advanced Merging" section of the git Reference Manual.

For this protocol, I'm assuming you're wanting to merge the file 'path/to/file.txt' from origin/master into HEAD - modify as appropriate. (You don't have to be in the top directory of your repository, but it helps.)

# Find the merge base SHA1 (the common ancestor) for the two commits:
git merge-base HEAD origin/master

# Get the contents of the files at each stage
git show <merge-base SHA1>:path/to/file.txt > ./file.common.txt
git show HEAD:path/to/file.txt > ./file.ours.txt
git show origin/master:path/to/file.txt > ./file.theirs.txt

# You can pre-edit any of the files (e.g. run a formatter on it), if you want.

# Merge the files
git merge-file -p ./file.ours.txt ./file.common.txt ./file.theirs.txt > ./file.merged.txt

# Resolve merge conflicts in ./file.merged.txt
# Copy the merged version to the destination
# Clean up the intermediate files

git merge-file should use all of your default merge settings for formatting and the like.

Also note that if your "ours" is the working copy version and you don't want to be overly cautious, you can operate directly on the file:

git merge-base HEAD origin/master
git show <merge-base SHA1>:path/to/file.txt > ./file.common.txt
git show origin/master:path/to/file.txt > ./file.theirs.txt
git merge-file path/to/file.txt ./file.common.txt ./file.theirs.txt

2 Comments

This is indeed the only option that actually merges the changes. I used it in a bash loop to do this for several files: for file in {file1,file2,etc}; do git show $(git merge-base HEAD dev-mysql-to-pdo):$file > common.tmp; git show HEAD:$file > current.tmp; git show dev-mysql-to-pdo:$file > other.tmp; git merge-file -p current.tmp common.tmp other.tmp > $file; rm current.tmp other.tmp common.tmp; done
Is there no way to do this with a merge tool? Seems like git-show is doing what mergetools usually do automatically.
21

Are all the modifications to file.py in branch2 in their own commits, separate from modifications to other files? If so, you can simply cherry-pick the changes over:

git checkout branch1
git cherry-pick <commit-with-changes-to-file.py>

Otherwise, merge does not operate over individual paths...you might as well just create a git diff patch of file.py changes from branch2 and git apply them to branch1:

git checkout branch2
git diff <base-commit-before-changes-to-file.py> -- file.py > my.patch
git checkout branch1
git apply my.patch

Comments

16

Git checkout provides a --merge option for this

git checkout --merge branch2 file.py

With this option a conflicted merge gets recreated.

Otherwise when a new merge should happen instead:

# Detach and overwrite file.py with content from branch2 
git checkout --detach
git checkout branch2 file.py

# Amend changes and switch back
git commit --amend --no-edit
git checkout -

# Merge the detached branch back in
git merge --no-commit -

2 Comments

git checkout --merge branch2 file.py says : error: pathspec 'branch2' did not match any file(s) known to git
Of course "branch2" is the branch you want to merge from, e.g. origin/main.
9

The solution I found that caused me the least headaches:

git checkout <b1>
git checkout -b dummy
git merge <b2>
git checkout <b1>
git checkout dummy <path to file>
git branch -D dummy

After doing that the file in path to file in b2 is what it would be after a full merge with b1.

7 Comments

Dumb question: Why wouldn't git checkout b2 <path to file> by itself reduce to the same thing?
You don;t want to merge to b2 because you only want to alter one file in the branch, not every file in the branch.
Why wouldn't <path to file> in git checkout b2 <path to file> accomplish that just as effectively as git checkout dummy <path to file> here? (pathspec in git docs -- can't tell if that's how it's intended to work or not.) (Btw, you have to @ me for me to get a notification you've replied to me directly. AIA if (likely) you already knew and were in a hurry; thanks for your follow-up.)
@ruffin We start with 2 branches b1, b2. We want to merge a single file between them but there are say 100 files that are different between the 2. If we did either merge from b1 or b2, we are merging every single file. So instead we make a dummy branch that is a perfect copy of b1. merge with b2 (and now all 100 files are merged). Chekcout back to b1, then checkout a single file in dummy. You can't just checkout the file in b2, because that file has not been merged with b1 it has changes that are not present in the current branch that we want to merge for that file only.
@ruffin does this make sense?
|
7

You can stash and stash pop the file:

git checkout branch1
git checkout branch2 file.py
git stash
git checkout branch1
git stash pop

3 Comments

This overwrites branch1/file.py with the content of branch2/file.py instead of a merge that should raise a merge conflict to resolve.
Doesn't "merge" the file, just replaces it... The last three statements change the outcome by literally nothing...
I like this - it's simple and easy to understand what's happening!
5

To merge only the changes from branch2's file.py, make the other changes go away.

git checkout -B wip branch2
git read-tree branch1
git checkout branch2 file.py
git commit -m'merging only file.py history from branch2 into branch1'
git checkout branch1
git merge wip

Merge will never even look at any other file. You might need to '-f' the checkouts if the trees are different enough.

Note that this will leave branch1 looking as if everything in branch2's history to that point has been merged, which may not be what you want. A better version of the first checkout above is probably

git checkout -B wip `git merge-base branch1 branch2`

in which case the commit message should probably also be

git commit -m"merging only $(git rev-parse branch2):file.py into branch1"

4 Comments

This is the first answer I find here that actually preserves the historic record of changes that file.py went through in branch2 after merging it into branch1 like the OP requested. I particularly like how this solution explicitly leaves behind all other, unwanted files from branch2 in a separate commit before the actual merge for extra clarity. The latter fact is also apparent in the (large) number of untracked files littering the working directory after git read-tree branch1 - which you may need to git clean -fd at some point.
The net end result of your second scenario will be the same I'm sure, but will a git log branch1 -- file.py report the same history of changes in that case? I think not.
@cueedee that's right, if you want an explicit per-file history you have to build it. You could do it, create a parallel branch with just that file's branch2 history and rebuild branch2 as merges from the file.py branch, and really, that's what per-file-history vcs's have to do under the hood for every file.
would it be possible to get rid of all the commits in wip that did not touch file.py before the commit/merge?
4

Matthew Turner's solution is the easiest but gives an error if branch1 and file have the same name. In that case, replace the second line with

git checkout branch2 -- file.py

Comments

3

I find some answers helpful but confusing, so to avoid any confusion for future. I'm trying to help anyone out there with same confusion.

I won't use names of branch1 and branch2, but master (live code) and hotfix/abc (extracted from master) and a testing branch.

Now, I want to merge some specific files from testing to hotfix/abc, because merging directly on master from testing or staging branches isn't recommended. To do that I'll do following:

  1. git checkout hotfix/abc
  2. git checkout --merge testing path/to/file1.php path/to/file2.js
  3. git add .
  4. git commit -m "Fixed specific issue"
  5. git push
  6. Now go to repo and make a pull request for hotfix/abc to master branch. If you don't know how to do that here I've a small tutorial on that. And if you generally want to learn how these branches and git work, I'd recommend you to watch this <= twenty minutes playlist.
  7. Now review your pull request with master and merge it. In case you see any conflicts, it's time to merge master into hotfix/abc. And resolve conflicts over there. And then again repeat step 3-5 followed by 7.

I also got help from a refernce tutorial.

Comments

2

The simplest solution is:

git checkout the name of the source branch and the paths to the specific files that we want to add to our current branch

git checkout sourceBranchName pathToFile

1 Comment

this is just a worse version of this answer
1

If you only care about the conflict resolution and not about keeping the commit history, the following method should work. Say you want to merge a.py b.py from BRANCHA into BRANCHB. First, make sure any changes in BRANCHB are either committed or stashed away, and that there are no untracked files. Then:

git checkout BRANCHB
git merge BRANCHA
# 'Accept' all changes
git add .
# Clear staging area
git reset HEAD -- .
# Stash only the files you want to keep
git stash push a.py b.py
# Remove all other changes
git add .
git reset --hard
# Now, pull the changes
git stash pop

git won't recognize that there are conflicts in a.py b.py, but the merge conflict markers are there if there were in fact conflicts. Using a third-party merge tool, such as VSCode, one will be able to resolve conflicts more comfortably.

Comments

1

If git checkout --patch branch2 file.py is going to be accepted, then I should share that we can also use:

git difftool <branch> [-- <file>]

([] means optional.)

If configured for diff.tool, merge tools like meld will allow you to manually merge two files using a graphical interface.

One weakness is that it won't be able to copy or remove a file if it doesn't exist in one of the branches. In that case, we need to git checkout branch2 -- file.py.

git difftool doesn't preserve history either.

Comments

1

Git migrate a specific file from one branch to another:

git checkout --merge SourceBranch “path/to/file/fileToMigrate.java”

Example exercise:

Create exercise branches:

git branch SourceBranch
git branch DestinationBranch

checkout the SourceBranch:

git switch SourceBranch

Create 2 files. One to migrate, another not to migrate:

  • create files “a.java” and “b.java”

checkout the destination branch and migrate the intended file from the source branch:

git switch DestinationBranch
git checkout --merge SourceBranch “path/to/file/a.java”

At this point “a.java” is merged to DestinationBranch, “b.java” is not merged (as intended).

While on DestinationBranch continue as usual such as:

  • if the newly merged file “a.java” is not staged then: git add .
  • if it is staged then: git commit -m “Merged: a.java”

1 Comment

Could you describe why is it different than xmnt's answer
0

I am in same situation, I want to merge a file from a branch which has many commits on it on 2 branch. I tried many ways above and other I found on the internet and all failed (because commit history is complex) so I decide to do my way (the crazy way).

git merge <other-branch>
cp file-to-merge file-to-merge.example
git reset --hard HEAD (or HEAD^1 if no conflicts happen)
cp file-to-merge.example file-to-merge

1 Comment

This does the same as git checkout --merge <other-branch> file-to-merge.
0

What I've done is a bit manual, but I:

  1. Merged the branches normally; Reverted the merge with revert;
  2. Checked out all my files to HEAD~1, that is, their state in the merge commit;
  3. Rebased my commits to hide this hackery from the commit history.

Ugly? Yes. Easy to remember? Also yes.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.