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