Commit 2c2f91c92d42beda4c872eb839b60ab9f02b6230

Authored by Shrikant Sharat
1 parent d36c6f93d8

Added `antigen-restore` command.

This command is used to restore from a snapshot file provided as an argument.
The snapshot file can be generated by the `antigen-snapshot` command.

Showing 2 changed files with 55 additions and 5 deletions Inline Diff

1 #!/bin/zsh 1 #!/bin/zsh
2 2
3 # Each line in this string has the following entries separated by a space 3 # Each line in this string has the following entries separated by a space
4 # character. 4 # character.
5 # <repo-url>, <plugin-location>, <bundle-type>, <has-local-clone> 5 # <repo-url>, <plugin-location>, <bundle-type>, <has-local-clone>
6 # FIXME: Is not kept local by zsh! 6 # FIXME: Is not kept local by zsh!
7 local _ANTIGEN_BUNDLE_RECORD="" 7 local _ANTIGEN_BUNDLE_RECORD=""
8 8
9 # Syntaxes 9 # Syntaxes
10 # antigen-bundle <url> [<loc>=/] 10 # antigen-bundle <url> [<loc>=/]
11 # Keyword only arguments: 11 # Keyword only arguments:
12 # branch - The branch of the repo to use for this bundle. 12 # branch - The branch of the repo to use for this bundle.
13 antigen-bundle () { 13 antigen-bundle () {
14 14
15 # Bundle spec arguments' default values. 15 # Bundle spec arguments' default values.
16 local url="$ANTIGEN_DEFAULT_REPO_URL" 16 local url="$ANTIGEN_DEFAULT_REPO_URL"
17 local loc=/ 17 local loc=/
18 local branch= 18 local branch=
19 local no_local_clone=false 19 local no_local_clone=false
20 local btype=plugin 20 local btype=plugin
21 21
22 # Parse the given arguments. (Will overwrite the above values). 22 # Parse the given arguments. (Will overwrite the above values).
23 eval "$(-antigen-parse-args \ 23 eval "$(-antigen-parse-args \
24 'url?, loc? ; branch:?, no-local-clone?, btype:?' \ 24 'url?, loc? ; branch:?, no-local-clone?, btype:?' \
25 "$@")" 25 "$@")"
26 26
27 # Check if url is just the plugin name. Super short syntax. 27 # Check if url is just the plugin name. Super short syntax.
28 if [[ "$url" != */* ]]; then 28 if [[ "$url" != */* ]]; then
29 loc="plugins/$url" 29 loc="plugins/$url"
30 url="$ANTIGEN_DEFAULT_REPO_URL" 30 url="$ANTIGEN_DEFAULT_REPO_URL"
31 fi 31 fi
32 32
33 # Resolve the url. 33 # Resolve the url.
34 url="$(-antigen-resolve-bundle-url "$url")" 34 url="$(-antigen-resolve-bundle-url "$url")"
35 35
36 # Add the branch information to the url. 36 # Add the branch information to the url.
37 if [[ ! -z $branch ]]; then 37 if [[ ! -z $branch ]]; then
38 url="$url|$branch" 38 url="$url|$branch"
39 fi 39 fi
40 40
41 # The `make_local_clone` variable better represents whether there should be 41 # The `make_local_clone` variable better represents whether there should be
42 # a local clone made. For cloning to be avoided, firstly, the `$url` should 42 # a local clone made. For cloning to be avoided, firstly, the `$url` should
43 # be an absolute local path and `$branch` should be empty. In addition to 43 # be an absolute local path and `$branch` should be empty. In addition to
44 # these two conditions, either the `--no-local-clone` option should be 44 # these two conditions, either the `--no-local-clone` option should be
45 # given, or `$url` should not a git repo. 45 # given, or `$url` should not a git repo.
46 local make_local_clone=true 46 local make_local_clone=true
47 if [[ $url == /* && -z $branch && 47 if [[ $url == /* && -z $branch &&
48 ( $no_local_clone == true || ! -d $url/.git ) ]]; then 48 ( $no_local_clone == true || ! -d $url/.git ) ]]; then
49 make_local_clone=false 49 make_local_clone=false
50 fi 50 fi
51 51
52 # Add the theme extension to `loc`, if this is a theme. 52 # Add the theme extension to `loc`, if this is a theme.
53 if [[ $btype == theme && $loc != *.zsh-theme ]]; then 53 if [[ $btype == theme && $loc != *.zsh-theme ]]; then
54 loc="$loc.zsh-theme" 54 loc="$loc.zsh-theme"
55 fi 55 fi
56 56
57 # Add it to the record. 57 # Add it to the record.
58 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD\n$url $loc $btype" 58 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD\n$url $loc $btype"
59 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD $make_local_clone" 59 _ANTIGEN_BUNDLE_RECORD="$_ANTIGEN_BUNDLE_RECORD $make_local_clone"
60 60
61 # Ensure a clone exists for this repo, if needed. 61 # Ensure a clone exists for this repo, if needed.
62 if $make_local_clone; then 62 if $make_local_clone; then
63 -antigen-ensure-repo "$url" 63 -antigen-ensure-repo "$url"
64 fi 64 fi
65 65
66 # Load the plugin. 66 # Load the plugin.
67 -antigen-load "$url" "$loc" "$btype" "$make_local_clone" 67 -antigen-load "$url" "$loc" "$btype" "$make_local_clone"
68 68
69 } 69 }
70 70
71 -antigen-resolve-bundle-url () { 71 -antigen-resolve-bundle-url () {
72 # Given an acceptable short/full form of a bundle's repo url, this function 72 # Given an acceptable short/full form of a bundle's repo url, this function
73 # echoes the full form of the repo's clone url. 73 # echoes the full form of the repo's clone url.
74 74
75 local url="$1" 75 local url="$1"
76 76
77 # Expand short github url syntax: `username/reponame`. 77 # Expand short github url syntax: `username/reponame`.
78 if [[ $url != git://* && 78 if [[ $url != git://* &&
79 $url != https://* && 79 $url != https://* &&
80 $url != /* && 80 $url != /* &&
81 $url != git@github.com:*/* 81 $url != git@github.com:*/*
82 ]]; then 82 ]]; then
83 url="https://github.com/${url%.git}.git" 83 url="https://github.com/${url%.git}.git"
84 fi 84 fi
85 85
86 echo "$url" 86 echo "$url"
87 } 87 }
88 88
89 antigen-bundles () { 89 antigen-bundles () {
90 # Bulk add many bundles at one go. Empty lines and lines starting with a `#` 90 # Bulk add many bundles at one go. Empty lines and lines starting with a `#`
91 # are ignored. Everything else is given to `antigen-bundle` as is, no 91 # are ignored. Everything else is given to `antigen-bundle` as is, no
92 # quoting rules applied. 92 # quoting rules applied.
93 93
94 local line 94 local line
95 95
96 grep -v '^\s*$\|^#' | while read line; do 96 grep -v '^\s*$\|^#' | while read line; do
97 # Using `eval` so that we can use the shell-style quoting in each line 97 # Using `eval` so that we can use the shell-style quoting in each line
98 # piped to `antigen-bundles`. 98 # piped to `antigen-bundles`.
99 eval "antigen-bundle $line" 99 eval "antigen-bundle $line"
100 done 100 done
101 } 101 }
102 102
103 antigen-update () { 103 antigen-update () {
104 # Update your bundles, i.e., `git pull` in all the plugin repos. 104 # Update your bundles, i.e., `git pull` in all the plugin repos.
105 105
106 date > $ADOTDIR/revert-info 106 date > $ADOTDIR/revert-info
107 107
108 -antigen-echo-record | 108 -antigen-echo-record |
109 awk '{print $1}' | 109 awk '{print $1}' |
110 sort -u | 110 sort -u |
111 while read url; do 111 while read url; do
112 echo "**** Pulling $url" 112 echo "**** Pulling $url"
113 (dir="$(-antigen-get-clone-dir "$url")" 113 (dir="$(-antigen-get-clone-dir "$url")"
114 echo -n "$dir:" 114 echo -n "$dir:"
115 cd "$dir" 115 cd "$dir"
116 git rev-parse HEAD) >> $ADOTDIR/revert-info 116 git rev-parse HEAD) >> $ADOTDIR/revert-info
117 -antigen-ensure-repo "$url" --update --verbose 117 -antigen-ensure-repo "$url" --update --verbose
118 echo 118 echo
119 done 119 done
120 } 120 }
121 121
122 antigen-revert () { 122 antigen-revert () {
123 if ! [[ -f $ADOTDIR/revert-info ]]; then 123 if ! [[ -f $ADOTDIR/revert-info ]]; then
124 echo 'No revert information available. Cannot revert.' >&2 124 echo 'No revert information available. Cannot revert.' >&2
125 fi 125 fi
126 126
127 cat $ADOTDIR/revert-info | sed '1!p' | while read line; do 127 cat $ADOTDIR/revert-info | sed '1!p' | while read line; do
128 dir="$(echo "$line" | cut -d: -f1)" 128 dir="$(echo "$line" | cut -d: -f1)"
129 git --git-dir="$dir/.git" --work-tree="$dir" \ 129 git --git-dir="$dir/.git" --work-tree="$dir" \
130 checkout "$(echo "$line" | cut -d: -f2)" 2> /dev/null 130 checkout "$(echo "$line" | cut -d: -f2)" 2> /dev/null
131 done 131 done
132 132
133 echo "Reverted to state before running -update on $( 133 echo "Reverted to state before running -update on $(
134 cat $ADOTDIR/revert-info | sed -n 1p)." 134 cat $ADOTDIR/revert-info | sed -n 1p)."
135 } 135 }
136 136
137 -antigen-get-clone-dir () { 137 -antigen-get-clone-dir () {
138 # Takes a repo url and gives out the path that this url needs to be cloned 138 # Takes a repo url and gives out the path that this url needs to be cloned
139 # to. Doesn't actually clone anything. 139 # to. Doesn't actually clone anything.
140 echo -n $ADOTDIR/repos/ 140 echo -n $ADOTDIR/repos/
141 echo "$1" | sed \ 141 echo "$1" | sed \
142 -e 's./.-SLASH-.g' \ 142 -e 's./.-SLASH-.g' \
143 -e 's.:.-COLON-.g' \ 143 -e 's.:.-COLON-.g' \
144 -e 's.|.-PIPE-.g' 144 -e 's.|.-PIPE-.g'
145 } 145 }
146 146
147 -antigen-get-clone-url () { 147 -antigen-get-clone-url () {
148 # Takes a repo's clone dir and gives out the repo's original url that was 148 # Takes a repo's clone dir and gives out the repo's original url that was
149 # used to create the given directory path. 149 # used to create the given directory path.
150 echo "$1" | sed \ 150 echo "$1" | sed \
151 -e "s:^$ADOTDIR/repos/::" \ 151 -e "s:^$ADOTDIR/repos/::" \
152 -e 's.-SLASH-./.g' \ 152 -e 's.-SLASH-./.g' \
153 -e 's.-COLON-.:.g' \ 153 -e 's.-COLON-.:.g' \
154 -e 's.-PIPE-.|.g' 154 -e 's.-PIPE-.|.g'
155 } 155 }
156 156
157 -antigen-ensure-repo () { 157 -antigen-ensure-repo () {
158 158
159 # Ensure that a clone exists for the given repo url and branch. If the first 159 # Ensure that a clone exists for the given repo url and branch. If the first
160 # argument is `--update` and if a clone already exists for the given repo 160 # argument is `--update` and if a clone already exists for the given repo
161 # and branch, it is pull-ed, i.e., updated. 161 # and branch, it is pull-ed, i.e., updated.
162 162
163 # Argument defaults. 163 # Argument defaults.
164 # The url. No sane default for this, so just empty. 164 # The url. No sane default for this, so just empty.
165 local url= 165 local url=
166 # Check if we have to update. 166 # Check if we have to update.
167 local update=false 167 local update=false
168 # Verbose output. 168 # Verbose output.
169 local verbose=false 169 local verbose=false
170 170
171 eval "$(-antigen-parse-args 'url ; update?, verbose?' "$@")" 171 eval "$(-antigen-parse-args 'url ; update?, verbose?' "$@")"
172 shift $# 172 shift $#
173 173
174 # Get the clone's directory as per the given repo url and branch. 174 # Get the clone's directory as per the given repo url and branch.
175 local clone_dir="$(-antigen-get-clone-dir $url)" 175 local clone_dir="$(-antigen-get-clone-dir $url)"
176 176
177 # A temporary function wrapping the `git` command with repeated arguments. 177 # A temporary function wrapping the `git` command with repeated arguments.
178 --plugin-git () { 178 --plugin-git () {
179 eval git --git-dir=$clone_dir/.git --work-tree=$clone_dir "$@" 179 eval git --git-dir=$clone_dir/.git --work-tree=$clone_dir "$@"
180 } 180 }
181 181
182 # Clone if it doesn't already exist. 182 # Clone if it doesn't already exist.
183 if [[ ! -d $clone_dir ]]; then 183 if [[ ! -d $clone_dir ]]; then
184 git clone "${url%|*}" "$clone_dir" 184 git clone "${url%|*}" "$clone_dir"
185 elif $update; then 185 elif $update; then
186 # Save current revision. 186 # Save current revision.
187 local old_rev="$(--plugin-git rev-parse HEAD)" 187 local old_rev="$(--plugin-git rev-parse HEAD)"
188 # Pull changes if update requested. 188 # Pull changes if update requested.
189 --plugin-git pull 189 --plugin-git pull
190 # Get the new revision. 190 # Get the new revision.
191 local new_rev="$(--plugin-git rev-parse HEAD)" 191 local new_rev="$(--plugin-git rev-parse HEAD)"
192 fi 192 fi
193 193
194 # If its a specific branch that we want, checkout that branch. 194 # If its a specific branch that we want, checkout that branch.
195 if [[ $url == *\|* ]]; then 195 if [[ $url == *\|* ]]; then
196 local current_branch=${$(--plugin-git symbolic-ref HEAD)##refs/heads/} 196 local current_branch=${$(--plugin-git symbolic-ref HEAD)##refs/heads/}
197 local requested_branch="${url#*|}" 197 local requested_branch="${url#*|}"
198 # Only do the checkout when we are not already on the branch. 198 # Only do the checkout when we are not already on the branch.
199 [[ $requested_branch != $current_branch ]] && 199 [[ $requested_branch != $current_branch ]] &&
200 --plugin-git checkout $requested_branch 200 --plugin-git checkout $requested_branch
201 fi 201 fi
202 202
203 if ! [[ -z $old_rev || $old_rev == $new_rev ]]; then 203 if ! [[ -z $old_rev || $old_rev == $new_rev ]]; then
204 echo Updated from ${old_rev:0:7} to ${new_rev:0:7}. 204 echo Updated from ${old_rev:0:7} to ${new_rev:0:7}.
205 if $verbose; then 205 if $verbose; then
206 --plugin-git log --oneline --reverse --no-merges --stat '@{1}..' 206 --plugin-git log --oneline --reverse --no-merges --stat '@{1}..'
207 fi 207 fi
208 fi 208 fi
209 209
210 # Remove the temporary git wrapper function. 210 # Remove the temporary git wrapper function.
211 unfunction -- --plugin-git 211 unfunction -- --plugin-git
212 212
213 } 213 }
214 214
215 -antigen-load () { 215 -antigen-load () {
216 216
217 local url="$1" 217 local url="$1"
218 local loc="$2" 218 local loc="$2"
219 local btype="$3" 219 local btype="$3"
220 local make_local_clone="$4" 220 local make_local_clone="$4"
221 221
222 # The full location where the plugin is located. 222 # The full location where the plugin is located.
223 local location 223 local location
224 if $make_local_clone; then 224 if $make_local_clone; then
225 location="$(-antigen-get-clone-dir "$url")/$loc" 225 location="$(-antigen-get-clone-dir "$url")/$loc"
226 else 226 else
227 location="$url" 227 location="$url"
228 fi 228 fi
229 229
230 if [[ $btype == theme ]]; then 230 if [[ $btype == theme ]]; then
231 231
232 # Of course, if its a theme, the location would point to the script 232 # Of course, if its a theme, the location would point to the script
233 # file. 233 # file.
234 source "$location" 234 source "$location"
235 235
236 else 236 else
237 237
238 # Source the plugin script. 238 # Source the plugin script.
239 # FIXME: I don't know. Looks very very ugly. Needs a better 239 # FIXME: I don't know. Looks very very ugly. Needs a better
240 # implementation once tests are ready. 240 # implementation once tests are ready.
241 local script_loc="$(ls "$location" | grep -m1 '\.plugin\.zsh$')" 241 local script_loc="$(ls "$location" | grep -m1 '\.plugin\.zsh$')"
242 242
243 if [[ -f $script_loc ]]; then 243 if [[ -f $script_loc ]]; then
244 # If we have a `*.plugin.zsh`, source it. 244 # If we have a `*.plugin.zsh`, source it.
245 source "$script_loc" 245 source "$script_loc"
246 246
247 elif [[ ! -z "$(ls "$location" | grep -m1 '\.zsh$')" ]]; then 247 elif [[ ! -z "$(ls "$location" | grep -m1 '\.zsh$')" ]]; then
248 # If there is no `*.plugin.zsh` file, source *all* the `*.zsh` 248 # If there is no `*.plugin.zsh` file, source *all* the `*.zsh`
249 # files. 249 # files.
250 for script ($location/*.zsh(N)) source "$script" 250 for script ($location/*.zsh(N)) source "$script"
251 251
252 elif [[ ! -z "$(ls "$location" | grep -m1 '\.sh$')" ]]; then 252 elif [[ ! -z "$(ls "$location" | grep -m1 '\.sh$')" ]]; then
253 # If there are no `*.zsh` files either, we look for and source any 253 # If there are no `*.zsh` files either, we look for and source any
254 # `*.sh` files instead. 254 # `*.sh` files instead.
255 for script ($location/*.sh(N)) source "$script" 255 for script ($location/*.sh(N)) source "$script"
256 256
257 fi 257 fi
258 258
259 # Add to $fpath, for completion(s). 259 # Add to $fpath, for completion(s).
260 fpath=($location $fpath) 260 fpath=($location $fpath)
261 261
262 fi 262 fi
263 263
264 } 264 }
265 265
266 antigen-cleanup () { 266 antigen-cleanup () {
267 267
268 # Cleanup unused repositories. 268 # Cleanup unused repositories.
269 269
270 local force=false 270 local force=false
271 if [[ $1 == --force ]]; then 271 if [[ $1 == --force ]]; then
272 force=true 272 force=true
273 fi 273 fi
274 274
275 if [[ ! -d "$ADOTDIR/repos" || -z "$(ls "$ADOTDIR/repos/")" ]]; then 275 if [[ ! -d "$ADOTDIR/repos" || -z "$(ls "$ADOTDIR/repos/")" ]]; then
276 echo "You don't have any bundles." 276 echo "You don't have any bundles."
277 return 0 277 return 0
278 fi 278 fi
279 279
280 # Find directores in ADOTDIR/repos, that are not in the bundles record. 280 # Find directores in ADOTDIR/repos, that are not in the bundles record.
281 local unused_clones="$(comm -13 \ 281 local unused_clones="$(comm -13 \
282 <(-antigen-echo-record | 282 <(-antigen-echo-record |
283 awk '$4 == "true" {print $1}' | 283 awk '$4 == "true" {print $1}' |
284 while read line; do 284 while read line; do
285 -antigen-get-clone-dir "$line" 285 -antigen-get-clone-dir "$line"
286 done | 286 done |
287 sort -u) \ 287 sort -u) \
288 <(ls -d "$ADOTDIR/repos/"* | sort -u))" 288 <(ls -d "$ADOTDIR/repos/"* | sort -u))"
289 289
290 if [[ -z $unused_clones ]]; then 290 if [[ -z $unused_clones ]]; then
291 echo "You don't have any unidentified bundles." 291 echo "You don't have any unidentified bundles."
292 return 0 292 return 0
293 fi 293 fi
294 294
295 echo 'You have clones for the following repos, but are not used.' 295 echo 'You have clones for the following repos, but are not used.'
296 echo "$unused_clones" | 296 echo "$unused_clones" |
297 while read line; do 297 while read line; do
298 -antigen-get-clone-url "$line" 298 -antigen-get-clone-url "$line"
299 done | 299 done |
300 sed -e 's/^/ /' -e 's/|/, branch /' 300 sed -e 's/^/ /' -e 's/|/, branch /'
301 301
302 if $force || (echo -n '\nDelete them all? [y/N] '; read -q); then 302 if $force || (echo -n '\nDelete them all? [y/N] '; read -q); then
303 echo 303 echo
304 echo 304 echo
305 echo "$unused_clones" | while read line; do 305 echo "$unused_clones" | while read line; do
306 echo -n "Deleting clone for $(-antigen-get-clone-url "$line")..." 306 echo -n "Deleting clone for $(-antigen-get-clone-url "$line")..."
307 rm -rf "$line" 307 rm -rf "$line"
308 echo ' done.' 308 echo ' done.'
309 done 309 done
310 else 310 else
311 echo 311 echo
312 echo Nothing deleted. 312 echo Nothing deleted.
313 fi 313 fi
314 } 314 }
315 315
316 antigen-lib () { 316 antigen-lib () {
317 antigen-bundle --loc=lib 317 antigen-bundle --loc=lib
318 } 318 }
319 319
320 antigen-theme () { 320 antigen-theme () {
321 321
322 if [[ "$1" != */* && "$1" != --* ]]; then 322 if [[ "$1" != */* && "$1" != --* ]]; then
323 # The first argument is just a name of the plugin, to be picked up from 323 # The first argument is just a name of the plugin, to be picked up from
324 # the default repo. 324 # the default repo.
325 local name="${1:-robbyrussell}" 325 local name="${1:-robbyrussell}"
326 antigen-bundle --loc=themes/$name --btype=theme 326 antigen-bundle --loc=themes/$name --btype=theme
327 327
328 else 328 else
329 antigen-bundle "$@" --btype=theme 329 antigen-bundle "$@" --btype=theme
330 330
331 fi 331 fi
332 332
333 } 333 }
334 334
335 antigen-apply () { 335 antigen-apply () {
336 # Initialize completion. 336 # Initialize completion.
337 # TODO: Only load completions if there are any changes to the bundle 337 # TODO: Only load completions if there are any changes to the bundle
338 # repositories. 338 # repositories.
339 compinit -i 339 compinit -i
340 } 340 }
341 341
342 antigen-list () { 342 antigen-list () {
343 # List all currently installed bundles. 343 # List all currently installed bundles.
344 if [[ -z "$_ANTIGEN_BUNDLE_RECORD" ]]; then 344 if [[ -z "$_ANTIGEN_BUNDLE_RECORD" ]]; then
345 echo "You don't have any bundles." >&2 345 echo "You don't have any bundles." >&2
346 return 1 346 return 1
347 else 347 else
348 -antigen-echo-record | sort -u 348 -antigen-echo-record | sort -u
349 fi 349 fi
350 } 350 }
351 351
352 antigen-snapshot () { 352 antigen-snapshot () {
353 353
354 local snapshot_file="${1:-antigen-shapshot}" 354 local snapshot_file="${1:-antigen-shapshot}"
355 355
356 # The snapshot content lines are pairs of repo-url and git version hash, in 356 # The snapshot content lines are pairs of repo-url and git version hash, in
357 # the form: 357 # the form:
358 # <version-hash> <repo-url> 358 # <version-hash> <repo-url>
359 local snapshot_content="$(-antigen-echo-record | sed 's/ .*$//' | sort -u | 359 local snapshot_content="$(-antigen-echo-record | sed 's/ .*$//' | sort -u |
360 while read url; do 360 while read url; do
361 dir="$(-antigen-get-clone-dir "$url")" 361 local dir="$(-antigen-get-clone-dir "$url")"
362 version_hash="$(cd "$dir" && git rev-parse HEAD)" 362 local version_hash="$(cd "$dir" && git rev-parse HEAD)"
363 echo "$version_hash $dir" 363 echo "$version_hash $url"
364 done)" 364 done)"
365 365
366 { 366 {
367 # The first line in the snapshot file is for metadata, in the form: 367 # The first line in the snapshot file is for metadata, in the form:
368 # key='value'; key='value'; key='value'; 368 # key='value'; key='value'; key='value';
369 # Where `key`s are valid shell variable names. 369 # Where `key`s are valid shell variable names.
370 370
371 # Snapshot version. Has no relation to antigen version. If the snapshot 371 # Snapshot version. Has no relation to antigen version. If the snapshot
372 # file format changes, this number can be incremented. 372 # file format changes, this number can be incremented.
373 echo -n "version='1';" 373 echo -n "version='1';"
374 374
375 # Snapshot creation date+time. 375 # Snapshot creation date+time.
376 echo -n " created_on='$(date)';" 376 echo -n " created_on='$(date)';"
377 377
378 # Add a checksum with the md5 checksum of all the snapshot lines. 378 # Add a checksum with the md5 checksum of all the snapshot lines.
379 local checksum="$(echo "$snapshot_content" | md5sum)" 379 local checksum="$(echo "$snapshot_content" | md5sum)"
380 echo -n " checksum='${checksum%% *}';" 380 echo -n " checksum='${checksum%% *}';"
381 381
382 # A newline after the metadata and then the snapshot lines. 382 # A newline after the metadata and then the snapshot lines.
383 echo "\n$snapshot_content" 383 echo "\n$snapshot_content"
384 384
385 } > "$snapshot_file" 385 } > "$snapshot_file"
386 386
387 } 387 }
388 388
389 antigen-restore () {
390
391 if [[ $# == 0 ]]; then
392 echo 'Please provide a snapshot file to restore from.' >&2
393 return 1
394 fi
395
396 local snapshot_file="$1"
397
398 # TODO: Before doing anything with the snapshot file, verify its checksum.
399 # If it fails, notify this to the user and confirm if restore should
400 # proceed.
401
402 echo -n "Restoring from $snapshot_file..."
403
404 sed -n '1!p' "$snapshot_file" |
405 while read line; do
406
407 local version_hash="${line%% *}"
408 local url="${line##* }"
409 local clone_dir="$(-antigen-get-clone-dir "$url")"
410
411 if [[ ! -d $clone_dir ]]; then
412 git clone "$url" "$clone_dir" > /dev/null
413 fi
414
415 (cd "$clone_dir" && git checkout $version_hash) 2> /dev/null
416
417 done
418
419 echo ' done.'
420 echo 'Please open a new shell to get the restored changes.'
421 }
422
389 antigen-help () { 423 antigen-help () {
390 cat <<EOF 424 cat <<EOF
391 Antigen is a plugin management system for zsh. It makes it easy to grab awesome 425 Antigen is a plugin management system for zsh. It makes it easy to grab awesome
392 shell scripts and utilities, put up on github. For further details and complete 426 shell scripts and utilities, put up on github. For further details and complete
393 documentation, visit the project's page at 'http://antigen.sharats.me'. 427 documentation, visit the project's page at 'http://antigen.sharats.me'.
394 EOF 428 EOF
395 } 429 }
396 430
397 # A syntax sugar to avoid the `-` when calling antigen commands. With this 431 # A syntax sugar to avoid the `-` when calling antigen commands. With this
398 # function, you can write `antigen-bundle` as `antigen bundle` and so on. 432 # function, you can write `antigen-bundle` as `antigen bundle` and so on.
399 antigen () { 433 antigen () {
400 local cmd="$1" 434 local cmd="$1"
401 shift 435 shift
402 "antigen-$cmd" "$@" 436 "antigen-$cmd" "$@"
403 } 437 }
404 438
405 -antigen-parse-args () { 439 -antigen-parse-args () {
406 # An argument parsing functionality to parse arguments the *antigen* way :). 440 # An argument parsing functionality to parse arguments the *antigen* way :).
407 # Takes one first argument (called spec), which dictates how to parse and 441 # Takes one first argument (called spec), which dictates how to parse and
408 # the rest of the arguments are parsed. Outputs a piece of valid shell code 442 # the rest of the arguments are parsed. Outputs a piece of valid shell code
409 # that can be passed to `eval` inside a function which creates the arguments 443 # that can be passed to `eval` inside a function which creates the arguments
410 # and their values as local variables. Suggested use is to set the defaults 444 # and their values as local variables. Suggested use is to set the defaults
411 # to all arguments first and then eval the output of this function. 445 # to all arguments first and then eval the output of this function.
412 446
413 # Spec: Only long argument supported. No support for parsing short options. 447 # Spec: Only long argument supported. No support for parsing short options.
414 # The spec must have two sections, separated by a `;`. 448 # The spec must have two sections, separated by a `;`.
415 # '<positional-arguments>;<keyword-only-arguments>' 449 # '<positional-arguments>;<keyword-only-arguments>'
416 # Positional arguments are passed as just values, like `command a b`. 450 # Positional arguments are passed as just values, like `command a b`.
417 # Keyword arguments are passed as a `--name=value` pair, like `command 451 # Keyword arguments are passed as a `--name=value` pair, like `command
418 # --arg1=a --arg2=b`. 452 # --arg1=a --arg2=b`.
419 453
420 # Each argument in the spec is separated by a `,`. Each keyword argument can 454 # Each argument in the spec is separated by a `,`. Each keyword argument can
421 # end in a `:` to specifiy that this argument wants a value, otherwise it 455 # end in a `:` to specifiy that this argument wants a value, otherwise it
422 # doesn't take a value. (The value in the output when the keyword argument 456 # doesn't take a value. (The value in the output when the keyword argument
423 # doesn't have a `:` is `true`). 457 # doesn't have a `:` is `true`).
424 458
425 # Arguments in either section can end with a `?` (should come after `:`, if 459 # Arguments in either section can end with a `?` (should come after `:`, if
426 # both are present), means optional. FIXME: Not yet implemented. 460 # both are present), means optional. FIXME: Not yet implemented.
427 461
428 # See the test file, tests/arg-parser.t for (working) examples. 462 # See the test file, tests/arg-parser.t for (working) examples.
429 463
430 local spec="$1" 464 local spec="$1"
431 shift 465 shift
432 466
433 # Sanitize the spec 467 # Sanitize the spec
434 spec="$(echo "$spec" | tr '\n' ' ' | sed 's/[[:space:]]//g')" 468 spec="$(echo "$spec" | tr '\n' ' ' | sed 's/[[:space:]]//g')"
435 469
436 local code='' 470 local code=''
437 471
438 --add-var () { 472 --add-var () {
439 test -z "$code" || code="$code\n" 473 test -z "$code" || code="$code\n"
440 code="${code}local $1='$2'" 474 code="${code}local $1='$2'"
441 } 475 }
442 476
443 local positional_args="$(echo "$spec" | cut -d\; -f1)" 477 local positional_args="$(echo "$spec" | cut -d\; -f1)"
444 local positional_args_count="$(echo $positional_args | 478 local positional_args_count="$(echo $positional_args |
445 awk -F, '{print NF}')" 479 awk -F, '{print NF}')"
446 480
447 # Set spec values based on the positional arguments. 481 # Set spec values based on the positional arguments.
448 local i=1 482 local i=1
449 while ! [[ -z $1 || $1 == --* ]]; do 483 while ! [[ -z $1 || $1 == --* ]]; do
450 484
451 if (( $i > $positional_args_count )); then 485 if (( $i > $positional_args_count )); then
452 echo "Only $positional_args_count positional arguments allowed." >&2 486 echo "Only $positional_args_count positional arguments allowed." >&2
453 echo "Found at least one more: '$1'" >&2 487 echo "Found at least one more: '$1'" >&2
454 return 488 return
455 fi 489 fi
456 490
457 local name_spec="$(echo "$positional_args" | cut -d, -f$i)" 491 local name_spec="$(echo "$positional_args" | cut -d, -f$i)"
458 local name="${${name_spec%\?}%:}" 492 local name="${${name_spec%\?}%:}"
459 local value="$1" 493 local value="$1"
460 494
461 if echo "$code" | grep -qm1 "^local $name="; then 495 if echo "$code" | grep -qm1 "^local $name="; then
462 echo "Argument '$name' repeated with the value '$value'". >&2 496 echo "Argument '$name' repeated with the value '$value'". >&2
463 return 497 return
464 fi 498 fi
465 499
466 --add-var $name "$value" 500 --add-var $name "$value"
467 501
468 shift 502 shift
469 i=$(($i + 1)) 503 i=$(($i + 1))
470 done 504 done
471 505
472 local keyword_args="$( 506 local keyword_args="$(
473 # Positional arguments can double up as keyword arguments too. 507 # Positional arguments can double up as keyword arguments too.
474 echo "$positional_args" | tr , '\n' | 508 echo "$positional_args" | tr , '\n' |
475 while read line; do 509 while read line; do
476 if [[ $line == *\? ]]; then 510 if [[ $line == *\? ]]; then
477 echo "${line%?}:?" 511 echo "${line%?}:?"
478 else 512 else
479 echo "$line:" 513 echo "$line:"
480 fi 514 fi
481 done 515 done
482 516
483 # Specified keyword arguments. 517 # Specified keyword arguments.
484 echo "$spec" | cut -d\; -f2 | tr , '\n' 518 echo "$spec" | cut -d\; -f2 | tr , '\n'
485 )" 519 )"
486 local keyword_args_count="$(echo $keyword_args | awk -F, '{print NF}')" 520 local keyword_args_count="$(echo $keyword_args | awk -F, '{print NF}')"
487 521
488 # Set spec values from keyword arguments, if any. The remaining arguments 522 # Set spec values from keyword arguments, if any. The remaining arguments
489 # are all assumed to be keyword arguments. 523 # are all assumed to be keyword arguments.
490 while [[ $1 == --* ]]; do 524 while [[ $1 == --* ]]; do
491 # Remove the `--` at the start. 525 # Remove the `--` at the start.
492 local arg="${1#--}" 526 local arg="${1#--}"
493 527
494 # Get the argument name and value. 528 # Get the argument name and value.
495 if [[ $arg != *=* ]]; then 529 if [[ $arg != *=* ]]; then
496 local name="$arg" 530 local name="$arg"
497 local value='' 531 local value=''
498 else 532 else
499 local name="${arg%\=*}" 533 local name="${arg%\=*}"
500 local value="${arg#*=}" 534 local value="${arg#*=}"
501 fi 535 fi
502 536
503 if echo "$code" | grep -qm1 "^local $name="; then 537 if echo "$code" | grep -qm1 "^local $name="; then
504 echo "Argument '$name' repeated with the value '$value'". >&2 538 echo "Argument '$name' repeated with the value '$value'". >&2
505 return 539 return
506 fi 540 fi
507 541
508 # The specification for this argument, used for validations. 542 # The specification for this argument, used for validations.
509 local arg_line="$(echo "$keyword_args" | grep -m1 "^$name:\??\?")" 543 local arg_line="$(echo "$keyword_args" | grep -m1 "^$name:\??\?")"
510 544
511 # Validate argument and value. 545 # Validate argument and value.
512 if [[ -z $arg_line ]]; then 546 if [[ -z $arg_line ]]; then
513 # This argument is not known to us. 547 # This argument is not known to us.
514 echo "Unknown argument '$name'." >&2 548 echo "Unknown argument '$name'." >&2
515 return 549 return
516 550
517 elif (echo "$arg_line" | grep -qm1 ':') && [[ -z $value ]]; then 551 elif (echo "$arg_line" | grep -qm1 ':') && [[ -z $value ]]; then
518 # This argument needs a value, but is not provided. 552 # This argument needs a value, but is not provided.
519 echo "Required argument for '$name' not provided." >&2 553 echo "Required argument for '$name' not provided." >&2
520 return 554 return
521 555
522 elif (echo "$arg_line" | grep -vqm1 ':') && [[ ! -z $value ]]; then 556 elif (echo "$arg_line" | grep -vqm1 ':') && [[ ! -z $value ]]; then
523 # This argument doesn't need a value, but is provided. 557 # This argument doesn't need a value, but is provided.
524 echo "No argument required for '$name', but provided '$value'." >&2 558 echo "No argument required for '$name', but provided '$value'." >&2
525 return 559 return
526 560
527 fi 561 fi
528 562
529 if [[ -z $value ]]; then 563 if [[ -z $value ]]; then
530 value=true 564 value=true
531 fi 565 fi
532 566
533 --add-var "${name//-/_}" "$value" 567 --add-var "${name//-/_}" "$value"
534 shift 568 shift
535 done 569 done
536 570
537 echo "$code" 571 echo "$code"
538 572
539 unfunction -- --add-var 573 unfunction -- --add-var
540 574
541 } 575 }
542 576
543 # Echo the bundle specs as in the record. The first line is not echoed since it 577 # Echo the bundle specs as in the record. The first line is not echoed since it
544 # is a blank line. 578 # is a blank line.
545 -antigen-echo-record () { 579 -antigen-echo-record () {
546 echo "$_ANTIGEN_BUNDLE_RECORD" | sed -n '1!p' 580 echo "$_ANTIGEN_BUNDLE_RECORD" | sed -n '1!p'
547 } 581 }
548 582
549 -antigen-env-setup () { 583 -antigen-env-setup () {
550 # Pre-startup initializations. 584 # Pre-startup initializations.
551 -set-default ANTIGEN_DEFAULT_REPO_URL \ 585 -set-default ANTIGEN_DEFAULT_REPO_URL \
552 https://github.com/robbyrussell/oh-my-zsh.git 586 https://github.com/robbyrussell/oh-my-zsh.git
553 -set-default ADOTDIR $HOME/.antigen 587 -set-default ADOTDIR $HOME/.antigen
554 588
555 # Load the compinit module. 589 # Load the compinit module.
556 autoload -U compinit 590 autoload -U compinit
557 591
558 # Without the following, `compdef` function is not defined. 592 # Without the following, `compdef` function is not defined.
559 compinit -i 593 compinit -i
560 594
561 compdef _antigen antigen 595 compdef _antigen antigen
562 } 596 }
563 597
564 # Same as `export $1=$2`, but will only happen if the name specified by `$1` is 598 # Same as `export $1=$2`, but will only happen if the name specified by `$1` is
565 # not already set. 599 # not already set.
566 -set-default () { 600 -set-default () {
567 local arg_name="$1" 601 local arg_name="$1"
568 local arg_value="$2" 602 local arg_value="$2"
569 eval "test -z \"\$$arg_name\" && export $arg_name='$arg_value'" 603 eval "test -z \"\$$arg_name\" && export $arg_name='$arg_value'"
570 } 604 }
571 605
572 # Setup antigen's autocompletion 606 # Setup antigen's autocompletion
573 _antigen () { 607 _antigen () {
574 compadd \ 608 compadd \
575 bundle\ 609 bundle\
576 bundles\ 610 bundles\
577 update\ 611 update\
578 revert\ 612 revert\
579 list\ 613 list\
580 cleanup\ 614 cleanup\
581 lib\ 615 lib\
582 theme\ 616 theme\
583 apply\ 617 apply\
584 help 618 help
585 } 619 }
586 620
587 -antigen-env-setup 621 -antigen-env-setup
588 622
1 Load a couple of plugins. 1 Load a couple of plugins.
2 2
3 $ antigen-bundle $PLUGIN_DIR 3 $ antigen-bundle $PLUGIN_DIR
4 Cloning into '*'... (glob) 4 Cloning into '*'... (glob)
5 done. 5 done.
6 $ antigen-bundle $PLUGIN_DIR2 6 $ antigen-bundle $PLUGIN_DIR2
7 Cloning into '*'... (glob) 7 Cloning into '*'... (glob)
8 done. 8 done.
9 9
10 Create a snapshot file. 10 Create a snapshot file.
11 11
12 $ test -f snapshot-file 12 $ test -f snapshot-file
13 [1] 13 [1]
14 $ antigen-snapshot snapshot-file 14 $ antigen-snapshot snapshot-file
15 $ test -f snapshot-file 15 $ test -f snapshot-file
16 16
17 See the contents of the snapshot file. 17 See the contents of the snapshot file.
18 18
19 $ cat snapshot-file 19 $ cat snapshot-file
20 version='1'; created_on='*'; checksum='*'; (glob) 20 version='1'; created_on='*'; checksum='*'; (glob)
21 .{40} .*-test-plugin (re) 21 .{40} .*/test-plugin (re)
22 .{40} .*-test-plugin2 (re) 22 .{40} .*/test-plugin2 (re)
23
24 Reset the antigen's bundle record and run cleanup.
25
26 $ unset _ANTIGEN_BUNDLE_RECORD
27 $ antigen-cleanup --force | grep '^Deleting' | wc -l
28 2
29
30 Restore from the snapshot.
31
32 $ ls dot-antigen/repos | wc -l
33 0
34 $ antigen-restore snapshot-file
35 Restoring from snapshot-file... done.
36 Please open a new shell to get the restored changes.
37 $ ls dot-antigen/repos | wc -l
38 2
23 39