From 1d49f0d1a1b7b2941e56cf8384a7a4267e98150c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kiedrowicz?= Date: Sat, 11 Apr 2009 17:26:24 +0200 Subject: tests: test applying criss-cross rename patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally reported by Linus in $gmane/116198 Signed-off-by: Michał Kiedrowicz Signed-off-by: Junio C Hamano --- t/t4130-apply-criss-cross-rename.sh | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 t/t4130-apply-criss-cross-rename.sh diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh new file mode 100755 index 0000000000..08c5f38b01 --- /dev/null +++ b/t/t4130-apply-criss-cross-rename.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +test_description='git apply handling criss-cross rename patch.' +. ./test-lib.sh + +create_file() { + cnt=0 + while test $cnt -le 100 + do + cnt=$(($cnt + 1)) + echo "$2" >> "$1" + done +} + +test_expect_success 'setup' ' + create_file file1 "File1 contents" && + create_file file2 "File2 contents" && + git add file1 file2 && + git commit -m 1 +' + +test_expect_success 'criss-cross rename' ' + mv file1 tmp && + mv file2 file1 && + mv tmp file2 +' + +test_expect_success 'diff -M -B' ' + git diff -M -B > diff && + git reset --hard + +' + +test_expect_failure 'apply' ' + git apply diff +' + +test_done -- cgit v1.2.3 From e8141fcf54b4e8c2c12300da1ad1bc3573a4e204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kiedrowicz?= Date: Sat, 11 Apr 2009 21:31:00 +0200 Subject: builtin-apply: keep information about files to be deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example correct diff generated by `diff -M -B' might look like this: diff --git a/file1 b/file2 similarity index 100% rename from file1 rename to file2 diff --git a/file2 b/file1 similarity index 100% rename from file2 rename to file1 Information about removing `file2' comes after information about creation of new `file2' (renamed from `file1'). Existing implementation isn't able to apply such patch, because it has to know in advance which files will be removed. This patch populates fn_table with information about removal of files before calling check_patch() for each patch to be applied. Signed-off-by: Michał Kiedrowicz Signed-off-by: Junio C Hamano --- builtin-apply.c | 53 ++++++++++++++++++++++++++++++++----- t/t4130-apply-criss-cross-rename.sh | 2 +- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index a664338643..c6feaf5ca8 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2271,6 +2271,25 @@ static struct patch *in_fn_table(const char *name) return NULL; } +/* + * item->util in the filename table records the status of the path. + * Usually it points at a patch (whose result records the contents + * of it after applying it), but it could be PATH_WAS_DELETED for a + * path that a previously applied patch has already removed. + */ + #define PATH_TO_BE_DELETED ((struct patch *) -2) +#define PATH_WAS_DELETED ((struct patch *) -1) + +static int to_be_deleted(struct patch *patch) +{ + return patch == PATH_TO_BE_DELETED; +} + +static int was_deleted(struct patch *patch) +{ + return patch == PATH_WAS_DELETED; +} + static void add_to_fn_table(struct patch *patch) { struct string_list_item *item; @@ -2291,7 +2310,22 @@ static void add_to_fn_table(struct patch *patch) */ if ((patch->new_name == NULL) || (patch->is_rename)) { item = string_list_insert(patch->old_name, &fn_table); - item->util = (struct patch *) -1; + item->util = PATH_WAS_DELETED; + } +} + +static void prepare_fn_table(struct patch *patch) +{ + /* + * store information about incoming file deletion + */ + while (patch) { + if ((patch->new_name == NULL) || (patch->is_rename)) { + struct string_list_item *item; + item = string_list_insert(patch->old_name, &fn_table); + item->util = PATH_TO_BE_DELETED; + } + patch = patch->next; } } @@ -2304,8 +2338,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * struct patch *tpatch; if (!(patch->is_copy || patch->is_rename) && - ((tpatch = in_fn_table(patch->old_name)) != NULL)) { - if (tpatch == (struct patch *) -1) { + (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) { + if (was_deleted(tpatch)) { return error("patch %s has been renamed/deleted", patch->old_name); } @@ -2399,10 +2433,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s assert(patch->is_new <= 0); if (!(patch->is_copy || patch->is_rename) && - (tpatch = in_fn_table(old_name)) != NULL) { - if (tpatch == (struct patch *) -1) { + (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) { + if (was_deleted(tpatch)) return error("%s: has been deleted/renamed", old_name); - } st_mode = tpatch->new_mode; } else if (!cached) { stat_ret = lstat(old_name, st); @@ -2410,6 +2443,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return error("%s: %s", old_name, strerror(errno)); } + if (to_be_deleted(tpatch)) + tpatch = NULL; + if (check_index && !tpatch) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { @@ -2471,6 +2507,7 @@ static int check_patch(struct patch *patch) const char *new_name = patch->new_name; const char *name = old_name ? old_name : new_name; struct cache_entry *ce = NULL; + struct patch *tpatch; int ok_if_exists; int status; @@ -2481,7 +2518,8 @@ static int check_patch(struct patch *patch) return status; old_name = patch->old_name; - if (in_fn_table(new_name) == (struct patch *) -1) + if ((tpatch = in_fn_table(new_name)) && + (was_deleted(tpatch) || to_be_deleted(tpatch))) /* * A type-change diff is always split into a patch to * delete old, immediately followed by a patch to @@ -2533,6 +2571,7 @@ static int check_patch_list(struct patch *patch) { int err = 0; + prepare_fn_table(patch); while (patch) { if (apply_verbosely) say_patch_name(stderr, diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh index 08c5f38b01..8623dbebab 100755 --- a/t/t4130-apply-criss-cross-rename.sh +++ b/t/t4130-apply-criss-cross-rename.sh @@ -31,7 +31,7 @@ test_expect_success 'diff -M -B' ' ' -test_expect_failure 'apply' ' +test_expect_success 'apply' ' git apply diff ' -- cgit v1.2.3 From f0583867e746985e9d62f57d5ba6ce27b2603447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kiedrowicz?= Date: Sat, 18 Apr 2009 23:03:57 +0200 Subject: tests: make test-apply-criss-cross-rename more robust I realized that this test does check if git-apply succeeds, but doesn't tell if it applies patches correctly. So I added test_cmp to check it. I also added a test which checks swapping three files. Signed-off-by: Junio C Hamano --- t/t4130-apply-criss-cross-rename.sh | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh index 8623dbebab..7cfa2d6287 100755 --- a/t/t4130-apply-criss-cross-rename.sh +++ b/t/t4130-apply-criss-cross-rename.sh @@ -15,14 +15,17 @@ create_file() { test_expect_success 'setup' ' create_file file1 "File1 contents" && create_file file2 "File2 contents" && - git add file1 file2 && + create_file file3 "File3 contents" && + git add file1 file2 file3 && git commit -m 1 ' test_expect_success 'criss-cross rename' ' mv file1 tmp && mv file2 file1 && - mv tmp file2 + mv tmp file2 && + cp file1 file1-swapped && + cp file2 file2-swapped ' test_expect_success 'diff -M -B' ' @@ -32,7 +35,32 @@ test_expect_success 'diff -M -B' ' ' test_expect_success 'apply' ' - git apply diff + git apply diff && + test_cmp file1 file1-swapped && + test_cmp file2 file2-swapped +' + +test_expect_success 'criss-cross rename' ' + git reset --hard && + mv file1 tmp && + mv file2 file1 && + mv file3 file2 + mv tmp file3 && + cp file1 file1-swapped && + cp file2 file2-swapped && + cp file3 file3-swapped +' + +test_expect_success 'diff -M -B' ' + git diff -M -B > diff && + git reset --hard +' + +test_expect_success 'apply' ' + git apply diff && + test_cmp file1 file1-swapped && + test_cmp file2 file2-swapped && + test_cmp file3 file3-swapped ' test_done -- cgit v1.2.3