Git: Jak przywrócić 2 pliki, które uparcie utknęły na „Zmieniono, ale nie zatwierdzono”?

84

Mam repozytorium zawierające dwa pliki, które podobno zmieniłem lokalnie.

Więc utknąłem z tym:

$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dir1/foo.aspx
#       modified:   dir2/foo.aspx
#
no changes added to commit (use "git add" and/or "git commit -a")

Doing git diffmówi, że zmieniła się cała zawartość pliku, mimo że patrząc na to, wydaje się to nieprawdą (wydaje się, że istnieją wspólne zakresy linii, których diff wydaje się nie widzieć).

Co ciekawe, nie pamiętam, żebym zmieniał te pliki lokalnie. To repozytorium jest używane z jednym zdalnym repozytorium (prywatnym, na GitHub.com, FWIW).

Bez względu na to, co próbowałem, nie mogę odrzucić tych lokalnych zmian. Wypróbowałem wszystkie:

$ git checkout -- .
$ git checkout -f
$ git checkout -- dir1/checkout_receipt.aspx
$ git reset --hard HEAD
$ git stash save --keep-index && git stash drop
$ git checkout-index -a -f

Innymi słowy, wypróbowałem wszystko, co opisano w sekcji Jak odrzucić zmiany niestacjonarne w Git? plus więcej. Ale te 2 pliki pozostają zablokowane jako „zmienione, ale niezatwierdzone”.

Co do cholery spowodowałoby, że dwa pliki utknęłyby w ten sposób i pozornie „cofnęły-tabelę”?

PS Na powyższej liście pokazującej polecenia, które już wypróbowałem, omyłkowo napisałem, git revertkiedy miałem na myśli git checkout. Przepraszam i dziękuję tym z Was, którzy odpowiedzieli, że powinienem spróbować checkout. Zredagowałem pytanie, aby je poprawić. Zdecydowanie już próbowałem checkout.

Greg Hendershott
źródło
Robi git diff --ignore-space-changelub git diff --ignore-all-spacedokonać zmian w wyjściu git diff?
jdd
@jermiahd Yes! Każda flaga oznacza, git diffże pliki są identyczne.
Greg Hendershott
2
Możliwy duplikat stackoverflow.com/questions/2016404/… . Zresztą bardziej podoba mi się przyjęta tam odpowiedź, czyli ustawienie git config --global core.autocrlf falsezamiast „prawda”.
Johann
2
Odpowiedź [tutaj] [1] zadziałała dla mnie i wielu innych. [1]: stackoverflow.com/questions/2016404/…
Mike K
2
Dzieje się tak również, gdy repozytorium zawierające 2 lub więcej plików w tym samym katalogu z różnymi wielkościami liter jest wyewidencjonowywane w systemie plików bez rozróżniania wielkości liter. Usuń lub zmień nazwę jednego z plików, aby rozwiązać problem.
465544

Odpowiedzi:

33

Jakie są zakończenia linii w plikach? Założę się, że to CRLF. Jeśli tak, zajrzyj do tego przewodnika: http://help.github.com/line-endings/

Krótko mówiąc, musisz upewnić się, że git jest ustawiony tak, aby konwertować zakończenia linii na LF przy zatwierdzaniu, a następnie zatwierdzać te pliki. Pliki w repozytorium powinny zawsze mieć format LF, pliki wypisane powinny być natywne dla systemu operacyjnego, zakładając, że ustawiłeś poprawnie git.

Tekkub
źródło
1
Dzięki. Ja już to zrobiłem, git config --global core.autocrlf truepodobnie jak druga strona pchająca do repozytorium na GitHubie.
Greg Hendershott
1
Następnie wystarczy wykonać bity z ostatniego <pre>bloku tego przewodnika, aby naprawić pliki w repozytorium.
Tekkub,
5
Nie zgadzam się, że zakończenia linii powinny zawsze znajdować się w repozytorium LF (szczególnie jeśli ktoś już zatwierdził CRLF), a także, że system operacyjny powinien zawsze być natywny. Mój edytor i środowisko Windows (głównie PHP, HTML, CSS itp.) Doskonale radzi sobie z zakończeniami linii LF.
Simon East,
Genialna odpowiedź, zapomniałem, że ostatnio użyłem gitattributes do wymuszenia LF w plikach repo i nie spodziewałem się, że git będzie automatycznie zmieniał plik. Mamy mieszankę programistów Windows i Linux i doprowadzało nas to do szału, ponieważ redaktorzy na różnych platformach ciągle wyłączali terminatory linii, gdy zmiana już się przejdzie, wszystko to powinno zniknąć.
Oliver Dungey
120

Spędziłem godziny próbując rozwiązać podobny problem - zdalną gałąź, którą sprawdziłem, która uparcie pokazywała cztery pliki jako „Zmienione, ale nie zaktualizowane”, nawet po usunięciu wszystkich plików i git checkout -fponownym uruchomieniu (lub innych odmianach z tego postu)!

Te cztery pliki były potrzebne, ale z pewnością nie zostały przeze mnie zmodyfikowane. Moje ostateczne rozwiązanie - przekonaj Gita, że ​​nie zostały zmienione. Poniższe działa dla wszystkich wyewidencjonowanych plików, pokazując status 'zmodyfikowany' - upewnij się, że zatwierdziłeś / ukryłeś te, które naprawdę zostały zmodyfikowane !:

git ls-files -m | xargs -i git update-index --assume-unchanged "{}"

Jednak na Mac OSX xargs działa trochę inaczej (dzięki Daniel za komentarz):

git ls-files -m | xargs -I {} git update-index --assume-unchanged {}

Dodałem to jako miejsce zastępcze dla siebie na następny raz, ale mam nadzieję, że pomoże to również komuś innemu.

-Glin

Alan Forsyth
źródło
10
Miałem kilka upartych plików i uruchomiłem to polecenie, status git teraz nie wyświetla żadnych zmian, ale kiedy próbuję zmienić gałąź, git nadal mówi mi, że nie mogę zmienić gałęzi, ponieważ te dwa pliki mają lokalne zmiany. Nie jesteś pewien, co zrobiłem źle, ale wydawało się, że to tylko zatuszowało problem, zamiast go naprawić? Nie mogłem również zatwierdzić plików po uruchomieniu tego polecenia. Rozwiązaniem dla mnie było ich usunięcie, zatwierdzenie i zamiana gałęzi.
RodH257
5
dzięki! Wypróbowałem WSZYSTKIE sztuczki wymienione w każdej innej odpowiedzi, jaką mogłem znaleźć - żadna nie działała. na komputerze Mac nie można było użyć tej linii w obecnej postaci, po prostu uruchomiłem git update-index --assume-unchanged <nazwa pliku> dla każdego pliku, co spowodowało, że problem zniknął.
Yonatan Karni
6
To jest dokładnie to, czego potrzebowałem, chociaż xargs na komputerze Mac wydaje się działać nieco inaczej (używam 10.10 Yosemite). To w końcu zadziałało dla mnie:git ls-files -m | xargs -I {} git update-index --assume-unchanged {}
Daniel
4
Aby cofnąć efekt polecenia:git ls-files -v|grep '^h' | cut -c3- | xargs -i git update-index --no-assume-unchanged "{}"
Marinos An
3
To rozwiązanie nie rozwiązuje problemu. Ukrywa to. Jest kilka rzeczy, których nie można wykonać później assume-unchaged, jak w przypadku @ RodH257. Uważam, że najbardziej poprawne odpowiedzi dla przypadku, gdy polecenia podoba git checkout -- file, git stasha git reset --hard HEADnie praca, jak już odpowiedział edycji.gitattributes
Marinos
20

tak naprawiłem ten sam problem w moim przypadku: otwórz .gitattributes change:

* text=auto

do:

#* text=auto

zapisz i zamknij, a następnie przywróć lub zresetuj, dzięki @Simon East za podpowiedź

Abu Assar
źródło
1
Usunięcie text=autoustawienia w .gitattributes działało u mnie, a potem po git reset --hardprzywróceniu tego ustawienia pliki nie były już wyświetlane jako zmodyfikowane!
ErikE
1
Oczywiście coś jest nie tak z tym text=autoustawieniem. Pracuję w repozytoriach z zatwierdzeniami z wielu systemów operacyjnych i nadal nie wiem, co powoduje więcej problemów: zachować je lub porzucić.
Marinos An
1
@ Marinos: Tak, w szczególności git umożliwia pozostawienie istniejących plików tekstowych z niewłaściwymi zakończeniami linii, gdy po raz pierwszy dodajesz to ustawienie. To po prostu złe i jeśli nie pamiętasz, aby zrobić to sam, w końcu napotkasz jedną z tych nieodwracalnych zmian.
Roman Starkov
12

Inną możliwością jest to, że różnica (która uniemożliwia przywrócenie tych plików za pomocą polecenia wypisania) dotyczy trybu pliku. To właśnie mi się przytrafiło. W mojej wersji gita możesz to odkryć za pomocą

git diff dir1 / foo.aspx

I pokaże ci zmiany trybu plików. Jednak nadal nie pozwala ci ich cofnąć. Do tego użyj też

git config core.filemode false

lub zmień git .config w edytorze tekstu, dodając

[rdzeń]

filemode = false

Po wykonaniu tej czynności możesz użyć

git reset HEAD dir1 / foo.aspx

a plik powinien zniknąć.

(Wszystko to otrzymałem z odpowiedzi na pytanie Jak wprowadzić zmiany trybu ignorowania git (chmod)? )

Eyal
źródło
1
Jeśli używasz systemu Windows, diagnoza / rozwiązanie Eyal powinno być Twoim pierwszym przypuszczeniem
AlcubierreDrive,
Bądź szczególnie ostrożny, aby nigdy nie używać cygwin git z cmd.exe. Jeśli chcesz, aby git w cmd.exe, zainstaluj msysgit.
AlcubierreDrive
Tylko po to, aby potwierdzić, że rzeczywiście był to problem w systemie Windows.
Dejan Marjanović
Dla mnie w systemie Windows to nie był problem ( core.filemodebyło już ustawione na fałsz). W moim przypadku poprawką / obejściem była odpowiedź Alana Forsytha .
Venryx
3

Spróbuj cofnąć zmiany lokalne :

git checkout -- dir1/foo.aspx
git checkout -- dir2/foo.aspx
Tadeck
źródło
Miałem „powrót” w mózgu i miałem zamiar pisać checkout. Już próbowałem checkout. Mimo wszystko dziękuję za odpowiedź. To była dobra odpowiedź na moje pierwotne pytanie, więc zagłosuję za.
Greg Hendershott
3

Miałem kilka plików zmienionych fantomowo, które były wyświetlane jako zmodyfikowane, ale w rzeczywistości były identyczne.

Uruchomienie tego polecenia czasami działa:
(wyłącza "inteligentne", ale często nieprzydatne konwersje kończące wiersz gita)

git config --local core.autocrlf false

Ale w innym przypadku stwierdziłem, że było to spowodowane .gitattributesplikiem w katalogu głównym, który miał pewne ustawienia końca linii, które próbowały zastosować autocrlfdo niektórych plików, nawet gdy był wyłączony. W rzeczywistości nie było to pomocne, więc usunąłem .gitattributes, zatwierdziłem i plik nie był już wyświetlany jako zmodyfikowany.

Simon East
źródło
Usunięcie text=autoustawienia w .gitattributes działało u mnie, a potem po git reset --hardprzywróceniu tego ustawienia pliki nie były już wyświetlane jako zmodyfikowane!
ErikE
2
git checkout dir1/foo.aspx
git checkout dir2/foo.aspx
Steve Prentice
źródło
Miałem „powrót” w mózgu i miałem zamiar pisać checkout. Już próbowałem checkout. Mimo wszystko dziękuję za odpowiedź. To była dobra odpowiedź na moje pierwotne pytanie, więc zagłosuję za.
Greg Hendershott
2

Mogłeś również mieć problem związany z nazwami katalogów wielkości liter. Niektórzy z Twoich kolegów mogli zmienić nazwę katalogu z np. MyHandler na MyHandler . Jeśli później wypchnąłbyś i ściągnął niektóre pliki z oryginalnego katalogu, miałbyś 2 oddzielne katalogi w zdalnym repozytorium ORAZ tylko jeden na twoim lokalnym komputerze, ponieważ w Windows możesz mieć tylko jeden. Masz kłopoty.

Aby sprawdzić, czy tak jest, po prostu sprawdź, czy zdalne repozytorium ma podwójną strukturę.

Aby to naprawić, wykonaj kopię zapasową katalogu nadrzędnego poza repozytorium, a następnie usuń katalog nadrzędny i wypchnij go. Wyciągnij (tutaj, kiedy drugi oznaczony jako usunięty powinien pojawić się na statusie) i naciśnij ponownie. Następnie odtwórz całą strukturę z kopii zapasowej i ponownie prześlij zmiany.

smexy
źródło
2

Myślę, że pomocne byłoby podpowiedź, jak odtworzyć problem , aby lepiej zrozumieć problem:

$ git init
$ echo "*.txt -text" > .gitattributes
$ echo -e "hello\r\nworld" > 1.txt
$ git add 1.txt 
$ git commit -m "committed as binary"
$ echo "*.txt text" > .gitattributes
$ echo "change.." >> 1.txt

# Ok let's revert now

$ git checkout -- 1.txt
$ git status
 modified:   1.txt

# Oooops, it didn't revert!!


# hm let's diff:

$ git diff
 warning: CRLF will be replaced by LF in 1.txt.
 The file will have its original line endings in your working 
 directory.
 diff --git a/1.txt b/1.txt
 index c78c505..94954ab 100644
 --- a/1.txt
 +++ b/1.txt
 @@ -1,2 +1,2 @@
 -hello
 +hello
  world

# No actual changes. Ahh, let's change the line endings...

$ file 1.txt 
 1.txt: ASCII text, with CRLF line terminators
$ dos2unix 1.txt
 dos2unix: converting file 1.txt to Unix format ...
$ git diff
 git diff 1.txt
 diff --git a/1.txt b/1.txt
 index c78c505..94954ab 100644
 --- a/1.txt
 +++ b/1.txt
 @@ -1,2 +1,2 @@
 -hello
 +hello
  world

# No, it didn't work, file is still considered modified.

# Let's try to revert for once more:
$ git checkout -- 1.txt
$ git status
 modified:   1.txt

# Nothing. Let's use a magic command that prints wrongly committed files.

$ git grep -I --files-with-matches --perl-regexp '\r' HEAD

HEAD:1.txt

2 sposób, aby odtworzyć: W powyższym skrypcie zastąpić tę linię:
echo "*.txt -text" > .gitattributes
z
git config core.autocrlf=false
i zachować resztę linii jak


Co mówią wszystkie powyższe informacje? Plik tekstowy można (w pewnych okolicznościach) zatwierdzić za pomocą CRLF (np. -textW .gitattributes/ lub core.autocrlf=false).

Kiedy później będziemy chcieli traktować ten sam plik jako tekst ( -text-> text), będzie musiał zostać ponownie zatwierdzony .
Oczywiście możesz tymczasowo ją cofnąć (zgodnie z poprawną odpowiedzią Abu Assar ). W naszym przypadku:

echo "*.txt -text" > .gitattributes
git checkout -- 1.txt
echo "*.txt text" > .gitattributes

Odpowiedź brzmi : czy naprawdę chcesz to zrobić, ponieważ spowoduje to ten sam problem za każdym razem, gdy zmienisz plik.


Dla przypomnienia:

Aby sprawdzić, które pliki mogą powodować ten problem w repozytorium, wykonaj następujące polecenie (git powinien być skompilowany z --with-libpcre):

git grep -I --files-with-matches --perl-regexp '\r' HEAD

Przekazując plik (i) (zakładając, że chcesz traktować je jako tekst), jest to to samo, co zrobienie tego, co jest proponowane w tym linku http://help.github.com/line-endings/ w celu naprawienia takich problemów . Ale zamiast usuwać .git/indexi wykonywać reset, możesz po prostu zmienić plik (i), następnie wykonać, git checkout -- xyz zyfa następnie zatwierdzić.

Marinos An
źródło
2

Miałem ten sam problem, z ciekawym dodatkiem, że pliki były zmieniane na oknach, ale nie przy oglądaniu ich z WSL. Żadne zamieszanie z zakończeniami linii, resetami itp. Nie było w stanie tego zmienić.

W końcu znalazłem rozwiązanie w tej odpowiedzi . Poniżej znajduje się tekst do przekonania:


Rozwiązałem ten problem, wykonując następujące czynności

1) Usuń każdy plik z indeksu Gita.

git rm --cached -r .

2) Przepisz indeks Git, aby zebrać wszystkie nowe zakończenia linii.

git reset --hard

Rozwiązanie było częścią kroków opisanych na stronie git https://help.github.com/articles/dealing-with-line-endings/

MCO
źródło
1

Ten problem może być również spowodowany tym, że git traktuje różnice w wielkości liter jako różne pliki, ale system Windows traktuje je jako ten sam plik. Jeśli w nazwie pliku zmieniono tylko wielkość liter, to każdy użytkownik systemu Windows tego repozytorium znajdzie się w takiej sytuacji.

Rozwiązaniem jest potwierdzenie, że zawartość plików jest poprawna, a następnie ponowne ich wprowadzenie. Musieliśmy połączyć zawartość obu plików, ponieważ były różne. Następnie pociągnij, a nastąpi konflikt scalania, który możesz rozwiązać, usuwając zduplikowany plik. Ponownie uruchom rozdzielczość scalania i wrócisz do stabilnego stanu.

Andrew West
źródło