I have some files in my repository that are bottom-growing: most of the changes involve adding new lines at the bottom of the file. This is mostly language and other property files.
As an annoying side effect, whenever two people make additions at the same time I get merge conflicts, and the resolution always involves manually copy-pasting so that lines from both versions get included.
Is there a tip, trick or methodology that will relieve some of the pain of this process?
For example, a simplistic solution would be to tell the developers to add new lines at random places in the middle of the file. This will probably work, but it involved a conscious effort, and a weird-looking history.
The most commonly used strategies are Fast Forward Merge and Recursive Merge. In this most commonly used merge strategy, history is just one straight line. When you create a branch, make some commits in that branch, the time you're ready to merge, there is no new merge on the master.
Recursive Git Merge Strategy Options Changes from the 'theirs' side are automatically incorporated if they do not conflict. The opposite of the 'ours' strategy. the "theirs" option favors the foreign merging tree in conflict resolution. This option spends extra time to avoid mis-merges on unimportant matching lines.
The merge-ort strategy is a from-scratch rewrite with the same concepts (recursion and rename-detection), but solving many of the long-standing correctness and performance problems. The result is much faster. For a merge (but a large, tricky one containing many renames), merge-ort gains over a 500x speedup.
octopus - This resolves more than two-head case, but refuses to do complex merge that needs manual resolution. It is primarily meant to be used for bundling topic branch heads together. This is the default merge strategy when pulling or merging more than one branches.
You could use the gitattributes mechanism to define a custom merge driver (like this one for instance) in order to copy automatically the relevant sections.
[merge "aggregate"] name = agregate both new sections driver = aggregate.sh %O %A %B
It will be a 3-way merge, which means you can easily diff %A
and %B
against %O
(common ancestor) in order to isolate said new sections, and aggregate them in the result merged file.
That aggregate merge driver needs only to do:
comm -13 $1 $3 >> $2
(The comm utility is part of the GoW -- Gnu on Windows -- distribution, if you are on Windows)
Here is a little demo:
First, let's set up a Git repo, with a file modified in two branches ('master
' and 'abranch
'):
C:\prog\git\tests>mkdir agg C:\prog\git\tests>cd agg C:\prog\git\tests\agg>git init r1 Initialized empty Git repository in C:/prog/git/tests/agg/r1/.git/ C:\prog\git\tests\agg>cd r1 # Who am I? C:\prog\git\tests\agg\r1>git config user.name VonC C:\prog\git\tests\agg\r1>git config user.email vonc@xxx # one file, first commit: C:\prog\git\tests\agg\r1>echo test > test.txt C:\prog\git\tests\agg\r1>git add . C:\prog\git\tests\agg\r1>git commit -m "first commit" [master c34668d] first commit 1 file changed, 1 insertion(+) create mode 100644 test.txt # Let's add one more common line: C:\prog\git\tests\agg\r1>echo base >> test.txt C:\prog\git\tests\agg\r1>more test.txt test base C:\prog\git\tests\agg\r1>git add . C:\prog\git\tests\agg\r1>git commit -m "base" [master d1cde8d] base 1 file changed, 1 insertion(+)
Now we create a new branch, and make concurrent modifications in both versions of that file, at the end of it like the OP itsadok specifies in the question.
C:\prog\git\tests\agg\r1>git checkout -b abranch Switched to a new branch 'abranch' C:\prog\git\tests\agg\r1>echo "modif from abranch" >> test.txt C:\prog\git\tests\agg\r1>git add . C:\prog\git\tests\agg\r1>git commit -m "abranch contrib" [abranch a4d2632] abranch contrib 1 file changed, 1 insertion(+) C:\prog\git\tests\agg\r1>type test.txt test base "modif from abranch" # back to master C:\prog\git\tests\agg\r1>git checkout master Switched to branch 'master' C:\prog\git\tests\agg\r1>echo "contrib from master" >> test.txt C:\prog\git\tests\agg\r1>git add . C:\prog\git\tests\agg\r1>git commit -m "contrib from master" [master 45bec4d] contrib from master 1 file changed, 1 insertion(+) C:\prog\git\tests\agg\r1>type test.txt test base "contrib from master"
We have out two branches (note: git lg
is an alias of mine)
C:\prog\git\tests\agg\r1>git lg * 45bec4d - (HEAD, master) contrib from master (86 minutes ago) VonC | * a4d2632 - (abranch) abranch contrib (86 minutes ago) VonC |/ * d1cde8d - base (87 minutes ago) VonC * c34668d - first commit (89 minutes ago) VonC
Now let's try a merge:
C:\prog\git\tests\agg\r1>git merge abranch Auto-merging test.txt CONFLICT (content): Merge conflict in test.txt Automatic merge failed; fix conflicts and then commit the result. C:\prog\git\tests\agg\r1>more test.txt test base <<<<<<< HEAD "contrib from master" ======= "modif from abranch" >>>>>>> abranch
... Failed as advertised ;) A git merge --abort
will reset the situation.
Let's put in place our merge driver:
C:\prog\git\tests\agg\r1>git config merge.aggregate.name "aggregate both new sections" C:\prog\git\tests\agg\r1>git config merge.aggregate.driver "aggregate.sh %O %A %B" C:\prog\git\tests\agg\r1>echo test.txt merge=aggregate > .gitattributes
At this point, a merge still fails:
C:\prog\git\tests\agg\r1>git merge abranch aggregate.sh .merge_file_a09308 .merge_file_b09308 .merge_file_c09308: aggregate.sh: command not found fatal: Failed to execute internal merge
Normal: we need to write that script, and add it to the PATH
:
vim aggregate.sh: #!/bin/bash # echo O: $1 # echo A: $2 # echo B: $3 # After http://serverfault.com/q/68684/783 # How can I get diff to show only added and deleted lines? # On Windows, install GoW (https://github.com/bmatzelle/gow/wiki/) ob=$(comm -13 $1 $3) # echo "ob: ${ob}" echo ${ob} >> $2 ---- C:\prog\git\tests\agg\r1>set PATH=%PATH%;C:\prog\git\tests\agg\r1
And now, the aggregate
merge driver can operate:
C:\prog\git\tests\agg\r1>git merge --no-commit abranch Auto-merging test.txt Automatic merge went well; stopped before committing as requested C:\prog\git\tests\agg\r1>type test.txt test base "contrib from master" "modif from abranch"
Here you go: the end of the test.txt
file from abranch
has been added to the file on master
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With