マスターにコミットした最後のいくつかのコミットを新しいブランチに移動し、それらのコミットが行われる前にマスターを戻したいのですが。残念ながら、私のGit-fuはまだ十分に強力ではありません。
つまりこれからどうやって行くのですか
master A - B - C - D - E
これに?
newbranch C - D - E
/
master A - B
マスターにコミットした最後のいくつかのコミットを新しいブランチに移動し、それらのコミットが行われる前にマスターを戻したいのですが。残念ながら、私のGit-fuはまだ十分に強力ではありません。
つまりこれからどうやって行くのですか
master A - B - C - D - E
これに?
newbranch C - D - E
/
master A - B
回答:
コミットを既存のブランチに移動する場合は、次のようになります。
git checkout existingbranch
git merge master # Bring the commits here
git checkout master
git reset --keep HEAD~3 # Move master back by 3 commits.
git checkout existingbranch
この--keep
オプションは、関係のないファイルにある可能性のあるコミットされていない変更を保持するか、それらの変更を上書きする必要がある場合は、処理と同様に中止git checkout
します。中止した場合はgit stash
、変更して再試行するか、またはを使用--hard
して変更を失います(コミット間で変更されなかったファイルからでも!)
この方法は、最初のコマンド(git branch newbranch
)で新しいブランチを作成することによって機能しますが、それには切り替えません。次に、現在のブランチ(マスター)をロールバックし、新しいブランチに切り替えて作業を続けます。
git branch newbranch # Create a new branch, containing all current commits
git reset --keep HEAD~3 # Move master back by 3 commits (Make sure you know how many commits you need to go back)
git checkout newbranch # Go to the new branch that still has the desired commits
# Warning: after this it's not safe to do a rebase in newbranch without extra care.
ただし、戻すコミットの数を確認してください。または、の代わりにHEAD~3
、origin/master
元に戻したいコミット(またはのような参照)のハッシュを単に提供することもできます。例:
git reset --keep a1b2c3d4
警告: Gitバージョン2.0以降では、後でgit rebase
元の(master
)ブランチ上に新しいブランチを作成する場合--no-fork-point
、マスターブランチから移動したコミットが失われないように、リベース中に明示的なオプションが必要になる場合があります。持ってbranch.autosetuprebase always
セットすると、この可能性が高くなります。詳細については、John Mellorの回答を参照してください。
なぜそれが機能するのかと思っている人のために(最初はそうでした):
Cに戻り、DとEを新しいブランチに移動します。最初は次のようになります。
A-B-C-D-E (HEAD)
↑
master
後git branch newBranch
:
newBranch
↓
A-B-C-D-E (HEAD)
↑
master
後git reset --hard HEAD~2
:
newBranch
↓
A-B-C-D-E (HEAD)
↑
master
ブランチは単なるポインタなので、マスターは最後のコミットをポイントしました。newBranchを作成したときは、最後のコミットへの新しいポインタを作成しただけです。次にgit reset
、マスターポインターを2つのコミットに戻しました。しかし、newBranchを移動しなかったので、それは最初に行ったコミットをまだ指し示しています。
git push origin master --force
、変更をメインリポジトリに表示するために必要な作業もありました。
origin/master
上の図には表示されていないことに注意してください。プッシュしてorigin/master
上記の変更を行った場合、確かに状況はおかしくなります。しかし、それは「医師、私がこれを行うと痛い」という種類の問題です。そして、それは元の質問が尋ねたものの範囲外です。このシナリオを乗っ取るのではなく、シナリオを探索するために独自の質問を書くことをお勧めします。
git branch -t newbranch
」と言いました 。戻ってもう一度答えを読んでください。 誰もそれをすることを提案しませんでした。
newbranch
彼らが既存のローカルmaster
ブランチをベースにしたいことは明らかです。ユーザーが実行している程度取得するときに受け入れ答えを実行した後、git rebase
にnewbranch
、gitのは、彼らが実行されますので、上流分岐を設定するのを忘れていること、それらを思い出させるgit branch --set-upstream-to=master
、その後git rebase
、同じ問題を抱えています。彼らはgit branch -t newbranch
そもそも使うかもしれません。
この場合、sykoraによって公開されるメソッドが最良のオプションです。しかし、これは最も簡単なことではなく、一般的な方法ではありません。一般的な方法では、git cherry-pickを使用します。
OPが望むものを達成するために、その2ステップのプロセス:
newbranch
実行する
git checkout master
git log
必要な(たとえば3つの)コミットのハッシュに注意してくださいnewbranch
。ここでは私が使用します:
Cコミット:9aa1233
Dコミット:453ac3d
Eコミット:612ecb3
注:最初の7文字またはコミットハッシュ全体を使用できます
newbranch
git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233
git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233
git cherry-pickはこれら3つのコミットをnewbranchに適用します。
これを行うもう1つの方法は、2つのコマンドだけを使用することです。また、現在の作業ツリーをそのまま維持します。
git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit
古いバージョン -私が知る前にgit branch -f
git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit
することができることpush
をすることは.
知っている素敵なトリックです。
git branch -f
こことどう違うのですか?
.
が現在のディレクターです。gitはREMOTESまたはGIT URLにプッシュできます。path to local directory
サポートされているGit URL構文。のGIT URLSセクションを参照してくださいgit help clone
。
こんなことしないで:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
次回git rebase
(またはgit pull --rebase
)を実行すると、これらの3つのコミットはnewbranch
!(以下の説明を参照)
代わりにこれを行います:
git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
--keep
に似--hard
ていますが、コミットされていない変更を破棄するよりも失敗するため、より安全です)。newbranch
ます。newbranch
。彼らはもはや、分岐によって参照しているので、それはそれはgitの使用しないREFLOGを:HEAD@{2}
それはコミットされたHEAD
2つの操作前に参照するために使用される、すなわち前に我々 1.チェックアウトしないnewbranch
と2を使用git reset
3つのコミットを破棄します。警告:reflogはデフォルトで有効になっていますが、手動で無効にした場合(「裸」のgitリポジトリを使用するなど)、実行後に3つのコミットを取得することはできませんgit reset --keep HEAD~3
。
reflogに依存しない別の方法は次のとおりです。
# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3
(必要に応じて@{-1}
、以前にチェックアウトしたブランチ-の代わりに書き込むことができますoldbranch
)。
git rebase
最初の例の後で3つのコミットを破棄するのはなぜですか?これgit rebase
は、引数を指定しないと--fork-point
デフォルトでオプションが有効になるためです。このオプションでは、ローカルreflogを使用して、強制的にプッシュされる上流ブランチに対してロバストになるように試みます。
コミットM1、M2、M3が含まれている場合にorigin / masterを分岐し、次に3つのコミットを自分で作成したとします。
M1--M2--M3 <-- origin/master
\
T1--T2--T3 <-- topic
しかし、誰かが起点/マスターを強制的にプッシュすることで履歴を書き換え、M2を削除します。
M1--M3' <-- origin/master
\
M2--M3--T1--T2--T3 <-- topic
ローカルのreflogを使用するgit rebase
と、origin / masterブランチの初期の化身から分岐したことがわかります。したがって、M2とM3のコミットは、実際にはトピックブランチの一部ではありません。したがって、M2は上流のブランチから削除されたため、トピックブランチがリベースされると、トピックブランチでM2が不要になると合理的に想定しています。
M1--M3' <-- origin/master
\
T1'--T2'--T3' <-- topic (rebased)
この動作は理にかなっており、通常、リベースするときに行うのが適切です。
したがって、次のコマンドが失敗する理由:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
これは、reflogを間違った状態のままにするためです。Git newbranch
は、3つのコミットを含むリビジョンでアップストリームブランチから分岐したと見なします。次に、reset --hard
アップストリームの履歴を書き換えてコミットを削除します。次回実行するgit rebase
と、アップストリームから削除された他のコミットと同様に、コミットが破棄されます。
ただし、この特定のケースでは、これらの3つのコミットをトピックブランチの一部と見なす必要があります。そのためには、3つのコミットを含まない以前のリビジョンでアップストリームを分岐する必要があります。これが私の提案する解決策です。したがって、どちらもreflogを正しい状態のままにします。
詳細について--fork-point
は、git rebaseおよびgit merge-baseのドキュメントの定義をご覧ください。
master
。したがって、それらは危険なほど間違っているわけではありません。
-t
あなたが言及しているあなたはgit branch
あなたがgit config --global branch.autosetuprebase always
設定した場合暗黙のうちに起こります。そうでない場合でも、これらのコマンドを実行した後にトラッキングを設定すると同じ問題が発生することをすでに説明しました。OPはおそらくそれらの質問を想定しているためです。
これは、間違ったブランチへのコミットのためのはるかに簡単な解決策です。master
3つの誤ったコミットがあるブランチから開始します。
git reset HEAD~3
git stash
git checkout newbranch
git stash pop
master
master
に戻しますが、すべての作業ファイルはそのまま残しますmaster
作業ツリーをHEAD〜3状態と完全に一致させますnewbranch
これで、使用することができますgit add
し、git commit
通常どおりに。新しいコミットはすべてに追加されnewbranch
ます。
OPの目標は、変更を失うことなく「マスターがそれらのコミットが行われる前に戻す」ことであり、このソリューションはそれを実現します。
これを少なくとも週に1回、誤ってのmaster
代わりに新しいコミットを作成したときに行いdevelop
ます。通常、ロールバックするコミットは1つだけです。その場合git reset HEAD^
、1行目を使用すると、1つのコミットだけをロールバックする最も簡単な方法になります。
マスターの変更を上流にプッシュした場合は、これを行わないでください
他の誰かがそれらの変更をプルした可能性があります。ローカルマスターのみを書き換える場合、上流にプッシュしても影響はありませんが、書き換えた履歴を共同編集者にプッシュすると頭痛の種になる可能性があります。
git add
とgit commit
コマンドの両方があったので、上矢印キーを押して数回入力してブームするだけで済みました!すべてが戻ってきましたが、今は正しいブランチにあります。
これは技術的な意味でそれらを「移動」させませんが、同じ効果があります:
A--B--C (branch-foo)
\ ^-- I wanted them here!
\
D--E--F--G (branch-bar)
^--^--^-- Opps wrong branch!
While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)
A--B--C (branch-foo)
\
\
D-(E--F--G) detached
^-- (branch-bar)
Switch to branch-foo
$ git cherry-pick E..G
A--B--C--E'--F'--G' (branch-foo)
\ E--F--G detached (This can be ignored)
\ /
D--H--I (branch-bar)
Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:
A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
\
\
D--H--I--J--K--.... (branch-bar)
rebase
同じ事に使えませんか?
rebase
、上記のシナリオの分離ブランチで代わりに使用できます。
履歴を書き換えずにこれを行うには(つまり、すでにコミットをプッシュしている場合):
git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>
その後、両方のブランチを無理なく押すことができます!
このような状況でした:
Branch one: A B C D E F J L M
\ (Merge)
Branch two: G I K N
私が実行した:
git branch newbranch
git reset --hard HEAD~8
git checkout newbranch
コミットはヘッドになると思っていましたが、コミットLは今です...
履歴の正しい場所に確実に到達するために、コミットのハッシュを操作する方が簡単です
git branch newbranch
git reset --hard #########
git checkout newbranch
これからどうやって行くの?
A - B - C - D - E
|
master
これに?
A - B - C - D - E
| |
master newbranch
2つのコマンドを使用
与える
A - B - C - D - E
|
newbranch
そして
与える
A - B - C - D - E
| |
master newbranch
プッシュされていないすべてのコミットを新しいブランチに移動する必要があるだけの場合は、
作成新しいブランチを現在の1から:git branch new-branch-name
新しいブランチをプッシュします。git push origin new-branch-name
あなたの古い(現在の)ブランチを最後にプッシュされた/安定した状態に戻します:git reset --hard origin/old-branch-name
一部の人々は、他のを持っているupstreams
のではなくorigin
、彼らが適切なを使用する必要がありますupstream
1)すべての変更をnew_branchに移動する新しいブランチを作成します。
git checkout -b new_branch
2)次に、古いブランチに戻ります。
git checkout master
3)git rebaseを実行します
git rebase -i <short-hash-of-B-commit>
4)次に、開いたエディターには最後の3つのコミット情報が含まれます。
...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...
5)これら3つのコミットすべてpick
をdrop
に変更します。次に、エディターを保存して閉じます。
...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...
6)最後の3つのコミットが現在のブランチから削除されmaster
ます()。次に、+
ブランチ名の前に記号を付けて、ブランチを強制的にプッシュします。
git push origin +master
これは、私が使用した3つの簡単なステップです。
1)最近の更新をコミットする場所に新しいブランチを作成します。
git branch <branch name>
2)新しいブランチでコミットするための最近のコミットIDを検索します。
git log
3)そのコミットIDをコピーします。最新のコミットリストが一番上に表示されることに注意してください。あなたはあなたのコミットを見つけることができます。また、メッセージを介してこれを見つけます。
git cherry-pick d34bcef232f6c...
また、いくつかのコミットIDを提供することもできます。
git cherry-pick d34bcef...86d2aec
これであなたの仕事は終わりました。正しいIDと正しいブランチを選択した場合、成功します。したがって、これを行う前に注意してください。そうしないと、別の問題が発生する可能性があります。
これでコードをプッシュできます
git push