Skip to contentSkip to conversation

libify reflog

PATCH Superseded John Cai via GitGitGadget 6 participants 63 messages Feb 18, 2022 – Mar 8, 2022 Kbuild

lore
Share & tools
lore
#0 lore raw

cover libify reflog

In [1], there was a discussion around a bug report of stash not recovering in the middle of the process. It turned out to not be a bug we need to fix. However, out of that discussion came the idea of libifying reflog. This can stand alone as a code improvement.

stash.c currently shells out to call reflog to delete reflogs. Libify reflog delete and call it from both builtin/reflog.c and builtin/stash.c.

This patch has three parts:

* libify reflog's delete functionality and move some of the helpers into a reflog.c library and export them
* call reflog_delete from builtin/reflog.c
* call reflog_delete from builtin/stash.c

1. https://lore.kernel.org/git/[email protected]/

John Cai (3):
reflog: libify delete reflog function and helpers
reflog: call reflog_delete from reflog.c
stash: call reflog_delete from reflog.c

 Makefile         |   1 +
 builtin/reflog.c | 451 +----------------------------------------------
 builtin/stash.c  |  16 +-
 reflog.c         | 432 +++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  49 +++++
 5 files changed, 490 insertions(+), 459 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h

base-commit: b80121027d1247a0754b3cc46897fee75c050b44
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1218%2Fjohn-cai%2Fjc-libify-reflog-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1218/john-cai/jc-libify-reflog-v1
Pull-Request: https://github.com/git/git/pull/1218

Signature · 1 line
gitgitgadget
#14 lore raw

PATCH 2/3 reflog: call reflog_delete from reflog.c

From: John Cai <[email protected]>

Now that reflog is libified into reflog.c, we can call reflog_delete from the reflog.c library.

---
 builtin/reflog.c | 42 ++----------------------------------------
 1 file changed, 2 insertions(+), 40 deletions(-)
builtin/reflog.c +2 -40
index 65198320cd2..03d347e5832 100644
@@ -316,12 +316,10 @@ static const char * reflog_delete_usage[] = {
 
 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 {
-	struct cmd_reflog_expire_cb cmd = { 0 };
 	int i, status = 0;
 	unsigned int flags = 0;
 	int verbose = 0;
 
-	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
 	const struct option options[] = {
 		OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
 			EXPIRE_REFLOGS_DRY_RUN),
@@ -337,48 +335,12 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
 
-	if (verbose)
-		should_prune_fn = should_expire_reflog_ent_verbose;
-
 	if (argc < 1)
 		return error(_("no reflog specified to delete"));
 
-	for (i = 0; i < argc; i++) {
-		const char *spec = strstr(argv[i], "@{");
-		char *ep, *ref;
-		int recno;
-		struct expire_reflog_policy_cb cb = {
-			.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
-		};
-
-		if (!spec) {
-			status |= error(_("not a reflog: %s"), argv[i]);
-			continue;
-		}
-
-		if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
-			status |= error(_("no reflog for '%s'"), argv[i]);
-			continue;
-		}
-
-		recno = strtoul(spec + 2, &ep, 10);
-		if (*ep == '}') {
-			cmd.recno = -recno;
-			for_each_reflog_ent(ref, count_reflog_ent, &cmd);
-		} else {
-			cmd.expire_total = approxidate(spec + 2);
-			for_each_reflog_ent(ref, count_reflog_ent, &cmd);
-			cmd.expire_total = 0;
-		}
+	for (i = 0; i < argc; i++)
+		status |= reflog_delete(argv[i], flags, verbose);
 
-		cb.cmd = cmd;
-		status |= reflog_expire(ref, flags,
-					reflog_expiry_prepare,
-					should_prune_fn,
-					reflog_expiry_cleanup,
-					&cb);
-		free(ref);
-	}
 	return status;
 }
 
Signature · 1 line
gitgitgadget
#17 lore raw

PATCH 3/3 stash: call reflog_delete from reflog.c

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

Now that cmd_reflog_delete has been libified an exported it into a new reflog.c library so we can call it directly from builtin/stash.c. This not only gives us a performance gain since we don't need to create a subprocess, but it also allows us to use the ref transactions api in the future.

---
 builtin/stash.c | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)
builtin/stash.c +4 -12
index 9638c56303e..d0967b3d3c3 100644
@@ -17,6 +17,7 @@
 #include "diffcore.h"
 #include "exec-cmd.h"
 #include "entry.h"
+#include "reflog.h"
 
 #define INCLUDE_ALL_FILES 2
 
@@ -635,18 +636,9 @@ static int reflog_is_empty(const char *refname)
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
 	int ret;
-	struct child_process cp_reflog = CHILD_PROCESS_INIT;
-
-	/*
-	 * reflog does not provide a simple function for deleting refs. One will
-	 * need to be added to avoid implementing too much reflog code here
-	 */
-
-	cp_reflog.git_cmd = 1;
-	strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-		     "--rewrite", NULL);
-	strvec_push(&cp_reflog.args, info->revision.buf);
-	ret = run_command(&cp_reflog);
+	ret = reflog_delete(info->revision.buf,
+			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
+			    0);
 	if (!ret) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
Signature · 1 line
gitgitgadget
#1 lore raw

PATCH 1/3 reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

---
 Makefile         |   1 +
 builtin/reflog.c | 409 +-------------------------------------------
 reflog.c         | 432 +++++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  49 ++++++
 4 files changed, 484 insertions(+), 407 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h
Makefile +1 -0
index 186f9ab6190..f1e295539ab 100644
@@ -990,6 +990,7 @@ LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
+LIB_OBJS += reflog.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
builtin/reflog.c +2 -407

444 lines not on this page · raw message

reflog.c new file +432 -0

432 lines not on this page · raw message

reflog.h new file +49 -0

49 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#2 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

Quoted 6 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Quoted 8 lines

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

Yay! This looks great. Even though I'll need to deal with some conflicts locally .. :)

Quoted 12 lines
reflog.c new file +12 -0
+int reflog_delete(const char *rev, int flags, int verbose)
+{
+	struct cmd_reflog_expire_cb cmd = { 0 };
+	int status = 0;
+	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
+	const char *spec = strstr(rev, "@{");
+	char *ep, *ref;
+	int recno;
+	struct expire_reflog_policy_cb cb = {
+		.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+	};
+

reflog.c line 408

Quoted 10 lines
reflog.c new file +10 -0
+	if (verbose)
+		should_prune_fn = should_expire_reflog_ent_verbose;
+
+	if (!spec) {
+		status |= error(_("not a reflog: %s"), rev);
+	}
+
+	if (!dwim_log(rev, spec - rev, NULL, &ref)) {
+		status |= error(_("no reflog for '%s'"), rev);
+	}

For these let's follow our usual style of not having braces for single-line if's.

Buuuut in this case doing so will make the diff move detection less useful for 1..2.

So probably best to leave it, or do some post-cleanup at the end maybe.

Quoted 6 lines
reflog.h new file +6 -0
+int reflog_delete(const char*, int, int);
+void reflog_expiry_cleanup(void *);
+void reflog_expiry_prepare(const char*, const struct object_id*,
+			   void *);
+int should_expire_reflog_ent(struct object_id *, struct object_id*,
+				    const char *, timestamp_t, int,

reflog.h line 49

Quoted 10 lines
reflog.h new file +10 -0
+				    const char *, void *);
+int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
+		const char *email, timestamp_t timestamp, int tz,
+		const char *message, void *cb_data);
+int should_expire_reflog_ent_verbose(struct object_id *,
+				     struct object_id *,
+				     const char *,
+				     timestamp_t, int,
+				     const char *, void *);
+#endif /* REFLOG_H */

Just a style preference, but I for one prefer the style where we retain the parameter names, it helps to read these, especially when we add API docs here.

Some of these are mis-indented. We align with the opening "(" with "\t" = 8 chars, so e.g. 2x \t + 5 SP for the count_reflog_ent() arguments etc.

#15 lore raw

Re: [PATCH 2/3] reflog: call reflog_delete from reflog.c

In reply to John Cai via GitGitGadget
Quoted 68 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Now that reflog is libified into reflog.c, we can call reflog_delete from the reflog.c library.

---
 builtin/reflog.c | 42 ++----------------------------------------
 1 file changed, 2 insertions(+), 40 deletions(-)
builtin/reflog.c +2 -32
index 65198320cd2..03d347e5832 100644
@@ -316,12 +316,10 @@ static const char * reflog_delete_usage[] = {
 
 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 {
-	struct cmd_reflog_expire_cb cmd = { 0 };
 	int i, status = 0;
 	unsigned int flags = 0;
 	int verbose = 0;
 
-	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
 	const struct option options[] = {
 		OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
 			EXPIRE_REFLOGS_DRY_RUN),
@@ -337,48 +335,12 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
 
-	if (verbose)
-		should_prune_fn = should_expire_reflog_ent_verbose;
-
 	if (argc < 1)
 		return error(_("no reflog specified to delete"));
 
-	for (i = 0; i < argc; i++) {
-		const char *spec = strstr(argv[i], "@{");
-		char *ep, *ref;
-		int recno;
-		struct expire_reflog_policy_cb cb = {
-			.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
-		};
-
-		if (!spec) {
-			status |= error(_("not a reflog: %s"), argv[i]);
-			continue;
-		}
-
-		if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
-			status |= error(_("no reflog for '%s'"), argv[i]);
-			continue;
-		}
-
-		recno = strtoul(spec + 2, &ep, 10);
-		if (*ep == '}') {
-			cmd.recno = -recno;
-			for_each_reflog_ent(ref, count_reflog_ent, &cmd);
-		} else {
-			cmd.expire_total = approxidate(spec + 2);
-			for_each_reflog_ent(ref, count_reflog_ent, &cmd);
-			cmd.expire_total = 0;
-		}
+	for (i = 0; i < argc; i++)
+		status |= reflog_delete(argv[i], flags, verbose);
 

builtin/reflog.c static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) · line 345

Quoted 10 lines
builtin/reflog.c +0 -8
-		cb.cmd = cmd;
-		status |= reflog_expire(ref, flags,
-					reflog_expiry_prepare,
-					should_prune_fn,
-					reflog_expiry_cleanup,
-					&cb);
-		free(ref);
-	}
 	return status;
 }

Maybe others will disagree, but per my comment on 1/2 I found reviewing this locally much easier with this squashed into 1/2 (without the {} changes I suggested).

I.e. the diff move/rename detection eats up this change & shows that the combinatino of 1/3 and 2/3 is almost entirely just moving around existing code (good!).

But without this squashed 1/3 has a reflog_delete() "addition", that we later can see is mostly just moving things around.

I'll leave it to you to decide what you want to do there, just suggestion on an otherwise very trivial-to-review change :)

#18 lore raw

Re: [PATCH 3/3] stash: call reflog_delete from reflog.c

In reply to John Cai via GitGitGadget
Quoted 10 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Now that cmd_reflog_delete has been libified an exported it into a new reflog.c library so we can call it directly from builtin/stash.c. This not only gives us a performance gain since we don't need to create a subprocess, but it also allows us to use the ref transactions api in the future.

Very nicely done, and nice that despite the ~500 lines added/removed in the diffstat that the "actual" changes in this series are so small.q

Review static int reflog_is_empty(const char *refname) · line 639

Quoted 6 lines
(quoted diff) +0 -2
@@ -635,18 +636,9 @@ static int reflog_is_empty(const char *refname)
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
 	int ret;
-	struct child_process cp_reflog = CHILD_PROCESS_INIT;
-

Nit: We usually separate variables decls with a \n\n, as is done in the pre-image, but you end up dropping that.

Quoted 6 lines
(quoted diff) +0 -6
-	/*
-	 * reflog does not provide a simple function for deleting refs. One will
-	 * need to be added to avoid implementing too much reflog code here
-	 */
-
-	cp_reflog.git_cmd = 1;

Review static int reflog_is_empty(const char *refname) · line 644

Quoted 10 lines
(quoted diff) +3 -4
-	strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-		     "--rewrite", NULL);
-	strvec_push(&cp_reflog.args, info->revision.buf);
-	ret = run_command(&cp_reflog);
+	ret = reflog_delete(info->revision.buf,
+			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
+			    0);
 	if (!ret) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,

I think per the above squashing this in would be nice, i.e. you get rid of the long line & it'sclear that "ret" is not used for anything now:

builtin/stash.c +3 -5
index d0967b3d3c3..7b939576720 100644
@@ -635,11 +635,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-	int ret;
-	ret = reflog_delete(info->revision.buf,
-			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
-			    0);
-	if (!ret) {
+	unsigned int flags = EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE;
+
+	if (!reflog_delete(info->revision.buf, flags, 0)) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 				  oid_to_hex(&info->w_commit));

But, having written that I notice that we have *_REWRITE twice there, so I almost just carried forward a new bug in 3/3 when composing this :)

So one should be EXPIRE_REFLOGS_UPDATE_REF, presumably.

And perhaps it's a big pain, but that suggests that the code isn't either used at all, or that we're missing a test for it.

So adding a prep commit to this series where we either drop it, or add the missing test would be a very nice addition.

See my quite recent 5ac15ad2509 (reflog tests: add --updateref tests, 2021-10-16) for adding such tests, but in that case I just covered "git reflog" itself, not "git stash". Maybe we can just add something to that for-loop in t1417 (or similar for a new stash test).

Also, a s/unsigned int flags/enum expire_reflog_flags/ while we're at it would be very nice here, but that could be done as another small prep commit. I.e. it's *not* a new issue since cmd_reflog_delete() had it before, but when converting this to a documented API it would be very nice to have it reflect the actual type we end up using.

#22 lore raw
In reply to John Cai via GitGitGadget
Quoted 8 lines · John Cai via GitGitGadget

In [1], there was a discussion around a bug report of stash not recovering in the middle of the process. It turned out to not be a bug we need to fix. However, out of that discussion came the idea of libifying reflog. This can stand alone as a code improvement.

stash.c currently shells out to call reflog to delete reflogs. Libify reflog delete and call it from both builtin/reflog.c and builtin/stash.c.

Quoted 8 lines

This patch has three parts:

* libify reflog's delete functionality and move some of the helpers into a reflog.c library and export them
* call reflog_delete from builtin/reflog.c
* call reflog_delete from builtin/stash.c

1. https://lore.kernel.org/git/[email protected]/

I just reviewed this in detail, it's very nice to see this done & another shell-out in our code API-ified.

There's at least one bad bug here (but easily fixed), and some nits etc. that I suggested mainly to make the diff easier to look at locally, but aside from those minor adjustments this looks very good to me.

#5 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget
Quoted 5 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Sounds great. For other reviewers, looking at this with --color-moved if you have the patches applied locally makes it much easier to see what is going on here, which is basically:

- All of the implementation that used to be in builtin/reflog.c moved to reflog.c.

- The function signatures and structure declarations moved to reflog.h.

Quoted 3 lines

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

As you mentioned, the reflog_delete() implementation is indeed new. It looks like Ævar reviewed most of it already, so I'll take a look at his message before I end up repeating everything he already said ;).

It's worth noting that the subsequent clean-up to rewrite cmd_reflog_delete() in terms of your new reflog_delete() happens in the subsequent commit. If you end up rerolling this series, mentioning that here may be worthwhile.

One question that I had which I don't see answered already is what the plan is for existing reflog-related functions that live in refs.h. Should or will those functions be moved to the new reflog-specific header, too?

Thanks,
Taylor

#3 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to Ævar Arnfjörð Bjarmason
Quoted 26 lines · Ævar Arnfjörð Bjarmason
Quoted 12 lines
reflog.c new file +12 -0
+int reflog_delete(const char *rev, int flags, int verbose)
+{
+	struct cmd_reflog_expire_cb cmd = { 0 };
+	int status = 0;
+	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
+	const char *spec = strstr(rev, "@{");
+	char *ep, *ref;
+	int recno;
+	struct expire_reflog_policy_cb cb = {
+		.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+	};
+

reflog.c line 408

Quoted 10 lines
reflog.c new file +10 -0
+	if (verbose)
+		should_prune_fn = should_expire_reflog_ent_verbose;
+
+	if (!spec) {
+		status |= error(_("not a reflog: %s"), rev);
+	}
+
+	if (!dwim_log(rev, spec - rev, NULL, &ref)) {
+		status |= error(_("no reflog for '%s'"), rev);
+	}

For these let's follow our usual style of not having braces for single-line if's.

Quoted 4 lines

Buuuut in this case doing so will make the diff move detection less useful for 1..2.

So probably best to leave it, or do some post-cleanup at the end maybe.

Hmm. I don't think the diff detection mechanism would have an opportunity to kick in here, since the code is added in one patch and then removed in another. I think I may be missing what you're trying to say here ;).

In any case, I don't think it's a huge deal if we can't accurately colorize this with --color-moved, so I'd probably just as soon clean up the style nits in this patch.

Quoted 21 lines
Quoted 6 lines
reflog.h new file +6 -0
+int reflog_delete(const char*, int, int);
+void reflog_expiry_cleanup(void *);
+void reflog_expiry_prepare(const char*, const struct object_id*,
+			   void *);
+int should_expire_reflog_ent(struct object_id *, struct object_id*,
+				    const char *, timestamp_t, int,

reflog.h line 49

Quoted 10 lines
reflog.h new file +10 -0
+				    const char *, void *);
+int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
+		const char *email, timestamp_t timestamp, int tz,
+		const char *message, void *cb_data);
+int should_expire_reflog_ent_verbose(struct object_id *,
+				     struct object_id *,
+				     const char *,
+				     timestamp_t, int,
+				     const char *, void *);
+#endif /* REFLOG_H */

Just a style preference, but I for one prefer the style where we retain the parameter names, it helps to read these, especially when we add API docs here.

Quoted 3 lines

Some of these are mis-indented. We align with the opening "(" with "\t" = 8 chars, so e.g. 2x \t + 5 SP for the count_reflog_ent() arguments etc.

Thanks for noting on both.

Thanks,
Taylor

#4 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

Quoted 32 lines · Taylor Blau
Quoted 26 lines · Ævar Arnfjörð Bjarmason
Quoted 12 lines
reflog.c new file +12 -0
+int reflog_delete(const char *rev, int flags, int verbose)
+{
+	struct cmd_reflog_expire_cb cmd = { 0 };
+	int status = 0;
+	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
+	const char *spec = strstr(rev, "@{");
+	char *ep, *ref;
+	int recno;
+	struct expire_reflog_policy_cb cb = {
+		.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+	};
+

reflog.c line 408

Quoted 10 lines
reflog.c new file +10 -0
+	if (verbose)
+		should_prune_fn = should_expire_reflog_ent_verbose;
+
+	if (!spec) {
+		status |= error(_("not a reflog: %s"), rev);
+	}
+
+	if (!dwim_log(rev, spec - rev, NULL, &ref)) {
+		status |= error(_("no reflog for '%s'"), rev);
+	}

For these let's follow our usual style of not having braces for single-line if's.

Quoted 4 lines

Buuuut in this case doing so will make the diff move detection less useful for 1..2.

So probably best to leave it, or do some post-cleanup at the end maybe.

Quoted 8 lines

Hmm. I don't think the diff detection mechanism would have an opportunity to kick in here, since the code is added in one patch and then removed in another. I think I may be missing what you're trying to say here ;).

In any case, I don't think it's a huge deal if we can't accurately colorize this with --color-moved, so I'd probably just as soon clean up the style nits in this patch.

Yes, this should really be read in combination with my comment on 2/3 to squash that commit into 1/3 to take full advantage of that, sorry.

#9 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget
Quoted 6 lines · John Cai via GitGitGadget
reflog.h new file +0 -0
new file mode 100644
index 00000000000..e4a8a104f45

reflog.h line 10

Quoted 10 lines
reflog.h new file +10 -0
@@ -0,0 +1,49 @@
+#ifndef REFLOG_H
+#define REFLOG_H
+
+#include "cache.h"
+#include "commit.h"
+
+/* Remember to update object flag allocation in object.h */
+#define INCOMPLETE	(1u<<10)
+#define STUDYING	(1u<<11)
+#define REACHABLE	(1u<<12)

These names were unique enough back when they were part of internal implementation detail inside builtin/reflog.c but it no longer is in the scope it is visible. builtin/stash.c (and whatever other things that may want to deal with reflogs in the future) is about stash entries and wants to have a way to differentiate these symbols from others and hint that these are about reflog operation.

Perhaps prefix them with REFLOG_ or something?

Or, do we even NEED to expose these bits outside the implementation of reflog.c? If these three constants are used ONLY by reflog.c and not by builtin/reflog.c (or builtin/stash.c), then moving them to where they are used, i.e. in reflog.c, without exposing them to others in reflog.h, would be a far better thing to do. That way, they can stay with their original name, without having to add a differentiating prefix.

I also checked if any of the following struct's can be left as a private implementation detail, but they are shared between reflog.c and builtin/reflog.c and need to be in reflog.h

Quoted 38 lines
reflog.h new file +38 -0
+struct cmd_reflog_expire_cb {
+	int stalefix;
+	int explicit_expiry;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
+	int recno;
+};
+
+struct expire_reflog_policy_cb {
+	enum {
+		UE_NORMAL,
+		UE_ALWAYS,
+		UE_HEAD
+	} unreachable_expire_kind;
+	struct commit_list *mark_list;
+	unsigned long mark_limit;
+	struct cmd_reflog_expire_cb cmd;
+	struct commit *tip_commit;
+	struct commit_list *tips;
+	unsigned int dry_run:1;
+};
+
+int reflog_delete(const char*, int, int);
+void reflog_expiry_cleanup(void *);
+void reflog_expiry_prepare(const char*, const struct object_id*,
+			   void *);
+int should_expire_reflog_ent(struct object_id *, struct object_id*,
+				    const char *, timestamp_t, int,
+				    const char *, void *);
+int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
+		const char *email, timestamp_t timestamp, int tz,
+		const char *message, void *cb_data);
+int should_expire_reflog_ent_verbose(struct object_id *,
+				     struct object_id *,
+				     const char *,
+				     timestamp_t, int,
+				     const char *, void *);
+#endif /* REFLOG_H */
#13 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget
Quoted 9 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

I do not quite get this step. Yes, eventually cmd_reflog_delete() and cmd_stash() can both call the new reflog_delete() function, but at this step, cmd_reflog_delete() should ALREADY be able to call it, but I see duplicated code.

Why?

builtin/reflog.c static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) · line 326

Quoted 8 lines
builtin/reflog.c +1 -0
@@ -726,6 +320,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 	int i, status = 0;
 	unsigned int flags = 0;
 	int verbose = 0;
+
 	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
 	const struct option options[] = {
 		OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),

In other words, why the only change this step makes to cmd_reflog_delete() is a style change here, instead of replacing the logic inside its "while loop" with a call to the new helper function, which is introduced ...

reflog.c line 388

Quoted 8 lines
reflog.c new file +2 -0
new file mode 100644
index 00000000000..227ed83b3da
...
+int reflog_delete(const char *rev, int flags, int verbose)
+{

... here?

Quoted 6 lines
reflog.c new file +6 -0
+	struct cmd_reflog_expire_cb cmd = { 0 };
+	int status = 0;
+	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
+	const char *spec = strstr(rev, "@{");
+	char *ep, *ref;
+	int recno;

reflog.c line 404

Quoted 10 lines
reflog.c new file +10 -0
+	struct expire_reflog_policy_cb cb = {
+		.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+	};
+
+	if (verbose)
+		should_prune_fn = should_expire_reflog_ent_verbose;
+
+	if (!spec) {
+		status |= error(_("not a reflog: %s"), rev);
+	}

If this were "we moved one iteration of an existing loop to a separate function, while trying to keep what the existing code looked like as pristine as possible", then it is fine, but it does not look like that is what is going on (and I do not think it is feasible to do so, as the original being an interation in a loop, it has "continue" here, instead you'd need to arrange to return an error from here to allow the caller to work in a similar way).

So, if the patch is adding an adjusted implementation by removing "continue;" that is not suitable in the context of this helper, it should remove the now-unnecessary {braces} around the single-statement block.

But I think you should keep {braces} around, because the "if spec is NULL" case should not be a single-statement block. You'd need to return or this code is buggy.

reflog.c line 408

Quoted 3 lines
reflog.c new file +3 -0
+	if (!dwim_log(rev, spec - rev, NULL, &ref)) {
+		status |= error(_("no reflog for '%s'"), rev);
+	}

I already hinted that the previous "if spec is NULL" code is buggy because it does not return to allow the original caller to keep working as before. Because you lack an early return there, you'll end up calling dwim_log with spec==NULL here, which does not end well.

#16 lore raw

Re: [PATCH 2/3] reflog: call reflog_delete from reflog.c

In reply to Ævar Arnfjörð Bjarmason
Quoted 3 lines · Ævar Arnfjörð Bjarmason

Maybe others will disagree, but per my comment on 1/2 I found reviewing this locally much easier with this squashed into 1/2 (without the {} changes I suggested).

Oh, I am pretty much on the same page.

The if() block has to retain {} after all but not for the reason you cite (i.e. help "diff --color-moved"), but for correctness reasons it has to have some "early return to avoid using NULL" to replace "continue", which means the body of the if() statement needs to stay a two statement block.

#19 lore raw

Re: [PATCH 3/3] stash: call reflog_delete from reflog.c

In reply to Ævar Arnfjörð Bjarmason
Quoted 20 lines · Ævar Arnfjörð Bjarmason
builtin/stash.c +3 -5
index d0967b3d3c3..7b939576720 100644
@@ -635,11 +635,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-	int ret;
-	ret = reflog_delete(info->revision.buf,
-			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
-			    0);
-	if (!ret) {
+	unsigned int flags = EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE;
+
+	if (!reflog_delete(info->revision.buf, flags, 0)) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 				  oid_to_hex(&info->w_commit));
Quoted 4 lines

But, having written that I notice that we have *_REWRITE twice there, so I almost just carried forward a new bug in 3/3 when composing this :)

So one should be EXPIRE_REFLOGS_UPDATE_REF, presumably.

Thanks for pointing it out. Just thinking aloud here, the old code ran:

    git reflog delete --updateref --rewrite

where --updateref sets the EXPIRE_REFLOGS_UPDATE_REF bit and -rewrite sets the EXPIRE_REFLOGS_REWRITE bit.

So yep, you and I have the same conclusion there, and one of these flags should be the UPDATE_REF variant.

Quoted 2 lines

And perhaps it's a big pain, but that suggests that the code isn't either used at all, or that we're missing a test for it.

I think this is much more in the "lacking test coverage" category than the "unused features we should remove" one.

Thanks,
Taylor

#10 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to Junio C Hamano
Quoted 28 lines · Junio C Hamano
Quoted 6 lines · John Cai via GitGitGadget
reflog.h new file +0 -0
new file mode 100644
index 00000000000..e4a8a104f45

reflog.h line 10

Quoted 10 lines
reflog.h new file +10 -0
@@ -0,0 +1,49 @@
+#ifndef REFLOG_H
+#define REFLOG_H
+
+#include "cache.h"
+#include "commit.h"
+
+/* Remember to update object flag allocation in object.h */
+#define INCOMPLETE	(1u<<10)
+#define STUDYING	(1u<<11)
+#define REACHABLE	(1u<<12)

These names were unique enough back when they were part of internal implementation detail inside builtin/reflog.c but it no longer is in the scope it is visible. builtin/stash.c (and whatever other things that may want to deal with reflogs in the future) is about stash entries and wants to have a way to differentiate these symbols from others and hint that these are about reflog operation.

Perhaps prefix them with REFLOG_ or something?

Quoted 7 lines

Or, do we even NEED to expose these bits outside the implementation of reflog.c? If these three constants are used ONLY by reflog.c and not by builtin/reflog.c (or builtin/stash.c), then moving them to where they are used, i.e. in reflog.c, without exposing them to others in reflog.h, would be a far better thing to do. That way, they can stay with their original name, without having to add a differentiating prefix.

No objection to this, but FWIW the general pattern for these object.h flags is to use these un-prefixed names:

git grep -A20 'Remember to update object flag allocation' | grep define

To be fair the only one that's really comparable is the revision.h ones, but those are very non-namespace-y, with names like SEEN, ADDED, SHOWN etc.

I'd be fine with just leaving these as-is, especially with compilers warning about macro re-definitions.

#11 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

Quoted 42 lines · Ævar Arnfjörð Bjarmason
Quoted 28 lines · Junio C Hamano
Quoted 6 lines · John Cai via GitGitGadget
reflog.h new file +0 -0
new file mode 100644
index 00000000000..e4a8a104f45

reflog.h line 10

Quoted 10 lines
reflog.h new file +10 -0
@@ -0,0 +1,49 @@
+#ifndef REFLOG_H
+#define REFLOG_H
+
+#include "cache.h"
+#include "commit.h"
+
+/* Remember to update object flag allocation in object.h */
+#define INCOMPLETE	(1u<<10)
+#define STUDYING	(1u<<11)
+#define REACHABLE	(1u<<12)

These names were unique enough back when they were part of internal implementation detail inside builtin/reflog.c but it no longer is in the scope it is visible. builtin/stash.c (and whatever other things that may want to deal with reflogs in the future) is about stash entries and wants to have a way to differentiate these symbols from others and hint that these are about reflog operation.

Perhaps prefix them with REFLOG_ or something?

Quoted 7 lines

Or, do we even NEED to expose these bits outside the implementation of reflog.c? If these three constants are used ONLY by reflog.c and not by builtin/reflog.c (or builtin/stash.c), then moving them to where they are used, i.e. in reflog.c, without exposing them to others in reflog.h, would be a far better thing to do. That way, they can stay with their original name, without having to add a differentiating prefix.

No objection to this, but FWIW the general pattern for these object.h flags is to use these un-prefixed names:

Quoted 8 lines

git grep -A20 'Remember to update object flag allocation' | grep define

To be fair the only one that's really comparable is the revision.h ones, but those are very non-namespace-y, with names like SEEN, ADDED, SHOWN etc.

I'd be fine with just leaving these as-is, especially with compilers warning about macro re-definitions.

I think I have a mild preference to avoid polluting the set of macros with something like INCOMPLETE outside of reflog.c's compilation unit when possible. Though I don't really feel strongly either way.

In any case, let's make sure to update object.h's flag allocation table, which as of this patch still indicates that these bits belong to "builtin/reflog.c" (instead of "reflog.h" or "reflog.c", depending on what the outcome of this discussion is).

Thanks,
Taylor

#12 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to Ævar Arnfjörð Bjarmason
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 9 lines

Perhaps prefix them with REFLOG_ or something?

Or, do we even NEED to expose these bits outside the implementation of reflog.c? If these three constants are used ONLY by reflog.c and not by builtin/reflog.c (or builtin/stash.c), then moving them to where they are used, i.e. in reflog.c, without exposing them to others in reflog.h, would be a far better thing to do. That way, they can stay with their original name, without having to add a differentiating prefix.

Quoted 8 lines

No objection to this, but FWIW the general pattern for these object.h flags is to use these un-prefixed names:

git grep -A20 'Remember to update object flag allocation' | grep define

To be fair the only one that's really comparable is the revision.h ones, but those are very non-namespace-y, with names like SEEN, ADDED, SHOWN etc.

Yes, but I consider that revision.h owns the flag bits, and all others that are local to a single *.c files borrow leftover bits (and that is why the allocations can be overlapping).

And that is why my preference is to keep these within reflog.c if we are separating it out of builtin/reflog.c to make sure they do not pollute global namespace.

#6 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to Taylor Blau

Hey Taylor,

Quoted 30 lines · Taylor Blau
Quoted 5 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Sounds great. For other reviewers, looking at this with --color-moved if you have the patches applied locally makes it much easier to see what is going on here, which is basically:

- All of the implementation that used to be in builtin/reflog.c moved to reflog.c.

- The function signatures and structure declarations moved to reflog.h.

Quoted 3 lines

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

As you mentioned, the reflog_delete() implementation is indeed new. It looks like Ævar reviewed most of it already, so I'll take a look at his message before I end up repeating everything he already said ;).

It's worth noting that the subsequent clean-up to rewrite cmd_reflog_delete() in terms of your new reflog_delete() happens in the subsequent commit. If you end up rerolling this series, mentioning that here may be worthwhile.

Quoted 4 lines

One question that I had which I don't see answered already is what the plan is for existing reflog-related functions that live in refs.h. Should or will those functions be moved to the new reflog-specific header, too?

Thanks for bringing ths up! Maybe this cleanup can be included in a separate patch series since this one is mainly about getting rid of the shell-out in stash.c. What do you think?

Quoted 3 lines

Thanks,
Taylor

#7 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

Quoted 8 lines · John Cai
Quoted 4 lines

One question that I had which I don't see answered already is what the plan is for existing reflog-related functions that live in refs.h. Should or will those functions be moved to the new reflog-specific header, too?

Thanks for bringing ths up! Maybe this cleanup can be included in a separate patch series since this one is mainly about getting rid of the shell-out in stash.c. What do you think?

Yeah; I think that's fine. We don't need to get all reflog-related functions moved into reflog.h in the first series, so I think it's fine to move things as you discover them.

I was just wondering whether it was something you planned to do at some point (as part of this series, or a future one).

Thanks,
Taylor

#20 lore raw

Re: [PATCH 3/3] stash: call reflog_delete from reflog.c

In reply to Ævar Arnfjörð Bjarmason

Hi Ævar,

Quoted 74 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Now that cmd_reflog_delete has been libified an exported it into a new reflog.c library so we can call it directly from builtin/stash.c. This not only gives us a performance gain since we don't need to create a subprocess, but it also allows us to use the ref transactions api in the future.

Very nicely done, and nice that despite the ~500 lines added/removed in the diffstat that the "actual" changes in this series are so small.q

Review static int reflog_is_empty(const char *refname) · line 639

Quoted 6 lines
(quoted diff) +0 -2
@@ -635,18 +636,9 @@ static int reflog_is_empty(const char *refname)
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
 	int ret;
-	struct child_process cp_reflog = CHILD_PROCESS_INIT;
-

Nit: We usually separate variables decls with a \n\n, as is done in the pre-image, but you end up dropping that.

Quoted 6 lines
(quoted diff) +0 -6
-	/*
-	 * reflog does not provide a simple function for deleting refs. One will
-	 * need to be added to avoid implementing too much reflog code here
-	 */
-
-	cp_reflog.git_cmd = 1;

Review static int reflog_is_empty(const char *refname) · line 644

Quoted 10 lines
(quoted diff) +3 -4
-	strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-		     "--rewrite", NULL);
-	strvec_push(&cp_reflog.args, info->revision.buf);
-	ret = run_command(&cp_reflog);
+	ret = reflog_delete(info->revision.buf,
+			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
+			    0);
 	if (!ret) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,

I think per the above squashing this in would be nice, i.e. you get rid of the long line & it'sclear that "ret" is not used for anything now:

builtin/stash.c +3 -5
index d0967b3d3c3..7b939576720 100644
@@ -635,11 +635,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-	int ret;
-	ret = reflog_delete(info->revision.buf,
-			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
-			    0);
-	if (!ret) {
+	unsigned int flags = EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE;
+
+	if (!reflog_delete(info->revision.buf, flags, 0)) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 				  oid_to_hex(&info->w_commit));

But, having written that I notice that we have *_REWRITE twice there, so I almost just carried forward a new bug in 3/3 when composing this :)

So one should be EXPIRE_REFLOGS_UPDATE_REF, presumably.

And perhaps it's a big pain, but that suggests that the code isn't either used at all, or that we're missing a test for it.

Quoted 7 lines

So adding a prep commit to this series where we either drop it, or add the missing test would be a very nice addition.

See my quite recent 5ac15ad2509 (reflog tests: add --updateref tests, 2021-10-16) for adding such tests, but in that case I just covered "git reflog" itself, not "git stash". Maybe we can just add something to that for-loop in t1417 (or similar for a new stash test).

So I was trying to write a test to exercise the reflog --updateref and --rewrite cases. --updateref is pretty straightforward, but with --rewrite I noticed that it rewrites the old sha in the .git/log/refs/stash file. But, I was having trouble finding somewhere that read this value. The test could reach into this file and check the literal contents, but wondering if there is a better way. Any help appreciated!

Quoted 6 lines

Also, a s/unsigned int flags/enum expire_reflog_flags/ while we're at it would be very nice here, but that could be done as another small prep commit. I.e. it's *not* a new issue since cmd_reflog_delete() had it before, but when converting this to a documented API it would be very nice to have it reflect the actual type we end up using.

#21 lore raw

Re: [PATCH 3/3] stash: call reflog_delete from reflog.c

Quoted 81 lines · John Cai

Hi Ævar,

Quoted 77 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Now that cmd_reflog_delete has been libified an exported it into a new reflog.c library so we can call it directly from builtin/stash.c. This not only gives us a performance gain since we don't need to create a subprocess, but it also allows us to use the ref transactions api in the future.

Very nicely done, and nice that despite the ~500 lines added/removed in the diffstat that the "actual" changes in this series are so small.q

Review static int reflog_is_empty(const char *refname) · line 639

Quoted 6 lines
(quoted diff) +0 -2
@@ -635,18 +636,9 @@ static int reflog_is_empty(const char *refname)
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
 	int ret;
-	struct child_process cp_reflog = CHILD_PROCESS_INIT;
-

Nit: We usually separate variables decls with a \n\n, as is done in the pre-image, but you end up dropping that.

Quoted 6 lines
(quoted diff) +0 -6
-	/*
-	 * reflog does not provide a simple function for deleting refs. One will
-	 * need to be added to avoid implementing too much reflog code here
-	 */
-
-	cp_reflog.git_cmd = 1;

Review static int reflog_is_empty(const char *refname) · line 644

Quoted 10 lines
(quoted diff) +3 -4
-	strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-		     "--rewrite", NULL);
-	strvec_push(&cp_reflog.args, info->revision.buf);
-	ret = run_command(&cp_reflog);
+	ret = reflog_delete(info->revision.buf,
+			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
+			    0);
 	if (!ret) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,

I think per the above squashing this in would be nice, i.e. you get rid of the long line & it'sclear that "ret" is not used for anything now:

builtin/stash.c +3 -5
index d0967b3d3c3..7b939576720 100644
@@ -635,11 +635,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-	int ret;
-	ret = reflog_delete(info->revision.buf,
-			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
-			    0);
-	if (!ret) {
+	unsigned int flags = EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE;
+
+	if (!reflog_delete(info->revision.buf, flags, 0)) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 				  oid_to_hex(&info->w_commit));

But, having written that I notice that we have *_REWRITE twice there, so I almost just carried forward a new bug in 3/3 when composing this :)

So one should be EXPIRE_REFLOGS_UPDATE_REF, presumably.

And perhaps it's a big pain, but that suggests that the code isn't either used at all, or that we're missing a test for it.

So adding a prep commit to this series where we either drop it, or add the missing test would be a very nice addition.

Quoted 10 lines
Quoted 4 lines

See my quite recent 5ac15ad2509 (reflog tests: add --updateref tests, 2021-10-16) for adding such tests, but in that case I just covered "git reflog" itself, not "git stash". Maybe we can just add something to that for-loop in t1417 (or similar for a new stash test).

So I was trying to write a test to exercise the reflog --updateref and --rewrite cases. --updateref is pretty straightforward, but with --rewrite I noticed that it rewrites the old sha in the .git/log/refs/stash file. But, I was having trouble finding somewhere that read this value. The test could reach into this file and check the literal contents, but wondering if there is a better way. Any help appreciated!

I can check it out, but to make that easier can you share the WIP diff you have for getting the test setup that far?

I think you mean that it munges the SHA-1 on the LHS of .git/refs/logs/stash, I tried to do that now locally and I didn't come up with some way where you could observably make "git stash show", "git stash list" etc. show anything different.

I.e. it'll just promiscuously show whatever OID is on the RHS, even in cases where some of them are turned into "deadbeef..." (i.e. "list", but "show" will die).

So maybe it's just observable with a subsequent "git fsck", but I just tried composing a reflog with these entries:

    0000000000000000000000000000000000000000 A
    A B
    B C

And then manually changing it to:

    0000000000000000000000000000000000000000 A
    A B
    A C

Which "git fsck" will pass, and only complain if that "A" isn't a valid OID at all.

Anyway, if there isn't a way to get fsck/reflog to spew it out, but we want to assert that it's correct wouldn't this catch it (will need to depend on REFFILES) (untested):

	cut -d' ' -f1-2 .git/logs/refs/stash >actual &&
	cat >expect <<-EOF &&
	$(test_oid zero) $(git rev-parse ...)
	$(git rev-parse ...) $(git rev-parse ...)
	EOF
	test_cmp expect actual

I.e. to simply run whatever operation we do now, check that the OIDs match what we expect, and which would be different if one of these flags wasn't given?

Quoted 6 lines
Quoted 6 lines

Also, a s/unsigned int flags/enum expire_reflog_flags/ while we're at it would be very nice here, but that could be done as another small prep commit. I.e. it's *not* a new issue since cmd_reflog_delete() had it before, but when converting this to a documented API it would be very nice to have it reflect the actual type we end up using.

#23 lore raw

v2 cover libify reflog

In reply to John Cai via GitGitGadget

In [1], there was a discussion around a bug report of stash not recovering in the middle of the process when killed with ctl-c. It turned out to not be a bug we need to fix. However, out of that discussion came the idea of libifying reflog. This can stand alone as a code improvement.

stash.c currently shells out to call reflog to delete reflogs. Libify reflog delete and call it from both builtin/reflog.c and builtin/stash.c.

This patch has three parts:

* add missing test coverage for git stash delete
* libify reflog's delete functionality and move some of the helpers into a reflog.c library and call reflog_delete from builtin/reflog.c
* call reflog_delete from builtin/stash.c

Updates since v1:

* added missing test coverage
* squashed 1/3 and 2/3 together
* moved enum into reflog.c
* updated object.h's flag allocation mapping

1. https://lore.kernel.org/git/[email protected]/

John Cai (3):
stash: add test to ensure reflog --rewrite --updatref behavior
reflog: libify delete reflog function and helpers
stash: call reflog_delete() in reflog.c

 Makefile         |   1 +
 builtin/reflog.c | 451 +----------------------------------------------
 builtin/stash.c  |  18 +-
 object.h         |   2 +-
 reflog.c         | 435 +++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  49 +++++
 t/t3903-stash.sh |  25 ++-
 7 files changed, 518 insertions(+), 463 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h

base-commit: e6ebfd0e8cbbd10878070c8a356b5ad1b3ca464e
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1218%2Fjohn-cai%2Fjc-libify-reflog-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1218/john-cai/jc-libify-reflog-v2
Pull-Request: https://github.com/git/git/pull/1218

Range-diff vs v1:

 -:  ----------- > 1:  6e136b62ca4 stash: add test to ensure reflog --rewrite --updatref behavior
 1:  9e17ece8d89 ! 2:  e7c950218b1 reflog: libify delete reflog function and helpers
     @@ builtin/reflog.c: static int cmd_reflog_expire(int argc, const char **argv, cons
       static const char * reflog_delete_usage[] = {
       	N_("git reflog delete [--rewrite] [--updateref] "
       	   "[--dry-run | -n] [--verbose] <refs>..."),
     -@@ builtin/reflog.c: static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
     +@@ builtin/reflog.c: static const char * reflog_delete_usage[] = {
     + 
     + static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
     + {
     +-	struct cmd_reflog_expire_cb cmd = { 0 };
       	int i, status = 0;
       	unsigned int flags = 0;
       	int verbose = 0;
     +-	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
      +
     - 	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
       	const struct option options[] = {
       		OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
     + 			EXPIRE_REFLOGS_DRY_RUN),
     +@@ builtin/reflog.c: static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
     + 
     + 	argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
     + 
     +-	if (verbose)
     +-		should_prune_fn = should_expire_reflog_ent_verbose;
     +-
     + 	if (argc < 1)
     + 		return error(_("no reflog specified to delete"));
     + 
     +-	for (i = 0; i < argc; i++) {
     +-		const char *spec = strstr(argv[i], "@{");
     +-		char *ep, *ref;
     +-		int recno;
     +-		struct expire_reflog_policy_cb cb = {
     +-			.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
     +-		};
     +-
     +-		if (!spec) {
     +-			status |= error(_("not a reflog: %s"), argv[i]);
     +-			continue;
     +-		}
     ++	for (i = 0; i < argc; i++)
     ++		status |= reflog_delete(argv[i], flags, verbose);
     + 
     +-		if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
     +-			status |= error(_("no reflog for '%s'"), argv[i]);
     +-			continue;
     +-		}
     +-
     +-		recno = strtoul(spec + 2, &ep, 10);
     +-		if (*ep == '}') {
     +-			cmd.recno = -recno;
     +-			for_each_reflog_ent(ref, count_reflog_ent, &cmd);
     +-		} else {
     +-			cmd.expire_total = approxidate(spec + 2);
     +-			for_each_reflog_ent(ref, count_reflog_ent, &cmd);
     +-			cmd.expire_total = 0;
     +-		}
     +-
     +-		cb.cmd = cmd;
     +-		status |= reflog_expire(ref, flags,
     +-					reflog_expiry_prepare,
     +-					should_prune_fn,
     +-					reflog_expiry_cleanup,
     +-					&cb);
     +-		free(ref);
     +-	}
     + 	return status;
     + }
     + 
     +
     + ## object.h ##
     +@@ object.h: struct object_array {
     +  * builtin/fsck.c:           0--3
     +  * builtin/gc.c:             0
     +  * builtin/index-pack.c:                                     2021
     +- * builtin/reflog.c:                   10--12
     ++ * reflog.c:                           10--12
     +  * builtin/show-branch.c:    0-------------------------------------------26
     +  * builtin/unpack-objects.c:                                 2021
     +  */
      
       ## reflog.c (new) ##
      @@
     @@ reflog.c (new)
      +#include "tree-walk.h"
      +#include "worktree.h"
      +
     ++/* Remember to update object flag allocation in object.h */
     ++#define INCOMPLETE	(1u<<10)
     ++#define STUDYING	(1u<<11)
     ++#define REACHABLE	(1u<<12)
     ++
      +static int tree_is_complete(const struct object_id *oid)
      +{
      +	struct tree_desc desc;
     @@ reflog.c (new)
      +	return 0;
      +}
      +
     -+int reflog_delete(const char *rev, int flags, int verbose)
     ++int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
      +{
      +	struct cmd_reflog_expire_cb cmd = { 0 };
      +	int status = 0;
     @@ reflog.c (new)
      +	if (verbose)
      +		should_prune_fn = should_expire_reflog_ent_verbose;
      +
     -+	if (!spec) {
     -+		status |= error(_("not a reflog: %s"), rev);
     -+	}
     ++	if (!spec)
     ++		return error(_("not a reflog: %s"), rev);
      +
      +	if (!dwim_log(rev, spec - rev, NULL, &ref)) {
      +		status |= error(_("no reflog for '%s'"), rev);
     ++		goto cleanup;
      +	}
      +
     -+	if (status)
     -+		return status;
     -+
      +	recno = strtoul(spec + 2, &ep, 10);
      +	if (*ep == '}') {
      +		cmd.recno = -recno;
     @@ reflog.c (new)
      +				should_prune_fn,
      +				reflog_expiry_cleanup,
      +				&cb);
     -+	free(ref);
      +
     ++ cleanup:
     ++	free(ref);
      +	return status;
      +}
      
     @@ reflog.h (new)
      +#ifndef REFLOG_H
      +#define REFLOG_H
      +
     -+#include "cache.h"
     -+#include "commit.h"
     -+
     -+/* Remember to update object flag allocation in object.h */
     -+#define INCOMPLETE	(1u<<10)
     -+#define STUDYING	(1u<<11)
     -+#define REACHABLE	(1u<<12)
     ++#include "refs.h"
      +
      +struct cmd_reflog_expire_cb {
      +	int stalefix;
     @@ reflog.h (new)
      +	unsigned int dry_run:1;
      +};
      +
     -+int reflog_delete(const char*, int, int);
     -+void reflog_expiry_cleanup(void *);
     -+void reflog_expiry_prepare(const char*, const struct object_id*,
     -+			   void *);
     -+int should_expire_reflog_ent(struct object_id *, struct object_id*,
     -+				    const char *, timestamp_t, int,
     -+				    const char *, void *);
     ++int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose);
     ++
     ++void reflog_expiry_cleanup(void *cb_data);
     ++
     ++void reflog_expiry_prepare(const char *refname, const struct object_id *oid,
     ++			   void *cb_data);
     ++
     ++int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
     ++			     const char *email, timestamp_t timestamp, int tz,
     ++			     const char *message, void *cb_data);
     ++
      +int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
     -+		const char *email, timestamp_t timestamp, int tz,
     -+		const char *message, void *cb_data);
     -+int should_expire_reflog_ent_verbose(struct object_id *,
     -+				     struct object_id *,
     -+				     const char *,
     -+				     timestamp_t, int,
     -+				     const char *, void *);
     ++		     const char *email, timestamp_t timestamp, int tz,
     ++		     const char *message, void *cb_data);
     ++
     ++int should_expire_reflog_ent_verbose(struct object_id *ooid,
     ++				     struct object_id *noid,
     ++				     const char *email,
     ++				     timestamp_t timestamp, int tz,
     ++				     const char *message, void *cb_data);
     ++
      +#endif /* REFLOG_H */
 2:  e4c0047a17c < -:  ----------- reflog: call reflog_delete from reflog.c
 3:  bcc1eae0531 ! 3:  a023a70092b stash: call reflog_delete from reflog.c
     @@ Metadata
      Author: John Cai <[email protected]>
      
       ## Commit message ##
     -    stash: call reflog_delete from reflog.c
     +    stash: call reflog_delete() in reflog.c
      
          Now that cmd_reflog_delete has been libified an exported it into a new
          reflog.c library so we can call it directly from builtin/stash.c. This
     @@ builtin/stash.c
       #define INCLUDE_ALL_FILES 2
       
      @@ builtin/stash.c: static int reflog_is_empty(const char *refname)
     + 
       static int do_drop_stash(struct stash_info *info, int quiet)
       {
     - 	int ret;
     +-	int ret;
      -	struct child_process cp_reflog = CHILD_PROCESS_INIT;
      -
      -	/*
     @@ builtin/stash.c: static int reflog_is_empty(const char *refname)
      -		     "--rewrite", NULL);
      -	strvec_push(&cp_reflog.args, info->revision.buf);
      -	ret = run_command(&cp_reflog);
     -+	ret = reflog_delete(info->revision.buf,
     -+			    EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_REWRITE,
     -+			    0);
     - 	if (!ret) {
     +-	if (!ret) {
     ++	if (!reflog_delete(info->revision.buf,
     ++			   EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_UPDATE_REF,
     ++			   0)) {
       		if (!quiet)
       			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
     + 				  oid_to_hex(&info->w_commit));
Signature · 1 line
gitgitgadget
#24 lore raw

PATCH v2 1/3 stash: add test to ensure reflog --rewrite --updatref behavior

From: John Cai <[email protected]>

There is missing test coverage to ensure that the resulting reflogs after a git stash drop has had its old oid rewritten if applicable, and if the refs/stash has been updated if applicable.

Add two tests that verify both of these happen.

---
 t/t3903-stash.sh | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)
t/t3903-stash.sh +24 -1
index b149e2af441..ec9cc5646d6 100755
@@ -185,10 +185,33 @@ test_expect_success 'drop middle stash by index' '
 	test 1 = $(git show HEAD:file)
 '
 
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
+	git reset --hard &&
+	echo 9 >file &&
+	git stash &&
+	oid="$(git rev-parse stash@{0})" &&
+	git stash drop stash@{1} &&
+	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $oid
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 3 = $(cat file) &&
+	test 9 = $(cat file) &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)
Signature · 1 line
gitgitgadget
#37 lore raw

PATCH v2 2/3 reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

---
 Makefile         |   1 +
 builtin/reflog.c | 451 +----------------------------------------------
 object.h         |   2 +-
 reflog.c         | 435 +++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  49 +++++
 5 files changed, 490 insertions(+), 448 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h
Makefile +1 -0
index 6f0b4b775fe..876d4dfd6cb 100644
@@ -989,6 +989,7 @@ LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
+LIB_OBJS += reflog.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
builtin/reflog.c +4 -447

499 lines not on this page · raw message

object.h +1 -1
index cb556ab7753..a2219464c2b 100644
@@ -75,7 +75,7 @@ struct object_array {
  * builtin/fsck.c:           0--3
  * builtin/gc.c:             0
  * builtin/index-pack.c:                                     2021
- * builtin/reflog.c:                   10--12
+ * reflog.c:                           10--12
  * builtin/show-branch.c:    0-------------------------------------------26
  * builtin/unpack-objects.c:                                 2021
  */
reflog.c new file +435 -0

435 lines not on this page · raw message

reflog.h new file +49 -0

49 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#41 lore raw

PATCH v2 3/3 stash: call reflog_delete() in reflog.c

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

Now that cmd_reflog_delete has been libified an exported it into a new reflog.c library so we can call it directly from builtin/stash.c. This not only gives us a performance gain since we don't need to create a subprocess, but it also allows us to use the ref transactions api in the future.

---
 builtin/stash.c | 18 ++++--------------
 1 file changed, 4 insertions(+), 14 deletions(-)
builtin/stash.c +4 -14
index 5897febfbec..3e2f478b761 100644
@@ -17,6 +17,7 @@
 #include "diffcore.h"
 #include "exec-cmd.h"
 #include "entry.h"
+#include "reflog.h"
 
 #define INCLUDE_ALL_FILES 2
 
@@ -634,20 +635,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-	int ret;
-	struct child_process cp_reflog = CHILD_PROCESS_INIT;
-
-	/*
-	 * reflog does not provide a simple function for deleting refs. One will
-	 * need to be added to avoid implementing too much reflog code here
-	 */
-
-	cp_reflog.git_cmd = 1;
-	strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-		     "--rewrite", NULL);
-	strvec_push(&cp_reflog.args, info->revision.buf);
-	ret = run_command(&cp_reflog);
-	if (!ret) {
+	if (!reflog_delete(info->revision.buf,
+			   EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_UPDATE_REF,
+			   0)) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 				  oid_to_hex(&info->w_commit));
Signature · 1 line
gitgitgadget
#25 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to John Cai via GitGitGadget
Quoted 5 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

There is missing test coverage to ensure that the resulting reflogs after a git stash drop has had its old oid rewritten if applicable, and if the refs/stash has been updated if applicable.

This test looks good, and if 3/3 is applied and either of the flags you're passing is omitted they'll fail, so we know we have the missing coverage here.

Quoted 13 lines
---
 t/t3903-stash.sh | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)
t/t3903-stash.sh +0 -0
index b149e2af441..ec9cc5646d6 100755
@@ -185,10 +185,33 @@ test_expect_success 'drop middle stash by index' '
 	test 1 = $(git show HEAD:file)
 '

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 196

Quoted 10 lines
t/t3903-stash.sh +9 -0
 
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'

This one will be portable to the reftable backend.

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 197

Quoted 1 line
t/t3903-stash.sh +1 -0
+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

But as I noted in <[email protected]> (but it was easy to miss) this test will need to depend on REFFILES. So just changing this line to:

test_expect_success REFFILES 'drop stash[...]'

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 208

Quoted 11 lines
t/t3903-stash.sh +11 -0
+	git reset --hard &&
+	echo 9 >file &&
+	git stash &&
+	oid="$(git rev-parse stash@{0})" &&
+	git stash drop stash@{1} &&
+	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $oid
+	EOF
+	test_cmp expect actual
+'

Then:

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 215

Quoted 8 lines
t/t3903-stash.sh +1 -1
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 3 = $(cat file) &&
+	test 9 = $(cat file) &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)

This test was already a bit broken in needing the preceding tests, but it will break now if REFFILES isn't true, which you can reproduce e.g. with:

    ./t3903-stash.sh --run=1-16,18-50 -vixd

Perhaps the least sucky solution to that is:

t/t3903-stash.sh +8 -2
index ec9cc5646d6..1d11c9bda20 100755
@@ -205,13 +205,19 @@ test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	cat >expect <<-EOF &&
 	$(test_oid zero) $oid
 	EOF
-	test_cmp expect actual
+	test_cmp expect actual &&
+	>dropped-stash
 '
 
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 9 = $(cat file) &&
+	if test -e dropped-stash
+	then
+		test 9 = $(cat file)
+	else
+		test 3 = $(cat file)
+	fi &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)
#38 lore raw

Re: [PATCH v2 2/3] reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget
Quoted 62 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

---
 Makefile         |   1 +
 builtin/reflog.c | 451 +----------------------------------------------
 object.h         |   2 +-
 reflog.c         | 435 +++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  49 +++++
 5 files changed, 490 insertions(+), 448 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h
Makefile +1 -0
index 6f0b4b775fe..876d4dfd6cb 100644
@@ -989,6 +989,7 @@ LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
+LIB_OBJS += reflog.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
builtin/reflog.c +1 -4
index 85b838720c3..03d347e5832 100644
@@ -1,16 +1,13 @@
 #include "builtin.h"
 #include "config.h"
 #include "lockfile.h"
-#include "object-store.h"
 #include "repository.h"
-#include "commit.h"
-#include "refs.h"
 #include "dir.h"
-#include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
 #include "reachable.h"
 #include "worktree.h"
+#include "reflog.h"
[...]
reflog.c new file +0 -0
new file mode 100644
index 00000000000..8d57dc43503

reflog.c line 9

Quoted 9 lines
reflog.c new file +9 -0
@@ -0,0 +1,435 @@
+#include "cache.h"
+#include "commit.h"
+#include "object-store.h"
+#include "reachable.h"
+#include "reflog.h"
+#include "refs.h"
+#include "revision.h"
+#include "tree-walk.h"
+#include "worktree.h"

I think you missed some now-redundant headers, and copied over others we didn't need. This compiles for me with this on top:

builtin/reflog.c +0 -4
index 03d347e5832..940db196f62 100644
@@ -1,9 +1,5 @@
 #include "builtin.h"
 #include "config.h"
-#include "lockfile.h"
-#include "repository.h"
-#include "dir.h"
-#include "diff.h"
 #include "revision.h"
 #include "reachable.h"
 #include "worktree.h"
reflog.c +0 -3
index 8d57dc43503..333fd8708fe 100644
@@ -1,11 +1,8 @@
 #include "cache.h"
-#include "commit.h"
 #include "object-store.h"
-#include "reachable.h"
 #include "reflog.h"
 #include "refs.h"
 #include "revision.h"
-#include "tree-walk.h"
 #include "worktree.h"
 

But perhaps some of those are really "needed" but brought in implicitly?

reflog.h line 4

Quoted 11 lines

[...]

reflog.h new file +4 -0
new file mode 100644
index 00000000000..3427021cdc2
@@ -0,0 +1,49 @@
+#ifndef REFLOG_H
+#define REFLOG_H
+
+#include "refs.h"

Just a nit but I think the reflog_delete() should be wrapped (ends up at 80 cols), and the usual style in this project is to not whitespace-pad so much, i.e. this on top:

reflog.h +2 -8
index 3427021cdc2..d2906fb9f8d 100644
@@ -1,6 +1,5 @@
 #ifndef REFLOG_H
 #define REFLOG_H
-
 #include "refs.h"
 
 struct cmd_reflog_expire_cb {
@@ -25,25 +24,20 @@ struct expire_reflog_policy_cb {
 	unsigned int dry_run:1;
 };
 
-int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose);
-
+int reflog_delete(const char *rev, enum expire_reflog_flags flags,
+		  int verbose);
 void reflog_expiry_cleanup(void *cb_data);
-
 void reflog_expiry_prepare(const char *refname, const struct object_id *oid,
 			   void *cb_data);
-
 int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 			     const char *email, timestamp_t timestamp, int tz,
 			     const char *message, void *cb_data);
-
 int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
 		     const char *email, timestamp_t timestamp, int tz,
 		     const char *message, void *cb_data);
-
 int should_expire_reflog_ent_verbose(struct object_id *ooid,
 				     struct object_id *noid,
 				     const char *email,
 				     timestamp_t timestamp, int tz,
 				     const char *message, void *cb_data);
-
 #endif /* REFLOG_H */

Other than all that I really can't find anything at all to comment on, and I see that all the points raised in previous rounds by others were addressed.

#39 lore raw

Re: [PATCH v2 2/3] reflog: libify delete reflog function and helpers

Hi Ævar,

Thanks for the review!

Quoted 68 lines · Ævar Arnfjörð Bjarmason
Quoted 66 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

---
 Makefile         |   1 +
 builtin/reflog.c | 451 +----------------------------------------------
 object.h         |   2 +-
 reflog.c         | 435 +++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  49 +++++
 5 files changed, 490 insertions(+), 448 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h
Makefile +1 -0
index 6f0b4b775fe..876d4dfd6cb 100644
@@ -989,6 +989,7 @@ LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
+LIB_OBJS += reflog.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
builtin/reflog.c +1 -4
index 85b838720c3..03d347e5832 100644
@@ -1,16 +1,13 @@
 #include "builtin.h"
 #include "config.h"
 #include "lockfile.h"
-#include "object-store.h"
 #include "repository.h"
-#include "commit.h"
-#include "refs.h"
 #include "dir.h"
-#include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
 #include "reachable.h"
 #include "worktree.h"
+#include "reflog.h"
[...]
reflog.c new file +4 -0
new file mode 100644
index 00000000000..8d57dc43503
@@ -0,0 +1,435 @@
+#include "cache.h"
+#include "commit.h"
+#include "object-store.h"
+#include "reachable.h"
Quoted 8 lines

reflog.c line 9

Quoted 5 lines
reflog.c new file +5 -0
+#include "reflog.h"
+#include "refs.h"
+#include "revision.h"
+#include "tree-walk.h"
+#include "worktree.h"

I think you missed some now-redundant headers, and copied over others we didn't need. This compiles for me with this on top:

Ah yeah, looks I left these in by mistake. Thanks for catching this.

Quoted 32 lines
builtin/reflog.c +0 -4
index 03d347e5832..940db196f62 100644
@@ -1,9 +1,5 @@
 #include "builtin.h"
 #include "config.h"
-#include "lockfile.h"
-#include "repository.h"
-#include "dir.h"
-#include "diff.h"
 #include "revision.h"
 #include "reachable.h"
 #include "worktree.h"
reflog.c +0 -3
index 8d57dc43503..333fd8708fe 100644
@@ -1,11 +1,8 @@
 #include "cache.h"
-#include "commit.h"
 #include "object-store.h"
-#include "reachable.h"
 #include "reflog.h"
 #include "refs.h"
 #include "revision.h"
-#include "tree-walk.h"
 #include "worktree.h"
 

But perhaps some of those are really "needed" but brought in implicitly?

Quoted 15 lines

reflog.h line 4

Quoted 11 lines

[...]

reflog.h new file +4 -0
new file mode 100644
index 00000000000..3427021cdc2
@@ -0,0 +1,49 @@
+#ifndef REFLOG_H
+#define REFLOG_H
+
+#include "refs.h"

Just a nit but I think the reflog_delete() should be wrapped (ends up at 80 cols), and the usual style in this project is to not whitespace-pad so much, i.e. this on top:

sounds good!

Quoted 45 lines
reflog.h +2 -8
index 3427021cdc2..d2906fb9f8d 100644
@@ -1,6 +1,5 @@
 #ifndef REFLOG_H
 #define REFLOG_H
-
 #include "refs.h"
 
 struct cmd_reflog_expire_cb {
@@ -25,25 +24,20 @@ struct expire_reflog_policy_cb {
 	unsigned int dry_run:1;
 };
 
-int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose);
-
+int reflog_delete(const char *rev, enum expire_reflog_flags flags,
+		  int verbose);
 void reflog_expiry_cleanup(void *cb_data);
-
 void reflog_expiry_prepare(const char *refname, const struct object_id *oid,
 			   void *cb_data);
-
 int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 			     const char *email, timestamp_t timestamp, int tz,
 			     const char *message, void *cb_data);
-
 int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
 		     const char *email, timestamp_t timestamp, int tz,
 		     const char *message, void *cb_data);
-
 int should_expire_reflog_ent_verbose(struct object_id *ooid,
 				     struct object_id *noid,
 				     const char *email,
 				     timestamp_t timestamp, int tz,
 				     const char *message, void *cb_data);
-
 #endif /* REFLOG_H */

Other than all that I really can't find anything at all to comment on, and I see that all the points raised in previous rounds by others were addressed.

#8 lore raw

Re: [PATCH 1/3] reflog: libify delete reflog function and helpers

In reply to Taylor Blau

Hi Taylor,

Quoted 10 lines · Taylor Blau
Quoted 8 lines · John Cai
Quoted 4 lines

One question that I had which I don't see answered already is what the plan is for existing reflog-related functions that live in refs.h. Should or will those functions be moved to the new reflog-specific header, too?

Thanks for bringing ths up! Maybe this cleanup can be included in a separate patch series since this one is mainly about getting rid of the shell-out in stash.c. What do you think?

Quoted 6 lines

Yeah; I think that's fine. We don't need to get all reflog-related functions moved into reflog.h in the first series, so I think it's fine to move things as you discover them.

I was just wondering whether it was something you planned to do at some point (as part of this series, or a future one).

Yes I will open a new series for this. I've been looking through refs.h, and as you alluded to there are a number of reflog related functions. But, they also relate to refs. I'm not sure which ones should go into reflog.h versus staying in refs.h. What are your thoughts?

Quoted 3 lines

Thanks,
Taylor

Thanks!
John

#26 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to Ævar Arnfjörð Bjarmason
Quoted 13 lines · Ævar Arnfjörð Bjarmason

This test was already a bit broken in needing the preceding tests, but it will break now if REFFILES isn't true, which you can reproduce e.g. with:

    ./t3903-stash.sh --run=1-16,18-50 -vixd

Perhaps the least sucky solution to that is:

t/t3903-stash.sh +0 -0
index ec9cc5646d6..1d11c9bda20 100755

t/t3903-stash.sh test_expect_success 'drop stash reflog updates refs/stash with rewrite' ' · line 210

Quoted 7 lines
t/t3903-stash.sh +2 -1
@@ -205,13 +205,19 @@ test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	cat >expect <<-EOF &&
 	$(test_oid zero) $oid
 	EOF
-	test_cmp expect actual
+	test_cmp expect actual &&
+	>dropped-stash
 '

If "git stash drop", invoked in earlier part of this test before the precontext, fails, then test_cmp would fail and we leave dropped-stash untouched, even though we did run "git stash drop" already.

Why does the next test need to depend on what has happened earlier?

Quoted 13 lines
t/t3903-stash.sh +6 -1
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 9 = $(cat file) &&
+	if test -e dropped-stash
+	then
+		test 9 = $(cat file)
+	else
+		test 3 = $(cat file)
+	fi &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)
#40 lore raw

Re: [PATCH v2 2/3] reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget
Quoted 14 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

Quoted 9 lines
---
 Makefile         |   1 +
 builtin/reflog.c | 451 +----------------------------------------------
 object.h         |   2 +-
 reflog.c         | 435 +++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  49 +++++
 5 files changed, 490 insertions(+), 448 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h

This round, without polluting global namespace in reflog.h by moving too many things there and instead keeping what is private to the implementation in reflog.c, looks much better than the previous round.

Looking good.

Thanks.

#27 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to Junio C Hamano

Hi Junio,

Quoted 28 lines · Junio C Hamano
Quoted 13 lines · Ævar Arnfjörð Bjarmason

This test was already a bit broken in needing the preceding tests, but it will break now if REFFILES isn't true, which you can reproduce e.g. with:

    ./t3903-stash.sh --run=1-16,18-50 -vixd

Perhaps the least sucky solution to that is:

t/t3903-stash.sh +0 -0
index ec9cc5646d6..1d11c9bda20 100755

t/t3903-stash.sh test_expect_success 'drop stash reflog updates refs/stash with rewrite' ' · line 210

Quoted 7 lines
t/t3903-stash.sh +2 -1
@@ -205,13 +205,19 @@ test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	cat >expect <<-EOF &&
 	$(test_oid zero) $oid
 	EOF
-	test_cmp expect actual
+	test_cmp expect actual &&
+	>dropped-stash
 '

If "git stash drop", invoked in earlier part of this test before the precontext, fails, then test_cmp would fail and we leave dropped-stash untouched, even though we did run "git stash drop" already.

Quoted 1 line

Why does the next test need to depend on what has happened earlier?

Ideally it shouldn't, but it seems like the way these test have been written makes subsequent tests depend on what previous tests have stashed.

I'm wondering now that while we're at it, if we should just clean up these tests so there are no dependencies between the tests. Otherwise it's quite painful the next time someone needs to add a test here.

We could follow the pattern in 5ac15ad2509 (reflog tests: add --updateref tests, 2021-10-16), where Ævar set up the tests and copied over the repo so each test is isolated from each other.

Quoted 14 lines
Quoted 14 lines
t/t3903-stash.sh +6 -1
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 9 = $(cat file) &&
+	if test -e dropped-stash
+	then
+		test 9 = $(cat file)
+	else
+		test 3 = $(cat file)
+	fi &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)
#28 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to Junio C Hamano
Quoted 19 lines · Junio C Hamano
Quoted 17 lines · Ævar Arnfjörð Bjarmason

This test was already a bit broken in needing the preceding tests, but it will break now if REFFILES isn't true, which you can reproduce e.g. with:

    ./t3903-stash.sh --run=1-16,18-50 -vixd

Perhaps the least sucky solution to that is:

t/t3903-stash.sh +0 -1
index ec9cc5646d6..1d11c9bda20 100755
@@ -205,13 +205,19 @@ test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	cat >expect <<-EOF &&
 	$(test_oid zero) $oid
 	EOF
-	test_cmp expect actual
Quoted 8 lines

t/t3903-stash.sh test_expect_success 'drop stash reflog updates refs/stash with rewrite' ' · line 210

Quoted 3 lines
t/t3903-stash.sh +2 -0
+	test_cmp expect actual &&
+	>dropped-stash
 '

If "git stash drop", invoked in earlier part of this test before the precontext, fails, then test_cmp would fail and we leave dropped-stash untouched, even though we did run "git stash drop" already.

Yes, that's an edge case that's exposed here, but which I thought wasn't worth bothering with. I.e. if you get such a failure on test N getting N+1 failing as well isn't that big of a deal.

The big deal is rather that we know we're adding a REFFILES dependency to this, which won't run this at all, which will make the "stash pop" below fail.

Quoted 1 line

Why does the next test need to depend on what has happened earlier?

They don't need to, and ideally wouldn't, but most of our test suite has this issue already. Try e.g. running it with:

    prove t[0-9]*.sh :: --run=10-20 --immediate

And for this particular file running e.g. this on master:

    ./t3903-stash.sh --run=1-10,30-40

Will fail 7 tests in the 30-40 range.

So while it's ideal that we can combine tests with arbitrary --run parameters, i.e. all tests would tear down fully, not depend on earlier tests etc. we're really far from that being the case in practice.

So insisting on some general refactoring of this file as part of this series seems a bit overzelous, which is why I'm suggesting the bare minimum to expect and work around the inevitable REFFILES failure, as Han-Wen is actively working in that area.

Quoted 13 lines
Quoted 13 lines
t/t3903-stash.sh +6 -1
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 9 = $(cat file) &&
+	if test -e dropped-stash
+	then
+		test 9 = $(cat file)
+	else
+		test 3 = $(cat file)
+	fi &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)
#32 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to Ævar Arnfjörð Bjarmason
Quoted 20 lines · Ævar Arnfjörð Bjarmason

Review

Quoted 9 lines
(quoted diff) +9 -0
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'

This one will be portable to the reftable backend.

Quoted 1 line

+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

But as I noted in <[email protected]> (but it was easy to miss) this test will need to depend on REFFILES. So just changing this line to:

test_expect_success REFFILES 'drop stash[...]'

Quoted 11 lines

Review

Quoted 11 lines
(quoted diff) +11 -0
+	git reset --hard &&
+	echo 9 >file &&
+	git stash &&
+	oid="$(git rev-parse stash@{0})" &&
+	git stash drop stash@{1} &&
+	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $oid
+	EOF
+	test_cmp expect actual
+'

Why should this be tested with "cut" in the first place, though?

If we start from

    stash@{0} = A
    stash@{1} = B
    stash@{2} = C

and after saying "drop stash@{1}", what we need to check is that

    stash@{0} = A
    stash@{1} = C

now holds, which can be done with "git rev-parse", and the fact that the ref-files backend happens to record both before-and-after object IDs is an irrelevant implementation detail, no?

I am still puzzled.

#33 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

Hi Junio,

Quoted 38 lines · Junio C Hamano
Quoted 20 lines · Ævar Arnfjörð Bjarmason

Review

Quoted 9 lines
(quoted diff) +9 -0
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'

This one will be portable to the reftable backend.

Quoted 1 line

+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

But as I noted in <[email protected]> (but it was easy to miss) this test will need to depend on REFFILES. So just changing this line to:

test_expect_success REFFILES 'drop stash[...]'

Quoted 11 lines

Review

Quoted 11 lines
(quoted diff) +11 -0
+	git reset --hard &&
+	echo 9 >file &&
+	git stash &&
+	oid="$(git rev-parse stash@{0})" &&
+	git stash drop stash@{1} &&
+	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $oid
+	EOF
+	test_cmp expect actual
+'

Why should this be tested with "cut" in the first place, though?

If we start from

Quoted 8 lines
    stash@{0} = A
    stash@{1} = B
    stash@{2} = C

and after saying "drop stash@{1}", what we need to check is that

    stash@{0} = A
    stash@{1} = C

Yes, this is true but that doesn't seem to test the --rewrite functionality. I could be missing something, but it seems that the reflog --rewrite option will write the LHS old oid value in the .git/logs/refs/stash file. When --rewrite isn't used, the reflog delete still does the right thing to the RHS entry.

I couldn't find any way to check this LFS value other than reaching into the actual file. If there is a way that would be preferable.

Quoted 6 lines

now holds, which can be done with "git rev-parse", and the fact that the ref-files backend happens to record both before-and-after object IDs is an irrelevant implementation detail, no?

I am still puzzled.

#34 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

Quoted 8 lines · John Cai

Yes, this is true but that doesn't seem to test the --rewrite functionality. I could be missing something, but it seems that the reflog --rewrite option will write the LHS old oid value in the .git/logs/refs/stash file. When --rewrite isn't used, the reflog delete still does the right thing to the RHS entry.

I couldn't find any way to check this LFS value other than reaching into the actual file. If there is a way that would be preferable.

Ah, that one.

As 2b81fab2 (git-reflog: add option --rewrite to update reflog entries while expiring, 2008-02-22) says, the redundant half of the reflog entry only matters to "certain sanity checks" and would not be even visible to normal ref API users. I wonder why we need to even say "--rewrite" in the first place. Perhaps we should implicitly set it always and eventually deprecate that option.

#36 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to John Cai
Quoted 51 lines · John Cai

Hi Junio,

Quoted 38 lines · Junio C Hamano
Quoted 20 lines · Ævar Arnfjörð Bjarmason

Review

Quoted 9 lines
(quoted diff) +9 -0
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'

This one will be portable to the reftable backend.

Quoted 1 line

+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

But as I noted in <[email protected]> (but it was easy to miss) this test will need to depend on REFFILES. So just changing this line to:

test_expect_success REFFILES 'drop stash[...]'

Quoted 11 lines

Review

Quoted 11 lines
(quoted diff) +11 -0
+	git reset --hard &&
+	echo 9 >file &&
+	git stash &&
+	oid="$(git rev-parse stash@{0})" &&
+	git stash drop stash@{1} &&
+	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $oid
+	EOF
+	test_cmp expect actual
+'

Why should this be tested with "cut" in the first place, though?

If we start from

Quoted 8 lines
    stash@{0} = A
    stash@{1} = B
    stash@{2} = C

and after saying "drop stash@{1}", what we need to check is that

    stash@{0} = A
    stash@{1} = C
Quoted 8 lines

Yes, this is true but that doesn't seem to test the --rewrite functionality. I could be missing something, but it seems that the reflog --rewrite option will write the LHS old oid value in the .git/logs/refs/stash file. When --rewrite isn't used, the reflog delete still does the right thing to the RHS entry.

I couldn't find any way to check this LFS value other than reaching into the actual file. If there is a way that would be preferable.

Thanks for that summary that's accurate as far as I know. I think that's how this all works, and I don't know of another way to extract this information than this reaching behind the curtain.

Which, I think is a lot clearer if we amend the test like this. Note that this doesn't really add anything for catching a regression goes, but I think helps guide the human reader through this step-by-step. So perhaps it would be good to fix the test up to have it (or maybe not):

t/t3903-stash.sh +15 -2
index ec9cc5646d6..bc58e99e3e6 100755
@@ -198,12 +198,25 @@ test_expect_success 'drop stash reflog updates refs/stash' '
 test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	git reset --hard &&
 	echo 9 >file &&
+
+	# Our two stashes
+	old_oid="$(git rev-parse stash@{0})" &&
 	git stash &&
-	oid="$(git rev-parse stash@{0})" &&
+	new_oid="$(git rev-parse stash@{0})" &&
+
+	# Our stash <old oid>/<new oid> before "drop"
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	# Our stash <old oid>/<new oid> after "drop"
 	git stash drop stash@{1} &&
 	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
 	cat >expect <<-EOF &&
-	$(test_oid zero) $oid
+	$(test_oid zero) $new_oid
 	EOF
 	test_cmp expect actual
 '

If this series is amended to drop the "EXPIRE_REFLOGS_REWRITE" flag then this will fail on that last test_cmp like:

    + diff -u expect actual
    --- expect      2022-02-23 23:37:40.438221222 +0000
    +++ actual      2022-02-23 23:37:40.434221258 +0000
    @@ -1 +1 @@
    -0000000000000000000000000000000000000000 236c59f58e239e74e90b6832a98fa4b7f4b33647
    +5c6ad4ca28e71ae3a007e6c77043d04bc42fa9ee 236c59f58e239e74e90b6832a98fa4b7f4b33647

I.e. our <old oid> is now referring to the now-deleted stash entry we just deleted, since we didn't rewrite it.

And as we can see with some manual inspection the state before we dropped stash@{1} was:

    0000000000000000000000000000000000000000 5c6ad4ca28e71ae3a007e6c77043d04bc42fa9ee
    5c6ad4ca28e71ae3a007e6c77043d04bc42fa9ee 236c59f58e239e74e90b6832a98fa4b7f4b33647

My usual method of checking my assumption about this not being otherwise inspectable would be something like:

    diff --git a/refs/files-backend.c b/refs/files-backend.c
    index f59589d6cce..590c13e7a2b 100644
    --- a/refs/files-backend.c
    +++ b/refs/files-backend.c
    @@ -3133,7 +3133,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
            const struct object_id *oid;
     
            memset(&cb, 0, sizeof(cb));
    -       cb.rewrite = !!(expire_flags & EXPIRE_REFLOGS_REWRITE);
    +       cb.rewrite = 0;
            cb.dry_run = !!(expire_flags & EXPIRE_REFLOGS_DRY_RUN);
            cb.policy_cb = policy_cb_data;
            cb.should_prune_fn = should_prune_fn;

I.e. let's intentionally break the flag, and see what else fails (it's set in a few places, but this is the only place where it's checked).

That should normally find the other things that are testing this, maybe there's a better way.

But, no such luck :) The only thing that'll fail is this new test being added here.

So just like my 5ac15ad2509 (reflog tests: add --updateref tests, 2021-10-16) this is covering a true blindspot in the "git reflog" functionality.

The only tests that used --rewrite were a test added in c41a87dd80c (refs: make rev-parse --quiet actually quiet, 2014-09-18), which will pass if --rewrite is omitted.

And the ones I added in 5ac15ad2509, which I added not to test --rewrite per-se, but to test that the --updateref part of it behaved as expected in combination with whatever effect it was having.

#35 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to Junio C Hamano

Hi Junio,

Quoted 11 lines · Junio C Hamano
Quoted 8 lines · John Cai

Yes, this is true but that doesn't seem to test the --rewrite functionality. I could be missing something, but it seems that the reflog --rewrite option will write the LHS old oid value in the .git/logs/refs/stash file. When --rewrite isn't used, the reflog delete still does the right thing to the RHS entry.

I couldn't find any way to check this LFS value other than reaching into the actual file. If there is a way that would be preferable.

Quoted 8 lines

Ah, that one.

As 2b81fab2 (git-reflog: add option --rewrite to update reflog entries while expiring, 2008-02-22) says, the redundant half of the reflog entry only matters to "certain sanity checks" and would not be even visible to normal ref API users. I wonder why we need to even say "--rewrite" in the first place. Perhaps we should implicitly set it always and eventually deprecate that option.

Yeah, that makes sense. I had this thought as I was figuring out how to test this. I can take care of this in a separate patch series

#29 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

In reply to Ævar Arnfjörð Bjarmason

Hi Ævar,

Quoted 51 lines · Ævar Arnfjörð Bjarmason
Quoted 19 lines · Junio C Hamano
Quoted 17 lines · Ævar Arnfjörð Bjarmason

This test was already a bit broken in needing the preceding tests, but it will break now if REFFILES isn't true, which you can reproduce e.g. with:

    ./t3903-stash.sh --run=1-16,18-50 -vixd

Perhaps the least sucky solution to that is:

t/t3903-stash.sh +0 -1
index ec9cc5646d6..1d11c9bda20 100755
@@ -205,13 +205,19 @@ test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	cat >expect <<-EOF &&
 	$(test_oid zero) $oid
 	EOF
-	test_cmp expect actual
Quoted 8 lines

t/t3903-stash.sh test_expect_success 'drop stash reflog updates refs/stash with rewrite' ' · line 210

Quoted 3 lines
t/t3903-stash.sh +2 -0
+	test_cmp expect actual &&
+	>dropped-stash
 '

If "git stash drop", invoked in earlier part of this test before the precontext, fails, then test_cmp would fail and we leave dropped-stash untouched, even though we did run "git stash drop" already.

Yes, that's an edge case that's exposed here, but which I thought wasn't worth bothering with. I.e. if you get such a failure on test N getting N+1 failing as well isn't that big of a deal.

The big deal is rather that we know we're adding a REFFILES dependency to this, which won't run this at all, which will make the "stash pop" below fail.

Quoted 1 line

Why does the next test need to depend on what has happened earlier?

They don't need to, and ideally wouldn't, but most of our test suite has this issue already. Try e.g. running it with:

    prove t[0-9]*.sh :: --run=10-20 --immediate

And for this particular file running e.g. this on master:

    ./t3903-stash.sh --run=1-10,30-40

Will fail 7 tests in the 30-40 range.

Quoted 8 lines

So while it's ideal that we can combine tests with arbitrary --run parameters, i.e. all tests would tear down fully, not depend on earlier tests etc. we're really far from that being the case in practice.

So insisting on some general refactoring of this file as part of this series seems a bit overzelous, which is why I'm suggesting the bare minimum to expect and work around the inevitable REFFILES failure, as Han-Wen is actively working in that area.

Curious what your thoughts are on an effort to isolate these tests from each other. I like your approach in t/t1417 in creating a test repo and copying it over each time. Something like this?

t/t3903-stash.sh +22 -12
index ac345eced8cb..40254f8dc99c 100755
@@ -41,7 +41,9 @@ diff_cmp () {
        rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
+test_expect_success 'setup' '
+       git init repo &&
+       cd repo &&
        echo 1 >file &&
        git add file &&
        echo unrelated >other-file &&
@@ -54,48 +56,54 @@ test_expect_success 'stash some dirty working directory' '
        test_tick &&
        git stash &&
        git diff-files --quiet &&
-       git diff-index --cached --quiet HEAD
+       git diff-index --cached --quiet HEAD &&
+       cat >expect <<-EOF &&
+       diff --git a/file b/file
+       index 0cfbf08..00750ed 100644
+       --- a/file
+       +++ b/file
+       @@ -1 +1 @@
+       -2
+       +3
+       EOF
+       cd ../
 '
 
-cat >expect <<EOF
-diff --git a/file b/file
-index 0cfbf08..00750ed 100644
---- a/file
-+++ b/file
-@@ -1 +1 @@
--2
-+3
-EOF
+test_stash () {
+       cp -R repo copy &&
+       cd copy &&
+       test_expect_success "$@" &&
+       cd ../ &&
+       rm -rf copy
+}
 
-test_expect_success 'parents of stash' '
+test_stash 'parents of stash' '
        test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
        git diff stash^2..stash >output &&
        diff_cmp expect output
 '
 

Not sure if it's worth it though?

Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
t/t3903-stash.sh +6 -1
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 9 = $(cat file) &&
+	if test -e dropped-stash
+	then
+		test 9 = $(cat file)
+	else
+		test 3 = $(cat file)
+	fi &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)
#30 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

Quoted 59 lines · John Cai

Hi Ævar,

Quoted 55 lines · Ævar Arnfjörð Bjarmason
Quoted 19 lines · Junio C Hamano
Quoted 17 lines · Ævar Arnfjörð Bjarmason

This test was already a bit broken in needing the preceding tests, but it will break now if REFFILES isn't true, which you can reproduce e.g. with:

    ./t3903-stash.sh --run=1-16,18-50 -vixd

Perhaps the least sucky solution to that is:

t/t3903-stash.sh +0 -1
index ec9cc5646d6..1d11c9bda20 100755
@@ -205,13 +205,19 @@ test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	cat >expect <<-EOF &&
 	$(test_oid zero) $oid
 	EOF
-	test_cmp expect actual
Quoted 8 lines

t/t3903-stash.sh test_expect_success 'drop stash reflog updates refs/stash with rewrite' ' · line 210

Quoted 3 lines
t/t3903-stash.sh +2 -0
+	test_cmp expect actual &&
+	>dropped-stash
 '

If "git stash drop", invoked in earlier part of this test before the precontext, fails, then test_cmp would fail and we leave dropped-stash untouched, even though we did run "git stash drop" already.

Yes, that's an edge case that's exposed here, but which I thought wasn't worth bothering with. I.e. if you get such a failure on test N getting N+1 failing as well isn't that big of a deal.

The big deal is rather that we know we're adding a REFFILES dependency to this, which won't run this at all, which will make the "stash pop" below fail.

Quoted 1 line

Why does the next test need to depend on what has happened earlier?

They don't need to, and ideally wouldn't, but most of our test suite has this issue already. Try e.g. running it with:

    prove t[0-9]*.sh :: --run=10-20 --immediate

And for this particular file running e.g. this on master:

    ./t3903-stash.sh --run=1-10,30-40

Will fail 7 tests in the 30-40 range.

So while it's ideal that we can combine tests with arbitrary --run parameters, i.e. all tests would tear down fully, not depend on earlier tests etc. we're really far from that being the case in practice.

Quoted 8 lines
Quoted 4 lines

So insisting on some general refactoring of this file as part of this series seems a bit overzelous, which is why I'm suggesting the bare minimum to expect and work around the inevitable REFFILES failure, as Han-Wen is actively working in that area.

Curious what your thoughts are on an effort to isolate these tests from each other. I like your approach in t/t1417 in creating a test repo and copying it over each time. Something like this?

That looks good to me if you're willing to do that legwork, probably better in a preceding cleanup commit.

Quoted 16 lines
t/t3903-stash.sh +3 -1
index ac345eced8cb..40254f8dc99c 100755
@@ -41,7 +41,9 @@ diff_cmp () {
        rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
+test_expect_success 'setup' '
+       git init repo &&
+       cd repo &&
        echo 1 >file &&
        git add file &&
        echo unrelated >other-file &&

t/t3903-stash.sh test_expect_success 'stash some dirty working directory' ' · line 60

Quoted 6 lines
t/t3903-stash.sh +2 -1
@@ -54,48 +56,54 @@ test_expect_success 'stash some dirty working directory' '
        test_tick &&
        git stash &&
        git diff-files --quiet &&
-       git diff-index --cached --quiet HEAD
+       git diff-index --cached --quiet HEAD &&
+       cat >expect <<-EOF &&

nit: you can add \ to that, i.e. <<-\EOF. Helps readability, i.e. it's obvious right away that no variables are in play..

Quoted 25 lines
t/t3903-stash.sh +14 -9
+       diff --git a/file b/file
+       index 0cfbf08..00750ed 100644
+       --- a/file
+       +++ b/file
+       @@ -1 +1 @@
+       -2
+       +3
+       EOF
+       cd ../
 '
 
-cat >expect <<EOF
-diff --git a/file b/file
-index 0cfbf08..00750ed 100644
---- a/file
-+++ b/file
-@@ -1 +1 @@
--2
-+3
-EOF
+test_stash () {
+       cp -R repo copy &&
+       cd copy &&
+       test_expect_success "$@" &&
+       cd ../ &&

t/t3903-stash.sh test_expect_success 'stash some dirty working directory' '

Quoted 10 lines
t/t3903-stash.sh +3 -1
+       rm -rf copy
+}
 
 
-test_expect_success 'parents of stash' '
+test_stash 'parents of stash' '
        test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
        git diff stash^2..stash >output &&
        diff_cmp expect output
 '

For this sort of thing I think it's usually better to override "test_expect_success" as a last resort, i.e. to have that "test_setup_stash_copy" just be a "setup_stash" or whatever function called from within your test_expect_success.

And instead of the "rm -rf" later, just do:

    test_when_finished "rm -rf copy" &&
    cp -R repo copy &&
    [...]

The test still needs to deal with the sub-repo, but it could cd or use "-C".

It's bad to add "cd .." in a &&-chain, because if earlier steps fail we're in the wrong directory for the next test, so either -C or a sub-shell...

Quoted 1 line

Not sure if it's worth it though?

Maybe not, which is why I suggested upthread to maybe go for some smallest possible change here and focus on the lib-ificitaion :)

Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
t/t3903-stash.sh +6 -1
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
-	test 9 = $(cat file) &&
+	if test -e dropped-stash
+	then
+		test 9 = $(cat file)
+	else
+		test 3 = $(cat file)
+	fi &&
 	test 1 = $(git show :file) &&
 	test 1 = $(git show HEAD:file) &&
 	test 0 = $(git stash list | wc -l)
#31 lore raw

Re: [PATCH v2 1/3] stash: add test to ensure reflog --rewrite --updatref behavior

Quoted 6 lines · Ævar Arnfjörð Bjarmason
Quoted 3 lines

Curious what your thoughts are on an effort to isolate these tests from each other. I like your approach in t/t1417 in creating a test repo and copying it over each time. Something like this?

That looks good to me if you're willing to do that legwork, probably better in a preceding cleanup commit.

Yup. Thanks for helping other contributors. I agree with many things you said in your review.

Quoted 8 lines
Quoted 8 lines
t/t3903-stash.sh +0 -0
index ac345eced8cb..40254f8dc99c 100755
@@ -41,7 +41,9 @@ diff_cmp () {
        rm -f "$1.compare" "$2.compare"
 }
 
Quoted 4 lines

t/t3903-stash.sh diff_cmp () { · line 46

Quoted 4 lines
t/t3903-stash.sh +3 -1
-test_expect_success 'stash some dirty working directory' '
+test_expect_success 'setup' '
+       git init repo &&
+       cd repo &&

We do not want to "chdir" around without isolating it in a subprocess. If this test fails after it goes to "repo" but before it does "cd ..", the next test begins in the "repo" directory, but it is most likely not expecting that.

Quoted 8 lines
Quoted 8 lines
t/t3903-stash.sh +0 -8
-cat >expect <<EOF
-diff --git a/file b/file
-index 0cfbf08..00750ed 100644
---- a/file
-+++ b/file
-@@ -1 +1 @@
--2
-+3
Quoted 8 lines

t/t3903-stash.sh test_expect_success 'stash some dirty working directory' ' · line 78

Quoted 8 lines
t/t3903-stash.sh +7 -1
-EOF
+test_stash () {
+       cp -R repo copy &&
+       cd copy &&
+       test_expect_success "$@" &&
+       cd ../ &&
+       rm -rf copy
+}

This will create an anti-pattern, because you would want to have the part between "cd copy" and "cd .." in a subshell, but you do not want to do test_expect_success inside a subshell. Hence, this is a bad helper that does not help and should not be used, I would think.

Quoted 12 lines

t/t3903-stash.sh test_expect_success 'stash some dirty working directory' ' · line 84

Quoted 6 lines
t/t3903-stash.sh +1 -1
-test_expect_success 'parents of stash' '
+test_stash 'parents of stash' '
        test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
        git diff stash^2..stash >output &&
        diff_cmp expect output
 '

For this sort of thing I think it's usually better to override "test_expect_success" as a last resort, i.e. to have that "test_setup_stash_copy" just be a "setup_stash" or whatever function called from within your test_expect_success.

Quoted 5 lines

And instead of the "rm -rf" later, just do:

    test_when_finished "rm -rf copy" &&
    cp -R repo copy &&
    [...]

Yup. I think this is how we would write it:

	test_expect_success 'parents of stash' '
		test_when_finished "rm -fr copy" &&
		cp -R repo copy &&
		(
			cd copy &&
			... the real body of the test here, like ...
			test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
		)
	'
Quoted 2 lines

The test still needs to deal with the sub-repo, but it could cd or use "-C".

I am not sure about this. test_expect_success does not take "-C".

#43 lore raw

PATCH v3 1/3 stash: add tests to ensure reflog --rewrite --updatref behavior

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

There is missing test coverage to ensure that the resulting reflogs after a git stash drop has had its old oid rewritten if applicable, and if the refs/stash has been updated if applicable.

Add two tests that verify both of these happen.

---
 t/t3903-stash.sh | 65 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 54 insertions(+), 11 deletions(-)
t/t3903-stash.sh +54 -11
index b149e2af441..73785cf862f 100755
@@ -41,20 +41,29 @@ diff_cmp () {
 	rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
-	echo 1 >file &&
-	git add file &&
-	echo unrelated >other-file &&
-	git add other-file &&
+setup_stash() {
+	repo_dir=$1
+	if test -z $repo_dir; then
+		repo_dir="."
+	fi
+
+	echo 1 >$repo_dir/file &&
+	git -C $repo_dir add file &&
+	echo unrelated >$repo_dir/other-file &&
+	git -C $repo_dir add other-file &&
 	test_tick &&
-	git commit -m initial &&
-	echo 2 >file &&
+	git -C $repo_dir commit -m initial &&
+	echo 2 >$repo_dir/file &&
 	git add file &&
-	echo 3 >file &&
+	echo 3 >$repo_dir/file &&
 	test_tick &&
-	git stash &&
-	git diff-files --quiet &&
-	git diff-index --cached --quiet HEAD
+	git -C $repo_dir stash &&
+	git -C $repo_dir diff-files --quiet &&
+	git -C $repo_dir diff-index --cached --quiet HEAD
+}
+
+test_expect_success 'stash some dirty working directory' '
+	setup_stash
 '
 
 cat >expect <<EOF
@@ -185,6 +194,40 @@ test_expect_success 'drop middle stash by index' '
 	test 1 = $(git show HEAD:file)
 '
 
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	setup_stash repo &&
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
Signature · 1 line
gitgitgadget
#42 lore raw

v3 cover libify reflog

In reply to John Cai via GitGitGadget

In [1], there was a discussion around a bug report of stash not recovering in the middle of the process when killed with ctl-c. It turned out to not be a bug we need to fix. However, out of that discussion came the idea of libifying reflog. This can stand alone as a code improvement.

stash.c currently shells out to call reflog to delete reflogs. Libify reflog delete and call it from both builtin/reflog.c and builtin/stash.c.

This patch has three parts:

* add missing test coverage for git stash delete
* libify reflog's delete functionality and move some of the helpers into a reflog.c library and call reflog_delete from builtin/reflog.c
* call reflog_delete from builtin/stash.c

Updates since v2:

* removed unnecessary includes
* adjusted wrapping/whitespace in reflog.h
* adjusted test to be isolated from other tests since currently tests for stash depend on each other. There was some discussion around this and even a possibility to refactor the tests. However, it would have been a larger effort than is worth for this series, so instead I just made one of the tests I added be isolated from the others.

Updates since v1:

* added missing test coverage
* squashed 1/3 and 2/3 together
* moved enum into reflog.c
* updated object.h's flag allocation mapping

1. https://lore.kernel.org/git/[email protected]/

John Cai (3):
stash: add tests to ensure reflog --rewrite --updatref behavior
reflog: libify delete reflog function and helpers
stash: call reflog_delete() in reflog.c

 Makefile         |   1 +
 builtin/reflog.c | 455 +----------------------------------------------
 builtin/stash.c  |  18 +-
 object.h         |   2 +-
 reflog.c         | 432 ++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  43 +++++
 t/t3903-stash.sh |  65 +++++--
 7 files changed, 539 insertions(+), 477 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h

base-commit: e6ebfd0e8cbbd10878070c8a356b5ad1b3ca464e
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1218%2Fjohn-cai%2Fjc-libify-reflog-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1218/john-cai/jc-libify-reflog-v3
Pull-Request: https://github.com/git/git/pull/1218

Range-diff vs v2:

1: 6e136b62ca4 ! 1: 33299825fc4 stash: add test to ensure reflog --rewrite --updatref behavior

     @@ Metadata
      Author: John Cai <[email protected]>
      
       ## Commit message ##
     -    stash: add test to ensure reflog --rewrite --updatref behavior
     +    stash: add tests to ensure reflog --rewrite --updatref behavior
      
          There is missing test coverage to ensure that the resulting reflogs
          after a git stash drop has had its old oid rewritten if applicable, and
     @@ Commit message
          Signed-off-by: John Cai <[email protected]>
      
       ## t/t3903-stash.sh ##
     +@@ t/t3903-stash.sh: diff_cmp () {
     + 	rm -f "$1.compare" "$2.compare"
     + }
     + 
     +-test_expect_success 'stash some dirty working directory' '
     +-	echo 1 >file &&
     +-	git add file &&
     +-	echo unrelated >other-file &&
     +-	git add other-file &&
     ++setup_stash() {
     ++	repo_dir=$1
     ++	if test -z $repo_dir; then
     ++		repo_dir="."
     ++	fi
     ++
     ++	echo 1 >$repo_dir/file &&
     ++	git -C $repo_dir add file &&
     ++	echo unrelated >$repo_dir/other-file &&
     ++	git -C $repo_dir add other-file &&
     + 	test_tick &&
     +-	git commit -m initial &&
     +-	echo 2 >file &&
     ++	git -C $repo_dir commit -m initial &&
     ++	echo 2 >$repo_dir/file &&
     + 	git add file &&
     +-	echo 3 >file &&
     ++	echo 3 >$repo_dir/file &&
     + 	test_tick &&
     +-	git stash &&
     +-	git diff-files --quiet &&
     +-	git diff-index --cached --quiet HEAD
     ++	git -C $repo_dir stash &&
     ++	git -C $repo_dir diff-files --quiet &&
     ++	git -C $repo_dir diff-index --cached --quiet HEAD
     ++}
     ++
     ++test_expect_success 'stash some dirty working directory' '
     ++	setup_stash
     + '
     + 
     + cat >expect <<EOF
      @@ t/t3903-stash.sh: test_expect_success 'drop middle stash by index' '
       	test 1 = $(git show HEAD:file)
       '
     @@ t/t3903-stash.sh: test_expect_success 'drop middle stash by index' '
      +	test_cmp expect actual
      +'
      +
     -+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
     -+	git reset --hard &&
     -+	echo 9 >file &&
     -+	git stash &&
     -+	oid="$(git rev-parse stash@{0})" &&
     -+	git stash drop stash@{1} &&
     -+	cut -d" " -f1-2 .git/logs/refs/stash >actual &&
     ++test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
     ++	git init repo &&
     ++	setup_stash repo &&
     ++	echo 9 >repo/file &&
     ++
     ++	old_oid="$(git -C repo rev-parse stash@{0})" &&
     ++	git -C repo stash &&
     ++	new_oid="$(git -C repo rev-parse stash@{0})" &&
     ++
     ++	cat >expect <<-EOF &&
     ++	$(test_oid zero) $old_oid
     ++	$old_oid $new_oid
     ++	EOF
     ++	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
     ++	test_cmp expect actual &&
     ++
     ++	git -C repo stash drop stash@{1} &&
     ++	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
      +	cat >expect <<-EOF &&
     -+	$(test_oid zero) $oid
     ++	$(test_oid zero) $new_oid
      +	EOF
      +	test_cmp expect actual
      +'
     @@ t/t3903-stash.sh: test_expect_success 'drop middle stash by index' '
       test_expect_success 'stash pop' '
       	git reset --hard &&
       	git stash pop &&
     --	test 3 = $(cat file) &&
     -+	test 9 = $(cat file) &&
     - 	test 1 = $(git show :file) &&
     - 	test 1 = $(git show HEAD:file) &&
     - 	test 0 = $(git stash list | wc -l)
 2:  e7c950218b1 ! 2:  33adfee4ca6 reflog: libify delete reflog function and helpers
     @@ builtin/reflog.c
      @@
       #include "builtin.h"
       #include "config.h"
     - #include "lockfile.h"
     +-#include "lockfile.h"
      -#include "object-store.h"
     - #include "repository.h"
     +-#include "repository.h"
      -#include "commit.h"
      -#include "refs.h"
     - #include "dir.h"
     +-#include "dir.h"
      -#include "tree-walk.h"
     - #include "diff.h"
     +-#include "diff.h"
       #include "revision.h"
       #include "reachable.h"
       #include "worktree.h"
     @@ object.h: struct object_array {
       ## reflog.c (new) ##
      @@
      +#include "cache.h"
     -+#include "commit.h"
      +#include "object-store.h"
     -+#include "reachable.h"
      +#include "reflog.h"
      +#include "refs.h"
      +#include "revision.h"
     -+#include "tree-walk.h"
      +#include "worktree.h"
      +
      +/* Remember to update object flag allocation in object.h */
     @@ reflog.h (new)
      @@
      +#ifndef REFLOG_H
      +#define REFLOG_H
     -+
      +#include "refs.h"
      +
      +struct cmd_reflog_expire_cb {
     @@ reflog.h (new)
      +	unsigned int dry_run:1;
      +};
      +
     -+int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose);
     -+
     ++int reflog_delete(const char *rev, enum expire_reflog_flags flags,
     ++		  int verbose);
      +void reflog_expiry_cleanup(void *cb_data);
     -+
      +void reflog_expiry_prepare(const char *refname, const struct object_id *oid,
      +			   void *cb_data);
     -+
      +int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
      +			     const char *email, timestamp_t timestamp, int tz,
      +			     const char *message, void *cb_data);
     -+
      +int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
      +		     const char *email, timestamp_t timestamp, int tz,
      +		     const char *message, void *cb_data);
     -+
      +int should_expire_reflog_ent_verbose(struct object_id *ooid,
      +				     struct object_id *noid,
      +				     const char *email,
      +				     timestamp_t timestamp, int tz,
      +				     const char *message, void *cb_data);
     -+
      +#endif /* REFLOG_H */

3: a023a70092b = 3: b17d8e5d43a stash: call reflog_delete() in reflog.c

Signature · 1 line
gitgitgadget
#46 lore raw

PATCH v3 3/3 stash: call reflog_delete() in reflog.c

From: John Cai <[email protected]>

Now that cmd_reflog_delete has been libified an exported it into a new reflog.c library so we can call it directly from builtin/stash.c. This not only gives us a performance gain since we don't need to create a subprocess, but it also allows us to use the ref transactions api in the future.

---
 builtin/stash.c | 18 ++++--------------
 1 file changed, 4 insertions(+), 14 deletions(-)
builtin/stash.c +4 -14
index 5897febfbec..3e2f478b761 100644
@@ -17,6 +17,7 @@
 #include "diffcore.h"
 #include "exec-cmd.h"
 #include "entry.h"
+#include "reflog.h"
 
 #define INCLUDE_ALL_FILES 2
 
@@ -634,20 +635,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-	int ret;
-	struct child_process cp_reflog = CHILD_PROCESS_INIT;
-
-	/*
-	 * reflog does not provide a simple function for deleting refs. One will
-	 * need to be added to avoid implementing too much reflog code here
-	 */
-
-	cp_reflog.git_cmd = 1;
-	strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-		     "--rewrite", NULL);
-	strvec_push(&cp_reflog.args, info->revision.buf);
-	ret = run_command(&cp_reflog);
-	if (!ret) {
+	if (!reflog_delete(info->revision.buf,
+			   EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_UPDATE_REF,
+			   0)) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 				  oid_to_hex(&info->w_commit));
Signature · 1 line
gitgitgadget
#45 lore raw

PATCH v3 2/3 reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

---
 Makefile         |   1 +
 builtin/reflog.c | 455 +----------------------------------------------
 object.h         |   2 +-
 reflog.c         | 432 ++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  43 +++++
 5 files changed, 481 insertions(+), 452 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h
Makefile +1 -0
index 6f0b4b775fe..876d4dfd6cb 100644
@@ -989,6 +989,7 @@ LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
+LIB_OBJS += reflog.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
builtin/reflog.c +4 -451

499 lines not on this page · raw message

object.h +1 -1
index cb556ab7753..a2219464c2b 100644
@@ -75,7 +75,7 @@ struct object_array {
  * builtin/fsck.c:           0--3
  * builtin/gc.c:             0
  * builtin/index-pack.c:                                     2021
- * builtin/reflog.c:                   10--12
+ * reflog.c:                           10--12
  * builtin/show-branch.c:    0-------------------------------------------26
  * builtin/unpack-objects.c:                                 2021
  */
reflog.c new file +432 -0

432 lines not on this page · raw message

reflog.h new file +43 -0

43 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#47 lore raw
In reply to John Cai via GitGitGadget
Quoted 5 lines · John Cai via GitGitGadget

John Cai (3):
stash: add tests to ensure reflog --rewrite --updatref behavior
reflog: libify delete reflog function and helpers
stash: call reflog_delete() in reflog.c

Quoted 10 lines
 Makefile         |   1 +
 builtin/reflog.c | 455 +----------------------------------------------
 builtin/stash.c  |  18 +-
 object.h         |   2 +-
 reflog.c         | 432 ++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  43 +++++
 t/t3903-stash.sh |  65 +++++--
 7 files changed, 539 insertions(+), 477 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h

Thanks; I glossed over the discussion about tests (since it looks like you and Ævar already have a good handle on how things are going there).

The rest of this version of the series (which I looked at more closely) looks good to me.

Thanks
Taylor

#48 lore raw

Hi,

Just wanted to bump this thread. It'd be good to get another ack on these last set of changes.

Quoted 25 lines · Taylor Blau
Quoted 5 lines · John Cai via GitGitGadget

John Cai (3):
stash: add tests to ensure reflog --rewrite --updatref behavior
reflog: libify delete reflog function and helpers
stash: call reflog_delete() in reflog.c

Quoted 10 lines
 Makefile         |   1 +
 builtin/reflog.c | 455 +----------------------------------------------
 builtin/stash.c  |  18 +-
 object.h         |   2 +-
 reflog.c         | 432 ++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  43 +++++
 t/t3903-stash.sh |  65 +++++--
 7 files changed, 539 insertions(+), 477 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h

Thanks; I glossed over the discussion about tests (since it looks like you and Ævar already have a good handle on how things are going there).

The rest of this version of the series (which I looked at more closely) looks good to me.

Thanks
Taylor

#44 lore raw

Re: [PATCH v3 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

In reply to John Cai via GitGitGadget
Quoted 90 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

There is missing test coverage to ensure that the resulting reflogs after a git stash drop has had its old oid rewritten if applicable, and if the refs/stash has been updated if applicable.

Add two tests that verify both of these happen.

---
 t/t3903-stash.sh | 65 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 54 insertions(+), 11 deletions(-)
t/t3903-stash.sh +47 -11
index b149e2af441..73785cf862f 100755
@@ -41,20 +41,29 @@ diff_cmp () {
 	rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
-	echo 1 >file &&
-	git add file &&
-	echo unrelated >other-file &&
-	git add other-file &&
+setup_stash() {
+	repo_dir=$1
+	if test -z $repo_dir; then
+		repo_dir="."
+	fi
+
+	echo 1 >$repo_dir/file &&
+	git -C $repo_dir add file &&
+	echo unrelated >$repo_dir/other-file &&
+	git -C $repo_dir add other-file &&
 	test_tick &&
-	git commit -m initial &&
-	echo 2 >file &&
+	git -C $repo_dir commit -m initial &&
+	echo 2 >$repo_dir/file &&
 	git add file &&
-	echo 3 >file &&
+	echo 3 >$repo_dir/file &&
 	test_tick &&
-	git stash &&
-	git diff-files --quiet &&
-	git diff-index --cached --quiet HEAD
+	git -C $repo_dir stash &&
+	git -C $repo_dir diff-files --quiet &&
+	git -C $repo_dir diff-index --cached --quiet HEAD
+}
+
+test_expect_success 'stash some dirty working directory' '
+	setup_stash
 '
 
 cat >expect <<EOF
@@ -185,6 +194,40 @@ test_expect_success 'drop middle stash by index' '
 	test 1 = $(git show HEAD:file)
 '
 
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	setup_stash repo &&
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 233

Quoted 10 lines
t/t3903-stash.sh +7 -0
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&

I just looked at the post-image of this and didn't notice it was altered existing code at first, so I was going to suggest just this:

	diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
	index 73785cf862f..422d1f7a306 100755
	--- a/t/t3903-stash.sh
	+++ b/t/t3903-stash.sh
	@@ -42,24 +42,14 @@ diff_cmp () {
	 }
	 
	 setup_stash() {
	-	repo_dir=$1
	-	if test -z $repo_dir; then
	-		repo_dir="."
	-	fi
	-
	-	echo 1 >$repo_dir/file &&
	-	git -C $repo_dir add file &&
	-	echo unrelated >$repo_dir/other-file &&
	-	git -C $repo_dir add other-file &&
	-	test_tick &&
	-	git -C $repo_dir commit -m initial &&
	-	echo 2 >$repo_dir/file &&
	+	test_commit initial file 1 &&
	+	test_commit second other-file unrelated &&
	+	echo 2 >file &&
	 	git add file &&
	-	echo 3 >$repo_dir/file &&
	-	test_tick &&
	-	git -C $repo_dir stash &&
	-	git -C $repo_dir diff-files --quiet &&
	-	git -C $repo_dir diff-index --cached --quiet HEAD
	+	echo 3 >file &&
	+	git stash &&
	+	git diff-files --quiet &&
	+	git diff-index --cached --quiet HEAD
	 }
	 
	 test_expect_success 'stash some dirty working directory' '
	@@ -206,7 +196,10 @@ test_expect_success 'drop stash reflog updates refs/stash' '
	 
	 test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
	 	git init repo &&
	-	setup_stash repo &&
	+	(
	+		cd repo &&
	+		setup_stash repo
	+	) &&
	 	echo 9 >repo/file &&
	 
	 	old_oid="$(git -C repo rev-parse stash@{0})" &&

But I think much better as a replacement is:

	diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
	index b149e2af441..9dd05a497bc 100755
	--- a/t/t3903-stash.sh
	+++ b/t/t3903-stash.sh
	@@ -41,7 +41,7 @@ diff_cmp () {
	 	rm -f "$1.compare" "$2.compare"
	 }
	 
	-test_expect_success 'stash some dirty working directory' '
	+setup_stash() {
	 	echo 1 >file &&
	 	git add file &&
	 	echo unrelated >other-file &&
	@@ -55,6 +55,10 @@ test_expect_success 'stash some dirty working directory' '
	 	git stash &&
	 	git diff-files --quiet &&
	 	git diff-index --cached --quiet HEAD
	+}
	+
	+test_expect_success 'stash some dirty working directory' '
	+	setup_stash
	 '
	 
	 cat >expect <<EOF
	@@ -185,6 +189,43 @@ test_expect_success 'drop middle stash by index' '
	 	test 1 = $(git show HEAD:file)
	 '
	 
	+test_expect_success 'drop stash reflog updates refs/stash' '
	+	git reset --hard &&
	+	git rev-parse refs/stash >expect &&
	+	echo 9 >file &&
	+	git stash &&
	+	git stash drop stash@{0} &&
	+	git rev-parse refs/stash >actual &&
	+	test_cmp expect actual
	+'
	+
	+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
	+	git init repo &&
	+	(
	+		cd repo &&
	+		setup_stash repo
	+	) &&
	+	echo 9 >repo/file &&
	+
	+	old_oid="$(git -C repo rev-parse stash@{0})" &&
	+	git -C repo stash &&
	+	new_oid="$(git -C repo rev-parse stash@{0})" &&
	+
	+	cat >expect <<-EOF &&
	+	$(test_oid zero) $old_oid
	+	$old_oid $new_oid
	+	EOF
	+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
	+	test_cmp expect actual &&
	+
	+	git -C repo stash drop stash@{1} &&
	+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
	+	cat >expect <<-EOF &&
	+	$(test_oid zero) $new_oid
	+	EOF
	+	test_cmp expect actual
	+'
	+
	 test_expect_success 'stash pop' '
	 	git reset --hard &&
	 	git stash pop &&

I>e. the only reason you need to add that -C etc. is because you'd like to set it up in a subdirectory. Easier to just add the tiny bity of logic to make that a sub-shell instead.

#49 lore raw
In reply to John Cai
Quoted 2 lines · John Cai

Just wanted to bump this thread. It'd be good to get another ack on these last set of changes.

Hi. Sorry that I didn't look at it earlier.

This looks good to me and I think everything that's been brought up has been addressed.

I left a nit on 1/3 suggesting a way to make that diff a bit smaller by using a subshell instead of refactoring an existing function.

But I think with or without that & a ro-roll this would be good to advance to "next" etc.

Thanks!

#50 lore raw

v4 cover libify reflog

In reply to John Cai via GitGitGadget

In [1], there was a discussion around a bug report of stash not recovering in the middle of the process when killed with ctl-c. It turned out to not be a bug we need to fix. However, out of that discussion came the idea of libifying reflog. This can stand alone as a code improvement.

stash.c currently shells out to call reflog to delete reflogs. Libify reflog delete and call it from both builtin/reflog.c and builtin/stash.c.

This patch has three parts:

* add missing test coverage for git stash delete
* libify reflog's delete functionality and move some of the helpers into a reflog.c library and call reflog_delete from builtin/reflog.c
* call reflog_delete from builtin/stash.c

Updates since v3:

* refactored test to have a smaller diff

Updates since v2:

* removed unnecessary includes
* adjusted wrapping/whitespace in reflog.h
* adjusted test to be isolated from other tests since currently tests for stash depend on each other. There was some discussion around this and even a possibility to refactor the tests. However, it would have been a larger effort than is worth for this series, so instead I just made one of the tests I added be isolated from the others.

Updates since v1:

* added missing test coverage
* squashed 1/3 and 2/3 together
* moved enum into reflog.c
* updated object.h's flag allocation mapping

1. https://lore.kernel.org/git/[email protected]/

John Cai (3):
stash: add tests to ensure reflog --rewrite --updatref behavior
reflog: libify delete reflog function and helpers
stash: call reflog_delete() in reflog.c

 Makefile         |   1 +
 builtin/reflog.c | 455 +----------------------------------------------
 builtin/stash.c  |  18 +-
 object.h         |   2 +-
 reflog.c         | 432 ++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  43 +++++
 t/t3903-stash.sh |  43 ++++-
 7 files changed, 527 insertions(+), 467 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h

base-commit: e6ebfd0e8cbbd10878070c8a356b5ad1b3ca464e
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1218%2Fjohn-cai%2Fjc-libify-reflog-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1218/john-cai/jc-libify-reflog-v4
Pull-Request: https://github.com/git/git/pull/1218

Range-diff vs v3:

1: 33299825fc4 ! 1: 08bb8d3a9b9 stash: add tests to ensure reflog --rewrite --updatref behavior

     @@ t/t3903-stash.sh: diff_cmp () {
       }
       
      -test_expect_success 'stash some dirty working directory' '
     --	echo 1 >file &&
     --	git add file &&
     --	echo unrelated >other-file &&
     --	git add other-file &&
      +setup_stash() {
     -+	repo_dir=$1
     -+	if test -z $repo_dir; then
     -+		repo_dir="."
     -+	fi
     -+
     -+	echo 1 >$repo_dir/file &&
     -+	git -C $repo_dir add file &&
     -+	echo unrelated >$repo_dir/other-file &&
     -+	git -C $repo_dir add other-file &&
     - 	test_tick &&
     --	git commit -m initial &&
     --	echo 2 >file &&
     -+	git -C $repo_dir commit -m initial &&
     -+	echo 2 >$repo_dir/file &&
     + 	echo 1 >file &&
       	git add file &&
     --	echo 3 >file &&
     -+	echo 3 >$repo_dir/file &&
     - 	test_tick &&
     --	git stash &&
     --	git diff-files --quiet &&
     --	git diff-index --cached --quiet HEAD
     -+	git -C $repo_dir stash &&
     -+	git -C $repo_dir diff-files --quiet &&
     -+	git -C $repo_dir diff-index --cached --quiet HEAD
     + 	echo unrelated >other-file &&
     +@@ t/t3903-stash.sh: test_expect_success 'stash some dirty working directory' '
     + 	git stash &&
     + 	git diff-files --quiet &&
     + 	git diff-index --cached --quiet HEAD
      +}
      +
      +test_expect_success 'stash some dirty working directory' '
     @@ t/t3903-stash.sh: test_expect_success 'drop middle stash by index' '
      +
      +test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
      +	git init repo &&
     -+	setup_stash repo &&
     ++	(
     ++		cd repo &&
     ++		setup_stash
     ++	) &&
      +	echo 9 >repo/file &&
      +
      +	old_oid="$(git -C repo rev-parse stash@{0})" &&
 2:  33adfee4ca6 = 2:  50471c2ee6f reflog: libify delete reflog function and helpers
 3:  b17d8e5d43a = 3:  cb32d9bfe60 stash: call reflog_delete() in reflog.c
Signature · 1 line
gitgitgadget
#51 lore raw

PATCH v4 1/3 stash: add tests to ensure reflog --rewrite --updatref behavior

From: John Cai <[email protected]>

There is missing test coverage to ensure that the resulting reflogs after a git stash drop has had its old oid rewritten if applicable, and if the refs/stash has been updated if applicable.

Add two tests that verify both of these happen.

---
 t/t3903-stash.sh | 43 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)
t/t3903-stash.sh +42 -1
index b149e2af441..a2f8d0c52e7 100755
@@ -41,7 +41,7 @@ diff_cmp () {
 	rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
+setup_stash() {
 	echo 1 >file &&
 	git add file &&
 	echo unrelated >other-file &&
@@ -55,6 +55,10 @@ test_expect_success 'stash some dirty working directory' '
 	git stash &&
 	git diff-files --quiet &&
 	git diff-index --cached --quiet HEAD
+}
+
+test_expect_success 'stash some dirty working directory' '
+	setup_stash
 '
 
 cat >expect <<EOF
@@ -185,6 +189,43 @@ test_expect_success 'drop middle stash by index' '
 	test 1 = $(git show HEAD:file)
 '
 
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
Signature · 1 line
gitgitgadget
#61 lore raw

PATCH v4 3/3 stash: call reflog_delete() in reflog.c

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

Now that cmd_reflog_delete has been libified an exported it into a new reflog.c library so we can call it directly from builtin/stash.c. This not only gives us a performance gain since we don't need to create a subprocess, but it also allows us to use the ref transactions api in the future.

---
 builtin/stash.c | 18 ++++--------------
 1 file changed, 4 insertions(+), 14 deletions(-)
builtin/stash.c +4 -14
index 5897febfbec..3e2f478b761 100644
@@ -17,6 +17,7 @@
 #include "diffcore.h"
 #include "exec-cmd.h"
 #include "entry.h"
+#include "reflog.h"
 
 #define INCLUDE_ALL_FILES 2
 
@@ -634,20 +635,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-	int ret;
-	struct child_process cp_reflog = CHILD_PROCESS_INIT;
-
-	/*
-	 * reflog does not provide a simple function for deleting refs. One will
-	 * need to be added to avoid implementing too much reflog code here
-	 */
-
-	cp_reflog.git_cmd = 1;
-	strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-		     "--rewrite", NULL);
-	strvec_push(&cp_reflog.args, info->revision.buf);
-	ret = run_command(&cp_reflog);
-	if (!ret) {
+	if (!reflog_delete(info->revision.buf,
+			   EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_UPDATE_REF,
+			   0)) {
 		if (!quiet)
 			printf_ln(_("Dropped %s (%s)"), info->revision.buf,
 				  oid_to_hex(&info->w_commit));
Signature · 1 line
gitgitgadget
#60 lore raw

PATCH v4 2/3 reflog: libify delete reflog function and helpers

In reply to John Cai via GitGitGadget

From: John Cai <[email protected]>

Currently stash shells out to reflog in order to delete refs. In an effort to reduce how much we shell out to a subprocess, libify the functionality that stash needs into reflog.c.

Add a reflog_delete function that is pretty much the logic in the while loop in builtin/reflog.c cmd_reflog_delete(). This is a function that builtin/reflog.c and builtin/stash.c can both call.

Also move functions needed by reflog_delete and export them.

---
 Makefile         |   1 +
 builtin/reflog.c | 455 +----------------------------------------------
 object.h         |   2 +-
 reflog.c         | 432 ++++++++++++++++++++++++++++++++++++++++++++
 reflog.h         |  43 +++++
 5 files changed, 481 insertions(+), 452 deletions(-)
 create mode 100644 reflog.c
 create mode 100644 reflog.h
Makefile +1 -0
index 6f0b4b775fe..876d4dfd6cb 100644
@@ -989,6 +989,7 @@ LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
+LIB_OBJS += reflog.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
builtin/reflog.c +4 -451

499 lines not on this page · raw message

object.h +1 -1
index cb556ab7753..a2219464c2b 100644
@@ -75,7 +75,7 @@ struct object_array {
  * builtin/fsck.c:           0--3
  * builtin/gc.c:             0
  * builtin/index-pack.c:                                     2021
- * builtin/reflog.c:                   10--12
+ * reflog.c:                           10--12
  * builtin/show-branch.c:    0-------------------------------------------26
  * builtin/unpack-objects.c:                                 2021
  */
reflog.c new file +432 -0

432 lines not on this page · raw message

reflog.h new file +43 -0

43 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#62 lore raw
In reply to John Cai via GitGitGadget
Quoted 3 lines · John Cai via GitGitGadget

This patch has three parts:

* add missing test coverage for git stash delete

This looking better, relative to the previous iteration.

Quoted 2 lines

* libify reflog's delete functionality and move some of the helpers into a reflog.c library and call reflog_delete from builtin/reflog.c

This part hasn't changed since the implementation in the previous round I've looked at; looked sane.

Quoted 1 line

* call reflog_delete from builtin/stash.c

Likewise.

Thanks. Will queue.

#52 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

In reply to John Cai via GitGitGadget
Quoted 19 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

There is missing test coverage to ensure that the resulting reflogs after a git stash drop has had its old oid rewritten if applicable, and if the refs/stash has been updated if applicable.

Add two tests that verify both of these happen.

---
 t/t3903-stash.sh | 43 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)
t/t3903-stash.sh +0 -0
index b149e2af441..a2f8d0c52e7 100755

t/t3903-stash.sh diff_cmp () { · line 44

Quoted 5 lines
t/t3903-stash.sh +1 -1
@@ -41,7 +41,7 @@ diff_cmp () {
 	rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
+setup_stash() {

Style.

	setup_stash () {

but more importantly, is this really setting up "stash"? "setup_stash_test" or something, perhaps?

t/t3903-stash.sh test_expect_success 'stash some dirty working directory' ' · line 62

Quoted 3 lines
t/t3903-stash.sh +2 -0
+test_expect_success 'stash some dirty working directory' '
+	setup_stash
 '

OK.

Quoted 6 lines
t/t3903-stash.sh +6 -0
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 207

Quoted 10 lines
t/t3903-stash.sh +10 -0
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.

But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.

i.e.

	git init repo &&
	(
		cd repo &&
		setup_stash_test &&

		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...

		test_cmp expect actual
	)
Quoted 24 lines
t/t3903-stash.sh +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
#53 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

Hi Junio,

Quoted 67 lines · Junio C Hamano
Quoted 19 lines · John Cai via GitGitGadget

From: John Cai <[email protected]>

There is missing test coverage to ensure that the resulting reflogs after a git stash drop has had its old oid rewritten if applicable, and if the refs/stash has been updated if applicable.

Add two tests that verify both of these happen.

---
 t/t3903-stash.sh | 43 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)
t/t3903-stash.sh +0 -0
index b149e2af441..a2f8d0c52e7 100755

t/t3903-stash.sh diff_cmp () { · line 44

Quoted 5 lines
t/t3903-stash.sh +1 -1
@@ -41,7 +41,7 @@ diff_cmp () {
 	rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
+setup_stash() {

Style.

	setup_stash () {

but more importantly, is this really setting up "stash"? "setup_stash_test" or something, perhaps?

t/t3903-stash.sh test_expect_success 'stash some dirty working directory' ' · line 62

Quoted 3 lines
t/t3903-stash.sh +2 -0
+test_expect_success 'stash some dirty working directory' '
+	setup_stash
 '

OK.

Quoted 6 lines
t/t3903-stash.sh +6 -0
+test_expect_success 'drop stash reflog updates refs/stash' '
+	git reset --hard &&
+	git rev-parse refs/stash >expect &&
+	echo 9 >file &&
+	git stash &&
+	git stash drop stash@{0} &&

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 207

Quoted 10 lines
t/t3903-stash.sh +10 -0
+	git rev-parse refs/stash >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.

But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.

i.e.

Quoted 14 lines
	git init repo &&
	(
		cd repo &&
		setup_stash_test &&

		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...

		test_cmp expect actual
	)

makes sense to me. Is this worth a re-roll and sending out another series to the list? or is it sufficient to make the change on my branch?

Quoted 24 lines
Quoted 24 lines
t/t3903-stash.sh +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'stash pop' '
 	git reset --hard &&
 	git stash pop &&
#54 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

In reply to Junio C Hamano
Quoted 19 lines · Junio C Hamano

"John Cai via GitGitGadget" <[email protected]> writes:
[...]

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 207

Quoted 6 lines
t/t3903-stash.sh +6 -0
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.

But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.

i.e.

Quoted 14 lines
	git init repo &&
	(
		cd repo &&
		setup_stash_test &&

		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...

		test_cmp expect actual
	)

I wonder if we could avoid the subshell entirely and avoid relying on REFFILES (assuming we're not trying to test the implementation details of that refs backend) by doing something like

test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

	old=$(git rev-parse stash@{0}) &&
	setup_stash_test &&
	git rev-list -g stash >tmp &&
	sed /$old/d tmp >expect &&
	git rev-list -g stash >actual &&
	test_cmp expect actual

'

Best Wishes

Phillip

Quoted 24 lines
Quoted 24 lines
t/t3903-stash.sh +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
  test_expect_success 'stash pop' '
  	git reset --hard &&
  	git stash pop &&
#55 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

Quoted 33 lines · Phillip Wood
Quoted 15 lines · Junio C Hamano

"John Cai via GitGitGadget" <[email protected]> writes:
[...]

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 207

Quoted 6 lines
t/t3903-stash.sh +6 -0
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.
But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.
i.e.

Quoted 12 lines
	git init repo &&
	(
		cd repo &&
		setup_stash_test &&
		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...
		test_cmp expect actual
	)

I wonder if we could avoid the subshell entirely and avoid relying on REFFILES (assuming we're not trying to test the implementation details of that refs backend) by doing something like

Quoted 8 lines

test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

	old=$(git rev-parse stash@{0}) &&
	setup_stash_test &&
	git rev-list -g stash >tmp &&
	sed /$old/d tmp >expect &&
	git rev-list -g stash >actual &&
	test_cmp expect actual

'

Unless I'm missing something that "rev-list -g" will emit only the RHS of the stash logs, i.e. no "0000..." etc.

And if we only look at that the difference with specifying the flag isn't visible, no?

Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
t/t3903-stash.sh +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
  test_expect_success 'stash pop' '
  	git reset --hard &&
  	git stash pop &&
#56 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

Quoted 45 lines · Ævar Arnfjörð Bjarmason
Quoted 33 lines · Phillip Wood
Quoted 15 lines · Junio C Hamano

"John Cai via GitGitGadget" <[email protected]> writes:
[...]

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 207

Quoted 6 lines
t/t3903-stash.sh +6 -0
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.
But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.
i.e.

Quoted 12 lines
	git init repo &&
	(
		cd repo &&
		setup_stash_test &&
		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...
		test_cmp expect actual
	)

I wonder if we could avoid the subshell entirely and avoid relying on REFFILES (assuming we're not trying to test the implementation details of that refs backend) by doing something like

Quoted 8 lines

test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

	old=$(git rev-parse stash@{0}) &&
	setup_stash_test &&
	git rev-list -g stash >tmp &&
	sed /$old/d tmp >expect &&
	git rev-list -g stash >actual &&
	test_cmp expect actual

'

Quoted 5 lines

Unless I'm missing something that "rev-list -g" will emit only the RHS of the stash logs, i.e. no "0000..." etc.

And if we only look at that the difference with specifying the flag isn't visible, no?

Maybe I'm missing what this test is actually needs to do. I thought it just needed to check that the deleted stash is removed from the reflog and the others are unchanged. You're right that it wont show the LHS and if that is important then you need to read the log file directly.

Best Wishes

Phillip

Quoted 25 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
t/t3903-stash.sh +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
   test_expect_success 'stash pop' '
   	git reset --hard &&
   	git stash pop &&
#57 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

Hi Phillip,

Quoted 46 lines · Phillip Wood
Quoted 45 lines · Ævar Arnfjörð Bjarmason
Quoted 41 lines · Phillip Wood
Quoted 15 lines · Junio C Hamano

"John Cai via GitGitGadget" <[email protected]> writes:
[...]

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 207

Quoted 6 lines
t/t3903-stash.sh +6 -0
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.
But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.
i.e.

Quoted 12 lines
	git init repo &&
	(
		cd repo &&
		setup_stash_test &&
		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...
		test_cmp expect actual
	)

I wonder if we could avoid the subshell entirely and avoid relying on REFFILES (assuming we're not trying to test the implementation details of that refs backend) by doing something like

test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

	old=$(git rev-parse stash@{0}) &&
	setup_stash_test &&
	git rev-list -g stash >tmp &&
	sed /$old/d tmp >expect &&
	git rev-list -g stash >actual &&
	test_cmp expect actual

'

Quoted 7 lines
Quoted 5 lines

Unless I'm missing something that "rev-list -g" will emit only the RHS of the stash logs, i.e. no "0000..." etc.

And if we only look at that the difference with specifying the flag isn't visible, no?

Maybe I'm missing what this test is actually needs to do. I thought it just needed to check that the deleted stash is removed from the reflog and the others are unchanged. You're right that it wont show the LHS and if that is important then you need to read the log file directly.

We had discussed this briefly in [1], but the --rewrite option for reflog delete will rewrite the LHS, which is not visible to normal ref API users. So the only way to test that this happened is to reach inside of the file.

1. https://lore.kernel.org/git/[email protected]/

Quoted 4 lines

Best Wishes

Phillip

thanks,
John

Quoted 27 lines
Quoted 27 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
t/t3903-stash.sh +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
   test_expect_success 'stash pop' '
   	git reset --hard &&
   	git stash pop &&
#58 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

Hi John

Quoted 53 lines · John Cai

Hi Phillip,

Quoted 49 lines · Phillip Wood
Quoted 48 lines · Ævar Arnfjörð Bjarmason
Quoted 33 lines · Phillip Wood
Quoted 15 lines · Junio C Hamano

"John Cai via GitGitGadget" <[email protected]> writes:
[...]

t/t3903-stash.sh test_expect_success 'drop middle stash by index' ' · line 207

Quoted 6 lines
t/t3903-stash.sh +6 -0
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.
But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.
i.e.

Quoted 12 lines
	git init repo &&
	(
		cd repo &&
		setup_stash_test &&
		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...
		test_cmp expect actual
	)

I wonder if we could avoid the subshell entirely and avoid relying on REFFILES (assuming we're not trying to test the implementation details of that refs backend) by doing something like

Quoted 8 lines

test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

	old=$(git rev-parse stash@{0}) &&
	setup_stash_test &&
	git rev-list -g stash >tmp &&
	sed /$old/d tmp >expect &&
	git rev-list -g stash >actual &&
	test_cmp expect actual

'

Unless I'm missing something that "rev-list -g" will emit only the RHS of the stash logs, i.e. no "0000..." etc.

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

And if we only look at that the difference with specifying the flag isn't visible, no?

Maybe I'm missing what this test is actually needs to do. I thought it just needed to check that the deleted stash is removed from the reflog and the others are unchanged. You're right that it wont show the LHS and if that is important then you need to read the log file directly.

We had discussed this briefly in [1], but the --rewrite option for reflog delete will rewrite the LHS, which is not visible to normal ref API users. So the only way to test that this happened is to reach inside of the file.

1. https://lore.kernel.org/git/[email protected]/

Thanks for the pointer, that was useful context that could maybe be added to the commit message to explain why the test needs to check the lhs of the reflog if you reroll.

Best Wishes

Phillip

Quoted 35 lines
Quoted 4 lines

Best Wishes

Phillip

thanks,
John

Quoted 27 lines
Quoted 27 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
t/t3903-stash.sh +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
    test_expect_success 'stash pop' '
    	git reset --hard &&
    	git stash pop &&
#59 lore raw

Re: [PATCH v4 1/3] stash: add tests to ensure reflog --rewrite --updatref behavior

Hi Phillip,

Quoted 59 lines · Phillip Wood

Hi John

Quoted 56 lines · John Cai

Hi Phillip,

Quoted 52 lines · Phillip Wood
Quoted 50 lines · Ævar Arnfjörð Bjarmason
Quoted 33 lines · Phillip Wood
Quoted 15 lines · Junio C Hamano

"John Cai via GitGitGadget" <[email protected]> writes:
[...]

Review

Quoted 6 lines
(quoted diff) +6 -0
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+	git init repo &&
+	(
+		cd repo &&
+		setup_stash
+	) &&

Hmph, so this is done inside the subdirectory. The implementation of the helper in this iteration does look cleaner than in the previous iteration.
But these many references to "repo/" and "-C repo" we see below makes me wonder why we do not put the whole thing inside the subshell we started earlier.
i.e.

Quoted 12 lines
	git init repo &&
	(
		cd repo &&
		setup_stash_test &&
		echo 9 >file &&
		old=$(git rev-parse stash@{0}) &&
		git stash &&
		new=$(git rev-parse stash@{0}) &&
		...
		test_cmp expect actual
	)

I wonder if we could avoid the subshell entirely and avoid relying on REFFILES (assuming we're not trying to test the implementation details of that refs backend) by doing something like

Quoted 8 lines

test_expect_success 'drop stash reflog updates refs/stash with rewrite' '

	old=$(git rev-parse stash@{0}) &&
	setup_stash_test &&
	git rev-list -g stash >tmp &&
	sed /$old/d tmp >expect &&
	git rev-list -g stash >actual &&
	test_cmp expect actual

'

Unless I'm missing something that "rev-list -g" will emit only the RHS of the stash logs, i.e. no "0000..." etc.

And if we only look at that the difference with specifying the flag isn't visible, no?

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

Maybe I'm missing what this test is actually needs to do. I thought it just needed to check that the deleted stash is removed from the reflog and the others are unchanged. You're right that it wont show the LHS and if that is important then you need to read the log file directly.

We had discussed this briefly in [1], but the --rewrite option for reflog delete will rewrite the LHS, which is not visible to normal ref API users. So the only way to test that this happened is to reach inside of the file.

1. https://lore.kernel.org/git/[email protected]/

Thanks for the pointer, that was useful context that could maybe be added to the commit message to explain why the test needs to check the lhs of the reflog if you reroll.

Good point. that would be helpful context since it took me a while to figure it out myself--will re-roll, thanks!

Quoted 40 lines

Best Wishes

Phillip

Quoted 35 lines
Quoted 4 lines

Best Wishes

Phillip

thanks,
John

Quoted 27 lines
Quoted 27 lines
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
(quoted diff) +21 -0
+	echo 9 >repo/file &&
+
+	old_oid="$(git -C repo rev-parse stash@{0})" &&
+	git -C repo stash &&
+	new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+	cat >expect <<-EOF &&
+	$(test_oid zero) $old_oid
+	$old_oid $new_oid
+	EOF
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	test_cmp expect actual &&
+
+	git -C repo stash drop stash@{1} &&
+	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	cat >expect <<-EOF &&
+	$(test_oid zero) $new_oid
+	EOF
+	test_cmp expect actual
+'
+
    test_expect_success 'stash pop' '
    	git reset --hard &&
    	git stash pop &&