You should not use git branch when writing scripts. Git provides a “plumbing” interface that is explicitly designed for use in scripting (many current and historical implementations of normal Git commands (add, checkout, merge, etc.) use this same interface).
The plumbing command you want is git for-each-ref:
git for-each-ref --shell \
--format='git log --oneline %(refname) ^origin/master' \
refs/heads/
Note: You do not need the remotes/
prefix on the remote ref unless you have other refs that cause origin/master
to match multiple places in the ref name search path (see “A symbolic ref name. …” in the Specifying Revisions section of git-rev-parse(1)). If you are trying to explictly avoid ambiguity, then go with the full ref name: refs/remotes/origin/master
.
You will get output like this:
git log --oneline 'refs/heads/master' ^origin/master
git log --oneline 'refs/heads/other' ^origin/master
git log --oneline 'refs/heads/pu' ^origin/master
You can pipe this output into sh.
If you do not like the idea of generating the shell code, you could give up a bit of robustness* and do this:
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
git log --oneline "$branch" ^origin/master
done
* Ref names should be safe from the shell’s word splitting (see git-check-ref-format(1)). Personally I would stick with the former version (generated shell code); I am more confident that nothing inappropriate can happen with it.
Since you specified bash and it supports arrays, you could maintain safety and still avoid generating the guts of your loop:
branches=()
eval "$(git for-each-ref --shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
# …
done
You could do something similar with $@
if you are not using a shell that supports arrays (set --
to initialize and set -- "$@" %(refname)
to add elements).
This is because git branch
marks the current branch with an asterisk, e.g.:
$ git branch
* master
mybranch
$
so $(git branch)
expands to e.g. * master mybranch
, and then the *
expands to the list of files in the current directory.
I don't see an obvious option for not printing the asterisk in the first place; but you could chop it off:
$(git branch | cut -c 3-)
The bash builtin, mapfile
, is built for this
all git branches: git branch --all --format='%(refname:short)'
all local git branches: git branch --format='%(refname:short)'
all remote git branches: git branch --remotes --format='%(refname:short)'
iterate through all git branches: mapfile -t -C my_callback -c 1 < <( get_branches )
example:
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 )
for the OP's specific situation:
#!/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}"
source: List all local git branches without an asterisk
I iterate as it for example :
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;
I would suggest
$(git branch|grep -o "[0-9A-Za-z]\+")
if your local branches are named by digits, a-z, and/or A-Z letters only
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With