Extract Git::SVN::Log from git-svn.
Straight cut & paste. Also noticed Git::SVN::Ra wasn't in the compile test. It is now. Signed-off-by: Eric Wong <normalperson@yhbt.net>
Michael G. Schwern committed
Jul 26, 2012 at 17:26 UTC
b74fda1c9b332b1216cc4d02ec256d6e350d5524
4 files changed
+401
-396
git-svn.perl
+1
-394
@@ -11,6 +11,7 @@ $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
11
$VERSION = '@@GIT_VERSION@@';
12
13
use Git::SVN;
14
+use Git::SVN::Log;
15
use Git::SVN::Utils qw(fatal can_compress);
16
17
# From which subdir have we been invoked?
@@ -2039,400 +2040,6 @@ sub gc_directory {
2040
}
2041
2042
2042
-package Git::SVN::Log;
2043
-use strict;
2044
-use warnings;
2045
-use Git::SVN::Utils qw(fatal);
2046
-use Git qw(command command_oneline command_output_pipe command_close_pipe);
2047
-use POSIX qw/strftime/;
2048
-use constant commit_log_separator => ('-' x 72) . "\n";
2049
-use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline
2050
- %rusers $show_commit $incremental/;
2051
-
2052
-# Option set in git-svn
2053
-our $_git_format;
2054
-
2055
-sub cmt_showable {
2056
- my ($c) = @_;
2057
- return 1 if defined $c->{r};
2058
-
2059
- # big commit message got truncated by the 16k pretty buffer in rev-list
2060
- if ($c->{l} && $c->{l}->[-1] eq "...\n" &&
2061
- $c->{a_raw} =~ /\@([a-f\d\-]+)>$/) {
2062
- @{$c->{l}} = ();
2063
- my @log = command(qw/cat-file commit/, $c->{c});
2064
-
2065
- # shift off the headers
2066
- shift @log while ($log[0] ne '');
2067
- shift @log;
2068
-
2069
- # TODO: make $c->{l} not have a trailing newline in the future
2070
- @{$c->{l}} = map { "$_\n" } grep !/^git-svn-id: /, @log;
2071
-
2072
- (undef, $c->{r}, undef) = ::extract_metadata(
2073
- (grep(/^git-svn-id: /, @log))[-1]);
2074
- }
2075
- return defined $c->{r};
2076
-}
2077
-
2078
-sub log_use_color {
2079
- return $color || Git->repository->get_colorbool('color.diff');
2080
-}
2081
-
2082
-sub git_svn_log_cmd {
2083
- my ($r_min, $r_max, @args) = @_;
2084
- my $head = 'HEAD';
2085
- my (@files, @log_opts);
2086
- foreach my $x (@args) {
2087
- if ($x eq '--' || @files) {
2088
- push @files, $x;
2089
- } else {
2090
- if (::verify_ref("$x^0")) {
2091
- $head = $x;
2092
- } else {
2093
- push @log_opts, $x;
2094
- }
2095
- }
2096
- }
2097
-
2098
- my ($url, $rev, $uuid, $gs) = ::working_head_info($head);
2099
-
2100
- require Git::SVN;
2101
- $gs ||= Git::SVN->_new;
2102
- my @cmd = (qw/log --abbrev-commit --pretty=raw --default/,
2103
- $gs->refname);
2104
- push @cmd, '-r' unless $non_recursive;
2105
- push @cmd, qw/--raw --name-status/ if $verbose;
2106
- push @cmd, '--color' if log_use_color();
2107
- push @cmd, @log_opts;
2108
- if (defined $r_max && $r_max == $r_min) {
2109
- push @cmd, '--max-count=1';
2110
- if (my $c = $gs->rev_map_get($r_max)) {
2111
- push @cmd, $c;
2112
- }
2113
- } elsif (defined $r_max) {
2114
- if ($r_max < $r_min) {
2115
- ($r_min, $r_max) = ($r_max, $r_min);
2116
- }
2117
- my (undef, $c_max) = $gs->find_rev_before($r_max, 1, $r_min);
2118
- my (undef, $c_min) = $gs->find_rev_after($r_min, 1, $r_max);
2119
- # If there are no commits in the range, both $c_max and $c_min
2120
- # will be undefined. If there is at least 1 commit in the
2121
- # range, both will be defined.
2122
- return () if !defined $c_min || !defined $c_max;
2123
- if ($c_min eq $c_max) {
2124
- push @cmd, '--max-count=1', $c_min;
2125
- } else {
2126
- push @cmd, '--boundary', "$c_min..$c_max";
2127
- }
2128
- }
2129
- return (@cmd, @files);
2130
-}
2131
-
2132
-# adapted from pager.c
2133
-sub config_pager {
2134
- if (! -t *STDOUT) {
2135
- $ENV{GIT_PAGER_IN_USE} = 'false';
2136
- $pager = undef;
2137
- return;
2138
- }
2139
- chomp($pager = command_oneline(qw(var GIT_PAGER)));
2140
- if ($pager eq 'cat') {
2141
- $pager = undef;
2142
- }
2143
- $ENV{GIT_PAGER_IN_USE} = defined($pager);
2144
-}
2145
-
2146
-sub run_pager {
2147
- return unless defined $pager;
2148
- pipe my ($rfd, $wfd) or return;
2149
- defined(my $pid = fork) or fatal "Can't fork: $!";
2150
- if (!$pid) {
2151
- open STDOUT, '>&', $wfd or
2152
- fatal "Can't redirect to stdout: $!";
2153
- return;
2154
- }
2155
- open STDIN, '<&', $rfd or fatal "Can't redirect stdin: $!";
2156
- $ENV{LESS} ||= 'FRSX';
2157
- exec $pager or fatal "Can't run pager: $! ($pager)";
2158
-}
2159
-
2160
-sub format_svn_date {
2161
- my $t = shift || time;
2162
- require Git::SVN;
2163
- my $gmoff = Git::SVN::get_tz($t);
2164
- return strftime("%Y-%m-%d %H:%M:%S $gmoff (%a, %d %b %Y)", localtime($t));
2165
-}
2166
-
2167
-sub parse_git_date {
2168
- my ($t, $tz) = @_;
2169
- # Date::Parse isn't in the standard Perl distro :(
2170
- if ($tz =~ s/^\+//) {
2171
- $t += tz_to_s_offset($tz);
2172
- } elsif ($tz =~ s/^\-//) {
2173
- $t -= tz_to_s_offset($tz);
2174
- }
2175
- return $t;
2176
-}
2177
-
2178
-sub set_local_timezone {
2179
- if (defined $TZ) {
2180
- $ENV{TZ} = $TZ;
2181
- } else {
2182
- delete $ENV{TZ};
2183
- }
2184
-}
2185
-
2186
-sub tz_to_s_offset {
2187
- my ($tz) = @_;
2188
- $tz =~ s/(\d\d)$//;
2189
- return ($1 * 60) + ($tz * 3600);
2190
-}
2191
-
2192
-sub get_author_info {
2193
- my ($dest, $author, $t, $tz) = @_;
2194
- $author =~ s/(?:^\s*|\s*$)//g;
2195
- $dest->{a_raw} = $author;
2196
- my $au;
2197
- if ($::_authors) {
2198
- $au = $rusers{$author} || undef;
2199
- }
2200
- if (!$au) {
2201
- ($au) = ($author =~ /<([^>]+)\@[^>]+>$/);
2202
- }
2203
- $dest->{t} = $t;
2204
- $dest->{tz} = $tz;
2205
- $dest->{a} = $au;
2206
- $dest->{t_utc} = parse_git_date($t, $tz);
2207
-}
2208
-
2209
-sub process_commit {
2210
- my ($c, $r_min, $r_max, $defer) = @_;
2211
- if (defined $r_min && defined $r_max) {
2212
- if ($r_min == $c->{r} && $r_min == $r_max) {
2213
- show_commit($c);
2214
- return 0;
2215
- }
2216
- return 1 if $r_min == $r_max;
2217
- if ($r_min < $r_max) {
2218
- # we need to reverse the print order
2219
- return 0 if (defined $limit && --$limit < 0);
2220
- push @$defer, $c;
2221
- return 1;
2222
- }
2223
- if ($r_min != $r_max) {
2224
- return 1 if ($r_min < $c->{r});
2225
- return 1 if ($r_max > $c->{r});
2226
- }
2227
- }
2228
- return 0 if (defined $limit && --$limit < 0);
2229
- show_commit($c);
2230
- return 1;
2231
-}
2232
-
2233
-my $l_fmt;
2234
-sub show_commit {
2235
- my $c = shift;
2236
- if ($oneline) {
2237
- my $x = "\n";
2238
- if (my $l = $c->{l}) {
2239
- while ($l->[0] =~ /^\s*$/) { shift @$l }
2240
- $x = $l->[0];
2241
- }
2242
- $l_fmt ||= 'A' . length($c->{r});
2243
- print 'r',pack($l_fmt, $c->{r}),' | ';
2244
- print "$c->{c} | " if $show_commit;
2245
- print $x;
2246
- } else {
2247
- show_commit_normal($c);
2248
- }
2249
-}
2250
-
2251
-sub show_commit_changed_paths {
2252
- my ($c) = @_;
2253
- return unless $c->{changed};
2254
- print "Changed paths:\n", @{$c->{changed}};
2255
-}
2256
-
2257
-sub show_commit_normal {
2258
- my ($c) = @_;
2259
- print commit_log_separator, "r$c->{r} | ";
2260
- print "$c->{c} | " if $show_commit;
2261
- print "$c->{a} | ", format_svn_date($c->{t_utc}), ' | ';
2262
- my $nr_line = 0;
2263
-
2264
- if (my $l = $c->{l}) {
2265
- while ($l->[$#$l] eq "\n" && $#$l > 0
2266
- && $l->[($#$l - 1)] eq "\n") {
2267
- pop @$l;
2268
- }
2269
- $nr_line = scalar @$l;
2270
- if (!$nr_line) {
2271
- print "1 line\n\n\n";
2272
- } else {
2273
- if ($nr_line == 1) {
2274
- $nr_line = '1 line';
2275
- } else {
2276
- $nr_line .= ' lines';
2277
- }
2278
- print $nr_line, "\n";
2279
- show_commit_changed_paths($c);
2280
- print "\n";
2281
- print $_ foreach @$l;
2282
- }
2283
- } else {
2284
- print "1 line\n";
2285
- show_commit_changed_paths($c);
2286
- print "\n";
2287
-
2288
- }
2289
- foreach my $x (qw/raw stat diff/) {
2290
- if ($c->{$x}) {
2291
- print "\n";
2292
- print $_ foreach @{$c->{$x}}
2293
- }
2294
- }
2295
-}
2296
-
2297
-sub cmd_show_log {
2298
- my (@args) = @_;
2299
- my ($r_min, $r_max);
2300
- my $r_last = -1; # prevent dupes
2301
- set_local_timezone();
2302
- if (defined $::_revision) {
2303
- if ($::_revision =~ /^(\d+):(\d+)$/) {
2304
- ($r_min, $r_max) = ($1, $2);
2305
- } elsif ($::_revision =~ /^\d+$/) {
2306
- $r_min = $r_max = $::_revision;
2307
- } else {
2308
- fatal "-r$::_revision is not supported, use ",
2309
- "standard 'git log' arguments instead";
2310
- }
2311
- }
2312
-
2313
- config_pager();
2314
- @args = git_svn_log_cmd($r_min, $r_max, @args);
2315
- if (!@args) {
2316
- print commit_log_separator unless $incremental || $oneline;
2317
- return;
2318
- }
2319
- my $log = command_output_pipe(@args);
2320
- run_pager();
2321
- my (@k, $c, $d, $stat);
2322
- my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
2323
- while (<$log>) {
2324
- if (/^${esc_color}commit (?:- )?($::sha1_short)/o) {
2325
- my $cmt = $1;
2326
- if ($c && cmt_showable($c) && $c->{r} != $r_last) {
2327
- $r_last = $c->{r};
2328
- process_commit($c, $r_min, $r_max, \@k) or
2329
- goto out;
2330
- }
2331
- $d = undef;
2332
- $c = { c => $cmt };
2333
- } elsif (/^${esc_color}author (.+) (\d+) ([\-\+]?\d+)$/o) {
2334
- get_author_info($c, $1, $2, $3);
2335
- } elsif (/^${esc_color}(?:tree|parent|committer) /o) {
2336
- # ignore
2337
- } elsif (/^${esc_color}:\d{6} \d{6} $::sha1_short/o) {
2338
- push @{$c->{raw}}, $_;
2339
- } elsif (/^${esc_color}[ACRMDT]\t/) {
2340
- # we could add $SVN->{svn_path} here, but that requires
2341
- # remote access at the moment (repo_path_split)...
2342
- s#^(${esc_color})([ACRMDT])\t#$1 $2 #o;
2343
- push @{$c->{changed}}, $_;
2344
- } elsif (/^${esc_color}diff /o) {
2345
- $d = 1;
2346
- push @{$c->{diff}}, $_;
2347
- } elsif ($d) {
2348
- push @{$c->{diff}}, $_;
2349
- } elsif (/^\ .+\ \|\s*\d+\ $esc_color[\+\-]*
2350
- $esc_color*[\+\-]*$esc_color$/x) {
2351
- $stat = 1;
2352
- push @{$c->{stat}}, $_;
2353
- } elsif ($stat && /^ \d+ files changed, \d+ insertions/) {
2354
- push @{$c->{stat}}, $_;
2355
- $stat = undef;
2356
- } elsif (/^${esc_color} (git-svn-id:.+)$/o) {
2357
- ($c->{url}, $c->{r}, undef) = ::extract_metadata($1);
2358
- } elsif (s/^${esc_color} //o) {
2359
- push @{$c->{l}}, $_;
2360
- }
2361
- }
2362
- if ($c && defined $c->{r} && $c->{r} != $r_last) {
2363
- $r_last = $c->{r};
2364
- process_commit($c, $r_min, $r_max, \@k);
2365
- }
2366
- if (@k) {
2367
- ($r_min, $r_max) = ($r_max, $r_min);
2368
- process_commit($_, $r_min, $r_max) foreach reverse @k;
2369
- }
2370
-out:
2371
- close $log;
2372
- print commit_log_separator unless $incremental || $oneline;
2373
-}
2374
-
2375
-sub cmd_blame {
2376
- my $path = pop;
2377
-
2378
- config_pager();
2379
- run_pager();
2380
-
2381
- my ($fh, $ctx, $rev);
2382
-
2383
- if ($_git_format) {
2384
- ($fh, $ctx) = command_output_pipe('blame', @_, $path);
2385
- while (my $line = <$fh>) {
2386
- if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
2387
- # Uncommitted edits show up as a rev ID of
2388
- # all zeros, which we can't look up with
2389
- # cmt_metadata
2390
- if ($1 !~ /^0+$/) {
2391
- (undef, $rev, undef) =
2392
- ::cmt_metadata($1);
2393
- $rev = '0' if (!$rev);
2394
- } else {
2395
- $rev = '0';
2396
- }
2397
- $rev = sprintf('%-10s', $rev);
2398
- $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
2399
- }
2400
- print $line;
2401
- }
2402
- } else {
2403
- ($fh, $ctx) = command_output_pipe('blame', '-p', @_, 'HEAD',
2404
- '--', $path);
2405
- my ($sha1);
2406
- my %authors;
2407
- my @buffer;
2408
- my %dsha; #distinct sha keys
2409
-
2410
- while (my $line = <$fh>) {
2411
- push @buffer, $line;
2412
- if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
2413
- $dsha{$1} = 1;
2414
- }
2415
- }
2416
-
2417
- my $s2r = ::cmt_sha2rev_batch([keys %dsha]);
2418
-
2419
- foreach my $line (@buffer) {
2420
- if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
2421
- $rev = $s2r->{$1};
2422
- $rev = '0' if (!$rev)
2423
- }
2424
- elsif ($line =~ /^author (.*)/) {
2425
- $authors{$rev} = $1;
2426
- $authors{$rev} =~ s/\s/_/g;
2427
- }
2428
- elsif ($line =~ /^\t(.*)$/) {
2429
- printf("%6s %10s %s\n", $rev, $authors{$rev}, $1);
2430
- }
2431
- }
2432
- }
2433
- command_close_pipe($fh, $ctx);
2434
-}
2435
-
2043
package Git::SVN::Migration;
2044
# these version numbers do NOT correspond to actual version numbers
2045
# of git nor git-svn. They are just relative.
perl/Git/SVN/Log.pm
new
+395
@@ -0,0 +1,395 @@
1
+package Git::SVN::Log;
2
+use strict;
3
+use warnings;
4
+use Git::SVN::Utils qw(fatal);
5
+use Git qw(command command_oneline command_output_pipe command_close_pipe);
6
+use POSIX qw/strftime/;
7
+use constant commit_log_separator => ('-' x 72) . "\n";
8
+use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline
9
+ %rusers $show_commit $incremental/;
10
+
11
+# Option set in git-svn
12
+our $_git_format;
13
+
14
+sub cmt_showable {
15
+ my ($c) = @_;
16
+ return 1 if defined $c->{r};
17
+
18
+ # big commit message got truncated by the 16k pretty buffer in rev-list
19
+ if ($c->{l} && $c->{l}->[-1] eq "...\n" &&
20
+ $c->{a_raw} =~ /\@([a-f\d\-]+)>$/) {
21
+ @{$c->{l}} = ();
22
+ my @log = command(qw/cat-file commit/, $c->{c});
23
+
24
+ # shift off the headers
25
+ shift @log while ($log[0] ne '');
26
+ shift @log;
27
+
28
+ # TODO: make $c->{l} not have a trailing newline in the future
29
+ @{$c->{l}} = map { "$_\n" } grep !/^git-svn-id: /, @log;
30
+
31
+ (undef, $c->{r}, undef) = ::extract_metadata(
32
+ (grep(/^git-svn-id: /, @log))[-1]);
33
+ }
34
+ return defined $c->{r};
35
+}
36
+
37
+sub log_use_color {
38
+ return $color || Git->repository->get_colorbool('color.diff');
39
+}
40
+
41
+sub git_svn_log_cmd {
42
+ my ($r_min, $r_max, @args) = @_;
43
+ my $head = 'HEAD';
44
+ my (@files, @log_opts);
45
+ foreach my $x (@args) {
46
+ if ($x eq '--' || @files) {
47
+ push @files, $x;
48
+ } else {
49
+ if (::verify_ref("$x^0")) {
50
+ $head = $x;
51
+ } else {
52
+ push @log_opts, $x;
53
+ }
54
+ }
55
+ }
56
+
57
+ my ($url, $rev, $uuid, $gs) = ::working_head_info($head);
58
+
59
+ require Git::SVN;
60
+ $gs ||= Git::SVN->_new;
61
+ my @cmd = (qw/log --abbrev-commit --pretty=raw --default/,
62
+ $gs->refname);
63
+ push @cmd, '-r' unless $non_recursive;
64
+ push @cmd, qw/--raw --name-status/ if $verbose;
65
+ push @cmd, '--color' if log_use_color();
66
+ push @cmd, @log_opts;
67
+ if (defined $r_max && $r_max == $r_min) {
68
+ push @cmd, '--max-count=1';
69
+ if (my $c = $gs->rev_map_get($r_max)) {
70
+ push @cmd, $c;
71
+ }
72
+ } elsif (defined $r_max) {
73
+ if ($r_max < $r_min) {
74
+ ($r_min, $r_max) = ($r_max, $r_min);
75
+ }
76
+ my (undef, $c_max) = $gs->find_rev_before($r_max, 1, $r_min);
77
+ my (undef, $c_min) = $gs->find_rev_after($r_min, 1, $r_max);
78
+ # If there are no commits in the range, both $c_max and $c_min
79
+ # will be undefined. If there is at least 1 commit in the
80
+ # range, both will be defined.
81
+ return () if !defined $c_min || !defined $c_max;
82
+ if ($c_min eq $c_max) {
83
+ push @cmd, '--max-count=1', $c_min;
84
+ } else {
85
+ push @cmd, '--boundary', "$c_min..$c_max";
86
+ }
87
+ }
88
+ return (@cmd, @files);
89
+}
90
+
91
+# adapted from pager.c
92
+sub config_pager {
93
+ if (! -t *STDOUT) {
94
+ $ENV{GIT_PAGER_IN_USE} = 'false';
95
+ $pager = undef;
96
+ return;
97
+ }
98
+ chomp($pager = command_oneline(qw(var GIT_PAGER)));
99
+ if ($pager eq 'cat') {
100
+ $pager = undef;
101
+ }
102
+ $ENV{GIT_PAGER_IN_USE} = defined($pager);
103
+}
104
+
105
+sub run_pager {
106
+ return unless defined $pager;
107
+ pipe my ($rfd, $wfd) or return;
108
+ defined(my $pid = fork) or fatal "Can't fork: $!";
109
+ if (!$pid) {
110
+ open STDOUT, '>&', $wfd or
111
+ fatal "Can't redirect to stdout: $!";
112
+ return;
113
+ }
114
+ open STDIN, '<&', $rfd or fatal "Can't redirect stdin: $!";
115
+ $ENV{LESS} ||= 'FRSX';
116
+ exec $pager or fatal "Can't run pager: $! ($pager)";
117
+}
118
+
119
+sub format_svn_date {
120
+ my $t = shift || time;
121
+ require Git::SVN;
122
+ my $gmoff = Git::SVN::get_tz($t);
123
+ return strftime("%Y-%m-%d %H:%M:%S $gmoff (%a, %d %b %Y)", localtime($t));
124
+}
125
+
126
+sub parse_git_date {
127
+ my ($t, $tz) = @_;
128
+ # Date::Parse isn't in the standard Perl distro :(
129
+ if ($tz =~ s/^\+//) {
130
+ $t += tz_to_s_offset($tz);
131
+ } elsif ($tz =~ s/^\-//) {
132
+ $t -= tz_to_s_offset($tz);
133
+ }
134
+ return $t;
135
+}
136
+
137
+sub set_local_timezone {
138
+ if (defined $TZ) {
139
+ $ENV{TZ} = $TZ;
140
+ } else {
141
+ delete $ENV{TZ};
142
+ }
143
+}
144
+
145
+sub tz_to_s_offset {
146
+ my ($tz) = @_;
147
+ $tz =~ s/(\d\d)$//;
148
+ return ($1 * 60) + ($tz * 3600);
149
+}
150
+
151
+sub get_author_info {
152
+ my ($dest, $author, $t, $tz) = @_;
153
+ $author =~ s/(?:^\s*|\s*$)//g;
154
+ $dest->{a_raw} = $author;
155
+ my $au;
156
+ if ($::_authors) {
157
+ $au = $rusers{$author} || undef;
158
+ }
159
+ if (!$au) {
160
+ ($au) = ($author =~ /<([^>]+)\@[^>]+>$/);
161
+ }
162
+ $dest->{t} = $t;
163
+ $dest->{tz} = $tz;
164
+ $dest->{a} = $au;
165
+ $dest->{t_utc} = parse_git_date($t, $tz);
166
+}
167
+
168
+sub process_commit {
169
+ my ($c, $r_min, $r_max, $defer) = @_;
170
+ if (defined $r_min && defined $r_max) {
171
+ if ($r_min == $c->{r} && $r_min == $r_max) {
172
+ show_commit($c);
173
+ return 0;
174
+ }
175
+ return 1 if $r_min == $r_max;
176
+ if ($r_min < $r_max) {
177
+ # we need to reverse the print order
178
+ return 0 if (defined $limit && --$limit < 0);
179
+ push @$defer, $c;
180
+ return 1;
181
+ }
182
+ if ($r_min != $r_max) {
183
+ return 1 if ($r_min < $c->{r});
184
+ return 1 if ($r_max > $c->{r});
185
+ }
186
+ }
187
+ return 0 if (defined $limit && --$limit < 0);
188
+ show_commit($c);
189
+ return 1;
190
+}
191
+
192
+my $l_fmt;
193
+sub show_commit {
194
+ my $c = shift;
195
+ if ($oneline) {
196
+ my $x = "\n";
197
+ if (my $l = $c->{l}) {
198
+ while ($l->[0] =~ /^\s*$/) { shift @$l }
199
+ $x = $l->[0];
200
+ }
201
+ $l_fmt ||= 'A' . length($c->{r});
202
+ print 'r',pack($l_fmt, $c->{r}),' | ';
203
+ print "$c->{c} | " if $show_commit;
204
+ print $x;
205
+ } else {
206
+ show_commit_normal($c);
207
+ }
208
+}
209
+
210
+sub show_commit_changed_paths {
211
+ my ($c) = @_;
212
+ return unless $c->{changed};
213
+ print "Changed paths:\n", @{$c->{changed}};
214
+}
215
+
216
+sub show_commit_normal {
217
+ my ($c) = @_;
218
+ print commit_log_separator, "r$c->{r} | ";
219
+ print "$c->{c} | " if $show_commit;
220
+ print "$c->{a} | ", format_svn_date($c->{t_utc}), ' | ';
221
+ my $nr_line = 0;
222
+
223
+ if (my $l = $c->{l}) {
224
+ while ($l->[$#$l] eq "\n" && $#$l > 0
225
+ && $l->[($#$l - 1)] eq "\n") {
226
+ pop @$l;
227
+ }
228
+ $nr_line = scalar @$l;
229
+ if (!$nr_line) {
230
+ print "1 line\n\n\n";
231
+ } else {
232
+ if ($nr_line == 1) {
233
+ $nr_line = '1 line';
234
+ } else {
235
+ $nr_line .= ' lines';
236
+ }
237
+ print $nr_line, "\n";
238
+ show_commit_changed_paths($c);
239
+ print "\n";
240
+ print $_ foreach @$l;
241
+ }
242
+ } else {
243
+ print "1 line\n";
244
+ show_commit_changed_paths($c);
245
+ print "\n";
246
+
247
+ }
248
+ foreach my $x (qw/raw stat diff/) {
249
+ if ($c->{$x}) {
250
+ print "\n";
251
+ print $_ foreach @{$c->{$x}}
252
+ }
253
+ }
254
+}
255
+
256
+sub cmd_show_log {
257
+ my (@args) = @_;
258
+ my ($r_min, $r_max);
259
+ my $r_last = -1; # prevent dupes
260
+ set_local_timezone();
261
+ if (defined $::_revision) {
262
+ if ($::_revision =~ /^(\d+):(\d+)$/) {
263
+ ($r_min, $r_max) = ($1, $2);
264
+ } elsif ($::_revision =~ /^\d+$/) {
265
+ $r_min = $r_max = $::_revision;
266
+ } else {
267
+ fatal "-r$::_revision is not supported, use ",
268
+ "standard 'git log' arguments instead";
269
+ }
270
+ }
271
+
272
+ config_pager();
273
+ @args = git_svn_log_cmd($r_min, $r_max, @args);
274
+ if (!@args) {
275
+ print commit_log_separator unless $incremental || $oneline;
276
+ return;
277
+ }
278
+ my $log = command_output_pipe(@args);
279
+ run_pager();
280
+ my (@k, $c, $d, $stat);
281
+ my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
282
+ while (<$log>) {
283
+ if (/^${esc_color}commit (?:- )?($::sha1_short)/o) {
284
+ my $cmt = $1;
285
+ if ($c && cmt_showable($c) && $c->{r} != $r_last) {
286
+ $r_last = $c->{r};
287
+ process_commit($c, $r_min, $r_max, \@k) or
288
+ goto out;
289
+ }
290
+ $d = undef;
291
+ $c = { c => $cmt };
292
+ } elsif (/^${esc_color}author (.+) (\d+) ([\-\+]?\d+)$/o) {
293
+ get_author_info($c, $1, $2, $3);
294
+ } elsif (/^${esc_color}(?:tree|parent|committer) /o) {
295
+ # ignore
296
+ } elsif (/^${esc_color}:\d{6} \d{6} $::sha1_short/o) {
297
+ push @{$c->{raw}}, $_;
298
+ } elsif (/^${esc_color}[ACRMDT]\t/) {
299
+ # we could add $SVN->{svn_path} here, but that requires
300
+ # remote access at the moment (repo_path_split)...
301
+ s#^(${esc_color})([ACRMDT])\t#$1 $2 #o;
302
+ push @{$c->{changed}}, $_;
303
+ } elsif (/^${esc_color}diff /o) {
304
+ $d = 1;
305
+ push @{$c->{diff}}, $_;
306
+ } elsif ($d) {
307
+ push @{$c->{diff}}, $_;
308
+ } elsif (/^\ .+\ \|\s*\d+\ $esc_color[\+\-]*
309
+ $esc_color*[\+\-]*$esc_color$/x) {
310
+ $stat = 1;
311
+ push @{$c->{stat}}, $_;
312
+ } elsif ($stat && /^ \d+ files changed, \d+ insertions/) {
313
+ push @{$c->{stat}}, $_;
314
+ $stat = undef;
315
+ } elsif (/^${esc_color} (git-svn-id:.+)$/o) {
316
+ ($c->{url}, $c->{r}, undef) = ::extract_metadata($1);
317
+ } elsif (s/^${esc_color} //o) {
318
+ push @{$c->{l}}, $_;
319
+ }
320
+ }
321
+ if ($c && defined $c->{r} && $c->{r} != $r_last) {
322
+ $r_last = $c->{r};
323
+ process_commit($c, $r_min, $r_max, \@k);
324
+ }
325
+ if (@k) {
326
+ ($r_min, $r_max) = ($r_max, $r_min);
327
+ process_commit($_, $r_min, $r_max) foreach reverse @k;
328
+ }
329
+out:
330
+ close $log;
331
+ print commit_log_separator unless $incremental || $oneline;
332
+}
333
+
334
+sub cmd_blame {
335
+ my $path = pop;
336
+
337
+ config_pager();
338
+ run_pager();
339
+
340
+ my ($fh, $ctx, $rev);
341
+
342
+ if ($_git_format) {
343
+ ($fh, $ctx) = command_output_pipe('blame', @_, $path);
344
+ while (my $line = <$fh>) {
345
+ if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
346
+ # Uncommitted edits show up as a rev ID of
347
+ # all zeros, which we can't look up with
348
+ # cmt_metadata
349
+ if ($1 !~ /^0+$/) {
350
+ (undef, $rev, undef) =
351
+ ::cmt_metadata($1);
352
+ $rev = '0' if (!$rev);
353
+ } else {
354
+ $rev = '0';
355
+ }
356
+ $rev = sprintf('%-10s', $rev);
357
+ $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
358
+ }
359
+ print $line;
360
+ }
361
+ } else {
362
+ ($fh, $ctx) = command_output_pipe('blame', '-p', @_, 'HEAD',
363
+ '--', $path);
364
+ my ($sha1);
365
+ my %authors;
366
+ my @buffer;
367
+ my %dsha; #distinct sha keys
368
+
369
+ while (my $line = <$fh>) {
370
+ push @buffer, $line;
371
+ if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
372
+ $dsha{$1} = 1;
373
+ }
374
+ }
375
+
376
+ my $s2r = ::cmt_sha2rev_batch([keys %dsha]);
377
+
378
+ foreach my $line (@buffer) {
379
+ if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
380
+ $rev = $s2r->{$1};
381
+ $rev = '0' if (!$rev)
382
+ }
383
+ elsif ($line =~ /^author (.*)/) {
384
+ $authors{$rev} = $1;
385
+ $authors{$rev} =~ s/\s/_/g;
386
+ }
387
+ elsif ($line =~ /^\t(.*)$/) {
388
+ printf("%6s %10s %s\n", $rev, $authors{$rev}, $1);
389
+ }
390
+ }
391
+ }
392
+ command_close_pipe($fh, $ctx);
393
+}
394
+
395
+1;
perl/Makefile
+1
@@ -33,6 +33,7 @@ modules += Git/SVN
33
modules += Git/SVN/Memoize/YAML
34
modules += Git/SVN/Fetcher
35
modules += Git/SVN/Editor
36
+modules += Git/SVN/Log
37
modules += Git/SVN/Prompt
38
modules += Git/SVN/Ra
39
modules += Git/SVN/Utils
t/Git-SVN/00compile.t
+4
-2
@@ -3,7 +3,9 @@
3
use strict;
4
use warnings;
5
6
-use Test::More tests => 2;
6
+use Test::More tests => 4;
7
8
-require_ok 'Git::SVN::Utils';
8
require_ok 'Git::SVN';
9
+require_ok 'Git::SVN::Utils';
10
+require_ok 'Git::SVN::Ra';
11
+require_ok 'Git::SVN::Log';