Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging a branch with deleted file back to master

In my master branch, I got 2 files:

file1.txt
file2.txt

I created new branch named b1 from there. In b1, I modified file2.txt, and accidentally deleted file1.txt (from disk, when I open windows explorer to the source folder, file1.txt isn't there). Now I want to keep the changes in file2.txt and recover file1.txt from master (I don't need the deleted version). I switched to master, did a merge to b1, no conflict happened, it said 'all up-to-date' but file1.txt isn't there. What should have I done?Tks.

like image 280
EyeQ Tech Avatar asked Sep 26 '12 09:09

EyeQ Tech


2 Answers

I think there are several questions you are asking here. The first one appears to be "How do I recover file1.txt?" Assuming your revision graph looks something like this:

               M
(master)  o -- o --- o ------o  HEAD
                \           /
(b1)             o -- o -- o
                 A    B    C

You can get the previous copy of file1.txt via git checkout:

git checkout HEAD~1 -- file1.txt

That will resurrect file1.txt and stage it in your working copy. You can the git commit and the file will be back. Note: HEAD~1 says take the first parent of HEAD, which points to the state of master before you merged b1. If you know the commit id, you can use that instead of HEAD~1 as well.

The other question you seem to be asking is "What should have I done in branch b1 to avoid this?" The most obvious choice would have been not to delete file1.txt in the first place. But let's assume you thought you needed to, and decided that choice was wrong. Next, let's assume you didn't share branch b1 by pushing the changes somewhere. If you noticed that you deleted the file right away, you could do:

git checkout HEAD~1 -- file1.txt
git commit --amend

That would say, "give me back file1.txt, and then merge that file into the latest commit." This has the effect of appearing like you never removed the file in the first place.

If you didn't notice the file was removed right away, and you had several commits in between, then you may want to look at using git rebase to fix things up.

If you deleted the file in it's own commit, then you can use git rebase -i to drop the commit from the history of the branch. Let's suppose that B was the commit that deleted the file, and that's the only thing done in that commit. While on b1, you'd run something like:

git rebase -i B~1

Remove the line that contains the offending commit (B), save and exit. Your branch just had it's history re-written without B in it. For example, I ran git rebase -i and this was shown in the editor:

pick 40f76a7 removed bar
pick 30a25f5 modified foo

I then dropped 40f76a7 from the list and then left me with:

pick 30a25f5 modified foo

The history now looks like this after you merge:

               M
(master)  o -- o --- o ------o  HEAD
                \           /
(b1)             o ------- o
                 A         C'

Note the commit id of C' is different than C because B no longer exists, and the parent sha1s are part of the commit id. IOW, the sha1 of C changed because we re-wrote history.

If you removed file1.txt and had a bunch of other changes in the same commit, then there are a few more steps. First, bring back the file and commit it:

git checkout B~1 -- file1.txt
git commit -m "Reinstate file1.txt"

Let's call the new commit D. Our revision graph now looks like

               M
(master)  o -- o --- o -----------o  HEAD
                \           
(b1)             o -- o -- o -- o
                 A    B    C    D

Now, do:

git rebase -i B~1

And move the line containing commit id D to the just after commit id B, and change pick to squash. For example, I get this when running git rebase -i B~1:

pick 40f76a7 removed bar plus other changes
pick 30a25f5 modified foo
pick 6177cb7 add bar

6177cb7 is the commit that reinstates bar. So I move it just below 40f76a7 and change the command to squash:

pick 40f76a7 removed bar plus other changes
squash 6177cb7 fix bar
pick 30a25f5 modified foo

Save and exit. It will ask you to fix up the commit message. Do that. When you're all done, you end up with a history that looks like this:

               M
(master)  o -- o --- o -----------o  HEAD
                \           
(b1)             o -- o -- o
                 A    B'   C'

The new B' no longer removes file1.txt. At this point, you're ready to merge with master.

A few closing remarks. Be careful with git rebase. You can lose history, if you aren't careful. Make sure to read the git rebase man page. There is lots of useful information on there. Note: all this work with git rebase is only necessary if you want to remove the fact that you deleted the file from history. If you're okay with having a commit showing you bringing the file back, then by all means, use git checkout to restore the file, and commit it. It's far less tedious, and easier for a new git user. git rebase is pretty advanced, and takes some practice. However, if you invest the time and learn it well, it's really quite useful.

like image 52
John Szakmeister Avatar answered Oct 20 '22 12:10

John Szakmeister


Merging is not something you do in this case, it is normal that Git kept the deletion when merging back to master. If you want to get a file from another branch or tag, it's git checkout master -- filename.

In your case, you need to cancel the merge before:

  1. make sure you're on master, and that your working directory is clean (git status shows no change)
  2. cancel the merge you made

     git reset --hard master@{1}
    
  3. go back to b1

    git checkout b1
    
  4. checkout file1.txt from master in your working copy:

    git checkout master -- file1.txt
    
  5. commit or amend your previous commit, or whatever you want to do
like image 3
CharlesB Avatar answered Oct 20 '22 11:10

CharlesB