Git: Jak usunąć plik z indeksu bez usuwania plików z dowolnego repozytorium

163

Kiedy używasz

git rm --cached myfile

nie usuwa się z lokalnego systemu plików, co jest celem. Ale jeśli już utworzyłeś wersję i zatwierdziłeś plik, umieściłeś go w centralnym repozytorium i przeciągnąłeś do innego repozytorium przed użyciem polecenia, usunie plik z tego systemu.

Czy istnieje sposób, aby po prostu usunąć plik z wersjonowania bez usuwania go z dowolnego systemu plików?

Edycja: wyjaśnione, mam nadzieję.

Fletcher Moore
źródło
Być może mógłbyś rozwinąć swój przypadek użycia. Indeks jest obszarem przejściowym dla następnego zatwierdzenia, więc dlaczego chcesz usunąć plik z indeksu, jeśli nie chcesz go usunąć z bieżącej gałęzi?
CB Bailey,
1
Przykro mi. Chciałem powiedzieć, że mój plik został z jakiegoś powodu zatwierdzony i rozpowszechniony dawno temu. Teraz celem jest usunięcie go z wersjonowania bez usuwania go z systemów innych użytkowników. Powodem tego jest to, że przypadkowo utworzyłem wersję pliku konfiguracyjnego. Plik konfiguracyjny jest niezbędny, więc nie chcę go usuwać z obcych systemów.
Fletcher Moore
Zredagowałem pytanie, aby było jaśniejsze.
Fletcher Moore
3
git rm --cached nie usunie pliku z innego katalogu roboczego. Plik zostanie usunięty tylko wtedy, gdy ktoś pracujący w tym katalogu wykona ściąganie. Plik można łatwo odzyskać za pomocą „git checkout HEAD @ {1} foo” (jeśli zostanie wykonany natychmiast po ściągnięciu).
William Pursell

Odpowiedzi:

119

Nie sądzę, aby zatwierdzenie Git mogło zarejestrować intencję typu „przestań śledzić ten plik, ale nie usuwaj go”.

Realizacja takiego zamiaru będzie wymagała interwencji poza Git we wszystkich repozytoriach, które łączą się (lub ponownie bazują na) zatwierdzeniu, które usuwa plik.


Zapisz kopię, zastosuj usunięcie, przywróć

Prawdopodobnie najłatwiejszą rzeczą do zrobienia jest poinformowanie dalszych użytkowników, aby zapisali kopię pliku, wycofali usunięcie, a następnie przywrócili plik. Jeśli ściągają przez rebase i „przenoszą” modyfikacje do pliku, będą miały konflikty. Aby rozwiązać takie konflikty, użyj git rm foo.conf && git rebase --continue(jeśli konfliktowe zatwierdzenie zawiera zmiany inne niż te w usuniętym pliku) lub git rebase --skip(jeśli konfliktujące zatwierdzenie zmieniło się tylko na usunięty plik).

Przywróć plik jako nieśledzony po wyciągnięciu zatwierdzenia, które go usuwa

Jeśli już wyciągnęli zatwierdzenie usunięcia, nadal mogą odzyskać poprzednią wersję pliku za pomocą git show :

git show @{1}:foo.conf >foo.conf

Lub z git checkout (za komentarzem Williama Pursella; ale pamiętaj, aby ponownie usunąć go z indeksu!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

Jeśli podjęli inne działania od czasu usunięcia twojego usunięcia (lub ciągną z rebase do odłączonego HEAD), mogą potrzebować czegoś innego niż @{1}. Mogliby użyć git log -gdo znalezienia zatwierdzenia tuż przed usunięciem.


W komentarzu wspominasz, że plik, który chcesz „odśledzić, ale zachować” to jakiś rodzaj pliku konfiguracyjnego, który jest wymagany do uruchomienia oprogramowania (bezpośrednio z repozytorium).

Zachowaj plik jako „domyślny” i aktywuj go ręcznie / automatycznie

Jeśli dalsze utrzymywanie zawartości pliku konfiguracyjnego w repozytorium nie jest całkowicie niedopuszczalne, możesz zmienić nazwę śledzonego pliku z (np.) foo.confNa, foo.conf.defaulta następnie poinstruować użytkowników, aby cp foo.conf.default foo.confpo zastosowaniu zmiany nazwy. Lub, jeśli użytkownicy już używają jakiejś istniejącej części repozytorium (np. Skryptu lub innego programu skonfigurowanego przez zawartość repozytorium (np. MakefileLub podobnego)) do uruchamiania / wdrażania oprogramowania, możesz włączyć mechanizm domyślny do uruchomienia / proces wdrażania:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

Mając taki mechanizm domyślny, użytkownicy powinni być w stanie pobrać zatwierdzenie, które zmienia nazwę foo.confna foo.conf.defaultbez konieczności wykonywania dodatkowej pracy. Ponadto unikniesz konieczności ręcznego kopiowania pliku konfiguracyjnego, jeśli w przyszłości wykonasz dodatkowe instalacje / repozytoria.

Przepisanie historii i tak wymaga ręcznej interwencji…

Jeśli utrzymywanie treści w repozytorium jest niedopuszczalne, prawdopodobnie będziesz chciał całkowicie usunąć ją z historii za pomocą czegoś podobnego git filter-branch --index-filter …. Sprowadza się to do przepisywania historii, co będzie wymagało ręcznej interwencji dla każdej gałęzi / repozytorium (zobacz sekcję „Recovering From Upstream Rebase” na stronie podręcznika git rebase ). Specjalne traktowanie wymagane dla twojego pliku konfiguracyjnego byłoby tylko kolejnym krokiem, który należy wykonać podczas odzyskiwania po przepisaniu:

  1. Zapisz kopię pliku konfiguracyjnego.
  2. Odzyskaj po przepisaniu.
  3. Przywróć plik konfiguracyjny.

Zignoruj ​​to, aby zapobiec nawrotom

Bez względu na to, jakiej metody użyjesz, prawdopodobnie będziesz chciał dołączyć nazwę pliku konfiguracyjnego do .gitignorepliku w repozytorium, aby nikt nie mógł niechcący git add foo.confponownie (jest to możliwe, ale wymaga -f/ --force). Jeśli masz więcej niż jeden plik konfiguracyjny, możesz rozważyć `` przeniesienie '' ich wszystkich do jednego katalogu i zignorowanie całej rzeczy (przez `` przeniesienie '' mam na myśli zmianę miejsca, w którym program spodziewa się znaleźć swoje pliki konfiguracyjne i pobranie użytkowników (lub mechanizm uruchamiania / wdrażania), aby skopiować / przenieść pliki do ich nowej lokalizacji; oczywiście nie chcesz umieszczać pliku mv w katalogu, który będziesz ignorować).

Chris Johnsen
źródło
5
Niezwykle dokładna odpowiedź. Dziękuję Ci!
Fletcher Moore
Poniższa odpowiedź Toma Powera wydaje się zaprzeczać Twojemu pierwszemu zdaniu.
Mike S
1
@MikeS: Efekt --{,no-}assume-unchangedjest czysto lokalny: jego stan nie jest bezpośrednio zapisywany w zatwierdzeniach. Może pomóc zapobiec zatwierdzaniu przez repozytorium nowych zmian w pliku, ale nie usuwa go z kontroli wersji. Jeśli możesz ustawić to dla wszystkich odpowiednich, powiązanych, nie-gołych repozytoriów, może to pomóc w twojej sytuacji, ale nie jest to coś, co możesz bezpośrednio wypchnąć + pull / rebase do innych powiązanych, nie-gołych repozytoriów (szczególnie tych, które nie kontrolujesz, zgodnie z wyjaśniającymi komentarzami pierwotnego pytającego do pytania: patrz „systemy ludzi” / „systemy obce”).
Chris Johnsen
1
I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.- teraz może, zgit rm --cached foo.conf
Nick Volynkin
1
@NickVolynkin: Czy pytanie nie wskazuje już, że jest to nieodpowiednie dla celu pytającego: (automatycznie) utrzymywanie pliku, gdy wynikowe zatwierdzenie jest wciągane do innego repozytorium?
Chris Johnsen
86

Miałem ten sam problem w tym tygodniu, kiedy przypadkowo popełniłem, a następnie próbowałem usunąć plik kompilacji ze współdzielonego repozytorium, a to:

http://gitready.com/intermediate/2009/02/18/temporary-ignoring-files.html

działa dobrze dla mnie i do tej pory nie wspomniano.

git update-index --assume-unchanged <file>

Aby usunąć plik, który Cię interesuje, z kontroli wersji, użyj wszystkich innych poleceń w normalny sposób.

git update-index --no-assume-unchanged <file>

Jeśli kiedykolwiek chciałeś go włożyć z powrotem.

Edycja: proszę zapoznać się z komentarzami Chrisa Johnsena i KPM, działa to tylko lokalnie, a plik pozostaje pod kontrolą wersji dla innych użytkowników, jeśli oni również tego nie robią. Zaakceptowana odpowiedź podaje pełniejsze / poprawne metody radzenia sobie z tym problemem. Również kilka uwag z linku, jeśli używasz tej metody:

Oczywiście wiąże się to z kilkoma zastrzeżeniami. Jeśli dodasz plik bezpośrednio, zostanie on dodany do indeksu. Scalanie zatwierdzenia z tą flagą spowoduje, że scalanie zakończy się niepowodzeniem, więc możesz obsłużyć to ręcznie.

Tom Power
źródło
7
To nie jest odpowiedź na wspomniany konkretny problem. Będzie działać tylko dla twojego lokalnego repozytorium, więc każdy użytkownik musi to zrobić sam. To ból.
KPM
28

Aby usunąć plik z indeksu, użyj:

git reset myfile

Nie powinno to mieć wpływu na twoją lokalną kopię ani nikogo innego.

Armand
źródło
1
resetusuwa plik z indeksu tylko wtedy, gdy plik nie jest w bieżącym zatwierdzeniu HEAD, w przeciwnym razie po prostu przywraca wersję indeksu do bieżącej wersji HEAD.
CB Bailey,
1
Być może źle zrozumiałem pytanie, ale wydaje się, że dotyczy to usunięcia pliku z indeksu bez wpływu na jakiekolwiek zatwierdzenia.
Armand
Jak powiedział Charles, resetowanie nie „usuwa pliku”. Wpisz polecenie git help reset, aby uzyskać więcej informacji.
Simon B.
4
To odpowiada na pytanie zatytułowane „Jak usunąć plik z indeksu bez usuwania plików z jakiegokolwiek repozytorium”. Chociaż OP naprawdę pytał „Jak odśledzić plik bez usuwania kopii lokalnej”.
hewsonizm
@hewsonism OP powiedział „dowolny system plików” (nawet przed jakąkolwiek edycją).
jbobbins
19
  1. git rm --cached remove_file
  2. dodaj plik do gitignore
  3. git add .gitignore
  4. git commit -m "Excluding"
  5. Baw się dobrze ;)
Анастасия Ермолик
źródło
1
To jest najłatwiejsze.
Julien
15

Po wykonaniu git rm --cachedpolecenia spróbuj dodać plik myfiledo .gitignorepliku (utwórz go, jeśli nie istnieje). Powinno to nakazać gitowi ignorowanie myfile.

.gitignorePlik oznaczenia wersji, więc musisz zobowiązać go i przesunąć go do zdalnego repozytorium.

awgy
źródło
1

Moim rozwiązaniem jest pobranie drugiej kopii roboczej, a następnie:

git log --pretty="format:" --name-only -n1 | xargs git checkout HEAD^1

który mówi, że pobierz wszystkie ścieżki plików w najnowszym komentarzu i sprawdź je od rodzica HEAD. Zadanie wykonane.

Tom Viner
źródło
0

Powyższe rozwiązania działają dobrze w większości przypadków. Jeśli jednak chcesz również usunąć wszystkie ślady tego pliku (tj. Poufne dane, takie jak hasła), będziesz również chciał usunąć go z całej historii zatwierdzeń, ponieważ plik nadal może zostać pobrany z tego pliku.

Oto rozwiązanie, które usuwa wszystkie ślady pliku z całej historii zmian, tak jakby nigdy nie istniał, ale utrzymuje plik na miejscu w systemie.

https://help.github.com/articles/remove-sensitive-data/

W rzeczywistości możesz przejść do kroku 3, jeśli jesteś w lokalnym repozytorium git i nie musisz wykonywać testu na sucho. W moim przypadku potrzebowałem tylko kroków 3 i 6, ponieważ utworzyłem już plik .gitignore i byłem w repozytorium, nad którym chciałem pracować.

Aby zobaczyć zmiany, może być konieczne przejście do katalogu głównego GitHub repozytorium i odświeżenie strony. Następnie przejdź przez linki, aby dostać się do starego zatwierdzenia, które kiedyś miało plik, i zobacz, że został on usunięty. Dla mnie zwykłe odświeżenie starej strony zmian nie pokazało zmiany.

Z początku wyglądało to onieśmielająco, ale naprawdę było łatwe i działało jak urok! :-)

SherylHohman
źródło