Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strategy for git and append-mostly files

Tags:

git

git-merge

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.

like image 282
itsadok Avatar asked Aug 07 '12 07:08

itsadok


People also ask

What is the best git merge strategy?

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.

What are some merge strategies you can use with git?

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.

What is ORT strategy in git?

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.

Which strategy is used by git for merging two branches?

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.


1 Answers

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.

like image 173
VonC Avatar answered Oct 08 '22 23:10

VonC