diff --git a/git-addons/git-ffwd b/git-addons/git-ffwd index f44ad62b..87ab823c 100755 --- a/git-addons/git-ffwd +++ b/git-addons/git-ffwd @@ -2,40 +2,100 @@ SUBDIRECTORY_OK=1 source "$(git --exec-path)/git-sh-setup" || die "Not a git repository." + +has_matching_push() { + # push.default is setup for 'simple', 'current', or 'matching'. + case "$(git config --get push.default || echo "matching")" in + current | simple | matching) + return 0 + esac + + return 1 +} + + +if has_matching_push; then + MATCHING_PUSH=t +else + MATCHING_PUSH= +fi + + +determine_upstream_branch() { + local ref="${1#refs/heads/}" + local upstream="$(git for-each-ref --format='%(upstream:short)' "refs/heads/$ref")" + local publish_branch + + if [[ -n "$upstream" ]]; then + echo "$upstream" + return 0 + fi + + if [[ -z "$MATCHING_PUSH" ]]; then + return 1 + fi + + # Use the push location as the upstream. + local remote=$(git config --get branch.${ref}.pushremote || + git config --get remote.pushdefault || + git config --get branch.${ref}.remote) + if [ -z "$remote" -a -n "$(git config --get remote.origin.url 2> /dev/null)" ]; then + remote="origin" + fi + if [[ -n "$remote" ]]; then + git rev-parse "$remote/$ref" > /dev/null 2>&1 && + publish_branch="$remote/$ref" + fi + + if [[ -n "$publish_branch" ]]; then + echo "$publish_branch" + return 0 + fi + + return 1 +} + + # Taken from http://stackoverflow.com/questions/620650/can-i-easily-update-all-local-git-branches-from-remote-branches-simultaneously/answer-9076361 # with a small tweak by me to make the fast-forward merge use --ff-only, and to -# help prune remote branches. +# help prune remote branches. It has also been updated to treat the push branch +# as the default upstream, if no upstream is configured. main() { - REMOTES="$@"; - if [ -z "$REMOTES" ]; then - REMOTES=$(git remote); - fi - REMOTES=$(echo "$REMOTES" | xargs -n1 echo) - CLB=$(git branch -l|awk '/^\*/{print $2}'); - echo "$REMOTES" | while read REMOTE; do + local upstream + local ALR + local LR + + local CLB=$(git branch -l|awk '/^\*/{print $2}') + + local REMOTES="$@" + if [ -z "$REMOTES" ]; then + REMOTES="$(git remote)" + fi + REMOTES="$(echo "$REMOTES" | xargs -n1 echo)" + git remote update -p $REMOTE - git remote show $REMOTE -n \ - | awk '/merges with remote/{print $5" "$1}' \ - | while read line; do - RB=$(echo "$line"|cut -f1 -d" "); - ARB="refs/remotes/$REMOTE/$RB"; - LB=$(echo "$line"|cut -f2 -d" "); - ALB="refs/heads/$LB"; - NBEHIND=$(( $(git rev-list --count $ALB..$ARB 2>/dev/null) +0)); - NAHEAD=$(( $(git rev-list --count $ARB..$ALB 2>/dev/null) +0)); - if [ "$NBEHIND" -gt 0 ]; then - if [ "$NAHEAD" -gt 0 ]; then - echo " branch $LB is $NBEHIND commit(s) behind and $NAHEAD commit(s) ahead of $REMOTE/$RB. could not be fast-forwarded"; - elif [ "$LB" = "$CLB" ]; then - echo " branch $LB was $NBEHIND commit(s) behind $REMOTE/$RB. fast-forward merge"; - git merge --ff --ff-only -q $ARB; - else - echo " branch $LB was $NBEHIND commit(s) behind $REMOTE/$RB. resetting local branch to remote"; - git branch -l -f $LB -t $ARB >/dev/null; + git show-ref --heads | cut -d ' ' -f 2 | while read ALB; do + LB="${ALB#refs/heads/}" + RB="$(determine_upstream_branch "${LB}")" + if [ -z "${RB}" ]; then + continue + fi + + NBEHIND=$(( $(git rev-list --count $LB..$RB 2>/dev/null) +0)) + NAHEAD=$(( $(git rev-list --count $RB..$LB 2>/dev/null) +0)) + + if [ "$NBEHIND" -gt 0 ]; then + if [ "$NAHEAD" -gt 0 ]; then + echo " branch $LB is $NBEHIND commit(s) behind and $NAHEAD commit(s) ahead of $RB. could not be fast-forwarded" + elif [ "$LB" = "$CLB" ]; then + echo " branch $LB was $NBEHIND commit(s) behind $RB. fast-forward merge"; + git merge --ff --ff-only -q $RB + else + echo " branch $LB was $NBEHIND commit(s) behind $RB. resetting local branch to remote" + git branch -l -f $LB -t $RB >/dev/null + fi fi - fi done - done } main $@