Zmiana kolejności zatwierdzeń

105

Obecnie pracuję nad gałęzią i chcę, aby niektóre zmiany zostały połączone w inne gałęzie:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(Litery oznaczają zatwierdzenia, a „x” to nieistotne zatwierdzenia).

Jednak zauważyłem, że dobrym pomysłem byłoby zebranie kilku zatwierdzeń. Chcę „połączyć” zatwierdzenie a, d, e i g w jednej łatce i zatwierdzić ją jako master. Zatwierdzenia b i f powinny przejść jako jedno zatwierdzenie do gałęziB. Czy jest dobry sposób na osiągnięcie tego celu?

Piotr
źródło
4
Dwie już podane odpowiedzi wspominają o poprawnym poleceniu, ale myślę, że przy tak podstawowym zadaniu jak to, warto mieć tutaj prawdziwe instrukcje na StackOverflow, więc opublikowałem kilka. (Jestem zaskoczony, że nie mogę znaleźć dobrego poprzedniego pytania do linku.)
Cascabel

Odpowiedzi:

126

Polecenie, którego szukasz, jest git rebasekonkretną -i/--interactiveopcją.

Zakładam, że chcesz zostawić zatwierdzenie c w gałęzi A i że naprawdę masz na myśli, że chcesz przenieść pozostałe zatwierdzenia do innych gałęzi, zamiast scalać, ponieważ scalenia są proste. Zacznijmy od manipulowania gałęzią A.

git rebase -i <SHA1 of commit a>^ branchA

Te ^środki poprzedni popełnienia, więc to polecenie mówi zmieniają bazę oddział używając popełnić przed „a” jako podstawy. Git przedstawi listę zatwierdzeń z tego zakresu. Zmień ich kolejność i powiedz gitowi, aby zgasił odpowiednie:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

Teraz historia powinna wyglądać tak:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

Teraz weźmy nowo zgniecione zatwierdzenie b + f dla branchB.

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

I to samo dla a + d + e + g dla mistrza:

git checkout master
git cherry-pick branchA^

Na koniec zaktualizuj gałąźA, aby wskazywała na c:

git branch -f branchA branchA^^

Powinniśmy teraz mieć:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)

Zauważ, że jeśli masz wiele zatwierdzeń, które chcesz przenosić między gałęziami, możesz ponownie użyć rebase (nieinteraktywnie):

# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

Na koniec zastrzeżenie: całkiem możliwe jest zmienić kolejność zatwierdzeń w taki sposób, że niektóre nie mają już czystego zastosowania. Może to być spowodowane tym, że wybrałeś złą kolejność (umieszczając łatkę przed zatwierdzeniem wprowadzającą poprawioną funkcję); w takim przypadku będziesz chciał przerwać rebase ( git rebase --abort). W przeciwnym razie będziesz musiał inteligentnie rozwiązać konflikty (tak jak w przypadku konfliktów scalania), dodać poprawki, a następnie biec, git rebase --continueaby przejść dalej. Instrukcje te są również zawarte w komunikacie o błędzie wyświetlanym w przypadku wystąpienia konfliktu.

Cascabel
źródło
czy diagram nie jest po git branch -f branchA branchA^^złym? Mianowicie, czy gałąźA nie powinna wskazywać c w tym miejscu, tak aby pierwsza linia diagramu była, c (branchA)a nie c - [a+d+e+g] - [b+f] (branchA)?
ntc2,
@enoksrd: Tak, wystąpił błąd, ale Twoja edycja zawierała błędy. Naprawię to, kiedy będę miał okazję tutaj.
Cascabel
Zamiast robić, git branch -f branchA branchA^^dlaczego nie możesz po prostu zrobić git reset --hard <sha1 of c>na gałęzi A?
Michaił Wasiljew
17
git rebase -i HEAD~3

Gdzie 3jest liczba zatwierdzeń, które wymagają zmiany kolejności ( źródło ).

Otworzy się vi , wypisując zmiany od najstarszych (na górze) do najnowszych (na dole).
Teraz zmień kolejność linii, zapisz i wyjdź z edytora.

ddpprzeniesie bieżącą linię w dół
ddkP przeniesie bieżącą linię w górę ( źródło )

Nelu
źródło
9

git rebase --interactive to polecenie, którego chcesz.

Przykład:

Obecny stan jest następujący:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

Chcę zmienić kolejność zatwierdzeń 9a24b81(trzecie zatwierdzenie) i 7bdfb68(drugie zatwierdzenie). Aby to zrobić, najpierw znajduję zatwierdzenie przed pierwszym zatwierdzeniem, które chcemy zmienić . To jest zatwierdzenie 186d1e0(pierwsze zatwierdzenie).

Polecenie do wykonania to git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGEw tym przypadku:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

Spowoduje to otwarcie pliku w domyślnym edytorze o następującej zawartości:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

Zauważ, że kolejność zatwierdzeń w pliku jest odwrotna do tej używanej w git log. W dzienniku git najnowsze zatwierdzenie znajduje się na górze. W tym pliku najnowsze zatwierdzenie znajduje się na dole.

Jak wyjaśnia komentarz na dole pliku, mogę robić różne rzeczy, na przykład zgniatać, upuszczać i zmieniać kolejność zatwierdzeń. Aby zmienić kolejność zatwierdzeń, edytuję plik tak, aby wyglądał tak (dolne komentarze nie są wyświetlane):

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

pickPolecenia na początku każdego „użytkowania (tj obejmuje) to commit” oznacza liniowe i kiedy zapisać plik temp z poleceniami rebase i wyjść z edytora, git wykona polecenia i aktualizacji repozytorium i katalog roboczy:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

Zwróć uwagę na przepisaną historię zatwierdzeń.

Spinki do mankietów:

codeape
źródło
1
Dodaj odpowiednie szczegóły z linku, aby udzielić pełnej i samodzielnej odpowiedzi. SO marszczy brwi na temat „odpowiedzi zawierających tylko łącze”. W szczególności, jak można wykorzystać, git rebase --interactiveaby osiągnąć cel PO?
SherylHohman
Drugi link teraz nie istnieje.
devsaw
Dodano treść, aby odpowiedź była kompletna i samodzielna. Usunięto drugi link.
codeape
-1

git rebase jest tym, czego chcesz. Sprawdź opcję --interactive.


źródło
4
Dodaj odpowiednie szczegóły z linku, aby udzielić pełnej i samodzielnej odpowiedzi. SO marszczy brwi na temat „odpowiedzi zawierających tylko łącze”.
SherylHohman