Last active
November 29, 2018 09:41
-
-
Save tkatochin/1754a062003b43951664 to your computer and use it in GitHub Desktop.
master、ブランチを一括pull
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
export current_branch=`git rev-parse --abbrev-ref HEAD` | |
if [ 0 == `git status | grep -E "nothing to commit, working (directory|tree) clean" | wc -l` ]; then | |
echo "管理外の未コミットのファイルがあるので中止します。" | |
exit 1; | |
fi | |
abs_dirname() { | |
local cwd="$(pwd)" | |
local path="$1" | |
while [ -n "$path" ]; do | |
cd "${path%/*}" | |
local name="${path##*/}" | |
path="$(readlink "$name" || true)" | |
done | |
pwd -P | |
cd "$cwd" | |
} | |
script_dir="$(abs_dirname "$0")" | |
deleteBranch () { | |
deleteTarget=$1 | |
deleteBranchParent=$2 | |
deletedMessage=$3 | |
# 作業中のブランチでもdevelopでもmasterでもなければローカルブランチを削除 | |
if [ $deleteTarget != $current_branch -a $deleteTarget != master -a $deleteTarget != develop ]; then | |
git checkout $deleteBranchParent > /dev/null 2>&1 | |
if [ $? = 0 ]; then | |
# 切り替えで他のブランチのファイルが未管理として現れる場合があるのでクリーン | |
git clean -d -f | |
git branch --delete $deleteTarget > /dev/null 2>&1 | |
if [ $? = 0 ]; then | |
echo "$deletedMessage" | |
else | |
echo "$deleteTarget のブランチの削除に失敗しました。" | |
fi | |
else | |
echo "$deleteTarget を削除しようと、マージ済みの親ブランチである $deleteBranchTarget に切り替えようとしましたが失敗しました。" | |
fi | |
fi | |
} | |
regexEscape () { | |
ruby_code="print Regexp.escape('$1')" | |
ruby -e "$ruby_code" | |
} | |
branchStatus () { | |
escaped_branch_name=`regexEscape $1` | |
git branch -vv | grep -E "\*?\s$escaped_branch_name\s" | |
} | |
#ブランチの-vvの情報からorigin/ のブランチ名を取り出す | |
pickupOriginBranch () { | |
source=`branchStatus $1` | |
source=${source#*[origin/} | |
source=${source%%]*} | |
source=${source%%: *} | |
echo $source | |
unset source | |
} | |
# 指定したローカルブランチとリモートブランチ名が一致している場合は1を返す | |
isMatchNameOrigin () { | |
#branchStatus $1 | grep -F "[origin/$1" > /dev/null | |
escaped_branch_name=`regexEscape $1` | |
branchStatus $1 | grep -E "\[origin\/$escaped_branch_name(\:|\]){1}" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
isSynchronizedOrigin () { | |
branchStatus $1 | grep -F "[origin/$2]" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
isLocalOnly () { | |
branchStatus $1 | grep -v -F "[origin/" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
isExistsRemote () { | |
escaped_branch_name=`regexEscape $1` | |
git branch -r | grep -E "^\s*origin/$escaped_branch_name$" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
isExistsLocal () { | |
escaped_branch_name=`regexEscape $1` | |
git branch | grep -E "^\*?\s*$escaped_branch_name$" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
#現在チェックアウト中のブランチに指定したブランチがマージ済みかどうか判定 | |
isMerged () { | |
escaped_branch_name=`regexEscape $1` | |
git branch --merged | grep -E "^\*?\s*$escaped_branch_name$" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
isAhead () { | |
escaped_branch_name=`regexEscape $2` | |
branchStatus $1 | grep -E "\[origin\/$escaped_branch_name\: ahead [0-9]+\]" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
isBehind () { | |
escaped_branch_name=`regexEscape $2` | |
branchStatus $1 | grep -E "\[origin\/$escaped_branch_name\: behind [0-9]+\]" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
isDiverged () { | |
escaped_branch_name=`regexEscape $2` | |
branchStatus $1 | grep -E "\[origin\/$escaped_branch_name\: ahead [0-9]+, behind [0-9]+\]" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
#追跡ブランチが消えてしまった。または一度削除して作り直されてどこかにいってしまった。 | |
isGone() { | |
branchStatus $1 | grep -E "\[origin\/.+ gone\]" > /dev/null | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
return 0 | |
} | |
checkout () { | |
git checkout $1 > /dev/null 2>&1 | |
if [ $? != 0 ]; then | |
echo "$1 のcheckoutができなかったため諦めてスキップします." | |
return 1 | |
fi | |
# 切り替えで他のブランチのファイルが未管理として現れる場合があるのでクリーン | |
git clean -d -f | |
if [ $? != 0 ]; then | |
echo "$1 のcleanができなかったため諦めてスキップします." | |
return 1 | |
fi | |
return 0 | |
} | |
deleteIfMerged() { | |
parent=$1 | |
branch=$2 | |
isExistsLocal $parent | |
if [ $? = 0 ]; then | |
return 1 | |
fi | |
checkout $parent | |
if [ $? != 0 ]; then | |
return 1 | |
fi | |
isMerged $branch | |
if [ $? = 1 ]; then | |
if [ "$3" = "" ]; then | |
deleteBranch $branch $parent "$branch は $parent にマージ済みのローカルブランチのため削除しました" | |
else | |
deleteBranch $branch $parent "$3" | |
fi | |
return 0 | |
fi | |
return 1 | |
} | |
git fetch --all > /dev/null 2>&1 | |
git fetch -p origin > /dev/null 2>&1 | |
export branches=`git branch | grep -v HEAD` | |
export branches=${branches//\*/} | |
for branch in $branches; do | |
echo "チェック中... $branch" | |
isMatchNameOrigin $branch | |
if [ $? = 0 ]; then | |
isLocalOnly $branch | |
if [ $? = 1 ]; then | |
#ローカルにしかないが追跡が切れているとみなす。リモートに同名ブランチがあれば紐つける | |
isExistsRemote $branch | |
if [ $? = 0 ]; then | |
#ただのローカルブランチは、masterかdevelopにマージ済みであれば削除する | |
deleteIfMerged master $branch | |
if [ $? != 0 ]; then | |
deleteIfMerged develop $branch | |
fi | |
continue | |
fi | |
echo "$branch は追跡できなくなっているため origin/$branch と紐つけます." | |
checkout $branch | |
if [ $? != 0 ]; then | |
continue | |
fi | |
git branch -u origin/$branch | |
if [ $? != 0 ]; then | |
# 失敗したのでスキップ | |
continue | |
fi | |
fi | |
fi | |
isGone $branch | |
if [ $? = 1 ]; then | |
#リモートに同名ブランチがあれば紐つける | |
isExistsRemote $branch | |
if [ $? = 0 ]; then | |
#紐付け先も不明になった | |
echo "$branch のリモートブランチ origin/$branch は存在しません。無効な追跡先を除きます。" | |
git branch --unset-upstream $branch | |
if [ $? = 0 ]; then | |
#ただのローカルブランチは、masterかdevelopにマージ済みであれば削除する | |
deleteIfMerged master $branch | |
if [ $? != 0 ]; then | |
deleteIfMerged develop $branch | |
fi | |
fi | |
continue | |
fi | |
echo "$branch は追跡できなくなっているため origin/$branch と紐つけます." | |
checkout $branch | |
if [ $? != 0 ]; then | |
continue | |
fi | |
git branch -u origin/$branch | |
if [ $? != 0 ]; then | |
# 失敗したのでスキップ | |
continue | |
fi | |
fi | |
remote_branch=`pickupOriginBranch $branch` | |
if [ "$remote_branch" = "" ]; then | |
echo "$branch は追跡ブランチがorginに見つからないためスキップします." | |
continue | |
fi | |
isSynchronizedOrigin $branch $remote_branch | |
if [ $? = 1 ]; then | |
# 一致しているのでなにもする必要がない | |
deleteIfMerged master $branch "$branch はorigin/$remote_branch と同じ位置です。作業中のブランチではないので削除しました。再び編集対象となった時にcheckoutしてください" | |
if [ $? != 0 ]; then | |
deleteIfMerged develop $branch "$branch はorigin/$remote_branch と同じ位置です。作業中のブランチではないので削除しました。再び編集対象となった時にcheckoutしてください" | |
fi | |
continue | |
fi | |
isBehind $branch $remote_branch | |
if [ $? = 1 ]; then | |
# pullできるので実行 | |
checkout $branch | |
if [ $? != 0 ]; then | |
continue | |
fi | |
# pull実行 | |
git pull --rebase origin $remote_branch | |
if [ $? = 0 ]; then | |
# 同じ位置になった。 | |
if [ `git show -s --format=%H` = `git show -s --format=%H origin/$remote_branch` ]; then | |
deleteIfMerged master $branch "$branch はorigin/$remote_branch と同じ位置になりました。作業中のブランチではないので削除しました。再び編集対象となった時にcheckoutしてください" | |
if [ $? != 0 ]; then | |
deleteIfMerged develop $branch "$branch はorigin/$remote_branch と同じ位置になりました。作業中のブランチではないので削除しました。再び編集対象となった時にcheckoutしてください" | |
fi | |
else | |
echo "$remote_branch のpullを行いましたが origin の最終コミットIDに合わせられませんでした。確認して対処してください." | |
fi | |
else | |
echo "$branch のpullが失敗しました。確認して対処してください." | |
exit 3 | |
fi | |
continue | |
fi | |
isAhead $branch $remote_branch | |
if [ $? = 1 ]; then | |
# ローカルが進んでいる.勝手にpushしない。 | |
echo "$branch はまだ origin/$remote_branch にマージしていない作業中の状態です。pullしません。" | |
continue | |
fi | |
isDiverged $branch $remote_branch | |
if [ $? = 1 ]; then | |
${script_dir}/is_last_commit_in_other_branch.sh $branch origin/$remote_branch | |
if [ $? = 1 ]; then | |
echo "origin/$remote_branch は $branch の最後のコミットを取り込み済みなので、origin/$remote_branch が同じか先に進んでいるとみなし、originに合わせます。" | |
checkout $branch | |
if [ $? != 0 ]; then | |
echo "$branch に切り替えられません!" | |
continue | |
fi | |
git reset --hard origin/$remote_branch | |
if [ $? = 0 ]; then | |
# 同じ位置になった。 | |
if [ `git show -s --format=%H` = `git show -s --format=%H origin/$remote_branch` ]; then | |
deleteIfMerged master $branch "$branch はorigin/$remote_branch と同じ位置になりました。作業中のブランチではないので削除しました。再び編集対象となった時にcheckoutしてください" | |
if [ $? != 0 ]; then | |
deleteIfMerged develop $branch "$branch はorigin/$remote_branch と同じ位置になりました。作業中のブランチではないので削除しました。再び編集対象となった時にcheckoutしてください" | |
fi | |
fi | |
fi | |
else | |
# ローカルコミットが進んでいたり、枝が変わってしまっていた場合何もしない。 | |
echo "$branch はローカルとリモートで分岐している上にローカルのコミットが未だorigin/$remote_branch にマージしていないものもあるため、pullしません." | |
git status | |
fi | |
continue | |
fi | |
branchStatus $branch | |
echo "対応方法が未定義の状態です。" | |
done | |
git checkout $current_branch > /dev/null 2>&1 | |
unset current_branch | |
unset force_move_to_remote |
developやmasterがoriginと紐ついていなかった場合に紐つけるという対応したというのに、それが同じ位置だった場合に、「対応方法が未定義の状態です。」と表示されてしまう不具合を修正。
originの追跡ブランチと一致していない、ローカルのみのブランチを削除する際には、マージされた別のブランチがチェックアウトされていなければ削除に失敗するため、master、developの順にチェックアウトしてマージ済みかを判断した上で実行するようにした。
リモートの追跡ブランチ名が異なるブランチ名だが既にどこかにいってしまっている場合は、同名ブランチ名を探して追跡させるようにした。リモート同名ブランチが無い場合は、masterかdevelopにそのローカルブランチがマージ済みであれば削除するようにした。
gitのバージョンによってgit statusの出力メッセージが異なるので両方対応できるようにした。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
・追跡ブランチが紐ついていないものは、何かのタイミングでリモートと切れたと見なして、同名ブランチがoriginに存在するかチェックし、あればoriginの同名ブランチに紐つけるようにした。
・追跡ブランチが異なる名前のものでも同じ名前の場合と同じようにpullできるようにした。
・危うい --force パラメータを無くした。
・divergedの場合は、is_lastcommit_in_other_branch.sh コマンド(私のgistにある別のコマンド)を使って originの追跡ブランチがローカルブランチの最後のコミットを取り込み済みであればリモートに合わせるようにした。なので--force不要を実現できた。