3 # debcheckout: checkout the development repository of a Debian package
4 # Copyright (C) 2007-2009 Stefano Zacchiroli <zack@debian.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # Created: Tue, 14 Aug 2007 10:20:55 +0200
21 # Last-Modified: $Date$
25 debcheckout - checkout the development repository of a Debian package
31 =item B<debcheckout> [I<OPTIONS>] I<PACKAGE> [I<DESTDIR>]
33 =item B<debcheckout> [I<OPTIONS>] I<REPOSITORY_URL> [I<DESTDIR>]
35 =item B<debcheckout> B<--help>
41 B<debcheckout> retrieves the information about the Version Control System used
42 to maintain a given Debian package (the I<PACKAGE> argument), and then checks
43 out the latest (potentially unreleased) version of the package from its
44 repository. By default the repository is checked out to the I<PACKAGE>
45 directory; this can be overridden by providing the I<DESTDIR> argument.
47 The information about where the repository is available is expected to be found
48 in B<Vcs-*> fields available in the source package record. For example, the vim
49 package exposes such information with a field like S<Vcs-Git:
50 git://git.debian.org/git/pkg-vim/vim.git>, you can see it by grepping through
51 C<apt-cache showsrc vim>.
53 If more than one source package record containing B<Vcs-*> fields is available,
54 B<debcheckout> will select the record with the highest version number.
55 Alternatively, a particular version may be selected from those available by
56 specifying the package name as I<PACKAGE>=I<VERSION>.
58 If you already know the URL of a given repository you can invoke
59 debcheckout directly on it, but you will probably need to pass the
60 appropriate B<-t> flag. That is, some heuristics are in use to guess
61 the repository type from the URL; if they fail, you might want to
62 override the guessed type using B<-t>.
64 The currently supported version control systems are: arch, bzr, cvs,
73 =item B<-a>, B<--auth>
75 Work in authenticated mode; this means that for known repositories (mainly those
76 hosted on S<http://alioth.debian.org>) URL rewriting is attempted before
77 checking out, to ensure that the repository can be committed to. For example,
78 for subversion repositories hosted on alioth this means that
79 S<svn+ssh://svn.debian.org/...> will be used instead of
80 S<svn://svn.debian.org/...>.
82 =item B<-d>, B<--details>
84 Only print a list of detailed information about the package
85 repository, without checking it out; the output format is a list of
86 fields, each field being a pair of TAB-separated field name and field
87 value. The actual fields depend on the repository type. This action
88 might require a network connection to the remote repository.
90 Also see B<-p>. This option and B<-p> are mutually exclusive.
92 =item B<-h>, B<--help>
94 Print a detailed help message and exit.
96 =item B<-p>, B<--print>
98 Only print a summary about package repository information, without
99 checking it out; the output format is TAB-separated with two fields:
100 repository type, repository URL. This action works offline, it only
101 uses "static" information as known by APT's cache.
103 Also see B<-d>. This option and B<-d> are mutually exclusive.
105 =item B<-t> I<TYPE>, B<--type> I<TYPE>
107 Override the repository type (which defaults to some heuristics based
108 on the URL or, in case of heuristic failure, the fallback "svn");
109 should be one of the currently supported repository types.
111 =item B<-u> I<USERNAME>, B<--user> I<USERNAME>
113 Specify the login name to be used in authenticated mode (see B<-a>). This option
114 implies B<-a>: you don't need to specify both.
116 =item B<-f>, B<--file>
118 Specify that the named file should be extracted from the repository and placed
119 in the destination directory. May be used more than once to extract mutliple
124 B<VCS-SPECIFIC OPTIONS>
126 I<GIT-SPECIFIC OPTIONS>
130 =item B<--git-track> I<BRANCHES>
132 Specify a list of remote branches which will be set up for tracking
133 (as in S<git branch --track>, see git-branch(1)) after the remote
134 GIT repository has been cloned. The list should be given as a
135 space-separated list of branch names.
137 As a shorthand, the string "*" can be given to require tracking of all
142 =head1 CONFIGURATION VARIABLES
144 The two configuration files F</etc/devscripts.conf> and
145 F<~/.devscripts> are sourced by a shell in that order to set
146 configuration variables. Command line options can be used to override
147 configuration file settings. Environment variable settings are ignored
148 for this purpose. The currently recognised variables are:
152 =item B<DEBCHECKOUT_AUTH_URLS>
154 This variable should be a space separated list of Perl regular
155 expressions and replacement texts, which must come in pairs: REGEXP
156 TEXT REGEXP TEXT ... and so on. Each pair denotes a substitution which
157 is applied to repository URLs if other built-in means of building URLs
158 for authenticated mode (see B<-a>) have failed.
160 References to matching substrings in the replacement texts are
161 allowed as usual in Perl by the means of $1, $2, ... and so on.
163 This setting can be used to enable authenticated mode for most repositories
164 out there. Note that the Debian repositories on S<alioth.debian.org>
165 (S<$vcs.debian.org>) are implicitly defined.
167 Here is a sample snippet suitable for the configuration files:
169 DEBCHECKOUT_AUTH_URLS='
170 ^\w+://(svn\.example\.com)/(.*) svn+ssh://$1/srv/svn/$2
171 ^\w+://(git\.example\.com)/(.*) git+ssh://$1/home/git/$2
174 Note that whitespace is not allowed in either regexps or
175 replacement texts. Also, given that configuration files are sourced by
176 a shell, you probably want to use single quotes around the value of
183 apt-cache(8), Section 6.2.5 of the Debian Developer's Reference (for
184 more information about Vcs-* fields): S<http://www.debian.org/doc/developers-reference/best-pkging-practices.html#bpp-vcs>
188 debcheckout and this manpage have been written by Stefano Zacchiroli
193 use feature 'switch';
199 use File::Copy qw/copy/;
200 use File::Temp qw/tempdir/;
202 use lib '/usr/share/devscripts';
203 use Devscripts::Versort;
205 my @files = (); # files to checkout
207 # <snippet from="bts.pl">
208 # <!-- TODO we really need to factor out in a Perl module the
209 # configuration file parsing code -->
210 my @config_files = ('/etc/devscripts.conf', '~/.devscripts');
212 'DEBCHECKOUT_AUTH_URLS' => '',
214 my %config_default = %config_vars;
217 foreach my $var (keys %config_vars) {
218 $shell_cmd .= qq[$var="$config_vars{$var}";\n];
220 $shell_cmd .= 'for file in ' . join(" ",@config_files) . "; do\n";
221 $shell_cmd .= '[ -f $file ] && . $file; done;' . "\n";
223 foreach my $var (keys %config_vars) { $shell_cmd .= "echo \$$var;\n" }
224 my $shell_out = `/bin/bash -c '$shell_cmd'`;
225 @config_vars{keys %config_vars} = split /\n/, $shell_out, -1;
232 return ($lwp_broken ? 0 : 1) if defined $lwp_broken;
235 require LWP::UserAgent;
239 if ($@ =~ m%^Can\'t locate LWP%) {
240 $lwp_broken="the libwww-perl package is not installed";
242 $lwp_broken="couldn't load LWP::UserAgent: $@";
245 else { $lwp_broken=''; }
246 return $lwp_broken ? 0 : 1;
250 $ua = new LWP::UserAgent; # we create a global UserAgent object
251 $ua->agent("LWP::UserAgent/Devscripts");
257 my @temp = split /\//, $dir;
259 foreach my $piece (@temp) {
260 if (! length $createdir and ! length $piece) {
262 } elsif (length $createdir and $createdir ne "/") {
265 $createdir .= "$piece";
266 if (! -d $createdir) {
267 mkdir($createdir) or return 0;
273 # Find the repository URL (and type) for a given package name, parsing Vcs-*
276 my ($pkg, $desired_ver) = @_;
284 open(APT, "apt-cache showsrc $pkg |");
285 while (my $line = <APT>) {
288 if ($line =~ /^(x-)?vcs-(\w+):\s*(.*)$/i) {
289 next if lc($2) eq "browser";
290 ($type, $url) = (lc($2), $3);
291 } elsif ($line =~ /^Version:\s*(.*)$/i) {
293 } elsif ($line =~ /^$/) {
294 push (@repos, [$version, $type, $url])
295 if ($version and $type and $url and
296 ($desired_ver eq "" or $desired_ver eq $version));
303 die "unknown package '$pkg'\n" unless $found;
306 @repos = Devscripts::Versort::versort(@repos);
307 @repo = ($repos[0][1], $repos[0][2])
312 # Find the browse URL for a given package name, parsing Vcs-* fields.
313 sub find_browse($$) {
314 my ($pkg, $desired_ver) = @_;
320 open(APT, "apt-cache showsrc $pkg |");
321 while (my $line = <APT>) {
324 if ($line =~ /^(x-)?vcs-(\w+):\s*(.*)$/i) {
325 if (lc($2) eq "browser") {
328 } elsif ($line =~ /^Version:\s*(.*)$/i) {
330 } elsif ($line =~ /^$/) {
331 push(@browses, [$version, $browse])
332 if $version and $browse and
333 ($desired_ver eq "" or $desired_ver eq $version);
339 die "unknown package '$pkg'\n" unless $found;
341 @browses = Devscripts::Versort::versort(@browses);
342 $browse = $browses[0][1];
347 # Patch the cmdline invocation of a VCS to ensure the repository is checkout to
348 # a given target directory.
349 sub set_destdir(@$$) {
350 my ($repo_type, $destdir, @cmd) = @_;
351 $destdir =~ s|^-d\s*||;
355 my $module = pop @cmd;
356 push @cmd, ("-d", $destdir, $module);
358 when (/^(bzr|darcs|git|hg|svn)$/) {
362 die "sorry, don't know how to set the destination directory for $repo_type repositories (patches welcome!)\n";
368 # try patching a repository URL to enable authenticated mode, *relying
369 # only on user defined rules*
370 sub user_set_auth($$) {
371 my ($repo_type, $url) = @_;
372 my @rules = split ' ', $config_vars{'DEBCHECKOUT_AUTH_URLS'};
373 while (my $pat = shift @rules) { # read pairs for s/$pat/$subst/
374 my $subst = shift @rules
375 or die "Configuration error for DEBCHECKOUT_AUTH_URLS: regexp and replacement texts must come in pairs. See debcheckout(1).\n";
376 $url =~ s/$pat/qq("$subst")/ee; # ZACK: my worst Perl line ever
381 # Patch a given repository URL to ensure that the checked out out repository
382 # can be committed to. Only works for well known repositories (mainly Alioth's).
384 my ($repo_type, $url, $user, $dont_act) = @_;
388 $user .= "@" if length $user;
389 my $user_local = $user;
390 $user_local =~ s|(.*)(@)|$1|;
395 $url =~ s|^[\w+]+://(bzr\.debian\.org)/(.*)|bzr+ssh://$user$1/bzr/$2|;
396 $url =~ s[^\w+://(?:(bazaar|code)\.)?(launchpad\.net/.*)][bzr+ssh://${user}bazaar.$2];
399 if ($url =~ m|(~)|) {
400 $user_url =~ s|^\w+://(darcs\.debian\.org)/(~)(.*?)/.*|$3|;
401 die "the local user '$user_local' doesn't own the personal repository '$url'\n"
402 if $user_local ne $user_url and !$dont_act;
403 $url =~ s|^\w+://(darcs\.debian\.org)/(~)(.*?)/(.*)|$user$1:~/public_darcs/$4|;
405 $url =~ s|^\w+://(darcs\.debian\.org)/(.*)|$user$1:/$2|;
409 if ($url =~ m%(/users/|~)%) {
410 $user_url =~ s|^\w+://(git\.debian\.org)/git/users/(.*?)/.*|$2|;
411 $user_url =~ s|^\w+://(git\.debian\.org)/~(.*?)/.*|$2|;
413 die "the local user '$user_local' doesn't own the personal repository '$url'\n"
414 if $user_local ne $user_url and !$dont_act;
415 $url =~ s|^\w+://(git\.debian\.org)/git/users/.*?/(.*)|git+ssh://$user$1/~/public_git/$2|;
416 $url =~ s|^\w+://(git\.debian\.org)/~.*?/(.*)|git+ssh://$user$1/~/public_git/$2|;
418 $url =~ s|^\w+://(git\.debian\.org)/(?:git/)?(.*)|git+ssh://$user$1/git/$2|;
421 # "hg ssh://" needs an extra slash so paths are not based in the user's $HOME
423 $url =~ s|^\w+://(hg\.debian\.org/)|ssh://$user$1/|;
426 $url =~ s|^\w+://(svn\.debian\.org)/(.*)|svn+ssh://$user$1/svn/$2|;
429 die "sorry, don't know how to enable authentication for $repo_type repositories (patches welcome!)\n";
432 if ($url eq $old_url) { # last attempt: try with user-defined rules
433 $url = user_set_auth($repo_type, $url);
435 die "can't use authenticated mode on repository '$url' since it is not a known repository (e.g. alioth)\n"
440 # Hack around specific, known deficiencies in repositories that don't follow
444 my ($repo_type, $repo_url) = @_;
448 # bzr.d.o explicitly doesn't run a smart server. Need to use nosmart
449 $repo_url =~ s|^http://(bzr\.debian\.org)/(.*)|nosmart+http://$1/$2|;
455 # Checkout a given repository in a given destination directory.
456 sub checkout_repo($$$) {
457 my ($repo_type, $repo_url, $destdir) = @_;
461 when ("arch") { @cmd = ("tla", "grab", $repo_url); } # XXX ???
462 when ("bzr") { @cmd = ("bzr", "branch", $repo_url); }
464 $repo_url =~ s|^-d\s*||;
465 my ($root, $module) = split /\s+/, $repo_url;
467 @cmd = ("cvs", "-d", $root, "checkout", $module);
469 when ("darcs") { @cmd = ("darcs", "get", $repo_url); }
470 when ("git") { @cmd = ("git", "clone", $repo_url); }
471 when ("hg") { @cmd = ("hg", "clone", $repo_url); }
472 when ("svn") { @cmd = ("svn", "co", $repo_url); }
473 default { die "unsupported version control system '$repo_type'.\n"; }
475 @cmd = set_destdir($repo_type, $destdir, @cmd) if length $destdir;
482 # Checkout a given set of files from a given repository in a given
483 # destination directory.
484 sub checkout_files($$$$) {
485 my ($repo_type, $repo_url, $destdir, $browse_url) = @_;
489 foreach my $file (@files) {
492 # Cheap'n'dirty escaping
493 # We should possibly depend on URI::Escape, but this should do...
494 my $escaped_file = $file;
495 $escaped_file =~ s|\+|%2B|g;
498 if (defined $destdir and length $destdir) {
503 $dir .= dirname($file);
505 if (! recurs_mkdir($dir)) {
506 print STDERR "Failed to create directory $dir\n";
512 # If we've already retrieved a copy of the repository,
514 if (!length($tempdir)) {
515 if (!($tempdir = tempdir( "debcheckoutXXXX", TMPDIR => 1, CLEANUP => 1 ))) {
517 "Failed to create temporary directory . $!\n";
521 my $oldcwd = getcwd();
523 @cmd = ("tla", "grab", $repo_url);
525 my $rc = system(@cmd);
527 return ($rc >> 8) if $rc != 0;
530 if (!copy("$tempdir/$file", $dir)) {
531 print STDERR "Failed to copy $file to $dir: $!\n";
536 if (!length($tempdir)) {
537 if (!($tempdir = tempdir( "debcheckoutXXXX", TMPDIR => 1, CLEANUP => 1 ))) {
539 "Failed to create temporary directory . $!\n";
543 $repo_url =~ s|^-d\s*||;
544 my ($root, $module) = split /\s+/, $repo_url;
545 # If an explicit module name isn't present, use the last
546 # component of the URL
547 if (!length($module)) {
549 $module =~ s%^.*/(.*?)$%$1%;
554 my $oldcwd = getcwd();
556 @cmd = ("cvs", "-d", $root, "export", "-r", "HEAD", "-f",
558 print "\n@cmd ...\n";
560 if (($? >> 8) != 0) {
565 if (copy("$tempdir/$module", $dir)) {
566 print "Copied to $destdir/$file\n";
568 print STDERR "Failed to copy $file to $dir: $!\n";
574 @cmd = ($repo_type, "cat", "$repo_url/$file");
575 print "@cmd > $dir/" . basename($file) . " ... \n";
576 if (! open CAT, '-|', @cmd) {
577 print STDERR "Failed to execute @cmd $!\n";
583 if (! open OUTPUT, ">", $dir . "/" . basename($file)) {
584 print STDERR "Failed to create output file "
585 . basename($file) ." $!\n";
588 print OUTPUT $content;
591 when (/(darcs|hg)/) {
592 # Subtly different but close enough
594 print "Attempting to retrieve $file via HTTP ...\n";
596 my $file_url = $repo_type eq "darcs"
597 ? "$repo_url/$escaped_file"
598 : "$repo_url/raw-file/tip/$file";
599 init_agent() unless $ua;
600 my $request = HTTP::Request->new('GET', "$file_url");
601 my $response = $ua->request($request);
602 if ($response->is_success) {
603 if (! open OUTPUT, ">", $dir . "/" . basename($file)) {
604 print STDERR "Failed to create output file "
605 . basename($file) . " $!\n";
608 print "Writing to $dir/" . basename($file) . " ... \n";
609 print OUTPUT $response->content;
615 # If we've already retrieved a copy of the repository,
617 if (!length($tempdir)) {
618 if (!($tempdir = tempdir( "debcheckoutXXXX", TMPDIR => 1, CLEANUP => 1 ))) {
620 "Failed to create temporary directory . $!\n";
624 # Can't get / clone in to a directory that already exists...
626 if ($repo_type eq "darcs") {
627 @cmd = ("darcs", "get", $repo_url, $tempdir);
629 @cmd = ("hg", "clone", $repo_url, $tempdir);
632 my $rc = system(@cmd);
633 return ($rc >> 8) if $rc != 0;
637 if (copy "$tempdir/$file", $dir) {
638 print "Copied $file to $dir\n";
640 print STDERR "Failed to copy $file to $dir: $!\n";
645 # If there isn't a browse URL (either because the package
646 # doesn't ship one, or because we were called with a URL,
647 # try a common pattern for gitweb
648 if (!length($browse_url)) {
649 if ($repo_url =~ m%^\w+://([^/]+)/(?:git/)?(.*)$%) {
650 $browse_url = "http://$1/?p=$2";
653 if (have_lwp and $browse_url =~ /^http/) {
654 $escaped_file =~ s|/|%2F|g;
656 print "Attempting to retrieve $file via HTTP ...\n";
658 init_agent() unless $ua;
659 my $file_url = "$browse_url;a=blob_plain";
660 $file_url .= ";f=$escaped_file;hb=HEAD";
661 my $request = HTTP::Request->new('GET', $file_url);
662 my $response = $ua->request($request);
664 if (!$response->is_success) {
665 if ($browse_url =~ /\.git$/) {
666 print "Error retrieving file: "
667 . $response->status_line . "\n";
670 $browse_url .= ".git";
671 $file_url = "$browse_url;a=blob_plain";
672 $file_url .= ";f=$escaped_file;hb=HEAD";
673 $request = HTTP::Request->new('GET', $file_url);
674 $response = $ua->request($request);
675 if (!$response->is_success) {
676 print "Error retrieving file: "
677 . $response->status_line . "\n";
683 if (! open OUTPUT, ">", $dir . "/" . basename($file)) {
684 print STDERR "Failed to create output file "
685 . basename($file) . " $!\n";
688 print "Writing to $dir/" . basename($file) . " ... \n";
689 print OUTPUT $response->content;
695 # If we've already retrieved a copy of the repository,
697 if (!length($tempdir)) {
698 if (!($tempdir = tempdir( "debcheckoutXXXX", TMPDIR => 1, CLEANUP => 1 ))) {
700 "Failed to create temporary directory . $!\n";
703 # Since git won't clone in to a directory that
706 # Can't shallow clone from an http:: URL
707 $repo_url =~ s/^http/git/;
708 @cmd = ("git", "clone", "--depth", "1", $repo_url,
710 print "@cmd ...\n\n";
711 my $rc = system(@cmd);
712 return ($rc >> 8) if $rc != 0;
716 my $oldcwd = getcwd();
719 @cmd = ($repo_type, "show", "HEAD:$file");
720 print "@cmd ... > $dir/" . basename($file) . "\n";
721 if (! open CAT, '-|', @cmd) {
722 print STDERR "Failed to execute @cmd $!\n";
730 if (! open OUTPUT, ">", $dir . "/" . basename($file)) {
731 print STDERR "Failed to create output file "
732 . basename($file) ." $!\n";
735 print OUTPUT $content;
740 die "unsupported version control system '$repo_type'.\n";
745 # If we've got this far, all the files were retrieved successfully
749 # Print information about a repository and quit.
751 my ($repo_type, $repo_url) = @_;
753 print "$repo_type\t$repo_url\n";
757 sub git_ls_remote($$) {
758 my ($url, $prefix) = @_;
760 my $cmd = "git ls-remote '$url'";
761 $cmd .= " '$prefix/*'" if length $prefix;
762 open GIT, "$cmd |" or die "can't execute $cmd\n";
764 while (my $line = <GIT>) {
766 my ($sha1, $name) = split /\s+/, $line;
768 $ref = substr($ref, length($prefix) + 1) if length $prefix;
775 # Given a GIT repository URL, extract its topgit info (if any), see
776 # the "topgit" package for more information
781 $info{'topgit'} = 'no';
782 $info{'top-bases'} = '';
783 my @bases = git_ls_remote($url, 'refs/top-bases');
785 $info{'topgit'} = 'yes';
786 $info{'top-bases'} = join ' ', @bases;
791 # Print details about a repository and quit.
792 sub print_details($$) {
793 my ($repo_type, $repo_url) = @_;
795 print "type\t$repo_type\n";
796 print "url\t$repo_url\n";
797 if ($repo_type eq "git") {
798 my $tg_info = tg_info($repo_url);
799 while (my ($k, $v) = each %$tg_info) {
806 sub guess_repo_type($$) {
807 my ($repo_url, $default) = @_;
808 my $repo_type = $default;
809 if ($repo_url =~ /^(git|svn)(\+ssh)?:/) {
811 } elsif ($repo_url =~ /^https?:\/\/(svn|git|hg|bzr|darcs)\.debian\.org/) {
817 # Does a given string match the lexical rules for package names?
821 return ($arg =~ /^[a-z0-9.+-]+$/); # lexical rule for package names
825 my $auth = 0; # authenticated mode
826 my $destdir = ""; # destination directory
827 my $pkg = ""; # package name
828 my $version = ""; # package version
829 my $print_mode = 0; # print only mode
830 my $details_mode = 0; # details only mode
831 my $repo_type = "svn"; # default repo typo, overridden by '-t'
832 my $repo_url = ""; # repository URL
833 my $user = ""; # login name (authenticated mode only)
834 my $browse_url = ""; # online browsable repository URL
835 my $git_track = ""; # list of remote GIT branches to --track
838 "help|h" => sub { pod2usage({-exitval => 0, -verbose => 1}); },
839 "print|p" => \$print_mode,
840 "details|d" => \$details_mode,
841 "type|t=s" => \$repo_type,
842 "user|u=s" => \$user,
843 "file|f=s" => sub { push(@files, $_[1]); },
844 "git-track=s" => \$git_track,
845 ) or pod2usage({-exitval => 3});
846 pod2usage({-exitval => 3}) if ($#ARGV < 0 or $#ARGV > 1);
847 pod2usage({-exitval => 3,
849 "-d and -p are mutually exclusive.\n", })
850 if ($print_mode and $details_mode);
851 my $dont_act = 1 if ($print_mode or $details_mode);
853 # -u|--user implies -a|--auth
854 $auth = 1 if length $user;
856 $destdir = $ARGV[1] if $#ARGV > 0;
857 ($pkg, $version) = split(/=/, $ARGV[0]);
859 if (not is_package($pkg)) { # repo-url passed on the command line
860 $repo_url = $ARGV[0];
861 $repo_type = guess_repo_type($repo_url, $repo_type);
862 $pkg = ""; $version = "";
863 } else { # package name passed on the command line
864 ($repo_type, $repo_url) = find_repo($pkg, $version);
865 unless ($repo_type) {
867 $vermsg = ", version $version" if length $version;
869 No repository found for package $pkg$vermsg.
871 A Vcs-* field is missing in its source record. See Debian Developer's
873 `http://www.debian.org/doc/developers-reference/best-pkging-practices.html#bpp-vcs'
874 If you know that the package is maintained via a version control
875 system consider asking the maintainer to expose such information.
877 Nevertheless, you can get the sources of package $pkg
878 from the Debian archive executing:
882 Note however that what you obtain will *not* be a local copy of
883 some version control system: your changes will not be preserved
884 and it will not be possible to commit them directly.
889 $browse_url = find_browse($pkg, $version) if @files;
892 $repo_url = munge_url($repo_type, $repo_url);
893 $repo_url = set_auth($repo_type, $repo_url, $user, $dont_act)
894 if $auth and not @files;
895 print_repo($repo_type, $repo_url) if $print_mode; # ... then quit
896 print_details($repo_type, $repo_url) if $details_mode; # ... then quit
898 print "declared $repo_type repository at $repo_url\n";
899 $destdir = $pkg unless length $destdir;
903 $rc = checkout_files($repo_type, $repo_url, $destdir, $browse_url);
905 $rc = checkout_repo($repo_type, $repo_url, $destdir);
906 } # XXX: there is no way to know for sure what is the destdir :-(
907 die "checkout failed (the command above returned a non-zero exit code)\n"
910 # post-checkout actions
911 if ($repo_type eq 'bzr' and $auth) {
912 if (open B, '>>', "$destdir/.bzr/branch/branch.conf") {
913 print B "\npush_location = $repo_url";
917 "failed to open branch.conf to add push_location: $!\n";
919 } elsif ($repo_type eq 'git') {
920 my $tg_info = tg_info($repo_url);
921 my $wcdir = $destdir;
922 # HACK: if $destdir is unknown, take last URL part and remove /.git$/
923 $wcdir = (split m|\.|, (split m|/|, $repo_url)[-1])[0]
924 unless length $wcdir;
925 if ($$tg_info{'topgit'} eq 'yes') {
926 print "TopGit detected, populating top-bases ...\n";
927 system("cd $wcdir && tg remote --populate origin");
929 print STDERR "TopGit population failed\n" if $rc != 0;
931 system("cd $wcdir && git config user.name \"$ENV{'DEBFULLNAME'}\"")
932 if (defined($ENV{'DEBFULLNAME'}));
933 system("cd $wcdir && git config user.email \"$ENV{'DEBEMAIL'}\"")
934 if (defined($ENV{'DEBEMAIL'}));
935 if (length $git_track) {
937 if ($git_track eq '*') {
938 @heads = git_ls_remote($repo_url, 'refs/heads');
940 @heads = split ' ', $git_track;
942 # Filter out any branches already populated via TopGit
943 my @tgheads = split ' ', $$tg_info{'top-bases'};
944 my $master = 'master';
945 if (open(HEAD, "env GIT_DIR=\"$wcdir/.git\" git symbolic-ref HEAD |")) {
948 $master =~ s@refs/heads/@@;
951 foreach my $head (@heads) {
952 next if $head eq $master;
953 next if grep { $head eq $_ } @tgheads;
954 my $cmd = "cd $wcdir";
955 $cmd .= " && git branch --track $head remotes/origin/$head";
959 } elsif ($repo_type eq 'hg') {
961 $username .= " $ENV{'DEBFULLNAME'}" if (defined($ENV{'DEBFULLNAME'}));
962 $username .= " <$ENV{'DEBEMAIL'}>" if (defined($ENV{'DEBEMAIL'}));
964 if (open(HGRC, '>>', "$destdir/.hg/hgrc")) {
965 print HGRC "[ui]\nusername =$username\n";
969 "failed to open hgrc to set username: $!\n";