[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を使う際にはアーキテクチャごと理解するのがお勧め.以下の本はgitのアーキテクチャを分かりやすく説明してくれるため一冊用意しておくと良い.

created by Rinker
¥3,080 (2024/04/23 09:00:43時点 楽天市場調べ-詳細)

参考文献

コメント

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