#!/bin/bash # # Slurp - add everything, slurp into a WIP commit and push # warn() { echo "$@" >&2 } die() { warn "$@" exit 2 } usage() { die "usage: git slurp [--force-slurp] [--force-push] [pathspec]..." } allowed_slurp() { # # Is slurp allowed in this repo? # test -f "$ok_file" } this_branch_upstream() { # # Print upstream of current bvranch # git branch -vv \ | grep '^\*' \ | cut -d \] -f1 \ | cut -d \[ -f2 \ | cut -d : -f1 } allowed_push() { # # Is push allowed for the remote branch?? # # If this branch is tracking a remote branch, and the remote # branch name is listed in .git-slurp-ok after PUSH, we are # allowed to push # # The remote tracking branch is parsed; example output is here: # # $ git branch -vv # main aaf02f0 [main/master: ahead 25] Some other commit # * master add0a03 [jdsumsion/master] Some commit # local tbranch test -n "$ok_file" || return 1 tbranch=$(this_branch_upstream) test -n "$tbranch" || return 0 grep -qxE "PUSH ([^ ]+ )?$tbranch" "$ok_file" 2>/dev/null } has_flag() { # # Has $ok_file have flag $1 for this branch? # local flag=$1 local tbranch test -n "$ok_file" || return 1 tbranch=$(this_branch_upstream) grep -xE "PUSH [^ ]+ $tbranch" "$ok_file" \ | cut -d' ' -f2 \ | grep -qwe "$flag" } git_relative() { # # Convert pathspec $1 to git-root-based pathspec # # Also try to clean up path a bit; i.e. remove double slashes # or unnecessary trailing dot. # local path="$1" test -n "$GIT_PREFIX" && path="$GIT_PREFIX/$path" local full=$(readlink -m "$path") test "$full" = "$git_root" && echo . && return printf %s "${full#$git_root/}" } slurp1() { # # Slurp pathspec $1 # local rel_pathspec # relative to git root rel_pathspec=$(git_relative "$1") git add "$rel_pathspec" || die git commit -m "WIP slurp, $date: $rel_pathspec" || die } go_slurp() { # # Do the slurp for pathspecs $@ # $force_slurp || allowed_slurp || die "you don't want this." local date=$(date -Isec) local pathspec for pathspec in "$@"; do slurp1 "$pathspec" done } go_push() { # # Do the push (if allowed) # if $force_push || allowed_push; then if has_flag ci; then git push else git push -o ci.skip fi fi } main() { local git_root local ok_file local force_slurp=false local force_push=false local skip_ci=true while true; do case $1 in --force-slurp) force_slurp=true; shift ;; --force-push) force_push=true; shift ;; --skip-ci) skip_ci=true; shift ;; --) shift; break ;; -*) usage ;; *) break ;; esac done test -n "$1" || set -- . git_root="$(git rev-parse --show-toplevel)" || die ok_file="$git_root/.git-slurp-ok" go_slurp "$@" go_push } main "$@"