Pytanie Jak zmodyfikować określone zatwierdzenie w git?


Zwykle przesyłam listę zatwierdzeń do sprawdzenia. Jeśli mam:

  • HEAD 
  • Commit3 
  • Commit2 
  • Commit1

Wiem, że mogę zmodyfikować head commit z git commit --amend, ale jak mogę to zmienić Commit1, ponieważ nie jest to HEAD popełnić?


1704
2017-07-27 05:19


pochodzenie


Zobacz alternatywną odpowiedź tutaj: stackoverflow.com/a/18150592/520567 Twoja zaakceptowana odpowiedź jest naprawdę dokładną odpowiedzią na twoje pytanie, ale jeśli masz gotowy nowy commit, zanim zdecydujesz się użyć edycji, ta odpowiedź będzie prostsza. Może również działać z wieloma zatwierdzeniami, które chcesz połączyć / połączyć ze starszym. - akostadinov
Również możesz po prostu zobaczyć Dzielenie zatwierdzenia w Narzędzia Git - Przepisywanie historii po więcej informacji. - hakre
Możliwy duplikat Jak modyfikować istniejące, nieusunięte zatwierdzenia? - tkruse


Odpowiedzi:


Możesz użyć git rebase, na przykład, jeśli chcesz zmodyfikować z powrotem, aby zatwierdzić bbc643cd, biegać

$ git rebase --interactive 'bbc643cd^'

W edytorze domyślnym zmodyfikuj pick do edit w wierszu, którego zatwierdzenie chcesz zmodyfikować. Dokonaj zmian, a następnie zatwierdź je z tą samą wiadomością, co wcześniej:

$ git commit --all --amend --no-edit

zmodyfikować zatwierdzenie, a po nim

$ git rebase --continue

aby powrócić do poprzedniego zatwierdzenia head.

OSTRZEŻENIE: Zauważ, że to zmieni SHA-1 tego zatwierdzenia jak również wszystkie dzieci - innymi słowy, to przepisuje historię od tego momentu. Możesz przerwać repo robiąc to jeśli naciskasz za pomocą polecenia git push --force


2240
2017-07-27 05:28



Inną ciekawą opcją w tym przepływie jest przeniesienie się do zatwierdzenia, które chcesz zmodyfikować, zamiast modyfikowania plików i ustawiania nad zatwierdzeniem na górze (edytowanym), możesz podzielić to zatwierdzenie na dwa różne zatwierdzenia (lub nawet więcej). W takim przypadku cofnij się do zatwierdzenia edycji i uruchom "git reset HEAD ^". które wprowadzą zmodyfikowane pliki tego zatwierdzenia na scenę. Teraz wybierz i zatwierdz dowolne pliki, jak chcesz. Ten przepływ jest dość dobrze wyjaśniony na stronie podręcznika "git-rebase". Zobacz rozdział "Podział obowiązków". bit.ly/d50w1M - Diego Pino
W Git 1.6.6 i nowszych możesz użyć reword akcja w git rebase -i zamiast edit (automatycznie otwiera edytor i kontynuuje z pozostałymi etapami reorganizacji, co pozwala uniknąć użycia git commit --ammend i git rebase --continue gdy potrzebujesz tylko zmienić komunikat zatwierdzenia, a nie treść). - Chris Johnsen
Po uruchomieniu 'git rebase hash ^ --interactive', a następnie zaznaczenie edit na commit, 'git commit --amend' pokazuje komunikat zatwierdzenia - nie rzeczywisty kod. Jak mogę zmienić kod, który został zatwierdzony? Dzięki! - mikemaccana
Warto zauważyć, że być może trzeba będzie uruchomić git stash przed git rebase i git stash pop potem, jeśli masz oczekujące zmiany. - user123444555621
Zauważ, że w przypadku nowszego gita, mądrzej byłoby postępować zgodnie z instrukcjami natychmiastowymi, zamiast ślepo używać git commit --all --amend --no-edit tutaj. Wszystko, co musiałem zrobić później git rebase -i ... było git commit --amend zwykle wtedy git rebase --continue. - Eric Chen


Skorzystaj z niesamowitego, interaktywnego rebase:

git rebase -i @~9   # Show the last 9 commits in a text editor

Znajdź zatwierdzenie, które chcesz zmienić pick do e (edit) i zapisz i zamknij plik. Git przewinie do tego zatwierdzenia, umożliwiając:

  • posługiwać się git commit --amend wprowadzić zmiany, lub
  • posługiwać się git reset @~ aby odrzucić ostatnie zatwierdzenie, ale nie zmiany w plikach (tj. zabrać cię do punktu, w którym byłeś, gdy edytowałeś pliki, ale jeszcze się nie zaangażowałeś).

Ten drugi jest przydatny do robienia bardziej złożonych rzeczy, takich jak dzielenie na wielokrotne zatwierdzenia.

Następnie uruchomić git rebase --continue, a Git będzie odtwarzał kolejne zmiany na górze twojego zmodyfikowanego zatwierdzenia. Możesz zostać poproszony o naprawienie niektórych konfliktów scalania.

Uwaga: @ jest skrótem dla HEAD, i ~ to zatwierdzenie przed określonym zatwierdzeniem.

Czytaj więcej na temat przepisywanie historii w dokumentach Git.


Nie bój się odnawiać

ProTip: Nie bój się eksperymentować z "niebezpiecznymi" poleceniami, które przepisują historię * - Git domyślnie nie usuwa twoich zatwierdzeń przez 90 dni; możesz je znaleźć w reflogie:

$ git reset @~3   # go back 3 commits
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~3
2c52489 HEAD@{1}: commit: more changes
4a5246d HEAD@{2}: commit: make important changes
e8571e4 HEAD@{3}: commit: make some changes
... earlier commits ...
$ git reset 2c52489
... and you're back where you started

* Uważaj na takie opcje, jak --hard i --force choć - mogą odrzucić dane.
* Nie przepisuj historii w żadnych gałęziach, z którymi współpracujesz.



W wielu systemach git rebase -i otworzy domyślnie Vima. Vim nie działa tak, jak większość współczesnych edytorów tekstu, więc spójrz na to jak zmienić bazę za pomocą Vima. Jeśli wolisz używać innego edytora, zmień go na git config --global core.editor your-favorite-text-editor.


304
2018-04-29 17:50



Środek twojej odpowiedzi to dziwne miejsce, w którym mogę opisać to, co mogę opisać jako reklamę miniture dla VIM. Nie ma to znaczenia dla pytania i po prostu zaśmieca twoją odpowiedź. - Intentss
@Intentss: Ah, rozumiem, dlaczego to wyglądało dziwnie. Rozumowanie było takie, że Vim jest domyślnym edytorem tekstowym w wielu systemach, więc wiele osób doświadcza interaktywnego przekierowania to ekran, w którym wpisywanie sprawia, że ​​kursor przemieszcza się w każdym miejscu. Następnie przełączają swój edytor na coś innego, a ich drugie doświadczenie z interaktywnym przesyłaniem jest dość normalne, ale pozostawiają ich, zastanawiając się, dlaczego używa pliku tekstowego zamiast GUI. Aby uzyskać przepływ z rebase, potrzebujesz czegoś takiego jak Vim lub tryb rebase Emacsa. - Zaz
Gdybym musiał użyć czegoś podobnego do Gedit lub nano do interaktywnego rebase, odrzuciłbym o wiele mniej. Może to nie byłoby takie złe, ponieważ jestem trochę uzależniony od rebase. - Zaz
W porządku. Ponieważ wielu ludzi uważa tę część za nieistotną, skondensowałem ją do 3 wierszy i wyjaśniłem, jak zmienić edytor, jeśli zajdzie taka potrzeba. - Zaz
Niesamowite! Nie wiedziałem, że możesz użyć @ jako skrót dla HEAD. Dzięki za zamieszczenie tego. - James Ko


Interaktywny rebase z --autosquash jest czymś, z czego często korzystam, kiedy muszę poprawić poprzednie poprawki głębiej w historii. Zasadniczo przyspiesza proces, który ilustruje odpowiedź ZelluX, i jest szczególnie przydatny, gdy masz więcej niż jedno zatwierdzenie, które musisz edytować.

Z dokumentacji:

--autosquash

Kiedy wiadomość z poleceniem zaczyna się od "squash! ..." (lub "fixup! ..."), a tam jest commit, którego tytuł zaczyna się od tego samego ..., automatycznie modyfikuj listę todo rebase -i tak, aby commit oznaczony jako "squashing" pojawia się zaraz po zatwierdzeniu do modyfikacji

Załóżmy, że masz historię, która wygląda następująco:

$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1

i masz zmiany, które chcesz zmienić w Commit2, a następnie zatwierdzić zmiany za pomocą

$ git commit -m "fixup! Commit2"

alternatywnie możesz użyć commit-sha zamiast komunikatu commit, więc "fixup! e8adec4 lub nawet tylko prefiks komunikatu zatwierdzenia.

Następnie zainicjuj interaktywny rebase na commit przed

$ git rebase e8adec4^ -i --autosquash

twój edytor otworzy się z zatwierdzonymi już zamówieniami

pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3

wszystko, co musisz zrobić, to zapisać i wyjść


57
2017-09-29 17:59



Możesz także użyć git commit --fixup=@~ zamiast git commit -m "fixup! Commit2". Jest to szczególnie przydatne, gdy wiadomości zatwierdzenia są dłuższe, a napisanie całego tekstu byłoby kłopotliwe. - Zaz


Biegać:

$ git rebase --interactive commit_hash^

każdy ^ wskazuje liczbę cofnięć, które chcesz edytować, jeśli jest tylko jedna (podana wartość skrótu zatwierdzenia), po prostu dodaj jedną ^.

Używając Vima, zmieniasz słowa pick do reword dla zatwierdzeń, które chcesz zmienić, zapisać i zamknąć (:wq). Następnie git poprosi cię o każde zatwierdzenie, które zaznaczyłeś jako reword, abyś mógł zmienić komunikat zatwierdzenia.

Każda wiadomość zatwierdzenia, którą musisz zapisać i zamknąć (:wq), aby przejść do następnego komunikatu zatwierdzenia

Jeśli chcesz wyjść bez wprowadzania zmian, naciśnij :q!

EDYTOWAĆ: aby nawigować w vim używasz j iść do góry, k zejść, h iść w lewo, i l iść w prawo (wszystko to w NORMAL tryb, naciśnij ESC iść do NORMAL tryb). Aby edytować tekst, naciśnij i abyś wszedł do INSERT tryb, w którym wstawiasz tekst. naciśnij ESC Wrócić do NORMAL tryb :)

AKTUALIZACJA: Oto świetny link z listy github Jak cofnąć (prawie) wszystko za pomocą git 


30
2017-07-02 19:11



Idealne dla mnie. Warte wspomnienia git push --force? - u01jmg3
Co git push --force to jest nadpisywane, a piloty są zatwierdzane lokalnymi zobowiązaniami. Tak nie jest w tym temacie :) - betoharres
@BetuUuUu oczywiście, jeśli twoje zatwierdzenia są wypychane na odległość i masz zmodyfikowany komunikat zatwierdzający lokalnie, chciałbyś wymusić push na zdalny, czyż nie? - Sudip Bhandari
@SudipBhandari To uczucie, które otrzymuję. Nie wymuszałem, a teraz mam dodatkowy oddział, odzwierciedlający wszystkie zobowiązania z powrotem do tego, którego wiadomość zmieniłem, co jest super-brzydkie. - ruffin
Interaktywny rebase może wydawać się trudny na początku. Napisałem post (ze zdjęciami), który przedstawia go szczegółowo, krok po kroku: blog.tratif.com/2018/04/19/the-power-of-git-interactive-rebase - Tomasz Kaczmarzyk


Jeśli z jakiegoś powodu nie lubisz interaktywnych edytorów, możesz użyć git rebase --onto.

Powiedz, że chcesz zmodyfikować Commit1. Najpierw gałąź od przed  Commit1:

git checkout -b amending [commit before Commit1]

Po drugie, chwyć Commit1 z cherry-pick:

git cherry-pick Commit1

Teraz zmień swoje zmiany, tworząc Commit1':

git add ...
git commit --amend -m "new message for Commit1"

I wreszcie, po usunięciu jakichkolwiek innych zmian, przeszczep resztę swoich zatwierdzeń do master na wierzchu nowe zatwierdzenie:

git rebase --onto amending Commit1 master

Przeczytaj: "rebase, na oddział amending, wszystkie zatwierdzenia pomiędzy Commit1 (nie obejmuje) i master (włącznie) "To znaczy Commit2 i Commit3, całkowicie odcinając stare Commit 1. Możesz je po prostu wybrać, ale ten sposób jest łatwiejszy.

Pamiętaj, aby oczyścić swoje oddziały!

git branch -d amending

12
2017-10-22 12:19



możesz użyć git checkout -b amending Commit1~1 aby uzyskać wcześniejsze zatwierdzenie - Arin Taylor


Przyszedł na to podejście (i prawdopodobnie jest to dokładnie to samo, co przy używaniu interaktywnego rebase), ale dla mnie jest to dość proste.

Uwaga: Przedstawiam to podejście ze względu na ilustrację tego, co możesz zrobić, zamiast codziennej alternatywy. Ponieważ ma wiele kroków (i być może pewne zastrzeżenia).

Powiedz, że chcesz zmienić zatwierdzenie 0 i jesteś obecnie włączony feature-branch

some-commit---0---1---2---(feature-branch)HEAD

Zrealizuj do tego zatwierdzenia i utwórz a quick-branch. Możesz również klonować gałąź funkcji jako punkt przywracania (przed uruchomieniem).

?(git checkout -b feature-branch-backup)
git checkout 0
git checkout -b quick-branch

Będziesz teraz miał coś takiego:

0(quick-branch)HEAD---1---2---(feature-branch)

Zmiany na scenie, ukryj wszystko.

git add ./example.txt
git stash

Zatwierdź zmiany i zamówienie z powrotem do feature-branch

git commit --amend
git checkout feature-branch

Będziesz teraz miał coś takiego:

some-commit---0---1---2---(feature-branch)HEAD
           \
             ---0'(quick-branch)

Rebase feature-branch na quick-branch (rozwiąż wszelkie konflikty po drodze). Zastosuj schowek i usuń quick-branch.

git rebase quick-branch
git stash pop
git branch -D quick-branch

I kończysz z:

some-commit---0'---1'---2'---HEAD(feature-branch)

Git nie będzie duplikować (chociaż nie mogę powiedzieć, w jakim stopniu) 0 commit przy ponownym tworzeniu.

Uwaga: wszystkie zatwierdzone skróty są zmieniane, począwszy od zatwierdzenia, które pierwotnie zamierzaliśmy zmienić.


6
2018-06-01 11:57





Aby uzyskać nieinteraktywne polecenie, umieść skrypt o tej treści w swojej zmiennej PATH:

#!/bin/sh
#
# git-fixup
# Use staged changes to modify a specified commit
set -e
cmt=$(git rev-parse $1)
git commit --fixup="$cmt"
GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"

Użyj go, przesuwając swoje zmiany (za pomocą git add), a następnie uruchomić git fixup <commit-to-modify>. Oczywiście nadal będzie interaktywny, jeśli wystąpią konflikty.


4
2018-01-16 15:27



To działa dobrze. Dodałem dodatkową funkcjonalność do robienia fragmentarycznych poprawek brudnego drzewa w celu udoskonalenia zestawu zatwierdzeń. `dirtydiff = $ (git diff); if ["$ {dirtydiff}"! = ""]; następnie echo "Stashing dirty tree"> & 2; git skrytka; fi; - Simon Feltman


Zupełnie nieinteraktywne polecenie(1)

Po prostu pomyślałem, że udostępnię alias, którego używam do tego. Jest oparty na nieinteraktywny interaktywny rebase. Aby dodać go do swojego git, uruchom to polecenie (wyjaśnienie podane poniżej):

git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'

Największą zaletą tego polecenia jest to, że jest no-vim.


(1)zważywszy, że oczywiście nie ma żadnych konfliktów podczas reorganizacji

Stosowanie

git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111

Imię amend-to wydaje się właściwe IMHO. Porównaj przepływ z --amend:

git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>

Wyjaśnienie

  • git config --global alias.<NAME> '!<COMMAND>' - tworzy globalny alias git o nazwie <NAME> które wykona polecenie inne niż git <COMMAND>
  • f() { <BODY> }; f - "anonimowa" funkcja bash.
  • SHA=`git rev-parse "$1"`; - konwertuje argument na wersję git i przypisuje wynik do zmiennej SHA
  • git commit --fixup "$SHA" - fixup-commit dla SHA. Widzieć git-commit docs
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^" część została ujęta w innych odpowiedziach.
    • --autosquash jest to, co jest używane w połączeniu z git commit --fixup, widzieć git-rebase docs po więcej informacji
    • GIT_SEQUENCE_EDITOR=true to sprawia, że ​​całość nie jest interaktywna. Ten hack, którego się nauczyłem z tego wpisu na blogu.

4
2018-02-27 01:47



Można też zrobić amend-to obsłużyć pliki nieprogramowe: git config --global alias.amend-to '!f() { SHA=git rev-parsowanie "$ 1"; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f' - Dethariel
Używam go z --autostash flaga na komendzie rebase. - idanp