All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ghanshyam Thakkar <shyamthakkar001@gmail.com>
To: git@vger.kernel.org
Cc: Ghanshyam Thakkar <shyamthakkar001@gmail.com>
Subject: [RFC PATCH 1/2] add-patch: compare object id instead of literal string
Date: Sun, 28 Jan 2024 23:41:22 +0530	[thread overview]
Message-ID: <20240128181202.986753-3-shyamthakkar001@gmail.com> (raw)
In-Reply-To: <20240128181202.986753-2-shyamthakkar001@gmail.com>

Add a new function reveq(), which takes repository struct and two revision
strings as arguments and returns 0 if the revisions point to the same
object. Passing a rev which does not point to an object is considered
undefined behavior as the underlying function memcmp() will be called
with NULL hash strings.

Subsequently, replace literal string comparison to HEAD in run_add_p()
with reveq() to handle more ways of saying HEAD (such as '@' or '$branch'
where $branch points to same commit as HEAD). This addresses the
NEEDSWORK comment in run_add_p().

However, in ADD_P_RESET mode keep string comparison in logical OR with
reveq() to handle unborn HEAD.

As for the behavior change, with this patch applied if the given
revision points to the same object as HEAD, the patch mode will be set to
patch_mode_(reset,checkout,worktree)_head instead of
patch_mode_(...)_nothead. That is equivalent of not setting -R flag in
diff-index, which would have been otherwise set before this patch.
However, when given same set of inputs, the actual outcome is same as
before this patch. Therefore, this does not affect any automated scripts.

Also, add testcases to check the similarity of result between different
ways of saying HEAD.

Signed-off-by: Ghanshyam Thakkar <shyamthakkar001@gmail.com>
---
Should the return values of repo_get_oid() be checked in reveq()? As
reveq() is not a global function and is only used in run_add_p(), the
validity of revisions is already checked beforehand by builtin/checkout.c
and builtin/reset.c before the call to run_add_p().

 add-patch.c               | 28 +++++++++++++++-------
 t/t2016-checkout-patch.sh | 50 +++++++++++++++++++++++----------------
 t/t2071-restore-patch.sh  | 21 ++++++++++------
 t/t7105-reset-patch.sh    | 14 +++++++++++
 4 files changed, 77 insertions(+), 36 deletions(-)

diff --git a/add-patch.c b/add-patch.c
index 79eda168eb..01eb71d90e 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -14,6 +14,7 @@
 #include "color.h"
 #include "compat/terminal.h"
 #include "prompt.h"
+#include "hash.h"
 
 enum prompt_mode_type {
 	PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_ADDITION, PROMPT_HUNK,
@@ -316,6 +317,18 @@ static void setup_child_process(struct add_p_state *s,
 		     INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
 }
 
+// Check if two revisions point to the same object. Passing a rev which does not
+// point to an object is undefined behavior.
+static inline int reveq(struct repository *r, const char *rev1,
+			const char *rev2)
+{
+	struct object_id oid_rev1, oid_rev2;
+	repo_get_oid(r, rev1, &oid_rev1);
+	repo_get_oid(r, rev2, &oid_rev2);
+
+	return !oideq(&oid_rev1, &oid_rev2);
+}
+
 static int parse_range(const char **p,
 		       unsigned long *offset, unsigned long *count)
 {
@@ -1730,28 +1743,25 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
 		s.mode = &patch_mode_stash;
 	else if (mode == ADD_P_RESET) {
 		/*
-		 * NEEDSWORK: Instead of comparing to the literal "HEAD",
-		 * compare the commit objects instead so that other ways of
-		 * saying the same thing (such as "@") are also handled
-		 * appropriately.
-		 *
-		 * This applies to the cases below too.
+		 * The literal string comparison to HEAD below is kept
+		 * to handle unborn HEAD.
 		 */
-		if (!revision || !strcmp(revision, "HEAD"))
+		if (!revision || !strcmp(revision, "HEAD") ||
+		    !reveq(r, revision, "HEAD"))
 			s.mode = &patch_mode_reset_head;
 		else
 			s.mode = &patch_mode_reset_nothead;
 	} else if (mode == ADD_P_CHECKOUT) {
 		if (!revision)
 			s.mode = &patch_mode_checkout_index;
-		else if (!strcmp(revision, "HEAD"))
+		else if (!reveq(r, revision, "HEAD"))
 			s.mode = &patch_mode_checkout_head;
 		else
 			s.mode = &patch_mode_checkout_nothead;
 	} else if (mode == ADD_P_WORKTREE) {
 		if (!revision)
 			s.mode = &patch_mode_checkout_index;
-		else if (!strcmp(revision, "HEAD"))
+		else if (!reveq(r, revision, "HEAD"))
 			s.mode = &patch_mode_worktree_head;
 		else
 			s.mode = &patch_mode_worktree_nothead;
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 747eb5563e..431f34fa9c 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -12,6 +12,7 @@ test_expect_success 'setup' '
 	git commit -m initial &&
 	test_tick &&
 	test_commit second dir/foo head &&
+	git branch newbranch &&
 	set_and_save_state bar bar_work bar_index &&
 	save_head
 '
@@ -38,26 +39,35 @@ test_expect_success 'git checkout -p with staged changes' '
 	verify_state dir/foo index index
 '
 
-test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
-	set_and_save_state dir/foo work head &&
-	test_write_lines n y n | git checkout -p HEAD &&
-	verify_saved_state bar &&
-	verify_saved_state dir/foo
-'
-
-test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
-	test_write_lines n y y | git checkout -p HEAD &&
-	verify_saved_state bar &&
-	verify_state dir/foo head head
-'
-
-test_expect_success 'git checkout -p HEAD with change already staged' '
-	set_state dir/foo index index &&
-	# the third n is to get out in case it mistakenly does not apply
-	test_write_lines n y n | git checkout -p HEAD &&
-	verify_saved_state bar &&
-	verify_state dir/foo head head
-'
+# Note: 'newbranch' points to the same commit as HEAD. And it is technically
+# allowed to name a branch '@' as of now, however in below test '@'
+# represents the shortcut for HEAD.
+for opt in "HEAD" "@" "newbranch"
+do
+	test_expect_success "git checkout -p $opt with NO staged changes: abort" '
+		set_and_save_state dir/foo work head &&
+		test_write_lines n y n | git checkout -p $opt >output &&
+		verify_saved_state bar &&
+		verify_saved_state dir/foo &&
+		test_grep "Discard" output
+	'
+
+	test_expect_success "git checkout -p $opt with NO staged changes: apply" '
+		test_write_lines n y y | git checkout -p $opt >output &&
+		verify_saved_state bar &&
+		verify_state dir/foo head head &&
+		test_grep "Discard" output
+	'
+
+	test_expect_success "git checkout -p $opt with change already staged" '
+		set_state dir/foo index index &&
+		# the third n is to get out in case it mistakenly does not apply
+		test_write_lines n y n | git checkout -p $opt >output &&
+		verify_saved_state bar &&
+		verify_state dir/foo head head &&
+		test_grep "Discard" output
+	'
+done
 
 test_expect_success 'git checkout -p HEAD^...' '
 	# the third n is to get out in case it mistakenly does not apply
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
index b5c5c0ff7e..305b4a0c4f 100755
--- a/t/t2071-restore-patch.sh
+++ b/t/t2071-restore-patch.sh
@@ -12,6 +12,7 @@ test_expect_success PERL 'setup' '
 	git commit -m initial &&
 	test_tick &&
 	test_commit second dir/foo head &&
+	git branch newbranch &&
 	set_and_save_state bar bar_work bar_index &&
 	save_head
 '
@@ -44,13 +45,19 @@ test_expect_success PERL 'git restore -p with staged changes' '
 	verify_state dir/foo index index
 '
 
-test_expect_success PERL 'git restore -p --source=HEAD' '
-	set_state dir/foo work index &&
-	# the third n is to get out in case it mistakenly does not apply
-	test_write_lines n y n | git restore -p --source=HEAD &&
-	verify_saved_state bar &&
-	verify_state dir/foo head index
-'
+# Note: 'newbranch' points to the same commit as HEAD. And '@' is a
+# shortcut for HEAD.
+for opt in "HEAD" "@" "newbranch"
+do
+	test_expect_success PERL "git restore -p --source=$opt" '
+		set_state dir/foo work index &&
+		# the third n is to get out in case it mistakenly does not apply
+		test_write_lines n y n | git restore -p --source=$opt >output &&
+		verify_saved_state bar &&
+		verify_state dir/foo head index &&
+		test_grep "Discard" output
+	'
+done
 
 test_expect_success PERL 'git restore -p --source=HEAD^' '
 	set_state dir/foo work index &&
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index 05079c7246..65a8802b29 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -13,6 +13,7 @@ test_expect_success PERL 'setup' '
 	git commit -m initial &&
 	test_tick &&
 	test_commit second dir/foo head &&
+	git branch newbranch &&
 	set_and_save_state bar bar_work bar_index &&
 	save_head
 '
@@ -33,6 +34,19 @@ test_expect_success PERL 'git reset -p' '
 	test_grep "Unstage" output
 '
 
+# Note: '@' can technically also be used as a branch name, but in below test
+# it represents the shortcut for HEAD. And 'newbranch' points to the same
+# commit as HEAD.
+for opt in "HEAD" "@" "newbranch"
+do
+	test_expect_success PERL "git reset -p $opt" '
+		test_write_lines n y | git reset -p $opt >output &&
+		verify_state dir/foo work head &&
+		verify_saved_state bar &&
+		test_grep "Unstage" output
+	'
+done
+
 test_expect_success PERL 'git reset -p HEAD^' '
 	test_write_lines n y | git reset -p HEAD^ >output &&
 	verify_state dir/foo work parent &&
-- 
2.43.0


  reply	other threads:[~2024-01-28 18:19 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-28 18:11 [GSOC][RFC PATCH 0/2] add-patch: compare object id Ghanshyam Thakkar
2024-01-28 18:11 ` Ghanshyam Thakkar [this message]
2024-01-29 11:48   ` [RFC PATCH 1/2] add-patch: compare object id instead of literal string Patrick Steinhardt
2024-01-30  6:39     ` Ghanshyam Thakkar
2024-01-30 16:42       ` Junio C Hamano
2024-01-29 18:27   ` Junio C Hamano
2024-01-29 18:58     ` Junio C Hamano
2024-01-30  5:35     ` Ghanshyam Thakkar
2024-01-28 18:11 ` [RFC PATCH 2/2] checkout: remove HEAD special case Ghanshyam Thakkar
2024-01-29 11:48   ` Patrick Steinhardt
2024-02-02 15:03 ` [PATCH v2 0/2] add-patch: Support '@' as a synonym for 'HEAD' Ghanshyam Thakkar
2024-02-02 15:03   ` [PATCH v2 1/2] add-patch: remove non-relevant NEEDSWORK comment Ghanshyam Thakkar
2024-02-02 15:03   ` [PATCH v2 2/2] add-patch: classify '@' as a synonym for 'HEAD' Ghanshyam Thakkar
2024-02-02 17:08     ` Junio C Hamano
2024-02-02 17:43       ` Junio C Hamano
2024-02-02 17:53         ` Ghanshyam Thakkar
2024-02-02 17:51       ` Ghanshyam Thakkar
2024-02-02 17:31   ` [PATCH v2 0/2] add-patch: Support " Ghanshyam Thakkar
2024-02-03 11:25   ` [PATCH v3 0/2] add-patch: " Ghanshyam Thakkar
2024-02-06 22:50     ` [PATCH v4 0/3] '@' as a synonym for 'HEAD' in patch mode Ghanshyam Thakkar
2024-02-11 20:20       ` [PATCH v5 0/2] add-patch: classify '@' as a synonym for 'HEAD' Ghanshyam Thakkar
2024-02-13  0:05         ` [PATCH v6 " Ghanshyam Thakkar
2024-02-14 11:06           ` Phillip Wood
2024-02-13  0:05         ` [PATCH v6 1/2] " Ghanshyam Thakkar
2024-02-13  0:05         ` [PATCH v6 2/2] add -p tests: remove PERL prerequisites Ghanshyam Thakkar
2024-02-11 20:20       ` [PATCH v5 1/2] add-patch: classify '@' as a synonym for 'HEAD' Ghanshyam Thakkar
2024-02-12 21:45         ` Junio C Hamano
2024-02-11 20:20       ` [PATCH v5 2/2] add -p tests: remove PERL prerequisites Ghanshyam Thakkar
2024-02-06 22:50     ` [PATCH v4 1/3] add-patch: remove unnecessary NEEDSWORK comment Ghanshyam Thakkar
2024-02-07 10:51       ` Phillip Wood
2024-02-06 22:50     ` [PATCH v4 2/3] add-patch: classify '@' as a synonym for 'HEAD' Ghanshyam Thakkar
2024-02-07  1:05       ` Junio C Hamano
2024-02-07 10:38         ` Phillip Wood
2024-02-09 15:57         ` Ghanshyam Thakkar
2024-02-06 22:50     ` [PATCH v4 3/3] add -p tests: remove Perl prerequisite Ghanshyam Thakkar
2024-02-07 10:50       ` Phillip Wood
2024-02-07 13:51         ` Phillip Wood
2024-02-07 16:02           ` Junio C Hamano
2024-02-07 16:58           ` Eric Sunshine
2024-02-03 11:25   ` [PATCH v3 1/2] add-patch: remove unnecessary NEEDSWORK comment Ghanshyam Thakkar
2024-02-03 11:25   ` [PATCH v3 2/2] add-patch: classify '@' as a synonym for 'HEAD' Ghanshyam Thakkar
2024-02-03 22:33     ` Junio C Hamano
2024-02-05 15:14       ` Ghanshyam Thakkar
2024-02-05 16:37     ` Phillip Wood
2024-02-05 20:38       ` Ghanshyam Thakkar
2024-02-05 23:07       ` Junio C Hamano

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

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240128181202.986753-3-shyamthakkar001@gmail.com \
    --to=shyamthakkar001@gmail.com \
    --cc=git@vger.kernel.org \
    /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.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.