mirror of
https://github.com/git/git.git
synced 2024-11-18 06:54:55 +01:00
Merge branch 'tl/anno' into next
* tl/anno: annotate should number lines starting with 1 contrib/git-svn: fix a copied-tree bug in an overzealous assertion show-branch --topics: omit more uninteresting commits. workaround fat/ntfs deficiencies for t3600-rm.sh (git-rm) git-mv: fix moves into a subdir from outside send-email: accept --no-signed-off-by-cc as the documentation states contrib/git-svn: better documenting of CLI switches contrib/git-svn: add --id/-i=$GIT_SVN_ID command-line switch contrib/git-svn: avoid re-reading the repository uuid, it never changes contrib/git-svn: create a more recent master if one does not exist contrib/git-svn: cleanup option parsing contrib/git-svn: allow --authors-file to be specified contrib/git-svn: strip 'git-svn-id:' when commiting to SVN contrib/git-svn: several small bug fixes and changes contrib/git-svn: add -b/--branch switch for branch detection Prevent --index-info from ignoring -z. manpages: insert two missing [verse] markers for multi-line SYNOPSIS gitview: pass the missing argument _show_clicked_cb. Fix test case for some sed git-branch: add -r switch to list refs/remotes/*
This commit is contained in:
commit
fe041ad68d
14 changed files with 254 additions and 99 deletions
|
@ -8,6 +8,7 @@ git-repo-config - Get and set options in .git/config.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
|
[verse]
|
||||||
'git-repo-config' [type] name [value [value_regex]]
|
'git-repo-config' [type] name [value [value_regex]]
|
||||||
'git-repo-config' [type] --replace-all name [value [value_regex]]
|
'git-repo-config' [type] --replace-all name [value [value_regex]]
|
||||||
'git-repo-config' [type] --get name [value_regex]
|
'git-repo-config' [type] --get name [value_regex]
|
||||||
|
|
|
@ -9,6 +9,7 @@ git-svnimport - Import a SVN repository into git
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
|
[verse]
|
||||||
'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ]
|
'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ]
|
||||||
[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
|
[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
|
||||||
[ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
|
[ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
|
||||||
|
|
|
@ -4,19 +4,12 @@
|
||||||
use warnings;
|
use warnings;
|
||||||
use strict;
|
use strict;
|
||||||
use vars qw/ $AUTHOR $VERSION
|
use vars qw/ $AUTHOR $VERSION
|
||||||
$SVN_URL $SVN_INFO $SVN_WC
|
$SVN_URL $SVN_INFO $SVN_WC $SVN_UUID
|
||||||
$GIT_SVN_INDEX $GIT_SVN
|
$GIT_SVN_INDEX $GIT_SVN
|
||||||
$GIT_DIR $REV_DIR/;
|
$GIT_DIR $REV_DIR/;
|
||||||
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
||||||
$VERSION = '0.10.0';
|
$VERSION = '0.10.0';
|
||||||
$GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git";
|
$GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git";
|
||||||
$GIT_SVN = $ENV{GIT_SVN_ID} || 'git-svn';
|
|
||||||
$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index";
|
|
||||||
$ENV{GIT_DIR} ||= $GIT_DIR;
|
|
||||||
$SVN_URL = undef;
|
|
||||||
$REV_DIR = "$GIT_DIR/$GIT_SVN/revs";
|
|
||||||
$SVN_WC = "$GIT_DIR/$GIT_SVN/tree";
|
|
||||||
|
|
||||||
# make sure the svn binary gives consistent output between locales and TZs:
|
# make sure the svn binary gives consistent output between locales and TZs:
|
||||||
$ENV{TZ} = 'UTC';
|
$ENV{TZ} = 'UTC';
|
||||||
$ENV{LC_ALL} = 'C';
|
$ENV{LC_ALL} = 'C';
|
||||||
|
@ -24,6 +17,7 @@
|
||||||
# If SVN:: library support is added, please make the dependencies
|
# If SVN:: library support is added, please make the dependencies
|
||||||
# optional and preserve the capability to use the command-line client.
|
# optional and preserve the capability to use the command-line client.
|
||||||
# use eval { require SVN::... } to make it lazy load
|
# use eval { require SVN::... } to make it lazy load
|
||||||
|
# We don't use any modules not in the standard Perl distribution:
|
||||||
use Carp qw/croak/;
|
use Carp qw/croak/;
|
||||||
use IO::File qw//;
|
use IO::File qw//;
|
||||||
use File::Basename qw/dirname basename/;
|
use File::Basename qw/dirname basename/;
|
||||||
|
@ -32,28 +26,30 @@
|
||||||
use File::Spec qw//;
|
use File::Spec qw//;
|
||||||
use POSIX qw/strftime/;
|
use POSIX qw/strftime/;
|
||||||
my $sha1 = qr/[a-f\d]{40}/;
|
my $sha1 = qr/[a-f\d]{40}/;
|
||||||
my $sha1_short = qr/[a-f\d]{6,40}/;
|
my $sha1_short = qr/[a-f\d]{4,40}/;
|
||||||
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
|
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
|
||||||
$_find_copies_harder, $_l, $_version, $_upgrade);
|
$_find_copies_harder, $_l, $_version, $_upgrade, $_authors);
|
||||||
|
my (@_branch_from, %tree_map, %users);
|
||||||
|
|
||||||
GetOptions( 'revision|r=s' => \$_revision,
|
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
||||||
'no-ignore-externals' => \$_no_ignore_ext,
|
'branch|b=s' => \@_branch_from,
|
||||||
'stdin|' => \$_stdin,
|
'authors-file|A=s' => \$_authors );
|
||||||
|
my %cmd = (
|
||||||
|
fetch => [ \&fetch, "Download new revisions from SVN",
|
||||||
|
{ 'revision|r=s' => \$_revision, %fc_opts } ],
|
||||||
|
init => [ \&init, "Initialize and fetch (import)", { } ],
|
||||||
|
commit => [ \&commit, "Commit git revisions to SVN",
|
||||||
|
{ 'stdin|' => \$_stdin,
|
||||||
'edit|e' => \$_edit,
|
'edit|e' => \$_edit,
|
||||||
'rmdir' => \$_rmdir,
|
'rmdir' => \$_rmdir,
|
||||||
'upgrade' => \$_upgrade,
|
|
||||||
'help|H|h' => \$_help,
|
|
||||||
'find-copies-harder' => \$_find_copies_harder,
|
'find-copies-harder' => \$_find_copies_harder,
|
||||||
'l=i' => \$_l,
|
'l=i' => \$_l,
|
||||||
'version|V' => \$_version,
|
%fc_opts,
|
||||||
'no-stop-on-copy' => \$_no_stop_copy );
|
} ],
|
||||||
my %cmd = (
|
'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", { } ],
|
||||||
fetch => [ \&fetch, "Download new revisions from SVN" ],
|
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)",
|
||||||
init => [ \&init, "Initialize and fetch (import)"],
|
{ 'no-ignore-externals' => \$_no_ignore_ext,
|
||||||
commit => [ \&commit, "Commit git revisions to SVN" ],
|
'upgrade' => \$_upgrade } ],
|
||||||
'show-ignore' => [ \&show_ignore, "Show svn:ignore listings" ],
|
|
||||||
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)" ],
|
|
||||||
help => [ \&usage, "Show help" ],
|
|
||||||
);
|
);
|
||||||
my $cmd;
|
my $cmd;
|
||||||
for (my $i = 0; $i < @ARGV; $i++) {
|
for (my $i = 0; $i < @ARGV; $i++) {
|
||||||
|
@ -64,16 +60,23 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
# we may be called as git-svn-(command), or git-svn(command).
|
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
|
||||||
foreach (keys %cmd) {
|
|
||||||
if (/git\-svn\-?($_)(?:\.\w+)?$/) {
|
GetOptions(%opts, 'help|H|h' => \$_help,
|
||||||
$cmd = $1;
|
'version|V' => \$_version,
|
||||||
last;
|
'id|i=s' => \$GIT_SVN) or exit 1;
|
||||||
}
|
|
||||||
}
|
$GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn';
|
||||||
|
$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index";
|
||||||
|
$ENV{GIT_DIR} ||= $GIT_DIR;
|
||||||
|
$SVN_URL = undef;
|
||||||
|
$REV_DIR = "$GIT_DIR/$GIT_SVN/revs";
|
||||||
|
$SVN_WC = "$GIT_DIR/$GIT_SVN/tree";
|
||||||
|
|
||||||
usage(0) if $_help;
|
usage(0) if $_help;
|
||||||
version() if $_version;
|
version() if $_version;
|
||||||
usage(1) unless (defined $cmd);
|
usage(1) unless defined $cmd;
|
||||||
|
load_authors() if $_authors;
|
||||||
svn_check_ignore_externals();
|
svn_check_ignore_externals();
|
||||||
$cmd{$cmd}->[0]->(@ARGV);
|
$cmd{$cmd}->[0]->(@ARGV);
|
||||||
exit 0;
|
exit 0;
|
||||||
|
@ -85,15 +88,25 @@ sub usage {
|
||||||
print $fd <<"";
|
print $fd <<"";
|
||||||
git-svn - bidirectional operations between a single Subversion tree and git
|
git-svn - bidirectional operations between a single Subversion tree and git
|
||||||
Usage: $0 <command> [options] [arguments]\n
|
Usage: $0 <command> [options] [arguments]\n
|
||||||
Available commands:
|
|
||||||
|
print $fd "Available commands:\n" unless $cmd;
|
||||||
|
|
||||||
foreach (sort keys %cmd) {
|
foreach (sort keys %cmd) {
|
||||||
print $fd ' ',pack('A10',$_),$cmd{$_}->[1],"\n";
|
next if $cmd && $cmd ne $_;
|
||||||
|
print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n";
|
||||||
|
foreach (keys %{$cmd{$_}->[2]}) {
|
||||||
|
# prints out arguments as they should be passed:
|
||||||
|
my $x = s#=s$## ? '<arg>' : s#=i$## ? '<num>' : '';
|
||||||
|
print $fd ' ' x 17, join(', ', map { length $_ > 1 ?
|
||||||
|
"--$_" : "-$_" }
|
||||||
|
split /\|/,$_)," $x\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
print $fd <<"";
|
print $fd <<"";
|
||||||
\nGIT_SVN_ID may be set in the environment to an arbitrary identifier if
|
\nGIT_SVN_ID may be set in the environment or via the --id/-i switch to an
|
||||||
you're tracking multiple SVN branches/repositories in one git repository
|
arbitrary identifier if you're tracking multiple SVN branches/repositories in
|
||||||
and want to keep them separate.
|
one git repository and want to keep them separate. See git-svn(1) for more
|
||||||
|
information.
|
||||||
|
|
||||||
exit $exit;
|
exit $exit;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +118,6 @@ sub version {
|
||||||
|
|
||||||
sub rebuild {
|
sub rebuild {
|
||||||
$SVN_URL = shift or undef;
|
$SVN_URL = shift or undef;
|
||||||
my $repo_uuid;
|
|
||||||
my $newest_rev = 0;
|
my $newest_rev = 0;
|
||||||
if ($_upgrade) {
|
if ($_upgrade) {
|
||||||
sys('git-update-ref',"refs/remotes/$GIT_SVN","$GIT_SVN-HEAD");
|
sys('git-update-ref',"refs/remotes/$GIT_SVN","$GIT_SVN-HEAD");
|
||||||
|
@ -141,7 +153,7 @@ sub rebuild {
|
||||||
|
|
||||||
# if we merged or otherwise started elsewhere, this is
|
# if we merged or otherwise started elsewhere, this is
|
||||||
# how we break out of it
|
# how we break out of it
|
||||||
next if (defined $repo_uuid && ($uuid ne $repo_uuid));
|
next if (defined $SVN_UUID && ($uuid ne $SVN_UUID));
|
||||||
next if (defined $SVN_URL && ($url ne $SVN_URL));
|
next if (defined $SVN_URL && ($url ne $SVN_URL));
|
||||||
|
|
||||||
print "r$rev = $c\n";
|
print "r$rev = $c\n";
|
||||||
|
@ -150,7 +162,7 @@ sub rebuild {
|
||||||
croak "SVN repository location required: $url\n";
|
croak "SVN repository location required: $url\n";
|
||||||
}
|
}
|
||||||
$SVN_URL ||= $url;
|
$SVN_URL ||= $url;
|
||||||
$repo_uuid ||= setup_git_svn();
|
$SVN_UUID ||= setup_git_svn();
|
||||||
$latest = $rev;
|
$latest = $rev;
|
||||||
}
|
}
|
||||||
assert_revision_eq_or_unknown($rev, $c);
|
assert_revision_eq_or_unknown($rev, $c);
|
||||||
|
@ -215,9 +227,6 @@ sub fetch {
|
||||||
sys(@svn_co, $SVN_URL, $SVN_WC);
|
sys(@svn_co, $SVN_URL, $SVN_WC);
|
||||||
chdir $SVN_WC or croak $!;
|
chdir $SVN_WC or croak $!;
|
||||||
$last_commit = git_commit($base, @parents);
|
$last_commit = git_commit($base, @parents);
|
||||||
unless (-f "$GIT_DIR/refs/heads/master") {
|
|
||||||
sys(qw(git-update-ref refs/heads/master),$last_commit);
|
|
||||||
}
|
|
||||||
assert_svn_wc_clean($base->{revision}, $last_commit);
|
assert_svn_wc_clean($base->{revision}, $last_commit);
|
||||||
} else {
|
} else {
|
||||||
chdir $SVN_WC or croak $!;
|
chdir $SVN_WC or croak $!;
|
||||||
|
@ -233,6 +242,9 @@ sub fetch {
|
||||||
$last_commit = git_commit($log_msg, $last_commit, @parents);
|
$last_commit = git_commit($log_msg, $last_commit, @parents);
|
||||||
}
|
}
|
||||||
assert_svn_wc_clean($last_rev, $last_commit);
|
assert_svn_wc_clean($last_rev, $last_commit);
|
||||||
|
unless (-e "$GIT_DIR/refs/heads/master") {
|
||||||
|
sys(qw(git-update-ref refs/heads/master),$last_commit);
|
||||||
|
}
|
||||||
return pop @$svn_log;
|
return pop @$svn_log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +255,7 @@ sub commit {
|
||||||
print "Reading from stdin...\n";
|
print "Reading from stdin...\n";
|
||||||
@commits = ();
|
@commits = ();
|
||||||
while (<STDIN>) {
|
while (<STDIN>) {
|
||||||
if (/\b([a-f\d]{6,40})\b/) {
|
if (/\b($sha1_short)\b/o) {
|
||||||
unshift @commits, $1;
|
unshift @commits, $1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,7 +277,6 @@ sub commit {
|
||||||
chdir $SVN_WC or croak $!;
|
chdir $SVN_WC or croak $!;
|
||||||
my $svn_current_rev = svn_info('.')->{'Last Changed Rev'};
|
my $svn_current_rev = svn_info('.')->{'Last Changed Rev'};
|
||||||
foreach my $c (@revs) {
|
foreach my $c (@revs) {
|
||||||
print "Committing $c\n";
|
|
||||||
my $mods = svn_checkout_tree($svn_current_rev, $c);
|
my $mods = svn_checkout_tree($svn_current_rev, $c);
|
||||||
if (scalar @$mods == 0) {
|
if (scalar @$mods == 0) {
|
||||||
print "Skipping, no changes detected\n";
|
print "Skipping, no changes detected\n";
|
||||||
|
@ -312,24 +323,31 @@ sub setup_git_svn {
|
||||||
mkpath(["$GIT_DIR/$GIT_SVN/info"]);
|
mkpath(["$GIT_DIR/$GIT_SVN/info"]);
|
||||||
mkpath([$REV_DIR]);
|
mkpath([$REV_DIR]);
|
||||||
s_to_file($SVN_URL,"$GIT_DIR/$GIT_SVN/info/url");
|
s_to_file($SVN_URL,"$GIT_DIR/$GIT_SVN/info/url");
|
||||||
my $uuid = svn_info($SVN_URL)->{'Repository UUID'} or
|
$SVN_UUID = svn_info($SVN_URL)->{'Repository UUID'} or
|
||||||
croak "Repository UUID unreadable\n";
|
croak "Repository UUID unreadable\n";
|
||||||
s_to_file($uuid,"$GIT_DIR/$GIT_SVN/info/uuid");
|
s_to_file($SVN_UUID,"$GIT_DIR/$GIT_SVN/info/uuid");
|
||||||
|
|
||||||
open my $fd, '>>', "$GIT_DIR/$GIT_SVN/info/exclude" or croak $!;
|
open my $fd, '>>', "$GIT_DIR/$GIT_SVN/info/exclude" or croak $!;
|
||||||
print $fd '.svn',"\n";
|
print $fd '.svn',"\n";
|
||||||
close $fd or croak $!;
|
close $fd or croak $!;
|
||||||
return $uuid;
|
return $SVN_UUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub assert_svn_wc_clean {
|
sub assert_svn_wc_clean {
|
||||||
my ($svn_rev, $treeish) = @_;
|
my ($svn_rev, $treeish) = @_;
|
||||||
croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/);
|
croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/);
|
||||||
croak "$treeish is not a sha1!\n" unless ($treeish =~ /^$sha1$/o);
|
croak "$treeish is not a sha1!\n" unless ($treeish =~ /^$sha1$/o);
|
||||||
my $svn_info = svn_info('.');
|
my $lcr = svn_info('.')->{'Last Changed Rev'};
|
||||||
if ($svn_rev != $svn_info->{'Last Changed Rev'}) {
|
if ($svn_rev != $lcr) {
|
||||||
croak "Expected r$svn_rev, got r",
|
print STDERR "Checking for copy-tree ... ";
|
||||||
$svn_info->{'Last Changed Rev'},"\n";
|
# use
|
||||||
|
my @diff = grep(/^Index: /,(safe_qx(qw(svn diff),
|
||||||
|
"-r$lcr:$svn_rev")));
|
||||||
|
if (@diff) {
|
||||||
|
croak "Nope! Expected r$svn_rev, got r$lcr\n";
|
||||||
|
} else {
|
||||||
|
print STDERR "OK!\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
my @status = grep(!/^Performing status on external/,(`svn status`));
|
my @status = grep(!/^Performing status on external/,(`svn status`));
|
||||||
@status = grep(!/^\s*$/,@status);
|
@status = grep(!/^\s*$/,@status);
|
||||||
|
@ -512,7 +530,7 @@ sub svn_checkout_tree {
|
||||||
my ($svn_rev, $treeish) = @_;
|
my ($svn_rev, $treeish) = @_;
|
||||||
my $from = file_to_s("$REV_DIR/$svn_rev");
|
my $from = file_to_s("$REV_DIR/$svn_rev");
|
||||||
assert_svn_wc_clean($svn_rev,$from);
|
assert_svn_wc_clean($svn_rev,$from);
|
||||||
print "diff-tree '$from' '$treeish'\n";
|
print "diff-tree $from $treeish\n";
|
||||||
my $pid = open my $diff_fh, '-|';
|
my $pid = open my $diff_fh, '-|';
|
||||||
defined $pid or croak $!;
|
defined $pid or croak $!;
|
||||||
if ($pid == 0) {
|
if ($pid == 0) {
|
||||||
|
@ -523,7 +541,7 @@ sub svn_checkout_tree {
|
||||||
}
|
}
|
||||||
my $mods = parse_diff_tree($diff_fh);
|
my $mods = parse_diff_tree($diff_fh);
|
||||||
unless (@$mods) {
|
unless (@$mods) {
|
||||||
# git can do empty commits, SVN doesn't allow it...
|
# git can do empty commits, but SVN doesn't allow it...
|
||||||
return $mods;
|
return $mods;
|
||||||
}
|
}
|
||||||
my ($rm, $add) = precommit_check($mods);
|
my ($rm, $add) = precommit_check($mods);
|
||||||
|
@ -624,8 +642,10 @@ sub svn_commit_tree {
|
||||||
while (<$msg_fh>) {
|
while (<$msg_fh>) {
|
||||||
if (!$in_msg) {
|
if (!$in_msg) {
|
||||||
$in_msg = 1 if (/^\s*$/);
|
$in_msg = 1 if (/^\s*$/);
|
||||||
|
} elsif (/^git-svn-id: /) {
|
||||||
|
# skip this, we regenerate the correct one
|
||||||
|
# on re-fetch anyways
|
||||||
} else {
|
} else {
|
||||||
$log_msg{msg} .= $_;
|
|
||||||
print $msg $_ or croak $!;
|
print $msg $_ or croak $!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -637,6 +657,15 @@ sub svn_commit_tree {
|
||||||
my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
|
my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
|
||||||
system($editor, $commit_msg);
|
system($editor, $commit_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# file_to_s removes all trailing newlines, so just use chomp() here:
|
||||||
|
open $msg, '<', $commit_msg or croak $!;
|
||||||
|
{ local $/; chomp($log_msg{msg} = <$msg>); }
|
||||||
|
close $msg or croak $!;
|
||||||
|
|
||||||
|
my ($oneline) = ($log_msg{msg} =~ /([^\n\r]+)/);
|
||||||
|
print "Committing $commit: $oneline\n";
|
||||||
|
|
||||||
my @ci_output = safe_qx(qw(svn commit -F),$commit_msg);
|
my @ci_output = safe_qx(qw(svn commit -F),$commit_msg);
|
||||||
my ($committed) = grep(/^Committed revision \d+\./,@ci_output);
|
my ($committed) = grep(/^Committed revision \d+\./,@ci_output);
|
||||||
unlink $commit_msg;
|
unlink $commit_msg;
|
||||||
|
@ -728,6 +757,10 @@ sub svn_log_raw {
|
||||||
author => $author,
|
author => $author,
|
||||||
lines => $lines,
|
lines => $lines,
|
||||||
msg => '' );
|
msg => '' );
|
||||||
|
if (defined $_authors && ! defined $users{$author}) {
|
||||||
|
die "Author: $author not defined in ",
|
||||||
|
"$_authors file\n";
|
||||||
|
}
|
||||||
push @svn_log, \%log_msg;
|
push @svn_log, \%log_msg;
|
||||||
$state = 'msg_start';
|
$state = 'msg_start';
|
||||||
next;
|
next;
|
||||||
|
@ -827,9 +860,9 @@ sub git_commit {
|
||||||
my ($log_msg, @parents) = @_;
|
my ($log_msg, @parents) = @_;
|
||||||
assert_revision_unknown($log_msg->{revision});
|
assert_revision_unknown($log_msg->{revision});
|
||||||
my $out_fh = IO::File->new_tmpfile or croak $!;
|
my $out_fh = IO::File->new_tmpfile or croak $!;
|
||||||
my $info = svn_info('.');
|
$SVN_UUID ||= svn_info('.')->{'Repository UUID'};
|
||||||
my $uuid = $info->{'Repository UUID'};
|
|
||||||
defined $uuid or croak "Unable to get Repository UUID\n";
|
map_tree_joins() if (@_branch_from && !%tree_map);
|
||||||
|
|
||||||
# commit parents can be conditionally bound to a particular
|
# commit parents can be conditionally bound to a particular
|
||||||
# svn revision via: "svn_revno=commit_sha1", filter them out here:
|
# svn revision via: "svn_revno=commit_sha1", filter them out here:
|
||||||
|
@ -852,19 +885,26 @@ sub git_commit {
|
||||||
git_addremove();
|
git_addremove();
|
||||||
chomp(my $tree = `git-write-tree`);
|
chomp(my $tree = `git-write-tree`);
|
||||||
croak if $?;
|
croak if $?;
|
||||||
|
if (exists $tree_map{$tree}) {
|
||||||
|
my %seen_parent = map { $_ => 1 } @exec_parents;
|
||||||
|
foreach (@{$tree_map{$tree}}) {
|
||||||
|
# MAXPARENT is defined to 16 in commit-tree.c:
|
||||||
|
if ($seen_parent{$_} || @exec_parents > 16) {
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
push @exec_parents, $_;
|
||||||
|
$seen_parent{$_} = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
my $msg_fh = IO::File->new_tmpfile or croak $!;
|
my $msg_fh = IO::File->new_tmpfile or croak $!;
|
||||||
print $msg_fh $log_msg->{msg}, "\ngit-svn-id: ",
|
print $msg_fh $log_msg->{msg}, "\ngit-svn-id: ",
|
||||||
"$SVN_URL\@$log_msg->{revision}",
|
"$SVN_URL\@$log_msg->{revision}",
|
||||||
" $uuid\n" or croak $!;
|
" $SVN_UUID\n" or croak $!;
|
||||||
$msg_fh->flush == 0 or croak $!;
|
$msg_fh->flush == 0 or croak $!;
|
||||||
seek $msg_fh, 0, 0 or croak $!;
|
seek $msg_fh, 0, 0 or croak $!;
|
||||||
|
|
||||||
$ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} =
|
set_commit_env($log_msg);
|
||||||
$log_msg->{author};
|
|
||||||
$ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} =
|
|
||||||
$log_msg->{author}."\@$uuid";
|
|
||||||
$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} =
|
|
||||||
$log_msg->{date};
|
|
||||||
my @exec = ('git-commit-tree',$tree);
|
my @exec = ('git-commit-tree',$tree);
|
||||||
push @exec, '-p', $_ foreach @exec_parents;
|
push @exec, '-p', $_ foreach @exec_parents;
|
||||||
open STDIN, '<&', $msg_fh or croak $!;
|
open STDIN, '<&', $msg_fh or croak $!;
|
||||||
|
@ -890,6 +930,16 @@ sub git_commit {
|
||||||
return $commit;
|
return $commit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_commit_env {
|
||||||
|
my ($log_msg) = @_;
|
||||||
|
my $author = $log_msg->{author};
|
||||||
|
my ($name,$email) = defined $users{$author} ? @{$users{$author}}
|
||||||
|
: ($author,"$author\@$SVN_UUID");
|
||||||
|
$ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name;
|
||||||
|
$ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = $email;
|
||||||
|
$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_msg->{date};
|
||||||
|
}
|
||||||
|
|
||||||
sub apply_mod_line_blob {
|
sub apply_mod_line_blob {
|
||||||
my $m = shift;
|
my $m = shift;
|
||||||
if ($m->{mode_b} =~ /^120/) {
|
if ($m->{mode_b} =~ /^120/) {
|
||||||
|
@ -975,6 +1025,41 @@ sub check_upgrade_needed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# fills %tree_map with a reverse mapping of trees to commits. Useful
|
||||||
|
# for finding parents to commit on.
|
||||||
|
sub map_tree_joins {
|
||||||
|
foreach my $br (@_branch_from) {
|
||||||
|
my $pid = open my $pipe, '-|';
|
||||||
|
defined $pid or croak $!;
|
||||||
|
if ($pid == 0) {
|
||||||
|
exec(qw(git-rev-list --pretty=raw), $br) or croak $?;
|
||||||
|
}
|
||||||
|
while (<$pipe>) {
|
||||||
|
if (/^commit ($sha1)$/o) {
|
||||||
|
my $commit = $1;
|
||||||
|
my ($tree) = (<$pipe> =~ /^tree ($sha1)$/o);
|
||||||
|
unless (defined $tree) {
|
||||||
|
die "Failed to parse commit $commit\n";
|
||||||
|
}
|
||||||
|
push @{$tree_map{$tree}}, $commit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close $pipe or croak $?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# '<svn username> = real-name <email address>' mapping based on git-svnimport:
|
||||||
|
sub load_authors {
|
||||||
|
open my $authors, '<', $_authors or die "Can't open $_authors $!\n";
|
||||||
|
while (<$authors>) {
|
||||||
|
chomp;
|
||||||
|
next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/;
|
||||||
|
my ($user, $name, $email) = ($1, $2, $3);
|
||||||
|
$users{$user} = [$name, $email];
|
||||||
|
}
|
||||||
|
close $authors or croak $!;
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
||||||
Data structures:
|
Data structures:
|
||||||
|
@ -999,7 +1084,7 @@ sub check_upgrade_needed {
|
||||||
mode_a => first column of diff-index output, no leading ':',
|
mode_a => first column of diff-index output, no leading ':',
|
||||||
mode_b => second column of diff-index output,
|
mode_b => second column of diff-index output,
|
||||||
sha1_b => sha1sum of the final blob,
|
sha1_b => sha1sum of the final blob,
|
||||||
chg => change type [MCRAD],
|
chg => change type [MCRADT],
|
||||||
file_a => original file name of a file (iff chg is 'C' or 'R')
|
file_a => original file name of a file (iff chg is 'C' or 'R')
|
||||||
file_b => new/current file name of a file (any chg)
|
file_b => new/current file name of a file (any chg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ For importing svn, git-svnimport is potentially more powerful when
|
||||||
operating on repositories organized under the recommended
|
operating on repositories organized under the recommended
|
||||||
trunk/branch/tags structure, and should be faster, too.
|
trunk/branch/tags structure, and should be faster, too.
|
||||||
|
|
||||||
git-svn completely ignores the very limited view of branching that
|
git-svn mostly ignores the very limited view of branching that
|
||||||
Subversion has. This allows git-svn to be much easier to use,
|
Subversion has. This allows git-svn to be much easier to use,
|
||||||
especially on repositories that are not organized in a manner that
|
especially on repositories that are not organized in a manner that
|
||||||
git-svnimport is designed for.
|
git-svnimport is designed for.
|
||||||
|
@ -116,8 +116,38 @@ OPTIONS
|
||||||
They are both passed directly to git-diff-tree see
|
They are both passed directly to git-diff-tree see
|
||||||
git-diff-tree(1) for more information.
|
git-diff-tree(1) for more information.
|
||||||
|
|
||||||
|
ADVANCED OPTIONS
|
||||||
|
----------------
|
||||||
|
-b<refname>::
|
||||||
|
--branch <refname>::
|
||||||
|
Used with 'fetch' or 'commit'.
|
||||||
|
|
||||||
|
This can be used to join arbitrary git branches to remotes/git-svn
|
||||||
|
on new commits where the tree object is equivalent.
|
||||||
|
|
||||||
|
When used with different GIT_SVN_ID values, tags and branches in
|
||||||
|
SVN can be tracked this way, as can some merges where the heads
|
||||||
|
end up having completely equivalent content. This can even be
|
||||||
|
used to track branches across multiple SVN _repositories_.
|
||||||
|
|
||||||
|
This option may be specified multiple times, once for each
|
||||||
|
branch.
|
||||||
|
|
||||||
|
-i<GIT_SVN_ID>::
|
||||||
|
--id <GIT_SVN_ID>::
|
||||||
|
This sets GIT_SVN_ID (instead of using the environment). See
|
||||||
|
the section on "Tracking Multiple Repositories or Branches" for
|
||||||
|
more information on using GIT_SVN_ID.
|
||||||
|
|
||||||
COMPATIBILITY OPTIONS
|
COMPATIBILITY OPTIONS
|
||||||
---------------------
|
---------------------
|
||||||
|
--upgrade::
|
||||||
|
Only used with the 'rebuild' command.
|
||||||
|
|
||||||
|
Run this if you used an old version of git-svn that used
|
||||||
|
'git-svn-HEAD' instead of 'remotes/git-svn' as the branch
|
||||||
|
for tracking the remote.
|
||||||
|
|
||||||
--no-ignore-externals::
|
--no-ignore-externals::
|
||||||
Only used with the 'fetch' and 'rebuild' command.
|
Only used with the 'fetch' and 'rebuild' command.
|
||||||
|
|
||||||
|
@ -162,7 +192,7 @@ Tracking and contributing to an Subversion managed-project:
|
||||||
git-svn commit remotes/git-svn..my-branch
|
git-svn commit remotes/git-svn..my-branch
|
||||||
# Something is committed to SVN, pull the latest into your branch::
|
# Something is committed to SVN, pull the latest into your branch::
|
||||||
git-svn fetch && git pull . remotes/git-svn
|
git-svn fetch && git pull . remotes/git-svn
|
||||||
# Append svn:ignore settings to the default git exclude file:
|
# Append svn:ignore settings to the default git exclude file::
|
||||||
git-svn show-ignore >> .git/info/exclude
|
git-svn show-ignore >> .git/info/exclude
|
||||||
|
|
||||||
DESIGN PHILOSOPHY
|
DESIGN PHILOSOPHY
|
||||||
|
|
|
@ -798,7 +798,7 @@ class GitView:
|
||||||
button.set_relief(gtk.RELIEF_NONE)
|
button.set_relief(gtk.RELIEF_NONE)
|
||||||
button.set_sensitive(True)
|
button.set_sensitive(True)
|
||||||
button.connect("clicked", self._show_clicked_cb,
|
button.connect("clicked", self._show_clicked_cb,
|
||||||
child_id, commit.commit_sha1)
|
child_id, commit.commit_sha1, self.encoding)
|
||||||
hbox.pack_start(button, expand=False, fill=True)
|
hbox.pack_start(button, expand=False, fill=True)
|
||||||
button.show()
|
button.show()
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ ()
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s\t(%10s\t%10s\t%d)%s\n", $rev, $committer,
|
printf("%s\t(%10s\t%10s\t%d)%s\n", $rev, $committer,
|
||||||
format_date($date), $i++, $output);
|
format_date($date), ++$i, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub init_claim {
|
sub init_claim {
|
||||||
|
|
|
@ -48,6 +48,12 @@ If you are sure you want to delete it, run 'git branch -D $branch_name'."
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ls_remote_branches () {
|
||||||
|
git-rev-parse --symbolic --all |
|
||||||
|
sed -ne 's|^refs/\(remotes/\)|\1|p' |
|
||||||
|
sort
|
||||||
|
}
|
||||||
|
|
||||||
force=
|
force=
|
||||||
while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac
|
while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac
|
||||||
do
|
do
|
||||||
|
@ -56,6 +62,10 @@ do
|
||||||
delete_branch "$@"
|
delete_branch "$@"
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
|
-r)
|
||||||
|
ls_remote_branches
|
||||||
|
exit
|
||||||
|
;;
|
||||||
-f)
|
-f)
|
||||||
force="$1"
|
force="$1"
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -62,9 +62,17 @@ ()
|
||||||
$dstDir = "";
|
$dstDir = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $subdir_prefix = `git rev-parse --show-prefix`;
|
||||||
|
chomp($subdir_prefix);
|
||||||
|
|
||||||
|
# run in git base directory, so that git-ls-files lists all revisioned files
|
||||||
|
chdir "$GIT_DIR/..";
|
||||||
|
|
||||||
# normalize paths, needed to compare against versioned files and update-index
|
# normalize paths, needed to compare against versioned files and update-index
|
||||||
# also, this is nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c"
|
# also, this is nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c"
|
||||||
for (@srcArgs, @dstArgs) {
|
for (@srcArgs, @dstArgs) {
|
||||||
|
# prepend git prefix as we run from base directory
|
||||||
|
$_ = $subdir_prefix.$_;
|
||||||
s|^\./||;
|
s|^\./||;
|
||||||
s|/\./|/| while (m|/\./|);
|
s|/\./|/| while (m|/\./|);
|
||||||
s|//+|/|g;
|
s|//+|/|g;
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
"compose" => \$compose,
|
"compose" => \$compose,
|
||||||
"quiet" => \$quiet,
|
"quiet" => \$quiet,
|
||||||
"suppress-from" => \$suppress_from,
|
"suppress-from" => \$suppress_from,
|
||||||
"no-signed-off-cc" => \$no_signed_off_cc,
|
"no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
|
||||||
);
|
);
|
||||||
|
|
||||||
# Now, let's fill any that aren't set in with defaults:
|
# Now, let's fill any that aren't set in with defaults:
|
||||||
|
|
|
@ -727,24 +727,16 @@ int main(int ac, char **av)
|
||||||
while (seen) {
|
while (seen) {
|
||||||
struct commit *commit = pop_one_commit(&seen);
|
struct commit *commit = pop_one_commit(&seen);
|
||||||
int this_flag = commit->object.flags;
|
int this_flag = commit->object.flags;
|
||||||
|
int is_merge_point = ((this_flag & all_revs) == all_revs);
|
||||||
|
|
||||||
shown_merge_point |= ((this_flag & all_revs) == all_revs);
|
shown_merge_point |= is_merge_point;
|
||||||
|
|
||||||
if (1 < num_rev) {
|
if (1 < num_rev) {
|
||||||
int is_merge = !!(commit->parents && commit->parents->next);
|
int is_merge = !!(commit->parents && commit->parents->next);
|
||||||
if (topics) {
|
if (topics &&
|
||||||
int interesting = 0;
|
!is_merge_point &&
|
||||||
for (i = 1; i < num_rev; i++) {
|
(this_flag & (1u << REV_SHIFT)))
|
||||||
if ((this_flag &
|
|
||||||
(1u << (i + REV_SHIFT)))) {
|
|
||||||
interesting = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!interesting)
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < num_rev; i++) {
|
for (i = 0; i < num_rev; i++) {
|
||||||
int mark;
|
int mark;
|
||||||
|
|
|
@ -8,11 +8,20 @@ test_description='Test of the various options to git-rm.'
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
# Setup some files to be removed, some with funny characters
|
# Setup some files to be removed, some with funny characters
|
||||||
touch -- foo bar baz 'space embedded' 'tab embedded' 'newline
|
touch -- foo bar baz 'space embedded' -q
|
||||||
embedded' -q
|
git-add -- foo bar baz 'space embedded' -q
|
||||||
git-add -- foo bar baz 'space embedded' 'tab embedded' 'newline
|
git-commit -m "add normal files"
|
||||||
embedded' -q
|
test_tabs=y
|
||||||
git-commit -m "add files"
|
if touch -- 'tab embedded' 'newline
|
||||||
|
embedded'
|
||||||
|
then
|
||||||
|
git-add -- 'tab embedded' 'newline
|
||||||
|
embedded'
|
||||||
|
git-commit -m "add files with tabs and newlines"
|
||||||
|
else
|
||||||
|
say 'Your filesystem does not allow tabs in filenames.'
|
||||||
|
test_tabs=n
|
||||||
|
fi
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'Pre-check that foo exists and is in index before git-rm foo' \
|
'Pre-check that foo exists and is in index before git-rm foo' \
|
||||||
|
@ -42,16 +51,18 @@ test_expect_success \
|
||||||
'Test that "git-rm -- -q" succeeds (remove a file that looks like an option)' \
|
'Test that "git-rm -- -q" succeeds (remove a file that looks like an option)' \
|
||||||
'git-rm -- -q'
|
'git-rm -- -q'
|
||||||
|
|
||||||
test_expect_success \
|
test "$test_tabs" = y && test_expect_success \
|
||||||
"Test that \"git-rm -f\" succeeds with embedded space, tab, or newline characters." \
|
"Test that \"git-rm -f\" succeeds with embedded space, tab, or newline characters." \
|
||||||
"git-rm -f 'space embedded' 'tab embedded' 'newline
|
"git-rm -f 'space embedded' 'tab embedded' 'newline
|
||||||
embedded'"
|
embedded'"
|
||||||
|
|
||||||
|
if test "$test_tabs" = y; then
|
||||||
chmod u-w .
|
chmod u-w .
|
||||||
test_expect_failure \
|
test_expect_failure \
|
||||||
'Test that "git-rm -f" fails if its rm fails' \
|
'Test that "git-rm -f" fails if its rm fails' \
|
||||||
'git-rm -f baz'
|
'git-rm -f baz'
|
||||||
chmod u+w .
|
chmod u+w .
|
||||||
|
fi
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
|
'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
|
||||||
|
|
|
@ -11,17 +11,31 @@ test_expect_success \
|
||||||
git-commit -m add -a'
|
git-commit -m add -a'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'moving the file' \
|
'moving the file out of subdirectory' \
|
||||||
'cd path0 && git-mv COPYING ../path1/COPYING'
|
'cd path0 && git-mv COPYING ../path1/COPYING'
|
||||||
|
|
||||||
# in path0 currently
|
# in path0 currently
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'commiting the change' \
|
'commiting the change' \
|
||||||
'cd .. && git-commit -m move -a'
|
'cd .. && git-commit -m move-out -a'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'checking the commit' \
|
'checking the commit' \
|
||||||
'git-diff-tree -r -M --name-status HEAD^ HEAD | \
|
'git-diff-tree -r -M --name-status HEAD^ HEAD | \
|
||||||
grep -E "^R100.+path0/COPYING.+path1/COPYING"'
|
grep -E "^R100.+path0/COPYING.+path1/COPYING"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'moving the file back into subdirectory' \
|
||||||
|
'cd path0 && git-mv ../path1/COPYING COPYING'
|
||||||
|
|
||||||
|
# in path0 currently
|
||||||
|
test_expect_success \
|
||||||
|
'commiting the change' \
|
||||||
|
'cd .. && git-commit -m move-in -a'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'checking the commit' \
|
||||||
|
'git-diff-tree -r -M --name-status HEAD^ HEAD | \
|
||||||
|
grep -E "^R100.+path1/COPYING.+path0/COPYING"'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -50,7 +50,8 @@ test_expect_success \
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'merge-setup part 2' \
|
'merge-setup part 2' \
|
||||||
'git checkout -b branch2 master &&
|
'git checkout -b branch2 master &&
|
||||||
sed -i -e "s/2A quick brown/4A quick brown lazy dog/" file &&
|
sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
|
||||||
|
mv file.new file &&
|
||||||
GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"'
|
GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
|
|
|
@ -577,9 +577,11 @@ int main(int argc, const char **argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!strcmp(path, "--index-info")) {
|
if (!strcmp(path, "--index-info")) {
|
||||||
|
if (i != argc - 1)
|
||||||
|
die("--index-info must be at the end");
|
||||||
allow_add = allow_replace = allow_remove = 1;
|
allow_add = allow_replace = allow_remove = 1;
|
||||||
read_index_info(line_termination);
|
read_index_info(line_termination);
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
if (!strcmp(path, "--ignore-missing")) {
|
if (!strcmp(path, "--ignore-missing")) {
|
||||||
not_new = 1;
|
not_new = 1;
|
||||||
|
|
Loading…
Reference in a new issue