diff mbox series

[v2,03/10] refs: add array of ref namespaces

Message ID d7486390d57849971ee6a4ac27a0a784c41623ee.1659122979.git.gitgitgadget@gmail.com (mailing list archive)
State Superseded
Headers show
Series log: create tighter default decoration filter | expand

Commit Message

Derrick Stolee July 29, 2022, 7:29 p.m. UTC
From: Derrick Stolee <derrickstolee@github.com>

Git interprets different meanings to different refs based on their
names. Some meanings are cosmetic, like how refs in  'refs/remotes/*'
are colored differently from refs in 'refs/heads/*'. Others are more
critical, such as how replace refs are interpreted.

Before making behavior changes based on ref namespaces, collect all
known ref namespaces into a array of ref_namespace_info structs. This
array is indexed by the new ref_namespace enum for quick access.

As of this change, this array is purely documentation. Future changes
will add dependencies on this array.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
---
 environment.c |  2 ++
 notes.c       |  1 +
 refs.c        | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++
 refs.h        | 46 ++++++++++++++++++++++++++++
 4 files changed, 132 insertions(+)

Comments

Junio C Hamano Aug. 3, 2022, 6:16 a.m. UTC | #1
"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +	[NAMESPACE_REPLACE] = {
> +		/*
> +		 * This namespace allows Git to act as if one object ID
> +		 * points to the content of another. Unlike the other
> +		 * ref namespaces, this one can be changed by the
> +		 * GIT_REPLACE_REF_BASE environment variable. This
> +		 * .namespace value will be overwritten in setup_git_env().
> +		 */

Thanks---it is a shame that we have unnecessary flexibility that
requires us to have this code.

> +		.ref = "refs/replace/",
> +		.decoration = DECORATION_GRAFTED,
> +	},
> +	[NAMESPACE_NOTES] = {
> +		/*
> +		 * The refs/notes/commit ref points to the tip of a
> +		 * parallel commit history that adds metadata to commits
> +		 * in the normal history. This branch can be overwritten

This is not "branch" but is a ref.


> +		 * by the core.notesRef config variable or the
> +		 * GIT_NOTES_REFS environment variable.
> +		 */
> +		.ref = "refs/notes/commit",
> +		.exact = 1,

Allowing just "the default" to be replaced by another "custom
default" is a good start, but we probably want to support more than
one notes refs, to parallel how "struct display_notes_opt" has
extra_notes_refs to allow multiple notes refs to decorate objects.
Josh Steadmon Aug. 3, 2022, 10:39 p.m. UTC | #2
On 2022.07.29 19:29, Derrick Stolee via GitGitGadget wrote:
> diff --git a/refs.h b/refs.h
> index 47cb9edbaa8..94e8dedf939 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -2,6 +2,7 @@
>  #define REFS_H
>  
>  #include "cache.h"
> +#include "commit.h"
>  
>  struct object_id;
>  struct ref_store;
> @@ -930,4 +931,49 @@ struct ref_store *get_main_ref_store(struct repository *r);
>  struct ref_store *get_submodule_ref_store(const char *submodule);
>  struct ref_store *get_worktree_ref_store(const struct worktree *wt);
>  
> +/*
> + * Some of the names specified by refs have special meaning to Git.
> + * Organize these namespaces in a comon 'ref_namespaces' array for
> + * reference from multiple places in the codebase.
> + */
> +
> +struct ref_namespace_info {
> +	char *ref;
> +	enum decoration_type decoration;
> +
> +	/*
> +	 * If 'exact' is true, then we must match the 'ref' exactly.
> +	 * Otherwise, use a prefix match.
> +	 *
> +	 * 'orig_ref' is for internal use. It represents whether the

s/orig_ref/ref_updated/ here I think.


> +	 * 'ref' value was replaced from its original literal version.
> +	 */
> +	unsigned exact:1,
> +		 ref_updated:1;
> +};
> +
> +enum ref_namespace {
> +	NAMESPACE_HEAD,
> +	NAMESPACE_BRANCHES,
> +	NAMESPACE_TAGS,
> +	NAMESPACE_REMOTE_REFS,
> +	NAMESPACE_STASH,
> +	NAMESPACE_REPLACE,
> +	NAMESPACE_NOTES,
> +	NAMESPACE_PREFETCH,
> +	NAMESPACE_REWRITTEN,
> +
> +	/* Must be last */
> +	NAMESPACE__COUNT
> +};
> +
> +/* See refs.c for the contents of this array. */
> +extern struct ref_namespace_info ref_namespaces[];
> +
> +/*
> + * Some ref namespaces can be modified by config values or environment
> + * variables. Modify a namespace as specified by its ref_namespace key.
> + */
> +void update_ref_namespace(enum ref_namespace namespace, char *ref);
> +
>  #endif /* REFS_H */
> -- 
> gitgitgadget
>
Derrick Stolee Aug. 4, 2022, 1:29 p.m. UTC | #3
On 8/3/2022 2:16 AM, Junio C Hamano wrote:
> "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
>> +	[NAMESPACE_REPLACE] = {
>> +		/*
>> +		 * This namespace allows Git to act as if one object ID
>> +		 * points to the content of another. Unlike the other
>> +		 * ref namespaces, this one can be changed by the
>> +		 * GIT_REPLACE_REF_BASE environment variable. This
>> +		 * .namespace value will be overwritten in setup_git_env().
>> +		 */
> 
> Thanks---it is a shame that we have unnecessary flexibility that
> requires us to have this code.
> 
>> +		.ref = "refs/replace/",
>> +		.decoration = DECORATION_GRAFTED,
>> +	},
>> +	[NAMESPACE_NOTES] = {
>> +		/*
>> +		 * The refs/notes/commit ref points to the tip of a
>> +		 * parallel commit history that adds metadata to commits
>> +		 * in the normal history. This branch can be overwritten
> 
> This is not "branch" but is a ref.

Thanks!

>> +		 * by the core.notesRef config variable or the
>> +		 * GIT_NOTES_REFS environment variable.
>> +		 */
>> +		.ref = "refs/notes/commit",
>> +		.exact = 1,
> 
> Allowing just "the default" to be replaced by another "custom
> default" is a good start, but we probably want to support more than
> one notes refs, to parallel how "struct display_notes_opt" has
> extra_notes_refs to allow multiple notes refs to decorate objects.

I imagine that if we allowed multiple notes refs, then we would need
to use a ref prefix to define a namespace. If we relaxed that, then
we could modify this as follows:

	.ref = "refs/notes/",
	/* .exact = 0, */

(The comment is included just to illustrate the change.)

There is additional logic in init_notes() to track multiple notes
refs, but the change to things that use ref_namespaces would be
minimal.

Thanks,
-Stolee
Derrick Stolee Aug. 4, 2022, 1:29 p.m. UTC | #4
On 8/3/2022 6:39 PM, Josh Steadmon wrote:
> On 2022.07.29 19:29, Derrick Stolee via GitGitGadget wrote:

>> +	/*
>> +	 * If 'exact' is true, then we must match the 'ref' exactly.
>> +	 * Otherwise, use a prefix match.
>> +	 *
>> +	 * 'orig_ref' is for internal use. It represents whether the
> 
> s/orig_ref/ref_updated/ here I think.

Thanks!
-Stolee
Junio C Hamano Aug. 4, 2022, 4:16 p.m. UTC | #5
Derrick Stolee <derrickstolee@github.com> writes:

>>> +		 * by the core.notesRef config variable or the
>>> +		 * GIT_NOTES_REFS environment variable.
>>> +		 */
>>> +		.ref = "refs/notes/commit",
>>> +		.exact = 1,
>> 
>> Allowing just "the default" to be replaced by another "custom
>> default" is a good start, but we probably want to support more than
>> one notes refs, to parallel how "struct display_notes_opt" has
>> extra_notes_refs to allow multiple notes refs to decorate objects.
>
> I imagine that if we allowed multiple notes refs, then we would need
> to use a ref prefix to define a namespace. If we relaxed that, then
> we could modify this as follows:
>
> 	.ref = "refs/notes/",
> 	/* .exact = 0, */
>
> (The comment is included just to illustrate the change.)

As I do not think it is so bad to decorate a commit with, say,
refs/notes/amlog in "git log --notes" (not "git log --notes=amlog")
output if such a commit happens to appear, so limiting to the single
notes ref that is given by notes.displayRef and ignoring others like
the posted patch felt like an over-engineering that may result in
even a negative gain.  Treating it just like "tags" and "remotes"
hierarchy would match intuition better for people, I suspect.

Thanks.
diff mbox series

Patch

diff --git a/environment.c b/environment.c
index b3296ce7d15..9eb22f975c7 100644
--- a/environment.c
+++ b/environment.c
@@ -185,6 +185,8 @@  void setup_git_env(const char *git_dir)
 	free(git_replace_ref_base);
 	git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
 							  : "refs/replace/");
+	update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
+
 	free(git_namespace);
 	git_namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
 	shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
diff --git a/notes.c b/notes.c
index 7452e71cc8d..7bade6d8f69 100644
--- a/notes.c
+++ b/notes.c
@@ -1005,6 +1005,7 @@  void init_notes(struct notes_tree *t, const char *notes_ref,
 
 	if (!notes_ref)
 		notes_ref = default_notes_ref();
+	update_ref_namespace(NAMESPACE_NOTES, xstrdup(notes_ref));
 
 	if (!combine_notes)
 		combine_notes = combine_notes_concatenate;
diff --git a/refs.c b/refs.c
index ec3134e4842..8e87cc7efb0 100644
--- a/refs.c
+++ b/refs.c
@@ -20,6 +20,7 @@ 
 #include "repository.h"
 #include "sigchain.h"
 #include "date.h"
+#include "commit.h"
 
 /*
  * List of all available backends
@@ -56,6 +57,88 @@  static unsigned char refname_disposition[256] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
 };
 
+struct ref_namespace_info ref_namespaces[] = {
+	[NAMESPACE_HEAD] = {
+		.ref = "HEAD",
+		.decoration = DECORATION_REF_HEAD,
+		.exact = 1,
+	},
+	[NAMESPACE_BRANCHES] = {
+		.ref = "refs/heads/",
+		.decoration = DECORATION_REF_LOCAL,
+	},
+	[NAMESPACE_TAGS] = {
+		.ref = "refs/tags/",
+		.decoration = DECORATION_REF_TAG,
+	},
+	[NAMESPACE_REMOTE_REFS] = {
+		/*
+		 * The default refspec for new remotes copies refs from
+		 * refs/heads/ on the remote into refs/remotes/<remote>/.
+		 * As such, "refs/remotes/" has special handling.
+		 */
+		.ref = "refs/remotes/",
+		.decoration = DECORATION_REF_REMOTE,
+	},
+	[NAMESPACE_STASH] = {
+		/*
+		 * The single ref "refs/stash" stores the latest stash.
+		 * Older stashes can be found in the reflog.
+		 */
+		.ref = "refs/stash",
+		.exact = 1,
+		.decoration = DECORATION_REF_STASH,
+	},
+	[NAMESPACE_REPLACE] = {
+		/*
+		 * This namespace allows Git to act as if one object ID
+		 * points to the content of another. Unlike the other
+		 * ref namespaces, this one can be changed by the
+		 * GIT_REPLACE_REF_BASE environment variable. This
+		 * .namespace value will be overwritten in setup_git_env().
+		 */
+		.ref = "refs/replace/",
+		.decoration = DECORATION_GRAFTED,
+	},
+	[NAMESPACE_NOTES] = {
+		/*
+		 * The refs/notes/commit ref points to the tip of a
+		 * parallel commit history that adds metadata to commits
+		 * in the normal history. This branch can be overwritten
+		 * by the core.notesRef config variable or the
+		 * GIT_NOTES_REFS environment variable.
+		 */
+		.ref = "refs/notes/commit",
+		.exact = 1,
+	},
+	[NAMESPACE_PREFETCH] = {
+		/*
+		 * Prefetch refs are written by the background 'fetch'
+		 * maintenance task. It allows faster foreground fetches
+		 * by advertising these previously-downloaded tips without
+		 * updating refs/remotes/ without user intervention.
+		 */
+		.ref = "refs/prefetch/",
+	},
+	[NAMESPACE_REWRITTEN] = {
+		/*
+		 * Rewritten refs are used by the 'label' command in the
+		 * sequencer. These are particularly useful during an
+		 * interactive rebase that uses the 'merge' command.
+		 */
+		.ref = "refs/rewritten/",
+	},
+};
+
+void update_ref_namespace(enum ref_namespace namespace, char *ref)
+{
+	struct ref_namespace_info *info = &ref_namespaces[namespace];
+	if (info->ref_updated)
+		free(info->ref);
+	info->ref = ref;
+	info->ref_updated = 1;
+}
+
 /*
  * Try to read one refname component from the front of refname.
  * Return the length of the component found, or -1 if the component is
diff --git a/refs.h b/refs.h
index 47cb9edbaa8..94e8dedf939 100644
--- a/refs.h
+++ b/refs.h
@@ -2,6 +2,7 @@ 
 #define REFS_H
 
 #include "cache.h"
+#include "commit.h"
 
 struct object_id;
 struct ref_store;
@@ -930,4 +931,49 @@  struct ref_store *get_main_ref_store(struct repository *r);
 struct ref_store *get_submodule_ref_store(const char *submodule);
 struct ref_store *get_worktree_ref_store(const struct worktree *wt);
 
+/*
+ * Some of the names specified by refs have special meaning to Git.
+ * Organize these namespaces in a comon 'ref_namespaces' array for
+ * reference from multiple places in the codebase.
+ */
+
+struct ref_namespace_info {
+	char *ref;
+	enum decoration_type decoration;
+
+	/*
+	 * If 'exact' is true, then we must match the 'ref' exactly.
+	 * Otherwise, use a prefix match.
+	 *
+	 * 'orig_ref' is for internal use. It represents whether the
+	 * 'ref' value was replaced from its original literal version.
+	 */
+	unsigned exact:1,
+		 ref_updated:1;
+};
+
+enum ref_namespace {
+	NAMESPACE_HEAD,
+	NAMESPACE_BRANCHES,
+	NAMESPACE_TAGS,
+	NAMESPACE_REMOTE_REFS,
+	NAMESPACE_STASH,
+	NAMESPACE_REPLACE,
+	NAMESPACE_NOTES,
+	NAMESPACE_PREFETCH,
+	NAMESPACE_REWRITTEN,
+
+	/* Must be last */
+	NAMESPACE__COUNT
+};
+
+/* See refs.c for the contents of this array. */
+extern struct ref_namespace_info ref_namespaces[];
+
+/*
+ * Some ref namespaces can be modified by config values or environment
+ * variables. Modify a namespace as specified by its ref_namespace key.
+ */
+void update_ref_namespace(enum ref_namespace namespace, char *ref);
+
 #endif /* REFS_H */