Jak podzielić ostatni zatwierdzenie na dwa w Git

277

Mam dwie działające gałęzie, master i forum, i właśnie dokonałem pewnych modyfikacji w gałęzi forum , które chciałbym wybrać w master . Niestety, zatwierdzenie, które chcę wybrać, zawiera również pewne modyfikacje, których nie chcę.

Rozwiązaniem byłoby prawdopodobnie usunięcie niewłaściwego zatwierdzenia i zastąpienie go dwoma osobnymi zatwierdzeniami, jednym ze zmianami, które chcę wybrać w master, i innymi, które tam nie należą.

Próbowałem robić

git reset --hard HEAD^

który usunął wszystkie zmiany, więc musiałem wrócić

git reset ORIG_HEAD

Więc moje pytanie brzmi: jaki jest najlepszy sposób na podzielenie ostatniego zatwierdzenia na dwa osobne zatwierdzenia?

Jakub Arnold
źródło

Odpowiedzi:

332

Powinieneś użyć indeksu. Po wykonaniu mieszanego resetu („ git reset HEAD ^”) dodaj pierwszy zestaw zmian do indeksu, a następnie zatwierdź je. Następnie dokonaj reszty.

Możesz użyć „ git add ”, aby umieścić wszystkie zmiany dokonane w pliku w indeksie. Jeśli nie chcesz stopniować wszystkich modyfikacji dokonanych w pliku, tylko niektóre z nich, możesz użyć „git add -p”.

Zobaczmy przykład. Załóżmy, że miałem plik o nazwie mój_plik, który zawiera następujący tekst:

something
something else
something again

Zmodyfikowałem go w ostatnim zatwierdzeniu, aby teraz wyglądał tak:

1
something
something else
something again
2

Teraz decyduję, że chcę podzielić go na dwie części i chcę, aby wstawienie pierwszego wiersza było w pierwszym zatwierdzeniu, a wstawienie ostatniego wiersza w drugim zatwierdzeniu.

Najpierw wracam do nadrzędnego HEAD, ale chcę zachować zmiany w systemie plików, więc używam „git reset” bez argumentów (co spowoduje tak zwany „mieszany” reset):

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

Teraz używam „git add -p”, aby dodać zmiany, które chcę zatwierdzić do indeksu (= wykonuję je). „git add -p” to interaktywne narzędzie, które pyta cię, jakie zmiany w pliku powinny dodać do indeksu.

$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

Następnie dokonuję pierwszej zmiany:

$ git commit -m "Added first line"
[master cef3d4e] Added first line
 1 files changed, 1 insertions(+), 0 deletions(-)

Teraz mogę zatwierdzić wszystkie pozostałe zmiany (mianowicie cyfrę „2” wstawioną w ostatnim wierszu):

$ git commit -am "Added last line"
[master 5e284e6] Added last line
 1 files changed, 1 insertions(+), 0 deletions(-)

Sprawdźmy dziennik, aby zobaczyć, jakie mamy zobowiązania:

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...

    Added last line

Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
 something
 something else
 something again
+2

Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...

    Added first line

Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
hcs42
źródło
1
Przez ostatnie półtora tygodnia powoli przyzwyczajałem się do korzystania z Mercurial i istnieje przydatne polecenie skrótu git reset [--patch|-p] <commit>, którego możesz użyć, aby zaoszczędzić ci kłopotów z koniecznością git add -pzresetowania. Czy mam rację? Korzystanie z git 1.7.9.5.
trojjer
2
Oto trochę więcej na temat tej techniki, w tym zmiany wersji, jeśli był to starszy zatwierdzenie, lub musisz zmienić N zatwierdzeń na M zatwierdzeń: emmanuelbernard.com/blog/2014/04/14/… .
Chris Westin
84

Cele:

  • Chcę podzielić wcześniejszy commit ( splitme) na dwa.
  • Chcę zachować komunikat zatwierdzenia .

Plan:

  1. rebase interaktywny z jednego wcześniej splitme.
  2. edytuj splitme.
  3. Zresetuj pliki, aby podzielić je na drugi zatwierdzenie.
  4. Zmień zatwierdzenie, zachowując komunikat, zmodyfikuj w razie potrzeby.
  5. Dodaj z powrotem pliki rozdzielone od pierwszego zatwierdzenia.
  6. Zatwierdź nową wiadomość.
  7. Kontynuuj rebase.

Kroki rebase (1 i 7) można pominąć, jeśli splitmejest to ostatni zatwierdzenie.

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

Gdybym chciał, aby podzielone pliki zostały zatwierdzone jako pierwsze, ponownie dokonałbym zmiany bazy -i i zmieniłbym kolejność

git rebase -i splitme^
# swap order of splitme and 'just some files'
spazm
źródło
1
git reset HEAD^był brakującym elementem układanki. Działa również dobrze -p. Dzięki!
Marius Gedminas
10
Należy zwrócić uwagę na -- $filesargument git reset. Po przejściu ścieżek git resetprzywraca te pliki do stanu przywołanego zatwierdzenia, ale nie zmienia żadnych zatwierdzeń. Jeśli opuścisz ścieżki, wówczas „stracisz” zatwierdzenie, które chcesz zmienić w następnym kroku.
znaczniki pojedynków
2
Ta metoda zapobiega ponownemu skopiowaniu i wklejeniu pierwszej wiadomości zatwierdzenia w porównaniu z zaakceptowaną odpowiedzią.
Calvin
Ponadto: jeśli chcesz zresetować wszystkie pliki, po prostu użyj git reset HEAD^ -- .. Niezwykle zaskakujące, nie jest to dokładnie zachowanie git reset HEAD^.
allidoiswin
52

Aby zmienić bieżące zatwierdzenie na dwa zatwierdzenia, możesz wykonać następujące czynności.

Zarówno:

git reset --soft HEAD^

Cofa to ostatnie zatwierdzenie, ale pozostawia wszystko wyreżyserowane. Następnie możesz wycofać scenę z niektórych plików:

git reset -- file.file

Opcjonalnie przywróć części tych plików:

git add -p file.file

Dokonaj nowego pierwszego zatwierdzenia:

git commit

Etap i zatwierdzanie pozostałych zmian w drugim zatwierdzeniu:

git commit -a

Lub:

Cofnij i odetnij wszystkie zmiany od ostatniego zatwierdzenia:

git reset HEAD^

Selektywnie przeprowadzaj pierwszą rundę zmian:

git add -p

Popełnić:

git commit

Zatwierdź resztę zmian:

git commit -a

(W obu krokach, jeśli cofnąłeś zatwierdzenie, które dodało nowy plik i chcesz dodać to do drugiego zatwierdzenia, musisz ręcznie dodać go, ponieważ commit -atylko etapy zmiany w już śledzonych plikach).

CB Bailey
źródło
22

Uruchom git gui, wybierz przycisk „Zmień ostatnie zatwierdzenie” i odznacz scenę (Zatwierdź> Odstąp od zatwierdzenia lub Ctrl- U) zmiany, których nie chcesz wprowadzać w pierwszym zatwierdzeniu. Myślę, że to najłatwiejszy sposób, aby to zrobić.

Inną rzeczą, którą możesz zrobić, jest wybranie zmiany bez zatwierdzania ( git cherry-pick -n), a następnie ręcznie lub z git guiwybranymi żądanymi zmianami przed zatwierdzeniem.

Michael Krelin - haker
źródło
15
git reset HEAD^

--hard zabija twoje zmiany.

semanticart
źródło
13

Dziwię się, że nikt nie zasugerował git cherry-pick -n forum. Spowoduje to wprowadzenie zmian w stosunku do ostatniego forumzatwierdzenia, ale nie zatwierdzenie ich - możesz następnie resetusunąć niepotrzebne zmiany i zatwierdzić to, co chcesz zachować.

dahlbyk
źródło
3

Metoda podwójnego cofania-squasha

  1. Wykonaj kolejne zatwierdzenie, które usuwa niechciane zmiany. (Jeśli to za plik, to jest bardzo proste: git checkout HEAD~1 -- files with unwanted changesa git commit. Jeżeli nie, to pliki z mieszanymi zmianami mogą być częściowo wystawił git reset filei git add -p filejako etap pośredni.) Nazywają to powracają .
  2. git revert HEAD- Dokonaj kolejnego zatwierdzenia, które doda niepożądane zmiany. To jest podwójne cofnięcie
  3. Z 2 zatwierdzonych przez ciebie wcześniej zatwierdzeń zgniataj pierwszy do zatwierdzenia do split ( git rebase -i HEAD~3). To zatwierdzenie staje się teraz wolne od niepożądanych zmian, ponieważ są one w drugim zatwierdzeniu.

Korzyści

  • Zachowuje komunikat zatwierdzenia
  • Działa, nawet jeśli zatwierdzenie podziału nie jest ostatnim. Wymaga tylko, aby niepożądane zmiany nie kolidowały z późniejszymi zatwierdzeniami
użytkownik2394284
źródło
1

Ponieważ wybierasz wiśnie, możesz:

  1. cherry-pickz --no-commitdodaną opcją.
  2. reseti użyj add --patch, add --editlub po prostu addna scenie, co chcesz zachować.
  3. commit zainscenizowane zmiany.
    • Aby ponownie użyć oryginalnego komunikatu zatwierdzenia, możesz dodać polecenie --reuse-message=<old-commit-ref>lub --reedit-message=<old-commit-ref>opcje do commitpolecenia.
  4. Zniszcz niesceniczne zmiany za pomocą reset --hard.

Innym sposobem, zachowując lub edytując oryginalny komunikat zatwierdzenia:

  1. cherry-pick oryginalne zatwierdzenie jak zwykle.
  2. Odwróć zmiany, których nie chcesz, i zastosuj je, addaby wprowadzić odwrócenie.
    • Ten krok byłby łatwy, jeśli usuwasz to, co dodałeś, ale nieco trudny, jeśli dodajesz to, co usunąłeś lub cofasz zmianę.
  3. commit --amend aby dokonać odwrócenia zatwierdzonego wyboru.
    • Otrzymasz ponownie ten sam komunikat zatwierdzenia, który możesz zachować lub zmienić w razie potrzeby.
ADTC
źródło
0

To może być kolejne rozwiązanie przeznaczone dla przypadków, w których istnieje ogromne zatwierdzenie i niewielka ilość plików musi zostać przeniesiona do nowego zatwierdzenia. Działa to, jeśli zestaw<path> plików ma zostać wyodrębniony z ostatniego zatwierdzenia w HEAD i wszystkie przeniesione do nowego zatwierdzenia. Jeśli potrzeba wielu zatwierdzeń, można zastosować inne rozwiązania.

Najpierw wykonaj poprawki do obszarów etapowych i niestacjonarnych, które będą zawierać zmiany, aby przywrócić kod odpowiednio przed modyfikacją i po modyfikacji:

git reset HEAD^ <path>

$ git status
On branch <your-branch>
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   <path>

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   <path>

Aby zrozumieć, co się stanie (strzałka i komentarze nie są częścią polecenia):

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

Cofnij <path>zmiany w ostatnim zatwierdzeniu:

git commit --amend  -> reverts changes on HEAD by amending with staged changes

Utwórz nowy zatwierdzenie ze <path>zmianami:

git commit -a -m "New Commit" -> adds new commit with unstaged changes

Powoduje to utworzenie nowego zatwierdzenia zawierającego zmiany wyodrębnione z ostatniego zatwierdzenia.

Jose Cifuentes
źródło