return if there's no mode change and no content change
[metze/old/samba4-sync/samba4-sync.scripts/.git] / SVN2GitPatch.pm
1 #!/usr/bin/perl
2 #
3
4 use strict;
5
6 use util;
7
8 use Time::Local;
9 use Time::gmtime;
10
11 use SVN::Core;
12 use SVN::Repos;
13 use SVN::Fs;
14 use SVN::Delta;
15 use SVN::Ra;
16 use SVK::Editor::Diff;
17 use SVN2GitEditor;
18
19 package SVN2GitPatch;
20
21 sub confess($)
22 {
23         # TODO use confess from 'use Carp'
24         my ($str) = @_;
25         die($str);
26 }
27
28 sub new($$;$) {
29         my ($this, $repo_path, $authors_file) = @_;
30
31         my $self = undef;
32
33         $self->{repos} = SVN::Repos::open($repo_path);
34         $self->{authors_file} = $authors_file;
35         $self->{authors} = undef;
36
37         if (defined($self->{authors_file})) {
38                 my $f = util::FileLoad($self->{authors_file});
39                 my @lines = split("\n", $f);
40
41                 foreach my $l (@lines) {
42                         if ($l =~ /^([\w\-]+) = (.*)$/) {
43                                 $self->{authors}->{$1} = $2;
44                                 next;
45                         }
46
47                         confess "line: $l: invalid";
48                 }
49         }
50
51         bless $self;
52         return $self;
53 }
54
55 sub get_last_svn_rev($$)
56 {
57         my ($self, $branch) = @_;
58
59         return $self->{repos}->fs->youngest_rev();
60 }
61
62 sub get_missing_svn_revs($$$)
63 {
64         my ($self, $branch, $start_rev) = @_;
65         my $last_rev = $self->get_last_svn_rev($branch);
66         my $r = undef;
67
68         my $nroot = $self->{repos}->fs->revision_root($last_rev);
69         my $hist = $nroot->node_history($branch);
70
71         while ($hist) {
72                 my ($cpath, $crev) = $hist->location();
73                 last unless defined($crev);
74                 last unless $crev > 0;
75                 last unless $crev > $start_rev;
76
77                 $r->{$crev}->{rev} = $crev;
78                 $r->{$crev}->{branch} = $cpath;
79
80                 $hist = $hist->prev(1);
81         }
82
83         return $r;
84 }
85
86 sub svn2git_author($$)
87 {
88         my ($self, $in) = @_;
89
90         my $out = $self->{authors}->{$in};
91
92         confess "author: $in:not found" unless defined($out);
93
94         return $out;
95 }
96
97 sub svn2git_date($$)
98 {
99         my ($self, $in) = @_;
100
101
102         my $tmp = $in;
103         my $out = undef;
104
105         if ($tmp =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\./) {
106                 my $year = $1;
107                 my $month = $2-1;
108                 my $day = $3;
109                 my $hour = $4;
110                 my $min = $5;
111                 my $sec = $6;
112
113                 use Time::Local;
114                 use Time::gmtime;
115                 my $time = timegm($sec, $min, $hour, $day, $month, $year);
116                 $out = gmctime($time);
117         }
118
119         confess "time: $in: invalid" unless defined($out);
120
121         return $out;
122 }
123
124 sub svn2git_log($$)
125 {
126         my ($self, $in) = @_;
127
128         my @tmp = split("\n", $in);
129
130         while (1){
131                 my $l = $tmp[0];
132                 last unless $l =~ /^[ \t]*$/;
133                 shift(@tmp);
134         }
135
136         my $out = join("\n", @tmp);
137
138         return $out;
139 }
140
141 sub get_git_patch($$$)
142 {
143         my ($self, $branch, $rev) = @_;
144         my $orev = $rev-1;
145         my $nrev = $rev;
146         my $oroot = $self->{repos}->fs->revision_root($orev);
147         my $nroot = $self->{repos}->fs->revision_root($nrev);
148
149         my $nbranch = $branch;
150         my $obranch = $nroot->copied_from($nbranch);
151         $obranch = $nbranch unless defined($obranch);
152
153         my $p = undef;
154
155         $p->{svn_author} = $self->{repos}->fs->revision_prop($nrev, "svn:author");
156         $p->{svn_date} = $self->{repos}->fs->revision_prop($nrev, "svn:date");
157         $p->{svn_log} = $self->{repos}->fs->revision_prop($nrev, "svn:log");
158         $p->{svn_rev} = $rev;
159
160         $p->{git_diff} = "";
161         my $editor = SVN2GitEditor->new(
162                 cb_basecontent => sub {
163                         my ($path, $pool) = @_;
164                         my $base = $oroot->file_contents("$obranch/$path", $pool);
165                         return $base;
166                 },
167                 cb_baseprop => sub {
168                         my ($path, $pname, $pool) = @_;
169                         my $prop = $oroot->node_prop("$obranch/$path", $pname, $pool);
170                         return $prop;
171                 },
172                 repos => $self->{repos},
173                 oldroot => $oroot,
174                 oldrev => $orev,
175                 oldbranch => $obranch,
176                 newroot => $nroot,
177                 newrev => $nrev,
178                 newbranch => $nbranch,
179                 llabel => "revision $orev",
180                 rlabel => "revision $nrev",
181                 external => undef,
182                 output => \$p->{git_diff});
183
184         SVN::Repos::dir_delta($oroot,
185                               $obranch, '',
186                               $nroot,
187                               $nbranch,
188                               $editor,
189                               undef, 1, 1, 1, 1);
190
191         $p->{git_author} = $self->svn2git_author($p->{svn_author});
192         $p->{git_date} = $self->svn2git_date($p->{svn_date});
193         $p->{git_log} = $self->svn2git_log($p->{svn_log});
194
195         my @log = split("\n", $p->{git_log});
196         $p->{git_log_subject} = shift @log;
197         $p->{git_log_body} = join("\n", @log);
198
199         if ($p->{git_diff} eq "") {
200                 $p->{git_patch} = undef;
201                 return $p;
202         }
203
204         $p->{git_patch} = "";
205         $p->{git_patch} .= "From 123456789abcdef\n";
206         $p->{git_patch} .= "From: $p->{git_author}\n";
207         $p->{git_patch} .= "Date: $p->{git_date}\n";
208         $p->{git_patch} .= "Subject: [PATCH] r$p->{svn_rev}: $p->{git_log_subject}\n";
209         $p->{git_patch} .= "$p->{git_log_body}\n";
210         $p->{git_patch} .= "\n";
211         $p->{git_patch} .= $p->{git_diff};
212         $p->{git_patch} .= "\n---\nsvn-sync script\n\n";
213
214         return $p;
215 }
216
217 1;