Git merge disappointment

Gary Johnson garyjohn at spocom.com
Sat Apr 24 21:22:13 CEST 2010


On 2010-04-23, chombee wrote:
> I'd like to keep a text file in my homedir called 'notes' which contains
> all my notes to myself, and track this file and sync it between
> computers using git. I write the file in markdown syntax and each new
> entry usually begins with a header. So on machine A I add a new entry to
> the top of my notes file:
> 
>     # A new entry written on machine A
> 
>     AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>     AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>     AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>     AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
> 
>     # An old entry
> 
>     CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
>     CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
>     CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
> 
> (Imagine the A's and C's being the contents of some notes, obviously.)
> On machine B I also add a new entry, so both machines have modifications
> to the file and a merge is going to be needed at some point:
> 
>     # A new entry written on machine B
> 
>     BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
>     BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
> 
>     # An old entry
> 
>     CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
>     CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
>     CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
> 
> When I pull from machine B to machine A or vice-versa the merge fails
> and git leaves the file looking something like this:

[merge mess deleted]

> ..and so on. Each entry A and B contains multiple paragraphs and git
> interleaves the paragraphs of the two entries together, leaving a
> complete mess for me to untangle. It's the worst thing git could
> possibly do. If there are more than two entries to be merged at once
> it's even worse.
> 
> Git is trying to merge commit A (which adds entry A to the top of the
> notes file) and commit B (which adds entry B). They are two simple
> commits, each one adds just one hunk of text to the file. I'd expect git
> to put all the text of entry A followed by all the text of entry B and
> then ask me to finish the merge, which would be trivial. But instead it
> interleaves them?

Git (or the merge tool that git uses) doesn't know that those
changes are unrelated.  From its perspective, the top of the file
has been added to with different sets of lines that should resolve
to one change.  It does its best, making some assumptions, to merge
those lines.

> So I have to resort to using a different text file for each note, which
> avoids the problem unless I edit the same note on two machines (in which
> case, fair enough). But multiple files sucks. It's much quicker and
> easier to make a new note if I don't need to create a new file and come
> up with a filename for it. I won't even bother to note down something
> very quick or short if I'm going to have to come up with a filename.
> It's much easier to find stuff in my notes if they're all in one file, I
> can use my text editor's incremental search to jump around.

I use Mercurial rather than git as my DVCS tool, so I developed the
following solution for Mercurial.  I understand the two tools to be
similar in many regards, though, so it shouldn't be too difficult to
modify this solution to work with git.

I wrote a script that merges two revisions of a file by assuming
that the user simply wants to insert the changes in the local copy
of the file ahead of the latest version in the repository.

    ------------------------ notesmerge ------------------------
    #!/usr/bin/sh

    base=$1
    local=$2
    output=$3
    other=$4

    diff "$base" "$other" |
    sed -n '/^> /s/^> //p' |
    cat - "$local" > "$output"
    ------------------------------------------------------------

I then told Mercurial to use that script for merging revisions to
any file named 'notes' by adding this to my ~/.hgrc.

    [merge-patterns]
    notes = notesmerge

    [merge-tools]
    notesmerge.executable = notesmerge
    notesmerge.args = $base $local $output $other

Note that this script is pretty simple-minded:  it inserts all the
added lines in the local copy of the file at the top of the copy in
the repository; any deletions are ignored.  I've done only limited
testing of it.

HTH,
Gary



More information about the vcs-home mailing list