291

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?

ruffin
  • 16,507
  • 9
  • 88
  • 138
rwolst
  • 12,904
  • 16
  • 54
  • 75
  • Possible duplicate of [How do you merge selective files with git-merge?](http://stackoverflow.com/questions/449541/how-do-you-merge-selective-files-with-git-merge) – phuclv Mar 08 '17 at 02:49
  • Possible duplicate of [How do I merge changes to a single file, rather than merging commits?](https://stackoverflow.com/questions/10784523/how-do-i-merge-changes-to-a-single-file-rather-than-merging-commits) – fbmd Jan 31 '18 at 14:30

15 Answers15

306

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.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
pdp
  • 4,117
  • 1
  • 17
  • 20
  • 6
    How can we use patch and at the same time use a merge tool? instead of the key stuff – Gabriel Jan 26 '16 at 19:29
  • @Gabriel I have used Git even with patch files and tarballs because it is so easy to create a repo (`git init `) and finally throw it away (`rm -r `). – pdp Jan 27 '16 at 14:41
  • 1
    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. – Btc Sources Nov 11 '20 at 16:36
  • Is there a way to do this non-interactively? – DarthCucumber Oct 08 '21 at 04:20
  • 5
    This is not a merge, history is not retained – Evan Apr 06 '22 at 20:14
  • This works for folders too! – zkytony Jul 02 '22 at 00:43
  • 2
    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**. – cueedee Jan 21 '23 at 10:21
164

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.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Matthew Turner
  • 3,564
  • 2
  • 20
  • 21
  • 147
    Easy, but that's not actually a *merge*. It just overwrites `file.py` with whatever is in branch 2. – Greg Hewgill Aug 08 '13 at 01:26
  • What if you merge back the file from branch1 to branch2? You'll get conflict! – Amir Apr 10 '17 at 09:26
  • 4
    Does this retain commit history? – Beez May 12 '20 at 16:04
  • 2
    @Beez no, see R.M's answer – Evan Apr 06 '22 at 20:15
  • @Evan While [R.M.'s answer](https://stackoverflow.com/a/36510671/1870254) - 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](https://stackoverflow.com/a/18116787/1870254) 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`) – jan-glx Mar 03 '23 at 07:59
51

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
R.M.
  • 3,461
  • 1
  • 21
  • 41
  • 1
    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` – DMJ Sep 28 '21 at 23:00
  • Is there no way to do this with a merge tool? Seems like git-show is doing what mergetools usually do automatically. – jacob Nov 14 '22 at 16:35
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
10

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.

Makogan
  • 8,208
  • 7
  • 44
  • 112
  • 2
    `git branch -D dummy` to clean up the dummy branch afterward – David Fridley Jul 27 '22 at 19:04
  • 1
    Dumb question: Why wouldn't `git checkout b2 ` by itself reduce to the same thing? – ruffin Oct 26 '22 at 13:57
  • 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. – Makogan Oct 26 '22 at 19:38
  • 2
    Why wouldn't `` in `git checkout b2 ` accomplish that just as effectively as `git checkout dummy ` here? ([`pathspec` in git docs](https://git-scm.com/docs/git-checkout#Documentation/git-checkout.txt-ltpathspecgt82308203) -- can't tell if that's how it's intended to work or not.) (Btw, [you have to `@` me](https://stackoverflow.blog/2010/01/16/new-improved-comments-with-reply/) 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 Oct 27 '22 at 12:58
  • @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. – Makogan Oct 27 '22 at 21:47
  • @ruffin does this make sense? – Makogan Oct 27 '22 at 21:49
  • @Makogan Between that [and reading through this question](https://stackoverflow.com/q/449541/1028230) I'm getting close. I need to set up a dummy repo to walk it through for it to really sink in, I think. Thanks for the info! – ruffin Oct 31 '22 at 13:34
  • Effectively, this is a slightly more convoluted way to do the `git checkout --patch branch2 file.py` of [the currently accepted answer](/a/33168094/5110545), admittedly without the interactive component (assuming no merge conflicts). And so, one is still left with a disconnected change on `file.py` in **branch1**. The historic record of changes it went through in **branch2** is lost. – cueedee Jan 21 '23 at 11:24
8

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 -
bananaspy
  • 551
  • 2
  • 10
xmnt
  • 81
  • 1
  • 4
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
Martin G
  • 17,357
  • 9
  • 82
  • 98
  • 8
    This overwrites branch1/file.py with the content of branch2/file.py instead of a merge that should raise a merge conflict to resolve. – maininformer Nov 26 '19 at 22:09
  • 1
    Doesn't "merge" the file, just replaces it... The last three statements change the outcome by literally nothing... – agent18 Aug 31 '21 at 14:30
4

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"
jthill
  • 55,082
  • 5
  • 77
  • 137
  • 1
    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. – cueedee Jan 21 '23 at 17:52
  • 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 Jan 21 '23 at 21:12
  • 2
    @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. – jthill Jan 21 '23 at 22:02
  • would it be possible to get rid of all the commits in `wip` that did not touch `file.py` before the commit/merge? – jan-glx Mar 03 '23 at 08:04
3

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

Claire. D
  • 31
  • 1
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
Jackkobec
  • 5,889
  • 34
  • 34
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.

Mohamed Laradji
  • 144
  • 3
  • 6
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.

techniao
  • 151
  • 1
  • 9
1

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.

Thumbs up, if it helps. Happy coding :)

Imran Zahoor
  • 2,521
  • 1
  • 28
  • 38
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
Trac Nguyen
  • 486
  • 4
  • 8
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.

Lucas Lima
  • 832
  • 11
  • 23