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

0edfe2fc1ec818e9c3048c3e59344999f380b038
[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/$REPO_NAME"
66         touch   "$HOME/.gitignore.d/$REPO_NAME"
67         git add "$HOME/.gitignore.d/$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' ]; then
88         [ -z $2 ] && echo "$SELF $1: error: please specify repository to work on" && return 1
89         [ -z $3 ] && echo "$SELF $1 $2: error: please specify a command" && 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 elif [ "$1" = 'seed-gitignore' ]; then
96         export VCSH_COMMAND="$1"
97         export VCSH_REPO_NAME="$2"
98         export GIT_DIR="$VCSH_BASE/$VCSH_REPO_NAME.git"
99 elif [ "$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                         exit 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; exit 2)
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                 exit 3
143         git merge origin/master
144 #       use || return 1
145         verbose "clone end"
146
147 elif [ "$VCSH_COMMAND" = 'help' ] || [ "$#" -eq 0 ]; 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/$2" > /dev/null &&
225                 rm -f "$tempfile" &&
226                 return
227         if [ -e "$HOME/.gitignore.d/$2" ]; then
228                 echo "$SELF: info: $HOME/.gitignore.d/$2 differs from new data, moving it to $HOME/.gitignore.d/$2.bak"
229                 mv -f "$HOME/.gitignore.d/$2" "$HOME/.gitignore.d/$2.bak" ||
230                         (echo "$SELF: fatal: could not move $HOME/.gitignore.d/$2 to $HOME/.gitignore.d/$2.bak" && exit 1)
231         fi
232         mv -f "$tempfile" "$HOME/.gitignore.d/$2" ||
233                 (echo "$SELF: fatal: could not move $tempfile to $HOME/.gitignore.d/$2" && 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