Skip to content

Commit

Permalink
Add git-spinoff command
Browse files Browse the repository at this point in the history
  • Loading branch information
nvie committed Nov 5, 2019
1 parent cbdfe4e commit 6841eff
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Everyday helpful commands:
* [git-branches-containing](#git-branches-containing)
* [git-recent-branches](#git-recent-branches)
* [git-remote-branches](#git-remote-branches)
* [git-remote-tracking-branch](#git-remote-tracking-branch)
* [git-repo](#git-repo)
* [git-root](#git-root)
* [git-initial-commit](#git-initial-commit)
Expand All @@ -38,6 +39,7 @@ Everyday helpful commands:
* ⭐️ [git-modified](#git-modified)
* ⭐️ [git-modified-since](#git-modified-since)
* ⭐️ [git-separator](#git-separator)
* ⭐️ [git-spinoff](#git-spinoff)

Statistics:

Expand Down Expand Up @@ -146,6 +148,19 @@ rebase operations. (They should be used as a temporary measure, and ideally
taken out of the history again when done rebasing.)


### git spinoff

Inspired by Magit's `spinoff` command. Creates and checks out
a new branch starting at and tracking the current branch. That
branch in turn is reset to the last commit it shares with its
upstream. If the current branch has no upstream or no unpushed
commits, then the new branch is created anyway and the previously
current branch is not touched.

This is useful to create a feature branch after work has already
began on the old branch (likely but not necessarily "master").


### git push-current

Pushed the current branch out to `origin`, and makes sure to setup tracking of
Expand Down Expand Up @@ -191,6 +206,12 @@ Returns a list of local branches, ordered by recency:
qux


### git remote-tracking-branch

Print the name of the remote tracking branch of the current or
given local branch name, if one exists. Errors otherwise.


### git local-commits / git has-local-commits

Returns a list of commits that are still in your local repo, but haven't been
Expand Down
31 changes: 31 additions & 0 deletions git-remote-tracking-branch
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/sh
set -eu

usage () {
echo "usage: git remote-tracking-branch [-h] [<branch>]" >&2
echo >&2
echo "Prints the fully qualified name of the remote tracking" >&2
echo "branch for the given local branch name." >&2
echo >&2
echo "Options:" >&2
echo "-h Show this help" >&2
}

while getopts h flag; do
case "$flag" in
h) usage; exit 2;;
esac
done
shift $(($OPTIND - 1))

if [ $# -gt 1 ]; then
usage
exit 2
fi

branch="${1:-}"

# This result is constructed so that it only prints the output of
# git-rev-parse if the command succeeded, and is silent otherwise
result="$(git rev-parse --symbolic-full-name --abbrev-ref "${branch}@{u}" 2>/dev/null)"
echo "$result"
65 changes: 65 additions & 0 deletions git-spinoff
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/sh
set -eu

#
# Inspired by Magit's super useful `magit-branch-spinoff` command.
# See also https://magit.vc/manual/magit/Branch-Commands.html
#

usage () {
echo "usage: git spinoff [-h] <new-name> [<base>]" >&2
echo >&2
echo "Creates and checks out a new branch starting at and tracking the" >&2
echo "current branch. That branch in turn is reset to the last commit it" >&2
echo "shares with its upstream. If the current branch has no upstream or no" >&2
echo "unpushed commits, then the new branch is created anyway and the" >&2
echo "previously current branch is not touched." >&2
echo >&2
echo "This is useful to create a feature branch after work has already" >&2
echo "began on the old branch (likely but not necessarily \"master\")." >&2
echo >&2
echo "Options:" >&2
echo "-h Show this help" >&2
}

while getopts h flag; do
case "$flag" in
h) usage; exit 2;;
esac
done
shift $(($OPTIND - 1))

if [ $# -lt 1 -o $# -gt 2 ]; then
usage
exit 2
fi

new_name="$1"
rawbase="${2:-}"
if [ -z "$rawbase" ]; then
base="$(git current-branch)"
else
base="$rawbase"
fi

base_sha="$(git sha -s "$base")"

#
# NOTE:
# The flag -B is the transactional equivalent of
# $ git branch -f <branch> [<start point>]
# $ git checkout <branch>
#
git checkout -q --track -B "$new_name" "$base"

rtb="$(git remote-tracking-branch "$base")"
if [ -n "$rtb" ]; then
merge_base="$(git merge-base "$base" "$rtb")"
git branch -vf "$base" "$merge_base"
fi

if [ "$(git sha -s "$base")" != "$base_sha" ]; then
echo "$base reset to $(git sha -s "$base") (was $base_sha)"
else
echo "$base not touched"
fi

0 comments on commit 6841eff

Please sign in to comment.