Skip to content

Commit 733a65a

Browse files
Eric Wonggitster
authored andcommitted
git-svn: allow dcommit to retain local merge information
dcommit will still rewrite the HEAD commit and the history of the first parents of each HEAD~1, HEAD~2, HEAD~3 as it always has. However, any merge parents (HEAD^2, HEAD^^2, HEAD~2^2) will now be preserved when the new HEAD and HEAD~[0-9]+ commits are rewritten to SVN with dcommit. Commits written to SVN will still not have any merge information besides anything in the commit message. Thanks to Joakim Tjernlund, Junio C Hamano and Steven Grimm for explanations, feedback, examples and test case. Signed-off-by: Eric Wong <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 38570a4 commit 733a65a

File tree

2 files changed

+152
-9
lines changed

2 files changed

+152
-9
lines changed

git-svn.perl

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -372,16 +372,9 @@ sub cmd_dcommit {
372372
die "Unable to determine upstream SVN information from ",
373373
"$head history\n";
374374
}
375-
my $c = $refs[-1];
376375
my $last_rev;
377-
foreach my $d (@refs) {
378-
if (!verify_ref("$d~1")) {
379-
fatal "Commit $d\n",
380-
"has no parent commit, and therefore ",
381-
"nothing to diff against.\n",
382-
"You should be working from a repository ",
383-
"originally created by git-svn\n";
384-
}
376+
my ($linear_refs, $parents) = linearize_history($gs, \@refs);
377+
foreach my $d (@$linear_refs) {
385378
unless (defined $last_rev) {
386379
(undef, $last_rev, undef) = cmt_metadata("$d~1");
387380
unless (defined $last_rev) {
@@ -403,6 +396,9 @@ sub cmd_dcommit {
403396
svn_path => '');
404397
if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) {
405398
print "No changes\n$d~1 == $d\n";
399+
} elsif ($parents->{$d} && @{$parents->{$d}}) {
400+
$gs->{inject_parents_dcommit}->{$last_rev} =
401+
$parents->{$d};
406402
}
407403
}
408404
}
@@ -821,6 +817,59 @@ sub working_head_info {
821817
(undef, undef, undef, undef);
822818
}
823819

820+
sub read_commit_parents {
821+
my ($parents, $c) = @_;
822+
my ($fh, $ctx) = command_output_pipe(qw/cat-file commit/, $c);
823+
while (<$fh>) {
824+
chomp;
825+
last if '';
826+
/^parent ($sha1)/ or next;
827+
push @{$parents->{$c}}, $1;
828+
}
829+
close $fh; # break the pipe
830+
}
831+
832+
sub linearize_history {
833+
my ($gs, $refs) = @_;
834+
my %parents;
835+
foreach my $c (@$refs) {
836+
read_commit_parents(\%parents, $c);
837+
}
838+
839+
my @linear_refs;
840+
my %skip = ();
841+
my $last_svn_commit = $gs->last_commit;
842+
foreach my $c (reverse @$refs) {
843+
next if $c eq $last_svn_commit;
844+
last if $skip{$c};
845+
846+
unshift @linear_refs, $c;
847+
$skip{$c} = 1;
848+
849+
# we only want the first parent to diff against for linear
850+
# history, we save the rest to inject when we finalize the
851+
# svn commit
852+
my $fp_a = verify_ref("$c~1");
853+
my $fp_b = shift @{$parents{$c}} if $parents{$c};
854+
if (!$fp_a || !$fp_b) {
855+
die "Commit $c\n",
856+
"has no parent commit, and therefore ",
857+
"nothing to diff against.\n",
858+
"You should be working from a repository ",
859+
"originally created by git-svn\n";
860+
}
861+
if ($fp_a ne $fp_b) {
862+
die "$c~1 = $fp_a, however parsing commit $c ",
863+
"revealed that:\n$c~1 = $fp_b\nBUG!\n";
864+
}
865+
866+
foreach my $p (@{$parents{$c}}) {
867+
$skip{$p} = 1;
868+
}
869+
}
870+
(\@linear_refs, \%parents);
871+
}
872+
824873
package Git::SVN;
825874
use strict;
826875
use warnings;
@@ -1541,6 +1590,11 @@ sub get_commit_parents {
15411590
if (my $cur = ::verify_ref($self->refname.'^0')) {
15421591
push @tmp, $cur;
15431592
}
1593+
if (my $ipd = $self->{inject_parents_dcommit}) {
1594+
if (my $commit = delete $ipd->{$log_entry->{revision}}) {
1595+
push @tmp, @$commit;
1596+
}
1597+
}
15441598
push @tmp, $_ foreach (@{$log_entry->{parents}}, @tmp);
15451599
while (my $p = shift @tmp) {
15461600
next if $seen{$p};

t/t9114-git-svn-dcommit-merge.sh

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2007 Eric Wong
4+
# Based on a script by Joakim Tjernlund <[email protected]>
5+
6+
test_description='git-svn dcommit handles merges'
7+
8+
. ./lib-git-svn.sh
9+
10+
big_text_block () {
11+
cat << EOF
12+
#
13+
# (C) Copyright 2000 - 2005
14+
# Wolfgang Denk, DENX Software Engineering, [email protected].
15+
#
16+
# See file CREDITS for list of people who contributed to this
17+
# project.
18+
#
19+
# This program is free software; you can redistribute it and/or
20+
# modify it under the terms of the GNU General Public License as
21+
# published by the Free Software Foundation; either version 2 of
22+
# the License, or (at your option) any later version.
23+
#
24+
# This program is distributed in the hope that it will be useful,
25+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27+
# GNU General Public License for more details.
28+
#
29+
# You should have received a copy of the GNU General Public License
30+
# along with this program; if not, write to the Free Software
31+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
32+
# MA 02111-1307 USA
33+
#
34+
EOF
35+
}
36+
37+
test_expect_success 'setup svn repository' "
38+
svn co $svnrepo mysvnwork &&
39+
mkdir -p mysvnwork/trunk &&
40+
cd mysvnwork &&
41+
big_text_block >> trunk/README &&
42+
svn add trunk &&
43+
svn ci -m 'first commit' trunk &&
44+
cd ..
45+
"
46+
47+
test_expect_success 'setup git mirror and merge' "
48+
git svn init $svnrepo -t tags -T trunk -b branches &&
49+
git svn fetch &&
50+
git checkout --track -b svn remotes/trunk &&
51+
git checkout -b merge &&
52+
echo new file > new_file &&
53+
git add new_file &&
54+
git commit -a -m 'New file' &&
55+
echo hello >> README &&
56+
git commit -a -m 'hello' &&
57+
echo add some stuff >> new_file &&
58+
git commit -a -m 'add some stuff' &&
59+
git checkout svn &&
60+
mv -f README tmp &&
61+
echo friend > README &&
62+
cat tmp >> README &&
63+
git commit -a -m 'friend' &&
64+
git pull . merge
65+
"
66+
67+
test_debug 'gitk --all & sleep 1'
68+
69+
test_expect_success 'verify pre-merge ancestry' "
70+
test x\`git rev-parse --verify refs/heads/svn^2\` = \
71+
x\`git rev-parse --verify refs/heads/merge\` &&
72+
git cat-file commit refs/heads/svn^ | grep '^friend$'
73+
"
74+
75+
test_expect_success 'git svn dcommit merges' "
76+
git svn dcommit
77+
"
78+
79+
test_debug 'gitk --all & sleep 1'
80+
81+
test_expect_success 'verify post-merge ancestry' "
82+
test x\`git rev-parse --verify refs/heads/svn\` = \
83+
x\`git rev-parse --verify refs/remotes/trunk \` &&
84+
test x\`git rev-parse --verify refs/heads/svn^2\` = \
85+
x\`git rev-parse --verify refs/heads/merge\` &&
86+
git cat-file commit refs/heads/svn^ | grep '^friend$'
87+
"
88+
89+
test_done

0 commit comments

Comments
 (0)