Repository: falconindy/asp Branch: master Commit: 2a1cd19332ec Files: 12 Total size: 24.2 KB Directory structure: gitextract_1nhrilat/ ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── archweb.inc.sh ├── asp.in ├── man/ │ └── asp.1.txt ├── package.inc.sh ├── remote.inc.sh ├── shell/ │ ├── bash-completion │ └── zsh-completion └── util.inc.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ asp asp.1 ================================================ FILE: LICENSE ================================================ Copyright (c) 2014 Dave Reisner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ PACKAGE_NAME = asp VERSION := $(shell git describe --dirty 2>/dev/null) PREFIX = /usr/local BINPROGS = \ asp MANPAGES = \ man/asp.1 BASH_COMPLETION = \ shell/bash-completion ZSH_COMPLETION = \ shell/zsh-completion INCLUDES = \ archweb.inc.sh \ package.inc.sh \ remote.inc.sh \ util.inc.sh all: $(BINPROGS) $(MANPAGES) V_GEN = $(_v_GEN_$(V)) _v_GEN_ = $(_v_GEN_0) _v_GEN_0 = @echo " GEN " $@; edit = $(V_GEN) m4 -P $@.in | sed 's/@ASP_VERSION@/$(VERSION)/' >$@ && chmod go-w,+x $@ %: %.in $(INCLUDES) $(edit) doc: $(MANPAGES) man/%: man/%.txt Makefile $(V_GEN) a2x \ -d manpage \ -f manpage \ -a manversion="$(PACKAGE_NAME) $(VERSION)" \ -a manmanual="$(PACKAGE_NAME) manual" $< check: $(BINPROGS) @for f in $(BINPROGS); do bash -O extglob -n $$f; done lint: $(BINPROGS) @for f in $(BINPROGS); do shellcheck $$f; done clean: $(RM) $(BINPROGS) $(MANPAGES) install: all install -dm755 $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/share/man/man1 install -m755 $(BINPROGS) $(DESTDIR)$(PREFIX)/bin install -m644 $(MANPAGES) $(DESTDIR)$(PREFIX)/share/man/man1 install -Dm644 $(BASH_COMPLETION) $(DESTDIR)$(PREFIX)/share/bash-completion/completions/asp install -Dm644 $(ZSH_COMPLETION) $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_asp .PHONY: all clean install ================================================ FILE: README.md ================================================ > [!CAUTION] > **THIS REPO IS ARCHIVED** > > In the context of the [git migration](https://archlinux.org/news/git-migration-announcement/) using `asp` has been deprecated in favor of `pkgctl` or plain `git`. > > For details see the respective wiki entry: https://wiki.archlinux.org/title/Arch_build_system#Retrieve_PKGBUILD_source --- # asp `asp` is a tool to manage the build source files used to create Arch Linux packages. It replaces the `abs` tool, offering more up to date sources (via the svntogit repositories) and uses a sparse checkout model to conserve diskspace. This probably won't be interesting to users who want a full checkout (for whatever reason that may be). # Setup None! Though, it should be noted that the `ASPROOT` environment variable will control where `asp` keeps its local git repo. By default, this is `${XDG_CACHE_HOME:-$HOME/.cache}/asp`. # Examples Get the source files for some packages: ~~~ asp export pacman testing/systemd extra/pkgfile ~~~ Get a fully functional git checkout of a single package: ~~~ asp checkout pkgfile ~~~ List the repositories a package has been pushed to: ~~~ asp list-repos pacman ~~~ ================================================ FILE: archweb.inc.sh ================================================ archweb_get_pkgbase() { local pkgbase pkgbase=$(curl -LGs 'https://archlinux.org/packages/search/json/' --data-urlencode "q=$1" | jq -r --arg pkgname "$1" 'limit(1; .results[] | select(.pkgname == $pkgname).pkgbase)') [[ $pkgbase ]] || return printf '%s\n' "$pkgbase" } ================================================ FILE: asp.in ================================================ #!/bin/bash ASP_VERSION=@ASP_VERSION@ ARCH_GIT_REPOS=(packages community) OPT_ARCH=$(uname -m) : "${ASPROOT:=${XDG_CACHE_HOME:-$HOME/.cache}/asp}" : "${ASPCACHE:=$ASPROOT/cache}" m4_include(util.inc.sh) m4_include(remote.inc.sh) m4_include(package.inc.sh) m4_include(archweb.inc.sh) usage() { cat< max )); then log_fatal '%s expects at most %d args, got %d' "${FUNCNAME[1]#action__}" "$max" "$argc" elif (( argc < min )); then log_fatal '%s expects at least %d args, got %d' "${FUNCNAME[1]#action__}" "$min" "$argc" fi } version() { printf 'asp %s\n' "$ASP_VERSION" } update_all() { local r for r in "${ARCH_GIT_REPOS[@]}"; do log_info "updating remote '%s'" "$r" remote_update "$r" done } update_local_branches() { local r=0 while read -r branchname; do git branch -qf "$branchname" "refs/remotes/$branchname" || r=1 done < <(git branch --no-color) return "$r" } update_remote_branches() { local refspecs=() remote pkgname declare -A refspec_map if (( $# == 0 )); then update_all return fi # map packages to remotes for pkgname; do package_init -n "$pkgname" remote || return 1 refspec_map["$remote"]+=" packages/$pkgname" done # update each remote all at once for remote in "${!refspec_map[@]}"; do read -ra refspecs <<<"${refspec_map["$remote"]}" remote_update_refs "$remote" "${refspecs[@]}" done } update_packages() { update_remote_branches "$@" && update_local_branches } initialize() { local remote url umask 0022 export GIT_DIR=$ASPROOT/.git if [[ ! -f $ASPROOT/.asp ]]; then git init -q "$ASPROOT" || return 1 for remote in "${ARCH_GIT_REPOS[@]}"; do git remote add "$remote" "https://github.com/archlinux/svntogit-$remote.git" || return 1 done touch "$ASPROOT/.asp" || return 1 else # migrate from git.archlinux.org to github.com for remote in "${ARCH_GIT_REPOS[@]}"; do url=$(git remote get-url "$remote") # https://github.blog/2021-09-01-improving-git-protocol-security-github/ if [[ $url = *'git.archlinux.org'* ]] || [[ $url = *'git://github.com'* ]]; then git remote set-url "$remote" "https://github.com/archlinux/svntogit-$remote.git" fi done fi if [[ ! -d $ASPCACHE ]]; then mkdir -p "$ASPCACHE" || return 1 fi return 0 } dump_packages() { local remote refspecs dumpfn case $1 in all) dumpfn=remote_get_all_refs ;; local) dumpfn=remote_get_tracked_refs ;; *) log_fatal 'BUG: invalid dump type: "%s"' "$1" ;; esac for remote in "${ARCH_GIT_REPOS[@]}"; do "$dumpfn" "$remote" refspecs if [[ $refspecs ]]; then printf '%s\n' "${refspecs[@]##*/}" fi done | sort } list_local() { dump_packages 'local' } list_all() { dump_packages 'all' } shortlog() { package_log "$@" "${FUNCNAME[0]}" } log() { package_log "$@" "${FUNCNAME[0]}" } difflog() { package_log "$@" "${FUNCNAME[0]}" } gc() { git gc --prune=all } untrack() { local pkgname=$1 remote package_init -n "$pkgname" remote || return 1 remote_untrack "$remote" "$pkgname" package_untrack "$pkgname" "$remote" } disk_usage() { local usage read -r usage _ < <(du -sh "$ASPROOT") log_info 'Using %s on disk.' "$usage" } action__checkout() { __require_argc 1- $# map package_checkout "$@" } action__difflog() { __require_argc 1 $# difflog "$1" } action__disk-usage() { __require_argc 0 $# disk_usage } action__export() { __require_argc 1- $# map package_export "$@" } action__gc() { __require_argc 0 $# gc } action__help() { __require_argc 0 $# usage } action__list-all() { __require_argc 0 $# list_all } action__list-arches() { __require_argc 1- $# map package_get_arches "$@" } action__list-local() { __require_argc 0 $# list_local } action__list-repos() { __require_argc 1- $# map package_get_repos "$@" } action__log() { __require_argc 1 $# log "$1" } action__shortlog() { __require_argc 1 $# shortlog "$1" } action__show() { __require_argc 1-2 $# package_show_file "$@" } action__untrack() { __require_argc 1- $# map untrack "$@" } action__update() { update_packages "$@" } action__ls-files() { __require_argc 1 $# package_list_files "$1" } action__set-git-protocol() { __require_argc 1 $# case $1 in git|http|https) ;; *) log_fatal 'invalid protocol: %s' "$1" ;; esac for remote in "${ARCH_GIT_REPOS[@]}"; do git remote set-url "$remote" "$1://github.com/archlinux/svntogit-$remote.git" done } dispatch_action() { local candidates [[ $1 ]] || log_fatal 'no action specified (use -h for help)' # exact match if declare -F "action__$1" &>/dev/null; then "action__$1" "${@:2}" return fi # prefix match mapfile -t candidates < <(compgen -A function "action__$1") case ${#candidates[*]} in 0) log_fatal 'unknown action: %s' "$1" ;; 1) "${candidates[0]}" "${@:2}" return ;; *) { printf "error: verb '%s' is ambiguous; possibilities:" "$1" printf " '%s'" "${candidates[@]#action__}" echo } >&2 return 1 ;; esac } initialize || log_fatal 'failed to initialize asp repository in %s' "$ASPROOT" case $1 in --version) version exit 0 ;; --help) usage exit 0 ;; esac while getopts ':a:hV' flag; do case $flag in a) OPT_ARCH=$OPTARG ;; h) usage exit 0 ;; V) version exit 0 ;; \?) log_fatal "invalid option -- '%s'" "$OPTARG" ;; :) log_fatal "option '-%s' requires an argument" "$OPTARG" ;; esac done shift $(( OPTIND - 1 )) dispatch_action "$@" ================================================ FILE: man/asp.1.txt ================================================ ///// vim:set ts=4 sw=4 syntax=asciidoc noet: ///// asp(1) ====== Name ---- asp - Manage Arch Linux build sources Synopsis -------- asp [options] command [targets...] Description ----------- Manage the version-controlled sources for the build scripts used to create Arch Linux packages. This program provides a thin wrapper over the svntogit repositories hosted at https://github.com/archlinux. It aims to provide a replacement for abs which favors a sparse checkout. Commands -------- The following commands are understood: *checkout* 'TARGET'...:: Create a new git repository containing the full source and history for each of the given targets. The new repository will pull from the repository in '$ASPROOT' and must be updated separately after using 'asp update'. If a checkout occurs on the same filesystem as '$ASPROOT', most of the metadata can be hard linked, making this a relatively cheap copy. *difflog* 'TARGET':: Show the full revision history of the target, with file diffs. *disk-usage*:: Report the approximate disk usage for locally tracked packages. *export* 'TARGET'...:: Dump the build source files for each target into a directory of the target's name in '$PWD'. Targets can be specified simply as 'package' to check out the source files at HEAD, or in 'repository/package' format to checkout the source files which were used to push the 'package' which exists in 'repository'. *gc*:: Perform housekeeping procedures on the local repo, optimizing and compacting the repo to free disk space. *help*:: Display the command line usage and exit. *list-all*:: List all known packages in the repositories. *list-arches* 'TARGET'...:: List the architectures the given targets are available for. *list-local*:: List all packages which are tracked locally. *list-repos* 'TARGET'...:: List the repositories the given targets exist in. *log* 'TARGET':: Show the revision history of the target. *ls-files* 'TARGET':: List source files for the given target. *set-git-protocol* 'PROTOCOL':: Set the protocol used to communicate with the remote git repositories. Must be one of 'git', 'http', or 'https'. *shortlog* 'TARGET':: Show a condensed revision history of the target. *show* 'TARGET' ['FILE']:: Show the file content of the target, which may be in the format 'package' or 'repository/package'. If an additional 'file' argument is provided, attempt to display that file rather than the PKGBUILD. If the repository is not specified, the file will be shown at the most recent revision (which may be newer than what is in the repositories). *untrack* 'TARGET'...:: Remove a remote tracking branch from the local repository. Disk usage for the removed package(s) may not be freed until garbage collection has taken place. *update* ['TARGET'...]:: For each target, if the package is not known to the local repository, attempt to track it. If the package is tracked, update the package to the newest version. If no targets are provided, all locally known packages will be updated. Options ------- *-a* 'architecture':: When relevant, specify an architecture other than that of the current host. *-h*:: Print a short help text and exit. *-V*:: Print a short version string and exit. Environment ----------- *ASPROOT*:: Determines where the metadata is stored for locally tracked packages. Defaults to '`${XDG_CACHE_HOME:-$HOME/.cache}/asp`'. *ASPCACHE*:: Determines where cached data is stored. Defaults to '$ASPROOT/cache'. Data in this directory can always be safely deleted. Authors ------- Dave Reisner ================================================ FILE: package.inc.sh ================================================ package_resolve() { local pkgbase [[ $pkgname ]] || log_fatal 'BUG: package_resolve called without pkgname var set' if package_find_remote "$1" "$2"; then return 0 fi if pkgbase=$(archweb_get_pkgbase "$1") && package_find_remote "$pkgbase" "$2"; then log_info '%s is part of package %s' "$1" "$pkgbase" pkgname=$pkgbase return 0 fi log_error 'unknown package: %s' "$pkgname" return 1 } package_init() { local do_update=1 if [[ $1 = -n ]]; then do_update=0 shift fi pkgname=$1 package_resolve "$pkgname" "$2" || return (( do_update )) || return 0 remote_is_tracking "${!2}" "$pkgname" || remote_update_refs "${!2}" "packages/$pkgname" } package_find_remote() { pkgname=$1 # fastpath, checks local caches only for r in "${ARCH_GIT_REPOS[@]}"; do if remote_is_tracking "$r" "$pkgname"; then printf -v "$2" %s "$r" return 0 fi done # slowpath, needs to talk to the remote for r in "${ARCH_GIT_REPOS[@]}"; do if remote_has_package "$r" "$pkgname"; then printf -v "$2" %s "$r" return 0 fi done return 1 } package_log() { local method=$2 logargs remote pkgname=$1 package_init "$pkgname" remote || return case $method in shortlog) logargs=('--pretty=oneline') ;; difflog) logargs=('-p') ;; log) logargs=() ;; *) log_fatal 'BUG: unknown log method: %s' "$method" ;; esac git log "${logargs[@]}" "$remote/packages/$pkgname" -- trunk/ } package_show_file() { local file=${2:-PKGBUILD} remote repo subtree pkgname=$1 if [[ $pkgname = */* ]]; then IFS=/ read -r repo pkgname <<<"$pkgname" fi package_init "$pkgname" remote || return if [[ $file != */* ]]; then if [[ $repo ]]; then subtree=repos/$repo-$OPT_ARCH/ else subtree=trunk/ fi fi git show "remotes/$remote/packages/$pkgname:$subtree$file" } package_list_files() { local remote subtree=trunk pkgname=$1 if [[ $pkgname = */* ]]; then IFS=/ read -r repo pkgname <<<"$pkgname" fi package_init "$pkgname" remote || return if [[ $repo ]]; then subtree=repos/$repo-$OPT_ARCH fi git ls-tree -r --name-only "remotes/$remote/packages/$pkgname" "$subtree" | awk -v "prefix=$subtree/" 'sub(prefix, "")' } package_export() { local remote repo arch=$OPT_ARCH arches subtree=trunk pkgname=$1 if [[ $pkgname = */* ]]; then IFS=/ read -r repo pkgname <<<"$pkgname" fi package_init "$pkgname" remote || return if [[ $repo ]]; then mapfile -t arches < <(package_get_arches "$pkgname") if (( ${#arches[*]} == 1 )) && [[ ${arches[0]} = any ]]; then arch=any fi subtree=repos/$repo-$arch fi if ! git show "remotes/$remote/packages/$pkgname:$subtree/" &>/dev/null; then if [[ $repo ]]; then log_error "package '%s' not found in repo '%s-%s'" "$pkgname" "$repo" "$OPT_ARCH" return 1 else log_error "package '%s' has no trunk directory!" "$pkgname" return 1 fi fi mkdir "$pkgname" || return log_info 'exporting %s:%s' "$pkgname" "$subtree" git archive --format=tar "remotes/$remote/packages/$pkgname" "$subtree/" | tar --transform "s,^$subtree,$pkgname," -xf - "$subtree/" } package_checkout() { local remote pkgname=$1 package_init "$pkgname" remote || return git show-ref -q "refs/heads/$remote/packages/$pkgname" || git branch -qf --no-track {,}"$remote/packages/$pkgname" quiet_git clone \ --shared \ --single-branch \ --branch "$remote/packages/$pkgname" \ --config "pull.rebase=true" \ "$ASPROOT" "$pkgname" || return } package_get_repos_with_arch() { local remote=$2 path arch repo pkgname=$1 while read -r path; do path=${path##*/} repo=${path%-*} arch=${path##*-} printf '%s %s\n' "$repo" "$arch" done < <(git ls-tree --name-only "remotes/$remote/packages/$pkgname" repos/) } package_get_arches() { local remote arch declare -A arches pkgname=$1 package_init "$pkgname" remote || return while read -r _ arch; do arches["$arch"]=1 done < <(package_get_repos_with_arch "$pkgname" "$remote") printf '%s\n' "${!arches[@]}" } package_get_repos() { local remote repo declare -A repos pkgname=$1 package_init "$pkgname" remote || return while read -r repo _; do repos["$repo"]=1 done < <(package_get_repos_with_arch "$pkgname" "$remote") printf '%s\n' "${!repos[@]}" } package_untrack() { local remote=$2 pkgname=$1 if git show-ref -q "refs/heads/$remote/packages/$pkgname"; then git branch -D "$remote/packages/$pkgname" fi } ================================================ FILE: remote.inc.sh ================================================ __remote_refcache_update() { local remote=$1 cachefile=$ASPCACHE/remote-$remote refs refs=$(git ls-remote "$remote" 'refs/heads/packages/*') || log_fatal "failed to update remote $remote" printf '%s' "$refs" | awk '{ sub(/refs\/heads\/packages\//, "", $2); print $2 }' >"$cachefile" } __remote_refcache_is_stale() { local now cachetime cachefile=$1 ttl=3600 printf -v now '%(%s)T' -1 # The cache is stale if we've exceeded the TTL. if ! cachetime=$(stat -c %Y "$cachefile" 2>/dev/null) || (( now > (cachetime + ttl) )); then return 0 fi # We also consider the cache to be stale when this script is newer than the # cache. This allows upgrades to asp to implicitly wipe the cache and not # make any guarantees about the file format. if (( $(stat -c %Y "${BASH_SOURCE[0]}" 2>/dev/null) > cachetime )); then return 0 fi return 1 } __remote_refcache_get() { local remote=$1 cachefile=$ASPCACHE/remote-$remote if __remote_refcache_is_stale "$cachefile"; then __remote_refcache_update "$remote" fi mapfile -t "$2" <"$cachefile" } remote_get_all_refs() { local remote=$1 __remote_refcache_get "$remote" "$2" } remote_has_package() { local remote=$1 pkgname=$2 refs remote_get_all_refs "$remote" refs in_array "$pkgname" "${refs[@]}" } remote_is_tracking() { local repo=$1 pkgname=$2 git show-ref -q "$repo/packages/$pkgname" } remote_get_tracked_refs() { local remote=$1 mapfile -t "$2" < \ <(git for-each-ref --format='%(refname:strip=3)' "refs/remotes/$remote") } remote_update_refs() { local remote=$1 refspecs=("${@:2}") quiet_git fetch "$remote" "${refspecs[@]}" } remote_update() { local remote=$1 refspecs remote_get_tracked_refs "$remote" refspecs # refuse to update everything [[ -z $refspecs ]] && return 0 remote_update_refs "$remote" "${refspecs[@]}" } remote_untrack() { local remote=$1 pkgname=$2 if git show-ref -q "refs/remotes/$remote/packages/$pkgname"; then git branch -dr "$remote/packages/$pkgname" fi } ================================================ FILE: shell/bash-completion ================================================ #!/bin/bash in_array() { for _ in "${@:2}"; do [[ $_ = "$1" ]] && return 0 done return 1 } _asp() { local verb='' i cur prev comps _get_comp_words_by_ref cur prev # top level commands local -A verbs=( [ALL_PACKAGES]='checkout difflog export list-arches list-repos log shortlog show ls-files' [LOCAL_PACKAGES]='untrack update' [NONE]='disk-usage gc help list-all list-local' [PROTO]='set-git-protocol' ) # flags local -A opts=( [UNKNOWN]='-a' [NONE]='-h -V' ) if in_array "$prev" ${opts[UNKNOWN]}; then return 0 fi if [[ $cur = -* ]]; then COMPREPLY=( $(compgen -W '${opts[*]}' -- "$cur") ) return 0 fi # verb completion for (( i = 0; i < ${#COMP_WORDS[@]}; ++i )); do word=${COMP_WORDS[i]} if in_array "$word" ${verbs[ALL_PACKAGES]}; then verb=$word comps=$(ASP_GIT_QUIET=1 \asp list-all | sed 's,.*/,,') break elif in_array "$word" ${verbs[LOCAL_PACKAGES]}; then verb=$word comps=$(ASP_GIT_QUIET=1 \asp list-local | sed 's,.*/,,') break elif in_array "$word" ${verbs[PROTO]}; then verb=$word comps='git http https' break elif in_array "$word" ${verbs[NONE]}; then verb=$word break fi done # sub-verb completion case $verb in show) if (( i < ${#COMP_WORDS[@]} - 2 )); then comps=$(ASP_GIT_QUIET=1 \asp ls-files "${COMP_WORDS[i+1]}" 2>/dev/null) fi ;; '') comps=${verbs[*]} ;; esac if [[ $comps ]]; then COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) fi } complete -F _asp asp ================================================ FILE: shell/zsh-completion ================================================ #compdef asp _asp_command() { local -a _asp_cmds _asp_cmds=( 'checkout' 'difflog' 'export' 'gc' 'disk-usage' 'help' 'list-all' 'list-arches' 'list-local' 'list-repos' 'ls-files' 'log' 'shortlog' 'show' 'set-git-protocol' 'update' 'untrack' ) if (( CURRENT == 1 )); then _describe -t commands 'asp command' _asp_cmds || compadd "$@" else local curcontext="$curcontext" cmd="${${_asp_cmds[(r)$words[1]:*]%%:*}}" if (( $#cmd )); then if (( $+functions[_asp_$cmd] )); then _asp_$cmd else _message "no more options" fi else _message "unknown asp command: $words[1]" fi fi } _arguments \ '-a[architecture]' \ '-h[print help and exit]' \ '-V[print version and exit]' \ '*::asp command:_asp_command' # vim: set et sw=2 ts=2 ft=zsh : ================================================ FILE: util.inc.sh ================================================ log_meta() { # shellcheck disable=SC2059 printf "$1 $2\\n" "${@:3}" } log_error() { log_meta 'error:' "$@" >&2 } log_fatal() { log_error "$@" exit 1 } log_warning() { log_meta 'warning:' "$@" >&2 } log_info() { log_meta '==>' "$@" } map() { local map_r=0 for _ in "${@:2}"; do "$1" "$_" || map_r=1 done return $map_r } in_array() { local item needle=$1 for item in "${@:2}"; do [[ $item = "$needle" ]] && return 0 done return 1 } quiet_git() { [[ $ASP_GIT_QUIET ]] && set -- "$1" -q "${@:2}" command git "$@" }