Dlaczego znaczenie „nasze” i „ich” jest odwrócone za pomocą git-svn

91

Używam git-svn i zauważyłem, że kiedy muszę naprawić konflikt scalania po wykonaniu a git svn rebase, znaczenie opcji --oursi --theirsnp. git checkoutJest odwrócone. To znaczy, jeśli istnieje konflikt i chcę zachować wersję, która pochodzi z serwera SVN i wyrzucić zmiany, które wprowadziłem lokalnie, muszę użyć ours, kiedy bym się tego spodziewał theirs.

Dlaczego?

Przykład:

mkdir test
cd test
svnadmin create svnrepo
svn co file://$PWD/svnrepo svnwc
cd svnwc
echo foo > test.txt
svn add test.txt
svn ci -m 'svn commit 1'
cd ..
git svn clone file://$PWD/svnrepo gitwc
cd svnwc
echo bar > test.txt 
svn ci -m 'svn commit 2'
cd ..
cd gitwc
echo baz > test.txt 
git commit -a -m 'git commit 1'
git svn rebase

git checkout --ours test.txt
cat test.txt 
# shows "bar" but I expect "baz"

git checkout --theirs test.txt
cat test.txt 
# shows "baz" but I expect "bar"
Marc Liyanage
źródło
Jut zaktualizował moją odpowiedź wieloma diagramami, aby lepiej zilustrować „naszą” i „ich” stronę.
VonC
1
Zobacz także github.com/git/git/commit/…
VonC

Odpowiedzi:

232

Wydaje się to zgodne z tym, co robi rebase.

  • git svn rebase pobierze wersje z rodzica SVN bieżącego HEAD i przebuduje bieżącą (niezatwierdzoną do SVN) pracę przeciwko niemu.

  • git rebasewspomina:
    Zauważ, że scalanie rebase działa poprzez ponowne odtworzenie każdego zatwierdzenia z gałęzi roboczej na wierzchu <upstream>gałęzi.
    Z tego powodu, gdy dochodzi do konfliktu scalania:

    • stroną zgłoszoną jako nasza jest dotychczas zmieniona seria, zaczynająca się od<upstream> ,
    • a ich gałąź robocza .
      Innymi słowy, strony są zamieniane .

git rebase odtwarza każde zatwierdzenie z gałęzi roboczej na górze <upstream>gałęzi.

Jeśli pogodzisz obie definicje:

  • zatwierdzenia pochodzące z SVN są tymi, na których są odtwarzane lokalne zatwierdzenia Gita. Są częścią „dotychczas zmienionej serii” i są określane jako „nasze” (w Twoim przypadku jest to test.txtplik z barzawartością)
  • gałąź robocza (zawierająca zatwierdzenia Gita nieznane w SVN, w twoim przypadku test.txtplik z bazzawartością) jest "ich" i każdy z tych lokalnych zatwierdzeń Git jest odtwarzany.

Innymi słowy, SVN czy nie:

  • <upstream>gałąź " " (na której wszystko jest odtwarzane i która jest częścią dotychczas zmienionych zatwierdzeń ") jest" nasza ".
  • to, co jest odtwarzane (gałąź robocza), jest „ ich ”.

Dobra wskazówka mnemoniczna od CommaToast :

wszystko, na co wskazuje HEAD, jest „nasze”

(i pierwszą rzeczą, jaką git rebase upstreamrobi to w celu pobrania upstreamgałęzi, na której chcesz zmienić bazę: HEAD odnosi się do upstream- oursteraz.)


Zamieszanie prawdopodobnie wynika z roli działającej gałęzi w klasyku git merge.
Podczas łączenia:

  • „gałąź robocza” to ta zawierająca to, co jest „do tej pory scalone” i jest uważane za „naszą”,
  • podczas gdy inne zatwierdzenie reprezentuje to, co jest - nie jest odtwarzane, ale - łączy się z gałęzią roboczą i jest uważane za „ich”.

Jak wspomniano na git rebasestronie podręcznika , scalanie podczas rebase oznacza zamianę strony.


Innym sposobem na powiedzenie tego samego jest rozważenie, że:

  • to co mamy na wypisanej gałęzi jest „ nasze ”,
  • to, co mieliśmy (i jest scalane lub odtwarzane), jest „ ich ”.

Przy scalaniu :

x--x--x--x--x(*) <- current branch B ('*'=HEAD)
    \
     \
      \--y--y--y <- other branch to merge

, nie zmieniamy bieżącej gałęzi `` B '', więc nadal mamy to, nad czym pracowaliśmy (i łączymy się z inną gałęzią)

x--x--x--x--x---------o(*)  MERGE, still on branch B
    \       ^        /
     \     ours     /
      \            /
       --y--y--y--/  
               ^
              their

Ale w przypadku rebase zmieniamy stronę, ponieważ pierwszą rzeczą, którą robi rebase, jest pobranie gałęzi upstream! (aby odtworzyć aktualne zatwierdzenia na górze)

x--x--x--x--x(*) <- current branch B
    \
     \
      \--y--y--y <- upstream branch

A git rebase upstreamnajpierw zmieni HEADB na gałąź upstream HEAD(stąd zamiana „nasz” i „ich” w porównaniu do poprzedniej „bieżącej” gałęzi roboczej).

x--x--x--x--x <- former "current" branch, new "theirs"
    \
     \
      \--y--y--y(*) <- upstream branch with B reset on it,  
                       new "ours", to replay x's on it

, a następnie rebase odtworzy „swoje” zatwierdzenia w nowej „naszej” gałęzi B:

x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
    \
     \
      \--y--y--y--x'--x'--x'(*) <-  branch B with HEAD updated ("ours")
               ^
               |
        upstream branch

Jedynym dodatkowym krokiem git svn rebasejest to, że „pobieranie” svn jest wykonywane najpierw w zdalnej gałęzi Git reprezentującej zatwierdzenia SVN.
Masz początkowo:

x--x--x--x--x(*) <- current branch B, "ours" for now.
    \                                   
     \
      \--y--y--y <- SVN tracking branch, "theirs for now"

, najpierw aktualizujesz gałąź śledzenia SVN o nowe zatwierdzenia pochodzące z SVN

x--x--x--x--x(*) <- current branch B, still "ours", not for long
    \                                   
     \
      \--y--y--y--y'--y' <- SVN tracking branch updated

, następnie przełączasz bieżącą gałąź na stronę SVN (która staje się „naszą”)

x--x--x--x--x <- for "B", now "their" during the rebase
    \                                   
     \
      \--y--y--y--y'--y'(*) <- SVN tracking branch updated, and branch B: 
                               now "ours" (this is "what we now have")

, przed odtworzeniem zatwierdzeń, nad którymi pracowałeś (ale które są teraz „ich” podczas rebase)

x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
    \
     \
      \--y--y--y--y'--y'--x'--x'--x'(*) <-  branch B with HEAD updated ("ours")
                      ^
                      |
        upstream SVN tracking branch
VonC
źródło
9
Wow, co za świetna odpowiedź, dzięki! Musiałem kompletnie przeoczyć tę uwagę na git rebasestronie podręcznika ...
Marc Liyanage
@epologee: nie ma za co. Jest to również przydatne, gdy używasz tylko git, aby zrozumieć, co się dzieje podczas rebase vs. merge. I dodaje do definicji upstream: stackoverflow.com/questions/2739376/ ...
VonC
5
Mój Boże!!! Jakie narkotyki brał Torvalds? To zbyt skomplikowane! Git to bardzo niebezpieczne narzędzie. Możesz łatwo zniszczyć całą swoją pracę, jeśli spróbujesz wykorzystać zewnętrzną wiedzę lub intuicję. Rozwój oprogramowania poszedł w dziurę robaka!
ATL_DEV
@ user148298 Nie ma nic złego w tej funkcji. Nie musisz wiedzieć tego wszystkiego, chyba że jesteś ekspertem od Git. A jeśli potrzebujesz zaawansowanych funkcji, będziesz musiał się ich najpierw nauczyć.
Earth Engine