2012年11月12日月曜日

[git] コンフリクトしたところだけ、vimdiffで分割して表示してほしい

背景


よくコンフリクトする気がする。

そんなとき普通、ファイル開いて、コンフリクトしてるところ検索して、解消する。でも結構めんどくさい。コンフリクトしてる箇所が1行とか2行程度ならいいけど、数十行にわたってコンフリクトしてるのを解消するのは結構しんどい。そんなときはやっぱりvimdiffとかで開いて、コンフリクトを解消したいと思うところ。

調べてみると,こんなやり方が見つかる。
Three-way merging for git using vim
なんかそれっぽいけど、このやり方だと「変更のもとになるファイル」(BASE)と、「LOCALの変更ファイル」(LOCAL)、「REMOTEの変更ファイル」(REMOTE)を一緒に表示しているだけ。つまり、「コンフリクトが発生している状態のファイル」(MERGED)で、コンフリクト箇所調べて、解消するのは変わりない。

やりたいのは、自動でマージできたところはそのままで、コンフリクトしたところだけ分割して、vimdiffで表示すること。ということで書いてみた。


やりかた


基本的なやり方は参考リンクと同じ。mergetool設定して、mergeする時それを呼び出す。違うところは、単純にvimdiffで開くのではなく、MERGEDファイルのコンフリクトしてる箇所を分割したファイルを作り、そのファイルとdiffをとっているところ。

mergetoolはこんな感じのを準備する。
#!/bin/bash

function fixpatch(){
    basefile=${1}
    tmp="tmp.php"
    tmp2="tmp2.php"

    cp $basefile $tmp
    if [ "${2}" = "local" ]; then
        sed "/>>>>>>>/d" $basefile > $tmp
        start_array=($(cat $tmp  | grep -n "<<<<<<<" | cut -d ':' -f 1))
        end_array=($(cat $tmp | grep -n "=======" | cut -d ':' -f 1))
    elif [ "${2}" = "remote"  ]; then
        sed "/<<<<<<</d" $basefile > $tmp
        start_array=($(cat $tmp  | grep -n "=======" | cut -d ':' -f 1))
        end_array=($(cat $tmp | grep -n ">>>>>>>" | cut -d ':' -f 1))
    fi

    cat $tmp > $tmp2

    # コンフリクト箇所を省く.
    for (( i=0; i<${#start_array[@]}; i++ ))
    do
        cp $tmp2 $tmp
        num=$((${#start_array[@]} - ${i} - 1))
        sed -e "${start_array[$num]},${end_array[$num]}d" $tmp > $tmp2
    done

    cat $tmp2
    rm $tmp
    rm $tmp2
    return 0
}

# ブランチ名を取得
BRANCH=$(git branch --no-color | sed -n -e 's/^\* //p')
gitroot="$(git rev-parse --show-toplevel)/"

# mergeに関するファイルを取得
MERGED=${1}
LOCAL=${2}
BASE=${3}
REMOTE=${4}

# MERGEDファイルのコンフリクト箇所を除外
fixedpatch_local="fixedpatch_local.php"
fixedpatch_remote="fixedpatch_remote.php"
fixpatch ${MERGED} "local" > $fixedpatch_local
fixpatch ${MERGED} "remote" > $fixedpatch_remote

echo ${fixedpatch_local}
echo ${fixedpatch_remote}

cp ${fixedpatch_local} ${MERGED}


# vimdiffで開く
vim -f \
    -c 'set nofoldenable' \
    -c 'wincmd p'         \
    -c 'set nofoldenable' \
    -c 'wincmd p'         \
    -c 'noremap J ]c'     \
    -c 'noremap K [c'     \
    -c "set tags+=.tags;${gitroot}"  \
    -d $MERGED $fixedpatch_remote

# ゴミファイルを削除
rm $fixedpatch_local
rm $fixedpatch_remote


そして、mergetoolに設定する。これは、参考リンクとほぼ同じ。
git config --global mergetool.vimdiff3.cmd 'git_merge_wrapper3 "$MERGED" "$LOCAL" "$BASE" "$REMOTE"'
git config --global mergetool.keepBackup false
git config --global merge.tool vimdiff3

使い方


コンフリクトしてるときに、git mergetoolを叩くと、コンフリクトしているファイルを開き、コンフリクトしている箇所を分割して、vimdiffで表示する。


karino@goldfish ~/src/trunk $ git st
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   lib/model/PersonGuildMember.php
#
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      apps/main/config/template.yml
#       both modified:      apps/main/modules/guild/actions/actions.class.php
#       both modified:      config/schema.yml
#       both modified:      lib/util/GuildUtil.php
#
karino@goldfish ~/src/trunk $ git mergetool 
Merging the files: apps/main/config/template.yml
apps/main/modules/guild/actions/actions.class.php
config/schema.yml
lib/util/GuildUtil.php

Normal merge conflict for 'apps/main/config/template.yml':
  {local}: modified
  {remote}: modified
Hit return to start merge resolution tool (vimdiff3): 

ここでenter押せばvimdiffが表示される。左側がworkingtreeのファイルで、右側がREMOTE側のファイル。diffの時と同じ用に、Shift-j、Shift-kで差分にジャンプできるようにしている。僕の好み。これで、コンフリクトなんか怖くない。


0 件のコメント:

コメントを投稿