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