Last active
March 23, 2023 06:02
-
-
Save pnovotnak/4dfe9b2867bf6fea60fa94b4c8a2cc9d to your computer and use it in GitHub Desktop.
List most recently used branches, with option to check out
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Requires bash 4+ (not shipped with macos) | |
set -e | |
set -o pipefail | |
most_recent_branches() { | |
git reflog show --pretty=format:'%gs ~ %gd' --date=relative \ | |
| grep 'checkout:' | grep -oE '[^ ]+ ~ .*' \ | |
| awk -F~ '!seen[$1]++' | |
} | |
get_all_branches() { | |
git for-each-ref --format='%(refname:short)' refs/heads/ | |
} | |
filter_non_existent_branches() { | |
# Build a set of branches, calling `git` only once | |
declare -lA branch_set=() | |
while read -r branch; do | |
branch_set["$branch"]=true | |
done < <(get_all_branches) | |
readonly branch_set | |
while read -r line; do | |
branch="$(echo "$line" | awk '{print $1}')" | |
# Breaks because of ANSI codes | |
if [ "${branch_set["$branch"]}" ]; then | |
echo "$line" | |
fi | |
done | |
} | |
get_most_recent_existing_branches() { | |
most_recent_branches \ | |
| filter_non_existent_branches | |
} | |
pretty_print_reflog() { | |
awk -F' ~ HEAD@{' '{printf(" \033[33m%s: \033[37m %s\033[0m\n", substr($2, 1, length($2)-1), $1)}' | |
} | |
pick_branch() { | |
pretty_print_reflog \ | |
| fzf \ | |
| awk '{print $NF}' | |
} | |
CHECKOUT=false | |
PICKER=false | |
VERBOSE=false | |
readonly FLAGS="chpv" | |
usage() { | |
local exit_code="$1" | |
cat <<EOF | |
$(basename $0) [$FLAGS] | |
-c pick a branch and check it out (infers -p) | |
-h print this help and exit with status 0 | |
-p invoke fzf to pick a branch | |
-v verbose | |
EOF | |
exit "$exit_code" | |
} | |
parse_args() { | |
while getopts "$FLAGS" flag; do | |
# stored in $OPTARG | |
case "${flag}" in | |
c) CHECKOUT=true ;; | |
p) PICKER=true ;; | |
v) VERBOSE=true ;; | |
h) usage 0 ;; | |
*) | |
usage 1 | |
;; | |
esac | |
done | |
# Checkout infers picker. | |
$CHECKOUT && PICKER=true | |
readonly CHECKOUT | |
readonly PICKER | |
readonly VERBOSE | |
} | |
main() { | |
parse_args "$@" | |
$VERBOSE && set -x | |
local chosen_branch | |
# We need the output in the current shell, so run the branch refinement logic | |
# in a subshell, processing the results here. | |
{ | |
if $PICKER; then | |
chosen_branch=$(pick_branch) | |
else | |
pretty_print_reflog | |
fi | |
} < <( | |
get_most_recent_existing_branches | |
) | |
if $CHECKOUT; then | |
git checkout "$chosen_branch" | |
fi | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I use this in two git aliases:
git lb
(git-lb
)git sb
(git-lb -p
)