The previous post, Practical Rebasing of Chained Branches, covered how to
use a custom git command to rebase several git branches stacked on each other.
Once wishing to share a set of branches; pushing a chain to a server risks
becoming another tedious task. Unless using something like this custom git
command, which I call git-force-push-chain
#!/bin/sh -eu
#
# git force-push-chain <branch-point> [remote]
#
# Force pushes all branches between HEAD and <branch-point> to [remote].
# If no remote is provided, 'origin' is assumed. Ignores branches without a
# tracking branch, as well as those tracking some other remote than the
# provided one.
#
# Only histories on a straight line are handled. (No trees are traversed.)
_from="${1:-}"
_to='HEAD'
[ "${_from:-}" ] ||
{ echo 'First argument must be <branch-point>.' >&2; exit 1; }
_remote="${2:-origin}"
git remote | grep -q "^${_remote}\$" ||
{ echo "fatal: '${_remote}' does not appear to be a remote" >&2; exit 1; }
_commits="$( git log "${_from}..${_to}" --no-merges --pretty='format: %h' )"
for _commit in ${_commits}; do
_branches="$( git branch --points-at="${_commit}" \
--format='%(refname:short)' )"
for _branch in ${_branches}; do
if _tracking="$( git rev-parse --symbolic-full-name \
--abbrev-ref "${_branch}@{push}" 2>/dev/null )"
then
if [ "${_tracking%%/*}" = "${_remote}" ]; then
git push --force "${_remote}" "${_branch}"
fi
fi
done
done
unset _branch _branches _commit _commits _from _remote _to _tracking
# vim: sw=2 et
Hopefully the usage comment at the start should be explanatory enough for anyone in a situation to make use of the script. The only thing to possible mention is to highlight that this will force-push your local branches and overwrite any with the same name already existing on the server, without asking. That is what the script is designed to do, updating many branches under parallel development.
Putting the following into .zshrc
should make tab completion work:
# https://stackoverflow.com/q/38725102/add-custom-git-command-to-zsh-completion
zstyle ':completion:*:*:git:*' user-commands \
force-push-chain:'fiercely update remote from <branch-point> to HEAD'
_git-force-push-chain() {
# Exit early if pwd is not a git repository.
git rev-parse 2>/dev/null || return 0
case "${CURRENT}" in
2)
_alternative \
"branches:branches:($( git branch --list --format="%(refname:short)" ))"
;;
3)
_alternative \
"remotes:remotes:($( git remote ))"
;;
esac
}
compdef _git-force-push-chain git-force-push-chain
With great power comes great responsability. It is easy to mess things up when
being able to rewrite git history and posting it with low effort. Think through
your policies and use of branches. Always make sure everyone on your team are
in agreement on rules and naming for rewritable branches, and never ever rebase
main
, okay?!