git mergeしたら改行コードが変わっていたのをrebase -iで何とかする。もしくはrebase -i時のコンフリクトを無視して次に積まれるコードを取得する

他の人にやってもらった数コミット分をmergeしたところ、どうやらその人が変更したファイルのみ、文字コードが変わっちゃってました。改行コードを元に戻すコミットを、次に重ねてもいいっちゃいいんだけど、改行コードが変わった境目のdiffが、全部remove全部addになっちゃって、後でわかりにくいです。せっかくなのでgit rebase -iで直してみようとやってみたら、意外とはまってしまいました。

自分の中で、一応出来たけど最善策じゃないという状態なので、エントリー書くのもなんかまとまらない構成になっちゃってます。何か思うところやアドバイスなどあったらコメント頂けるとうれしいです。

現状の解決方法

その前に状況
$ git log --oneline
e1e73aa (3)#containerのpaddingを8pxに統一
90087ef (2)#containerのwidth:100%に
18b8ba0 (1)#containerのmargin削除(改行コードが変わった) #ここから上が他の人の作業分
be82342 fix with jsHint
1c8d04c add destroy as stab

このログはこのエントリー用に作ったダミーです。

まずコンフリクトする
$ git rebase --continue
[detached HEAD 0a70bdc] (1)#containerのmargin削除(改行コードが変わった)
 1 file changed, 1 deletion(-)
error: could not apply 90087ef... (2)#containerのwidth:100%に
[snip]
Could not apply 90087ef... (2)#containerのwidth:100%に

git rebase -iで古いほうから順番に改行コードを戻そうとすると、当たり前だけど2個目のコミットと、改行コードを修正した1個目のコミットが、改行コードが違うという事で、ファイル全行でコンフリクトする。

rebaseで次に積むコミットの内容を無理やり取り出す
$ git checkout 90087ef -- asset/css/base.css
$ git status
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   asset/css/base.css
#
HEAD 18b8ba0 -> 改行コード変更でハッシュ変更 -> 0a70bdc
index 90087ef
work tree コンフリクトして<<<<<<< HEADとか入ったファイル

ややこしいけど、HEADの0a70bdcは、要するに18b8ba0 (1)#containerのmargin削除(改行コードが変わった)だけど改行コードが変わる変更を入れたので、リビジョンハッシュが変わって0a70bdcになっている。
ここで問題は、次にrebaseする90087ef (2)#containerのwidth:100%にも、内容は変えずに改行コードだけ変えたいのに、work treeには<<<<<<< HEADとか入ったコンフリクト状態のファイルしかない事。indexにちょうど90087efがあるけど取り出せない*1。いろいろ試した中で、唯一できたのがファイルとリビジョンハッシュを指定してのgit checkout [] [--] []でした。

改行コードを戻す変更をしてaddして次に進む
$ vim asset/css/base.css #改行コードを戻す
$ git add asset/css/base.css
$ git diff --cached
diff --git a/asset/css/base.css b/asset/css/base.css
index 4fbf79c..2541e3a 100644
--- a/asset/css/base.css
+++ b/asset/css/base.css
@@ -2,7 +2,7 @@
 
 #container{
   padding: 32px 8px 8px 8px;
-  width:750px;
+  width:100%;
 }
 
 nav{
$ git rebase --continue

work treeに90087ef (2)#containerのwidth:100%にの内容を取り出せたら、あとは改行コードさえ戻したものに出来ればいいので、その変更をした上で普通にrebaseを進められます。

ただし、この工程を繰り返すしかないので、もっと良い方法を探してます

この方法の場合、毎回コンフリクトが発生するのが約束されているので、この作業をrebaseする全コミットに対して繰り返す必要があります。正確には1個目のコミットは問題なく修正できるので、1個少ないですが。なんにせよ、コミットの数が多かったり、ファイルが数個ならともかく数十個以上あったら、正直やってられません。
なのでもっと良い方法を探しています。

詳細の解説
$ git log --oneline
e1e73aa (3)#containerのpaddingを8pxに統一
90087ef (2)#containerのwidth:100%に
18b8ba0 (1)#containerのmargin削除(改行コードが変わった) #ここから上が他の人の作業分
be82342 fix with jsHint
1c8d04c add destroy as stab

$ git rebase -i 18b8ba0^ #書き換えたいコミットの1個前までを指定
edit 18b8ba0 (1)#containerのmargin削除(改行コードが変わった)
edit 90087ef (2)#containerのwidth:100%に
edit e1e73aa (3)#containerのpaddingを8pxに統一
Stopped at 18b8ba0... (1)#containerのmargin削除(改行コードが変わった)
You can amend the commit now, with

	git commit --amend

Once you are satisfied with your changes, run

	git rebase --continue

$ git status
# Not currently on any branch.
nothing to commit (working directory clean)

$ vim asset/css/base.css #改行コードを元に戻して
$ git add asset/css/base.css
$ git status
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   asset/css/base.css
#
$ git rebase --continue
[detached HEAD 0a70bdc] (1)#containerのmargin削除(改行コードが変わった)
 1 file changed, 1 deletion(-)
error: could not apply 90087ef... (2)#containerのwidth:100%に

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To check out the original branch and stop rebasing run "git rebase --abort".
Could not apply 90087ef... (2)#containerのwidth:100%に
HEAD 18b8ba0 -> 改行コード変更でハッシュ変更 -> 0a70bdc
index 90087ef
work tree コンフリクトして<<<<<<< HEADとか入ったファイル

ここまで進めたとき、実はindexには欲しい内容である90087ef (2)#containerのwidth:100%にがそのまま入ってます。これをまるっと取り出せれば、もうちょい楽になるんですが・・・

$ git checkout .
error: path 'asset/css/base.css' is unmerged
$ git checkout asset/css/base.css 
error: path 'asset/css/base.css' is unmerged
$ git checkout 90087ef
asset/css/base.css: needs merge
error: you need to resolve your current index first
$ git checkout 90087ef --
asset/css/base.css: needs merge
error: you need to resolve your current index first

$ git checkout 90087ef -- asset/css/base.css

indexからwork treeに持ってくるのは、確かcheckout file... or dir...だと思ったので、このように色々と試してみたけど、rebase/コンフリクト中には使えないみたい。最終的にうまくいったのがgit checkout 90087ef -- asset/css/base.cssでした。

MERGE STRATEGIESで何とかならないのか?

git rebaseにはs , --strategy=でMERGE STRATEGIESが指定できます。まったく使った事ないけどmanを読む限りでは、マージする際のアルゴリズムを指定できるようなものっぽい。MERGE STRATEGIESを変えてなんとかならないかなと、色々試してみたけど、コンフリクトの仕方や、そのdiffには変化がありませんでした。

ours
This resolves any number of heads, but the resulting tree of the merge is
always that of the current branch head, effectively ignoring all changes from
all other branches. It is meant to be used to supersede old development history
of side branches. Note that this is different from the -Xours option to the
recursive merge strategy.

これみたく、無条件で片方の変更を採用する的な事が書いてあるMERGE STRATEGIESもあるんですけど・・・

git rebase interactive does not respect merge options
  • Date: Wed, 25 Apr 2012 12:53:10 -0400
  • From: Mathieu Boespflug
  • Subject: git rebase interactive does not respect merge options

Hi,

I have the following issue with git rebase -i: it does not seem to
respect any merge options that I pass it.

git rebase interactive does not respect merge options

検索しても情報すくない中、気になるタイトルの投稿がMLにあったみたいだけど、リプライついてない様子。

ちょっとお手上げ状態です。

環境

git 1.7.9
Windows Windows7
Cygwin 1.7.11-1

*1:詳細は後述