git merge-file fails with bash process substitution operator that uses git show
I’m trying to merge changes from a file which is in git into one which isn’t under version control (more context) using a 3-way-merge.
As there is a
git merge-file <local> <base> <other> which expects 3 files and as I don’t fancy creating intermediate files and forgetting to clean them up, i used the bash
<(...) process substitution operator as i do quite often.
It turns out that this doesn’t seem to work as expected as the following example shows on git 2.4.2 and 2.4.3 on two different systems.
Let’s create a small test for this so we know what we’re talking about. We’ll create a file
foo.txt (the one in git with 2 versions at least) and a file
bar.txt which is not under version control:
git init echo -e 'foo\nbar' > foo.txt git add foo.txt git commit -m'init' echo -e 'foo\nbar\nend' > foo.txt git commit -a -m'end' echo -e 'start\nfoo\nbar' > bar.txt git show HEAD^:foo.txt > foo.txt.base git show master:foo.txt > foo.txt.end
This leaves us with a couple of files that look like this:
foo.txt @ HEAD^ & foo.txt.base:
foo.txt @ master & foo.txt.end:
foo bar end
start foo bar
With intermediate files:
So now let’s run
git merge-file -p bar.txt foo.txt.base foo.txt.end:
start foo bar end
This is the output i’d like to get with the following method as well.
With Process substitution:
But if i run:
git merge-file -p bar.txt <(git show HEAD^:foo.txt) <(git show master:foo.txt), i get this output:
start foo bar
This is not expected and (sometimes!)
echo $? prints
1 indicating an error.
What’s even weirder is that as i couldn’t understand this behavior i decided to re-run the above command (up-arrow) and was even more puzzled when this output appeared:
<<<<<<< bar.txt start foo bar ======= foo bar end >>>>>>> /dev/fd/62
As i didn’t really expect this fail, much less to be non-deterministic i ran again a couple of times to find this output as well:
<<<<<<< bar.txt start foo bar ======= >>>>>>> /dev/fd/62
To narrow down the problem i tried
git merge-file -p bar.txt <(echo -e 'foo\nbar') <(echo -e 'foo\nbar\nend') which reliably prints the expected merge:
start foo bar end
Can someone explain this weird behavior?
I can obviously fix it by not using process substitution, but i’d like to understand why this happens as i’m using process substitution in a lot of bash scripts. Also i’d be interested in workarounds which allow me to still use process substitution.
Also if you think this is a bug i’m interested in which component it originates from:
git merge-file or bash?
One Solution collect form web for “git merge-file fails with bash process substitution operator that uses git show”
As pointed out in the comments and here this might be a problem with
<(...) providing a FIFO like file that doesn’t support
seek. The same behavior is shown in ZSH unless invoked like this:
git merge-file -p bar.txt =(git show HEAD^:foo.txt) =(git show master:foo.txt)
This means that the problem originates from
git merge-file expecting files that it can
seek on, while
<(...) doesn’t provide these.