76

私♥ git stash -p。しかし、時々、、、およびの満足のいくセッションの後y、私はこれを取得します:ns

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

なんで?

4

4 に答える 4

29

これは、ハンクを互いに近すぎる(変更間の3行未満)小さなハンクに分割しようとするたびに発生します。簡単に説明すると、パッチには、ローカルの変更と競合するコンテキスト行が含まれています。以下のより完全な説明。


これらのコミットされていない変更を含むgitリポジトリがあるとします。

--- a/pangram
+++ b/pangram
@@ -1,8 +1,8 @@
 The
-quick
+relatively quick
 brown
 fox
-jumps
+walks
 over
 the
 lazy

最初の変更を隠しておくと、次のようになります。

--- a/pangram
+++ b/pangram
@@ -1,5 +1,5 @@
 The
-quick
+relatively quick
 brown
 fox
 jumps

コマンドは実際にはパッチの保存に成功しますがgit stash(check git stash list)、gitはそのパッチを逆に使用して、隠された変更を作業ディレクトリから削除します。ハンクの後のコンテキストには「ジャンプ」がありますが、これはまだ私の作業ディレクトリにある「ウォーク」と一致しません。だからgitは

エラー:パッチが失敗しました:パングラム:1
エラー:パングラム:パッチは適用されません
ワークツリーの変更を削除できません

そして、私の作業ディレクトリにすべての変更を残し、隠し場所はほとんど価値がなくなります。


私はこれをgitのハンク分割サポートのバグと呼んでいます。変更をあまりにも近くに分割していることがわかっている場合は、パッチから数行のコンテキストを削除するか、パッチをジミーして、元のコンテキスト行の代わりに変更されたコンテキスト行を使用することができます。あるいは、このクローズのハンクの分割が公式にサポートされていない場合、実際には、このクローズのハンクの分割を拒否する必要があります。

于 2012-10-06T21:08:57.603 に答える
14

git stash -pGit 2.17(2018年第2四半期)で失敗が少なくなるはずです。
その前は、 " git add -p"(とロジックを共有しgit stashている)は、結果を基になる ""に渡す前に、分割パッチの合体に怠惰でありgit apply、コーナーケースのバグにつながりました。ハンクの選択が厳しくされた後に適用されるパッチを準備するロジック。

Phillip Wood()によるcommit 3a8522f、commit b3e0fcfcommit 2b8ea7f(2018年3月5日)、commit fecc6f3commit 23fea4ccommit 902f414(01 Mar 2018)、およびcommit 11489a6commit e4d671ccommit 492e60c(2018年2月19日)を参照してください。濱野純雄による合併---コミット436d18f2018年3月14日phillipwood
gitster

add -p:1つがスキップされたときに、後続のハンクのオフセットを調整します

(追加しますが、ここでも、スタッシュに適用できます)

commit 8cbd431(" git-add--interactive:ハンクの再カウントをapply --recountに置き換えます"、2008-7-2、Git v1.6.0-rc0)なので、ハンクがスキップされた場合、コンテキスト行に依存して後続のハンクを適切な場所に適用します。

これはほとんどの場合機能しますが、ハンクが間違った場所に適用される可能性があります。

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

ここでいくつかのテストを見ることができます。


Git 2.19の改善git add -p:ユーザーが " git add -p"でパッチを編集し、ユーザーのエディターが末尾の空白を無差別に削除するように設定されている場合、パッチで変更されていない空の行は完全に空になります(SPが1つしかない行ではありません)。
Git 2.17の時間枠で導入されたコードは、そのようなパッチの解析に失敗しましたが、今では状況に気づき、それに対処することを学びました。

Phillip Wood()によるcommit f4d35a6(2018年6月11日)を参照してください。( Junio C Hamanoによってマージされました---コミット5eb8da8、2018年6月28日phillipwood
gitster

add -p:編集されたパッチの空のコンテキスト行のカウントを修正

recount_edited_hunk()commit 2b8ea7f ( "add -p:編集されたパッチのオフセットデルタの計算"、2018-03-05、Git v2.17.0)で導入されたものでは、すべてのコンテキスト行がスペースで始まる必要があり、空の行はカウントされません。
これは、ユーザーがパッチの編集時に最後に空の行を導入した場合の再カウントの問題を回避することを目的としています。

ただしgit add -p、パッチを編集するときに編集者が空のコンテキスト行から末尾の空白を削除して、カウントする必要のある空の行を導入するのが一般的であるため、これにより''に回帰が導入されました。
「gitapply」はそのような空の行を処理する方法を知っており、POSIXは、空のコンテキスト行にスペースがあるかどうかは実装で定義されていると述べています(diffコマンドを参照)。

改行のみで構成される行と、スペースで始まる行をコンテキスト行としてカウントすることで回帰を修正し、将来の回帰を防ぐためのテストを追加します。


Git 2.23(Q3 2019)は、パッチを逆に選択的に適用する必要があるgit add -p""によって使用されるを改善しgit checkout -pます。以前はうまく機能しませんでした。

Phillip Wood()によるcommit 2bd69b9(2019年6月12日)を参照してください。( Junio C Hamanoによってマージされました---コミット1b074e1、2019年7月9日phillipwood
gitster

add -pcheckout -p:病理学的コンテキストで修正

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

ただし、逆に適用されるパッチには対応していません。

その場合、パッチを逆に適用したときにポストイメージオフセットが正しく調整されるように、プレイメージオフセットを調整する必要があります。
パッチが逆になっているため、デルタを加算するのではなく減算します(これを考える最も簡単な方法は、スキップされた削除の塊を検討することです。その場合、オフセットを減らしたいので、減算する必要があります)。


Git 2.25(2020年第1四半期)では、" git-add--interactive"PerlスクリプトをCに移行する取り組みが続いています。

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

commit 2e40831、commit 54d9d9bcommit ade246ecommit d6cf873commit 9254bdf、commit bcdd297 commit b38dd9e commit 11f2c0dcommit 510aecacommit 0ecd9d2commit 5906d5d 、commit 47dc4fd 、commit 80399 e3bd11bcommit 1942ee4commit f6aa7ec(2019年12月13日)Johannes Schindelin(dscho( Junio C Hamano
によってマージされました---コミット45b96a6、2019年12月25日gitster

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

サインオフ-作成者:Johannes Schindelin

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

この問題は23fea4c240( " t3701add病理学的コンテキスト行のテストに失敗しました"、2018-03-01、Git v2.17.0-rc0 --merge )で識別され、 fecc6f3a68のPerlバージョンで修正されました( " add -p:後続のハンクのオフセットを調整する場合1つはスキップされます」、2018-03-01、Git v2.17.0-rc0 --merge

そして、このパッチはのCバージョンでそれを修正しgit add -pます。

Perlバージョンとは対照的に、ハンクヘッダー(通常、ハンク内でコードが変更される関数のシグネチャを含む)の余分なテキストをそのまま維持しようとします。

注:Cバージョンはこの段階でステージングモードの変更をサポートしていませんが、古いオフセットと新しいオフセットの両方が0の場合はハンクヘッダーをスキップするだけでこれに備えています(これは通常のハンクでは発生しないため、これを特別な塊を見ていることを示すインジケーター)。

同様に、ハンクヘッダーに余分なテキストがないことを適切に処理することで、ハンク分割の準備をすでに行っています。最初の分割されたハンクだけがそのテキストを持ち、他の部分は持っていません(空の余分なテキストの開始/終了範囲で示されます)。この段階ですでにハンク分割の準備をすることで、後でハンクヘッダー印刷ブロック全体のインデントを変更する必要がなくなり、その処理を行わない場合とほぼ同じように簡単に確認できます。


git stash -pGit 2.27(2020年第2四半期)より前では、「 」がうまく機能していないときにユーザーがパッチハンクを分割できるようになりました。これを(部分的に)より良く機能させるために、バンドエイドが追加されました。

Johannes Schindelin()によるcommit 7723436commit 121c0d4(2020年4月8日)を参照してください。濱野純雄による合併---コミットe81ecff、 20204月28日)dscho
gitster

stash -p:(部分的に)スプリットハンクに関するバグを修正

サインオフ-作成者:Johannes Schindelin

ハンクを分割し、分割されたビットとピースを部分的にのみ受け入れることによってワークツリーの変更の一部を隠そうとすると、ユーザーにはかなり不可解なエラーが表示されます。

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

また、コマンドは、ワークツリーの変更の目的の部分を隠しておくことができません(stash参照が実際に正しく更新された場合でも)。

その失敗を実証するテストケースもあり、すでに4年間使用しています。

説明:ハンクを分割するとき、変更された行は3行(Gitのdiffがデフォルトで使用するコンテキスト行の量)を超えて区切られなくなりますが、それより少なくなります。

したがって、diffハンクの一部のみをスタッシュするためにステージングする場合、逆にワークツリーに適用する結果のdiffには、3つのコンテキスト行で囲まれたドロップされる変更が含まれますが、diffはワークツリー、これらのコンテキスト行は一致しません。

時間の例。ファイルREADMEに次の行が含まれていると仮定します。

We
the
people

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

We
are
the
kind
people

ユーザーが「are」を含む行を隠そうとすると、コマンドはこの行を一時的なインデックスファイルに内部的にステージングし、HEADとそのインデックスファイル間の差分を元に戻そうとします。元に戻そうとする
diffハンクは次のようになります。git stash

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

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

diffのコンテキスト行は、diffが正確に適用されない場合(ただし、パッチを適用するファイルの正確な行番号がdiffに示されている行番号と異なる場合)、正確な場所を見つけるという主な目的を果たします。これを回避するには、コンテキスト行の量を減らします。差分が生成されたばかりです。

注:これは問題の完全な修正ではありません。
t3701の「add-pは病理学的コンテキストラインで機能する」テストケースで示されているように、diff形式にはあいまいさがあります。もちろん、実際には、このような繰り返しの行に遭遇することは非常にまれです。

このような場合の完全な解決策は、スタッシュから差分を生成し、それをエミュレートすることによって逆に適用するgit revert(つまり、3方向マージを行う)アプローチを置き換えることです。ただし、これはワークツリーにgit stash -pは適用されませんがHEAD、代わりにワークツリーに適用されます。これにより、スクリプトバージョンのを維持している限り、これを実装するのは簡単ではありませんadd -i


Git 2.29(2020年第4四半期)は、git add -p(によって使用されるstash -p)にリークフィックスをもたらします

Phillip Wood()によるcommit 324efcf(2020年9月7日)を参照してください。( Junio C Hamanoによってマージされました---コミット3ad8d3e、2020年9月18日phillipwood
gitster

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

サインオフ-作成者:Phillip Wood
Acked-作成者:Johannes Schindelin

asanは、のCバージョンがadd -p割り当てたすべてのメモリを解放していないことを報告しています。

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

于 2018-03-16T23:54:54.970 に答える
6

同じように失敗した後git stash -p、私はこの回避策(git 2.0.2)で運が良かった:

  • git add -p、まったく同じハンクを分割しますが、逆の答えを使用します(「y」addは「変更を保持」、「n」stashは変更を保持します)。
  • git stash -kインデックスを保持し、他のすべてを隠します
  • git resetファイルの作業を続行するには

なぜgit add -p同じように失敗しなかったのかわかりませんgit stash -p。追加はパッチファイルを作成するのではなく、インデックスで機能するためだと思いますか?

于 2014-10-17T00:34:32.867 に答える
5

残念ながら、現時点で受け入れられている回答は、Git2.17でも失敗する可能性があります。

私のように、完璧な隠し場所を構築するために多くの努力を費やし、その努力を捨てたくない場合でも、ほとんどの場合、必要なものを手に入れることができます。

git stash show -p | patch -p1 -R

これは拒否で失敗しますが、ほとんどのハンクが正しく適用され、少なくともすべてのファイルを再度確認する時間を節約できる可能性があります。

于 2018-04-07T20:46:34.713 に答える