From: "René Scharfe" <l.s.r@web.de>
To: Junio C Hamano <gitster@pobox.com>
Cc: Vegard Nossum <vegard.nossum@oracle.com>,
git@vger.kernel.org,
Christian Couder <christian.couder@gmail.com>,
Michal Zalewski <lcamtuf@google.com>
Subject: Re: [PATCH 2/2] apply: handle assertion failure gracefully
Date: Tue, 28 Feb 2017 11:50:51 +0100 [thread overview]
Message-ID: <05fe5800-ebc0-76d7-579d-77f64a851fc1@web.de> (raw)
In-Reply-To: <xmqq1sujnu1g.fsf@gitster.mtv.corp.google.com>
Am 27.02.2017 um 23:33 schrieb Junio C Hamano:
> René Scharfe <l.s.r@web.de> writes:
>
>> Am 27.02.2017 um 21:04 schrieb Junio C Hamano:
>>> René Scharfe <l.s.r@web.de> writes:
>>>
>>>>> diff --git a/apply.c b/apply.c
>>>>> index cbf7cc7f2..9219d2737 100644
>>>>> --- a/apply.c
>>>>> +++ b/apply.c
>>>>> @@ -3652,7 +3652,6 @@ static int check_preimage(struct apply_state *state,
>>>>> if (!old_name)
>>>>> return 0;
>>>>>
>>>>> - assert(patch->is_new <= 0);
>>>>
>>>> 5c47f4c6 (builtin-apply: accept patch to an empty file) added that
>>>> line. Its intent was to handle diffs that contain an old name even for
>>>> a file that's created. Citing from its commit message: "When we
>>>> cannot be sure by parsing the patch that it is not a creation patch,
>>>> we shouldn't complain when if there is no such a file." Why not stop
>>>> complaining also in case we happen to know for sure that it's a
>>>> creation patch? I.e., why not replace the assert() with:
>>>>
>>>> if (patch->is_new == 1)
>>>> goto is_new;
>>>>
>>>>> previous = previous_patch(state, patch, &status);
>>>
>>> When the caller does know is_new is true, old_name must be made/left
>>> NULL. That is the invariant this assert is checking to catch an
>>> error in the calling code.
>>
>> There are some places in apply.c that set ->is_new to 1, but none of
>> them set ->old_name to NULL at the same time.
>
> I thought all of these are flipping ->is_new that used to be -1
> (unknown) to (now we know it is new), and sets only new_name without
> doing anything to old_name, because they know originally both names
> are set to NULL.
>
>> Having to keep these two members in sync sounds iffy anyway. Perhaps
>> accessors can help, e.g. a setter which frees old_name when is_new is
>> set to 1, or a getter which returns NULL for old_name if is_new is 1.
>
> Definitely, the setter would make it harder to make the mistake.
When I added setters, apply started to passed NULL to unlink(2) and
rmdir(2) in some of the new tests, which still failed.
That's because three of the diffs trigger both gitdiff_delete(), which
sets is_delete and old_name, and gitdiff_newfile(), which sets is_new
and new_name. Create and delete equals move, right? Or should we
error out at this point already?
The last new diff adds a new file that is copied. Sounds impossible.
How about something like this, which forbids combinations that make no
sense. Hope it's not too strict; at least all tests succeed.
---
apply.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 61 insertions(+), 18 deletions(-)
diff --git a/apply.c b/apply.c
index 21b0bebec5..6cb6860511 100644
--- a/apply.c
+++ b/apply.c
@@ -197,6 +197,14 @@ struct fragment {
#define BINARY_DELTA_DEFLATED 1
#define BINARY_LITERAL_DEFLATED 2
+enum patch_type {
+ CHANGE,
+ CREATE,
+ DELETE,
+ RENAME,
+ COPY
+};
+
/*
* This represents a "patch" to a file, both metainfo changes
* such as creation/deletion, filemode and content changes represented
@@ -205,6 +213,7 @@ struct fragment {
struct patch {
char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode;
+ enum patch_type type;
int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */
int rejected;
unsigned ws_rule;
@@ -229,6 +238,36 @@ struct patch {
struct object_id threeway_stage[3];
};
+static int set_patch_type(struct patch *patch, enum patch_type type)
+{
+ if (patch->type != CHANGE && patch->type != type)
+ return error(_("conflicting patch types"));
+ patch->type = type;
+ switch (type) {
+ case CHANGE:
+ break;
+ case CREATE:
+ patch->is_new = 1;
+ patch->is_delete = 0;
+ free(patch->old_name);
+ patch->old_name = NULL;
+ break;
+ case DELETE:
+ patch->is_new = 0;
+ patch->is_delete = 1;
+ free(patch->new_name);
+ patch->new_name = NULL;
+ break;
+ case RENAME:
+ patch->is_rename = 1;
+ break;
+ case COPY:
+ patch->is_copy = 1;
+ break;
+ }
+ return 0;
+}
+
static void free_fragment_list(struct fragment *list)
{
while (list) {
@@ -907,13 +946,13 @@ static int parse_traditional_patch(struct apply_state *state,
}
}
if (is_dev_null(first)) {
- patch->is_new = 1;
- patch->is_delete = 0;
+ if (set_patch_type(patch, CREATE))
+ return -1;
name = find_name_traditional(state, second, NULL, state->p_value);
patch->new_name = name;
} else if (is_dev_null(second)) {
- patch->is_new = 0;
- patch->is_delete = 1;
+ if (set_patch_type(patch, DELETE))
+ return -1;
name = find_name_traditional(state, first, NULL, state->p_value);
patch->old_name = name;
} else {
@@ -922,12 +961,12 @@ static int parse_traditional_patch(struct apply_state *state,
name = find_name_traditional(state, second, first_name, state->p_value);
free(first_name);
if (has_epoch_timestamp(first)) {
- patch->is_new = 1;
- patch->is_delete = 0;
+ if (set_patch_type(patch, CREATE))
+ return -1;
patch->new_name = name;
} else if (has_epoch_timestamp(second)) {
- patch->is_new = 0;
- patch->is_delete = 1;
+ if (set_patch_type(patch, DELETE))
+ return -1;
patch->old_name = name;
} else {
patch->old_name = name;
@@ -1031,7 +1070,8 @@ static int gitdiff_delete(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->is_delete = 1;
+ if (set_patch_type(patch, DELETE))
+ return -1;
free(patch->old_name);
patch->old_name = xstrdup_or_null(patch->def_name);
return gitdiff_oldmode(state, line, patch);
@@ -1041,7 +1081,8 @@ static int gitdiff_newfile(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->is_new = 1;
+ if (set_patch_type(patch, CREATE))
+ return -1;
free(patch->new_name);
patch->new_name = xstrdup_or_null(patch->def_name);
return gitdiff_newmode(state, line, patch);
@@ -1051,7 +1092,8 @@ static int gitdiff_copysrc(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->is_copy = 1;
+ if (set_patch_type(patch, COPY))
+ return -1;
free(patch->old_name);
patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
@@ -1061,7 +1103,8 @@ static int gitdiff_copydst(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->is_copy = 1;
+ if (set_patch_type(patch, COPY))
+ return -1;
free(patch->new_name);
patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
@@ -1071,7 +1114,8 @@ static int gitdiff_renamesrc(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->is_rename = 1;
+ if (set_patch_type(patch, RENAME))
+ return -1;
free(patch->old_name);
patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
@@ -1081,7 +1125,8 @@ static int gitdiff_renamedst(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->is_rename = 1;
+ if (set_patch_type(patch, RENAME))
+ return -1;
free(patch->new_name);
patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
@@ -3704,10 +3749,8 @@ static int check_preimage(struct apply_state *state,
return 0;
is_new:
- patch->is_new = 1;
- patch->is_delete = 0;
- free(patch->old_name);
- patch->old_name = NULL;
+ if (set_patch_type(patch, CREATE))
+ return -1;
return 0;
}
--
2.12.0
next prev parent reply other threads:[~2017-02-28 10:57 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-02-25 10:13 [PATCH 1/2] apply: guard against renames of non-existant empty files Vegard Nossum
2017-02-25 10:13 ` [PATCH 2/2] apply: handle assertion failure gracefully Vegard Nossum
2017-02-25 21:21 ` René Scharfe
2017-02-27 20:04 ` Junio C Hamano
2017-02-27 22:18 ` René Scharfe
2017-02-27 22:33 ` Junio C Hamano
2017-02-28 10:50 ` René Scharfe [this message]
2017-06-27 17:03 ` René Scharfe
2017-06-27 18:08 ` Junio C Hamano
2017-06-27 20:20 ` René Scharfe
2017-06-27 21:39 ` Junio C Hamano
2017-06-27 17:03 ` René Scharfe
2017-02-25 11:59 ` [PATCH 1/2] apply: guard against renames of non-existant empty files Philip Oakley
2017-02-25 12:06 ` Vegard Nossum
2017-02-25 12:47 ` Philip Oakley
2017-02-25 20:51 ` René Scharfe
2017-02-27 20:10 ` Junio C Hamano
2017-02-27 22:18 ` René Scharfe
2017-06-27 17:03 ` René Scharfe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=05fe5800-ebc0-76d7-579d-77f64a851fc1@web.de \
--to=l.s.r@web.de \
--cc=christian.couder@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=lcamtuf@google.com \
--cc=vegard.nossum@oracle.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).