8 # ./sync-all [-q] [-s] [--ignore-failure] [-r repo]
9 # [--nofib] [--testsuite] [--checked-out] cmd [git flags]
11 # Applies the command "cmd" to each repository in the tree.
12 # sync-all will try to do the right thing for both git and darcs repositories.
15 # ./sync-all -r http://darcs.haskell.org/ghc get
16 # To get any repos which do not exist in the local tree
19 # To pull everything from the default repos
21 # -------------- Flags -------------------
22 # -q says to be quite, and -s to be silent.
24 # --ignore-failure says to ignore errors and move on to the next repository
26 # -r repo says to use repo as the location of package repositories
28 # --checked-out says that the remote repo is in checked-out layout, as
29 # opposed to the layout used for the main repo. By default a repo on
30 # the local filesystem is assumed to be checked-out, and repos accessed
31 # via HTTP or SSH are assumed to be in the main repo layout; use
32 # --checked-out to override the latter.
34 # --nofib, --testsuite also get the nofib and testsuite repos respectively
36 # ------------ Which repos to use -------------
37 # sync-all uses the following algorithm to decide which remote repos to use
39 # It always computes the remote repos from a single base, $repo_base
40 # How is $repo_base set?
41 # If you say "-r repo", then that's $repo_base
42 # otherwise $repo_base is set by asking git where the ghc repo came
43 # from, and removing the last component (e.g. /ghc.git/ of /ghc/).
45 # Then sync-all iterates over the package found in the file
46 # ./packages; see that file for a description of the contents.
48 # If $repo_base looks like a local filesystem path, or if you give
49 # the --checked-out flag, sync-all works on repos of form
50 # $repo_base/<local-path>
51 # otherwise sync-all works on repos of form
52 # $repo_base/<remote-path>
53 # This logic lets you say
54 # both sync-all -r http://darcs.haskell.org/ghc-6.12 pull
55 # and sync-all -r ../HEAD pull
56 # The latter is called a "checked-out tree".
58 # NB: sync-all *ignores* the defaultrepo of all repos other than the
59 # root one. So the remote repos must be laid out in one of the two
60 # formats given by <local-path> and <remote-path> in the file 'packages'.
62 $| = 1; # autoflush stdout after each print, to avoid output after die
67 my $ignore_failure = 0;
68 my $checked_out_flag = 0;
73 # Figure out where to get the other repositories from.
78 if (defined($defaultrepo)) {
82 # Figure out where to get the other repositories from,
83 # based on where this GHC repo came from.
84 my $branch = `git branch | grep "\* " | sed "s/^\* //"`; chomp $branch;
85 my $remote = `git config branch.$branch.remote`; chomp $remote;
86 $repo = `git config remote.$remote.url`; chomp $repo;
92 if ($repo =~ /^...*:/) {
94 # Above regex says "at least two chars before the :", to avoid
95 # catching Win32 drives ("C:\").
98 # --checked-out is needed if you want to use a checked-out repo
100 if ($checked_out_flag) {
101 $checked_out_tree = 1;
103 $checked_out_tree = 0;
106 # Don't drop the last part of the path if specified with -r, as
107 # it expects repos of the form:
109 # http://darcs.haskell.org
113 # http://darcs.haskell.org/ghc
116 $repo_base =~ s#/[^/]+/?$##;
119 elsif ($repo =~ /^\/|\.\.\/|.:(\/|\\)/) {
120 # Local filesystem, either absolute or relative path
121 # (assumes a checked-out tree):
123 $checked_out_tree = 1;
126 die "Couldn't work out repo";
129 return $repo_base, $checked_out_tree;
136 open IN, "< packages" or die "Can't open packages file";
145 if (/^([^# ]+) +([^ ]+) +([^ ]+) +([^ ]+)$/) {
147 $line{"localpath"} = $1;
149 $line{"remotepath"} = $3;
151 push @packages, \%line;
153 elsif (! /^(#.*)?$/) {
154 die "Bad content on line $lineNum of packages file: $_";
167 print "warning: @_\n";
177 message "== running $scm @_";
179 message "== $dir: running $scm @_";
184 system ($scm, @_) == 0
186 or die "$scm failed: $?";
205 my $wd_before = getcwd;
210 my ($repo_base, $checked_out_tree) = getrepo();
212 my $is_github_repo = $repo_base =~ m/(git@|git:\/\/|https:\/\/)github.com/;
218 if ($command =~ /^remote$/) {
219 while (@_ > 0 && $_[0] =~ /^-/) {
222 if (@_ < 1) { help(); }
224 if ($subcommand ne 'add' && $subcommand ne 'rm' && $subcommand ne 'set-url') {
227 while (@_ > 0 && $_[0] =~ /^-/) {
230 if (($subcommand eq 'add' || $subcommand eq 'rm') && @_ < 1) {
232 } elsif (@_ < 1) { # set-url
233 $branch_name = 'origin';
235 $branch_name = shift;
237 } elsif ($command eq 'new') {
239 $branch_name = 'origin';
241 $branch_name = shift;
247 for $line (@packages) {
249 $localpath = $$line{"localpath"};
250 $tag = $$line{"tag"};
251 $remotepath = $$line{"remotepath"};
252 $scm = $$line{"vcs"};
254 # Check the SCM is OK as early as possible
255 die "Unknown SCM: $scm" if (($scm ne "darcs") and ($scm ne "git"));
257 # We can't create directories on GitHub, so we translate
258 # "package/foo" into "package-foo".
259 if ($is_github_repo) {
260 $remotepath =~ s/\//-/;
263 # Work out the path for this package in the repo we pulled from
264 if ($checked_out_tree) {
265 $path = "$repo_base/$localpath";
268 $path = "$repo_base/$remotepath";
271 if ($command =~ /^(?:g|ge|get)$/) {
272 # Skip any repositories we have not included the tag for
273 if (not defined($tags{$tag})) {
276 if ($tags{$tag} == 0) {
281 warning("$localpath already present; omitting")
282 if $localpath ne ".";
284 scm ($localpath, $scm, "config", "core.ignorecase", "true");
289 # Note that we use "." as the path, as $localpath
291 if ($scm eq "darcs") {
292 # The first time round the loop, default the get-mode
293 if (not defined($get_mode)) {
294 warning("adding --partial, to override use --complete");
295 $get_mode = "--partial";
297 scm (".", $scm, "get", $get_mode, $path, $localpath, @args);
300 scm (".", $scm, "clone", $path, $localpath, @args);
301 scm ($localpath, $scm, "config", "core.ignorecase", "true");
306 if (-d "$localpath/_darcs") {
307 if (-d "$localpath/.git") {
308 die "Found both _darcs and .git in $localpath";
311 } elsif (-d "$localpath/.git") {
313 } elsif ($tag eq "") {
314 die "Required repo $localpath is missing";
316 message "== $localpath repo not present; skipping";
320 # Work out the arguments we should give to the SCM
321 if ($command =~ /^(?:w|wh|wha|what|whats|whatsn|whatsne|whatsnew|status)$/) {
322 if ($scm eq "darcs") {
323 $command = "whatsnew";
325 elsif ($scm eq "git") {
332 # Hack around 'darcs whatsnew' failing if there are no changes
334 scm ($localpath, $scm, $command, @args);
336 elsif ($command =~ /^commit$/) {
337 # git fails if there is nothing to commit, so ignore failures
339 scm ($localpath, $scm, "commit", @args);
341 elsif ($command =~ /^(?:pus|push)$/) {
342 scm ($localpath, $scm, "push", @args);
344 elsif ($command =~ /^(?:pul|pull)$/) {
345 scm ($localpath, $scm, "pull", @args);
347 elsif ($command =~ /^(?:s|se|sen|send)$/) {
348 if ($scm eq "darcs") {
351 elsif ($scm eq "git") {
352 $command = "send-email";
357 scm ($localpath, $scm, $command, @args);
359 elsif ($command =~ /^fetch$/) {
360 scm ($localpath, $scm, "fetch", @args);
362 elsif ($command =~ /^new$/) {
363 my @scm_args = ("log", "$branch_name..");
364 scm ($localpath, $scm, @scm_args, @args);
366 elsif ($command =~ /^log$/) {
367 scm ($localpath, $scm, "log", @args);
369 elsif ($command =~ /^remote$/) {
371 if ($subcommand eq 'add') {
372 @scm_args = ("remote", "add", $branch_name, $path);
373 } elsif ($subcommand eq 'rm') {
374 @scm_args = ("remote", "rm", $branch_name);
375 } elsif ($subcommand eq 'set-url') {
376 @scm_args = ("remote", "set-url", $branch_name, $path);
378 scm ($localpath, $scm, @scm_args, @args);
380 elsif ($command =~ /^checkout$/) {
381 # Not all repos are necessarily branched, so ignore failure
383 scm ($localpath, $scm, "checkout", @args)
384 unless $scm eq "darcs";
386 elsif ($command =~ /^grep$/) {
387 # Hack around 'git grep' failing if there are no matches
389 scm ($localpath, $scm, "grep", @args)
390 unless $scm eq "darcs";
392 elsif ($command =~ /^clean$/) {
393 scm ($localpath, $scm, "clean", @args)
394 unless $scm eq "darcs";
396 elsif ($command =~ /^reset$/) {
397 scm ($localpath, $scm, "reset", @args)
398 unless $scm eq "darcs";
400 elsif ($command =~ /^config$/) {
401 scm ($localpath, $scm, "config", @args)
402 unless $scm eq "darcs";
405 die "Unknown command: $command";
413 # Get the built in help
415 What do you want to do?
429 * remote add <branch-name>
430 * remote rm <branch-name>
431 * remote set-url [--push] <branch-name>
439 Available package-tags are:
442 # Collect all the tags in the packages file
444 open IN, "< packages" or die "Can't open packages file";
447 if (/^([^# ]+) +(?:([^ ]+) +)?([^ ]+) +([^ ]+)/) {
448 if (defined($2) && $2 ne "-") {
449 $available_tags{$2} = 1;
452 elsif (! /^(#.*)?$/) {
458 # Show those tags and the help text
459 my @available_tags = keys %available_tags;
460 print "$help@available_tags\n";
465 if (! -d ".git" || ! -d "compiler") {
466 die "error: sync-all must be run from the top level of the ghc tree."
474 # We handle -q here as well as lower down as we need to skip over it
475 # if it comes before the source-control command
479 elsif ($arg eq "-s") {
482 elsif ($arg eq "-r") {
483 $defaultrepo = shift;
485 elsif ($arg eq "--ignore-failure") {
488 elsif ($arg eq "--complete" || $arg eq "--partial") {
491 # Use --checked-out if the remote repos are a checked-out tree,
492 # rather than the master trees.
493 elsif ($arg eq "--checked-out") {
494 $checked_out_flag = 1;
496 # --<tag> says we grab the libs tagged 'tag' with
497 # 'get'. It has no effect on the other commands.
498 elsif ($arg =~ m/^--no-(.*)$/) {
501 elsif ($arg =~ m/^--(.*)$/) {
506 if (grep /^-q$/, @_) {
517 # Give the command and rest of the arguments to the main loop