Pytanie strategia dla git i append - głównie pliki


Mam trochę plików w moim repozytorium, które są denne: większość zmian wymaga dodania nowych linii na dole pliku. Są to głównie pliki językowe i inne pliki właściwości.

Jako irytujący efekt uboczny, gdy dwie osoby dodają dodatki w tym samym czasie, pojawiają się konflikty scalania, a rozdzielczość zawsze wymaga ręcznego wklejenia kopii, aby uwzględnić linie z obu wersji.

Czy istnieje wskazówka, sztuczka lub metodologia, które spowodują ból związany z tym procesem?

Na przykład uproszczonym rozwiązaniem byłoby poinformowanie twórców o dodaniu nowych linii w przypadkowych miejscach w środku pliku. To prawdopodobnie zadziała, ale wymaga świadomego wysiłku i dziwnie wyglądającej historii.


22
2017-08-07 07:23


pochodzenie


Właśnie dodano demo, aby zilustrować taki sterownik scalający - VonC


Odpowiedzi:


Możesz użyć gitattributes mechanizm definiowania a niestandardowy sterownik scalający (lubić ten na przykład) w celu automatycznego skopiowania odpowiednich sekcji.

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

Będzie to łączenie w 3 kierunkach, co oznacza, że ​​możesz łatwo go zmienić %A i %B przeciwko %O (wspólny przodek) w celu odizolowania wspomnianych nowych sekcji i zagregowania ich w wynikowym pliku scalonym.

Ten sterownik scalania musi tylko wykonać:

comm -13 $1 $3 >> $2

(Narzędzie komunikacyjne jest częścią GoW - Gnu w systemie Windows - dystrybucja, jeśli jesteś w systemie Windows)


Oto mała wersja demonstracyjna:

Najpierw skonfigurujmy repozytorium Git z plikiem zmodyfikowanym w dwóch oddziałach ("master' i '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(+)

Teraz tworzymy nową gałąź i dokonujemy jednoczesnych modyfikacji w obu wersjach tego pliku, na końcu tak jak w OP itadok określa w pytaniu.

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"

Mamy dwie gałęzie (uwaga: git lg to mój alias)

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

Teraz spróbujmy scalić:

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

... Nieudane jak w reklamie;) A git merge --abort zresetuje sytuację.

Ustawmy nasz sterownik scalający:

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

W tym momencie scalanie nadal nie działa:

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

Normalny: musimy napisać ten skrypt i dodać go do 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

I teraz, aggregate sterownik scalający może działać:

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"

Proszę bardzo: koniec test.txt plik z abranch został dodany do pliku na master.


15
2017-08-07 07:49



Uwaga: jak wspomniano w stackoverflow.com/questions/8839496/..., skrypt sterownika scalania i .gitattributes plik może być wersjonowany, ale konfiguracja deklarująca, że ​​sterownik scalania musi być zadeklarowany przez każdego użytkownika klonującego to repo. - VonC
Wow ... robisz coś więcej. Dzięki! - itsadok
teraz, jeśli jest jakikolwiek sposób, aby powiedzieć o tym githubowi, byłoby to bezcenne - akostadinov


Innym rozwiązaniem jest użycie --union opcja polecenia instalacyjnego git-merge-file.

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

Jest to bardziej niezawodne niż użycie comm: comm może generować złe wyjście, gdy linie wejściowe nie są posortowane zgodnie z LC_COLLATE.


4
2017-12-29 18:48



Myślę, że możesz uzyskać to samo bez dodawania niestandardowej strategii scalania i justowania myfile merge=union wewnątrz .gitattributes  about.gitlab.com/2015/02/10/... - nulll


Jeśli nie chcesz ponownie dodawać linii, które zostały usunięte, musisz użyć nieco bardziej skomplikowanej inkantacji comm:

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

Najpierw wybierasz wszystkie linie wspólne dla wersji lokalnej i zdalnej; to upewnia się, że wszystkie usunięte linie pozostają usunięte. Następnie dodajesz linie dodane przez wersję zdalną, a następnie te dodane przez wersję lokalną.

Może to być strategia, w której można zainstalować .gitconfig za pomocą pojedynczego polecenia:

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

1
2017-07-11 14:31