Subject: [PATCH 1/3] checkout: move orphaned_commit_warning()

From: Rubén Justo <[email-masked]>
Date: Sun, 23 Apr 2023 00:19:05 +0200

In 8e2dc6ac06 (commit: give final warning when reattaching HEAD to leave
commits behind, 2011-02-18) we introduced orphaned_commit_warning() in
builtin/checkout.c.

In subsequent commits we're going to use orphaned_commit_warning() not
only from builtin/checkout.c, but from other builtin commands too.

Let's move the function and its helpers to checkout.c and make it an
API callable not just from builtin/checkout.c.

Signed-off-by: Rubén Justo <[email-masked]>
---
 builtin/checkout.c | 124 -------------------------------------------
 checkout.c         | 129 +++++++++++++++++++++++++++++++++++++++++++++
 checkout.h         |   9 ++++
 3 files changed, 138 insertions(+), 124 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 6f5d82ed3d..991413ef1a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -656,24 +656,6 @@ static void show_local_changes(struct object *head,
 	release_revisions(&rev);
 }
 
-static void describe_detached_head(const char *msg, struct commit *commit)
-{
-	struct strbuf sb = STRBUF_INIT;
-
-	if (!repo_parse_commit(the_repository, commit))
-		pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
-	if (print_sha1_ellipsis()) {
-		fprintf(stderr, "%s %s... %s\n", msg,
-			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
-			sb.buf);
-	} else {
-		fprintf(stderr, "%s %s %s\n", msg,
-			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
-			sb.buf);
-	}
-	strbuf_release(&sb);
-}
-
 static int reset_tree(struct tree *tree, const struct checkout_opts *o,
 		      int worktree, int *writeout_error,
 		      struct branch_info *info)
@@ -1016,112 +998,6 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 		report_tracking(new_branch_info);
 }
 
-static int add_pending_uninteresting_ref(const char *refname,
-					 const struct object_id *oid,
-					 int flags UNUSED, void *cb_data)
-{
-	add_pending_oid(cb_data, refname, oid, UNINTERESTING);
-	return 0;
-}
-
-static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
-{
-	strbuf_addstr(sb, "  ");
-	strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
-	strbuf_addch(sb, ' ');
-	if (!repo_parse_commit(the_repository, commit))
-		pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
-	strbuf_addch(sb, '\n');
-}
-
-#define ORPHAN_CUTOFF 4
-static void suggest_reattach(struct commit *commit, struct rev_info *revs)
-{
-	struct commit *c, *last = NULL;
-	struct strbuf sb = STRBUF_INIT;
-	int lost = 0;
-	while ((c = get_revision(revs)) != NULL) {
-		if (lost < ORPHAN_CUTOFF)
-			describe_one_orphan(&sb, c);
-		last = c;
-		lost++;
-	}
-	if (ORPHAN_CUTOFF < lost) {
-		int more = lost - ORPHAN_CUTOFF;
-		if (more == 1)
-			describe_one_orphan(&sb, last);
-		else
-			strbuf_addf(&sb, _(" ... and %d more.\n"), more);
-	}
-
-	fprintf(stderr,
-		Q_(
-		/* The singular version */
-		"Warning: you are leaving %d commit behind, "
-		"not connected to\n"
-		"any of your branches:\n\n"
-		"%s\n",
-		/* The plural version */
-		"Warning: you are leaving %d commits behind, "
-		"not connected to\n"
-		"any of your branches:\n\n"
-		"%s\n",
-		/* Give ngettext() the count */
-		lost),
-		lost,
-		sb.buf);
-	strbuf_release(&sb);
-
-	if (advice_enabled(ADVICE_DETACHED_HEAD))
-		fprintf(stderr,
-			Q_(
-			/* The singular version */
-			"If you want to keep it by creating a new branch, "
-			"this may be a good time\nto do so with:\n\n"
-			" git branch <new-branch-name> %s\n\n",
-			/* The plural version */
-			"If you want to keep them by creating a new branch, "
-			"this may be a good time\nto do so with:\n\n"
-			" git branch <new-branch-name> %s\n\n",
-			/* Give ngettext() the count */
-			lost),
-			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
-}
-
-/*
- * We are about to leave commit that was at the tip of a detached
- * HEAD.  If it is not reachable from any ref, this is the last chance
- * for the user to do so without resorting to reflog.
- */
-static void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit)
-{
-	struct rev_info revs;
-	struct object *object = &old_commit->object;
-
-	repo_init_revisions(the_repository, &revs, NULL);
-	setup_revisions(0, NULL, &revs, NULL);
-
-	object->flags &= ~UNINTERESTING;
-	add_pending_object(&revs, object, oid_to_hex(&object->oid));
-
-	for_each_ref(add_pending_uninteresting_ref, &revs);
-	if (new_commit)
-		add_pending_oid(&revs, "HEAD",
-				&new_commit->object.oid,
-				UNINTERESTING);
-
-	if (prepare_revision_walk(&revs))
-		die(_("internal error in revision walk"));
-	if (!(old_commit->object.flags & UNINTERESTING))
-		suggest_reattach(old_commit, &revs);
-	else
-		describe_detached_head(_("Previous HEAD position was"), old_commit);
-
-	/* Clean up objects used, as they will be reused. */
-	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
-	release_revisions(&revs);
-}
-
 static int switch_branches(const struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
diff --git a/checkout.c b/checkout.c
index 04238b2713..18e7362043 100644
--- a/checkout.c
+++ b/checkout.c
@@ -5,6 +5,11 @@
 #include "checkout.h"
 #include "config.h"
 #include "strbuf.h"
+#include "environment.h"
+#include "revision.h"
+#include "advice.h"
+#include "hex.h"
+#include "refs.h"
 
 struct tracking_name_data {
 	/* const */ char *src_ref;
@@ -70,3 +75,127 @@ const char *unique_tracking_name(const char *name, struct object_id *oid,
 	}
 	return NULL;
 }
+
+void describe_detached_head(const char *msg, struct commit *commit)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	if (!repo_parse_commit(the_repository, commit))
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
+	if (print_sha1_ellipsis()) {
+		fprintf(stderr, "%s %s... %s\n", msg,
+			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+			sb.buf);
+	} else {
+		fprintf(stderr, "%s %s %s\n", msg,
+			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+			sb.buf);
+	}
+	strbuf_release(&sb);
+}
+
+static int add_pending_uninteresting_ref(const char *refname,
+					 const struct object_id *oid,
+					 int flags UNUSED, void *cb_data)
+{
+	add_pending_oid(cb_data, refname, oid, UNINTERESTING);
+	return 0;
+}
+
+static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
+{
+	strbuf_addstr(sb, "  ");
+	strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
+	strbuf_addch(sb, ' ');
+	if (!repo_parse_commit(the_repository, commit))
+		pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
+	strbuf_addch(sb, '\n');
+}
+
+#define ORPHAN_CUTOFF 4
+static void suggest_reattach(struct commit *commit, struct rev_info *revs)
+{
+	struct commit *c, *last = NULL;
+	struct strbuf sb = STRBUF_INIT;
+	int lost = 0;
+	while ((c = get_revision(revs)) != NULL) {
+		if (lost < ORPHAN_CUTOFF)
+			describe_one_orphan(&sb, c);
+		last = c;
+		lost++;
+	}
+	if (ORPHAN_CUTOFF < lost) {
+		int more = lost - ORPHAN_CUTOFF;
+		if (more == 1)
+			describe_one_orphan(&sb, last);
+		else
+			strbuf_addf(&sb, _(" ... and %d more.\n"), more);
+	}
+
+	fprintf(stderr,
+		Q_(
+		/* The singular version */
+		"Warning: you are leaving %d commit behind, "
+		"not connected to\n"
+		"any of your branches:\n\n"
+		"%s\n",
+		/* The plural version */
+		"Warning: you are leaving %d commits behind, "
+		"not connected to\n"
+		"any of your branches:\n\n"
+		"%s\n",
+		/* Give ngettext() the count */
+		lost),
+		lost,
+		sb.buf);
+	strbuf_release(&sb);
+
+	if (advice_enabled(ADVICE_DETACHED_HEAD))
+		fprintf(stderr,
+			Q_(
+			/* The singular version */
+			"If you want to keep it by creating a new branch, "
+			"this may be a good time\nto do so with:\n\n"
+			" git branch <new-branch-name> %s\n\n",
+			/* The plural version */
+			"If you want to keep them by creating a new branch, "
+			"this may be a good time\nto do so with:\n\n"
+			" git branch <new-branch-name> %s\n\n",
+			/* Give ngettext() the count */
+			lost),
+			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
+}
+
+/*
+ * We are about to leave commit that was at the tip of a detached
+ * HEAD.  If it is not reachable from any ref, this is the last chance
+ * for the user to do so without resorting to reflog.
+ */
+void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit)
+{
+	struct rev_info revs;
+	struct object *object = &old_commit->object;
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	setup_revisions(0, NULL, &revs, NULL);
+
+	object->flags &= ~UNINTERESTING;
+	add_pending_object(&revs, object, oid_to_hex(&object->oid));
+
+	for_each_ref(add_pending_uninteresting_ref, &revs);
+	if (new_commit)
+		add_pending_oid(&revs, "HEAD",
+				&new_commit->object.oid,
+				UNINTERESTING);
+
+	if (prepare_revision_walk(&revs))
+		die(_("internal error in revision walk"));
+	if (!(old_commit->object.flags & UNINTERESTING))
+		suggest_reattach(old_commit, &revs);
+	else
+		describe_detached_head(_("Previous HEAD position was"), old_commit);
+
+	/* Clean up objects used, as they will be reused. */
+	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+	release_revisions(&revs);
+}
diff --git a/checkout.h b/checkout.h
index 1917f3b323..c7dc056544 100644
--- a/checkout.h
+++ b/checkout.h
@@ -2,6 +2,7 @@
 #define CHECKOUT_H
 
 #include "hash.h"
+#include "commit.h"
 
 /*
  * Check if the branch name uniquely matches a branch name on a remote
@@ -12,4 +13,12 @@ const char *unique_tracking_name(const char *name,
 				 struct object_id *oid,
 				 int *dwim_remotes_matched);
 
+/*
+ * We are about to leave commit that was at the tip of a detached
+ * HEAD.  If it is not reachable from any ref, this is the last chance
+ * for the user to do so without resorting to reflog.
+ */
+void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit);
+
+void describe_detached_head(const char *msg, struct commit *commit);
 #endif /* CHECKOUT_H */
-- 
2.39.2