Nasıl sıfırlanır - bir alt dizine?


197

GÜNCELLEME² : Git 2.23 (Ağustos 2019) ile git restorebunu yapan yeni bir komut var, kabul edilen cevaba bakın .

GÜNCELLEME : Bu Git 1.8.3'ten itibaren daha sezgisel olarak çalışacaktır, kendi cevabımı görün .

Aşağıdaki kullanım durumunu düşünün: Git çalışma ağacımın belirli bir alt dizinindeki tüm değişikliklerden kurtulmak ve diğer tüm alt dizinleri olduğu gibi bırakmak istiyorum.

Bu işlem için uygun Git komutu nedir?

Aşağıdaki komut dosyası sorunu göstermektedir. How to make filesYorumun altına uygun komutu ekleyin - geçerli komut a/c/ac, seyrek ödeme tarafından hariç tutulması gereken dosyayı geri yükler . O Not yok açıkça geri yüklemek istediğiniz a/ave a/bben sadece "bilmek" ave aşağıdaki her şeyi geri istiyorum. EDIT : Ve ben de "bilmiyorum" b, ya da diğer dizinlerin aynı düzeyde bulunduğu 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

3
ne hakkında git stash && git stash drop?
CharlesB

1
Ne hakkında git checkout -- /path/to/subdir/?
iberbeu

3
@CharlesB: git stashbir yol argümanı kabul etmiyor ...
krlmlr

@iberbeu: Hayır. Ayrıca seyrek ödeme tarafından hariç tutulan dosyalar ekler.
krlmlr

1
@CharlesBailey: O zaman neden "Güvenilir ve / veya resmi kaynaklardan bir cevap çizimi arıyorum" yazan bir radyo düğmesi var. Ödül iletişim kutusunda? Bunu kendim yazmadım! Ayrıca "git reset altdizin" i (tırnak işaretleri olmadan) deneyin ve ilk 3 konumda neler olduğunu görün. Kesinlikle bir kernel.org posta listesine bir mesaj bulmak daha zor olacaktır. - Ayrıca, bu davranışın bir hata veya özellik olup olmadığı henüz net değil.
krlmlr

Yanıtlar:


164

Git 2.23 (Ağustos 2019) ile yeni komutunuz vargit restore

git restore --source=HEAD --staged --worktree -- aDirectory
# or, shorter
git restore -s@ -SW  -- aDirectory

Bu, hem dizini hem de çalışma ağacını HEAD, olduğu gibi reset --hard, ancak belirli bir yol için içerikle değiştirir .


Orijinal cevap (2013)

Not ( Dan Fabulich tarafından yorumlandığı gibi ):

  • git checkout -- <path> donanımdan sıfırlama yapmaz: çalışan ağaç içeriklerini aşamalı içeriklerle değiştirir.
  • git checkout HEAD -- <path>hem dizin hem de çalışma ağacını HEADkomuttaki sürümle değiştirerek yol için sabit sıfırlama yapar .

Şöyle cevap tarafından Ajedi32 , hem çıkış biçimleri hedef revizyonunda silinen dosyaları çıkarmayın .
Çalışma ağacında HEAD'de bulunmayan fazladan dosyalarınız varsa, git checkout HEAD -- <path>bunları kaldırmazsınız.

Not: git checkout --overlay HEAD -- <path>(Git 2.22, Q1 2019) ile , dizinde ve çalışma ağacında görünen ancak içinde olmayan dosyalar tam olarak <tree-ish>eşleşmeleri için kaldırılır <tree-ish>.

Ancak, git update-index --skip-worktree" Hariç tutulan dosyalar neden git seyrek ödememde tekrar görünmeye devam ediyor? " Bölümünde belirtildiği gibi bu ödeme (yoksaymak istediğiniz dizinler için) ile ilgili olabilir .


1
Lütfen açıkla. Sonra git checkout HEAD -- ., seyrek ödeme tarafından hariç tutulan dosyalar yeniden görünür. Ne git update-index --skip-worktreeyapmalı?
krlmlr

@krlmlr skip-worktree veya varsayım-değişmedi git için "görünmez" dizinine bir giriş yapmaya çalışmanın iki yoludur: fallengamer.livejournal.com/93321.html , stackoverflow.com/q/13630849/6309 ve stackoverflow. com / a / 6139470/6309
VonC

@krlmlr bu bağlantılar, 'atlanmış-çalışma ağacı' olarak işaretlendikten sonra bir kasanın yine de bu girişleri geri yükleyip yüklemeyeceğini görmek için sadece işaretçilerdir.
VonC

Üzgünüm, ama eldeki görev için çok karmaşık. Özel bir değil, kapsayıcı bir sıfırlama istiyorum. Git'te bunu yapmanın gerçekten güzel bir yolu yok mu?
krlmlr

@krlmlr no: yapmak için en iyisi git checkout HEAD -- <path>, ve sonra geri yüklenen (ancak seyrek kasada bildirilen) dizinleri silin.
VonC

126

Özelliği ve bir uyumluluk anahtarını nazikçe uygulayan Git geliştiricisi Duy Nguyen'e göre, Git 1.8.3'ten itibaren beklendiği gibi çalışıyor :

git checkout -- a

( adonanımdan sıfırlamak istediğiniz dizin nerede ). Orijinal davranışa

git checkout --ignore-skip-worktree-bits -- a

5
Git geliştirme ekibini takip ettiğiniz için teşekkür ederiz.
Dan Cruz

13
Ve bu durumda "a" nın geri döndürmek istediğiniz dizin anlamına geldiğine dikkat edin, bu nedenle geri döndürmek istediğiniz dizindeyseniz, komut geçerli dizinin olduğu git checkout -- .yerde olmalıdır ..
TheWestIsThe ...

5
Benim tarafımdan bir yorum, ilk önce klasörü bozmanız gerektiğidir git reset -- a(burada a, sıfırlamak istediğiniz dizindir)
Boyan

git reset --hardTüm repoya katılmak istiyorsanız bu doğru değil mi?
krlmlr

Ve bu dizine yeni dosyalar eklediyseniz, daha rm -rf aönce yapın.
Tobias Feil

30

Değiştirmeyi deneyin

git checkout -- a

için

git checkout -- `git ls-files -m -- a`

1.7.0 sürümünden beri Git'in atlamalı çalışma bayrağını onurlandırıyor .ls-files

Test komut dosyanızı çalıştırma (bazı küçük ayarlamalar git commit... git commit -qve git statusdeğiştirerek git status --short) çıktılarını çalıştırma:

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/c/ac
a/b/ab
b/a/ba

Test komut dosyanızı önerilen checkoutdeğişiklik çıktılarıyla çalıştırma:

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/b/ab
b/a/ba

Kulağa iyi geliyor. Ama git checkoutilk etapta "skip-worktree" bitine saygı duymamalısınız?
krlmlr

Atlama-çalışma ağacı bayrağının hızlı bir şekilde gözden geçirilmesi checkout.cve tree.ckullanılmadığını göstermez.
Dan Cruz

Bu komut için bash takma adı ayarlamam gerekse bile, pratikte kullanışlı olacak kadar basit. Duy Nguyen, Git posta listesine mesajımı yanıtladı , yakında daha kullanıcı dostu bir alternatifin ortaya çıkıp çıkmayacağını görelim.
krlmlr

18

Değişiklikleri basitçe atmak için, diğer yanıtların önerdiği git checkout -- path/veya git checkout HEAD -- path/komutları harika çalışır. Ancak, bir dizini HEAD dışında bir düzeltmeye sıfırlamak istediğinizde, bu çözümün önemli bir sorunu vardır: hedef revizyonda silinen dosyaları kaldırmaz.

Bunun yerine, aşağıdaki komutu kullanmaya başladım:

git diff --cached commit -- subdir | git apply -R --index

Bu, hedef kesinleştirme ve dizin arasındaki farkı bularak, daha sonra bu farkı çalışma dizinine ve dizinine ters uygulayarak çalışır. Temel olarak, bu, dizinin içeriğini belirttiğiniz revizyonun içeriğiyle eşleştirdiği anlamına gelir. git diffBir yol argümanı alan gerçek, bu efekti belirli bir dosya veya dizine sınırlamanızı sağlar.

Bu komut oldukça uzun olduğundan ve sık sık kullanmayı planladığım için, adlandırdığım bir takma ad ayarladım reset-checkout:

git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f'

Bu şekilde kullanabilirsiniz:

git reset-checkout 451a9a4 -- path/to/directory

Ya da sadece:

git reset-checkout 451a9a4

Dün yorumunu gördüm ve bugün denedim. Takma adınız yardımcı olur. +1
VonC

Bu seçenek git checkout --overlay HEAD -- <path>@VonC'un cevabında bahsettiği komutla nasıl karşılaştırılır ?
Ehtesh Choudhury

1
@EhteshChoudhury Not git checkout --overlay HEAD -- <path>henüz yayınlanmadı (Git 2.22, 2019 2. Çeyrekte yayınlanacak)
VonC

5

Git dışında nasıl bir şey yapacağımı bilmediğim için burada korkunç bir seçenek sunacağım add commitve pushişte bir alt dizini nasıl "geri döndürdüm":

Yerel bilgisayarımda yeni bir depo başlattım, kodu kopyalamak istediğim taahhüde geri döndüm ve daha sonra bu dosyaları çalışma add commit pushdizinime kopyaladım . Oyuncudan nefret etme, Bay Torvalds'tan hepimizden daha akıllı olduğu için nefret et.


4

Sıfırlama normalde her şeyi değiştirir, ancak git stashtutmak istediğiniz şeyi seçmek için kullanabilirsiniz . Bahsettiğiniz gibi, stashdoğrudan bir yolu kabul etmez, ancak yine de --keep-indexbayrakla belirli bir yolu tutmak için kullanılabilir . Örneğinizde, b dizinini saklar ve ardından diğer her şeyi sıfırlarsınız.

# How to make files a/* reappear without changing b and without recreating a/c?
git add b               #add the directory you want to keep
git stash --keep-index  #stash anything that isn't added
git reset               #unstage the b directory
git stash drop          #clean up the stash (optional)

Bu, komut dosyanızın son bölümünün bunu çıkaracağı bir noktaya götürür:

After checkout:
# On branch master
# Changes not staged for commit:
#
#   modified:   b/a/ba
#
no changes added to commit (use "git add" and/or "git commit -a")
a/a/aa
a/b/ab
b/a/ba

Bunun hedef sonuç olduğuna inanıyorum (b değiştirildi, a / * dosyaları geri döndü, a / c yeniden oluşturulmadı).

Bu yaklaşımın çok esnek olmasının yararı da vardır; bir dizine belirli dosyaları eklemek istediğiniz kadar ayrıntılı olabilir, ancak başka dosyaları değil.


Bu güzel, ama ben git addhariç her şeye ihtiyacım vardı a, değil mi? Uygulamada zor görünüyor.
krlmlr

1
@krlmlr Pek değil. Şunları yapabilirsiniz git add .ardından git reset ahariç her şeyi eklemek için a.
Jonathan Wren

1
@krlmlr Ayrıca, git addsilinen dosyaları eklemediğini belirtmek gerekir . Bu nedenle, yalnızca silinen dosyaları kurtarıyorsanız git add ., değiştirilen tüm dosyaları ekler, ancak silinen dosyaları eklemez.
Jonathan Wren

3

Alt dizinin boyutu özellikle büyük değilse ve CLI'den uzak kalmak istiyorsanız , alt dizini manuel olarak sıfırlamak için hızlı bir çözüm :

  1. Ana şubeye geçin ve sıfırlanacak alt dizini kopyalayın.
  2. Şimdi özellik dalınıza geri dönün ve alt dizini 1. adımda oluşturduğunuz kopyayla değiştirin.
  3. Değişiklikleri yapın.

Şerefe. Özellik dalınızdaki bir alt dizini ana daldakiyle aynı olacak şekilde el ile sıfırlayabilirsiniz


Bu cevap için herhangi bir oy görmedim, ancak bazen bu başarıya giden en basit garantili yoldur.
Sue Spence

1

Ajedi32 bireyin cevabı ben ama bu hata ile karşılaştım bazı kaydedilmesini arıyordu şudur:

error: cannot apply binary patch to 'path/to/directory' without full index line

Dizinin bazı dosyalarının ikili dosya olması olabilir. Git diff komutuna '--binary' seçeneği eklemek sorunu çözdü:

git diff --binary --cached commit -- path/to/directory | git apply -R --index

0

Ne dersin

subdir=thesubdir
for fn in $(find $subdir); do
  git ls-files --error-unmatch $fn 2>/dev/null >/dev/null;
  if [ "$?" = "1" ]; then
    continue;
  fi
  echo "Restoring $fn";
  git show HEAD:$fn > $fn;
done 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.