29

環境

チームメイトの 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

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

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

質問

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

とりわけ、da8b496( git checkout -b new-develop da8b496) から新しいブランチを作成し、現在のブランチを放棄することを検討しましたdevelopが、それは適切ではないと感じました。


やったこと(詳細)

最初に、コミットa78b993および用の新しいブランチを作成しました8c29252。これらのコミットには保持したい作業が含まれており、最終的にはメインの開発ブランチにマージし直すためです。

$ 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
4

4 に答える 4

13

履歴が変更された場合でも、戻って実験できるブランチを作成できます。Git とは、「そうするべきだ」と言う必要がないことを意味します。</a> 好みの現実に収束する場合は、それを選択してください。そうでなければ、捨ててください。

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

代替案 1: git revert

まず、冒険を始めた場所にスクラッチ ブランチを作成します。

$ git checkout -b tmp-revert faada93

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

$ git revert da8b496..faada93

代替案 2: git commit-tree

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

Git オブジェクト グラフ、*Pro Git* の図 151 Pro Gitの図 151 、第 2 版。

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

を使用してこの低レベルのステッチングを実行する方法については、以下をお読みくださいgit commit-tree

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

$ git checkout -b tmp-ctree faada93

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

git commit-tree「plumbing」は git の低レベル コマンドであり、「porcelain」とは対照的です。使いにくい、または使い慣れていないと感じるかもしれませんが、この場合、必要な結果を正確に制御できます。

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

$ echo da8b496 に戻す | \
    git commit-tree da8b496^{tree} -p $(git rev-parse tmp-ctree)
新しいコミットsha1

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

$ git マージnew-commit-sha1

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

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

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

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

掃除

上記の一時的なブランチを削除するには、別のブランチに切り替えて発砲します、McManus さん。他のブランチはそのまま残します。

$ git checkout 開発
$ git branch -D tmp-revert tmp-ctree

で確認できるように、2つは同一である必要があります

$ git diff tmp-revert tmp-ctree

develop1 つを保持するには、ブランチにマージします。

$ git checkout 開発
$ git merge --ff-only tmp-ctree
$ git push オリジン開発
于 2013-08-06T14:39:27.213 に答える
11

あなたは同じ場所に小さなチームを持っているので、コミュニケーションは問題になりません。コミット履歴を本来あるべき姿にします:

git branch -f develop dab4896
git branch newfeature 8c29252
git push -f origin develop newfeature

全員に再取得してもらいます。あなたは終わった。

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

于 2013-08-07T15:44:31.497 に答える
5

これは、この回答の複製と見なすことができることをお勧めします: Make the current git branch a master branch

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
于 2016-02-24T00:59:37.850 に答える
0

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

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

これを行う (元に戻して commit を削除する) のは、まだ誰もこのコミットをプルしていない場合のみです。

于 2013-08-06T20:47:09.660 に答える