2 # aur-fetch - retrieve build files from the AUR
3 [[ -v AUR_DEBUG
]] && set -o xtrace
6 XDG_CACHE_HOME
=${XDG_CACHE_HOME:-$HOME/.cache}
7 XDG_CONFIG_HOME
=${XDG_CONFIG_HOME:-$HOME/.config}
8 AUR_FETCH_USE_MIRROR
=${AUR_FETCH_USE_MIRROR:-0}
9 AUR_LOCATION
=${AUR_LOCATION:-https://aur.archlinux.org}
10 PS4
='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[1]}(): }'
12 # Author information for merge commits
13 export GIT_AUTHOR_NAME
=aurutils
14 export GIT_AUTHOR_EMAIL
=aurutils@localhost
15 export GIT_COMMITTER_NAME
=aurutils
16 export GIT_COMMITTER_EMAIL
=aurutils@localhost
17 export GIT_HTTP_USER_AGENT
=aurutils
19 # Placeholder for repositories without commits
20 git_empty_object
=$
(git hash-object
-t tree
/dev
/null
)
23 existing
=0 recurse
=0 discard
=0 sync
=fetch
26 # shellcheck disable=SC2155
27 local str
=$
(printf '%s,' "$@")
28 printf '%s' "${str%,}"
31 # XXX: races with multiple fetch instances
33 local mode
=$1 prev
=$2 current
=$3 path
=$4 dest
=$5
35 if [[ -w $dest ]]; then
36 printf >> "$dest" '%s:%s:%s:file://%s\n' "$mode" "$prev" "$current" "$path"
40 sync_package_config
() {
41 case $
(git config
--get --type bool aurutils.rebase
) in
43 printf >&2 '%s: aurutils.rebase is set for %s\n' "$argv0" "$1"
52 ICAgICAgICAgICAgIC4tLX5
+LF9fCjotLi4uLiwtLS0tLS0tYH5
+Jy5fLicKIGAtLCwsICAs
53 XyAgICAgIDsnflUnCiAgXywtJyAsJ2AtX187ICctLS4KIChfLyd
+fiAgICAgICcnJycoOwoK
55 printf >&2 'usage: %s [-Sefr] [--rebase|--reset|--merge] [--] pkgname...\n' "$argv0"
61 opt_long
=('auto' 'merge' 'reset' 'rebase' 'discard' 'existing' 'results:' 'ff'
62 'ff-only' 'no-ff' 'no-commit' 'recurse')
63 opt_hidden
=('dump-options' 'sync:')
65 if opts
=$
(getopt
-o "$opt_short" -l "$(args_csv "${opt_long[@]}" "${opt_hidden[@]}")" -n "$argv0" -- "$@"); then
71 unset rebase_args merge_args results_file
88 shift; results_file
=$
(realpath
-- "$1") ;;
93 merge_args
+=(--ff-only) ;;
95 merge_args
+=(--no-commit) ;;
97 merge_args
+=(--no-ff); rebase_args
+=(--no-ff) ;;
98 # Compatibility options
104 printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
105 printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
112 # Default to only allowing fast-forward merges (as git-pull)
113 if (( ! ${#merge_args[@]} )); then
114 merge_args=(--ff-only)
118 if [[ $sync == !(auto|merge|rebase|reset|fetch) ]]; then
119 printf >&2 '%s: invalid --sync mode\n' "$argv0"
124 printf >&2 '%s: no targets specified\n' "$argv0"
128 # XXX: race with concurrent processes
129 if [[ -v results_file ]]; then
130 : >"$results_file" || exit 1 # truncate file
133 # Save stdin/depends in array (2 passes)
134 if (( recurse )); then
135 mapfile -t packages < <(aur depends --reverse "$@
" | tsort)
138 elif (( $# == 1 )) && [[ $1 == "-" || $1 == "/dev
/stdin
" ]]; then
145 # Exit gracefully on empty stdin, e.g. when piping from `aur repo -u`.
146 if (( ! ${#packages[@]} )); then
150 # Update revisions in local AUR mirror
151 declare -A local_clones
153 # With an AUR mirror, updates are retrieved in two steps. First, updates to the
154 # mirror are synchronized with `git-fetch`. Secondly, local clones of the miror
155 # are created and are updated with `git-fetch` and `git-merge` as usual.
156 if (( AUR_FETCH_USE_MIRROR )); then
157 while IFS=':' read -r pkg head; do
158 printf "Cloning into
'%s'\n" "$pkg"
159 git -C "$pkg" --no-pager log --pretty=reference -1
161 if [[ -v results_file ]]; then
162 results 'clone' "$git_empty_object" "$head" "$PWD/$pkg" "$results_file"
164 local_clones[$pkg]=$head
166 aur fetch--mirror --lclone "${packages[@]}"
172 for pkg in "${packages[@]}"; do
175 # Local clone by fetch--mirror
176 if [[ ${local_clones[$pkg]} ]]; then
179 # Verify if the repository is hosted on AUR (#959)
180 elif (( existing )) && ! git ls-remote --exit-code "$AUR_LOCATION/$pkg" >/dev/null; then
181 printf >&2 '%s: warning: package %s is not in AUR, skipping\n' "$argv0" "$pkg"
184 # Clone package if not existing
185 elif [[ ! -d $pkg/.git ]]; then
186 git clone "$AUR_LOCATION/$pkg" || exit 1
188 head=$(git -C "$pkg" rev-parse --verify HEAD)
189 [[ $head ]] && git -C "$pkg" --no-pager log --pretty=reference -1
191 if [[ -v results_file ]]; then
192 results 'clone' "$git_empty_object" "${head:-$git_empty_object}" "$PWD/$pkg" "$results_file"
195 # Update existing git repository
198 exec {fd}< "$pkg"/.git
199 flock --wait 5 "$fd" || exit 1
201 # Avoid issues with filesystem boundaries (#274)
202 git() { command git -C "$pkg" "$@
"; }
204 # Retrieve per-package configuration (aurutils.rebase, #1007)
205 if [[ $sync == 'auto' ]]; then
206 sync_pkg=$(sync_package_config "$pkg")
211 # Retrieve new upstream commits
212 git fetch origin || exit
214 # Store original HEAD for --results output
215 orig_head=$(git rev-parse --verify --quiet HEAD)
216 orig_head=${orig_head:-$git_empty_object}
218 # Set correct branch for rebasing local clones
219 if (( AUR_FETCH_USE_MIRROR )); then
220 upstream=origin/$pkg # master -> origin/$pkg
222 upstream=origin/master # master -> origin/master
225 # Merge in new history
229 if (( discard )); then
233 git rebase "${rebase_args[@]}" "$upstream" ;;
236 if (( discard )); then
237 # reset worktree if new commits are available
238 git merge-base --is-ancestor 'master@{u}' HEAD || git checkout ./
240 git merge "${merge_args[@]}" "$upstream" ;;
243 git reset --hard 'master@{u}' ;;
248 printf >&2 '%s: failed to %s %s\n' "$argv0" "$sync_pkg" "$pkg"
251 head=$(git rev-parse --verify "$dest")
253 if [[ -v results_file ]]; then
254 results "$sync_pkg" "$orig_head" "$head" "$PWD/$pkg" "$results_file"
256 exec {fd}<&- # release lock
257 fi >&2 # print all git output to stderr
260 # vim: set et sw=4 sts=4 ft=sh: