私は数日前に master から分岐し、newbranch.
後で、最初に作成したコミットのファイルが変更されていないことに気付きました。マスター ブランチにはまだ何もプッシュしていません。すべての変更は にありnewbranchます。newbranchこの 1 つのファイルを、最初に master から分岐したときの場所にロールバックするにはどうすればよいですか?
私は数日前に master から分岐し、newbranch.
後で、最初に作成したコミットのファイルが変更されていないことに気付きました。マスター ブランチにはまだ何もプッシュしていません。すべての変更は にありnewbranchます。newbranchこの 1 つのファイルを、最初に master から分岐したときの場所にロールバックするにはどうすればよいですか?
git checkout master -- filepath
必要な結果に応じて、これを行うにはいくつかの方法があります。
euphoria83 からの回答はこれを行います...
最初は、次のようなコミット履歴があります (各文字はコミットを表します)。
A - B - C <-- master
\
D
\
E <-- HEAD=newbranch
コミットCは、開始時にマスターにあったものです。いくつかの変更を加えてコミットし ( を作成していますD)、さらにいくつか変更してコミットしました ( を作成していますE)。
今、あなたは実行しますgit checkout HEAD~1。Dこれにより、コミット D に一致するように作業ディレクトリが更新され、ブランチ名を含むのではなく、commit を直接指す「切り離された HEAD」(フランス革命の色合い!) が得られます。
A - B - C <-- master
\
D <-- HEAD
\
E <-- newbranch
(この形式のチェックアウト , はgit checkout <branch-name-or-rev>、どのHEAD名前を変更するかを git に指示します。<rev>この場合の ,HEAD~1は、「どのコミットの HEAD 名を調べて、1 つ前に移動するか」を意味するため、これは commit から 1 つ前に移動するようEに<rev>指示します。ブランチ名ではなく、「HEAD を切り離す」効果もあります。)
今、あなたは実行しますgit checkout HEAD~1 filename。git に対してより明確にするには、git checkout HEAD~1 -- filename. は、--「残りの引数はファイル名であり、ブランチ名やリビジョン名ではありません」と述べています。 を省略すると--、git はそれがブランチ名かファイル名かを推測しようとします。
引数を指定したこの形式の はcheckout、-- filenameかなり異なることを行います。HEAD~1通常どおりリビジョンを検索するように指示されますが、今回は を変更 HEADせずに、指定されたファイルを抽出するだけです。HEAD現在は rev. という名前が付けられているためD、git はもう 1 つ、 rev. にバックアップし、そのリビジョンからCファイルのバージョンを抽出しfilenameて、作業ディレクトリに配置します。
(ブランチ名はリビジョンにmaster も名前を付けていることに注意してください。したがって、この時点でC書くことができます。)git checkout master -- filename
次に、git commit -a --amend変更されたファイルを追加するように git に指示します。この場合、実際には必要ありませんが、通常は、修正した他のファイルが追加されます。その後、「修正コミット」を実行します。「修正コミット」とは、「新しいコミットを作成するが、その親コミットを親コミットと同じにする」ことを意味します。D'これにより、新しいコミットが作成されます—親が であるとしましょうC。いつものように、新しいコミットはHEAD. HEADはデタッチされているため、ブランチ名はまだ移動していません。結果のコミット ツリーは次のようになります。
A - B - C <-- master
| \
\ D
\ \
\ E <-- newbranch
\
D' <-- HEAD
のファイルはD'、Dfile を除いて のファイルと同じです。ただし、filename現在は Revision と同じCです。
次に、git cherry-pick newbranchによって名前が付けられたリビジョンを取得するように指示します—それはnewbranchリビジョンですE—新しいコミットとして HEAD リビジョンに同じ変更を加えます (それを と呼びましょうE'):
A - B - C <-- master
| \
\ D
\ \
\ E <-- newbranch
\
D' - E' <-- HEAD
あとは、 にブランチ名を付けるだけですHEAD。次に、 を削除するnewbranchか、邪魔にならないように名前を変更してから、 に名前newnewを変更しても安全newbranchです。
git checkout -b newnew # now HEAD=newnew which points to E'
git branch -m newbranch oldnew # rename out of the way
git branch -m newnew newbranch # and rename newnew to newbranch
(これを少し短くすることもできますが、次に進みましょう...)
同じことをもっと簡単に行う方法があります。実行するだけgit rebase -i masterです。newbranchこれは、現在のブランチ ( ) をmaster「上流」として使用してリベースすることを示しています。つまり、以降のすべてのコミットを検索しmaster(つまり、Dand )、それらを(以前に基づいていた場所にE) リベースします。masterこれがなけれ-iばばかげています—リベースDしEて以前とまったく同じようにすることは、何もしないのに費用がかかる方法です—しかし、-igit を使用すると、一連のコマンドでエディターが開きます。コマンドはpickコミットDし、E. pickfor commitDを に変更editし、ファイルを書き出します。
その後、リベースはチェリーピックDして停止し、コミットを修正します。シェルで、次のように入力します。
git checkout master -- filename
git commit -a --amend
前と同じように、修正されたコミットを保存します。これは になりD'ます。次に実行します。
git rebase --continue
Git は commit をチェリーピックし、 commitEを与えE'ます。これですべてが完了したので、リベースが終了し、ブランチnewbranchには必要なコミットが含まれます。
ДМИТРИЙ МАЛИКОВ からの回答は、何か違うことをします。同じgit checkout master -- filename(綴りfilepathの ) が含まれていますが、リベースのようなシーケンスは含まれていません。
したがって、前と同様に、次のように開始します。
A - B - C <-- master
\
D
\
E <-- HEAD=newbranch
filename次に、改訂中の のバージョンをC作業ディレクトリに抽出します。あなたが今git commit(またはgit commit -a、この特定のケースで同じこと - git これはcheckout、抽出されたrev-Cファイルを最初にステージング領域に書き込むためです)、Fファイルfilenameを元の状態に戻す新しいコミットを取得しますC.
言い換えれば、コミットDしEてもファイルへの変更は残ります。新しいコミットFは変更を元に戻します。