2 # Wrap "svn ci" and help generate ChangeLog entries.
4 # This script takes an optional list of files and/or directories, just like
7 # If you save this somewhere on your PATH, you can just run "svn-ci" instead
8 # of "svn ci" when you want to check in a change to the Xapian tree.
14 # Set these if you don't like the default values
18 ###############################################################################
20 if (defined $ARGV[0] && $ARGV[0] eq '--help') {
24 The default is to check in all changed files in the current directory or
27 If there is no ChangeLog in the current directory, $0 will look for one
28 in the parent directory and above.
30 If ChangeLog has already been updated and doesn't contain evidence of
31 conflicts or diff fragments, check in the requested files plus ChangeLog.
33 If ChangeLog hasn't updated, a suitable entry header is prepended, plus
34 the diffs between CVS and the files to be checked in, and the user is
37 If ChangeLog contains evidence of conflicts or diff fragments, the user
38 is prompted to edit it.
43 # Set default values if the user hasn't set their own above...
44 my @passwd = getpwuid(getuid
());
47 if ($email eq 'olly') {
48 $email = 'olly@survex.com';
49 } elsif ($email eq 'richard') {
50 $email = 'richard@tartarus.org';
51 } elsif ($email eq 'kosei') {
52 $email = 'cou929@gmail.com';
54 my $host = `hostname -d`;
56 $email = $passwd[0] . '@' . $host;
59 $realname ||= (split /,/, $passwd[6])[0];
62 my $revert_changelog = 0;
63 if (scalar @ARGV && $ARGV[0] eq '--revert') {
65 $revert_changelog = 1;
70 if (scalar @ARGV && $ARGV[0] eq '--backport') {
72 my $left = (<ChangeLog
.merge
-left
.r?
*>)[0];
73 my $right = (<ChangeLog
.merge
-right
.r?
*>)[0];
74 if (defined $left && defined $right) {
75 system "diff -c \Q$left\E \Q$right\E > ChangeLog.rej";
76 system("svn", "revert", "ChangeLog");
78 } elsif (! -f
'ChangeLog.rej') {
79 die "--backport specified, but no svn merge telltales and no ChangeLog.rej\n";
84 # Remove any trailing slashes on directories for consistency.
85 @ARGV = map {s
,/+$,,; $_} @ARGV;
87 if (! -f
"ChangeLog") {
89 for (reverse(split m!/!, getcwd
())) {
92 last if -f
"ChangeLog";
95 @ARGV = map {$path.$_} @ARGV;
102 my $tmp = "ChangeLog.$$.new";
105 push @ARGV, "ChangeLog" unless grep {$_ eq "ChangeLog"} @ARGV;
111 # FIXME: ought to search down and then up for one...
112 # Searching down should check-in with the same message in each subdir
113 # Searching up should check-in using the first changelog in a parent dir
114 # but only files below this directory...
115 if (! -f
"ChangeLog") {
116 die "No ChangeLog found in this directory or a parent directory!\n";
118 if ($revert_changelog) {
119 system("svn revert ChangeLog");
122 open I
, "<ChangeLog" or die $!;
124 # conflicts, or diff fragments
133 # See if the ChangeLog is in conflict, and automatically resolve it if it is.
134 if (`svn status ChangeLog` =~ /^C/) {
135 print "Automatically merging conflicts in 'ChangeLog'\n";
136 system("svn resolved ChangeLog");
137 open I
, "<ChangeLog" or die $!;
138 open O
, ">ChangeLog.$$.tmp" or die $!;
140 # Strip out the conflict markers.
146 rename "ChangeLog.$$.tmp", "ChangeLog" or die $!;
151 system("svn diff ChangeLog");
152 my $exit = ($?
>> 8);
153 if ($exit != 0 && $exit != 1) {
154 die "svn diff failed ($?)\n";
160 # FIXME: ChangeLog changed - check if they want to edit it ?
166 open CHANGELOGREJ
, 'ChangeLog.rej' or die $!;
168 my $change_count = 0;
169 while (<CHANGELOGREJ
>) {
170 next unless s/^\+ //;
179 if ($change_count == 0) {
180 die "No entries found in ChangeLog.rej!\n";
182 if ($change_count > 1) {
183 $entry = "\t* Backport changes from trunk:\n".$entry;
185 $entry = "\t* Backport change from trunk:\n".$entry;
187 add_new_changelog_entry
($entry);
188 } elsif ($add_diff) {
189 # ChangeLog unchanged - add diff to top
190 open CI
, "svn diff ".join(" ", map quotemeta, @ARGV)."|";
195 # Property changes don't have an "Index: [...]" line.
199 if (/^Index: (.*)/) {
202 if ($fnm =~ /\.(?:cc|[ch])$/) {
204 } elsif ($fnm =~ /\.py(?:\.in)?$/) {
210 } elsif (/^\+.*[ \t]$/) {
212 $whitespace .= ": added/changed line has trailing whitespace:\n";
214 } elsif (/^\+.* \t/) {
216 $whitespace .= ": added/changed line has space before tab:\n";
218 } elsif ($want_tabs == 1 and /^\+\t* {8}/) {
220 $whitespace .= ": added/changed line uses spaces for indentation rather than tab:\n";
222 } elsif (!$want_tabs and /^\+ *\t/) {
224 $whitespace .= ": added/changed line uses tab for indentation rather than spaces:\n";
242 if (scalar @d > 3 && length $dir) {
243 push @newfiles, $dir;
259 if (scalar @newfiles == 0) {
260 die "No modified files found to commit!\n";
262 my $filelist = "\t* ";
263 my $line = join ',', @newfiles;
264 while (length($line) >= 68 && $line =~ s/^(.{1,68},)//) {
265 $filelist .= "$1\n\t ";
267 $diff = "$filelist$line:\n\n$whitespace\n$diff";
268 add_new_changelog_entry
($diff);
270 system(($ENV{VISUAL
}||$ENV{EDITOR
}||'vi'), "ChangeLog");
271 ($?
>> 8 == 0) || die "$?\n";
273 unlink "ChangeLog.rej";
279 open CI
, "<ChangeLog" or die $!;
281 # conflicts, or diff fragments
288 # conflicts, or diff fragments
296 # conflicts, or diff fragments
303 $msg =~ s/^\s+(?:\* \b)?//mg;
304 die "No changelog entry" if length($msg) == 0;
305 my $newclentry = "ChangeLog.newentry.$$";
306 open M
, ">$newclentry" or die $!;
309 # Use a temporary file and -F rather than -m to avoid any risk of overflowing
310 # limits on command line length.
311 system("svn", "ci", "-F", $newclentry, @ARGV);
314 sub add_new_changelog_entry
{
316 open CO
, ">$tmp" or die $!;
317 print CO strftime
("%a %h %d %H:%M:%S GMT %Y", gmtime);
318 print CO
" $realname <$email>\n\n";
320 open CI
, "<ChangeLog" or die $!;
326 rename $tmp, "ChangeLog";
331 print STDERR
"Unresolved conflicts in ChangeLog\n";
333 print STDERR
"Diff fragments in ChangeLog\n";
335 print STDERR
"Rerunning the ci command will give you a chance to edit ChangeLog\n";