AKTUALIZACJA² : W Git 2.23 (sierpień 2019) pojawiło się nowe polecenie,
git restore
które to robi, zobacz przyjętą odpowiedź .AKTUALIZACJA : Będzie działać bardziej intuicyjnie od wersji Git 1.8.3, zobacz moją własną odpowiedź .
Wyobraź sobie następujący przypadek użycia: Chcę pozbyć się wszystkich zmian w określonym podkatalogu mojego drzewa roboczego Git, pozostawiając wszystkie pozostałe podkatalogi nietknięte.
Mogę
git checkout .
, ale idź do kasy. dodaje katalogi wykluczone przez rzadkie pobranieJest
git reset --hard
, ale nie pozwoli mi to zrobić dla podkatalogu:> git reset --hard . fatal: Cannot do hard reset with paths.
Znowu: Dlaczego git nie może wykonać twardego / miękkiego resetu według ścieżki?
Mogę odwrócić łatkę bieżącego stanu za pomocą
git diff subdir | patch -p1 -R
, ale jest to dość dziwny sposób na zrobienie tego.
Jakie jest właściwe polecenie Git dla tej operacji?
Poniższy skrypt ilustruje problem. Wstaw właściwe polecenie poniżej How to make files
komentarza - bieżące polecenie przywróci plik, a/c/ac
który powinien zostać wykluczony przez rzadkie pobieranie. Pamiętaj, że nie chcę jawnie przywracać, a/a
a a/b
jedynie „wiem” a
i chcę przywrócić wszystko poniżej. EDYCJA : I też nie „wiem” b
, ani które inne katalogi znajdują się na tym samym poziomie co a
.
#!/bin/sh
rm -rf repo; git init repo; cd repo
for f in a b; do
for g in a b c; do
mkdir -p $f/$g
touch $f/$g/$f$g
git add $f/$g
git commit -m "added $f/$g"
done
done
git config core.sparsecheckout true
echo a/a > .git/info/sparse-checkout
echo a/b >> .git/info/sparse-checkout
echo b/a >> .git/info/sparse-checkout
git read-tree -m -u HEAD
echo "After read-tree:"
find * -type f
rm a/a/aa
rm a/b/ab
echo >> b/a/ba
echo "After modifying:"
find * -type f
git status
# How to make files a/* reappear without changing b and without recreating a/c?
git checkout -- a
echo "After checkout:"
git status
find * -type f
źródło
git stash && git stash drop
?git checkout -- /path/to/subdir/
?git stash
nie akceptuje argumentu ścieżki ...Odpowiedzi:
W Git 2.23 (sierpień 2019 r.) Masz nowe polecenie
git restore
To zastąpiłoby zarówno indeks, jak i drzewo robocze
HEAD
zawartością, tak jakreset --hard
by to zrobiło, ale dla określonej ścieżki.Oryginalna odpowiedź (2013)
Zauważ ( skomentowane przez Dana Fabulicha ), że:
git checkout -- <path>
nie wykonuje twardego resetu: zastępuje zawartość drzewa roboczego zawartością etapową.git checkout HEAD -- <path>
wykonuje twardy reset ścieżki, zastępując zarówno indeks, jak i drzewo robocze wersją zHEAD
zatwierdzenia.Jak odpowiedział przez Ajedi32 , obie formy Zamówienie nie usuwać pliki, które zostały usunięte w rewizji docelowego .
Jeśli masz dodatkowe pliki w drzewie roboczym, które nie istnieją w HEAD, a
git checkout HEAD -- <path>
nie usuwa ich.Uwaga: W przypadku
git checkout --overlay HEAD -- <path>
(Git 2.22, I kwartał 2019 r.) Pliki pojawiające się w indeksie i drzewie roboczym, ale nie w,<tree-ish>
są usuwane, aby były<tree-ish>
dokładnie dopasowane .Ale ta kasa może respektować
git update-index --skip-worktree
(dla tych katalogów, które chcesz zignorować), jak wspomniano w „ Dlaczego wykluczone pliki pojawiają się ponownie w mojej git rzadkiej kasie? ”.źródło
git checkout HEAD -- .
ponownie pojawiają się pliki wykluczone przez rzadkie pobieranie. Cogit update-index --skip-worktree
ma robić?git checkout HEAD -- <path>
, a następnie usunąć katalogi, które zostały przywrócone (ale nadal są zadeklarowane w rzadkim kasie).Według dewelopera Git Duy Nguyen, który uprzejmie wdrożył tę funkcję i przełącznik zgodności , następujące działania działają zgodnie z oczekiwaniami od wersji Git 1.8.3 :
(gdzie
a
jest katalog, który chcesz zresetować na stałe). Oryginalne zachowanie można uzyskać za pośrednictwemźródło
git checkout -- .
tam, gdzie.
oznacza bieżący katalog.git reset -- a
(gdzie jest katalog, który chcesz zresetować)git reset --hard
całe repo?rm -rf a
wcześniej.Spróbuj zmienić
do
Od wersji 1.7.0 Git honoruje flagę skip-worktree .
ls-files
Uruchomienie skryptu testowego (z kilkoma drobnymi poprawkami zmieniającymi
git commit
... nagit commit -q
igit status
nagit status --short
) wyniki:Uruchamianie skryptu testowego z proponowanymi
checkout
wynikami zmian:źródło
git checkout
przede wszystkim szanować drzewka „pomiń pracę”?checkout.c
itree.c
nie ujawnia, że używana jest flaga skip-worktree.W przypadku zwykłego odrzucania zmian polecenia
git checkout -- path/
lubgit checkout HEAD -- path/
polecenia sugerowane przez inne odpowiedzi działają świetnie. Jeśli jednak chcesz zresetować katalog do wersji innej niż HEAD, to rozwiązanie ma poważny problem: nie usuwa plików, które zostały usunięte w wersji docelowej.Zamiast tego zacząłem używać następującego polecenia:
Działa to poprzez znalezienie różnicy między docelowym zatwierdzeniem a indeksem, a następnie zastosowanie tej różnicy w odwrotnej kolejności do katalogu roboczego i indeksu. Zasadniczo oznacza to, że dopasowuje zawartość indeksu do treści określonej wersji. Fakt, że
git diff
pobiera argument ścieżki, pozwala ograniczyć ten efekt do określonego pliku lub katalogu.Ponieważ to polecenie jest dość długie i planuję go często używać, skonfigurowałem dla niego alias, który nazwałem
reset-checkout
:Możesz użyć tego w następujący sposób:
Lub tylko:
źródło
git checkout --overlay HEAD -- <path>
wypada w porównaniu z poleceniem, które @VonC wspomina w swojej odpowiedzi?git checkout --overlay HEAD -- <path>
nie została jeszcze wydana (Git 2.22 zostanie wydany w drugim kwartale 2019 r.)Mam tutaj do zaoferowania straszną opcję, ponieważ nie mam pojęcia, jak zrobić cokolwiek z Gitem,
add
commit
apush
oto, w jaki sposób „przywróciłem” podkatalog:Założyłem nowe repozytorium na moim komputerze lokalnym, przywróciłem całą treść do zatwierdzenia, z którego chciałem skopiować kod, a następnie skopiowałem te pliki do mojego katalogu roboczego,
add
commit
push
et voila. Nie nienawidź gracza, nienawidź pana Torvaldsa za to, że jest mądrzejszy od nas wszystkich.źródło
Reset zwykle zmienia wszystko, ale możesz użyć,
git stash
aby wybrać to, co chcesz zachować. Jak wspomniałeś,stash
nie akceptuje ścieżki bezpośrednio, ale nadal można jej użyć do utrzymania określonej ścieżki z--keep-index
flagą. W twoim przykładzie przechowujesz katalog b, a następnie resetujesz wszystko inne.To prowadzi cię do punktu, w którym ostatnia część skryptu wyświetli:
Myślę, że to był wynik docelowy (b pozostaje zmodyfikowany, pliki a / * powróciły, a / c nie zostało odtworzone).
Takie podejście ma tę dodatkową zaletę, że jest bardzo elastyczne; możesz uzyskać tak szczegółowe, jak chcesz, dodawanie określonych plików, ale nie innych, do katalogu.
źródło
git add
wszystko oprócza
, prawda? Brzmi trudnie w praktyce.git add .
Następnie możeszgit reset a
dodać wszystko oprócza
.git add
nie dodaje usuniętych plików. Tak więc, jeśli odzyskujesz tylko usunięte pliki,git add .
dodasz wszystkie zmodyfikowane pliki, ale nie usunięte.Jeśli wielkość podkatalogu nie jest szczególnie duża ORAZ chcesz trzymać się z dala od interfejsu CLI, oto szybkie rozwiązanie, aby ręcznie zresetować podkatalog:
Twoje zdrowie. Wystarczy ręcznie zresetować podkatalog w gałęzi funkcji, aby był taki sam jak w gałęzi master !!
źródło
Ajedi32 „s odpowiedź jest to, czego szukałem, ale dla niektórych zobowiązuje wpadłem na tego błędu:
error: cannot apply binary patch to 'path/to/directory' without full index line
Być może niektóre pliki katalogu są plikami binarnymi. Naprawiono to, dodając opcję „--binary” do polecenia git diff:
źródło
Co powiesz na
źródło