return if there's no mode change and no content change
[metze/old/samba4-sync/samba4-sync.scripts/.git] / samba4-sync.pl
1 #!/usr/bin/perl
2 #
3
4 use strict;
5
6 use FindBin qw($RealBin);
7
8 use lib "$RealBin";
9 use util;
10
11 use POSIX;
12 use Data::Dumper;
13 use File::stat;
14 use Carp;
15
16 my $SVN_REPO="file:///home/svn/samba";
17 my $SVN_BRANCH="branches/SAMBA_4_0/";
18 my $SVN_PATH="$SVN_REPO/$SVN_BRANCH";
19 my $PATCH_PATH="$ENV{HOME}/svnmirror/samba4-sync.patches";
20 my $AUTHORS_PATH="$ENV{HOME}/svnmirror/samba4-sync.scripts/svn-authors";
21 my $GIT_PATH="$ENV{HOME}/svnmirror/samba4-sync.git";
22
23 my $LAST_SVN_REV_PATH = "$PATCH_PATH/latest.rev";
24
25 $ENV{LANG} = "en_US.UTF-8";
26
27 sub get_last_svn_rev()
28 {
29         my $v = util::FileLoad($LAST_SVN_REV_PATH);
30         my $def = 25600;
31
32         $v = $def if $v eq "";
33         $v *= 1;
34         $v = $def if $v == 0;
35
36         print "get_last_svn_rev: $v\n";
37         return $v;
38 }
39
40 sub set_last_svn_rev($)
41 {
42         my ($r) = @_;
43         my $v = $r->{svnrev};
44         util::FileSave($LAST_SVN_REV_PATH, $v);
45         print "set_last_svn_rev: $v\n";
46 }
47
48 sub get_cur_svn_rev()
49 {
50         my $def = 25650;
51         my $infocmd = "svn info --non-interactive $SVN_PATH";
52         my $info = `$infocmd` || confess "$infocmd: failed";
53
54         my $v = $info;
55         if ($v =~ /\nRevision: (\d+)/) {
56                 $v = $1;
57         }
58         $v = $def if $v eq $info;
59         $v *= 1;
60         $v = $def if $v == 0;
61
62         print "get_cur_svn_rev: $v\n";
63         return $v;
64 }
65
66 sub load_authors()
67 {
68         my $f = util::FileLoad($AUTHORS_PATH);
69         my @lines = split("\n", $f);
70         my $authors = undef;
71
72         foreach my $l (@lines) {
73                 if ($l =~ /^([\w\-]+) = (.*)$/) {
74                         $authors->{$1} = $2;
75                         next;
76                 }
77
78                 confess "line: $l: invalid";
79         }
80
81         return $authors;
82 }
83
84 sub fix_author($)
85 {
86         my ($in) = @_;
87         my $authors = undef;
88
89         $authors = load_authors() unless defined($authors);
90
91         my $out = $authors->{$in};
92
93         confess "author: $in:not found" unless defined($out);
94
95         return $out;
96 }
97
98 sub fix_log($)
99 {
100         my ($in) = @_;
101         my $delim = "------------------------------------------------------------------------";
102         my @tmp1 = split($delim, $in);
103         my @tmp2 = split("\n", $tmp1[0]);
104
105         while (1){
106                 my $l = $tmp2[0];
107                 last unless $l =~ /^[ \t]*$/;
108                 shift(@tmp2);
109         }
110
111         my $out = join("\n", @tmp2);
112
113         return $out;
114 }
115
116 sub fix_date($)
117 {
118         my ($in) = @_;
119
120         return $in;
121 }
122
123 sub is_dir($)
124 {
125         my ($in) = @_;
126         return 1 if $in eq ".";
127         return 1 if $in =~ /\/$/;
128         return 0;
129 }
130
131 sub construct_chunk($)
132 {
133         my ($chunk) = @_;
134         return undef unless defined($chunk->{content}->{lines});
135         my @content =  @{$chunk->{content}->{lines}};
136
137         my @hdr = splice(@content, 0, 5);
138
139         confess "hdr[0]" unless $hdr[0] =~ /^Index: /;
140         confess "hdr[1]" unless $hdr[1] =~ /^[=]+$/;
141         confess "hdr[2]" unless $hdr[2] =~ /^--- /;
142         confess "hdr[3]" unless $hdr[3] =~ /^\+\+\+ /;
143         confess "hdr[4]" unless $hdr[4] =~ /^\@@ /;
144
145         $hdr[0] =~ s/^Index: ([^\n]+)$/diff --git a\/$1 b\/$1/g;
146         $hdr[2] =~ s/--- ([^\(]+)\(revision 0\)/--- \/dev\/null/g;
147         $hdr[2] =~ s/--- ([^\(]+)\([^\n]+/--- a\/$1/g;
148         $hdr[3] =~ s/\+\+\+ ([^\(]+)\(revision 0\)/+++ \/dev\/null/g;
149         $hdr[3] =~ s/\+\+\+ ([^\(]+)\([^\n]+/+++ b\/$1/g;
150
151         if ($hdr[4] =~ /\@\@ -\d+,\d+ \+0,0 \@\@/) {
152                 $hdr[3] = "+++ /dev/null";
153         }
154
155         my $oldsha1 = "1234567";
156         my $newsha1 = "7654321";
157         my $mode = "";
158
159         if (grep(/svn:executable/, @{$chunk->{properties}->{lines}})) {
160                 print join("\n", @{$chunk->{properties}->{lines}});
161                 $mode = "new file mode 100755\n";
162         }
163
164         if ($hdr[2] =~ /\/dev\/null/) {
165                 $oldsha1 = "0000000";
166                 $mode = "new file mode 100644\n" if $mode eq "";
167         }
168
169         if ($hdr[3] =~ /\/dev\/null/) {
170                 $newsha1 = "0000000";
171                 $mode = "deleted file mode 100644\n";
172         }
173
174         $hdr[1] = $mode."index $oldsha1..$newsha1";
175
176         splice(@content, 0, 0, @hdr);
177
178         my $out = join("\n", @content);
179
180         return $out;
181 }
182
183 sub strip_svn_properties($)
184 {
185         my ($in) = @_;
186         my @in = split("\n", $in);
187
188         my $chunk = undef;
189         my $chunks = undef;
190         foreach my $l (@in) {
191                 if ($l =~ /^Index: ([\w\.\_\-\/]+)/) {
192                         #print "content: $1\n";
193
194                         $chunk = undef;
195                         $chunk->{type} = "content";
196                         $chunk->{file} = $1;
197                         confess "$1 content exists" if defined($chunks->{$1}) and defined($chunks->{$1}->{content});
198                         $chunks->{$1}->{content} = $chunk;
199                 } elsif ($l =~ /^Property changes on: ([\w\.\_\-\/]+)/) {
200                         #print "properties(".is_dir($1)."): $1\n";
201
202                         $chunk = undef;
203                         $chunk->{type} = "property";
204                         $chunk->{path} = $1;
205                         confess "$1 properties exists" if defined($chunks->{$1}) and defined($chunks->{$1}->{properties});
206                         $chunks->{$1}->{properties} = $chunk unless is_dir($1);
207                 }
208
209                 push(@{$chunk->{lines}}, $l);
210         }
211
212         #print Data::Dumper::Dumper($chunks);
213
214         my @out = ();
215         foreach $chunk (values %{$chunks}) {
216                 #print Data::Dumper::Dumper($chunk);
217                 my $v = construct_chunk($chunk);
218                 push(@out, $v) if defined($v);
219         }
220
221         my $out = join("\n", @out);
222         return undef if $out eq "";
223
224         return $out;
225 }
226
227 sub fix_diff($)
228 {
229         my ($in) = @_;
230         my $out = $in;
231
232         confess("binary diff") if ($in =~ /Cannot display: file marked as a binary type/);
233         return undef unless ($in =~ /^Index: [\w\.\_\-\/]+/);
234
235         $out = strip_svn_properties($in);
236
237         return $out;
238 }
239
240 sub get_new_svn_revs($$)
241 {
242         my ($lastrev, $currev) = @_;
243         my $nextrev = $lastrev + 1;
244         return undef if $nextrev > $currev;
245         my $logcmd = "svn log --non-interactive -r $nextrev:$currev $SVN_PATH";
246         my $log = `$logcmd` || confess "$logcmd: failed";
247         my $revs = undef;
248
249         while($log =~ /\nr(\d+) \| (\w+) \| ([^\|]+) \|.*?line(s?)\n(.*)$/s) {
250                 $log = $5;
251                 $revs->{$1}->{svnrev} = $1;
252                 $revs->{$1}->{author} = fix_author($2);
253                 $revs->{$1}->{log}    = fix_log($5);
254                 $revs->{$1}->{date}   = fix_date($3);
255         }
256
257         return $revs;
258 }
259
260 sub get_svn_diff($)
261 {
262         my ($rev) = @_;
263         my $orev = $rev - 1;
264         my $diffcmd = "svn diff --non-interactive -r $orev:$rev $SVN_PATH";
265         my $diff = `$diffcmd` || confess "$diffcmd: failed";
266
267         return fix_diff($diff); 
268 }
269
270 sub generate_patch($$)
271 {
272         my ($r, $diff) = @_;
273         my @log = split("\n", $r->{log});
274         my $subject = shift @log;
275         my $body = join("\n", @log);
276         my $p = "";
277
278         $p .= "From 123456780abcdef\n";
279         $p .= "From: $r->{author}\n";
280         $p .= "Date: $r->{date}\n";
281         $p .= "Subject: [PATCH] r$r->{svnrev}: $subject\n";
282         $p .= "$body\n";
283         $p .= "\n";
284         $p .= $diff;
285         $p .= "\n---\nsvn-sync script\n\n";
286
287         return $p;
288 }
289
290 sub apply_patch($)
291 {
292         my ($r) = @_;
293         my $applycmd = "cd $GIT_PATH && git am --whitespace=nowarn --binary $r->{patch_path}";
294
295         my $apply = `$applycmd` || confess "$applycmd: failed";
296 }
297
298 sub store_patches($)
299 {
300         my ($revs) = @_;
301
302         foreach my $rev (sort keys %{$revs}) {
303                 my $r = $revs->{$rev};
304                 $r->{patch_path} = "$PATCH_PATH/$r->{svnrev}.patch";
305                 my $diff = get_svn_diff($r->{svnrev});
306                 next unless defined($diff);
307                 my $patch = generate_patch($r, $diff);
308                 #print $patch;
309                 util::FileSave($r->{patch_path}, $patch);
310                 apply_patch($r);
311                 set_last_svn_rev($r);
312         }
313 }
314
315 sub apply_patches($)
316 {
317         my ($revs) = @_;
318
319         
320 }
321
322 my $lastrev = get_last_svn_rev();
323 my $currev = get_cur_svn_rev();
324
325 my $revs = get_new_svn_revs($lastrev, $currev);
326 store_patches($revs);
327 #print Data::Dumper::Dumper($revs);
328