add code based on SVN and SVK perl bindings to get
[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 @ret = ();
67
68         my $nroot = $self->{repos}->fs->revision_root($last_rev);
69         my $hist = $nroot->node_history($branch);
70
71         while ($hist = $hist->prev(0)) {
72                 my $crev = $hist->location();
73                 last unless defined($crev);
74                 last unless $crev > 0;
75                 last unless $crev > $start_rev;
76
77                 unshift(@ret, $crev);
78         }
79
80         return @ret;
81 }
82
83 sub svn2git_author($$)
84 {
85         my ($self, $in) = @_;
86
87         my $out = $self->{authors}->{$in};
88
89         confess "author: $in:not found" unless defined($out);
90
91         return $out;
92 }
93
94 sub svn2git_date($$)
95 {
96         my ($self, $in) = @_;
97
98
99         my $tmp = $in;
100         my $out = undef;
101
102         if ($tmp =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\./) {
103                 my $year = $1;
104                 my $month = $2-1;
105                 my $day = $3;
106                 my $hour = $4;
107                 my $min = $5;
108                 my $sec = $6;
109
110                 use Time::Local;
111                 use Time::gmtime;
112                 my $time = timegm($sec, $min, $hour, $day, $month, $year);
113                 $out = gmctime($time);
114         }
115
116         confess "time: $in: invalid" unless defined($out);
117
118         return $out;
119 }
120
121 sub svn2git_log($$)
122 {
123         my ($self, $in) = @_;
124
125         my $out = $in;
126
127         return $out;
128 }
129
130 sub get_git_patch($$$)
131 {
132         my ($self, $branch, $rev) = @_;
133         my $orev = $rev-1;
134         my $nrev = $rev;
135         my $oroot = $self->{repos}->fs->revision_root($orev);
136         my $nroot = $self->{repos}->fs->revision_root($nrev);
137
138         my $p = undef;
139
140         $p->{svn_author} = $self->{repos}->fs->revision_prop($nrev, "svn:author");
141         $p->{svn_date} = $self->{repos}->fs->revision_prop($nrev, "svn:date");
142         $p->{svn_log} = $self->{repos}->fs->revision_prop($nrev, "svn:log");
143         $p->{svn_rev} = $rev;
144
145         $p->{git_diff} = "";
146         my $editor = SVN2GitEditor->new(
147                 cb_basecontent => sub {
148                         my ($path, $pool) = @_;
149                         my $base = $oroot->file_contents("$branch/$path", $pool);
150                         return $base;
151                 },
152                 cb_baseprop => sub {
153                         my ($path, $pname, $pool) = @_;
154                         my $prop = $oroot->node_prop("$branch/$path", $pname, $pool);
155                         return $prop;
156                 },
157                 oldrepos => $self->{repos},
158                 oldroot => $oroot,
159                 oldrev => $orev,
160                 newrev => $nrev,
161                 branch => $branch,
162                 llabel => "revision $orev",
163                 rlabel => "revision $nrev",
164                 external => undef,
165                 output => \$p->{git_diff});
166
167         SVN::Repos::dir_delta($oroot,
168                               $branch, '',
169                               $nroot,
170                               $branch,
171                               $editor,
172                               undef, 1, 1, 1, 1);
173
174         $p->{git_author} = $self->svn2git_author($p->{svn_author});
175         $p->{git_date} = $self->svn2git_date($p->{svn_date});
176         $p->{git_log} = $self->svn2git_log($p->{svn_log});
177
178         my @log = split("\n", $p->{git_log});
179         $p->{git_log_subject} = shift @log;
180         $p->{git_log_body} = join("\n", @log);
181
182         if ($p->{git_diff} eq "") {
183                 $p->{git_patch} = undef;
184                 return $p;
185         }
186
187         $p->{git_patch} = "";
188         $p->{git_patch} .= "From 123456789abcdef\n";
189         $p->{git_patch} .= "From: $p->{git_author}\n";
190         $p->{git_patch} .= "Date: $p->{git_date}\n";
191         $p->{git_patch} .= "Subject: [PATCH] r$p->{svn_rev}: $p->{git_log_subject}\n";
192         $p->{git_patch} .= "$p->{git_log_body}\n";
193         $p->{git_patch} .= "\n";
194         $p->{git_patch} .= $p->{git_diff};
195         $p->{git_patch} .= "\n---\nsvn-sync script\n\n";
196
197         return $p;
198 }
199
200 1;