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

Make seed-gitignore more robust and only update the ignore if needed
[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 2
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"
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
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 #       vcsh use $REPO_NAME
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 #       vcsh use "$REPO_NAME"
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"
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         files=$(git ls-files)
189         gitignores=$(for file in $(git ls-files); do
190                 while true; do
191                         echo $file; new="${file%/*}"
192                         [ "$file" = "$new" ] && break
193                         file="$new"
194                 done;
195         done | sort -u | sed 's/^/!/')
196         tempfile=$(mktemp) ||
197                 (echo "$SELF: fatal: could not create tempfile" && exit 1) 
198         echo '*' > "$tempfile"
199         for gitignore in $gitignores; do
200                 echo "$gitignore" >> "$tempfile"
201         done
202         diff -N "$tempfile" "$HOME/.gitignore.d/$2" > /dev/null &&
203                 rm -f "$tempfile" &&
204                 return
205         if [ -e "$HOME/.gitignore.d/$2" ]; then
206                 echo "$SELF: info: $HOME/.gitignore.d/$2 differs from new data, moving it to $HOME/.gitignore.d/$2.bak"
207                 mv -f "$HOME/.gitignore.d/$2" "$HOME/.gitignore.d/$2.bak" ||
208                         (echo "$SELF: fatal: could not move $HOME/.gitignore.d/$2 to $HOME/.gitignore.d/$2.bak" && exit 1)
209         fi
210         mv -f "$tempfile" "$HOME/.gitignore.d/$2" ||
211                 (echo "$SELF: fatal: could not move $tempfile to $HOME/.gitignore.d/$2" && exit 1)
212         cd "$old_dir"
213         verbose "seed-gitignore end"
214
215 else
216         verbose "defaulting to calling help()"
217         help
218         exit 3
219
220 fi
221