ChatGPT解决这个技术问题 Extra ChatGPT

How to merge specific files from Git branches

I have 2 git branches branch1 and branch2 and I want to merge 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?


D
Dave Jarvis

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.


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.
Is there a way to do this non-interactively?
This is not a merge, history is not retained
D
Dave Jarvis

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.


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
R
R.M.

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

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
佚名

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

M
Martin G

You can stash and stash pop the file:

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

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...
M
Makogan

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>

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


J
Jackkobec

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

b
bananaspy

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 -

j
jthill

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"

C
Claire. D

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


M
Mohamed Laradji

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.


T
Trac Nguyen

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

L
Lucas Lima

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

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

Ugly? Yes. Easy to remember? Also yes.


t
techniao

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.