Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
master、ブランチを一括pull
#!/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
@tkatochin
Owner

・スタート時に管理外のファイルがある場合は中止させるようにした。
・今まではリモートブランチを全てチェックアウトしていたのを辞めた。
・ローカルブランチと同じ名前のリモートブランチを追跡ブランチとして扱うようにした。
・ローカルブランチよりリモートブランチのほうが先に進んでいる場合だけ、リモートブランチに合わせるようにした。
・リモートブランチが別の枝にリベースされているなどpullできない場合は、--forceパラメータで強制的に各ローカルブランチをリモートに合わせられるようにした。
・リモートブランチにない名前のブランチは削除するようにしたので注意!

@tkatochin
Owner

--force のときだけ、リモートブランチにない名前のブランチは削除するようにした。

@tkatochin
Owner

チェックアウト中のブランチの頭に * が付いていたので、カレントディレクトリのファイルがブランチ名とされてしまっていたバグを修正。

@tkatochin
Owner

対象外だったブランチが分かるようにした。

@tkatochin
Owner

リモートにないローカルブランチ削除ってやっぱり危険すぎるので、それはそれで別途やりゃあいいじゃんってことで除去。

@tkatochin
Owner

違う枝ではなく、リモートブランチから順送りで自分のコミットが進んでいるのであれば、--forceで変更が破棄されないようにした。

@tkatochin
Owner

・リモートと同期がとれたローカルブランチのうち、注目してないブランチは削除するようにした。
(実行時のブランチとmasterと、あればdevelopは削除対象外)

・リモートが同じラインで進んでいたら reset --hardじゃなくpullするようにした。
・沢山のリポジトリを連続実行すると画面がウザウザなので出力省力化

@tkatochin
Owner

メッセージ文字列内で$branchの後にスペースを置かなかったためおかしな変数名として化けちゃっていたのを修正。

@tkatochin
Owner

・追跡ブランチが紐ついていないものは、何かのタイミングでリモートと切れたと見なして、同名ブランチがoriginに存在するかチェックし、あればoriginの同名ブランチに紐つけるようにした。

・追跡ブランチが異なる名前のものでも同じ名前の場合と同じようにpullできるようにした。

・危うい --force パラメータを無くした。

・divergedの場合は、is_lastcommit_in_other_branch.sh コマンド(私のgistにある別のコマンド)を使って originの追跡ブランチがローカルブランチの最後のコミットを取り込み済みであればリモートに合わせるようにした。なので--force不要を実現できた。

@tkatochin
Owner

developやmasterがoriginと紐ついていなかった場合に紐つけるという対応したというのに、それが同じ位置だった場合に、「対応方法が未定義の状態です。」と表示されてしまう不具合を修正。

@tkatochin
Owner

originの追跡ブランチと一致していない、ローカルのみのブランチを削除する際には、マージされた別のブランチがチェックアウトされていなければ削除に失敗するため、master、developの順にチェックアウトしてマージ済みかを判断した上で実行するようにした。

@tkatochin
Owner

リモートの追跡ブランチ名が異なるブランチ名だが既にどこかにいってしまっている場合は、同名ブランチ名を探して追跡させるようにした。リモート同名ブランチが無い場合は、masterかdevelopにそのローカルブランチがマージ済みであれば削除するようにした。

@tkatochin
Owner

gitのバージョンによってgit statusの出力メッセージが異なるので両方対応できるようにした。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment