Czy mogę dokonać częściowego przywrócenia w GIT

156

Czy można cofnąć tylko jeden plik lub pewne zmiany w pliku przy zatwierdzaniu wielu plików?

Cała historia Popełniłem kilka plików. Kilka zatwierdzeń później ktoś, kto pozostanie bezimienny (JACK !!!), skopiował plik do swojego repozytorium i zatwierdził kilka plików, nadpisując niektóre ze zmian, które zrobiłem. Chcę przywrócić jeden plik, który został zbity lub jeszcze lepiej, wejść i cofnąć dwie zmiany w tym pliku. Będzie to musiało być oddzielnym zatwierdzeniem przywrócenia, ponieważ zostało wyciągnięte i wypchnięte.

Sprzęgło
źródło
Brian doprowadził mnie do właściwej drogi. Skończyło się na tym, że użyłem git add --patch, a następnie użyłem funkcji edycji w patchu, aby uzyskać to, czego chciałem.
Sprzęgło
2
Jestem dość spóźniony do gry, ale myślę, że znalazłem „właściwą” odpowiedź .
ntc2
@ ntc2 Dziwne, zaakceptowana odpowiedź zadziałała dla mnie znacznie lepiej niż ta, którą połączyłeś - stworzyła konflikty scalania, które musiałem rozwiązać, zamiast po prostu nie stosować poprawek.
Iiridayn
@liridayn Czy przeczytałeś całą moją odpowiedź? W sekcji „Odmiany” twierdzę, że dodanie --3waypowoduje konflikty scalania zamiast niepowodzenia w zastosowaniu poprawek.
ntc2

Odpowiedzi:

204

Możesz cofnąć zatwierdzenie bez tworzenia nowego, dodając opcję „--no-commit”. Spowoduje to pozostawienie wszystkich przywróconych plików w obszarze przemieszczania. Stamtąd wykonałem miękki reset i dodałem zmiany, których naprawdę chciałem. Przykładowy przepływ pracy:

git revert <sha-of-bad-commit> --no-commit
git reset   // This gets them out of the staging area
<edit bad file to look like it should, if necessary>
git add <bad-file>
git checkout . // This wipes all the undesired reverts still hanging around in the working copy
git commit
bobDevil
źródło
42
Po miękkim resecie możesz również git add -prozpocząć interaktywną sesję, która pozwala selektywnie dodawać fragmenty plików zamiast całych plików. (Istnieje również git reset -pmożliwość wybiórczego wycofywania zmian. Dobrze wiedzieć, ale prawdopodobnie nie to, co chcesz w tym scenariuszu.)
Stéphan Kochen
1
Właśnie tego szukałem: Przywróć kopię roboczą, wybierz interaktywnie porcje, zatwierdź. Zasługujesz za to na wielkiego, słonego faceta: *
das_weezul
Innym rozwiązaniem zainspirowany tym jednym byłoby revert, reset, stash -p(zapas „anuluje, że nie chcę, aby właściwie make”), addreszta,commit
Cyril chapon
82

Możesz interaktywnie zastosować starą wersję pliku za pomocą checkoutpolecenia.

Na przykład, jeśli wiesz, COMMITgdzie kod do dodania został usunięty, możesz uruchomić następujące polecenie:

git checkout -p COMMIT^ -- FILE_TO_REVERT

Git poprosi cię o dodanie porcji, których brakuje w bieżącej wersji pliku. Możesz użyć, eaby utworzyć łatkę zmiany przed jej ponownym zastosowaniem.

cmcginty
źródło
Bardzo przydatne, jeśli chcesz przywrócić tylko część pliku! Zasadniczo, żeby dostać kawałki z głowy,git checkout -p path/to/file
falstaff
40

Możesz po prostu ręcznie sprawdzić starą, dobrą zawartość plików, które chcesz przywrócić git checkout. Na przykład, jeśli chcesz powrócić my-important-filedo wersji, która była w wersji abc123, możesz to zrobić

git checkout abc123 -- my-important-file

Teraz masz starą zawartość z my-important-filepowrotem i możesz ją nawet edytować, jeśli masz na to ochotę, i zatwierdzić jak zwykle, aby dokonać zatwierdzenia, które przywróci zmiany, które wprowadził. Jeśli są tylko niektóre części jego zatwierdzenia, które chcesz cofnąć, użyj, git add -paby wybrać tylko kilka porcji z poprawki, którą zatwierdzasz.

Brian Campbell
źródło
19
To nie jest plik revert, po prostu przywracasz starą wersję pliku. Właściwy revertusunie złe zmiany w pliku, nawet jeśli był modyfikowany kilka razy od tego złego zatwierdzenia, a nie chcesz stracić tych modyfikacji.
Totor
2
Totor, zobacz moją odpowiedź. Możesz użyć '-p', aby zapisać modyfikacje. Odpowiedź jest właściwie bardzo bliska tej.
cmcginty
1
Lub możesz połączyć to w git checkout -p.
Léo Lam
31

Znalazłem sposób na zrobienie tego na liście mailingowej Git:

git show <commit> -- <path> | git apply --reverse

Źródło: http://git.661346.n2.nabble.com/Revert-a-single-commit-in-a-single-file-td6064050.html#a6064406

Wariacje

To polecenie kończy się niepowodzeniem (nie powodując żadnych zmian), jeśli łatka nie działa prawidłowo, ale --3wayzamiast tego pojawiają się konflikty, które można następnie rozwiązać ręcznie (w tym przypadku odpowiedź Casey może być bardziej praktyczna):

git show <commit> -- <path> | git apply --reverse --3way

Możesz również użyć tego do częściowego cofnięcia wielu zatwierdzeń, np .:

git log -S<string> --patch | git apply --reverse

aby przywrócić pliki ze zmianami pasującymi <string>do dowolnego zatwierdzenia. To jest dokładnie to, czego potrzebowałem w moim przypadku użycia (kilka oddzielnych zatwierdzeń wprowadziło podobne zmiany w różnych plikach, a także zmiany innych plików w niepowiązany sposób, którego nie chciałem cofać).

Jeśli diff.noprefix=trueustawiłeś w swoim ~/.gitconfigto musisz dodać -p0do git applypolecenia np

git show <commit> -- <path> | git apply -p0 --reverse
ntc2
źródło
Ta odpowiedź jest znacznie lepsza niż zaakceptowana: „jeszcze lepiej, wejdź i cofnij dwie zmiany w tym pliku”.
przepisano
Chciałem cofnąć tylko część zatwierdzenia, więc zrobiłem: (1) git show ... > foo.txt (2) edytując, foo.txtaby usunąć różnice, których nie chciałem cofać (3), git apply --reverse foo.txt aby przywrócić różnice wciąż wymienione w foo.txt.
cxw
Dodanie --binary --full-index do części git show może być również przydatną odmianą
Laurynas Biveinis