How can you merge two branches in git, retaining necessary files from a branch?
When merging two branches, if a file was deleted in one branch and not in another, the file is ultimately deleted.
For example:
- A file exists in master when you make a new branch
- you remove the file from master since we don't need it (yet)
- you make changes in the branch to add a feature, which relies on the file existing
- you make bug fixes in master (cannot be discarded)
- you merge some day, and the file is gone!
How to Reproduce:
Create a git repo with one file.
git init echo "test" > test.txt git add . git commit -m "initial commit"
Create a branch
git branch branchA
Delete the file in master
git rm test.txt git commit -m "removed file from master"
Make ANY changes in branchA that don't touch the deleted file (it has to be unchanged to avoid Conflict)
git checkout branchA touch something.txt git add . git commit -m "some branch changes"
From here, any way I've found to merge these two branches, the test.txt file is deleted. Assuming we were relying on the file for branchA
, this is a big problem.
Failing examples:
Merge 1
git checkout branchA
git merge master
ls test.txt
Merge 2
git checkout master
git merge branchA
ls test.txt
Rebase 1
git checkout branchA
git rebase master
ls test.txt
My solution to this was to simply modify the files I needed to keep (added a comment which was needed anyway) and commit those changes on the target branch, thus generating a merge conflict which could easily be resolved with a
git add
and a normal commit.My history went something like this. Branch names have been changed to protect the innocent.
git merge master
on feature_branch causes original files to be removed (of course),git reset --hard
to before the mergeFor a quick fix in this case, "git revert" the commit that deleted the file.
When this situation comes up in the future, the better way to handle it is to ensure that the creation of the new file happens on the branch. Then it gets added on master when you merge, but you don't have the file lying around in master in the meantime.
This is an interesting issue. Because you deleted the file after
BranchA
was created, and then are mergingmaster
intoBranchA
, I'm not sure how Git would be able to realize there is a conflict.After the bad merge you can undo, and then re-merge, but add back the file:
You need to modify the file in the branch, so that there's a merge conflict with the delete in the trunk.
The exact same thing will happen if you, for example, delete a declaration for something in a headerfile in the trunk (because nothing needs it), and add a dependency on that declaration to some non-header file(s) in the branch. When you merge, since the branch doesn't touch (that part of) the header, it will just delete the declaration and things will break.
Whenever you have stuff in multiple places that is interdependent and needs to be kept in sync, its very easy for a merge to silently introduce problems. Its just one of the things you have to know about and check when merging. Ideally, you use compile-time asserts or other build time checks that will make any failures immediately apparent.
Casey's example didn't work for my case - I couldn't checkout
test.txt
frommaster
, because it was no longer in that branch:Happily I could pull the file out of
branchA
's ownHEAD
: