[git] 異なるブランチから1つのファイルだけマージする

この記事ではgitを用いて,異なるブランチから1つのファイルだけマージする方法を説明する.

背景としては,jupyter notebookを含んだrepositoryをgithubで共有していると,jupyter notebook上のファイルのコード自体は変更されていないが,どこまで回すかで別ファイルとして認識されてしまう.そういった場合に一部のファイルだけを持ってきてマージしたい,という状態になった.

サンプルデータ,gitの準備

以下のbashコードを sample_git.sh で登録し回す.

if [ -e .git ]; then
    ls | grep -v "sample_git.sh" | xargs rm -r
    rm -rf .git
fi

git init
echo "text1" > file1.txt
echo "text2" > file2.txt 
git add .
git commit -m "first commit"

git branch branch1 
git branch branch2 

git checkout branch1
echo "add text1 to file1" >> file1.txt 
git add . 
git commit -m "branch1 is updated"

git checkout branch2 
echo "add text2 to file1" >> file1.txt
git add . 
git commit -m "branch2 is updated" 

git checkout branch1

echo -e "\nThe following is the commit graph"
git log --graph --pretty=oneline --abbrev-commit --all

すると,以下のようなメッセージとコミットグラフが出力される.

The following is the commit graph
* 62d40d4 (HEAD -> branch1) branch1 is updated
| * 58f3e6f (branch2) branch2 is updated
|/  
* bce5f63 (master) first commit

branch2をマージすると,以下のようなconflictが生じる.

text1
<<<<<<< HEAD
add text1 to file1
=======
add text2 to file1
>>>>>>> branch2

この記事の最後の目的としては,git merge を使わずに,このファイルだけ,conflictを生じさせる状況を作り出すことである.

注: bash sample_git.sh を回せば再び,マージする前の状態まで戻すことが可能.

git merge-file

この用を足すコマンドが git merge-file である.
git merge-file の使い方とては,
git merge-file <current-file> <base-file> <other-file> > <output-file>
とすることで,<base-file>を起点として<other-file> と<current-file> の違いをマージしたときのように<output-file> に出力する.

そこで以下のようにするとfile1.txtをbranch2のfile1.txtとconflictを起こした状態に出来る.

git show branch1:./file1.txt > ./file1.ours.txt
git show branch1~:./file1.txt > ./file1.base.txt
git show branch2:./file1.txt > ./file1.theirs.txt
git merge-file -p ./file1.ours.txt ./file1.base.txt ./file1.theirs.txt > ./file1.merged.txt

ただ,このコードをいちいち書くのはめんどくさいため以下のbashコードを.bash_profileなり,.bashrc に登録しておくと楽になる.

function git_merge_one_file(){
    if [ $# -ne 4 ]; then 
        echo -e "\nUsage : git_merge_one_file    "
        echo "     should include './' for a prefix" 
        echo "    if you want to select a file in the currenct directry."
    else
        file_name=$1
        ours=$2
        base=$3
        theirs=$4
        git show ${ours}:${file_name} > ${file_name}.ours
        git show ${base}:${file_name} > ${file_name}.base
        git show ${theirs}:${file_name} > ${file_name}.theirs
        git merge-file -p ${file_name}.ours ${file_name}.base ${file_name}.theirs > ${file_name}
        rm ${file_name}.ours ${file_name}.base ${file_name}.theirs
    fi 
}

この関数を用いれば以下の一行を打って1つのファイルのみをマージさせることが可能である.

git_merge_one_file ./file1.txt branch1 branch1~ branch2

補足: git checkout と git cherry-pick

以上までは,conflictが起きるようなファイルを持ってきてマージさせる方法であった.これは,他のファイルに影響を与えずに1つのファイルだけをmergeさせたい方法であった.

もし,対象のファイルが他の人によっていじられておらず,コンフリクトする恐れがないならば,git checkout <branch> <file> によって他のブランチからファイルを持ってこれる.

今回の例でいうと,

bash sample_git.sh
git checkout branch2 file1.txt
cat file1.txt
text1
add text2 to file1

branch1のfile1.txtが完全に上書きされていることが分かる.


また,一箇所だけのcommitを反映させる方法として,git cherry-pick が存在する.

bash sample_git.sh
git cherry-pick branch2
cat file1.txt
text1
<<<<<<< HEAD
add text1 to file1
=======
add text2 to file1
>>>>>>> 9ff9b12... branch2 is updated

こちらは,conflict が起きたことを知らせてくれる.

———-雑感(`・ω・´)———-
これで,jupyter notebookを適当にまわして,ソースコードをいじっても苦労を減らすことが出来る.
jupyter notebook/Labが絡んだときのgithubの共有方法は悩みどころが多い.

参考文献

git/github その他
スポンサーリンク
あきとしのスクラップノート

コメント

タイトルとURLをコピーしました