我通常至少有3个远程分支:master、staging和production。我有3个本地分支来跟踪这些远程分支。
更新我所有的本地分支是乏味的:
git fetch --all
git rebase origin/master
git checkout staging
git rebase origin/staging
git checkout production
git rebase origin/production
我很想做一个“git pull -all”,但我还没能让它工作。它似乎做了一个“fetch -all”,然后更新(快进或合并)当前工作的分支,但不包括其他本地分支。
我仍然需要手动切换到每个本地分支并进行更新。
以上答案都没有考虑存在多个工作树的可能性。使用git update-ref或git branch -f更新当前在其他工作树中签出的分支会产生意想不到的副作用。
考虑一下我处理工作树的解决方案:
#! /usr/bin/env bash
set -euo pipefail
# Read the default remote from config, defaulting to "origin".
DEFAULT_REMOTE=$(git config --default origin --get clone.defaultRemoteName)
# Use first argument as remote name, fallback to default.
REMOTE=${1:-$DEFAULT_REMOTE}
# Resolve the rev that HEAD points at, so that we can give it
# a special treatment.
HEAD_REV=$(git rev-parse HEAD)
# Format that allows us to easily grep for local branches that are behind,
# and have an upstream at $REMOTE.
FORMAT="%(upstream:trackshort)%(upstream:remotename)|%(refname:short)"
# Get a list of revs that are checked out. We don't want to
# update refs that are pointing at them.
set +e
WORKTREE_REVS=$(git worktree list --porcelain | grep -Po "HEAD \K(.+)" | grep -v "$HEAD_REV")
set -e
git fetch $REMOTE
for BRANCH in $(git for-each-ref refs/heads --format="$FORMAT" | grep -Po "<$REMOTE\|\K(.+)")
do
BRANCH_REV=$(git rev-parse $BRANCH)
if [ "$BRANCH_REV" = "$HEAD_REV" ]
then
# This branch is currently checked out "here". Forward it carefully.
set +e
git merge --no-autostash --ff-only $BRANCH@{u}
set -e
elif grep -q "$BRANCH_REV" <<< "$WORKTREE_REVS"
then
# This branch is currently checked out by another. Leave it alone.
echo "$BRANCH skipped, because it is checked out in another worktree. Use 'git worktree list' to diagnose."
else
# This branch is not checked out. Just update it!
git update-ref refs/heads/$BRANCH $BRANCH@{u}
echo "$BRANCH forwarded"
fi
done
如果refs/heads/master可以快进到refs/remotes/foo/master,则
git merge-base refs/heads/master refs/remotes/foo/master
应该返回refs/heads/master指向的SHA1 id。这样,您就可以将一个脚本组合在一起,自动更新没有应用转移提交的所有本地分支。
这个小shell脚本(我称之为git-can-ff)说明了如何做到这一点。
#!/bin/sh
set -x
usage() {
echo "usage: $(basename $0) <from-ref> <to-ref>" >&2
exit 2
}
[ $# -ne 2 ] && usage
FROM_REF=$1
TO_REF=$2
FROM_HASH=$(git show-ref --hash $FROM_REF)
TO_HASH=$(git show-ref --hash $TO_REF)
BASE_HASH=$(git merge-base $FROM_REF $TO_REF)
if [ "$BASE_HASH" = "$FROM_HASH" -o \
"$BASE_HASH" = "$FROM_REF" ]; then
exit 0
else
exit 1
fi