Подтвердить что ты не робот

Стратегия для git и файлов append-most

У меня есть некоторые файлы в моем репозитории, которые растут: большинство изменений связано с добавлением новых строк в нижней части файла. Это в основном языковые и другие файлы свойств.

Как раздражающий побочный эффект, когда два человека делают дополнения одновременно, я получаю конфликты слияния, и разрешение всегда включает в себя ручное копирование, чтобы строки из обеих версий включались.

Есть ли подсказка, трюк или методология, которые облегчат часть боли этого процесса?

Например, упрощенным решением было бы сказать разработчикам добавить новые строки в случайных местах в середине файла. Это, вероятно, будет работать, но это связано с сознательным усилием и странной историей.

4b9b3361

Ответ 1

Вы можете использовать механизм gitattributes для определения настраиваемого драйвера слияния (например, этот, например,), чтобы автоматически копировать соответствующие разделы.

[merge "aggregate"]
        name = agregate both new sections
        driver = aggregate.sh %O %A %B

Это будет трехстороннее слияние, что означает, что вы можете легко отличить %A и %B от %O (общий предок), чтобы изолировать указанные новые разделы и объединить их в объединенный файл результата.

Этот агрегатный драйвер слияния должен выполнять только:

comm -13 $1 $3 >> $2

(Коммутационная утилита является частью GoW - Gnu в Windows - дистрибутив, если вы находитесь в Windows)


Вот небольшая демонстрация:

Сначала настройте репо Git с файлом, измененным в двух ветвях ('master' и '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 [email protected]

# 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 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(+)

Теперь мы создаем новую ветку и делаем параллельные модификации в обеих версиях этого файла, в конце ее, как OP itsadok, задается в вопросе.

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"

У нас есть две ветки (примечание: git lg является псевдонимом моего)

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

Теперь попробуйте слияние:

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

... Не удалось объявить;) A git merge --abort будет reset ситуация.

Вставьте наш драйвер слияния:

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

В этот момент слияние все еще не выполняется:

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

Нормальный: нам нужно написать script и добавить его в 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

И теперь драйвер слияния aggregate может работать:

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"

Здесь вы: конец файла test.txt из abranch был добавлен в файл на master.

Ответ 2

Другим решением является использование опции --union команды сантехники git-merge-file.

[merge "aggregate"]
    name = aggregate both new sections
    driver = git merge-file --union -L %P %A %O %B

Это более надежно, чем использование comm: comm может генерировать неправильный вывод, когда строки ввода не сортируются в соответствии с LC_COLLATE.

Ответ 3

Если вы не хотите повторно добавлять линии, которые были удалены, вы должны использовать несколько более сложное заклинание comm:

tmp=$(mktemp)
(comm -12 %A %B ; comm -13 %O %A ; comm -13 %O %B ) >| $tmp
mv $tmp %A

Сначала вы выбираете всю линию, общую для локальных и удаленных версий; это гарантирует, что все удаленные строки будут удалены. Затем вы добавляете строки, добавленные удаленной версией, затем добавляемые локальной версией.

Эту стратегию можно установить в .gitconfig с помощью одной команды:

git config merge.aggregate.driver 'tmp=$(mktemp) ; (comm -12 %A %B ; comm -13 %O %A ; comm -13 %O %B ) >| $tmp ; mv $tmp %A'