]> 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 remote-tracking branch 'Dieter_be/master'
[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 help() {
10         echo "usage: $SELF <args>
11
12    clone <remote> \\
13          [<repo>]       Clone from an existing repository
14    help                 Display this help text
15    delete               Delete an existing repository
16    enter                Enter repository; spawn new $SHELL
17    init <repo>          Initialize a new repository
18    list                 List all repositories
19    rename <repo> \\
20           <newname>     Rename repository
21    run <repo> \\
22        <command>        Use this repository
23
24    seed-gitignore \\
25    <repo>               Seed .gitignore.d/<repo> from git ls-files
26    setup                Set up repository with recommended settings
27
28    <repo> <git command> Special command that allows you to run git commands
29                         directly without having to type so much ;)" >&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 fatal() {
41         echo "$SELF: fatal: $1" >&2
42         exit $2
43 }
44
45 error() {
46         echo "$SELF: error: $1" >&2
47 }
48
49 info() {
50         echo "$SELF: info: $1"
51 }
52
53 clone() {
54         init
55         git remote add origin "$GIT_REMOTE"
56         git config branch.master.remote origin
57         git config branch.master.merge  refs/heads/master
58         git fetch
59         for object in $(git ls-tree -r origin/master | awk '{print $4}'); do
60                 [ -e "$object" ] &&
61                         error "'$object' exists." &&
62                         VCSH_CONFLICT=1;
63         done
64         [ "$VCSH_CONFLICT" = '1' ] &&
65                 fatal "will stop after fetching and not try to merge!
66   Once this situation has been resolved, run 'vcsh run $VCSH_REPO_NAME git pull' to finish cloning.\n" 17
67         git merge origin/master
68 }
69
70 delete() {
71         cd "$HOME" || fatal "could not enter '$HOME'" 11
72         use
73         info "This operation WILL DETROY DATA!"
74         files=$(git ls-files)
75         echo "These files will be deleted:
76
77 $files
78
79 AGAIN, THIS WILL DELETE YOUR DATA!
80 To continue, type \"Yes, do as I say\""
81         read answer
82         [ "x$answer" = "xYes, do as I say" ] || exit 16
83         for file in $files; do
84                 rm -f $file || info "could not delete '$file', continuing with deletion"
85         done
86         rmdir "$GIT_DIR" || error "could not delete '$GIT_DIR'"
87 }
88
89 enter() {
90         use
91         $SHELL
92 }
93
94 git_dir_exists() {
95         [ -d "$GIT_DIR" ] || fatal "no repository found for '$VCSH_REPO_NAME'" 12
96 }
97
98 init() {
99         [ ! -e "$GIT_DIR" ] || fatal "'$GIT_DIR' exists" 10
100         export GIT_WORK_TREE="$HOME"
101         mkdir -p "$GIT_WORK_TREE" || fatal "could not create '$GIT_WORK_TREE'" 50
102         cd "$GIT_WORK_TREE" || fatal "could not enter '$GIT_WORK_TREE'" 11
103         git init
104         setup
105 }
106
107 list() {
108         for i in "$VCSH_BASE"/*.git; do
109                 echo $(basename "$i" .git)
110         done
111 }
112
113 rename() {
114         git_dir_exists
115         [ -d "$GIT_DIR_NEW" ] && fatal "'$GIT_DIR_NEW' exists" 54
116         mv -f "$GIT_DIR" "$GIT_DIR_NEW" || fatal "Could not mv '$GIT_DIR' '$GIT_DIR_NEW'" 52
117
118 }
119
120 run() {
121         use
122         $VCSH_EXTERNAL_COMMAND
123 }
124
125 seed_gitignore() {
126         use
127         cd "$HOME" || fatal "could not enter '$HOME'" 11
128         gitignores=$(for file in $(git ls-files); do
129                 while true; do
130                         echo $file; new="${file%/*}"
131                         [ "$file" = "$new" ] && break
132                         file="$new"
133                 done;
134         done | sort -u)
135         tempfile=$(mktemp) || fatal "could not create tempfile" 51
136         echo '*' > "$tempfile" || fatal "could not write to '$tempfile'" 57
137         for gitignore in $gitignores; do
138                 echo "$gitignore" | sed 's/^/!/' >> "$tempfile" || fatal "could not write to '$tempfile'" 57
139                 [ -d "$gitignore" ] && echo "$gitignore/*" | sed 's/^/!/'>> "$tempfile" || fatal "could not write to '$tempfile'" 57
140         done
141         if [ diff -N "$tempfile" "$HOME/.gitignore.d/$VCSH_REPO_NAME" > /dev/null ]; then
142                 rm -f "$tempfile" || error "could not delete '$tempfile'"
143                 exit
144         fi
145         if [ -e "$HOME/.gitignore.d/$VCSH_REPO_NAME" ]; then
146                 info "'$HOME/.gitignore.d/$VCSH_REPO_NAME' differs from new data, moving it to '$HOME/.gitignore.d/$VCSH_REPO_NAME.bak'"
147                 mv -f "$HOME/.gitignore.d/$VCSH_REPO_NAME" "$HOME/.gitignore.d/$VCSH_REPO_NAME.bak" ||
148                         fatal "could not move '$HOME/.gitignore.d/$VCSH_REPO_NAME' to '$HOME/.gitignore.d/$VCSH_REPO_NAME.bak'" 53
149         fi
150         mv -f "$tempfile" "$HOME/.gitignore.d/$VCSH_REPO_NAME" ||
151                 fatal "could not move '$tempfile' to '$HOME/.gitignore.d/$VCSH_REPO_NAME'" 53
152 }
153
154 setup() {
155         use
156         git config core.worktree     "$GIT_WORK_TREE"
157         git config core.excludesfile ".gitignore.d/$VCSH_REPO_NAME"
158         git config vcsh.vcsh         'true'
159         touch   "$HOME/.gitignore.d/$VCSH_REPO_NAME" || error "could not touch '$HOME/.gitignore.d/$VCSH_REPO_NAME'"
160         git add "$HOME/.gitignore.d/$VCSH_REPO_NAME"
161 }
162
163 use() {
164         git_dir_exists
165         export GIT_WORK_TREE="$(git config --get core.worktree)"
166         export VCSH_DIRECTORY="$VCSH_REPO_NAME"
167 }
168
169 if [ "$1" = 'clone' ]; then
170         [ -z $2 ] && fatal "$1: please specify a remote" 1
171         export VCSH_COMMAND="$1"
172         GIT_REMOTE="$2"
173         [ -n "$3" ] && VCSH_REPO_NAME="$3" || VCSH_REPO_NAME=$(basename "$GIT_REMOTE" .git)
174         export VCSH_REPO_NAME
175         export GIT_DIR="$VCSH_BASE/$VCSH_REPO_NAME.git"
176 elif [ "$1" = 'delete' ] ||
177      [ "$1" = 'enter' ] ||
178      [ "$1" = 'init' ] ||
179      [ "$1" = 'rename' ] ||
180      [ "$1" = 'run' ] ||
181      [ "$1" = 'seed-gitignore' ] ||
182      [ "$1" = 'setup' ]; then
183         [ -z $2 ]                      && fatal "$1: please specify repository to work on" 1
184         [ "$1" = 'rename' -a -z "$3" ] && fatal "$1: please specify a target name" 1
185         [ "$1" = 'run' -a -z "$3" ]    && fatal "$1: please specify a command" 1
186         export VCSH_COMMAND="$1"
187         export VCSH_REPO_NAME="$2"
188         export GIT_DIR="$VCSH_BASE/$VCSH_REPO_NAME.git"
189         [ "$VCSH_COMMAND" = 'rename' ]         && export GIT_DIR_NEW="$VCSH_BASE/$3.git"
190         [ "$VCSH_COMMAND" = 'run' ] && shift 2 && export VCSH_EXTERNAL_COMMAND="$@"
191         [ "$VCSH_COMMAND" = 'seed-gitignore' ] && export VCSH_COMMAND='seed_gitignore'
192 elif [ "$1" = 'list' ]; then
193         export VCSH_COMMAND="$1"
194 elif [ -n "$1" ]; then
195         export VCSH_COMMAND=run
196         export VCSH_REPO_NAME="$1"
197         export GIT_DIR="$VCSH_BASE/$VCSH_REPO_NAME.git"
198         [ -d $GIT_DIR ] || { help; exit 1; }
199         shift 1
200         export VCSH_EXTERNAL_COMMAND="git $*"
201 else
202         # $1 is empty, or 'help'
203         help && exit
204 fi
205
206 for check_directory in "$VCSH_BASE" "$HOME/.gitignore.d"
207 do
208         if [ ! -d "$check_directory" ]; then
209                 if [ -e "$check_directory" ]; then
210                         fatal "'$check_directory' exists but is not a directory" 13
211                 else
212                         info "attempting to create '$check_directory'"
213                         mkdir -p "$check_directory" || fatal "could not create '$check_directory'" 50
214                 fi
215         fi
216 done
217
218 verbose "$VCSH_COMMAND begin"
219 $VCSH_COMMAND
220 verbose "$VCSH_COMMAND end, exiting"