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

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