Check own name for command
[zit] / zit
1 #!/bin/bash
2
3 abort() {
4         echo $1
5         exit 1
6 }
7
8 me="$(basename $0)"
9 if test "$me" = zit ; then
10         cmd="$1"
11         shift
12 else
13         cmd="$me"
14 fi
15
16 USAGE="usage: zit COMMAND FILE [ARGS...]"
17
18 zit_help() {
19         cmd="$1"
20         shift
21         case "$cmd" in
22                 git)
23                 git help "$@"
24                 ;;
25                 list|tracked)
26                 echo "usage: zit list"
27                 echo "Show tracked files, with a one-letter prefix indicating their status:"
28                 echo "   H   cached"
29                 echo "   M   unmerged"
30                 echo "   R   removed/deleted"
31                 echo "   C   modified/changed"
32                 echo "   K   to be killed"
33                 echo "   ?   other"
34                 ;;
35                 import)
36                 echo "usage: zit import FILE"
37                 echo "Import history from an RCS-tracked file. Requires rcs-fast-export."
38                 ;;
39                 view)
40                 echo "usage: zit view FILE"
41                 echo "Browse FILE's history with gitk (if possible) or tig."
42                 ;;
43                 with)
44                 echo "usage: zit with FILE COMMAND..."
45                 echo "Run COMMAND after setting up the git environment for FILE."
46                 ;;
47                 *)
48                 echo $USAGE
49                 echo ""
50                 echo "Set up a git repository under .zit.FILE to track changes for FILE."
51                 echo "File must be a regular file and in the current directory."
52                 echo ""
53                 echo "Zit commands:"
54                 echo "   import Import RCS history for FILE"
55                 echo "   init           Synonym for track"
56                 echo "   list           Synonym for tracked"
57                 echo "   track  Start tracking changes to FILE"
58                 echo "   tracked        List tracked files in current directory"
59                 echo "   view           Browse FILE's history with gitk or tig"
60                 echo "   with           Run a command in FILE's context"
61                 echo ""
62                 echo "See 'zit help git' or 'git help' for git commands."
63                 ;;
64                 zig)
65                 echo "usage: zig FILE"
66                 echo "Browse FILE's history with tig."
67                 ;;
68                 zik)
69                 echo "usage: zik FILE"
70                 echo "Browse FILE's history with gitk."
71                 ;;
72         esac
73 }
74
75 ZIT_DIR=.zit
76
77 zit_setup() {
78         ZIT_FILE="$1"
79         test $ZIT_FILE || abort "Please specify a file"
80         test -f $ZIT_FILE || abort "No such file $ZIT_FILE"
81         test $ZIT_FILE = "`basename $ZIT_FILE`" || abort "Sorry, Zit only works on files in the current directory"
82
83         export GIT_WORK_TREE="`pwd`"
84
85         # first, check if a repo exists already, looking for
86         # .zit/file.git or .file.git, in that order
87         # if neither is found, and .zit exists, set the repo dir
88         # to .zit/file.git, otherwise set it to .file.git
89         GIT_DIR="$ZIT_DIR/$ZIT_FILE.git"
90         if ! test -d "$GIT_DIR"; then
91                 GIT_DIR=".$ZIT_FILE.git"
92                 if ! test -d "$GIT_DIR"; then
93                         test -d "$ZIT_DIR" && GIT_DIR="$ZIT_DIR/$ZIT_FILE.git"
94                 fi
95         fi
96         export GIT_DIR
97 }
98
99 # initialize the zitdir, without actually making the first commit
100 zitdir_init() {
101         zit_setup $1
102         test -e $GIT_DIR && abort "$GIT_DIR exists, is $ZIT_FILE tracked already?"
103         mkdir $GIT_DIR && echo "Initializing Zit repository in $GIT_DIR"
104         test -d $GIT_DIR || abort "Failed to create $GIT_DIR"
105         git init || abort "Failed to initialize Git repository in $GIT_DIR"
106         rm -rf $GIT_DIR/{hooks,info,branches,refs/tags,objects/pack,description}
107         if test -d "$ZIT_DIR"; then
108                 ZIT_EXCLUDE="$ZIT_DIR/exclude"
109         else
110                 ZIT_EXCLUDE="$GIT_DIR/exclude"
111         fi
112         if ! test -f "$ZIT_EXCLUDE"; then
113                 touch "$ZIT_EXCLUDE" || abort "Cannot create $ZIT_EXCLUDE file"
114                 echo "# Ignore patterns used by Zit repositories in the parent worktree." > "$ZIT_EXCLUDE"
115                 echo "# By default it's the single '*' glob, since we want to ignore all" >> "$ZIT_EXCLUDE"
116                 echo "# non-tracked files in the work-tree." >> "$ZIT_EXCLUDE"
117                 echo "# This file is autogenerated and there's usually no need to edit it." >> "$ZIT_EXCLUDE"
118                 echo "*" >> "$ZIT_EXCLUDE"
119         fi
120         git config core.excludesfile "$ZIT_EXCLUDE"
121 }
122
123 zit_init() {
124         if test "$1"; then
125                 zitdir_init "$1"
126                 git add -f $ZIT_FILE || abort "Failed to add $ZIT_FILE"
127                 git commit "$@" || abort "Failed to make first commit for $ZIT_FILE"
128         else
129                 if test -d "$ZIT_DIR"; then
130                         echo "$ZIT_DIR exists already"
131                         exit
132                 fi
133                 test -e $ZIT_DIR && abort "$ZIT_DIR exists but it's not a directory, cannot continue"
134                 mkdir $ZIT_DIR
135         fi
136 }
137
138 zit_list() {
139         export GIT_WORK_TREE="`pwd`"
140         GIT_DIR=""
141         for file in "$ZIT_DIR"/*.git .*.git; do
142                 if ! test -e $file; then
143                         continue
144                 fi
145                 export GIT_DIR="$file"
146                 file="${file#.}"
147                 file="${file#zit/}"
148                 file="${file%.git}"
149                 (git ls-files -m -d -t; git ls-files -t) | uniq -f 1
150         done;
151         # if $GIT_DIR is empty, no files were found
152         test "$GIT_DIR" || echo "(no files tracked by zit)"
153 }
154
155 # import an RCS-tracked file using rcs-fast-export, if found
156 zit_import() {
157         which rcs-fast-export || abort "rcs-fast-export not found, I can't import RCS-tracked files, sorry"
158         zitdir_init $1
159         # git-fast-import creates a pack file, so (re)build the objects/pack dir
160         mkdir -p $GIT_DIR/objects/pack
161         rcs-fast-export $1 | git-fast-import
162         # for some reason, rcs-fast-export | git-fast-import leaves the original
163         # file in 'deleted' state, a situation which is easily fixed by adding
164         # it back
165         git add -f $1
166 }
167
168 case "$cmd" in
169         "")
170         echo $USAGE
171         ;;
172         help|--help|-h|-?)
173         zit_help "$@"
174         ;;
175         init|track)
176         zit_init $1
177         ;;
178         list|tracked)
179         zit_list
180         ;;
181         import)
182         zit_import $1
183         ;;
184         view)
185         zit_setup $1
186         shift
187         if test -n "$DISPLAY" -a -n "$(which gitk)" ; then
188                 gitk "$@"
189         elif test -n "$(which tig)" ; then
190                 tig "$@"
191         else
192                 abort "Neither gitk or tig could be launched"
193         fi
194         ;;
195         with)
196         zit_setup $1
197         shift
198         "$@"
199         ;;
200         zig)
201         zit_setup $1
202         shift
203         tig "$@"
204         ;;
205         zik)
206         zit_setup $1
207         shift
208         gitk "$@"
209         ;;
210         # Most commands will work with the generic catch-all mechanism used
211         # below, but some of them require a more thorough analysis of the
212         # parameters to decide whether $ZIT_FILE should be put back into the
213         # parameter list or not. For example,
214         # $ zit commit somefile
215         # wouldn't do what one expects it to do, unless 'add' is run first,
216         # (except that
217         # $ zit add somefile
218         # wouldn't work either), however
219         # $ zit commit somefile -a
220         # would work correctly. So we handle some commands separately (for the
221         # moment just add and commit)
222         add|commit)
223         zit_setup $1
224         shift
225         git $cmd "$@" "$ZIT_FILE"
226         ;;
227         # the raw<command> method can be used to not replicate $ZIT_FILE in the
228         # parameter list
229         raw*)
230         zit_setup $1
231         shift
232         git ${cmd#raw} "$@"
233         ;;
234         *)
235         zit_setup $1
236         shift
237         git $cmd "$@"
238         ;;
239 esac