247

UPDATE² : Git 2.23 (2019 年 8 月) では、これを行う新しいコマンドがあります。受け入れられた回答git restoreを参照してください。

更新:これはGit 1.8.3の時点でより直感的に機能します。私自身の回答を参照してください。

次の使用例を想像してみてください: Git 作業ツリーの特定のサブディレクトリにあるすべての変更を取り除き、他のすべてのサブディレクトリはそのままにしたいと考えています。

この操作に適した Git コマンドは何ですか?

以下のスクリプトは、この問題を示しています。How to make filesコメントの下に適切なコマンドを挿入します。現在のコマンドはa/c/ac、スパース チェックアウトによって除外されるはずのファイルを復元します。明示的に復元したくないことに注意してくださいa/aand a/b、私は「知っている」だけで、a以下のすべてを復元したい. EDIT:そして、私は「知らない」b、または他のどのディレクトリが と同じレベルにあるかも知りません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
4

9 に答える 9

248

Git 2.23 (2019 年 8 月) では、新しいコマンドgit restoreがあります(ここにも示されています) 。

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

これは、インデックスと作業ツリーの両方をHEADコンテンツに置き換えますreset --hardが、特定のパスを対象としています。


元の回答 (2013)

Dan Fabulichのコメントによると)次のことに注意してください。

  • git checkout -- <path>ハード リセットは行いません。作業ツリーの内容をステージングされた内容に置き換えます。
  • git checkout HEAD -- <path>パスのハード リセットを実行し、インデックスと作業ツリーの両方をHEADコミットのバージョンに置き換えます。

Ajedi32の回答よると、両方のチェックアウト フォームは、ターゲット リビジョンで削除されたファイルを削除しません
HEAD に存在しない余分なファイルが作業ツリーにある場合、agit checkout HEAD -- <path>はそれらを削除しません。

注: git checkout --overlay HEAD -- <path>(Git 2.22, Q1 2019)では、インデックスとワーキング ツリーに表示されるが、表示されないファイル<tree-ish>は削除され、完全に一致<tree-ish>します。

しかし、そのチェックアウトはgit update-index --skip-worktree(無視したいディレクトリに対して) を尊重することができます

于 2013-03-14T08:51:14.530 に答える
138

この機能と互換性スイッチを親切に実装した Git 開発者の Duy Nguyen 氏によると、Git 1.8.3では次のように動作します。

git checkout -- a

aハードリセットするディレクトリはどこですか)。元の動作には次の方法でアクセスできます

git checkout --ignore-skip-worktree-bits -- a
于 2013-05-16T14:00:27.297 に答える
32

変更してみる

git checkout -- a

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

バージョン 1.7.0 以降、Gitは skip-worktree フラグを尊重します。ls-files

テスト スクリプトを実行すると (いくつかの微調整を加えてgit commit...git commit -qgit statusに変更git status --short)、次のように出力されます。

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

提案されたcheckout変更出力でテスト スクリプトを実行します。

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
于 2013-03-16T22:26:45.180 に答える
4

リセットは通常すべてを変更しますが、git stash保持したいものを選択するために使用できます。あなたが述べたようにstash、パスを直接受け入れませんが、--keep-indexフラグを使用して特定のパスを保持するために使用できます。あなたの例では、 b ディレクトリを隠してから、他のすべてをリセットします。

# 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)

これにより、スクリプトの最後の部分が次のように出力されるようになります。

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

これが目標の結果だと思います (b は変更されたままで、a/* ファイルは元に戻り、a/c は再作成されません)。

このアプローチには、非常に柔軟であるという追加の利点があります。ディレクトリに特定のファイルを追加するだけで、他のファイルは追加しないように、必要に応じてきめ細かく設定できます。

于 2013-03-16T22:26:00.547 に答える