Bashスクリプトを使用して、リポジトリ内のすべてのローカルブランチを反復処理するにはどうすればよいですか。ブランチといくつかのリモートブランチに違いがあるかどうかを繰り返し確認する必要があります。例
for branch in $(git branch);
do
git log --oneline $branch ^remotes/Origin/master;
done
上記のようなことをする必要がありますが、私が直面している問題は$(git branch)であり、リポジトリに存在するブランチとともにリポジトリフォルダ内のフォルダを提供します。
これはこの問題を解決する正しい方法ですか?それとも別の方法がありますか?
ありがとうございました
スクリプトを記述するときは、git branchを使用しないでください。 Gitは 「配管」インターフェイスを提供します スクリプトで使用するために明示的に設計されています(通常のGitコマンドの多くの現在および過去の実装(追加、チェックアウト、マージなど)がこれを使用します)インタフェース)。
必要な配管コマンドはgit for-each-refです。
_git for-each-ref --Shell \
--format='git log --oneline %(refname) ^Origin/master' \
refs/heads/
_
注:_remotes/
_を参照名検索パスの複数の場所と一致させる他の参照がない限り、リモート参照に_Origin/master
_プレフィックスは必要ありません(「シンボリック参照名...」を参照) git-rev-parse(1) のリビジョンの指定セクション。明確にあいまいさを回避しようとしている場合は、完全な参照名_refs/remotes/Origin/master
_を使用します。
次のような出力が得られます。
_git log --oneline 'refs/heads/master' ^Origin/master
git log --oneline 'refs/heads/other' ^Origin/master
git log --oneline 'refs/heads/pu' ^Origin/master
_
この出力をshにパイプすることができます。
シェルコードを生成するという考え方が気に入らない場合は、堅牢性を少しあきらめることができます。* そしてこれを行う:
_for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
git log --oneline "$branch" ^Origin/master
done
_
*参照名は、シェルのWord分割から安全でなければなりません( git-check-ref-format(1) を参照)。個人的には、以前のバージョン(生成されたシェルコード)を使い続けました。不適切なことは何も起こらないと確信しています。
bashを指定し、配列をサポートしているため、安全性を維持しながら、ループの内臓の生成を回避できます。
_branches=()
eval "$(git for-each-ref --Shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
# …
done
_
配列をサポートするシェル(_$@
_を初期化し、set -- "$@" %(refname)
を使用して要素を追加する)を使用していない場合は、_set --
_で同様のことができます。
これは、_git branch
_が現在のブランチをアスタリスクでマークするためです。例:
_$ git branch
* master
mybranch
$
_
$(git branch)
は、たとえば_* master mybranch
_、次に_*
_が現在のディレクトリ内のファイルのリストに展開されます。
そもそもアスタリスクを印刷しないという明白なオプションはありません。しかし、あなたはそれを切り落とすことができます:
_$(git branch | cut -c 3-)
_
Bashビルトインmapfile
は、このために構築されます
すべてのgitブランチ:git branch --all --format='%(refname:short)'
すべてのローカルgitブランチ:git branch --format='%(refname:short)'
すべてのリモートgitブランチ:git branch --remotes --format='%(refname:short)'
すべてのgitブランチを反復処理します:mapfile -t -C my_callback -c 1 < <( get_branches )
例:
my_callback () {
INDEX=${1}
BRANCH=${2}
echo "${INDEX} ${BRANCH}"
}
get_branches () {
git branch --all --format='%(refname:short)'
}
# mapfile -t -C my_callback -c 1 BRANCHES < <( get_branches ) # if you want the branches that were sent to mapfile in a new array as well
# echo "${BRANCHES[@]}"
mapfile -t -C my_callback -c 1 < <( get_branches )
oPの特定の状況の場合:
#!/usr/bin/env bash
_map () {
ARRAY=${1?}
CALLBACK=${2?}
mapfile -t -C "${CALLBACK}" -c 1 <<< "${ARRAY[@]}"
}
get_history_differences () {
REF1=${1?}
REF2=${2?}
shift
shift
git log --oneline "${REF1}" ^"${REF2}" "${@}"
}
has_different_history () {
REF1=${1?}
REF2=${2?}
HIST_DIFF=$( get_history_differences "${REF1}" "${REF2}" )
return $( test -n "${HIST_DIFF}" )
}
print_different_branches () {
read -r -a ARGS <<< "${@}"
LOCAL=${ARGS[-1]?}
for REMOTE in "${SOME_REMOTE_BRANCHES[@]}"; do
if has_different_history "${LOCAL}" "${REMOTE}"; then
# { echo; echo; get_history_differences "${LOCAL}" "${REMOTE}" --color=always; } # show differences
echo local branch "${LOCAL}" is different than remote branch "${REMOTE}";
fi
done
}
get_local_branches () {
git branch --format='%(refname:short)'
}
get_different_branches () {
_map "$( get_local_branches )" print_different_branches
}
# read -r -a SOME_REMOTE_BRANCHES <<< "${@}" # use this instead for command line input
declare -a SOME_REMOTE_BRANCHES
SOME_REMOTE_BRANCHES=( Origin/master remotes/Origin/another-branch another-remote/another-interesting-branch )
DIFFERENT_BRANCHES=$( get_different_branches )
echo "${DIFFERENT_BRANCHES}"
私はそれを例として繰り返します:
for BRANCH in `git branch --list|sed 's/\*//g'`;
do
git checkout $BRANCH
git fetch
git branch --set-upstream-to=Origin/$BRANCH $BRANCH
done
git checkout master;
受け入れられた答えは正解であり、実際に使用されるアプローチである必要がありますが、bashで問題を解決することは、シェルがどのように機能するかを理解する上で素晴らしい練習です。追加のテキスト操作を実行せずにbashを使用してこれを行うコツは、シェルによって実行されるコマンドの一部としてgitブランチの出力が展開されないようにすることです。これにより、シェル展開のファイル名展開(ステップ8)でアスタリスクが展開されなくなります( http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html を参照してください) )
Readコマンドでbashwhileコンストラクトを使用して、gitブランチの出力を行に分割します。 「*」はリテラル文字として読み込まれます。一致するパターンに特に注意を払いながら、caseステートメントを使用して一致させます。
git branch | while read line ; do
case $line in
\*\ *) branch=${line#\*\ } ;; # match the current branch
*) branch=$line ;; # match all the other branches
esac
git log --oneline $branch ^remotes/Origin/master
done
Bashcaseコンストラクトとパラメーター置換の両方のアスタリスクは、シェルがパターン一致文字として解釈するのを防ぐために、バックスラッシュでエスケープする必要があります。文字列で「*」と一致するため、スペースもエスケープされます(トークン化を防ぐため)。
あなたのローカルブランチが数字、a-z、および/またはA-Z文字のみで名前が付けられている場合、$(git branch|grep -o "[0-9A-Za-z]\+")
をお勧めします
私の意見では覚えやすい最も簡単なオプション:
git branch | grep "[^* ]+" -Eo
出力:
bamboo
develop
master
Grepの-oオプション(--only-matching)は、出力を入力の一致する部分のみに制限します。
Gitブランチ名ではスペースも*も有効ではないため、これは余分な文字を含まないブランチのリストを返します。
編集: 'detached head' state の場合、現在のエントリを除外する必要があります。
git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE
私がやったことをあなたの質問に適用しました(&ccpizzaからtr
に言及したことに触発されました):
git branch | tr -d ' *' | while IFS='' read -r line; do git log --oneline "$line" ^remotes/Origin/master; done
(私はwhileループを頻繁に使用します。特定のことについては、尖った変数名["branch"など]を使用したいことは間違いありませんが、ほとんどの場合は入力の各lineで何かを実行します。ここで 'branch'の代わりに 'line'を使用することは、再利用性/筋肉の記憶/効率性にうなずきます。)
@finnの答えから拡張して(ありがとう!)、次のコマンドを使用すると、介在するシェルスクリプトを作成せずにブランチを反復処理できます。ブランチ名に改行がない限り、十分に堅牢です:)
git for-each-ref --format='%(refname)' refs/heads | while read x ; do echo === $x === ; done
Whileループはサブシェルで実行されますが、現在のシェルでアクセスしたいシェル変数を設定していない限り、通常は問題ありません。その場合、プロセス置換を使用してパイプを逆にします。
while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )
Googlianの答えですが、forを使用せずに
git for-each-ref --format='%(refname:lstrip=-1)' refs/heads/
for branch in "$(git for-each-ref --format='%(refname:short)' refs/heads)"; do
...
done
これは、スクリプト用に設計された git plumbing コマンドを使用します。また、シンプルで標準的です。
参照: GitのBashの完了
この状態の場合:
git branch -a
* master
remotes/Origin/HEAD -> Origin/master
remotes/Origin/branch1
remotes/Origin/branch2
remotes/Origin/branch3
remotes/Origin/master
そして、このコードを実行します:
git branch -a | grep remotes/Origin/*
for BRANCH in `git branch -a | grep remotes/Origin/*` ;
do
A="$(cut -d'/' -f3 <<<"$BRANCH")"
echo $A
done
次の結果が得られます。
branch1
branch2
branch3
master
シンプルにする
Bashスクリプトを使用してループ内でブランチ名を取得する簡単な方法。
#!/bin/bash
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
echo "${branch/'refs/heads/'/''}"
done
出力:
master
other