Some sync-all refactoring
[ghc.git] / sync-all
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Cwd;
5
6 $| = 1; # autoflush stdout after each print, to avoid output after die
7
8 my $initial_working_directory;
9
10 my $defaultrepo;
11 my @packages;
12 my $verbose = 2;
13 my $try_to_resume = 0;
14 my $ignore_failure = 0;
15 my $checked_out_flag = 0; # NOT the opposite of bare_flag (describes remote repo state)
16 my $get_mode;
17 my $bare_flag = ""; # NOT the opposite of checked_out_flag (describes local repo state)
18
19 my %tags;
20
21 sub inDir {
22     my $dir = shift;
23     my $code = shift;
24
25     if ($dir ne '.') {
26         chdir($dir);
27     }
28
29     my $result = &$code();
30
31     if ($dir ne '.') {
32         chdir($initial_working_directory);
33     }
34     return $result;
35 }
36
37 sub parsePackages {
38     my @repos;
39     my $lineNum;
40
41     open IN, "< packages.conf"
42         or open IN, "< packages" # clashes with packages directory when using --bare
43         or die "Can't open packages file (or packages.conf)";
44     @repos = <IN>;
45     close IN;
46
47     @packages = ();
48     $lineNum = 0;
49     foreach (@repos) {
50         chomp;
51         $lineNum++;
52         if (/^([^# ]+) +([^ ]+) +([^ ]+)$/) {
53             my %line;
54             $line{"localpath"}  = $1;
55             $line{"tag"}        = $2;
56             $line{"remotepath"} = $3;
57             push @packages, \%line;
58         }
59         elsif (! /^(#.*)?$/) {
60             die "Bad content on line $lineNum of packages file: $_";
61         }
62     }
63 }
64
65 sub tryReadFile {
66     my $filename = shift;
67     my @lines;
68
69     open (FH, $filename) or return "";
70     @lines = <FH>;
71     close FH;
72     return join('', @lines);
73 }
74
75 sub message {
76     if ($verbose >= 2) {
77         print "@_\n";
78     }
79 }
80
81 sub warning {
82     if ($verbose >= 1) {
83         print "warning: @_\n";
84     }
85 }
86
87 sub gitNewWorkdir {
88     my $dir = shift;
89     my $target = shift;
90     my $target_dir = "$target/$dir";
91
92     if ($dir eq '.') {
93         message "== running git-new-workdir . $target_dir @_";
94     } else {
95         message "== $dir: running git-new-workdir . $target_dir @_";
96         chdir($dir);
97     }
98
99     system ("git-new-workdir", ".", $target_dir, @_) == 0
100         or $ignore_failure
101         or die "git-new-workdir failed: $?";
102
103     if ($dir ne '.') {
104         chdir($initial_working_directory);
105     }
106 }
107
108 sub git {
109     my $dir = shift;
110     my @args = @_;
111
112     &inDir($dir, sub {
113         my $prefix = $dir eq '.' ? "" : "$dir: ";
114         message "== ${prefix}running git @args";
115
116         system ("git", @args) == 0
117             or $ignore_failure
118             or die "git failed: $?";
119     });
120 }
121
122 sub readgit {
123     my $dir = shift;
124     my @args = @_;
125
126     &inDir($dir, sub {
127         open my $fh, '-|', 'git', @args
128             or die "Executing git @args failed: $!";
129         my $line = <$fh>;
130         $line = "" unless defined($line);
131         chomp $line;
132         close $fh;
133         return $line;
134     });
135 }
136
137 sub configure_repository {
138     my $localpath = shift;
139
140     &git($localpath, "config", "--local", "core.ignorecase", "true");
141
142     my $autocrlf = &readgit($localpath, 'config', '--get', 'core.autocrlf');
143     if ($autocrlf eq "true") {
144         &git($localpath, "config", "--local", "core.autocrlf", "false");
145         &git($localpath, "reset", "--hard");
146     }
147 }
148
149 # Figure out where to get the other repositories from.
150 sub getrepo {
151     my $repo;
152
153     if (defined($defaultrepo)) {
154         $repo = $defaultrepo;
155         chomp $repo;
156     } else {
157         # Figure out where to get the other repositories from,
158         # based on where this GHC repo came from.
159         my $git_dir = $bare_flag ? "ghc.git" : ".";
160         my $branch = &readgit($git_dir, "rev-parse", "--abbrev-ref", "HEAD");
161         die "Bad branch: $branch"
162             unless $branch =~ /^[a-zA-Z][a-zA-Z0-9.-]*$/;
163         my $remote = &readgit($git_dir, "config", "branch.$branch.remote");
164         if ($remote eq "") {
165             # remotes are not mandatory for branches (e.g. not recorded by default for bare repos)
166             $remote = "origin";
167         }
168         die "Bad remote: $remote"
169             unless $remote =~ /^[a-zA-Z][a-zA-Z0-9.-]*$/;
170         $repo = &readgit($git_dir, "config", "remote.$remote.url");
171     }
172
173     my $repo_base;
174     my $checked_out_tree;
175
176     if ($repo =~ /^...*:/) {
177         # HTTP or SSH
178         # Above regex says "at least two chars before the :", to avoid
179         # catching Win32 drives ("C:\").
180         $repo_base = $repo;
181
182         # --checked-out is needed if you want to use a checked-out repo
183         # over SSH or HTTP
184         if ($checked_out_flag) {
185             $checked_out_tree = 1;
186         } else {
187             $checked_out_tree = 0;
188         }
189
190         # Don't drop the last part of the path if specified with -r, as
191         # it expects repos of the form:
192         #
193         #   http://darcs.haskell.org
194         #
195         # rather than
196         #
197         #   http://darcs.haskell.org/ghc
198         #
199         if (!$defaultrepo) {
200             $repo_base =~ s#/[^/]+/?$##;
201         }
202     }
203     elsif ($repo =~ /^\/|\.\.\/|.:(\/|\\)/) {
204         # Local filesystem, either absolute (C:/ or /) or relative (../) path
205         $repo_base = $repo;
206         if (-f "$repo/HEAD") {
207             # assume a local mirror:
208             $checked_out_tree = 0;
209             $repo_base =~ s#/[^/]+/?$##;
210         } elsif (-d "$repo/ghc.git") {
211             # assume a local mirror:
212             $checked_out_tree = 0;
213         } else {
214             # assume a checked-out tree:
215             $checked_out_tree = 1;
216         }
217     }
218     else {
219         die "Couldn't work out repo";
220     }
221
222     return $repo_base, $checked_out_tree;
223 }
224
225 sub gitall {
226     my $command = shift;
227
228     my $localpath;
229     my $tag;
230     my $remotepath;
231     my $line;
232     my $branch_name;
233     my $subcommand;
234
235     my $path;
236
237     my @args;
238
239     my $started;
240     my $doing;
241     my $start_repo;
242
243     my ($repo_base, $checked_out_tree) = getrepo();
244
245     my $is_github_repo = $repo_base =~ m/(git@|git:\/\/|https:\/\/)github.com/;
246
247     parsePackages;
248
249     @args = ();
250
251     if ($command =~ /^remote$/) {
252         while (@_ > 0 && $_[0] =~ /^-/) {
253             push(@args,shift);
254         }
255         if (@_ < 1) { help(1); }
256         $subcommand = shift;
257         if ($subcommand ne 'add' &&
258             $subcommand ne 'rm' &&
259             $subcommand ne 'set-branches' &&
260             $subcommand ne 'set-url') {
261             help(1);
262         }
263         while (@_ > 0 && $_[0] =~ /^-/) {
264             push(@args,shift);
265         }
266         if (($subcommand eq 'add' || $subcommand eq 'rm') && @_ < 1) {
267             help(1);
268         } elsif (@_ < 1) { # set-url
269             $branch_name = 'origin';
270         } else {
271             $branch_name = shift;
272         }
273     } elsif ($command eq 'new') {
274         if (@_ < 1) {
275             $branch_name = 'origin';
276         } else {
277             $branch_name = shift;
278         }
279     }
280
281     push(@args, @_);
282
283     # $doing is a good enough approximation to what we are doing that
284     # we can use it to check that --resume is resuming the right command
285     $doing = join(" ", ($command, @args));
286     $started = 1;
287     if ($try_to_resume && -f "resume") {
288         my $what;
289         open RESUME, "< resume"
290             or die "Can't open resume file";
291         $start_repo = <RESUME>;
292         chomp $start_repo;
293         $what = <RESUME>;
294         chomp $what;
295         close RESUME;
296         if ($what eq $doing) {
297             $started = 0;
298         }
299     }
300
301     for $line (@packages) {
302         $tag        = $$line{"tag"};
303         # Use the "remote" structure for bare git repositories
304         $localpath  = ($bare_flag) ?
305                       $$line{"remotepath"} : $$line{"localpath"};
306         $remotepath = ($checked_out_tree) ?
307                       $$line{"localpath"}  : $$line{"remotepath"};
308
309         if (!$started) {
310             if ($start_repo eq $localpath) {
311                 $started = 1;
312             }
313             else {
314                 next;
315             }
316         }
317
318         open RESUME, "> resume.tmp";
319         print RESUME "$localpath\n";
320         print RESUME "$doing\n";
321         close RESUME;
322         rename "resume.tmp", "resume";
323
324         # We can't create directories on GitHub, so we translate
325         # "packages/foo" into "package-foo".
326         if ($is_github_repo) {
327             $remotepath =~ s/\//-/;
328         }
329
330         # Construct the path for this package in the repo we pulled from
331         $path = "$repo_base/$remotepath";
332
333         if ($command eq "get") {
334             next if $remotepath eq "-"; # "git submodule init/update" will get this later
335
336             # Skip any repositories we have not included the tag for
337             if (not defined($tags{$tag})) {
338                 $tags{$tag} = 0;
339             }
340             if ($tags{$tag} == 0) {
341                 next;
342             }
343
344             if (-d $localpath) {
345                 warning("$localpath already present; omitting")
346                     if $localpath ne ".";
347                 &configure_repository($localpath);
348                 next;
349             }
350
351             # Note that we use "." as the path, as $localpath
352             # doesn't exist yet.
353             my @argsWithBare = @args;
354             push @argsWithBare, $bare_flag if $bare_flag;
355             &git(".", "clone", $path, $localpath, @argsWithBare);
356             &configure_repository($localpath);
357             next;
358         }
359
360         my $git_repo_present = 1 if -e "$localpath/.git" || ($bare_flag && -d "$localpath");
361         if (not $git_repo_present) {
362             if ($tag eq "") {
363                 die "Required repo $localpath is missing";
364             }
365             else {
366                  message "== $localpath repo not present; skipping";
367                  next;
368             }
369         }
370
371         # Work out the arguments we should give to the SCM
372         if ($command eq "status") {
373             &git($localpath, $command, @args);
374         }
375         elsif ($command eq "commit") {
376             # git fails if there is nothing to commit, so ignore failures
377             $ignore_failure = 1;
378             &git($localpath, "commit", @args);
379         }
380         elsif ($command eq "check_submodules") {
381             # If we have a submodule then check whether it is up-to-date
382             if ($remotepath eq "-") {
383                 my %remote_heads;
384
385                 message "== Checking sub-module $localpath";
386
387                 chdir($localpath);
388
389                 open my $lsremote, '-|', 'git', 'ls-remote', '--heads', '-q'
390                     or die "Executing ls-remote failed: $!";
391                 while (<$lsremote>) {
392                     if (/^([0-9a-f]{40})\s*refs\/heads\//) {
393                         $remote_heads{$1} = 1;
394                     }
395                     else {
396                         die "Bad output from ls-remote: $_";
397                     }
398                 }
399                 close($lsremote);
400
401                 my $myhead = &readgit('.', 'rev-parse', '--verify', 'HEAD');
402
403                 if (not defined($remote_heads{$myhead})) {
404                     die "Sub module $localpath needs to be pushed; see http://hackage.haskell.org/trac/ghc/wiki/Repositories/Upstream";
405                 }
406                 
407                 chdir($initial_working_directory);
408             }
409         }
410         elsif ($command eq "push") {
411             # We don't automatically push to the submodules. If you want
412             # to push to them then you need to use a special command, as
413             # described on
414             # http://hackage.haskell.org/trac/ghc/wiki/Repositories/Upstream
415             if ($remotepath ne "-") {
416                 &git($localpath, "push", @args);
417             }
418         }
419         elsif ($command eq "pull") {
420             my $realcmd;
421             my @realargs;
422             if ($remotepath eq "-") {
423                 # Only fetch for the submodules. "git submodule update"
424                 # will take care of making us point to the right commit.
425                 $realcmd = "fetch";
426                 # we like "sync-all pull --rebase" to work:
427                 @realargs = grep(!/--rebase/,@args);
428             }
429             else {
430                 $realcmd = "pull";
431                 @realargs = @args;
432             }
433             &git($localpath, $realcmd, @realargs);
434         }
435         elsif ($command eq "new-workdir") {
436             gitNewWorkdir ($localpath, @args);
437         }
438         elsif ($command eq "send") {
439             $command = "send-email";
440             &git($localpath, $command, @args);
441         }
442         elsif ($command eq "fetch") {
443             &git($localpath, "fetch", @args);
444         }
445         elsif ($command eq "new") {
446             my @scm_args = ("log", "$branch_name..");
447             &git($localpath, @scm_args, @args);
448         }
449         elsif ($command eq "log") {
450             &git($localpath, "log", @args);
451         }
452         elsif ($command eq "remote") {
453             my @scm_args;
454             my $rpath;
455             $ignore_failure = 1;
456             if ($remotepath eq '-') {
457                 $rpath = "$repo_base/$localpath";
458             } else {
459                 $rpath = $path;
460             }
461             if ($subcommand eq 'add') {
462                 @scm_args = ("remote", "add", $branch_name, $rpath);
463             } elsif ($subcommand eq 'rm') {
464                 @scm_args = ("remote", "rm", $branch_name);
465             } elsif ($subcommand eq 'set-branches') {
466                 @scm_args = ("remote", "set-branches", $branch_name);
467             } elsif ($subcommand eq 'set-url') {
468                 @scm_args = ("remote", "set-url", $branch_name, $rpath);
469             }
470             &git($localpath, @scm_args, @args);
471         }
472         elsif ($command eq "checkout") {
473             # Not all repos are necessarily branched, so ignore failure
474             $ignore_failure = 1;
475             &git($localpath, "checkout", @args);
476         }
477         elsif ($command eq "grep") {
478             # Hack around 'git grep' failing if there are no matches
479             $ignore_failure = 1;
480             &git($localpath, "grep", @args);
481         }
482         elsif ($command eq "diff") {
483             &git($localpath, "diff", @args);
484         }
485         elsif ($command eq "clean") {
486             &git($localpath, "clean", @args);
487         }
488         elsif ($command eq "reset") {
489             &git($localpath, "reset", @args);
490         }
491         elsif ($command eq "branch") {
492             &git($localpath, "branch", @args);
493         }
494         elsif ($command eq "config") {
495             &git($localpath, "config", @args);
496         }
497         elsif ($command eq "repack") {
498             &git($localpath, "repack", @args);
499         }
500         elsif ($command eq "format-patch") {
501             &git($localpath, "format-patch", @args);
502         }
503         elsif ($command eq "gc") {
504             &git($localpath, "gc", @args);
505         }
506         elsif ($command eq "tag") {
507             &git($localpath, "tag", @args);
508         }
509         else {
510             die "Unknown command: $command";
511         }
512     }
513
514     unlink "resume";
515 }
516
517 sub help
518 {
519         my $exit = shift;
520
521         # Get the built in help
522         my $help = <<END;
523 Usage:
524
525 ./sync-all [-q] [-s] [--ignore-failure] [-r repo] [--checked-out] [--bare]
526            [--nofib] [--extra] [--testsuite] [--no-dph] [--resume]
527            cmd [git flags]
528
529 Applies the command "cmd" to each repository in the tree.
530
531 A full repository tree is obtained by first cloning the ghc
532 repository, then getting the subrepositories with "sync-all get":
533
534   \$ git clone http://darcs.haskell.org/ghc.git
535   \$ cd ghc
536   \$ ./sync-all get
537
538 After this, "./sync-all pull" will pull from the original repository
539 tree.
540
541 A remote pointing to another local repository tree can be added like
542 this:
543
544   \$ ./sync-all -r /path/to/ghc remote add otherlocal
545
546 and then we can pull from this other tree with
547
548   \$ ./sync-all pull otherlocal
549
550 -------------- Commands -----------------
551 get
552
553     Clones all sub-repositories from the same place that the ghc
554     repository was cloned from. See "which repos to use" below
555     for details of how the subrepositories are laid out.
556
557     There are various --<package-tag> options that can be given
558     before "get" that enable extra repositories. The full list is
559     given at the end of this help. For example:
560
561     ./sync-all --testsuite get
562
563     would get the testsuite repository in addition to the usual set of
564     subrepositories.
565
566 remote add <remote-name>
567 remote rm <remote-name>
568 remote set-url [--push] <remote-name>
569
570     Runs a "git remote" command on each subrepository, adjusting the
571     repository location in each case appropriately. For example, to
572     add a new remote pointing to the upstream repositories:
573
574     ./sync-all -r http://darcs.haskell.org/ remote add upstream
575
576     The -r flag points to the root of the repository tree (see "which
577     repos to use" below). For a repository on the local filesystem it
578     would point to the ghc repository, and for a remote repository it
579     points to the directory containing "ghc.git".
580
581 These commands just run the equivalent git command on each repository, passing
582 any extra arguments to git:
583
584   branch
585   checkout
586   clean
587   commit
588   config
589   diff
590   fetch
591   format-patch
592   gc
593   grep
594   log
595   new
596   new-workdir
597   pull
598   push
599   repack
600   reset
601   send
602   status
603   tag
604
605 -------------- Flags -------------------
606 These flags are given *before* the command and modify the way sync-all behaves.
607 Flags given *after* the command are passed to git.
608
609   -q says to be quiet, and -s to be silent.
610
611   --resume will restart a command that failed, from the repo at which it
612   failed. This means you don't need to wait while, e.g., "pull" goes through
613   all the repos it's just pulled, and tries to pull them again.
614
615   --ignore-failure says to ignore errors and move on to the next repository
616
617   -r repo says to use repo as the location of package repositories
618
619   --checked-out says that the remote repo is in checked-out layout, as opposed
620   to the layout used for the main repo. By default a repo on the local
621   filesystem is assumed to be checked-out, and repos accessed via HTTP or SSH
622   are assumed to be in the main repo layout; use --checked-out to override the
623   latter.
624
625   --bare says that the local repo is in bare layout, same as the main repo. It
626   also means that these repos are bare. You only have to use this flag if you
627   don't have a bare ghc.git in the current directory and would like to 'get'
628   all of the repos bare. Requires packages.conf to be present in the current
629   directory (a renamed packages file from the main ghc repo).
630
631   Note: --checked-out and --bare flags are NOT the opposite of each other.
632         --checked-out: describes the layout of the remote repository tree.
633         --bare:        describes the layout of the local repository tree.
634
635   --nofib also clones the nofib benchmark suite
636
637   --testsuite also clones the ghc testsuite 
638
639   --extra also clone some extra library packages
640
641   --no-dph avoids cloning the dph pacakges
642
643
644 ------------ Checking out a branch -------------
645 To check out a branch you can run the following command:
646
647   \$ ./sync-all checkout ghc-7.4
648
649
650 ------------ Which repos to use -------------
651 sync-all uses the following algorithm to decide which remote repos to use
652
653 It always computes the remote repos from a single base, <repo_base> How is
654 <repo_base> set? If you say "-r repo", then that's <repo_base> otherwise
655 <repo_base> is set by asking git where the ghc repo came from, and removing the
656 last component (e.g. /ghc.git/ or /ghc/).
657
658 Then sync-all iterates over the package found in the file ./packages; see that
659 file for a description of the contents.
660
661 If <repo_base> looks like a local filesystem path, or if you give the
662 --checked-out flag, sync-all works on repos of form:
663
664   <repo_base>/<local-path>
665
666 otherwise sync-all works on repos of form:
667
668   <repo_base>/<remote-path>
669
670 This logic lets you say
671   both    sync-all -r http://darcs.haskell.org/ghc-6.12 remote add ghc-6.12
672   and     sync-all -r ../working remote add working
673 The latter is called a "checked-out tree".
674
675 sync-all *ignores* the defaultrepo of all repos other than the root one. So the
676 remote repos must be laid out in one of the two formats given by <local-path>
677 and <remote-path> in the file 'packages'.
678
679 Available package-tags are:
680 END
681
682         # Collect all the tags in the packages file
683         my %available_tags;
684         open IN, "< packages.conf"
685             or open IN, "< packages" # clashes with packages directory when using --bare
686             or die "Can't open packages file (or packages.conf)";
687         while (<IN>) {
688             chomp;
689             if (/^([^# ]+) +(?:([^ ]+) +)?([^ ]+) +([^ ]+)/) {
690                 if (defined($2) && $2 ne "-") {
691                     $available_tags{$2} = 1;
692                 }
693             }
694             elsif (! /^(#.*)?$/) {
695                 die "Bad line: $_";
696             }
697         }
698         close IN;
699
700         # Show those tags and the help text
701         my @available_tags = keys %available_tags;
702         print "$help@available_tags\n\n";
703         exit $exit;
704 }
705
706 sub main {
707
708     $tags{"-"} = 1;
709     $tags{"dph"} = 1;
710
711     while ($#_ ne -1) {
712         my $arg = shift;
713         # We handle -q here as well as lower down as we need to skip over it
714         # if it comes before the source-control command
715         if ($arg eq "-q") {
716             $verbose = 1;
717         }
718         elsif ($arg eq "-s") {
719             $verbose = 0;
720         }
721         elsif ($arg eq "-r") {
722             $defaultrepo = shift;
723         }
724         elsif ($arg eq "--resume") {
725             $try_to_resume = 1;
726         }
727         elsif ($arg eq "--ignore-failure") {
728             $ignore_failure = 1;
729         }
730         elsif ($arg eq "--complete" || $arg eq "--partial") {
731             $get_mode = $arg;
732         }
733         # Use --checked-out if the _remote_ repos are a checked-out tree,
734         # rather than the master trees.
735         elsif ($arg eq "--checked-out") {
736             $checked_out_flag = 1;
737         }
738         # Use --bare if the _local_ repos are bare repos,
739         # rather than a checked-out tree.
740         elsif ($arg eq "--bare") {
741             $bare_flag = $arg;
742         }
743         elsif ($arg eq "--help") {
744             help(0);
745         }
746         # --<tag> says we grab the libs tagged 'tag' with
747         # 'get'. It has no effect on the other commands.
748         elsif ($arg =~ m/^--no-(.*)$/) {
749             $tags{$1} = 0;
750         }
751         elsif ($arg =~ m/^--(.*)$/) {
752             $tags{$1} = 1;
753         }
754         else {
755             unshift @_, $arg;
756             if (grep /^-q$/, @_) {
757                 $verbose = 1;
758             }
759             last;
760         }
761     }
762
763     # check for ghc repositories in cwd
764     my $checked_out_found = 1 if (-d ".git" && -d "compiler");
765     my $bare_found = 1 if (-d "ghc.git");
766
767     if ($bare_flag && ! $bare_found && ! $defaultrepo) {
768         die "error: bare repository ghc.git not found.\n"
769           . "       Either clone a bare ghc repo first or specify the repo location. E.g.:\n"
770           . "       ./sync-all --bare [--testsuite --nofib --extra] -r http://darcs.haskell.org/ get\n"
771     }
772     elsif ($bare_found) {
773         $bare_flag = "--bare";
774     }
775     elsif (! $bare_flag && ! $checked_out_found) {
776         die "error: sync-all must be run from the top level of the ghc tree.";
777     }
778
779     if ($#_ eq -1) {
780         help(1);
781     }
782     else {
783         # Give the command and rest of the arguments to the main loop
784         # We normalise command names here to avoid duplicating the
785         # abbreviations that we allow.
786         my $command = shift;
787
788         if ($command =~ /^(?:g|ge|get)$/) {
789             $command = "get";
790         }
791         elsif ($command =~ /^(?:pus|push)$/) {
792             $command = "push";
793         }
794         elsif ($command =~ /^(?:pul|pull)$/) {
795             $command = "pull";
796         }
797         elsif ($command =~ /^(?:s|se|sen|send)$/) {
798             $command = "send";
799         }
800         elsif ($command =~ /^(?:w|wh|wha|what|whats|whatsn|whatsne|whatsnew|status)$/) {
801             $command = "status";
802         }
803
804         if ($command eq "push") {
805             &gitall("check_submodules", @_);
806         }
807
808         &gitall($command, @_);
809
810         my @submodule_args = grep(/^-q/,@_);
811
812         if ($command eq "get") {
813             &git(".", "submodule", "init", @submodule_args);
814         }
815         if ($command eq "pull") {
816             my $gitConfig = &tryReadFile(".git/config");
817             if ($gitConfig !~ /submodule/) {
818                 &git(".", "submodule", "init", @submodule_args);
819             }
820         }
821         if ($command eq "get" or $command eq "pull") {
822             my $gitConfig = &tryReadFile(".git/config");
823             if ($gitConfig !~ /submodule/) {
824                 &git(".", "submodule", "init", @submodule_args);
825             }
826             &git(".", "submodule", "update", @submodule_args);
827         }
828     }
829 }
830
831 BEGIN {
832     $initial_working_directory = getcwd();
833 }
834
835 END {
836     my $ec = $?;
837
838     chdir($initial_working_directory);
839
840     message "== Checking for old haddock repo";
841     if (-d "utils/haddock/.git") {
842         chdir("utils/haddock");
843         if ((system "git log -1 87e2ca11c3d1b1bc49900fba0b5c5c6f85650718 > /dev/null 2> /dev/null") == 0) {
844             print <<EOF;
845 ============================
846 ATTENTION!
847
848 You have an old haddock repository in your GHC tree!
849
850 Please remove it (e.g. "rm -r utils/haddock"), and then run
851 "./sync-all get" to get the new repository.
852 ============================
853 EOF
854         }
855         chdir($initial_working_directory);
856     }
857
858     message "== Checking for old binary repo";
859     if (-d "libraries/binary/.git") {
860         chdir("libraries/binary");
861         if ((system "git log -1 749ac0efbde3b14901417364a872796598747aaf > /dev/null 2> /dev/null") == 0) {
862             print <<EOF;
863 ============================
864 ATTENTION!
865
866 You have an old binary repository in your GHC tree!
867
868 Please remove it (e.g. "rm -r libraries/binary"), and then run
869 "./sync-all get" to get the new repository.
870 ============================
871 EOF
872         }
873         chdir($initial_working_directory);
874     }
875
876     message "== Checking for old mtl repo";
877     if (-d "libraries/mtl/.git") {
878         chdir("libraries/mtl");
879         if ((system "git log -1 c67d8f7247c612dc35242bc67e616f7ea35eadb9 > /dev/null 2> /dev/null") == 0) {
880             print <<EOF;
881 ============================
882 ATTENTION!
883
884 You have an old mtl repository in your GHC tree!
885
886 Please remove it (e.g. "rm -r libraries/mtl"), and then run
887 "./sync-all get" to get the new repository.
888 ============================
889 EOF
890         }
891         chdir($initial_working_directory);
892     }
893
894     message "== Checking for old Cabal repo";
895     if (-d "libraries/Cabal/.git") {
896         chdir("libraries/Cabal");
897         if ((system "git log -1 c8ebd66a32865f72ae03ee0663c62df3d77f08fe > /dev/null 2> /dev/null") == 0) {
898             print <<EOF;
899 ============================
900 ATTENTION!
901
902 You have an old Cabal repository in your GHC tree!
903
904 Please remove it (e.g. "rm -r libraries/Cabal"), and then run
905 "./sync-all get" to get the new repository.
906 ============================
907 EOF
908         }
909         chdir($initial_working_directory);
910     }
911
912     message "== Checking for old time from tarball";
913     if (-d "libraries/time" and ! -e "libraries/time/.git") {
914             print <<EOF;
915 ============================
916 ATTENTION!
917
918 You have an old time package in your GHC tree!
919
920 Please remove it (e.g. "rm -r libraries/time"), and then run
921 "./sync-all get" to get the new repository.
922 ============================
923 EOF
924     }
925
926     $? = $ec;
927 }
928
929 main(@ARGV);
930