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 $want_remote_repo = 0;
69 my $checked_out_flag = 0;
72 # Flags specific to a particular command
73 my $local_repo_unnecessary = 0;
77 # Figure out where to get the other repositories from.
82 if (defined($defaultrepo)) {
86 # Figure out where to get the other repositories from,
87 # based on where this GHC repo came from.
88 my $branch = `git branch | grep "\* " | sed "s/^\* //"`; chomp $branch;
89 my $remote = `git config branch.$branch.remote`; chomp $remote;
90 $repo = `git config remote.$remote.url`; chomp $repo;
96 if ($repo =~ /^...*:/) {
98 # Above regex says "at least two chars before the :", to avoid
99 # catching Win32 drives ("C:\").
102 # --checked-out is needed if you want to use a checked-out repo
104 if ($checked_out_flag) {
105 $checked_out_tree = 1;
107 $checked_out_tree = 0;
110 # Don't drop the last part of the path if specified with -r, as
111 # it expects repos of the form:
113 # http://darcs.haskell.org
117 # http://darcs.haskell.org/ghc
120 $repo_base =~ s#/[^/]+/?$##;
123 elsif ($repo =~ /^\/|\.\.\/|.:(\/|\\)/) {
124 # Local filesystem, either absolute or relative path
125 # (assumes a checked-out tree):
127 $checked_out_tree = 1;
130 die "Couldn't work out repo";
133 return $repo_base, $checked_out_tree;
140 open IN, "< packages" or die "Can't open packages file";
149 if (/^([^# ]+) +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+)$/) {
151 $line{"localpath"} = $1;
153 $line{"remotepath"} = $3;
155 $line{"upstream"} = $5;
156 push @packages, \%line;
158 elsif (! /^(#.*)?$/) {
159 die "Bad content on line $lineNum of packages file: $_";
172 print "warning: @_\n";
182 message "== running $scm @_";
184 message "== $dir: running $scm @_";
189 system ($scm, @_) == 0
191 or die "$scm failed: $?";
199 my ($scm, $localpath) = @_;
201 if ($scm eq "darcs") {
202 -d "$localpath/_darcs";
205 -d "$localpath/.git";
220 my $wd_before = getcwd;
226 my ($repo_base, $checked_out_tree) = getrepo();
230 for $line (@packages) {
232 $localpath = $$line{"localpath"};
233 $tag = $$line{"tag"};
234 $remotepath = $$line{"remotepath"};
235 $scm = $$line{"vcs"};
236 $upstream = $$line{"upstream"};
238 # Check the SCM is OK as early as possible
239 die "Unknown SCM: $scm" if (($scm ne "darcs") and ($scm ne "git"));
241 # Work out the path for this package in the repo we pulled from
242 if ($checked_out_tree) {
243 $path = "$repo_base/$localpath";
246 $path = "$repo_base/$remotepath";
249 # Work out the arguments we should give to the SCM
250 if ($command =~ /^(?:w|wh|wha|what|whats|whatsn|whatsne|whatsnew)$/) {
251 @scm_args = (($scm eq "darcs" and "whatsnew")
252 or ($scm eq "git" and "status"));
254 # Hack around 'darcs whatsnew' failing if there are no changes
257 elsif ($command =~ /^(?:pus|push)$/) {
259 $want_remote_repo = 1;
261 elsif ($command =~ /^(?:pul|pull)$/) {
263 $want_remote_repo = 1;
264 # Q: should we append the -a argument for darcs repos?
266 elsif ($command =~ /^(?:g|ge|get)$/) {
267 # Skip any repositories we have not included the tag for
268 if (not defined($tags{$tag})) {
273 warning("$localpath already present; omitting") if $localpath ne ".";
277 # The first time round the loop, default the get-mode
278 if ($scm eq "darcs" && not defined($get_mode)) {
279 warning("adding --partial, to override use --complete");
280 $get_mode = "--partial";
283 # The only command that doesn't need a repo
284 $local_repo_unnecessary = 1;
286 if ($scm eq "darcs") {
287 # Note: we can only use the get-mode with darcs for now
288 @scm_args = ("get", $get_mode, $path, $localpath);
291 @scm_args = ("clone", $path, $localpath);
294 elsif ($command =~ /^(?:s|se|sen|send)$/) {
295 @scm_args = (($scm eq "darcs" and "send")
296 or ($scm eq "git" and "send-email"));
297 $want_remote_repo = 1;
299 elsif ($command =~ /^set-origin$/) {
300 @scm_args = ("remote", "set-url", "origin", $path);
302 elsif ($command =~ /^set-push$/) {
303 @scm_args = ("remote", "set-url", "--push", "origin", $path);
304 print "foo\n", @scm_args;
306 elsif ($command =~ /^fetch$/) {
307 @scm_args = ("fetch", "origin");
309 elsif ($command =~ /^new$/) {
310 @scm_args = ("log", "origin..");
313 die "Unknown command: $command";
316 # Actually execute the command
317 if (repoexists ($scm, $localpath)) {
318 if ($want_remote_repo) {
319 if ($scm eq "darcs") {
320 scm (".", $scm, @scm_args, @_, "--repodir=$localpath", $path);
322 # git pull doesn't like to be used with --work-dir
323 # I couldn't find an alternative to chdir() here
324 scm ($localpath, $scm, @scm_args, @_, $path, "master");
327 # git status *must* be used with --work-dir, if we don't chdir() to the dir
328 scm ($localpath, $scm, @scm_args, @_);
331 elsif ($local_repo_unnecessary) {
332 # Don't bother to change directory in this case
333 scm (".", $scm, @scm_args, @_);
336 message "== Required repo $localpath is missing! Skipping";
339 message "== $localpath repo not present; skipping";
345 if (! -d ".git" || ! -d "compiler") {
346 die "error: sync-all must be run from the top level of the ghc tree."
354 # We handle -q here as well as lower down as we need to skip over it
355 # if it comes before the source-control command
359 elsif ($arg eq "-s") {
362 elsif ($arg eq "-r") {
363 $defaultrepo = shift;
365 elsif ($arg eq "--ignore-failure") {
368 elsif ($arg eq "--complete" || $arg eq "--partial") {
371 # Use --checked-out if the remote repos are a checked-out tree,
372 # rather than the master trees.
373 elsif ($arg eq "--checked-out") {
374 $checked_out_flag = 1;
376 # --<tag> says we grab the libs tagged 'tag' with
377 # 'get'. It has no effect on the other commands.
378 elsif ($arg =~ m/^--/) {
384 if (grep /^-q$/, @_) {
392 # Get the built in help
394 What do you want to do?
410 Available package-tags are:
413 # Collect all the tags in the packages file
415 open IN, "< packages" or die "Can't open packages file";
418 if (/^([^# ]+) +(?:([^ ]+) +)?([^ ]+) +([^ ]+)/) {
419 if (defined($2) && $2 ne "-") {
420 $available_tags{$2} = 1;
423 elsif (! /^(#.*)?$/) {
429 # Show those tags and the help text
430 my @available_tags = keys %available_tags;
431 print "$help@available_tags\n";
435 # Give the command and rest of the arguments to the main loop