この記事では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のアーキテクチャを分かりやすく説明してくれるため一冊用意しておくと良い.
コメント