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