(no commit message)
[ikiwiki] / doc / todo / Attempt_to_extend_Mercurial_backend_support.mdwn
1 Using the Mercurial backend, the lack of `rcs_commit_staged` is noticed frequently. I couldn't find any tries to update `mercurial.pm`, so not letting lack of Mercurial AND Perl knowledge bring me down, I copy-pasted from `git.pm` to mimic its behaviour from a Mercurial perspective. I hope it can be a foundation for development by those more proficient in ikiwiki's inner workings. I have doubts that I personally will be able to revise it more, based on my Perl skills.
2
3 I've tested it briefly. `ikiwiki-calendar` and posting of comments now works with automatic commits, i.e. the `rcs_commit_staged` function works in those cases. Under my current setup, I don't know where else to expect it to work. I would be flabberghasted if there wasn't any problems with it, though.
4
5 Diff follows, for anyone to annotate. Code is also available at [my hg-repo](http://510x.se/hg/program/ikiwiki/file/e741fcfd800f/Plugin/mercurial.pm).
6
7         diff -r 20c61288d7bd Plugin/mercurial.pm
8         --- a/Plugin/mercurial.pm       Fri Jul 15 02:55:12 2011 +0200
9         +++ b/Plugin/mercurial.pm       Fri Jul 15 03:29:10 2011 +0200
10         @@ -7,6 +7,8 @@
11          use Encode;
12          use open qw{:utf8 :std};
13          
14         +my $hg_dir=undef;
15         +
16          sub import {
17                 hook(type => "checkconfig", id => "mercurial", call => \&checkconfig);
18                 hook(type => "getsetup", id => "mercurial", call => \&getsetup);
19
20 A corresponding variable is declared for git. It is unused as of yet for Mercurial, but when more advanced merge features become available for `mercurial.pm`, I think it will come into play.
21
22         @@ -69,6 +71,62 @@
23                         },
24          }
25          
26         +sub safe_hg (&@) {
27         +       # Start a child process safely without resorting to /bin/sh.
28         +       # Returns command output (in list content) or success state
29         +       # (in scalar context), or runs the specified data handler.
30         +
31         +       my ($error_handler, $data_handler, @cmdline) = @_;
32         +
33         +       my $pid = open my $OUT, "-|";
34         +
35         +       error("Cannot fork: $!") if !defined $pid;
36         +
37         +       if (!$pid) {
38         +               # In child.
39         +               # hg commands want to be in wc.
40         +               if (! defined $hg_dir) {
41         +                       chdir $config{srcdir}
42         +                           or error("cannot chdir to $config{srcdir}: $!");
43         +               }
44         +               else {
45         +                       chdir $hg_dir
46         +                           or error("cannot chdir to $hg_dir: $!");
47         +               }
48         +               exec @cmdline or error("Cannot exec '@cmdline': $!");
49         +       }
50         +       # In parent.
51         +
52         +       # hg output is probably utf-8 encoded, but may contain
53         +       # other encodings or invalidly encoded stuff. So do not rely
54         +       # on the normal utf-8 IO layer, decode it by hand.
55         +       binmode($OUT);
56         +
57         +       my @lines;
58         +       while (<$OUT>) {
59         +               $_=decode_utf8($_, 0);
60         +
61         +               chomp;
62         +
63         +               if (! defined $data_handler) {
64         +                       push @lines, $_;
65         +               }
66         +               else {
67         +                       last unless $data_handler->($_);
68         +               }
69         +       }
70         +
71         +       close $OUT;
72         +
73         +       $error_handler->("'@cmdline' failed: $!") if $? && $error_handler;
74         +
75         +       return wantarray ? @lines : ($? == 0);
76         +}
77         +# Convenient wrappers.
78         +sub run_or_die ($@) { safe_hg(\&error, undef, @_) }
79         +sub run_or_cry ($@) { safe_hg(sub { warn @_ }, undef, @_) }
80         +sub run_or_non ($@) { safe_hg(undef, undef, @_) }
81         +
82          sub mercurial_log ($) {
83                 my $out = shift;
84                 my @infos;
85         @@ -116,10 +174,7 @@
86          }
87          
88          sub rcs_update () {
89         -       my @cmdline = ("hg", "-q", "-R", "$config{srcdir}", "update");
90         -       if (system(@cmdline) != 0) {
91         -               warn "'@cmdline' failed: $!";
92         -       }
93         +       run_or_cry('hg', '-q', 'update');
94          }
95          
96          sub rcs_prepedit ($) {
97
98 With the `run_or_{die,cry,non}()` functions defined as in `git.pm`, some old Mercurial functions can be rewritten more compactly.
99
100         @@ -129,6 +184,14 @@
101          sub rcs_commit (@) {
102                 my %params=@_;
103          
104         +       return rcs_commit_helper(@_);
105         +}
106         +
107         +sub rcs_commit_helper (@) {
108         +       my %params=@_;
109         +
110         +       my %env=%ENV;
111         +
112                 my $user="Anonymous";
113                 if (defined $params{session}) {
114                         if (defined $params{session}->param("name")) {
115
116 Here comes the `rcs_commit{,_staged}` part. It is modeled on a `rcs_commit_helper` function, as in `git.pm`.
117
118 Some old `mercurial.pm` logic concerning commiter name is kept instead of transplanting the more elaborate logic from `git.pm`. Maybe it is better to "steal" that as well.
119
120         @@ -143,43 +206,45 @@
121                         $params{message} = "no message given";
122                 }
123          
124         -       my @cmdline = ("hg", "-q", "-R", $config{srcdir}, "commit", 
125         -                      "-m", IkiWiki::possibly_foolish_untaint($params{message}),
126         -                      "-u", IkiWiki::possibly_foolish_untaint($user));
127         -       if (system(@cmdline) != 0) {
128         -               warn "'@cmdline' failed: $!";
129         +       $params{message} = IkiWiki::possibly_foolish_untaint($params{message});
130         +
131         +       my @opts;
132         +       
133         +       if (exists $params{file}) {
134         +               push @opts, '--', $params{file};
135                 }
136         -
137         +       # hg commit returns non-zero if nothing really changed.
138         +       # So we should ignore its exit status (hence run_or_non).
139         +       run_or_non('hg', 'commit', '-m', $params{message}, '-q', @opts);
140         +       
141         +       %ENV=%env;
142                 return undef; # success
143          }
144          
145          sub rcs_commit_staged (@) {
146                 # Commits all staged changes. Changes can be staged using rcs_add,
147                 # rcs_remove, and rcs_rename.
148         -       my %params=@_;
149         -       
150         -       error("rcs_commit_staged not implemented for mercurial"); # TODO
151         +       return rcs_commit_helper(@_);
152          }
153          
154          sub rcs_add ($) {
155                 my ($file) = @_;
156          
157         -       my @cmdline = ("hg", "-q", "-R", "$config{srcdir}", "add", "$config{srcdir}/$file");
158         -       if (system(@cmdline) != 0) {
159         -               warn "'@cmdline' failed: $!";
160         -       }
161         +       run_or_cry('hg', 'add', $file);
162          }
163          
164          sub rcs_remove ($) {
165         +       # Remove file from archive.
166         +
167                 my ($file) = @_;
168          
169         -       error("rcs_remove not implemented for mercurial"); # TODO
170         +       run_or_cry('hg', 'remove', '-f', $file);
171          }
172          
173          sub rcs_rename ($$) {
174                 my ($src, $dest) = @_;
175          
176         -       error("rcs_rename not implemented for mercurial"); # TODO
177         +       run_or_cry('hg', 'rename', '-f', $src, $dest);
178          }
179          
180          sub rcs_recentchanges ($) {