Merge branch 'lt/apply' into next
* lt/apply: apply --whitespace fixes and enhancements. The war on trailing whitespace svnimport: Convert the svn:ignore property svnimport: Convert executable flag svnimport: Mention -r in usage summary Make git diff-generation use a simpler spawn-like interface
This commit is contained in:
commit
0a26233859
@ -10,10 +10,10 @@ git-svnimport - Import a SVN repository into git
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ]
|
||||
[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
|
||||
[ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
|
||||
[ -s start_chg ] [ -m ] [ -M regex ]
|
||||
<SVN_repository_URL> [ <path> ]
|
||||
[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
|
||||
[ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
|
||||
[ -s start_chg ] [ -m ] [ -r ] [ -M regex ]
|
||||
[ -I <ignorefile_name> ] <SVN_repository_URL> [ <path> ]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@ -65,6 +65,12 @@ When importing incrementally, you might need to edit the .git/svn2git file.
|
||||
Prepend 'rX: ' to commit messages, where X is the imported
|
||||
subversion revision.
|
||||
|
||||
-I <ignorefile_name>::
|
||||
Import the svn:ignore directory property to files with this
|
||||
name in each directory. (The Subversion and GIT ignore
|
||||
syntaxes are similar enough that using the Subversion patterns
|
||||
directly with "-I .gitignore" will almost always just work.)
|
||||
|
||||
-m::
|
||||
Attempt to detect merges based on the commit message. This option
|
||||
will enable default regexes that try to capture the name source
|
||||
|
80
apply.c
80
apply.c
@ -34,6 +34,15 @@ static int line_termination = '\n';
|
||||
static const char apply_usage[] =
|
||||
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] <patch>...";
|
||||
|
||||
static enum whitespace_eol {
|
||||
nowarn,
|
||||
warn_on_whitespace,
|
||||
error_on_whitespace,
|
||||
strip_and_apply,
|
||||
} new_whitespace = nowarn;
|
||||
static int whitespace_error = 0;
|
||||
static const char *patch_input_file = NULL;
|
||||
|
||||
/*
|
||||
* For "diff-stat" like behaviour, we keep track of the biggest change
|
||||
* we've seen, and the longest filename. That allows us to do simple
|
||||
@ -815,6 +824,20 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
|
||||
oldlines--;
|
||||
break;
|
||||
case '+':
|
||||
/*
|
||||
* We know len is at least two, since we have a '+' and
|
||||
* we checked that the last character was a '\n' above.
|
||||
* That is, an addition of an empty line would check
|
||||
* the '+' here. Sneaky...
|
||||
*/
|
||||
if ((new_whitespace != nowarn) &&
|
||||
isspace(line[len-2])) {
|
||||
fprintf(stderr, "Added whitespace\n");
|
||||
fprintf(stderr, "%s:%d:%.*s\n",
|
||||
patch_input_file,
|
||||
linenr, len-2, line+1);
|
||||
whitespace_error = 1;
|
||||
}
|
||||
added++;
|
||||
newlines--;
|
||||
break;
|
||||
@ -1092,6 +1115,27 @@ struct buffer_desc {
|
||||
unsigned long alloc;
|
||||
};
|
||||
|
||||
static int apply_line(char *output, const char *patch, int plen)
|
||||
{
|
||||
/* plen is number of bytes to be copied from patch,
|
||||
* starting at patch+1 (patch[0] is '+'). Typically
|
||||
* patch[plen] is '\n'.
|
||||
*/
|
||||
int add_nl_to_tail = 0;
|
||||
if ((new_whitespace == strip_and_apply) &&
|
||||
1 < plen && isspace(patch[plen-1])) {
|
||||
if (patch[plen] == '\n')
|
||||
add_nl_to_tail = 1;
|
||||
plen--;
|
||||
while (0 < plen && isspace(patch[plen]))
|
||||
plen--;
|
||||
}
|
||||
memcpy(output, patch + 1, plen);
|
||||
if (add_nl_to_tail)
|
||||
output[plen++] = '\n';
|
||||
return plen;
|
||||
}
|
||||
|
||||
static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
|
||||
{
|
||||
char *buf = desc->buffer;
|
||||
@ -1127,10 +1171,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
|
||||
break;
|
||||
/* Fall-through for ' ' */
|
||||
case '+':
|
||||
if (*patch != '+' || !no_add) {
|
||||
memcpy(new + newsize, patch + 1, plen);
|
||||
newsize += plen;
|
||||
}
|
||||
if (*patch != '+' || !no_add)
|
||||
newsize += apply_line(new + newsize, patch,
|
||||
plen);
|
||||
break;
|
||||
case '@': case '\\':
|
||||
/* Ignore it, we already handled it */
|
||||
@ -1699,7 +1742,7 @@ static int use_patch(struct patch *p)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int apply_patch(int fd)
|
||||
static int apply_patch(int fd, const char *filename)
|
||||
{
|
||||
int newfd;
|
||||
unsigned long offset, size;
|
||||
@ -1707,6 +1750,7 @@ static int apply_patch(int fd)
|
||||
struct patch *list = NULL, **listp = &list;
|
||||
int skipped_patch = 0;
|
||||
|
||||
patch_input_file = filename;
|
||||
if (!buffer)
|
||||
return -1;
|
||||
offset = 0;
|
||||
@ -1733,6 +1777,9 @@ static int apply_patch(int fd)
|
||||
}
|
||||
|
||||
newfd = -1;
|
||||
if (whitespace_error && (new_whitespace == error_on_whitespace))
|
||||
apply = 0;
|
||||
|
||||
write_index = check_index && apply;
|
||||
if (write_index)
|
||||
newfd = hold_index_file_for_update(&cache_file, get_index_file());
|
||||
@ -1779,7 +1826,7 @@ int main(int argc, char **argv)
|
||||
int fd;
|
||||
|
||||
if (!strcmp(arg, "-")) {
|
||||
apply_patch(0);
|
||||
apply_patch(0, "<stdin>");
|
||||
read_stdin = 0;
|
||||
continue;
|
||||
}
|
||||
@ -1839,6 +1886,21 @@ int main(int argc, char **argv)
|
||||
line_termination = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(arg, "--whitespace=", 13)) {
|
||||
if (!strcmp(arg+13, "warn")) {
|
||||
new_whitespace = warn_on_whitespace;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg+13, "error")) {
|
||||
new_whitespace = error_on_whitespace;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg+13, "strip")) {
|
||||
new_whitespace = strip_and_apply;
|
||||
continue;
|
||||
}
|
||||
die("unrecognixed whitespace option '%s'", arg+13);
|
||||
}
|
||||
|
||||
if (check_index && prefix_length < 0) {
|
||||
prefix = setup_git_directory();
|
||||
@ -1852,10 +1914,12 @@ int main(int argc, char **argv)
|
||||
if (fd < 0)
|
||||
usage(apply_usage);
|
||||
read_stdin = 0;
|
||||
apply_patch(fd);
|
||||
apply_patch(fd, arg);
|
||||
close(fd);
|
||||
}
|
||||
if (read_stdin)
|
||||
apply_patch(0);
|
||||
apply_patch(0, "<stdin>");
|
||||
if (whitespace_error && new_whitespace == error_on_whitespace)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
138
diff.c
138
diff.c
@ -178,11 +178,12 @@ static void emit_rewrite_diff(const char *name_a,
|
||||
copy_file('+', temp[1].name);
|
||||
}
|
||||
|
||||
static void builtin_diff(const char *name_a,
|
||||
static const char *builtin_diff(const char *name_a,
|
||||
const char *name_b,
|
||||
struct diff_tempfile *temp,
|
||||
const char *xfrm_msg,
|
||||
int complete_rewrite)
|
||||
int complete_rewrite,
|
||||
const char **args)
|
||||
{
|
||||
int i, next_at, cmd_size;
|
||||
const char *const diff_cmd = "diff -L%s -L%s";
|
||||
@ -242,19 +243,24 @@ static void builtin_diff(const char *name_a,
|
||||
}
|
||||
if (xfrm_msg && xfrm_msg[0])
|
||||
puts(xfrm_msg);
|
||||
/*
|
||||
* we do not run diff between different kind
|
||||
* of objects.
|
||||
*/
|
||||
if (strncmp(temp[0].mode, temp[1].mode, 3))
|
||||
/* we do not run diff between different kind
|
||||
* of objects.
|
||||
*/
|
||||
exit(0);
|
||||
return NULL;
|
||||
if (complete_rewrite) {
|
||||
fflush(NULL);
|
||||
emit_rewrite_diff(name_a, name_b, temp);
|
||||
exit(0);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
fflush(NULL);
|
||||
execlp("/bin/sh","sh", "-c", cmd, NULL);
|
||||
|
||||
/* This is disgusting */
|
||||
*args++ = "sh";
|
||||
*args++ = "-c";
|
||||
*args++ = cmd;
|
||||
*args = NULL;
|
||||
return "/bin/sh";
|
||||
}
|
||||
|
||||
struct diff_filespec *alloc_filespec(const char *path)
|
||||
@ -559,6 +565,40 @@ static void remove_tempfile_on_signal(int signo)
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
static int spawn_prog(const char *pgm, const char **arg)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
fflush(NULL);
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
die("unable to fork");
|
||||
if (!pid) {
|
||||
execvp(pgm, (char *const*) arg);
|
||||
exit(255);
|
||||
}
|
||||
|
||||
while (waitpid(pid, &status, 0) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Earlier we did not check the exit status because
|
||||
* diff exits non-zero if files are different, and
|
||||
* we are not interested in knowing that. It was a
|
||||
* mistake which made it harder to quit a diff-*
|
||||
* session that uses the git-apply-patch-script as
|
||||
* the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
|
||||
* should also exit non-zero only when it wants to
|
||||
* abort the entire diff-* session.
|
||||
*/
|
||||
if (WIFEXITED(status) && !WEXITSTATUS(status))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* An external diff command takes:
|
||||
*
|
||||
* diff-cmd name infile1 infile1-sha1 infile1-mode \
|
||||
@ -573,9 +613,9 @@ static void run_external_diff(const char *pgm,
|
||||
const char *xfrm_msg,
|
||||
int complete_rewrite)
|
||||
{
|
||||
const char *spawn_arg[10];
|
||||
struct diff_tempfile *temp = diff_temp;
|
||||
pid_t pid;
|
||||
int status;
|
||||
int retval;
|
||||
static int atexit_asked = 0;
|
||||
const char *othername;
|
||||
|
||||
@ -592,59 +632,41 @@ static void run_external_diff(const char *pgm,
|
||||
signal(SIGINT, remove_tempfile_on_signal);
|
||||
}
|
||||
|
||||
fflush(NULL);
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
die("unable to fork");
|
||||
if (!pid) {
|
||||
if (pgm) {
|
||||
if (one && two) {
|
||||
const char *exec_arg[10];
|
||||
const char **arg = &exec_arg[0];
|
||||
*arg++ = pgm;
|
||||
*arg++ = name;
|
||||
*arg++ = temp[0].name;
|
||||
*arg++ = temp[0].hex;
|
||||
*arg++ = temp[0].mode;
|
||||
*arg++ = temp[1].name;
|
||||
*arg++ = temp[1].hex;
|
||||
*arg++ = temp[1].mode;
|
||||
if (other) {
|
||||
*arg++ = other;
|
||||
*arg++ = xfrm_msg;
|
||||
}
|
||||
*arg = NULL;
|
||||
execvp(pgm, (char *const*) exec_arg);
|
||||
if (pgm) {
|
||||
const char **arg = &spawn_arg[0];
|
||||
if (one && two) {
|
||||
*arg++ = pgm;
|
||||
*arg++ = name;
|
||||
*arg++ = temp[0].name;
|
||||
*arg++ = temp[0].hex;
|
||||
*arg++ = temp[0].mode;
|
||||
*arg++ = temp[1].name;
|
||||
*arg++ = temp[1].hex;
|
||||
*arg++ = temp[1].mode;
|
||||
if (other) {
|
||||
*arg++ = other;
|
||||
*arg++ = xfrm_msg;
|
||||
}
|
||||
else
|
||||
execlp(pgm, pgm, name, NULL);
|
||||
} else {
|
||||
*arg++ = pgm;
|
||||
*arg++ = name;
|
||||
}
|
||||
/*
|
||||
* otherwise we use the built-in one.
|
||||
*/
|
||||
if (one && two)
|
||||
builtin_diff(name, othername, temp, xfrm_msg,
|
||||
complete_rewrite);
|
||||
else
|
||||
*arg = NULL;
|
||||
} else {
|
||||
if (one && two) {
|
||||
pgm = builtin_diff(name, othername, temp, xfrm_msg, complete_rewrite, spawn_arg);
|
||||
} else
|
||||
printf("* Unmerged path %s\n", name);
|
||||
exit(0);
|
||||
}
|
||||
if (waitpid(pid, &status, 0) < 0 ||
|
||||
!WIFEXITED(status) || WEXITSTATUS(status)) {
|
||||
/* Earlier we did not check the exit status because
|
||||
* diff exits non-zero if files are different, and
|
||||
* we are not interested in knowing that. It was a
|
||||
* mistake which made it harder to quit a diff-*
|
||||
* session that uses the git-apply-patch-script as
|
||||
* the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
|
||||
* should also exit non-zero only when it wants to
|
||||
* abort the entire diff-* session.
|
||||
*/
|
||||
remove_tempfile();
|
||||
|
||||
retval = 0;
|
||||
if (pgm)
|
||||
retval = spawn_prog(pgm, spawn_arg);
|
||||
remove_tempfile();
|
||||
if (retval) {
|
||||
fprintf(stderr, "external diff died, stopping at %s.\n", name);
|
||||
exit(1);
|
||||
}
|
||||
remove_tempfile();
|
||||
}
|
||||
|
||||
static void diff_fill_sha1_info(struct diff_filespec *one)
|
||||
|
@ -29,19 +29,21 @@ die "Need SVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1";
|
||||
$SIG{'PIPE'}="IGNORE";
|
||||
$ENV{'TZ'}="UTC";
|
||||
|
||||
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,$opt_b,$opt_r,$opt_s,$opt_l,$opt_d,$opt_D);
|
||||
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
|
||||
$opt_b,$opt_r,$opt_I,$opt_s,$opt_l,$opt_d,$opt_D);
|
||||
|
||||
sub usage() {
|
||||
print STDERR <<END;
|
||||
Usage: ${\basename $0} # fetch/update GIT from SVN
|
||||
[-o branch-for-HEAD] [-h] [-v] [-l max_rev]
|
||||
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
|
||||
[-d|-D] [-i] [-u] [-r] [-s start_chg] [-m] [-M regex] [SVN_URL]
|
||||
[-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
|
||||
[-m] [-M regex] [SVN_URL]
|
||||
END
|
||||
exit(1);
|
||||
}
|
||||
|
||||
getopts("b:C:dDhil:mM:o:rs:t:T:uv") or usage();
|
||||
getopts("b:C:dDhiI:l:mM:o:rs:t:T:uv") or usage();
|
||||
usage if $opt_h;
|
||||
|
||||
my $tag_name = $opt_t || "tags";
|
||||
@ -112,16 +114,40 @@ sub file {
|
||||
DIR => File::Spec->tmpdir(), UNLINK => 1);
|
||||
|
||||
print "... $rev $path ...\n" if $opt_v;
|
||||
my $pool = SVN::Pool->new();
|
||||
eval { $self->{'svn'}->get_file($path,$rev,$fh,$pool); };
|
||||
$pool->clear;
|
||||
my (undef, $properties);
|
||||
eval { (undef, $properties)
|
||||
= $self->{'svn'}->get_file($path,$rev,$fh); };
|
||||
if($@) {
|
||||
return undef if $@ =~ /Attempted to get checksum/;
|
||||
die $@;
|
||||
}
|
||||
my $mode;
|
||||
if (exists $properties->{'svn:executable'}) {
|
||||
$mode = '0755';
|
||||
} else {
|
||||
$mode = '0644';
|
||||
}
|
||||
close ($fh);
|
||||
|
||||
return $name;
|
||||
return ($name, $mode);
|
||||
}
|
||||
|
||||
sub ignore {
|
||||
my($self,$path,$rev) = @_;
|
||||
|
||||
print "... $rev $path ...\n" if $opt_v;
|
||||
my (undef,undef,$properties)
|
||||
= $self->{'svn'}->get_dir($path,$rev,undef);
|
||||
if (exists $properties->{'svn:ignore'}) {
|
||||
my ($fh, $name) = tempfile('gitsvn.XXXXXX',
|
||||
DIR => File::Spec->tmpdir(),
|
||||
UNLINK => 1);
|
||||
print $fh $properties->{'svn:ignore'};
|
||||
close($fh);
|
||||
return $name;
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
package main;
|
||||
@ -296,7 +322,7 @@ sub get_file($$$) {
|
||||
my $svnpath = revert_split_path($branch,$path);
|
||||
|
||||
# now get it
|
||||
my $name;
|
||||
my ($name,$mode);
|
||||
if($opt_d) {
|
||||
my($req,$res);
|
||||
|
||||
@ -316,8 +342,9 @@ sub get_file($$$) {
|
||||
return undef if $res->code == 301; # directory?
|
||||
die $res->status_line." at $url\n";
|
||||
}
|
||||
$mode = '0644'; # can't obtain mode via direct http request?
|
||||
} else {
|
||||
$name = $svn->file("$svnpath",$rev);
|
||||
($name,$mode) = $svn->file("$svnpath",$rev);
|
||||
return undef unless defined $name;
|
||||
}
|
||||
|
||||
@ -331,10 +358,37 @@ sub get_file($$$) {
|
||||
chomp $sha;
|
||||
close $F;
|
||||
unlink $name;
|
||||
my $mode = "0644"; # SV does not seem to store any file modes
|
||||
return [$mode, $sha, $path];
|
||||
}
|
||||
|
||||
sub get_ignore($$$$$) {
|
||||
my($new,$old,$rev,$branch,$path) = @_;
|
||||
|
||||
return unless $opt_I;
|
||||
my $svnpath = revert_split_path($branch,$path);
|
||||
my $name = $svn->ignore("$svnpath",$rev);
|
||||
if ($path eq '/') {
|
||||
$path = $opt_I;
|
||||
} else {
|
||||
$path = File::Spec->catfile($path,$opt_I);
|
||||
}
|
||||
if (defined $name) {
|
||||
my $pid = open(my $F, '-|');
|
||||
die $! unless defined $pid;
|
||||
if (!$pid) {
|
||||
exec("git-hash-object", "-w", $name)
|
||||
or die "Cannot create object: $!\n";
|
||||
}
|
||||
my $sha = <$F>;
|
||||
chomp $sha;
|
||||
close $F;
|
||||
unlink $name;
|
||||
push(@$new,['0644',$sha,$path]);
|
||||
} else {
|
||||
push(@$old,$path);
|
||||
}
|
||||
}
|
||||
|
||||
sub split_path($$) {
|
||||
my($rev,$path) = @_;
|
||||
my $branch;
|
||||
@ -540,6 +594,9 @@ sub commit {
|
||||
my $opath = $action->[3];
|
||||
print STDERR "$revision: $branch: could not fetch '$opath'\n";
|
||||
}
|
||||
} elsif ($node_kind eq $SVN::Node::dir) {
|
||||
get_ignore(\@new, \@old, $revision,
|
||||
$branch,$path);
|
||||
}
|
||||
} elsif ($action->[0] eq "D") {
|
||||
push(@old,$path);
|
||||
@ -548,6 +605,9 @@ sub commit {
|
||||
if ($node_kind eq $SVN::Node::file) {
|
||||
my $f = get_file($revision,$branch,$path);
|
||||
push(@new,$f) if $f;
|
||||
} elsif ($node_kind eq $SVN::Node::dir) {
|
||||
get_ignore(\@new, \@old, $revision,
|
||||
$branch,$path);
|
||||
}
|
||||
} else {
|
||||
die "$revision: unknown action '".$action->[0]."' for $path\n";
|
||||
|
Loading…
Reference in New Issue
Block a user