1. ホーム
  2. git

なぜ `git stash -p` は時々失敗するのですか?

2023-09-15 13:12:48

質問

I ♥ git stash -p . しかし、時には、満足のいくセッションの後 y , n そして s は、私はこれを取得します。

Saved working directory and index state WIP on foo: 9794c1a lorum ipsum
error: patch failed: spec/models/thing_spec.rb:65
error: spec/models/thing_spec.rb: patch does not apply
Cannot remove worktree changes

どうして?

どのように解決するのか?

git stash -p は、Git 2.17 (Q2 2018) で失敗が少なくなるはずです。

その前に、" git add -p "(とロジックを共有する git stash とロジックを共有する) は、分割されたパッチを結合して、その結果を基にした " に渡すのに手間がかかっています。 git apply の結果を渡す前に分割されたパッチを結合するのが遅く、コーナーケースのバグを引き起こしていました。

参照 コミット 3a8522f , コミット b3e0fcf , コミット 2b8ea7f (を追加しました(2018/03/05)。 コミット fecc6f3 , コミット 23fea4c , コミット 902f414 (2018年3月1日)、および コミット 11489a6 , コミット e4d671c , コミット 492e60c (2018年2月19日)によって フィリップ・ウッド ( phillipwood ) .

(によって統合されました。 ジュニオ・C・ハマノ--。 gitster -- コミット 436d18f , 2018年3月14日)

add -p : 一つがスキップされた場合、後続のハンクのオフセットを調整します。

(追加ですが、やはりstashに適用可能)

このため コミット 8cbd431 (" git-add--interactive : hunkを置き換える recounting with apply --recount", 2008-7-2, Git v1.6.0-rc0) もしハンクがスキップされたら、その後のハンクを正しい位置に適用するために文脈行に依存する。 を適用します。

これはほとんどの場合うまくいきますが、ハンクが間違った場所に適用されてしまうこともありえます。 は間違った場所に適用されてしまう可能性があります。

これを修正するには、次のハンクのオフセットを調整します。 を修正するために、後続のハンクのオフセットを調整します。 スキップされたハンクによる挿入または削除の数の変化を修正するために、後続のハンクのオフセットを調整します。オフセットの変更 編集されたハンクで、挿入または削除の数が変更されたことによるオフセットの変化 はここでは無視され、次のコミットで修正される予定です。

ご覧のように のテストがあります。 .


Git 2.19 の改善点 git add -p : ユーザーが " でパッチを編集したとき。 git add -p " で、ユーザーのエディタが末尾の空白を無差別に取り除くように設定されている場合、パッチ内で変更されていない空行が完全に空になる(代わりに単独の SP がある行になる)ことに注意してください。

Git 2.17 のタイムフレームで導入されたコードは、そのようなパッチを解析するのに失敗しましたが、今ではその状況に気付き、対処することを学びました。

参照 コミット f4d35a6 (2018年6月11日)による フィリップ・ウッド ( phillipwood ) .

(によって統合されました。 ジュニオ・C・ハマノ--。 gitster -- コミット 5eb8da8 , 2018年6月28日)

add -p : 編集されたパッチに含まれる空のコンテキスト行をカウントするよう修正

<ブロッククオート

recount_edited_hunk() で紹介した コミット 2b8ea7f ("add -p: calculate offset delta for edited patches", 2018-03-05, Git v2.17.0) では、すべてのコンテキスト行をスペースで開始する必要があり、空行はカウントされないようになりました。

これは、ユーザーがパッチを編集する際に最後に空行を導入していた場合、再カウントの問題を回避することを意図しています。

しかし、これは、' git add -p パッチが編集されるとき、エディタが空のコンテキスト行から末尾の空白を取り除くことがよくあるようで、それによってカウントされるべき空の行が発生するからです。 をカウントする必要があります。

git apply' はこのような空行の扱い方を知っており、POSIX では空の文脈行に空白があるかどうかは実装で定義されているとされています ( diffコマンド ).

改行のみからなる行やスペースから始まる行を文脈行としてカウントすることによりリグレッションを修正し とスペースで始まる行をコンテキスト行としてカウントすることでリグレッションを修正し、今後のリグレッションを防ぐため を追加し、今後のリグレッションを防止します。


Git 2.23 (2019年第3四半期) では、以下の点が改善されました。 git add -p で使用される、" git checkout -p は、パッチを選択的に逆に適用する必要があります: 以前はうまく機能しませんでした。

参照 コミット 2bd69b9 (2019 年 6 月 12 日) による フィリップ・ウッド ( phillipwood ) .

(によって統合されました。 ジュニオ・C・ハマノ--。 gitster -- コミット 1b074e1 , 2019年07月09日)

add -p 修正 checkout -p 病的な文脈で

<ブロッククオート

コミット fecc6f3 (" add -p : 1つのハンクがスキップされたときに後続のハンクのオフセットを調整します。 skipped", 2018-03-01, Git v2.17.0-rc0) は、前の hunk がスキップされたときに正しい場所に hunk を追加するように修正しました。

しかし、それは逆に適用されるパッチに対処していませんでした。

その場合、適用がパッチを逆転させたときに後画像のオフセットが正しく調整されるように、前画像のオフセットを調整する必要があります。

パッチが反転されるときに、デルタを追加するのではなく、減算します (これについて考える最も簡単な方法は、スキップされる削除の塊を考えることです - その場合、オフセットを減らしたいので、減算する必要があります)。


Git 2.25 (2020年第1四半期) で、" を移動させる取り組みが行われました。 git-add--interactive "PerlスクリプトをCに移行する努力が続けられています。

その結果、上記のような修正が再実装されます。

参照 コミット 2e40831 , コミット 54d9d9b , コミット ade246e , コミット d6cf873 , コミット 9254bdf , コミット bcdd297 , コミット b38dd9e , コミット 11f2c0d , コミット 510aeca , コミット 0ecd9d2 , コミット 5906d5d , コミット 47dc4fd , コミット 80399ae , コミット 7584dd3 , コミット 12c24cf , コミット 25ea47a , コミット e3bd11b , コミット 1942ee4 , コミット f6aa7ec (2019年12月13日)によって ヨハネス・シンデリン ( dscho ) .

(によって統合されました。 ジュニオ・C・ハマノ--。 gitster -- コミット 45b96a6 , 2019年12月25日)

built-in add -p : 必要に応じてハンクヘッダーを調整します。

署名: Johannes Schindelin

<ブロッククオート

削除する行数と異なる行数を追加するハンクをスキップする場合、スキップしないハンクの後続のハンクヘッダーを調整する必要があります。病的なケースでは、コンテキストは、パッチを適用すべき場所を正確に決定するのに十分ではありません。

この問題は 23fea4c240 (" t3701 : add failing test for pathological context lines", 2018-03-01, Git v2.17.0-rc0 --。 マージ で修正)、Perl版では fecc6f3a68 (" add -p : 1つのハンクがスキップされたときに後続のハンクのオフセットを調整する", 2018-03-01, Git v2.17.0-rc0 --。 マージ ).

そしてこのパッチは、C言語版で修正された git add -p .

Perl版とは対照的に、hunkヘッダ上の余分なテキスト(通常、hunk内でコードが変更された関数のシグネチャを含む)をそのままにするようにしています。

注意: Cバージョンはこの段階でステージングモードの変更をサポートしませんが、私たちはすでに、古いオフセットと新しいオフセットの両方が0の場合、単にハンクヘッダーをスキップすることでこの準備をしています (これは通常のハンクでは起こりえず、私たちが特別なハンクを見ていることの指標としてこれを使用します)。

同様に、hunk ヘッダーの余分なテキストがないことを優雅に処理することによって、hunk 分割の準備をすでに行っています: 最初の分割 hunk のみがそのテキストを持ち、他のものは持ちません (空の余分なテキストの開始/終了範囲によって示されます)。この段階ですでに hunk 分割の準備をしておくと、後で hunk ヘッダー印刷ブロック全体のインデントを変更する必要がなくなり、その処理なしとほぼ同じように簡単にレビューすることができます。


Git 2.27 (2020 年第 2 四半期) 以前では、パッチハンクを " しながら分割することを可能にしました。 git stash -p "はうまく動作しません。これを(部分的に)うまく動作させるための応急処置が追加されました。

参照 コミット 7723436 , コミット 121c0d4 (2020年04月08日) によるものです。 Johannes Schindelin ( dscho ) .

(マージされた ジュニオ・C・ハマノ--。 gitster -- コミット e81ecff 2020年4月28日)

stash -p : 分割されたハンクに関するバグを (部分的に) 修正

署名: Johannes Schindelin

<ブロッククオート

ハンクを分割して、分割された断片を部分的に受け入れるだけで、ワークツリーの変更の一部をため込もうとすると、ユーザーはかなり不可解なエラーを提示されます。

error: patch failed: <file>:<line>
error: test: patch does not apply
Cannot remove worktree changes

で、コマンドはワークツリーの変更の希望する部分を保存することに失敗するでしょう(たとえ stash が実際に正しく更新されたとしても)。

私たちはその失敗を実証するテストケースさえ持っており、すでに4年間それを持ち続けています。

その説明です。 ハンクを分割するとき、変更された行はもはや3行以上離れていません(これはGitの差分がデフォルトで使用するコンテキスト行の量です)。 で区切られることはなく、それ以下になります。

そのため、stash 用に diff ハンクの一部だけをステージングする場合、ワークツリーに逆に適用したい結果の diff には、削除する変更が 3 つのコンテキスト行で囲まれて含まれますが、diff はワークツリーに対してではなく HEAD に対して相対的なので、これらのコンテキスト行は一致しないことになります。

例題です。ファイル README がこれらの行を含むと仮定してみましょう。

We
the
people

で、ワークツリーはいくつかの行を追加し、代わりにこれらの行を含むようにしました。

We
are
the
kind
people

で、ユーザが "are" を含む行を隠そうとした場合、コマンドは内部的にこの行を一時インデックスファイルにステージし、HEAD とそのインデックスファイル間の diff を元に戻そうとします。

という diff ハンクは git stash が戻そうとする diff の塊は、次のようなものになります。

@@ -1776,3 +1776,4
 We
+are
 the
 people

これで明らかに、末尾のコンテキスト行は、ユーザーが行った元の diff hunk の部分と重なっています。 ではなく の部分と重なっていることは明らかです。

diff のコンテキスト行は、diff が正確に適用されない場合 (ただし、パッチを適用するファイルの正確な行番号が diff で示される行番号と異なる場合) に正確な場所を見つけるという主な目的に役立つことを念頭に置き、コンテキスト行の量を減らすことでこれを回避します: diff はちょうど生成されました。

注意: これは フル の修正ではありません。

t3701 の 'add -p works with pathological context lines' テストケースで示されたように、diff の形式には曖昧な点が存在します。もちろん、そのような繰り返し行に遭遇することは、実際には非常にまれです。

このようなケースに対する完全な解決策は、隠し場所から diff を生成し、それを逆に適用するアプローチをエミュレートすることによって置き換えることでしょう。 git revert をエミュレートして逆適用する (つまり、3 者間マージを行う) ことです。しかし git stash -p には適用されないでしょう。 HEAD を適用するのではなく、ワークツリーに適用することになるため、スクリプト化された add -i .


Git 2.29 (2020 年第 4 四半期) では、リークフィックスにより git add -p (で使用される)。 stash -p )

参照 コミット 324efcf (2020年09月07日) によるものです。 フィリップ・ウッド ( phillipwood ) .

(によって統合されました。 ジュニオ・C・ハマノ--。 gitster -- コミット 3ad8d3e 2020年9月18日)

add -p : メモリリークを修正

署名: Phillip Wood


署名者:Johannes Schindelin

<ブロッククオート

asan の報告によると、C 版の add -p が割り当てたメモリをすべて解放していないことを報告します。

をクリアする関数を導入することで、これを修正します。 struct add_p_state``をクリアする関数を導入し、個々のメンバーを解放する代わりにそれを使用するようにします。