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