2

状況: プル リクエストを作成するとき、受信者がどのような変更を行うかを理解できるようにしたいと考えています。特に次の場合は、それらを 1 つのコミットに押しつぶすと混乱する可能性があります。

  1. 同じく移動されたコードへの編集があります - diff はこれを大規模な削除と追加としてレンダリングし、編集を強調表示しません。

  2. コードは一連の同様のセクションに追加されます。例: case ステートメント、カスケード ifs、yacc 生成 - diff は、多くの場合、重複するセクションとして変更を再構築します (たとえば、セクションを追加する代わりに、前のセクションの先頭を使用し、新しい末尾を追加し、別の始まり、次にその前のセクションの仕上げを使用します); は、新しいコーディアン エンディングを追加し、場合によっては、いくつかのマイナーな類似点を選択してから、大量の同一コードを削除および挿入します。(diff は LCS を使用しており、驚くほど高速であることは認識していますが、diff が構文を認識しておらず、表示されるコードの「セクション」を認識できない場合でも、結果を理解するのが難しい場合があります)。

ところで:私は を使用していますgit diff --color-words --ignore-space-changeが、これは素晴らしいですが、再構成を誤ると、詳細を隠すことができます。また、受信者がプレーンgit diffを使用して、まったく異なるものを表示する可能性があることを懸念しています(再構成が異なる可能性があります)。

TASK : わかりました。これに対する明白な解決策は、プル リクエストを別々のコミットに分割することです。場合によっては、これらは私が開始した実際のコミットである可能性があるため、最初からリベース/スカッシュする必要はありません。しかし、それでも差分が不明確になる可能性があり (特に上記の理由 (2) により)、それらをさらに分離する必要があることがわかりました。

  1. これを行う明白な方法は、 を使用することgit add --patch/-pです。ただし、重複する変更に対してパッチを使用するのは困難です。dhunks を ivid したり、edit したりすることもできますが、必要な変更が追加、削除、および共通コードを組み合わせた場合、diff を逆にするという観点から考えると、やや気が遠くなります。

  2. 私が実際に行ったことは、ファイルを直接編集することです。不要な部分を削除してコミットします。次に、その削除を(私のエディターで)元に戻し、それをコミットします。実際のソースに関して作業することは、差分に関して作業するよりもはるかに明確で直感的です - しかし、私は git と戦って間違ったことをしているように感じます (また、エディターの取り消しに頼るのは事故を起こしやすいようです)。

  3. 代わりに最初git stashにファイルを作成し、不要な部分を削除して最初のコミットを準備することに私は思いつきます。次にgit stash apply、その削除を「元に戻す」ことで、2 番目のコミットを準備します。しかし、途中でそれができるかどうかはわかりませんrebase(まだ試していません)。

質問: これをすべて行うには何時間もかかります... 練習すれば上達すると思いますが... 正しい道を進んでいますか? より良い方法はありますか?そもそも、誤って再構築された差分を防ぐことができますか? 私は明確にするために一生懸命働きすぎていますか?

(公平を期すために、これは少し前に行われた微妙で複雑なコードの多くの編集でした。これらの時間を費やすことで、より深い洞察が明らかになりました。)

4

2 に答える 2

2

これらの回答に基づいて、インタラクティブなリベース ( get rebase -i ...) を開始し、 e1 つのコミットを編集した後:

git reset HEAD^     # reverts index to previous commit (not change files)
                    # so it's as if you are just about to add and commit
git stash           # save
git stash apply     # get it back
...edit the file, deleting the changes you don't want in the first commit
git add .
git commit -m "...first changes..."

git stash apply     # get it back again (ie undo the above delete)
...(I needed to resolve a merge conflict)
git add .
git commit -m "...second changes..."

git rebase --continue

残念ながら、git stash copy元に戻さずに変更を保存する はありません。これを行うためのよりスムーズな方法があるかもしれません。

私にとって驚くべきことは、インタラクティブな rebase の途中でgit の全機能を使用できることです。編集する「はず」の古いコミットを無視して、代わりに 2 つのコミットを追加できます。隠して適用できます。rebaseが実際にどのように実装されているかを調べて、抽象化として考えないようにする必要があるでしょう。実際、リベースのマンページには、コミットを分割するための見出しがあります。

于 2013-06-27T19:04:40.793 に答える
1

コミットを使用して変更を元に戻す (stash の代わりに) のはなぜですか? 2 つの問題があります。コミットを参照することと、ファイル (作業ツリー) とインデックスを正しい状態にすることです。

  1. コミットの参照 コミットのハッシュをカットアンドペーストできます。git tag tmpまたは、 (delete with ) を使用して、一時的なタグを作成しますgit tag -d tmpnまたは、ブランチからコミットを数えてを使用しますbranch~n。または、リベースが現在修正しているコミットについては、保存されているハッシュを使用しますcat .git/rebase-merge/amend(ただし、厄介で文書化されていない実装の詳細- I got info here)。

  2. files and index 私の現在の理解:ファイル (パス) を指定しても変更されませんreset。このように使用すると、インデックスのみが変更されます。インデックスとファイルの両方を変更します。ファイルのみを変更するには、それを clobber にします( の代わりにファイルの奇妙な構文に注意してください)。checkoutHEADresetcheckoutgit show <commit>:file > file:--

それを一緒に入れて:

git checkout -b newbranch  # I'm on a dev branch already; make a new one
git rebase -i master       # only the commits not part of master
...mark one with `edit` or `e`...

git tag tmp    
git reset HEAD^            # changes index only, as if we had just edited
...edit myfile, deleting what is to be split into another commit...
git add .
git commit -m "first commit"

git tag tmp2
git checkout tmp -- myfile # get file and index before above edit
git reset tmp2             # ...so need to reset *index* to first commit
                           # 1. index is same as "first commit"
                           # 2. file is same as commit we wanted to split
                           # (the diff is what we deleted above)
git add .
git commit -m "second commit"

git rebase --continue
git tag -d tmp tmp2        # clean up

'git show' を使用すると、2 番目のコミットは少し簡単になります。必要がないからですgit reset tmp2

git show tmp:myfile > myfile   # clobber file, but not index
git add .
git commit -m "second commit"

このすべてで何が起こっているのかを伝えるのは難しいです! 現在の状態を確認するいくつかの方法:

git log -1                 # see HEAD
git diff                   # between files and index
git diff --cached HEAD     # between index and HEAD
git show-ref tmp           # see tag

とにかく、これはすべて、最初に行ったエディター内での*元に戻す*よりもはるかに複雑に思えます。しかし、私はこのより良い理解が役に立つresetcheckout確信しています...

于 2013-06-29T19:49:23.620 に答える