Add commit-template support
[mirror/qt/qt5.git] / init-repository
1 #!/usr/bin/env perl
2 #############################################################################
3 ##
4 ## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ## All rights reserved.
6 ## Contact: Nokia Corporation (qt-info@nokia.com)
7 ##
8 ## This file is part of the utilities of the Qt Toolkit.
9 ##
10 ## $QT_BEGIN_LICENSE:LGPL$
11 ## No Commercial Usage
12 ## This file contains pre-release code and may not be distributed.
13 ## You may use this file in accordance with the terms and conditions
14 ## contained in the Technology Preview License Agreement accompanying
15 ## this package.
16 ##
17 ## GNU Lesser General Public License Usage
18 ## Alternatively, this file may be used under the terms of the GNU Lesser
19 ## General Public License version 2.1 as published by the Free Software
20 ## Foundation and appearing in the file LICENSE.LGPL included in the
21 ## packaging of this file.  Please review the following information to
22 ## ensure the GNU Lesser General Public License version 2.1 requirements
23 ## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ##
25 ## In addition, as a special exception, Nokia gives you certain additional
26 ## rights.  These rights are described in the Nokia Qt LGPL Exception
27 ## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ##
29 ## If you have questions regarding the use of this file, please contact
30 ## Nokia at qt-info@nokia.com.
31 ##
32 ##
33 ##
34 ##
35 ##
36 ##
37 ##
38 ##
39 ## $QT_END_LICENSE$
40 ##
41 #############################################################################
42
43 use strict;
44 use warnings;
45
46 package Qt::InitRepository;
47
48
49 =head1 NAME
50
51 init-repository - initialize the Qt5 repository and all submodules
52
53 =head1 SYNOPSIS
54
55   ./init-repository [options]
56
57 This script may be run after an initial `git clone' of Qt5 in order to check
58 out all submodules.
59
60
61 =head2 Global options:
62
63 =over
64
65 =item --force, -f
66
67 Force initialization (even if the submodules are already checked out).
68
69
70 =item --quiet, -q
71
72 Be quiet.  Will exit cleanly if the repository is already initialized.
73
74 =back
75
76
77 =head2 Module options:
78
79 =over
80
81 =item --no-webkit
82
83 Skip webkit and webkit examples submodules.
84 It may be desirable to skip these modules due to the large size of the webkit
85 git repository.
86
87
88 =item --no-update
89
90 Skip the `git submodule update' command.
91
92
93 =item --ignore-submodules
94
95 Set git config to ignore submodules by default when doing operations on the
96 qt5 repo, such as `pull', `fetch', `diff' etc.
97
98 After using this option, pass `--ignore-submodules=none' to git to override
99 it as needed.
100
101 =back
102
103
104 =head2 Repository options:
105
106 =over
107
108 =item --nokia-developer
109
110 Switch to internal Nokia URLs.
111
112
113 =item --brisbane
114
115 Switch to internal Nokia URLs and make use of the Brisbane git mirrors.
116 (Implies `--mirror' and `--mirror-webkit').
117
118
119 =item --ssh
120
121 Use the SSH protocol for git operations.  This may be useful if the git
122 protocol is blocked by a firewall. Note that this requires a user account
123 with an uploaded SSH key on all servers used.  (Implies `--nokia-developer').
124
125
126 =item --http
127
128 Use the HTTP protocol for git operations.  This may be useful if the git
129 protocol is blocked by a firewall.  Note that this only works with the
130 external Gitorious server.
131
132
133 =item --alternates <path to other Qt5 repo>
134
135 Adds alternates for each submodule to another full qt5 checkout. This makes
136 this qt5 checkout very small, as it will use the object store of the
137 alternates before unique objects are stored in its own object store.
138
139 This option has no effect when using `--no-update'.
140
141 B<NOTE:> This will make this repo dependent on the alternate, which is
142 potentially dangerous!  The dependency can be broken by also using
143 the `--copy-objects' option, or by running C<git repack -a> in each
144 submodule, where required.  Please read the note about the `--shared' option
145 in the documentation of `git clone' for more information.
146
147
148 =item --copy-objects
149
150 When `--alternates' is used, automatically do a C<git repack -a> in each
151 submodule after cloning, to ensure that the repositories are independent
152 from the source used as a reference for cloning.
153
154 Note that this negates the disk usage benefits gained from the use of
155 `--alternates'.
156
157
158 =item --mirror <url-base>
159
160 Uses <url-base> as the base URL for submodule git mirrors.
161
162 For example:
163
164   --mirror user@machine:/foo/bar
165
166 ...will use the following as a mirror for qtbase:
167
168   user@machine:/foo/bar/qtbase.git
169
170
171 =item --mirror-webkit <url>
172
173 Uses <url> as the URL for the webkit git mirror.
174
175 =back
176
177 =cut
178
179 use Carp         qw( confess             );
180 use English      qw( -no_match_vars      );
181 use Getopt::Long qw( GetOptionsFromArray );
182 use Pod::Usage   qw( pod2usage           );
183 use Cwd          qw( getcwd              );
184
185 my %PROTOCOLS = (
186     'internal'  => 'git://scm.dev.nokia.troll.no/' ,
187     'ssh'       => 'git@scm.dev.nokia.troll.no:'   ,
188     'http'      => 'http://git.gitorious.org/'     ,
189 );
190
191 my %STAGING_REPOS = map { $_ => "git://gitorious.org/qt/$_.git" } qw(
192     qt5-staging
193     qt3support-staging
194     qtactiveqt-staging
195     qtbase-earth-staging
196     qtbase-staging
197     qtdeclarative-staging
198     qtdoc-staging
199     qtmultimedia-staging
200     qtphonon-staging
201     qtqa-staging
202     qtscript-staging
203     qtsvg-staging
204     qttools-staging
205     qttranslations-staging
206     qtwebkit-examples-and-demos-staging
207     qtxmlpatterns-staging
208     qtlocation-staging
209 );
210
211 my $BNE_MIRROR_URL_BASE
212     = 'git://bq-git.apac.nokia.com/qtsoftware/qt/';
213
214 my $BNE_MIRROR_WEBKIT_URL
215     = 'git://bq-git.apac.nokia.com/qtsoftware/research/gitorious-org-webkit-qtwebkit-mirror.git';
216
217 sub new
218 {
219     my ($class, @arguments) = @_;
220
221     my $self = {};
222     bless $self, $class;
223     $self->parse_arguments(@arguments);
224
225     return $self;
226 }
227
228 # Like `system', but possibly log the command, and die on non-zero exit code
229 sub exe
230 {
231     my ($self, @cmd) = @_;
232
233     if (!$self->{quiet}) {
234         print "+ @cmd\n";
235     }
236
237     if (system(@cmd) != 0) {
238         confess "@cmd exited with status $CHILD_ERROR";
239     }
240
241     return;
242 }
243
244 sub parse_arguments
245 {
246     my ($self, @args) = @_;
247
248     %{$self} = (%{$self},
249         'alternates'          => "",
250         'detach-alternates'   => 0 ,
251         'force'               => 0 ,
252         'ignore-submodules'   => 0 ,
253         'mirror-url'          => "",
254         'mirror-webkit-url'   => "",
255         'nokia-developer'     => 0 ,
256         'protocol'            => "",
257         'update'              => 1 ,
258         'webkit'              => 1 ,
259     );
260
261     GetOptionsFromArray(\@args,
262         'alternates=s'      =>  \$self->{qw{ alternates        }},
263         'copy-objects'      =>  \$self->{qw{ detach-alternates }},
264         'force'             =>  \$self->{qw{ force             }},
265         'ignore-submodules' =>  \$self->{qw{ ignore_submodules }},
266         'mirror-webkit=s'   =>  \$self->{qw{ mirror-webkit-url }},
267         'mirror=s'          =>  \$self->{qw{ mirror-url        }},
268         'nokia-developer'   =>  \$self->{qw{ nokia-developer   }},
269         'quiet'             =>  \$self->{qw{ quiet             }},
270         'update!'           =>  \$self->{qw{ update            }},
271         'webkit!'           =>  \$self->{qw{ webkit            }},
272
273         'help|?'            =>  sub { pod2usage(1);               },
274         'http'              =>  sub { $self->{protocol} = 'http'; },
275         'ssh|ssh-protocol'  =>  sub { $self->{protocol} = 'ssh';  },
276
277         'brisbane|brisbane-nokia-developer' => sub {
278             $self->{'nokia-developer'}   = 1;
279             $self->{'protocol'}          = 'internal';
280             $self->{'mirror-url'}        = $BNE_MIRROR_URL_BASE;
281             $self->{'mirror-webkit-url'} = $BNE_MIRROR_WEBKIT_URL;
282         },
283
284         'nokia-developer' => sub {
285             $self->{'nokia-developer'}   = 1;
286             $self->{'protocol'}          = 'internal';
287         },
288     ) || pod2usage(2);
289
290     if ($self->{'nokia-developer'} && $self->{'protocol'} eq 'http') {
291         print "*** Ignoring use of HTTP protocol, as it's only usable with external server\n";
292         $self->{'protocol'} = '';
293     }
294
295     # Replace any double trailing slashes from end of mirror
296     $self->{'mirror-url'} =~ s{//+$}{/};
297
298     return;
299 }
300
301 sub check_if_already_initialized
302 {
303     my ($self) = @_;
304
305     # We consider the repo as `initialized' if submodule.qtbase.url is set
306     if (qx(git config --get submodule.qtbase.url)) {
307         if ($self->{force}) {
308             my @configresult = qx(git config -l);
309             foreach (@configresult) {
310                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
311                 if (/(submodule\.[^.=]+)\.url=.*/) {
312                     $self->exe('git', 'config', '--remove-section', $1);
313                 }
314             }
315         }
316         else {
317             exit 0 if ($self->{quiet});
318             print "Will not reinitialize already initialized repository (use -f to force)!\n";
319             exit 1;
320         }
321     }
322
323     return;
324 }
325
326 sub git_submodule_init
327 {
328     my ($self) = @_;
329
330     my @init_args;
331     if ($self->{quiet}) {
332         push @init_args, '--quiet';
333     }
334     $self->exe('git', 'submodule', 'init', @init_args);
335
336     my $template = getcwd()."/.commit-template";
337     if (-e $template) {
338         $self->exe('git', 'config', 'commit.template', $template);
339     }
340
341     return;
342 }
343
344 sub git_disable_webkit_submodule
345 {
346     my ($self) = @_;
347
348     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit');
349     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit-examples-and-demos');
350
351     return;
352 }
353
354 sub git_set_submodule_config
355 {
356     my ($self) = @_;
357
358     my @configresult          = qx(git config -l);
359     my $protocol              = $self->{protocol};
360     my $url_base_for_protocol = $PROTOCOLS{$protocol};
361
362     GITCONFIG:
363     foreach my $line (@configresult) {
364         # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
365         next GITCONFIG if ($line !~ /(submodule\.[^.=]+\.url)=(.*)/);
366
367         my $key   = $1;
368         my $value = $2;
369
370         if ($protocol) {
371             # WebKit is special, and has only external link.
372             if ($key ne 'submodule.qtwebkit.url') {
373                 # qt-labs projects are still hosted under qt internally.
374                 if ($protocol ne 'http') {
375                     $value =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
376                 }
377                 $value =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
378             }
379         }
380
381         $self->exe('git', 'config', $key, $value);
382
383         if ($self->{'ignore-submodules'}) {
384             $key =~ s,\.url,.ignore,;
385             $self->exe('git', 'config', $key, 'all');
386         }
387     }
388
389     return;
390 }
391
392 sub git_clone_all_submodules
393 {
394     my ($self) = @_;
395
396     # manually clone each repo here, so we can easily use reference repos, mirrors and
397     # add all staging repos
398     my @configresult = qx(git config -l);
399     foreach my $line (@configresult) {
400         if ($line =~ /submodule\.([^.=]+)\.url=(.*)/) {
401             $self->git_clone_one_submodule($1, $2);
402         }
403     }
404
405     $self->exe('git', 'submodule', 'update');
406
407     return;
408 }
409
410 sub git_add_staging_remote
411 {
412     my ($self, $repo_basename) = @_;
413
414     my $protocol              = $self->{protocol};
415     my $url_base_for_protocol = $PROTOCOLS{$protocol};
416
417     my %current_remotes;
418     for my $line (qx(git remote show)) {
419         chomp $line;
420         $current_remotes{$line} = 1;
421     }
422
423     # We assume that any staging starting with `$repo_basename-' relates to this
424     # repo.  For example, for the `qtbase' module, `qtbase-staging'
425     # and `qtbase-earth-staging' are considered as related staging repos.
426     my @staging = grep { /^\Q$repo_basename\E-/; } keys %STAGING_REPOS;
427
428     STAGING:
429     foreach my $staging_repo (@staging) {
430         # nothing to do if remote already exists
431         next STAGING if ($current_remotes{$staging_repo});
432
433         my $staging_repo_url = $STAGING_REPOS{$staging_repo};
434         if ($protocol) {
435             if ($protocol ne 'http') {
436                 $staging_repo_url =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
437             }
438             $staging_repo_url =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
439         }
440         $self->exe('git', 'remote', 'add', $staging_repo, $staging_repo_url);
441     }
442
443     return;
444 }
445
446 sub git_clone_one_submodule
447 {
448     my ($self, $submodule, $url) = @_;
449
450     my $alternates            = $self->{ 'alternates'        };
451     my $mirror_url            = $self->{ 'mirror-url'        };
452     my $mirror_webkit_url     = $self->{ 'mirror-webkit-url' };
453
454     # `--reference FOO' args for the clone, if any.
455     my @reference_args;
456
457     if ($alternates) {
458         # alternates is a qt5 repo, so the submodule will be under that.
459         if (-d "$alternates/$submodule") {
460             @reference_args = ('--reference', "$alternates/$submodule");
461         }
462         else {
463             print " *** $alternates/$submodule not found, ignoring alternate for this submodule\n";
464         }
465     }
466
467     my $mirror;
468     if ($mirror_url && ($submodule ne 'qtwebkit')) {
469         $mirror = $mirror_url.$submodule;
470         $mirror .= ".git" unless (-d $mirror); # Support local disk mirror
471     }
472     elsif ($mirror_webkit_url && ($submodule eq 'qtwebkit')) {
473         $mirror = $mirror_webkit_url;
474     }
475
476     my $do_clone = (! -d "$submodule/.git");
477     if ($do_clone) {
478         $self->exe('git', 'clone', @reference_args, ($mirror ? $mirror : $url), $submodule);
479     }
480
481     chdir($submodule) or confess "chdir $submodule: $OS_ERROR";
482
483     if (!$do_clone) {
484         $self->exe('git', 'fetch', ($mirror ? $mirror : $url));
485     }
486
487     my $template = getcwd()."/../.commit-template";
488     if (-e $template) {
489         $self->exe('git', 'config', 'commit.template', $template);
490     }
491
492     if ($mirror) {
493         $self->exe('git', 'config', 'remote.origin.url', $url);
494
495         # In `force' mode, remove the mirror if it already exists,
496         # since we may be reinitializing the module.
497         if ($self->{force}) {
498             eval { $self->exe('git', 'remote', 'rm', 'mirror'); }; # failure is OK
499         }
500
501         $self->exe('git', 'remote', 'add', 'mirror', $mirror);
502     }
503
504     $self->git_add_staging_remote($submodule);
505
506     if ($self->{'detach-alternates'}) {
507         $self->exe('git', 'repack', '-a');
508
509         my $alternates_path = '.git/objects/info/alternates';
510         unlink($alternates_path) || confess "unlink $alternates_path: $OS_ERROR";
511     }
512
513     chdir("..") or confess "cd ..: $OS_ERROR";
514
515     return;
516 }
517
518 sub run
519 {
520     my ($self) = @_;
521
522     $self->check_if_already_initialized;
523     $self->git_submodule_init;
524
525     if (!$self->{webkit}) {
526         $self->git_disable_webkit_submodule;
527     }
528
529     $self->git_set_submodule_config;
530
531     if ($self->{update}) {
532         $self->git_clone_all_submodules;
533     }
534
535     $self->git_add_staging_remote('qt5');
536
537     return;
538 }
539
540 #==============================================================================
541
542 Qt::InitRepository->new(@ARGV)->run if (!caller);
543 1;