diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-10-15 11:52:10 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-10-15 11:52:10 -0400 |
commit | 4d5104757eeb06d3daa31295784d664a8204b632 (patch) | |
tree | 7b6d9c3af49d1b1a00073a8e1a8ae9e8d3533f95 /git-smh | |
parent | 479dae36ebcd194bc61b0f7d9298e87e65128db5 (diff) |
this was sitting heregit-smh
Diffstat (limited to 'git-smh')
-rwxr-xr-x | git-smh | 255 |
1 files changed, 193 insertions, 62 deletions
@@ -12,14 +12,50 @@ fi print() { printf "$(_ "$1")\n" "${@:2}" } - + +usage() { + local varname=use_$1 + print "${!varname}" | sed 's|\$dashless|'"$dashless|g" +} +# # low-ish level smh commands -smh-root() ( +use_toplevel='usage: $dashless +Find the most-parent git repository of (possibly nested) git +submodules. + +That is, it is like a submodule-aware `git rev-parse --show-toplevel`.' +cmd_toplevel() { + local mode + local args + if ! args=$(getopt -n "$dashless" -o h -- "$@"); then + mode=err + else + eval set -- "$args" + local mode=normal + while true; do + case "$1" in + -h) shift; mode=help;; + --) shift; break;; + esac + done + if [[ $# -gt 0 ]]; then + mode=err + fi + fi + case "$mode" in + err) usage toplevel; return 2;; + help) usage toplevel;; + normal) smh-toplevel;; + esac +} +smh-toplevel() ( local gitdir + local cdup while true; do gitdir="$(git rev-parse --git-dir)" || return $? - cd "$(git rev-parse --cdup)" || return $? + cdup="$(git rev-parse --show-cdup)" || return $? + [[ -z "$cdup" ]] || cd "$cdup" || return $? if [[ "$gitdir" != */.git/modules/* ]]; then break fi @@ -28,9 +64,52 @@ smh-root() ( pwd ) +use_split='$dashless [-H|-z] [--] <pathspec>... +Split a list of filenames into submodule-path/file-path pairs. + +If neither -H nor -z is given, it will assume -H if stdout is a TTY, +or -z otherwise. + + -H human friendly; use spaces and newlines in the output + -z separate output with NUL character' +cmd_split() { + local mode + local args + if ! args=$(getopt -n "$dashless" -o hHz -- "$@"); then + mode=err + else + eval set -- "$args" + local mode + if [[ -t 1 ]]; then + mode=human + else + mode=machine + fi + while true; do + case "$1" in + -h) shift; mode=help;; + -H) shift; [[ "$mode" = help ]] || mode=human;; + -z) shift; [[ "$mode" = help ]] || mode=machine;; + --) shift; break;; + esac + done + fi + case "$mode" in + err) usage split >&2; return 2;; + help) usage split;; + machine) smh-split "$@";; + human) + set -o pipefail + smh-split "$@" | xargs -0r printf '%q %q\n' + ;; + esac +} smh-split() { - local root - root="$(smh-root)" || return $? + if [[ $# = 0 ]]; then + return 0 + fi + local toplevel + toplevel="$(smh-toplevel)" || return $? local cwd files cwd_files=("$@") @@ -56,43 +135,51 @@ smh-split() { "$(realpath --no-symlinks --relative-to "$gittop" "$cwd_file")" fi i+=1 - done < <(realpath -z --canonicalize-missing --no-symlinks --relative-to "$root" -- "${cwd_files[@]}") + done < <(realpath -z --canonicalize-missing --no-symlinks --relative-to "$toplevel" -- "${cwd_files[@]}") } -cmd_split() { - local args - args=$(getopt -a "$0" -o Hz -- "$@") || return $? - eval set -- "$args" + +use_foreach='usage: $dashless <command> +Run a command in the root of the repository, and in each submodule. + +This is similar to `git submodule foreach`, but with two important +differences: + 1. It also runs the command in the parent repository + 2. It runs the command in the deepest repositories first, then the + parents. This is backward of `git submodule foreach`. + +Bugs: + 1. Does not yet set the $name, $path, $sha1 and $toplevel + variables that `git submodule foreach` does.' +cmd_foreach() { local mode - if [[ -t 1 ]]; then - mode=human + local args + if ! args=$(getopt -n "$dashless" -o h -- "$@"); then + mode=err else - mode=machine + eval set -- "$args" + local mode=normal + while true; do + case "$1" in + -h) shift; mode=help;; + --) shift; break;; + esac + done + if [[ $# -gt 0 ]]; then + mode=err + fi fi - while true; do - case "$1" in - -H) shift; mode=human;; - -z) shift; mode=machine;; - --) shift; break;; - esac - done case "$mode" in - machine) - smh-split "$@" - ;; - human) - set -o pipefail - smh-split "$@" | xargs -0r printf '%q %q\n' - ;; + err) usage foreach >&2; return 2;; + help) usage foreach;; + normal) smh-toplevel;; esac } - -smh-foreach- smh-foreach() { local cmd="$1" exec 3<&0 ( - cd "$(smh-root)" || return $? + cd "$(smh-toplevel)" || return $? export LC_ALL=C git submodule foreach --quiet --recursive pwd | sort --reverse pwd @@ -106,8 +193,7 @@ smh-foreach() { } done } - - +# # smh add smh-add--helper() { @@ -118,35 +204,60 @@ smh-add--helper() { exec git add "$@" "${extra[@]}" } +use_add='usage: $dashless [options] [--] files +Like `git add`, but intelligently changes directory to the correct +submodule. + + -i, --interactive interactive picking + -p, --patch select hunks interactively + -e, --edit edit current diff and apply (not currently implemented) + + -v, --verbose be verbose + -f, --force allow adding otherwise ignored files + -u, --update update tracked files + -N, --intent-to-add record only the fact that the path will be added later + -A, --all add changes from all tracked and untracked files + --ignore-removal ignore paths removed in the working tree (same as --no-all) + --refresh don'\''t add, only refresh the index + --ignore-errors just skip files which cannot be added because of errors' smh-add() { local mode=batch local gitflags=() local args - args=$(getopt -a "$0" -o vfipeuAN -l verbose,force,interactive,patch,edit,update,all,no-ignore-removal,no-all,ignore-removal,intent-to-add,refresh,ignore-errors -- "$@") || return $? - eval set -- "$args" - while true; do - case "$1" in - --verbose|-v) gitflags+=("$1");; - --force|-f) gitflags+=("$1");; - --interactive|-i) mode=interactive;; - --patch|-p) mode=patch;; - --edit|-e) mode=edit;; - --update|-u) gitflags+=("$1");; - -A|--all|-no-ignore-removal) gitflags+=("$1");; - --no-all|--ignore-removal) gitflags+=("$1");; - --intent-to-add|-N) gitflags+=("$1");; - --refresh) gitflags+=("$1");; - --ignore-errors) gitflags+=("$1");; - --) shift; break;; - esac - shift - done - while read -r -d '' dir && read -r -d '' file; do - printf '%s\0' "$file" >> "$(cd "$dir" && git rev-parse --git-path 'SMH_ADD')" - done < <(smh-split "$@") + if ! args=$(getopt -n "$dashless" -o hvfipeuAN -l verbose,force,interactive,patch,edit,update,all,no-ignore-removal,no-all,ignore-removal,intent-to-add,refresh,ignore-errors -- "$@"); then + mode=err + else + eval set -- "$args" + while true; do + case "$1" in + -h) mode=help;; + --interactive|-i) [[ "$mode" = help ]] || mode=interactive;; + --patch|-p) [[ "$mode" = help ]] || mode=patch;; + --edit|-e) [[ "$mode" = help ]] || mode=edit;; + + --verbose|-v) gitflags+=("$1");; + --force|-f) gitflags+=("$1");; + --update|-u) gitflags+=("$1");; + -A|--all|-no-ignore-removal) gitflags+=("$1");; + --no-all|--ignore-removal) gitflags+=("$1");; + --intent-to-add|-N) gitflags+=("$1");; + --refresh) gitflags+=("$1");; + --ignore-errors) gitflags+=("$1");; + --) shift; break;; + esac + shift + done + fi + if [[ "$mode" != err ]] && [[ "$mode" != help ]]; then + while read -r -d '' dir && read -r -d '' file; do + printf '%s\0' "$file" >> "$(cd "$dir" && git rev-parse --git-path 'SMH_ADD')" + done < <(smh-split "$@") + fi local cmd case "$mode" in + err) usage add >&2; return 2;; + help) usage add;; batch) printf -v cmd '%q ' git smh add--helper "${gitflags[@]}" -- smh-foreach "$cmd" @@ -170,7 +281,7 @@ smh-add() { ;; esac } - +# # smh commit smh-commit--helper() { @@ -178,35 +289,55 @@ smh-commit--helper() { cd .. && git add "$OLDPWD" } +use_commit='usage: $dashless [options] +Recursively commit in each submodule (and parent repository). +All arguments are passed directly to `git commit`, so you should avoid +using this command to add files.' smh-commit() { local cmd printf -v '%q ' git smh commit--helper "$@" smh-foreach "$cmd" } - +# # smh push +use_push='usage: $dashless [options] +Recursively push in each submodule (and parent repository). + +All arguments are passed directly to `git push`.' smh-push() { local cmd printf -v '%q ' git push "$@" smh-foreach "$cmd" } - +# main() { + dashless=$(basename "$0" | sed -e 's/-/ /') local cmd="$1"; shift case "$cmd" in - root|add|add--helper|commit|commit--helper|push) - smh-"$cmd" "$@" + -h) + local _dashless="$dashless" + for cmd in toplevel split foreach add commit push; do + dashless="$_dashless $cmd" + usage "$cmd" + done | grep -e '^usage: ' -e '^ or: ' | sed '2,$s/^usage: / or: /' ;; - split) + toplevel|split|foreach) cmd_"$cmd" "$@" ;; + add|commit|push) + smh-"$cmd" "$@" + ;; + add--helper|commit--helper) + smh-"$cmd" "$@" + ;; *) print 'error: Unknown subcommand: %s' "$cmd" >&2 - return 129 + usage >&2 + return 2 ;; esac -} +} main "$@" |