blob: 98f3ede566a6cb0c902ce84795f7de8f8afbe633 [file] [log] [blame]
Martin Langhoffd3968362005-08-30 21:56:52 +12001#!/usr/bin/perl -w
2#
3# This tool is copyright (c) 2005, Martin Langhoff.
4# It is released under the Gnu Public License, version 2.
5#
Junio C Hamanoa6080a02007-06-07 00:04:01 -07006# The basic idea is to walk the output of tla abrowse,
7# fetch the changesets and apply them.
Martin Langhoffd3968362005-08-30 21:56:52 +12008#
martin@catalyst.net.nz241b5962005-09-11 21:26:05 +12009
Martin Langhoffd3968362005-08-30 21:56:52 +120010=head1 Invocation
11
Stephan Beyer1b1dd232008-07-13 15:36:15 +020012 git archimport [ -h ] [ -v ] [ -o ] [ -a ] [ -f ] [ -T ]
Junio C Hamanoa6080a02007-06-07 00:04:01 -070013 [ -D depth] [ -t tempdir ] <archive>/<branch> [ <archive>/<branch> ]
Martin Langhoffd3968362005-08-30 21:56:52 +120014
martin@catalyst.net.nz241b5962005-09-11 21:26:05 +120015Imports a project from one or more Arch repositories. It will follow branches
16and repositories within the namespaces defined by the <archive/branch>
Pavel Roskin82e5a822006-07-10 01:50:18 -040017parameters supplied. If it cannot find the remote branch a merge comes from
Junio C Hamanoa6080a02007-06-07 00:04:01 -070018it will just import it as a regular commit. If it can find it, it will mark it
martin@catalyst.net.nz241b5962005-09-11 21:26:05 +120019as a merge whenever possible.
Martin Langhoffd3968362005-08-30 21:56:52 +120020
martin@catalyst.net.nz241b5962005-09-11 21:26:05 +120021See man (1) git-archimport for more details.
Martin Langhoffd3968362005-08-30 21:56:52 +120022
martin@catalyst.net.nz241b5962005-09-11 21:26:05 +120023=head1 TODO
Martin Langhoffd3968362005-08-30 21:56:52 +120024
martin@catalyst.net.nz241b5962005-09-11 21:26:05 +120025 - create tag objects instead of ref tags
Martin Langhoffd3968362005-08-30 21:56:52 +120026 - audit shell-escaping of filenames
martin@catalyst.net.nz241b5962005-09-11 21:26:05 +120027 - hide our private tags somewhere smarter
Junio C Hamanoa6080a02007-06-07 00:04:01 -070028 - find a way to make "cat *patches | patch" safe even when patchfiles are missing newlines
Eric Wong3e525e62005-11-23 23:55:04 -080029 - sort and apply patches by graphing ancestry relations instead of just
30 relying in dates supplied in the changeset itself.
31 tla ancestry-graph -m could be helpful here...
Martin Langhoffd3968362005-08-30 21:56:52 +120032
33=head1 Devel tricks
34
Junio C Hamanoa6080a02007-06-07 00:04:01 -070035Add print in front of the shell commands invoked via backticks.
Martin Langhoffd3968362005-08-30 21:56:52 +120036
Eric Wong22ff00f2005-11-12 01:29:20 -080037=head1 Devel Notes
38
39There are several places where Arch and git terminology are intermixed
40and potentially confused.
41
42The notion of a "branch" in git is approximately equivalent to
43a "archive/category--branch--version" in Arch. Also, it should be noted
44that the "--branch" portion of "archive/category--branch--version" is really
45optional in Arch although not many people (nor tools!) seem to know this.
46This means that "archive/category--version" is also a valid "branch"
47in git terms.
48
49We always refer to Arch names by their fully qualified variant (which
50means the "archive" name is prefixed.
51
52For people unfamiliar with Arch, an "archive" is the term for "repository",
53and can contain multiple, unrelated branches.
54
Martin Langhoffd3968362005-08-30 21:56:52 +120055=cut
56
57use strict;
58use warnings;
59use Getopt::Std;
Eric Wong42f44b02005-11-23 23:52:43 -080060use File::Temp qw(tempdir);
Eric Wongf88961a2005-11-23 23:48:57 -080061use File::Path qw(mkpath rmtree);
Martin Langhoffd3968362005-08-30 21:56:52 +120062use File::Basename qw(basename dirname);
Martin Langhoffd3968362005-08-30 21:56:52 +120063use Data::Dumper qw/ Dumper /;
64use IPC::Open2;
65
66$SIG{'PIPE'}="IGNORE";
67$ENV{'TZ'}="UTC";
68
martin@catalyst.net.nz1d4710d2005-09-11 21:26:05 +120069my $git_dir = $ENV{"GIT_DIR"} || ".git";
70$ENV{"GIT_DIR"} = $git_dir;
Eric Wonga7fb51d2005-11-12 01:25:33 -080071my $ptag_dir = "$git_dir/archimport/tags";
martin@catalyst.net.nz1d4710d2005-09-11 21:26:05 +120072
Eric Wong3e525e62005-11-23 23:55:04 -080073our($opt_h,$opt_f,$opt_v,$opt_T,$opt_t,$opt_D,$opt_a,$opt_o);
Martin Langhoffd3968362005-08-30 21:56:52 +120074
75sub usage() {
76 print STDERR <<END;
Stephan Beyer1b1dd232008-07-13 15:36:15 +020077Usage: git archimport # fetch/update GIT from Arch
Eric Wong42f45702005-12-18 17:23:50 -080078 [ -h ] [ -v ] [ -o ] [ -a ] [ -f ] [ -T ] [ -D depth ] [ -t tempdir ]
Martin Langhoffd3968362005-08-30 21:56:52 +120079 repository/arch-branch [ repository/arch-branch] ...
80END
81 exit(1);
82}
83
Eric Wong3e525e62005-11-23 23:55:04 -080084getopts("fThvat:D:") or usage();
Martin Langhoffd3968362005-08-30 21:56:52 +120085usage if $opt_h;
86
87@ARGV >= 1 or usage();
Eric Wong42f44b02005-11-23 23:52:43 -080088# $arch_branches:
89# values associated with keys:
90# =1 - Arch version / git 'branch' detected via abrowse on a limit
Pavel Roskin82e5a822006-07-10 01:50:18 -040091# >1 - Arch version / git 'branch' of an auxiliary branch we've merged
Paolo Bonzinid9cb5392007-03-07 10:43:41 +010092my %arch_branches = map { my $branch = $_; $branch =~ s/:[^:]*$//; $branch => 1 } @ARGV;
93
94# $branch_name_map:
95# maps arch branches to git branch names
96my %branch_name_map = map { m/^(.*):([^:]*)$/; $1 => $2 } grep { m/:/ } @ARGV;
Martin Langhoffd3968362005-08-30 21:56:52 +120097
Eric Wong5744f272005-11-23 23:50:27 -080098$ENV{'TMPDIR'} = $opt_t if $opt_t; # $ENV{TMPDIR} will affect tempdir() calls:
99my $tmp = tempdir('git-archimport-XXXXXX', TMPDIR => 1, CLEANUP => 1);
martin@catalyst.net.nz127bf002005-09-11 21:26:05 +1200100$opt_v && print "+ Using $tmp as temporary directory\n";
Martin Langhoffd3968362005-08-30 21:56:52 +1200101
Gerrit Pape11dbe9e2007-02-03 22:38:59 +0000102unless (-d $git_dir) { # initial import needs empty directory
103 opendir DIR, '.' or die "Unable to open current directory: $!\n";
104 while (my $entry = readdir DIR) {
105 $entry =~ /^\.\.?$/ or
106 die "Initial import needs an empty current working directory.\n"
107 }
108 closedir DIR
109}
110
Paolo Bonzinid9cb5392007-03-07 10:43:41 +0100111my $default_archive; # default Arch archive
Eric Wong42f44b02005-11-23 23:52:43 -0800112my %reachable = (); # Arch repositories we can access
113my %unreachable = (); # Arch repositories we can't access :<
Martin Langhoffd3968362005-08-30 21:56:52 +1200114my @psets = (); # the collection
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200115my %psets = (); # the collection, by name
Eric Wong3e525e62005-11-23 23:55:04 -0800116my %stats = ( # Track which strategy we used to import:
117 get_tag => 0, replay => 0, get_new => 0, get_delta => 0,
118 simple_changeset => 0, import_or_tag => 0
119);
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200120
121my %rptags = (); # my reverse private tags
122 # to map a SHA1 to a commitid
Eric Wong2777ef72005-11-23 23:47:39 -0800123my $TLA = $ENV{'ARCH_CLIENT'} || 'tla';
Martin Langhoffd3968362005-08-30 21:56:52 +1200124
Eric Wong42f44b02005-11-23 23:52:43 -0800125sub do_abrowse {
126 my $stage = shift;
127 while (my ($limit, $level) = each %arch_branches) {
128 next unless $level == $stage;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700129
130 open ABROWSE, "$TLA abrowse -fkD --merges $limit |"
Eric Wong42f44b02005-11-23 23:52:43 -0800131 or die "Problems with tla abrowse: $!";
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700132
Eric Wong42f44b02005-11-23 23:52:43 -0800133 my %ps = (); # the current one
134 my $lastseen = '';
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700135
Eric Wong42f44b02005-11-23 23:52:43 -0800136 while (<ABROWSE>) {
137 chomp;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700138
Eric Wong42f44b02005-11-23 23:52:43 -0800139 # first record padded w 8 spaces
140 if (s/^\s{8}\b//) {
141 my ($id, $type) = split(m/\s+/, $_, 2);
Martin Langhoffd3968362005-08-30 21:56:52 +1200142
Eric Wong42f44b02005-11-23 23:52:43 -0800143 my %last_ps;
144 # store the record we just captured
145 if (%ps && !exists $psets{ $ps{id} }) {
146 %last_ps = %ps; # break references
147 push (@psets, \%last_ps);
148 $psets{ $last_ps{id} } = \%last_ps;
Martin Langhoffd3968362005-08-30 21:56:52 +1200149 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700150
Eric Wong42f44b02005-11-23 23:52:43 -0800151 my $branch = extract_versionname($id);
152 %ps = ( id => $id, branch => $branch );
153 if (%last_ps && ($last_ps{branch} eq $branch)) {
154 $ps{parent_id} = $last_ps{id};
155 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700156
Eric Wong42f44b02005-11-23 23:52:43 -0800157 $arch_branches{$branch} = 1;
158 $lastseen = 'id';
Martin Langhoffd3968362005-08-30 21:56:52 +1200159
Eric Wong42f44b02005-11-23 23:52:43 -0800160 # deal with types (should work with baz or tla):
161 if ($type =~ m/\(.*changeset\)/) {
162 $ps{type} = 's';
163 } elsif ($type =~ /\(.*import\)/) {
164 $ps{type} = 'i';
Eric Wong6df896b2005-11-23 23:53:55 -0800165 } elsif ($type =~ m/\(tag.*?(\S+\@\S+).*?\)/) {
Eric Wong42f44b02005-11-23 23:52:43 -0800166 $ps{type} = 't';
167 # read which revision we've tagged when we parse the log
Eric Wong6df896b2005-11-23 23:53:55 -0800168 $ps{tag} = $1;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700169 } else {
Eric Wong42f44b02005-11-23 23:52:43 -0800170 warn "Unknown type $type";
171 }
172
173 $arch_branches{$branch} = 1;
174 $lastseen = 'id';
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700175 } elsif (s/^\s{10}//) {
176 # 10 leading spaces or more
Eric Wong42f44b02005-11-23 23:52:43 -0800177 # indicate commit metadata
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700178
Eric Wong42f44b02005-11-23 23:52:43 -0800179 # date
180 if ($lastseen eq 'id' && m/^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d)/){
181 $ps{date} = $1;
182 $lastseen = 'date';
183 } elsif ($_ eq 'merges in:') {
184 $ps{merges} = [];
185 $lastseen = 'merges';
186 } elsif ($lastseen eq 'merges' && s/^\s{2}//) {
187 my $id = $_;
188 push (@{$ps{merges}}, $id);
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700189
Eric Wong42f44b02005-11-23 23:52:43 -0800190 # aggressive branch finding:
191 if ($opt_D) {
192 my $branch = extract_versionname($id);
193 my $repo = extract_reponame($branch);
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700194
Eric Wong42f44b02005-11-23 23:52:43 -0800195 if (archive_reachable($repo) &&
196 !defined $arch_branches{$branch}) {
197 $arch_branches{$branch} = $stage + 1;
198 }
199 }
200 } else {
201 warn "more metadata after merges!?: $_\n" unless /^\s*$/;
202 }
203 }
204 }
205
206 if (%ps && !exists $psets{ $ps{id} }) {
207 my %temp = %ps; # break references
208 if (@psets && $psets[$#psets]{branch} eq $ps{branch}) {
209 $temp{parent_id} = $psets[$#psets]{id};
210 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700211 push (@psets, \%temp);
Eric Wong42f44b02005-11-23 23:52:43 -0800212 $psets{ $temp{id} } = \%temp;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700213 }
214
Eric Wong42f44b02005-11-23 23:52:43 -0800215 close ABROWSE or die "$TLA abrowse failed on $limit\n";
216 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200217} # end foreach $root
218
Eric Wong42f44b02005-11-23 23:52:43 -0800219do_abrowse(1);
220my $depth = 2;
221$opt_D ||= 0;
222while ($depth <= $opt_D) {
223 do_abrowse($depth);
224 $depth++;
225}
226
Martin Langhoffd3968362005-08-30 21:56:52 +1200227## Order patches by time
Eric Wong42f44b02005-11-23 23:52:43 -0800228# FIXME see if we can find a more optimal way to do this by graphing
229# the ancestry data and walking it, that way we won't have to rely on
230# client-supplied dates
Martin Langhoffd3968362005-08-30 21:56:52 +1200231@psets = sort {$a->{date}.$b->{id} cmp $b->{date}.$b->{id}} @psets;
232
233#print Dumper \@psets;
234
235##
236## TODO cleanup irrelevant patches
237## and put an initial import
238## or a full tag
Martin Langhoff3292ae42005-09-04 22:55:06 +1200239my $import = 0;
martin@catalyst.net.nz1d4710d2005-09-11 21:26:05 +1200240unless (-d $git_dir) { # initial import
Martin Langhoffd3968362005-08-30 21:56:52 +1200241 if ($psets[0]{type} eq 'i' || $psets[0]{type} eq 't') {
242 print "Starting import from $psets[0]{id}\n";
Nicolas Pitre5c94f872007-01-12 16:01:46 -0500243 `git-init`;
Martin Langhoff3292ae42005-09-04 22:55:06 +1200244 die $! if $?;
245 $import = 1;
Martin Langhoffd3968362005-08-30 21:56:52 +1200246 } else {
247 die "Need to start from an import or a tag -- cannot use $psets[0]{id}";
248 }
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200249} else { # progressing an import
250 # load the rptags
Eric Wong42f44b02005-11-23 23:52:43 -0800251 opendir(DIR, $ptag_dir)
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200252 || die "can't opendir: $!";
253 while (my $file = readdir(DIR)) {
Eric Wonga7fb51d2005-11-12 01:25:33 -0800254 # skip non-interesting-files
255 next unless -f "$ptag_dir/$file";
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700256
Eric Wonga7fb51d2005-11-12 01:25:33 -0800257 # convert first '--' to '/' from old git-archimport to use
258 # as an archivename/c--b--v private tag
259 if ($file !~ m!,!) {
260 my $oldfile = $file;
261 $file =~ s!--!,!;
262 print STDERR "converting old tag $oldfile to $file\n";
263 rename("$ptag_dir/$oldfile", "$ptag_dir/$file") or die $!;
264 }
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200265 my $sha = ptag($file);
266 chomp $sha;
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200267 $rptags{$sha} = $file;
268 }
269 closedir DIR;
Martin Langhoffd3968362005-08-30 21:56:52 +1200270}
271
Martin Langhoff3292ae42005-09-04 22:55:06 +1200272# process patchsets
Eric Wong22ff00f2005-11-12 01:29:20 -0800273# extract the Arch repository name (Arch "archive" in Arch-speak)
274sub extract_reponame {
275 my $fq_cvbr = shift; # archivename/[[[[category]branch]version]revision]
276 return (split(/\//, $fq_cvbr))[0];
277}
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700278
Eric Wong22ff00f2005-11-12 01:29:20 -0800279sub extract_versionname {
280 my $name = shift;
281 $name =~ s/--(?:patch|version(?:fix)?|base)-\d+$//;
282 return $name;
283}
Martin Langhoffd3968362005-08-30 21:56:52 +1200284
Eric Wong22ff00f2005-11-12 01:29:20 -0800285# convert a fully-qualified revision or version to a unique dirname:
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700286# normalperson@yhbt.net-05/mpd--uclinux--1--patch-2
Eric Wong22ff00f2005-11-12 01:29:20 -0800287# becomes: normalperson@yhbt.net-05,mpd--uclinux--1
288#
289# the git notion of a branch is closer to
290# archive/category--branch--version than archive/category--branch, so we
291# use this to convert to git branch names.
292# Also, keep archive names but replace '/' with ',' since it won't require
293# subdirectories, and is safer than swapping '--' which could confuse
294# reverse-mapping when dealing with bastard branches that
295# are just archive/category--version (no --branch)
296sub tree_dirname {
297 my $revision = shift;
298 my $name = extract_versionname($revision);
299 $name =~ s#/#,#;
300 return $name;
301}
302
Martin Langhofffee33652005-11-17 21:20:45 +1300303# old versions of git-archimport just use the <category--branch> part:
304sub old_style_branchname {
305 my $id = shift;
306 my $ret = safe_pipe_capture($TLA,'parse-package-name','-p',$id);
307 chomp $ret;
308 return $ret;
309}
310
Paolo Bonzinid9cb5392007-03-07 10:43:41 +0100311*git_default_branchname = $opt_o ? *old_style_branchname : *tree_dirname;
312
313# retrieve default archive, since $branch_name_map keys might not include it
314sub get_default_archive {
315 if (!defined $default_archive) {
316 $default_archive = safe_pipe_capture($TLA,'my-default-archive');
317 chomp $default_archive;
318 }
319 return $default_archive;
320}
321
322sub git_branchname {
323 my $revision = shift;
324 my $name = extract_versionname($revision);
325
326 if (exists $branch_name_map{$name}) {
327 return $branch_name_map{$name};
328
329 } elsif ($name =~ m#^([^/]*)/(.*)$#
330 && $1 eq get_default_archive()
331 && exists $branch_name_map{$2}) {
332 # the names given in the command-line lacked the archive.
333 return $branch_name_map{$2};
334
335 } else {
336 return git_default_branchname($revision);
337 }
338}
Eric Wong22ff00f2005-11-12 01:29:20 -0800339
Eric Wong3e525e62005-11-23 23:55:04 -0800340sub process_patchset_accurate {
341 my $ps = shift;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700342
Eric Wong3e525e62005-11-23 23:55:04 -0800343 # switch to that branch if we're not already in that branch:
344 if (-e "$git_dir/refs/heads/$ps->{branch}") {
345 system('git-checkout','-f',$ps->{branch}) == 0 or die "$! $?\n";
Martin Langhoffd3968362005-08-30 21:56:52 +1200346
Eric Wong3e525e62005-11-23 23:55:04 -0800347 # remove any old stuff that got leftover:
348 my $rm = safe_pipe_capture('git-ls-files','--others','-z');
349 rmtree(split(/\0/,$rm)) if $rm;
Martin Langhoffd3968362005-08-30 21:56:52 +1200350 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700351
Eric Wong3e525e62005-11-23 23:55:04 -0800352 # Apply the import/changeset/merge into the working tree
353 my $dir = sync_to_ps($ps);
354 # read the new log entry:
355 my @commitlog = safe_pipe_capture($TLA,'cat-log','-d',$dir,$ps->{id});
356 die "Error in cat-log: $!" if $?;
357 chomp @commitlog;
Martin Langhoffd3968362005-08-30 21:56:52 +1200358
Eric Wong3e525e62005-11-23 23:55:04 -0800359 # grab variables we want from the log, new fields get added to $ps:
360 # (author, date, email, summary, message body ...)
361 parselog($ps, \@commitlog);
Martin Langhoff3292ae42005-09-04 22:55:06 +1200362
Eric Wong3e525e62005-11-23 23:55:04 -0800363 if ($ps->{id} =~ /--base-0$/ && $ps->{id} ne $psets[0]{id}) {
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700364 # this should work when importing continuations
Eric Wong3e525e62005-11-23 23:55:04 -0800365 if ($ps->{tag} && (my $branchpoint = eval { ptag($ps->{tag}) })) {
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700366
Eric Wong3e525e62005-11-23 23:55:04 -0800367 # find where we are supposed to branch from
Paolo Bonzinid9cb5392007-03-07 10:43:41 +0100368 if (! -e "$git_dir/refs/heads/$ps->{branch}") {
369 system('git-branch',$ps->{branch},$branchpoint) == 0 or die "$! $?\n";
370
371 # We trust Arch with the fact that this is just a tag,
372 # and it does not affect the state of the tree, so
373 # we just tag and move on. If the user really wants us
374 # to consolidate more branches into one, don't tag because
375 # the tag name would be already taken.
376 tag($ps->{id}, $branchpoint);
377 ptag($ps->{id}, $branchpoint);
378 print " * Tagged $ps->{id} at $branchpoint\n";
379 }
380 system('git-checkout','-f',$ps->{branch}) == 0 or die "$! $?\n";
381
Eric Wong3e525e62005-11-23 23:55:04 -0800382 # remove any old stuff that got leftover:
383 my $rm = safe_pipe_capture('git-ls-files','--others','-z');
384 rmtree(split(/\0/,$rm)) if $rm;
Eric Wong3e525e62005-11-23 23:55:04 -0800385 return 0;
386 } else {
387 warn "Tagging from unknown id unsupported\n" if $ps->{tag};
388 }
389 # allow multiple bases/imports here since Arch supports cherry-picks
390 # from unrelated trees
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700391 }
392
Eric Wong3e525e62005-11-23 23:55:04 -0800393 # update the index with all the changes we got
Eric Wong3ff903b2006-02-18 03:49:38 -0800394 system('git-diff-files --name-only -z | '.
395 'git-update-index --remove -z --stdin') == 0 or die "$! $?\n";
Eric Wong3e525e62005-11-23 23:55:04 -0800396 system('git-ls-files --others -z | '.
397 'git-update-index --add -z --stdin') == 0 or die "$! $?\n";
Eric Wong3e525e62005-11-23 23:55:04 -0800398 return 1;
399}
400
401# the native changeset processing strategy. This is very fast, but
402# does not handle permissions or any renames involving directories
403sub process_patchset_fast {
404 my $ps = shift;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700405 #
Martin Langhoffd3968362005-08-30 21:56:52 +1200406 # create the branch if needed
407 #
Martin Langhoff3292ae42005-09-04 22:55:06 +1200408 if ($ps->{type} eq 'i' && !$import) {
409 die "Should not have more than one 'Initial import' per GIT import: $ps->{id}";
Martin Langhoffd3968362005-08-30 21:56:52 +1200410 }
411
Martin Langhoff3292ae42005-09-04 22:55:06 +1200412 unless ($import) { # skip for import
martin@catalyst.net.nz1d4710d2005-09-11 21:26:05 +1200413 if ( -e "$git_dir/refs/heads/$ps->{branch}") {
Martin Langhoffd3968362005-08-30 21:56:52 +1200414 # we know about this branch
Eric Wongf88961a2005-11-23 23:48:57 -0800415 system('git-checkout',$ps->{branch});
Martin Langhoffd3968362005-08-30 21:56:52 +1200416 } else {
417 # new branch! we need to verify a few things
418 die "Branch on a non-tag!" unless $ps->{type} eq 't';
419 my $branchpoint = ptag($ps->{tag});
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700420 die "Tagging from unknown id unsupported: $ps->{tag}"
Martin Langhoffd3968362005-08-30 21:56:52 +1200421 unless $branchpoint;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700422
Martin Langhoffd3968362005-08-30 21:56:52 +1200423 # find where we are supposed to branch from
Paolo Bonzinid9cb5392007-03-07 10:43:41 +0100424 if (! -e "$git_dir/refs/heads/$ps->{branch}") {
425 system('git-branch',$ps->{branch},$branchpoint) == 0 or die "$! $?\n";
Martin Langhoff52586ec2005-09-04 22:55:29 +1200426
Paolo Bonzinid9cb5392007-03-07 10:43:41 +0100427 # We trust Arch with the fact that this is just a tag,
428 # and it does not affect the state of the tree, so
429 # we just tag and move on. If the user really wants us
430 # to consolidate more branches into one, don't tag because
431 # the tag name would be already taken.
432 tag($ps->{id}, $branchpoint);
433 ptag($ps->{id}, $branchpoint);
434 print " * Tagged $ps->{id} at $branchpoint\n";
435 }
436 system('git-checkout',$ps->{branch}) == 0 or die "$! $?\n";
Eric Wong3e525e62005-11-23 23:55:04 -0800437 return 0;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700438 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200439 die $! if $?;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700440 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200441
Martin Langhoffd3968362005-08-30 21:56:52 +1200442 #
443 # Apply the import/changeset/merge into the working tree
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700444 #
Martin Langhoffd3968362005-08-30 21:56:52 +1200445 if ($ps->{type} eq 'i' || $ps->{type} eq 't') {
Martin Langhoffd3968362005-08-30 21:56:52 +1200446 apply_import($ps) or die $!;
Eric Wong3e525e62005-11-23 23:55:04 -0800447 $stats{import_or_tag}++;
Martin Langhoff3292ae42005-09-04 22:55:06 +1200448 $import=0;
Martin Langhoffd3968362005-08-30 21:56:52 +1200449 } elsif ($ps->{type} eq 's') {
450 apply_cset($ps);
Eric Wong3e525e62005-11-23 23:55:04 -0800451 $stats{simple_changeset}++;
Martin Langhoffd3968362005-08-30 21:56:52 +1200452 }
453
454 #
455 # prepare update git's index, based on what arch knows
456 # about the pset, resolve parents, etc
457 #
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700458
459 my @commitlog = safe_pipe_capture($TLA,'cat-archive-log',$ps->{id});
Martin Langhoffd3968362005-08-30 21:56:52 +1200460 die "Error in cat-archive-log: $!" if $?;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700461
Eric Wong6df896b2005-11-23 23:53:55 -0800462 parselog($ps,\@commitlog);
Martin Langhoffd3968362005-08-30 21:56:52 +1200463
464 # imports don't give us good info
465 # on added files. Shame on them
Eric Wong6df896b2005-11-23 23:53:55 -0800466 if ($ps->{type} eq 'i' || $ps->{type} eq 't') {
Eric Wong6df896b2005-11-23 23:53:55 -0800467 system('git-ls-files --deleted -z | '.
468 'git-update-index --remove -z --stdin') == 0 or die "$! $?\n";
Eric Wong3ff903b2006-02-18 03:49:38 -0800469 system('git-ls-files --others -z | '.
470 'git-update-index --add -z --stdin') == 0 or die "$! $?\n";
Martin Langhoffd3968362005-08-30 21:56:52 +1200471 }
472
Eric Wong6df896b2005-11-23 23:53:55 -0800473 # TODO: handle removed_directories and renamed_directories:
Eric Wong3ff903b2006-02-18 03:49:38 -0800474
Eric Wong6df896b2005-11-23 23:53:55 -0800475 if (my $del = $ps->{removed_files}) {
476 unlink @$del;
Martin Langhoffd3968362005-08-30 21:56:52 +1200477 while (@$del) {
478 my @slice = splice(@$del, 0, 100);
Eric Wong6df896b2005-11-23 23:53:55 -0800479 system('git-update-index','--remove','--',@slice) == 0 or
480 die "Error in git-update-index --remove: $! $?\n";
Martin Langhoffd3968362005-08-30 21:56:52 +1200481 }
482 }
Eric Wong6df896b2005-11-23 23:53:55 -0800483
484 if (my $ren = $ps->{renamed_files}) { # renamed
Martin Langhoffd3968362005-08-30 21:56:52 +1200485 if (@$ren % 2) {
486 die "Odd number of entries in rename!?";
487 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700488
Martin Langhoffd3968362005-08-30 21:56:52 +1200489 while (@$ren) {
Eric Wong6df896b2005-11-23 23:53:55 -0800490 my $from = shift @$ren;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700491 my $to = shift @$ren;
Martin Langhoffd3968362005-08-30 21:56:52 +1200492
493 unless (-d dirname($to)) {
494 mkpath(dirname($to)); # will die on err
495 }
Eric Wong3e525e62005-11-23 23:55:04 -0800496 # print "moving $from $to";
Eric Wong6df896b2005-11-23 23:53:55 -0800497 rename($from, $to) or die "Error renaming '$from' '$to': $!\n";
498 system('git-update-index','--remove','--',$from) == 0 or
499 die "Error in git-update-index --remove: $! $?\n";
500 system('git-update-index','--add','--',$to) == 0 or
501 die "Error in git-update-index --add: $! $?\n";
Martin Langhoffd3968362005-08-30 21:56:52 +1200502 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200503 }
Eric Wong6df896b2005-11-23 23:53:55 -0800504
Eric Wong3ff903b2006-02-18 03:49:38 -0800505 if (my $add = $ps->{new_files}) {
506 while (@$add) {
507 my @slice = splice(@$add, 0, 100);
508 system('git-update-index','--add','--',@slice) == 0 or
509 die "Error in git-update-index --add: $! $?\n";
510 }
511 }
512
Eric Wong6df896b2005-11-23 23:53:55 -0800513 if (my $mod = $ps->{modified_files}) {
Martin Langhoffd3968362005-08-30 21:56:52 +1200514 while (@$mod) {
515 my @slice = splice(@$mod, 0, 100);
Eric Wong6df896b2005-11-23 23:53:55 -0800516 system('git-update-index','--',@slice) == 0 or
517 die "Error in git-update-index: $! $?\n";
Martin Langhoffd3968362005-08-30 21:56:52 +1200518 }
519 }
Eric Wong3e525e62005-11-23 23:55:04 -0800520 return 1; # we successfully applied the changeset
521}
522
523if ($opt_f) {
524 print "Will import patchsets using the fast strategy\n",
525 "Renamed directories and permission changes will be missed\n";
526 *process_patchset = *process_patchset_fast;
527} else {
528 print "Using the default (accurate) import strategy.\n",
529 "Things may be a bit slow\n";
530 *process_patchset = *process_patchset_accurate;
531}
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700532
Eric Wong3e525e62005-11-23 23:55:04 -0800533foreach my $ps (@psets) {
534 # process patchsets
535 $ps->{branch} = git_branchname($ps->{id});
536
537 #
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700538 # ensure we have a clean state
539 #
Eric Wong3e525e62005-11-23 23:55:04 -0800540 if (my $dirty = `git-diff-files`) {
541 die "Unclean tree when about to process $ps->{id} " .
542 " - did we fail to commit cleanly before?\n$dirty";
543 }
544 die $! if $?;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700545
Eric Wong3e525e62005-11-23 23:55:04 -0800546 #
547 # skip commits already in repo
548 #
549 if (ptag($ps->{id})) {
550 $opt_v && print " * Skipping already imported: $ps->{id}\n";
Eric Wong10945e02005-11-23 23:58:16 -0800551 next;
Eric Wong3e525e62005-11-23 23:55:04 -0800552 }
553
554 print " * Starting to work on $ps->{id}\n";
555
556 process_patchset($ps) or next;
557
Junio C Hamano215a7ad2005-09-07 17:26:23 -0700558 # warn "errors when running git-update-index! $!";
Eric Wong3e525e62005-11-23 23:55:04 -0800559 my $tree = `git-write-tree`;
Martin Langhoffd3968362005-08-30 21:56:52 +1200560 die "cannot write tree $!" if $?;
561 chomp $tree;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700562
Martin Langhoffd3968362005-08-30 21:56:52 +1200563 #
564 # Who's your daddy?
565 #
566 my @par;
martin@catalyst.net.nz1d4710d2005-09-11 21:26:05 +1200567 if ( -e "$git_dir/refs/heads/$ps->{branch}") {
Eric Wongf88961a2005-11-23 23:48:57 -0800568 if (open HEAD, "<","$git_dir/refs/heads/$ps->{branch}") {
Martin Langhoffd3968362005-08-30 21:56:52 +1200569 my $p = <HEAD>;
570 close HEAD;
571 chomp $p;
572 push @par, '-p', $p;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700573 } else {
Martin Langhoffd3968362005-08-30 21:56:52 +1200574 if ($ps->{type} eq 's') {
575 warn "Could not find the right head for the branch $ps->{branch}";
576 }
577 }
578 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700579
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200580 if ($ps->{merges}) {
581 push @par, find_parents($ps);
582 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200583
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700584 #
Martin Langhoffd3968362005-08-30 21:56:52 +1200585 # Commit, tag and clean state
586 #
587 $ENV{TZ} = 'GMT';
588 $ENV{GIT_AUTHOR_NAME} = $ps->{author};
589 $ENV{GIT_AUTHOR_EMAIL} = $ps->{email};
590 $ENV{GIT_AUTHOR_DATE} = $ps->{date};
591 $ENV{GIT_COMMITTER_NAME} = $ps->{author};
592 $ENV{GIT_COMMITTER_EMAIL} = $ps->{email};
593 $ENV{GIT_COMMITTER_DATE} = $ps->{date};
594
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700595 my $pid = open2(*READER, *WRITER,'git-commit-tree',$tree,@par)
Martin Langhoffd3968362005-08-30 21:56:52 +1200596 or die $!;
Paolo Bonzinia94f4572007-02-28 21:02:02 +0100597 print WRITER $ps->{summary},"\n\n";
Miles Bader608403d2007-08-29 21:56:56 -0400598
599 # only print message if it's not empty, to avoid a spurious blank line;
600 # also append an extra newline, so there's a blank line before the
601 # following "git-archimport-id:" line.
602 print WRITER $ps->{message},"\n\n" if ($ps->{message} ne "");
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700603
Eric Wong6df896b2005-11-23 23:53:55 -0800604 # make it easy to backtrack and figure out which Arch revision this was:
605 print WRITER 'git-archimport-id: ',$ps->{id},"\n";
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700606
Martin Langhoffd3968362005-08-30 21:56:52 +1200607 close WRITER;
608 my $commitid = <READER>; # read
609 chomp $commitid;
610 close READER;
611 waitpid $pid,0; # close;
612
613 if (length $commitid != 40) {
614 die "Something went wrong with the commit! $! $commitid";
615 }
616 #
617 # Update the branch
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700618 #
Eric Wongf88961a2005-11-23 23:48:57 -0800619 open HEAD, ">","$git_dir/refs/heads/$ps->{branch}";
Martin Langhoffd3968362005-08-30 21:56:52 +1200620 print HEAD $commitid;
621 close HEAD;
Pavel Roskin8366a102005-11-16 13:27:28 -0500622 system('git-update-ref', 'HEAD', "$ps->{branch}");
Martin Langhoffd3968362005-08-30 21:56:52 +1200623
624 # tag accordingly
625 ptag($ps->{id}, $commitid); # private tag
626 if ($opt_T || $ps->{type} eq 't' || $ps->{type} eq 'i') {
627 tag($ps->{id}, $commitid);
628 }
629 print " * Committed $ps->{id}\n";
630 print " + tree $tree\n";
631 print " + commit $commitid\n";
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200632 $opt_v && print " + commit date is $ps->{date} \n";
Eric Wongf88961a2005-11-23 23:48:57 -0800633 $opt_v && print " + parents: ",join(' ',@par),"\n";
Eric Wong3e525e62005-11-23 23:55:04 -0800634}
635
636if ($opt_v) {
637 foreach (sort keys %stats) {
638 print" $_: $stats{$_}\n";
639 }
640}
641exit 0;
642
643# used by the accurate strategy:
644sub sync_to_ps {
645 my $ps = shift;
646 my $tree_dir = $tmp.'/'.tree_dirname($ps->{id});
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700647
Eric Wong3e525e62005-11-23 23:55:04 -0800648 $opt_v && print "sync_to_ps($ps->{id}) method: ";
649
650 if (-d $tree_dir) {
651 if ($ps->{type} eq 't') {
652 $opt_v && print "get (tag)\n";
653 # looks like a tag-only or (worse,) a mixed tags/changeset branch,
654 # can't rely on replay to work correctly on these
655 rmtree($tree_dir);
656 safe_pipe_capture($TLA,'get','--no-pristine',$ps->{id},$tree_dir);
657 $stats{get_tag}++;
658 } else {
659 my $tree_id = arch_tree_id($tree_dir);
660 if ($ps->{parent_id} && ($ps->{parent_id} eq $tree_id)) {
661 # the common case (hopefully)
662 $opt_v && print "replay\n";
663 safe_pipe_capture($TLA,'replay','-d',$tree_dir,$ps->{id});
664 $stats{replay}++;
665 } else {
666 # getting one tree is usually faster than getting two trees
667 # and applying the delta ...
668 rmtree($tree_dir);
669 $opt_v && print "apply-delta\n";
670 safe_pipe_capture($TLA,'get','--no-pristine',
671 $ps->{id},$tree_dir);
672 $stats{get_delta}++;
673 }
674 }
675 } else {
676 # new branch work
677 $opt_v && print "get (new tree)\n";
678 safe_pipe_capture($TLA,'get','--no-pristine',$ps->{id},$tree_dir);
679 $stats{get_new}++;
680 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700681
Eric Wong3e525e62005-11-23 23:55:04 -0800682 # added -I flag to rsync since we're going to fast! AIEEEEE!!!!
683 system('rsync','-aI','--delete','--exclude',$git_dir,
684# '--exclude','.arch-inventory',
685 '--exclude','.arch-ids','--exclude','{arch}',
686 '--exclude','+*','--exclude',',*',
687 "$tree_dir/",'./') == 0 or die "Cannot rsync $tree_dir: $! $?";
688 return $tree_dir;
Martin Langhoffd3968362005-08-30 21:56:52 +1200689}
690
Martin Langhoffd3968362005-08-30 21:56:52 +1200691sub apply_import {
692 my $ps = shift;
Eric Wong22ff00f2005-11-12 01:29:20 -0800693 my $bname = git_branchname($ps->{id});
Martin Langhoffd3968362005-08-30 21:56:52 +1200694
Eric Wongf88961a2005-11-23 23:48:57 -0800695 mkpath($tmp);
Martin Langhoffd3968362005-08-30 21:56:52 +1200696
Eric Wongf88961a2005-11-23 23:48:57 -0800697 safe_pipe_capture($TLA,'get','-s','--no-pristine',$ps->{id},"$tmp/import");
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700698 die "Cannot get import: $!" if $?;
Eric Wongf88961a2005-11-23 23:48:57 -0800699 system('rsync','-aI','--delete', '--exclude',$git_dir,
700 '--exclude','.arch-ids','--exclude','{arch}',
701 "$tmp/import/", './');
Martin Langhoffd3968362005-08-30 21:56:52 +1200702 die "Cannot rsync import:$!" if $?;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700703
Eric Wongf88961a2005-11-23 23:48:57 -0800704 rmtree("$tmp/import");
Martin Langhoffd3968362005-08-30 21:56:52 +1200705 die "Cannot remove tempdir: $!" if $?;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700706
Martin Langhoffd3968362005-08-30 21:56:52 +1200707
708 return 1;
709}
710
711sub apply_cset {
712 my $ps = shift;
713
Eric Wongf88961a2005-11-23 23:48:57 -0800714 mkpath($tmp);
Martin Langhoffd3968362005-08-30 21:56:52 +1200715
716 # get the changeset
Eric Wongf88961a2005-11-23 23:48:57 -0800717 safe_pipe_capture($TLA,'get-changeset',$ps->{id},"$tmp/changeset");
Martin Langhoffd3968362005-08-30 21:56:52 +1200718 die "Cannot get changeset: $!" if $?;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700719
Martin Langhoffd3968362005-08-30 21:56:52 +1200720 # apply patches
721 if (`find $tmp/changeset/patches -type f -name '*.patch'`) {
722 # this can be sped up considerably by doing
723 # (find | xargs cat) | patch
Pavel Roskin82e5a822006-07-10 01:50:18 -0400724 # but that can get mucked up by patches
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700725 # with missing trailing newlines or the standard
Martin Langhoffd3968362005-08-30 21:56:52 +1200726 # 'missing newline' flag in the patch - possibly
727 # produced with an old/buggy diff.
728 # slow and safe, we invoke patch once per patchfile
729 `find $tmp/changeset/patches -type f -name '*.patch' -print0 | grep -zv '{arch}' | xargs -iFILE -0 --no-run-if-empty patch -p1 --forward -iFILE`;
730 die "Problem applying patches! $!" if $?;
731 }
732
733 # apply changed binary files
734 if (my @modified = `find $tmp/changeset/patches -type f -name '*.modified'`) {
735 foreach my $mod (@modified) {
736 chomp $mod;
737 my $orig = $mod;
738 $orig =~ s/\.modified$//; # lazy
739 $orig =~ s!^\Q$tmp\E/changeset/patches/!!;
740 #print "rsync -p '$mod' '$orig'";
Eric Wongf88961a2005-11-23 23:48:57 -0800741 system('rsync','-p',$mod,"./$orig");
Martin Langhoffd3968362005-08-30 21:56:52 +1200742 die "Problem applying binary changes! $!" if $?;
743 }
744 }
745
746 # bring in new files
Eric Wongf88961a2005-11-23 23:48:57 -0800747 system('rsync','-aI','--exclude',$git_dir,
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700748 '--exclude','.arch-ids',
Eric Wongf88961a2005-11-23 23:48:57 -0800749 '--exclude', '{arch}',
750 "$tmp/changeset/new-files-archive/",'./');
Martin Langhoffd3968362005-08-30 21:56:52 +1200751
752 # deleted files are hinted from the commitlog processing
753
Eric Wongf88961a2005-11-23 23:48:57 -0800754 rmtree("$tmp/changeset");
Martin Langhoffd3968362005-08-30 21:56:52 +1200755}
756
757
758# =for reference
Eric Wong6df896b2005-11-23 23:53:55 -0800759# notes: *-files/-directories keys cannot have spaces, they're always
760# pika-escaped. Everything after the first newline
761# A log entry looks like:
Martin Langhoffd3968362005-08-30 21:56:52 +1200762# Revision: moodle-org--moodle--1.3.3--patch-15
763# Archive: arch-eduforge@catalyst.net.nz--2004
764# Creator: Penny Leach <penny@catalyst.net.nz>
765# Date: Wed May 25 14:15:34 NZST 2005
766# Standard-date: 2005-05-25 02:15:34 GMT
767# New-files: lang/de/.arch-ids/block_glossary_random.php.id
768# lang/de/.arch-ids/block_html.php.id
769# New-directories: lang/de/help/questionnaire
770# lang/de/help/questionnaire/.arch-ids
771# Renamed-files: .arch-ids/db_sears.sql.id db/.arch-ids/db_sears.sql.id
772# db_sears.sql db/db_sears.sql
773# Removed-files: lang/be/docs/.arch-ids/release.html.id
774# lang/be/docs/.arch-ids/releaseold.html.id
775# Modified-files: admin/cron.php admin/delete.php
776# admin/editor.html backup/lib.php backup/restore.php
777# New-patches: arch-eduforge@catalyst.net.nz--2004/moodle-org--moodle--1.3.3--patch-15
778# Summary: Updating to latest from MOODLE_14_STABLE (1.4.5+)
Eric Wong6df896b2005-11-23 23:53:55 -0800779# summary can be multiline with a leading space just like the above fields
Martin Langhoffd3968362005-08-30 21:56:52 +1200780# Keywords:
781#
782# Updating yadda tadda tadda madda
783sub parselog {
Eric Wong6df896b2005-11-23 23:53:55 -0800784 my ($ps, $log) = @_;
785 my $key = undef;
Martin Langhoffd3968362005-08-30 21:56:52 +1200786
Eric Wong6df896b2005-11-23 23:53:55 -0800787 # headers we want that contain filenames:
788 my %want_headers = (
789 new_files => 1,
790 modified_files => 1,
791 renamed_files => 1,
792 renamed_directories => 1,
793 removed_files => 1,
794 removed_directories => 1,
795 );
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700796
Eric Wong6df896b2005-11-23 23:53:55 -0800797 chomp (@$log);
798 while ($_ = shift @$log) {
799 if (/^Continuation-of:\s*(.*)/) {
800 $ps->{tag} = $1;
801 $key = undef;
802 } elsif (/^Summary:\s*(.*)$/ ) {
Paolo Bonzinia94f4572007-02-28 21:02:02 +0100803 # summary can be multiline as long as it has a leading space.
804 # we squeeze it onto a single line, though.
Eric Wong6df896b2005-11-23 23:53:55 -0800805 $ps->{summary} = [ $1 ];
806 $key = 'summary';
807 } elsif (/^Creator: (.*)\s*<([^\>]+)>/) {
808 $ps->{author} = $1;
809 $ps->{email} = $2;
810 $key = undef;
811 # any *-files or *-directories can be read here:
812 } elsif (/^([A-Z][a-z\-]+):\s*(.*)$/) {
813 my $val = $2;
814 $key = lc $1;
815 $key =~ tr/-/_/; # too lazy to quote :P
816 if ($want_headers{$key}) {
817 push @{$ps->{$key}}, split(/\s+/, $val);
818 } else {
819 $key = undef;
820 }
821 } elsif (/^$/) {
822 last; # remainder of @$log that didn't get shifted off is message
823 } elsif ($key) {
824 if (/^\s+(.*)$/) {
825 if ($key eq 'summary') {
826 push @{$ps->{$key}}, $1;
827 } else { # files/directories:
828 push @{$ps->{$key}}, split(/\s+/, $1);
829 }
830 } else {
831 $key = undef;
832 }
833 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200834 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700835
Paolo Bonzinia94f4572007-02-28 21:02:02 +0100836 # drop leading empty lines from the log message
837 while (@$log && $log->[0] eq '') {
838 shift @$log;
839 }
840 if (exists $ps->{summary} && @{$ps->{summary}}) {
841 $ps->{summary} = join(' ', @{$ps->{summary}});
842 }
843 elsif (@$log == 0) {
844 $ps->{summary} = 'empty commit message';
845 } else {
846 $ps->{summary} = $log->[0] . '...';
847 }
Eric Wong6df896b2005-11-23 23:53:55 -0800848 $ps->{message} = join("\n",@$log);
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700849
Eric Wong6df896b2005-11-23 23:53:55 -0800850 # skip Arch control files, unescape pika-escaped files
851 foreach my $k (keys %want_headers) {
852 next unless (defined $ps->{$k});
Eric Wong6e331012005-11-23 23:56:31 -0800853 my @tmp = ();
Eric Wong6df896b2005-11-23 23:53:55 -0800854 foreach my $t (@{$ps->{$k}}) {
855 next unless length ($t);
856 next if $t =~ m!\{arch\}/!;
857 next if $t =~ m!\.arch-ids/!;
858 # should we skip this?
859 next if $t =~ m!\.arch-inventory$!;
Martin Langhofff84f9d32005-11-11 18:00:57 +1300860 # tla cat-archive-log will give us filenames with spaces as file\(sp)name - why?
861 # we can assume that any filename with \ indicates some pika escaping that we want to get rid of.
Eric Wong6df896b2005-11-23 23:53:55 -0800862 if ($t =~ /\\/ ){
Eric Wongf88961a2005-11-23 23:48:57 -0800863 $t = (safe_pipe_capture($TLA,'escape','--unescaped',$t))[0];
Martin Langhofff84f9d32005-11-11 18:00:57 +1300864 }
Eric Wong6df896b2005-11-23 23:53:55 -0800865 push @tmp, $t;
Martin Langhoffd3968362005-08-30 21:56:52 +1200866 }
Eric Wong6e331012005-11-23 23:56:31 -0800867 $ps->{$k} = \@tmp;
Martin Langhoffd3968362005-08-30 21:56:52 +1200868 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200869}
870
871# write/read a tag
872sub tag {
873 my ($tag, $commit) = @_;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700874
Martin Langhofffee33652005-11-17 21:20:45 +1300875 if ($opt_o) {
876 $tag =~ s|/|--|g;
877 } else {
Paolo Bonzinid9cb5392007-03-07 10:43:41 +0100878 my $patchname = $tag;
879 $patchname =~ s/.*--//;
880 $tag = git_branchname ($tag) . '--' . $patchname;
Martin Langhofffee33652005-11-17 21:20:45 +1300881 }
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700882
Martin Langhoffd3968362005-08-30 21:56:52 +1200883 if ($commit) {
Eric Wonga7fb51d2005-11-12 01:25:33 -0800884 open(C,">","$git_dir/refs/tags/$tag")
Martin Langhoffd3968362005-08-30 21:56:52 +1200885 or die "Cannot create tag $tag: $!\n";
886 print C "$commit\n"
887 or die "Cannot write tag $tag: $!\n";
888 close(C)
889 or die "Cannot write tag $tag: $!\n";
Eric Wonga7fb51d2005-11-12 01:25:33 -0800890 print " * Created tag '$tag' on '$commit'\n" if $opt_v;
Martin Langhoffd3968362005-08-30 21:56:52 +1200891 } else { # read
Eric Wonga7fb51d2005-11-12 01:25:33 -0800892 open(C,"<","$git_dir/refs/tags/$tag")
Martin Langhoffd3968362005-08-30 21:56:52 +1200893 or die "Cannot read tag $tag: $!\n";
894 $commit = <C>;
895 chomp $commit;
896 die "Error reading tag $tag: $!\n" unless length $commit == 40;
897 close(C)
898 or die "Cannot read tag $tag: $!\n";
899 return $commit;
900 }
901}
902
903# write/read a private tag
904# reads fail softly if the tag isn't there
905sub ptag {
906 my ($tag, $commit) = @_;
Eric Wonga7fb51d2005-11-12 01:25:33 -0800907
908 # don't use subdirs for tags yet, it could screw up other porcelains
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700909 $tag =~ s|/|,|g;
910
Eric Wonga7fb51d2005-11-12 01:25:33 -0800911 my $tag_file = "$ptag_dir/$tag";
912 my $tag_branch_dir = dirname($tag_file);
913 mkpath($tag_branch_dir) unless (-d $tag_branch_dir);
Martin Langhoffd3968362005-08-30 21:56:52 +1200914
915 if ($commit) { # write
Eric Wonga7fb51d2005-11-12 01:25:33 -0800916 open(C,">",$tag_file)
Martin Langhoffd3968362005-08-30 21:56:52 +1200917 or die "Cannot create tag $tag: $!\n";
918 print C "$commit\n"
919 or die "Cannot write tag $tag: $!\n";
920 close(C)
921 or die "Cannot write tag $tag: $!\n";
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700922 $rptags{$commit} = $tag
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200923 unless $tag =~ m/--base-0$/;
Martin Langhoffd3968362005-08-30 21:56:52 +1200924 } else { # read
925 # if the tag isn't there, return 0
Eric Wonga7fb51d2005-11-12 01:25:33 -0800926 unless ( -s $tag_file) {
Martin Langhoffd3968362005-08-30 21:56:52 +1200927 return 0;
928 }
Eric Wonga7fb51d2005-11-12 01:25:33 -0800929 open(C,"<",$tag_file)
Martin Langhoffd3968362005-08-30 21:56:52 +1200930 or die "Cannot read tag $tag: $!\n";
931 $commit = <C>;
932 chomp $commit;
933 die "Error reading tag $tag: $!\n" unless length $commit == 40;
934 close(C)
935 or die "Cannot read tag $tag: $!\n";
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200936 unless (defined $rptags{$commit}) {
937 $rptags{$commit} = $tag;
938 }
Martin Langhoffd3968362005-08-30 21:56:52 +1200939 return $commit;
940 }
941}
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200942
943sub find_parents {
944 #
945 # Identify what branches are merging into me
946 # and whether we are fully merged
947 # git-merge-base <headsha> <headsha> should tell
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700948 # me what the base of the merge should be
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200949 #
950 my $ps = shift;
951
952 my %branches; # holds an arrayref per branch
953 # the arrayref contains a list of
954 # merged patches between the base
955 # of the merge and the current head
956
957 my @parents; # parents found for this commit
958
959 # simple loop to split the merges
960 # per branch
961 foreach my $merge (@{$ps->{merges}}) {
Eric Wong22ff00f2005-11-12 01:29:20 -0800962 my $branch = git_branchname($merge);
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200963 unless (defined $branches{$branch} ){
964 $branches{$branch} = [];
965 }
966 push @{$branches{$branch}}, $merge;
967 }
968
969 #
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700970 # foreach branch find a merge base and walk it to the
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200971 # head where we are, collecting the merged patchsets that
972 # Arch has recorded. Keep that in @have
973 # Compare that with the commits on the other branch
974 # between merge-base and the tip of the branch (@need)
975 # and see if we have a series of consecutive patches
976 # starting from the merge base. The tip of the series
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700977 # of consecutive patches merged is our new parent for
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200978 # that branch.
979 #
980 foreach my $branch (keys %branches) {
Martin Langhoff37f15d52005-09-30 19:15:12 +1200981
982 # check that we actually know about the branch
983 next unless -e "$git_dir/refs/heads/$branch";
984
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200985 my $mergebase = `git-merge-base $branch $ps->{branch}`;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700986 if ($?) {
987 # Don't die here, Arch supports one-way cherry-picking
988 # between branches with no common base (or any relationship
989 # at all beforehand)
990 warn "Cannot find merge base for $branch and $ps->{branch}";
991 next;
992 }
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200993 chomp $mergebase;
994
995 # now walk up to the mergepoint collecting what patches we have
996 my $branchtip = git_rev_parse($ps->{branch});
Linus Torvalds765ac8e2006-02-28 15:07:20 -0800997 my @ancestors = `git-rev-list --topo-order $branchtip ^$mergebase`;
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +1200998 my %have; # collected merges this branch has
999 foreach my $merge (@{$ps->{merges}}) {
1000 $have{$merge} = 1;
1001 }
1002 my %ancestorshave;
1003 foreach my $par (@ancestors) {
1004 $par = commitid2pset($par);
1005 if (defined $par->{merges}) {
1006 foreach my $merge (@{$par->{merges}}) {
1007 $ancestorshave{$merge}=1;
1008 }
1009 }
1010 }
1011 # print "++++ Merges in $ps->{id} are....\n";
1012 # my @have = sort keys %have; print Dumper(\@have);
1013
1014 # merge what we have with what ancestors have
1015 %have = (%have, %ancestorshave);
1016
Junio C Hamanoa6080a02007-06-07 00:04:01 -07001017 # see what the remote branch has - these are the merges we
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +12001018 # will want to have in a consecutive series from the mergebase
1019 my $otherbranchtip = git_rev_parse($branch);
Linus Torvalds765ac8e2006-02-28 15:07:20 -08001020 my @needraw = `git-rev-list --topo-order $otherbranchtip ^$mergebase`;
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +12001021 my @need;
1022 foreach my $needps (@needraw) { # get the psets
1023 $needps = commitid2pset($needps);
1024 # git-rev-list will also
Junio C Hamanoa6080a02007-06-07 00:04:01 -07001025 # list commits merged in via earlier
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +12001026 # merges. we are only interested in commits
1027 # from the branch we're looking at
1028 if ($branch eq $needps->{branch}) {
1029 push @need, $needps->{id};
1030 }
1031 }
1032
1033 # print "++++ Merges from $branch we want are....\n";
1034 # print Dumper(\@need);
1035
1036 my $newparent;
1037 while (my $needed_commit = pop @need) {
1038 if ($have{$needed_commit}) {
1039 $newparent = $needed_commit;
1040 } else {
1041 last; # break out of the while
1042 }
1043 }
1044 if ($newparent) {
1045 push @parents, $newparent;
1046 }
1047
1048
1049 } # end foreach branch
1050
1051 # prune redundant parents
1052 my %parents;
1053 foreach my $p (@parents) {
1054 $parents{$p} = 1;
1055 }
1056 foreach my $p (@parents) {
1057 next unless exists $psets{$p}{merges};
1058 next unless ref $psets{$p}{merges};
1059 my @merges = @{$psets{$p}{merges}};
1060 foreach my $merge (@merges) {
Junio C Hamanoa6080a02007-06-07 00:04:01 -07001061 if ($parents{$merge}) {
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +12001062 delete $parents{$merge};
1063 }
1064 }
1065 }
Eric Wong42f44b02005-11-23 23:52:43 -08001066
Eric Wongf88961a2005-11-23 23:48:57 -08001067 @parents = ();
1068 foreach (keys %parents) {
1069 push @parents, '-p', ptag($_);
1070 }
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +12001071 return @parents;
1072}
1073
1074sub git_rev_parse {
1075 my $name = shift;
1076 my $val = `git-rev-parse $name`;
1077 die "Error: git-rev-parse $name" if $?;
1078 chomp $val;
1079 return $val;
1080}
1081
1082# resolve a SHA1 to a known patchset
1083sub commitid2pset {
1084 my $commitid = shift;
1085 chomp $commitid;
Junio C Hamanoa6080a02007-06-07 00:04:01 -07001086 my $name = $rptags{$commitid}
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +12001087 || die "Cannot find reverse tag mapping for $commitid";
Eric Wonga7fb51d2005-11-12 01:25:33 -08001088 $name =~ s|,|/|;
Junio C Hamanoa6080a02007-06-07 00:04:01 -07001089 my $ps = $psets{$name}
martin@catalyst.net.nzb779d5f2005-09-10 23:42:24 +12001090 || (print Dumper(sort keys %psets)) && die "Cannot find patchset for $name";
1091 return $ps;
1092}
Eric Wong2777ef72005-11-23 23:47:39 -08001093
Eric Wong42f44b02005-11-23 23:52:43 -08001094
Pavel Roskin82e5a822006-07-10 01:50:18 -04001095# an alternative to `command` that allows input to be passed as an array
Eric Wong2777ef72005-11-23 23:47:39 -08001096# to work around shell problems with weird characters in arguments
1097sub safe_pipe_capture {
1098 my @output;
1099 if (my $pid = open my $child, '-|') {
1100 @output = (<$child>);
1101 close $child or die join(' ',@_).": $! $?";
1102 } else {
Eric Wong3e525e62005-11-23 23:55:04 -08001103 exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
Eric Wong2777ef72005-11-23 23:47:39 -08001104 }
1105 return wantarray ? @output : join('',@output);
1106}
1107
Eric Wong42f44b02005-11-23 23:52:43 -08001108# `tla logs -rf -d <dir> | head -n1` or `baz tree-id <dir>`
1109sub arch_tree_id {
1110 my $dir = shift;
1111 chomp( my $ret = (safe_pipe_capture($TLA,'logs','-rf','-d',$dir))[0] );
1112 return $ret;
1113}
1114
1115sub archive_reachable {
1116 my $archive = shift;
1117 return 1 if $reachable{$archive};
1118 return 0 if $unreachable{$archive};
Junio C Hamanoa6080a02007-06-07 00:04:01 -07001119
Eric Wong42f44b02005-11-23 23:52:43 -08001120 if (system "$TLA whereis-archive $archive >/dev/null") {
1121 if ($opt_a && (system($TLA,'register-archive',
1122 "http://mirrors.sourcecontrol.net/$archive") == 0)) {
1123 $reachable{$archive} = 1;
1124 return 1;
1125 }
1126 print STDERR "Archive is unreachable: $archive\n";
1127 $unreachable{$archive} = 1;
1128 return 0;
1129 } else {
1130 $reachable{$archive} = 1;
1131 return 1;
1132 }
1133}