13

私はGCC git ミラーを使用しています。C と C++ のフロント エンドしか使用していないため、git のスパース チェックアウト機能を使用して、必要のない何百ものファイルを除外しています。

$ git config core.sparseCheckout
true
$ cat .git/info/sparse-checkout 
/*
!gnattools/
!libada/
!libgfortran/
!libgo/
!libjava/
!libobjc/
!libquadmath/
!gcc/ada/
!gcc/fortran/
!gcc/go/
!gcc/java/
!gcc/objc/
!gcc/objcp/
!gcc/testsuite/ada/
!gcc/testsuite/gfortran.dg/
!gcc/testsuite/gfortran.fortran-torture/
!gcc/testsuite/gnat.dg/
!gcc/testsuite/go.dg/
!gcc/testsuite/go.go-torture/
!gcc/testsuite/go.test/
!gcc/testsuite/objc/
!gcc/testsuite/objc.dg/
!gcc/testsuite/obj-c++.dg/
!gcc/testsuite/objc-obj-c++-shared/

これはしばらくは機能しますが、時々、除外されたファイルの一部が返されていることに気付きます

$ ls gnattools/
ChangeLog  configure  configure.ac  Makefile.in
$ ls  gcc/fortran/ | wc -l 
86

ファイルがいつ再表示されるか正確にはわかりません。さまざまなブランチ (リモート追跡とローカルの両方) に何度も切り替えており、非常に忙しいレポであるため、頻繁にプルする新しい変更があります。

gitの比較的初心者として、作業ツリーを「リセット」してそれらのファイルを再度削除する方法がわかりません。

実験として、sparseCheckout を無効にしてプルしてみました。後で sparseCheckout を再度有効にして、何らかの方法でツリーを更新できると考えましたが、うまくいきませんでした。

$ git config core.sparseCheckout false
$ git config core.sparseCheckout 
false
$ git pull
remote: Counting objects: 276, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 117 (delta 98), reused 0 (delta 0)
Receiving objects: 100% (117/117), 64.05 KiB, done.
Resolving deltas: 100% (98/98), completed with 64 local objects.
From git://gcc.gnu.org/git/gcc
   7618909..0984ea0  gcc-4_5-branch -> origin/gcc-4_5-branch
   b96fd63..bb95412  gcc-4_6-branch -> origin/gcc-4_6-branch
   d2cdd74..2e8ef12  gcc-4_7-branch -> origin/gcc-4_7-branch
   c62ec2b..fd9cb2c  master     -> origin/master
   2e2713b..29daec8  melt-branch -> origin/melt-branch
   c62ec2b..fd9cb2c  trunk      -> origin/trunk
Updating c62ec2b..fd9cb2c
error: Your local changes to the following files would be overwritten by merge:
        gcc/fortran/ChangeLog
        gcc/fortran/iresolve.c
        libgfortran/ChangeLog
        libgfortran/io/intrinsics.c
Please, commit your changes or stash them before you can merge.
Aborting

どうやら、私が要求したことのないファイルにローカルで変更を加えたようで、AFAIKが触れたこともありません!

しかし、git statusそれらの変更は表示されません:

$ git st
# On branch master
# Your branch is behind 'origin/master' by 9 commits, and can be fast-forwarded.
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       libstdc++-v3/53270.txt
#       libstdc++-v3/TODO

試してみましgit read-tree -m -u HEADたが、何もしません。

だから私の質問は:

  • ファイルが再表示されるのはなぜですか?
  • それらを再び非表示にするにはどうすればよいですか?
  • 彼らが戻ってくるのを防ぐにはどうすればよいですか?
  • これは、私のファイルに、ファイル内で除外される (つまり、 で名前が付けられている).git/info/excludeはずのディレクトリ内のファイルへの参照が含まれているという事実に関連している可能性がありますか? 指示に従って、SVNと同じファイルを無視しました!sparse-checkout

    $ git svn show-ignore >> .git/info/exclude

したがって、私のexcludeファイルには次のようなパスが含まれています

# /gcc/fortran/
/gcc/fortran/TAGS
/gcc/fortran/TAGS.sub
/gcc/fortran/gfortran.info*

sparse-checkoutファイルで名前が付けられたディレクトリのいずれかの下になります。

!gcc/fortran/

いくつかのコピーを複製してそれぞれを編集し、ブランチを作成/切り替え/削除し、それらの間で変更をマージするテストリポジトリで問題を再現しようとしましたが、私のおもちゃのテストケースでは決してうまくいきません。GCC リポジトリは少し大きく (2GB 以上)、「失敗」の間隔 (1 ~ 2 週間程度) が長すぎて、人々が問題を正確に再現しようとすることは期待できません。とで同じパスを使用することは試していません。競合が発生する可能性があることは、今日だけに思い浮かびました。sparse-checkoutexclude

数週間前に #git on freenode でこれについて尋ねたところ、IIRC は基本的に「おそらくバグであり、誰もスパース チェックアウトを使用していない」と言われましたが、より良い答えを期待しています ;-)

アップデート:

問題が実際に発生するのを最近見たとき (つまり、ファイルがそこになく、単一のコマンドの後に表示された) は、上流のオリジンからプルを実行していました。

   bac6f1f..6c760a6  master     -> origin/master

表示された変更の中には、次の名前の変更が含まれていました。

 create mode 100644 libgo/go/crypto/x509/root.go
 rename libgo/go/crypto/{tls => x509}/root_darwin.go (90%)
 rename libgo/go/crypto/{tls => x509}/root_stub.go (51%)
 rename libgo/go/crypto/{tls => x509}/root_unix.go (76%)
 create mode 100644 libgo/go/crypto/x509/root_windows.go

必要に応じて、プルの前にlibgoディレクトリは存在しませんでした。dir が存在し、これらのファイル (およびその他のファイル) がその下にあることをプルした後:

$ ls libgo/go/crypto/x509/root_<TAB>
root_darwin.go  root_stub.go    root_unix.go    

名前を変更したファイルのskip-worktreeビットが失われたかどうかわかりません。どうすれば確認できますか?

libgfortran/ChangeLogたとえば、上記の例に示されているファイルは新しいファイルではないか、最近名前が変更されたものではないため、名前が変更されたときに常に問題が発生するとは限りません。

4

3 に答える 3

4

スキップ ワークツリー ビットは で変更できますgit update-index --skip-worktree。存在するファイルに気付いたら、確認できますgit ls-files -v |grep ^S(S は、skip-worktree でマークされたファイルです)。

しかし、#git 関係者が言うように、奇妙な動作が見られる場合は、git のバグである可能性が最も高いです。結局のところ、これは非常に難解な機能です。おそらく、調査結果を git メーリング リストに報告する必要があります。

編集: また、git 1.7.7.6 を使用している場合は、アップグレードすることを強くお勧めします。1.7.10 ツリーはかなり進んでおり、問題が解決される可能性が高いと思います。

于 2012-06-27T08:17:46.410 に答える
1

私の場合、スパース チェックアウトを使用してレポでいくつかの単体テストを実行していました。私のテスト ケースの 1 つは、スパース チェックアウト サブツリー リストに含まれていないファイルを含むコミットを作成しました。

しようとしたところgit reset --hard 123456、次のエラーが表示されました。

error: Entry 'a.c' not uptodate. Cannot update sparse checkout.
fatal: Could not reset index file to revision '123456'.

解決策は、sparse-checkout ルールを再適用して作業ツリー内のファイルを削除することでした。

git read-tree -mu HEAD
于 2015-11-19T21:45:39.233 に答える
1

最新の Git 2.13 (2017 年第 2 四半期、5 年後) でも問題が解決しないかどうかを確認します。
次の理由により、スキップ ワークツリー ファイルは、スパース チェックアウト中に変更したり、参照したりしないでください。

preload-indexコードは、「スパース チェックアウト」によってチェックアウトされないパスであるインデックス エントリを気にしないように教えられています。

Jeff Hostetlerによるcommit e596acc (2017 年 2 月 10 日)参照してください。( 2017 年 2 月 27 日コミット c7e234fJunio C Hamanoによってマージされました)jeffhostetler
gitster

preload-index:アイテムlstatは避けるskip-worktree

スキップ ワークツリー ビットが設定されたインデックス エントリの呼び出しpreload-indexを避けるように教えます。 これはパフォーマンスの最適化です。lstat()

sparse-checkout の間、skip-worktree ビットは、入力されていないためワークツリーに存在しないアイテムに設定されます。
スレッドごとの preload-index ループは、ワークツリーのバージョンとインデックスを比較して最新のマークを付けようとするときに、各インデックス エントリに対して一連のテストを実行します。
このパッチは機能するショートカットです。

非常に大きなレポ (450MB インデックス) とさまざまなレベルのスパース性を持つ Windows 10 システムでは、さまざまなコマンドのパフォーマンスが{preloadindex=true, fscache=false}ケースで 80%、{preloadindex=true, fscache=true}ケースで 20% 向上しました。


Git 2.27 (2020 年第 2 四半期) では、" sparse-checkout" はスキップ ワークツリーを別の方法で管理します。

コミット5644CA2、コミット681C637 コミットEBB568B コミット22AB0B3、6271D77コミット、 1AC83F4のコミット、 CD002C1のコミット、コミット4EE5D50 、F56F31Aコミット7AF7A25コミット30E89C1コミット3CHIC50505064コミットメントを参照してくださいd7dc1e1コミット 031ba55 (2020 年 3 月 27 日)、Elijah Newren ( )による。newren
( 2020 年 4 月 29 日、コミット 48eee46Junio C Hamanoによってマージされました)gitster

unpack-trees: ビットの設定に失敗するとSKIP_WORKTREE、常に単なる警告になります

レビュー者: Derrick Stolee
署名者: Elijah Newren

ビットの設定とクリアは、SKIP_WORKTREEユーザーが「sparse-checkout」を実行したときだけではありません。「チェックアウト」などの他のコマンドも実行されunpack_trees()、この特別なビットを処理するためのロジックがあります。そのため、特殊なケースをどのように処理するかを検討する必要があります。

unpack_trees()いくつかの比較ポイントは、これらのビットの処理方法を変更する理由を説明するのに役立ちます。

  • まばらなチェックアウトをしばらく無視します。ブランチを切り替えていてダーティな変更がある場合、ダーティ ファイルがたまたま異なる内容のパスの 1 つであった場合に、ブランチの切り替えが成功しないエラーと見なされるだけです。

  • SKIP_WORKTREE常に助言と見なされてきました。たとえば、リベースまたはマージが必要な場合、または作業の一部としてパスを具体化したい場合でも、SKIP_WORKTREE設定に関係なく、常にそうすることが許可されています。
    これはマージされていないパスに使用されてきましたが、コードを単純にするという理由だけで必要のないパスによく使用されていました。
    これはベスト エフォート型の考慮事項であり、設定に反するパスが発生した場合SKIP_WORKTREE、警告メッセージを出力する必要さえありませんでした。

過去に「git checkout」などを実行しようとした場合:

  1. 具体化されたパスがあり、いくつかの汚い変更がありました
  2. パスは $GITDIR/info/sparse-checkout にリストされていました
  3. このパスは、現在のブランチとターゲット ブランチの間で違いはありませんでした

上記の比較ポイントにもかかわらず、設定できないことは、チェックアウト操作を中止するハードSKIP_WORKTREEエラーとして扱われました。

これは他の場所での の処理方法と完全に矛盾してSKIP_WORKTREEおり、(簡単な警告で) パスが作業コピーに実体化されたままになっているため、ユーザーにとっては何の問題もありません。

SKIP_WORKTREEビットを警告に切り替えることができないことからエラーをダウングレードし、操作を続行できるようにします。

したがって、メッセージは次のようにはなりません。

error: The following untracked working tree files would be overwritten by checkout:

しかし:

warning: The following paths were already present and thus not updated despite sparse patterns:

Git 2.28 (Q3 2020) で、"git clone --no-checkout" のままの状態で "sparse-checkout" の挙動が 2.27 で誤って変更されていたので修正しました。

Elijah Newren ( )によるcommit b5bfc08 (2020 年 6 月 5 日)を参照してください。( 2020 年 6 月 18 日コミット a554228Junio C Hamanoによってマージされました)newren
gitster

sparse-checkout: すべてのファイルのステージング削除を避ける

署名者: Elijah Newren

sparse-checkout の目的は、作業ツリーを更新して、追跡されたファイルのサブセットを反映させることです。

そのため、ブランチの切り替え、コミットの作成、データのダウンロードまたはアップロード、変更のステージングまたはステージング解除を行うべきではありません。

ワークツリーを更新する以外に、sparse-checkout が触れる必要があるのSKIP_WORKTREEは、インデックスのビットだけです。

特に、これは素晴らしい不変条件を設定します: sparse-checkout を実行しても、ファイルのステータスは決して変更されません(ファイルが安全に削除できる場合、つまりファイルが変更されていない場合git statusにのみビットを設定するという事実を反映しています)。SKIP_WORKTREE

伝統的に、私たちは_reallyこの目標に対して _ 悪い仕事をしました。

sparse-checkout の前身は、 .git/info/sparse-checkout を手動で編集して実行する必要がありgit read-tree -mu HEADました。

そのコマンドは、変更をステージングおよびステージング解除し、作業ツリー内のダーティ チェンジを上書きします。

sparse-checkout コマンドの最初の実装はそれほど良くありませんでした。単にgit read-tree -mu HEADサブプロセスとして呼び出され、同じ警告がありましたが、この問題はレビュー コメントで繰り返し取り上げられ、機能がマージされる前に問題の回避策が導入されました [1、2、3、4、5、6; 特に 4 & 6 を参照]。

ただし、これらの回避策は、多くの重要なケースで機能を無効にすることに加えて、1 つの特別なケースを見逃していました。

後で戻ってきます。

2.27.0 サイクルでは、機能の無効化は最終的に の内部同等物をgit read-tree -mu HEAD、私たちが望んでいたことを行う何かに置き換えることによって解除されました: の新しいupdate_sparsity()関数は、インデックス内のビットunpack-trees.cのみを更新し、一致するように作業ツリーを更新します。SKIP_WORKTREE

この新しい関数は、古い実装で問題があったすべてのケースを処理します。ただし、古い実装の回避策を回避した同じ特殊なケースを別の方法で壊したことを除きます。

というわけで、特殊なケース、つまり a でgit clone実行される--no-checkout.

フラグの意味によると、--no-checkoutどのブランチもチェックアウトしません。これは、あなたが 1 つに属しておらず、クローンの後に 1 つに切り替える必要があることを意味します。

実装上、HEAD はまだ設定されています (つまり、ある意味で部分的にブランチにいる) が、次のようになります。

  • インデックスは「生まれていない」(存在しない)
  • 作業ツリーにファイルがありません (.git/ 以外)
  • 次に git switch (または git checkout) を実行すると、フラグを true に設定して unpack_trees が実行initial_checkoutされます。

git switch <somebranch>たとえば、インデックスが書き込まれ、作業ツリー内のファイルが生成されるのは、実行するまでではありません。

この特殊な--no-checkoutケースでは、従来のread-tree -mu HEAD動作はチェックアウトのように動作するのと同じことを行っていたでしょう。つまり、デフォルト ブランチ (HEAD) に切り替え、HEAD に一致するインデックスを書き出し、一致するように作業ツリーを更新します。

この特殊なケースは、元の sparse-checkout コマンドでの変更回避チェックをすり抜けたため、そこで続行されました。

update_sparsity()が導入され使用された後(コミット f56f31af03 (" sparse-checkout: 新しいupdate_sparsity()関数を使用する"、2020-03-27、Git v2.27.0-rc0 --バッチ #5に記載されているマージを参照))、 --no-checkout ケースの動作変更: 空のメモリ内インデックスの git の自動有効化 ( falseを参照) と、sparse-checkout のコードが完了後に常にインデックスを書き出すため、新しいバグが発生しました。do_read_index()must_existupdate_working_directory()

これにより、sparse-checkout がリポジトリを「生まれていない」インデックスを持つクローンから切り替えることができるようになりました (つまりinitial_checkout),、エントリのない記録されたインデックスを持つクローンが必要です)。

したがって、すべてのファイルが削除されたように見える代わりに、git statusまだブランチ上にない特別なアーティファクトとして git に認識されるのではなく、空のインデックスを記録すると、すべてのファイルがステージングされたブランチ上に確実に存在するかのように突然 git に表示されます。削除のために!
その後のチェックアウトまたは切り替えは、それがオンではなくinitial_checkout、段階的な削除が多数あったという事実に対処する必要がありました。

SKIP_WORKTREEsparse-checkout がビット以外のインデックスで何も変更しないことを確認してください。特に、インデックスが生まれていないときは、チェックアウトされたブランチがないため、スパース化または逆スパース化の作業はありません。

update_working_directory()早くから戻るだけです。


Git 2.35 (2022 年第 1 四半期) では、" git reset" ( man )のさまざまな操作モードがスパース インデックスでより適切に機能するようになりました。

commit f2a454e、commit 4d1cfc1commit 20ec2d0commit c01b1cbcommit 291d77e (2021 年 11 月 29 日)、commit 86609dbcommit 71471b2 (2021 年 10 月 27 日)、commit 1f86b7c (2021 年 10 月 7 日) by Victoria Dye ( vdye)を参照してください。
( 2021 年 12 月 10 日、コミット f085087Junio C Hamanoによってマージされました)gitster

sparse-index: 展開/折りたたみテストの更新コマンド

支援者: Derrick Stolee
署名者: Victoria Dye

( man )がスパース インデックスを展開せずに使用できることを見越して、コマンド inを( man )に置き換えます。このコマンドは、残りの部分を疎インデックスと 統合した後でも、適切に機能するようにインデックスを展開する必要があります。git reset --hardsparse-index is expanded and converted backgit reset -- folder1/a
reset

于 2017-03-26T19:05:49.953 に答える