diff mbox series

[v4,2/3] apply: make i-t-a entries never match worktree

Message ID 20200806060119.74587-3-ray@ameretat.dev (mailing list archive)
State Superseded
Headers show
Series apply: handle i-t-a entries in index | expand

Commit Message

Raymond E. Pasco Aug. 6, 2020, 6:01 a.m. UTC
By definition, an intent-to-add index entry can never match the
worktree, because worktrees have no concept of intent-to-add entries.
Therefore, "apply --index" should always fail on intent-to-add paths.

Because check_preimage() calls verify_index_match(), it already fails
for patches other than creation patches, which check_preimage() ignores.
This patch adds a check to check_preimage()'s rough equivalent for
creation patches, check_to_create().

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Raymond E. Pasco <ray@ameretat.dev>
---
 apply.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

Comments

Junio C Hamano Aug. 6, 2020, 9 p.m. UTC | #1
"Raymond E. Pasco" <ray@ameretat.dev> writes:

> By definition, an intent-to-add index entry can never match the
> worktree, because worktrees have no concept of intent-to-add entries.
> Therefore, "apply --index" should always fail on intent-to-add paths.
>
> Because check_preimage() calls verify_index_match(), it already fails
> for patches other than creation patches, which check_preimage() ignores.
> This patch adds a check to check_preimage()'s rough equivalent for
> creation patches, check_to_create().
>
> Helped-by: Junio C Hamano <gitster@pobox.com>
> Signed-off-by: Raymond E. Pasco <ray@ameretat.dev>
> ---
>  apply.c | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)

At first glance, it feels somewhat sad that this check is not done
in check_preimage(); after all, the caller of check_preimage() feeds
it to all kind of patches, without excluding path creation, so the
helper should be allowed to say "heh, you are trying to create path
F with this patch, but there already is F in the index", "you are
renaming into F but there is F already", etc.

But ensuring the state of the path to be patched is done by
check_to_create() for paths that are to be created, even before this
patch, because it simply needs to do different checks from what is
done on modification patch, and also because we need to resolve
patch->is_new bit for non-git patches before being able to perform
the checks done in check_to_create().

So I agree with the location of this additonal logic.

It is somewhat unsatisfactory that we need to do the same
index_name_pos probing twice.  I wonder if we somehow can
consolidate them?

Perhaps something along this line, instead of this patch?

 apply.c | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/apply.c b/apply.c
index 4cba4ce71a..c5ecb64102 100644
--- a/apply.c
+++ b/apply.c
@@ -3740,6 +3740,7 @@ static int check_preimage(struct apply_state *state,
 
 #define EXISTS_IN_INDEX 1
 #define EXISTS_IN_WORKTREE 2
+#define EXISTS_IN_INDEX_AS_ITA 3
 
 static int check_to_create(struct apply_state *state,
 			   const char *new_name,
@@ -3747,11 +3748,21 @@ static int check_to_create(struct apply_state *state,
 {
 	struct stat nst;
 
-	if (state->check_index && !ok_if_exists) {
-		int pos = index_name_pos(state->repo->index, new_name, strlen(new_name));
-		if (pos >= 0 &&
-		    !(state->repo->index->cache[pos]->ce_flags & CE_INTENT_TO_ADD))
-			return EXISTS_IN_INDEX;
+	if (state->check_index && (!ok_if_exists || !state->cached)) {
+		int pos;
+
+		pos = index_name_pos(state->repo->index, new_name, strlen(new_name));
+		if (pos >= 0) {
+			struct cache_entry *ce = state->repo->index->cache[pos];
+
+			/* allow ITA, as they do not yet exist in the index */
+			if (!ok_if_exists && !(ce->ce_flags & CE_INTENT_TO_ADD))
+				return EXISTS_IN_INDEX;
+
+			/* ITA entries can never match working tree files */
+			if (!state->cached && (ce->ce_flags & CE_INTENT_TO_ADD))
+				return EXISTS_IN_INDEX_AS_ITA;
+		}
 	}
 
 	if (state->cached)
@@ -3938,6 +3949,9 @@ static int check_patch(struct apply_state *state, struct patch *patch)
 		case EXISTS_IN_INDEX:
 			return error(_("%s: already exists in index"), new_name);
 			break;
+		case EXISTS_IN_INDEX_AS_ITA:
+			return error(_("%s: does not match index"), new_name);
+			break;
 		case EXISTS_IN_WORKTREE:
 			return error(_("%s: already exists in working directory"),
 				     new_name);
Raymond E. Pasco Aug. 6, 2020, 9:47 p.m. UTC | #2
On Thu Aug 6, 2020 at 5:00 PM EDT, Junio C Hamano wrote:
> At first glance, it feels somewhat sad that this check is not done
> in check_preimage(); after all, the caller of check_preimage() feeds
> it to all kind of patches, without excluding path creation, so the
> helper should be allowed to say "heh, you are trying to create path
> F with this patch, but there already is F in the index", "you are
> renaming into F but there is F already", etc.

I spent some time trying to put it in there before deciding it was
better off in check_to_create().

> It is somewhat unsatisfactory that we need to do the same
> index_name_pos probing twice. I wonder if we somehow can
> consolidate them?
>
> Perhaps something along this line, instead of this patch?

I think this logic can be consolidated and still readable, yeah. I'll
send a patch.
diff mbox series

Patch

diff --git a/apply.c b/apply.c
index 4cba4ce71a..6328591489 100644
--- a/apply.c
+++ b/apply.c
@@ -3754,6 +3754,21 @@  static int check_to_create(struct apply_state *state,
 			return EXISTS_IN_INDEX;
 	}
 
+	/* If the new path to be added has an intent-to-add entry, then
+	 * by definition it does not match what is in the work tree. So
+	 * "apply --index" should always fail in this case. Patches other
+	 * than creation patches are already held to this standard by
+	 * check_preimage() calling verify_index_match().
+	 */
+	if (state->check_index && !state->cached) {
+		int pos = index_name_pos(state->repo->index, new_name,
+					 strlen(new_name));
+		if (pos >= 0 &&
+		    state->repo->index->cache[pos]->ce_flags & CE_INTENT_TO_ADD)
+			return error(_("%s: does not match index"), new_name);
+	}
+
+
 	if (state->cached)
 		return 0;