Cofanie części zatwierdzenia za pomocą git

144

Chcę cofnąć określone zatwierdzenie w git. Niestety, nasza organizacja nadal standardowo używa CVS, więc kiedy ponownie zatwierdzam CVS, wiele zatwierdzeń git jest łączonych w jedno. W tym przypadku chciałbym wyróżnić oryginalne zatwierdzenie git, ale jest to niemożliwe.

Czy istnieje podejście podobne do git add --patchtego, które pozwoliłoby mi selektywnie edytować różnice, aby zdecydować, które części zatwierdzenia przywrócić?

skiphoppy
źródło
Więcej rozwiązań tutaj , ale skupienie się na ograniczeniu częściowego powrotu do określonych plików.
ntc2

Odpowiedzi:

226

Użyj opcji --no-commit( -n), aby git revertcofnąć zmiany, a następnie użyj git add --patch:

$ git revert -n $bad_commit    # Revert the commit, but don't commit the changes
$ git reset HEAD .             # Unstage the changes
$ git add --patch .            # Add whatever changes you want
$ git commit                   # Commit those changes

Uwaga: Pliki, które dodajesz za pomocą git add --patch, to pliki, które chcesz przywrócić, a nie pliki, które chcesz zachować.

mipadi
źródło
13
Może warto dodać ostatnią wymaganą komendę, dla tych, którzy nie są tak zaznajomieni z git: po git reset --hardzatwierdzeniu , aby odrzucić inne zmiany, których nie chciałeś cofnąć.
tremby
15
git reset --hardjest niebezpieczny dla początkujących, ponieważ może utracić pożądane zmiany. Zamiast się przyzwyczaić git status, to podpowiada, git checkout -- FILE..jak bezpiecznie cofnąć rzeczy.
Tino,
Co za ziejące zaniedbanie git; git revertpowinien po prostu się --patchspierać.
Kaz
@Kaz: git revertsłuży do cofania całych zatwierdzeń. Możesz użyć, git checkout -paby interaktywnie wybrać bity do przywrócenia.
mipadi
1
Chciałbym również dodać (być może) oczywiste, czyli najpierw zapisać swoją pracę . Albo commitnajpierw, albo stashpotem spróbuj revert.
Felipe Alvarez
39

Z powodzeniem użyłem następujących.

Najpierw przywróć pełne zatwierdzenie (umieszcza je w indeksie), ale nie zatwierdzaj.

git revert -n <sha1>  # -n is short for --no-commit

Następnie interaktywnie usuń cofnięte DOBRE zmiany z indeksu

git reset -p          # -p is short for --patch  

Następnie dokonaj odwrócenia różnicy złych zmian

git commit -m "Partially revert <sha1>..."

Wreszcie cofnięte zmiany DOBRE (które zostały cofnięte za pomocą polecenia resetowania) nadal znajdują się w drzewie roboczym. Trzeba je oczyścić. Jeśli w drzewie roboczym nie pozostały żadne niezatwierdzone zmiany, można to zrobić za pomocą

git reset --hard
user1338062
źródło
5
Czy nie jest to lepsza alternatywa dla przyjętej odpowiedzi (której używa reset HEAD .), ponieważ nie wymaga ostatecznego uporządkowania pracy w reż?
Steven Lu
2
Ta odpowiedź jest lepsza, ponieważ reset -pjest krótsza niż reset HEADnastępuje add -p. Ale nadal wymaga czyszczenia, ponieważ „dobre” porcje, które zostały zresetowane, nadal znajdują się w katalogu roboczym po zatwierdzeniu.
Chiel ten Brinke
Ta odpowiedź nie jest lepszy, ponieważ interaktywnie usunięcie zmiany mają często jest mylące i podatny na błędy --- zwłaszcza jeśli któryś z nich wymagają zmienił.
Kaz
5

Osobiście wolę tę wersję, która ponownie wykorzystuje automatycznie wygenerowany komunikat o zatwierdzeniu i daje użytkownikowi możliwość edycji i wstawienia słowa „Częściowo” przed ostatecznym zatwierdzeniem.

# generate a revert commit
# note the hash printed to console on success
git revert --no-edit <hash to revert>

# undo that commit, but not its changes to the working tree
# (reset index to commit-before-last; that is, one graph entry up from HEAD)
git reset HEAD~1

# interactively add reversions
git add -p

# commit with pre-filled message
git commit -c <hash from revert commit, printed to console after first command>

# reset the rest of the current directory's working tree to match git
# this will reapply the excluded parts of the reversion to the working tree
# you may need to change the paths to be checked out
# be careful not to accidentally overwrite unsaved work
git checkout -- .
jeffcook2150
źródło
4

Rozwiązanie:

git revert --no-commit <commit hash>
git reset -p        # every time choose 'y' if you want keep the change, otherwise choose 'n'
git commit -m "Revert ..."
git checkout -- .   # Don't forget to use it.
Krzysztof Kaczmarski
źródło
Pomogłoby to ludziom, gdybyś powiedział, że różni się ono od przyjętego rozwiązania
CharlesB,
1
@Krzysztof Dlaczego na koniec ważna jest kasa i dlaczego to rozwiązanie różni się od rozwiązania user1338062?
martin
3

Inną alternatywą (jeśli twoja aktualna wersja pliku nie jest zbyt daleko od wersji, którą próbujesz przywrócić) jest pobranie skrótu zatwierdzenia bezpośrednio przed tym, z którego chcesz częściowo przywrócić (z git log). Wtedy twoje polecenie staje się:

$ git checkout -p <hash_preceding_commit_to_revert> -- file/you/want/to/fix.ext

To zmienia pliki w twoim drzewie roboczym, ale nie tworzy żadnych zatwierdzeń, więc jeśli naprawdę zrobisz coś, możesz po prostu zacząć od nowa git reset --hard -- file/you/want/to/fix.ext.

Walf
źródło
1

Możesz użyć git-revert -n, a następnie add --patch, aby wybrać porcje.

William Pursell
źródło