Undo deleting file from previous commit

advertisements

I am using gitflow branching model for development. I've branched from develop to feature/X and in the very first commit in feature/X I also deleted some files (using git rm <file>). Now after a couple of other commits in that branch I have realized, that I need the files I have deleted earlier.

Here's a short sample to illustrate what i mean for clarification:

git flow init -d
echo "file contents" > file.txt
git commit -m "Added file.txt"
git checkout -b feature/X
git rm file.txt
echo "foo" > foo.txt
git add --all
git commit -m "Deleted file.txt and added another file"
<some more commits in feature/X>

git log -u

...

commit 04948fc4fc36d83901a0862b057657f3ccb9cf0d
Author: <...>
Date:   Wed Aug 10 12:26:58 2016 +0200

    Deleted file.txt and added another file

diff --git a/file.txt b/file.txt
deleted file mode 100644
index d03e242..0000000
--- a/file.txt
+++ /dev/null
@@ -1 +0,0 @@
-file contents
diff --git a/foo.txt b/foo.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/foo.txt
@@ -0,0 +1 @@
+foo

...

I don't just want to re-add the file in a new commit to avoid issues when merging feature/X to develop while there has been some changes in file.txt in develop branch.

Is there any way to take out the removal of file.txt from that earlier commit?

I have tried git reset <sha-of-previous-commit> file.txt but that didn't bring back the file to my working copy.

EDIT 1:
I know about the down sides of rewriting history that has been pushed to a remote already. However I know nobody has done any commits on feature/X except me hence it should be safe for me to rewrite the history although it has been pushed already.


Answer

I don't just want to re-add the file in a new commit to avoid issues when merging feature/X to develop while there has been some changes in file.txt in develop branch.

Yes, you do want to just re-add the file in a new commit. git is wise about this (well, actually there is no special intelligence involved here, it just comes from the way git handles files).

Example

git init test                       # init a repos, create test.txt
cd test
echo start > test.txt
git add -A ; git commit -m 'x'

git checkout -b bra                 # create branch, add a line

echo line1 >> test.txt
git add -A ; git commit -m 'x'

git rm test.txt                     # delete file + commit
git commit -m "x"

echo start > test.txt               # restore file and add a line
echo line2 >> test.txt
git add -A ; git commit -m 'x'

git checkout master                 # back to master, add the same line and another one
echo line2 >> test.txt
echo line3 >> test.txt
git add -A ; git commit -m 'x'

git merge bra                       # merge

cat test.txt
   start
   line2
   <<<<<<< HEAD
   line3
   =======
   >>>>>>> bra

The conflict is as expected (sic!); the important part about it is that line2 is not part of the conflict. git merge did not care about all the shenannigans that happened to the file on the branch, it is only interested about the end result.