私が「master」ブランチに取り入れた変更の1つが GCC2.95 でGitの構築を失敗させる事になりました。 それらは意図的な移植性の修正でしたが、gcc-2.95で動作し続けることも重要でした。 これは、私が、コアGitツールと基本磁器コマンド(barebone Porcelain)を使用して、「master」ブランチの変更を元に戻し、「seen」ブランチを調整するために行ったことです。

最初に、私が物事を台無しちゃった場合に備えて、使い捨てのブランチを準備します。

$ git checkout -b revert-c99 master

今、私は revert-c99 ブランチにいます。 どのコミットを元に戻すかを考えてみましょう。 master ブランチの先頭がマージであり、そこから2つ目の親(つまり、マージ元の外部コミット)に元に戻したい変更があることを偶然知っています。 さらに、そのマージによって5つのコミットが導入されたことを知っています。

 $ git show-branch --more=4 master master^2 | head
 * [master] Merge refs/heads/portable from http://www.cs.berkeley....
  ! [master^2] Replace C99 array initializers with code.
 --
 -  [master] Merge refs/heads/portable from http://www.cs.berkeley....
 *+ [master^2] Replace C99 array initializers with code.
 *+ [master^2~1] Replace unsetenv() and setenv() with older putenv().
 *+ [master^2~2] Include sys/time.h in daemon.c.
 *+ [master^2~3] Fix ?: statements.
 *+ [master^2~4] Replace zero-length array decls with [].
 *  [master~1] tutorial note about git branch

上記の --more=4 は、「refのマージベースに到達した後、さらに4つの共通のコミットが表示されるまで表示する」ことを意味します。 その最後のコミットは、「portable」ブランチがメインのgit.gitリポジトリからフォークされた場所であったため、それ以降、両方のブランチのすべてが表示されます。 head コマンドを使用して、出力を最初の一握りに制限しました。

これで、 master^2~4 (「masterの(複数の親のうち)2番目の親を見つけて、その親から辿って4代前まで遡る(その親自身は1代目)」といいます)が元に戻したいものであることがわかりました。 なぜそれを元に戻すのかについても言及したいので、 -n フラグが「git revert」に与えられます。 これにより、実際のコミットが行われなくなり、代わりに「git revert」が使用したいコミットログメッセージを「.msg」ファイルに残します:

$ git revert -n master^2~4
$ cat .msg
Revert "Replace zero-length array decls with []."

This reverts 6c5f9baa3bc0d63e141e0afc23110205379905a4 commit.
$ git diff HEAD ;# to make sure what we are reverting makes sense.
$ make CC=gcc-2.95 clean test ;# make sure it fixed the breakage.
$ make clean test ;# make sure it did not cause other breakage.

変更の戻しは道理にかなっており(上記「diff」出力の読み取りのところ)、問題を修正し(上記 make CC=gcc-2.95 テストのところ)、新しい破損を引き起こしません(上記の最後の make test ののところ)。 私はコミットする準備ができました:

$ git commit -a -s ;# read .msg into the log,
                    # and explain why I am reverting.

私が上記の手順のいずれかを台無しにする可能性もありましたが、最悪の場合、 git checkout master を実行して最初からやり直すことができました。 幸いなことに、私はその必要はありませんでした。 私が現在のブランチ revert-c99 に持っているものが、私が欲したものです。 したがって、それを「master」にマージし直します。

$ git checkout master
$ git merge revert-c99 ;# this should be a fast-forward
Updating from 10d781b9caa4f71495c7b34963bef137216f86a8 to e3a693c...
 cache.h        |    8 ++++----
 commit.c       |    2 +-
 ls-files.c     |    2 +-
 receive-pack.c |    2 +-
 server-info.c  |    2 +-
 5 files changed, 8 insertions(+), 8 deletions(-)

この時点でテストをやり直す必要はありません。 早送り(fast-forward)すると、 masterrevert-c99 と正確に一致することがわかります。 事実、以下を実行すると:

$ git diff master..revert-c99

これは、何も出力しません。

次に、私達は通常どおり seen ランチをリベースします。

$ git checkout seen
$ git tag seen-anchor seen
$ git rebase master
* Applying: Redo "revert" using three-way merge machinery.
First trying simple merge strategy to cherry-pick.
* Applying: Remove git-apply-patch-script.
First trying simple merge strategy to cherry-pick.
Simple cherry-pick fails; trying Automatic cherry-pick.
Removing Documentation/git-apply-patch-script.txt
Removing git-apply-patch-script
* Applying: Document "git cherry-pick" and "git revert"
First trying simple merge strategy to cherry-pick.
* Applying: mailinfo and applymbox updates
First trying simple merge strategy to cherry-pick.
* Applying: Show commits in topo order and name all commits.
First trying simple merge strategy to cherry-pick.
* Applying: More documentation updates.
First trying simple merge strategy to cherry-pick.

一時的なタグ seen-anchor は、「git rebase」が失敗した場合に備えて注意しているだけです。 この後、健全性チェックのために以下を行うことができます:

$ git diff seen-anchor..seen ;# make sure we got the master fix.
$ make CC=gcc-2.95 clean test ;# make sure it fixed the breakage.
$ make clean test ;# make sure it did not cause other breakage.

すべてが順調です。 一時的なブランチやタグはもう必要ないので、削除します:

$ rm -f .git/refs/tags/seen-anchor
$ git branch -d revert-c99

これは緊急の修正(emergency fix)だったので、次のリリースは何日か先になると思いますが、「release candidate」(リリース候補)ブランチにマージした方がいいかもしれませんね。

$ git checkout rc
$ git pull . master
Packing 0 objects
Unpacking 0 objects

* commit-ish: e3a693c...        refs/heads/master from .
Trying to merge e3a693c... into 8c1f5f0... using 10d781b...
Committed merge 7fb9b7262a1d1e0a47bbfdcbbcf50ce0635d3f8f
 cache.h        |    8 ++++----
 commit.c       |    2 +-
 ls-files.c     |    2 +-
 receive-pack.c |    2 +-
 server-info.c  |    2 +-
 5 files changed, 8 insertions(+), 8 deletions(-)

そして、最終的なリポジトリのステータスは以下のようになります:

 $ git show-branch --more=1 master seen rc
 ! [master] Revert "Replace zero-length array decls with []."
  ! [seen] git-repack: Add option to repack all objects.
   * [rc] Merge refs/heads/master from .
 ---
  +  [seen] git-repack: Add option to repack all objects.
  +  [seen~1] More documentation updates.
  +  [seen~2] Show commits in topo order and name all commits.
  +  [seen~3] mailinfo and applymbox updates
  +  [seen~4] Document "git cherry-pick" and "git revert"
  +  [seen~5] Remove git-apply-patch-script.
  +  [seen~6] Redo "revert" using three-way merge machinery.
   - [rc] Merge refs/heads/master from .
 ++* [master] Revert "Replace zero-length array decls with []."
   - [rc~1] Merge refs/heads/master from .
 ... [master~1] Merge refs/heads/portable from http://www.cs.berkeley....