Re: Should "head" also work for "HEAD" on case-insensitive FS?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Junio C Hamano <gitster@xxxxxxxxx> writes:

> Once Michael's packed-refs backend stabilizes, we may have a nice
> calm period in the refs subsystem and I expect that this will become
> a good medium-sized project for a contributor who does not have to 
> be so experienced (but not a complete newbie).
>
> It needs to:
>
>  - add icase-files-backend, preferrably sharing as much code as the
>    existing files-backend, in refs/.
>
>  - design a mechanism to configure which refs backend to use at
>    runtime; as this has to be done fairly early in the control flow,
>    this will likely to use early configuration mechanism and will
>    probably need to be done in the set-up code, but doing it lazy
>    may even be nicer, as not all subcommands need access to refs.
>
> Thanks for a pointer to the archive.

So here is an early WIP/illustration I did to see how involved such
a change would be, which should apply cleanly on top of 'pu'.

I was pleasantly surprised how cleanly refs/files-backend.c
separates the notion of "path" and "refname".  Only two functions,
files_reflog_path() and files_ref_path(), are responsible for taking
the refname and turning it to the pathname of an filesystem entity.
One one function, loose_fill_ref_dir(), is responsible for running
readdir() to find pathname, and turning the result into a refname.
So in theory, these three are the only things that need to know
about the "encoding".

The exact detail of the encoding used here is immaterial, but I just
used "encode uppercase letters and % as % followed by two hex",
which was simple enough.  Usual refs/heads/master and friends will
not have to be touched when encoded this way.  Perhaps the decoding
side should be tweaked so that uppercase letters it sees needs to be
downcased to avoid "refs/heads/Foo" getting returned as "Foo" branch,
as a "Foo" branch should have been encoded as "refs/heads/%46oo".

Having said that, this patch alone does not quite work yet.

 * In the repository discovery code, we have some logic that
   hard-codes the path in the directory (which is a candidate for
   being a repository) to check, like "refs/" and "HEAD".  In the
   attached illustration patch, files_path_encode() special cases
   "HEAD" so that it is not munged, which is a bit of ugly
   workaround for this.

 * I haven't figured out why, but what refs.c calls "pseudo refs"
   bypasses the files backend layer for some but not all operations,
   which causes t1405-main-ref-store to fail.  The test creates a
   "pseudo ref" FOO and then tries to remove it.  Creation seems to
   follow the files-backend.c and thusly goes through the escaping;
   refs.::refs_delete_ref() however does not consult files-backend.c
   and fails to find and delete .git/FOO, because the creation side
   encoded it as ".git/%46%4F%4F".

Michael is CC'ed as I thought it would be simpler to just ask about
the latter bullet point than digging it further myself ;-)

Thanks.

 refs/files-backend.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 56 insertions(+), 6 deletions(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index 923e481e06..5bde77cbf8 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -23,6 +23,7 @@ struct ref_lock {
 struct files_ref_store {
 	struct ref_store base;
 	unsigned int store_flags;
+	int encode_names;
 
 	char *gitdir;
 	char *gitcommondir;
@@ -54,6 +55,9 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
 	base_ref_store_init(ref_store, &refs_be_files);
 	refs->store_flags = flags;
 
+	/* git_config_get_bool("core.escapeLooseRefNames", &refs->encode_names); */
+	refs->encode_names = 1;
+
 	refs->gitdir = xstrdup(gitdir);
 	get_common_dir_noenv(&sb, gitdir);
 	refs->gitcommondir = strbuf_detach(&sb, NULL);
@@ -102,6 +106,49 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
 	return refs;
 }
 
+static void files_path_encode(struct files_ref_store *refs,
+			      struct strbuf *sb, const char *refname)
+{
+	if (!refs->encode_names || !strcmp(refname, "HEAD")) {
+		strbuf_addstr(sb, refname);
+	} else {
+		const char *cp;
+
+		for (cp = refname; *cp; cp++) {
+			int ch = *cp;
+			if (('A' <= ch && ch <= 'Z') || (ch == '%'))
+				strbuf_addf(sb, "%%%02x", ch);
+			else
+				strbuf_addch(sb, ch);
+		}
+	}
+}
+
+static int files_path_decode(struct files_ref_store *refs,
+			     struct strbuf *sb, const char *name, int namelen)
+{
+	if (!refs->encode_names) {
+		strbuf_add(sb, name, namelen);
+	} else {
+		size_t origlen = sb->len;
+
+		while (namelen--) {
+			int ch = *name++;
+
+			if (ch == '%') {
+				if (namelen < 2 || (ch = hex2chr(name)) < 0) {
+					strbuf_setlen(sb, origlen);
+					return -1;
+				}
+				namelen -= 2;
+				name += 2;
+			}
+			strbuf_addch(sb, ch);
+		}
+	}
+	return 0;
+}
+
 static void files_reflog_path(struct files_ref_store *refs,
 			      struct strbuf *sb,
 			      const char *refname)
@@ -118,15 +165,16 @@ static void files_reflog_path(struct files_ref_store *refs,
 	switch (ref_type(refname)) {
 	case REF_TYPE_PER_WORKTREE:
 	case REF_TYPE_PSEUDOREF:
-		strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
+		strbuf_addf(sb, "%s/logs/", refs->gitdir);
 		break;
 	case REF_TYPE_NORMAL:
-		strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
+		strbuf_addf(sb, "%s/logs/", refs->gitcommondir);
 		break;
 	default:
 		die("BUG: unknown ref type %d of ref %s",
 		    ref_type(refname), refname);
 	}
+	files_path_encode(refs, sb, refname);
 }
 
 static void files_ref_path(struct files_ref_store *refs,
@@ -136,15 +184,16 @@ static void files_ref_path(struct files_ref_store *refs,
 	switch (ref_type(refname)) {
 	case REF_TYPE_PER_WORKTREE:
 	case REF_TYPE_PSEUDOREF:
-		strbuf_addf(sb, "%s/%s", refs->gitdir, refname);
+		strbuf_addf(sb, "%s/", refs->gitdir);
 		break;
 	case REF_TYPE_NORMAL:
-		strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
+		strbuf_addf(sb, "%s/", refs->gitcommondir);
 		break;
 	default:
 		die("BUG: unknown ref type %d of ref %s",
 		    ref_type(refname), refname);
 	}
+	files_path_encode(refs, sb, refname);
 }
 
 /*
@@ -174,7 +223,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
 	}
 
 	strbuf_init(&refname, dirnamelen + 257);
-	strbuf_add(&refname, dirname, dirnamelen);
+	files_path_decode(refs, &refname, dirname, dirnamelen);
 
 	while ((de = readdir(d)) != NULL) {
 		struct object_id oid;
@@ -185,7 +234,8 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
 			continue;
 		if (ends_with(de->d_name, ".lock"))
 			continue;
-		strbuf_addstr(&refname, de->d_name);
+		if (files_path_decode(refs, &refname, de->d_name, strlen(de->d_name)))
+			continue;
 		strbuf_addstr(&path, de->d_name);
 		if (stat(path.buf, &st) < 0) {
 			; /* silently ignore */







[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux