Skip to content

Commit c2eae0a

Browse files
committed
Merge branch 'mk/maint-apply-swap' into maint
* mk/maint-apply-swap: tests: make test-apply-criss-cross-rename more robust builtin-apply: keep information about files to be deleted tests: test applying criss-cross rename patch
2 parents 00473fd + f058386 commit c2eae0a

File tree

2 files changed

+112
-7
lines changed

2 files changed

+112
-7
lines changed

builtin-apply.c

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,6 +2271,25 @@ static struct patch *in_fn_table(const char *name)
22712271
return NULL;
22722272
}
22732273

2274+
/*
2275+
* item->util in the filename table records the status of the path.
2276+
* Usually it points at a patch (whose result records the contents
2277+
* of it after applying it), but it could be PATH_WAS_DELETED for a
2278+
* path that a previously applied patch has already removed.
2279+
*/
2280+
#define PATH_TO_BE_DELETED ((struct patch *) -2)
2281+
#define PATH_WAS_DELETED ((struct patch *) -1)
2282+
2283+
static int to_be_deleted(struct patch *patch)
2284+
{
2285+
return patch == PATH_TO_BE_DELETED;
2286+
}
2287+
2288+
static int was_deleted(struct patch *patch)
2289+
{
2290+
return patch == PATH_WAS_DELETED;
2291+
}
2292+
22742293
static void add_to_fn_table(struct patch *patch)
22752294
{
22762295
struct string_list_item *item;
@@ -2291,7 +2310,22 @@ static void add_to_fn_table(struct patch *patch)
22912310
*/
22922311
if ((patch->new_name == NULL) || (patch->is_rename)) {
22932312
item = string_list_insert(patch->old_name, &fn_table);
2294-
item->util = (struct patch *) -1;
2313+
item->util = PATH_WAS_DELETED;
2314+
}
2315+
}
2316+
2317+
static void prepare_fn_table(struct patch *patch)
2318+
{
2319+
/*
2320+
* store information about incoming file deletion
2321+
*/
2322+
while (patch) {
2323+
if ((patch->new_name == NULL) || (patch->is_rename)) {
2324+
struct string_list_item *item;
2325+
item = string_list_insert(patch->old_name, &fn_table);
2326+
item->util = PATH_TO_BE_DELETED;
2327+
}
2328+
patch = patch->next;
22952329
}
22962330
}
22972331

@@ -2304,8 +2338,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
23042338
struct patch *tpatch;
23052339

23062340
if (!(patch->is_copy || patch->is_rename) &&
2307-
((tpatch = in_fn_table(patch->old_name)) != NULL)) {
2308-
if (tpatch == (struct patch *) -1) {
2341+
(tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
2342+
if (was_deleted(tpatch)) {
23092343
return error("patch %s has been renamed/deleted",
23102344
patch->old_name);
23112345
}
@@ -2399,17 +2433,19 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
23992433
assert(patch->is_new <= 0);
24002434

24012435
if (!(patch->is_copy || patch->is_rename) &&
2402-
(tpatch = in_fn_table(old_name)) != NULL) {
2403-
if (tpatch == (struct patch *) -1) {
2436+
(tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
2437+
if (was_deleted(tpatch))
24042438
return error("%s: has been deleted/renamed", old_name);
2405-
}
24062439
st_mode = tpatch->new_mode;
24072440
} else if (!cached) {
24082441
stat_ret = lstat(old_name, st);
24092442
if (stat_ret && errno != ENOENT)
24102443
return error("%s: %s", old_name, strerror(errno));
24112444
}
24122445

2446+
if (to_be_deleted(tpatch))
2447+
tpatch = NULL;
2448+
24132449
if (check_index && !tpatch) {
24142450
int pos = cache_name_pos(old_name, strlen(old_name));
24152451
if (pos < 0) {
@@ -2471,6 +2507,7 @@ static int check_patch(struct patch *patch)
24712507
const char *new_name = patch->new_name;
24722508
const char *name = old_name ? old_name : new_name;
24732509
struct cache_entry *ce = NULL;
2510+
struct patch *tpatch;
24742511
int ok_if_exists;
24752512
int status;
24762513

@@ -2481,7 +2518,8 @@ static int check_patch(struct patch *patch)
24812518
return status;
24822519
old_name = patch->old_name;
24832520

2484-
if (in_fn_table(new_name) == (struct patch *) -1)
2521+
if ((tpatch = in_fn_table(new_name)) &&
2522+
(was_deleted(tpatch) || to_be_deleted(tpatch)))
24852523
/*
24862524
* A type-change diff is always split into a patch to
24872525
* delete old, immediately followed by a patch to
@@ -2533,6 +2571,7 @@ static int check_patch_list(struct patch *patch)
25332571
{
25342572
int err = 0;
25352573

2574+
prepare_fn_table(patch);
25362575
while (patch) {
25372576
if (apply_verbosely)
25382577
say_patch_name(stderr,

t/t4130-apply-criss-cross-rename.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/sh
2+
3+
test_description='git apply handling criss-cross rename patch.'
4+
. ./test-lib.sh
5+
6+
create_file() {
7+
cnt=0
8+
while test $cnt -le 100
9+
do
10+
cnt=$(($cnt + 1))
11+
echo "$2" >> "$1"
12+
done
13+
}
14+
15+
test_expect_success 'setup' '
16+
create_file file1 "File1 contents" &&
17+
create_file file2 "File2 contents" &&
18+
create_file file3 "File3 contents" &&
19+
git add file1 file2 file3 &&
20+
git commit -m 1
21+
'
22+
23+
test_expect_success 'criss-cross rename' '
24+
mv file1 tmp &&
25+
mv file2 file1 &&
26+
mv tmp file2 &&
27+
cp file1 file1-swapped &&
28+
cp file2 file2-swapped
29+
'
30+
31+
test_expect_success 'diff -M -B' '
32+
git diff -M -B > diff &&
33+
git reset --hard
34+
35+
'
36+
37+
test_expect_success 'apply' '
38+
git apply diff &&
39+
test_cmp file1 file1-swapped &&
40+
test_cmp file2 file2-swapped
41+
'
42+
43+
test_expect_success 'criss-cross rename' '
44+
git reset --hard &&
45+
mv file1 tmp &&
46+
mv file2 file1 &&
47+
mv file3 file2
48+
mv tmp file3 &&
49+
cp file1 file1-swapped &&
50+
cp file2 file2-swapped &&
51+
cp file3 file3-swapped
52+
'
53+
54+
test_expect_success 'diff -M -B' '
55+
git diff -M -B > diff &&
56+
git reset --hard
57+
'
58+
59+
test_expect_success 'apply' '
60+
git apply diff &&
61+
test_cmp file1 file1-swapped &&
62+
test_cmp file2 file2-swapped &&
63+
test_cmp file3 file3-swapped
64+
'
65+
66+
test_done

0 commit comments

Comments
 (0)