Compare commits
21 Commits
v1.6.6-rc3
...
v1.6.6
Author | SHA1 | Date | |
---|---|---|---|
902f235378 | |||
b7f44fdf27 | |||
150d38c4f3 | |||
129a5a6dea | |||
0fe19753f2 | |||
c8cba79181 | |||
063681d72e | |||
7a955a5365 | |||
ea020cbd6a | |||
33973a5b17 | |||
7d944c3399 | |||
1d144aa25e | |||
af57b41d41 | |||
577e9fcad2 | |||
ab0964d951 | |||
b6b9f83ea1 | |||
e49ca974d6 | |||
8eca03c861 | |||
a5b80d9263 | |||
afab0fe052 | |||
63b76948e1 |
@ -22,10 +22,10 @@ These changes were discussed long time ago and existing behaviours have
|
|||||||
been identified as more problematic to the userbase than keeping them for
|
been identified as more problematic to the userbase than keeping them for
|
||||||
the sake of backward compatibility.
|
the sake of backward compatibility.
|
||||||
|
|
||||||
When necessary, transition strategy for existing users has been designed
|
When necessary, a transition strategy for existing users has been designed
|
||||||
not to force them running around setting configuration variables and
|
not to force them running around setting configuration variables and
|
||||||
updating their scripts in order to either keep the traditional behaviour
|
updating their scripts in order to either keep the traditional behaviour
|
||||||
or adjust to the new behaviour on the day their sysadmin decides to install
|
or adjust to the new behaviour, on the day their sysadmin decides to install
|
||||||
the new version of git. When we switched from "git-foo" to "git foo" in
|
the new version of git. When we switched from "git-foo" to "git foo" in
|
||||||
1.6.0, even though the change had been advertised and the transition
|
1.6.0, even though the change had been advertised and the transition
|
||||||
guide had been provided for a very long time, the users procrastinated
|
guide had been provided for a very long time, the users procrastinated
|
||||||
@ -34,11 +34,12 @@ their sysadmins updated their git installation. We are trying to avoid
|
|||||||
repeating that unpleasantness in the 1.7.0 release.
|
repeating that unpleasantness in the 1.7.0 release.
|
||||||
|
|
||||||
For changes decided to be in 1.7.0, commands that will be affected
|
For changes decided to be in 1.7.0, commands that will be affected
|
||||||
have been much louder to strongly discourage such procrastination. If
|
have been much louder to strongly discourage such procrastination, and
|
||||||
you have been using recent versions of git, you would have seen
|
they continue to be in this release. If you have been using recent
|
||||||
warnings issued when you exercised features whose behaviour will
|
versions of git, you would have seen warnings issued when you used
|
||||||
change, with a clear instruction on how to keep the existing behaviour
|
features whose behaviour will change, with a clear instruction on how
|
||||||
if you want to. You hopefully are already well prepared.
|
to keep the existing behaviour if you want to. You hopefully are
|
||||||
|
already well prepared.
|
||||||
|
|
||||||
Of course, we have also been giving "this and that will change in
|
Of course, we have also been giving "this and that will change in
|
||||||
1.7.0; prepare yourselves" warnings in the release notes and
|
1.7.0; prepare yourselves" warnings in the release notes and
|
||||||
@ -208,15 +209,14 @@ Updates since v1.6.5
|
|||||||
|
|
||||||
* "git svn" learned to read SVN 1.5+ and SVK merge tickets.
|
* "git svn" learned to read SVN 1.5+ and SVK merge tickets.
|
||||||
|
|
||||||
|
* "git svn" learned to recreate empty directories tracked only by SVN.
|
||||||
|
|
||||||
* "gitweb" can optionally render its "blame" output incrementally (this
|
* "gitweb" can optionally render its "blame" output incrementally (this
|
||||||
requires JavaScript on the client side).
|
requires JavaScript on the client side).
|
||||||
|
|
||||||
* Author names shown in gitweb output are links to search commits by the
|
* Author names shown in gitweb output are links to search commits by the
|
||||||
author.
|
author.
|
||||||
|
|
||||||
|
|
||||||
(developers)
|
|
||||||
|
|
||||||
Fixes since v1.6.5
|
Fixes since v1.6.5
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -43,6 +43,11 @@ unreleased) version of git, that is available from 'master'
|
|||||||
branch of the `git.git` repository.
|
branch of the `git.git` repository.
|
||||||
Documentation for older releases are available here:
|
Documentation for older releases are available here:
|
||||||
|
|
||||||
|
* link:v1.6.6/git.html[documentation for release 1.6.6]
|
||||||
|
|
||||||
|
* release notes for
|
||||||
|
link:RelNotes-1.6.6.txt[1.6.6].
|
||||||
|
|
||||||
* link:v1.6.5.7/git.html[documentation for release 1.6.5.7]
|
* link:v1.6.5.7/git.html[documentation for release 1.6.5.7]
|
||||||
|
|
||||||
* release notes for
|
* release notes for
|
||||||
|
@ -1,6 +1,52 @@
|
|||||||
hash API
|
hash API
|
||||||
========
|
========
|
||||||
|
|
||||||
Talk about <hash.h>
|
The hash API is a collection of simple hash table functions. Users are expected
|
||||||
|
to implement their own hashing.
|
||||||
|
|
||||||
(Linus)
|
Data Structures
|
||||||
|
---------------
|
||||||
|
|
||||||
|
`struct hash_table`::
|
||||||
|
|
||||||
|
The hash table structure. The `array` member points to the hash table
|
||||||
|
entries. The `size` member counts the total number of valid and invalid
|
||||||
|
entries in the table. The `nr` member keeps track of the number of
|
||||||
|
valid entries.
|
||||||
|
|
||||||
|
`struct hash_table_entry`::
|
||||||
|
|
||||||
|
An opaque structure representing an entry in the hash table. The `hash`
|
||||||
|
member is the entry's hash key and the `ptr` member is the entry's
|
||||||
|
value.
|
||||||
|
|
||||||
|
Functions
|
||||||
|
---------
|
||||||
|
|
||||||
|
`init_hash`::
|
||||||
|
|
||||||
|
Initialize the hash table.
|
||||||
|
|
||||||
|
`free_hash`::
|
||||||
|
|
||||||
|
Release memory associated with the hash table.
|
||||||
|
|
||||||
|
`insert_hash`::
|
||||||
|
|
||||||
|
Insert a pointer into the hash table. If an entry with that hash
|
||||||
|
already exists, a pointer to the existing entry's value is returned.
|
||||||
|
Otherwise NULL is returned. This allows callers to implement
|
||||||
|
chaining, etc.
|
||||||
|
|
||||||
|
`lookup_hash`::
|
||||||
|
|
||||||
|
Lookup an entry in the hash table. If an entry with that hash exists
|
||||||
|
the entry's value is returned. Otherwise NULL is returned.
|
||||||
|
|
||||||
|
`for_each_hash`::
|
||||||
|
|
||||||
|
Call a function for each entry in the hash table. The function is
|
||||||
|
expected to take the entry's value as its only argument and return an
|
||||||
|
int. If the function returns a negative int the loop is aborted
|
||||||
|
immediately. Otherwise, the return value is accumulated and the sum
|
||||||
|
returned upon completion of the loop.
|
||||||
|
@ -12,7 +12,7 @@ strbuf API actually relies on the string being free of NULs.
|
|||||||
|
|
||||||
strbufs has some invariants that are very important to keep in mind:
|
strbufs has some invariants that are very important to keep in mind:
|
||||||
|
|
||||||
. The `buf` member is never NULL, so you it can be used in any usual C
|
. The `buf` member is never NULL, so it can be used in any usual C
|
||||||
string operations safely. strbuf's _have_ to be initialized either by
|
string operations safely. strbuf's _have_ to be initialized either by
|
||||||
`strbuf_init()` or by `= STRBUF_INIT` before the invariants, though.
|
`strbuf_init()` or by `= STRBUF_INIT` before the invariants, though.
|
||||||
+
|
+
|
||||||
@ -55,7 +55,7 @@ Data structures
|
|||||||
|
|
||||||
* `struct strbuf`
|
* `struct strbuf`
|
||||||
|
|
||||||
This is string buffer structure. The `len` member can be used to
|
This is the string buffer structure. The `len` member can be used to
|
||||||
determine the current length of the string, and `buf` member provides access to
|
determine the current length of the string, and `buf` member provides access to
|
||||||
the string itself.
|
the string itself.
|
||||||
|
|
||||||
@ -253,3 +253,9 @@ same behaviour as well.
|
|||||||
comments are considered contents to be removed or not.
|
comments are considered contents to be removed or not.
|
||||||
|
|
||||||
`launch_editor`::
|
`launch_editor`::
|
||||||
|
|
||||||
|
Launch the user preferred editor to edit a file and fill the buffer
|
||||||
|
with the file's contents upon the user completing their editing. The
|
||||||
|
third argument can be used to set the environment which the editor is
|
||||||
|
run in. If the buffer is NULL the editor is launched as usual but the
|
||||||
|
file's contents are not read into the buffer upon completion.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
GVF=GIT-VERSION-FILE
|
GVF=GIT-VERSION-FILE
|
||||||
DEF_VER=v1.6.6-rc3.GIT
|
DEF_VER=v1.6.6
|
||||||
|
|
||||||
LF='
|
LF='
|
||||||
'
|
'
|
||||||
|
@ -1305,6 +1305,7 @@ static void get_ac_line(const char *inbuf, const char *what,
|
|||||||
error_out:
|
error_out:
|
||||||
/* Ugh */
|
/* Ugh */
|
||||||
*tz = "(unknown)";
|
*tz = "(unknown)";
|
||||||
|
strcpy(person, *tz);
|
||||||
strcpy(mail, *tz);
|
strcpy(mail, *tz);
|
||||||
*time = 0;
|
*time = 0;
|
||||||
return;
|
return;
|
||||||
@ -1314,20 +1315,26 @@ static void get_ac_line(const char *inbuf, const char *what,
|
|||||||
tmp = person;
|
tmp = person;
|
||||||
tmp += len;
|
tmp += len;
|
||||||
*tmp = 0;
|
*tmp = 0;
|
||||||
while (*tmp != ' ')
|
while (person < tmp && *tmp != ' ')
|
||||||
tmp--;
|
tmp--;
|
||||||
|
if (tmp <= person)
|
||||||
|
goto error_out;
|
||||||
*tz = tmp+1;
|
*tz = tmp+1;
|
||||||
tzlen = (person+len)-(tmp+1);
|
tzlen = (person+len)-(tmp+1);
|
||||||
|
|
||||||
*tmp = 0;
|
*tmp = 0;
|
||||||
while (*tmp != ' ')
|
while (person < tmp && *tmp != ' ')
|
||||||
tmp--;
|
tmp--;
|
||||||
|
if (tmp <= person)
|
||||||
|
goto error_out;
|
||||||
*time = strtoul(tmp, NULL, 10);
|
*time = strtoul(tmp, NULL, 10);
|
||||||
timepos = tmp;
|
timepos = tmp;
|
||||||
|
|
||||||
*tmp = 0;
|
*tmp = 0;
|
||||||
while (*tmp != ' ')
|
while (person < tmp && *tmp != ' ')
|
||||||
tmp--;
|
tmp--;
|
||||||
|
if (tmp <= person)
|
||||||
|
return;
|
||||||
mailpos = tmp + 1;
|
mailpos = tmp + 1;
|
||||||
*tmp = 0;
|
*tmp = 0;
|
||||||
maillen = timepos - tmp;
|
maillen = timepos - tmp;
|
||||||
|
@ -779,7 +779,7 @@ EOF
|
|||||||
|
|
||||||
cp "$TODO" "$TODO".backup
|
cp "$TODO" "$TODO".backup
|
||||||
git_editor "$TODO" ||
|
git_editor "$TODO" ||
|
||||||
die "Could not execute editor"
|
die_abort "Could not execute editor"
|
||||||
|
|
||||||
has_action "$TODO" ||
|
has_action "$TODO" ||
|
||||||
die_abort "Nothing to do"
|
die_abort "Nothing to do"
|
||||||
|
281
git-svn.perl
281
git-svn.perl
@ -663,7 +663,8 @@ sub cmd_branch {
|
|||||||
}
|
}
|
||||||
$head ||= 'HEAD';
|
$head ||= 'HEAD';
|
||||||
|
|
||||||
my ($src, $rev, undef, $gs) = working_head_info($head);
|
my (undef, $rev, undef, $gs) = working_head_info($head);
|
||||||
|
my $src = $gs->full_url;
|
||||||
|
|
||||||
my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
|
my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
|
||||||
my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' };
|
my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' };
|
||||||
@ -1634,6 +1635,7 @@ use Carp qw/croak/;
|
|||||||
use File::Path qw/mkpath/;
|
use File::Path qw/mkpath/;
|
||||||
use File::Copy qw/copy/;
|
use File::Copy qw/copy/;
|
||||||
use IPC::Open3;
|
use IPC::Open3;
|
||||||
|
use Memoize; # core since 5.8.0, Jul 2002
|
||||||
|
|
||||||
my ($_gc_nr, $_gc_period);
|
my ($_gc_nr, $_gc_period);
|
||||||
|
|
||||||
@ -1741,7 +1743,11 @@ sub fetch_all {
|
|||||||
my $ra = Git::SVN::Ra->new($url);
|
my $ra = Git::SVN::Ra->new($url);
|
||||||
my $uuid = $ra->get_uuid;
|
my $uuid = $ra->get_uuid;
|
||||||
my $head = $ra->get_latest_revnum;
|
my $head = $ra->get_latest_revnum;
|
||||||
$ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] });
|
|
||||||
|
# ignore errors, $head revision may not even exist anymore
|
||||||
|
eval { $ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] }) };
|
||||||
|
warn "W: $@\n" if $@;
|
||||||
|
|
||||||
my $base = defined $fetch ? $head : 0;
|
my $base = defined $fetch ? $head : 0;
|
||||||
|
|
||||||
# read the max revs for wildcard expansion (branches/*, tags/*)
|
# read the max revs for wildcard expansion (branches/*, tags/*)
|
||||||
@ -2446,12 +2452,6 @@ sub get_commit_parents {
|
|||||||
next if $seen{$p};
|
next if $seen{$p};
|
||||||
$seen{$p} = 1;
|
$seen{$p} = 1;
|
||||||
push @ret, $p;
|
push @ret, $p;
|
||||||
# MAXPARENT is defined to 16 in commit-tree.c:
|
|
||||||
last if @ret >= 16;
|
|
||||||
}
|
|
||||||
if (@tmp) {
|
|
||||||
die "r$log_entry->{revision}: No room for parents:\n\t",
|
|
||||||
join("\n\t", @tmp), "\n";
|
|
||||||
}
|
}
|
||||||
@ret;
|
@ret;
|
||||||
}
|
}
|
||||||
@ -2740,21 +2740,44 @@ sub do_fetch {
|
|||||||
|
|
||||||
sub mkemptydirs {
|
sub mkemptydirs {
|
||||||
my ($self, $r) = @_;
|
my ($self, $r) = @_;
|
||||||
my %empty_dirs = ();
|
|
||||||
|
|
||||||
open my $fh, '<', "$self->{dir}/unhandled.log" or return;
|
sub scan {
|
||||||
binmode $fh or croak "binmode: $!";
|
my ($r, $empty_dirs, $line) = @_;
|
||||||
while (<$fh>) {
|
if (defined $r && $line =~ /^r(\d+)$/) {
|
||||||
if (defined $r && /^r(\d+)$/) {
|
return 0 if $1 > $r;
|
||||||
last if $1 > $r;
|
} elsif ($line =~ /^ \+empty_dir: (.+)$/) {
|
||||||
} elsif (/^ \+empty_dir: (.+)$/) {
|
$empty_dirs->{$1} = 1;
|
||||||
$empty_dirs{$1} = 1;
|
} elsif ($line =~ /^ \-empty_dir: (.+)$/) {
|
||||||
} elsif (/^ \-empty_dir: (.+)$/) {
|
my @d = grep {m[^\Q$1\E(/|$)]} (keys %$empty_dirs);
|
||||||
my @d = grep {m[^\Q$1\E(/|$)]} (keys %empty_dirs);
|
delete @$empty_dirs{@d};
|
||||||
delete @empty_dirs{@d};
|
}
|
||||||
|
1; # continue
|
||||||
|
};
|
||||||
|
|
||||||
|
my %empty_dirs = ();
|
||||||
|
my $gz_file = "$self->{dir}/unhandled.log.gz";
|
||||||
|
if (-f $gz_file) {
|
||||||
|
if (!$can_compress) {
|
||||||
|
warn "Compress::Zlib could not be found; ",
|
||||||
|
"empty directories in $gz_file will not be read\n";
|
||||||
|
} else {
|
||||||
|
my $gz = Compress::Zlib::gzopen($gz_file, "rb") or
|
||||||
|
die "Unable to open $gz_file: $!\n";
|
||||||
|
my $line;
|
||||||
|
while ($gz->gzreadline($line) > 0) {
|
||||||
|
scan($r, \%empty_dirs, $line) or last;
|
||||||
|
}
|
||||||
|
$gz->gzclose;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close $fh;
|
|
||||||
|
if (open my $fh, '<', "$self->{dir}/unhandled.log") {
|
||||||
|
binmode $fh or croak "binmode: $!";
|
||||||
|
while (<$fh>) {
|
||||||
|
scan($r, \%empty_dirs, $_) or last;
|
||||||
|
}
|
||||||
|
close $fh;
|
||||||
|
}
|
||||||
|
|
||||||
my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/;
|
my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/;
|
||||||
foreach my $d (sort keys %empty_dirs) {
|
foreach my $d (sort keys %empty_dirs) {
|
||||||
@ -2967,6 +2990,111 @@ sub find_extra_svk_parents {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub lookup_svn_merge {
|
||||||
|
my $uuid = shift;
|
||||||
|
my $url = shift;
|
||||||
|
my $merge = shift;
|
||||||
|
|
||||||
|
my ($source, $revs) = split ":", $merge;
|
||||||
|
my $path = $source;
|
||||||
|
$path =~ s{^/}{};
|
||||||
|
my $gs = Git::SVN->find_by_url($url.$source, $url, $path);
|
||||||
|
if ( !$gs ) {
|
||||||
|
warn "Couldn't find revmap for $url$source\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
my @ranges = split ",", $revs;
|
||||||
|
my ($tip, $tip_commit);
|
||||||
|
my @merged_commit_ranges;
|
||||||
|
# find the tip
|
||||||
|
for my $range ( @ranges ) {
|
||||||
|
my ($bottom, $top) = split "-", $range;
|
||||||
|
$top ||= $bottom;
|
||||||
|
my $bottom_commit = $gs->find_rev_after( $bottom, 1, $top );
|
||||||
|
my $top_commit = $gs->find_rev_before( $top, 1, $bottom );
|
||||||
|
|
||||||
|
unless ($top_commit and $bottom_commit) {
|
||||||
|
warn "W:unknown path/rev in svn:mergeinfo "
|
||||||
|
."dirprop: $source:$range\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
push @merged_commit_ranges,
|
||||||
|
"$bottom_commit^..$top_commit";
|
||||||
|
|
||||||
|
if ( !defined $tip or $top > $tip ) {
|
||||||
|
$tip = $top;
|
||||||
|
$tip_commit = $top_commit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ($tip_commit, @merged_commit_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _rev_list {
|
||||||
|
my ($msg_fh, $ctx) = command_output_pipe(
|
||||||
|
"rev-list", @_,
|
||||||
|
);
|
||||||
|
my @rv;
|
||||||
|
while ( <$msg_fh> ) {
|
||||||
|
chomp;
|
||||||
|
push @rv, $_;
|
||||||
|
}
|
||||||
|
command_close_pipe($msg_fh, $ctx);
|
||||||
|
@rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_cherry_pick {
|
||||||
|
my $base = shift;
|
||||||
|
my $tip = shift;
|
||||||
|
my @ranges = @_;
|
||||||
|
my %commits = map { $_ => 1 }
|
||||||
|
_rev_list("--no-merges", $tip, "--not", $base);
|
||||||
|
for my $range ( @ranges ) {
|
||||||
|
delete @commits{_rev_list($range)};
|
||||||
|
}
|
||||||
|
return (keys %commits);
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
memoize 'lookup_svn_merge';
|
||||||
|
memoize 'check_cherry_pick';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub parents_exclude {
|
||||||
|
my $parents = shift;
|
||||||
|
my @commits = @_;
|
||||||
|
return unless @commits;
|
||||||
|
|
||||||
|
my @excluded;
|
||||||
|
my $excluded;
|
||||||
|
do {
|
||||||
|
my @cmd = ('rev-list', "-1", @commits, "--not", @$parents );
|
||||||
|
$excluded = command_oneline(@cmd);
|
||||||
|
if ( $excluded ) {
|
||||||
|
my @new;
|
||||||
|
my $found;
|
||||||
|
for my $commit ( @commits ) {
|
||||||
|
if ( $commit eq $excluded ) {
|
||||||
|
push @excluded, $commit;
|
||||||
|
$found++;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
push @new, $commit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
die "saw commit '$excluded' in rev-list output, "
|
||||||
|
."but we didn't ask for that commit (wanted: @commits --not @$parents)"
|
||||||
|
unless $found;
|
||||||
|
@commits = @new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ($excluded and @commits);
|
||||||
|
|
||||||
|
return @excluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# note: this function should only be called if the various dirprops
|
# note: this function should only be called if the various dirprops
|
||||||
# have actually changed
|
# have actually changed
|
||||||
sub find_extra_svn_parents {
|
sub find_extra_svn_parents {
|
||||||
@ -2979,82 +3107,73 @@ sub find_extra_svn_parents {
|
|||||||
# are now marked as merge, we can add the tip as a parent.
|
# are now marked as merge, we can add the tip as a parent.
|
||||||
my @merges = split "\n", $mergeinfo;
|
my @merges = split "\n", $mergeinfo;
|
||||||
my @merge_tips;
|
my @merge_tips;
|
||||||
my @merged_commit_ranges;
|
|
||||||
my $url = $self->rewrite_root || $self->{url};
|
my $url = $self->rewrite_root || $self->{url};
|
||||||
|
my $uuid = $self->ra_uuid;
|
||||||
|
my %ranges;
|
||||||
for my $merge ( @merges ) {
|
for my $merge ( @merges ) {
|
||||||
my ($source, $revs) = split ":", $merge;
|
my ($tip_commit, @ranges) =
|
||||||
my $path = $source;
|
lookup_svn_merge( $uuid, $url, $merge );
|
||||||
$path =~ s{^/}{};
|
|
||||||
my $gs = Git::SVN->find_by_url($url.$source, $url, $path);
|
|
||||||
if ( !$gs ) {
|
|
||||||
warn "Couldn't find revmap for $url$source\n";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
my @ranges = split ",", $revs;
|
|
||||||
my ($tip, $tip_commit);
|
|
||||||
# find the tip
|
|
||||||
for my $range ( @ranges ) {
|
|
||||||
my ($bottom, $top) = split "-", $range;
|
|
||||||
$top ||= $bottom;
|
|
||||||
my $bottom_commit =
|
|
||||||
$gs->rev_map_get($bottom, $self->ra_uuid) ||
|
|
||||||
$gs->rev_map_get($bottom+1, $self->ra_uuid);
|
|
||||||
my $top_commit;
|
|
||||||
for (; !$top_commit && $top >= $bottom; --$top) {
|
|
||||||
$top_commit =
|
|
||||||
$gs->rev_map_get($top, $self->ra_uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
unless ($top_commit and $bottom_commit) {
|
|
||||||
warn "W:unknown path/rev in svn:mergeinfo "
|
|
||||||
."dirprop: $source:$range\n";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
push @merged_commit_ranges,
|
|
||||||
"$bottom_commit..$top_commit";
|
|
||||||
|
|
||||||
if ( !defined $tip or $top > $tip ) {
|
|
||||||
$tip = $top;
|
|
||||||
$tip_commit = $top_commit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unless (!$tip_commit or
|
unless (!$tip_commit or
|
||||||
grep { $_ eq $tip_commit } @$parents ) {
|
grep { $_ eq $tip_commit } @$parents ) {
|
||||||
push @merge_tips, $tip_commit;
|
push @merge_tips, $tip_commit;
|
||||||
|
$ranges{$tip_commit} = \@ranges;
|
||||||
} else {
|
} else {
|
||||||
push @merge_tips, undef;
|
push @merge_tips, undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my %excluded = map { $_ => 1 }
|
||||||
|
parents_exclude($parents, grep { defined } @merge_tips);
|
||||||
|
|
||||||
|
# check merge tips for new parents
|
||||||
|
my @new_parents;
|
||||||
for my $merge_tip ( @merge_tips ) {
|
for my $merge_tip ( @merge_tips ) {
|
||||||
my $spec = shift @merges;
|
my $spec = shift @merges;
|
||||||
next unless $merge_tip;
|
next unless $merge_tip and $excluded{$merge_tip};
|
||||||
my @cmd = ('rev-list', "-1", $merge_tip,
|
|
||||||
"--not", @$parents );
|
my $ranges = $ranges{$merge_tip};
|
||||||
my ($msg_fh, $ctx) = command_output_pipe(@cmd);
|
|
||||||
my $new;
|
# check out 'new' tips
|
||||||
while ( <$msg_fh> ) {
|
my $merge_base = command_oneline(
|
||||||
$new=1;last;
|
"merge-base",
|
||||||
|
@$parents, $merge_tip,
|
||||||
|
);
|
||||||
|
|
||||||
|
# double check that there are no missing non-merge commits
|
||||||
|
my (@incomplete) = check_cherry_pick(
|
||||||
|
$merge_base, $merge_tip,
|
||||||
|
@$ranges,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( @incomplete ) {
|
||||||
|
warn "W:svn cherry-pick ignored ($spec) - missing "
|
||||||
|
.@incomplete." commit(s) (eg $incomplete[0])\n";
|
||||||
|
} else {
|
||||||
|
warn
|
||||||
|
"Found merge parent (svn:mergeinfo prop): ",
|
||||||
|
$merge_tip, "\n";
|
||||||
|
push @new_parents, $merge_tip;
|
||||||
}
|
}
|
||||||
command_close_pipe($msg_fh, $ctx);
|
}
|
||||||
if ( $new ) {
|
|
||||||
push @cmd, @merged_commit_ranges;
|
# cater for merges which merge commits from multiple branches
|
||||||
my ($msg_fh, $ctx) = command_output_pipe(@cmd);
|
if ( @new_parents > 1 ) {
|
||||||
my $unmerged;
|
for ( my $i = 0; $i <= $#new_parents; $i++ ) {
|
||||||
while ( <$msg_fh> ) {
|
for ( my $j = 0; $j <= $#new_parents; $j++ ) {
|
||||||
$unmerged=1;last;
|
next if $i == $j;
|
||||||
}
|
next unless $new_parents[$i];
|
||||||
command_close_pipe($msg_fh, $ctx);
|
next unless $new_parents[$j];
|
||||||
if ( $unmerged ) {
|
my $revs = command_oneline(
|
||||||
warn "W:svn cherry-pick ignored ($spec)\n";
|
"rev-list", "-1",
|
||||||
} else {
|
"$new_parents[$i]..$new_parents[$j]",
|
||||||
warn
|
);
|
||||||
"Found merge parent (svn:mergeinfo prop): ",
|
if ( !$revs ) {
|
||||||
$merge_tip, "\n";
|
undef($new_parents[$i]);
|
||||||
push @$parents, $merge_tip;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
push @$parents, grep { defined } @new_parents;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub make_log_entry {
|
sub make_log_entry {
|
||||||
|
@ -144,4 +144,17 @@ test_expect_success 'blame path that used to be a directory' '
|
|||||||
git blame HEAD^.. -- path
|
git blame HEAD^.. -- path
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'blame to a commit with no author name' '
|
||||||
|
TREE=`git rev-parse HEAD:`
|
||||||
|
cat >badcommit <<EOF
|
||||||
|
tree $TREE
|
||||||
|
author <noname> 1234567890 +0000
|
||||||
|
committer David Reiss <dreiss@facebook.com> 1234567890 +0000
|
||||||
|
|
||||||
|
some message
|
||||||
|
EOF
|
||||||
|
COMMIT=`git hash-object -t commit -w badcommit`
|
||||||
|
git --no-pager blame $COMMIT -- uno >/dev/null
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -114,5 +114,29 @@ test_expect_success 'removed top-level directory does not exist' '
|
|||||||
test ! -e removed/d
|
test ! -e removed/d
|
||||||
|
|
||||||
'
|
'
|
||||||
|
unhandled=.git/svn/refs/remotes/git-svn/unhandled.log
|
||||||
|
test_expect_success 'git svn gc-ed files work' '
|
||||||
|
(
|
||||||
|
cd removed &&
|
||||||
|
git svn gc &&
|
||||||
|
: Compress::Zlib may not be available &&
|
||||||
|
if test -f "$unhandled".gz
|
||||||
|
then
|
||||||
|
svn_cmd mkdir -m gz "$svnrepo"/gz &&
|
||||||
|
git reset --hard $(git rev-list HEAD | tail -1) &&
|
||||||
|
git svn rebase &&
|
||||||
|
test -f "$unhandled".gz &&
|
||||||
|
test -f "$unhandled" &&
|
||||||
|
for i in a b c "weird file name" gz "! !"
|
||||||
|
do
|
||||||
|
if ! test -d "$i"
|
||||||
|
then
|
||||||
|
echo >&2 "$i does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -15,12 +15,27 @@ test_expect_success 'load svn dump' "
|
|||||||
git svn fetch --all
|
git svn fetch --all
|
||||||
"
|
"
|
||||||
|
|
||||||
test_expect_success 'represent svn merges without intervening commits' "
|
test_expect_success 'all svn merges became git merge commits' '
|
||||||
[ `git cat-file commit HEAD^1 | grep parent | wc -l` -eq 2 ]
|
unmarked=$(git rev-list --parents --all --grep=Merge |
|
||||||
"
|
grep -v " .* " | cut -f1 -d" ")
|
||||||
|
[ -z "$unmarked" ]
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'represent svn merges with intervening commits' "
|
test_expect_success 'cherry picks did not become git merge commits' '
|
||||||
[ `git cat-file commit HEAD | grep parent | wc -l` -eq 2 ]
|
bad_cherries=$(git rev-list --parents --all --grep=Cherry |
|
||||||
"
|
grep " .* " | cut -f1 -d" ")
|
||||||
|
[ -z "$bad_cherries" ]
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'svn non-merge merge commits did not become git merge commits' '
|
||||||
|
bad_non_merges=$(git rev-list --parents --all --grep=non-merge |
|
||||||
|
grep " .* " | cut -f1 -d" ")
|
||||||
|
[ -z "$bad_non_merges" ]
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'everything got merged in the end' '
|
||||||
|
unmerged=$(git rev-list --all --not master)
|
||||||
|
[ -z "$unmerged" ]
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -11,93 +11,151 @@ mkdir foo.svn
|
|||||||
svnadmin create foo.svn
|
svnadmin create foo.svn
|
||||||
svn co file://`pwd`/foo.svn foo
|
svn co file://`pwd`/foo.svn foo
|
||||||
|
|
||||||
|
commit() {
|
||||||
|
i=$(( $1 + 1 ))
|
||||||
|
shift;
|
||||||
|
svn commit -m "(r$i) $*" >/dev/null || exit 1
|
||||||
|
echo $i
|
||||||
|
}
|
||||||
|
|
||||||
|
say() {
|
||||||
|
echo "[1m * $*[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
i=0
|
||||||
cd foo
|
cd foo
|
||||||
mkdir trunk
|
mkdir trunk
|
||||||
mkdir branches
|
mkdir branches
|
||||||
svn add trunk branches
|
svn add trunk branches
|
||||||
svn commit -m "Setup trunk and branches"
|
i=$(commit $i "Setup trunk and branches")
|
||||||
cd trunk
|
|
||||||
|
|
||||||
git cat-file blob 6683463e:Makefile > Makefile
|
git cat-file blob 6683463e:Makefile > trunk/Makefile
|
||||||
svn add Makefile
|
svn add trunk/Makefile
|
||||||
|
|
||||||
echo "Committing ANCESTOR"
|
say "Committing ANCESTOR"
|
||||||
svn commit -m "ancestor"
|
i=$(commit $i "ancestor")
|
||||||
cd ..
|
|
||||||
svn cp trunk branches/left
|
svn cp trunk branches/left
|
||||||
|
|
||||||
echo "Committing BRANCH POINT"
|
say "Committing BRANCH POINT"
|
||||||
svn commit -m "make left branch"
|
i=$(commit $i "make left branch")
|
||||||
svn cp trunk branches/right
|
svn cp trunk branches/right
|
||||||
|
|
||||||
echo "Committing other BRANCH POINT"
|
say "Committing other BRANCH POINT"
|
||||||
svn commit -m "make right branch"
|
i=$(commit $i "make right branch")
|
||||||
cd branches/left/
|
|
||||||
|
|
||||||
#$sm init
|
say "Committing LEFT UPDATE"
|
||||||
#svn commit -m "init svnmerge"
|
git cat-file blob 5873b67e:Makefile > branches/left/Makefile
|
||||||
|
i=$(commit $i "left update 1")
|
||||||
|
|
||||||
git cat-file blob 5873b67e:Makefile > Makefile
|
git cat-file blob 75118b13:Makefile > branches/right/Makefile
|
||||||
echo "Committing BRANCH UPDATE 1"
|
say "Committing RIGHT UPDATE"
|
||||||
svn commit -m "left update 1"
|
pre_right_update_1=$i
|
||||||
cd ../..
|
i=$(commit $i "right update 1")
|
||||||
|
|
||||||
cd trunk
|
say "Making more commits on LEFT"
|
||||||
git cat-file blob 75118b13:Makefile > Makefile
|
git cat-file blob ff5ebe39:Makefile > branches/left/Makefile
|
||||||
echo "Committing TRUNK UPDATE"
|
i=$(commit $i "left update 2")
|
||||||
svn commit -m "trunk update"
|
git cat-file blob b5039db6:Makefile > branches/left/Makefile
|
||||||
|
i=$(commit $i "left update 3")
|
||||||
|
|
||||||
cd ../branches/left
|
say "Making a LEFT SUB-BRANCH"
|
||||||
git cat-file blob ff5ebe39:Makefile > Makefile
|
svn cp branches/left branches/left-sub
|
||||||
echo "Committing BRANCH UPDATE 2"
|
sub_left_make=$i
|
||||||
svn commit -m "left update 2"
|
i=$(commit $i "make left sub-branch")
|
||||||
|
|
||||||
git cat-file blob b5039db6:Makefile > Makefile
|
say "Making a commit on LEFT SUB-BRANCH"
|
||||||
echo "Committing BRANCH UPDATE 3"
|
echo "crunch" > branches/left-sub/README
|
||||||
svn commit -m "left update 3"
|
svn add branches/left-sub/README
|
||||||
|
i=$(commit $i "left sub-branch update 1")
|
||||||
|
|
||||||
# merge to trunk
|
say "Merging LEFT to TRUNK"
|
||||||
|
|
||||||
cd ../..
|
|
||||||
svn update
|
svn update
|
||||||
cd trunk
|
cd trunk
|
||||||
|
|
||||||
svn merge ../branches/left --accept postpone
|
svn merge ../branches/left --accept postpone
|
||||||
|
|
||||||
git cat-file blob b51ad431:Makefile > Makefile
|
|
||||||
|
|
||||||
svn resolved Makefile
|
|
||||||
|
|
||||||
svn commit -m "Merge trunk 1"
|
|
||||||
|
|
||||||
# create commits on both branches
|
|
||||||
|
|
||||||
cd ../branches/left
|
|
||||||
git cat-file blob ff5ebe39:Makefile > Makefile
|
|
||||||
echo "Committing BRANCH UPDATE 4"
|
|
||||||
svn commit -m "left update 4"
|
|
||||||
|
|
||||||
cd ../right
|
|
||||||
git cat-file blob b5039db6:Makefile > Makefile
|
git cat-file blob b5039db6:Makefile > Makefile
|
||||||
echo "Committing other BRANCH UPDATE 1"
|
svn resolved Makefile
|
||||||
svn commit -m "right update 1"
|
i=$(commit $i "Merge left to trunk 1")
|
||||||
|
cd ..
|
||||||
|
|
||||||
# merge to trun again
|
say "Making more commits on LEFT and RIGHT"
|
||||||
|
echo "touche" > branches/left/zlonk
|
||||||
|
svn add branches/left/zlonk
|
||||||
|
i=$(commit $i "left update 4")
|
||||||
|
echo "thwacke" > branches/right/bang
|
||||||
|
svn add branches/right/bang
|
||||||
|
i=$(commit $i "right update 2")
|
||||||
|
|
||||||
cd ../..
|
say "Squash merge of RIGHT tip 2 commits onto TRUNK"
|
||||||
svn update
|
svn update
|
||||||
cd trunk
|
cd trunk
|
||||||
|
svn merge -r$pre_right_update_1:$i ../branches/right
|
||||||
|
i=$(commit $i "Cherry-pick right 2 commits to trunk")
|
||||||
|
cd ..
|
||||||
|
|
||||||
svn merge ../branches/left --accept postpone
|
say "Merging RIGHT to TRUNK"
|
||||||
|
svn update
|
||||||
|
cd trunk
|
||||||
|
svn merge ../branches/right --accept postpone
|
||||||
git cat-file blob b51ad431:Makefile > Makefile
|
git cat-file blob b51ad431:Makefile > Makefile
|
||||||
|
|
||||||
svn resolved Makefile
|
svn resolved Makefile
|
||||||
|
i=$(commit $i "Merge right to trunk 1")
|
||||||
|
cd ..
|
||||||
|
|
||||||
svn commit -m "Merge trunk 2"
|
say "Making more commits on RIGHT and TRUNK"
|
||||||
|
echo "whamm" > branches/right/urkkk
|
||||||
|
svn add branches/right/urkkk
|
||||||
|
i=$(commit $i "right update 3")
|
||||||
|
echo "pow" > trunk/vronk
|
||||||
|
svn add trunk/vronk
|
||||||
|
i=$(commit $i "trunk update 1")
|
||||||
|
|
||||||
|
say "Merging RIGHT to LEFT SUB-BRANCH"
|
||||||
|
svn update
|
||||||
|
cd branches/left-sub
|
||||||
|
svn merge ../right --accept postpone
|
||||||
|
git cat-file blob b51ad431:Makefile > Makefile
|
||||||
|
svn resolved Makefile
|
||||||
|
i=$(commit $i "Merge right to left sub-branch")
|
||||||
cd ../..
|
cd ../..
|
||||||
|
|
||||||
|
say "Making more commits on LEFT SUB-BRANCH and LEFT"
|
||||||
|
echo "zowie" > branches/left-sub/wham_eth
|
||||||
|
svn add branches/left-sub/wham_eth
|
||||||
|
pre_sub_left_update_2=$i
|
||||||
|
i=$(commit $i "left sub-branch update 2")
|
||||||
|
sub_left_update_2=$i
|
||||||
|
echo "eee_yow" > branches/left/glurpp
|
||||||
|
svn add branches/left/glurpp
|
||||||
|
i=$(commit $i "left update 5")
|
||||||
|
|
||||||
|
say "Cherry pick LEFT SUB-BRANCH commit to LEFT"
|
||||||
|
svn update
|
||||||
|
cd branches/left
|
||||||
|
svn merge -r$pre_sub_left_update_2:$sub_left_update_2 ../left-sub
|
||||||
|
i=$(commit $i "Cherry-pick left sub-branch commit to left")
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
say "Merging LEFT SUB-BRANCH back to LEFT"
|
||||||
|
svn update
|
||||||
|
cd branches/left
|
||||||
|
# it's only a merge because the previous merge cherry-picked the top commit
|
||||||
|
svn merge -r$sub_left_make:$sub_left_update_2 ../left-sub --accept postpone
|
||||||
|
i=$(commit $i "Merge left sub-branch to left")
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
say "Merging EVERYTHING to TRUNK"
|
||||||
|
svn update
|
||||||
|
cd trunk
|
||||||
|
svn merge ../branches/left --accept postpone
|
||||||
|
svn resolved bang
|
||||||
|
i=$(commit $i "Merge left to trunk 2")
|
||||||
|
# this merge, svn happily updates the mergeinfo, but there is actually
|
||||||
|
# nothing to merge. git-svn will not make a meaningless merge commit.
|
||||||
|
svn merge ../branches/right --accept postpone
|
||||||
|
i=$(commit $i "non-merge right to trunk 2")
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
cd ..
|
||||||
svnadmin dump foo.svn > svn-mergeinfo.dump
|
svnadmin dump foo.svn > svn-mergeinfo.dump
|
||||||
|
|
||||||
rm -rf foo foo.svn
|
rm -rf foo foo.svn
|
||||||
|
File diff suppressed because it is too large
Load Diff
40
t/t9152-svn-empty-dirs-after-gc.sh
Executable file
40
t/t9152-svn-empty-dirs-after-gc.sh
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2009 Robert Zeh
|
||||||
|
|
||||||
|
test_description='git svn creates empty directories, calls git gc, makes sure they are still empty'
|
||||||
|
. ./lib-git-svn.sh
|
||||||
|
|
||||||
|
test_expect_success 'initialize repo' '
|
||||||
|
for i in a b c d d/e d/e/f "weird file name"
|
||||||
|
do
|
||||||
|
svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
|
||||||
|
done
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
|
||||||
|
|
||||||
|
test_expect_success 'git svn gc runs' '
|
||||||
|
(
|
||||||
|
cd cloned &&
|
||||||
|
git svn gc
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git svn mkdirs recreates empty directories after git svn gc' '
|
||||||
|
(
|
||||||
|
cd cloned &&
|
||||||
|
rm -r * &&
|
||||||
|
git svn mkdirs &&
|
||||||
|
for i in a b c d d/e d/e/f "weird file name"
|
||||||
|
do
|
||||||
|
if ! test -d "$i"
|
||||||
|
then
|
||||||
|
echo >&2 "$i does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Reference in New Issue
Block a user