Avoid the deletion by cherry-pick
-ing around it:
Yes, you can do this quite simply: you must avoid merging those commits which delete the files. So, of course, it will behoove you not to make commits which make changes and delete files.
Example:
Create a git repo with a master
branch which has two files:
~$ mkdir gittest
~$ cd gittest
~/gittest$ git init
Initialized empty Git repository in /home/kaz/gittest/.git/
~/gittest$ echo foo > foo
~/gittest$ echo bar > bar
~/gittest$ git add foo bar
~/gittest$ git commit -m "root commit"
[master (root-commit) 1c1860f] root commit
2 files changed, 2 insertions(+)
create mode 100644 bar
create mode 100644 foo
Shoot a topic
branch from the root commit:
~/gittest$ git branch topic
Create a second version on master
of both files:
~/gittest$ echo foo >> foo
~/gittest$ echo bar >> bar
~/gittest$ git diff
diff --git a/bar b/bar
index 5716ca5..a486f1a 100644
--- a/bar
+++ b/bar
@@ -1 +1,2 @@
bar
+bar
diff --git a/foo b/foo
index 257cc56..0d55bed 100644
--- a/foo
+++ b/foo
@@ -1 +1,2 @@
foo
+foo
~/gittest$ git commit -a -m "hack 1"
[master ded65d8] hack 1
2 files changed, 2 insertions(+)
Switch to topic
. Delete bar
and commit. Modify foo
in conflicting way with master
and commit:
~/gittest$ git checkout topic
Switched to branch 'topic'
~/gittest$ git rm bar
rm 'bar'
~/gittest$ git commit -m "delete bar"
[topic 8a4009d] delete bar
1 file changed, 1 deletion(-)
delete mode 100644 bar
~/gittest$ echo "foo2" > foo
~/gittest$ git commit -a -m "replace foo"
[topic abfa329] replace foo
1 file changed, 1 insertion(+), 1 deletion(-)
Recap history on topic
:
~/gittest$ git log --oneline
abfa329 replace foo
8a4009d delete bar
1c1860f root commit
Back to master
:
~/gittest$ git checkout master
Switched to branch 'master'
~/gittest$ git log --oneline
ded65d8 hack 1
1c1860f root commit
Now cherry pick abfa329 replace foo
from topic, avoiding 8a4009d delete bar
.
$ git cherry-pick abfa329
error: could not apply abfa329... replace foo
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
Fix the conflict on foo
and commit:
~/gittest$ cat foo
<<<<<<< HEAD
foo
foo
=======
foo2
>>>>>>> abfa329... replace foo
~/gittest$ cat > foo
foo
foo2
~/gittest$ git add foo
~/gittest$ git commit -m "merged topic, avoiding deletion"
[master 320818f] merged topic, avoiding deletion
1 file changed, 1 insertion(+), 1 deletion(-)
Note that bar
is still there:
~/gittest$ ls bar
bar
~/gittest$ cat bar
bar
bar
Use git reset
to undo unwanted deletions, during or after merge:
If you do pick up an unwanted deletion, you can undo it easily.
Let's roll back our example master
branch to the hack 1
commit and rewrite that so that bar
is not modified on master
, only foo
:
~/gittest$ git reset --hard ded65d8
HEAD is now at ded65d8 hack 1
~/gittest$ git reset HEAD^ -- bar
Unstaged changes after reset:
M bar
~/gittest$ git commit --amend -m "hack1: bar only"
[master 623908a] hack1: bar only
1 file changed, 1 insertion(+)
~/gittest$ git log --oneline
623908a hack1: bar only
1c1860f root commit
~/gittest$ git show -p
commit 623908a3bf6b2fb81b0bf8309fa2e83cd602986a
Author: Kaz <kaz@stackexchange.example.com>
Date: Sat Dec 20 08:03:27 2014 -0800
hack1: bar only
diff --git a/foo b/foo
index 257cc56..0d55bed 100644
--- a/foo
+++ b/foo
@@ -1 +1,2 @@
foo
+foo
Now, let us merge topic
without suspecting there is an unwanted deletion:
~/gittest$ git merge topic
error: Your local changes to the following files would be overwritten by merge:
bar
Please, commit your changes or stash them before you can merge.
Aborting
Oops! I had forgot to git reset --hard
after undoing the bar
changes. Take two:
~/gittest$ git reset --hard
HEAD is now at 623908a hack1: bar only
~/gittest$ git merge topic
Auto-merging foo
CONFLICT (content): Merge conflict in foo
Removing bar
Automatic merge failed; fix conflicts and then commit the result.
Okay, so the merge automatically deleted bar
(no conflict there because bar
is not modified on master
). There is a conflict on foo
, exactly the same as in the cherry-pick
case, so let's deal with that first:
~/gittest$ cat > foo
foo
foo2
~/gittest$ git add foo
Now, roll back the deletion of bar
. Note that bar
is not in the working tree and remains that way for now:
~/gittest$ git reset HEAD^ -- bar
Unstaged changes after reset:
D bar
We commit everything:
~/gittest$ git commit -m "merged topic, except deletion of bar"
[master 57b7fca] merged topic, except deletion of bar
Now we bring back bar
:
~/gittest$ git reset
Unstaged changes after reset:
D bar
Oops, I meant --hard
:
~/gittest$ git reset --hard
HEAD is now at 57b7fca merged topic, except deletion of bar
Review the log, with --graph
:
~/gittest$ git log --graph --oneline
* 57b7fca merged topic, except deletion of bar
|\
| * abfa329 replace foo
| * 8a4009d delete bar
* | 623908a hack1: bar only
|/
* 1c1860f root commit
Verify file contents. foo
is merged, bar
is there:
~/gittest$ cat foo
foo
foo2
~/gittest$ cat bar
bar