"recursive" merge strategy fails when renaming a file into a subdirectory: fatal: git write-tree failed to write a tree
From: Marcel Cary <hidden>
Date: 2016-06-15 22:49:04
When I merge with a branch that renames hi to hi/there, the
"recursive" strategy fails with this error:
hi/there: unmerged (45b983be36b73c0788dc9cbcb76cbb80fc7bb057)
fatal: git write-tree failed to write a tree
The "resolve" strategy works; that is my work-around for now:
git merge -s resolve master
Here's a transcript of a simple example anyone can use to replicate
the problem, with some commentary interspersed. I see this behavior
on 1.6.3.3 on Linux and 1.7.1 on Mac.
~/tmp$ mkdir test-repo
~/tmp$ cd test-repo/
~/tmp/test-repo$ git init
Initialized empty Git repository in /Users/marcel/tmp/test-repo/.git/
~/tmp/test-repo|master$ echo hi > hi
~/tmp/test-repo|master$ git add hi
~/tmp/test-repo|master$ git commit -m hi
[master (root-commit) 635cab8] hi
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 hi
~/tmp/test-repo|master$ git branch other-feature
~/tmp/test-repo|master$ git rm hi
rm 'hi'
~/tmp/test-repo|master$ mkdir hi
~/tmp/test-repo|master$ echo hi > hi/there
~/tmp/test-repo|master$ git add hi/there
~/tmp/test-repo|master$ git commit -m 'hi there'
[master eee387a] hi there
1 files changed, 0 insertions(+), 0 deletions(-)
rename hi => hi/there (100%)
~/tmp/test-repo|master$ git checkout other-feature
Switched to branch 'other-feature'
~/tmp/test-repo|other-feature$ echo foo > foo
~/tmp/test-repo|other-feature$ git add foo
gmarcel@marcel-carys-macbook:~/tmp/test-repo|other-feature$ git
commit -m 'foo'
[other-feature 5d7bd95] foo
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 foo
~/tmp/test-repo|other-feature$ git merge master
hi/there: unmerged (45b983be36b73c0788dc9cbcb76cbb80fc7bb057)
fatal: git write-tree failed to write a tree
That was the error. Now, I've seen long-past posts about this error
message that suggested using the standard conflict resolution
procedure, so here goes... (but it will produce a commit which is not
in fact a merge commit).
~/tmp/test-repo|other-feature$ git status
# On branch other-feature
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: hi
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# added by them: hi/there
#
~/tmp/test-repo|other-feature$ ls -l hi
ls: hi: No such file or directory
~/tmp/test-repo|other-feature$ git checkout master -- hi/there
~/tmp/test-repo|other-feature$ git status
# On branch other-feature
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: hi -> hi/there
#
~/tmp/test-repo|other-feature$ git commit
[other-feature ef1d221] merged
1 files changed, 0 insertions(+), 0 deletions(-)
rename hi => hi/there (100%)
~/tmp/test-repo|other-feature$ git log -n 1
commit ef1d221d87f09b08fd705f55ede4870dd3df338b
Author: Marcel Cary [off-list ref]
Date: Fri Jul 2 13:21:29 2010 -0700
merged
Ok, there was the commit I got from the standard resolution procedure.
Note how there's no line "Merge: 5d7bd95 eee387a" ? Normally such a
commit header would be shown if the commit really had two parents. So
the standard resolution procedure failed. If I were to try merging
again, instead of "Already up-to-date" it would fail again with a
similar message.
So I'll try again with the work-around...
~/tmp/test-repo|other-feature$ git reset --hard HEAD@{1}
HEAD is now at 5d7bd95 foo
~/tmp/test-repo|other-feature$ git merge -s resolve master
Trying really trivial in-index merge...
error: Merge requires file-level merging
Nope.
Trying simple merge.
Simple merge failed, trying Automatic merge.
Adding hi/there
Merge made by resolve.
hi => hi/there | 0
1 files changed, 0 insertions(+), 0 deletions(-)
rename hi => hi/there (100%)
~/tmp/test-repo|other-feature$ git --version
git version 1.7.1
~/tmp/test-repo|other-feature$
The "resolve" strategy works great. It would be nice if the
"recursive" merge strategy could also handle such a case, so users
don't have to know to choose a special one for this case. Git
generally seems to handle this sort of repurposing of path names
pretty well, so perhaps this is an oversight.
Note that simply changing a file to a directory is not enough; one of
the new files in the directory has to appear as an exact or fuzzy
rename of the original file.
I'm not volunteering to fix it; when I first searched Google for the
error message there was no conclusion (that applied to me at least)
about (a) what class of conditions leads to the error, nor (b) how to
resolve or work around the problem, I thought I'd share this remedy,
for what it's worth.
Here are the two threads I found:
http://osdir.com/ml/git/2009-03/msg00247.html
http://git.661346.n2.nabble.com/fatal-error-when-merging-a-rename-td2664375.html
And there was even an attempt to fix it, but no mention of the "-s
resolve" work around:
http://thread.gmane.org/gmane.comp.version-control.git/100859/
Marcel