web-dev-qa-db-ja.com

一連のプッシュされたマージとコミットをGitで元に戻す(履歴を書き換えない)

環境

チームメイトの1人が、いくつかのコミットをメインの開発ブランチに誤ってプッシュしました。私たちは小さな共同チームです。私たちのリモートリポジトリは、内部サーバーでホストされています。

これがコミットログの先頭です(これらのコミットはすべてすでにプッシュされています)。

$ git log develop -6 --pretty=oneline --abbrev-commit
faada93 Merge branch 'develop' of <our_repo_path>.git
244d174 Support classes again
a97a877 Pruned all unused references (again).
8c29252 Merge branch 'develop' of <our_repo_path>.git
a78b993 Support models & methods - product types & categories
da8b496 Resolved JIRA issue PPF-182

da8b496developブランチに残したい最後のコミットなので、最後の5つのコミットを元に戻す必要がありました。 「機能ブランチ」で作業を続けるために、8c29252から新しいブランチを作成しました。

私は この答えこのLinusからの投稿 に導かれて多くのことを試しましたが、最終的には以下のターミナル履歴で確認できることを行いました。しかし、私がやったことが「正しい方法」であるかどうかはわかりません。私が見つけた情報は複雑でした。この特定の問題に対する「最善の解決策」を見分けることができませんでした。

質問

私が選択したアプローチ(詳細は以下を参照)は、履歴を損なうことなく、これらの5つのコミットを元に戻す良い方法でしたか?同じことを達成するためのより簡単なまたは「より正しい」方法はありますか?

とりわけ、da8b496git checkout -b new-develop da8b496)から新しいブランチを作成して、現在のdevelopブランチを破棄することを検討しましたが、それは正しくありませんでした。


私がやったこと(詳細)

最初に、コミットa78b9938c29252の新しいブランチを作成しました。これらのコミットには保持したい作業が含まれ、最終的にメインの開発ブランチにマージされます。

$ git checkout -b new-feature-brach 8c29252

その後、開発ブランチで問題のコミットを元に戻しました。

私は最初にこれを試しましたが、うまくいきませんでした(コミットの一部がマージである可能性が高いため)

$ git revert a78b993..HEAD
error: a cherry-pick or revert is already in progress
hint: try "git cherry-pick (--continue | --quit | --abort)"
fatal: revert failed

だから…代わりに各コミットを手動で元に戻しました。一つずつ:

$ git revert -m 1 faada93
[develop 40965a5] Revert "Merge branch 'develop' of <our_repo_path>.git"
8 files changed, 167 insertions(+), 3 deletions(-)

$ git revert 244d174
[develop 3cebd68] Revert "Support classes again"
45 files changed, 557 insertions(+), 1572 deletions(-)
(list of affected files)

$ git revert a97a877
error: could not revert a97a877... Pruned all unused references (again).
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

$ git mergetool
Merging:
exampleFile1.cs
exampleFile2.cs

Deleted merge conflict for 'exampleFile1.cs':
{local}: deleted
{remote}: modified file
Use (m)odified or (d)eleted file, or (a)bort? m

Deleted merge conflict for 'exampleFile2.cs':
{local}: deleted
{remote}: modified file
Use (m)odified or (d)eleted file, or (a)bort? m

$ git commit -m "Adding files to be reverted along with the next commit."
[develop 15bc02b] Adding files to be able to revert the next commit in line.
2 files changed, 239 insertions(+)
(list of affected files here)

$ git revert -m 1 8c29252
# On branch develop
# Your branch is ahead of 'Origin/develop' by 3 commits.
#   (use "git Push" to publish your local commits)
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       exampleFile1.cs.orig
#       exampleFile2.cs.orig
nothing added to commit but untracked files present (use "git add" to track)

$ git revert a78b993
[develop 841e77c] Revert "Support models & methods - product types & categories"
2 files changed, 239 deletions(-)
(list of affected files here)

すべての復元が完了した後、ログをコミットします。

$ git log develop -10 --pretty=oneline --abbrev-commit
841e77c Revert "Support models & methods - product types & categories"
15bc02b Adding files to be able to revert the next commit in line.
3cebd68 Revert "Support classes again"
40965a5 Revert "Merge branch 'develop' of <our_repo_path>.git"
faada93 Merge branch 'develop' of <our_repo_path>.git
244d174 Support classes again
a97a877 Pruned all unused references (again).
8c29252 Merge branch 'develop' of <our_repo_path>.git
a78b993 Support models & methods - product types & categories
da8b496 Resolved JIRA issue PPF-182

復帰後のグラフ:

$ git log --graph --oneline -8 develop
* 841e77c Revert "Support models & methods - product types & categories"
* 15bc02b Adding files to be able to revert the next commit in line.
* 3cebd68 Revert "Support classes again"
* 40965a5 Revert "Merge branch 'develop' of <our_repo_path>.git"
*   faada93 Merge branch 'develop' of <our_repo_path>.git
|\
| * a97a877 Pruned all unused references (again).
| *   8c29252 Merge branch 'develop' of <our_repo_path>.git
| |\
| | * da8b496 Resolved JIRA issue PPF-182

私には正しいようです。最後に、保持したくないバックアップファイルをいくつか削除します。

$ git clean -fd
(list of affected files here)

現在のステータスはクリーンです:

$ git status
# On branch develop
# Your branch is ahead of 'Origin/develop' by 4 commits.
#   (use "git Push" to publish your local commits)
#
nothing to commit, working directory clean

次に、すべてをリモートにプッシュします。

git Push Origin develop
25
Leif

同じ場所に配置された小さなチームがあるので、コミュニケーションは問題ではありません。見たとおりにコミット履歴を表示します。

git branch -f develop dab4896
git branch newfeature 8c29252
git Push -f Origin develop newfeature

全員に再フェッチしてもらいます。完了です。

このような間違いは、書き直す理由の1つです。

9
jthill

履歴が変わっても、ブランチを作成して前に戻って実験することができます。 Gitは、「あなたが持っているべきである」と決して言う必要がないことを意味します。 あなたがより好きな現実に収束するなら、それを続けてください。そうでなければ、それを捨てます。

以下の例では、リポジトリ内の他のすべてをそのままにする新しいブランチを作成します。

代替1:git revert

最初に、冒険を開始した時点でスクラッチブランチを作成します。

$ git checkout -b tmp-revert faada93

コミット範囲を指定すると、 git revert は複数のコミットを取り消します。

$ git revert da8b496..faada93

代替2:git commit-tree

スコットチャコンによる Pro Git の第2版の Git Internals — Git Objects 、セクション10.2の図を検討してください。ベン・ストラウブ。最上位のコミット(「3番目のコミット」)には、1a410eで始まるSHA1ハッシュがあります。この履歴のコンテキストでは、1a410e^{tree}3c4e9cに解決されます。つまり、3番目のコミットの右側にあるツリーオブジェクトがすぐに解決されます。

Git Object Graph, Figure 151 from *Pro Git*図151 from Pro Git 、第2版.

このモデルを調べて、gitがcontentを追跡する方法を理解します。ツリーが2番目のコミットと同じである4番目の新しいコミット(つまり、0155eb)を作成すると、新しいコミットオブジェクトが追加され、shareまたは「ポイント」新しい重複オブジェクトを追加するのではなく、既存のツリーとブロブ。

git commit-tree を使用してこの低レベルのスティッチングを実行する方法を学ぶために読んでください。

作業する別の一時的なブランチを作成することから始めます。

$ git checkout -b tmp-ctree faada93

この時点で、ツリー(つまり、コミットされたコード)が、保持したい最後のコミットであるda8b496のツリーと同一である新しいコミットを作成します。このツリーは、git:da8b496^{tree}で直接アドレス指定できます。

git commit-treeは、「磁器」ではなく、gitの低レベルコマンドである「配管」です。使いづらい、または使い慣れていないように感じるかもしれませんが、この場合、必要な結果を正確に制御できます。

ツリーがda8b496と同じであり、親(-p)が現在のブランチの先端であるfaada93である、アタッチされていない新しいコミットを作成します。 git commit-treeは、以下のコマンドがechoコマンドで提供する標準入力の新しいコミットのコミットメッセージを読み取ることに注意してください。

$ echo da8b496に戻す|\
 git commit-tree da8b496 ^ {tree} -p $(git rev-parse tmp-ctree)
new-commit-sha1

上記の斜体部分はコマンドの一部ではありません。これは、git commit-treeが新しく作成されたコミットのSHA1ハッシュを出力することを示しています。新しいコミットのSHA1がわかっているため、ブランチをそのポイントに移動できます。例:

$ gitマージ new-commit-sha1

上記のコマンドで、new-commit-sha1git commit-treeからの出力に置き換えます。 (同じことをgit reset --hard new-commit-sha1で行うこともできますが、ハードリセットは鋭いツールであり、カジュアルな使用は避けた方がよいでしょう。)

上記のすべてを1つの複合コマンドにまとめることができます。

$ git merge --ff-only $(echo Revert back to da8b496 |\
 git commit-tree da8b496 ^ {tree} -p $(git rev-parse tmp-ctree))

--ff-onlyの-​​ git merge への切り替えは、予期しない事態を防ぐためのものです。あなたの意図は、新しいコミットが現在のブランチヘッドの早送りまたは子孫になることです。実際、その直接の子です!

掃除

上の一時的なブランチを削除するには、別のブランチに切り替えて、McManus氏を解雇します。あなたの他のブランチはあなたがそれらを残したのと同じようになります。

$ git checkout develop 
 $ git branch -D tmp-revert tmp-ctree

あなたが確認できるように、2つは同一でなければなりません

$ git diff tmp-revert tmp-ctree

保持するには、それをdevelopブランチにマージします。

$ git checkout develop 
 $ git merge --ff-only tmp-ctree 
 $ git Push Origin develop
12
Greg Bacon

これはこの回答の複製と見なすことができることをお勧めします: 現在のgitブランチをマスターブランチにする

Jefromiの優れたソリューションは次のとおりです。

[git branch better_branch <last good commit>]
git checkout better_branch
git merge --strategy=ours master    # keep the content of this branch, but record a merge
git checkout master
git merge better_branch             # fast-forward master up to the merge
3
Chris Conover

あなたがしようとしていることは非常に危険です。

実際、すでにリポジトリにプッシュしたコミットを元に戻して削除できますが、誰かがすでに変更をプルしていて、削除しようとしているcommitIdを持っている場合、リポジトリは「不安定」になり、gitは次のことができなくなります履歴から削除されたコミットを削除したため、プルとプッシュを処理します。

これを行う(commitを元に戻して削除する)のは、誰もまだこのcommitをプルしていない場合に限ります。

0
CodeWizard