How can I selectively merge or pick changes from another branch in Git?
Asked Answered
C

29

1810

I'm using Git on a new project that has two parallel -- but currently experimental -- development branches:

  • master: import of existing codebase plus a few modifications that I'm generally sure of
  • exp1: experimental branch #1
  • exp2: experimental branch #2

exp1 and exp2 represent two very different architectural approaches. Until I get further along I have no way of knowing which one (if either) will work. As I make progress in one branch I sometimes have edits that would be useful in the other branch and would like to merge just those.

What is the best way to merge selective changes from one development branch to another while leaving behind everything else?

Approaches I've considered:

  1. git merge --no-commit followed by manual unstaging of a large number of edits that I don't want to make common between the branches.

  2. Manual copying of common files into a temporary directory followed by git checkout to move to the other branch and then more manual copying out of the temporary directory into the working tree.

  3. A variation on the above. Abandon the exp branches for now and use two additional local repositories for experimentation. This makes the manual copying of files much more straightforward.

All three of these approaches seem tedious and error-prone. I'm hoping there is a better approach; something akin to a filter path parameter that would make git-merge more selective.

Cissie answered 16/1, 2009 at 4:55 Comment(4)
If changes in your experimental branches are well organized in separate commits, it's better to think in terms of merging selective commits instead of selective files. Most of the answers below assume this is the case.Meddle
Wouldn't a combination of git merge -s ours --no-commitfollowed by some git read-tree be a good solution for this? See #1215406Seigler
A more recent question has a one-line, well-written answer: #10785023Sestina
Checkout this blog for merging specific files only jasonrudolph.com/blog/2009/02/25/…Cabernet
M
573

You use the cherry-pick command to get individual commits from one branch.

If the change(s) you want are not in individual commits, then use the method shown here to split the commit into individual commits. Roughly speaking, you use git rebase -i to get the original commit to edit, then git reset HEAD^ to selectively revert changes, then git commit to commit that bit as a new commit in the history.

There is another nice method here in Red Hat Magazine, where they use git add --patch or possibly git add --interactive which allows you to add just parts of a hunk, if you want to split different changes to an individual file (search in that page for "split").

Having split the changes, you can now cherry-pick just the ones you want.

Macswan answered 16/1, 2009 at 6:1 Comment(9)
From my understanding, this is needlessly more convoluted than the higher-voted answer.Headsman
This is technically the correct answer, the correct answer does appear "convoluted". --- The higher voted answer is just a quick and dirty "does the trick" answer, which for most people is all they are about (:Advisable
@akaihola: HEAD^ is correct. See man git-rev-parse: A suffix ^ to a revision parameter means the first parent of that commit object. The prefix ^ notation is used to exclude commits reachable from a commit.Froufrou
To split the commit into individual commits you have to be on the branch where the commit is located. Else you will fail in the 'rebase -i' step, because you cannot find the commit. Happened to me, hope it does not happen to others.Chemosynthesis
@Torek - Yet another awful git solution to sync'ing a dev-branch.... I'll use either (1) manual backup; or (2) separate clone to restore things to where I want them after the complete merge.Sanjiv
Confused by the debate over which approach is 'correct'? Consider the difference between files & commits (see Remarks at bottom). OP wants to merge FILES & does not mention COMMITS. The higher-voted answer is specific to files; the accepted answer uses cherry-pick, which is specific to commits. Cherry-pick may be key for merging commits selectively, but it can be very painful for moving files from one branch to another. Though commits are the heart of git's strength, don't forget files still have a role!Lalo
Can't git use logic to distinguish what you have staged from out of date files in your working branch? The out of date files should not be merged, the newer file from the default branch should simply be copied in to the working branch IMO.Carty
Upvoted this, downvoted the top. The question specifically, and unfortunately states "merge or pick" so the answer is not wrong at all because the "or pick" part. BUT its not a merge at all and that sucks, no history, basically nothing git is actually made for, just a blatant sledgehammer method and everyone it all over it.Thorman
And to claim OP wants to "merge files" and arguing that for that reason the top answer is correct is actually wrong. There is no merge in the top answer. The file may be updated, copied / whatever. If there is a message to that commit that sates it from branch x its still just a commit message you have to trust without any data attached, its NOT a merge!Thorman
M
1186

tl;dr

git checkout source_branch -- path/to/file
# resolve conflicts if any
git commit -am '...'

I had the exact same problem as mentioned by you above. But I found this clearer in explaining the answer.

Summary:

  • Check out the path(s) from the branch you want to merge,

     $ git checkout source_branch -- <paths>...
    
    Hint: It also works without `--` like seen in the linked post.
    
  • or to selectively merge hunks

     $ git checkout -p source_branch -- <paths>...
    

Alternatively, use reset and then add with the option -p,

    $ git reset <paths>...
    $ git add -p <paths>...
  • Finally commit

     $ git commit -m "'Merge' these changes"
    
Manysided answered 16/1, 2009 at 4:55 Comment(16)
This isn't a real merge. You're picking changes by file instead of by commit, and you'll lose any existing commit information (author, message). Granted, this is good if you want to merge all changes in some files and it's ok that you must re-do all commits. But if files contain both changes to merge and others to discard, one of the methods provided in other answers will serve you better.Meddle
seems to work, unfortunately, i don't see the changes with git diffAusterity
this is great if you've created changes that shouldn't be a part of the topical branch your working on, but of some other.Console
@Austerity and others: this stages the files in the index automatically, so you if you checked out foo.c do git reset HEAD foo.c to unstage that file and you can then diff it. I found this out after trying it and coming back here to look for an answer to thisWinze
to see the changes you could also use: git diff --cachedWalburga
If you're going to use diff to manually merge the files, you could just do the diff in the first place and manually make the changes in your local branch rather than checking out first, then diffing, then manually making the changes you already had.Depute
@spacemanaki has what I think is the best answer here. git checkout feature-branch foo.c followed by git reset HEAD foo.c then followed by git add -p to pull in the differences you want or exclude bad ones, then git commit foo.c to save your changes, and git checkout foo.c to reset the index. Worked like a charm.Facilitate
Taking @Zach 's advice worked for me. To expand on it, if you use a difftool, you can use git difftool origin develop src/main/path/to/file.ext and bring over the lines of code you need. This works particularly well if you just want to grab a few lines.Zabaglione
Am I the only one who doesn't see an answer here? All I see is "I had the exact same problem as mentioned by you above. But I found this clearer in explaining the answer."Arenas
As far as I understand, in case of conflict this 'merge' just replaces the conflicting lines in current branch with lines from source_branch, which is not a merge, it is a replace.Adanadana
According to this answer git checkout -p <revision> -- <path> will be the same as issuing the first three commands you described :)Unimpeachable
This is NOT merging, it's substituting. A closer action to an actual merge by file could be git diff branch1 branch2 filepath > 1.patch and then git apply 1.patch. Of course it is only going bring pure file changes and nothing else.Taillight
The linked post uses $ git checkout source_branch <paths>... not $ git checkout source_branch -- <paths>.... What is right?!Declaim
If it doesn't refer to this answer, then "Bart J" is now "Cory" and the link is sourcemage.org/Git_Guide (as "1800 INFORMATION" did not change name (it was "1800 INFORMATION" on 2009-05-07 - before Pistos' comment), and nosatalian's answer doesn't have any links).Fortunna
As mentioned by others, it's really not a merge, and it won't be obvious at all from the graph that it's merge, even a partial one.Ardie
"# resolve conflicts if any" is quite a ridiculous misleading comment, as you do NOT MERGE anything. You just blatantly sledgehammer overwrite the file. Git will never ask you about any conflicts, it will just overwrite the file. This is a checkout, not a merge.Thorman
M
573

You use the cherry-pick command to get individual commits from one branch.

If the change(s) you want are not in individual commits, then use the method shown here to split the commit into individual commits. Roughly speaking, you use git rebase -i to get the original commit to edit, then git reset HEAD^ to selectively revert changes, then git commit to commit that bit as a new commit in the history.

There is another nice method here in Red Hat Magazine, where they use git add --patch or possibly git add --interactive which allows you to add just parts of a hunk, if you want to split different changes to an individual file (search in that page for "split").

Having split the changes, you can now cherry-pick just the ones you want.

Macswan answered 16/1, 2009 at 6:1 Comment(9)
From my understanding, this is needlessly more convoluted than the higher-voted answer.Headsman
This is technically the correct answer, the correct answer does appear "convoluted". --- The higher voted answer is just a quick and dirty "does the trick" answer, which for most people is all they are about (:Advisable
@akaihola: HEAD^ is correct. See man git-rev-parse: A suffix ^ to a revision parameter means the first parent of that commit object. The prefix ^ notation is used to exclude commits reachable from a commit.Froufrou
To split the commit into individual commits you have to be on the branch where the commit is located. Else you will fail in the 'rebase -i' step, because you cannot find the commit. Happened to me, hope it does not happen to others.Chemosynthesis
@Torek - Yet another awful git solution to sync'ing a dev-branch.... I'll use either (1) manual backup; or (2) separate clone to restore things to where I want them after the complete merge.Sanjiv
Confused by the debate over which approach is 'correct'? Consider the difference between files & commits (see Remarks at bottom). OP wants to merge FILES & does not mention COMMITS. The higher-voted answer is specific to files; the accepted answer uses cherry-pick, which is specific to commits. Cherry-pick may be key for merging commits selectively, but it can be very painful for moving files from one branch to another. Though commits are the heart of git's strength, don't forget files still have a role!Lalo
Can't git use logic to distinguish what you have staged from out of date files in your working branch? The out of date files should not be merged, the newer file from the default branch should simply be copied in to the working branch IMO.Carty
Upvoted this, downvoted the top. The question specifically, and unfortunately states "merge or pick" so the answer is not wrong at all because the "or pick" part. BUT its not a merge at all and that sucks, no history, basically nothing git is actually made for, just a blatant sledgehammer method and everyone it all over it.Thorman
And to claim OP wants to "merge files" and arguing that for that reason the top answer is correct is actually wrong. There is no merge in the top answer. The file may be updated, copied / whatever. If there is a message to that commit that sates it from branch x its still just a commit message you have to trust without any data attached, its NOT a merge!Thorman
C
464

To selectively merge files from one branch into another branch, run

git merge --no-ff --no-commit branchX

where branchX is the branch you want to merge from into the current branch.

The --no-commit option will stage the files that have been merged by Git without actually committing them. This will give you the opportunity to modify the merged files however you want to and then commit them yourself.

Depending on how you want to merge files, there are four cases:

1) You want a true merge.

In this case, you accept the merged files the way Git merged them automatically and then commit them.

2) There are some files you don't want to merge.

For example, you want to retain the version in the current branch and ignore the version in the branch you are merging from.

To select the version in the current branch, run:

git checkout HEAD file1

This will retrieve the version of file1 in the current branch and overwrite the file1 automerged by Git.

3) If you want the version in branchX (and not a true merge).

Run:

git checkout branchX file1

This will retrieve the version of file1 in branchX and overwrite file1 auto-merged by Git.

4) The last case is if you want to select only specific merges in file1.

In this case, you can edit the modified file1 directly, update it to whatever you'd want the version of file1 to become, and then commit.

If Git cannot merge a file automatically, it will report the file as "unmerged" and produce a copy where you will need to resolve the conflicts manually.



To explain further with an example, let's say you want to merge branchX into the current branch:

git merge --no-ff --no-commit branchX

You then run the git status command to view the status of modified files.

For example:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Where file1, file2, and file3 are the files git have successfully auto-merged.

What this means is that changes in the master and branchX for all those three files have been combined together without any conflicts.

You can inspect how the merge was done by running the git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

If you find some merge undesirable then you can

  1. edit the file directly
  2. save
  3. git commit

If you don't want to merge file1 and want to retain the version in the current branch

Run

git checkout HEAD file1

If you don't want to merge file2 and only want the version in branchX

Run

git checkout branchX file2

If you want file3 to be merged automatically, don't do anything.

Git has already merged it at this point.


file4 above is a failed merge by Git. This means there are changes in both branches that occur on the same line. This is where you will need to resolve the conflicts manually. You can discard the merged done by editing the file directly or running the checkout command for the version in the branch you want file4 to become.


Finally, don't forget to git commit.

Condensation answered 3/9, 2011 at 8:48 Comment(10)
Careful though: If the git merge --no-commit branchX is just a fast-forward, the pointer will be updated, and the --no-commit is thus silently ignoredNeuritis
@Neuritis What about adding --no-ff to prevent that behavior?Cutlip
I definitely suggest updating this answer with Eduardo's "--no-ff" option. I read through the whole thing (which was otherwise great) only to have my merge get fast-forwarded.Zacharie
Unlike the answer with the most votes, this solution preserved my merge history, which is important to me as I weave partial commits back and forth across branches. I didn't try all of the other suggested solutions, so perhaps some of them do this as well.Haplite
Good alvinabad; but, sadly, Android Studio says "Partial Commit During a Merge is not allowed"! Used your command, then reviewed the 106 staged to commit files in Android Studio. There I checked the 30 or so I want to put in this branch. Our use case is we have multi-day to week long code review (PR) process step, so I do all the work in a prototype and then spoonfeed changes into the code review. This way I can make progress and not wait on the code reviewers.Artimas
Best solution in company's where coders do regular complete rewrites/rearrangements of modules. This merge method gives maximum control and (p)review.Runion
If you find file1 did not exist in currentBranch and you don't want to commit it as part of the merge, then remove it manually before committing. Trying to do a git checkout HEAD file1 will fail.Cyte
BIG CAUTION. This merge works in one direction, but the files that you did not include in the merge are seen as DELETED if you decide to merge your upstream master back into the source branch. I use a git-flow like process using a master branch (production main-line), a staging branch (staging server main-line), and topic branches based off of the staging branch. Using this strategy has resulted in my "reverse merge" from master back into staging to competely fail thinking everything I didn't merge from staging to master is deleted. This includes whole files and hunks. YOU ARE WARNEDOutofdate
To expand upon the comment by @PabloAdames: Note that file1 has already been staged, so to remove the file from staging I would either git reset file1 before deleting it manually, or git add file1 after deleting it manually.Cilia
I found that running git merge --quit after git merge --no-ff --no-commit <other_branch> allows the changes to be made as regular commits (not as merge)Trinee
S
119

I don't like the above approaches. Using cherry-pick is great for picking a single change, but it is a pain if you want to bring in all the changes except for some bad ones. Here is my approach.

There is no --interactive argument you can pass to git merge.

Here is the alternative:

You have some changes in branch 'feature' and you want to bring some but not all of them over to 'master' in a not sloppy way (i.e. you don't want to cherry pick and commit each one)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

So just wrap that in a shell script, change master into $to and change feature into $from and you are good to go:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp
Supersaturated answered 21/5, 2009 at 3:0 Comment(1)
You might want to change git rebase -i $to to git rebase -i $to || $SHELL, so that the user can call git --skip etc, as necessary if the rebase fails. Also worth chaining the lines together with && instead of newlines.Sikhism
H
116

There is another way to go:

git checkout -p

It is a mix between git checkout and git add -p and might quite be exactly what you are looking for:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.
Harry answered 28/8, 2012 at 18:47 Comment(4)
This is by far the easiest, simplest method, as long as you only have a manageable number of changes to merge in. I hope more people will notice this answer and upvote it. Example: git checkout --patch exp1 file_to_mergeFroufrou
Similar answer posted on this question: https://mcmap.net/q/45877/-how-do-i-merge-changes-to-a-single-file-rather-than-merging-commitsFroufrou
Oh, I didn't know checkout had a patch! I did checkout/reset/add -p instead.Eleni
Truly the most simplest method. git checkout -p featurebranch filename. And the best thing is when the command is run, it gives you a y/n/e/?/...etc. option to decide how to merge the file. I tried with e and I could even edit the patch before applying ... How cool is it. A true one liner for merging selective files from other branches.Smashing
C
69

While some of these answers are pretty good, I feel like none actually answered the OP's original constraint: selecting particular files from particular branches. This solution does that, but it may be tedious if there are many files.

Let’s say you have the master, exp1, and exp2 branches. You want to merge one file from each of the experimental branches into master. I would do something like this:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# Save these files as a stash
git stash

# Merge stash with master
git merge stash

This will give you in-file diffs for each of the files you want. Nothing more. Nothing less. It's useful you have radically different file changes between versions --in my case, changing an application from Ruby on Rails 2 to Ruby on Rails 3.

This will merge files, but it does a smart merge. I wasn't able to figure out how to use this method to get in-file diff information (maybe it still will for extreme differences. Annoying small things like whitespace get merged back in unless you use the -s recursive -X ignore-all-space option)

Cooperage answered 25/8, 2011 at 1:31 Comment(8)
Also note: you can do multiple files from a given branch all inline, eg git checkout exp1 path/to/file_a path/to/file_xCalida
This is beautiful. I did git checkout feature <path>/* to get groups of files.Location
This works ok, but added two extra commit objects. Not a huge deal but a bit messyOak
@Oak you're right. Unfortunately, since I wrote this so long ago, I'm no longer sure why the "git stash" and "git merge stash" steps are in there instead of a "git commit".Cooperage
Oh that's clear, I think. This way it merges the one file, not necessarily overwriting previous changes on the target branch.Oak
@EricHu like you commented, there should be no need to bother with the stash and merge. Just do a commit after the checkout of the files from the other branches. The changes are already staged at that point.Kyat
It doesn't appear to do a merge at all. It simply replaced the HEAD file with the stash file.Denice
This was the easiest answer I could find. The only weird thing are the two commit messages that you get, but the merging itself works perfectly.Beatific
E
57

1800 INFORMATION's answer is completely correct. As someone new to Git, though, "use git cherry-pick" wasn't enough for me to figure this out without a bit more digging on the Internet, so I thought I'd post a more detailed guide in case anyone else is in a similar boat.

My use case was wanting to selectively pull changes from someone else's GitHub branch into my own. If you already have a local branch with the changes, you only need to do steps 2 and 5-7.

  1. Create (if not created) a local branch with the changes you want to bring in.

    $ git branch mybranch <base branch>

  2. Switch into it.

    $ git checkout mybranch

  3. Pull down the changes you want from the other person's account. If you haven't already, you'll want to add them as a remote.

    $ git remote add repos-w-changes <git url>

  4. Pull down everything from their branch.

    $ git pull repos-w-changes branch-i-want

  5. View the commit logs to see which changes you want:

    $ git log

  6. Switch back to the branch you want to pull the changes into.

    $ git checkout originalbranch

  7. Cherry pick your commits, one by one, with the hashes.

    $ git cherry-pick -x hash-of-commit

Hat tip: http://www.sourcemage.org/Git_Guide (archived copy)

Ellisellison answered 7/5, 2009 at 11:38 Comment(5)
Tip: first use the git cherry command (see manual first) to identify commits you haven't yet merged.Meddle
This works .. 1. created a new branch 2.created some files/ made some changes 3. commit 4. checkout the master branch 5. Run git cherry-pick -x hash-of-commit and resolve merge conflicts are you are good to go.Formication
Your link is not working anymore. Can you update it please?Glennglenna
The link is (effectively) broken: "Authentication Required ... https://www.sourcemage.org is requesting your username and password. The site says: 'Restricted Zone'". (It is also redirecting to HTTPS.)Fortunna
thanks, I just removed it as I'm not sure where to find the original at this pointEllisellison
D
46

Here is how you can replace Myclass.java file in master branch with Myclass.java in feature1 branch. It will work even if Myclass.java doesn't exist on master.

git checkout master
git checkout feature1 Myclass.java

Note this will overwrite - not merge - and ignore local changes in the master branch rather.

Discriminant answered 29/7, 2013 at 8:37 Comment(4)
This won't merge. It'll just overwrite the changes on master with the changes from the feature1 branch.Motto
Perfectly, I was looking for this kind of merge where theirs overwrite ours => +1 Cheers ;)Bonaventure
Some times all you want to do is replace the whole file, so this is what I wanted, but you need to make sure you want to lose all the changes you made to this file.Grass
Cleanest solution, given that the OP specifically wanted to replace the entire file with the equivalent on another branch: 2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.Cudlip
W
32

The simple way, to actually merge specific files from two branches, not just replace specific files with ones from another branch.

Step one: Diff the branches

git diff branch_b > my_patch_file.patch

Creates a patch file of the difference between the current branch and branch_b.

Step two: Apply the patch on files matching a pattern

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

Useful notes on the options

You can use * as a wildcard in the include pattern.

Slashes don't need to be escaped.

Also, you could use --exclude instead, and apply it to everything except the files matching the pattern, or reverse the patch with -R.

The -p1 option is a holdover from the *Unix patch command and the fact that the patch file's contents prepend each file name with a/ or b/ (or more, depending on how the patch file was generated), which you need to strip so that it can figure out the real file to the path to the file the patch needs to be applied to.

Check out the man page for git-apply for more options.

Step three: There is no step three

Obviously you'd want to commit your changes, but who's to say you don't have some other related tweaks you want to do before making your commit.

Welbie answered 27/2, 2012 at 22:42 Comment(4)
This was very useful where current_branch had lots of "additional" changes which needed to be preserved. Got the diff of only changes brought in by branch_b as: git diff HEAD...branch_b (yes--three periods does the magic trick).Tapestry
@masukomi, On step 2, shouldn't you add the patch-file created in step 1 as an argument?Hideous
For me, all changes are rejected. Any idea why?Jacobo
initial thought @Jacobo is that maybe you got the branches backwards when making the patch? will followup outside of SO to see if we can figure it out.Welbie
S
25

Here's how you can get history to follow just a couple of files from another branch with a minimum of fuss, even if a more "simple" merge would have brought over a lot more changes that you don't want.

First, you'll take the unusual step of declaring in advance that what you're about to commit is a merge, without Git doing anything at all to the files in your working directory:

git merge --no-ff --no-commit -s ours branchname1

... where "branchname" is whatever you claim to be merging from. If you were to commit right away, it would make no changes, but it would still show ancestry from the other branch. You can add more branches, tags, etc. to the command line if you need to, as well. At this point though, there are no changes to commit, so get the files from the other revisions, next.

git checkout branchname1 -- file1 file2 etc.

If you were merging from more than one other branch, repeat as needed.

git checkout branchname2 -- file3 file4 etc.

Now the files from the other branch are in the index, ready to be committed, with history.

git commit

And you'll have a lot of explaining to do in that commit message.

Please note though, in case it wasn't clear, that this is a messed up thing to do. It is not in the spirit of what a "branch" is for, and cherry-pick is a more honest way to do what you'd be doing, here. If you wanted to do another "merge" for other files on the same branch that you didn't bring over last time, it will stop you with an "already up to date" message. It's a symptom of not branching when we should have, in that the "from" branch should be more than one different branch.

Sigma answered 16/1, 2009 at 4:55 Comment(4)
Your first command (git merge --no-ff --no-commit -s outs branchname1) is exactly what I was looking for! Thanks!Portal
With multiple branches, required history, need to merge single file(s) and have to change the content of the file before pushing, this seems to be a decent alternative. For example dev => master, but you want to change the a host definition or similar before pushing to master.Telephone
This worked perfectly, The only change I made was to do a git merge --continue instead of the final git commit. Thanks!Wendelin
Looking back on this, I think I would rather do the -s ours --no-ff merge and commit it, establishing parentage, but do not push it yet; Then I'd stage some or all of the changes I'd wanted, and commit them with --amend, repeating until satisfied. I'd recommend doing all that on a temporary branch. If it works, go back to the intended branch and fast forward to the temp - deleting the temp or not, as befitting your local customs.Sigma
G
21

The easiest way is to set your repository to the branch you want to merge with, and then run

git checkout [branch with file] [path to file you would like to merge]

If you run

git status

you will see the file already staged...

Then run

git commit -m "Merge changes on '[branch]' to [file]"

Simple.

Gasp answered 29/10, 2013 at 20:54 Comment(3)
This almost the best answer I found. please see jasonrudolph.com/blog/2009/02/25/… So clear, concise and it just works!Cf
that will completely replace the files content from the source branch rather than mergingLyall
I was just about to answer like this, I thought I invent new things that haven't been answered yet! But this is the the most simple way to do it. This should be on top!Nag
R
20

This is my workflow for merging selective files.

# Make a new branch (this will be temporary)
git checkout -b newbranch

# Grab the changes
git merge --no-commit  featurebranch

# Unstage those changes
git reset HEAD
(You can now see the files from the merge are unstaged)

# Now you can chose which files are to be merged.
git add -p

# Remember to "git add" any new files you wish to keep
git commit
Raquel answered 18/2, 2010 at 4:28 Comment(1)
I used a slight variation on this. Instead of merging I cherry-picked. It does the job. The only downside to this approach is you lose the reference to the original commit hash.Stoneblind
K
14

I found this post to contain the simplest answer. Merely do:

git checkout <branch from which you want files> <file paths>

Example

Pulling .gitignore file from branchB into current branch:

git checkout branchB .gitignore

See the post for more information.

Kipkipling answered 4/11, 2013 at 10:51 Comment(2)
This doesn't really merge, it overwrites the file on current branch.Aideaidedecamp
@igrali That's a useful comment, but compared to the difficulty of the "proper" ways to do this, this is a good workaround. One just has to be very careful.Skiba
B
10

I had the exact same problem as mentioned by you above. But I found this Git blog clearer in explaining the answer.

Command from the above link:

# You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
Belongings answered 19/6, 2012 at 10:15 Comment(1)
did you test this? i am sure the files will be replaced from <branch_you_want_to_merge_from> rather than being mergedLyall
J
10

It is not exactly what you were looking for, but it was useful to me:

git checkout -p <branch> -- <paths> ...

It is a mix of some answers.

Joist answered 9/2, 2016 at 1:2 Comment(2)
This is indeed usefull and could be added to what is for me the best answer, the @alvinabad's answer. When doing: git checkout HEAD file1 to keep the current version and unmerge the file file1, one can use -p option to select part of the file to be merged. thanks for the trick!Bandaranaike
What does this do? The comments in your answer fail to explain how this command behaves.Kipkipling
H
7

I would do a

git diff commit1..commit2 filepattern | git-apply --index && git commit

This way you can limit the range of commits for a filepattern from a branch.

It is stolen from Re: How to pull only a few files from one branch to another?

Hime answered 26/1, 2011 at 10:50 Comment(3)
In some situations, this could be very handy. However, if the changes are in a different branch, you can just checkout from the tip of that branch, as in Bart J's answer above.Computerize
Who is Bart Js?Declaim
This works the same as git checkout branch ... below - the local changes not present in branch are removed(!). They are marked as "removed" in the patch file as plain diff does not check the commits history.Cerebrum
S
7

I like the previous 'git-interactive-merge' answer, but there's one easier. Let Git do this for you using a rebase combination of interactive and onto:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

So the case is you want C1 and C2 from the 'feature' branch (branch point 'A'), but none of the rest for now.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Which, as in the previous answer, drops you in to the interactive editor where you select the 'pick' lines for C1 and C2 (as above). Save and quit, and then it will proceed with the rebase and give you branch 'temp' and also HEAD at master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Then you can just update master to HEAD and delete the temp branch and you're good to go:

# git branch -f master HEAD
# git branch -d temp
Shift answered 9/12, 2011 at 19:46 Comment(2)
Can you link directly to the other answer you are referring to (user names are not stable enough as they can change at any time)? (Sort by "Oldest" to restrict the number of possible answers (before yours).)Fortunna
@PeterMortensen good point, I'm pretty sure I got the right one in there now.Shift
H
7

You can use read-tree to read or merge a given remote tree into the current index, for example:

git remote add foo [email protected]/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

To perform the merge, use -m instead.

See also: How do I merge a sub directory in Git?

Huxley answered 25/2, 2016 at 18:25 Comment(0)
I
7

For me, git reset --soft branch is the easiest way to selectively pick the changes from another branch, since, this command puts in my working tree, all the diff changes, and I can easily pick or revert which one I need.

In this way, I have full control over the committed files.

Incommunicado answered 1/4, 2019 at 22:8 Comment(0)
S
5

I wrote my own script called 'pmerge' to partially merge directories. It's a work in progress and I'm still learning both Git and Bash scripting.

This command uses git merge --no-commit and then unapplies changes that don't match the path provided.

Usage: git pmerge branch path
Example: git merge develop src/

I haven't tested it extensively. The working directory should be free of any uncommitted changes and untracked files.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'

# List of changes due to merge | replace nulls with newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # Reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS
Sultanate answered 27/5, 2011 at 1:33 Comment(0)
P
5

A simple approach for selective merging/committing by file:

git checkout dstBranch
git merge srcBranch

// Make changes, including resolving conflicts to single files
git add singleFile1 singleFile2
git commit -m "message specific to a few files"
git reset --hard # Blow away uncommitted changes
Phio answered 22/11, 2017 at 17:25 Comment(2)
Doesn't git reset --hard get rid of committed changes?Heddle
@Prometheus according to documentation: "Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded." So the commit itself is safe.Vespucci
C
4

If you don't have too many files that have changed, this will leave you with no extra commits.

1. Duplicate branch temporarily
$ git checkout -b temp_branch

2. Reset to last wanted commit
$ git reset --hard HEAD~n, where n is the number of commits you need to go back

3. Checkout each file from original branch
$ git checkout origin/original_branch filename.ext

Now you can commit and force push (to overwrite remote), if needed.

Chivalrous answered 16/10, 2018 at 18:15 Comment(0)
A
3

When only a few files have changed between the current commits of the two branches, I manually merge the changes by going through the different files.

git difftool <branch-1>..<branch-2>

see also https://sites.google.com/site/icusite/setup/git-difftool

Ashling answered 7/10, 2017 at 19:49 Comment(0)
U
3

If you only need to merge a particular directory and leave everything else intact and yet preserve history, you could possibly try this... create a new target-branch off of the master before you experiment.

The steps below assume you have two branches target-branch and source-branch, and the directory dir-to-merge that you want to merge is in the source-branch. Also assume you have other directories like dir-to-retain in the target that you don't want to change and retain history. Also, assumes there are merge conflicts in the dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.
Unionize answered 6/11, 2018 at 16:35 Comment(0)
S
3

I found a solution to this question which is really beautiful & simplistic. So I'm just keeping it here for myself & others who want to use this solution.

ANSWER
Say I am in branch1 which contains a single file, file1.txt
Now, I want to the file1.txt with another file file2.txt which is in branch2.
So the command should be,

git checkout --patch branch2 <path_of_file2.txt_in_context_of_branch2>

which prompts a console saying you need to input specific character to reflect the changes in your current branch which is branch1. Sometimes I use a to accept all the hunks.

I hope this answer is helpful.
Thanks

Splay answered 17/12, 2022 at 18:59 Comment(0)
A
1

I'm going to concentrate on the subset of this problem that was of interest to me: I have two branches and I want to pseudo-merge one file from one into the other.

(I say "pseudo-merge" because I don't need or want a merge commit; I just want to combine the contributions of both versions of the file, in the way that I see fit.)

My approach is based on the approach taken in https://mcmap.net/q/45879/-git-merge-only-single-file-from-master-into-current-branch-duplicate. Unfortunately that question is closed as a duplicate (wrongly, in my opinion: it's not a duplicate of this question, and it is wrong to answer-and-close-as-duplicate which is what the answerer did there). But there are some things wrong with that answer, so I've modernized and cleaned up the approach. Instead of checkout and reset, I use restore, and I don't bother to commit anything I don't have to.

Okay, so imagine I have three files:

$ ls
a   b   f

But I only want to pseudo-merge one of them, a, from otherbranch. Let's peek at them to see what the situation will look like. Here's my version:

$ cat a
line one
line two
line three
line four
line five

Here's otherbranch's version:

$ git show otherbranch:a
line one
line two edited
line three
line four
line five
line six

Now the trick here is that we're going to use the index as a scratch pad (which is, after all, what it is for). So we start (STEP 1) by making sure that our version is copied into the index:

$ git add a

Now (STEP 2) we can use restore to fetch the version from otherbranch (nowadays, restore is better than checkout as it lets us talk more clearly):

$ git restore --source otherbranch a

At first blush, this looks bad. We have now completely overwritten our a with the version from otherbranch, as you can see:

$ cat a
line one
line two edited
line three
line four
line five
line six

But not to worry! The previous version of a is still in the index, as you can see:

$ git diff a
diff --git a/a b/a
index abf51fa..333614b 100644
--- a/a
+++ b/a
@@ -1,6 +1,7 @@
 line one
-line two
+line two edited
 line three
 line four
 line five
+line six

Very well, now we're ready for the key move (STEP 3). We do an interactive patch add of our file from the working tree to the index.

We could say git add -p a to start the interactive patch process, in which case we are fed hunks one at a time. But in this case there is just one hunk, and I want to edit it anyway, so I say:

$ git add --e a

The result is that we open a patch file of diffs in our editor! It looks like this:

 line one
-line two
+line two edited
 line three
 line four
 line five
+line six

By careful editing we can now decide what parts we want to accept and what parts we don't. Let's accept "line six" but not "line two edited". So we edit to look like this:

 line one
 line two
 line three
 line four
 line five
+line six

We close the editor and the patch is applied to the index version of a. But we are not quite finished! The otherbranch version of a is still sitting in the working tree:

$ cat a
line one
line two edited
line three
line four
line five
line six

The version we like is in the index, remember? To get it, (STEP 4) we just call git restore plain and simple (again, this the modern way; restore is nicer than reset and can apply to a single file):

$ git restore a

Now our a is correct, and we're all finished:

$ cat a
line one
line two
line three
line four
line five
line six

We could at this point commit, but we don't have to; we've accomplished what we set out to accomplish.

Adaline answered 2/5, 2021 at 21:30 Comment(0)
I
1

What I want: Interactively pick hunks from a branch (which had several messy commits) into a clean commit in a new branch.

git diff + git apply won't work if you had any binary files in that diff.

My approach:

# New branch from a clean starting point, e.g. master
git checkout new-clean-branch origin/master

# Get all changes from the messy branch
# (quote the star so your shell doesn't expand it)
git checkout messy-branch -- '*'

# Unstage ("un-add") everything
git restore --staged .

# Interactively add hunks to be staged for commit
git add -p
Islamism answered 5/10, 2021 at 13:25 Comment(0)
E
1

This adapts @masukomi's diff/apply answer into a one-liner:

git diff HEAD...other_branch -- path/to/file | git apply

You are on a branch my_cool_branch.
You want something from other_branch.

You both modified path/to/file.
You want those changes, but you don't want to obliterate your own changes.

This compares how path/to/file has changed (via diff), and then stages those changes (via apply), without committing.

====

A simpler scenario exists.

You want path/to/file file from other_branch.
Your branch did not modify path/to/file.

In this situation, you can just grab that branch's snapshot of the file:

git checkout other_branch -- path/to/file

This stages a change that modifies path/to/file to match what's in other_branch.
No commit is created. Any other staged changes that you have, remain staged.
You do not switch branch (even though checkout was used).

Ordinarily this would obliterate your own changes to the file, so we should only do this if we know we have no changes to lose.

Echo answered 18/10, 2023 at 13:0 Comment(0)
E
0

If you are a Gitkraken User here you have a small guide

In summary:

  1. move to the branch which you want to bring the changes. (e.g. develop)
  2. right click on the branch that has the new changes and choose the "cherrypick commit" option (e.g. feature-ABC).
  3. Finally Accept and check for conflicts if any.
Embroider answered 20/11, 2021 at 16:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.