diff --git a/builtin/apply.c b/builtin/apply.c index 47622bee69..980bb34522 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -34,6 +34,20 @@ enum ws_ignore { ignore_ws_change }; +/* + * We need to keep track of how symlinks in the preimage are + * manipulated by the patches. A patch to add a/b/c where a/b + * is a symlink should not be allowed to affect the directory + * the symlink points at, but if the same patch removes a/b, + * it is perfectly fine, as the patch removes a/b to make room + * to create a directory a/b so that a/b/c can be created. + * + * See also "struct string_list symlink_changes" in "struct + * apply_state". + */ +#define SYMLINK_GOES_AWAY 01 +#define SYMLINK_IN_RESULT 02 + struct apply_state { const char *prefix; int prefix_length; @@ -75,6 +89,7 @@ struct apply_state { /* Various "current state" */ int linenr; /* current line number */ + struct string_list symlink_changes; /* we have to track symlinks */ /* * For "diff-stat" like behaviour, we keep track of the biggest change @@ -3702,52 +3717,42 @@ static int check_to_create(struct apply_state *state, return 0; } -/* - * We need to keep track of how symlinks in the preimage are - * manipulated by the patches. A patch to add a/b/c where a/b - * is a symlink should not be allowed to affect the directory - * the symlink points at, but if the same patch removes a/b, - * it is perfectly fine, as the patch removes a/b to make room - * to create a directory a/b so that a/b/c can be created. - */ -static struct string_list symlink_changes; -#define SYMLINK_GOES_AWAY 01 -#define SYMLINK_IN_RESULT 02 - -static uintptr_t register_symlink_changes(const char *path, uintptr_t what) +static uintptr_t register_symlink_changes(struct apply_state *state, + const char *path, + uintptr_t what) { struct string_list_item *ent; - ent = string_list_lookup(&symlink_changes, path); + ent = string_list_lookup(&state->symlink_changes, path); if (!ent) { - ent = string_list_insert(&symlink_changes, path); + ent = string_list_insert(&state->symlink_changes, path); ent->util = (void *)0; } ent->util = (void *)(what | ((uintptr_t)ent->util)); return (uintptr_t)ent->util; } -static uintptr_t check_symlink_changes(const char *path) +static uintptr_t check_symlink_changes(struct apply_state *state, const char *path) { struct string_list_item *ent; - ent = string_list_lookup(&symlink_changes, path); + ent = string_list_lookup(&state->symlink_changes, path); if (!ent) return 0; return (uintptr_t)ent->util; } -static void prepare_symlink_changes(struct patch *patch) +static void prepare_symlink_changes(struct apply_state *state, struct patch *patch) { for ( ; patch; patch = patch->next) { if ((patch->old_name && S_ISLNK(patch->old_mode)) && (patch->is_rename || patch->is_delete)) /* the symlink at patch->old_name is removed */ - register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY); + register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY); if (patch->new_name && S_ISLNK(patch->new_mode)) /* the symlink at patch->new_name is created or remains */ - register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT); + register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT); } } @@ -3761,7 +3766,7 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na if (!name->len) break; name->buf[name->len] = '\0'; - change = check_symlink_changes(name->buf); + change = check_symlink_changes(state, name->buf); if (change & SYMLINK_IN_RESULT) return 1; if (change & SYMLINK_GOES_AWAY) @@ -3930,7 +3935,7 @@ static int check_patch_list(struct apply_state *state, struct patch *patch) { int err = 0; - prepare_symlink_changes(patch); + prepare_symlink_changes(state, patch); prepare_fn_table(state, patch); while (patch) { if (state->apply_verbosely) @@ -4668,6 +4673,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) static void clear_apply_state(struct apply_state *state) { string_list_clear(&state->limit_by_name, 0); + string_list_clear(&state->symlink_changes, 0); strbuf_release(&state->root); /* &state->fn_table is cleared at the end of apply_patch() */