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.

  • Get files deleted between git revisions
  • set up git alias, but then calling it gives 'command not found'
  • How to set correctly parameters to batch file to run “git pull”?
  • Git - for a single file, number of times each line changed
  • How do i make a GIT credential helper?
  • Git completion and PS1 not working: “__git_ps1: command not found” on “sudo -s” or “sudo su” on Ubuntu 12.04
  • 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.

    Example:

    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
    bar
    

    foo.txt @ master & foo.txt.end:

    foo
    bar
    end
    

    bar.txt:

    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
    

    Question

    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 show, git merge-file or bash?

  • git: query regarding merging notes ref
  • calling git diff in pre-commit shell script
  • git config: “remote.origin.push” vs “push.default”
  • Git Extensions: Unable to push to repo after renaming it (gitolite)
  • Why is Git committing to the wrong repository?
  • How to manage a local git repository using Rubberduck
  • 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.

    Git Baby is a git and github fan, let's start git clone.