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

bcd416c37724b868157a465d90a9a757dd15bef9
[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                  exit 20) || exit 20
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                         exit 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; exit 2)
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                 exit 3
142         git merge origin/master
143 #       use || return 1
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 1
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" ] || exit
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 1
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 1
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 1
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 | sed 's/^/!/')
218         tempfile=$(mktemp) ||
219                 (echo "$SELF: fatal: could not create tempfile" && exit 1) 
220         echo '*' > "$tempfile"
221         for gitignore in $gitignores; do
222                 echo "$gitignore" >> "$tempfile"
223         done
224         diff -N "$tempfile" "$HOME/.gitignore.d/$VCSH_REPO_NAME" > /dev/null &&
225                 rm -f "$tempfile" &&
226                 return
227         if [ -e "$HOME/.gitignore.d/$VCSH_REPO_NAME" ]; then
228                 echo "$SELF: info: $HOME/.gitignore.d/$VCSH_REPO_NAME differs from new data, moving it to $HOME/.gitignore.d/$VCSH_REPO_NAME.bak"
229                 mv -f "$HOME/.gitignore.d/$VCSH_REPO_NAME" "$HOME/.gitignore.d/$VCSH_REPO_NAME.bak" ||
230                         (echo "$SELF: fatal: could not move $HOME/.gitignore.d/$VCSH_REPO_NAME to $HOME/.gitignore.d/$VCSH_REPO_NAME.bak" && exit 1)
231         fi
232         mv -f "$tempfile" "$HOME/.gitignore.d/$VCSH_REPO_NAME" ||
233                 (echo "$SELF: fatal: could not move $tempfile to $HOME/.gitignore.d/$VCSH_REPO_NAME" && exit 1)
234         cd "$old_dir"
235         verbose "seed-gitignore end"
236
237 #elif [ "$VCSH_COMMAND" = 'use' ]; then
238 #       verbose "use begin"
239 #       if [ -n "$ZSH_VERSION" ]; then
240 #               if [ -o NO_IGNORE_EOF ]; then
241 #                       export VCSH_NO_IGNORE_EOF=1
242 #                       setopt IGNORE_EOF
243 #               fi
244 #               vcsh_exit() {
245 #                       vcsh exit;
246 #                       zle reset-prompt;
247 #               }
248 #               zle -N vcsh_exit
249 #               bindkey '^d' 'vcsh_exit'
250 #       fi
251 #       use || return 1
252 #       [ -n "$ZSH_VERSION" ] && [ "$USER" = richih ] && buildPS1
253 #       verbose "use end"
254
255 else
256         verbose "defaulting to calling help()"
257         help
258         echo "$SELF: fatal: You should never reach this code. File a bug, please."
259         exit 99
260
261 fi