]> git.madduck.net Git - code/vcsh.git/blob - vcsh

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge branch 'feature--overlay_functions'
[code/vcsh.git] / vcsh
1 #!/bin/sh
2
3 # This program is licensed under the GNU GPL version 2 or later.
4 # (c) Richard "RichiH" Hartmann <richih@debian.org>, 2011-2014
5 # For details, see LICENSE. To submit patches, you have to agree to
6 # license your code under the GNU GPL version 2 or later.
7
8 # While the following is not legally binding, the author would like to
9 # explain the choice of GPLv2+ over GPLv3+.
10 # The author prefers GPLv3+ over GPLv2+ but feels it's better to maintain
11 # full compatibility's with Git. In case Git ever changes its licensing terms,
12 # which is admittedly extremely unlikely to the point of being impossible,
13 # this software will most likely follow suit.
14
15 # This should always be the first line of code to facilitate debugging
16 [ -n "$VCSH_DEBUG" ] && set -vx
17
18
19 # If '.git-HEAD' is appended to the version, you are seeing an unreleased
20 # version of vcsh; the master branch is supposed to be clean at all times
21 # so you can most likely just use it nonetheless
22 VERSION='1.20141009'
23 SELF=$(basename $0)
24
25 fatal() {
26         echo "$SELF: fatal: $1" >&2
27         [ -z $2] && exit 1
28         exit $2
29 }
30
31 # We need to run getops as soon as possible so we catch -d and other
32 # options that will modify our behaviour.
33 # Commands are handled at the end of this script.
34 while getopts "c:dv" flag; do
35         if [ x"$1" = x'-d' ] || [ x"$1" = x'--debug' ]; then
36                 set -vx
37                 VCSH_DEBUG=1
38                 echo "debug mode on"
39                 echo "$SELF $VERSION"
40         elif [ x"$1" = x'-v' ]; then
41                 VCSH_VERBOSE=1
42                 echo "verbose mode on"
43                 echo "$SELF $VERSION"
44         elif [ x"$1" = x'-c' ]; then
45                 VCSH_OPTION_CONFIG=$OPTARG
46         fi
47         shift 1
48 done
49
50 source_all() {
51         # Source file even if it's in $PWD and does not have any slashes in it
52         case $1 in
53                 */*) . "$1";;
54                 *)   . "$PWD/$1";;
55         esac;
56 }
57
58
59 # Read configuration and set defaults if anything's not set
60 [ -n "$VCSH_DEBUG" ]                  && set -vx
61 : ${XDG_CONFIG_HOME:="$HOME/.config"}
62
63 # Read configuration files if there are any
64 [ -r "/etc/vcsh/config" ]             && . "/etc/vcsh/config"
65 [ -r "$XDG_CONFIG_HOME/vcsh/config" ] && . "$XDG_CONFIG_HOME/vcsh/config"
66 if [ -n "$VCSH_OPTION_CONFIG" ]; then
67         # Source $VCSH_OPTION_CONFIG if it can be read and is in $PWD of $PATH
68         if [ -r "$VCSH_OPTION_CONFIG" ]; then
69                 source_all "$VCSH_OPTION_CONFIG"
70         else
71                 fatal "Can not read configuration file '$VCSH_OPTION_CONFIG'" 1
72         fi
73 fi
74 [ -n "$VCSH_DEBUG" ]                  && set -vx
75
76 # Read defaults
77 : ${VCSH_REPO_D:="$XDG_CONFIG_HOME/vcsh/repo.d"}
78 : ${VCSH_HOOK_D:="$XDG_CONFIG_HOME/vcsh/hooks-enabled"}
79 : ${VCSH_OVERLAY_D:="$XDG_CONFIG_HOME/vcsh/overlays-enabled"}
80 : ${VCSH_BASE:="$HOME"}
81 : ${VCSH_GITIGNORE:=exact}
82 : ${VCSH_GITATTRIBUTES:=none}
83 : ${VCSH_WORKTREE:=absolute}
84
85 if [ ! "x$VCSH_GITIGNORE" = 'xexact' ] && [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && [ ! "x$VCSH_GITIGNORE" = 'xrecursive' ]; then
86         fatal "'\$VCSH_GITIGNORE' must equal 'exact', 'none', or 'recursive'" 1
87 fi
88
89 if [ ! "x$VCSH_WORKTREE" = 'xabsolute' ] && [ ! "x$VCSH_WORKTREE" = 'xrelative' ]; then
90         fatal "'\$VCSH_WORKTREE' must equal 'absolute', or 'relative'" 1
91 fi
92
93
94 help() {
95         echo "usage: $SELF <options> <command>
96
97    options:
98    -c <file>            Source file
99    -d                   Enable debug mode
100    -v                   Enable verbose mode
101
102    commands:
103    clone <remote> \\
104          [<repo>]       Clone from an existing repository
105    commit               Commit in all repositories
106    delete <repo>        Delete an existing repository
107    enter <repo>         Enter repository; spawn new instance of \$SHELL
108    help                 Display this help text
109    init <repo>          Initialize a new repository
110    list                 List all repositories
111    list-tracked         List all files tracked by vcsh
112    list-tracked-by \\
113         <repo>          List files tracked by a repository
114    pull                 Pull from all vcsh remotes
115    push                 Push to vcsh remotes
116    rename <repo> \\
117           <newname>     Rename repository
118    run <repo> \\
119        <command>        Use this repository
120    status [<repo>]      Show statuses of all/one vcsh repositories
121    upgrade <repo>       Upgrade repository to currently recommended settings
122    version              Print version information
123    which <substring>    Find substring in name of any tracked file
124    write-gitignore \\
125    <repo>               Write .gitignore.d/<repo> via git ls-files
126
127    <repo> <git command> Shortcut to run git commands directly
128    <repo>               Shortcut to enter repository" >&2
129 }
130
131 debug() {
132         [ -n "$VCSH_DEBUG" ] && echo "$SELF: debug: $@"
133 }
134
135 verbose() {
136         if [ -n "$VCSH_DEBUG" ] || [ -n "$VCSH_VERBOSE" ]; then echo "$SELF: verbose: $@"; fi
137 }
138
139 error() {
140         echo "$SELF: error: $1" >&2
141 }
142
143 info() {
144         echo "$SELF: info: $1"
145 }
146
147 clone() {
148         hook pre-clone
149         init
150         git remote add origin "$GIT_REMOTE"
151         git config branch.master.remote origin
152         git config branch.master.merge  refs/heads/master
153         VCSH_CLONE_ERROR=$(git ls-remote origin master 2>&1)
154         if [ -n "$VCSH_CLONE_ERROR" ]; then
155                 rm -rf "$GIT_DIR"
156                 fatal "$VCSH_CLONE_ERROR" 1
157         fi
158         git fetch
159         hook pre-merge
160         git ls-tree -r --name-only origin/master | (while read object; do
161                 [ -e "$object" ] &&
162                         error "'$object' exists." &&
163                         VCSH_CONFLICT=1
164         done
165         [ x"$VCSH_CONFLICT" = x'1' ]) &&
166                 fatal "will stop after fetching and not try to merge!
167   Once this situation has been resolved, run 'vcsh $VCSH_REPO_NAME pull' to finish cloning." 17
168         git merge origin/master
169         hook post-merge
170         hook post-clone
171         retire
172         hook post-clone-retired
173 }
174
175 commit() {
176         hook pre-commit
177         for VCSH_REPO_NAME in $(list); do
178                 echo "$VCSH_REPO_NAME: "
179                 GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
180                 use
181                 git commit --untracked-files=no --quiet
182                 VCSH_COMMAND_RETURN_CODE=$?
183                 echo
184         done
185         hook post-commit
186 }
187
188 delete() {
189         cd "$VCSH_BASE" || fatal "could not enter '$VCSH_BASE'" 11
190         use
191         info "This operation WILL DESTROY DATA!"
192         files=$(git ls-files)
193         echo "These files will be deleted:
194
195 $files
196
197 AGAIN, THIS WILL DELETE YOUR DATA!
198 To continue, type 'Yes, do as I say'"
199         read answer
200         [ "x$answer" = 'xYes, do as I say' ] || exit 16
201         for file in $files; do
202                 rm -f $file || info "could not delete '$file', continuing with deletion"
203         done
204         rm -rf "$GIT_DIR" || error "could not delete '$GIT_DIR'"
205 }
206
207 enter() {
208         hook pre-enter
209         use
210         $SHELL
211         hook post-enter
212 }
213
214 git_dir_exists() {
215         [ -d "$GIT_DIR" ] || fatal "no repository found for '$VCSH_REPO_NAME'" 12
216 }
217
218 hook() {
219         for hook in "$VCSH_HOOK_D/$1"* "$VCSH_HOOK_D/$VCSH_REPO_NAME.$1"*; do
220                 [ -x "$hook" ] || continue
221                 verbose "executing '$hook'"
222                 "$hook"
223         done
224 }
225
226 init() {
227         hook pre-init
228         [ ! -e "$GIT_DIR" ] || fatal "'$GIT_DIR' exists" 10
229         mkdir -p "$VCSH_BASE" || fatal "could not create '$VCSH_BASE'" 50
230         cd "$VCSH_BASE" || fatal "could not enter '$VCSH_BASE'" 11
231         git init --shared=0600
232         upgrade
233         hook post-init
234 }
235
236 list() {
237         for repo in "$VCSH_REPO_D"/*.git; do
238                 [ -d "$repo" ] && [ -r "$repo" ] && echo "$(basename "$repo" .git)"
239         done
240 }
241
242 get_files() {
243         GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
244         git ls-files
245 }
246
247 list_tracked() {
248         for VCSH_REPO_NAME in $(list); do
249                 get_files
250         done | sed "s,^,$(printf '%s\n' "$VCSH_BASE/" | \
251             sed 's/[,\&]/\\&/g')," | sort -u
252 }
253
254 list_tracked_by() {
255         use
256         git ls-files | sed "s,^,$(printf '%s\n' "$VCSH_BASE/" | \
257             sed 's/[,\&]/\\&/g')," | sort -u
258 }
259
260 pull() {
261         hook pre-pull
262         for VCSH_REPO_NAME in $(list); do
263                 printf '%s: ' "$VCSH_REPO_NAME"
264                 GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
265                 use
266                 git pull
267                 VCSH_COMMAND_RETURN_CODE=$?
268                 echo
269         done
270         hook post-pull
271 }
272
273 push() {
274         hook pre-push
275         for VCSH_REPO_NAME in $(list); do
276                 printf '%s: ' "$VCSH_REPO_NAME"
277                 GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
278                 use
279                 git push
280                 VCSH_COMMAND_RETURN_CODE=$?
281                 echo
282         done
283         hook post-push
284 }
285
286 retire() {
287         unset VCSH_DIRECTORY
288 }
289
290 rename() {
291         git_dir_exists
292         [ -d "$GIT_DIR_NEW" ] && fatal "'$GIT_DIR_NEW' exists" 54
293         mv -f "$GIT_DIR" "$GIT_DIR_NEW" || fatal "Could not mv '$GIT_DIR' '$GIT_DIR_NEW'" 52
294
295         # Now that the repository has been renamed, we need to fix up its configuration
296         # Overwrite old name..
297         GIT_DIR=$GIT_DIR_NEW
298         VCSH_REPO_NAME=$VCSH_REPO_NAME_NEW
299         # ..and clobber all old configuration
300         upgrade
301 }
302
303 run() {
304         hook pre-run
305         use
306         "$@"
307         VCSH_COMMAND_RETURN_CODE=$?
308         hook post-run
309 }
310
311 status() {
312         if [ -n "$VCSH_REPO_NAME" ]; then
313                 GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
314                 use
315                 git status --short --untracked-files='no'
316                 VCSH_COMMAND_RETURN_CODE=$?
317         else
318                 for VCSH_REPO_NAME in $(list); do
319                         echo "$VCSH_REPO_NAME:"
320                         GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
321                         use
322                         git status --short --untracked-files='no'
323                         VCSH_COMMAND_RETURN_CODE=$?
324                         echo
325                 done
326         fi
327 }
328
329 upgrade() {
330         hook pre-upgrade
331         # fake-bare repositories are not bare, actually. Set this to false
332         # because otherwise Git complains "fatal: core.bare and core.worktree
333         # do not make sense"
334         git config core.bare false
335         # core.worktree may be absolute or relative to $GIT_DIR, depending on
336         # user preference
337         if [ ! "x$VCSH_WORKTREE" = 'xabsolute' ]; then
338                 git config core.worktree "$(cd "$GIT_DIR" && GIT_WORK_TREE=$VCSH_BASE git rev-parse --show-cdup)"
339         elif [ ! "x$VCSH_WORKTREE" = 'xrelative' ]; then
340                 git config core.worktree "$VCSH_BASE"
341         fi
342         [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && git config core.excludesfile ".gitignore.d/$VCSH_REPO_NAME"
343         [ ! "x$VCSH_GITATTRIBUTES" = 'xnone' ] && git config core.attributesfile ".gitattributes.d/$VCSH_REPO_NAME"
344         git config vcsh.vcsh 'true'
345         use
346         [ -e "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" ] && git add -f "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME"
347         [ -e "$VCSH_BASE/.gitattributes.d/$VCSH_REPO_NAME" ] && git add -f "$VCSH_BASE/.gitattributes.d/$VCSH_REPO_NAME"
348         hook post-upgrade
349 }
350
351 use() {
352         git_dir_exists
353         VCSH_DIRECTORY=$VCSH_REPO_NAME; export VCSH_DIRECTORY
354 }
355
356 which() {
357         [ -e "$VCSH_COMMAND_PARAMETER" ] || fatal "'$VCSH_COMMAND_PARAMETER' does not exist" 1
358         for VCSH_REPO_NAME in $(list); do
359                 for VCSH_FILE in $(get_files); do
360                         echo "$VCSH_FILE" | grep -q "$VCSH_COMMAND_PARAMETER" && echo "$VCSH_REPO_NAME: $VCSH_FILE"
361                 done
362         done | sort -u
363 }
364
365 write_gitignore() {
366         # Don't do anything if the user does not want to write gitignore
367         if [ "x$VCSH_GITIGNORE" = 'xnone' ]; then
368                 info "Not writing gitignore as '\$VCSH_GITIGNORE' is set to 'none'"
369                 exit
370         fi
371
372         use
373         cd "$VCSH_BASE" || fatal "could not enter '$VCSH_BASE'" 11
374         OLDIFS=$IFS
375         IFS=$(printf '\n\t')
376         gitignores=$(for file in $(git ls-files); do
377                 while true; do
378                         echo "$file"; new=${file%/*}
379                         [ x"$file" = x"$new" ] && break
380                         file=$new
381                 done;
382         done | sort -u)
383
384         # Contrary to GNU mktemp, mktemp on BSD/OSX requires a template for temp files
385         # Using a template makes GNU mktemp default to $PWD and not #TMPDIR for tempfile location
386         # To make every OS happy, set full path explicitly
387         tempfile=$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX") || fatal "could not create tempfile: '${tempfile}'" 51
388
389         echo '*' > "$tempfile" || fatal "could not write to '$tempfile'" 57
390         for gitignore in $gitignores; do
391                 echo "$gitignore" | sed 's@^@!/@' >> "$tempfile" || fatal "could not write to '$tempfile'" 57
392                 if [ "x$VCSH_GITIGNORE" = 'xrecursive' ] && [ -d "$gitignore" ]; then
393                         { echo "$gitignore/*" | sed 's@^@!/@' >> "$tempfile" || fatal "could not write to '$tempfile'" 57; }
394                 fi
395         done
396         IFS=$OLDIFS
397         if diff -N "$tempfile" "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" > /dev/null; then
398                 rm -f "$tempfile" || error "could not delete '$tempfile'"
399                 exit
400         fi
401         if [ -e "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" ]; then
402                 info "'$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME' differs from new data, moving it to '$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME.bak'"
403                 mv -f "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME.bak" ||
404                         fatal "could not move '$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME' to '$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME.bak'" 53
405         fi
406         mv -f "$tempfile" "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" ||
407                 fatal "could not move '$tempfile' to '$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME'" 53
408 }
409
410 debug $(git version)
411
412 if [ ! "x$VCSH_GITIGNORE" = 'xexact' ] && [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && [ ! "x$VCSH_GITIGNORE" = 'xrecursive' ]; then
413         fatal "'\$VCSH_GITIGNORE' must equal 'exact', 'none', or 'recursive'" 1
414 fi
415
416 VCSH_COMMAND=$1; export VCSH_COMMAND
417
418 case $VCSH_COMMAND in
419         clon|clo|cl) VCSH_COMMAND=clone;;
420         commi|comm|com|co) VCSH_COMMAND=commit;;
421         delet|dele|del|de) VCSH_COMMAND=delete;;
422         ente|ent|en) VCSH_COMMAND=enter;;
423         hel|he) VCSH_COMMAND=help;;
424         ini|in) VCSH_COMMAND=init;;
425         pul) VCSH_COMMAND=pull;;
426         pus) VCSH_COMMAND=push;;
427         renam|rena|ren|re) VCSH_COMMAND=rename;;
428         ru) VCSH_COMMAND=run;;
429         statu|stat|sta|st) VCSH_COMMAND=status;;
430         upgrad|upgra|upgr|up) VCSH_COMMAND=upgrade;;
431         versio|versi|vers|ver|ve) VCSH_COMMAND=version;;
432         which|whi|wh) VCSH_COMMAND=which;;
433         write|writ|wri|wr) VCSH_COMMAND=write-gitignore;;
434 esac    
435
436 if [ x"$VCSH_COMMAND" = x'clone' ]; then
437         [ -z "$2" ] && fatal "$VCSH_COMMAND: please specify a remote" 1
438         GIT_REMOTE="$2"
439         [ -n "$3" ] && VCSH_REPO_NAME=$3 || VCSH_REPO_NAME=$(basename "${GIT_REMOTE#*:}" .git)
440         [ -z "$VCSH_REPO_NAME" ] && fatal "$VCSH_COMMAND: could not determine repository name" 1
441         export VCSH_REPO_NAME
442         GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
443 elif [ "$VCSH_COMMAND" = 'version' ]; then
444         echo "$SELF $VERSION"
445         git version
446         exit
447 elif [ x"$VCSH_COMMAND" = x'which' ]; then
448         [ -z "$2" ] && fatal "$VCSH_COMMAND: please specify a filename" 1
449         [ -n "$3" ] && fatal "$VCSH_COMMAND: too many parameters" 1
450         VCSH_COMMAND_PARAMETER=$2; export VCSH_COMMAND_PARAMETER
451 elif [ x"$VCSH_COMMAND" = x'delete' ]           ||
452      [ x"$VCSH_COMMAND" = x'enter' ]            ||
453      [ x"$VCSH_COMMAND" = x'init' ]             ||
454      [ x"$VCSH_COMMAND" = x'list-tracked-by' ]  ||
455      [ x"$VCSH_COMMAND" = x'rename' ]           ||
456      [ x"$VCSH_COMMAND" = x'run' ]              ||
457      [ x"$VCSH_COMMAND" = x'upgrade' ]          ||
458      [ x"$VCSH_COMMAND" = x'write-gitignore' ]; then
459         [ -z "$2" ]                                     && fatal "$VCSH_COMMAND: please specify repository to work on" 1
460         [ x"$VCSH_COMMAND" = x'rename' ] && [ -z "$3" ] && fatal "$VCSH_COMMAND: please specify a target name" 1
461         [ x"$VCSH_COMMAND" = x'run'    ] && [ -z "$3" ] && fatal "$VCSH_COMMAND: please specify a command" 1
462         VCSH_REPO_NAME=$2; export VCSH_REPO_NAME
463         GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
464         [ x"$VCSH_COMMAND" = x'rename' ] && { VCSH_REPO_NAME_NEW=$3; export VCSH_REPO_NAME_NEW;
465                                               GIT_DIR_NEW=$VCSH_REPO_D/$VCSH_REPO_NAME_NEW.git; export GIT_DIR_NEW; }
466         [ x"$VCSH_COMMAND" = x'run' ]    && shift 2
467 elif [ x"$VCSH_COMMAND" = x'commit' ] ||
468      [ x"$VCSH_COMMAND" = x'list'   ] ||
469      [ x"$VCSH_COMMAND" = x'list-tracked' ] ||
470      [ x"$VCSH_COMMAND" = x'pull'   ] ||
471      [ x"$VCSH_COMMAND" = x'push'   ]; then
472         :
473 elif [ x"$VCSH_COMMAND" = x'status' ]; then
474         VCSH_REPO_NAME=$2; export VCSH_REPO_NAME
475 elif [ -n "$2" ]; then
476         VCSH_COMMAND='run'; export VCSH_COMMAND
477         VCSH_REPO_NAME=$1; export VCSH_REPO_NAME
478         GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
479         [ -d "$GIT_DIR" ] || { help; exit 1; }
480         shift 1
481         set -- "git" "$@"
482 elif [ -n "$VCSH_COMMAND" ]; then
483         VCSH_COMMAND='enter'; export VCSH_COMMAND
484         VCSH_REPO_NAME=$1; export VCSH_REPO_NAME
485         GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
486         [ -d "$GIT_DIR" ] || { help; exit 1; }
487 else
488         # $1 is empty, or 'help'
489         help && exit
490 fi
491
492 # Did we receive a directory instead of a name?
493 # Mangle the input to fit normal operation.
494 if echo "$VCSH_REPO_NAME" | grep -q '/'; then
495         GIT_DIR=$VCSH_REPO_NAME; export GIT_DIR
496         VCSH_REPO_NAME=$(basename "$VCSH_REPO_NAME" .git); export VCSH_REPO_NAME
497 fi
498
499 check_dir() {
500         check_directory="$1"
501         if [ ! -d "$check_directory" ]; then
502                 if [ -e "$check_directory" ]; then
503                         fatal "'$check_directory' exists but is not a directory" 13
504                 else
505                         verbose "attempting to create '$check_directory'"
506                         mkdir -p "$check_directory" || fatal "could not create '$check_directory'" 50
507                 fi
508         fi
509 }
510
511 check_dir "$VCSH_REPO_D"
512 [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && check_dir "$VCSH_BASE/.gitignore.d"
513 [ ! "x$VCSH_GITATTRIBUTES" = 'xnone' ] && check_dir "$VCSH_BASE/.gitattributes.d"
514
515 verbose "$VCSH_COMMAND begin"
516 VCSH_COMMAND=$(echo "$VCSH_COMMAND" | sed 's/-/_/g'); export VCSH_COMMAND
517
518 # source overlay functions
519 for overlay in "$VCSH_OVERLAY_D/$VCSH_COMMAND"* "$VCSH_OVERLAY_D/$VCSH_REPO_NAME.$VCSH_COMMAND"*; do
520         [ -r "$overlay" ] || continue
521         info "sourcing '$overlay'"
522         . "$overlay"
523 done
524
525 hook pre-command
526 $VCSH_COMMAND "$@"
527 hook post-command
528 verbose "$VCSH_COMMAND end, exiting"
529 exit $VCSH_COMMAND_RETURN_CODE