Merge git://git.bogomips.org/git-svn
* git://git.bogomips.org/git-svn: git svn: Doc update for multiple branch and tag paths git svn: cleanup t9138-multiple-branches git-svn: Canonicalize svn urls to prevent libsvn assertion t9138: remove stray dot in test which broke bash git-svn: convert globs to regexps for branch destinations git svn: Support multiple branch and tag paths in the svn repository. Add 'git svn reset' to unwind 'git svn fetch' git-svn: speed up find_rev_before Add 'git svn help [cmd]' which works outside a repo. git-svn: let 'dcommit $rev' work on $rev instead of HEAD
This commit is contained in:
commit
4f2b15ce88
@ -3,7 +3,7 @@ git-svn(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-svn - Bidirectional operation between a single Subversion branch and git
|
||||
git-svn - Bidirectional operation between a Subversion repository and git
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
@ -15,13 +15,12 @@ DESCRIPTION
|
||||
It provides a bidirectional flow of changes between a Subversion and a git
|
||||
repository.
|
||||
|
||||
'git-svn' can track a single Subversion branch simply by using a
|
||||
URL to the branch, follow branches laid out in the Subversion recommended
|
||||
method (trunk, branches, tags directories) with the --stdlayout option, or
|
||||
follow branches in any layout with the -T/-t/-b options (see options to
|
||||
'init' below, and also the 'clone' command).
|
||||
'git-svn' can track a standard Subversion repository,
|
||||
following the common "trunk/branches/tags" layout, with the --stdlayout option.
|
||||
It can also follow branches and tags in any layout with the -T/-t/-b options
|
||||
(see options to 'init' below, and also the 'clone' command).
|
||||
|
||||
Once tracking a Subversion branch (with any of the above methods), the git
|
||||
Once tracking a Subversion repository (with any of the above methods), the git
|
||||
repository can be updated from Subversion by the 'fetch' command and
|
||||
Subversion updated from git by the 'dcommit' command.
|
||||
|
||||
@ -48,8 +47,11 @@ COMMANDS
|
||||
--stdlayout;;
|
||||
These are optional command-line options for init. Each of
|
||||
these flags can point to a relative repository path
|
||||
(--tags=project/tags') or a full url
|
||||
(--tags=https://foo.org/project/tags). The option --stdlayout is
|
||||
(--tags=project/tags) or a full url
|
||||
(--tags=https://foo.org/project/tags).
|
||||
You can specify more than one --tags and/or --branches options, in case
|
||||
your Subversion repository places tags or branches under multiple paths.
|
||||
The option --stdlayout is
|
||||
a shorthand way of setting trunk,tags,branches as the relative paths,
|
||||
which is the Subversion default. If any of the other options are given
|
||||
as well, they take precedence.
|
||||
@ -170,8 +172,9 @@ and have no uncommitted changes.
|
||||
It is recommended that you run 'git-svn' fetch and rebase (not
|
||||
pull or merge) your commits against the latest changes in the
|
||||
SVN repository.
|
||||
An optional command-line argument may be specified as an
|
||||
alternative to HEAD.
|
||||
An optional revision or branch argument may be specified, and
|
||||
causes 'git-svn' to do all work on that revision/branch
|
||||
instead of HEAD.
|
||||
This is advantageous over 'set-tree' (below) because it produces
|
||||
cleaner, more linear history.
|
||||
+
|
||||
@ -204,6 +207,20 @@ config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
|
||||
Create a tag by using the tags_subdir instead of the branches_subdir
|
||||
specified during git svn init.
|
||||
|
||||
-d;;
|
||||
--destination;;
|
||||
If more than one --branches (or --tags) option was given to the 'init'
|
||||
or 'clone' command, you must provide the location of the branch (or
|
||||
tag) you wish to create in the SVN repository. The value of this
|
||||
option must match one of the paths specified by a --branches (or
|
||||
--tags) option. You can see these paths with the commands
|
||||
+
|
||||
git config --get-all svn-remote.<name>.branches
|
||||
git config --get-all svn-remote.<name>.tags
|
||||
+
|
||||
where <name> is the name of the SVN repository as specified by the -R option to
|
||||
'init' (or "svn" by default).
|
||||
|
||||
'tag'::
|
||||
Create a tag in the SVN repository. This is a shorthand for
|
||||
'branch -t'.
|
||||
@ -215,7 +232,7 @@ config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
|
||||
The following features from `svn log' are supported:
|
||||
+
|
||||
--
|
||||
--revision=<n>[:<n>];;
|
||||
-r/--revision=<n>[:<n>];;
|
||||
is supported, non-numeric args are not:
|
||||
HEAD, NEXT, BASE, PREV, etc ...
|
||||
-v/--verbose;;
|
||||
@ -313,6 +330,63 @@ Any other arguments are passed directly to 'git-log'
|
||||
Shows the Subversion externals. Use -r/--revision to specify a
|
||||
specific revision.
|
||||
|
||||
'reset'::
|
||||
Undoes the effects of 'fetch' back to the specified revision.
|
||||
This allows you to re-'fetch' an SVN revision. Normally the
|
||||
contents of an SVN revision should never change and 'reset'
|
||||
should not be necessary. However, if SVN permissions change,
|
||||
or if you alter your --ignore-paths option, a 'fetch' may fail
|
||||
with "not found in commit" (file not previously visible) or
|
||||
"checksum mismatch" (missed a modification). If the problem
|
||||
file cannot be ignored forever (with --ignore-paths) the only
|
||||
way to repair the repo is to use 'reset'.
|
||||
|
||||
Only the rev_map and refs/remotes/git-svn are changed. Follow 'reset'
|
||||
with a 'fetch' and then 'git-reset' or 'git-rebase' to move local
|
||||
branches onto the new tree.
|
||||
|
||||
-r/--revision=<n>;;
|
||||
Specify the most recent revision to keep. All later revisions
|
||||
are discarded.
|
||||
-p/--parent;;
|
||||
Discard the specified revision as well, keeping the nearest
|
||||
parent instead.
|
||||
Example:;;
|
||||
Assume you have local changes in "master", but you need to refetch "r2".
|
||||
|
||||
------------
|
||||
r1---r2---r3 remotes/git-svn
|
||||
\
|
||||
A---B master
|
||||
------------
|
||||
|
||||
Fix the ignore-paths or SVN permissions problem that caused "r2" to
|
||||
be incomplete in the first place. Then:
|
||||
|
||||
[verse]
|
||||
git svn reset -r2 -p
|
||||
git svn fetch
|
||||
|
||||
------------
|
||||
r1---r2'--r3' remotes/git-svn
|
||||
\
|
||||
r2---r3---A---B master
|
||||
------------
|
||||
|
||||
Then fixup "master" with 'git-rebase'.
|
||||
Do NOT use 'git-merge' or your history will not be compatible with a
|
||||
future 'dcommit'!
|
||||
|
||||
[verse]
|
||||
git rebase --onto remotes/git-svn A^ master
|
||||
|
||||
------------
|
||||
r1---r2'--r3' remotes/git-svn
|
||||
\
|
||||
A'--B' master
|
||||
------------
|
||||
|
||||
|
||||
--
|
||||
|
||||
OPTIONS
|
||||
@ -669,6 +743,16 @@ already dcommitted. It is considered bad practice to --amend commits
|
||||
you've already pushed to a remote repository for other users, and
|
||||
dcommit with SVN is analogous to that.
|
||||
|
||||
When using multiple --branches or --tags, 'git-svn' does not automatically
|
||||
handle name collisions (for example, if two branches from different paths have
|
||||
the same name, or if a branch and a tag have the same name). In these cases,
|
||||
use 'init' to set up your git repository then, before your first 'fetch', edit
|
||||
the .git/config file so that the branches and tags are associated with
|
||||
different name spaces. For example:
|
||||
|
||||
branches = stable/*:refs/remotes/svn/stable/*
|
||||
branches = debug/*:refs/remotes/svn/debug/*
|
||||
|
||||
BUGS
|
||||
----
|
||||
|
||||
|
173
git-svn.perl
173
git-svn.perl
@ -63,7 +63,7 @@ my ($SVN);
|
||||
$sha1 = qr/[a-f\d]{40}/;
|
||||
$sha1_short = qr/[a-f\d]{4,40}/;
|
||||
my ($_stdin, $_help, $_edit,
|
||||
$_message, $_file,
|
||||
$_message, $_file, $_branch_dest,
|
||||
$_template, $_shared,
|
||||
$_version, $_fetch_all, $_no_rebase, $_fetch_parent,
|
||||
$_merge, $_strategy, $_dry_run, $_local,
|
||||
@ -92,11 +92,11 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
|
||||
'localtime' => \$Git::SVN::_localtime,
|
||||
%remote_opts );
|
||||
|
||||
my ($_trunk, $_tags, $_branches, $_stdlayout);
|
||||
my ($_trunk, @_tags, @_branches, $_stdlayout);
|
||||
my %icv;
|
||||
my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared,
|
||||
'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags,
|
||||
'branches|b=s' => \$_branches, 'prefix=s' => \$_prefix,
|
||||
'trunk|T=s' => \$_trunk, 'tags|t=s@' => \@_tags,
|
||||
'branches|b=s@' => \@_branches, 'prefix=s' => \$_prefix,
|
||||
'stdlayout|s' => \$_stdlayout,
|
||||
'minimize-url|m' => \$Git::SVN::_minimize_url,
|
||||
'no-metadata' => sub { $icv{noMetadata} = 1 },
|
||||
@ -141,11 +141,13 @@ my %cmd = (
|
||||
branch => [ \&cmd_branch,
|
||||
'Create a branch in the SVN repository',
|
||||
{ 'message|m=s' => \$_message,
|
||||
'destination|d=s' => \$_branch_dest,
|
||||
'dry-run|n' => \$_dry_run,
|
||||
'tag|t' => \$_tag } ],
|
||||
tag => [ sub { $_tag = 1; cmd_branch(@_) },
|
||||
'Create a tag in the SVN repository',
|
||||
{ 'message|m=s' => \$_message,
|
||||
'destination|d=s' => \$_branch_dest,
|
||||
'dry-run|n' => \$_dry_run } ],
|
||||
'set-tree' => [ \&cmd_set_tree,
|
||||
"Set an SVN repository to a git tree-ish",
|
||||
@ -211,6 +213,10 @@ my %cmd = (
|
||||
'blame' => [ \&Git::SVN::Log::cmd_blame,
|
||||
"Show what revision and author last modified each line of a file",
|
||||
{ 'git-format' => \$_git_format } ],
|
||||
'reset' => [ \&cmd_reset,
|
||||
"Undo fetches back to the specified SVN revision",
|
||||
{ 'revision|r=s' => \$_revision,
|
||||
'parent|p' => \$_fetch_parent } ],
|
||||
);
|
||||
|
||||
my $cmd;
|
||||
@ -219,6 +225,9 @@ for (my $i = 0; $i < @ARGV; $i++) {
|
||||
$cmd = $ARGV[$i];
|
||||
splice @ARGV, $i, 1;
|
||||
last;
|
||||
} elsif ($ARGV[$i] eq 'help') {
|
||||
$cmd = $ARGV[$i+1];
|
||||
usage(0);
|
||||
}
|
||||
};
|
||||
|
||||
@ -358,7 +367,7 @@ sub init_subdir {
|
||||
sub cmd_clone {
|
||||
my ($url, $path) = @_;
|
||||
if (!defined $path &&
|
||||
(defined $_trunk || defined $_branches || defined $_tags ||
|
||||
(defined $_trunk || @_branches || @_tags ||
|
||||
defined $_stdlayout) &&
|
||||
$url !~ m#^[a-z\+]+://#) {
|
||||
$path = $url;
|
||||
@ -372,14 +381,15 @@ sub cmd_clone {
|
||||
sub cmd_init {
|
||||
if (defined $_stdlayout) {
|
||||
$_trunk = 'trunk' if (!defined $_trunk);
|
||||
$_tags = 'tags' if (!defined $_tags);
|
||||
$_branches = 'branches' if (!defined $_branches);
|
||||
@_tags = 'tags' if (! @_tags);
|
||||
@_branches = 'branches' if (! @_branches);
|
||||
}
|
||||
if (defined $_trunk || defined $_branches || defined $_tags) {
|
||||
if (defined $_trunk || @_branches || @_tags) {
|
||||
return cmd_multi_init(@_);
|
||||
}
|
||||
my $url = shift or die "SVN repository location required ",
|
||||
"as a command-line argument\n";
|
||||
$url = canonicalize_url($url);
|
||||
init_subdir(@_);
|
||||
do_git_init_db();
|
||||
|
||||
@ -454,8 +464,22 @@ sub cmd_dcommit {
|
||||
'Cannot dcommit with a dirty index. Commit your changes first, '
|
||||
. "or stash them with `git stash'.\n";
|
||||
$head ||= 'HEAD';
|
||||
|
||||
my $old_head;
|
||||
if ($head ne 'HEAD') {
|
||||
$old_head = eval {
|
||||
command_oneline([qw/symbolic-ref -q HEAD/])
|
||||
};
|
||||
if ($old_head) {
|
||||
$old_head =~ s{^refs/heads/}{};
|
||||
} else {
|
||||
$old_head = eval { command_oneline(qw/rev-parse HEAD/) };
|
||||
}
|
||||
command(['checkout', $head], STDERR => 0);
|
||||
}
|
||||
|
||||
my @refs;
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD', \@refs);
|
||||
unless ($gs) {
|
||||
die "Unable to determine upstream SVN information from ",
|
||||
"$head history.\nPerhaps the repository is empty.";
|
||||
@ -541,7 +565,7 @@ sub cmd_dcommit {
|
||||
if (@diff) {
|
||||
@refs = ();
|
||||
my ($url_, $rev_, $uuid_, $gs_) =
|
||||
working_head_info($head, \@refs);
|
||||
working_head_info('HEAD', \@refs);
|
||||
my ($linear_refs_, $parents_) =
|
||||
linearize_history($gs_, \@refs);
|
||||
if (scalar(@$linear_refs) !=
|
||||
@ -579,6 +603,22 @@ sub cmd_dcommit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($old_head) {
|
||||
my $new_head = command_oneline(qw/rev-parse HEAD/);
|
||||
my $new_is_symbolic = eval {
|
||||
command_oneline(qw/symbolic-ref -q HEAD/);
|
||||
};
|
||||
if ($new_is_symbolic) {
|
||||
print "dcommitted the branch ", $head, "\n";
|
||||
} else {
|
||||
print "dcommitted on a detached HEAD because you gave ",
|
||||
"a revision argument.\n",
|
||||
"The rewritten commit is: ", $new_head, "\n";
|
||||
}
|
||||
command(['checkout', $old_head], STDERR => 0);
|
||||
}
|
||||
|
||||
unlink $gs->{index};
|
||||
}
|
||||
|
||||
@ -593,7 +633,33 @@ sub cmd_branch {
|
||||
my ($src, $rev, undef, $gs) = working_head_info($head);
|
||||
|
||||
my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
|
||||
my $glob = $remote->{ $_tag ? 'tags' : 'branches' };
|
||||
my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' };
|
||||
my $glob;
|
||||
if ($#{$allglobs} == 0) {
|
||||
$glob = $allglobs->[0];
|
||||
} else {
|
||||
unless(defined $_branch_dest) {
|
||||
die "Multiple ",
|
||||
$_tag ? "tag" : "branch",
|
||||
" paths defined for Subversion repository.\n",
|
||||
"You must specify where you want to create the ",
|
||||
$_tag ? "tag" : "branch",
|
||||
" with the --destination argument.\n";
|
||||
}
|
||||
foreach my $g (@{$allglobs}) {
|
||||
# SVN::Git::Editor could probably be moved to Git.pm..
|
||||
my $re = SVN::Git::Editor::glob2pat($g->{path}->{left});
|
||||
if ($_branch_dest =~ /$re/) {
|
||||
$glob = $g;
|
||||
last;
|
||||
}
|
||||
}
|
||||
unless (defined $glob) {
|
||||
die "Unknown ",
|
||||
$_tag ? "tag" : "branch",
|
||||
" destination $_branch_dest\n";
|
||||
}
|
||||
}
|
||||
my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};
|
||||
my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ());
|
||||
|
||||
@ -741,6 +807,12 @@ sub canonicalize_path {
|
||||
return $path;
|
||||
}
|
||||
|
||||
sub canonicalize_url {
|
||||
my ($url) = @_;
|
||||
$url =~ s#^([^:]+://[^/]*/)(.*)$#$1 . canonicalize_path($2)#e;
|
||||
return $url;
|
||||
}
|
||||
|
||||
# get_svnprops(PATH)
|
||||
# ------------------
|
||||
# Helper for cmd_propget and cmd_proplist below.
|
||||
@ -800,7 +872,7 @@ sub cmd_proplist {
|
||||
|
||||
sub cmd_multi_init {
|
||||
my $url = shift;
|
||||
unless (defined $_trunk || defined $_branches || defined $_tags) {
|
||||
unless (defined $_trunk || @_branches || @_tags) {
|
||||
usage(1);
|
||||
}
|
||||
|
||||
@ -810,7 +882,7 @@ sub cmd_multi_init {
|
||||
|
||||
$_prefix = '' unless defined $_prefix;
|
||||
if (defined $url) {
|
||||
$url =~ s#/+$##;
|
||||
$url = canonicalize_url($url);
|
||||
init_subdir(@_);
|
||||
}
|
||||
do_git_init_db();
|
||||
@ -825,10 +897,14 @@ sub cmd_multi_init {
|
||||
undef, $trunk_ref);
|
||||
}
|
||||
}
|
||||
return unless defined $_branches || defined $_tags;
|
||||
return unless @_branches || @_tags;
|
||||
my $ra = $url ? Git::SVN::Ra->new($url) : undef;
|
||||
complete_url_ls_init($ra, $_branches, '--branches/-b', $_prefix);
|
||||
complete_url_ls_init($ra, $_tags, '--tags/-t', $_prefix . 'tags/');
|
||||
foreach my $path (@_branches) {
|
||||
complete_url_ls_init($ra, $path, '--branches/-b', $_prefix);
|
||||
}
|
||||
foreach my $path (@_tags) {
|
||||
complete_url_ls_init($ra, $path, '--tags/-t', $_prefix.'tags/');
|
||||
}
|
||||
}
|
||||
|
||||
sub cmd_multi_fetch {
|
||||
@ -1021,6 +1097,20 @@ sub cmd_info {
|
||||
print $result, "\n";
|
||||
}
|
||||
|
||||
sub cmd_reset {
|
||||
my $target = shift || $_revision or die "SVN revision required\n";
|
||||
$target = $1 if $target =~ /^r(\d+)$/;
|
||||
$target =~ /^\d+$/ or die "Numeric SVN revision expected\n";
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
|
||||
unless ($gs) {
|
||||
die "Unable to determine upstream SVN information from ".
|
||||
"history\n";
|
||||
}
|
||||
my ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent);
|
||||
$gs->rev_map_set($r, $c, 'reset', $uuid);
|
||||
print "r$r = $c ($gs->{ref_id})\n";
|
||||
}
|
||||
|
||||
########################### utility functions #########################
|
||||
|
||||
sub rebase_cmd {
|
||||
@ -1099,6 +1189,7 @@ sub complete_url_ls_init {
|
||||
die "--prefix='$pfx' must have a trailing slash '/'\n";
|
||||
}
|
||||
command_noisy('config',
|
||||
'--add',
|
||||
"svn-remote.$gs->{repo_id}.$n",
|
||||
"$remote_path:refs/remotes/$pfx*" .
|
||||
('/*' x (($remote_path =~ tr/*/*/) - 1)) );
|
||||
@ -1565,7 +1656,8 @@ sub fetch_all {
|
||||
# read the max revs for wildcard expansion (branches/*, tags/*)
|
||||
foreach my $t (qw/branches tags/) {
|
||||
defined $remote->{$t} or next;
|
||||
push @globs, $remote->{$t};
|
||||
push @globs, @{$remote->{$t}};
|
||||
|
||||
my $max_rev = eval { tmp_config(qw/--int --get/,
|
||||
"svn-remote.$repo_id.${t}-maxRev") };
|
||||
if (defined $max_rev && ($max_rev < $base)) {
|
||||
@ -1612,15 +1704,16 @@ sub read_all_remotes {
|
||||
} elsif (m!^(.+)\.(branches|tags)=
|
||||
(.*):refs/remotes/(.+)\s*$/!x) {
|
||||
my ($p, $g) = ($3, $4);
|
||||
my $rs = $r->{$1}->{$2} = {
|
||||
t => $2,
|
||||
remote => $1,
|
||||
path => Git::SVN::GlobSpec->new($p),
|
||||
ref => Git::SVN::GlobSpec->new($g) };
|
||||
my $rs = {
|
||||
t => $2,
|
||||
remote => $1,
|
||||
path => Git::SVN::GlobSpec->new($p),
|
||||
ref => Git::SVN::GlobSpec->new($g) };
|
||||
if (length($rs->{ref}->{right}) != 0) {
|
||||
die "The '*' glob character must be the last ",
|
||||
"character of '$g'\n";
|
||||
}
|
||||
push @{ $r->{$1}->{$2} }, $rs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1760,9 +1853,10 @@ sub find_by_url { # repos_root and, path are optional
|
||||
next if defined $repos_root && $repos_root ne $u;
|
||||
|
||||
my $fetch = $remotes->{$repo_id}->{fetch} || {};
|
||||
foreach (qw/branches tags/) {
|
||||
resolve_local_globs($u, $fetch,
|
||||
$remotes->{$repo_id}->{$_});
|
||||
foreach my $t (qw/branches tags/) {
|
||||
foreach my $globspec (@{$remotes->{$repo_id}->{$t}}) {
|
||||
resolve_local_globs($u, $fetch, $globspec);
|
||||
}
|
||||
}
|
||||
my $p = $path;
|
||||
my $rwr = rewrite_root({repo_id => $repo_id});
|
||||
@ -2990,6 +3084,14 @@ sub _rev_map_set {
|
||||
croak "write: $!";
|
||||
}
|
||||
|
||||
sub _rev_map_reset {
|
||||
my ($fh, $rev, $commit) = @_;
|
||||
my $c = _rev_map_get($fh, $rev);
|
||||
$c eq $commit or die "_rev_map_reset(@_) commit $c does not match!\n";
|
||||
my $offset = sysseek($fh, 0, SEEK_CUR) or croak "seek: $!";
|
||||
truncate $fh, $offset or croak "truncate: $!";
|
||||
}
|
||||
|
||||
sub mkfile {
|
||||
my ($path) = @_;
|
||||
unless (-e $path) {
|
||||
@ -3006,6 +3108,7 @@ sub rev_map_set {
|
||||
my $db = $self->map_path($uuid);
|
||||
my $db_lock = "$db.lock";
|
||||
my $sig;
|
||||
$update_ref ||= 0;
|
||||
if ($update_ref) {
|
||||
$SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
|
||||
$SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] };
|
||||
@ -3029,7 +3132,8 @@ sub rev_map_set {
|
||||
|
||||
sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
|
||||
or croak "Couldn't open $db_lock: $!\n";
|
||||
_rev_map_set($fh, $rev, $commit);
|
||||
$update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
|
||||
_rev_map_set($fh, $rev, $commit);
|
||||
if ($sync) {
|
||||
$fh->flush or die "Couldn't flush $db_lock: $!\n";
|
||||
$fh->sync or die "Couldn't sync $db_lock: $!\n";
|
||||
@ -3037,7 +3141,9 @@ sub rev_map_set {
|
||||
close $fh or croak $!;
|
||||
if ($update_ref) {
|
||||
$_head = $self;
|
||||
command_noisy('update-ref', '-m', "r$rev",
|
||||
my $note = "";
|
||||
$note = " ($update_ref)" if ($update_ref !~ /^\d*$/);
|
||||
command_noisy('update-ref', '-m', "r$rev$note",
|
||||
$self->refname, $commit);
|
||||
}
|
||||
rename $db_lock, $db or die "rev_map_set(@_): ", "Failed to rename: ",
|
||||
@ -3099,12 +3205,19 @@ sub rev_map_get {
|
||||
return undef unless -e $map_path;
|
||||
|
||||
sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
|
||||
my $c = _rev_map_get($fh, $rev);
|
||||
close($fh) or croak "close: $!";
|
||||
$c
|
||||
}
|
||||
|
||||
sub _rev_map_get {
|
||||
my ($fh, $rev) = @_;
|
||||
|
||||
binmode $fh or croak "binmode: $!";
|
||||
my $size = (stat($fh))[7];
|
||||
($size % 24) == 0 or croak "inconsistent size: $size";
|
||||
|
||||
if ($size == 0) {
|
||||
close $fh or croak "close: $fh";
|
||||
return undef;
|
||||
}
|
||||
|
||||
@ -3122,11 +3235,9 @@ sub rev_map_get {
|
||||
} elsif ($r > $rev) {
|
||||
$u = $i - 24;
|
||||
} else { # $r == $rev
|
||||
close($fh) or croak "close: $!";
|
||||
return $c eq ('0' x 40) ? undef : $c;
|
||||
}
|
||||
}
|
||||
close($fh) or croak "close: $!";
|
||||
undef;
|
||||
}
|
||||
|
||||
@ -3138,6 +3249,8 @@ sub find_rev_before {
|
||||
my ($self, $rev, $eq_ok, $min_rev) = @_;
|
||||
--$rev unless $eq_ok;
|
||||
$min_rev ||= 1;
|
||||
my $max_rev = $self->rev_map_max;
|
||||
$rev = $max_rev if ($rev > $max_rev);
|
||||
while ($rev >= $min_rev) {
|
||||
if (my $c = $self->rev_map_get($rev)) {
|
||||
return ($rev, $c);
|
||||
|
@ -231,6 +231,25 @@ test_expect_success \
|
||||
"^:refs/${remotes_git_svn}$"
|
||||
'
|
||||
|
||||
test_expect_success 'dcommit $rev does not clobber current branch' '
|
||||
git svn fetch -i bar &&
|
||||
git checkout -b my-bar refs/remotes/bar &&
|
||||
echo 1 > foo &&
|
||||
git add foo &&
|
||||
git commit -m "change 1" &&
|
||||
echo 2 > foo &&
|
||||
git add foo &&
|
||||
git commit -m "change 2" &&
|
||||
old_head=$(git rev-parse HEAD) &&
|
||||
git svn dcommit -i bar HEAD^ &&
|
||||
test $old_head = $(git rev-parse HEAD) &&
|
||||
test refs/heads/my-bar = $(git symbolic-ref HEAD) &&
|
||||
git log refs/remotes/bar | grep "change 1" &&
|
||||
! git log refs/remotes/bar | grep "change 2" &&
|
||||
git checkout master &&
|
||||
git branch -D my-bar
|
||||
'
|
||||
|
||||
test_expect_success 'able to dcommit to a subdirectory' "
|
||||
git svn fetch -i bar &&
|
||||
git checkout -b my-bar refs/remotes/bar &&
|
||||
|
122
t/t9138-git-svn-multiple-branches.sh
Executable file
122
t/t9138-git-svn-multiple-branches.sh
Executable file
@ -0,0 +1,122 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009 Marc Branchaud
|
||||
#
|
||||
|
||||
test_description='git svn multiple branch and tag paths in the svn repo'
|
||||
. ./lib-git-svn.sh
|
||||
|
||||
test_expect_success 'setup svnrepo' '
|
||||
mkdir project \
|
||||
project/trunk \
|
||||
project/b_one \
|
||||
project/b_two \
|
||||
project/tags_A \
|
||||
project/tags_B &&
|
||||
echo 1 > project/trunk/a.file &&
|
||||
svn_cmd import -m "$test_description" project "$svnrepo/project" &&
|
||||
rm -rf project &&
|
||||
svn_cmd cp -m "Branch 1" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/b_one/first" &&
|
||||
svn_cmd cp -m "Tag 1" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/tags_A/1.0" &&
|
||||
svn_cmd co "$svnrepo/project" svn_project &&
|
||||
( cd svn_project &&
|
||||
echo 2 > trunk/a.file &&
|
||||
svn_cmd ci -m "Change 1" trunk/a.file &&
|
||||
svn_cmd cp -m "Branch 2" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/b_one/second" &&
|
||||
svn_cmd cp -m "Tag 2" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/tags_A/2.0" &&
|
||||
echo 3 > trunk/a.file &&
|
||||
svn_cmd ci -m "Change 2" trunk/a.file &&
|
||||
svn_cmd cp -m "Branch 3" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/b_two/1" &&
|
||||
svn_cmd cp -m "Tag 3" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/tags_A/3.0" &&
|
||||
echo 4 > trunk/a.file &&
|
||||
svn_cmd ci -m "Change 3" trunk/a.file &&
|
||||
svn_cmd cp -m "Branch 4" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/b_two/2" &&
|
||||
svn_cmd cp -m "Tag 4" "$svnrepo/project/trunk" \
|
||||
"$svnrepo/project/tags_A/4.0" &&
|
||||
svn_cmd up &&
|
||||
echo 5 > b_one/first/a.file &&
|
||||
svn_cmd ci -m "Change 4" b_one/first/a.file &&
|
||||
svn_cmd cp -m "Tag 5" "$svnrepo/project/b_one/first" \
|
||||
"$svnrepo/project/tags_B/v5" &&
|
||||
echo 6 > b_one/second/a.file &&
|
||||
svn_cmd ci -m "Change 5" b_one/second/a.file &&
|
||||
svn_cmd cp -m "Tag 6" "$svnrepo/project/b_one/second" \
|
||||
"$svnrepo/project/tags_B/v6" &&
|
||||
echo 7 > b_two/1/a.file &&
|
||||
svn_cmd ci -m "Change 6" b_two/1/a.file &&
|
||||
svn_cmd cp -m "Tag 7" "$svnrepo/project/b_two/1" \
|
||||
"$svnrepo/project/tags_B/v7" &&
|
||||
echo 8 > b_two/2/a.file &&
|
||||
svn_cmd ci -m "Change 7" b_two/2/a.file &&
|
||||
svn_cmd cp -m "Tag 8" "$svnrepo/project/b_two/2" \
|
||||
"$svnrepo/project/tags_B/v8"
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'clone multiple branch and tag paths' '
|
||||
git svn clone -T trunk \
|
||||
-b b_one/* --branches b_two/* \
|
||||
-t tags_A/* --tags tags_B \
|
||||
"$svnrepo/project" git_project &&
|
||||
( cd git_project &&
|
||||
git rev-parse refs/remotes/first &&
|
||||
git rev-parse refs/remotes/second &&
|
||||
git rev-parse refs/remotes/1 &&
|
||||
git rev-parse refs/remotes/2 &&
|
||||
git rev-parse refs/remotes/tags/1.0 &&
|
||||
git rev-parse refs/remotes/tags/2.0 &&
|
||||
git rev-parse refs/remotes/tags/3.0 &&
|
||||
git rev-parse refs/remotes/tags/4.0 &&
|
||||
git rev-parse refs/remotes/tags/v5 &&
|
||||
git rev-parse refs/remotes/tags/v6 &&
|
||||
git rev-parse refs/remotes/tags/v7 &&
|
||||
git rev-parse refs/remotes/tags/v8
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'Multiple branch or tag paths require -d' '
|
||||
( cd git_project &&
|
||||
test_must_fail git svn branch -m "No new branch" Nope &&
|
||||
test_must_fail git svn tag -m "No new tag" Tagless &&
|
||||
test_must_fail git rev-parse refs/remotes/Nope &&
|
||||
test_must_fail git rev-parse refs/remotes/tags/Tagless
|
||||
) &&
|
||||
( cd svn_project &&
|
||||
svn_cmd up &&
|
||||
test_must_fail test -d b_one/Nope &&
|
||||
test_must_fail test -d b_two/Nope &&
|
||||
test_must_fail test -d tags_A/Tagless &&
|
||||
test_must_fail test -d tags_B/Tagless
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'create new branches and tags' '
|
||||
( cd git_project &&
|
||||
git svn branch -m "New branch 1" -d project/b_one New1 ) &&
|
||||
( cd svn_project &&
|
||||
svn_cmd up && test -e b_one/New1/a.file ) &&
|
||||
|
||||
( cd git_project &&
|
||||
git svn branch -m "New branch 2" -d project/b_two New2 ) &&
|
||||
( cd svn_project &&
|
||||
svn_cmd up && test -e b_two/New2/a.file ) &&
|
||||
|
||||
( cd git_project &&
|
||||
git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) &&
|
||||
( cd svn_project &&
|
||||
svn_cmd up && test -e tags_A/Tag1/a.file ) &&
|
||||
|
||||
( cd git_project &&
|
||||
git svn tag -m "New tag 2" -d project/tags_B Tag2 ) &&
|
||||
( cd svn_project &&
|
||||
svn_cmd up && test -e tags_B/Tag2/a.file )
|
||||
'
|
||||
|
||||
test_done
|
66
t/t9139-git-svn-reset.sh
Executable file
66
t/t9139-git-svn-reset.sh
Executable file
@ -0,0 +1,66 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009 Ben Jackson
|
||||
#
|
||||
|
||||
test_description='git svn reset'
|
||||
. ./lib-git-svn.sh
|
||||
|
||||
test_expect_success 'setup test repository' '
|
||||
svn_cmd co "$svnrepo" s &&
|
||||
(
|
||||
cd s &&
|
||||
mkdir vis &&
|
||||
echo always visible > vis/vis.txt &&
|
||||
svn_cmd add vis &&
|
||||
svn_cmd commit -m "create visible files" &&
|
||||
mkdir hid &&
|
||||
echo initially hidden > hid/hid.txt &&
|
||||
svn_cmd add hid &&
|
||||
svn_cmd commit -m "create initially hidden files" &&
|
||||
svn_cmd up &&
|
||||
echo mod >> vis/vis.txt &&
|
||||
svn_cmd commit -m "modify vis" &&
|
||||
svn_cmd up
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'clone SVN repository with hidden directory' '
|
||||
git svn init "$svnrepo" g &&
|
||||
( cd g && git svn fetch --ignore-paths="^hid" )
|
||||
'
|
||||
|
||||
test_expect_success 'modify hidden file in SVN repo' '
|
||||
( cd s &&
|
||||
echo mod hidden >> hid/hid.txt &&
|
||||
svn_cmd commit -m "modify hid" &&
|
||||
svn_cmd up
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'fetch fails on modified hidden file' '
|
||||
( cd g &&
|
||||
git svn find-rev refs/remotes/git-svn > ../expect &&
|
||||
! git svn fetch 2> ../errors &&
|
||||
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
|
||||
fgrep "not found in commit" errors &&
|
||||
test_cmp expect expect2
|
||||
'
|
||||
|
||||
test_expect_success 'reset unwinds back to r1' '
|
||||
( cd g &&
|
||||
git svn reset -r1 &&
|
||||
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
|
||||
echo 1 >expect &&
|
||||
test_cmp expect expect2
|
||||
'
|
||||
|
||||
test_expect_success 'refetch succeeds not ignoring any files' '
|
||||
( cd g &&
|
||||
git svn fetch &&
|
||||
git svn rebase &&
|
||||
fgrep "mod hidden" hid/hid.txt
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user