Skip to content

Commit fe142b3

Browse files
robinrosenbergJunio C Hamano
authored andcommitted
Rework cvsexportcommit to handle binary files for all cases.
Also adds test cases for adding removing and deleting binary and text files plus two tests for the checks on binary files. Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 3d12d0c commit fe142b3

File tree

4 files changed

+195
-20
lines changed

4 files changed

+195
-20
lines changed

git-cvsexportcommit.perl

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/perl -w
22

33
# Known limitations:
4-
# - cannot add or remove binary files
54
# - does not propagate permissions
65
# - tells "ready for commit" even when things could not be completed
7-
# (eg addition of a binary file)
6+
# (not sure this is true anymore, more testing is needed)
7+
# - does not handle whitespace in pathnames at all.
88

99
use strict;
1010
use Getopt::Std;
@@ -68,9 +68,9 @@
6868
if ($stage eq 'headers') {
6969
if ($line =~ m/^parent (\w{40})$/) { # found a parent
7070
push @parents, $1;
71-
} elsif ($line =~ m/^author (.+) \d+ \+\d+$/) {
71+
} elsif ($line =~ m/^author (.+) \d+ [-+]\d+$/) {
7272
$author = $1;
73-
} elsif ($line =~ m/^committer (.+) \d+ \+\d+$/) {
73+
} elsif ($line =~ m/^committer (.+) \d+ [-+]\d+$/) {
7474
$committer = $1;
7575
}
7676
} else {
@@ -139,6 +139,17 @@
139139
push @dfiles, $fields[5];
140140
}
141141
}
142+
my (@binfiles, @abfiles, @dbfiles, @bfiles, @mbfiles);
143+
@binfiles = grep m/^Binary files/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit);
144+
map { chomp } @binfiles;
145+
@abfiles = grep s/^Binary files \/dev\/null and b\/(.*) differ$/$1/, @binfiles;
146+
@dbfiles = grep s/^Binary files a\/(.*) and \/dev\/null differ$/$1/, @binfiles;
147+
@mbfiles = grep s/^Binary files a\/(.*) and b\/(.*) differ$/$1/, @binfiles;
148+
push @bfiles, @abfiles;
149+
push @bfiles, @dbfiles;
150+
push @bfiles, @mbfiles;
151+
push @mfiles, @mbfiles;
152+
142153
$opt_v && print "The commit affects:\n ";
143154
$opt_v && print join ("\n ", @afiles,@mfiles,@dfiles) . "\n\n";
144155
undef @files; # don't need it anymore
@@ -153,6 +164,10 @@
153164
}
154165
foreach my $f (@afiles) {
155166
# This should return only one value
167+
if ($f =~ m,(.*)/[^/]*$,) {
168+
my $p = $1;
169+
next if (grep { $_ eq $p } @dirs);
170+
}
156171
my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f));
157172
if (@status > 1) { warn 'Strange! cvs status returned more than one line?'};
158173
if (-d dirname $f and $status[0] !~ m/Status: Unknown$/
@@ -162,6 +177,7 @@
162177
warn "Status was: $status[0]\n";
163178
}
164179
}
180+
165181
foreach my $f (@mfiles, @dfiles) {
166182
# TODO:we need to handle removed in cvs
167183
my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f));
@@ -200,24 +216,31 @@
200216

201217
print "'Patching' binary files\n";
202218

203-
my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit));
204-
@bfiles = map { chomp } @bfiles;
205219
foreach my $f (@bfiles) {
206220
# check that the file in cvs matches the "old" file
207221
# extract the file to $tmpdir and compare with cmp
208-
my $tree = safe_pipe_capture('git-rev-parse', "$parent^{tree}");
209-
chomp $tree;
210-
my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
211-
chomp $blob;
212-
`git-cat-file blob $blob > $tmpdir/blob`;
213-
if (system('cmp', '-s', $f, "$tmpdir/blob")) {
214-
warn "Binary file $f in CVS does not match parent.\n";
215-
$dirty = 1;
216-
next;
222+
if (not(grep { $_ eq $f } @afiles)) {
223+
my $tree = safe_pipe_capture('git-rev-parse', "$parent^{tree}");
224+
chomp $tree;
225+
my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
226+
chomp $blob;
227+
`git-cat-file blob $blob > $tmpdir/blob`;
228+
if (system('cmp', '-s', $f, "$tmpdir/blob")) {
229+
warn "Binary file $f in CVS does not match parent.\n";
230+
if (not $opt_f) {
231+
$dirty = 1;
232+
next;
233+
}
234+
}
235+
}
236+
if (not(grep { $_ eq $f } @dfiles)) {
237+
my $tree = safe_pipe_capture('git-rev-parse', "$commit^{tree}");
238+
chomp $tree;
239+
my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
240+
chomp $blob;
241+
# replace with the new file
242+
`git-cat-file blob $blob > $f`;
217243
}
218-
219-
# replace with the new file
220-
`git-cat-file blob $blob > $f`;
221244

222245
# TODO: something smart with file modes
223246

@@ -231,7 +254,10 @@
231254
my $fuzz = $opt_p ? 0 : 2;
232255

233256
print "Patching non-binary files\n";
234-
print `(git-diff-tree -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`;
257+
258+
if (scalar(@afiles)+scalar(@dfiles)+scalar(@mfiles) != scalar(@bfiles)) {
259+
print `(git-diff-tree -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`;
260+
}
235261

236262
my $dirtypatch = 0;
237263
if (($? >> 8) == 2) {
@@ -242,7 +268,11 @@
242268
}
243269

244270
foreach my $f (@afiles) {
245-
system('cvs', 'add', $f);
271+
if (grep { $_ eq $f } @bfiles) {
272+
system('cvs', 'add','-kb',$f);
273+
} else {
274+
system('cvs', 'add', $f);
275+
}
246276
if ($?) {
247277
$dirty = 1;
248278
warn "Failed to cvs add $f -- you may need to do it manually";

t/t9200-git-cvsexportcommit.sh

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/bin/bash
2+
#
3+
# Copyright (c) Robin Rosenberg
4+
#
5+
test_description='CVS export comit. '
6+
7+
. ./test-lib.sh
8+
9+
cvs >/dev/null 2>&1
10+
if test $? -ne 1
11+
then
12+
test_expect_success 'skipping git-cvsexportcommit tests, cvs not found' :
13+
test_done
14+
exit
15+
fi
16+
17+
export CVSROOT=$(pwd)/cvsroot
18+
export CVSWORK=$(pwd)/cvswork
19+
rm -rf "$CVSROOT" "$CVSWORK"
20+
mkdir "$CVSROOT" &&
21+
cvs init &&
22+
cvs -Q co -d "$CVSWORK" . &&
23+
export GIT_DIR=$(pwd)/.git &&
24+
echo >empty &&
25+
git add empty &&
26+
git commit -a -m "Initial" 2>/dev/null ||
27+
exit 1
28+
29+
test_expect_success \
30+
'New file' \
31+
'mkdir A B C D E F &&
32+
echo hello1 >A/newfile1.txt &&
33+
echo hello2 >B/newfile2.txt &&
34+
cp ../test9200a.png C/newfile3.png &&
35+
cp ../test9200a.png D/newfile4.png &&
36+
git add A/newfile1.txt &&
37+
git add B/newfile2.txt &&
38+
git add C/newfile3.png &&
39+
git add D/newfile4.png &&
40+
git commit -a -m "Test: New file" &&
41+
id=$(git rev-list --max-count=1 HEAD) &&
42+
(cd "$CVSWORK" &&
43+
git cvsexportcommit -c $id &&
44+
test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.1/" &&
45+
test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "newfile2.txt/1.1/" &&
46+
test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "newfile3.png/1.1/-kb" &&
47+
test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.1/-kb" &&
48+
diff A/newfile1.txt ../A/newfile1.txt &&
49+
diff B/newfile2.txt ../B/newfile2.txt &&
50+
diff C/newfile3.png ../C/newfile3.png &&
51+
diff D/newfile4.png ../D/newfile4.png
52+
)'
53+
54+
test_expect_success \
55+
'Remove two files, add two and update two' \
56+
'echo Hello1 >>A/newfile1.txt &&
57+
rm -f B/newfile2.txt &&
58+
rm -f C/newfile3.png &&
59+
echo Hello5 >E/newfile5.txt &&
60+
cp ../test9200b.png D/newfile4.png &&
61+
cp ../test9200a.png F/newfile6.png &&
62+
git add E/newfile5.txt &&
63+
git add F/newfile6.png &&
64+
git commit -a -m "Test: Remove, add and update" &&
65+
id=$(git rev-list --max-count=1 HEAD) &&
66+
(cd "$CVSWORK" &&
67+
git cvsexportcommit -c $id &&
68+
test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
69+
test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
70+
test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
71+
test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.2/-kb" &&
72+
test "$(echo $(sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
73+
test "$(echo $(sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
74+
diff A/newfile1.txt ../A/newfile1.txt &&
75+
diff D/newfile4.png ../D/newfile4.png &&
76+
diff E/newfile5.txt ../E/newfile5.txt &&
77+
diff F/newfile6.png ../F/newfile6.png
78+
)'
79+
80+
# Should fail (but only on the git-cvsexportcommit stage)
81+
test_expect_success \
82+
'Fail to change binary more than one generation old' \
83+
'cat F/newfile6.png >>D/newfile4.png &&
84+
git commit -a -m "generatiion 1" &&
85+
cat F/newfile6.png >>D/newfile4.png &&
86+
git commit -a -m "generation 2" &&
87+
id=$(git rev-list --max-count=1 HEAD) &&
88+
(cd "$CVSWORK" &&
89+
! git cvsexportcommit -c $id
90+
)'
91+
92+
# Should fail, but only on the git-cvsexportcommit stage
93+
test_expect_success \
94+
'Fail to remove binary file more than one generation old' \
95+
'git reset --hard HEAD^ &&
96+
cat F/newfile6.png >>D/newfile4.png &&
97+
git commit -a -m "generation 2 (again)" &&
98+
rm -f D/newfile4.png &&
99+
git commit -a -m "generation 3" &&
100+
id=$(git rev-list --max-count=1 HEAD) &&
101+
(cd "$CVSWORK" &&
102+
! git cvsexportcommit -c $id
103+
)'
104+
105+
# We reuse the state from two tests back here
106+
107+
# This test is here because a patch for only binary files will
108+
# fail with gnu patch, so cvsexportcommit must handle that.
109+
test_expect_success \
110+
'Remove only binary files' \
111+
'git reset --hard HEAD^^^ &&
112+
rm -f D/newfile4.png &&
113+
git commit -a -m "test: remove only a binary file" &&
114+
id=$(git rev-list --max-count=1 HEAD) &&
115+
(cd "$CVSWORK" &&
116+
git cvsexportcommit -c $id &&
117+
test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
118+
test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
119+
test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
120+
test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
121+
test "$(echo $(sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
122+
test "$(echo $(sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
123+
diff A/newfile1.txt ../A/newfile1.txt &&
124+
diff E/newfile5.txt ../E/newfile5.txt &&
125+
diff F/newfile6.png ../F/newfile6.png
126+
)'
127+
128+
test_expect_success \
129+
'Remove only a text file' \
130+
'rm -f A/newfile1.txt &&
131+
git commit -a -m "test: remove only a binary file" &&
132+
id=$(git rev-list --max-count=1 HEAD) &&
133+
(cd "$CVSWORK" &&
134+
git cvsexportcommit -c $id &&
135+
test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
136+
test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
137+
test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
138+
test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
139+
test "$(echo $(sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
140+
test "$(echo $(sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
141+
diff E/newfile5.txt ../E/newfile5.txt &&
142+
diff F/newfile6.png ../F/newfile6.png
143+
)'
144+
145+
test_done

t/test9200a.png

5.53 KB
Loading

t/test9200b.png

275 Bytes
Loading

0 commit comments

Comments
 (0)