Rozpakuj usunięty plik w git

504

Zwykle, aby odrzucić zmiany w pliku, należy:

git checkout -- <file>

Co jeśli zmiana, którą chcę odrzucić, polega na usunięciu pliku? Powyższa linia dałaby błąd:

error: pathspec '<file>' did not match any file(s) known to git.

Jakie polecenie przywróci ten pojedynczy plik bez cofania innych zmian?

punkt bonusowy: Co też, jeśli zmiana, którą chcę odrzucić, polega na dodaniu pliku? Chciałbym również wiedzieć, jak wprowadzić tę zmianę w życie.

lurscher
źródło
1
Odrzucanie zmian i wycofywanie z gry to dwie różne rzeczy, które próbujesz zrobić?
Andrew Marshall,
1
To dwa różne pytania i problemy w jednym poście. To sprawia, że ​​odpowiedzi też są niepotrzebnie mylące.
David Sopko

Odpowiedzi:

778

Zakładając, że chcesz cofnąć efekty, git rm <file>lub rm <file>po nich, git add -Alub coś podobnego:

# this restores the file status in the index
git reset -- <file>
# then check out a copy from the index
git checkout -- <file>

Aby cofnąć git add <file>, wystarczy pierwsza linia powyżej, zakładając, że jeszcze tego nie zrobiłeś.

twalberg
źródło
69
To --jest klucz. git reset <file>nie działa, co mnie tu sprowadziło.
Ale przywróci to również wszystkie modyfikacje. co zrobić, jeśli chcę przywrócić tylko wszystkie usunięte pliki. I wszystkie modyfikacje pozostają nietknięte.
Dainius Kreivys,
2
Dlaczego jest to end-of-options-markerwymagane tylko w przypadku usuniętego pliku?
haridsv
4
@handsv Nie jest to bezwzględnie wymagane (możesz to zrobić na przemian git reset HEAD <file>, co jest równoważne), ale git resettraktuje swój pierwszy argument wcześniej end-of-options-markerjako nazwę ref, a nie nazwę pliku. Czy można to napisać nieco bardziej elastycznie? Prawdopodobnie. Dlaczego nie było Prawdopodobnie tylko programiści wiedzą na pewno.
Twalberg,
2
@twalberg git reset filenamedziała dobrze w przypadku plików nieskasowanych.
Brian Gordon
56

Na oba pytania udzielono odpowiedzi git status.

Aby wycofać scenę z dodawania nowego pliku, użyj git rm --cached filename.ext

# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   test

Aby cofnąć scenę, usuń plik git reset HEAD filename.ext

# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   deleted:    test

Z drugiej strony, git checkout --nigdy nie rezygnuj ze sceny, po prostu odrzuca zmiany bezetapowe.

seppo0010
źródło
5
Nie widzę podpowiedzi do usuniętego pliku w git 1.7.2.5 na Debianie.
tripleee
Miło widzieć git statuscytowane; pokazuje użytkownikom sposób na samopomoc teraz i następnym razem, w przypadku gdy informacje zostaną dodane lub zaktualizowane w przyszłych wersjach git.
Will Cain
To jest źle. „Zmiany do zatwierdzenia ” to to, co widzisz przedgit reset . Po tym git resetzobaczysz „Zmieniono, ale nie zaktualizowano”, co najwyraźniej oznacza „Zmiany niewprowadzone” w ojczystym języku autorów git. Co ważniejsze, cały dogmat o „statusie gita mówi ci wszystko, co wiesz” jest kłamstwem. (Menedżerowie, którzy twierdzą, że marnują czas ludzi i powinni zostać zwolnieni).
personal_cloud
11

Odpowiedzi na twoje dwa pytania są powiązane. Zacznę od drugiego:

Po umieszczeniu pliku (często za pomocą git add, choć niektóre inne polecenia pośrednio również wprowadzają zmiany, na przykład git rm), możesz wycofać tę zmianę za pomocą git reset -- <file>.

W twoim przypadku musiałeś użyć git rmdo usunięcia pliku, co jest równoznaczne z usunięciem go, rma następnie zainscenizowaniem tej zmiany. Jeśli najpierw go odetniesz git reset -- <file>, możesz go odzyskać git checkout -- <file>.

Ben Jackson
źródło
7

Jeśli został wyreżyserowany i zatwierdzony, następujące operacje zresetują plik:

git reset COMMIT_HASH file_path
git checkout COMMIT_HASH file_path
git add file_path

Będzie to działać w przypadku usunięcia, które miało miejsce kilka poprzednich zatwierdzeń.

Michaeldever
źródło
1
Bardziej efektywne jestgit revert COMMIT_HASH
Flair
2

Począwszy od git v2.23 , masz inną opcję:

git restore --staged -- <file>

Kreempuff
źródło