2 # stodo commits the changes made to a subversion backed todo db, and
3 # is released under the terms of the GPL version 2, or any later
4 # version, at your option. See the file README and COPYING for more
5 # information. Copyright 2004 by Don Armstrong <don@donarmstrong.com>.
18 stodo - devtodo event script which attempts to commit the changes made to a .todo file after a change
25 --simulate, -s simulate: don't actually do anything
26 --quiet, -q don't output to STDOUT
27 --debug, -d debugging level (Default 0)
28 --help, -h display this help
29 --man, -m display manual
35 =item B<--simulate, -s>
37 Simulation mode, don't actually commit anything.
41 Don't output anything to STDOUT
45 Debug verbosity. (Default 0)
49 Display brief useage information.
57 =head1 ENVIRONMENTAL VARIABLES
63 Todo databse location, set by devtodo
65 =item B<STODO_NO_COMMIT>
67 If set, stodo assumes that there is no network, and doesn't commit
86 use Data::Diff qw(Diff);
87 use File::Basename qw(dirname basename);
91 # XXX parse config file
93 my %options = (quiet => 0,
100 GetOptions(\%options,'debug|d+','help|h|?','man|m','quiet|q+','simulate|s+');
102 pod2usage() if $options{help} or not exists $ENV{TODODB};
103 pod2usage({verbose=>2}) if $options{man};
105 $DEBUG = $options{debug};
107 # Figure out what we're doing
108 my $tododb = $ENV{TODODB};
109 if ($tododb !~ m#^/#) {
110 $tododb = cwd().'/'.$tododb;
112 $tododb = full_readlink($tododb);
113 my $base_dir = dirname($tododb);
114 my $tododb_name = basename($tododb);
116 if (not -e "$base_dir/.svn") {
117 print "$base_dir/.svn doesn't exist, not commiting\n" unless $options{quiet};
121 if (not -e "$base_dir/.svn/text-base/${tododb_name}.svn-base") {
122 print "$base_dir/.svn/text-base/${tododb_name}.svn-base doesn't exist, not committing\n" unless $options{quiet};
126 # Make sure that we can ping the subversion server
127 my $svn_entries_fh = new IO::File "$base_dir/.svn/entries",'r'
128 or die "unable to open $base_dir/.svn/entries: $!";
131 while (<$svn_entries_fh>) {
132 next unless m#^(?:\s+url=\")?(http|svn\+ssh|file):///?([^\/]+)#;
134 last if $url_type eq 'file';
138 if ($ENV{STODO_NO_COMMIT}) {
139 print "Exiting because of STODO_NO_COMMIT env variable\n" unless $options{quiet};
142 if (not defined $svn_host and (not defined $url_type or $url_type ne 'file')) {
143 die "Was unable to find which host the svn repository is located on";
145 if ($url_type ne 'file') {
146 qx(ping -q -c 3 $svn_host >/dev/null 2>&1);
148 print "Unable to ping $svn_host\n" unless $options{quiet};
149 exit 0 unless $options{simulate};
154 # See what has changed
155 #my $svn_fh = new IO::Handle;
156 #open($svn_fh,'-|','svn','diff',$tododb) or die "Unable to execute svn diff $tododb; $!";
158 # next unless /^[-+]/;
161 my $todo_db_old = fix_content(XMLin("$base_dir/.svn/text-base/${tododb_name}.svn-base",KeyAttr=>'time')->{note});
162 my $todo_db_new = fix_content(XMLin($tododb,KeyAttr=>'time')->{note});
166 my $diff = Diff($todo_db_new,$todo_db_old);
169 #print Dumper($diff);
171 if (exists $diff->{uniq_a}) {
172 foreach (values %{$diff->{uniq_a}}) {
173 my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$_}{qw(content priority)};
174 push @commit_message,qq( * Added todo: [$priority] $content);
178 if (exists $diff->{uniq_b}) {
179 foreach (values %{$diff->{uniq_b}}) {
180 my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$_}{qw(content priority)};
181 push @commit_message,qq( * Deleted todo: [$priority] $content);
185 if (exists $diff->{diff}) {
186 # This entry has either been edited, completed, or uncompleted
187 foreach my $entry_key (keys %{$diff->{diff}}) {
188 my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$todo_db_new->{$entry_key}}{qw(content priority)};
189 my $diff_entry = $diff->{diff}{$entry_key};
190 if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_a}{done}) {
191 push @commit_message,qq( * Finished todo: [$priority] $content);
193 if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_b}{done}) {
194 push @commit_message,qq( * Marked as undone: [$priority] $content);
196 if (exists $diff_entry->{diff}) {
198 if (exists $diff_entry->{diff}{content}) {
199 push @what_changed,'content';
201 if (exists $diff_entry->{diff}{priority}) {
202 push @what_changed,'priority';
204 if (not @what_changed) {
205 @what_changed = keys %{$diff_entry->{diff}};
207 push @commit_message,qq( * Todo changed ).join(' and ',@what_changed).qq( [$priority] $content);
212 if (exists $diff->{same}) {
213 foreach my $entry_key (keys %{$diff->{same}}) {
214 my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$todo_db_new->{$entry_key}}{qw(content priority)};
215 my $diff_entry = $diff->{same}{$entry_key};
216 if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_a}{done}) {
217 push @commit_message,qq( * Finished todo: [$priority] $content);
219 if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_b}{done}) {
220 push @commit_message,qq( * Marked as undone: [$priority] $content);
222 if (exists $diff_entry->{diff}) {
224 if (exists $diff_entry->{diff}{content}) {
225 push @what_changed,'content';
227 if (exists $diff_entry->{diff}{priority}) {
228 push @what_changed,'priority';
230 if (not @what_changed) {
231 @what_changed = keys %{$diff_entry->{diff}};
233 push @commit_message,qq( * Todo changed ).join(' and ',@what_changed).qq( [$priority] $content);
239 if (not @commit_message) {
240 print "No commit message\n" if $DEBUG or $options{simulate};
244 my $commit_message = join('', map {qq($_\n)} @commit_message);
245 if ($options{simulate}) {
246 print "svn commit -F - $tododb <<EOF\n";
247 print $commit_message;
251 system('svn','commit','-m',$commit_message,$tododb);
255 # Commit the changes with an appropriate commit message
260 if (not ref(${[values %{$s}]}[0]) eq 'HASH') {
261 $s = {$s->{time} => $s};
263 foreach my $entry (values %{$s}) {
264 if (exists $entry->{content}) {
265 $entry->{content} =~ s/^\s*|\s*$//g;
267 if (exists $entry->{comment}) {
268 $entry->{comment} =~ s/^\s*|\s*$//g;
278 $file = readlink $file;