]> 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:

Get rid exit(3)
[code/vcsh.git] / vcsh
1 #!/bin/sh
2
3 [ -n "$VCSH_DEBUG" ]      && set -x
4 [ -z "$XDG_CONFIG_HOME" ] && XDG_CONFIG_HOME="$HOME/.config"
5 [ -z "$VCSH_BASE" ]       && VCSH_BASE="$XDG_CONFIG_HOME/vcsh/repo.d"
6
7 SELF=$(basename $0)
8
9
10 help() {
11         echo "usage: $SELF <args>
12
13    clone <remote> \\
14          [<repo>]       Clone from an existing repository
15    help                 Display this help text
16    delete               Delete an existing repository
17    init <repo>          Initialize a new repository
18    list                 List all repos
19    run <repo> \\
20        <command>        Use this repository
21
22    seed-gitignore \\
23    <repo>               Seed .gitignore.d/<repo> from git ls-files
24
25    <repo> <git command> Special command that allows you to run git commands
26                         directly without having to type so much ;)" >&2
27 #   use <repo>     Use this repository
28 #
29 #   exit              Exit vcsh mode" >&2
30 }
31
32 debug() {
33         [ -n "$VCSH_DEBUG" ] && echo "$SELF: debug: $@"
34 }
35
36 verbose() {
37         if [ -n "$VCSH_DEBUG" ] || [ -n "$VCSH_VERBOSE" ]; then echo "$SELF: verbose: $@"; fi
38 }
39
40 use() {
41         verbose "use() begin"
42         if [ ! -d "$GIT_DIR" ]; then
43                 echo E: no repository found for "$VCSH_REPO_NAME" >&2
44                 return 1
45         fi
46         export GIT_DIR
47         export GIT_WORK_TREE="$(git config --get core.worktree)"
48         export VCSH_DIRECTORY="$VCSH_REPO_NAME"
49         verbose "use() end"
50 }
51
52 init() {
53         verbose "init() begin"
54         [ -e "$GIT_DIR" ] &&
55                 echo "$SELF: fatal: $GIT_DIR exists" &&
56                 return 21
57         export GIT_WORK_TREE="$HOME"
58         mkdir -p "$GIT_WORK_TREE"
59         cd "$GIT_WORK_TREE" ||
60                 (echo "$SELF: fatal: could not enter $GIT_WORK_TREE" &&
61                  return 1) || return $?
62         cd "$GIT_WORK_TREE"
63         git init
64         git config core.worktree     "$GIT_WORK_TREE"
65         git config core.excludesfile ".gitignore.d/$VCSH_REPO_NAME"
66         touch   "$HOME/.gitignore.d/$VCSH_REPO_NAME"
67         git add "$HOME/.gitignore.d/$VCSH_REPO_NAME"
68         verbose "init() end"
69 }
70
71 leave() {
72         unset GIT_DIR
73         unset GIT_WORK_TREE
74         unset VCSH_DIRECTORY
75 }
76
77
78 if [ "$1" = 'clone' ]; then
79         GIT_REMOTE="$2"
80         export GIT_REMOTE
81         VCSH_REPO_NAME="$3"
82         [ -z "$VCSH_REPO_NAME" ] && VCSH_REPO_NAME=$(basename "$GIT_REMOTE" .git)
83         export VCSH_REPO_NAME
84         export GIT_DIR="$VCSH_BASE/$VCSH_REPO_NAME.git"
85 elif [ "$1" = 'delete' ] ||
86      [ "$1" = 'init' ] ||
87      [ "$1" = 'run' ] ||
88      [ "$1" = 'seed-gitignore' ]; then
89         [ -z $2 ] && echo "$SELF $1: error: please specify repository to work on" && return 1
90         export VCSH_COMMAND="$1"
91         export VCSH_REPO_NAME="$2"
92         export GIT_DIR="$VCSH_BASE/$VCSH_REPO_NAME.git"
93         shift 2
94         export VCSH_EXTERNAL_COMMAND="$*"
95         if [ "$VCSH_COMMAND" = 'run' ]; then
96                 [ -z "$VCSH_EXTERNAL_COMMAND" ] && echo "$SELF $1 $2: error: please specify a command" && return 1
97         fi
98 elif [ "$1" = 'help' ] ||
99      [ "$1" = 'list' ]; then
100         export VCSH_COMMAND="$1"
101 else
102         [ -z $1 ] && help && return 0
103         export VCSH_COMMAND='run'
104         export VCSH_REPO_NAME="$1"
105         export GIT_DIR="$VCSH_BASE/$VCSH_REPO_NAME.git"
106         [ -d $GIT_DIR ] || (help && return 1) || return 0
107         shift 1
108         export VCSH_EXTERNAL_COMMAND="git $*"
109 fi
110
111
112 for check_directory in "$VCSH_BASE" "$HOME/.gitignore.d"
113 do
114         if [ ! -d "$check_directory" ]; then
115                 if [ -e "$check_directory" ]; then
116                         echo "$SELF: error: $check_directory exists but is not a directory" >&2
117                         return 2
118                 else
119                         echo "$SELF: info: attempting to create $check_directory"
120                         mkdir -p "$check_directory" || (echo "$SELF: error: could not create $check_directory" >&2; return 2) || return $?
121                 fi
122         fi
123 done
124
125
126 if [ "$VCSH_COMMAND" = 'clone' ]; then
127         verbose "clone begin"
128         init
129         git remote add origin "$GIT_REMOTE"
130         git config branch.master.remote origin
131         git config branch.master.merge  refs/heads/master
132         git fetch
133         for object in $(git ls-tree -r origin/master | awk '{print $4}'); do
134                 [ -e "$object" ] &&
135                         echo "$SELF: error: $object exists." &&
136                         VCSH_CONFLICT=1;
137         done
138         [ "$VCSH_CONFLICT" = '1' ] &&
139                 echo "$SELF: fatal: will stop after fetching and not try to merge!\n" &&
140                 echo "  Once this situation has been resolved, run 'vcsh run <foo> git pull' to finish cloning.\n" &&
141                 return 3
142         git merge origin/master
143 #       use || return $?
144         verbose "clone end"
145
146 #elif [ "$VCSH_COMMAND" = 'help' ] || [ "$#" -eq 0 ]; then
147 elif [ "$VCSH_COMMAND" = 'help' ]; then
148         help
149
150 elif [ "$VCSH_COMMAND" = 'delete' ]; then
151         verbose "delete begin"
152         old_dir="$PWD"
153         cd "$HOME"
154         use || return $?
155         echo "$SELF: info: This operation WILL DETROY DATA!"
156         files=$(git ls-files)
157         echo "These files will be deleted:
158
159 $files
160
161 AGAIN, THIS WILL DELETE YOUR DATA!
162 To continue, type \"Yes, do as I say\""
163         read answer
164         [ "x$answer" = "xYes, do as I say" ] || return 1
165         for file in $files; do
166                 rm -f $file || echo "$SELF: info: could not delete '$file', continuing with deletion"
167         done
168         rm -rf "$GIT_DIR" || echo "$SELF: info: could not delete '$GIT_DIR'"
169         cd "$old_dir"
170         verbose "delete end"
171
172 #elif [ "$VCSH_COMMAND" = 'exit' ]; then
173 #       verbose "exit begin"
174 #       if [ -n "$ZSH_VERSION" ] && [ "$VCSH_NO_IGNORE_EOF" = '1' ]; then
175 #               unset VCSH_NO_IGNORE_EOF
176 #               setopt NO_IGNORE_EOF
177 #       fi
178 #       leave
179 #       [ -n "$ZSH_VERSION" ] && [ "$USER" = richih ] && buildPS1
180 #       verbose "exit end"
181 #       exit 0
182
183 elif [ "$VCSH_COMMAND" = 'init' ]; then
184         verbose "init begin"
185         init
186 #       use || return $?
187         verbose "init end"
188
189 elif [ "$VCSH_COMMAND" = 'list' ]; then
190         verbose "list begin"
191         for i in "$VCSH_BASE"/*.git; do
192                 echo $(basename "$i" .git)
193         done
194         verbose "list end"
195
196 elif [ "$VCSH_COMMAND" = 'run' ]; then
197         verbose "run begin"
198         use || return $?
199         $VCSH_EXTERNAL_COMMAND
200         leave
201         verbose "run end"
202
203 elif [ "$VCSH_COMMAND" = 'seed-gitignore' ]; then
204         verbose "seed-gitignore begin"
205         use || return $?
206         # Switching directory as this has to be executed from $HOME to be of any use.
207         # Going back into old directory at the end in case `vcsh use` is reactivated.
208         old_dir="$PWD"
209         cd "$HOME"
210         git config core.excludesfile ".gitignore.d/$VCSH_REPO_NAME"
211         gitignores=$(for file in $(git ls-files); do
212                 while true; do
213                         echo $file; new="${file%/*}"
214                         [ "$file" = "$new" ] && break
215                         file="$new"
216                 done;
217         done | sort -u)
218         tempfile=$(mktemp) ||
219                 (echo "$SELF: fatal: could not create tempfile" && return 1) || return $?
220         echo '*' > "$tempfile"
221         for gitignore in $gitignores; do
222                 echo "$gitignore" | sed 's/^/!/' >> "$tempfile"
223                 [ -d "$gitignore" ] && echo "$gitignore/*" | sed 's/^/!/'>> "$tempfile"
224         done
225         diff -N "$tempfile" "$HOME/.gitignore.d/$VCSH_REPO_NAME" > /dev/null &&
226                 rm -f "$tempfile" &&
227                 return
228         if [ -e "$HOME/.gitignore.d/$VCSH_REPO_NAME" ]; then
229                 echo "$SELF: info: $HOME/.gitignore.d/$VCSH_REPO_NAME differs from new data, moving it to $HOME/.gitignore.d/$VCSH_REPO_NAME.bak"
230                 mv -f "$HOME/.gitignore.d/$VCSH_REPO_NAME" "$HOME/.gitignore.d/$VCSH_REPO_NAME.bak" ||
231                         (echo "$SELF: fatal: could not move $HOME/.gitignore.d/$VCSH_REPO_NAME to $HOME/.gitignore.d/$VCSH_REPO_NAME.bak" &&
232                          return 1) || return $?
233         fi
234         mv -f "$tempfile" "$HOME/.gitignore.d/$VCSH_REPO_NAME" ||
235                 (echo "$SELF: fatal: could not move $tempfile to $HOME/.gitignore.d/$VCSH_REPO_NAME" && return 1) || return $?
236         cd "$old_dir"
237         verbose "seed-gitignore end"
238
239 #elif [ "$VCSH_COMMAND" = 'use' ]; then
240 #       verbose "use begin"
241 #       if [ -n "$ZSH_VERSION" ]; then
242 #               if [ -o NO_IGNORE_EOF ]; then
243 #                       export VCSH_NO_IGNORE_EOF=1
244 #                       setopt IGNORE_EOF
245 #               fi
246 #               vcsh_exit() {
247 #                       vcsh exit;
248 #                       zle reset-prompt;
249 #               }
250 #               zle -N vcsh_exit
251 #               bindkey '^d' 'vcsh_exit'
252 #       fi
253 #       use || return $?
254 #       [ -n "$ZSH_VERSION" ] && [ "$USER" = richih ] && buildPS1
255 #       verbose "use end"
256
257 else
258         verbose "defaulting to calling help()"
259         help
260         echo "$SELF: fatal: You should never reach this code. File a bug, please."
261         return 99
262
263 fi