Jak mogę podzielić zatwierdzenie Git zakopane w historii?

292

Ulepszyłem swoją historię i chcę wprowadzić do niej zmiany. Problem polega na tym, że mam zatwierdzenie z dwiema niepowiązanymi zmianami, a zatwierdzenie to jest otoczone innymi zmianami w mojej lokalnej (nieprzesuniętej) historii.

Chcę podzielić to zatwierdzenie, zanim go wypchnę, ale większość przewodników, które widzę, ma związek z podzieleniem ostatniego zatwierdzenia lub niezatwierdzonych zmian lokalnych. Czy jest to możliwe, aby zrobić to z zatwierdzeniem, które jest trochę zakopane w historii, bez konieczności „ponownego dokonywania” moich zobowiązań od tego czasu?

Ben
źródło

Odpowiedzi:

450

Na stronie podręcznika rebase znajduje się przewodnik do dzielenia zatwierdzeń . Szybkie podsumowanie to:

  • Wykonaj interaktywną zmianę bazy, w tym zatwierdzenie celu (np. git rebase -i <commit-to-split>^ branch) I zaznacz ją do edycji.

  • Kiedy rebase osiągnie to zatwierdzenie, użyj, git reset HEAD^aby zresetować do przed zatwierdzeniem, ale nie zmieniaj drzewa roboczego.

  • Stopniowo dodawaj zmiany i zatwierdzaj je, wykonując tyle zatwierdzeń, ile chcesz. add -pmoże być użyteczne dodanie tylko niektórych zmian w danym pliku. Posługiwać sięcommit -c ORIG_HEAD jeśli chcesz ponownie użyć oryginalnej wiadomości zatwierdzenia dla określonego zatwierdzenia.

  • Jeśli chcesz przetestować to, co popełniasz (dobry pomysł!), Użyj, git stashaby ukryć część, której nie popełniłeś (lub stash --keep-indexjeszcze zanim ją wykonasz), przetestuj, a następniegit stash pop przywróć resztę do drzewa roboczego. Wykonuj zatwierdzenia, dopóki nie wprowadzisz wszystkich modyfikacji, tzn. Nie będziesz mieć czystego drzewa roboczego.

  • Uruchom, git rebase --continueaby kontynuować stosowanie zatwierdzeń po podzielonym teraz zatwierdzeniu.

Cascabel
źródło
17
... ale nic z tego, jeśli już pchnąłeś historię od czasu zatwierdzenia podziału.
wilhelmtell
29
@wilhelmtell: Pominąłem mój zwykły „potencjalnie niebezpieczny; patrz„ odzyskiwanie z bazy wydobywczej ”, ponieważ PO wyraźnie powiedział, że nie opublikował tej historii.
Cascabel
2
i zrobiłeś perfekcyjną lekturę. Próbowałem ominąć „płytę kotłową”, kiedy podałem, że nie ma jeszcze wspólnej historii :) Pod każdym względem odniosłem sukces dzięki pańskiej sugestii. Jednak faktem jest fakt, że robienie tego jest dużym problemem. Nauczyłem się tutaj lekcji, aby upewnić się, że zatwierdzenia zostały poprawnie wprowadzone na początek!
Ben
2
Pierwszy krok można lepiej określić jako git rebase -i <sha1_of_the_commit_to_split>^ branch. I git guijest dobrym narzędziem do zadania dzielenia, którego można użyć do dodania różnych części pliku do różnych zatwierdzeń.
Qiang Xu,
3
@QiangXu: Pierwsza jest rozsądną sugestią. Drugi jest właśnie powodem, dla którego zasugerowałem git add -p, który może zrobić więcej niż git guimoże w tym dziale (zwłaszcza edytowanie kawałków, ustawianie wszystkiego, zaczynając od bieżącego kawałka i szukanie kawałków według wyrażenia regularnego).
Cascabel,
3

Oto jak to zrobić za pomocą Magit .

Powiedzmy, że ed417ae jest tym, który chcesz zmienić; zawiera dwie niepowiązane zmiany i jest pochowany pod co najmniej jednym zatwierdzeniem. Naciśnij, llaby wyświetlić dziennik i przejdź do ed417ae:

początkowy dziennik

Następnie naciśnij, raby otworzyć okienko rebase

wyskakujące okienko

i mzmodyfikować zatwierdzenie w punkcie.

Zauważ, @że teraz jest zatwierdzenie, które chcesz podzielić - oznacza to, że HEAD jest teraz przy tym zatwierdzeniu:

modyfikowanie zatwierdzenia

Chcemy przenieść HEAD do nadrzędnego, więc przejdź do nadrzędnego (47e18b3) i naciśnij x( magit-reset-quickly, związany, ojeśli używasz evil-magit) i wejdź, aby powiedzieć „tak, miałem na myśli zatwierdzenie w punkcie”. Twój dziennik powinien teraz wyglądać następująco:

zaloguj się po zresetowaniu

Teraz wciśnij, qaby przejść do normalnego statusu Magit, a następnie użyj zwykłego upolecenia unstage, aby cofnąć scenę, co nie idzie w pierwszym zatwierdzeniu, cresztę zatwierdzić jak zwykle, a następnie tage si cpominąć to, co wchodzi w drugie zatwierdzenie, a po zakończeniu: naciśnij, raby otworzyć wyskakujące okienko

wyskakujące okienko

i kolejny, raby kontynuować, i gotowe! llteraz pokazuje:

wszystko gotowe log

młot
źródło
1

Aby podzielić zatwierdzenie <commit>i dodać nowe zatwierdzenie przed tym , i zapisać datę autora <commit>, - kroki są następujące:

  1. Edytuj zatwierdzenie wcześniej <commit>

    git rebase -i <commit>^^
    

    Uwaga: być może będzie również konieczna edycja <commit>.

  2. Cherry pick <commit>do indeksu

    git cherry-pick -n <commit>
    
  3. Interaktywnie resetuj niepotrzebne zmiany z indeksu i resetuj działające drzewo

    git reset -p && git checkout-index -f -a
    

    Alternatywnie, po prostu interaktywnie przechowuj niepotrzebne zmiany: git stash push -p -m "tmp other changes"

  4. Wprowadź inne zmiany (jeśli występują) i utwórz nowy zatwierdzenie

    git commit -m "upd something" .
    

    Opcjonalnie powtórz pozycje 2-4, aby dodać więcej pośrednich zatwierdzeń.

  5. Kontynuuj bazowanie

    git rebase --continue
    
ruvim
źródło
0

Istnieje szybsza wersja, jeśli chcesz wyodrębnić zawartość tylko z jednego pliku. Jest szybszy, ponieważ interaktywny rebase nie jest już tak naprawdę interaktywny (i oczywiście jest jeszcze szybszy, jeśli chcesz wyodrębnić z ostatniego zatwierdzenia, to nie musisz wcale go rebase)

  1. Użyj edytora i usuń wiersze, z których chcesz wyodrębnić the_file. Close the_file. To jedyne wydanie, którego potrzebujesz, cała reszta to tylko polecenia git.
  2. Etap usuwania z indeksu:

    git  add  the_file
    
  3. Przywróć właśnie usunięte linie do pliku bez wpływu na indeks !

    git show HEAD:./the_file > the_file
    
  4. „SHA1” to zatwierdzenie, z którego chcesz wyodrębnić linie:

    git commit -m 'fixup! SHA1' 
    
  5. Utwórz drugi, zupełnie nowy zatwierdzenie z zawartością do wyodrębnienia przywróconą przez krok 3:

    git commit -m 'second and new commit' the_file 
    
  6. Nie edytuj, nie zatrzymuj / kontynuuj - akceptuj wszystko:

    git rebase --autosquash -i SHA1~1
    

Oczywiście jeszcze szybciej, gdy zatwierdzenie do wyodrębnienia jest ostatnim zatwierdzeniem:

4. git commit -C HEAD --amend
5. git commit -m 'second and new commit' thefile
6. no rebase, nothing

Jeśli używasz, magitwówczas kroki 4, 5 i 6 są pojedynczym działaniem: Zatwierdź, natychmiastowa naprawa

Marsz
źródło
-2

Jeśli jeszcze nie naciskałeś, po prostu użyj git rebase. Co więcej, użyj git rebase -ido interaktywnego przemieszczania zatwierdzeń. Możesz przesunąć naruszający zatwierdzenie na przód, a następnie podzielić go według własnego uznania i przesunąć łatki do tyłu (w razie potrzeby).

Gintautas Miliauskas
źródło
14
Nigdzie nie trzeba go przenosić. Podziel go tam, gdzie jest.
Cascabel
1
Niestety, to nie działa dla mnie, ponieważ część historii po zatwierdzeniu jest od tego zależna, więc jestem trochę ograniczony. Byłby to jednak mój pierwszy wybór.
Ben
@Ben: to jest w porządku - późniejsze zatwierdzenia nie będą musiały się w ogóle zmieniać (zakładając, że zachowasz wszystkie zmiany, zamiast wyrzucić niektóre z nich). Więcej informacji tutaj - stackoverflow.com/questions/1440050/…
Ether