{"thread":{"id":"27515","subject":"[PATCHv4 1/4] Refactor for_each_ref variants to use for_each_ref_in and avoid magic numbers","startedAt":"2011-06-01T00:24:27Z","lastAt":"2011-06-09T09:09:00Z","messageCount":20,"participants":["Jamey Sharp","Junio C Hamano","Josh Triplett","josh@joshtriplett.org","Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"169088","messageId":"1306887870-3875-1-git-send-email-jamey@minilop.net","threadId":"27515","inReplyTo":null,"subject":"[PATCHv4 1/4] Refactor for_each_ref variants to use for_each_ref_in and avoid magic numbers","fromName":"Jamey Sharp","fromEmail":"jamey@minilop.net","sentAt":"2011-06-01T00:24:27Z","receivedAt":"2011-06-01T00:24:27Z","isPatch":false,"sender":{"key":"jamey@minilop.net","avatar":"https://gravatar.com/avatar/979ed4f9190c19c13a84945afe9a6cf141e8884ef6ddaae342db7a74ae28828e?d=mp&s=160"},"body":"From: Josh Triplett <josh@joshtriplett.org>\n\nSeveral variants of the for_each_ref functions call do_for_each_ref with\nboth a fixed string prefix and the hardcoded length of that prefix.\nFurthermore, for_each_ref and for_each_ref_submodule passed \"refs/\" but\na length of 0, which caused do_for_each_ref to ignore the \"refs/\".\n\nChange do_for_each_ref to use prefixcmp instead, and change the\nfor_each_ref variants to call for_each_ref_in with the prefix they\nactually want.\n\nLeave the separate \"trim\" parameter for callers that want to require a\nprefix but not strip off that prefix.\n\nCommit by Josh Triplett and Jamey Sharp.\n\nSigned-off-by: Josh Triplett <josh@joshtriplett.org>\nSigned-off-by: Jamey Sharp <jamey@minilop.net>\n---\nWith this change the \"trim\" parameter always equals strlen(base), but we\nuse trim=0 again in a later patch in this series.\n\n refs.c |   10 +++++-----\n 1 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex e3c0511..60cebe6 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -584,7 +584,7 @@ int read_ref(const char *ref, unsigned char *sha1)\n static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n \t\t      int flags, void *cb_data, struct ref_list *entry)\n {\n-\tif (strncmp(base, entry->name, trim))\n+\tif (prefixcmp(entry->name, base))\n \t\treturn 0;\n \n \tif (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {\n@@ -728,12 +728,12 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n \n int for_each_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"refs/\", fn, 0, 0, cb_data);\n+\treturn for_each_ref_in(\"\", fn, cb_data);\n }\n \n int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(submodule, \"refs/\", fn, 0, 0, cb_data);\n+\treturn for_each_ref_in_submodule(submodule, \"\", fn, cb_data);\n }\n \n int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)\n@@ -779,7 +779,7 @@ int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *c\n \n int for_each_replace_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"refs/replace/\", fn, 13, 0, cb_data);\n+\treturn for_each_ref_in(\"refs/replace/\", fn, cb_data);\n }\n \n int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n@@ -819,7 +819,7 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)\n \n int for_each_rawref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"refs/\", fn, 0,\n+\treturn do_for_each_ref(NULL, \"\", fn, 0,\n \t\t\t       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n }\n \n-- \n1.7.5.3\n"},{"id":"169089","messageId":"1306887870-3875-2-git-send-email-jamey@minilop.net","threadId":"27515","inReplyTo":"1306887870-3875-1-git-send-email-jamey@minilop.net","subject":"[PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Jamey Sharp","fromEmail":"jamey@minilop.net","sentAt":"2011-06-01T00:24:28Z","receivedAt":"2011-06-01T00:24:28Z","isPatch":false,"sender":{"key":"jamey@minilop.net","avatar":"https://gravatar.com/avatar/979ed4f9190c19c13a84945afe9a6cf141e8884ef6ddaae342db7a74ae28828e?d=mp&s=160"},"body":"From: Josh Triplett <josh@joshtriplett.org>\n\nAdd support for dividing the refs of a single repository into multiple\nnamespaces, each of which can have its own branches, tags, and HEAD.\nGit can expose each namespace as an independent repository to pull from\nand push to, while sharing the object store, and exposing all the refs\nto operations such as git-gc.\n\nStoring multiple repositories as namespaces of a single repository\navoids storing duplicate copies of the same objects, such as when\nstoring multiple branches of the same source.  The alternates mechanism\nprovides similar support for avoiding duplicates, but alternates do not\nprevent duplication between new objects added to the repositories\nwithout ongoing maintenance, while namespaces do.\n\nTo specify a namespace, set the GIT_NAMESPACE environment variable to\nthe namespace.  For each ref namespace, git stores the corresponding\nrefs in a directory under refs/namespaces/.  For example,\nGIT_NAMESPACE=foo will store refs under refs/namespaces/foo/.  You can\nalso specify namespaces via the --namespace option to git.\n\nNote that namespaces which include a / will expand to a hierarchy of\nnamespaces; for example, GIT_NAMESPACE=foo/bar will store refs under\nrefs/namespaces/foo/refs/namespaces/bar/.  This makes GIT_NAMESPACE\nbehave hierarchically, and avoids ambiguity with namespaces such as\nfoo/refs/heads.\n\nThis adds the infrastructure for ref namespaces: handling the\nGIT_NAMESPACE environment variable and --namespace option, and iterating\nover refs in a namespace.  Subsequent commits use this infrastructure to\nimplement the user-visible support for ref namespaces.\n\nCommit by Josh Triplett and Jamey Sharp.\n\nSigned-off-by: Josh Triplett <josh@joshtriplett.org>\nSigned-off-by: Jamey Sharp <jamey@minilop.net>\n---\nIn v4, we took Jeff King's suggestion to do most of the work in refs.c,\nenforcing a naming convention that places HEAD with the namespaced refs.\nWe also renamed the concept from \"virtual repositories\" to \"namespaces\",\nin hopes that that term will be more clear.\n\n cache.h                                |    3 ++\n contrib/completion/git-completion.bash |    1 +\n environment.c                          |   41 ++++++++++++++++++++++++++++++++\n git.c                                  |   18 ++++++++++++-\n refs.c                                 |   25 +++++++++++++++++++\n refs.h                                 |    3 ++\n 6 files changed, 89 insertions(+), 2 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex e11cf6a..b256a94 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -379,6 +379,7 @@ static inline enum object_type object_type(unsigned int mode)\n }\n \n #define GIT_DIR_ENVIRONMENT \"GIT_DIR\"\n+#define GIT_NAMESPACE_ENVIRONMENT \"GIT_NAMESPACE\"\n #define GIT_WORK_TREE_ENVIRONMENT \"GIT_WORK_TREE\"\n #define DEFAULT_GIT_DIR_ENVIRONMENT \".git\"\n #define DB_ENVIRONMENT \"GIT_OBJECT_DIRECTORY\"\n@@ -419,6 +420,8 @@ extern char *get_object_directory(void);\n extern char *get_index_file(void);\n extern char *get_graft_file(void);\n extern int set_git_dir(const char *path);\n+extern const char *get_git_namespace(void);\n+extern const char *strip_namespace(const char *namespaced_ref);\n extern const char *get_git_work_tree(void);\n extern const char *read_gitfile_gently(const char *path);\n extern void set_git_work_tree(const char *tree);\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex b36290f..b10a1ec 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2640,6 +2640,7 @@ _git ()\n \t\t\t--exec-path\n \t\t\t--html-path\n \t\t\t--work-tree=\n+\t\t\t--namespace=\n \t\t\t--help\n \t\t\t\"\n \t\t\t;;\ndiff --git a/environment.c b/environment.c\nindex 94d58fd..aad274b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -8,6 +8,7 @@\n  * are.\n  */\n #include \"cache.h\"\n+#include \"refs.h\"\n \n char git_default_email[MAX_GITNAME];\n char git_default_name[MAX_GITNAME];\n@@ -65,6 +66,9 @@ int core_preload_index = 0;\n char *git_work_tree_cfg;\n static char *work_tree;\n \n+static const char *namespace;\n+static size_t namespace_len;\n+\n static const char *git_dir;\n static char *git_object_dir, *git_index_file, *git_graft_file;\n \n@@ -86,6 +90,27 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {\n \tNULL\n };\n \n+static char *expand_namespace(const char *raw_namespace)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf **components, **c;\n+\n+\tif (!raw_namespace || !*raw_namespace)\n+\t\treturn xstrdup(\"\");\n+\n+\tstrbuf_addstr(&buf, raw_namespace);\n+\tcomponents = strbuf_split(&buf, '/');\n+\tstrbuf_reset(&buf);\n+\tfor (c = components; *c; c++)\n+\t\tif (strcmp((*c)->buf, \"/\") != 0)\n+\t\t\tstrbuf_addf(&buf, \"refs/namespaces/%s\", (*c)->buf);\n+\tstrbuf_list_free(components);\n+\tif (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK)\n+\t\tdie(\"bad git namespace path \\\"%s\\\"\", raw_namespace);\n+\tstrbuf_addch(&buf, '/');\n+\treturn strbuf_detach(&buf, NULL);\n+}\n+\n static void setup_git_env(void)\n {\n \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n@@ -111,6 +136,8 @@ static void setup_git_env(void)\n \t\tgit_graft_file = git_pathdup(\"info/grafts\");\n \tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n \t\tread_replace_refs = 0;\n+\tnamespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));\n+\tnamespace_len = strlen(namespace);\n }\n \n int is_bare_repository(void)\n@@ -131,6 +158,20 @@ const char *get_git_dir(void)\n \treturn git_dir;\n }\n \n+const char *get_git_namespace(void)\n+{\n+\tif (!namespace)\n+\t\tsetup_git_env();\n+\treturn namespace;\n+}\n+\n+const char *strip_namespace(const char *namespaced_ref)\n+{\n+\tif (prefixcmp(namespaced_ref, get_git_namespace()) != 0)\n+\t\treturn NULL;\n+\treturn namespaced_ref + namespace_len;\n+}\n+\n static int git_work_tree_initialized;\n \n /*\ndiff --git a/git.c b/git.c\nindex 89721d4..98cbf7b 100644\n--- a/git.c\n+++ b/git.c\n@@ -7,8 +7,8 @@\n \n const char git_usage_string[] =\n \t\"git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\\n\"\n-\t\"           [-p|--paginate|--no-pager] [--no-replace-objects]\\n\"\n-\t\"           [--bare] [--git-dir=<path>] [--work-tree=<path>]\\n\"\n+\t\"           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\\n\"\n+\t\"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\\n\"\n \t\"           [-c name=value] [--help]\\n\"\n \t\"           <command> [<args>]\";\n \n@@ -126,6 +126,20 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tsetenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);\n \t\t\tif (envchanged)\n \t\t\t\t*envchanged = 1;\n+\t\t} else if (!strcmp(cmd, \"--namespace\")) {\n+\t\t\tif (*argc < 2) {\n+\t\t\t\tfprintf(stderr, \"No directory given for --namespace.\\n\" );\n+\t\t\t\tusage(git_usage_string);\n+\t\t\t}\n+\t\t\tsetenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);\n+\t\t\tif (envchanged)\n+\t\t\t\t*envchanged = 1;\n+\t\t\t(*argv)++;\n+\t\t\t(*argc)--;\n+\t\t} else if (!prefixcmp(cmd, \"--namespace=\")) {\n+\t\t\tsetenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);\n+\t\t\tif (envchanged)\n+\t\t\t\t*envchanged = 1;\n \t\t} else if (!strcmp(cmd, \"--work-tree\")) {\n \t\t\tif (*argc < 2) {\n \t\t\t\tfprintf(stderr, \"No directory given for --work-tree.\\n\" );\ndiff --git a/refs.c b/refs.c\nindex 60cebe6..96f64ed 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -782,6 +782,31 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)\n \treturn for_each_ref_in(\"refs/replace/\", fn, cb_data);\n }\n \n+int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint ret = 0;\n+\tunsigned char sha1[20];\n+\tint flag;\n+\n+\tstrbuf_addf(&buf, \"%sHEAD\", get_git_namespace());\n+\tif (resolve_ref(buf.buf, sha1, 1, &flag))\n+\t\tret = fn(buf.buf, sha1, flag, cb_data);\n+\tstrbuf_release(&buf);\n+\n+\treturn ret;\n+}\n+\n+int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint ret;\n+\tstrbuf_addf(&buf, \"%srefs/\", get_git_namespace());\n+\tret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);\n+\tstrbuf_release(&buf);\n+\treturn ret;\n+}\n+\n int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n \tconst char *prefix, void *cb_data)\n {\ndiff --git a/refs.h b/refs.h\nindex 5e7a9a5..9a5c9e0 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -36,6 +36,9 @@ extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, voi\n extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);\n extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);\n \n+extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);\n+extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);\n+\n static inline const char *has_glob_specials(const char *pattern)\n {\n \treturn strpbrk(pattern, \"?*[\");\n-- \n1.7.5.3\n"},{"id":"169090","messageId":"1306887870-3875-3-git-send-email-jamey@minilop.net","threadId":"27515","inReplyTo":"1306887870-3875-1-git-send-email-jamey@minilop.net","subject":"[PATCHv4 3/4] Support ref namespaces for remote repositories via upload-pack and receive-pack","fromName":"Jamey Sharp","fromEmail":"jamey@minilop.net","sentAt":"2011-06-01T00:24:29Z","receivedAt":"2011-06-01T00:24:29Z","isPatch":false,"sender":{"key":"jamey@minilop.net","avatar":"https://gravatar.com/avatar/979ed4f9190c19c13a84945afe9a6cf141e8884ef6ddaae342db7a74ae28828e?d=mp&s=160"},"body":"From: Josh Triplett <josh@joshtriplett.org>\n\nChange upload-pack and receive-pack to use the namespace-prefixed refs\nwhen working with the repository, and use the unprefixed refs when\ntalking to the client, maintaining the masquerade.  This allows\nclone, pull, fetch, and push to work with a suitably configured\nGIT_NAMESPACE.\n\nWith appropriate configuration, this also allows http-backend to expose\nnamespaces as multiple repositories with different paths.  This only\nrequires setting GIT_NAMESPACE, which http-backend passes through to\nupload-pack and receive-pack.\n\nCommit by Josh Triplett and Jamey Sharp.\n\nSigned-off-by: Josh Triplett <josh@joshtriplett.org>\nSigned-off-by: Jamey Sharp <jamey@minilop.net>\n---\nThis patch is not that different from v3, but now uses general\ninfrastructure from refs.c introduced in patch 2/4.\n\n builtin/receive-pack.c |   32 +++++++++++++++++++++++++-------\n upload-pack.c          |   15 ++++++++-------\n 2 files changed, 33 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex e1a687a..9bb268a 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -109,6 +109,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \n static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n+\tpath = path ? strip_namespace(path) : \"capabilities^{}\";\n \tif (sent_capabilities)\n \t\tpacket_write(1, \"%s %s\\n\", sha1_to_hex(sha1), path);\n \telse\n@@ -122,9 +123,9 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void\n \n static void write_head_info(void)\n {\n-\tfor_each_ref(show_ref, NULL);\n+\tfor_each_namespaced_ref(show_ref, NULL);\n \tif (!sent_capabilities)\n-\t\tshow_ref(\"capabilities^{}\", null_sha1, 0, NULL);\n+\t\tshow_ref(NULL, null_sha1, 0, NULL);\n \n }\n \n@@ -333,6 +334,8 @@ static void refuse_unconfigured_deny_delete_current(void)\n static const char *update(struct command *cmd)\n {\n \tconst char *name = cmd->ref_name;\n+\tstruct strbuf namespaced_name_buf = STRBUF_INIT;\n+\tconst char *namespaced_name;\n \tunsigned char *old_sha1 = cmd->old_sha1;\n \tunsigned char *new_sha1 = cmd->new_sha1;\n \tstruct ref_lock *lock;\n@@ -343,7 +346,10 @@ static const char *update(struct command *cmd)\n \t\treturn \"funny refname\";\n \t}\n \n-\tif (is_ref_checked_out(name)) {\n+\tstrbuf_addf(&namespaced_name_buf, \"%s%s\", get_git_namespace(), name);\n+\tnamespaced_name = strbuf_detach(&namespaced_name_buf, NULL);\n+\n+\tif (is_ref_checked_out(namespaced_name)) {\n \t\tswitch (deny_current_branch) {\n \t\tcase DENY_IGNORE:\n \t\t\tbreak;\n@@ -371,7 +377,7 @@ static const char *update(struct command *cmd)\n \t\t\treturn \"deletion prohibited\";\n \t\t}\n \n-\t\tif (!strcmp(name, head_name)) {\n+\t\tif (!strcmp(namespaced_name, head_name)) {\n \t\t\tswitch (deny_delete_current) {\n \t\t\tcase DENY_IGNORE:\n \t\t\t\tbreak;\n@@ -427,14 +433,14 @@ static const char *update(struct command *cmd)\n \t\t\trp_warning(\"Allowing deletion of corrupt ref.\");\n \t\t\told_sha1 = NULL;\n \t\t}\n-\t\tif (delete_ref(name, old_sha1, 0)) {\n+\t\tif (delete_ref(namespaced_name, old_sha1, 0)) {\n \t\t\trp_error(\"failed to delete %s\", name);\n \t\t\treturn \"failed to delete\";\n \t\t}\n \t\treturn NULL; /* good */\n \t}\n \telse {\n-\t\tlock = lock_any_ref_for_update(name, old_sha1, 0);\n+\t\tlock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);\n \t\tif (!lock) {\n \t\t\trp_error(\"failed to lock %s\", name);\n \t\t\treturn \"failed to lock\";\n@@ -491,17 +497,29 @@ static void run_update_post_hook(struct command *commands)\n \n static void check_aliased_update(struct command *cmd, struct string_list *list)\n {\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *dst_name;\n \tstruct string_list_item *item;\n \tstruct command *dst_cmd;\n \tunsigned char sha1[20];\n \tchar cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n \tint flag;\n \n-\tconst char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);\n+\tstrbuf_addf(&buf, \"%s%s\", get_git_namespace(), cmd->ref_name);\n+\tdst_name = resolve_ref(buf.buf, sha1, 0, &flag);\n+\tstrbuf_release(&buf);\n \n \tif (!(flag & REF_ISSYMREF))\n \t\treturn;\n \n+\tdst_name = strip_namespace(dst_name);\n+\tif (!dst_name) {\n+\t\trp_error(\"refusing update to broken symref '%s'\", cmd->ref_name);\n+\t\tcmd->skip_update = 1;\n+\t\tcmd->error_string = \"broken symref\";\n+\t\treturn;\n+\t}\n+\n \tif ((item = string_list_lookup(list, dst_name)) == NULL)\n \t\treturn;\n \ndiff --git a/upload-pack.c b/upload-pack.c\nindex ce5cbbe..267e5b1 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -641,16 +641,17 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo\n \t\t\" side-band-64k ofs-delta shallow no-progress\"\n \t\t\" include-tag multi_ack_detailed\";\n \tstruct object *o = parse_object(sha1);\n+\tconst char *refname_nons = strip_namespace(refname);\n \n \tif (!o)\n \t\tdie(\"git upload-pack: cannot find object %s:\", sha1_to_hex(sha1));\n \n \tif (capabilities)\n-\t\tpacket_write(1, \"%s %s%c%s%s\\n\", sha1_to_hex(sha1), refname,\n+\t\tpacket_write(1, \"%s %s%c%s%s\\n\", sha1_to_hex(sha1), refname_nons,\n \t\t\t     0, capabilities,\n \t\t\t     stateless_rpc ? \" no-done\" : \"\");\n \telse\n-\t\tpacket_write(1, \"%s %s\\n\", sha1_to_hex(sha1), refname);\n+\t\tpacket_write(1, \"%s %s\\n\", sha1_to_hex(sha1), refname_nons);\n \tcapabilities = NULL;\n \tif (!(o->flags & OUR_REF)) {\n \t\to->flags |= OUR_REF;\n@@ -659,7 +660,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo\n \tif (o->type == OBJ_TAG) {\n \t\to = deref_tag(o, refname, 0);\n \t\tif (o)\n-\t\t\tpacket_write(1, \"%s %s^{}\\n\", sha1_to_hex(o->sha1), refname);\n+\t\t\tpacket_write(1, \"%s %s^{}\\n\", sha1_to_hex(o->sha1), refname_nons);\n \t}\n \treturn 0;\n }\n@@ -680,12 +681,12 @@ static void upload_pack(void)\n {\n \tif (advertise_refs || !stateless_rpc) {\n \t\treset_timeout();\n-\t\thead_ref(send_ref, NULL);\n-\t\tfor_each_ref(send_ref, NULL);\n+\t\thead_ref_namespaced(send_ref, NULL);\n+\t\tfor_each_namespaced_ref(send_ref, NULL);\n \t\tpacket_flush(1);\n \t} else {\n-\t\thead_ref(mark_our_ref, NULL);\n-\t\tfor_each_ref(mark_our_ref, NULL);\n+\t\thead_ref_namespaced(mark_our_ref, NULL);\n+\t\tfor_each_namespaced_ref(mark_our_ref, NULL);\n \t}\n \tif (advertise_refs)\n \t\treturn;\n-- \n1.7.5.3\n"},{"id":"169091","messageId":"1306887870-3875-4-git-send-email-jamey@minilop.net","threadId":"27515","inReplyTo":"1306887870-3875-1-git-send-email-jamey@minilop.net","subject":"[PATCHv4 4/4] Add documentation for ref namespaces","fromName":"Jamey Sharp","fromEmail":"jamey@minilop.net","sentAt":"2011-06-01T00:24:30Z","receivedAt":"2011-06-01T00:24:30Z","isPatch":false,"sender":{"key":"jamey@minilop.net","avatar":"https://gravatar.com/avatar/979ed4f9190c19c13a84945afe9a6cf141e8884ef6ddaae342db7a74ae28828e?d=mp&s=160"},"body":"From: Josh Triplett <josh@joshtriplett.org>\n\nDocument the namespace mechanism in a new gitnamespaces(7) page.\nReference it from receive-pack and upload-pack.\n\nDocument the new --namespace option and GIT_NAMESPACE environment\nvariable in git(1), and reference gitnamespaces(7).\n\nAdd a sample Apache configuration to http-backend(1) to support\nnamespaced repositories, and reference gitnamespaces(7).\n\nCommit by Josh Triplett and Jamey Sharp.\n\nSigned-off-by: Josh Triplett <josh@joshtriplett.org>\nSigned-off-by: Jamey Sharp <jamey@minilop.net>\n---\nIn v4 we've tried to address reviewers' concerns about unclear and\ninadequate documentation, and of course updated for functional changes\nand revised terminology in this revision of the patch series.\n\nNote that the \"CONVENTIONS\" section from v3's documentation is gone\nbecause there is now only one way to set up namespaces.\n\n Documentation/Makefile                 |    2 +-\n Documentation/git-http-backend.txt     |    8 ++++\n Documentation/git-receive-pack.txt     |    2 +-\n Documentation/git-upload-pack.txt      |    4 ++\n Documentation/git.txt                  |   13 +++++-\n Documentation/gitnamespaces.txt        |   71 ++++++++++++++++++++++++++++++++\n contrib/completion/git-completion.bash |    2 +-\n 7 files changed, 97 insertions(+), 5 deletions(-)\n create mode 100644 Documentation/gitnamespaces.txt\n\ndiff --git a/Documentation/Makefile b/Documentation/Makefile\nindex 36989b7..2004fbe 100644\n--- a/Documentation/Makefile\n+++ b/Documentation/Makefile\n@@ -6,7 +6,7 @@ MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt \\\n \tgitrepository-layout.txt\n MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \\\n \tgitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \\\n-\tgitdiffcore.txt gitrevisions.txt gitworkflows.txt\n+\tgitdiffcore.txt gitnamespaces.txt gitrevisions.txt gitworkflows.txt\n \n MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)\n MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))\ndiff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt\nindex 277d9e1..f4e0741 100644\n--- a/Documentation/git-http-backend.txt\n+++ b/Documentation/git-http-backend.txt\n@@ -119,6 +119,14 @@ ScriptAliasMatch \\\n \n ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/\n ----------------------------------------------------------------\n++\n+To serve multiple repositories from different linkgit:gitnamespaces[7] in a\n+single repository:\n++\n+----------------------------------------------------------------\n+SetEnvIf Request_URI \"^/git/([^/]*)\" GIT_NAMESPACE=$1\n+ScriptAliasMatch ^/git/[^/]*(.*) /usr/libexec/git-core/git-http-backend/storage.git$1\n+----------------------------------------------------------------\n \n Accelerated static Apache 2.x::\n \tSimilar to the above, but Apache can be used to return static\ndiff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt\nindex f34e0ae..3534ba0 100644\n--- a/Documentation/git-receive-pack.txt\n+++ b/Documentation/git-receive-pack.txt\n@@ -149,7 +149,7 @@ if the repository is packed and is served via a dumb transport.\n \n SEE ALSO\n --------\n-linkgit:git-send-pack[1]\n+linkgit:git-send-pack[1], linkgit:gitnamespaces[7]\n \n GIT\n ---\ndiff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt\nindex 4c0ca9d..61a9a04 100644\n--- a/Documentation/git-upload-pack.txt\n+++ b/Documentation/git-upload-pack.txt\n@@ -33,6 +33,10 @@ OPTIONS\n <directory>::\n \tThe repository to sync from.\n \n+SEE ALSO\n+--------\n+linkgit:gitnamespaces[7]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 65317cc..297590f 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -10,8 +10,8 @@ SYNOPSIS\n --------\n [verse]\n 'git' [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n-    [-p|--paginate|--no-pager] [--no-replace-objects]\n-    [--bare] [--git-dir=<path>] [--work-tree=<path>]\n+    [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n+    [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n     [-c <name>=<value>]\n     [--help] <command> [<args>]\n \n@@ -324,6 +324,11 @@ help ...`.\n \tvariable (see core.worktree in linkgit:git-config[1] for a\n \tmore detailed discussion).\n \n+--namespace=<path>::\n+\tSet the git namespace.  See linkgit:gitnamespaces[7] for more\n+\tdetails.  Equivalent to setting the `GIT_NAMESPACE` environment\n+\tvariable.\n+\n --bare::\n \tTreat the repository as a bare repository.  If GIT_DIR\n \tenvironment is not set, it is set to the current working\n@@ -588,6 +593,10 @@ git so take care if using Cogito etc.\n \tThis can also be controlled by the '--work-tree' command line\n \toption and the core.worktree configuration variable.\n \n+'GIT_NAMESPACE'::\n+\tSet the git namespace; see linkgit:gitnamespaces[7] for details.\n+\tThe '--namespace' command-line option also sets this value.\n+\n 'GIT_CEILING_DIRECTORIES'::\n \tThis should be a colon-separated list of absolute paths.\n \tIf set, it is a list of directories that git should not chdir\ndiff --git a/Documentation/gitnamespaces.txt b/Documentation/gitnamespaces.txt\nnew file mode 100644\nindex 0000000..c11dd56\n--- /dev/null\n+++ b/Documentation/gitnamespaces.txt\n@@ -0,0 +1,71 @@\n+gitnamespaces(7)\n+================\n+\n+NAME\n+----\n+gitnamespaces - Git namespaces\n+\n+DESCRIPTION\n+-----------\n+\n+Git supports dividing the refs of a single repository into multiple\n+namespaces, each of which has its own branches, tags, and HEAD.  Git can\n+expose each namespace as an independent repository to pull from and push\n+to, while sharing the object store, and exposing all the refs to\n+operations such as linkgit:git-gc[1].\n+\n+Storing multiple repositories as namespaces of a single repository\n+avoids storing duplicate copies of the same objects, such as when\n+storing multiple branches of the same source.  The alternates mechanism\n+provides similar support for avoiding duplicates, but alternates do not\n+prevent duplication between new objects added to the repositories\n+without ongoing maintenance, while namespaces do.\n+\n+To specify a namespace, set the `GIT_NAMESPACE` environment variable to\n+the namespace.  For each ref namespace, git stores the corresponding\n+refs in a directory under `refs/namespaces/`.  For example,\n+`GIT_NAMESPACE=foo` will store refs under `refs/namespaces/foo/`.  You\n+can also specify namespaces via the `--namespace` option to\n+linkgit:git[1].\n+\n+Note that namespaces which include a `/` will expand to a hierarchy of\n+namespaces; for example, `GIT_NAMESPACE=foo/bar` will store refs under\n+`refs/namespaces/foo/refs/namespaces/bar/`.  This makes `GIT_NAMESPACE`\n+behave hierarchically, and avoids ambiguity with namespaces such as\n+`foo/refs/heads`.\n+\n+linkgit:git-upload-pack[1] and linkgit:git-receive-pack[1] rewrite the\n+names of refs as specified by `GIT_NAMESPACE`.  git-upload-pack and\n+git-receive-pack will ignore all references outside the specified\n+namespace.\n+\n+The smart HTTP server, linkgit:git-http-backend[1], will pass\n+GIT_NAMESPACE through to the backend programs; see\n+linkgit:git-http-backend[1] for sample configuration to expose\n+repository namespaces as repositories.\n+\n+For a simple local test, you can use linkgit:git-remote-ext[1]:\n+\n+----------\n+git clone ext::'git --namespace=foo %s /tmp/prefixed.git'\n+----------\n+\n+SECURITY\n+--------\n+\n+Anyone with access to any namespace within a repository can potentially\n+access objects from any other namespace stored in the same repository.\n+You can't directly say \"give me object ABCD\" if you don't have a ref to\n+it, but you can do some other sneaky things like:\n+\n+. Claiming to push ABCD, at which point the server will optimize out the\n+  need for you to actually send it. Now you have a ref to ABCD and can\n+  fetch it (claiming not to have it, of course).\n+\n+. Requesting other refs, claiming that you have ABCD, at which point the\n+  server may generate deltas against ABCD.\n+\n+None of this causes a problem if you only host public repositories, or\n+if everyone who may read one namespace may also read everything in every\n+other namespace (for instance, if everyone in an organization has read\n+permission to every repository).\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex b10a1ec..ec1c986 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1469,7 +1469,7 @@ _git_help ()\n \t__gitcomp \"$__git_all_commands $(__git_aliases)\n \t\tattributes cli core-tutorial cvs-migration\n \t\tdiffcore gitk glossary hooks ignore modules\n-\t\trepository-layout tutorial tutorial-2\n+\t\tnamespaces repository-layout tutorial tutorial-2\n \t\tworkflows\n \t\t\"\n }\n-- \n1.7.5.3\n"},{"id":"169222","messageId":"7vk4d4c6ns.fsf@alter.siamese.dyndns.org","threadId":"27515","inReplyTo":"1306887870-3875-1-git-send-email-jamey@minilop.net","subject":"Re: [PATCHv4 1/4] Refactor for_each_ref variants to use for_each_ref_in and avoid magic numbers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-02T20:36:23Z","receivedAt":"2011-06-02T20:36:23Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jamey Sharp <jamey@minilop.net> writes:\n\n> Furthermore, for_each_ref and for_each_ref_submodule passed \"refs/\" but\n> a length of 0, which caused do_for_each_ref to ignore the \"refs/\".\n\nI had to read, stop, think for two days, until finally get to the point\nthat I _think_ I understand what you wanted to say.\n\nAs we use the same \"trim\" (meant to say \"strip this many bytes from the\nbeginning of the full refname when calling the callback\") to reject refs\noutside the area we are interested in with the strncmp() at the beginning\nof do_one_ref(), if do_for_each_ref() that is called by for_each_ref() fed\nsomething outside \"refs/\" hierarchy to the function, the garbage ref that\nis not a ref (as it is outside \"refs/\") will _not_ get filtered, which I\nthink is what you are trying to say by 'ignore the \"refs/\"'.\n\nWhich is technically a bug (we should be rejecting anything outside\n\"refs/\", even when trim is set to 0) that dates as far back as e1e22e3\n(Start handling references internally as a sorted in-memory list,\n2006-09-11), but it didn't matter an iota because everything we read from\neither loose or packed refs have \"refs/\" prefix.\n\nAm I following your train of thought correctly so far?\n\n> diff --git a/refs.c b/refs.c\n> index e3c0511..60cebe6 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -584,7 +584,7 @@ int read_ref(const char *ref, unsigned char *sha1)\n>  static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n>  \t\t      int flags, void *cb_data, struct ref_list *entry)\n>  {\n> -\tif (strncmp(base, entry->name, trim))\n> +\tif (prefixcmp(entry->name, base))\n>  \t\treturn 0;\n>  \n>  \tif (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {\n> ...\n>  int for_each_ref(each_ref_fn fn, void *cb_data)\n>  {\n> -\treturn do_for_each_ref(NULL, \"refs/\", fn, 0, 0, cb_data);\n> +\treturn for_each_ref_in(\"\", fn, cb_data);\n>  }\n\nBut then this looks like a bad way to fix that issue.  It will be a\nnon-issue as long as do-for-each-ref will never give anything outside\n\"refs/\", but once that happens (say, a contaminated .git/packed-refs\nfile), this will show whatever that is outside \"refs/\", i.e. the issue the\nproposed commit log message claims to address, which is \"... which caused\ndo_for_each_ref to ignore\", is not fixed here at all.\n\nShouldn't you be passing prefix and trim the same way as we have always\ndone, but just fixing the strncmp() at the beginning of do_one_ref()?\n"},{"id":"169223","messageId":"20110602205747.GA2022@leaf","threadId":"27515","inReplyTo":"7vk4d4c6ns.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4 1/4] Refactor for_each_ref variants to use for_each_ref_in and avoid magic numbers","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2011-06-02T20:57:47Z","receivedAt":"2011-06-02T20:57:47Z","isPatch":false,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Thu, Jun 02, 2011 at 01:36:23PM -0700, Junio C Hamano wrote:\n> Jamey Sharp <jamey@minilop.net> writes:\n> > Furthermore, for_each_ref and for_each_ref_submodule passed \"refs/\" but\n> > a length of 0, which caused do_for_each_ref to ignore the \"refs/\".\n> \n> I had to read, stop, think for two days, until finally get to the point\n> that I _think_ I understand what you wanted to say.\n> \n> As we use the same \"trim\" (meant to say \"strip this many bytes from the\n> beginning of the full refname when calling the callback\") to reject refs\n> outside the area we are interested in with the strncmp() at the beginning\n> of do_one_ref(), if do_for_each_ref() that is called by for_each_ref() fed\n> something outside \"refs/\" hierarchy to the function, the garbage ref that\n> is not a ref (as it is outside \"refs/\") will _not_ get filtered, which I\n> think is what you are trying to say by 'ignore the \"refs/\"'.\n> \n> Which is technically a bug (we should be rejecting anything outside\n> \"refs/\", even when trim is set to 0) that dates as far back as e1e22e3\n> (Start handling references internally as a sorted in-memory list,\n> 2006-09-11), but it didn't matter an iota because everything we read from\n> either loose or packed refs have \"refs/\" prefix.\n> \n> Am I following your train of thought correctly so far?\n\nYes.  The calls that currently pass base=\"refs/\" and trim=0 do not\nfilter the refs to those starting with \"refs/\" because they strncmp with\n0 bytes.  We very intentionally ensured that this refactoring commit\nmade no semantic change to the current behavior.  As you point out,\neverything produced from loose or packed refs will always start with\n\"refs/\" anyway.\n\n> > diff --git a/refs.c b/refs.c\n> > index e3c0511..60cebe6 100644\n> > --- a/refs.c\n> > +++ b/refs.c\n> > @@ -584,7 +584,7 @@ int read_ref(const char *ref, unsigned char *sha1)\n> >  static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n> >  \t\t      int flags, void *cb_data, struct ref_list *entry)\n> >  {\n> > -\tif (strncmp(base, entry->name, trim))\n> > +\tif (prefixcmp(entry->name, base))\n> >  \t\treturn 0;\n> >  \n> >  \tif (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {\n> > ...\n> >  int for_each_ref(each_ref_fn fn, void *cb_data)\n> >  {\n> > -\treturn do_for_each_ref(NULL, \"refs/\", fn, 0, 0, cb_data);\n> > +\treturn for_each_ref_in(\"\", fn, cb_data);\n> >  }\n> \n> But then this looks like a bad way to fix that issue.  It will be a\n> non-issue as long as do-for-each-ref will never give anything outside\n> \"refs/\", but once that happens (say, a contaminated .git/packed-refs\n> file), this will show whatever that is outside \"refs/\", i.e. the issue the\n> proposed commit log message claims to address, which is \"... which caused\n> do_for_each_ref to ignore\", is not fixed here at all.\n\nWe didn't intend the commit message to suggest that we changed that\nbehavior; we intended that commit message to document why the commit\n*didn't* change the behavior despite changing \"refs/\" to \"\".\n\n> Shouldn't you be passing prefix and trim the same way as we have always\n> done, but just fixing the strncmp() at the beginning of do_one_ref()?\n\nI still think prefixcmp makes the most sense; if you pass a given base,\nyou expect do_for_each_ref to use that entire base as the prefix.  If\nyou want for_each_ref to start filtering out anything that doesn't start\nwith \"refs/\", then it could continue passing \"refs/\" and 0 rather than\ncalling for_each_ref_in.  It doesn't matter for this patch series either\nway; we just didn't want this refactor to change the existing behavior.\n\n- Josh Triplett\n"},{"id":"169228","messageId":"7vfwnrdfam.fsf@alter.siamese.dyndns.org","threadId":"27515","inReplyTo":"1306887870-3875-2-git-send-email-jamey@minilop.net","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-02T22:44:33Z","receivedAt":"2011-06-02T22:44:33Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jamey Sharp <jamey@minilop.net> writes:\n\n> Note that namespaces which include a / will expand to a hierarchy of\n> namespaces; for example, GIT_NAMESPACE=foo/bar will store refs under\n> refs/namespaces/foo/refs/namespaces/bar/.  This makes GIT_NAMESPACE\n> behave hierarchically, and avoids ambiguity with namespaces such as\n> foo/refs/heads.\n\nSorry, but I fail to see what problem you are trying to solve here.  I am\nnot suggesting that it would be better to do things in a way different\nfrom what your patch does, but what problem will you have if you stored\nthe branch head for baz in refs/namespaces/foo/bar/refs/heads/baz given\nthe namespace foo/bar, and how does it solve that problem to store it\ninstead at refs/namespaces/foo/refs/namespaces/bar/refs/heads/baz?\n\n\n> +int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n\nJust a naming and interface preference, but I would have called this\nfor-each-ref-in-namespace, perhaps giving the namespace as a parameter.\n"},{"id":"169231","messageId":"7v8vtjdebw.fsf@alter.siamese.dyndns.org","threadId":"27515","inReplyTo":"1306887870-3875-3-git-send-email-jamey@minilop.net","subject":"Re: [PATCHv4 3/4] Support ref namespaces for remote repositories via upload-pack and receive-pack","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-02T23:05:23Z","receivedAt":"2011-06-02T23:05:23Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jamey Sharp <jamey@minilop.net> writes:\n\n> diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\n> index e1a687a..9bb268a 100644\n> --- a/builtin/receive-pack.c\n> +++ b/builtin/receive-pack.c\n> @@ -109,6 +109,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n>  \n>  static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n>  {\n> +\tpath = path ? strip_namespace(path) : \"capabilities^{}\";\n>  \tif (sent_capabilities)\n>  \t\tpacket_write(1, \"%s %s\\n\", sha1_to_hex(sha1), path);\n>  \telse\n\nThis feels really ugly.\n\nLogically the stripping of \"path\" should happen before the caller calls\nthis function, as the purpose of this function is \"given a token and\nobject name, produce one line of 'I have this at here' protocol message,\nwhich is defined to have the capability list tucked after the first of\nsuch messages in an exchange\". It now is \"the token has to be a path in a\nnamespace; the only exception is when the token is NULL, in which case we\nalways send 'capabilities^{}'\".\n\nIt also is a very selfish solution for an immediate issue(*) that does not\ngive much considertation for people who may want to add new things in the\nfuture, as the _only_ possible special case is to send in NULL.\n\nThe immediate issue you wanted to solve, I think, is that it is not\nconvenient to strip in the caller as this is a callback. Still, I think it\nshould be easy to do something like...\n\n\tstatic int show_ref_message(const char *path,\n        \t\t\t\t const unsigned char *sha1)\n\t{\n\t\t... original show_ref() implementation comes here ...\n\t}\n\n        static int show_ref_cb(const char *path,\n\t\t\t        const unsigned char *sha1,\n                                int flag, void *cb_data)\n\t{\n\t\treturn show_ref_message(strip_namespace(path), sha1);\n        }\n        \nand give the latter as the callback to for_each_ref_in_namespace().\n\nAnd the call to run \"capabilities^{}\" when there is no ref can call\nshow_ref_message() directly.\n"},{"id":"169232","messageId":"7v4o47dd8j.fsf@alter.siamese.dyndns.org","threadId":"27515","inReplyTo":"20110602205747.GA2022@leaf","subject":"Re: [PATCHv4 1/4] Refactor for_each_ref variants to use for_each_ref_in and avoid magic numbers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-02T23:29:00Z","receivedAt":"2011-06-02T23:29:00Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Triplett <josh@joshtriplett.org> writes:\n\n>> Shouldn't you be passing prefix and trim the same way as we have always\n>> done, but just fixing the strncmp() at the beginning of do_one_ref()?\n>\n> I still think prefixcmp makes the most sense; if you pass a given base,\n\nUsing prefixcmp() instead of strncmp() there is what I meant by \"fixing\nthe strncmp() at the beginning of do_one_ref()\", so we are in agreement on\nthat point. What I found questionable was the removal of the trim\nvalue. IOW, I would have expected the patch to be something like:\n\n\tif (prefixcmp(base, entry->name))\n        \treturn 0; /* outside of our area -- ignore */\n\t... some other logic ...\n        /* feed the callback, stripping the prefix */\n        return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);\n"},{"id":"169233","messageId":"20110602233619.GA1931@leaf","threadId":"27515","inReplyTo":"7vfwnrdfam.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2011-06-02T23:36:19Z","receivedAt":"2011-06-02T23:36:19Z","isPatch":false,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Thu, Jun 02, 2011 at 03:44:33PM -0700, Junio C Hamano wrote:\n> Jamey Sharp <jamey@minilop.net> writes:\n> \n> > Note that namespaces which include a / will expand to a hierarchy of\n> > namespaces; for example, GIT_NAMESPACE=foo/bar will store refs under\n> > refs/namespaces/foo/refs/namespaces/bar/.  This makes GIT_NAMESPACE\n> > behave hierarchically, and avoids ambiguity with namespaces such as\n> > foo/refs/heads.\n> \n> Sorry, but I fail to see what problem you are trying to solve here.  I am\n> not suggesting that it would be better to do things in a way different\n> from what your patch does, but what problem will you have if you stored\n> the branch head for baz in refs/namespaces/foo/bar/refs/heads/baz given\n> the namespace foo/bar, and how does it solve that problem to store it\n> instead at refs/namespaces/foo/refs/namespaces/bar/refs/heads/baz?\n\nTwo reasons.  First, if you use GIT_NAMESPACE=foo (which puts its refs\nunder refs/namespaces/foo/refs/{heads,tags}), and also used\nGIT_NAMESPACE=foo/refs/heads, that would put its refs under\nrefs/namespaces/foo/refs/heads/refs/{heads,tags}, which would make them\npotentially conflict with foo's references.  So, for instance, you could\nend up with directory/file conflicts in the refs directory.  Using\nhierarchies avoids any possible conflicts and corner cases there.\n\nSecond, by making the namespaces hierarchical, we provide a kind of\ncomposability, similar to that suggested by the analogy to chroots.\nWith the way we've constructed them, cloning a repo with\nGIT_NAMESPACE=foo/bar has the same effect as cloning a repo with\nGIT_NAMESPACE=foo and cloning from that repo with GIT_NAMESPACE=bar.\n\n> > +int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n> \n> Just a naming and interface preference, but I would have called this\n> for-each-ref-in-namespace, perhaps giving the namespace as a parameter.\n\nfor_each_ref_in and other variants already exist for that purpose;\nfor_each_namespaced_ref exists to automatically uses GIT_NAMESPACE.\nHappy to rename it if you have another preference, but I don't think it\nmakes sense to support passing in arbitrary namespaces when the callers\nonly use it to access the currently requested namespace.  If some\nsituation arises in later code that needs to handle arbitrary\nnamespaces, it seems easy enough to provide a more generalized function\nat that point, but doing so now would just make the existing callers\nmore complex by forcing them to do the call to get_git_namespace()\nrather than allowing for_each_namespaced_ref to do it.\n\nAs far as naming, though, we have no preference whatsoever about the\ncolor of the bikeshed. :)\n\n- Josh Triplett\n"},{"id":"169235","messageId":"20110603000612.GB30975@cloud","threadId":"27515","inReplyTo":"7v8vtjdebw.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4 3/4] Support ref namespaces for remote repositories via upload-pack and receive-pack","fromName":"","fromEmail":"josh@joshtriplett.org","sentAt":"2011-06-03T00:06:12Z","receivedAt":"2011-06-03T00:06:12Z","isPatch":false,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Thu, Jun 02, 2011 at 04:05:23PM -0700, Junio C Hamano wrote:\n> Jamey Sharp <jamey@minilop.net> writes:\n> \n> > diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\n> > index e1a687a..9bb268a 100644\n> > --- a/builtin/receive-pack.c\n> > +++ b/builtin/receive-pack.c\n> > @@ -109,6 +109,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n> >  \n> >  static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n> >  {\n> > +\tpath = path ? strip_namespace(path) : \"capabilities^{}\";\n> >  \tif (sent_capabilities)\n> >  \t\tpacket_write(1, \"%s %s\\n\", sha1_to_hex(sha1), path);\n> >  \telse\n> \n> This feels really ugly.\n> \n> Logically the stripping of \"path\" should happen before the caller calls\n> this function, as the purpose of this function is \"given a token and\n> object name, produce one line of 'I have this at here' protocol message,\n> which is defined to have the capability list tucked after the first of\n> such messages in an exchange\". It now is \"the token has to be a path in a\n> namespace; the only exception is when the token is NULL, in which case we\n> always send 'capabilities^{}'\".\n> \n> It also is a very selfish solution for an immediate issue(*) that does not\n> give much considertation for people who may want to add new things in the\n> future, as the _only_ possible special case is to send in NULL.\n> \n> The immediate issue you wanted to solve, I think, is that it is not\n> convenient to strip in the caller as this is a callback. Still, I think it\n> should be easy to do something like...\n> \n> \tstatic int show_ref_message(const char *path,\n>         \t\t\t\t const unsigned char *sha1)\n> \t{\n> \t\t... original show_ref() implementation comes here ...\n> \t}\n> \n>         static int show_ref_cb(const char *path,\n> \t\t\t        const unsigned char *sha1,\n>                                 int flag, void *cb_data)\n> \t{\n> \t\treturn show_ref_message(strip_namespace(path), sha1);\n>         }\n>         \n> and give the latter as the callback to for_each_ref_in_namespace().\n> \n> And the call to run \"capabilities^{}\" when there is no ref can call\n> show_ref_message() directly.\n\nFair enough.  We'd thought of NULL as a fairly logical representation\nfor a null ref sent as a dummy ref just to send capabilities, but we can\neasily rework the functions so that show_ref has the semantic you\nsuggest and expects an un-namespaced ref, since show_ref doesn't need\nthe original namespaced ref.  We'll do this in the next version of the\npatch series.\n\n- Josh Triplett\n"},{"id":"169237","messageId":"7vvcwnbpat.fsf@alter.siamese.dyndns.org","threadId":"27515","inReplyTo":"20110602233619.GA1931@leaf","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-03T02:51:22Z","receivedAt":"2011-06-03T02:51:22Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Triplett <josh@joshtriplett.org> writes:\n\n> ... I don't think it\n> makes sense to support passing in arbitrary namespaces when the callers\n> only use it to access the currently requested namespace.  If some\n> situation arises in later code that needs to handle arbitrary\n> namespaces, it seems easy enough to provide a more generalized function\n> at that point, but doing so now would just make the existing callers\n> more complex by forcing them to do the call to get_git_namespace()\n> rather than allowing for_each_namespaced_ref to do it.\n\nIf you do not pass the namespace around from day one, wouldn't it make it\nmore cumbersome to later extend the API so that you can have more than one\nnamespace active at the same time? For example, even with today's code,\nwhen responding to a push, the receiving repository issues a ls-remote\nrequest to its alternate repository to learn the tips of its refs, and at\nthat point, the side of you who is responding to a push is using the\nnamespace from the push client, while you acting as a fetch/ls-remote\nclient would be in a different namespace. The different namespace happens\nto be \"no funny namespace business\" plain vanilla one, but I think you get\nthe point. I do not mind seeing the very top-level caller of ref iterator\ncalling get-namespace, but I would find it a bad taste if a function very\ndeep in a callchain has to call get-namespace (meaning, you can only have\none namespace active at a time) only because the caller does not pass it\nin.\n\nBut perhaps I am looking too far into the future and worried too much.\n"},{"id":"169246","messageId":"m3wrh3wc0x.fsf@localhost.localdomain","threadId":"27515","inReplyTo":"1306887870-3875-1-git-send-email-jamey@minilop.net","subject":"Re: [PATCHv4 1/4] Refactor for_each_ref variants to use for_each_ref_in and avoid magic numbers","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-06-03T08:33:21Z","receivedAt":"2011-06-03T08:33:21Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"This patch series would surely benefit from a cover letter...\n\nJamey Sharp <jamey@minilop.net> writes:\n\n> From: Josh Triplett <josh@joshtriplett.org>\n> \n> Several variants of the for_each_ref functions call do_for_each_ref with\n> both a fixed string prefix and the hardcoded length of that prefix.\n> Furthermore, for_each_ref and for_each_ref_submodule passed \"refs/\" but\n> a length of 0, which caused do_for_each_ref to ignore the \"refs/\".\n[...]\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"169247","messageId":"m3sjrrwbyp.fsf@localhost.localdomain","threadId":"27515","inReplyTo":"1306887870-3875-2-git-send-email-jamey@minilop.net","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-06-03T08:35:34Z","receivedAt":"2011-06-03T08:35:34Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Jamey Sharp <jamey@minilop.net> writes:\n\n> From: Josh Triplett <josh@joshtriplett.org>\n> \n> Add support for dividing the refs of a single repository into multiple\n> namespaces, each of which can have its own branches, tags, and HEAD.\n> Git can expose each namespace as an independent repository to pull from\n> and push to, while sharing the object store, and exposing all the refs\n> to operations such as git-gc.\n[...]\n\nBy the way, after this feature is merged in, would you like for gitweb\nto understand ref namespaces and offer browsing of separate namespaces\n(sub-repositories)?\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"169261","messageId":"7vy61ian8n.fsf@alter.siamese.dyndns.org","threadId":"27515","inReplyTo":"20110603000612.GB30975@cloud","subject":"Re: [PATCHv4 3/4] Support ref namespaces for remote repositories via upload-pack and receive-pack","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-03T16:33:28Z","receivedAt":"2011-06-03T16:33:28Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"josh@joshtriplett.org writes:\n\n> Fair enough.  We'd thought of NULL as a fairly logical representation\n> for a null ref sent as a dummy ref just to send capabilities,...\n\nI am not objecting to that part. NULL may be a logical thing to throw at\nwhen the interface is to take only strings that begin with refs/... and\nyou have to occasionally send something different.\n\nBut that is a line of thought that is only valid while there is only one\n\"something different\" (i.e. \"capabilities^{}\") and the current code\nhappens to have only one such different thing to send, but the approach\ncloses the door to allow us sending things other than that single\nexception in the future without redoing your patch. That was what I found\nobjectionable.\n"},{"id":"169262","messageId":"20110603172612.GC3839@leaf","threadId":"27515","inReplyTo":"7vvcwnbpat.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2011-06-03T17:26:12Z","receivedAt":"2011-06-03T17:26:12Z","isPatch":false,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Thu, Jun 02, 2011 at 07:51:22PM -0700, Junio C Hamano wrote:\n> Josh Triplett <josh@joshtriplett.org> writes:\n> \n> > ... I don't think it\n> > makes sense to support passing in arbitrary namespaces when the callers\n> > only use it to access the currently requested namespace.  If some\n> > situation arises in later code that needs to handle arbitrary\n> > namespaces, it seems easy enough to provide a more generalized function\n> > at that point, but doing so now would just make the existing callers\n> > more complex by forcing them to do the call to get_git_namespace()\n> > rather than allowing for_each_namespaced_ref to do it.\n> \n> If you do not pass the namespace around from day one, wouldn't it make it\n> more cumbersome to later extend the API so that you can have more than one\n> namespace active at the same time? For example, even with today's code,\n> when responding to a push, the receiving repository issues a ls-remote\n> request to its alternate repository to learn the tips of its refs, and at\n> that point, the side of you who is responding to a push is using the\n> namespace from the push client, while you acting as a fetch/ls-remote\n> client would be in a different namespace. The different namespace happens\n> to be \"no funny namespace business\" plain vanilla one, but I think you get\n> the point. I do not mind seeing the very top-level caller of ref iterator\n> calling get-namespace, but I would find it a bad taste if a function very\n> deep in a callchain has to call get-namespace (meaning, you can only have\n> one namespace active at a time) only because the caller does not pass it\n> in.\n> \n> But perhaps I am looking too far into the future and worried too much.\n\nI do understand your concern, and in the future some tools might need to\nsupport multiple namespaces, but for now all the users only need to use\nGIT_NAMESPACE, and I'd prefer to tailor the API for the convenience of\nthe callers that exist now on the assumption that the API seems trivial\nto change later if it turns out we need it.  I don't think it makes\nsense to try to design that future API without specific callers in mind.\n\n- Josh Triplett\n"},{"id":"169268","messageId":"20110603210156.GA1806@leaf","threadId":"27515","inReplyTo":"m3sjrrwbyp.fsf@localhost.localdomain","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2011-06-03T21:01:56Z","receivedAt":"2011-06-03T21:01:56Z","isPatch":false,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Fri, Jun 03, 2011 at 01:35:34AM -0700, Jakub Narebski wrote:\n> Jamey Sharp <jamey@minilop.net> writes:\n> \n> > From: Josh Triplett <josh@joshtriplett.org>\n> > \n> > Add support for dividing the refs of a single repository into multiple\n> > namespaces, each of which can have its own branches, tags, and HEAD.\n> > Git can expose each namespace as an independent repository to pull from\n> > and push to, while sharing the object store, and exposing all the refs\n> > to operations such as git-gc.\n> [...]\n> \n> By the way, after this feature is merged in, would you like for gitweb\n> to understand ref namespaces and offer browsing of separate namespaces\n> (sub-repositories)?\n\nYes, definitely!  We'd love to see gitweb handling namespaces\ntransparently, and presenting them identically to top-level\nrepositories.  (Also, gitweb would need to present the appropriate\nrepository URIs for cloning.)\n\nThanks,\nJosh Triplett and Jamey Sharp\n"},{"id":"169585","messageId":"201106081141.26994.jnareb@gmail.com","threadId":"27515","inReplyTo":"20110603210156.GA1806@leaf","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-06-08T09:41:25Z","receivedAt":"2011-06-08T09:41:25Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Fri, 3 June 2011, Josh Triplett wrote:\n> On Fri, Jun 03, 2011 at 01:35:34AM -0700, Jakub Narebski wrote:\n> > Jamey Sharp <jamey@minilop.net> writes:\n> > \n> > > From: Josh Triplett <josh@joshtriplett.org>\n> > > \n> > > Add support for dividing the refs of a single repository into multiple\n> > > namespaces, each of which can have its own branches, tags, and HEAD.\n> > > Git can expose each namespace as an independent repository to pull from\n> > > and push to, while sharing the object store, and exposing all the refs\n> > > to operations such as git-gc.\n> > [...]\n> > \n> > By the way, after this feature is merged in, would you like for gitweb\n> > to understand ref namespaces and offer browsing of separate namespaces\n> > (sub-repositories)?\n> \n> Yes, definitely!  We'd love to see gitweb handling namespaces\n> transparently, and presenting them identically to top-level\n> repositories.  (Also, gitweb would need to present the appropriate\n> repository URIs for cloning.)\n\nNote that for gitweb support we would need some easy way to list all\nnamespaces that given repository provides.  But that is for the future.\n\nBTW. what do you think about treating repository with own refs and with\nnamespaces similarly to how \"forks\" are handled in gitweb now?\n-- \nJakub Narebski\nPoland\n"},{"id":"169725","messageId":"20110609033817.GB6167@leaf","threadId":"27515","inReplyTo":"201106081141.26994.jnareb@gmail.com","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2011-06-09T03:38:17Z","receivedAt":"2011-06-09T03:38:17Z","isPatch":false,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Wed, Jun 08, 2011 at 11:41:25AM +0200, Jakub Narebski wrote:\n> On Fri, 3 June 2011, Josh Triplett wrote:\n> > On Fri, Jun 03, 2011 at 01:35:34AM -0700, Jakub Narebski wrote:\n> > > Jamey Sharp <jamey@minilop.net> writes:\n> > > \n> > > > From: Josh Triplett <josh@joshtriplett.org>\n> > > > \n> > > > Add support for dividing the refs of a single repository into multiple\n> > > > namespaces, each of which can have its own branches, tags, and HEAD.\n> > > > Git can expose each namespace as an independent repository to pull from\n> > > > and push to, while sharing the object store, and exposing all the refs\n> > > > to operations such as git-gc.\n> > > [...]\n> > > \n> > > By the way, after this feature is merged in, would you like for gitweb\n> > > to understand ref namespaces and offer browsing of separate namespaces\n> > > (sub-repositories)?\n> > \n> > Yes, definitely!  We'd love to see gitweb handling namespaces\n> > transparently, and presenting them identically to top-level\n> > repositories.  (Also, gitweb would need to present the appropriate\n> > repository URIs for cloning.)\n> \n> Note that for gitweb support we would need some easy way to list all\n> namespaces that given repository provides.  But that is for the future.\n> \n> BTW. what do you think about treating repository with own refs and with\n> namespaces similarly to how \"forks\" are handled in gitweb now?\n\nDifferent namespaces won't necessarily represent forks of the same\nrepository; they may represent entirely separate repositories, with the\nuse of namespaces merely a server-side implementation detail to save\nspace.  I'd suggest presenting them as separate repositories.\n\n- Josh Triplett\n"},{"id":"169747","messageId":"201106091109.01168.jnareb@gmail.com","threadId":"27515","inReplyTo":"20110609033817.GB6167@leaf","subject":"Re: [PATCHv4 2/4] Add infrastructure for ref namespaces","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-06-09T09:09:00Z","receivedAt":"2011-06-09T09:09:00Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Thu, 9 Jun 2011, Josh Triplett wrote:\n> On Wed, Jun 08, 2011 at 11:41:25AM +0200, Jakub Narebski wrote:\n\n> > BTW. what do you think about treating repository with own refs and with\n                                                     ^^^^^^^^^^^^^\n> > namespaces similarly to how \"forks\" are handled in gitweb now?\n> \n> Different namespaces won't necessarily represent forks of the same\n> repository; they may represent entirely separate repositories, with the\n> use of namespaces merely a server-side implementation detail to save\n> space.  I'd suggest presenting them as separate repositories.\n\nWhat I wanted to say that if repository with namespaces has its own\nnot namespaced refs, then (sub)namespaces are probably forks, and\n\"forks\"-like behavior would be a good idea.\n\nIf namespaced repository serves only to save space, then it wouldn't\nhave its own refs, only namespaced ones.  Then we would list them\nas separate repositories.\n\n-- \nJakub Narebski\nPoland\n"}]}