Cofnąć modyfikacje kopii roboczej jednego pliku w Git?


1632

Po ostatnim zatwierdzeniu zmodyfikowałem kilka plików w kopii roboczej, ale chcę cofnąć zmiany w jednym z tych plików, ponieważ przywracam go do tego samego stanu, co w ostatnim zatwierdzeniu.

Chcę tylko cofnąć zmiany kopii roboczej tylko jednego pliku, nic więcej z tym.

Jak mogę to zrobić?

Odpowiedzi:


2212

Możesz użyć

git checkout -- file

Możesz to zrobić bez --(jak sugeruje nimrodm), ale jeśli nazwa pliku wygląda jak gałąź lub znacznik (lub inny identyfikator wersji), może się pomylić, więc użycie --jest najlepsze.

Możesz także sprawdzić konkretną wersję pliku:

git checkout v1.2.3 -- file         # tag v1.2.3
git checkout stable -- file         # stable branch
git checkout origin/master -- file  # upstream master
git checkout HEAD -- file           # the version from the most recent commit
git checkout HEAD^ -- file          # the version before the most recent commit

34
jaka jest różnica między HEAD a HEAD ^?
hasen

62
HEAD to najnowsze zatwierdzenie w bieżącej gałęzi, a HEAD ^ to wcześniejsze zatwierdzenie w bieżącej gałęzi. W opisanej sytuacji możesz użyć git checkout HEAD - nazwa pliku.
Paul

16
W skrócie „git checkout sha-reference - nazwa pliku”, gdzie sha-referencja jest odniesieniem do sha zatwierdzenia, w dowolnej formie (gałąź, tag, rodzic itp.)
Lakshman Prasad

29
UWAGA: Jeśli plik jest już przemieszczany, najpierw musisz go zresetować. git reset HEAD <filename> ; git checkout -- <filename>
Olie,

14
@gwho Tak, możesz zrobić HEAD^^dla 2 zatwierdzeń od najnowszego lub HEAD^^^dla 3 zatwierdzeń z powrotem. Możesz także użyć HEAD~2, lub HEAD~3, co staje się wygodniejsze, jeśli chcesz cofnąć więcej zatwierdzeń, podczas gdy HEAD^2oznacza to „drugiego rodzica tego zatwierdzenia”; z powodu zatwierdzeń scalania, zatwierdzenie może mieć więcej niż jedno poprzednie zatwierdzenie, więc z HEAD^liczbą wybiera, który z tych rodziców, podczas gdy z HEAD~liczbą zawsze wybiera pierwszego rodzica, ale ta liczba zatwierdza z powrotem. Zobacz git help rev-parsepo więcej szczegółów.
Brian Campbell

139

Po prostu użyj

git checkout filename

Spowoduje to zastąpienie nazwy pliku najnowszą wersją z bieżącego oddziału.

OSTRZEŻENIE: zmiany zostaną odrzucone - kopia zapasowa nie jest przechowywana.


22
@duckx ma na celu odróżnienie nazw gałęzi od nazw plików. jeśli powiesz, że git checkout xx to nazwa gałęzi, a także nazwa pliku, nie jestem pewien, jakie jest domyślne zachowanie, ale myślę, że git zakłada, że ​​chcesz przejść na gałąź x. Kiedy używasz --, mówisz, że to, co następuje, to nazwy plików.
hasen

1
ic dzięki za wyjaśnienie. wszyscy zakładają, że wiesz co - co oznacza, kiedy pokazują ci przykłady. i nie jest to również coś, co możesz łatwo znaleźć w Google.
Patoshi パ ト シ

1
Wygląda na to, że odpowiedź została edytowana w celu jej usunięcia --. Mimo że nadal jest poprawny, jak wskazuje @hasen, jeśli istnieje niejednoznaczność między nazwą pliku a nazwą gałęzi, może dojść do bardzo niepożądanego zachowania!
BrainSlugs83

2
Podoba mi się taki, jaki jest, bez --, przyjemny i łatwy. Kiedy nazywasz gałęzie za pomocą nazw plików, gdzieś musi być złe myślenie ...
Marco Faustinelli,

133
git checkout <commit> <filename>

Użyłem tego dzisiaj, ponieważ zdałem sobie sprawę, że mój favicon został zastąpiony kilka commits temu, kiedy uaktualniłem do drupal 6.10, więc musiałem go odzyskać. Oto co zrobiłem:

git checkout 088ecd favicon.ico

1
Jak uzyskać zatwierdzenie (wcześniej usuniętego pliku), z wyjątkiem przewijania ton wyjściowych „git log --stat”?
Alex

4
IMO jest dość trudne za pomocą wiersza polecenia, aby przejrzeć dziennik gits i znaleźć odpowiedni plik. O wiele łatwiej jest z aplikacją GUI, taką jak sourcetreeapp.com
neoneye

6
git log --oneline <filename>da ci bardziej zwarty dziennik i będzie zawierać tylko zmiany w konkretnym pliku
rjmunro

1
alternatywnie możesz użyćgit reflog <filename>
ygesher

70

Jeśli plik jest już przemieszczany (dzieje się to po dodaniu git itp. Po edycji pliku), aby wycofać zmiany.

Posługiwać się

git reset HEAD <file>

Następnie

git checkout <file>

Jeśli nie jest już wystawiany, po prostu użyj

git checkout <file>

2
To było bardziej pomocne niż przyjęte haha. Łatwo zapomnieć, jakie zmiany zostały wprowadzone, a co nie, więc resetowanie pomogło. Chociaż próbowałem też wcześniej „git reset --hard”, nie zrobiłem tego, co zrobił „git reset HEAD”. Zastanawiam się dlaczego?
Arman Bimatov

20

Jeśli chcesz po prostu cofnąć zmiany poprzedniego zatwierdzenia dla tego jednego pliku, możesz spróbować:

git checkout branchname^ filename

Spowoduje to pobranie pliku tak, jak przed ostatnim zatwierdzeniem. Jeśli chcesz cofnąć się o kilka kolejnych zatwierdzeń, użyj branchname~nnotacji.


To nie usunie zmian z zatwierdzenia, po prostu zastosuje różnicę do wersji HEAD.
FernandoEscher

2
Choć prawda, oryginalny plakat chciał tylko przywrócić modyfikacje kopii roboczej (tak myślę), a nie cofnąć zmiany z ostatniego zatwierdzenia. Pytanie pierwotnego plakatu było trochę niejasne, więc rozumiem zamieszanie.

może nie do wykorzystania przez OP, ale szukałem sposobu na zastąpienie mojego oddziału kopią mistrza - działa całkiem nieźle podczas zastępowaniabranchname^
Alex

15

Zrobiłem poprzez git bash:

(use "git checkout -- <file>..." to discard changes in working directory)

  1. Status Git. [Więc widzieliśmy zmodyfikowany jeden plik wad.]
  2. git checkout - index.html [Zmieniłem plik index.html:
  3. status git [teraz zmiany zostały usunięte]

wprowadź opis zdjęcia tutaj


8

Zawsze się z tym mylę, więc oto przypadek testowy przypomnienia; powiedzmy, że mamy ten bashskrypt do przetestowania git:

set -x
rm -rf test
mkdir test
cd test
git init
git config user.name test
git config user.email test@test.com
echo 1 > a.txt
echo 1 > b.txt
git add *
git commit -m "initial commit"
echo 2 >> b.txt
git add b.txt
git commit -m "second commit"
echo 3 >> b.txt

W tym momencie zmiana nie jest przenoszona do pamięci podręcznej, podobnie jak git status:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

Jeśli od tego momentu to zrobimy git checkout, wynik będzie następujący:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

Jeśli zamiast tego zrobimy git reset, wynik będzie:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

Tak więc w tym przypadku - jeśli zmiany nie są wprowadzane, nie git resetma znaczenia, a zmiany są git checkoutzastępowane.


Powiedzmy teraz, że ostatnia zmiana z powyższego skryptu jest przenoszona / buforowana, to znaczy, że zrobiliśmy to również git add b.txtna końcu.

W tym przypadku git statusw tym momencie jest:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   b.txt

Jeśli od tego momentu to zrobimy git checkout, wynik będzie następujący:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

Jeśli zamiast tego zrobimy git reset, wynik będzie:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

Tak więc w tym przypadku - jeśli zmiany są wprowadzane etapowo, git resetzasadniczo zmieniają zmiany etapowe w zmiany niestacjonarne - jednocześnie git checkoutcałkowicie je nadpisując.


7

Ta odpowiedź dotyczy polecenia potrzebnego do cofnięcia lokalnych zmian, które znajdują się w wielu określonych plikach w tym samym lub wielu folderach (lub katalogach). Odpowiedzi te dotyczą w szczególności pytania, w którym użytkownik ma więcej niż jeden plik, ale nie chce cofnąć wszystkich lokalnych zmian:

jeśli masz jeden lub więcej plików, możesz zastosować to samo polecenie ( git checkout -- file) do każdego z tych plików, wymieniając każdą z ich lokalizacji oddzieloną spacją jak w:

git checkout -- name1/name2/fileOne.ext nameA/subFolder/fileTwo.ext

pamiętaj o spacji powyżej między nazwą1 / nazwą2 / fileOne.ext nameA / subFolder / fileTwo.ext

W przypadku wielu plików w tym samym folderze:

Jeśli musisz odrzucić zmiany dla wszystkich plików w określonym katalogu, skorzystaj z usługi git Checkout w następujący sposób:

git checkout -- name1/name2/*

Gwiazdka w powyższym przykładzie polega na cofnięciu wszystkich plików w tej lokalizacji pod nazwą1 / nazwa2.

Podobnie, następujące czynności mogą cofnąć zmiany we wszystkich plikach dla wielu folderów:

git checkout -- name1/name2/* nameA/subFolder/*

ponownie pamiętaj o spacji między nazwą1 / nazwą2 / * nazwąA / podfolderem / * powyżej.

Uwaga: nazwa1, nazwa2, nazwaA, podfolder - wszystkie te przykładowe nazwy folderów wskazują folder lub pakiet, w którym mogą znajdować się dane pliki.


5

Przywracam moje pliki przy użyciu identyfikatora SHA. To, co robię, to git checkout <sha hash id> <file name>



2

Jeśli jeszcze nie wypchnąłeś lub w inny sposób nie podzieliłeś się swoim zatwierdzeniem:

git diff --stat HEAD^...HEAD | \
fgrep filename_snippet_to_revert | cut -d' ' -f2 | xargs git checkout HEAD^ --
git commit -a --amend

0

Jeśli jest już zatwierdzony, możesz cofnąć zmianę pliku i zatwierdzić ponownie, a następnie zgnieść nowe zatwierdzenie z ostatnim zatwierdzeniem.


1
Dodanie określonych poleceń do użycia pomogłoby oryginalnemu plakatowi i przyszłym użytkownikom.
Adrian

0

Nie wiem dlaczego, ale kiedy próbuję wprowadzić kod, pojawia się on jako obraz.

wprowadź opis zdjęcia tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.