Cofnij zmianę w git (nie przepisywanie historii)

Dokonałem zmiany w scenariuszu i go popełniłem. Potem wprowadziłem kilka innych zmian i wrzuciłem je do zdalnego repozytorium i tym podobne.

Wtedy zdałem sobie sprawę, że pierwsza zmiana, o której wspomniałem, była głupia i chcę ją cofnąć.. Czy mogę "odpisać" ten commit bez ręcznego kopiowania / wklejania różnic?

Jako przykład: mam dwa pliki, a.py i b.py:

Commit 1:
I delete a function in a.py

Commit 2:
I change a few lines in b.py

Commit 3:
I change the docstring in a.py

Czy mogę cofnąć usunięcie tej funkcji i sprawić, że będzie ona wyświetlana jako "commit 4" (zamiast usuwać commit 1)

Author: PlagueHammer, 2009-03-13

4 answers

Tak, możesz do tego użyć git revert. Więcej informacji można znaleźć w sekcji podręcznika Gita na tej stronie.

Istotą jest to, że możesz powiedzieć:
git revert 4f4k2a

Gdzie 4f4k2a jest identyfikatorem commita, który chcesz cofnąć, i będzie próbował go cofnąć.

 59
Author: Jesse Rusak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-28 13:45:07

Tylko komentarz:

git revert aCommit

Zwraca wszystkie commit (jak w "wszystkie pliki część commita"):
oblicza odwrotną łatkę, nakłada ją na HEAD I commit.

Więc dwa problemy tutaj (pierwszy jest łatwo rozwiązany):

  • to zawsze commit, więc możesz chcieć dodać -no-commit option: "git revert --no-commit aCommit": jest to przydatne przy przywracaniu więcej niż jednego efektu commitów do indeksu w wierszu.
  • Nie dotyczy konkretnego pliku (co jeśli a.py była częścią commita z 1000 innymi zmianami, których nie chcesz cofnąć) ?
    W tym celu, jeśli chcesz wyodrębnić określone pliki w innym zatwierdzeniu, powinieneś zobaczyć git-checkout, w szczególności git checkout <commit> <filename> składnia (nie jest to dokładnie to, czego potrzebujesz w tym przypadku)

Easy Git (Elijah Newren) próbował wprowadzić bardziej "kompletny revert" na listę dyskusyjną Git ; ale bez większego sukces:

LUDZIE od czasu do czasu chcą "cofnąć zmiany".

Teraz to może być:

  • zmiany między 32 i 29 rewizji temu,
  • mogą to być wszystkie zmiany od ostatniego commita,
  • mogą to być zmiany od 3 commitów temu, lub
  • to może być tylko jeden konkretny commit.
  • użytkownik może chcieć podzestawić takie rewersy tylko do określonych plików ,

(eg revert na udokumentowane tutaj , ale nie jestem pewien, czy jest to część obecnego rozkładu np.)

Ale wszystko sprowadza się do" powrotu zmian " w końcu.

eg revert --since HEAD~3  # Undo all changes since HEAD~3
eg revert --in HEAD~8     # much like git revert HEAD~8, but nocommit by default
eg revert --since HEAD foo.py  # Undo changes to foo.py since last commit
eg revert foo.py               # Same as above
eg revert --in trial~7 bar.c baz.  # Undo changes made in trial~7 to bar.[ch]

Czy te rodzaje "przywracania danych" naprawdę są tak różne, że powinny istnieć różne polecenia, czy też niektóre z tych operacji nie powinny być obsługiwane przez proste polecenie revert?
Oczywiście, większość użytkowników przez większość czasu prawdopodobnie będzie używać " eg revert FILE1 FILE2..." forma, ale ja nie widziałem szkody w wspieraniu dodatkowych możliwości.

Also...is czy jest coś fundamentalnego, co powstrzymałoby core git od przyjęcia takiego zachowania?

Eliasz

Notatka: commity domyślnie nie mają sensu dla uogólnionego polecenia revert, a "git revert REVISION" powodowałoby błąd z instrukcjami (nakazującymi użytkownikowi dodanie flagi --in).


Powiedzmy, że masz, z 50 popełnionych, 20 plików zdajesz sobie sprawę, że stary commit x wprowadził zmiany, które nie powinny mieć miejsca.
Mała kanalizacja jest w porządku.
To, czego potrzebujesz, to sposób, aby wyświetlić listę wszystkich konkretnych plików, których potrzebujesz, aby przywrócić
(jak w "anulowanie zmian wprowadzonych w commicie X przy zachowaniu wszystkich kolejnych zmian"),
a potem dla każdego z nich:

git-merge-file -p a.py X X^

Problem polega na odzyskaniu utraconej funkcji bez niszczenia wszystkich późniejszych zmian w a.py lepiej zatrzymaj.
Technika ta nazywana jest czasem " negatywnym scalanie".

Od git merge-file <current-file> <base-file> <other-file> środki:
zawiera wszystkie zmiany, które prowadzą od <base-file> do <other-file> w <current-file>, możesz przywrócić usuniętą funkcję, mówiąc, że chcesz włączyć wszystkie zmiany.)

  • from: X (gdzie funkcja została usunięta)
  • do: X^ (poprzedni commit przed X, gdzie funkcja nadal tam była)

Uwaga:'-p' argument, który pozwala na przegląd najpierw zmiany bez robienia czegokolwiek na bieżącym pliku. Gdy masz pewność, usuń tę opcję.

Uwaga :git merge-file czy nie jest takie proste : nie można tak po prostu odwoływać się do poprzednich wersji pliku.
(miałbyś w kółko frustrujące przesłanie: error: Could not stat X)
Musisz:

git cat-file blob a.py > tmp/ori # current file before any modification
git cat-file blob HEAD~2:a.py > tmp/X # file with the function deleted
git cat-file blob HEAD~3:a.py > tmp/F # file with the function which was still there

git merge-file a.py tmp/X tmp/F # basically a RCS-style merge
                                 # note the inversed commit order: X as based, then F
                                 # that is why is is a "negative merge"
diff -u a.py tmp/ori # eyeball the merge result
git add a.py 
git commit -m "function restored" # and any other changes made from X are preserved!

Jeśli ma to być zrobione dla dużej liczby plików w poprzednim commicie... niektóre skrypty są w porządku;)

 32
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-15 01:43:01

Aby przywrócić zmiany tylko do jednego pliku w commicie, jak zauważył VonC, chciałbym checkout gałąź (master lub trunk lub cokolwiek), a następnie checkout wersję pliku, którą chciałem przywrócić i traktować jako nowy commit:

$ git checkout trunk
$ git checkout 4f4k2a^ a.py
$ git add a.py
$ git diff              #verify I'm only changing what I want; edit as needed
$ git commit -m 'recover function deleted from a.py in 4f4k2a'
Prawdopodobnie jest komenda hydrauliczna, która zrobiłaby to bezpośrednio, ale nie użyłabym jej, gdybym wiedziała. Nie chodzi o to, że nie ufam Gitowi, ale nie ufam sobie . nie ufałbym, że wiem, nie patrząc na to, co zostało zmienione w tym pliku w tego zobowiązania i od tego czasu. Jak już spojrzę, łatwiej jest stworzyć nowy commit edytując diff. Może to tylko osobisty styl pracy.
 5
Author: Paul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:48

Spójrz na to pytanie git revert . Wydaje się, że występuje problem z przywróceniem starszych commitów, jeśli nie w kolejnych sekwencjach, w tym najnowszych commitów.

 1
Author: Mohan Nayaka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:30:59