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

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