{"thread":{"id":"45126","subject":"[PATCH/RFC 00/11] Remove submodule from files-backend.c","startedAt":"2017-02-13T15:20:30Z","lastAt":"2017-04-14T13:03:03Z","messageCount":250,"participants":["Nguyễn Thái Ngọc Duy","Ramsay Jones","Stefan Beller","Duy Nguyen","Junio C Hamano","Michael Haggerty","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"311396","messageId":"20170213152011.12050-1-pclouds@gmail.com","threadId":"45126","inReplyTo":null,"subject":"[PATCH/RFC 00/11] Remove submodule from files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:00Z","receivedAt":"2017-02-13T15:20:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is on top of mh/submodule-hash, it:\n\n - centralizes path manipulation around submodule, $GIT_DIR... in\n   files-backend.c to a new function files_path() with the intention\n   to make it easier to change later. files_path() is destined to\n   become\n\n      strbuf_addbuf(&sb, refs->gitdir);\n      strbuf_complete(&sb, '/');\n      strbuf_vaddf(&sb, fmt, vap);\n\n   but that can only be achieved once we get compound ref store.\n\n - removes git_path() and submodule_git_path() from files-backend.c. No\n   more magic path translation as far as ref backend is concerned.\n\n - moves submodule path resolution code outside the backend.\n   files-backend is now oblivious of submodules and in theory a\n   submodule ref-store supports all operations (but I could be wrong,\n   I didn't stare hard)\n\n - exposes get_submodule_ref_store() and get_main_ref_store() as public\n   API. A new set of API around ref-store will be added. And\n   get_worktree_ref_store() of course. The *_submodule() API might be\n   removed, we'll see.\n\nThe problem with set_worktree_head_symref() (which peeks into another\ngitdir) should be solved once we have get_worktree_ref_store(). That's\nmy next step.\n\nCompound ref store will have to wait until I'm done with my\ngc-in-worktree problem as I think I can live without it for now. I\nthink after compound ref store is in place, adding lmdb backend back\nshould be much cleaner because it does not care about either\nsubmodules or worktrees.\n\nNguyễn Thái Ngọc Duy (11):\n  refs-internal.c: make files_log_ref_write() static\n  files-backend: convert git_path() to strbuf_git_path()\n  files-backend: add files_path()\n  files-backend: replace *git_path*() with files_path()\n  refs.c: share is_per_worktree_ref() to files-backend.c\n  refs-internal.h: correct is_per_worktree_ref()\n  files-backend: remove the use of git_path()\n  refs.c: factor submodule code out of get_ref_store()\n  refs: move submodule code out of files-backend.c\n  files-backend: remove submodule_allowed from files_downcast()\n  refs: split and make get_*_ref_store() public API\n\n refs.c               | 125 ++++++++++++------\n refs.h               |  13 ++\n refs/files-backend.c | 350 ++++++++++++++++++++++++++++++---------------------\n refs/refs-internal.h |  28 ++---\n 4 files changed, 316 insertions(+), 200 deletions(-)\n\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311397","messageId":"20170213152011.12050-4-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 03/11] files-backend: add files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:03Z","receivedAt":"2017-02-13T15:20:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This will be the replacement for both git_path() and git_path_submodule()\nin this file. The idea is backend takes a git path and use that,\noblivious of submodule, linked worktrees and such.\n\nThis is the middle step towards that. Eventually the \"submodule\" field\nin 'struct files_ref_store' should be replace by \"gitdir\". And a\ncompound ref_store is created to combine two files backends together,\none represents the shared refs in $GIT_COMMON_DIR, one per-worktree. At\nthat point, files_path() becomes a wrapper of strbuf_vaddf().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 6582c9b2d..39217a2ca 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -169,6 +169,9 @@ static int files_log_ref_write(const char *refname, const unsigned char *old_sha\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n+void files_path(struct files_ref_store *refs, struct strbuf *sb,\n+\t\tconst char *fmt, ...) __attribute__((format (printf, 3, 4)));\n+\n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\n \tstruct ref_dir *dir;\n@@ -930,6 +933,23 @@ struct files_ref_store {\n /* Lock used for the main packed-refs file: */\n static struct lock_file packlock;\n \n+void files_path(struct files_ref_store *refs, struct strbuf *sb,\n+\t\tconst char *fmt, ...)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tva_list vap;\n+\n+\tva_start(vap, fmt);\n+\tstrbuf_vaddf(&tmp, fmt, vap);\n+\tva_end(vap);\n+\tif (refs->submodule)\n+\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n+\t\t\t\t\t  \"%s\", tmp.buf);\n+\telse\n+\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\tstrbuf_release(&tmp);\n+}\n+\n /*\n  * Increment the reference count of *packed_refs.\n  */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311398","messageId":"20170213152011.12050-3-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 02/11] files-backend: convert git_path() to strbuf_git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:02Z","receivedAt":"2017-02-13T15:20:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git_path() and friends are going to be killed in files-backend.c in near\nfuture. And because there's a risk with overwriting buffer in\ngit_path(), let's convert them all to strbuf_git_path(). We'll have\neasier time killing/converting strbuf_git_path() then because we won't\nhave to worry about memory management again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 114 ++++++++++++++++++++++++++++++++++++++++-----------\n 1 file changed, 90 insertions(+), 24 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 75565c3aa..6582c9b2d 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2169,6 +2169,8 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstatic int timeout_configured = 0;\n \tstatic int timeout_value = 1000;\n \tstruct packed_ref_cache *packed_ref_cache;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n \n \tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n \n@@ -2177,10 +2179,13 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tif (hold_lock_file_for_update_timeout(\n-\t\t\t    &packlock, git_path(\"packed-refs\"),\n-\t\t\t    flags, timeout_value) < 0)\n+\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n+\t\t\t\t\t\tflags, timeout_value);\n+\tstrbuf_release(&sb);\n+\tif (ret < 0)\n \t\treturn -1;\n+\n \t/*\n \t * Get the current packed-refs while holding the lock.  If the\n \t * packed-refs file has been modified since we last read it,\n@@ -2335,6 +2340,9 @@ static void try_remove_empty_parents(char *name)\n \tfor (q = p; *q; q++)\n \t\t;\n \twhile (1) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret;\n+\n \t\twhile (q > p && *q != '/')\n \t\t\tq--;\n \t\twhile (q > p && *(q-1) == '/')\n@@ -2342,7 +2350,10 @@ static void try_remove_empty_parents(char *name)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\t*q = '\\0';\n-\t\tif (rmdir(git_path(\"%s\", name)))\n+\t\tstrbuf_git_path(&sb, \"%s\", name);\n+\t\tret = rmdir(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\tif (ret)\n \t\t\tbreak;\n \t}\n }\n@@ -2431,7 +2442,11 @@ static int repack_without_refs(struct files_ref_store *refs,\n \t\treturn 0; /* no refname exists in packed refs */\n \n \tif (lock_packed_refs(refs, 0)) {\n-\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tunable_to_lock_message(sb.buf, errno, err);\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \tpacked = get_packed_refs(refs);\n@@ -2529,11 +2544,12 @@ static int rename_tmp_log(const char *newrefname)\n {\n \tint attempts_remaining = 4;\n \tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n \tint ret = -1;\n \n- retry:\n-\tstrbuf_reset(&path);\n \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+ retry:\n \tswitch (safe_create_leading_directories_const(path.buf)) {\n \tcase SCLD_OK:\n \t\tbreak; /* success */\n@@ -2546,7 +2562,7 @@ static int rename_tmp_log(const char *newrefname)\n \t\tgoto out;\n \t}\n \n-\tif (rename(git_path(TMP_RENAMED_LOG), path.buf)) {\n+\tif (rename(tmp_renamed_log.buf, path.buf)) {\n \t\tif ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {\n \t\t\t/*\n \t\t\t * rename(a, b) when b is an existing\n@@ -2574,6 +2590,7 @@ static int rename_tmp_log(const char *newrefname)\n \tret = 0;\n out:\n \tstrbuf_release(&path);\n+\tstrbuf_release(&tmp_renamed_log);\n \treturn ret;\n }\n \n@@ -2614,9 +2631,15 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n+\tstruct strbuf sb_oldref = STRBUF_INIT;\n+\tstruct strbuf sb_newref = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n+\tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tlog = !lstat(sb_oldref.buf, &loginfo);\n+\tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n \n@@ -2630,7 +2653,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n+\tif (ret)\n \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n@@ -2709,13 +2737,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n+\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n+\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n+\tstrbuf_release(&sb_newref);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n \n \treturn 1;\n }\n@@ -3111,22 +3145,32 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n+\tint ret;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \n-\treturn !lstat(git_path(\"logs/%s\", refname), &st) &&\n-\t\tS_ISREG(st.st_mode);\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \n-\treturn remove_path(git_path(\"logs/%s\", refname));\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = remove_path(sb.buf);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)\n@@ -3181,7 +3225,9 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3287,7 +3333,9 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3369,12 +3417,15 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n {\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\titer->dir_iterator = dir_iterator_begin(git_path(\"logs\"));\n+\tstrbuf_git_path(&sb, \"logs\");\n+\titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n \n@@ -3843,8 +3894,13 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\tret = TRANSACTION_GENERIC_ERROR;\n \t\tgoto cleanup;\n \t}\n-\tfor_each_string_list_item(ref_to_delete, &refs_to_delete)\n-\t\tunlink_or_warn(git_path(\"logs/%s\", ref_to_delete->string));\n+\tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tunlink_or_warn(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t}\n \tclear_loose_ref_cache(refs);\n \n cleanup:\n@@ -4098,18 +4154,28 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"init_db\");\n \n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n+\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tadjust_shared_perm(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tadjust_shared_perm(sb.buf);\n \t}\n+\tstrbuf_release(&sb);\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311399","messageId":"20170213152011.12050-2-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 01/11] refs-internal.c: make files_log_ref_write() static","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:01Z","receivedAt":"2017-02-13T15:20:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\nbut probably never used outside refs-internal.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 +++\n refs/refs-internal.h | 4 ----\n 2 files changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex cdb6b8ff5..75565c3aa 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err);\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 33adbf93b..59e65958a 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -222,10 +222,6 @@ struct ref_transaction {\n \tenum ref_transaction_state state;\n };\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err);\n-\n /*\n  * Check for entries in extras that are within the specified\n  * directory, where dirname is a reference directory name including\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311400","messageId":"20170213152011.12050-5-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 04/11] files-backend: replace *git_path*() with files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:04Z","receivedAt":"2017-02-13T15:21:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This centralizes all path rewriting of files-backend.c in one place so\nwe have easier time removing the path rewriting later. There could be\nsome hidden indirect git_path() though, I didn't audit the code to the\nbottom.\n\nSide note: set_worktree_head_symref() is a bad boy and should not be in\nfiles-backend.c (probably should not exist in the first place). But\nwe'll leave it there until we have better multi-worktree support in refs\nbefore we update it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 193 ++++++++++++++++++++++++++-------------------------\n 1 file changed, 98 insertions(+), 95 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 39217a2ca..c69e4fe84 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,12 +165,13 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n-void files_path(struct files_ref_store *refs, struct strbuf *sb,\n-\t\tconst char *fmt, ...) __attribute__((format (printf, 3, 4)));\n+static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n+\t\t       const char *fmt, ...) __attribute__((format (printf, 3, 4)));\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\n@@ -933,8 +934,8 @@ struct files_ref_store {\n /* Lock used for the main packed-refs file: */\n static struct lock_file packlock;\n \n-void files_path(struct files_ref_store *refs, struct strbuf *sb,\n-\t\tconst char *fmt, ...)\n+static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n+\t\t       const char *fmt, ...)\n {\n \tstruct strbuf tmp = STRBUF_INIT;\n \tva_list vap;\n@@ -1180,12 +1181,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n {\n \tchar *packed_refs_file;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n-\tif (refs->submodule)\n-\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n-\t\t\t\t\t\t\t \"packed-refs\");\n-\telse\n-\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n+\tpacked_refs_file = strbuf_detach(&sb, NULL);\n \n \tif (refs->packed &&\n \t    !stat_validity_check(&refs->packed->validity, packed_refs_file))\n@@ -1251,10 +1250,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tsize_t path_baselen;\n \tint err = 0;\n \n-\tif (refs->submodule)\n-\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n-\telse\n-\t\tstrbuf_git_path(&path, \"%s\", dirname);\n+\tfiles_path(refs, &path, \"%s\", dirname);\n \tpath_baselen = path.len;\n \n \tif (err) {\n@@ -1396,10 +1392,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(&sb_path, refs->submodule, \"%s\", refname);\n-\telse\n-\t\tstrbuf_git_path(&sb_path, \"%s\", refname);\n+\tfiles_path(refs, &sb_path, \"%s\", refname);\n \n \tpath = sb_path.buf;\n \n@@ -1587,7 +1580,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -2054,7 +2047,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\tlock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n@@ -2199,7 +2192,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n \tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n \t\t\t\t\t\tflags, timeout_value);\n \tstrbuf_release(&sb);\n@@ -2345,7 +2338,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)\n  * Remove empty parents, but spare refs/ and immediate subdirs.\n  * Note: munges *name.\n  */\n-static void try_remove_empty_parents(char *name)\n+static void try_remove_empty_parents(struct files_ref_store *refs, char *name)\n {\n \tchar *p, *q;\n \tint i;\n@@ -2370,7 +2363,7 @@ static void try_remove_empty_parents(char *name)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\t*q = '\\0';\n-\t\tstrbuf_git_path(&sb, \"%s\", name);\n+\t\tfiles_path(refs, &sb, \"%s\", name);\n \t\tret = rmdir(sb.buf);\n \t\tstrbuf_release(&sb);\n \t\tif (ret)\n@@ -2379,7 +2372,7 @@ static void try_remove_empty_parents(char *name)\n }\n \n /* make sure nobody touched the ref, and unlink */\n-static void prune_ref(struct ref_to_prune *r)\n+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -2399,13 +2392,13 @@ static void prune_ref(struct ref_to_prune *r)\n \t}\n \tref_transaction_free(transaction);\n \tstrbuf_release(&err);\n-\ttry_remove_empty_parents(r->name);\n+\ttry_remove_empty_parents(refs, r->name);\n }\n \n-static void prune_refs(struct ref_to_prune *r)\n+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \twhile (r) {\n-\t\tprune_ref(r);\n+\t\tprune_ref(refs, r);\n \t\tr = r->next;\n \t}\n }\n@@ -2428,7 +2421,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n \tif (commit_packed_refs(refs))\n \t\tdie_errno(\"unable to overwrite old ref-pack file\");\n \n-\tprune_refs(cbdata.ref_to_prune);\n+\tprune_refs(refs, cbdata.ref_to_prune);\n \treturn 0;\n }\n \n@@ -2464,7 +2457,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tif (lock_packed_refs(refs, 0)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tfiles_path(refs, &sb, \"packed-refs\");\n \t\tunable_to_lock_message(sb.buf, errno, err);\n \t\tstrbuf_release(&sb);\n \t\treturn -1;\n@@ -2560,15 +2553,15 @@ static int files_delete_refs(struct ref_store *ref_store,\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-static int rename_tmp_log(const char *newrefname)\n+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n {\n \tint attempts_remaining = 4;\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n \tint ret = -1;\n \n-\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &path, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n  retry:\n \tswitch (safe_create_leading_directories_const(path.buf)) {\n \tcase SCLD_OK:\n@@ -2657,7 +2650,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n@@ -2673,8 +2666,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n \tstrbuf_release(&sb_oldref);\n \tstrbuf_release(&tmp_renamed_log);\n@@ -2701,7 +2694,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tstrbuf_git_path(&path, \"%s\", newrefname);\n+\t\t\tfiles_path(refs, &path, \"%s\", newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -2715,7 +2708,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t}\n \t}\n \n-\tif (log && rename_tmp_log(newrefname))\n+\tif (log && rename_tmp_log(refs, newrefname))\n \t\tgoto rollback;\n \n \tlogmoved = log;\n@@ -2757,12 +2750,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_newref, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n@@ -2818,11 +2811,13 @@ static int commit_ref(struct ref_lock *lock)\n  * should_autocreate_reflog returns non-zero.  Otherwise, create it\n  * regardless of the ref name.  Fill in *err and return -1 on failure.\n  */\n-static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)\n+static int log_ref_setup(struct files_ref_store *refs, const char *refname,\n+\t\t\t struct strbuf *logfile, struct strbuf *err,\n+\t\t\t int force_create)\n {\n \tint logfd, oflags = O_APPEND | O_WRONLY;\n \n-\tstrbuf_git_path(logfile, \"logs/%s\", refname);\n+\tfiles_path(refs, logfile, \"logs/%s\", refname);\n \tif (force_create || should_autocreate_reflog(refname)) {\n \t\tif (safe_create_leading_directories(logfile->buf) < 0) {\n \t\t\tstrbuf_addf(err, \"unable to create directory for '%s': \"\n@@ -2865,11 +2860,10 @@ static int files_create_reflog(struct ref_store *ref_store,\n {\n \tint ret;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"create_reflog\");\n-\n-\tret = log_ref_setup(refname, &sb, err, force_create);\n+\tret = log_ref_setup(refs, refname, &sb, err, force_create);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\n@@ -2900,7 +2894,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n+static int log_ref_write_1(struct files_ref_store *refs,\n+\t\t\t   const char *refname, const unsigned char *old_sha1,\n \t\t\t   const unsigned char *new_sha1, const char *msg,\n \t\t\t   struct strbuf *logfile, int flags,\n \t\t\t   struct strbuf *err)\n@@ -2910,7 +2905,7 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n \tif (log_all_ref_updates < 0)\n \t\tlog_all_ref_updates = !is_bare_repository();\n \n-\tresult = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);\n+\tresult = log_ref_setup(refs, refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);\n \n \tif (result)\n \t\treturn result;\n@@ -2934,21 +2929,23 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-static int log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int log_ref_write(struct files_ref_store *refs,\n+\t\t\t const char *refname, const unsigned char *old_sha1,\n \t\t\t const unsigned char *new_sha1, const char *msg,\n \t\t\t int flags, struct strbuf *err)\n {\n-\treturn files_log_ref_write(refname, old_sha1, new_sha1, msg, flags,\n-\t\t\t\t   err);\n+\treturn files_log_ref_write(refs, refname, old_sha1, new_sha1,\n+\t\t\t\t   msg, flags, err);\n }\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\tconst char *refname, const unsigned char *old_sha1,\n \t\t\tconst unsigned char *new_sha1, const char *msg,\n \t\t\tint flags, struct strbuf *err)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n-\tint ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,\n-\t\t\t\t  err);\n+\tint ret = log_ref_write_1(refs, refname, old_sha1, new_sha1, msg,\n+\t\t\t\t  &sb, flags, err);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\n@@ -3005,7 +3002,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \tfiles_assert_main_repository(refs, \"commit_ref_update\");\n \n \tclear_loose_ref_cache(refs);\n-\tif (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {\n+\tif (log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n+\t\t\t  sha1, logmsg, 0, err)) {\n \t\tchar *old_msg = strbuf_detach(err, NULL);\n \t\tstrbuf_addf(err, \"cannot update the ref '%s': %s\",\n \t\t\t    lock->ref_name, old_msg);\n@@ -3036,8 +3034,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n-\t\t\tif (log_ref_write(\"HEAD\", lock->old_oid.hash, sha1,\n-\t\t\t\t\t  logmsg, 0, &log_err)) {\n+\t\t\tif (log_ref_write(refs, \"HEAD\", lock->old_oid.hash,\n+\t\t\t\t\t  sha1, logmsg, 0, &log_err)) {\n \t\t\t\terror(\"%s\", log_err.buf);\n \t\t\t\tstrbuf_release(&log_err);\n \t\t\t}\n@@ -3069,23 +3067,26 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)\n \treturn ret;\n }\n \n-static void update_symref_reflog(struct ref_lock *lock, const char *refname,\n+static void update_symref_reflog(struct files_ref_store *refs,\n+\t\t\t\t struct ref_lock *lock, const char *refname,\n \t\t\t\t const char *target, const char *logmsg)\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n \tif (logmsg && !read_ref(target, new_sha1) &&\n-\t    log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {\n+\t    log_ref_write(refs, refname, lock->old_oid.hash,\n+\t\t\t  new_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t}\n }\n \n-static int create_symref_locked(struct ref_lock *lock, const char *refname,\n+static int create_symref_locked(struct files_ref_store *refs,\n+\t\t\t\tstruct ref_lock *lock, const char *refname,\n \t\t\t\tconst char *target, const char *logmsg)\n {\n \tif (prefer_symlink_refs && !create_ref_symlink(lock, target)) {\n-\t\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\t\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \t\treturn 0;\n \t}\n \n@@ -3093,7 +3094,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,\n \t\treturn error(\"unable to fdopen %s: %s\",\n \t\t\t     lock->lk->tempfile.filename.buf, strerror(errno));\n \n-\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \n \t/* no error check; commit_ref will check ferror */\n \tfprintf(lock->lk->tempfile.fp, \"ref: %s\\n\", target);\n@@ -3122,13 +3123,19 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\treturn -1;\n \t}\n \n-\tret = create_symref_locked(lock, refname, target, logmsg);\n+\tret = create_symref_locked(refs, lock, refname, target, logmsg);\n \tunlock_ref(lock);\n \treturn ret;\n }\n \n int set_worktree_head_symref(const char *gitdir, const char *target)\n {\n+\t/*\n+\t * FIXME: this obviously will not work well for future refs\n+\t * backends. This function needs to die.\n+\t */\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3155,7 +3162,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \tlock->lk = &head_lock;\n \tlock->ref_name = xstrdup(head_rel);\n \n-\tret = create_symref_locked(lock, head_rel, target, NULL);\n+\tret = create_symref_locked(refs, lock, head_rel, target, NULL);\n \n \tunlock_ref(lock); /* will free lock */\n \tstrbuf_release(&head_path);\n@@ -3165,14 +3172,13 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3181,13 +3187,12 @@ static int files_reflog_exists(struct ref_store *ref_store,\n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = remove_path(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3237,15 +3242,14 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     each_reflog_ent_fn fn,\n \t\t\t\t\t     void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n \tint ret = 0, at_tail = 1;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3346,14 +3350,13 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     const char *refname,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3435,15 +3438,14 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n \n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n-\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\tstrbuf_git_path(&sb, \"logs\");\n+\tfiles_path(refs, &sb, \"logs\");\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n@@ -3867,8 +3869,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t\tif (update->flags & REF_NEEDS_COMMIT ||\n \t\t    update->flags & REF_LOG_ONLY) {\n-\t\t\tif (log_ref_write(lock->ref_name, lock->old_oid.hash,\n-\t\t\t\t\t  update->new_sha1,\n+\t\t\tif (log_ref_write(refs, lock->ref_name,\n+\t\t\t\t\t  lock->old_oid.hash, update->new_sha1,\n \t\t\t\t\t  update->msg, update->flags, err)) {\n \t\t\t\tchar *old_msg = strbuf_detach(err, NULL);\n \n@@ -3917,7 +3919,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tfiles_path(refs, &sb, \"logs/%s\", ref_to_delete->string);\n \t\tunlink_or_warn(sb.buf);\n \t\tstrbuf_release(&sb);\n \t}\n@@ -4079,6 +4081,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \tint status = 0;\n \tint type;\n \tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tmemset(&cb, 0, sizeof(cb));\n \tcb.flags = flags;\n@@ -4103,7 +4106,8 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tlog_file = git_pathdup(\"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n+\tlog_file = strbuf_detach(&sb, NULL);\n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n \t\t/*\n \t\t * Even though holding $GIT_DIR/logs/$reflog.lock has\n@@ -4174,25 +4178,24 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"init_db\");\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tfiles_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n-\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tfiles_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tfiles_path(refs, &sb, \"refs/heads\");\n \t\tadjust_shared_perm(sb.buf);\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tfiles_path(refs, &sb, \"refs/tags\");\n \t\tadjust_shared_perm(sb.buf);\n \t}\n \tstrbuf_release(&sb);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311401","messageId":"20170213152011.12050-7-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 06/11] refs-internal.h: correct is_per_worktree_ref()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:06Z","receivedAt":"2017-02-13T15:21:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"All refs outside refs/ directory is per-worktree, not just HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/refs-internal.h | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f4aed49f5..69d02b6ba 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -653,7 +653,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n \n static inline int is_per_worktree_ref(const char *refname)\n {\n-\treturn !strcmp(refname, \"HEAD\") ||\n+\treturn !starts_with(refname, \"refs/\") ||\n \t\tstarts_with(refname, \"refs/bisect/\");\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311402","messageId":"20170213152011.12050-8-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 07/11] files-backend: remove the use of git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:07Z","receivedAt":"2017-02-13T15:21:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\ndeciding what goes where. The end goal is to pass $GIT_DIR only. A\nrefs \"view\" of a linked worktree is a logical ref store that combines\ntwo files backends together.\n\n(*) Not entirely true since strbuf_git_path_submodule() still does path\ntranslation underneath. But that's for another patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex c69e4fe84..50eb9edb6 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -927,6 +927,9 @@ struct files_ref_store {\n \t */\n \tconst char *submodule;\n \n+\tstruct strbuf gitdir;\n+\tstruct strbuf gitcommondir;\n+\n \tstruct ref_entry *loose;\n \tstruct packed_ref_cache *packed;\n };\n@@ -939,6 +942,7 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n {\n \tstruct strbuf tmp = STRBUF_INIT;\n \tva_list vap;\n+\tconst char *ref;\n \n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n@@ -946,8 +950,12 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tif (refs->submodule)\n \t\tstrbuf_git_path_submodule(sb, refs->submodule,\n \t\t\t\t\t  \"%s\", tmp.buf);\n+\telse if (is_per_worktree_ref(tmp.buf) ||\n+\t\t (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n+\t\t  is_per_worktree_ref(ref)))\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n \telse\n-\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n \tstrbuf_release(&tmp);\n }\n \n@@ -1006,7 +1014,15 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\trefs->submodule = xstrdup_or_null(submodule);\n+\tstrbuf_init(&refs->gitdir, 0);\n+\tstrbuf_init(&refs->gitcommondir, 0);\n+\n+\tif (submodule) {\n+\t\trefs->submodule = xstrdup_or_null(submodule);\n+\t} else {\n+\t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n+\t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n+\t}\n \n \treturn ref_store;\n }\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311403","messageId":"20170213152011.12050-6-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 05/11] refs.c: share is_per_worktree_ref() to files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:05Z","receivedAt":"2017-02-13T15:21:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 6 ------\n refs/refs-internal.h | 6 ++++++\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f03dcf58b..2cacd934e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -489,12 +489,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n \treturn logs_found;\n }\n \n-static int is_per_worktree_ref(const char *refname)\n-{\n-\treturn !strcmp(refname, \"HEAD\") ||\n-\t\tstarts_with(refname, \"refs/bisect/\");\n-}\n-\n static int is_pseudoref_syntax(const char *refname)\n {\n \tconst char *c;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 59e65958a..f4aed49f5 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -651,4 +651,10 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    unsigned char *sha1, int *flags);\n \n+static inline int is_per_worktree_ref(const char *refname)\n+{\n+\treturn !strcmp(refname, \"HEAD\") ||\n+\t\tstarts_with(refname, \"refs/bisect/\");\n+}\n+\n #endif /* REFS_REFS_INTERNAL_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311404","messageId":"20170213152011.12050-10-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 09/11] refs: move submodule code out of files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:09Z","receivedAt":"2017-02-13T15:21:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend is now initialized with a $GIT_DIR. Converting a submodule\npath to where real submodule gitdir is located is done in get_ref_store().\n\nThe new code in init_submodule_ref_store() is basically a copy of\nstrbuf_git_path_submodule().\n\nThis gives a slight performance improvement for submodules since we\ndon't convert submodule path to gitdir at every backend call like\nbefore. We pay that once at ref-store creation.\n\nMore cleanup in files_downcast() follows shortly. It's separate to keep\nnoises from this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 48 +++++++++++++++++++++++++++++++++++++++++-------\n refs/files-backend.c | 25 +++++++------------------\n refs/refs-internal.h |  6 +++---\n 3 files changed, 51 insertions(+), 28 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 8ef7a52ba..9ac194945 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -9,6 +9,7 @@\n #include \"refs/refs-internal.h\"\n #include \"object.h\"\n #include \"tag.h\"\n+#include \"submodule-config.h\"\n \n /*\n  * List of all available backends\n@@ -1410,7 +1411,7 @@ static void register_ref_store(struct ref_store *refs, const char *submodule)\n  * Create, record, and return a ref_store instance for the specified\n  * submodule (or the main repository if submodule is NULL).\n  */\n-static struct ref_store *ref_store_init(const char *submodule)\n+static struct ref_store *ref_store_init(const char *submodule, const char *gitdir)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1419,7 +1420,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(submodule);\n+\trefs = be->init(gitdir);\n \tregister_ref_store(refs, submodule);\n \treturn refs;\n }\n@@ -1445,15 +1446,48 @@ static struct ref_store *lookup_ref_store(const char *submodule)\n \treturn entry ? entry->refs : NULL;\n }\n \n-static struct ref_store *init_submodule_ref_store(const char *submodule)\n+static struct ref_store *init_submodule_ref_store(const char *path)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n+\tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n+\tstruct strbuf git_submodule_dir = STRBUF_INIT;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *git_dir;\n+\tconst struct submodule *sub;\n \tstruct ref_store *refs = NULL;\n \n-\tstrbuf_addstr(&submodule_sb, submodule);\n-\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\trefs = ref_store_init(submodule);\n+\tstrbuf_addstr(&submodule_sb, path);\n+\tif (!is_nonbare_repository_dir(&submodule_sb))\n+\t\tgoto done;\n+\n+\tstrbuf_addstr(&buf, path);\n+\tstrbuf_complete(&buf, '/');\n+\tstrbuf_addstr(&buf, \".git\");\n+\n+\tgit_dir = read_gitfile(buf.buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addstr(&buf, git_dir);\n+\t}\n+\tif (!is_git_directory(buf.buf)) {\n+\t\tgitmodules_config();\n+\t\tsub = submodule_from_path(null_sha1, path);\n+\t\tif (!sub)\n+\t\t\tgoto done;\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_git_path(&buf, \"%s/%s\", \"modules\", sub->name);\n+\t}\n+\tstrbuf_addch(&buf, '/');\n+\tstrbuf_addbuf(&git_submodule_dir, &buf);\n+\n+\trefs = ref_store_init(path, git_submodule_dir.buf);\n+\n+done:\n+\tstrbuf_release(&git_submodule_dir);\n+\tstrbuf_release(&git_submodule_common_dir);\n \tstrbuf_release(&submodule_sb);\n+\tstrbuf_release(&buf);\n+\n \treturn refs;\n }\n \n@@ -1465,7 +1499,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\trefs = lookup_ref_store(NULL);\n \n \t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL);\n+\t\t\trefs = ref_store_init(NULL, NULL);\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 50eb9edb6..834bc6fdf 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -920,13 +920,6 @@ struct packed_ref_cache {\n struct files_ref_store {\n \tstruct ref_store base;\n \n-\t/*\n-\t * The name of the submodule represented by this object, or\n-\t * NULL if it represents the main repository's reference\n-\t * store:\n-\t */\n-\tconst char *submodule;\n-\n \tstruct strbuf gitdir;\n \tstruct strbuf gitcommondir;\n \n@@ -947,12 +940,9 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n \tva_end(vap);\n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n-\t\t\t\t\t  \"%s\", tmp.buf);\n-\telse if (is_per_worktree_ref(tmp.buf) ||\n-\t\t (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n-\t\t  is_per_worktree_ref(ref)))\n+\tif (is_per_worktree_ref(tmp.buf) ||\n+\t    (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n+\t     is_per_worktree_ref(ref)))\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n \telse\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n@@ -1007,7 +997,7 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *submodule)\n+static struct ref_store *files_ref_store_create(const char *gitdir)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n@@ -1017,8 +1007,9 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \tstrbuf_init(&refs->gitdir, 0);\n \tstrbuf_init(&refs->gitcommondir, 0);\n \n-\tif (submodule) {\n-\t\trefs->submodule = xstrdup_or_null(submodule);\n+\tif (gitdir) {\n+\t\tstrbuf_addstr(&refs->gitdir, gitdir);\n+\t\tget_common_dir_noenv(&refs->gitcommondir, gitdir);\n \t} else {\n \t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n \t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n@@ -1034,8 +1025,6 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\tif (refs->submodule)\n-\t\tdie(\"BUG: %s called for a submodule\", caller);\n }\n \n /*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 69d02b6ba..d7112770d 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -476,12 +476,12 @@ struct ref_store;\n /* refs backends */\n \n /*\n- * Initialize the ref_store for the specified submodule, or for the\n- * main repository if submodule == NULL. These functions should call\n+ * Initialize the ref_store for the specified gitdir, or for the\n+ * current repository if gitdir == NULL. These functions should call\n  * base_ref_store_init() to initialize the shared part of the\n  * ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *submodule);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311405","messageId":"20170213152011.12050-11-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 10/11] files-backend: remove submodule_allowed from files_downcast()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:10Z","receivedAt":"2017-02-13T15:21:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since submodule or not is irrelevant to files-backend after the last\npatch, remove the parameter from files_downcast() and kill\nfiles_assert_main_repository().\n\nPS. This implies that all ref operations are allowed for submodules. But\nwe may need to look more closely to see if that's really true...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 69 ++++++++++++++++------------------------------------\n 1 file changed, 21 insertions(+), 48 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 834bc6fdf..2fb270b6f 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1019,23 +1019,13 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n }\n \n /*\n- * Die if refs is for a submodule (i.e., not for the main repository).\n- * caller is used in any necessary error messages.\n- */\n-static void files_assert_main_repository(struct files_ref_store *refs,\n-\t\t\t\t\t const char *caller)\n-{\n-}\n-\n-/*\n  * Downcast ref_store to files_ref_store. Die if ref_store is not a\n  * files_ref_store. If submodule_allowed is not true, then also die if\n  * files_ref_store is for a submodule (i.e., not for the main\n  * repository). caller is used in any necessary error messages.\n  */\n-static struct files_ref_store *files_downcast(\n-\t\tstruct ref_store *ref_store, int submodule_allowed,\n-\t\tconst char *caller)\n+static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n+\t\t\t\t\t      const char *caller)\n {\n \tstruct files_ref_store *refs;\n \n@@ -1045,9 +1035,6 @@ static struct files_ref_store *files_downcast(\n \n \trefs = (struct files_ref_store *)ref_store;\n \n-\tif (!submodule_allowed)\n-\t\tfiles_assert_main_repository(refs, caller);\n-\n \treturn refs;\n }\n \n@@ -1383,7 +1370,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t\t\t      struct strbuf *referent, unsigned int *type)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"read_raw_ref\");\n+\t\tfiles_downcast(ref_store, \"read_raw_ref\");\n \tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n \tconst char *path;\n@@ -1576,7 +1563,6 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \tint ret = TRANSACTION_GENERIC_ERROR;\n \n \tassert(err);\n-\tfiles_assert_main_repository(refs, \"lock_raw_ref\");\n \n \t*type = 0;\n \n@@ -1800,7 +1786,7 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n static int files_peel_ref(struct ref_store *ref_store,\n \t\t\t  const char *refname, unsigned char *sha1)\n {\n-\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"peel_ref\");\n \tint flag;\n \tunsigned char base[20];\n \n@@ -1909,7 +1895,7 @@ static struct ref_iterator *files_ref_iterator_begin(\n \t\tconst char *prefix, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"ref_iterator_begin\");\n \tstruct ref_dir *loose_dir, *packed_dir;\n \tstruct ref_iterator *loose_iter, *packed_iter;\n \tstruct files_ref_iterator *iter;\n@@ -2042,7 +2028,6 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tint attempts_remaining = 3;\n \tint resolved;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_sha1_basic\");\n \tassert(err);\n \n \tlock = xcalloc(1, sizeof(struct ref_lock));\n@@ -2190,8 +2175,6 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n-\n \tif (!timeout_configured) {\n \t\tgit_config_get_int(\"core.packedrefstimeout\", &timeout_value);\n \t\ttimeout_configured = 1;\n@@ -2231,8 +2214,6 @@ static int commit_packed_refs(struct files_ref_store *refs)\n \tint save_errno = 0;\n \tFILE *out;\n \n-\tfiles_assert_main_repository(refs, \"commit_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \n@@ -2264,8 +2245,6 @@ static void rollback_packed_refs(struct files_ref_store *refs)\n \tstruct packed_ref_cache *packed_ref_cache =\n \t\tget_packed_ref_cache(refs);\n \n-\tfiles_assert_main_repository(refs, \"rollback_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \trollback_lock_file(packed_ref_cache->lock);\n@@ -2411,7 +2390,7 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"pack_refs\");\n+\t\tfiles_downcast(ref_store, \"pack_refs\");\n \tstruct pack_refs_cb_data cbdata;\n \n \tmemset(&cbdata, 0, sizeof(cbdata));\n@@ -2444,7 +2423,6 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tstruct string_list_item *refname;\n \tint ret, needs_repacking = 0, removed = 0;\n \n-\tfiles_assert_main_repository(refs, \"repack_without_refs\");\n \tassert(err);\n \n \t/* Look for a packed ref */\n@@ -2512,7 +2490,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \t\t\t     struct string_list *refnames, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_refs\");\n+\t\tfiles_downcast(ref_store, \"delete_refs\");\n \tstruct strbuf err = STRBUF_INIT;\n \tint i, result = 0;\n \n@@ -2619,7 +2597,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,\n \t\t\t\t\t  struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"verify_refname_available\");\n+\t\tfiles_downcast(ref_store, \"verify_refname_available\");\n \tstruct ref_dir *packed_refs = get_packed_refs(refs);\n \tstruct ref_dir *loose_refs = get_loose_refs(refs);\n \n@@ -2644,7 +2622,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"rename_ref\");\n+\t\tfiles_downcast(ref_store, \"rename_ref\");\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n@@ -2866,7 +2844,7 @@ static int files_create_reflog(struct ref_store *ref_store,\n \tint ret;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n+\t\tfiles_downcast(ref_store, \"create_reflog\");\n \n \tret = log_ref_setup(refs, refname, &sb, err, force_create);\n \tstrbuf_release(&sb);\n@@ -3004,8 +2982,6 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\t\t     const unsigned char *sha1, const char *logmsg,\n \t\t\t     struct strbuf *err)\n {\n-\tfiles_assert_main_repository(refs, \"commit_ref_update\");\n-\n \tclear_loose_ref_cache(refs);\n \tif (log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n \t\t\t  sha1, logmsg, 0, err)) {\n@@ -3114,7 +3090,7 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\t\t       const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_symref\");\n+\t\tfiles_downcast(ref_store, \"create_symref\");\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_lock *lock;\n \tint ret;\n@@ -3140,7 +3116,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3178,7 +3154,7 @@ static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n+\t\tfiles_downcast(ref_store, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n@@ -3193,7 +3169,7 @@ static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n+\t\tfiles_downcast(ref_store, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n@@ -3248,7 +3224,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n@@ -3356,7 +3332,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n@@ -3444,7 +3420,7 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -3656,8 +3632,6 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \tint ret;\n \tstruct ref_lock *lock;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_for_update\");\n-\n \tif ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))\n \t\tupdate->flags |= REF_DELETING;\n \n@@ -3782,7 +3756,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list refs_to_delete = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *ref_to_delete;\n@@ -3956,7 +3930,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"initial_ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"initial_ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list affected_refnames = STRING_LIST_INIT_NODUP;\n \n@@ -4078,7 +4052,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\t       void *policy_cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_expire\");\n+\t\tfiles_downcast(ref_store, \"reflog_expire\");\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n@@ -4183,8 +4157,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n-\tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"init_db\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n \t/*\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311406","messageId":"20170213152011.12050-12-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 11/11] refs: split and make get_*_ref_store() public API","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:11Z","receivedAt":"2017-02-13T15:21:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 59 ++++++++++++++++++++++++++++++----------------------\n refs.h               | 13 ++++++++++++\n refs/files-backend.c |  2 +-\n refs/refs-internal.h | 12 -----------\n 4 files changed, 48 insertions(+), 38 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 9ac194945..48350da87 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1160,7 +1160,7 @@ int head_ref(each_ref_fn fn, void *cb_data)\n static int do_for_each_ref(const char *submodule, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(submodule);\n+\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1304,7 +1304,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n /* backend functions */\n int refs_init_db(struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->init_db(refs, err);\n }\n@@ -1312,7 +1312,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_ref_store(NULL), refname,\n+\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1333,10 +1333,10 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \t\t/* We need to strip off one or more trailing slashes */\n \t\tchar *stripped = xmemdupz(submodule, len);\n \n-\t\trefs = get_ref_store(stripped);\n+\t\trefs = get_submodule_ref_store(stripped);\n \t\tfree(stripped);\n \t} else {\n-\t\trefs = get_ref_store(submodule);\n+\t\trefs = get_submodule_ref_store(submodule);\n \t}\n \n \tif (!refs)\n@@ -1491,15 +1491,24 @@ static struct ref_store *init_submodule_ref_store(const char *path)\n \treturn refs;\n }\n \n-struct ref_store *get_ref_store(const char *submodule)\n+struct ref_store *get_main_ref_store(void)\n {\n \tstruct ref_store *refs;\n \n-\tif (!submodule || !*submodule) {\n-\t\trefs = lookup_ref_store(NULL);\n+\trefs = lookup_ref_store(NULL);\n \n-\t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL, NULL);\n+\tif (!refs)\n+\t\trefs = ref_store_init(NULL, NULL);\n+\n+\treturn refs;\n+}\n+\n+struct ref_store *get_submodule_ref_store(const char *submodule)\n+{\n+\tstruct ref_store *refs;\n+\n+\tif (!submodule || !*submodule) {\n+\t\treturn get_main_ref_store();\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n@@ -1519,14 +1528,14 @@ void base_ref_store_init(struct ref_store *refs,\n /* backend functions */\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->pack_refs(refs, flags);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->peel_ref(refs, refname, sha1);\n }\n@@ -1534,7 +1543,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n \t\t\t\t       logmsg);\n@@ -1543,7 +1552,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1553,14 +1562,14 @@ int verify_refname_available(const char *refname,\n \t\t\t     const struct string_list *skip,\n \t\t\t     struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1571,7 +1580,7 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n \t\t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n@@ -1580,14 +1589,14 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_exists(refs, refname);\n }\n@@ -1595,14 +1604,14 @@ int reflog_exists(const char *refname)\n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_reflog(refs, refname);\n }\n@@ -1614,7 +1623,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n \t\t\t\t       prepare_fn, should_prune_fn,\n@@ -1624,21 +1633,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\ndiff --git a/refs.h b/refs.h\nindex 694784391..f5fba16c8 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -553,4 +553,17 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \n int ref_storage_backend_exists(const char *name);\n \n+/*\n+ * Return the ref_store instance for the specified submodule. For the\n+ * main repository, use submodule==NULL; such a call cannot fail. For\n+ * a submodule, the submodule must exist and be a nonbare repository,\n+ * otherwise return NULL. If the requested reference store has not yet\n+ * been initialized, initialize it first.\n+ *\n+ * For backwards compatibility, submodule==\"\" is treated the same as\n+ * submodule==NULL.\n+ */\n+struct ref_store *get_submodule_ref_store(const char *submodule);\n+struct ref_store *get_main_ref_store(void);\n+\n #endif /* REFS_H */\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 2fb270b6f..eab156733 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3116,7 +3116,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex d7112770d..cb6882779 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -634,18 +634,6 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-/*\n- * Return the ref_store instance for the specified submodule. For the\n- * main repository, use submodule==NULL; such a call cannot fail. For\n- * a submodule, the submodule must exist and be a nonbare repository,\n- * otherwise return NULL. If the requested reference store has not yet\n- * been initialized, initialize it first.\n- *\n- * For backwards compatibility, submodule==\"\" is treated the same as\n- * submodule==NULL.\n- */\n-struct ref_store *get_ref_store(const char *submodule);\n-\n const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311407","messageId":"20170213152011.12050-9-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH 08/11] refs.c: factor submodule code out of get_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-13T15:20:08Z","receivedAt":"2017-02-13T15:21:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This code is going to be expanded a bit soon. Keep it out for\nbetter readability later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 22 ++++++++++++++--------\n 1 file changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2cacd934e..8ef7a52ba 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1445,6 +1445,18 @@ static struct ref_store *lookup_ref_store(const char *submodule)\n \treturn entry ? entry->refs : NULL;\n }\n \n+static struct ref_store *init_submodule_ref_store(const char *submodule)\n+{\n+\tstruct strbuf submodule_sb = STRBUF_INIT;\n+\tstruct ref_store *refs = NULL;\n+\n+\tstrbuf_addstr(&submodule_sb, submodule);\n+\tif (is_nonbare_repository_dir(&submodule_sb))\n+\t\trefs = ref_store_init(submodule);\n+\tstrbuf_release(&submodule_sb);\n+\treturn refs;\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct ref_store *refs;\n@@ -1457,14 +1469,8 @@ struct ref_store *get_ref_store(const char *submodule)\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n-\t\tif (!refs) {\n-\t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n-\n-\t\t\tstrbuf_addstr(&submodule_sb, submodule);\n-\t\t\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\t\t\trefs = ref_store_init(submodule);\n-\t\t\tstrbuf_release(&submodule_sb);\n-\t\t}\n+\t\tif (!refs)\n+\t\t\trefs = init_submodule_ref_store(submodule);\n \t}\n \n \treturn refs;\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311429","messageId":"6510f4b2-51bb-eabd-9cd7-30bc4164b25d@ramsayjones.plus.com","threadId":"45126","inReplyTo":"20170213152011.12050-2-pclouds@gmail.com","subject":"Re: [PATCH 01/11] refs-internal.c: make files_log_ref_write() static","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2017-02-13T20:14:03Z","receivedAt":"2017-02-13T20:14:16Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 13/02/17 15:20, Nguyễn Thái Ngọc Duy wrote:\n> Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\n> but probably never used outside refs-internal.c\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 3 +++\n>  refs/refs-internal.h | 4 ----\n>  2 files changed, 3 insertions(+), 4 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index cdb6b8ff5..75565c3aa 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n>  \t\t\t\t\t  const char *dirname, size_t len,\n>  \t\t\t\t\t  int incomplete);\n>  static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n> +static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n> +\t\t\t       const unsigned char *new_sha1, const char *msg,\n> +\t\t\t       int flags, struct strbuf *err);\n\nWhy? I don't like adding forward declarations unless it\nis absolutely necessary (ie mutually recursive functions),\nand even in the current 'pu' branch (@c04899d50), the\ndefinition of this function appears before all uses in\nthis file. (ie, just add static to the definition).\n\nWhat am I missing?\n\nATB,\nRamsay Jones\n\n>  \n>  static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n>  {\n> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n> index 33adbf93b..59e65958a 100644\n> --- a/refs/refs-internal.h\n> +++ b/refs/refs-internal.h\n> @@ -222,10 +222,6 @@ struct ref_transaction {\n>  \tenum ref_transaction_state state;\n>  };\n>  \n> -int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n> -\t\t\tconst unsigned char *new_sha1, const char *msg,\n> -\t\t\tint flags, struct strbuf *err);\n> -\n>  /*\n>   * Check for entries in extras that are within the specified\n>   * directory, where dirname is a reference directory name including\n> \n"},{"id":"311433","messageId":"e8a83077-1d4d-1274-956c-83388957bc63@ramsayjones.plus.com","threadId":"45126","inReplyTo":"20170213152011.12050-3-pclouds@gmail.com","subject":"Re: [PATCH 02/11] files-backend: convert git_path() to strbuf_git_path()","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2017-02-13T20:38:35Z","receivedAt":"2017-02-13T20:38:51Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 13/02/17 15:20, Nguyễn Thái Ngọc Duy wrote:\n> git_path() and friends are going to be killed in files-backend.c in near\n> future. And because there's a risk with overwriting buffer in\n> git_path(), let's convert them all to strbuf_git_path(). We'll have\n> easier time killing/converting strbuf_git_path() then because we won't\n> have to worry about memory management again.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 114 ++++++++++++++++++++++++++++++++++++++++-----------\n>  1 file changed, 90 insertions(+), 24 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 75565c3aa..6582c9b2d 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -2169,6 +2169,8 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n>  \tstatic int timeout_configured = 0;\n>  \tstatic int timeout_value = 1000;\n>  \tstruct packed_ref_cache *packed_ref_cache;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\tint ret;\n>  \n>  \tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n>  \n> @@ -2177,10 +2179,13 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n>  \t\ttimeout_configured = 1;\n>  \t}\n>  \n> -\tif (hold_lock_file_for_update_timeout(\n> -\t\t\t    &packlock, git_path(\"packed-refs\"),\n> -\t\t\t    flags, timeout_value) < 0)\n> +\tstrbuf_git_path(&sb, \"packed-refs\");\n> +\tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n> +\t\t\t\t\t\tflags, timeout_value);\n> +\tstrbuf_release(&sb);\n> +\tif (ret < 0)\n>  \t\treturn -1;\n> +\n>  \t/*\n>  \t * Get the current packed-refs while holding the lock.  If the\n>  \t * packed-refs file has been modified since we last read it,\n> @@ -2335,6 +2340,9 @@ static void try_remove_empty_parents(char *name)\n>  \tfor (q = p; *q; q++)\n>  \t\t;\n>  \twhile (1) {\n> +\t\tstruct strbuf sb = STRBUF_INIT;\n> +\t\tint ret;\n> +\n>  \t\twhile (q > p && *q != '/')\n>  \t\t\tq--;\n>  \t\twhile (q > p && *(q-1) == '/')\n> @@ -2342,7 +2350,10 @@ static void try_remove_empty_parents(char *name)\n>  \t\tif (q == p)\n>  \t\t\tbreak;\n>  \t\t*q = '\\0';\n> -\t\tif (rmdir(git_path(\"%s\", name)))\n> +\t\tstrbuf_git_path(&sb, \"%s\", name);\n> +\t\tret = rmdir(sb.buf);\n> +\t\tstrbuf_release(&sb);\n> +\t\tif (ret)\n>  \t\t\tbreak;\n>  \t}\n>  }\n> @@ -2431,7 +2442,11 @@ static int repack_without_refs(struct files_ref_store *refs,\n>  \t\treturn 0; /* no refname exists in packed refs */\n>  \n>  \tif (lock_packed_refs(refs, 0)) {\n> -\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n> +\t\tstruct strbuf sb = STRBUF_INIT;\n> +\n> +\t\tstrbuf_git_path(&sb, \"packed-refs\");\n> +\t\tunable_to_lock_message(sb.buf, errno, err);\n> +\t\tstrbuf_release(&sb);\n>  \t\treturn -1;\n>  \t}\n>  \tpacked = get_packed_refs(refs);\n> @@ -2529,11 +2544,12 @@ static int rename_tmp_log(const char *newrefname)\n>  {\n>  \tint attempts_remaining = 4;\n>  \tstruct strbuf path = STRBUF_INIT;\n> +\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n>  \tint ret = -1;\n>  \n> - retry:\n> -\tstrbuf_reset(&path);\n>  \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n> +\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n> + retry:\n>  \tswitch (safe_create_leading_directories_const(path.buf)) {\n>  \tcase SCLD_OK:\n>  \t\tbreak; /* success */\n> @@ -2546,7 +2562,7 @@ static int rename_tmp_log(const char *newrefname)\n>  \t\tgoto out;\n>  \t}\n>  \n> -\tif (rename(git_path(TMP_RENAMED_LOG), path.buf)) {\n> +\tif (rename(tmp_renamed_log.buf, path.buf)) {\n>  \t\tif ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {\n>  \t\t\t/*\n>  \t\t\t * rename(a, b) when b is an existing\n> @@ -2574,6 +2590,7 @@ static int rename_tmp_log(const char *newrefname)\n>  \tret = 0;\n>  out:\n>  \tstrbuf_release(&path);\n> +\tstrbuf_release(&tmp_renamed_log);\n>  \treturn ret;\n>  }\n>  \n> @@ -2614,9 +2631,15 @@ static int files_rename_ref(struct ref_store *ref_store,\n>  \tint flag = 0, logmoved = 0;\n>  \tstruct ref_lock *lock;\n>  \tstruct stat loginfo;\n> -\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n> +\tstruct strbuf sb_oldref = STRBUF_INIT;\n> +\tstruct strbuf sb_newref = STRBUF_INIT;\n> +\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n> +\tint log, ret;\n>  \tstruct strbuf err = STRBUF_INIT;\n>  \n> +\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n> +\tlog = !lstat(sb_oldref.buf, &loginfo);\n> +\tstrbuf_release(&sb_oldref);\n>  \tif (log && S_ISLNK(loginfo.st_mode))\n>  \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n>  \n> @@ -2630,7 +2653,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n>  \tif (!rename_ref_available(oldrefname, newrefname))\n>  \t\treturn 1;\n>  \n> -\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n> +\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n> +\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n> +\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n\nIt probably won't make too much difference, but the two code\nsequences above are not similar in terms of side-effects when\n'log' is false. In that case, the two calls to git_path() and\nthe call to rename() are not made in the original code. In the\nnew sequence, the two calls to strbuf_git_path() are always made\n(but rename() is not).\n\n> +\tstrbuf_release(&sb_oldref);\n> +\tstrbuf_release(&tmp_renamed_log);\n> +\tif (ret)\n>  \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n>  \t\t\toldrefname, strerror(errno));\n>  \n> @@ -2709,13 +2737,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n>  \tlog_all_ref_updates = flag;\n>  \n>   rollbacklog:\n> -\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n> +\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n> +\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n> +\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n\nditto\n\n>  \t\terror(\"unable to restore logfile %s from %s: %s\",\n>  \t\t\toldrefname, newrefname, strerror(errno));\n> +\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n>  \tif (!logmoved && log &&\n> -\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n> +\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n\nsimilar\n\n>  \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n>  \t\t\toldrefname, strerror(errno));\n> +\tstrbuf_release(&sb_newref);\n> +\tstrbuf_release(&sb_oldref);\n> +\tstrbuf_release(&tmp_renamed_log);\n>  \n>  \treturn 1;\n>  }\n\nATB,\nRamsay Jones\n\n"},{"id":"311436","messageId":"efab3ec2-e7c6-595a-8781-699bdc685a26@ramsayjones.plus.com","threadId":"45126","inReplyTo":"20170213152011.12050-4-pclouds@gmail.com","subject":"Re: [PATCH 03/11] files-backend: add files_path()","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2017-02-13T20:43:46Z","receivedAt":"2017-02-13T20:43:59Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 13/02/17 15:20, Nguyễn Thái Ngọc Duy wrote:\n> This will be the replacement for both git_path() and git_path_submodule()\n> in this file. The idea is backend takes a git path and use that,\n> oblivious of submodule, linked worktrees and such.\n> \n> This is the middle step towards that. Eventually the \"submodule\" field\n> in 'struct files_ref_store' should be replace by \"gitdir\". And a\n> compound ref_store is created to combine two files backends together,\n> one represents the shared refs in $GIT_COMMON_DIR, one per-worktree. At\n> that point, files_path() becomes a wrapper of strbuf_vaddf().\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 20 ++++++++++++++++++++\n>  1 file changed, 20 insertions(+)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 6582c9b2d..39217a2ca 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -169,6 +169,9 @@ static int files_log_ref_write(const char *refname, const unsigned char *old_sha\n>  \t\t\t       const unsigned char *new_sha1, const char *msg,\n>  \t\t\t       int flags, struct strbuf *err);\n>  \n> +void files_path(struct files_ref_store *refs, struct strbuf *sb,\n> +\t\tconst char *fmt, ...) __attribute__((format (printf, 3, 4)));\n> +\n\nWhy?\n\n>  static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n>  {\n>  \tstruct ref_dir *dir;\n> @@ -930,6 +933,23 @@ struct files_ref_store {\n>  /* Lock used for the main packed-refs file: */\n>  static struct lock_file packlock;\n>  \n> +void files_path(struct files_ref_store *refs, struct strbuf *sb,\n> +\t\tconst char *fmt, ...)\n> +{\n> +\tstruct strbuf tmp = STRBUF_INIT;\n> +\tva_list vap;\n> +\n> +\tva_start(vap, fmt);\n> +\tstrbuf_vaddf(&tmp, fmt, vap);\n> +\tva_end(vap);\n> +\tif (refs->submodule)\n> +\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n> +\t\t\t\t\t  \"%s\", tmp.buf);\n> +\telse\n> +\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n> +\tstrbuf_release(&tmp);\n> +}\n> +\n>  /*\n>   * Increment the reference count of *packed_refs.\n>   */\n> \n\nATB,\nRamsay Jones\n\n\n"},{"id":"311440","messageId":"8f6e09aa-f578-6faf-6acc-e35be71ee990@ramsayjones.plus.com","threadId":"45126","inReplyTo":"20170213152011.12050-5-pclouds@gmail.com","subject":"Re: [PATCH 04/11] files-backend: replace *git_path*() with files_path()","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2017-02-13T20:58:37Z","receivedAt":"2017-02-13T20:58:46Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 13/02/17 15:20, Nguyễn Thái Ngọc Duy wrote:\n> This centralizes all path rewriting of files-backend.c in one place so\n> we have easier time removing the path rewriting later. There could be\n> some hidden indirect git_path() though, I didn't audit the code to the\n> bottom.\n> \n> Side note: set_worktree_head_symref() is a bad boy and should not be in\n> files-backend.c (probably should not exist in the first place). But\n> we'll leave it there until we have better multi-worktree support in refs\n> before we update it.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 193 ++++++++++++++++++++++++++-------------------------\n>  1 file changed, 98 insertions(+), 95 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 39217a2ca..c69e4fe84 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -165,12 +165,13 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n>  \t\t\t\t\t  const char *dirname, size_t len,\n>  \t\t\t\t\t  int incomplete);\n>  static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n> -static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n> +static int files_log_ref_write(struct files_ref_store *refs,\n> +\t\t\t       const char *refname, const unsigned char *old_sha1,\n>  \t\t\t       const unsigned char *new_sha1, const char *msg,\n>  \t\t\t       int flags, struct strbuf *err);\n>  \n> -void files_path(struct files_ref_store *refs, struct strbuf *sb,\n> -\t\tconst char *fmt, ...) __attribute__((format (printf, 3, 4)));\n\nYou only added this in the last commit, so maybe mark it static in\nthe previous patch! Also, just in case you were wondering, the 'Why?'\nof the previous email was, \"Why do you need this forward declaration?\"\n(hint: you don't ;-)\n\n> +static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n> +\t\t       const char *fmt, ...) __attribute__((format (printf, 3, 4)));\n>  \n>  static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n>  {\n> @@ -933,8 +934,8 @@ struct files_ref_store {\n>  /* Lock used for the main packed-refs file: */\n>  static struct lock_file packlock;\n>  \n> -void files_path(struct files_ref_store *refs, struct strbuf *sb,\n> -\t\tconst char *fmt, ...)\n> +static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n> +\t\t       const char *fmt, ...)\n>  {\n>  \tstruct strbuf tmp = STRBUF_INIT;\n>  \tva_list vap;\n\nATB,\nRamsay Jones\n\n"},{"id":"311461","messageId":"CAGZ79kYkc-_=RiK1uJ+ndhQu=B8u=UDVusXZu-dYe7KnGNye3Q@mail.gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-7-pclouds@gmail.com","subject":"Re: [PATCH 06/11] refs-internal.h: correct is_per_worktree_ref()","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-13T22:37:59Z","receivedAt":"2017-02-13T22:38:04Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Feb 13, 2017 at 7:20 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> All refs outside refs/ directory is per-worktree, not just HEAD.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/refs-internal.h | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n> index f4aed49f5..69d02b6ba 100644\n> --- a/refs/refs-internal.h\n> +++ b/refs/refs-internal.h\n> @@ -653,7 +653,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n>\n>  static inline int is_per_worktree_ref(const char *refname)\n>  {\n> -       return !strcmp(refname, \"HEAD\") ||\n> +       return !starts_with(refname, \"refs/\") ||\n>                 starts_with(refname, \"refs/bisect/\");\n\nyou're loosing HEAD here? (assuming we get HEAD in\nshort form here, as well as long form refs/HEAD)\n\n>  }\n>\n> --\n> 2.11.0.157.gd943d85\n>\n"},{"id":"311466","messageId":"CAGZ79kbJXUY=UGvHtkeLLj-qMaoOyTwa2dr3-FqEdYi8eFs4LA@mail.gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-8-pclouds@gmail.com","subject":"Re: [PATCH 07/11] files-backend: remove the use of git_path()","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-13T23:09:40Z","receivedAt":"2017-02-13T23:09:46Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"> +\n> +       if (submodule) {\n> +               refs->submodule = xstrdup_or_null(submodule);\n\ndrop the _or_null here?\n\nSo in this patch we have either\n* submodule set\n* or gitdir/gitcommondir set\n\nwhich means that we exercise the commondir for regular repos.\nIn the future when we want to be able to have a combination of worktrees\nand submodules this ought to be possible by setting submodule != NULL\nand still populating the gitdir/commondir buffers.\n\nThanks,\nStefan\n"},{"id":"311468","messageId":"CAGZ79kb+upRpmzjwKkUCBad4JvVsjQGRt_uRsd6XhdizMGkqtg@mail.gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-9-pclouds@gmail.com","subject":"Re: [PATCH 08/11] refs.c: factor submodule code out of get_ref_store()","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-13T23:13:27Z","receivedAt":"2017-02-13T23:13:33Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Feb 13, 2017 at 7:20 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> This code is going to be expanded a bit soon. Keep it out for\n> better readability later.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nLooks good,\nThanks,\nStefan\n"},{"id":"311473","messageId":"CAGZ79kannQDWWYN5rHr6q-6=o1_ajeEjLn8Wzsc4RjqkfYOYwg@mail.gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-10-pclouds@gmail.com","subject":"Re: [PATCH 09/11] refs: move submodule code out of files-backend.c","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-13T23:35:27Z","receivedAt":"2017-02-13T23:35:33Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Feb 13, 2017 at 7:20 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> files-backend is now initialized with a $GIT_DIR. Converting a submodule\n> path to where real submodule gitdir is located is done in get_ref_store().\n>\n> The new code in init_submodule_ref_store() is basically a copy of\n> strbuf_git_path_submodule().\n>\n> This gives a slight performance improvement for submodules since we\n> don't convert submodule path to gitdir at every backend call like\n> before. We pay that once at ref-store creation.\n>\n> More cleanup in files_downcast() follows shortly. It's separate to keep\n> noises from this patch.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c               | 48 +++++++++++++++++++++++++++++++++++++++++-------\n>  refs/files-backend.c | 25 +++++++------------------\n>  refs/refs-internal.h |  6 +++---\n>  3 files changed, 51 insertions(+), 28 deletions(-)\n>\n> diff --git a/refs.c b/refs.c\n> index 8ef7a52ba..9ac194945 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -9,6 +9,7 @@\n>  #include \"refs/refs-internal.h\"\n>  #include \"object.h\"\n>  #include \"tag.h\"\n> +#include \"submodule-config.h\"\n>\n>  /*\n>   * List of all available backends\n> @@ -1410,7 +1411,7 @@ static void register_ref_store(struct ref_store *refs, const char *submodule)\n>   * Create, record, and return a ref_store instance for the specified\n>   * submodule (or the main repository if submodule is NULL).\n>   */\n> -static struct ref_store *ref_store_init(const char *submodule)\n> +static struct ref_store *ref_store_init(const char *submodule, const char *gitdir)\n>  {\n>         const char *be_name = \"files\";\n>         struct ref_storage_be *be = find_ref_storage_backend(be_name);\n> @@ -1419,7 +1420,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n>         if (!be)\n>                 die(\"BUG: reference backend %s is unknown\", be_name);\n>\n> -       refs = be->init(submodule);\n> +       refs = be->init(gitdir);\n>         register_ref_store(refs, submodule);\n>         return refs;\n>  }\n> @@ -1445,15 +1446,48 @@ static struct ref_store *lookup_ref_store(const char *submodule)\n>         return entry ? entry->refs : NULL;\n>  }\n>\n> -static struct ref_store *init_submodule_ref_store(const char *submodule)\n> +static struct ref_store *init_submodule_ref_store(const char *path)\n>  {\n>         struct strbuf submodule_sb = STRBUF_INIT;\n> +       struct strbuf git_submodule_common_dir = STRBUF_INIT;\n> +       struct strbuf git_submodule_dir = STRBUF_INIT;\n> +       struct strbuf buf = STRBUF_INIT;\n> +       const char *git_dir;\n> +       const struct submodule *sub;\n>         struct ref_store *refs = NULL;\n>\n> -       strbuf_addstr(&submodule_sb, submodule);\n> -       if (is_nonbare_repository_dir(&submodule_sb))\n> -               refs = ref_store_init(submodule);\n> +       strbuf_addstr(&submodule_sb, path);\n> +       if (!is_nonbare_repository_dir(&submodule_sb))\n> +               goto done;\n> +\n> +       strbuf_addstr(&buf, path);\n> +       strbuf_complete(&buf, '/');\n> +       strbuf_addstr(&buf, \".git\");\n> +\n> +       git_dir = read_gitfile(buf.buf);\n\nif buf.buf is a (git) directory as opposed to a git file,\nwe error out in read_gitfile. Did you mean to use\nread_gitfile_gently here or rather even resolve_gitdir_gently ?\n\n> +       if (git_dir) {\n\nwhen not using the _gently version git_dir is always\nnon NULL here (or we're dead)?\n\n> +               strbuf_reset(&buf);\n> +               strbuf_addstr(&buf, git_dir);\n> +       }\n> +       if (!is_git_directory(buf.buf)) {\n> +               gitmodules_config();\n> +               sub = submodule_from_path(null_sha1, path);\n> +               if (!sub)\n> +                       goto done;\n> +               strbuf_reset(&buf);\n> +               strbuf_git_path(&buf, \"%s/%s\", \"modules\", sub->name);\n\nYou can inline \"modules\" into the format string?\n\n> +       }\n> +       strbuf_addch(&buf, '/');\n> +       strbuf_addbuf(&git_submodule_dir, &buf);\n> +\n> +       refs = ref_store_init(path, git_submodule_dir.buf);\n\nstrbuf_detach (git_submodule_dir) here, such that we keep\nthe string alive despite the release of the strbuf below?\n\nso essentially this function\n* takes a submodule path\n* checks if there is a repo at the given path in the working tree\n* resolves the gitfile if any\n* if the gitfile could not resolve to a valid repo just make up the\n  location to be $GIT_DIR/modules/<name>\n\nsounds confusing to me. I need to reread it later.\n\n>\n> -       if (submodule) {\n> -               refs->submodule = xstrdup_or_null(submodule);\n> +       if (gitdir) {\n> +               strbuf_addstr(&refs->gitdir, gitdir);\n> +               get_common_dir_noenv(&refs->gitcommondir, gitdir);\n\nOh I see. we loose the _or_null here, so my remark on the previous patch\nmight be just unneeded work.\n\n>         } else {\n>                 strbuf_addstr(&refs->gitdir, get_git_dir());\n>                 strbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n> @@ -1034,8 +1025,6 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n>  static void files_assert_main_repository(struct files_ref_store *refs,\n>                                          const char *caller)\n>  {\n> -       if (refs->submodule)\n> -               die(\"BUG: %s called for a submodule\", caller);\n>  }\n\nIn a followup we'd get rid of files_assert_main_repository\npresumably?\n\nThanks,\nStefan\n"},{"id":"311474","messageId":"CAGZ79kaYfdNgZ-AxQq3Tf+CF095O+GQ0ecqq1Oe2LcAGYjZP3A@mail.gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-11-pclouds@gmail.com","subject":"Re: [PATCH 10/11] files-backend: remove submodule_allowed from files_downcast()","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-13T23:44:16Z","receivedAt":"2017-02-13T23:44:23Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Feb 13, 2017 at 7:20 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Since submodule or not is irrelevant to files-backend after the last\n> patch, remove the parameter from files_downcast() and kill\n> files_assert_main_repository().\n>\n> PS. This implies that all ref operations are allowed for submodules. But\n> we may need to look more closely to see if that's really true...\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nLooks good,\nStefan\n"},{"id":"311477","messageId":"CAGZ79kZC6TntQrW7MF6-h5z5En-u6rwNX=zuaHRNDpbO80ALHA@mail.gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-12-pclouds@gmail.com","subject":"Re: [PATCH 11/11] refs: split and make get_*_ref_store() public API","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-13T23:55:48Z","receivedAt":"2017-02-13T23:55:55Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":">\n> +/*\n> + * Return the ref_store instance for the specified submodule. For the\n> + * main repository, use submodule==NULL; such a call cannot fail.\n\nSo now we have both a get_main as well as a get_submodule function,\nbut the submodule function can return the main as well?\n\nI'd rather see this as a BUG; or asking another way:\nWhat is the difference between get_submodule_ref_store(NULL)\nand get_main_ref_store() ?\n\nAs you went through all call sites (by renaming the function), we'd\nbe able to tell that there is no caller with NULL, or is it?\n\nStefan\n\n> For\n> + * a submodule, the submodule must exist and be a nonbare repository,\n> + * otherwise return NULL. If the requested reference store has not yet\n> + * been initialized, initialize it first.\n> + *\n> + * For backwards compatibility, submodule==\"\" is treated the same as\n> + * submodule==NULL.\n> + */\n> +struct ref_store *get_submodule_ref_store(const char *submodule);\n> +struct ref_store *get_main_ref_store(void);\n"},{"id":"311526","messageId":"CACsJy8BnSdaUHx1tCu6YkMedwp3RL4riKLYfT5N4cHHjG9fNLw@mail.gmail.com","threadId":"45126","inReplyTo":"6510f4b2-51bb-eabd-9cd7-30bc4164b25d@ramsayjones.plus.com","subject":"Re: [PATCH 01/11] refs-internal.c: make files_log_ref_write() static","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-14T09:23:38Z","receivedAt":"2017-02-14T09:24:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 14, 2017 at 3:14 AM, Ramsay Jones\n<ramsay@ramsayjones.plus.com> wrote:\n>\n>\n> On 13/02/17 15:20, Nguyễn Thái Ngọc Duy wrote:\n>> Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\n>> but probably never used outside refs-internal.c\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs/files-backend.c | 3 +++\n>>  refs/refs-internal.h | 4 ----\n>>  2 files changed, 3 insertions(+), 4 deletions(-)\n>>\n>> diff --git a/refs/files-backend.c b/refs/files-backend.c\n>> index cdb6b8ff5..75565c3aa 100644\n>> --- a/refs/files-backend.c\n>> +++ b/refs/files-backend.c\n>> @@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n>>                                         const char *dirname, size_t len,\n>>                                         int incomplete);\n>>  static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n>> +static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n>> +                            const unsigned char *new_sha1, const char *msg,\n>> +                            int flags, struct strbuf *err);\n>\n> Why? I don't like adding forward declarations unless it\n> is absolutely necessary (ie mutually recursive functions),\n> and even in the current 'pu' branch (@c04899d50), the\n> definition of this function appears before all uses in\n> this file. (ie, just add static to the definition).\n>\n> What am I missing?\n\nIt may have been needed at one point. With all the code changes and\nmovements, I guess I forgot to remove it.\n-- \nDuy\n"},{"id":"311527","messageId":"CACsJy8B-B9Ebz2tXCq4sUzMGfnp6ojGcEXA4amSOsCN1u2SjNQ@mail.gmail.com","threadId":"45126","inReplyTo":"CAGZ79kannQDWWYN5rHr6q-6=o1_ajeEjLn8Wzsc4RjqkfYOYwg@mail.gmail.com","subject":"Re: [PATCH 09/11] refs: move submodule code out of files-backend.c","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-14T09:32:08Z","receivedAt":"2017-02-14T09:32:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 14, 2017 at 6:35 AM, Stefan Beller <sbeller@google.com> wrote:\n>> +       git_dir = read_gitfile(buf.buf);\n>\n> if buf.buf is a (git) directory as opposed to a git file,\n> we error out in read_gitfile. Did you mean to use\n> read_gitfile_gently here or rather even resolve_gitdir_gently ?\n\nThis is what strbuf_git_path_submodule() does. I don't know the\nbackstory so I'm going to keep it as it is to keep the behavior\nexactly (or very close) as before. We can replace it with a better\nversion (with explanation and all).\n\n>> +       if (git_dir) {\n>\n> when not using the _gently version git_dir is always\n> non NULL here (or we're dead)?\n>\n>> +               strbuf_reset(&buf);\n>> +               strbuf_addstr(&buf, git_dir);\n>> +       }\n>> +       if (!is_git_directory(buf.buf)) {\n>> +               gitmodules_config();\n>> +               sub = submodule_from_path(null_sha1, path);\n>> +               if (!sub)\n>> +                       goto done;\n>> +               strbuf_reset(&buf);\n>> +               strbuf_git_path(&buf, \"%s/%s\", \"modules\", sub->name);\n>\n> You can inline \"modules\" into the format string?\n\nHm.. because this is strbuf_git_path_submodule() code. Perhaps it's\nbetter to split it to a separate function and call it from here? Then\nyou can make more improvements on top that benefit everybody.\n\n>>         } else {\n>>                 strbuf_addstr(&refs->gitdir, get_git_dir());\n>>                 strbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n>> @@ -1034,8 +1025,6 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n>>  static void files_assert_main_repository(struct files_ref_store *refs,\n>>                                          const char *caller)\n>>  {\n>> -       if (refs->submodule)\n>> -               die(\"BUG: %s called for a submodule\", caller);\n>>  }\n>\n> In a followup we'd get rid of files_assert_main_repository\n> presumably?\n\nYes. Can't delete it now because I would need to touch all callers.\n-- \nDuy\n"},{"id":"311528","messageId":"CACsJy8DorHk9Xc9iPhw4DYAUeufVhd8229-GXzC_SaTEUb89RA@mail.gmail.com","threadId":"45126","inReplyTo":"CAGZ79kbJXUY=UGvHtkeLLj-qMaoOyTwa2dr3-FqEdYi8eFs4LA@mail.gmail.com","subject":"Re: [PATCH 07/11] files-backend: remove the use of git_path()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-14T09:38:05Z","receivedAt":"2017-02-14T09:38:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 14, 2017 at 6:09 AM, Stefan Beller <sbeller@google.com> wrote:\n>> +\n>> +       if (submodule) {\n>> +               refs->submodule = xstrdup_or_null(submodule);\n>\n> drop the _or_null here?\n>\n> So in this patch we have either\n> * submodule set\n> * or gitdir/gitcommondir set\n>\n> which means that we exercise the commondir for regular repos.\n> In the future when we want to be able to have a combination of worktrees\n> and submodules this ought to be possible by setting submodule != NULL\n> and still populating the gitdir/commondir buffers.\n\nYou probably have seen it by now. In the near future, submodule is\ncompletely gone from here. We convert to a .git dir before we pass in\nhere. In a farther future, gitcommondir will be gone too with all the\nper-worktree logic in this file. A linked worktree consists of two\nbackends actually, one per-worktree (which remains files-based), the\nother for shared refs, which could be files, lmdb or whatever.\nStacking up submodule on top of a linked worktree should not be a\nproblem.\n-- \nDuy\n"},{"id":"311529","messageId":"CACsJy8D=qFQ2_62e4oO1pSBz4JfZV4Zcoai=Ghjw5DTaNxrwog@mail.gmail.com","threadId":"45126","inReplyTo":"CAGZ79kYkc-_=RiK1uJ+ndhQu=B8u=UDVusXZu-dYe7KnGNye3Q@mail.gmail.com","subject":"Re: [PATCH 06/11] refs-internal.h: correct is_per_worktree_ref()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-14T09:40:30Z","receivedAt":"2017-02-14T09:41:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 14, 2017 at 5:37 AM, Stefan Beller <sbeller@google.com> wrote:\n> On Mon, Feb 13, 2017 at 7:20 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> All refs outside refs/ directory is per-worktree, not just HEAD.\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs/refs-internal.h | 2 +-\n>>  1 file changed, 1 insertion(+), 1 deletion(-)\n>>\n>> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n>> index f4aed49f5..69d02b6ba 100644\n>> --- a/refs/refs-internal.h\n>> +++ b/refs/refs-internal.h\n>> @@ -653,7 +653,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n>>\n>>  static inline int is_per_worktree_ref(const char *refname)\n>>  {\n>> -       return !strcmp(refname, \"HEAD\") ||\n>> +       return !starts_with(refname, \"refs/\") ||\n>>                 starts_with(refname, \"refs/bisect/\");\n>\n> you're loosing HEAD here? (assuming we get HEAD in\n> short form here, as well as long form refs/HEAD)\n\nI don't understand. if refname is HEAD then both !strcmp(...) and\n!starts_with(refname, \"refs/\") return 1. If it's refs/HEAD, both\nreturn 0. In other words, there's no functional changes?\n-- \nDuy\n"},{"id":"311530","messageId":"CACsJy8CeEZ9XESGaCLZs8uznw_XazjTmbumzo6foiGbUp6Qa5Q@mail.gmail.com","threadId":"45126","inReplyTo":"8f6e09aa-f578-6faf-6acc-e35be71ee990@ramsayjones.plus.com","subject":"Re: [PATCH 04/11] files-backend: replace *git_path*() with files_path()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-14T09:43:49Z","receivedAt":"2017-02-14T09:45:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 14, 2017 at 3:58 AM, Ramsay Jones\n<ramsay@ramsayjones.plus.com> wrote:\n>> -void files_path(struct files_ref_store *refs, struct strbuf *sb,\n>> -             const char *fmt, ...) __attribute__((format (printf, 3, 4)));\n>\n> You only added this in the last commit, so maybe mark it static in\n> the previous patch! Also, just in case you were wondering, the 'Why?'\n> of the previous email was, \"Why do you need this forward declaration?\"\n> (hint: you don't ;-)\n\nYeah, my compiler considers unused static functions an error. But I\nguess I should adjust those million compiler flags instead of doing it\nlike this. I'll need to check if it's an error with DEVELOPER_CFLAGS\nbecause people may bisect with that.\n-- \nDuy\n"},{"id":"311532","messageId":"CACsJy8ChQqUd4poeeKQruQSwdys=ydzxGDC8fU6ZgfrFEEm7NQ@mail.gmail.com","threadId":"45126","inReplyTo":"CAGZ79kZC6TntQrW7MF6-h5z5En-u6rwNX=zuaHRNDpbO80ALHA@mail.gmail.com","subject":"Re: [PATCH 11/11] refs: split and make get_*_ref_store() public API","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-14T10:04:06Z","receivedAt":"2017-02-14T10:06:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 14, 2017 at 6:55 AM, Stefan Beller <sbeller@google.com> wrote:\n>>\n>> +/*\n>> + * Return the ref_store instance for the specified submodule. For the\n>> + * main repository, use submodule==NULL; such a call cannot fail.\n>\n> So now we have both a get_main as well as a get_submodule function,\n> but the submodule function can return the main as well?\n>\n> I'd rather see this as a BUG; or asking another way:\n> What is the difference between get_submodule_ref_store(NULL)\n> and get_main_ref_store() ?\n\nTechnical debts :) In order to do that, we need to make sure\n_submodule() never takes NULL as main repo. But current code does. On\nexample is revision.c where submodule==NULL is the main repo. In the\nend, I agree that get_submodule_ref_store(NULL) should be a bug.\n\n> As you went through all call sites (by renaming the function), we'd\n> be able to tell that there is no caller with NULL, or is it?\n\nDirect call sites are just middle men though, e.g.\nfor_each_ref_submodule(). I'll attempt to clean this up at some point\nin future (probably at the same time I attempt to kill *_submodule ref\napi). But I think for now I'll just put a TODO or FIXME comment here.\n-- \nDuy\n"},{"id":"311550","messageId":"CAGZ79kaN-Z5zKHBCoeVzsQbJiHm408RB2QYZ8=W4QNP0Gdy7sQ@mail.gmail.com","threadId":"45126","inReplyTo":"CACsJy8D=qFQ2_62e4oO1pSBz4JfZV4Zcoai=Ghjw5DTaNxrwog@mail.gmail.com","subject":"Re: [PATCH 06/11] refs-internal.h: correct is_per_worktree_ref()","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-14T17:40:57Z","receivedAt":"2017-02-14T17:41:03Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Feb 14, 2017 at 1:40 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Feb 14, 2017 at 5:37 AM, Stefan Beller <sbeller@google.com> wrote:\n>> On Mon, Feb 13, 2017 at 7:20 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>>> All refs outside refs/ directory is per-worktree, not just HEAD.\n>>>\n>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>>> ---\n>>>  refs/refs-internal.h | 2 +-\n>>>  1 file changed, 1 insertion(+), 1 deletion(-)\n>>>\n>>> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n>>> index f4aed49f5..69d02b6ba 100644\n>>> --- a/refs/refs-internal.h\n>>> +++ b/refs/refs-internal.h\n>>> @@ -653,7 +653,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n>>>\n>>>  static inline int is_per_worktree_ref(const char *refname)\n>>>  {\n>>> -       return !strcmp(refname, \"HEAD\") ||\n>>> +       return !starts_with(refname, \"refs/\") ||\n>>>                 starts_with(refname, \"refs/bisect/\");\n>>\n>> you're loosing HEAD here? (assuming we get HEAD in\n>> short form here, as well as long form refs/HEAD)\n>\n> I don't understand. if refname is HEAD then both !strcmp(...) and\n> !starts_with(refname, \"refs/\") return 1. If it's refs/HEAD, both\n> return 0. In other words, there's no functional changes?\n\neh, my bad. You're right.\n"},{"id":"311555","messageId":"xmqq8tp8aawb.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"CACsJy8ChQqUd4poeeKQruQSwdys=ydzxGDC8fU6ZgfrFEEm7NQ@mail.gmail.com","subject":"Re: [PATCH 11/11] refs: split and make get_*_ref_store() public API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-14T18:24:20Z","receivedAt":"2017-02-14T18:25:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> Direct call sites are just middle men though, e.g.\n> for_each_ref_submodule(). I'll attempt to clean this up at some point\n> in future (probably at the same time I attempt to kill *_submodule ref\n> api). But I think for now I'll just put a TODO or FIXME comment here.\n\nSo we'd have get_*_ref_store() for various cases and then will have\na unifying for_each_ref_in_refstore() that takes a ref-store, which\nsupersedes all the ad-hoc iterators like for_each_ref_submodule(),\nfor_each_namespaced_ref(), etc?\n\nThat's a very sensible longer-term goal, methinks.\n\nI am wondering what we should do to for_each_{tag,branch,...}_ref().  \nWould that become\n\n\tref_store = get_ref_main_store();\n\ttag_ref_store = filter_ref_store(ref_store, \"refs/tags/\");\n\tfor_each_ref_in_refstore(tag_ref_store, ...);\n\nor do you plan to have some other pattern?\n"},{"id":"311556","messageId":"CAGZ79kb9jA9AfpPPPyOeiJPowvZempSq9V_3HAKYXUJQumU-zA@mail.gmail.com","threadId":"45126","inReplyTo":"CACsJy8ChQqUd4poeeKQruQSwdys=ydzxGDC8fU6ZgfrFEEm7NQ@mail.gmail.com","subject":"Re: [PATCH 11/11] refs: split and make get_*_ref_store() public API","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-14T18:43:59Z","receivedAt":"2017-02-14T18:44:06Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Feb 14, 2017 at 2:04 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Feb 14, 2017 at 6:55 AM, Stefan Beller <sbeller@google.com> wrote:\n>>>\n>>> +/*\n>>> + * Return the ref_store instance for the specified submodule. For the\n>>> + * main repository, use submodule==NULL; such a call cannot fail.\n>>\n>> So now we have both a get_main as well as a get_submodule function,\n>> but the submodule function can return the main as well?\n>>\n>> I'd rather see this as a BUG; or asking another way:\n>> What is the difference between get_submodule_ref_store(NULL)\n>> and get_main_ref_store() ?\n>\n> Technical debts :) In order to do that, we need to make sure\n> _submodule() never takes NULL as main repo. But current code does. On\n> example is revision.c where submodule==NULL is the main repo. In the\n> end, I agree that get_submodule_ref_store(NULL) should be a bug.\n>\n>> As you went through all call sites (by renaming the function), we'd\n>> be able to tell that there is no caller with NULL, or is it?\n>\n> Direct call sites are just middle men though, e.g.\n> for_each_ref_submodule(). I'll attempt to clean this up at some point\n> in future (probably at the same time I attempt to kill *_submodule ref\n> api). But I think for now I'll just put a TODO or FIXME comment here.\n\nok, fine with me.\n\nMy line of thinking when originally responding was to put the FIXME\ncomment at the caller site, and there we'd differentiate between the\ntwo cases and call the appropriate function.\n\n> --\n> Duy\n"},{"id":"311625","messageId":"CACsJy8AfwGK_Y8vH-mF4NXWts_4_CPZamO0L_rWD-1WR3=36Yg@mail.gmail.com","threadId":"45126","inReplyTo":"xmqq8tp8aawb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 11/11] refs: split and make get_*_ref_store() public API","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-15T00:44:14Z","receivedAt":"2017-02-15T00:44:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Feb 15, 2017 at 1:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n>> Direct call sites are just middle men though, e.g.\n>> for_each_ref_submodule(). I'll attempt to clean this up at some point\n>> in future (probably at the same time I attempt to kill *_submodule ref\n>> api). But I think for now I'll just put a TODO or FIXME comment here.\n>\n> So we'd have get_*_ref_store() for various cases and then will have\n> a unifying for_each_ref_in_refstore() that takes a ref-store, which\n> supersedes all the ad-hoc iterators like for_each_ref_submodule(),\n> for_each_namespaced_ref(), etc?\n>\n> That's a very sensible longer-term goal, methinks.\n\nAh I forgot about ref namespace. I'll need to try something out in that area.\n\n> I am wondering what we should do to for_each_{tag,branch,...}_ref().\n> Would that become\n>\n>         ref_store = get_ref_main_store();\n>         tag_ref_store = filter_ref_store(ref_store, \"refs/tags/\");\n>         for_each_ref_in_refstore(tag_ref_store, ...);\n>\n> or do you plan to have some other pattern?\n\nLong term, I think that's what Mike wants. Though the filter_ref_store\nmight return an (opaque) iterator instead of a ref store and\nfor_each_refstore() becomes a thin wrapper around the iterator\ninterface.\n-- \nDuy\n"},{"id":"311627","messageId":"xmqqo9y45k3d.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"CACsJy8AfwGK_Y8vH-mF4NXWts_4_CPZamO0L_rWD-1WR3=36Yg@mail.gmail.com","subject":"Re: [PATCH 11/11] refs: split and make get_*_ref_store() public API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-15T01:16:54Z","receivedAt":"2017-02-15T01:17:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> Long term, I think that's what Mike wants. Though the filter_ref_store\n> might return an (opaque) iterator instead of a ref store and\n> for_each_refstore() becomes a thin wrapper around the iterator\n> interface.\n\nAh, yes, an iterator would be a lot more suitable abstraction there.\n\nThanks.\n"},{"id":"311738","messageId":"20170216114818.6080-1-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170213152011.12050-1-pclouds@gmail.com","subject":"[PATCH v2 00/16] Remove submodule from files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:02Z","receivedAt":"2017-02-16T11:48:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Changes in v2:\n\n - should fix all feedbacks I've got in v1\n - rebased on mh/submodule-hash v2\n - share the \"submodule -> gitdir\" code that I copied from\n   strbuf_git_path_submodule() to files-backend.c\n - kill register_ref_store() and lookup_ref_store(). Their main code\n   for submdules is still there, under the names\n   {register,lookup}_submodule_ref_store(). There's no mixing up\n   main and submodule stores anymore.\n\nI think the end result looks pretty good. refs.c is now almost\nsubmodule-free, if you don't count the submodule hashmap and the\nwrapper API.\n\nI'll be sending two more follow-up series, if you are interested, soon:\n\n1) v2 of nd/worktree-gc-protection\n\nwhich kills parse_ref() in worktree.c _and_ set_worktree_head_symref()\nin files-backend.c. Both are bad things that should not have happened.\n(PS. The topic name is misleading as this is mostly about eliminating\nwarts, unless Junio intended to combine my second series as well)\n\nThis series introduces get_worktree_ref_store() and adds two new APIs\nrefs_resolve_ref_unsafe() and refs_create_symref(). I'm still not sure\nif the refs_ prefix is good naming, but I had to pick something to get\nthings going. Name suggestions are welcome.\n\n2) the real worktree-gc-protection\n\nThis series adds a bunch of new refs API, enough for revision.c to\ntraverses all sorts of refs with a ref store instead of \"submodule\".\nMany _submodule API are removed as a result because they no longer\nhave any callers (yay!). One of them remains though.\n\n\nI'm also playing a bit on the side with the \"linked worktree\" ref\nstore, in order to get rid of worktree-specific logic from\nfiles-backend. After that lmdb patches can use some cleanup and\nfinally enter. But this is kinda low priority for me.\n\nNguyễn Thái Ngọc Duy (16):\n  refs-internal.c: make files_log_ref_write() static\n  files-backend: convert git_path() to strbuf_git_path()\n  files-backend: add files_path()\n  files-backend: replace *git_path*() with files_path()\n  refs.c: share is_per_worktree_ref() to files-backend.c\n  refs-internal.h: correct is_per_worktree_ref()\n  files-backend: remove the use of git_path()\n  refs.c: introduce get_main_ref_store()\n  refs: rename lookup_ref_store() to lookup_submodule_ref_store()\n  refs.c: flatten get_ref_store() a bit\n  refs.c: kill register_ref_store(), add register_submodule_ref_store()\n  refs.c: make get_main_ref_store() public and use it\n  path.c: move some code out of strbuf_git_path_submodule()\n  refs: move submodule code out of files-backend.c\n  files-backend: remove submodule_allowed from files_downcast()\n  refs: rename get_ref_store() to get_submodule_ref_store() and make it\n    public\n\n path.c               |  34 ++---\n refs.c               | 144 +++++++++++----------\n refs.h               |  13 ++\n refs/files-backend.c | 347 ++++++++++++++++++++++++++++++---------------------\n refs/refs-internal.h |  28 ++---\n submodule.c          |  31 +++++\n submodule.h          |   1 +\n 7 files changed, 346 insertions(+), 252 deletions(-)\n\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311739","messageId":"20170216114818.6080-2-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 01/16] refs-internal.c: make files_log_ref_write() static","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:03Z","receivedAt":"2017-02-16T11:49:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\nbut probably never used outside refs-internal.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 +++\n refs/refs-internal.h | 4 ----\n 2 files changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex cdb6b8ff5..75565c3aa 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err);\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 33adbf93b..59e65958a 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -222,10 +222,6 @@ struct ref_transaction {\n \tenum ref_transaction_state state;\n };\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err);\n-\n /*\n  * Check for entries in extras that are within the specified\n  * directory, where dirname is a reference directory name including\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311740","messageId":"20170216114818.6080-3-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 02/16] files-backend: convert git_path() to strbuf_git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:04Z","receivedAt":"2017-02-16T11:49:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git_path() and friends are going to be killed in files-backend.c in near\nfuture. And because there's a risk with overwriting buffer in\ngit_path(), let's convert them all to strbuf_git_path(). We'll have\neasier time killing/converting strbuf_git_path() then because we won't\nhave to worry about memory management again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 111 +++++++++++++++++++++++++++++++++++++++++----------\n 1 file changed, 89 insertions(+), 22 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 75565c3aa..f0c878b92 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2169,6 +2169,8 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstatic int timeout_configured = 0;\n \tstatic int timeout_value = 1000;\n \tstruct packed_ref_cache *packed_ref_cache;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n \n \tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n \n@@ -2177,10 +2179,13 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tif (hold_lock_file_for_update_timeout(\n-\t\t\t    &packlock, git_path(\"packed-refs\"),\n-\t\t\t    flags, timeout_value) < 0)\n+\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n+\t\t\t\t\t\tflags, timeout_value);\n+\tstrbuf_release(&sb);\n+\tif (ret < 0)\n \t\treturn -1;\n+\n \t/*\n \t * Get the current packed-refs while holding the lock.  If the\n \t * packed-refs file has been modified since we last read it,\n@@ -2335,6 +2340,9 @@ static void try_remove_empty_parents(char *name)\n \tfor (q = p; *q; q++)\n \t\t;\n \twhile (1) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret;\n+\n \t\twhile (q > p && *q != '/')\n \t\t\tq--;\n \t\twhile (q > p && *(q-1) == '/')\n@@ -2342,7 +2350,10 @@ static void try_remove_empty_parents(char *name)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\t*q = '\\0';\n-\t\tif (rmdir(git_path(\"%s\", name)))\n+\t\tstrbuf_git_path(&sb, \"%s\", name);\n+\t\tret = rmdir(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\tif (ret)\n \t\t\tbreak;\n \t}\n }\n@@ -2431,7 +2442,11 @@ static int repack_without_refs(struct files_ref_store *refs,\n \t\treturn 0; /* no refname exists in packed refs */\n \n \tif (lock_packed_refs(refs, 0)) {\n-\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tunable_to_lock_message(sb.buf, errno, err);\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \tpacked = get_packed_refs(refs);\n@@ -2529,8 +2544,10 @@ static int rename_tmp_log(const char *newrefname)\n {\n \tint attempts_remaining = 4;\n \tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n \tint ret = -1;\n \n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n  retry:\n \tstrbuf_reset(&path);\n \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n@@ -2546,7 +2563,7 @@ static int rename_tmp_log(const char *newrefname)\n \t\tgoto out;\n \t}\n \n-\tif (rename(git_path(TMP_RENAMED_LOG), path.buf)) {\n+\tif (rename(tmp_renamed_log.buf, path.buf)) {\n \t\tif ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {\n \t\t\t/*\n \t\t\t * rename(a, b) when b is an existing\n@@ -2574,6 +2591,7 @@ static int rename_tmp_log(const char *newrefname)\n \tret = 0;\n out:\n \tstrbuf_release(&path);\n+\tstrbuf_release(&tmp_renamed_log);\n \treturn ret;\n }\n \n@@ -2614,9 +2632,15 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n+\tstruct strbuf sb_oldref = STRBUF_INIT;\n+\tstruct strbuf sb_newref = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n+\tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tlog = !lstat(sb_oldref.buf, &loginfo);\n+\tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n \n@@ -2630,7 +2654,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n+\tif (ret)\n \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n@@ -2709,13 +2738,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n+\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n+\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n+\tstrbuf_release(&sb_newref);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n \n \treturn 1;\n }\n@@ -3111,22 +3146,32 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n+\tint ret;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \n-\treturn !lstat(git_path(\"logs/%s\", refname), &st) &&\n-\t\tS_ISREG(st.st_mode);\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \n-\treturn remove_path(git_path(\"logs/%s\", refname));\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = remove_path(sb.buf);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)\n@@ -3181,7 +3226,9 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3287,7 +3334,9 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3369,12 +3418,15 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n {\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\titer->dir_iterator = dir_iterator_begin(git_path(\"logs\"));\n+\tstrbuf_git_path(&sb, \"logs\");\n+\titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n \n@@ -3843,8 +3895,13 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\tret = TRANSACTION_GENERIC_ERROR;\n \t\tgoto cleanup;\n \t}\n-\tfor_each_string_list_item(ref_to_delete, &refs_to_delete)\n-\t\tunlink_or_warn(git_path(\"logs/%s\", ref_to_delete->string));\n+\tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tunlink_or_warn(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t}\n \tclear_loose_ref_cache(refs);\n \n cleanup:\n@@ -4098,18 +4155,28 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"init_db\");\n \n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n+\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tadjust_shared_perm(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tadjust_shared_perm(sb.buf);\n \t}\n+\tstrbuf_release(&sb);\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311741","messageId":"20170216114818.6080-6-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 05/16] refs.c: share is_per_worktree_ref() to files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:07Z","receivedAt":"2017-02-16T11:49:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 6 ------\n refs/refs-internal.h | 6 ++++++\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 4f845798b..7a474198e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -489,12 +489,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n \treturn logs_found;\n }\n \n-static int is_per_worktree_ref(const char *refname)\n-{\n-\treturn !strcmp(refname, \"HEAD\") ||\n-\t\tstarts_with(refname, \"refs/bisect/\");\n-}\n-\n static int is_pseudoref_syntax(const char *refname)\n {\n \tconst char *c;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 59e65958a..f4aed49f5 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -651,4 +651,10 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    unsigned char *sha1, int *flags);\n \n+static inline int is_per_worktree_ref(const char *refname)\n+{\n+\treturn !strcmp(refname, \"HEAD\") ||\n+\t\tstarts_with(refname, \"refs/bisect/\");\n+}\n+\n #endif /* REFS_REFS_INTERNAL_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311742","messageId":"20170216114818.6080-7-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 06/16] refs-internal.h: correct is_per_worktree_ref()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:08Z","receivedAt":"2017-02-16T11:49:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"All refs outside refs/ directory is per-worktree, not just HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/refs-internal.h | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f4aed49f5..69d02b6ba 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -653,7 +653,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n \n static inline int is_per_worktree_ref(const char *refname)\n {\n-\treturn !strcmp(refname, \"HEAD\") ||\n+\treturn !starts_with(refname, \"refs/\") ||\n \t\tstarts_with(refname, \"refs/bisect/\");\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311743","messageId":"20170216114818.6080-4-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 03/16] files-backend: add files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:05Z","receivedAt":"2017-02-16T11:49:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This will be the replacement for both git_path() and git_path_submodule()\nin this file. The idea is backend takes a git path and use that,\noblivious of submodule, linked worktrees and such.\n\nThis is the middle step towards that. Eventually the \"submodule\" field\nin 'struct files_ref_store' should be replace by \"gitdir\". And a\ncompound ref_store is created to combine two files backends together,\none represents the shared refs in $GIT_COMMON_DIR, one per-worktree. At\nthat point, files_path() becomes a wrapper of strbuf_vaddf().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 18 ++++++++++++++++++\n 1 file changed, 18 insertions(+)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex f0c878b92..abb8a95e0 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -930,6 +930,24 @@ struct files_ref_store {\n /* Lock used for the main packed-refs file: */\n static struct lock_file packlock;\n \n+__attribute__((format (printf, 3, 4)))\n+static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n+\t\t       const char *fmt, ...)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tva_list vap;\n+\n+\tva_start(vap, fmt);\n+\tstrbuf_vaddf(&tmp, fmt, vap);\n+\tva_end(vap);\n+\tif (refs->submodule)\n+\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n+\t\t\t\t\t  \"%s\", tmp.buf);\n+\telse\n+\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\tstrbuf_release(&tmp);\n+}\n+\n /*\n  * Increment the reference count of *packed_refs.\n  */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311744","messageId":"20170216114818.6080-5-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 04/16] files-backend: replace *git_path*() with files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:06Z","receivedAt":"2017-02-16T11:49:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This centralizes all path rewriting of files-backend.c in one place so\nwe have easier time removing the path rewriting later. There could be\nsome hidden indirect git_path() though, I didn't audit the code to the\nbottom.\n\nSide note: set_worktree_head_symref() is a bad boy and should not be in\nfiles-backend.c (probably should not exist in the first place). But\nwe'll leave it there until we have better multi-worktree support in refs\nbefore we update it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 185 ++++++++++++++++++++++++++-------------------------\n 1 file changed, 94 insertions(+), 91 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex abb8a95e0..24f5bf7f1 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,7 +165,8 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n@@ -1178,12 +1179,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n {\n \tchar *packed_refs_file;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n-\tif (refs->submodule)\n-\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n-\t\t\t\t\t\t\t \"packed-refs\");\n-\telse\n-\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n+\tpacked_refs_file = strbuf_detach(&sb, NULL);\n \n \tif (refs->packed &&\n \t    !stat_validity_check(&refs->packed->validity, packed_refs_file))\n@@ -1249,10 +1248,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tsize_t path_baselen;\n \tint err = 0;\n \n-\tif (refs->submodule)\n-\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n-\telse\n-\t\tstrbuf_git_path(&path, \"%s\", dirname);\n+\tfiles_path(refs, &path, \"%s\", dirname);\n \tpath_baselen = path.len;\n \n \tif (err) {\n@@ -1394,10 +1390,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(&sb_path, refs->submodule, \"%s\", refname);\n-\telse\n-\t\tstrbuf_git_path(&sb_path, \"%s\", refname);\n+\tfiles_path(refs, &sb_path, \"%s\", refname);\n \n \tpath = sb_path.buf;\n \n@@ -1585,7 +1578,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -2052,7 +2045,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\tlock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n@@ -2197,7 +2190,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n \tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n \t\t\t\t\t\tflags, timeout_value);\n \tstrbuf_release(&sb);\n@@ -2343,7 +2336,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)\n  * Remove empty parents, but spare refs/ and immediate subdirs.\n  * Note: munges *name.\n  */\n-static void try_remove_empty_parents(char *name)\n+static void try_remove_empty_parents(struct files_ref_store *refs, char *name)\n {\n \tchar *p, *q;\n \tint i;\n@@ -2368,7 +2361,7 @@ static void try_remove_empty_parents(char *name)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\t*q = '\\0';\n-\t\tstrbuf_git_path(&sb, \"%s\", name);\n+\t\tfiles_path(refs, &sb, \"%s\", name);\n \t\tret = rmdir(sb.buf);\n \t\tstrbuf_release(&sb);\n \t\tif (ret)\n@@ -2377,7 +2370,7 @@ static void try_remove_empty_parents(char *name)\n }\n \n /* make sure nobody touched the ref, and unlink */\n-static void prune_ref(struct ref_to_prune *r)\n+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -2397,13 +2390,13 @@ static void prune_ref(struct ref_to_prune *r)\n \t}\n \tref_transaction_free(transaction);\n \tstrbuf_release(&err);\n-\ttry_remove_empty_parents(r->name);\n+\ttry_remove_empty_parents(refs, r->name);\n }\n \n-static void prune_refs(struct ref_to_prune *r)\n+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \twhile (r) {\n-\t\tprune_ref(r);\n+\t\tprune_ref(refs, r);\n \t\tr = r->next;\n \t}\n }\n@@ -2426,7 +2419,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n \tif (commit_packed_refs(refs))\n \t\tdie_errno(\"unable to overwrite old ref-pack file\");\n \n-\tprune_refs(cbdata.ref_to_prune);\n+\tprune_refs(refs, cbdata.ref_to_prune);\n \treturn 0;\n }\n \n@@ -2462,7 +2455,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tif (lock_packed_refs(refs, 0)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tfiles_path(refs, &sb, \"packed-refs\");\n \t\tunable_to_lock_message(sb.buf, errno, err);\n \t\tstrbuf_release(&sb);\n \t\treturn -1;\n@@ -2558,17 +2551,17 @@ static int files_delete_refs(struct ref_store *ref_store,\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-static int rename_tmp_log(const char *newrefname)\n+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n {\n \tint attempts_remaining = 4;\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n \tint ret = -1;\n \n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n  retry:\n \tstrbuf_reset(&path);\n-\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &path, \"logs/%s\", newrefname);\n \tswitch (safe_create_leading_directories_const(path.buf)) {\n \tcase SCLD_OK:\n \t\tbreak; /* success */\n@@ -2656,7 +2649,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n@@ -2672,8 +2665,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n \tstrbuf_release(&sb_oldref);\n \tstrbuf_release(&tmp_renamed_log);\n@@ -2700,7 +2693,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tstrbuf_git_path(&path, \"%s\", newrefname);\n+\t\t\tfiles_path(refs, &path, \"%s\", newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -2714,7 +2707,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t}\n \t}\n \n-\tif (log && rename_tmp_log(newrefname))\n+\tif (log && rename_tmp_log(refs, newrefname))\n \t\tgoto rollback;\n \n \tlogmoved = log;\n@@ -2756,12 +2749,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_newref, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n@@ -2817,11 +2810,13 @@ static int commit_ref(struct ref_lock *lock)\n  * should_autocreate_reflog returns non-zero.  Otherwise, create it\n  * regardless of the ref name.  Fill in *err and return -1 on failure.\n  */\n-static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)\n+static int log_ref_setup(struct files_ref_store *refs, const char *refname,\n+\t\t\t struct strbuf *logfile, struct strbuf *err,\n+\t\t\t int force_create)\n {\n \tint logfd, oflags = O_APPEND | O_WRONLY;\n \n-\tstrbuf_git_path(logfile, \"logs/%s\", refname);\n+\tfiles_path(refs, logfile, \"logs/%s\", refname);\n \tif (force_create || should_autocreate_reflog(refname)) {\n \t\tif (safe_create_leading_directories(logfile->buf) < 0) {\n \t\t\tstrbuf_addf(err, \"unable to create directory for '%s': \"\n@@ -2864,11 +2859,10 @@ static int files_create_reflog(struct ref_store *ref_store,\n {\n \tint ret;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"create_reflog\");\n-\n-\tret = log_ref_setup(refname, &sb, err, force_create);\n+\tret = log_ref_setup(refs, refname, &sb, err, force_create);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\n@@ -2899,7 +2893,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n+static int log_ref_write_1(struct files_ref_store *refs,\n+\t\t\t   const char *refname, const unsigned char *old_sha1,\n \t\t\t   const unsigned char *new_sha1, const char *msg,\n \t\t\t   struct strbuf *logfile, int flags,\n \t\t\t   struct strbuf *err)\n@@ -2909,7 +2904,7 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n \tif (log_all_ref_updates < 0)\n \t\tlog_all_ref_updates = !is_bare_repository();\n \n-\tresult = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);\n+\tresult = log_ref_setup(refs, refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);\n \n \tif (result)\n \t\treturn result;\n@@ -2933,21 +2928,23 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-static int log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int log_ref_write(struct files_ref_store *refs,\n+\t\t\t const char *refname, const unsigned char *old_sha1,\n \t\t\t const unsigned char *new_sha1, const char *msg,\n \t\t\t int flags, struct strbuf *err)\n {\n-\treturn files_log_ref_write(refname, old_sha1, new_sha1, msg, flags,\n-\t\t\t\t   err);\n+\treturn files_log_ref_write(refs, refname, old_sha1, new_sha1,\n+\t\t\t\t   msg, flags, err);\n }\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\tconst char *refname, const unsigned char *old_sha1,\n \t\t\tconst unsigned char *new_sha1, const char *msg,\n \t\t\tint flags, struct strbuf *err)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n-\tint ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,\n-\t\t\t\t  err);\n+\tint ret = log_ref_write_1(refs, refname, old_sha1, new_sha1, msg,\n+\t\t\t\t  &sb, flags, err);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\n@@ -3004,7 +3001,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \tfiles_assert_main_repository(refs, \"commit_ref_update\");\n \n \tclear_loose_ref_cache(refs);\n-\tif (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {\n+\tif (log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n+\t\t\t  sha1, logmsg, 0, err)) {\n \t\tchar *old_msg = strbuf_detach(err, NULL);\n \t\tstrbuf_addf(err, \"cannot update the ref '%s': %s\",\n \t\t\t    lock->ref_name, old_msg);\n@@ -3035,8 +3033,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n-\t\t\tif (log_ref_write(\"HEAD\", lock->old_oid.hash, sha1,\n-\t\t\t\t\t  logmsg, 0, &log_err)) {\n+\t\t\tif (log_ref_write(refs, \"HEAD\", lock->old_oid.hash,\n+\t\t\t\t\t  sha1, logmsg, 0, &log_err)) {\n \t\t\t\terror(\"%s\", log_err.buf);\n \t\t\t\tstrbuf_release(&log_err);\n \t\t\t}\n@@ -3068,23 +3066,26 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)\n \treturn ret;\n }\n \n-static void update_symref_reflog(struct ref_lock *lock, const char *refname,\n+static void update_symref_reflog(struct files_ref_store *refs,\n+\t\t\t\t struct ref_lock *lock, const char *refname,\n \t\t\t\t const char *target, const char *logmsg)\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n \tif (logmsg && !read_ref(target, new_sha1) &&\n-\t    log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {\n+\t    log_ref_write(refs, refname, lock->old_oid.hash,\n+\t\t\t  new_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t}\n }\n \n-static int create_symref_locked(struct ref_lock *lock, const char *refname,\n+static int create_symref_locked(struct files_ref_store *refs,\n+\t\t\t\tstruct ref_lock *lock, const char *refname,\n \t\t\t\tconst char *target, const char *logmsg)\n {\n \tif (prefer_symlink_refs && !create_ref_symlink(lock, target)) {\n-\t\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\t\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \t\treturn 0;\n \t}\n \n@@ -3092,7 +3093,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,\n \t\treturn error(\"unable to fdopen %s: %s\",\n \t\t\t     lock->lk->tempfile.filename.buf, strerror(errno));\n \n-\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \n \t/* no error check; commit_ref will check ferror */\n \tfprintf(lock->lk->tempfile.fp, \"ref: %s\\n\", target);\n@@ -3121,13 +3122,19 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\treturn -1;\n \t}\n \n-\tret = create_symref_locked(lock, refname, target, logmsg);\n+\tret = create_symref_locked(refs, lock, refname, target, logmsg);\n \tunlock_ref(lock);\n \treturn ret;\n }\n \n int set_worktree_head_symref(const char *gitdir, const char *target)\n {\n+\t/*\n+\t * FIXME: this obviously will not work well for future refs\n+\t * backends. This function needs to die.\n+\t */\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3154,7 +3161,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \tlock->lk = &head_lock;\n \tlock->ref_name = xstrdup(head_rel);\n \n-\tret = create_symref_locked(lock, head_rel, target, NULL);\n+\tret = create_symref_locked(refs, lock, head_rel, target, NULL);\n \n \tunlock_ref(lock); /* will free lock */\n \tstrbuf_release(&head_path);\n@@ -3164,14 +3171,13 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3180,13 +3186,12 @@ static int files_reflog_exists(struct ref_store *ref_store,\n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = remove_path(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3236,15 +3241,14 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     each_reflog_ent_fn fn,\n \t\t\t\t\t     void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n \tint ret = 0, at_tail = 1;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3345,14 +3349,13 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     const char *refname,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3434,15 +3437,14 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n \n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n-\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\tstrbuf_git_path(&sb, \"logs\");\n+\tfiles_path(refs, &sb, \"logs\");\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n@@ -3866,8 +3868,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t\tif (update->flags & REF_NEEDS_COMMIT ||\n \t\t    update->flags & REF_LOG_ONLY) {\n-\t\t\tif (log_ref_write(lock->ref_name, lock->old_oid.hash,\n-\t\t\t\t\t  update->new_sha1,\n+\t\t\tif (log_ref_write(refs, lock->ref_name,\n+\t\t\t\t\t  lock->old_oid.hash, update->new_sha1,\n \t\t\t\t\t  update->msg, update->flags, err)) {\n \t\t\t\tchar *old_msg = strbuf_detach(err, NULL);\n \n@@ -3916,7 +3918,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tfiles_path(refs, &sb, \"logs/%s\", ref_to_delete->string);\n \t\tunlink_or_warn(sb.buf);\n \t\tstrbuf_release(&sb);\n \t}\n@@ -4078,6 +4080,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \tint status = 0;\n \tint type;\n \tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tmemset(&cb, 0, sizeof(cb));\n \tcb.flags = flags;\n@@ -4102,7 +4105,8 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tlog_file = git_pathdup(\"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n+\tlog_file = strbuf_detach(&sb, NULL);\n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n \t\t/*\n \t\t * Even though holding $GIT_DIR/logs/$reflog.lock has\n@@ -4173,25 +4177,24 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"init_db\");\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tfiles_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n-\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tfiles_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tfiles_path(refs, &sb, \"refs/heads\");\n \t\tadjust_shared_perm(sb.buf);\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tfiles_path(refs, &sb, \"refs/tags\");\n \t\tadjust_shared_perm(sb.buf);\n \t}\n \tstrbuf_release(&sb);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311745","messageId":"20170216114818.6080-8-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 07/16] files-backend: remove the use of git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:09Z","receivedAt":"2017-02-16T11:49:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\ndeciding what goes where. The end goal is to pass $GIT_DIR only. A\nrefs \"view\" of a linked worktree is a logical ref store that combines\ntwo files backends together.\n\n(*) Not entirely true since strbuf_git_path_submodule() still does path\ntranslation underneath. But that's for another patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 24f5bf7f1..74e31d041 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -924,6 +924,9 @@ struct files_ref_store {\n \t */\n \tconst char *submodule;\n \n+\tstruct strbuf gitdir;\n+\tstruct strbuf gitcommondir;\n+\n \tstruct ref_entry *loose;\n \tstruct packed_ref_cache *packed;\n };\n@@ -937,6 +940,7 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n {\n \tstruct strbuf tmp = STRBUF_INIT;\n \tva_list vap;\n+\tconst char *ref;\n \n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n@@ -944,8 +948,12 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tif (refs->submodule)\n \t\tstrbuf_git_path_submodule(sb, refs->submodule,\n \t\t\t\t\t  \"%s\", tmp.buf);\n+\telse if (is_per_worktree_ref(tmp.buf) ||\n+\t\t (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n+\t\t  is_per_worktree_ref(ref)))\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n \telse\n-\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n \tstrbuf_release(&tmp);\n }\n \n@@ -1004,7 +1012,15 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\trefs->submodule = xstrdup_or_null(submodule);\n+\tstrbuf_init(&refs->gitdir, 0);\n+\tstrbuf_init(&refs->gitcommondir, 0);\n+\n+\tif (submodule) {\n+\t\trefs->submodule = xstrdup(submodule);\n+\t} else {\n+\t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n+\t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n+\t}\n \n \treturn ref_store;\n }\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311746","messageId":"20170216114818.6080-9-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 08/16] refs.c: introduce get_main_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:10Z","receivedAt":"2017-02-16T11:49:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 7a474198e..10994d992 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1445,15 +1445,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n+static struct ref_store *get_main_ref_store(void)\n+{\n+\tstruct ref_store *refs;\n+\n+\tif (main_ref_store)\n+\t\treturn main_ref_store;\n+\n+\trefs = ref_store_init(NULL);\n+\treturn refs;\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n-\t\trefs = lookup_ref_store(NULL);\n-\n-\t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL);\n+\t\treturn get_main_ref_store();\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311747","messageId":"20170216114818.6080-10-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 09/16] refs: rename lookup_ref_store() to lookup_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:11Z","receivedAt":"2017-02-16T11:49:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With get_main_ref_store() being used inside get_ref_store(),\nlookup_ref_store() is only used for submodule code path. Rename to\nreflect that and delete dead code.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 13 ++++---------\n 1 file changed, 4 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 10994d992..ea13a5b86 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1384,17 +1384,13 @@ static struct ref_store *main_ref_store;\n static struct hashmap submodule_ref_stores;\n \n /*\n- * Return the ref_store instance for the specified submodule (or the\n- * main repository if submodule is NULL). If that ref_store hasn't\n- * been initialized yet, return NULL.\n+ * Return the ref_store instance for the specified submodule. If that\n+ * ref_store hasn't been initialized yet, return NULL.\n  */\n-static struct ref_store *lookup_ref_store(const char *submodule)\n+static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n {\n \tstruct submodule_hash_entry *entry;\n \n-\tif (!submodule)\n-\t\treturn main_ref_store;\n-\n \tif (!submodule_ref_stores.tablesize)\n \t\t/* It's initialized on demand in register_ref_store(). */\n \t\treturn NULL;\n@@ -1463,7 +1459,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n \t} else {\n-\t\trefs = lookup_ref_store(submodule);\n+\t\trefs = lookup_submodule_ref_store(submodule);\n \n \t\tif (!refs) {\n \t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1474,7 +1470,6 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\t\tstrbuf_release(&submodule_sb);\n \t\t}\n \t}\n-\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311748","messageId":"20170216114818.6080-11-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 10/16] refs.c: flatten get_ref_store() a bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:12Z","receivedAt":"2017-02-16T11:49:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This helps the future changes in this code. And because get_ref_store()\nis destined to become get_submodule_ref_store(), the \"get main store\"\ncode path will be removed eventually. After this the patch to delete\nthat code will be cleaner.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 19 +++++++++----------\n 1 file changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex ea13a5b86..76a0e7b5a 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1454,22 +1454,21 @@ static struct ref_store *get_main_ref_store(void)\n \n struct ref_store *get_ref_store(const char *submodule)\n {\n+\tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n-\t} else {\n-\t\trefs = lookup_submodule_ref_store(submodule);\n+\t}\n \n-\t\tif (!refs) {\n-\t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n+\trefs = lookup_submodule_ref_store(submodule);\n+\tif (refs)\n+\t\treturn refs;\n \n-\t\t\tstrbuf_addstr(&submodule_sb, submodule);\n-\t\t\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\t\t\trefs = ref_store_init(submodule);\n-\t\t\tstrbuf_release(&submodule_sb);\n-\t\t}\n-\t}\n+\tstrbuf_addstr(&submodule_sb, submodule);\n+\tif (is_nonbare_repository_dir(&submodule_sb))\n+\t\trefs = ref_store_init(submodule);\n+\tstrbuf_release(&submodule_sb);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311749","messageId":"20170216114818.6080-12-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 11/16] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:13Z","receivedAt":"2017-02-16T11:50:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the last function in this code (besides public API) that takes\nsubmodule argument and handles both main/submodule cases. Break it down,\nmove main store registration in get_main_ref_store() and keep the rest\nin register_submodule_ref_store().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 36 +++++++++++++++++++-----------------\n 1 file changed, 19 insertions(+), 17 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 76a0e7b5a..55a80a83d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1402,25 +1402,19 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n \n /*\n  * Register the specified ref_store to be the one that should be used\n- * for submodule (or the main repository if submodule is NULL). It is\n- * a fatal error to call this function twice for the same submodule.\n+ * for submodule. It is a fatal error to call this function twice for\n+ * the same submodule.\n  */\n-static void register_ref_store(struct ref_store *refs, const char *submodule)\n+static void register_submodule_ref_store(struct ref_store *refs,\n+\t\t\t\t\t const char *submodule)\n {\n-\tif (!submodule) {\n-\t\tif (main_ref_store)\n-\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n-\n-\t\tmain_ref_store = refs;\n-\t} else {\n-\t\tif (!submodule_ref_stores.tablesize)\n-\t\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n+\tif (!submodule_ref_stores.tablesize)\n+\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n \n-\t\tif (hashmap_put(&submodule_ref_stores,\n-\t\t\t\talloc_submodule_hash_entry(submodule, refs)))\n-\t\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n-\t\t\t    submodule);\n-\t}\n+\tif (hashmap_put(&submodule_ref_stores,\n+\t\t\talloc_submodule_hash_entry(submodule, refs)))\n+\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n+\t\t    submodule);\n }\n \n /*\n@@ -1437,7 +1431,6 @@ static struct ref_store *ref_store_init(const char *submodule)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n \trefs = be->init(submodule);\n-\tregister_ref_store(refs, submodule);\n \treturn refs;\n }\n \n@@ -1449,6 +1442,12 @@ static struct ref_store *get_main_ref_store(void)\n \t\treturn main_ref_store;\n \n \trefs = ref_store_init(NULL);\n+\tif (refs) {\n+\t\tif (main_ref_store)\n+\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n+\n+\t\tmain_ref_store = refs;\n+\t}\n \treturn refs;\n }\n \n@@ -1469,6 +1468,9 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (is_nonbare_repository_dir(&submodule_sb))\n \t\trefs = ref_store_init(submodule);\n \tstrbuf_release(&submodule_sb);\n+\n+\tif (refs)\n+\t\tregister_submodule_ref_store(refs, submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311750","messageId":"20170216114818.6080-13-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 12/16] refs.c: make get_main_ref_store() public and use it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:14Z","receivedAt":"2017-02-16T11:50:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"get_ref_store() will soon be renamed to get_submodule_ref_store().\nTogether with future get_worktree_ref_store(), the three functions\nprovide an appropriate ref store for different operation modes. New APIs\nwill be added to operate directly on ref stores.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 36 ++++++++++++++++++------------------\n refs.h |  2 ++\n 2 files changed, 20 insertions(+), 18 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 55a80a83d..25f657a6f 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1303,7 +1303,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n /* backend functions */\n int refs_init_db(struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->init_db(refs, err);\n }\n@@ -1311,7 +1311,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_ref_store(NULL), refname,\n+\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1434,7 +1434,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n-static struct ref_store *get_main_ref_store(void)\n+struct ref_store *get_main_ref_store(void)\n {\n \tstruct ref_store *refs;\n \n@@ -1483,14 +1483,14 @@ void base_ref_store_init(struct ref_store *refs,\n /* backend functions */\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->pack_refs(refs, flags);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->peel_ref(refs, refname, sha1);\n }\n@@ -1498,7 +1498,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n \t\t\t\t       logmsg);\n@@ -1507,7 +1507,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1517,14 +1517,14 @@ int verify_refname_available(const char *refname,\n \t\t\t     const struct string_list *skip,\n \t\t\t     struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1535,7 +1535,7 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n \t\t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n@@ -1544,14 +1544,14 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_exists(refs, refname);\n }\n@@ -1559,14 +1559,14 @@ int reflog_exists(const char *refname)\n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_reflog(refs, refname);\n }\n@@ -1578,7 +1578,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n \t\t\t\t       prepare_fn, should_prune_fn,\n@@ -1588,21 +1588,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\ndiff --git a/refs.h b/refs.h\nindex 694784391..f803528fc 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -553,4 +553,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \n int ref_storage_backend_exists(const char *name);\n \n+struct ref_store *get_main_ref_store(void);\n+\n #endif /* REFS_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311751","messageId":"20170216114818.6080-14-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 13/16] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:15Z","receivedAt":"2017-02-16T11:50:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"refs is learning to avoid path rewriting that is done by\nstrbuf_git_path_submodule(). Factor out this code so it could be reused\nby refs*\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n path.c      | 34 +++++++---------------------------\n submodule.c | 31 +++++++++++++++++++++++++++++++\n submodule.h |  1 +\n 3 files changed, 39 insertions(+), 27 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex efcedafba..3451d2916 100644\n--- a/path.c\n+++ b/path.c\n@@ -475,35 +475,16 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n static int do_submodule_path(struct strbuf *buf, const char *path,\n \t\t\t     const char *fmt, va_list args)\n {\n-\tconst char *git_dir;\n \tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n \tstruct strbuf git_submodule_dir = STRBUF_INIT;\n-\tconst struct submodule *sub;\n-\tint err = 0;\n+\tint ret;\n \n-\tstrbuf_addstr(buf, path);\n-\tstrbuf_complete(buf, '/');\n-\tstrbuf_addstr(buf, \".git\");\n-\n-\tgit_dir = read_gitfile(buf->buf);\n-\tif (git_dir) {\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, git_dir);\n-\t}\n-\tif (!is_git_directory(buf->buf)) {\n-\t\tgitmodules_config();\n-\t\tsub = submodule_from_path(null_sha1, path);\n-\t\tif (!sub) {\n-\t\t\terr = SUBMODULE_PATH_ERR_NOT_CONFIGURED;\n-\t\t\tgoto cleanup;\n-\t\t}\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n-\t}\n-\n-\tstrbuf_addch(buf, '/');\n-\tstrbuf_addbuf(&git_submodule_dir, buf);\n+\tret = submodule_to_gitdir(&git_submodule_dir, path);\n+\tif (ret)\n+\t\tgoto cleanup;\n \n+\tstrbuf_complete(&git_submodule_dir, '/');\n+\tstrbuf_addbuf(buf, &git_submodule_dir);\n \tstrbuf_vaddf(buf, fmt, args);\n \n \tif (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))\n@@ -514,8 +495,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,\n cleanup:\n \tstrbuf_release(&git_submodule_dir);\n \tstrbuf_release(&git_submodule_common_dir);\n-\n-\treturn err;\n+\treturn ret;\n }\n \n char *git_pathdup_submodule(const char *path, const char *fmt, ...)\ndiff --git a/submodule.c b/submodule.c\nindex ece17315d..3ce589d55 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1335,3 +1335,34 @@ void prepare_submodule_repo_env(struct argv_array *out)\n \t}\n \targv_array_push(out, \"GIT_DIR=.git\");\n }\n+\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n+{\n+\tconst struct submodule *sub;\n+\tconst char *git_dir;\n+\tint ret = 0;\n+\n+\tstrbuf_reset(buf);\n+\tstrbuf_addstr(buf, submodule);\n+\tstrbuf_complete(buf, '/');\n+\tstrbuf_addstr(buf, \".git\");\n+\n+\tgit_dir = read_gitfile(buf->buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, git_dir);\n+\t}\n+\tif (!is_git_directory(buf->buf)) {\n+\t\tgitmodules_config();\n+\t\tsub = submodule_from_path(null_sha1, submodule);\n+\t\tif (!sub) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n+\t}\n+\n+cleanup:\n+\treturn ret;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex 23d76682b..2728494ce 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -70,6 +70,7 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n \t\t\t\t    int dry_run);\n void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n int parallel_submodules(void);\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n \n /*\n  * Prepare the \"env_array\" parameter of a \"struct child_process\" for executing\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311752","messageId":"20170216114818.6080-15-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 14/16] refs: move submodule code out of files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:16Z","receivedAt":"2017-02-16T11:50:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend is now initialized with a $GIT_DIR. Converting a submodule\npath to where real submodule gitdir is located is done in get_ref_store().\n\nThe new code in init_submodule_ref_store() is basically a copy of\nstrbuf_git_path_submodule().\n\nThis gives a slight performance improvement for submodules since we\ndon't convert submodule path to gitdir at every backend call like\nbefore. We pay that once at ref-store creation.\n\nMore cleanup in files_downcast() follows shortly. It's separate to keep\nnoises from this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 18 +++++++++++++-----\n refs/files-backend.c | 26 ++++++++------------------\n refs/refs-internal.h |  6 +++---\n 3 files changed, 24 insertions(+), 26 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 25f657a6f..58ac9a2a8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -9,6 +9,7 @@\n #include \"refs/refs-internal.h\"\n #include \"object.h\"\n #include \"tag.h\"\n+#include \"submodule.h\"\n \n /*\n  * List of all available backends\n@@ -1419,9 +1420,9 @@ static void register_submodule_ref_store(struct ref_store *refs,\n \n /*\n  * Create, record, and return a ref_store instance for the specified\n- * submodule (or the main repository if submodule is NULL).\n+ * gitdir (or the main repository if gitdir is NULL).\n  */\n-static struct ref_store *ref_store_init(const char *submodule)\n+static struct ref_store *ref_store_init(const char *gitdir)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1430,7 +1431,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(submodule);\n+\trefs = be->init(gitdir);\n \treturn refs;\n }\n \n@@ -1455,6 +1456,7 @@ struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n+\tint ret;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n@@ -1465,8 +1467,14 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn refs;\n \n \tstrbuf_addstr(&submodule_sb, submodule);\n-\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\trefs = ref_store_init(submodule);\n+\tret = is_nonbare_repository_dir(&submodule_sb);\n+\tstrbuf_release(&submodule_sb);\n+\tif (!ret)\n+\t\treturn refs;\n+\n+\tret = submodule_to_gitdir(&submodule_sb, submodule);\n+\tif (!ret)\n+\t\trefs = ref_store_init(submodule_sb.buf);\n \tstrbuf_release(&submodule_sb);\n \n \tif (refs)\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 74e31d041..e8946e638 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -917,13 +917,6 @@ struct packed_ref_cache {\n struct files_ref_store {\n \tstruct ref_store base;\n \n-\t/*\n-\t * The name of the submodule represented by this object, or\n-\t * NULL if it represents the main repository's reference\n-\t * store:\n-\t */\n-\tconst char *submodule;\n-\n \tstruct strbuf gitdir;\n \tstruct strbuf gitcommondir;\n \n@@ -945,12 +938,9 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n \tva_end(vap);\n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n-\t\t\t\t\t  \"%s\", tmp.buf);\n-\telse if (is_per_worktree_ref(tmp.buf) ||\n-\t\t (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n-\t\t  is_per_worktree_ref(ref)))\n+\tif (is_per_worktree_ref(tmp.buf) ||\n+\t    (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n+\t     is_per_worktree_ref(ref)))\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n \telse\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n@@ -1005,7 +995,7 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *submodule)\n+static struct ref_store *files_ref_store_create(const char *gitdir)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n@@ -1015,8 +1005,9 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \tstrbuf_init(&refs->gitdir, 0);\n \tstrbuf_init(&refs->gitcommondir, 0);\n \n-\tif (submodule) {\n-\t\trefs->submodule = xstrdup(submodule);\n+\tif (gitdir) {\n+\t\tstrbuf_addstr(&refs->gitdir, gitdir);\n+\t\tget_common_dir_noenv(&refs->gitcommondir, gitdir);\n \t} else {\n \t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n \t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n@@ -1032,8 +1023,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\tif (refs->submodule)\n-\t\tdie(\"BUG: %s called for a submodule\", caller);\n+\t/* This function is to be deleted in the next patch */\n }\n \n /*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 69d02b6ba..d7112770d 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -476,12 +476,12 @@ struct ref_store;\n /* refs backends */\n \n /*\n- * Initialize the ref_store for the specified submodule, or for the\n- * main repository if submodule == NULL. These functions should call\n+ * Initialize the ref_store for the specified gitdir, or for the\n+ * current repository if gitdir == NULL. These functions should call\n  * base_ref_store_init() to initialize the shared part of the\n  * ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *submodule);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311753","messageId":"20170216114818.6080-16-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 15/16] files-backend: remove submodule_allowed from files_downcast()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:17Z","receivedAt":"2017-02-16T11:50:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since submodule or not is irrelevant to files-backend after the last\npatch, remove the parameter from files_downcast() and kill\nfiles_assert_main_repository().\n\nPS. This implies that all ref operations are allowed for submodules. But\nwe may need to look more closely to see if that's really true...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 70 ++++++++++++++++------------------------------------\n 1 file changed, 21 insertions(+), 49 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex e8946e638..d9fc29d8d 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1017,24 +1017,13 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n }\n \n /*\n- * Die if refs is for a submodule (i.e., not for the main repository).\n- * caller is used in any necessary error messages.\n- */\n-static void files_assert_main_repository(struct files_ref_store *refs,\n-\t\t\t\t\t const char *caller)\n-{\n-\t/* This function is to be deleted in the next patch */\n-}\n-\n-/*\n  * Downcast ref_store to files_ref_store. Die if ref_store is not a\n  * files_ref_store. If submodule_allowed is not true, then also die if\n  * files_ref_store is for a submodule (i.e., not for the main\n  * repository). caller is used in any necessary error messages.\n  */\n-static struct files_ref_store *files_downcast(\n-\t\tstruct ref_store *ref_store, int submodule_allowed,\n-\t\tconst char *caller)\n+static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n+\t\t\t\t\t      const char *caller)\n {\n \tstruct files_ref_store *refs;\n \n@@ -1044,9 +1033,6 @@ static struct files_ref_store *files_downcast(\n \n \trefs = (struct files_ref_store *)ref_store;\n \n-\tif (!submodule_allowed)\n-\t\tfiles_assert_main_repository(refs, caller);\n-\n \treturn refs;\n }\n \n@@ -1382,7 +1368,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t\t\t      struct strbuf *referent, unsigned int *type)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"read_raw_ref\");\n+\t\tfiles_downcast(ref_store, \"read_raw_ref\");\n \tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n \tconst char *path;\n@@ -1575,7 +1561,6 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \tint ret = TRANSACTION_GENERIC_ERROR;\n \n \tassert(err);\n-\tfiles_assert_main_repository(refs, \"lock_raw_ref\");\n \n \t*type = 0;\n \n@@ -1799,7 +1784,7 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n static int files_peel_ref(struct ref_store *ref_store,\n \t\t\t  const char *refname, unsigned char *sha1)\n {\n-\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"peel_ref\");\n \tint flag;\n \tunsigned char base[20];\n \n@@ -1908,7 +1893,7 @@ static struct ref_iterator *files_ref_iterator_begin(\n \t\tconst char *prefix, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"ref_iterator_begin\");\n \tstruct ref_dir *loose_dir, *packed_dir;\n \tstruct ref_iterator *loose_iter, *packed_iter;\n \tstruct files_ref_iterator *iter;\n@@ -2041,7 +2026,6 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tint attempts_remaining = 3;\n \tint resolved;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_sha1_basic\");\n \tassert(err);\n \n \tlock = xcalloc(1, sizeof(struct ref_lock));\n@@ -2189,8 +2173,6 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n-\n \tif (!timeout_configured) {\n \t\tgit_config_get_int(\"core.packedrefstimeout\", &timeout_value);\n \t\ttimeout_configured = 1;\n@@ -2230,8 +2212,6 @@ static int commit_packed_refs(struct files_ref_store *refs)\n \tint save_errno = 0;\n \tFILE *out;\n \n-\tfiles_assert_main_repository(refs, \"commit_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \n@@ -2263,8 +2243,6 @@ static void rollback_packed_refs(struct files_ref_store *refs)\n \tstruct packed_ref_cache *packed_ref_cache =\n \t\tget_packed_ref_cache(refs);\n \n-\tfiles_assert_main_repository(refs, \"rollback_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \trollback_lock_file(packed_ref_cache->lock);\n@@ -2410,7 +2388,7 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"pack_refs\");\n+\t\tfiles_downcast(ref_store, \"pack_refs\");\n \tstruct pack_refs_cb_data cbdata;\n \n \tmemset(&cbdata, 0, sizeof(cbdata));\n@@ -2443,7 +2421,6 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tstruct string_list_item *refname;\n \tint ret, needs_repacking = 0, removed = 0;\n \n-\tfiles_assert_main_repository(refs, \"repack_without_refs\");\n \tassert(err);\n \n \t/* Look for a packed ref */\n@@ -2511,7 +2488,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \t\t\t     struct string_list *refnames, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_refs\");\n+\t\tfiles_downcast(ref_store, \"delete_refs\");\n \tstruct strbuf err = STRBUF_INIT;\n \tint i, result = 0;\n \n@@ -2619,7 +2596,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,\n \t\t\t\t\t  struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"verify_refname_available\");\n+\t\tfiles_downcast(ref_store, \"verify_refname_available\");\n \tstruct ref_dir *packed_refs = get_packed_refs(refs);\n \tstruct ref_dir *loose_refs = get_loose_refs(refs);\n \n@@ -2644,7 +2621,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"rename_ref\");\n+\t\tfiles_downcast(ref_store, \"rename_ref\");\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n@@ -2866,7 +2843,7 @@ static int files_create_reflog(struct ref_store *ref_store,\n \tint ret;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n+\t\tfiles_downcast(ref_store, \"create_reflog\");\n \n \tret = log_ref_setup(refs, refname, &sb, err, force_create);\n \tstrbuf_release(&sb);\n@@ -3004,8 +2981,6 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\t\t     const unsigned char *sha1, const char *logmsg,\n \t\t\t     struct strbuf *err)\n {\n-\tfiles_assert_main_repository(refs, \"commit_ref_update\");\n-\n \tclear_loose_ref_cache(refs);\n \tif (log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n \t\t\t  sha1, logmsg, 0, err)) {\n@@ -3114,7 +3089,7 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\t\t       const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_symref\");\n+\t\tfiles_downcast(ref_store, \"create_symref\");\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_lock *lock;\n \tint ret;\n@@ -3140,7 +3115,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3178,7 +3153,7 @@ static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n+\t\tfiles_downcast(ref_store, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n@@ -3193,7 +3168,7 @@ static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n+\t\tfiles_downcast(ref_store, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n@@ -3248,7 +3223,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n@@ -3356,7 +3331,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n@@ -3444,7 +3419,7 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -3656,8 +3631,6 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \tint ret;\n \tstruct ref_lock *lock;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_for_update\");\n-\n \tif ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))\n \t\tupdate->flags |= REF_DELETING;\n \n@@ -3782,7 +3755,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list refs_to_delete = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *ref_to_delete;\n@@ -3956,7 +3929,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"initial_ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"initial_ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list affected_refnames = STRING_LIST_INIT_NODUP;\n \n@@ -4078,7 +4051,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\t       void *policy_cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_expire\");\n+\t\tfiles_downcast(ref_store, \"reflog_expire\");\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n@@ -4183,8 +4156,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n-\tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"init_db\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n \t/*\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311754","messageId":"20170216114818.6080-17-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v2 16/16] refs: rename get_ref_store() to get_submodule_ref_store() and make it public","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-16T11:48:18Z","receivedAt":"2017-02-16T11:50:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is intended to replace *_submodule() refs API. It provides\na ref store for a specific submodule, which can be operated on by a new\nset of refs API.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 12 ++++++++----\n refs.h               | 11 +++++++++++\n refs/files-backend.c |  2 +-\n refs/refs-internal.h | 12 ------------\n 4 files changed, 20 insertions(+), 17 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 58ac9a2a8..e7206a420 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1160,7 +1160,7 @@ int head_ref(each_ref_fn fn, void *cb_data)\n static int do_for_each_ref(const char *submodule, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(submodule);\n+\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1333,10 +1333,10 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \t\t/* We need to strip off one or more trailing slashes */\n \t\tchar *stripped = xmemdupz(submodule, len);\n \n-\t\trefs = get_ref_store(stripped);\n+\t\trefs = get_submodule_ref_store(stripped);\n \t\tfree(stripped);\n \t} else {\n-\t\trefs = get_ref_store(submodule);\n+\t\trefs = get_submodule_ref_store(submodule);\n \t}\n \n \tif (!refs)\n@@ -1452,13 +1452,17 @@ struct ref_store *get_main_ref_store(void)\n \treturn refs;\n }\n \n-struct ref_store *get_ref_store(const char *submodule)\n+struct ref_store *get_submodule_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \tint ret;\n \n \tif (!submodule || !*submodule) {\n+\t\t/*\n+\t\t * FIXME: This case is ideally not allowed. But that\n+\t\t * can't happen until we clean up all the callers.\n+\t\t */\n \t\treturn get_main_ref_store();\n \t}\n \ndiff --git a/refs.h b/refs.h\nindex f803528fc..1287ba59c 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -554,5 +554,16 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int ref_storage_backend_exists(const char *name);\n \n struct ref_store *get_main_ref_store(void);\n+/*\n+ * Return the ref_store instance for the specified submodule. For the\n+ * main repository, use submodule==NULL; such a call cannot fail. For\n+ * a submodule, the submodule must exist and be a nonbare repository,\n+ * otherwise return NULL. If the requested reference store has not yet\n+ * been initialized, initialize it first.\n+ *\n+ * For backwards compatibility, submodule==\"\" is treated the same as\n+ * submodule==NULL.\n+ */\n+struct ref_store *get_submodule_ref_store(const char *submodule);\n \n #endif /* REFS_H */\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex d9fc29d8d..685ea5c14 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3115,7 +3115,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex d7112770d..cb6882779 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -634,18 +634,6 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-/*\n- * Return the ref_store instance for the specified submodule. For the\n- * main repository, use submodule==NULL; such a call cannot fail. For\n- * a submodule, the submodule must exist and be a nonbare repository,\n- * otherwise return NULL. If the requested reference store has not yet\n- * been initialized, initialize it first.\n- *\n- * For backwards compatibility, submodule==\"\" is treated the same as\n- * submodule==NULL.\n- */\n-struct ref_store *get_ref_store(const char *submodule);\n-\n const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311820","messageId":"CAGZ79kYwCK=WRbMRoKNzecL8XtxLriJxVYzJBixopmOa=hqtcg@mail.gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"Re: [PATCH v2 00/16] Remove submodule from files-backend.c","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-16T22:55:14Z","receivedAt":"2017-02-16T22:55:38Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This series looks good to me.\n\nThanks,\nStefan\n"},{"id":"311862","messageId":"20170217140436.17336-2-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 01/16] refs-internal.c: make files_log_ref_write() static","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:21Z","receivedAt":"2017-02-17T14:05:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\nbut probably never used outside refs-internal.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 +++\n refs/refs-internal.h | 4 ----\n 2 files changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex cdb6b8ff5..75565c3aa 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err);\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 33adbf93b..59e65958a 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -222,10 +222,6 @@ struct ref_transaction {\n \tenum ref_transaction_state state;\n };\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err);\n-\n /*\n  * Check for entries in extras that are within the specified\n  * directory, where dirname is a reference directory name including\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311863","messageId":"20170217140436.17336-3-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 02/16] files-backend: convert git_path() to strbuf_git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:22Z","receivedAt":"2017-02-17T14:05:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git_path() and friends are going to be killed in files-backend.c in near\nfuture. And because there's a risk with overwriting buffer in\ngit_path(), let's convert them all to strbuf_git_path(). We'll have\neasier time killing/converting strbuf_git_path() then because we won't\nhave to worry about memory management again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 111 +++++++++++++++++++++++++++++++++++++++++----------\n 1 file changed, 89 insertions(+), 22 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 75565c3aa..f0c878b92 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2169,6 +2169,8 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstatic int timeout_configured = 0;\n \tstatic int timeout_value = 1000;\n \tstruct packed_ref_cache *packed_ref_cache;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n \n \tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n \n@@ -2177,10 +2179,13 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tif (hold_lock_file_for_update_timeout(\n-\t\t\t    &packlock, git_path(\"packed-refs\"),\n-\t\t\t    flags, timeout_value) < 0)\n+\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n+\t\t\t\t\t\tflags, timeout_value);\n+\tstrbuf_release(&sb);\n+\tif (ret < 0)\n \t\treturn -1;\n+\n \t/*\n \t * Get the current packed-refs while holding the lock.  If the\n \t * packed-refs file has been modified since we last read it,\n@@ -2335,6 +2340,9 @@ static void try_remove_empty_parents(char *name)\n \tfor (q = p; *q; q++)\n \t\t;\n \twhile (1) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret;\n+\n \t\twhile (q > p && *q != '/')\n \t\t\tq--;\n \t\twhile (q > p && *(q-1) == '/')\n@@ -2342,7 +2350,10 @@ static void try_remove_empty_parents(char *name)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\t*q = '\\0';\n-\t\tif (rmdir(git_path(\"%s\", name)))\n+\t\tstrbuf_git_path(&sb, \"%s\", name);\n+\t\tret = rmdir(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\tif (ret)\n \t\t\tbreak;\n \t}\n }\n@@ -2431,7 +2442,11 @@ static int repack_without_refs(struct files_ref_store *refs,\n \t\treturn 0; /* no refname exists in packed refs */\n \n \tif (lock_packed_refs(refs, 0)) {\n-\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tunable_to_lock_message(sb.buf, errno, err);\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \tpacked = get_packed_refs(refs);\n@@ -2529,8 +2544,10 @@ static int rename_tmp_log(const char *newrefname)\n {\n \tint attempts_remaining = 4;\n \tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n \tint ret = -1;\n \n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n  retry:\n \tstrbuf_reset(&path);\n \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n@@ -2546,7 +2563,7 @@ static int rename_tmp_log(const char *newrefname)\n \t\tgoto out;\n \t}\n \n-\tif (rename(git_path(TMP_RENAMED_LOG), path.buf)) {\n+\tif (rename(tmp_renamed_log.buf, path.buf)) {\n \t\tif ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {\n \t\t\t/*\n \t\t\t * rename(a, b) when b is an existing\n@@ -2574,6 +2591,7 @@ static int rename_tmp_log(const char *newrefname)\n \tret = 0;\n out:\n \tstrbuf_release(&path);\n+\tstrbuf_release(&tmp_renamed_log);\n \treturn ret;\n }\n \n@@ -2614,9 +2632,15 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n+\tstruct strbuf sb_oldref = STRBUF_INIT;\n+\tstruct strbuf sb_newref = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n+\tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tlog = !lstat(sb_oldref.buf, &loginfo);\n+\tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n \n@@ -2630,7 +2654,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n+\tif (ret)\n \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n@@ -2709,13 +2738,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n+\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n+\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n+\tstrbuf_release(&sb_newref);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n \n \treturn 1;\n }\n@@ -3111,22 +3146,32 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n+\tint ret;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \n-\treturn !lstat(git_path(\"logs/%s\", refname), &st) &&\n-\t\tS_ISREG(st.st_mode);\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \n-\treturn remove_path(git_path(\"logs/%s\", refname));\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = remove_path(sb.buf);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)\n@@ -3181,7 +3226,9 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3287,7 +3334,9 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3369,12 +3418,15 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n {\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\titer->dir_iterator = dir_iterator_begin(git_path(\"logs\"));\n+\tstrbuf_git_path(&sb, \"logs\");\n+\titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n \n@@ -3843,8 +3895,13 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\tret = TRANSACTION_GENERIC_ERROR;\n \t\tgoto cleanup;\n \t}\n-\tfor_each_string_list_item(ref_to_delete, &refs_to_delete)\n-\t\tunlink_or_warn(git_path(\"logs/%s\", ref_to_delete->string));\n+\tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tunlink_or_warn(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t}\n \tclear_loose_ref_cache(refs);\n \n cleanup:\n@@ -4098,18 +4155,28 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"init_db\");\n \n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n+\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tadjust_shared_perm(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tadjust_shared_perm(sb.buf);\n \t}\n+\tstrbuf_release(&sb);\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311864","messageId":"20170217140436.17336-4-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 03/16] files-backend: add files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:23Z","receivedAt":"2017-02-17T14:05:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This will be the replacement for both git_path() and git_path_submodule()\nin this file. The idea is backend takes a git path and use that,\noblivious of submodule, linked worktrees and such.\n\nThis is the middle step towards that. Eventually the \"submodule\" field\nin 'struct files_ref_store' should be replace by \"gitdir\". And a\ncompound ref_store is created to combine two files backends together,\none represents the shared refs in $GIT_COMMON_DIR, one per-worktree. At\nthat point, files_path() becomes a wrapper of strbuf_vaddf().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 18 ++++++++++++++++++\n 1 file changed, 18 insertions(+)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex f0c878b92..abb8a95e0 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -930,6 +930,24 @@ struct files_ref_store {\n /* Lock used for the main packed-refs file: */\n static struct lock_file packlock;\n \n+__attribute__((format (printf, 3, 4)))\n+static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n+\t\t       const char *fmt, ...)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tva_list vap;\n+\n+\tva_start(vap, fmt);\n+\tstrbuf_vaddf(&tmp, fmt, vap);\n+\tva_end(vap);\n+\tif (refs->submodule)\n+\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n+\t\t\t\t\t  \"%s\", tmp.buf);\n+\telse\n+\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\tstrbuf_release(&tmp);\n+}\n+\n /*\n  * Increment the reference count of *packed_refs.\n  */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311865","messageId":"20170217140436.17336-1-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"[PATCH v3 00/16] Remove submodule from files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:20Z","receivedAt":"2017-02-17T14:05:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v3 only changes 07/16 but it's kinda important because I broke\npacked-refs. packed-refs' path became $GIT_DIR/packed-refs instead of\n$GIT_COMMON_DIR/packed-refs and as a result the majority of refs will\ndisappear in linked worktrees. Interdiff\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 685ea5c14..82be3f90f 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -938,9 +938,11 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n \tva_end(vap);\n-\tif (is_per_worktree_ref(tmp.buf) ||\n-\t    (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n-\t     is_per_worktree_ref(ref)))\n+\tif (!strcmp(tmp.buf, \"packed-refs\") || !strcmp(tmp.buf, \"logs\"))\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n+\telse if (is_per_worktree_ref(tmp.buf) ||\n+\t\t (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n+\t\t  is_per_worktree_ref(ref)))\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n \telse\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n\nNguyễn Thái Ngọc Duy (16):\n  refs-internal.c: make files_log_ref_write() static\n  files-backend: convert git_path() to strbuf_git_path()\n  files-backend: add files_path()\n  files-backend: replace *git_path*() with files_path()\n  refs.c: share is_per_worktree_ref() to files-backend.c\n  refs-internal.h: correct is_per_worktree_ref()\n  files-backend: remove the use of git_path()\n  refs.c: introduce get_main_ref_store()\n  refs: rename lookup_ref_store() to lookup_submodule_ref_store()\n  refs.c: flatten get_ref_store() a bit\n  refs.c: kill register_ref_store(), add register_submodule_ref_store()\n  refs.c: make get_main_ref_store() public and use it\n  path.c: move some code out of strbuf_git_path_submodule()\n  refs: move submodule code out of files-backend.c\n  files-backend: remove submodule_allowed from files_downcast()\n  refs: rename get_ref_store() to get_submodule_ref_store() and make it public\n\n path.c               |  34 ++---\n refs.c               | 144 +++++++++++----------\n refs.h               |  13 ++\n refs/files-backend.c | 349 +++++++++++++++++++++++++++++++--------------------\n refs/refs-internal.h |  28 ++---\n submodule.c          |  31 +++++\n submodule.h          |   1 +\n 7 files changed, 348 insertions(+), 252 deletions(-)\n\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311866","messageId":"20170217140436.17336-5-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 04/16] files-backend: replace *git_path*() with files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:24Z","receivedAt":"2017-02-17T14:05:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This centralizes all path rewriting of files-backend.c in one place so\nwe have easier time removing the path rewriting later. There could be\nsome hidden indirect git_path() though, I didn't audit the code to the\nbottom.\n\nSide note: set_worktree_head_symref() is a bad boy and should not be in\nfiles-backend.c (probably should not exist in the first place). But\nwe'll leave it there until we have better multi-worktree support in refs\nbefore we update it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 185 ++++++++++++++++++++++++++-------------------------\n 1 file changed, 94 insertions(+), 91 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex abb8a95e0..24f5bf7f1 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,7 +165,8 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n@@ -1178,12 +1179,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n {\n \tchar *packed_refs_file;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n-\tif (refs->submodule)\n-\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n-\t\t\t\t\t\t\t \"packed-refs\");\n-\telse\n-\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n+\tpacked_refs_file = strbuf_detach(&sb, NULL);\n \n \tif (refs->packed &&\n \t    !stat_validity_check(&refs->packed->validity, packed_refs_file))\n@@ -1249,10 +1248,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tsize_t path_baselen;\n \tint err = 0;\n \n-\tif (refs->submodule)\n-\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n-\telse\n-\t\tstrbuf_git_path(&path, \"%s\", dirname);\n+\tfiles_path(refs, &path, \"%s\", dirname);\n \tpath_baselen = path.len;\n \n \tif (err) {\n@@ -1394,10 +1390,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(&sb_path, refs->submodule, \"%s\", refname);\n-\telse\n-\t\tstrbuf_git_path(&sb_path, \"%s\", refname);\n+\tfiles_path(refs, &sb_path, \"%s\", refname);\n \n \tpath = sb_path.buf;\n \n@@ -1585,7 +1578,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -2052,7 +2045,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\tlock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n@@ -2197,7 +2190,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n \tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n \t\t\t\t\t\tflags, timeout_value);\n \tstrbuf_release(&sb);\n@@ -2343,7 +2336,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)\n  * Remove empty parents, but spare refs/ and immediate subdirs.\n  * Note: munges *name.\n  */\n-static void try_remove_empty_parents(char *name)\n+static void try_remove_empty_parents(struct files_ref_store *refs, char *name)\n {\n \tchar *p, *q;\n \tint i;\n@@ -2368,7 +2361,7 @@ static void try_remove_empty_parents(char *name)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\t*q = '\\0';\n-\t\tstrbuf_git_path(&sb, \"%s\", name);\n+\t\tfiles_path(refs, &sb, \"%s\", name);\n \t\tret = rmdir(sb.buf);\n \t\tstrbuf_release(&sb);\n \t\tif (ret)\n@@ -2377,7 +2370,7 @@ static void try_remove_empty_parents(char *name)\n }\n \n /* make sure nobody touched the ref, and unlink */\n-static void prune_ref(struct ref_to_prune *r)\n+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -2397,13 +2390,13 @@ static void prune_ref(struct ref_to_prune *r)\n \t}\n \tref_transaction_free(transaction);\n \tstrbuf_release(&err);\n-\ttry_remove_empty_parents(r->name);\n+\ttry_remove_empty_parents(refs, r->name);\n }\n \n-static void prune_refs(struct ref_to_prune *r)\n+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \twhile (r) {\n-\t\tprune_ref(r);\n+\t\tprune_ref(refs, r);\n \t\tr = r->next;\n \t}\n }\n@@ -2426,7 +2419,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n \tif (commit_packed_refs(refs))\n \t\tdie_errno(\"unable to overwrite old ref-pack file\");\n \n-\tprune_refs(cbdata.ref_to_prune);\n+\tprune_refs(refs, cbdata.ref_to_prune);\n \treturn 0;\n }\n \n@@ -2462,7 +2455,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tif (lock_packed_refs(refs, 0)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tfiles_path(refs, &sb, \"packed-refs\");\n \t\tunable_to_lock_message(sb.buf, errno, err);\n \t\tstrbuf_release(&sb);\n \t\treturn -1;\n@@ -2558,17 +2551,17 @@ static int files_delete_refs(struct ref_store *ref_store,\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-static int rename_tmp_log(const char *newrefname)\n+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n {\n \tint attempts_remaining = 4;\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n \tint ret = -1;\n \n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n  retry:\n \tstrbuf_reset(&path);\n-\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &path, \"logs/%s\", newrefname);\n \tswitch (safe_create_leading_directories_const(path.buf)) {\n \tcase SCLD_OK:\n \t\tbreak; /* success */\n@@ -2656,7 +2649,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n@@ -2672,8 +2665,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n \tstrbuf_release(&sb_oldref);\n \tstrbuf_release(&tmp_renamed_log);\n@@ -2700,7 +2693,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tstrbuf_git_path(&path, \"%s\", newrefname);\n+\t\t\tfiles_path(refs, &path, \"%s\", newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -2714,7 +2707,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t}\n \t}\n \n-\tif (log && rename_tmp_log(newrefname))\n+\tif (log && rename_tmp_log(refs, newrefname))\n \t\tgoto rollback;\n \n \tlogmoved = log;\n@@ -2756,12 +2749,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_newref, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n@@ -2817,11 +2810,13 @@ static int commit_ref(struct ref_lock *lock)\n  * should_autocreate_reflog returns non-zero.  Otherwise, create it\n  * regardless of the ref name.  Fill in *err and return -1 on failure.\n  */\n-static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)\n+static int log_ref_setup(struct files_ref_store *refs, const char *refname,\n+\t\t\t struct strbuf *logfile, struct strbuf *err,\n+\t\t\t int force_create)\n {\n \tint logfd, oflags = O_APPEND | O_WRONLY;\n \n-\tstrbuf_git_path(logfile, \"logs/%s\", refname);\n+\tfiles_path(refs, logfile, \"logs/%s\", refname);\n \tif (force_create || should_autocreate_reflog(refname)) {\n \t\tif (safe_create_leading_directories(logfile->buf) < 0) {\n \t\t\tstrbuf_addf(err, \"unable to create directory for '%s': \"\n@@ -2864,11 +2859,10 @@ static int files_create_reflog(struct ref_store *ref_store,\n {\n \tint ret;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"create_reflog\");\n-\n-\tret = log_ref_setup(refname, &sb, err, force_create);\n+\tret = log_ref_setup(refs, refname, &sb, err, force_create);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\n@@ -2899,7 +2893,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n+static int log_ref_write_1(struct files_ref_store *refs,\n+\t\t\t   const char *refname, const unsigned char *old_sha1,\n \t\t\t   const unsigned char *new_sha1, const char *msg,\n \t\t\t   struct strbuf *logfile, int flags,\n \t\t\t   struct strbuf *err)\n@@ -2909,7 +2904,7 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n \tif (log_all_ref_updates < 0)\n \t\tlog_all_ref_updates = !is_bare_repository();\n \n-\tresult = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);\n+\tresult = log_ref_setup(refs, refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);\n \n \tif (result)\n \t\treturn result;\n@@ -2933,21 +2928,23 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-static int log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int log_ref_write(struct files_ref_store *refs,\n+\t\t\t const char *refname, const unsigned char *old_sha1,\n \t\t\t const unsigned char *new_sha1, const char *msg,\n \t\t\t int flags, struct strbuf *err)\n {\n-\treturn files_log_ref_write(refname, old_sha1, new_sha1, msg, flags,\n-\t\t\t\t   err);\n+\treturn files_log_ref_write(refs, refname, old_sha1, new_sha1,\n+\t\t\t\t   msg, flags, err);\n }\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\tconst char *refname, const unsigned char *old_sha1,\n \t\t\tconst unsigned char *new_sha1, const char *msg,\n \t\t\tint flags, struct strbuf *err)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n-\tint ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,\n-\t\t\t\t  err);\n+\tint ret = log_ref_write_1(refs, refname, old_sha1, new_sha1, msg,\n+\t\t\t\t  &sb, flags, err);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\n@@ -3004,7 +3001,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \tfiles_assert_main_repository(refs, \"commit_ref_update\");\n \n \tclear_loose_ref_cache(refs);\n-\tif (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {\n+\tif (log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n+\t\t\t  sha1, logmsg, 0, err)) {\n \t\tchar *old_msg = strbuf_detach(err, NULL);\n \t\tstrbuf_addf(err, \"cannot update the ref '%s': %s\",\n \t\t\t    lock->ref_name, old_msg);\n@@ -3035,8 +3033,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n-\t\t\tif (log_ref_write(\"HEAD\", lock->old_oid.hash, sha1,\n-\t\t\t\t\t  logmsg, 0, &log_err)) {\n+\t\t\tif (log_ref_write(refs, \"HEAD\", lock->old_oid.hash,\n+\t\t\t\t\t  sha1, logmsg, 0, &log_err)) {\n \t\t\t\terror(\"%s\", log_err.buf);\n \t\t\t\tstrbuf_release(&log_err);\n \t\t\t}\n@@ -3068,23 +3066,26 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)\n \treturn ret;\n }\n \n-static void update_symref_reflog(struct ref_lock *lock, const char *refname,\n+static void update_symref_reflog(struct files_ref_store *refs,\n+\t\t\t\t struct ref_lock *lock, const char *refname,\n \t\t\t\t const char *target, const char *logmsg)\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n \tif (logmsg && !read_ref(target, new_sha1) &&\n-\t    log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {\n+\t    log_ref_write(refs, refname, lock->old_oid.hash,\n+\t\t\t  new_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t}\n }\n \n-static int create_symref_locked(struct ref_lock *lock, const char *refname,\n+static int create_symref_locked(struct files_ref_store *refs,\n+\t\t\t\tstruct ref_lock *lock, const char *refname,\n \t\t\t\tconst char *target, const char *logmsg)\n {\n \tif (prefer_symlink_refs && !create_ref_symlink(lock, target)) {\n-\t\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\t\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \t\treturn 0;\n \t}\n \n@@ -3092,7 +3093,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,\n \t\treturn error(\"unable to fdopen %s: %s\",\n \t\t\t     lock->lk->tempfile.filename.buf, strerror(errno));\n \n-\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \n \t/* no error check; commit_ref will check ferror */\n \tfprintf(lock->lk->tempfile.fp, \"ref: %s\\n\", target);\n@@ -3121,13 +3122,19 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\treturn -1;\n \t}\n \n-\tret = create_symref_locked(lock, refname, target, logmsg);\n+\tret = create_symref_locked(refs, lock, refname, target, logmsg);\n \tunlock_ref(lock);\n \treturn ret;\n }\n \n int set_worktree_head_symref(const char *gitdir, const char *target)\n {\n+\t/*\n+\t * FIXME: this obviously will not work well for future refs\n+\t * backends. This function needs to die.\n+\t */\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3154,7 +3161,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \tlock->lk = &head_lock;\n \tlock->ref_name = xstrdup(head_rel);\n \n-\tret = create_symref_locked(lock, head_rel, target, NULL);\n+\tret = create_symref_locked(refs, lock, head_rel, target, NULL);\n \n \tunlock_ref(lock); /* will free lock */\n \tstrbuf_release(&head_path);\n@@ -3164,14 +3171,13 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3180,13 +3186,12 @@ static int files_reflog_exists(struct ref_store *ref_store,\n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = remove_path(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3236,15 +3241,14 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     each_reflog_ent_fn fn,\n \t\t\t\t\t     void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n \tint ret = 0, at_tail = 1;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3345,14 +3349,13 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     const char *refname,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3434,15 +3437,14 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n \n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n-\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\tstrbuf_git_path(&sb, \"logs\");\n+\tfiles_path(refs, &sb, \"logs\");\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n@@ -3866,8 +3868,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t\tif (update->flags & REF_NEEDS_COMMIT ||\n \t\t    update->flags & REF_LOG_ONLY) {\n-\t\t\tif (log_ref_write(lock->ref_name, lock->old_oid.hash,\n-\t\t\t\t\t  update->new_sha1,\n+\t\t\tif (log_ref_write(refs, lock->ref_name,\n+\t\t\t\t\t  lock->old_oid.hash, update->new_sha1,\n \t\t\t\t\t  update->msg, update->flags, err)) {\n \t\t\t\tchar *old_msg = strbuf_detach(err, NULL);\n \n@@ -3916,7 +3918,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tfiles_path(refs, &sb, \"logs/%s\", ref_to_delete->string);\n \t\tunlink_or_warn(sb.buf);\n \t\tstrbuf_release(&sb);\n \t}\n@@ -4078,6 +4080,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \tint status = 0;\n \tint type;\n \tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tmemset(&cb, 0, sizeof(cb));\n \tcb.flags = flags;\n@@ -4102,7 +4105,8 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tlog_file = git_pathdup(\"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n+\tlog_file = strbuf_detach(&sb, NULL);\n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n \t\t/*\n \t\t * Even though holding $GIT_DIR/logs/$reflog.lock has\n@@ -4173,25 +4177,24 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"init_db\");\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tfiles_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n-\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tfiles_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tfiles_path(refs, &sb, \"refs/heads\");\n \t\tadjust_shared_perm(sb.buf);\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tfiles_path(refs, &sb, \"refs/tags\");\n \t\tadjust_shared_perm(sb.buf);\n \t}\n \tstrbuf_release(&sb);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311867","messageId":"20170217140436.17336-7-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 06/16] refs-internal.h: correct is_per_worktree_ref()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:26Z","receivedAt":"2017-02-17T14:05:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"All refs outside refs/ directory is per-worktree, not just HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/refs-internal.h | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f4aed49f5..69d02b6ba 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -653,7 +653,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n \n static inline int is_per_worktree_ref(const char *refname)\n {\n-\treturn !strcmp(refname, \"HEAD\") ||\n+\treturn !starts_with(refname, \"refs/\") ||\n \t\tstarts_with(refname, \"refs/bisect/\");\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311868","messageId":"20170217140436.17336-6-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 05/16] refs.c: share is_per_worktree_ref() to files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:25Z","receivedAt":"2017-02-17T14:05:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 6 ------\n refs/refs-internal.h | 6 ++++++\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 4f845798b..7a474198e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -489,12 +489,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n \treturn logs_found;\n }\n \n-static int is_per_worktree_ref(const char *refname)\n-{\n-\treturn !strcmp(refname, \"HEAD\") ||\n-\t\tstarts_with(refname, \"refs/bisect/\");\n-}\n-\n static int is_pseudoref_syntax(const char *refname)\n {\n \tconst char *c;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 59e65958a..f4aed49f5 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -651,4 +651,10 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    unsigned char *sha1, int *flags);\n \n+static inline int is_per_worktree_ref(const char *refname)\n+{\n+\treturn !strcmp(refname, \"HEAD\") ||\n+\t\tstarts_with(refname, \"refs/bisect/\");\n+}\n+\n #endif /* REFS_REFS_INTERNAL_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311869","messageId":"20170217140436.17336-8-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 07/16] files-backend: remove the use of git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:27Z","receivedAt":"2017-02-17T14:05:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\ndeciding what goes where. The end goal is to pass $GIT_DIR only. A\nrefs \"view\" of a linked worktree is a logical ref store that combines\ntwo files backends together.\n\n(*) Not entirely true since strbuf_git_path_submodule() still does path\ntranslation underneath. But that's for another patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 22 ++++++++++++++++++++--\n 1 file changed, 20 insertions(+), 2 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 24f5bf7f1..07cf2cb93 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -924,6 +924,9 @@ struct files_ref_store {\n \t */\n \tconst char *submodule;\n \n+\tstruct strbuf gitdir;\n+\tstruct strbuf gitcommondir;\n+\n \tstruct ref_entry *loose;\n \tstruct packed_ref_cache *packed;\n };\n@@ -937,6 +940,7 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n {\n \tstruct strbuf tmp = STRBUF_INIT;\n \tva_list vap;\n+\tconst char *ref;\n \n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n@@ -944,8 +948,14 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tif (refs->submodule)\n \t\tstrbuf_git_path_submodule(sb, refs->submodule,\n \t\t\t\t\t  \"%s\", tmp.buf);\n+\telse if (!strcmp(tmp.buf, \"packed-refs\") || !strcmp(tmp.buf, \"logs\"))\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n+\telse if (is_per_worktree_ref(tmp.buf) ||\n+\t\t (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n+\t\t  is_per_worktree_ref(ref)))\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n \telse\n-\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n \tstrbuf_release(&tmp);\n }\n \n@@ -1004,7 +1014,15 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\trefs->submodule = xstrdup_or_null(submodule);\n+\tstrbuf_init(&refs->gitdir, 0);\n+\tstrbuf_init(&refs->gitcommondir, 0);\n+\n+\tif (submodule) {\n+\t\trefs->submodule = xstrdup(submodule);\n+\t} else {\n+\t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n+\t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n+\t}\n \n \treturn ref_store;\n }\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311870","messageId":"20170217140436.17336-9-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 08/16] refs.c: introduce get_main_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:28Z","receivedAt":"2017-02-17T14:06:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 7a474198e..10994d992 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1445,15 +1445,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n+static struct ref_store *get_main_ref_store(void)\n+{\n+\tstruct ref_store *refs;\n+\n+\tif (main_ref_store)\n+\t\treturn main_ref_store;\n+\n+\trefs = ref_store_init(NULL);\n+\treturn refs;\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n-\t\trefs = lookup_ref_store(NULL);\n-\n-\t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL);\n+\t\treturn get_main_ref_store();\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311871","messageId":"20170217140436.17336-10-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 09/16] refs: rename lookup_ref_store() to lookup_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:29Z","receivedAt":"2017-02-17T14:06:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With get_main_ref_store() being used inside get_ref_store(),\nlookup_ref_store() is only used for submodule code path. Rename to\nreflect that and delete dead code.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 13 ++++---------\n 1 file changed, 4 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 10994d992..ea13a5b86 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1384,17 +1384,13 @@ static struct ref_store *main_ref_store;\n static struct hashmap submodule_ref_stores;\n \n /*\n- * Return the ref_store instance for the specified submodule (or the\n- * main repository if submodule is NULL). If that ref_store hasn't\n- * been initialized yet, return NULL.\n+ * Return the ref_store instance for the specified submodule. If that\n+ * ref_store hasn't been initialized yet, return NULL.\n  */\n-static struct ref_store *lookup_ref_store(const char *submodule)\n+static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n {\n \tstruct submodule_hash_entry *entry;\n \n-\tif (!submodule)\n-\t\treturn main_ref_store;\n-\n \tif (!submodule_ref_stores.tablesize)\n \t\t/* It's initialized on demand in register_ref_store(). */\n \t\treturn NULL;\n@@ -1463,7 +1459,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n \t} else {\n-\t\trefs = lookup_ref_store(submodule);\n+\t\trefs = lookup_submodule_ref_store(submodule);\n \n \t\tif (!refs) {\n \t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1474,7 +1470,6 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\t\tstrbuf_release(&submodule_sb);\n \t\t}\n \t}\n-\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311872","messageId":"20170217140436.17336-11-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 10/16] refs.c: flatten get_ref_store() a bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:30Z","receivedAt":"2017-02-17T14:06:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This helps the future changes in this code. And because get_ref_store()\nis destined to become get_submodule_ref_store(), the \"get main store\"\ncode path will be removed eventually. After this the patch to delete\nthat code will be cleaner.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 19 +++++++++----------\n 1 file changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex ea13a5b86..76a0e7b5a 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1454,22 +1454,21 @@ static struct ref_store *get_main_ref_store(void)\n \n struct ref_store *get_ref_store(const char *submodule)\n {\n+\tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n-\t} else {\n-\t\trefs = lookup_submodule_ref_store(submodule);\n+\t}\n \n-\t\tif (!refs) {\n-\t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n+\trefs = lookup_submodule_ref_store(submodule);\n+\tif (refs)\n+\t\treturn refs;\n \n-\t\t\tstrbuf_addstr(&submodule_sb, submodule);\n-\t\t\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\t\t\trefs = ref_store_init(submodule);\n-\t\t\tstrbuf_release(&submodule_sb);\n-\t\t}\n-\t}\n+\tstrbuf_addstr(&submodule_sb, submodule);\n+\tif (is_nonbare_repository_dir(&submodule_sb))\n+\t\trefs = ref_store_init(submodule);\n+\tstrbuf_release(&submodule_sb);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311873","messageId":"20170217140436.17336-12-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 11/16] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:31Z","receivedAt":"2017-02-17T14:06:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the last function in this code (besides public API) that takes\nsubmodule argument and handles both main/submodule cases. Break it down,\nmove main store registration in get_main_ref_store() and keep the rest\nin register_submodule_ref_store().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 36 +++++++++++++++++++-----------------\n 1 file changed, 19 insertions(+), 17 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 76a0e7b5a..55a80a83d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1402,25 +1402,19 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n \n /*\n  * Register the specified ref_store to be the one that should be used\n- * for submodule (or the main repository if submodule is NULL). It is\n- * a fatal error to call this function twice for the same submodule.\n+ * for submodule. It is a fatal error to call this function twice for\n+ * the same submodule.\n  */\n-static void register_ref_store(struct ref_store *refs, const char *submodule)\n+static void register_submodule_ref_store(struct ref_store *refs,\n+\t\t\t\t\t const char *submodule)\n {\n-\tif (!submodule) {\n-\t\tif (main_ref_store)\n-\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n-\n-\t\tmain_ref_store = refs;\n-\t} else {\n-\t\tif (!submodule_ref_stores.tablesize)\n-\t\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n+\tif (!submodule_ref_stores.tablesize)\n+\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n \n-\t\tif (hashmap_put(&submodule_ref_stores,\n-\t\t\t\talloc_submodule_hash_entry(submodule, refs)))\n-\t\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n-\t\t\t    submodule);\n-\t}\n+\tif (hashmap_put(&submodule_ref_stores,\n+\t\t\talloc_submodule_hash_entry(submodule, refs)))\n+\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n+\t\t    submodule);\n }\n \n /*\n@@ -1437,7 +1431,6 @@ static struct ref_store *ref_store_init(const char *submodule)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n \trefs = be->init(submodule);\n-\tregister_ref_store(refs, submodule);\n \treturn refs;\n }\n \n@@ -1449,6 +1442,12 @@ static struct ref_store *get_main_ref_store(void)\n \t\treturn main_ref_store;\n \n \trefs = ref_store_init(NULL);\n+\tif (refs) {\n+\t\tif (main_ref_store)\n+\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n+\n+\t\tmain_ref_store = refs;\n+\t}\n \treturn refs;\n }\n \n@@ -1469,6 +1468,9 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (is_nonbare_repository_dir(&submodule_sb))\n \t\trefs = ref_store_init(submodule);\n \tstrbuf_release(&submodule_sb);\n+\n+\tif (refs)\n+\t\tregister_submodule_ref_store(refs, submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311874","messageId":"20170217140436.17336-13-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 12/16] refs.c: make get_main_ref_store() public and use it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:32Z","receivedAt":"2017-02-17T14:06:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"get_ref_store() will soon be renamed to get_submodule_ref_store().\nTogether with future get_worktree_ref_store(), the three functions\nprovide an appropriate ref store for different operation modes. New APIs\nwill be added to operate directly on ref stores.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 36 ++++++++++++++++++------------------\n refs.h |  2 ++\n 2 files changed, 20 insertions(+), 18 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 55a80a83d..25f657a6f 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1303,7 +1303,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n /* backend functions */\n int refs_init_db(struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->init_db(refs, err);\n }\n@@ -1311,7 +1311,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_ref_store(NULL), refname,\n+\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1434,7 +1434,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n-static struct ref_store *get_main_ref_store(void)\n+struct ref_store *get_main_ref_store(void)\n {\n \tstruct ref_store *refs;\n \n@@ -1483,14 +1483,14 @@ void base_ref_store_init(struct ref_store *refs,\n /* backend functions */\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->pack_refs(refs, flags);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->peel_ref(refs, refname, sha1);\n }\n@@ -1498,7 +1498,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n \t\t\t\t       logmsg);\n@@ -1507,7 +1507,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1517,14 +1517,14 @@ int verify_refname_available(const char *refname,\n \t\t\t     const struct string_list *skip,\n \t\t\t     struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1535,7 +1535,7 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n \t\t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n@@ -1544,14 +1544,14 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_exists(refs, refname);\n }\n@@ -1559,14 +1559,14 @@ int reflog_exists(const char *refname)\n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_reflog(refs, refname);\n }\n@@ -1578,7 +1578,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n \t\t\t\t       prepare_fn, should_prune_fn,\n@@ -1588,21 +1588,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\ndiff --git a/refs.h b/refs.h\nindex 694784391..f803528fc 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -553,4 +553,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \n int ref_storage_backend_exists(const char *name);\n \n+struct ref_store *get_main_ref_store(void);\n+\n #endif /* REFS_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311875","messageId":"20170217140436.17336-14-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 13/16] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:33Z","receivedAt":"2017-02-17T14:06:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"refs is learning to avoid path rewriting that is done by\nstrbuf_git_path_submodule(). Factor out this code so it could be reused\nby refs*\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n path.c      | 34 +++++++---------------------------\n submodule.c | 31 +++++++++++++++++++++++++++++++\n submodule.h |  1 +\n 3 files changed, 39 insertions(+), 27 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex efcedafba..3451d2916 100644\n--- a/path.c\n+++ b/path.c\n@@ -475,35 +475,16 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n static int do_submodule_path(struct strbuf *buf, const char *path,\n \t\t\t     const char *fmt, va_list args)\n {\n-\tconst char *git_dir;\n \tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n \tstruct strbuf git_submodule_dir = STRBUF_INIT;\n-\tconst struct submodule *sub;\n-\tint err = 0;\n+\tint ret;\n \n-\tstrbuf_addstr(buf, path);\n-\tstrbuf_complete(buf, '/');\n-\tstrbuf_addstr(buf, \".git\");\n-\n-\tgit_dir = read_gitfile(buf->buf);\n-\tif (git_dir) {\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, git_dir);\n-\t}\n-\tif (!is_git_directory(buf->buf)) {\n-\t\tgitmodules_config();\n-\t\tsub = submodule_from_path(null_sha1, path);\n-\t\tif (!sub) {\n-\t\t\terr = SUBMODULE_PATH_ERR_NOT_CONFIGURED;\n-\t\t\tgoto cleanup;\n-\t\t}\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n-\t}\n-\n-\tstrbuf_addch(buf, '/');\n-\tstrbuf_addbuf(&git_submodule_dir, buf);\n+\tret = submodule_to_gitdir(&git_submodule_dir, path);\n+\tif (ret)\n+\t\tgoto cleanup;\n \n+\tstrbuf_complete(&git_submodule_dir, '/');\n+\tstrbuf_addbuf(buf, &git_submodule_dir);\n \tstrbuf_vaddf(buf, fmt, args);\n \n \tif (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))\n@@ -514,8 +495,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,\n cleanup:\n \tstrbuf_release(&git_submodule_dir);\n \tstrbuf_release(&git_submodule_common_dir);\n-\n-\treturn err;\n+\treturn ret;\n }\n \n char *git_pathdup_submodule(const char *path, const char *fmt, ...)\ndiff --git a/submodule.c b/submodule.c\nindex ece17315d..3ce589d55 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1335,3 +1335,34 @@ void prepare_submodule_repo_env(struct argv_array *out)\n \t}\n \targv_array_push(out, \"GIT_DIR=.git\");\n }\n+\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n+{\n+\tconst struct submodule *sub;\n+\tconst char *git_dir;\n+\tint ret = 0;\n+\n+\tstrbuf_reset(buf);\n+\tstrbuf_addstr(buf, submodule);\n+\tstrbuf_complete(buf, '/');\n+\tstrbuf_addstr(buf, \".git\");\n+\n+\tgit_dir = read_gitfile(buf->buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, git_dir);\n+\t}\n+\tif (!is_git_directory(buf->buf)) {\n+\t\tgitmodules_config();\n+\t\tsub = submodule_from_path(null_sha1, submodule);\n+\t\tif (!sub) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n+\t}\n+\n+cleanup:\n+\treturn ret;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex 23d76682b..2728494ce 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -70,6 +70,7 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n \t\t\t\t    int dry_run);\n void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n int parallel_submodules(void);\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n \n /*\n  * Prepare the \"env_array\" parameter of a \"struct child_process\" for executing\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311876","messageId":"20170217140436.17336-15-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 14/16] refs: move submodule code out of files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:34Z","receivedAt":"2017-02-17T14:06:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend is now initialized with a $GIT_DIR. Converting a submodule\npath to where real submodule gitdir is located is done in get_ref_store().\n\nThe new code in init_submodule_ref_store() is basically a copy of\nstrbuf_git_path_submodule().\n\nThis gives a slight performance improvement for submodules since we\ndon't convert submodule path to gitdir at every backend call like\nbefore. We pay that once at ref-store creation.\n\nMore cleanup in files_downcast() follows shortly. It's separate to keep\nnoises from this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 18 +++++++++++++-----\n refs/files-backend.c | 22 ++++++----------------\n refs/refs-internal.h |  6 +++---\n 3 files changed, 22 insertions(+), 24 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 25f657a6f..58ac9a2a8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -9,6 +9,7 @@\n #include \"refs/refs-internal.h\"\n #include \"object.h\"\n #include \"tag.h\"\n+#include \"submodule.h\"\n \n /*\n  * List of all available backends\n@@ -1419,9 +1420,9 @@ static void register_submodule_ref_store(struct ref_store *refs,\n \n /*\n  * Create, record, and return a ref_store instance for the specified\n- * submodule (or the main repository if submodule is NULL).\n+ * gitdir (or the main repository if gitdir is NULL).\n  */\n-static struct ref_store *ref_store_init(const char *submodule)\n+static struct ref_store *ref_store_init(const char *gitdir)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1430,7 +1431,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(submodule);\n+\trefs = be->init(gitdir);\n \treturn refs;\n }\n \n@@ -1455,6 +1456,7 @@ struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n+\tint ret;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n@@ -1465,8 +1467,14 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn refs;\n \n \tstrbuf_addstr(&submodule_sb, submodule);\n-\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\trefs = ref_store_init(submodule);\n+\tret = is_nonbare_repository_dir(&submodule_sb);\n+\tstrbuf_release(&submodule_sb);\n+\tif (!ret)\n+\t\treturn refs;\n+\n+\tret = submodule_to_gitdir(&submodule_sb, submodule);\n+\tif (!ret)\n+\t\trefs = ref_store_init(submodule_sb.buf);\n \tstrbuf_release(&submodule_sb);\n \n \tif (refs)\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 07cf2cb93..627466043 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -917,13 +917,6 @@ struct packed_ref_cache {\n struct files_ref_store {\n \tstruct ref_store base;\n \n-\t/*\n-\t * The name of the submodule represented by this object, or\n-\t * NULL if it represents the main repository's reference\n-\t * store:\n-\t */\n-\tconst char *submodule;\n-\n \tstruct strbuf gitdir;\n \tstruct strbuf gitcommondir;\n \n@@ -945,10 +938,7 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n \tva_end(vap);\n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n-\t\t\t\t\t  \"%s\", tmp.buf);\n-\telse if (!strcmp(tmp.buf, \"packed-refs\") || !strcmp(tmp.buf, \"logs\"))\n+\tif (!strcmp(tmp.buf, \"packed-refs\") || !strcmp(tmp.buf, \"logs\"))\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n \telse if (is_per_worktree_ref(tmp.buf) ||\n \t\t (skip_prefix(tmp.buf, \"logs/\", &ref) &&\n@@ -1007,7 +997,7 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *submodule)\n+static struct ref_store *files_ref_store_create(const char *gitdir)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n@@ -1017,8 +1007,9 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \tstrbuf_init(&refs->gitdir, 0);\n \tstrbuf_init(&refs->gitcommondir, 0);\n \n-\tif (submodule) {\n-\t\trefs->submodule = xstrdup(submodule);\n+\tif (gitdir) {\n+\t\tstrbuf_addstr(&refs->gitdir, gitdir);\n+\t\tget_common_dir_noenv(&refs->gitcommondir, gitdir);\n \t} else {\n \t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n \t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n@@ -1034,8 +1025,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\tif (refs->submodule)\n-\t\tdie(\"BUG: %s called for a submodule\", caller);\n+\t/* This function is to be deleted in the next patch */\n }\n \n /*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 69d02b6ba..d7112770d 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -476,12 +476,12 @@ struct ref_store;\n /* refs backends */\n \n /*\n- * Initialize the ref_store for the specified submodule, or for the\n- * main repository if submodule == NULL. These functions should call\n+ * Initialize the ref_store for the specified gitdir, or for the\n+ * current repository if gitdir == NULL. These functions should call\n  * base_ref_store_init() to initialize the shared part of the\n  * ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *submodule);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311877","messageId":"20170217140436.17336-16-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 15/16] files-backend: remove submodule_allowed from files_downcast()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:35Z","receivedAt":"2017-02-17T14:06:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since submodule or not is irrelevant to files-backend after the last\npatch, remove the parameter from files_downcast() and kill\nfiles_assert_main_repository().\n\nPS. This implies that all ref operations are allowed for submodules. But\nwe may need to look more closely to see if that's really true...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 70 ++++++++++++++++------------------------------------\n 1 file changed, 21 insertions(+), 49 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 627466043..d35032fcd 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1019,24 +1019,13 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n }\n \n /*\n- * Die if refs is for a submodule (i.e., not for the main repository).\n- * caller is used in any necessary error messages.\n- */\n-static void files_assert_main_repository(struct files_ref_store *refs,\n-\t\t\t\t\t const char *caller)\n-{\n-\t/* This function is to be deleted in the next patch */\n-}\n-\n-/*\n  * Downcast ref_store to files_ref_store. Die if ref_store is not a\n  * files_ref_store. If submodule_allowed is not true, then also die if\n  * files_ref_store is for a submodule (i.e., not for the main\n  * repository). caller is used in any necessary error messages.\n  */\n-static struct files_ref_store *files_downcast(\n-\t\tstruct ref_store *ref_store, int submodule_allowed,\n-\t\tconst char *caller)\n+static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n+\t\t\t\t\t      const char *caller)\n {\n \tstruct files_ref_store *refs;\n \n@@ -1046,9 +1035,6 @@ static struct files_ref_store *files_downcast(\n \n \trefs = (struct files_ref_store *)ref_store;\n \n-\tif (!submodule_allowed)\n-\t\tfiles_assert_main_repository(refs, caller);\n-\n \treturn refs;\n }\n \n@@ -1384,7 +1370,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t\t\t      struct strbuf *referent, unsigned int *type)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"read_raw_ref\");\n+\t\tfiles_downcast(ref_store, \"read_raw_ref\");\n \tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n \tconst char *path;\n@@ -1577,7 +1563,6 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \tint ret = TRANSACTION_GENERIC_ERROR;\n \n \tassert(err);\n-\tfiles_assert_main_repository(refs, \"lock_raw_ref\");\n \n \t*type = 0;\n \n@@ -1801,7 +1786,7 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n static int files_peel_ref(struct ref_store *ref_store,\n \t\t\t  const char *refname, unsigned char *sha1)\n {\n-\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"peel_ref\");\n \tint flag;\n \tunsigned char base[20];\n \n@@ -1910,7 +1895,7 @@ static struct ref_iterator *files_ref_iterator_begin(\n \t\tconst char *prefix, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"ref_iterator_begin\");\n \tstruct ref_dir *loose_dir, *packed_dir;\n \tstruct ref_iterator *loose_iter, *packed_iter;\n \tstruct files_ref_iterator *iter;\n@@ -2043,7 +2028,6 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tint attempts_remaining = 3;\n \tint resolved;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_sha1_basic\");\n \tassert(err);\n \n \tlock = xcalloc(1, sizeof(struct ref_lock));\n@@ -2191,8 +2175,6 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n-\n \tif (!timeout_configured) {\n \t\tgit_config_get_int(\"core.packedrefstimeout\", &timeout_value);\n \t\ttimeout_configured = 1;\n@@ -2232,8 +2214,6 @@ static int commit_packed_refs(struct files_ref_store *refs)\n \tint save_errno = 0;\n \tFILE *out;\n \n-\tfiles_assert_main_repository(refs, \"commit_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \n@@ -2265,8 +2245,6 @@ static void rollback_packed_refs(struct files_ref_store *refs)\n \tstruct packed_ref_cache *packed_ref_cache =\n \t\tget_packed_ref_cache(refs);\n \n-\tfiles_assert_main_repository(refs, \"rollback_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \trollback_lock_file(packed_ref_cache->lock);\n@@ -2412,7 +2390,7 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"pack_refs\");\n+\t\tfiles_downcast(ref_store, \"pack_refs\");\n \tstruct pack_refs_cb_data cbdata;\n \n \tmemset(&cbdata, 0, sizeof(cbdata));\n@@ -2445,7 +2423,6 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tstruct string_list_item *refname;\n \tint ret, needs_repacking = 0, removed = 0;\n \n-\tfiles_assert_main_repository(refs, \"repack_without_refs\");\n \tassert(err);\n \n \t/* Look for a packed ref */\n@@ -2513,7 +2490,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \t\t\t     struct string_list *refnames, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_refs\");\n+\t\tfiles_downcast(ref_store, \"delete_refs\");\n \tstruct strbuf err = STRBUF_INIT;\n \tint i, result = 0;\n \n@@ -2621,7 +2598,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,\n \t\t\t\t\t  struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"verify_refname_available\");\n+\t\tfiles_downcast(ref_store, \"verify_refname_available\");\n \tstruct ref_dir *packed_refs = get_packed_refs(refs);\n \tstruct ref_dir *loose_refs = get_loose_refs(refs);\n \n@@ -2646,7 +2623,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"rename_ref\");\n+\t\tfiles_downcast(ref_store, \"rename_ref\");\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n@@ -2868,7 +2845,7 @@ static int files_create_reflog(struct ref_store *ref_store,\n \tint ret;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n+\t\tfiles_downcast(ref_store, \"create_reflog\");\n \n \tret = log_ref_setup(refs, refname, &sb, err, force_create);\n \tstrbuf_release(&sb);\n@@ -3006,8 +2983,6 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\t\t     const unsigned char *sha1, const char *logmsg,\n \t\t\t     struct strbuf *err)\n {\n-\tfiles_assert_main_repository(refs, \"commit_ref_update\");\n-\n \tclear_loose_ref_cache(refs);\n \tif (log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n \t\t\t  sha1, logmsg, 0, err)) {\n@@ -3116,7 +3091,7 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\t\t       const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_symref\");\n+\t\tfiles_downcast(ref_store, \"create_symref\");\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_lock *lock;\n \tint ret;\n@@ -3142,7 +3117,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3180,7 +3155,7 @@ static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n+\t\tfiles_downcast(ref_store, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n@@ -3195,7 +3170,7 @@ static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n+\t\tfiles_downcast(ref_store, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n@@ -3250,7 +3225,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n@@ -3358,7 +3333,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n@@ -3446,7 +3421,7 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -3658,8 +3633,6 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \tint ret;\n \tstruct ref_lock *lock;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_for_update\");\n-\n \tif ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))\n \t\tupdate->flags |= REF_DELETING;\n \n@@ -3784,7 +3757,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list refs_to_delete = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *ref_to_delete;\n@@ -3958,7 +3931,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"initial_ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"initial_ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list affected_refnames = STRING_LIST_INIT_NODUP;\n \n@@ -4080,7 +4053,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\t       void *policy_cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_expire\");\n+\t\tfiles_downcast(ref_store, \"reflog_expire\");\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n@@ -4185,8 +4158,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n-\tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"init_db\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n \t/*\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311878","messageId":"20170217140436.17336-17-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v3 16/16] refs: rename get_ref_store() to get_submodule_ref_store() and make it public","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-17T14:04:36Z","receivedAt":"2017-02-17T14:06:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is intended to replace *_submodule() refs API. It provides\na ref store for a specific submodule, which can be operated on by a new\nset of refs API.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 12 ++++++++----\n refs.h               | 11 +++++++++++\n refs/files-backend.c |  2 +-\n refs/refs-internal.h | 12 ------------\n 4 files changed, 20 insertions(+), 17 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 58ac9a2a8..e7206a420 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1160,7 +1160,7 @@ int head_ref(each_ref_fn fn, void *cb_data)\n static int do_for_each_ref(const char *submodule, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(submodule);\n+\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1333,10 +1333,10 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \t\t/* We need to strip off one or more trailing slashes */\n \t\tchar *stripped = xmemdupz(submodule, len);\n \n-\t\trefs = get_ref_store(stripped);\n+\t\trefs = get_submodule_ref_store(stripped);\n \t\tfree(stripped);\n \t} else {\n-\t\trefs = get_ref_store(submodule);\n+\t\trefs = get_submodule_ref_store(submodule);\n \t}\n \n \tif (!refs)\n@@ -1452,13 +1452,17 @@ struct ref_store *get_main_ref_store(void)\n \treturn refs;\n }\n \n-struct ref_store *get_ref_store(const char *submodule)\n+struct ref_store *get_submodule_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \tint ret;\n \n \tif (!submodule || !*submodule) {\n+\t\t/*\n+\t\t * FIXME: This case is ideally not allowed. But that\n+\t\t * can't happen until we clean up all the callers.\n+\t\t */\n \t\treturn get_main_ref_store();\n \t}\n \ndiff --git a/refs.h b/refs.h\nindex f803528fc..1287ba59c 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -554,5 +554,16 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int ref_storage_backend_exists(const char *name);\n \n struct ref_store *get_main_ref_store(void);\n+/*\n+ * Return the ref_store instance for the specified submodule. For the\n+ * main repository, use submodule==NULL; such a call cannot fail. For\n+ * a submodule, the submodule must exist and be a nonbare repository,\n+ * otherwise return NULL. If the requested reference store has not yet\n+ * been initialized, initialize it first.\n+ *\n+ * For backwards compatibility, submodule==\"\" is treated the same as\n+ * submodule==NULL.\n+ */\n+struct ref_store *get_submodule_ref_store(const char *submodule);\n \n #endif /* REFS_H */\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex d35032fcd..82be3f90f 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3117,7 +3117,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex d7112770d..cb6882779 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -634,18 +634,6 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-/*\n- * Return the ref_store instance for the specified submodule. For the\n- * main repository, use submodule==NULL; such a call cannot fail. For\n- * a submodule, the submodule must exist and be a nonbare repository,\n- * otherwise return NULL. If the requested reference store has not yet\n- * been initialized, initialize it first.\n- *\n- * For backwards compatibility, submodule==\"\" is treated the same as\n- * submodule==NULL.\n- */\n-struct ref_store *get_ref_store(const char *submodule);\n-\n const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"311920","messageId":"xmqqzihksm0i.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"20170216114818.6080-1-pclouds@gmail.com","subject":"Re: [PATCH v2 00/16] Remove submodule from files-backend.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-17T18:35:57Z","receivedAt":"2017-02-17T18:36:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> I'll be sending two more follow-up series, if you are interested, soon:\n>\n> 1) v2 of nd/worktree-gc-protection\n>\n> which kills parse_ref() in worktree.c _and_ set_worktree_head_symref()\n> in files-backend.c. Both are bad things that should not have happened.\n> (PS. The topic name is misleading as this is mostly about eliminating\n> warts, unless Junio intended to combine my second series as well)\n\nYour description sounded that these two are just preparatory step\nfor the main one that would soon follow, and that was why these two\npatches landed on a topic named as such without any of its friends\n(which were yet to come).  If you prefer to keep these a separate\npreparatory step from the remainder and have them graduate sooner,\nlet's do so, as that is my preference as well.  \n\nRename it \"nd/worktree-kill-parse-ref\" perhaps?\n\n> This series introduces get_worktree_ref_store() and adds two new APIs\n> refs_resolve_ref_unsafe() and refs_create_symref(). I'm still not sure\n> if the refs_ prefix is good naming, but I had to pick something to get\n> things going. Name suggestions are welcome.\n\n> 2) the real worktree-gc-protection\n>\n> This series adds a bunch of new refs API, enough for revision.c to\n> traverses all sorts of refs with a ref store instead of \"submodule\".\n> Many _submodule API are removed as a result because they no longer\n> have any callers (yay!). One of them remains though.\n\nYay indeed.\n"},{"id":"311925","messageId":"xmqqvas8sl0o.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"20170217140436.17336-4-pclouds@gmail.com","subject":"Re: [PATCH v3 03/16] files-backend: add files_path()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-17T18:57:27Z","receivedAt":"2017-02-17T18:57:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This will be the replacement for both git_path() and git_path_submodule()\n> in this file. The idea is backend takes a git path and use that,\n> oblivious of submodule, linked worktrees and such.\n>\n> This is the middle step towards that. Eventually the \"submodule\" field\n> in 'struct files_ref_store' should be replace by \"gitdir\". And a\n\ns/replace/&d/;\n\n> compound ref_store is created to combine two files backends together,\n> one represents the shared refs in $GIT_COMMON_DIR, one per-worktree. At\n> that point, files_path() becomes a wrapper of strbuf_vaddf().\n\nI like the general direction, obviously ;-)\n\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 18 ++++++++++++++++++\n>  1 file changed, 18 insertions(+)\n>\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index f0c878b92..abb8a95e0 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -930,6 +930,24 @@ struct files_ref_store {\n>  /* Lock used for the main packed-refs file: */\n>  static struct lock_file packlock;\n>  \n> +__attribute__((format (printf, 3, 4)))\n> +static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n> +\t\t       const char *fmt, ...)\n> +{\n> +\tstruct strbuf tmp = STRBUF_INIT;\n> +\tva_list vap;\n> +\n> +\tva_start(vap, fmt);\n> +\tstrbuf_vaddf(&tmp, fmt, vap);\n> +\tva_end(vap);\n> +\tif (refs->submodule)\n> +\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n> +\t\t\t\t\t  \"%s\", tmp.buf);\n> +\telse\n> +\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n> +\tstrbuf_release(&tmp);\n> +}\n> +\n>  /*\n>   * Increment the reference count of *packed_refs.\n>   */\n"},{"id":"311926","messageId":"xmqqlgt4sjmx.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"20170217140436.17336-5-pclouds@gmail.com","subject":"Re: [PATCH v3 04/16] files-backend: replace *git_path*() with files_path()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-17T19:27:18Z","receivedAt":"2017-02-17T19:27:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This centralizes all path rewriting of files-backend.c in one place so\n> we have easier time removing the path rewriting later. There could be\n> some hidden indirect git_path() though, I didn't audit the code to the\n> bottom.\n>\n> Side note: set_worktree_head_symref() is a bad boy and should not be in\n> files-backend.c (probably should not exist in the first place). But\n> we'll leave it there until we have better multi-worktree support in refs\n> before we update it.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 185 ++++++++++++++++++++++++++-------------------------\n>  1 file changed, 94 insertions(+), 91 deletions(-)\n\nIn this step, files_path() is still \"if refs->submodule field is\nthere, then use that to call strbuf_git_path_submodule() and\notherwise call strbuf_git_path().\"  That is a very sensible\nrefactoring for things like packed-refs-file in this hunk:\n\n>  static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n>  {\n>  \tchar *packed_refs_file;\n> +\tstruct strbuf sb = STRBUF_INIT;\n>  \n> -\tif (refs->submodule)\n> -\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n> -\t\t\t\t\t\t\t \"packed-refs\");\n> -\telse\n> -\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n> +\tfiles_path(refs, &sb, \"packed-refs\");\n> +\tpacked_refs_file = strbuf_detach(&sb, NULL);\n\nBut the original code of some other changes do not follow that\npattern, e.g.\n\n> @@ -1585,7 +1578,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n>  \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n>  \n>  \tlock->ref_name = xstrdup(refname);\n> -\tstrbuf_git_path(&ref_file, \"%s\", refname);\n> +\tfiles_path(refs, &ref_file, \"%s\", refname);\n\nIs it the right way to review these changes to make sure that a\nconversion from the original that is an unconditional\nstrbuf_git_path() to files_path() happens only if the function is\n\"files-assert-main-repository\" clean?  lock_raw_ref() certainly is\none of those functions where the caller should not have a non-empty\nsubmodule field in refs.\n\n> @@ -2052,7 +2045,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n>  \tif (flags & REF_DELETING)\n>  \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n>  \n> -\tstrbuf_git_path(&ref_file, \"%s\", refname);\n> +\tfiles_path(refs, &ref_file, \"%s\", refname);\n\nSo is this one; lock_ref_sha1_basic() is protected with assert-main-repo.\n\n> @@ -2343,7 +2336,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)\n>   * Remove empty parents, but spare refs/ and immediate subdirs.\n>   * Note: munges *name.\n>   */\n> -static void try_remove_empty_parents(char *name)\n> +static void try_remove_empty_parents(struct files_ref_store *refs, char *name)\n>  {\n>  \tchar *p, *q;\n>  \tint i;\n> @@ -2368,7 +2361,7 @@ static void try_remove_empty_parents(char *name)\n>  \t\tif (q == p)\n>  \t\t\tbreak;\n>  \t\t*q = '\\0';\n> -\t\tstrbuf_git_path(&sb, \"%s\", name);\n> +\t\tfiles_path(refs, &sb, \"%s\", name);\n\nBut here it gets iffy.  try_remove_empty_parents() itself does not\nassert, and its sole caller prune_ref() does not, either.  The sole\ncaller of prune_ref() which is prune_refs() does not.  As we climb\nthe call chain up, we reach files_pack_refs().  Am I confused to\ndoubt that the method is inherently main-repo only?\n\n    ... ah, OK, files_downcast() at the beginning of pack_refs\n    forbids submodule.  So this is safe.\n\n\n> @@ -2462,7 +2455,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n>  \tif (lock_packed_refs(refs, 0)) {\n>  \t\tstruct strbuf sb = STRBUF_INIT;\n>  \n> -\t\tstrbuf_git_path(&sb, \"packed-refs\");\n> +\t\tfiles_path(refs, &sb, \"packed-refs\");\n\nThis is safe, as repack_without_refs() asserts that it is main-repo\nonly.\n\n> @@ -2558,17 +2551,17 @@ static int files_delete_refs(struct ref_store *ref_store,\n>   */\n>  #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n>  \n> -static int rename_tmp_log(const char *newrefname)\n> +static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n>  {\n\nThe sole caller files_rename_ref() is main-repo only and that is\nguaranteed when downcast is done.\n\n> -static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)\n> +static int log_ref_setup(struct files_ref_store *refs, const char *refname,\n> +\t\t\t struct strbuf *logfile, struct strbuf *err,\n> +\t\t\t int force_create)\n>  {\n>  \tint logfd, oflags = O_APPEND | O_WRONLY;\n>  \n> -\tstrbuf_git_path(logfile, \"logs/%s\", refname);\n> +\tfiles_path(refs, logfile, \"logs/%s\", refname);\n\nThis and friends of log_ref_write() eventually rolls up to\ncommit_ref_update() that has the main-repo only assertion, so they\nshould be safe.\n\nAnother entry point files_create_symref() via create_symref_locked()\nalso reaches log_ref_write() and friends but the safety is guaranteed\nvia the downcast that asserts.\n\nOK, overall I really like the loss of \"Check the validity but we do\nnot need the result\" with this step.  The same checks are still done\nbut the code looks much less hacky.\n\n"},{"id":"311927","messageId":"xmqqh93ssjj8.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"20170217140436.17336-12-pclouds@gmail.com","subject":"Re: [PATCH v3 11/16] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-17T19:29:31Z","receivedAt":"2017-02-17T19:29:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This is the last function in this code (besides public API) that takes\n> submodule argument and handles both main/submodule cases. Break it down,\n> move main store registration in get_main_ref_store() and keep the rest\n> in register_submodule_ref_store().\n\nVery nice.\n"},{"id":"311934","messageId":"xmqq8tp4sfup.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"xmqqzihksm0i.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 00/16] Remove submodule from files-backend.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-17T20:49:02Z","receivedAt":"2017-02-17T20:49:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> I'll be sending two more follow-up series, if you are interested, soon:\n>>\n>> 1) v2 of nd/worktree-gc-protection\n>> ...\n>> 2) the real worktree-gc-protection\n>>\n>> This series adds a bunch of new refs API, enough for revision.c to\n>> traverses all sorts of refs with a ref store instead of \"submodule\".\n>> Many _submodule API are removed as a result because they no longer\n>> have any callers (yay!). One of them remains though.\n>\n> Yay indeed.\n\nHaving said all that, I have a bad news.\n\nI won't be able to queue this as-is, as this heavily conflicts with\nmh/ref-remove-empty-directory that has already been cooking in\n'next' for some time.  I started trying conflict resolution myself\nand it seemed doable, but stopped after finishing perhaps 40% of it,\nas I cannot afford the same amount of time for conflict resolution\nevery time the series gets rerolled.\n\nIf the topic is rebased on the result of doing:\n\n - checking out master^0\n - merging mh/ref-remove-empty-directory on it\n - mergeing mh/submodule-hash on the result\n\nI suspect that we might be able start cooking it in 'pu' sooner, but\nI cannot spend any more time on this topic today so I'd stop analyzing\nthe situation for now.\n\n\n"},{"id":"312034","messageId":"CACsJy8AhVPTLNkW+y+pHDa+FfknR4x4Gj-eksKp4MZEZmNdviw@mail.gmail.com","threadId":"45126","inReplyTo":"xmqqzihksm0i.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 00/16] Remove submodule from files-backend.c","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:15:38Z","receivedAt":"2017-02-18T13:16:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Feb 18, 2017 at 1:35 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> I'll be sending two more follow-up series, if you are interested, soon:\n>>\n>> 1) v2 of nd/worktree-gc-protection\n>>\n>> which kills parse_ref() in worktree.c _and_ set_worktree_head_symref()\n>> in files-backend.c. Both are bad things that should not have happened.\n>> (PS. The topic name is misleading as this is mostly about eliminating\n>> warts, unless Junio intended to combine my second series as well)\n>\n> Your description sounded that these two are just preparatory step\n> for the main one that would soon follow, and that was why these two\n> patches landed on a topic named as such without any of its friends\n> (which were yet to come).  If you prefer to keep these a separate\n> preparatory step from the remainder and have them graduate sooner,\n> let's do so, as that is my preference as well.\n>\n> Rename it \"nd/worktree-kill-parse-ref\" perhaps?\n\nThat's exactly my branch name (minus the nd/)\n-- \nDuy\n"},{"id":"312035","messageId":"20170218133303.3682-1-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170217140436.17336-1-pclouds@gmail.com","subject":"[PATCH v4 00/15] Remove submodule from files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:48Z","receivedAt":"2017-02-18T13:33:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v4:\n\n - is based on master+mh/ref-remove-empty-directory+mh/submodule-hash\n   instead of mh/submodule-hash alone. It could merge with pu cleanly.\n\n - 06/16 from v5, \"correct is_per_worktree_ref\", is removed. The\n   \"remove the use of git_path\" patch is updated to handle it\n   correctly even without that patch. More on this later.\n\n - update \"replace\" typo in a commit message\n\nThe story behind the 06/16 removal is, in my simple view, there are\nper-repo and per-worktree refs. But refs.c and backends see them as\nthree types: per-worktree, pseudo and normal/per-repo. Refs like\nORIG_HEAD are _both_ per-worktree and pseudo.\n\n06/16 reclassifies ORIG_HEAD from pseudo to per-worktree, which\ntriggers a failure in t1400 because as pseudo refs, reflog will not be\ncreated while it will be for per-worktree. This new test is on master,\nnot maint. I now fall back to treating both per-worktree and pseudo as\nper-worktree in files_path() only and keep classification unchanged.\n\nSide note, my other two series seem to apply cleanly on top of this\nv3, so no resend.\n\nNguyễn Thái Ngọc Duy (15):\n  refs-internal.c: make files_log_ref_write() static\n  files-backend: convert git_path() to strbuf_git_path()\n  files-backend: add files_path()\n  files-backend: replace *git_path*() with files_path()\n  refs.c: share is_per_worktree_ref() to files-backend.c\n  files-backend: remove the use of git_path()\n  refs.c: introduce get_main_ref_store()\n  refs: rename lookup_ref_store() to lookup_submodule_ref_store()\n  refs.c: flatten get_ref_store() a bit\n  refs.c: kill register_ref_store(), add register_submodule_ref_store()\n  refs.c: make get_main_ref_store() public and use it\n  path.c: move some code out of strbuf_git_path_submodule()\n  refs: move submodule code out of files-backend.c\n  files-backend: remove submodule_allowed from files_downcast()\n  refs: rename get_ref_store() to get_submodule_ref_store() and make it public\n\n path.c               |  34 +----\n refs.c               | 144 +++++++++---------\n refs.h               |  13 ++\n refs/files-backend.c | 406 ++++++++++++++++++++++++++++++++-------------------\n refs/refs-internal.h |  28 ++--\n submodule.c          |  31 ++++\n submodule.h          |   1 +\n 7 files changed, 396 insertions(+), 261 deletions(-)\n\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312036","messageId":"20170218133303.3682-2-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 01/15] refs-internal.c: make files_log_ref_write() static","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:49Z","receivedAt":"2017-02-18T13:33:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\nbut probably never used outside refs-internal.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 +++\n refs/refs-internal.h | 4 ----\n 2 files changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex db3bd42a9..1ebd59ec0 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err);\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex fa93c9a32..f732473e1 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -228,10 +228,6 @@ struct ref_transaction {\n \tenum ref_transaction_state state;\n };\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err);\n-\n /*\n  * Check for entries in extras that are within the specified\n  * directory, where dirname is a reference directory name including\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312037","messageId":"20170218133303.3682-3-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 02/15] files-backend: convert git_path() to strbuf_git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:50Z","receivedAt":"2017-02-18T13:33:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git_path() and friends are going to be killed in files-backend.c in near\nfuture. And because there's a risk with overwriting buffer in\ngit_path(), let's convert them all to strbuf_git_path(). We'll have\neasier time killing/converting strbuf_git_path() then because we won't\nhave to worry about memory management again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 146 ++++++++++++++++++++++++++++++++++++++++-----------\n 1 file changed, 115 insertions(+), 31 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 1ebd59ec0..c6c86f850 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2151,6 +2151,8 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstatic int timeout_configured = 0;\n \tstatic int timeout_value = 1000;\n \tstruct packed_ref_cache *packed_ref_cache;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n \n \tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n \n@@ -2159,10 +2161,13 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tif (hold_lock_file_for_update_timeout(\n-\t\t\t    &packlock, git_path(\"packed-refs\"),\n-\t\t\t    flags, timeout_value) < 0)\n+\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n+\t\t\t\t\t\tflags, timeout_value);\n+\tstrbuf_release(&sb);\n+\tif (ret < 0)\n \t\treturn -1;\n+\n \t/*\n \t * Get the current packed-refs while holding the lock.  If the\n \t * packed-refs file has been modified since we last read it,\n@@ -2312,6 +2317,7 @@ enum {\n static void try_remove_empty_parents(const char *refname, unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \tchar *p, *q;\n \tint i;\n \n@@ -2333,14 +2339,19 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) &&\n-\t\t    rmdir(git_path(\"%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&\n-\t\t    rmdir(git_path(\"logs/%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n \tstrbuf_release(&buf);\n+\tstrbuf_release(&sb);\n }\n \n /* make sure nobody touched the ref, and unlink */\n@@ -2426,7 +2437,11 @@ static int repack_without_refs(struct files_ref_store *refs,\n \t\treturn 0; /* no refname exists in packed refs */\n \n \tif (lock_packed_refs(refs, 0)) {\n-\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tunable_to_lock_message(sb.buf, errno, err);\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \tpacked = get_packed_refs(refs);\n@@ -2504,9 +2519,11 @@ static int files_delete_refs(struct ref_store *ref_store,\n \n static int rename_tmp_log_callback(const char *path, void *cb)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tint *true_errno = cb;\n \n-\tif (rename(git_path(TMP_RENAMED_LOG), path)) {\n+\tstrbuf_git_path(&sb, TMP_RENAMED_LOG);\n+\tif (rename(sb.buf, path)) {\n \t\t/*\n \t\t * rename(a, b) when b is an existing directory ought\n \t\t * to result in ISDIR, but Solaris 5.8 gives ENOTDIR.\n@@ -2515,30 +2532,40 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \t\t * that it knows to retry.\n \t\t */\n \t\t*true_errno = errno;\n+\t\tstrbuf_release(&sb);\n \t\tif (errno == ENOTDIR)\n \t\t\terrno = EISDIR;\n \t\treturn -1;\n \t} else {\n+\t\tstrbuf_release(&sb);\n \t\treturn 0;\n \t}\n }\n \n static int rename_tmp_log(const char *newrefname)\n {\n-\tchar *path = git_pathdup(\"logs/%s\", newrefname);\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *path;\n \tint ret, true_errno;\n \n+\tstrbuf_git_path(&sb, \"logs/%s\", newrefname);\n+\tpath = sb.buf;\n \tret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);\n \tif (ret) {\n \t\tif (errno == EISDIR)\n \t\t\terror(\"directory not empty: %s\", path);\n-\t\telse\n+\t\telse {\n+\t\t\tstruct strbuf tmp_renamed = STRBUF_INIT;\n+\n+\t\t\tstrbuf_git_path(&tmp_renamed, TMP_RENAMED_LOG);\n \t\t\terror(\"unable to move logfile %s to %s: %s\",\n-\t\t\t      git_path(TMP_RENAMED_LOG), path,\n+\t\t\t      tmp_renamed.buf, path,\n \t\t\t      strerror(true_errno));\n+\t\t\tstrbuf_release(&tmp_renamed);\n+\t\t}\n \t}\n \n-\tfree(path);\n+\tstrbuf_release(&sb);\n \treturn ret;\n }\n \n@@ -2579,9 +2606,15 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n+\tstruct strbuf sb_oldref = STRBUF_INIT;\n+\tstruct strbuf sb_newref = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n+\tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tlog = !lstat(sb_oldref.buf, &loginfo);\n+\tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n \n@@ -2595,7 +2628,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n+\tif (ret)\n \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n@@ -2674,13 +2712,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n+\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n+\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n+\tstrbuf_release(&sb_newref);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n \n \treturn 1;\n }\n@@ -2854,18 +2898,24 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \tresult = log_ref_write_fd(logfd, old_sha1, new_sha1,\n \t\t\t\t  git_committer_info(0), msg);\n \tif (result) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\tclose(logfd);\n \t\treturn -1;\n \t}\n \tif (close(logfd)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \treturn 0;\n@@ -3085,22 +3135,32 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n+\tint ret;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \n-\treturn !lstat(git_path(\"logs/%s\", refname), &st) &&\n-\t\tS_ISREG(st.st_mode);\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \n-\treturn remove_path(git_path(\"logs/%s\", refname));\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = remove_path(sb.buf);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)\n@@ -3155,7 +3215,9 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3261,7 +3323,9 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3343,12 +3407,15 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n {\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\titer->dir_iterator = dir_iterator_begin(git_path(\"logs\"));\n+\tstrbuf_git_path(&sb, \"logs\");\n+\titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n \n@@ -3686,6 +3753,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tchar *head_ref = NULL;\n \tint head_type;\n \tstruct object_id head_oid;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tassert(err);\n \n@@ -3807,7 +3875,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\tif (!(update->type & REF_ISPACKED) ||\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n-\t\t\t\tif (unlink_or_msg(git_path(\"%s\", lock->ref_name), err)) {\n+\t\t\t\tstrbuf_reset(&sb);\n+\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n \t\t\t\t}\n@@ -3827,9 +3897,13 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t/* Delete the reflogs of any references that were deleted: */\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n-\t\tif (!unlink_or_warn(git_path(\"logs/%s\", ref_to_delete->string)))\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tif (!unlink_or_warn(sb.buf))\n \t\t\ttry_remove_empty_parents(ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n+\t\tstrbuf_release(&sb);\n \t}\n \n \tclear_loose_ref_cache(refs);\n@@ -4101,18 +4175,28 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"init_db\");\n \n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n+\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tadjust_shared_perm(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tadjust_shared_perm(sb.buf);\n \t}\n+\tstrbuf_release(&sb);\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312038","messageId":"20170218133303.3682-4-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 03/15] files-backend: add files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:51Z","receivedAt":"2017-02-18T13:33:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This will be the replacement for both git_path() and git_path_submodule()\nin this file. The idea is backend takes a git path and use that,\noblivious of submodule, linked worktrees and such.\n\nThis is the middle step towards that. Eventually the \"submodule\" field\nin 'struct files_ref_store' should be replaced by \"gitdir\". And a\ncompound ref_store is created to combine two files backends together,\none represents the shared refs in $GIT_COMMON_DIR, one per-worktree. At\nthat point, files_path() becomes a wrapper of strbuf_vaddf().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 18 ++++++++++++++++++\n 1 file changed, 18 insertions(+)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex c6c86f850..7eaf28bd6 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -930,6 +930,24 @@ struct files_ref_store {\n /* Lock used for the main packed-refs file: */\n static struct lock_file packlock;\n \n+__attribute__((format (printf, 3, 4)))\n+static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n+\t\t       const char *fmt, ...)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tva_list vap;\n+\n+\tva_start(vap, fmt);\n+\tstrbuf_vaddf(&tmp, fmt, vap);\n+\tva_end(vap);\n+\tif (refs->submodule)\n+\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n+\t\t\t\t\t  \"%s\", tmp.buf);\n+\telse\n+\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\tstrbuf_release(&tmp);\n+}\n+\n /*\n  * Increment the reference count of *packed_refs.\n  */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312039","messageId":"20170218133303.3682-5-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 04/15] files-backend: replace *git_path*() with files_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:52Z","receivedAt":"2017-02-18T13:33:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This centralizes all path rewriting of files-backend.c in one place so\nwe have easier time removing the path rewriting later. There could be\nsome hidden indirect git_path() though, I didn't audit the code to the\nbottom.\n\nSide note: set_worktree_head_symref() is a bad boy and should not be in\nfiles-backend.c (probably should not exist in the first place). But\nwe'll leave it there until we have better multi-worktree support in refs\nbefore we update it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 208 +++++++++++++++++++++++++++------------------------\n 1 file changed, 111 insertions(+), 97 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 7eaf28bd6..b599ddf92 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,7 +165,8 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n@@ -1178,12 +1179,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n {\n \tchar *packed_refs_file;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n-\tif (refs->submodule)\n-\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n-\t\t\t\t\t\t\t \"packed-refs\");\n-\telse\n-\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n+\tpacked_refs_file = strbuf_detach(&sb, NULL);\n \n \tif (refs->packed &&\n \t    !stat_validity_check(&refs->packed->validity, packed_refs_file))\n@@ -1249,10 +1248,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tsize_t path_baselen;\n \tint err = 0;\n \n-\tif (refs->submodule)\n-\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n-\telse\n-\t\tstrbuf_git_path(&path, \"%s\", dirname);\n+\tfiles_path(refs, &path, \"%s\", dirname);\n \tpath_baselen = path.len;\n \n \tif (err) {\n@@ -1394,10 +1390,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(&sb_path, refs->submodule, \"%s\", refname);\n-\telse\n-\t\tstrbuf_git_path(&sb_path, \"%s\", refname);\n+\tfiles_path(refs, &sb_path, \"%s\", refname);\n \n \tpath = sb_path.buf;\n \n@@ -1585,7 +1578,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -2057,7 +2050,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_path(refs, &ref_file, \"%s\", refname);\n \tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\tlock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n@@ -2179,7 +2172,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t\ttimeout_configured = 1;\n \t}\n \n-\tstrbuf_git_path(&sb, \"packed-refs\");\n+\tfiles_path(refs, &sb, \"packed-refs\");\n \tret = hold_lock_file_for_update_timeout(&packlock, sb.buf,\n \t\t\t\t\t\tflags, timeout_value);\n \tstrbuf_release(&sb);\n@@ -2332,7 +2325,9 @@ enum {\n  * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or\n  * REMOVE_EMPTY_PARENTS_REFLOG.\n  */\n-static void try_remove_empty_parents(const char *refname, unsigned int flags)\n+static void try_remove_empty_parents(struct files_ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -2359,12 +2354,12 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tfiles_path(refs, &sb, \"%s\", buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tfiles_path(refs, &sb, \"logs/%s\", buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n@@ -2373,7 +2368,7 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n }\n \n /* make sure nobody touched the ref, and unlink */\n-static void prune_ref(struct ref_to_prune *r)\n+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -2395,10 +2390,10 @@ static void prune_ref(struct ref_to_prune *r)\n \tstrbuf_release(&err);\n }\n \n-static void prune_refs(struct ref_to_prune *r)\n+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \twhile (r) {\n-\t\tprune_ref(r);\n+\t\tprune_ref(refs, r);\n \t\tr = r->next;\n \t}\n }\n@@ -2421,7 +2416,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n \tif (commit_packed_refs(refs))\n \t\tdie_errno(\"unable to overwrite old ref-pack file\");\n \n-\tprune_refs(cbdata.ref_to_prune);\n+\tprune_refs(refs, cbdata.ref_to_prune);\n \treturn 0;\n }\n \n@@ -2457,7 +2452,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tif (lock_packed_refs(refs, 0)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"packed-refs\");\n+\t\tfiles_path(refs, &sb, \"packed-refs\");\n \t\tunable_to_lock_message(sb.buf, errno, err);\n \t\tstrbuf_release(&sb);\n \t\treturn -1;\n@@ -2535,12 +2530,17 @@ static int files_delete_refs(struct ref_store *ref_store,\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-static int rename_tmp_log_callback(const char *path, void *cb)\n+struct rename_tmp_log_cb {\n+\tstruct files_ref_store *refs;\n+\tint true_errno;\n+};\n+\n+static int rename_tmp_log_callback(const char *path, void *cb_)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n-\tint *true_errno = cb;\n+\tstruct rename_tmp_log_cb *cb = cb_;\n \n-\tstrbuf_git_path(&sb, TMP_RENAMED_LOG);\n+\tfiles_path(cb->refs, &sb, TMP_RENAMED_LOG);\n \tif (rename(sb.buf, path)) {\n \t\t/*\n \t\t * rename(a, b) when b is an existing directory ought\n@@ -2549,7 +2549,7 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \t\t * but report EISDIR to raceproof_create_file() so\n \t\t * that it knows to retry.\n \t\t */\n-\t\t*true_errno = errno;\n+\t\tcb->true_errno = errno;\n \t\tstrbuf_release(&sb);\n \t\tif (errno == ENOTDIR)\n \t\t\terrno = EISDIR;\n@@ -2560,25 +2560,27 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \t}\n }\n \n-static int rename_tmp_log(const char *newrefname)\n+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct rename_tmp_log_cb cb;\n \tconst char *path;\n-\tint ret, true_errno;\n+\tint ret;\n \n-\tstrbuf_git_path(&sb, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &sb, \"logs/%s\", newrefname);\n \tpath = sb.buf;\n-\tret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);\n+\tcb.refs = refs;\n+\tret = raceproof_create_file(path, rename_tmp_log_callback, &cb);\n \tif (ret) {\n \t\tif (errno == EISDIR)\n \t\t\terror(\"directory not empty: %s\", path);\n \t\telse {\n \t\t\tstruct strbuf tmp_renamed = STRBUF_INIT;\n \n-\t\t\tstrbuf_git_path(&tmp_renamed, TMP_RENAMED_LOG);\n+\t\t\tfiles_path(refs, &tmp_renamed, TMP_RENAMED_LOG);\n \t\t\terror(\"unable to move logfile %s to %s: %s\",\n \t\t\t      tmp_renamed.buf, path,\n-\t\t\t      strerror(true_errno));\n+\t\t\t      strerror(cb.true_errno));\n \t\t\tstrbuf_release(&tmp_renamed);\n \t\t}\n \t}\n@@ -2630,7 +2632,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n@@ -2646,8 +2648,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n \tstrbuf_release(&sb_oldref);\n \tstrbuf_release(&tmp_renamed_log);\n@@ -2674,7 +2676,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tstrbuf_git_path(&path, \"%s\", newrefname);\n+\t\t\tfiles_path(refs, &path, \"%s\", newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -2688,7 +2690,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t}\n \t}\n \n-\tif (log && rename_tmp_log(newrefname))\n+\tif (log && rename_tmp_log(refs, newrefname))\n \t\tgoto rollback;\n \n \tlogmoved = log;\n@@ -2730,12 +2732,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_path(refs, &sb_newref, \"logs/%s\", newrefname);\n+\tfiles_path(refs, &sb_oldref, \"logs/%s\", oldrefname);\n \tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tfiles_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n@@ -2803,10 +2805,15 @@ static int open_or_create_logfile(const char *path, void *cb)\n  * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and\n  * return -1.\n  */\n-static int log_ref_setup(const char *refname, int force_create,\n+static int log_ref_setup(struct files_ref_store *refs,\n+\t\t\t const char *refname, int force_create,\n \t\t\t int *logfd, struct strbuf *err)\n {\n-\tchar *logfile = git_pathdup(\"logs/%s\", refname);\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tchar *logfile;\n+\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n+\tlogfile = strbuf_detach(&sb, NULL);\n \n \tif (force_create || should_autocreate_reflog(refname)) {\n \t\tif (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {\n@@ -2856,12 +2863,11 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname, int force_create,\n \t\t\t       struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n \tint fd;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"create_reflog\");\n-\n-\tif (log_ref_setup(refname, force_create, &fd, err))\n+\tif (log_ref_setup(refs, refname, force_create, &fd, err))\n \t\treturn -1;\n \n \tif (fd >= 0)\n@@ -2896,7 +2902,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\tconst char *refname, const unsigned char *old_sha1,\n \t\t\tconst unsigned char *new_sha1, const char *msg,\n \t\t\tint flags, struct strbuf *err)\n {\n@@ -2905,7 +2912,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \tif (log_all_ref_updates == LOG_REFS_UNSET)\n \t\tlog_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;\n \n-\tresult = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,\n+\tresult = log_ref_setup(refs, refname, flags & REF_FORCE_CREATE_REFLOG,\n \t\t\t       &logfd, err);\n \n \tif (result)\n@@ -2919,7 +2926,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2930,7 +2937,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2991,8 +2998,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \tfiles_assert_main_repository(refs, \"commit_ref_update\");\n \n \tclear_loose_ref_cache(refs);\n-\tif (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,\n-\t\t\t\tlogmsg, 0, err)) {\n+\tif (files_log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n+\t\t\t\tsha1, logmsg, 0, err)) {\n \t\tchar *old_msg = strbuf_detach(err, NULL);\n \t\tstrbuf_addf(err, \"cannot update the ref '%s': %s\",\n \t\t\t    lock->ref_name, old_msg);\n@@ -3023,8 +3030,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n-\t\t\tif (files_log_ref_write(\"HEAD\", lock->old_oid.hash, sha1,\n-\t\t\t\t\t  logmsg, 0, &log_err)) {\n+\t\t\tif (files_log_ref_write(refs, \"HEAD\", lock->old_oid.hash,\n+\t\t\t\t\t\tsha1, logmsg, 0, &log_err)) {\n \t\t\t\terror(\"%s\", log_err.buf);\n \t\t\t\tstrbuf_release(&log_err);\n \t\t\t}\n@@ -3056,24 +3063,26 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)\n \treturn ret;\n }\n \n-static void update_symref_reflog(struct ref_lock *lock, const char *refname,\n+static void update_symref_reflog(struct files_ref_store *refs,\n+\t\t\t\t struct ref_lock *lock, const char *refname,\n \t\t\t\t const char *target, const char *logmsg)\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n \tif (logmsg && !read_ref(target, new_sha1) &&\n-\t    files_log_ref_write(refname, lock->old_oid.hash, new_sha1,\n+\t    files_log_ref_write(refs, refname, lock->old_oid.hash, new_sha1,\n \t\t\t\tlogmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t}\n }\n \n-static int create_symref_locked(struct ref_lock *lock, const char *refname,\n+static int create_symref_locked(struct files_ref_store *refs,\n+\t\t\t\tstruct ref_lock *lock, const char *refname,\n \t\t\t\tconst char *target, const char *logmsg)\n {\n \tif (prefer_symlink_refs && !create_ref_symlink(lock, target)) {\n-\t\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\t\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \t\treturn 0;\n \t}\n \n@@ -3081,7 +3090,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,\n \t\treturn error(\"unable to fdopen %s: %s\",\n \t\t\t     lock->lk->tempfile.filename.buf, strerror(errno));\n \n-\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \n \t/* no error check; commit_ref will check ferror */\n \tfprintf(lock->lk->tempfile.fp, \"ref: %s\\n\", target);\n@@ -3110,13 +3119,19 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\treturn -1;\n \t}\n \n-\tret = create_symref_locked(lock, refname, target, logmsg);\n+\tret = create_symref_locked(refs, lock, refname, target, logmsg);\n \tunlock_ref(lock);\n \treturn ret;\n }\n \n int set_worktree_head_symref(const char *gitdir, const char *target)\n {\n+\t/*\n+\t * FIXME: this obviously will not work well for future refs\n+\t * backends. This function needs to die.\n+\t */\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3143,7 +3158,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \tlock->lk = &head_lock;\n \tlock->ref_name = xstrdup(head_rel);\n \n-\tret = create_symref_locked(lock, head_rel, target, NULL);\n+\tret = create_symref_locked(refs, lock, head_rel, target, NULL);\n \n \tunlock_ref(lock); /* will free lock */\n \tstrbuf_release(&head_path);\n@@ -3153,14 +3168,13 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3169,13 +3183,12 @@ static int files_reflog_exists(struct ref_store *ref_store,\n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tret = remove_path(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3225,15 +3238,14 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     each_reflog_ent_fn fn,\n \t\t\t\t\t     void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n \tint ret = 0, at_tail = 1;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3334,14 +3346,13 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     const char *refname,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3423,15 +3434,14 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n \n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n-\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\tstrbuf_git_path(&sb, \"logs\");\n+\tfiles_path(refs, &sb, \"logs\");\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n@@ -3856,7 +3866,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t\tif (update->flags & REF_NEEDS_COMMIT ||\n \t\t    update->flags & REF_LOG_ONLY) {\n-\t\t\tif (files_log_ref_write(lock->ref_name,\n+\t\t\tif (files_log_ref_write(refs,\n+\t\t\t\t\t\tlock->ref_name,\n \t\t\t\t\t\tlock->old_oid.hash,\n \t\t\t\t\t\tupdate->new_sha1,\n \t\t\t\t\t\tupdate->msg, update->flags,\n@@ -3894,7 +3905,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n \t\t\t\tstrbuf_reset(&sb);\n-\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tfiles_path(refs, &sb, \"%s\", lock->ref_name);\n \t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n@@ -3917,9 +3928,10 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tfiles_path(refs, &sb, \"logs/%s\", ref_to_delete->string);\n \t\tif (!unlink_or_warn(sb.buf))\n-\t\t\ttry_remove_empty_parents(ref_to_delete->string,\n+\t\t\ttry_remove_empty_parents(refs,\n+\t\t\t\t\t\t ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n \t\tstrbuf_release(&sb);\n \t}\n@@ -3943,7 +3955,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t * can only work because we have already\n \t\t\t * removed the lockfile.)\n \t\t\t */\n-\t\t\ttry_remove_empty_parents(update->refname,\n+\t\t\ttry_remove_empty_parents(refs,\n+\t\t\t\t\t\t update->refname,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REF);\n \t\t}\n \t}\n@@ -4098,6 +4111,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \tint status = 0;\n \tint type;\n \tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tmemset(&cb, 0, sizeof(cb));\n \tcb.flags = flags;\n@@ -4122,7 +4136,8 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tlog_file = git_pathdup(\"logs/%s\", refname);\n+\tfiles_path(refs, &sb, \"logs/%s\", refname);\n+\tlog_file = strbuf_detach(&sb, NULL);\n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n \t\t/*\n \t\t * Even though holding $GIT_DIR/logs/$reflog.lock has\n@@ -4193,25 +4208,24 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"init_db\");\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tfiles_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n-\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tfiles_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tfiles_path(refs, &sb, \"refs/heads\");\n \t\tadjust_shared_perm(sb.buf);\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tfiles_path(refs, &sb, \"refs/tags\");\n \t\tadjust_shared_perm(sb.buf);\n \t}\n \tstrbuf_release(&sb);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312040","messageId":"20170218133303.3682-6-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 05/15] refs.c: share is_per_worktree_ref() to files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:53Z","receivedAt":"2017-02-18T13:34:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 6 ------\n refs/refs-internal.h | 6 ++++++\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 81b64b4ed..c6af84357 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -489,12 +489,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n \treturn logs_found;\n }\n \n-static int is_per_worktree_ref(const char *refname)\n-{\n-\treturn !strcmp(refname, \"HEAD\") ||\n-\t\tstarts_with(refname, \"refs/bisect/\");\n-}\n-\n static int is_pseudoref_syntax(const char *refname)\n {\n \tconst char *c;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f732473e1..8c5febf54 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -657,4 +657,10 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    unsigned char *sha1, int *flags);\n \n+static inline int is_per_worktree_ref(const char *refname)\n+{\n+\treturn !strcmp(refname, \"HEAD\") ||\n+\t\tstarts_with(refname, \"refs/bisect/\");\n+}\n+\n #endif /* REFS_REFS_INTERNAL_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312041","messageId":"20170218133303.3682-7-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 06/15] files-backend: remove the use of git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:54Z","receivedAt":"2017-02-18T13:34:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\ndeciding what goes where. The end goal is to pass $GIT_DIR only. A\nrefs \"view\" of a linked worktree is a logical ref store that combines\ntwo files backends together.\n\n(*) Not entirely true since strbuf_git_path_submodule() still does path\ntranslation underneath. But that's for another patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 37 +++++++++++++++++++++++++++++++++----\n 1 file changed, 33 insertions(+), 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex b599ddf92..dbcaf9bda 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -924,6 +924,9 @@ struct files_ref_store {\n \t */\n \tconst char *submodule;\n \n+\tstruct strbuf gitdir;\n+\tstruct strbuf gitcommondir;\n+\n \tstruct ref_entry *loose;\n \tstruct packed_ref_cache *packed;\n };\n@@ -937,15 +940,33 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n {\n \tstruct strbuf tmp = STRBUF_INIT;\n \tva_list vap;\n+\tconst char *ref;\n \n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n \tva_end(vap);\n-\tif (refs->submodule)\n+\tif (refs->submodule) {\n \t\tstrbuf_git_path_submodule(sb, refs->submodule,\n \t\t\t\t\t  \"%s\", tmp.buf);\n-\telse\n-\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n+\t} else if (!strcmp(tmp.buf, \"packed-refs\") ||\n+\t\t   !strcmp(tmp.buf, \"logs\")) { /* non refname path */\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n+\t} else if (skip_prefix(tmp.buf, \"logs/\", &ref)) { /* reflog */\n+\t\tif (is_per_worktree_ref(ref))\n+\t\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n+\t\telse\n+\t\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n+\t} else {\t\t/* refname */\n+\t\tswitch (ref_type(tmp.buf)) {\n+\t\tcase REF_TYPE_PER_WORKTREE:\n+\t\tcase REF_TYPE_PSEUDOREF:\n+\t\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n+\t\t\tbreak;\n+\t\tcase REF_TYPE_NORMAL:\n+\t\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n \tstrbuf_release(&tmp);\n }\n \n@@ -1004,7 +1025,15 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\trefs->submodule = xstrdup_or_null(submodule);\n+\tstrbuf_init(&refs->gitdir, 0);\n+\tstrbuf_init(&refs->gitcommondir, 0);\n+\n+\tif (submodule) {\n+\t\trefs->submodule = xstrdup(submodule);\n+\t} else {\n+\t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n+\t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n+\t}\n \n \treturn ref_store;\n }\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312042","messageId":"20170218133303.3682-8-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 07/15] refs.c: introduce get_main_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:55Z","receivedAt":"2017-02-18T13:34:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c6af84357..c3bdc99d8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1450,15 +1450,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n+static struct ref_store *get_main_ref_store(void)\n+{\n+\tstruct ref_store *refs;\n+\n+\tif (main_ref_store)\n+\t\treturn main_ref_store;\n+\n+\trefs = ref_store_init(NULL);\n+\treturn refs;\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n-\t\trefs = lookup_ref_store(NULL);\n-\n-\t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL);\n+\t\treturn get_main_ref_store();\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312043","messageId":"20170218133303.3682-9-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 08/15] refs: rename lookup_ref_store() to lookup_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:56Z","receivedAt":"2017-02-18T13:34:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With get_main_ref_store() being used inside get_ref_store(),\nlookup_ref_store() is only used for submodule code path. Rename to\nreflect that and delete dead code.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 13 ++++---------\n 1 file changed, 4 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c3bdc99d8..6aab5e4ac 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1389,17 +1389,13 @@ static struct ref_store *main_ref_store;\n static struct hashmap submodule_ref_stores;\n \n /*\n- * Return the ref_store instance for the specified submodule (or the\n- * main repository if submodule is NULL). If that ref_store hasn't\n- * been initialized yet, return NULL.\n+ * Return the ref_store instance for the specified submodule. If that\n+ * ref_store hasn't been initialized yet, return NULL.\n  */\n-static struct ref_store *lookup_ref_store(const char *submodule)\n+static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n {\n \tstruct submodule_hash_entry *entry;\n \n-\tif (!submodule)\n-\t\treturn main_ref_store;\n-\n \tif (!submodule_ref_stores.tablesize)\n \t\t/* It's initialized on demand in register_ref_store(). */\n \t\treturn NULL;\n@@ -1468,7 +1464,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n \t} else {\n-\t\trefs = lookup_ref_store(submodule);\n+\t\trefs = lookup_submodule_ref_store(submodule);\n \n \t\tif (!refs) {\n \t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1479,7 +1475,6 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\t\tstrbuf_release(&submodule_sb);\n \t\t}\n \t}\n-\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312044","messageId":"20170218133303.3682-10-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 09/15] refs.c: flatten get_ref_store() a bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:57Z","receivedAt":"2017-02-18T13:34:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This helps the future changes in this code. And because get_ref_store()\nis destined to become get_submodule_ref_store(), the \"get main store\"\ncode path will be removed eventually. After this the patch to delete\nthat code will be cleaner.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 19 +++++++++----------\n 1 file changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6aab5e4ac..549eeccb4 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1459,22 +1459,21 @@ static struct ref_store *get_main_ref_store(void)\n \n struct ref_store *get_ref_store(const char *submodule)\n {\n+\tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n-\t} else {\n-\t\trefs = lookup_submodule_ref_store(submodule);\n+\t}\n \n-\t\tif (!refs) {\n-\t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n+\trefs = lookup_submodule_ref_store(submodule);\n+\tif (refs)\n+\t\treturn refs;\n \n-\t\t\tstrbuf_addstr(&submodule_sb, submodule);\n-\t\t\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\t\t\trefs = ref_store_init(submodule);\n-\t\t\tstrbuf_release(&submodule_sb);\n-\t\t}\n-\t}\n+\tstrbuf_addstr(&submodule_sb, submodule);\n+\tif (is_nonbare_repository_dir(&submodule_sb))\n+\t\trefs = ref_store_init(submodule);\n+\tstrbuf_release(&submodule_sb);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312045","messageId":"20170218133303.3682-11-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 10/15] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:58Z","receivedAt":"2017-02-18T13:34:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the last function in this code (besides public API) that takes\nsubmodule argument and handles both main/submodule cases. Break it down,\nmove main store registration in get_main_ref_store() and keep the rest\nin register_submodule_ref_store().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 36 +++++++++++++++++++-----------------\n 1 file changed, 19 insertions(+), 17 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 549eeccb4..8b640bd05 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1407,25 +1407,19 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n \n /*\n  * Register the specified ref_store to be the one that should be used\n- * for submodule (or the main repository if submodule is NULL). It is\n- * a fatal error to call this function twice for the same submodule.\n+ * for submodule. It is a fatal error to call this function twice for\n+ * the same submodule.\n  */\n-static void register_ref_store(struct ref_store *refs, const char *submodule)\n+static void register_submodule_ref_store(struct ref_store *refs,\n+\t\t\t\t\t const char *submodule)\n {\n-\tif (!submodule) {\n-\t\tif (main_ref_store)\n-\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n-\n-\t\tmain_ref_store = refs;\n-\t} else {\n-\t\tif (!submodule_ref_stores.tablesize)\n-\t\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n+\tif (!submodule_ref_stores.tablesize)\n+\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n \n-\t\tif (hashmap_put(&submodule_ref_stores,\n-\t\t\t\talloc_submodule_hash_entry(submodule, refs)))\n-\t\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n-\t\t\t    submodule);\n-\t}\n+\tif (hashmap_put(&submodule_ref_stores,\n+\t\t\talloc_submodule_hash_entry(submodule, refs)))\n+\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n+\t\t    submodule);\n }\n \n /*\n@@ -1442,7 +1436,6 @@ static struct ref_store *ref_store_init(const char *submodule)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n \trefs = be->init(submodule);\n-\tregister_ref_store(refs, submodule);\n \treturn refs;\n }\n \n@@ -1454,6 +1447,12 @@ static struct ref_store *get_main_ref_store(void)\n \t\treturn main_ref_store;\n \n \trefs = ref_store_init(NULL);\n+\tif (refs) {\n+\t\tif (main_ref_store)\n+\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n+\n+\t\tmain_ref_store = refs;\n+\t}\n \treturn refs;\n }\n \n@@ -1474,6 +1473,9 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (is_nonbare_repository_dir(&submodule_sb))\n \t\trefs = ref_store_init(submodule);\n \tstrbuf_release(&submodule_sb);\n+\n+\tif (refs)\n+\t\tregister_submodule_ref_store(refs, submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312046","messageId":"20170218133303.3682-12-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 11/15] refs.c: make get_main_ref_store() public and use it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:32:59Z","receivedAt":"2017-02-18T13:34:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"get_ref_store() will soon be renamed to get_submodule_ref_store().\nTogether with future get_worktree_ref_store(), the three functions\nprovide an appropriate ref store for different operation modes. New APIs\nwill be added to operate directly on ref stores.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 36 ++++++++++++++++++------------------\n refs.h |  2 ++\n 2 files changed, 20 insertions(+), 18 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 8b640bd05..1b60e6180 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1308,7 +1308,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n /* backend functions */\n int refs_init_db(struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->init_db(refs, err);\n }\n@@ -1316,7 +1316,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_ref_store(NULL), refname,\n+\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1439,7 +1439,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n-static struct ref_store *get_main_ref_store(void)\n+struct ref_store *get_main_ref_store(void)\n {\n \tstruct ref_store *refs;\n \n@@ -1488,14 +1488,14 @@ void base_ref_store_init(struct ref_store *refs,\n /* backend functions */\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->pack_refs(refs, flags);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->peel_ref(refs, refname, sha1);\n }\n@@ -1503,7 +1503,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n \t\t\t\t       logmsg);\n@@ -1512,7 +1512,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1522,14 +1522,14 @@ int verify_refname_available(const char *refname,\n \t\t\t     const struct string_list *skip,\n \t\t\t     struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1540,7 +1540,7 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n \t\t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n@@ -1549,14 +1549,14 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_exists(refs, refname);\n }\n@@ -1564,14 +1564,14 @@ int reflog_exists(const char *refname)\n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_reflog(refs, refname);\n }\n@@ -1583,7 +1583,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n \t\t\t\t       prepare_fn, should_prune_fn,\n@@ -1593,21 +1593,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\ndiff --git a/refs.h b/refs.h\nindex 9fbff90e7..a35b687d7 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -555,4 +555,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \n int ref_storage_backend_exists(const char *name);\n \n+struct ref_store *get_main_ref_store(void);\n+\n #endif /* REFS_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312047","messageId":"20170218133303.3682-13-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 12/15] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:33:00Z","receivedAt":"2017-02-18T13:34:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"refs is learning to avoid path rewriting that is done by\nstrbuf_git_path_submodule(). Factor out this code so it could be reused\nby refs*\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n path.c      | 34 +++++++---------------------------\n submodule.c | 31 +++++++++++++++++++++++++++++++\n submodule.h |  1 +\n 3 files changed, 39 insertions(+), 27 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex efcedafba..3451d2916 100644\n--- a/path.c\n+++ b/path.c\n@@ -475,35 +475,16 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n static int do_submodule_path(struct strbuf *buf, const char *path,\n \t\t\t     const char *fmt, va_list args)\n {\n-\tconst char *git_dir;\n \tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n \tstruct strbuf git_submodule_dir = STRBUF_INIT;\n-\tconst struct submodule *sub;\n-\tint err = 0;\n+\tint ret;\n \n-\tstrbuf_addstr(buf, path);\n-\tstrbuf_complete(buf, '/');\n-\tstrbuf_addstr(buf, \".git\");\n-\n-\tgit_dir = read_gitfile(buf->buf);\n-\tif (git_dir) {\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, git_dir);\n-\t}\n-\tif (!is_git_directory(buf->buf)) {\n-\t\tgitmodules_config();\n-\t\tsub = submodule_from_path(null_sha1, path);\n-\t\tif (!sub) {\n-\t\t\terr = SUBMODULE_PATH_ERR_NOT_CONFIGURED;\n-\t\t\tgoto cleanup;\n-\t\t}\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n-\t}\n-\n-\tstrbuf_addch(buf, '/');\n-\tstrbuf_addbuf(&git_submodule_dir, buf);\n+\tret = submodule_to_gitdir(&git_submodule_dir, path);\n+\tif (ret)\n+\t\tgoto cleanup;\n \n+\tstrbuf_complete(&git_submodule_dir, '/');\n+\tstrbuf_addbuf(buf, &git_submodule_dir);\n \tstrbuf_vaddf(buf, fmt, args);\n \n \tif (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))\n@@ -514,8 +495,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,\n cleanup:\n \tstrbuf_release(&git_submodule_dir);\n \tstrbuf_release(&git_submodule_common_dir);\n-\n-\treturn err;\n+\treturn ret;\n }\n \n char *git_pathdup_submodule(const char *path, const char *fmt, ...)\ndiff --git a/submodule.c b/submodule.c\nindex 3b98766a6..36b8d1d11 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1514,3 +1514,34 @@ void absorb_git_dir_into_superproject(const char *prefix,\n \t\tstrbuf_release(&sb);\n \t}\n }\n+\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n+{\n+\tconst struct submodule *sub;\n+\tconst char *git_dir;\n+\tint ret = 0;\n+\n+\tstrbuf_reset(buf);\n+\tstrbuf_addstr(buf, submodule);\n+\tstrbuf_complete(buf, '/');\n+\tstrbuf_addstr(buf, \".git\");\n+\n+\tgit_dir = read_gitfile(buf->buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, git_dir);\n+\t}\n+\tif (!is_git_directory(buf->buf)) {\n+\t\tgitmodules_config();\n+\t\tsub = submodule_from_path(null_sha1, submodule);\n+\t\tif (!sub) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n+\t}\n+\n+cleanup:\n+\treturn ret;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex 05ab674f0..fc3d0303e 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -81,6 +81,7 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n \t\t\t\t    int dry_run);\n extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n extern int parallel_submodules(void);\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n \n /*\n  * Prepare the \"env_array\" parameter of a \"struct child_process\" for executing\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312048","messageId":"20170218133303.3682-14-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 13/15] refs: move submodule code out of files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:33:01Z","receivedAt":"2017-02-18T13:35:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend is now initialized with a $GIT_DIR. Converting a submodule\npath to where real submodule gitdir is located is done in get_ref_store().\n\nThe new code in init_submodule_ref_store() is basically a copy of\nstrbuf_git_path_submodule().\n\nThis gives a slight performance improvement for submodules since we\ndon't convert submodule path to gitdir at every backend call like\nbefore. We pay that once at ref-store creation.\n\nMore cleanup in files_downcast() follows shortly. It's separate to keep\nnoises from this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 18 +++++++++++++-----\n refs/files-backend.c | 24 +++++++-----------------\n refs/refs-internal.h |  6 +++---\n 3 files changed, 23 insertions(+), 25 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 1b60e6180..8035c3ea7 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -9,6 +9,7 @@\n #include \"refs/refs-internal.h\"\n #include \"object.h\"\n #include \"tag.h\"\n+#include \"submodule.h\"\n \n /*\n  * List of all available backends\n@@ -1424,9 +1425,9 @@ static void register_submodule_ref_store(struct ref_store *refs,\n \n /*\n  * Create, record, and return a ref_store instance for the specified\n- * submodule (or the main repository if submodule is NULL).\n+ * gitdir (or the main repository if gitdir is NULL).\n  */\n-static struct ref_store *ref_store_init(const char *submodule)\n+static struct ref_store *ref_store_init(const char *gitdir)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1435,7 +1436,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(submodule);\n+\trefs = be->init(gitdir);\n \treturn refs;\n }\n \n@@ -1460,6 +1461,7 @@ struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n+\tint ret;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n@@ -1470,8 +1472,14 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn refs;\n \n \tstrbuf_addstr(&submodule_sb, submodule);\n-\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\trefs = ref_store_init(submodule);\n+\tret = is_nonbare_repository_dir(&submodule_sb);\n+\tstrbuf_release(&submodule_sb);\n+\tif (!ret)\n+\t\treturn refs;\n+\n+\tret = submodule_to_gitdir(&submodule_sb, submodule);\n+\tif (!ret)\n+\t\trefs = ref_store_init(submodule_sb.buf);\n \tstrbuf_release(&submodule_sb);\n \n \tif (refs)\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex dbcaf9bda..529c80af1 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -917,13 +917,6 @@ struct packed_ref_cache {\n struct files_ref_store {\n \tstruct ref_store base;\n \n-\t/*\n-\t * The name of the submodule represented by this object, or\n-\t * NULL if it represents the main repository's reference\n-\t * store:\n-\t */\n-\tconst char *submodule;\n-\n \tstruct strbuf gitdir;\n \tstruct strbuf gitcommondir;\n \n@@ -945,11 +938,8 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n \tva_start(vap, fmt);\n \tstrbuf_vaddf(&tmp, fmt, vap);\n \tva_end(vap);\n-\tif (refs->submodule) {\n-\t\tstrbuf_git_path_submodule(sb, refs->submodule,\n-\t\t\t\t\t  \"%s\", tmp.buf);\n-\t} else if (!strcmp(tmp.buf, \"packed-refs\") ||\n-\t\t   !strcmp(tmp.buf, \"logs\")) { /* non refname path */\n+\tif (!strcmp(tmp.buf, \"packed-refs\") || !strcmp(tmp.buf, \"logs\")) {\n+\t\t/* non refname path */\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n \t} else if (skip_prefix(tmp.buf, \"logs/\", &ref)) { /* reflog */\n \t\tif (is_per_worktree_ref(ref))\n@@ -1018,7 +1008,7 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *submodule)\n+static struct ref_store *files_ref_store_create(const char *gitdir)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n@@ -1028,8 +1018,9 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \tstrbuf_init(&refs->gitdir, 0);\n \tstrbuf_init(&refs->gitcommondir, 0);\n \n-\tif (submodule) {\n-\t\trefs->submodule = xstrdup(submodule);\n+\tif (gitdir) {\n+\t\tstrbuf_addstr(&refs->gitdir, gitdir);\n+\t\tget_common_dir_noenv(&refs->gitcommondir, gitdir);\n \t} else {\n \t\tstrbuf_addstr(&refs->gitdir, get_git_dir());\n \t\tstrbuf_addstr(&refs->gitcommondir, get_git_common_dir());\n@@ -1045,8 +1036,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\tif (refs->submodule)\n-\t\tdie(\"BUG: %s called for a submodule\", caller);\n+\t/* This function is to be deleted in the next patch */\n }\n \n /*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 8c5febf54..3ef020acf 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -482,12 +482,12 @@ struct ref_store;\n /* refs backends */\n \n /*\n- * Initialize the ref_store for the specified submodule, or for the\n- * main repository if submodule == NULL. These functions should call\n+ * Initialize the ref_store for the specified gitdir, or for the\n+ * current repository if gitdir == NULL. These functions should call\n  * base_ref_store_init() to initialize the shared part of the\n  * ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *submodule);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312049","messageId":"20170218133303.3682-15-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 14/15] files-backend: remove submodule_allowed from files_downcast()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:33:02Z","receivedAt":"2017-02-18T13:35:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since submodule or not is irrelevant to files-backend after the last\npatch, remove the parameter from files_downcast() and kill\nfiles_assert_main_repository().\n\nPS. This implies that all ref operations are allowed for submodules. But\nwe may need to look more closely to see if that's really true...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 70 ++++++++++++++++------------------------------------\n 1 file changed, 21 insertions(+), 49 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 529c80af1..b8ccf4e47 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1030,24 +1030,13 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n }\n \n /*\n- * Die if refs is for a submodule (i.e., not for the main repository).\n- * caller is used in any necessary error messages.\n- */\n-static void files_assert_main_repository(struct files_ref_store *refs,\n-\t\t\t\t\t const char *caller)\n-{\n-\t/* This function is to be deleted in the next patch */\n-}\n-\n-/*\n  * Downcast ref_store to files_ref_store. Die if ref_store is not a\n  * files_ref_store. If submodule_allowed is not true, then also die if\n  * files_ref_store is for a submodule (i.e., not for the main\n  * repository). caller is used in any necessary error messages.\n  */\n-static struct files_ref_store *files_downcast(\n-\t\tstruct ref_store *ref_store, int submodule_allowed,\n-\t\tconst char *caller)\n+static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n+\t\t\t\t\t      const char *caller)\n {\n \tstruct files_ref_store *refs;\n \n@@ -1057,9 +1046,6 @@ static struct files_ref_store *files_downcast(\n \n \trefs = (struct files_ref_store *)ref_store;\n \n-\tif (!submodule_allowed)\n-\t\tfiles_assert_main_repository(refs, caller);\n-\n \treturn refs;\n }\n \n@@ -1395,7 +1381,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t\t\t      struct strbuf *referent, unsigned int *type)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"read_raw_ref\");\n+\t\tfiles_downcast(ref_store, \"read_raw_ref\");\n \tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n \tconst char *path;\n@@ -1588,7 +1574,6 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \tint ret = TRANSACTION_GENERIC_ERROR;\n \n \tassert(err);\n-\tfiles_assert_main_repository(refs, \"lock_raw_ref\");\n \n \t*type = 0;\n \n@@ -1812,7 +1797,7 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n static int files_peel_ref(struct ref_store *ref_store,\n \t\t\t  const char *refname, unsigned char *sha1)\n {\n-\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"peel_ref\");\n \tint flag;\n \tunsigned char base[20];\n \n@@ -1921,7 +1906,7 @@ static struct ref_iterator *files_ref_iterator_begin(\n \t\tconst char *prefix, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"ref_iterator_begin\");\n \tstruct ref_dir *loose_dir, *packed_dir;\n \tstruct ref_iterator *loose_iter, *packed_iter;\n \tstruct files_ref_iterator *iter;\n@@ -2059,7 +2044,6 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tint resolve_flags = RESOLVE_REF_NO_RECURSE;\n \tint resolved;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_sha1_basic\");\n \tassert(err);\n \n \tlock = xcalloc(1, sizeof(struct ref_lock));\n@@ -2184,8 +2168,6 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tfiles_assert_main_repository(refs, \"lock_packed_refs\");\n-\n \tif (!timeout_configured) {\n \t\tgit_config_get_int(\"core.packedrefstimeout\", &timeout_value);\n \t\ttimeout_configured = 1;\n@@ -2225,8 +2207,6 @@ static int commit_packed_refs(struct files_ref_store *refs)\n \tint save_errno = 0;\n \tFILE *out;\n \n-\tfiles_assert_main_repository(refs, \"commit_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \n@@ -2258,8 +2238,6 @@ static void rollback_packed_refs(struct files_ref_store *refs)\n \tstruct packed_ref_cache *packed_ref_cache =\n \t\tget_packed_ref_cache(refs);\n \n-\tfiles_assert_main_repository(refs, \"rollback_packed_refs\");\n-\n \tif (!packed_ref_cache->lock)\n \t\tdie(\"internal error: packed-refs not locked\");\n \trollback_lock_file(packed_ref_cache->lock);\n@@ -2420,7 +2398,7 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"pack_refs\");\n+\t\tfiles_downcast(ref_store, \"pack_refs\");\n \tstruct pack_refs_cb_data cbdata;\n \n \tmemset(&cbdata, 0, sizeof(cbdata));\n@@ -2453,7 +2431,6 @@ static int repack_without_refs(struct files_ref_store *refs,\n \tstruct string_list_item *refname;\n \tint ret, needs_repacking = 0, removed = 0;\n \n-\tfiles_assert_main_repository(refs, \"repack_without_refs\");\n \tassert(err);\n \n \t/* Look for a packed ref */\n@@ -2503,7 +2480,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \t\t\t     struct string_list *refnames, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_refs\");\n+\t\tfiles_downcast(ref_store, \"delete_refs\");\n \tstruct strbuf err = STRBUF_INIT;\n \tint i, result = 0;\n \n@@ -2615,7 +2592,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,\n \t\t\t\t\t  struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"verify_refname_available\");\n+\t\tfiles_downcast(ref_store, \"verify_refname_available\");\n \tstruct ref_dir *packed_refs = get_packed_refs(refs);\n \tstruct ref_dir *loose_refs = get_loose_refs(refs);\n \n@@ -2640,7 +2617,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"rename_ref\");\n+\t\tfiles_downcast(ref_store, \"rename_ref\");\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n@@ -2883,7 +2860,7 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n+\t\tfiles_downcast(ref_store, \"create_reflog\");\n \tint fd;\n \n \tif (log_ref_setup(refs, refname, force_create, &fd, err))\n@@ -3014,8 +2991,6 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\t\t     const unsigned char *sha1, const char *logmsg,\n \t\t\t     struct strbuf *err)\n {\n-\tfiles_assert_main_repository(refs, \"commit_ref_update\");\n-\n \tclear_loose_ref_cache(refs);\n \tif (files_log_ref_write(refs, lock->ref_name, lock->old_oid.hash,\n \t\t\t\tsha1, logmsg, 0, err)) {\n@@ -3124,7 +3099,7 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\t\t       const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_symref\");\n+\t\tfiles_downcast(ref_store, \"create_symref\");\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_lock *lock;\n \tint ret;\n@@ -3150,7 +3125,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3188,7 +3163,7 @@ static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n+\t\tfiles_downcast(ref_store, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n@@ -3203,7 +3178,7 @@ static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n+\t\tfiles_downcast(ref_store, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n@@ -3258,7 +3233,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n@@ -3366,7 +3341,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n+\t\tfiles_downcast(ref_store, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n@@ -3454,7 +3429,7 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n+\t\tfiles_downcast(ref_store, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -3666,8 +3641,6 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \tint ret;\n \tstruct ref_lock *lock;\n \n-\tfiles_assert_main_repository(refs, \"lock_ref_for_update\");\n-\n \tif ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))\n \t\tupdate->flags |= REF_DELETING;\n \n@@ -3792,7 +3765,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list refs_to_delete = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *ref_to_delete;\n@@ -4000,7 +3973,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"initial_ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, \"initial_ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list affected_refnames = STRING_LIST_INIT_NODUP;\n \n@@ -4122,7 +4095,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\t       void *policy_cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_expire\");\n+\t\tfiles_downcast(ref_store, \"reflog_expire\");\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n@@ -4227,8 +4200,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n-\tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"init_db\");\n+\tstruct files_ref_store *refs = files_downcast(ref_store, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n \t/*\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312050","messageId":"20170218133303.3682-16-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v4 15/15] refs: rename get_ref_store() to get_submodule_ref_store() and make it public","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-18T13:33:03Z","receivedAt":"2017-02-18T13:35:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is intended to replace *_submodule() refs API. It provides\na ref store for a specific submodule, which can be operated on by a new\nset of refs API.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 12 ++++++++----\n refs.h               | 11 +++++++++++\n refs/files-backend.c |  2 +-\n refs/refs-internal.h | 12 ------------\n 4 files changed, 20 insertions(+), 17 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 8035c3ea7..bc2247b59 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1165,7 +1165,7 @@ int head_ref(each_ref_fn fn, void *cb_data)\n static int do_for_each_ref(const char *submodule, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(submodule);\n+\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1338,10 +1338,10 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \t\t/* We need to strip off one or more trailing slashes */\n \t\tchar *stripped = xmemdupz(submodule, len);\n \n-\t\trefs = get_ref_store(stripped);\n+\t\trefs = get_submodule_ref_store(stripped);\n \t\tfree(stripped);\n \t} else {\n-\t\trefs = get_ref_store(submodule);\n+\t\trefs = get_submodule_ref_store(submodule);\n \t}\n \n \tif (!refs)\n@@ -1457,13 +1457,17 @@ struct ref_store *get_main_ref_store(void)\n \treturn refs;\n }\n \n-struct ref_store *get_ref_store(const char *submodule)\n+struct ref_store *get_submodule_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \tint ret;\n \n \tif (!submodule || !*submodule) {\n+\t\t/*\n+\t\t * FIXME: This case is ideally not allowed. But that\n+\t\t * can't happen until we clean up all the callers.\n+\t\t */\n \t\treturn get_main_ref_store();\n \t}\n \ndiff --git a/refs.h b/refs.h\nindex a35b687d7..5bd065f12 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -556,5 +556,16 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int ref_storage_backend_exists(const char *name);\n \n struct ref_store *get_main_ref_store(void);\n+/*\n+ * Return the ref_store instance for the specified submodule. For the\n+ * main repository, use submodule==NULL; such a call cannot fail. For\n+ * a submodule, the submodule must exist and be a nonbare repository,\n+ * otherwise return NULL. If the requested reference store has not yet\n+ * been initialized, initialize it first.\n+ *\n+ * For backwards compatibility, submodule==\"\" is treated the same as\n+ * submodule==NULL.\n+ */\n+struct ref_store *get_submodule_ref_store(const char *submodule);\n \n #endif /* REFS_H */\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex b8ccf4e47..603601f1b 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3125,7 +3125,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(), \"set_head_symref\");\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 3ef020acf..ed6527ce1 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -640,18 +640,6 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-/*\n- * Return the ref_store instance for the specified submodule. For the\n- * main repository, use submodule==NULL; such a call cannot fail. For\n- * a submodule, the submodule must exist and be a nonbare repository,\n- * otherwise return NULL. If the requested reference store has not yet\n- * been initialized, initialize it first.\n- *\n- * For backwards compatibility, submodule==\"\" is treated the same as\n- * submodule==NULL.\n- */\n-struct ref_store *get_ref_store(const char *submodule);\n-\n const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312135","messageId":"330bfd35-ec00-92a2-a221-c7be9f0199e5@alum.mit.edu","threadId":"45126","inReplyTo":"20170216114818.6080-5-pclouds@gmail.com","subject":"Re: [PATCH v2 04/16] files-backend: replace *git_path*() with files_path()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-20T11:23:52Z","receivedAt":"2017-02-20T11:24:08Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/16/2017 12:48 PM, Nguyễn Thái Ngọc Duy wrote:\n> This centralizes all path rewriting of files-backend.c in one place so\n> we have easier time removing the path rewriting later. There could be\n> some hidden indirect git_path() though, I didn't audit the code to the\n> bottom.\n\nAlmost all of the calls to `files_path()` [1] take one of the following\nforms:\n\n* `files_path(refs, &sb, \"packed-refs\")`\n* `files_path(refs, &sb, \"%s\", refname)`\n* `files_path(refs, &sb, \"logs/%s\", refname)`\n\n(though sometimes `refname` is not the name of a reference but rather\nthe name of a directory containing references, like \"refs/heads\").\n\nThis suggests to me that it would be more natural to have three\nfunctions, `files_packed_refs_path()`, `files_loose_ref_path()`, and\n`files_reflog_path()`, with no `fmt` arguments. Aside from making the\ncallers a bit simpler and the implementation of each of the three\nfunctions simpler (they wouldn't have to deal with variable argument\nlists), at the cost of needing three similar functions.\n\nBut I think the split would also be advantageous from a design point of\nview. The relative path locations of loose reference files, reflog\nfiles, and the packed-refs file is kind of a coincidence, and it would\nbe advantageous to encode that policy in three functions rather than\ncontinuing to spread knowledge of those assumptions around.\n\nIt would also make it easier to switch to a new system of encoding\nreference names, for example so that reference names that differ only in\ncase can be stored on a case-insensitive filesystem, or to store reflogs\nusing a naming scheme that is not susceptible to D/F conflicts so that\nwe can retain reflogs for deleted references.\n\nMichael\n\n[1] The only exception I see is one call `files_path(refs, &sb,\n\"logs\")`, which is a prelude to iterating over the reflog files. This is\nactually an example of the code giving us a hint that the design is\nwrong: if you iterate only over the directories under `git_path(logs)`,\nyou miss the reflogs for worktree references.\n\n"},{"id":"312136","messageId":"1d750672-441d-14ae-21e9-d7bdd47a50a4@alum.mit.edu","threadId":"45126","inReplyTo":"20170218133303.3682-7-pclouds@gmail.com","subject":"Re: [PATCH v4 06/15] files-backend: remove the use of git_path()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-20T11:34:24Z","receivedAt":"2017-02-20T11:34:39Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/18/2017 02:32 PM, Nguyễn Thái Ngọc Duy wrote:\n> Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\n> deciding what goes where. The end goal is to pass $GIT_DIR only. A\n> refs \"view\" of a linked worktree is a logical ref store that combines\n> two files backends together.\n> \n> (*) Not entirely true since strbuf_git_path_submodule() still does path\n> translation underneath. But that's for another patch.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 37 +++++++++++++++++++++++++++++++++----\n>  1 file changed, 33 insertions(+), 4 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index b599ddf92..dbcaf9bda 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -924,6 +924,9 @@ struct files_ref_store {\n>  \t */\n>  \tconst char *submodule;\n>  \n> +\tstruct strbuf gitdir;\n> +\tstruct strbuf gitcommondir;\n\nIs there a reason for these to be `strbuf`s rather than `const char *`?\n(One reason would be if you planned to use the `len` field, but I don't\nthink you do so.)\n\n> @@ -937,15 +940,33 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n>  {\n>  \tstruct strbuf tmp = STRBUF_INIT;\n>  \tva_list vap;\n> +\tconst char *ref;\n>  \n>  \tva_start(vap, fmt);\n>  \tstrbuf_vaddf(&tmp, fmt, vap);\n>  \tva_end(vap);\n> -\tif (refs->submodule)\n> +\tif (refs->submodule) {\n>  \t\tstrbuf_git_path_submodule(sb, refs->submodule,\n>  \t\t\t\t\t  \"%s\", tmp.buf);\n> -\telse\n> -\t\tstrbuf_git_path(sb, \"%s\", tmp.buf);\n> +\t} else if (!strcmp(tmp.buf, \"packed-refs\") ||\n> +\t\t   !strcmp(tmp.buf, \"logs\")) { /* non refname path */\n> +\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n> +\t} else if (skip_prefix(tmp.buf, \"logs/\", &ref)) { /* reflog */\n> +\t\tif (is_per_worktree_ref(ref))\n> +\t\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n> +\t\telse\n> +\t\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n\nThis code would also be simpler if there were separate functions for\npacked-refs, loose references, and reflogs.\n\n> [...]\n\nMichael\n\n"},{"id":"312139","messageId":"25fcb527-595a-7865-41e3-ee7c4c1ad668@alum.mit.edu","threadId":"45126","inReplyTo":"20170218133303.3682-15-pclouds@gmail.com","subject":"Re: [PATCH v4 14/15] files-backend: remove submodule_allowed from files_downcast()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-20T12:11:01Z","receivedAt":"2017-02-20T12:11:21Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/18/2017 02:33 PM, Nguyễn Thái Ngọc Duy wrote:\n> Since submodule or not is irrelevant to files-backend after the last\n> patch, remove the parameter from files_downcast() and kill\n> files_assert_main_repository().\n> \n> PS. This implies that all ref operations are allowed for submodules. But\n> we may need to look more closely to see if that's really true...\n\nI think you are jumping the gun here.\n\nEven after your changes, there is still a significant difference between\nthe main repository and submodules: we have access to the object\ndatabase for the main repository, but not for submodules.\n\nSo, for example, the following don't work for submodules:\n\n* `parse_object()` is used to ensure that references are only pointed at\nvalid objects, and that branches are only pointed at commit objects.\n\n* `peel_object()` is used to write the peeled version of references in\n`packed-refs`, and to peel references while they are being iterated\nover. Since this doesn't work, I think you can't write `packed-refs` in\na submodule.\n\nThese limitations, I think, imply that submodule references have to be\ntreated as read-only.\n\nWhen I was working on a patch series similar to yours, I introduced a\nboolean \"main_repository\" flag in `struct ref_store`, and use that\nmember to implement `files_assert_main_repository()`. That way\n`files_ref_store::submodule` can still be removed, which is the more\nimportant goal from a design standpoint.\n\nMichael\n\n"},{"id":"312140","messageId":"CACsJy8AZ27O-pxTqHOzYXRBuyv8dkxdGJ_5Z0u3eaxkNdnaEYA@mail.gmail.com","threadId":"45126","inReplyTo":"25fcb527-595a-7865-41e3-ee7c4c1ad668@alum.mit.edu","subject":"Re: [PATCH v4 14/15] files-backend: remove submodule_allowed from files_downcast()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-20T12:21:54Z","receivedAt":"2017-02-20T12:22:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 20, 2017 at 7:11 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/18/2017 02:33 PM, Nguyễn Thái Ngọc Duy wrote:\n>> Since submodule or not is irrelevant to files-backend after the last\n>> patch, remove the parameter from files_downcast() and kill\n>> files_assert_main_repository().\n>>\n>> PS. This implies that all ref operations are allowed for submodules. But\n>> we may need to look more closely to see if that's really true...\n>\n> I think you are jumping the gun here.\n>\n> Even after your changes, there is still a significant difference between\n> the main repository and submodules: we have access to the object\n> database for the main repository, but not for submodules.\n>\n> So, for example, the following don't work for submodules:\n>\n> * `parse_object()` is used to ensure that references are only pointed at\n> valid objects, and that branches are only pointed at commit objects.\n>\n> * `peel_object()` is used to write the peeled version of references in\n> `packed-refs`, and to peel references while they are being iterated\n> over. Since this doesn't work, I think you can't write `packed-refs` in\n> a submodule.\n>\n> These limitations, I think, imply that submodule references have to be\n> treated as read-only.\n\nBehind the scene submodule does add_submodule_odb(), which basically\nmakes the submodule's odb an alternate of in-core odb. So odb access\nworks. I was puzzled how submodules could by pass odb access at the\nbeginning only to find that out. technical/api-ref-iteration.txt also\nmentions that you need to add_submodule_odb(), so I think it's\ndeliberate (albeit hacky) design.\n\n> When I was working on a patch series similar to yours, I introduced a\n> boolean \"main_repository\" flag in `struct ref_store`, and use that\n> member to implement `files_assert_main_repository()`. That way\n> `files_ref_store::submodule` can still be removed, which is the more\n> important goal from a design standpoint.\n\nI could keep the submodule check back (and replace the submodule\nstring in files_ref_store with just a flag). But I really think all\nbackend functions work with submodule. Perhaps add some tests to\nexercise/verify that files-backend-on-submodule works?\n-- \nDuy\n"},{"id":"312141","messageId":"CACsJy8AJuosFnamsJT8BWGcicFpZF=5gdifhazAigrdQ7R2vXA@mail.gmail.com","threadId":"45126","inReplyTo":"330bfd35-ec00-92a2-a221-c7be9f0199e5@alum.mit.edu","subject":"Re: [PATCH v2 04/16] files-backend: replace *git_path*() with files_path()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-20T12:25:50Z","receivedAt":"2017-02-20T12:26:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 20, 2017 at 6:23 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/16/2017 12:48 PM, Nguyễn Thái Ngọc Duy wrote:\n>> This centralizes all path rewriting of files-backend.c in one place so\n>> we have easier time removing the path rewriting later. There could be\n>> some hidden indirect git_path() though, I didn't audit the code to the\n>> bottom.\n>\n> Almost all of the calls to `files_path()` [1] take one of the following\n> forms:\n>\n> * `files_path(refs, &sb, \"packed-refs\")`\n> * `files_path(refs, &sb, \"%s\", refname)`\n> * `files_path(refs, &sb, \"logs/%s\", refname)`\n>\n> (though sometimes `refname` is not the name of a reference but rather\n> the name of a directory containing references, like \"refs/heads\").\n>\n> This suggests to me that it would be more natural to have three\n> functions, `files_packed_refs_path()`, `files_loose_ref_path()`, and\n> `files_reflog_path()`, with no `fmt` arguments. Aside from making the\n> callers a bit simpler and the implementation of each of the three\n> functions simpler (they wouldn't have to deal with variable argument\n> lists), at the cost of needing three similar functions.\n>\n> But I think the split would also be advantageous from a design point of\n> view. The relative path locations of loose reference files, reflog\n> files, and the packed-refs file is kind of a coincidence, and it would\n> be advantageous to encode that policy in three functions rather than\n> continuing to spread knowledge of those assumptions around.\n>\n> It would also make it easier to switch to a new system of encoding\n> reference names, for example so that reference names that differ only in\n> case can be stored on a case-insensitive filesystem, or to store reflogs\n> using a naming scheme that is not susceptible to D/F conflicts so that\n> we can retain reflogs for deleted references.\n\nI agree. I didn't see it clearly at the beginning (and made several\nmistakes in earlier iterations) but as you have seen files_path() the\nseparation is pretty clear there. I was going to do it when\nintroducing the \"linked worktree\" backend. But since you pointed it\nout, I'll update it in this series too.\n\n> Michael\n>\n> [1] The only exception I see is one call `files_path(refs, &sb,\n> \"logs\")`, which is a prelude to iterating over the reflog files. This is\n> actually an example of the code giving us a hint that the design is\n> wrong: if you iterate only over the directories under `git_path(logs)`,\n> you miss the reflogs for worktree references.\n\nOh yes, I had to fix the reflog iterator [1] exactly for that :)\n\n[1] https://public-inbox.org/git/20170217141908.18012-14-pclouds@gmail.com/T/#u\n-- \nDuy\n"},{"id":"312143","messageId":"c4265faf-a04a-552b-fd72-1631a220f788@alum.mit.edu","threadId":"45126","inReplyTo":"CACsJy8AZ27O-pxTqHOzYXRBuyv8dkxdGJ_5Z0u3eaxkNdnaEYA@mail.gmail.com","subject":"Re: [PATCH v4 14/15] files-backend: remove submodule_allowed from files_downcast()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-20T12:30:41Z","receivedAt":"2017-02-20T12:30:57Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/20/2017 01:21 PM, Duy Nguyen wrote:\n> On Mon, Feb 20, 2017 at 7:11 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> On 02/18/2017 02:33 PM, Nguyễn Thái Ngọc Duy wrote:\n>>> Since submodule or not is irrelevant to files-backend after the last\n>>> patch, remove the parameter from files_downcast() and kill\n>>> files_assert_main_repository().\n>>>\n>>> PS. This implies that all ref operations are allowed for submodules. But\n>>> we may need to look more closely to see if that's really true...\n>>\n>> I think you are jumping the gun here.\n>>\n>> Even after your changes, there is still a significant difference between\n>> the main repository and submodules: we have access to the object\n>> database for the main repository, but not for submodules.\n>>\n>> So, for example, the following don't work for submodules:\n>>\n>> * `parse_object()` is used to ensure that references are only pointed at\n>> valid objects, and that branches are only pointed at commit objects.\n>>\n>> * `peel_object()` is used to write the peeled version of references in\n>> `packed-refs`, and to peel references while they are being iterated\n>> over. Since this doesn't work, I think you can't write `packed-refs` in\n>> a submodule.\n>>\n>> These limitations, I think, imply that submodule references have to be\n>> treated as read-only.\n> \n> Behind the scene submodule does add_submodule_odb(), which basically\n> makes the submodule's odb an alternate of in-core odb. So odb access\n> works. I was puzzled how submodules could by pass odb access at the\n> beginning only to find that out. technical/api-ref-iteration.txt also\n> mentions that you need to add_submodule_odb(), so I think it's\n> deliberate (albeit hacky) design.\n\nThat's interesting. I didn't know it before.\n\nBut I still don't think that means that reference writing can work\ncorrectly. If I try to set a submodule branch to an SHA-1 and I verify\nthat the SHA-1 points to a valid commit, how do I know that the commit\nis in the same submodule that holds the reference?\n\n> [...]\n\nMichael\n"},{"id":"312144","messageId":"CACsJy8Dk+DY0Evf0_P9mg_ezkTWJDL-eDdXChrp8K1P5C1rQdg@mail.gmail.com","threadId":"45126","inReplyTo":"1d750672-441d-14ae-21e9-d7bdd47a50a4@alum.mit.edu","subject":"Re: [PATCH v4 06/15] files-backend: remove the use of git_path()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-20T12:31:38Z","receivedAt":"2017-02-20T12:32:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 20, 2017 at 6:34 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/18/2017 02:32 PM, Nguyễn Thái Ngọc Duy wrote:\n>> Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\n>> deciding what goes where. The end goal is to pass $GIT_DIR only. A\n>> refs \"view\" of a linked worktree is a logical ref store that combines\n>> two files backends together.\n>>\n>> (*) Not entirely true since strbuf_git_path_submodule() still does path\n>> translation underneath. But that's for another patch.\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs/files-backend.c | 37 +++++++++++++++++++++++++++++++++----\n>>  1 file changed, 33 insertions(+), 4 deletions(-)\n>>\n>> diff --git a/refs/files-backend.c b/refs/files-backend.c\n>> index b599ddf92..dbcaf9bda 100644\n>> --- a/refs/files-backend.c\n>> +++ b/refs/files-backend.c\n>> @@ -924,6 +924,9 @@ struct files_ref_store {\n>>        */\n>>       const char *submodule;\n>>\n>> +     struct strbuf gitdir;\n>> +     struct strbuf gitcommondir;\n>\n> Is there a reason for these to be `strbuf`s rather than `const char *`?\n> (One reason would be if you planned to use the `len` field, but I don't\n> think you do so.)\n\nNope. I just didn't think about char *. It may have to lose \"const\"\nthough because in submodule case we may need a new allocation.\n\n>\n>> @@ -937,15 +940,33 @@ static void files_path(struct files_ref_store *refs, struct strbuf *sb,\n>>  {\n>>       struct strbuf tmp = STRBUF_INIT;\n>>       va_list vap;\n>> +     const char *ref;\n>>\n>>       va_start(vap, fmt);\n>>       strbuf_vaddf(&tmp, fmt, vap);\n>>       va_end(vap);\n>> -     if (refs->submodule)\n>> +     if (refs->submodule) {\n>>               strbuf_git_path_submodule(sb, refs->submodule,\n>>                                         \"%s\", tmp.buf);\n>> -     else\n>> -             strbuf_git_path(sb, \"%s\", tmp.buf);\n>> +     } else if (!strcmp(tmp.buf, \"packed-refs\") ||\n>> +                !strcmp(tmp.buf, \"logs\")) { /* non refname path */\n>> +             strbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n>> +     } else if (skip_prefix(tmp.buf, \"logs/\", &ref)) { /* reflog */\n>> +             if (is_per_worktree_ref(ref))\n>> +                     strbuf_addf(sb, \"%s/%s\", refs->gitdir.buf, tmp.buf);\n>> +             else\n>> +                     strbuf_addf(sb, \"%s/%s\", refs->gitcommondir.buf, tmp.buf);\n>\n> This code would also be simpler if there were separate functions for\n> packed-refs, loose references, and reflogs.\n\nAnd maybe keep the path to packed-refs, the base path up to \"logs\" in\nstruct files_ref_store too (they will be calculated at ref store\ninit)? That way the files_packed_refs_path() does no calculation.\nfiles_reflog_path() and files_ref_path() will just do string\nconcatenation, no fancy addf.\n-- \nDuy\n"},{"id":"312145","messageId":"CACsJy8ChAwwPawTrq4gqZqAO3k6PrgzzvceVFjbJmxkE0Rn8SQ@mail.gmail.com","threadId":"45126","inReplyTo":"c4265faf-a04a-552b-fd72-1631a220f788@alum.mit.edu","subject":"Re: [PATCH v4 14/15] files-backend: remove submodule_allowed from files_downcast()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-20T12:33:30Z","receivedAt":"2017-02-20T12:34:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 20, 2017 at 7:30 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/20/2017 01:21 PM, Duy Nguyen wrote:\n>> On Mon, Feb 20, 2017 at 7:11 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>>> On 02/18/2017 02:33 PM, Nguyễn Thái Ngọc Duy wrote:\n>>>> Since submodule or not is irrelevant to files-backend after the last\n>>>> patch, remove the parameter from files_downcast() and kill\n>>>> files_assert_main_repository().\n>>>>\n>>>> PS. This implies that all ref operations are allowed for submodules. But\n>>>> we may need to look more closely to see if that's really true...\n>>>\n>>> I think you are jumping the gun here.\n>>>\n>>> Even after your changes, there is still a significant difference between\n>>> the main repository and submodules: we have access to the object\n>>> database for the main repository, but not for submodules.\n>>>\n>>> So, for example, the following don't work for submodules:\n>>>\n>>> * `parse_object()` is used to ensure that references are only pointed at\n>>> valid objects, and that branches are only pointed at commit objects.\n>>>\n>>> * `peel_object()` is used to write the peeled version of references in\n>>> `packed-refs`, and to peel references while they are being iterated\n>>> over. Since this doesn't work, I think you can't write `packed-refs` in\n>>> a submodule.\n>>>\n>>> These limitations, I think, imply that submodule references have to be\n>>> treated as read-only.\n>>\n>> Behind the scene submodule does add_submodule_odb(), which basically\n>> makes the submodule's odb an alternate of in-core odb. So odb access\n>> works. I was puzzled how submodules could by pass odb access at the\n>> beginning only to find that out. technical/api-ref-iteration.txt also\n>> mentions that you need to add_submodule_odb(), so I think it's\n>> deliberate (albeit hacky) design.\n>\n> That's interesting. I didn't know it before.\n>\n> But I still don't think that means that reference writing can work\n> correctly. If I try to set a submodule branch to an SHA-1 and I verify\n> that the SHA-1 points to a valid commit, how do I know that the commit\n> is in the same submodule that holds the reference?\n\nGood point. So will the new flag be \"read_only\" (no reference to\nsubmodule), or \"submodule\"? This flag will be passed in at ref-store\ninit time and kept in files_ref_store.\n-- \nDuy\n"},{"id":"312146","messageId":"ded1a8c2-1792-b755-9ce3-923762c29d62@alum.mit.edu","threadId":"45126","inReplyTo":"CACsJy8ChAwwPawTrq4gqZqAO3k6PrgzzvceVFjbJmxkE0Rn8SQ@mail.gmail.com","subject":"Re: [PATCH v4 14/15] files-backend: remove submodule_allowed from files_downcast()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-20T12:38:25Z","receivedAt":"2017-02-20T12:39:41Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/20/2017 01:33 PM, Duy Nguyen wrote:\n> On Mon, Feb 20, 2017 at 7:30 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> On 02/20/2017 01:21 PM, Duy Nguyen wrote:\n>>> On Mon, Feb 20, 2017 at 7:11 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>>>> [...]\n>>>> These limitations, I think, imply that submodule references have to be\n>>>> treated as read-only.\n>>>\n>>> Behind the scene submodule does add_submodule_odb(), which basically\n>>> makes the submodule's odb an alternate of in-core odb. So odb access\n>>> works. I was puzzled how submodules could by pass odb access at the\n>>> beginning only to find that out. technical/api-ref-iteration.txt also\n>>> mentions that you need to add_submodule_odb(), so I think it's\n>>> deliberate (albeit hacky) design.\n>>\n>> That's interesting. I didn't know it before.\n>>\n>> But I still don't think that means that reference writing can work\n>> correctly. If I try to set a submodule branch to an SHA-1 and I verify\n>> that the SHA-1 points to a valid commit, how do I know that the commit\n>> is in the same submodule that holds the reference?\n> \n> Good point. So will the new flag be \"read_only\" (no reference to\n> submodule), or \"submodule\"? This flag will be passed in at ref-store\n> init time and kept in files_ref_store.\n\nI haven't checked carefully whether there are other operations that\ndon't work and/or don't make sense for submodules. If not, then\n`read_only` would also be a fine name for the flag.\n\nMichael\n\n"},{"id":"312147","messageId":"8f0c7355-008e-8435-b6a7-1d3b4c2ae06a@alum.mit.edu","threadId":"45126","inReplyTo":"20170218133303.3682-12-pclouds@gmail.com","subject":"Re: [PATCH v4 11/15] refs.c: make get_main_ref_store() public and use it","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-20T12:37:44Z","receivedAt":"2017-02-20T12:40:20Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/18/2017 02:32 PM, Nguyễn Thái Ngọc Duy wrote:\n> get_ref_store() will soon be renamed to get_submodule_ref_store().\n> Together with future get_worktree_ref_store(), the three functions\n> provide an appropriate ref store for different operation modes. New APIs\n> will be added to operate directly on ref stores.\n\nI see where you're going with this, but as of the end of this patch\nseries, there is still nothing that a caller outside of the refs module\ncan do with a `struct ref_store *`. This means that it would be enough\nto put this declaration (and that of `get_submodule_ref_store()`, added\nin a later patch) in refs/refs-internal.h for now.\n\nIf you want to move the declarations straight to `refs.h` now to avoid\ncode churn in some later patch series, then please mention that fact in\nthe commit message.\n\nMichael\n\n"},{"id":"312150","messageId":"cdf01f55-f3df-d3ce-149c-0249a03d27bf@alum.mit.edu","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"Re: [PATCH v4 00/15] Remove submodule from files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-20T12:42:21Z","receivedAt":"2017-02-20T12:44:32Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/18/2017 02:32 PM, Nguyễn Thái Ngọc Duy wrote:\n> v4:\n\nI very much like the direction of this patch series. I reviewed the\ndesign pretty carefully and left some comments about it, and skimmed\nthrough the code changes. But I haven't yet reviewed the code in detail.\nI'll wait for your reaction to my design comments before doing so.\n\nThanks,\nMichael\n\n"},{"id":"312151","messageId":"CACsJy8CsiB39G36FC1pqjWqmrqSnW2Vf4ooBLzhQ97Dk8tXuFA@mail.gmail.com","threadId":"45126","inReplyTo":"cdf01f55-f3df-d3ce-149c-0249a03d27bf@alum.mit.edu","subject":"Re: [PATCH v4 00/15] Remove submodule from files-backend.c","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-20T12:47:55Z","receivedAt":"2017-02-20T12:48:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 20, 2017 at 7:42 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/18/2017 02:32 PM, Nguyễn Thái Ngọc Duy wrote:\n>> v4:\n>\n> I very much like the direction of this patch series. I reviewed the\n> design pretty carefully and left some comments about it, and skimmed\n> through the code changes. But I haven't yet reviewed the code in detail.\n> I'll wait for your reaction to my design comments before doing so.\n\nUnless there are some mails in transit, thanks for the review. The\ncomments I haven't replied to usually mean \"I agree\" (but not writing\nunless I could add anything after).\n-- \nDuy\n"},{"id":"312200","messageId":"CACsJy8CqHo_sCz0mFasvWJBP_cwWUu-dccowvtWs4pMoc5uLiQ@mail.gmail.com","threadId":"45126","inReplyTo":"25fcb527-595a-7865-41e3-ee7c4c1ad668@alum.mit.edu","subject":"Re: [PATCH v4 14/15] files-backend: remove submodule_allowed from files_downcast()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-21T13:25:03Z","receivedAt":"2017-02-21T13:25:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 20, 2017 at 7:11 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/18/2017 02:33 PM, Nguyễn Thái Ngọc Duy wrote:\n>> Since submodule or not is irrelevant to files-backend after the last\n>> patch, remove the parameter from files_downcast() and kill\n>> files_assert_main_repository().\n>>\n>> PS. This implies that all ref operations are allowed for submodules. But\n>> we may need to look more closely to see if that's really true...\n>\n> I think you are jumping the gun here.\n>\n> Even after your changes, there is still a significant difference between\n> the main repository and submodules: we have access to the object\n> database for the main repository, but not for submodules.\n\nI did jump the gun for another reason: files-backend makes function\ncalls to the frontend, which unconditionally target the main ref store\n(e.g. resolve_ref_unsafe, delete_ref...). Of course, because\nstore-aware api does not exist. My decision (off-list) to add\ntest-ref-store was the right call. I would not have seen these because\nI was not (and still am not) familiar with files-backend.c enough to\nsee its dark corners.\n\nfiles-backend.c is not all unicorn and rainbow :(\n-- \nDuy\n"},{"id":"312283","messageId":"20170222140450.30886-1-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170218133303.3682-1-pclouds@gmail.com","subject":"[PATCH v5 00/24] Remove submodule from files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:26Z","receivedAt":"2017-02-22T14:05:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v5 goes a bit longer than v4, basically:\n\n - files_path() is broken down into three smaller functions,\n   files_{packed_refs,reflog,refname}_path().\n\n - most of store-based api is added because..\n\n - test-ref-store.c is added with t1405 and t1406 for some basic tests\n   I'm not aimimg for complete ref store coverage. But we can continue\n   to improve from there.\n\n - refs_store_init() now takes a \"permission\" flag, like open().\n   Operations are allowed or forbidden based on this flag. The\n   submodule_allowed flag is killed. files_assert_main.. remains.\n\n - get_*_ref_store() remain public api because it's used by\n   test-ref-store.c and pack-refs.c.\n\n - files-backend.c should now make no function calls that implicitly\n   target the main store. But this will have to be tested more to be\n   sure. I'm tempted to add a tracing backend just for this purpose.\n\nJunio, if you take this on 'pu', you'll have to kick my other two\nseries out (they should not even compile). I'm not resending them\nuntil I get a \"looks mostly ok\" from Michael. No point in updating\nthem when this series keeps moving.\n\nThis series is also available on my github repo. branch\nfiles-backend-git-dir-2.\n\nNguyễn Thái Ngọc Duy (24):\n  refs.h: add forward declaration for structs used in this file\n  files-backend: make files_log_ref_write() static\n  files-backend: add and use files_packed_refs_path()\n  files-backend: convert git_path() to strbuf_git_path()\n  files-backend: move \"logs/\" out of TMP_RENAMED_LOG\n  files-backend: add and use files_reflog_path()\n  files-backend: add and use files_refname_path()\n  files-backend: remove the use of git_path()\n  refs.c: introduce get_main_ref_store()\n  refs: rename lookup_ref_store() to lookup_submodule_ref_store()\n  refs.c: flatten get_ref_store() a bit\n  refs.c: kill register_ref_store(), add register_submodule_ref_store()\n  refs.c: make get_main_ref_store() public and use it\n  path.c: move some code out of strbuf_git_path_submodule()\n  refs: move submodule code out of files-backend.c\n  files-backend: replace submodule_allowed check in files_downcast()\n  refs: rename get_ref_store() to get_submodule_ref_store() and make it public\n  refs: add new ref-store api\n  refs: new transaction related ref-store api\n  files-backend: avoid ref api targetting main ref store\n  refs: delete pack_refs() in favor of refs_pack_refs()\n  t/helper: add test-ref-store to test ref-store functions\n  t1405: some basic tests on main ref store\n  t1406: new tests for submodule ref store\n\n Makefile                                |   1 +\n builtin/pack-refs.c                     |   2 +-\n path.c                                  |  34 +--\n refs.c                                  | 411 ++++++++++++++++++--------\n refs.h                                  | 100 ++++++-\n refs/files-backend.c                    | 509 +++++++++++++++++++++-----------\n refs/refs-internal.h                    |  64 +---\n submodule.c                             |  31 ++\n submodule.h                             |   1 +\n t/helper/.gitignore                     |   1 +\n t/helper/test-ref-store.c (new)         | 274 +++++++++++++++++\n t/t1405-main-ref-store.sh (new +x)      | 123 ++++++++\n t/t1406-submodule-ref-store.sh (new +x) |  95 ++++++\n 13 files changed, 1269 insertions(+), 377 deletions(-)\n create mode 100644 t/helper/test-ref-store.c\n create mode 100755 t/t1405-main-ref-store.sh\n create mode 100755 t/t1406-submodule-ref-store.sh\n\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312284","messageId":"20170222140450.30886-2-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 01/24] refs.h: add forward declaration for structs used in this file","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:27Z","receivedAt":"2017-02-22T14:05:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.h | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/refs.h b/refs.h\nindex 9fbff90e7..c494b641a 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -1,6 +1,11 @@\n #ifndef REFS_H\n #define REFS_H\n \n+struct object_id;\n+struct ref_transaction;\n+struct strbuf;\n+struct string_list;\n+\n /*\n  * Resolve a reference, recursively following symbolic refererences.\n  *\n@@ -144,7 +149,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n  * `ref_transaction_commit` is called.  So `ref_transaction_verify`\n  * won't report a verification failure until the commit is attempted.\n  */\n-struct ref_transaction;\n \n /*\n  * Bit values set in the flags argument passed to each_ref_fn() and\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312285","messageId":"20170222140450.30886-3-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 02/24] files-backend: make files_log_ref_write() static","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:28Z","receivedAt":"2017-02-22T14:05:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\nbut probably never used outside refs-internal.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 +++\n refs/refs-internal.h | 4 ----\n 2 files changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex db3bd42a9..1ebd59ec0 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err);\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex fa93c9a32..f732473e1 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -228,10 +228,6 @@ struct ref_transaction {\n \tenum ref_transaction_state state;\n };\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err);\n-\n /*\n  * Check for entries in extras that are within the specified\n  * directory, where dirname is a reference directory name including\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312286","messageId":"20170222140450.30886-4-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 03/24] files-backend: add and use files_packed_refs_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:29Z","receivedAt":"2017-02-22T14:06:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 29 ++++++++++++++++++-----------\n 1 file changed, 18 insertions(+), 11 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 1ebd59ec0..4676525de 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -923,6 +923,8 @@ struct files_ref_store {\n \t */\n \tconst char *submodule;\n \n+\tchar *packed_refs_path;\n+\n \tstruct ref_entry *loose;\n \tstruct packed_ref_cache *packed;\n };\n@@ -985,7 +987,14 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\trefs->submodule = xstrdup_or_null(submodule);\n+\tif (submodule) {\n+\t\trefs->submodule = xstrdup(submodule);\n+\t\trefs->packed_refs_path = git_pathdup_submodule(\n+\t\t\trefs->submodule, \"packed-refs\");\n+\t\treturn ref_store;\n+\t}\n+\n+\trefs->packed_refs_path = git_pathdup(\"packed-refs\");\n \n \treturn ref_store;\n }\n@@ -1153,19 +1162,18 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n \tstrbuf_release(&line);\n }\n \n+static const char *files_packed_refs_path(struct files_ref_store *refs)\n+{\n+\treturn refs->packed_refs_path;\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n  */\n static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n {\n-\tchar *packed_refs_file;\n-\n-\tif (refs->submodule)\n-\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n-\t\t\t\t\t\t\t \"packed-refs\");\n-\telse\n-\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n+\tconst char *packed_refs_file = files_packed_refs_path(refs);\n \n \tif (refs->packed &&\n \t    !stat_validity_check(&refs->packed->validity, packed_refs_file))\n@@ -1184,7 +1192,6 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref\n \t\t\tfclose(f);\n \t\t}\n \t}\n-\tfree(packed_refs_file);\n \treturn refs->packed;\n }\n \n@@ -2160,7 +2167,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t}\n \n \tif (hold_lock_file_for_update_timeout(\n-\t\t\t    &packlock, git_path(\"packed-refs\"),\n+\t\t\t    &packlock, files_packed_refs_path(refs),\n \t\t\t    flags, timeout_value) < 0)\n \t\treturn -1;\n \t/*\n@@ -2426,7 +2433,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n \t\treturn 0; /* no refname exists in packed refs */\n \n \tif (lock_packed_refs(refs, 0)) {\n-\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n+\t\tunable_to_lock_message(files_packed_refs_path(refs), errno, err);\n \t\treturn -1;\n \t}\n \tpacked = get_packed_refs(refs);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312287","messageId":"20170222140450.30886-5-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 04/24] files-backend: convert git_path() to strbuf_git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:30Z","receivedAt":"2017-02-22T14:06:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git_path() and friends are going to be killed in files-backend.c in near\nfuture. And because there's a risk with overwriting buffer in\ngit_path(), let's convert them all to strbuf_git_path(). We'll have\neasier time killing/converting strbuf_git_path() then because we won't\nhave to worry about memory management again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 139 +++++++++++++++++++++++++++++++++++++++------------\n 1 file changed, 106 insertions(+), 33 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 4676525de..435db1293 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2319,6 +2319,7 @@ enum {\n static void try_remove_empty_parents(const char *refname, unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \tchar *p, *q;\n \tint i;\n \n@@ -2340,14 +2341,19 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) &&\n-\t\t    rmdir(git_path(\"%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&\n-\t\t    rmdir(git_path(\"logs/%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n \tstrbuf_release(&buf);\n+\tstrbuf_release(&sb);\n }\n \n /* make sure nobody touched the ref, and unlink */\n@@ -2509,11 +2515,16 @@ static int files_delete_refs(struct ref_store *ref_store,\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-static int rename_tmp_log_callback(const char *path, void *cb)\n+struct rename_cb {\n+\tconst char *tmp_renamed_log;\n+\tint true_errno;\n+};\n+\n+static int rename_tmp_log_callback(const char *path, void *cb_data)\n {\n-\tint *true_errno = cb;\n+\tstruct rename_cb *cb = cb_data;\n \n-\tif (rename(git_path(TMP_RENAMED_LOG), path)) {\n+\tif (rename(cb->tmp_renamed_log, path)) {\n \t\t/*\n \t\t * rename(a, b) when b is an existing directory ought\n \t\t * to result in ISDIR, but Solaris 5.8 gives ENOTDIR.\n@@ -2521,7 +2532,7 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \t\t * but report EISDIR to raceproof_create_file() so\n \t\t * that it knows to retry.\n \t\t */\n-\t\t*true_errno = errno;\n+\t\tcb->true_errno = errno;\n \t\tif (errno == ENOTDIR)\n \t\t\terrno = EISDIR;\n \t\treturn -1;\n@@ -2532,20 +2543,26 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \n static int rename_tmp_log(const char *newrefname)\n {\n-\tchar *path = git_pathdup(\"logs/%s\", newrefname);\n-\tint ret, true_errno;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tstruct rename_cb cb;\n+\tint ret;\n \n-\tret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);\n+\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&tmp, TMP_RENAMED_LOG);\n+\tcb.tmp_renamed_log = tmp.buf;\n+\tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n \t\tif (errno == EISDIR)\n-\t\t\terror(\"directory not empty: %s\", path);\n+\t\t\terror(\"directory not empty: %s\", path.buf);\n \t\telse\n \t\t\terror(\"unable to move logfile %s to %s: %s\",\n-\t\t\t      git_path(TMP_RENAMED_LOG), path,\n-\t\t\t      strerror(true_errno));\n+\t\t\t      tmp.buf, path.buf,\n+\t\t\t      strerror(cb.true_errno));\n \t}\n \n-\tfree(path);\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&tmp);\n \treturn ret;\n }\n \n@@ -2586,9 +2603,15 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n+\tstruct strbuf sb_oldref = STRBUF_INIT;\n+\tstruct strbuf sb_newref = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n+\tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tlog = !lstat(sb_oldref.buf, &loginfo);\n+\tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n \n@@ -2602,7 +2625,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n+\tif (ret)\n \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n@@ -2681,13 +2709,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n+\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n+\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n+\tstrbuf_release(&sb_newref);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n \n \treturn 1;\n }\n@@ -2861,18 +2895,24 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \tresult = log_ref_write_fd(logfd, old_sha1, new_sha1,\n \t\t\t\t  git_committer_info(0), msg);\n \tif (result) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\tclose(logfd);\n \t\treturn -1;\n \t}\n \tif (close(logfd)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \treturn 0;\n@@ -3092,22 +3132,32 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n+\tint ret;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \n-\treturn !lstat(git_path(\"logs/%s\", refname), &st) &&\n-\t\tS_ISREG(st.st_mode);\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \n-\treturn remove_path(git_path(\"logs/%s\", refname));\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = remove_path(sb.buf);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)\n@@ -3162,7 +3212,9 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3268,7 +3320,9 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3350,12 +3404,15 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n {\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\titer->dir_iterator = dir_iterator_begin(git_path(\"logs\"));\n+\tstrbuf_git_path(&sb, \"logs\");\n+\titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n \n@@ -3693,6 +3750,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tchar *head_ref = NULL;\n \tint head_type;\n \tstruct object_id head_oid;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tassert(err);\n \n@@ -3814,7 +3872,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\tif (!(update->type & REF_ISPACKED) ||\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n-\t\t\t\tif (unlink_or_msg(git_path(\"%s\", lock->ref_name), err)) {\n+\t\t\t\tstrbuf_reset(&sb);\n+\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n \t\t\t\t}\n@@ -3834,7 +3894,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t/* Delete the reflogs of any references that were deleted: */\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n-\t\tif (!unlink_or_warn(git_path(\"logs/%s\", ref_to_delete->string)))\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tif (!unlink_or_warn(sb.buf))\n \t\t\ttry_remove_empty_parents(ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n \t}\n@@ -3842,6 +3904,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tclear_loose_ref_cache(refs);\n \n cleanup:\n+\tstrbuf_release(&sb);\n \ttransaction->state = REF_TRANSACTION_CLOSED;\n \n \tfor (i = 0; i < transaction->nr; i++) {\n@@ -4108,18 +4171,28 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"init_db\");\n \n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n+\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tsafe_create_dir(sb.buf, 1);\n+\tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n+\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tadjust_shared_perm(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tadjust_shared_perm(sb.buf);\n \t}\n+\tstrbuf_release(&sb);\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312288","messageId":"20170222140450.30886-6-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 05/24] files-backend: move \"logs/\" out of TMP_RENAMED_LOG","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:31Z","receivedAt":"2017-02-22T14:06:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes reflog path building consistent, always in the form of\n\n    strbuf_git_path(sb, \"logs/%s\", refname);\n\nIt reduces the mental workload a bit in the next patch when that\nfunction call is converted.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 435db1293..69946b0de 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2513,7 +2513,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n  * IOW, to avoid cross device rename errors, the temporary renamed log must\n  * live into logs/refs.\n  */\n-#define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n+#define TMP_RENAMED_LOG  \"refs/.tmp-renamed-log\"\n \n struct rename_cb {\n \tconst char *tmp_renamed_log;\n@@ -2549,7 +2549,7 @@ static int rename_tmp_log(const char *newrefname)\n \tint ret;\n \n \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp, TMP_RENAMED_LOG);\n+\tstrbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n \tcb.tmp_renamed_log = tmp.buf;\n \tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n@@ -2626,12 +2626,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\treturn 1;\n \n \tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n \tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n \tstrbuf_release(&sb_oldref);\n \tstrbuf_release(&tmp_renamed_log);\n \tif (ret)\n-\t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n+\t\treturn error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n \tif (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {\n@@ -2714,10 +2714,10 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n-\t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n+\t\terror(\"unable to restore logfile %s from logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \tstrbuf_release(&sb_newref);\n \tstrbuf_release(&sb_oldref);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312289","messageId":"20170222140450.30886-8-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 07/24] files-backend: add and use files_refname_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:33Z","receivedAt":"2017-02-22T14:06:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nThis automatically adds the \"if submodule then use the submodule version\nof git_path\" to other call sites too. But it does not mean those\noperations are sumodule-ready. Not yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 45 +++++++++++++++++++++++++--------------------\n 1 file changed, 25 insertions(+), 20 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 7b4ea4c56..72f4e1746 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1180,6 +1180,18 @@ static void files_reflog_path(struct files_ref_store *refs,\n \tstrbuf_git_path(sb, \"logs/%s\", refname);\n }\n \n+static void files_refname_path(struct files_ref_store *refs,\n+\t\t\t       struct strbuf *sb,\n+\t\t\t       const char *refname)\n+{\n+\tif (refs->submodule) {\n+\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_git_path(sb, \"%s\", refname);\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n@@ -1251,10 +1263,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tsize_t path_baselen;\n \tint err = 0;\n \n-\tif (refs->submodule)\n-\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n-\telse\n-\t\tstrbuf_git_path(&path, \"%s\", dirname);\n+\tfiles_refname_path(refs, &path, dirname);\n \tpath_baselen = path.len;\n \n \tif (err) {\n@@ -1396,10 +1405,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(&sb_path, refs->submodule, \"%s\", refname);\n-\telse\n-\t\tstrbuf_git_path(&sb_path, \"%s\", refname);\n+\tfiles_refname_path(refs, &sb_path, refname);\n \n \tpath = sb_path.buf;\n \n@@ -1587,7 +1593,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_refname_path(refs, &ref_file, refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -2059,7 +2065,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_refname_path(refs, &ref_file, refname);\n \tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\tlock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n@@ -2358,7 +2364,7 @@ static void try_remove_empty_parents(struct files_ref_store *refs,\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tfiles_refname_path(refs, &sb, buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n@@ -2668,7 +2674,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tstrbuf_git_path(&path, \"%s\", newrefname);\n+\t\t\tfiles_refname_path(refs, &path, newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -3901,7 +3907,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n \t\t\t\tstrbuf_reset(&sb);\n-\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tfiles_refname_path(refs, &sb, lock->ref_name);\n \t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n@@ -4201,25 +4207,24 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"init_db\");\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tfiles_refname_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n-\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tfiles_refname_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \tstrbuf_reset(&sb);\n \tif (get_shared_repository()) {\n-\t\tstrbuf_git_path(&sb, \"refs/heads\");\n+\t\tfiles_refname_path(refs, &sb, \"refs/heads\");\n \t\tadjust_shared_perm(sb.buf);\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"refs/tags\");\n+\t\tfiles_refname_path(refs, &sb, \"refs/tags\");\n \t\tadjust_shared_perm(sb.buf);\n \t}\n \tstrbuf_release(&sb);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312290","messageId":"20170222140450.30886-7-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 06/24] files-backend: add and use files_reflog_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:32Z","receivedAt":"2017-02-22T14:06:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 148 +++++++++++++++++++++++++++++++--------------------\n 1 file changed, 89 insertions(+), 59 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 69946b0de..7b4ea4c56 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,7 +165,8 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n@@ -1167,6 +1168,18 @@ static const char *files_packed_refs_path(struct files_ref_store *refs)\n \treturn refs->packed_refs_path;\n }\n \n+static void files_reflog_path(struct files_ref_store *refs,\n+\t\t\t      struct strbuf *sb,\n+\t\t\t      const char *refname)\n+{\n+\tif (!refname) {\n+\t\tstrbuf_git_path(sb, \"logs\");\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_git_path(sb, \"logs/%s\", refname);\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n@@ -2316,7 +2329,9 @@ enum {\n  * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or\n  * REMOVE_EMPTY_PARENTS_REFLOG.\n  */\n-static void try_remove_empty_parents(const char *refname, unsigned int flags)\n+static void try_remove_empty_parents(struct files_ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -2348,7 +2363,7 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tfiles_reflog_path(refs, &sb, buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n@@ -2541,15 +2556,15 @@ static int rename_tmp_log_callback(const char *path, void *cb_data)\n \t}\n }\n \n-static int rename_tmp_log(const char *newrefname)\n+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n {\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf tmp = STRBUF_INIT;\n \tstruct rename_cb cb;\n \tint ret;\n \n-\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n+\tfiles_reflog_path(refs, &path, newrefname);\n+\tfiles_reflog_path(refs, &tmp, TMP_RENAMED_LOG);\n \tcb.tmp_renamed_log = tmp.buf;\n \tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n@@ -2609,7 +2624,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tstrbuf_release(&sb_oldref);\n \tif (log && S_ISLNK(loginfo.st_mode))\n@@ -2625,8 +2640,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (!rename_ref_available(oldrefname, newrefname))\n \t\treturn 1;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n+\tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n+\tfiles_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n \tstrbuf_release(&sb_oldref);\n \tstrbuf_release(&tmp_renamed_log);\n@@ -2667,7 +2682,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t}\n \t}\n \n-\tif (log && rename_tmp_log(newrefname))\n+\tif (log && rename_tmp_log(refs, newrefname))\n \t\tgoto rollback;\n \n \tlogmoved = log;\n@@ -2709,12 +2724,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tfiles_reflog_path(refs, &sb_newref, newrefname);\n+\tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n \tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n-\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n+\tfiles_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from logs/\"TMP_RENAMED_LOG\": %s\",\n@@ -2782,10 +2797,15 @@ static int open_or_create_logfile(const char *path, void *cb)\n  * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and\n  * return -1.\n  */\n-static int log_ref_setup(const char *refname, int force_create,\n+static int log_ref_setup(struct files_ref_store *refs,\n+\t\t\t const char *refname, int force_create,\n \t\t\t int *logfd, struct strbuf *err)\n {\n-\tchar *logfile = git_pathdup(\"logs/%s\", refname);\n+\tstruct strbuf logfile_sb = STRBUF_INIT;\n+\tchar *logfile;\n+\n+\tfiles_reflog_path(refs, &logfile_sb, refname);\n+\tlogfile = strbuf_detach(&logfile_sb, NULL);\n \n \tif (force_create || should_autocreate_reflog(refname)) {\n \t\tif (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {\n@@ -2835,12 +2855,11 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname, int force_create,\n \t\t\t       struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n \tint fd;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"create_reflog\");\n-\n-\tif (log_ref_setup(refname, force_create, &fd, err))\n+\tif (log_ref_setup(refs, refname, force_create, &fd, err))\n \t\treturn -1;\n \n \tif (fd >= 0)\n@@ -2875,7 +2894,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\tconst char *refname, const unsigned char *old_sha1,\n \t\t\tconst unsigned char *new_sha1, const char *msg,\n \t\t\tint flags, struct strbuf *err)\n {\n@@ -2884,7 +2904,8 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \tif (log_all_ref_updates == LOG_REFS_UNSET)\n \t\tlog_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;\n \n-\tresult = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,\n+\tresult = log_ref_setup(refs, refname,\n+\t\t\t       flags & REF_FORCE_CREATE_REFLOG,\n \t\t\t       &logfd, err);\n \n \tif (result)\n@@ -2898,7 +2919,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_reflog_path(refs, &sb, refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2909,7 +2930,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_reflog_path(refs, &sb, refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2970,7 +2991,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \tfiles_assert_main_repository(refs, \"commit_ref_update\");\n \n \tclear_loose_ref_cache(refs);\n-\tif (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,\n+\tif (files_log_ref_write(refs, lock->ref_name,\n+\t\t\t\tlock->old_oid.hash, sha1,\n \t\t\t\tlogmsg, 0, err)) {\n \t\tchar *old_msg = strbuf_detach(err, NULL);\n \t\tstrbuf_addf(err, \"cannot update the ref '%s': %s\",\n@@ -3002,8 +3024,9 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n-\t\t\tif (files_log_ref_write(\"HEAD\", lock->old_oid.hash, sha1,\n-\t\t\t\t\t  logmsg, 0, &log_err)) {\n+\t\t\tif (files_log_ref_write(refs, \"HEAD\",\n+\t\t\t\t\t\tlock->old_oid.hash, sha1,\n+\t\t\t\t\t\tlogmsg, 0, &log_err)) {\n \t\t\t\terror(\"%s\", log_err.buf);\n \t\t\t\tstrbuf_release(&log_err);\n \t\t\t}\n@@ -3035,24 +3058,26 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)\n \treturn ret;\n }\n \n-static void update_symref_reflog(struct ref_lock *lock, const char *refname,\n+static void update_symref_reflog(struct files_ref_store *refs,\n+\t\t\t\t struct ref_lock *lock, const char *refname,\n \t\t\t\t const char *target, const char *logmsg)\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n \tif (logmsg && !read_ref(target, new_sha1) &&\n-\t    files_log_ref_write(refname, lock->old_oid.hash, new_sha1,\n-\t\t\t\tlogmsg, 0, &err)) {\n+\t    files_log_ref_write(refs, refname, lock->old_oid.hash,\n+\t\t\t\tnew_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t}\n }\n \n-static int create_symref_locked(struct ref_lock *lock, const char *refname,\n+static int create_symref_locked(struct files_ref_store *refs,\n+\t\t\t\tstruct ref_lock *lock, const char *refname,\n \t\t\t\tconst char *target, const char *logmsg)\n {\n \tif (prefer_symlink_refs && !create_ref_symlink(lock, target)) {\n-\t\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\t\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \t\treturn 0;\n \t}\n \n@@ -3060,7 +3085,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,\n \t\treturn error(\"unable to fdopen %s: %s\",\n \t\t\t     lock->lk->tempfile.filename.buf, strerror(errno));\n \n-\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \n \t/* no error check; commit_ref will check ferror */\n \tfprintf(lock->lk->tempfile.fp, \"ref: %s\\n\", target);\n@@ -3089,13 +3114,20 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\treturn -1;\n \t}\n \n-\tret = create_symref_locked(lock, refname, target, logmsg);\n+\tret = create_symref_locked(refs, lock, refname, target, logmsg);\n \tunlock_ref(lock);\n \treturn ret;\n }\n \n int set_worktree_head_symref(const char *gitdir, const char *target)\n {\n+\t/*\n+\t * FIXME: this obviously will not work well for future refs\n+\t * backends. This function needs to die.\n+\t */\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3122,7 +3154,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \tlock->lk = &head_lock;\n \tlock->ref_name = xstrdup(head_rel);\n \n-\tret = create_symref_locked(lock, head_rel, target, NULL);\n+\tret = create_symref_locked(refs, lock, head_rel, target, NULL);\n \n \tunlock_ref(lock); /* will free lock */\n \tstrbuf_release(&head_path);\n@@ -3132,14 +3164,13 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3148,13 +3179,12 @@ static int files_reflog_exists(struct ref_store *ref_store,\n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tret = remove_path(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3204,15 +3234,14 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     each_reflog_ent_fn fn,\n \t\t\t\t\t     void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n \tint ret = 0, at_tail = 1;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3313,14 +3342,13 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     const char *refname,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3402,15 +3430,14 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n \n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n-\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\tstrbuf_git_path(&sb, \"logs\");\n+\tfiles_reflog_path(refs, &sb, NULL);\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n@@ -3835,7 +3862,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t\tif (update->flags & REF_NEEDS_COMMIT ||\n \t\t    update->flags & REF_LOG_ONLY) {\n-\t\t\tif (files_log_ref_write(lock->ref_name,\n+\t\t\tif (files_log_ref_write(refs,\n+\t\t\t\t\t\tlock->ref_name,\n \t\t\t\t\t\tlock->old_oid.hash,\n \t\t\t\t\t\tupdate->new_sha1,\n \t\t\t\t\t\tupdate->msg, update->flags,\n@@ -3895,9 +3923,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t/* Delete the reflogs of any references that were deleted: */\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tfiles_reflog_path(refs, &sb, ref_to_delete->string);\n \t\tif (!unlink_or_warn(sb.buf))\n-\t\t\ttry_remove_empty_parents(ref_to_delete->string,\n+\t\t\ttry_remove_empty_parents(refs, ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n \t}\n \n@@ -3921,7 +3949,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t * can only work because we have already\n \t\t\t * removed the lockfile.)\n \t\t\t */\n-\t\t\ttry_remove_empty_parents(update->refname,\n+\t\t\ttry_remove_empty_parents(refs, update->refname,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REF);\n \t\t}\n \t}\n@@ -4072,6 +4100,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n+\tstruct strbuf log_file_sb = STRBUF_INIT;\n \tchar *log_file;\n \tint status = 0;\n \tint type;\n@@ -4100,7 +4129,8 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tlog_file = git_pathdup(\"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &log_file_sb, refname);\n+\tlog_file = strbuf_detach(&log_file_sb, NULL);\n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n \t\t/*\n \t\t * Even though holding $GIT_DIR/logs/$reflog.lock has\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312291","messageId":"20170222140450.30886-9-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 08/24] files-backend: remove the use of git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:34Z","receivedAt":"2017-02-22T14:06:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\ndeciding what goes where (*). The end goal is to pass $GIT_DIR only. A\nrefs \"view\" of a linked worktree is a logical ref store that combines\ntwo files backends together.\n\n(*) Not entirely true since strbuf_git_path_submodule() still does path\ntranslation underneath. But that's for another patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 43 ++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 38 insertions(+), 5 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 72f4e1746..a390eaadf 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -923,7 +923,8 @@ struct files_ref_store {\n \t * store:\n \t */\n \tconst char *submodule;\n-\n+\tchar *gitdir;\n+\tchar *gitcommondir;\n \tchar *packed_refs_path;\n \n \tstruct ref_entry *loose;\n@@ -985,6 +986,8 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *gitdir = get_git_dir();\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n@@ -995,7 +998,11 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \t\treturn ref_store;\n \t}\n \n-\trefs->packed_refs_path = git_pathdup(\"packed-refs\");\n+\trefs->gitdir = xstrdup(gitdir);\n+\tget_common_dir_noenv(&sb, gitdir);\n+\trefs->gitcommondir = strbuf_detach(&sb, NULL);\n+\tstrbuf_addf(&sb, \"%s/packed-refs\", refs->gitcommondir);\n+\trefs->packed_refs_path = strbuf_detach(&sb, NULL);\n \n \treturn ref_store;\n }\n@@ -1173,11 +1180,26 @@ static void files_reflog_path(struct files_ref_store *refs,\n \t\t\t      const char *refname)\n {\n \tif (!refname) {\n-\t\tstrbuf_git_path(sb, \"logs\");\n+\t\t/*\n+\t\t * FIXME: of course this is wrong in multi worktree\n+\t\t * setting. To be fixed real soon.\n+\t\t */\n+\t\tstrbuf_addf(sb, \"%s/logs\", refs->gitcommondir);\n \t\treturn;\n \t}\n \n-\tstrbuf_git_path(sb, \"logs/%s\", refname);\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_PER_WORKTREE:\n+\tcase REF_TYPE_PSEUDOREF:\n+\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n+\t\tbreak;\n+\tcase REF_TYPE_NORMAL:\n+\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n+\t\t    ref_type(refname), refname);\n+\t}\n }\n \n static void files_refname_path(struct files_ref_store *refs,\n@@ -1189,7 +1211,18 @@ static void files_refname_path(struct files_ref_store *refs,\n \t\treturn;\n \t}\n \n-\tstrbuf_git_path(sb, \"%s\", refname);\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_PER_WORKTREE:\n+\tcase REF_TYPE_PSEUDOREF:\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n+\t\tbreak;\n+\tcase REF_TYPE_NORMAL:\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n+\t\t    ref_type(refname), refname);\n+\t}\n }\n \n /*\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312292","messageId":"20170222140450.30886-10-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 09/24] refs.c: introduce get_main_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:35Z","receivedAt":"2017-02-22T14:06:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 81b64b4ed..dab1a21ac 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1456,15 +1456,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n+static struct ref_store *get_main_ref_store(void)\n+{\n+\tstruct ref_store *refs;\n+\n+\tif (main_ref_store)\n+\t\treturn main_ref_store;\n+\n+\trefs = ref_store_init(NULL);\n+\treturn refs;\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n-\t\trefs = lookup_ref_store(NULL);\n-\n-\t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL);\n+\t\treturn get_main_ref_store();\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312293","messageId":"20170222140450.30886-11-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 10/24] refs: rename lookup_ref_store() to lookup_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:36Z","receivedAt":"2017-02-22T14:06:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With get_main_ref_store() being used inside get_ref_store(),\nlookup_ref_store() is only used for submodule code path. Rename to\nreflect that and delete dead code.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 13 ++++---------\n 1 file changed, 4 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex dab1a21ac..8b36d97c0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1395,17 +1395,13 @@ static struct ref_store *main_ref_store;\n static struct hashmap submodule_ref_stores;\n \n /*\n- * Return the ref_store instance for the specified submodule (or the\n- * main repository if submodule is NULL). If that ref_store hasn't\n- * been initialized yet, return NULL.\n+ * Return the ref_store instance for the specified submodule. If that\n+ * ref_store hasn't been initialized yet, return NULL.\n  */\n-static struct ref_store *lookup_ref_store(const char *submodule)\n+static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n {\n \tstruct submodule_hash_entry *entry;\n \n-\tif (!submodule)\n-\t\treturn main_ref_store;\n-\n \tif (!submodule_ref_stores.tablesize)\n \t\t/* It's initialized on demand in register_ref_store(). */\n \t\treturn NULL;\n@@ -1474,7 +1470,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n \t} else {\n-\t\trefs = lookup_ref_store(submodule);\n+\t\trefs = lookup_submodule_ref_store(submodule);\n \n \t\tif (!refs) {\n \t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1485,7 +1481,6 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\t\tstrbuf_release(&submodule_sb);\n \t\t}\n \t}\n-\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312294","messageId":"20170222140450.30886-13-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 12/24] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:38Z","receivedAt":"2017-02-22T14:07:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the last function in this code (besides public API) that takes\nsubmodule argument and handles both main/submodule cases. Break it down,\nmove main store registration in get_main_ref_store() and keep the rest\nin register_submodule_ref_store().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 50 ++++++++++++++++++++++++++------------------------\n 1 file changed, 26 insertions(+), 24 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c284cb4f4..6adc38e42 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1412,29 +1412,6 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n }\n \n /*\n- * Register the specified ref_store to be the one that should be used\n- * for submodule (or the main repository if submodule is NULL). It is\n- * a fatal error to call this function twice for the same submodule.\n- */\n-static void register_ref_store(struct ref_store *refs, const char *submodule)\n-{\n-\tif (!submodule) {\n-\t\tif (main_ref_store)\n-\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n-\n-\t\tmain_ref_store = refs;\n-\t} else {\n-\t\tif (!submodule_ref_stores.tablesize)\n-\t\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n-\n-\t\tif (hashmap_put(&submodule_ref_stores,\n-\t\t\t\talloc_submodule_hash_entry(submodule, refs)))\n-\t\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n-\t\t\t    submodule);\n-\t}\n-}\n-\n-/*\n  * Create, record, and return a ref_store instance for the specified\n  * submodule (or the main repository if submodule is NULL).\n  */\n@@ -1448,7 +1425,6 @@ static struct ref_store *ref_store_init(const char *submodule)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n \trefs = be->init(submodule);\n-\tregister_ref_store(refs, submodule);\n \treturn refs;\n }\n \n@@ -1460,9 +1436,32 @@ static struct ref_store *get_main_ref_store(void)\n \t\treturn main_ref_store;\n \n \trefs = ref_store_init(NULL);\n+\tif (refs) {\n+\t\tif (main_ref_store)\n+\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n+\n+\t\tmain_ref_store = refs;\n+\t}\n \treturn refs;\n }\n \n+/*\n+ * Register the specified ref_store to be the one that should be used\n+ * for submodule. It is a fatal error to call this function twice for\n+ * the same submodule.\n+ */\n+static void register_submodule_ref_store(struct ref_store *refs,\n+\t\t\t\t\t const char *submodule)\n+{\n+\tif (!submodule_ref_stores.tablesize)\n+\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n+\n+\tif (hashmap_put(&submodule_ref_stores,\n+\t\t\talloc_submodule_hash_entry(submodule, refs)))\n+\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n+\t\t    submodule);\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1480,6 +1479,9 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (is_nonbare_repository_dir(&submodule_sb))\n \t\trefs = ref_store_init(submodule);\n \tstrbuf_release(&submodule_sb);\n+\n+\tif (refs)\n+\t\tregister_submodule_ref_store(refs, submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312295","messageId":"20170222140450.30886-12-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 11/24] refs.c: flatten get_ref_store() a bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:37Z","receivedAt":"2017-02-22T14:07:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This helps the future changes in this code. And because get_ref_store()\nis destined to become get_submodule_ref_store(), the \"get main store\"\ncode path will be removed eventually. After this the patch to delete\nthat code will be cleaner.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 19 +++++++++----------\n 1 file changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 8b36d97c0..c284cb4f4 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1465,22 +1465,21 @@ static struct ref_store *get_main_ref_store(void)\n \n struct ref_store *get_ref_store(const char *submodule)\n {\n+\tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n-\t} else {\n-\t\trefs = lookup_submodule_ref_store(submodule);\n+\t}\n \n-\t\tif (!refs) {\n-\t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n+\trefs = lookup_submodule_ref_store(submodule);\n+\tif (refs)\n+\t\treturn refs;\n \n-\t\t\tstrbuf_addstr(&submodule_sb, submodule);\n-\t\t\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\t\t\trefs = ref_store_init(submodule);\n-\t\t\tstrbuf_release(&submodule_sb);\n-\t\t}\n-\t}\n+\tstrbuf_addstr(&submodule_sb, submodule);\n+\tif (is_nonbare_repository_dir(&submodule_sb))\n+\t\trefs = ref_store_init(submodule);\n+\tstrbuf_release(&submodule_sb);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312296","messageId":"20170222140450.30886-15-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 14/24] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:40Z","receivedAt":"2017-02-22T14:07:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"refs is learning to avoid path rewriting that is done by\nstrbuf_git_path_submodule(). Factor out this code so it could be reused\nby refs*\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n path.c      | 34 +++++++---------------------------\n submodule.c | 31 +++++++++++++++++++++++++++++++\n submodule.h |  1 +\n 3 files changed, 39 insertions(+), 27 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex efcedafba..3451d2916 100644\n--- a/path.c\n+++ b/path.c\n@@ -475,35 +475,16 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n static int do_submodule_path(struct strbuf *buf, const char *path,\n \t\t\t     const char *fmt, va_list args)\n {\n-\tconst char *git_dir;\n \tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n \tstruct strbuf git_submodule_dir = STRBUF_INIT;\n-\tconst struct submodule *sub;\n-\tint err = 0;\n+\tint ret;\n \n-\tstrbuf_addstr(buf, path);\n-\tstrbuf_complete(buf, '/');\n-\tstrbuf_addstr(buf, \".git\");\n-\n-\tgit_dir = read_gitfile(buf->buf);\n-\tif (git_dir) {\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, git_dir);\n-\t}\n-\tif (!is_git_directory(buf->buf)) {\n-\t\tgitmodules_config();\n-\t\tsub = submodule_from_path(null_sha1, path);\n-\t\tif (!sub) {\n-\t\t\terr = SUBMODULE_PATH_ERR_NOT_CONFIGURED;\n-\t\t\tgoto cleanup;\n-\t\t}\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n-\t}\n-\n-\tstrbuf_addch(buf, '/');\n-\tstrbuf_addbuf(&git_submodule_dir, buf);\n+\tret = submodule_to_gitdir(&git_submodule_dir, path);\n+\tif (ret)\n+\t\tgoto cleanup;\n \n+\tstrbuf_complete(&git_submodule_dir, '/');\n+\tstrbuf_addbuf(buf, &git_submodule_dir);\n \tstrbuf_vaddf(buf, fmt, args);\n \n \tif (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))\n@@ -514,8 +495,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,\n cleanup:\n \tstrbuf_release(&git_submodule_dir);\n \tstrbuf_release(&git_submodule_common_dir);\n-\n-\treturn err;\n+\treturn ret;\n }\n \n char *git_pathdup_submodule(const char *path, const char *fmt, ...)\ndiff --git a/submodule.c b/submodule.c\nindex 3b98766a6..36b8d1d11 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1514,3 +1514,34 @@ void absorb_git_dir_into_superproject(const char *prefix,\n \t\tstrbuf_release(&sb);\n \t}\n }\n+\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n+{\n+\tconst struct submodule *sub;\n+\tconst char *git_dir;\n+\tint ret = 0;\n+\n+\tstrbuf_reset(buf);\n+\tstrbuf_addstr(buf, submodule);\n+\tstrbuf_complete(buf, '/');\n+\tstrbuf_addstr(buf, \".git\");\n+\n+\tgit_dir = read_gitfile(buf->buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, git_dir);\n+\t}\n+\tif (!is_git_directory(buf->buf)) {\n+\t\tgitmodules_config();\n+\t\tsub = submodule_from_path(null_sha1, submodule);\n+\t\tif (!sub) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n+\t}\n+\n+cleanup:\n+\treturn ret;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex 05ab674f0..fc3d0303e 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -81,6 +81,7 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n \t\t\t\t    int dry_run);\n extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n extern int parallel_submodules(void);\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n \n /*\n  * Prepare the \"env_array\" parameter of a \"struct child_process\" for executing\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312297","messageId":"20170222140450.30886-14-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 13/24] refs.c: make get_main_ref_store() public and use it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:39Z","receivedAt":"2017-02-22T14:07:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"get_ref_store() will soon be renamed to get_submodule_ref_store().\nTogether with future get_worktree_ref_store(), the three functions\nprovide an appropriate ref store for different operation modes. New APIs\nwill be added to operate directly on ref stores.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 36 ++++++++++++++++++------------------\n refs.h               |  3 +++\n refs/files-backend.c |  2 +-\n 3 files changed, 22 insertions(+), 19 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6adc38e42..562834fc0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1314,7 +1314,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n /* backend functions */\n int refs_init_db(struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->init_db(refs, err);\n }\n@@ -1322,7 +1322,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_ref_store(NULL), refname,\n+\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1428,7 +1428,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n-static struct ref_store *get_main_ref_store(void)\n+struct ref_store *get_main_ref_store(void)\n {\n \tstruct ref_store *refs;\n \n@@ -1494,14 +1494,14 @@ void base_ref_store_init(struct ref_store *refs,\n /* backend functions */\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->pack_refs(refs, flags);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->peel_ref(refs, refname, sha1);\n }\n@@ -1509,7 +1509,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n \t\t\t\t       logmsg);\n@@ -1518,7 +1518,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1528,14 +1528,14 @@ int verify_refname_available(const char *refname,\n \t\t\t     const struct string_list *skip,\n \t\t\t     struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1546,7 +1546,7 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n \t\t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n@@ -1555,14 +1555,14 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_exists(refs, refname);\n }\n@@ -1570,14 +1570,14 @@ int reflog_exists(const char *refname)\n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_reflog(refs, refname);\n }\n@@ -1589,7 +1589,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n \t\t\t\t       prepare_fn, should_prune_fn,\n@@ -1599,21 +1599,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\ndiff --git a/refs.h b/refs.h\nindex c494b641a..29013cb7e 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -2,6 +2,7 @@\n #define REFS_H\n \n struct object_id;\n+struct ref_store;\n struct ref_transaction;\n struct strbuf;\n struct string_list;\n@@ -559,4 +560,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \n int ref_storage_backend_exists(const char *name);\n \n+struct ref_store *get_main_ref_store(void);\n+\n #endif /* REFS_H */\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex a390eaadf..d80c27837 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3165,7 +3165,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(), 0, \"set_head_symref\");\n \n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312298","messageId":"20170222140450.30886-17-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 16/24] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:42Z","receivedAt":"2017-02-22T14:07:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend.c is unlearning submodules. Instead of having a specific\ncheck for submodules to see what operation is allowed, files backend\nnow takes a set of flags at init. Each operation will check if the\nrequired flags is present before performing.\n\nFor now we have four flags: read, write and odb access. Main ref store\nhas all flags, obviously, while submodule stores are read-only and have\naccess to odb (*).\n\nThe \"main\" flag stays because many functions in the backend calls\nfrontend ones without a ref store, so these functions always target the\nmain ref store. Ideally the flag should be gone after ref-store-aware\napi is in place and used by backends.\n\n(*) Submodule code needs for_each_ref. Try take REF_STORE_ODB flag\nout. At least t3404 would fail. The \"have access to odb\" in submodule is\na bit hacky since we don't know from he whether add_submodule_odb() has\nbeen called.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 15 ++++++---\n refs/files-backend.c | 86 +++++++++++++++++++++++++++++++++-------------------\n refs/refs-internal.h |  9 +++++-\n 3 files changed, 73 insertions(+), 37 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 67acae60c..2dc97891a 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1416,7 +1416,8 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n  * Create, record, and return a ref_store instance for the specified\n  * gitdir.\n  */\n-static struct ref_store *ref_store_init(const char *gitdir)\n+static struct ref_store *ref_store_init(const char *gitdir,\n+\t\t\t\t\tunsigned int flags)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1425,7 +1426,7 @@ static struct ref_store *ref_store_init(const char *gitdir)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(gitdir);\n+\trefs = be->init(gitdir, flags);\n \treturn refs;\n }\n \n@@ -1436,7 +1437,11 @@ struct ref_store *get_main_ref_store(void)\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\trefs = ref_store_init(get_git_dir());\n+\trefs = ref_store_init(get_git_dir(),\n+\t\t\t      (REF_STORE_READ |\n+\t\t\t       REF_STORE_WRITE |\n+\t\t\t       REF_STORE_ODB |\n+\t\t\t       REF_STORE_MAIN));\n \tif (refs) {\n \t\tif (main_ref_store)\n \t\t\tdie(\"BUG: main_ref_store initialized twice\");\n@@ -1485,7 +1490,9 @@ struct ref_store *get_ref_store(const char *submodule)\n \n \tret = submodule_to_gitdir(&submodule_sb, submodule);\n \tif (!ret)\n-\t\trefs = ref_store_init(submodule_sb.buf);\n+\t\t/* pretend that add_submodule_odb() has been called */\n+\t\trefs = ref_store_init(submodule_sb.buf,\n+\t\t\t\t      REF_STORE_READ | REF_STORE_ODB);\n \tstrbuf_release(&submodule_sb);\n \n \tif (refs)\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 37443369b..474d1027c 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -916,6 +916,7 @@ struct packed_ref_cache {\n  */\n struct files_ref_store {\n \tstruct ref_store base;\n+\tunsigned int store_flags;\n \n \tchar *gitdir;\n \tchar *gitcommondir;\n@@ -976,13 +977,15 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *gitdir)\n+static struct ref_store *files_ref_store_create(const char *gitdir,\n+\t\t\t\t\t\tunsigned int flags)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n+\trefs->store_flags = flags;\n \n \trefs->gitdir = xstrdup(gitdir);\n \tget_common_dir_noenv(&sb, gitdir);\n@@ -994,13 +997,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n }\n \n /*\n- * Die if refs is for a submodule (i.e., not for the main repository).\n- * caller is used in any necessary error messages.\n+ * Die if refs is not the main ref store. caller is used in any\n+ * necessary error messages.\n  */\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\t/* This function is to be deleted in the next patch */\n+\tif (refs->store_flags & REF_STORE_MAIN)\n+\t\treturn;\n+\n+\tdie(\"BUG: unallowed operation (%s), only works \"\n+\t    \"on main ref store\\n\", caller);\n }\n \n /*\n@@ -1009,9 +1016,9 @@ static void files_assert_main_repository(struct files_ref_store *refs,\n  * files_ref_store is for a submodule (i.e., not for the main\n  * repository). caller is used in any necessary error messages.\n  */\n-static struct files_ref_store *files_downcast(\n-\t\tstruct ref_store *ref_store, int submodule_allowed,\n-\t\tconst char *caller)\n+static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n+\t\t\t\t\t      unsigned int required_flags,\n+\t\t\t\t\t      const char *caller)\n {\n \tstruct files_ref_store *refs;\n \n@@ -1021,8 +1028,9 @@ static struct files_ref_store *files_downcast(\n \n \trefs = (struct files_ref_store *)ref_store;\n \n-\tif (!submodule_allowed)\n-\t\tfiles_assert_main_repository(refs, caller);\n+\tif ((refs->store_flags & required_flags) != required_flags)\n+\t\tdie(\"BUG: unallowed operation (%s), requires %x, has %x\\n\",\n+\t\t    caller, required_flags, refs->store_flags);\n \n \treturn refs;\n }\n@@ -1404,7 +1412,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t\t\t      struct strbuf *referent, unsigned int *type)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"read_raw_ref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"read_raw_ref\");\n \tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n \tconst char *path;\n@@ -1821,10 +1829,14 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n static int files_peel_ref(struct ref_store *ref_store,\n \t\t\t  const char *refname, unsigned char *sha1)\n {\n-\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,\n+\t\t\t       \"peel_ref\");\n \tint flag;\n \tunsigned char base[20];\n \n+\tfiles_assert_main_repository(refs, \"peel_ref\");\n+\n \tif (current_ref_iter && current_ref_iter->refname == refname) {\n \t\tstruct object_id peeled;\n \n@@ -1929,21 +1941,23 @@ static struct ref_iterator *files_ref_iterator_begin(\n \t\tstruct ref_store *ref_store,\n \t\tconst char *prefix, unsigned int flags)\n {\n-\tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n+\tstruct files_ref_store *refs;\n \tstruct ref_dir *loose_dir, *packed_dir;\n \tstruct ref_iterator *loose_iter, *packed_iter;\n \tstruct files_ref_iterator *iter;\n \tstruct ref_iterator *ref_iterator;\n \n-\tif (!refs)\n-\t\treturn empty_ref_iterator_begin();\n-\n \tif (ref_paranoia < 0)\n \t\tref_paranoia = git_env_bool(\"GIT_REF_PARANOIA\", 0);\n \tif (ref_paranoia)\n \t\tflags |= DO_FOR_EACH_INCLUDE_BROKEN;\n \n+\trefs = files_downcast(ref_store,\n+\t\t\t      REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),\n+\t\t\t      \"ref_iterator_begin\");\n+\tif (!refs)\n+\t\treturn empty_ref_iterator_begin();\n+\n \titer = xcalloc(1, sizeof(*iter));\n \tref_iterator = &iter->base;\n \tbase_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);\n@@ -2424,7 +2438,8 @@ static void prune_refs(struct ref_to_prune *r)\n static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"pack_refs\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,\n+\t\t\t       \"pack_refs\");\n \tstruct pack_refs_cb_data cbdata;\n \n \tmemset(&cbdata, 0, sizeof(cbdata));\n@@ -2503,7 +2518,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \t\t\t     struct string_list *refnames, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_refs\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"delete_refs\");\n \tstruct strbuf err = STRBUF_INIT;\n \tint i, result = 0;\n \n@@ -2607,7 +2622,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,\n \t\t\t\t\t  struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"verify_refname_available\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"verify_refname_available\");\n \tstruct ref_dir *packed_refs = get_packed_refs(refs);\n \tstruct ref_dir *loose_refs = get_loose_refs(refs);\n \n@@ -2632,7 +2647,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"rename_ref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"rename_ref\");\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n@@ -2875,7 +2890,7 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"create_reflog\");\n \tint fd;\n \n \tif (log_ref_setup(refs, refname, force_create, &fd, err))\n@@ -3119,7 +3134,7 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\t\t       const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_symref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"create_symref\");\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_lock *lock;\n \tint ret;\n@@ -3145,7 +3160,9 @@ int set_worktree_head_symref(const char *gitdir, const char *target)\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_main_ref_store(), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(),\n+\t\t\t       REF_STORE_WRITE,\n+\t\t\t       \"set_head_symref\");\n \n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n@@ -3184,7 +3201,7 @@ static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n@@ -3199,7 +3216,7 @@ static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n@@ -3254,7 +3271,8 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n@@ -3362,7 +3380,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n@@ -3450,7 +3469,8 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -3788,7 +3808,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE,\n+\t\t\t       \"ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list refs_to_delete = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *ref_to_delete;\n@@ -3993,7 +4014,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"initial_ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE,\n+\t\t\t       \"initial_ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list affected_refnames = STRING_LIST_INIT_NODUP;\n \n@@ -4115,7 +4137,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\t       void *policy_cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_expire\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"reflog_expire\");\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n@@ -4221,7 +4243,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"init_db\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n \t/*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex dfa181792..0cca280b5 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -481,12 +481,19 @@ struct ref_store;\n \n /* refs backends */\n \n+/* ref_store_init flags */\n+#define REF_STORE_READ\t\t(1 << 0)\n+#define REF_STORE_WRITE\t\t(1 << 1) /* can perform update operations */\n+#define REF_STORE_ODB\t\t(1 << 2) /* has access to object database */\n+#define REF_STORE_MAIN\t\t(1 << 3)\n+\n /*\n  * Initialize the ref_store for the specified gitdir. These functions\n  * should call base_ref_store_init() to initialize the shared part of\n  * the ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir,\n+\t\t\t\t\t    unsigned int flags);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312299","messageId":"20170222140450.30886-16-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 15/24] refs: move submodule code out of files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:41Z","receivedAt":"2017-02-22T14:07:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend is now initialized with a $GIT_DIR. Converting a submodule\npath to where real submodule gitdir is located is done in get_ref_store().\n\nThis gives a slight performance improvement for submodules since we\ndon't convert submodule path to gitdir at every backend call like\nbefore. We pay that once at ref-store creation.\n\nMore cleanup in files_downcast() and files_assert_main_repository()\nfollows shortly. It's separate to keep noises from this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 20 ++++++++++++++------\n refs/files-backend.c | 24 ++----------------------\n refs/refs-internal.h |  9 ++++-----\n 3 files changed, 20 insertions(+), 33 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 562834fc0..67acae60c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -9,6 +9,7 @@\n #include \"refs/refs-internal.h\"\n #include \"object.h\"\n #include \"tag.h\"\n+#include \"submodule.h\"\n \n /*\n  * List of all available backends\n@@ -1413,9 +1414,9 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n \n /*\n  * Create, record, and return a ref_store instance for the specified\n- * submodule (or the main repository if submodule is NULL).\n+ * gitdir.\n  */\n-static struct ref_store *ref_store_init(const char *submodule)\n+static struct ref_store *ref_store_init(const char *gitdir)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1424,7 +1425,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(submodule);\n+\trefs = be->init(gitdir);\n \treturn refs;\n }\n \n@@ -1435,7 +1436,7 @@ struct ref_store *get_main_ref_store(void)\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\trefs = ref_store_init(NULL);\n+\trefs = ref_store_init(get_git_dir());\n \tif (refs) {\n \t\tif (main_ref_store)\n \t\t\tdie(\"BUG: main_ref_store initialized twice\");\n@@ -1466,6 +1467,7 @@ struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n+\tint ret;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n@@ -1476,8 +1478,14 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn refs;\n \n \tstrbuf_addstr(&submodule_sb, submodule);\n-\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\trefs = ref_store_init(submodule);\n+\tret = is_nonbare_repository_dir(&submodule_sb);\n+\tstrbuf_release(&submodule_sb);\n+\tif (!ret)\n+\t\treturn refs;\n+\n+\tret = submodule_to_gitdir(&submodule_sb, submodule);\n+\tif (!ret)\n+\t\trefs = ref_store_init(submodule_sb.buf);\n \tstrbuf_release(&submodule_sb);\n \n \tif (refs)\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex d80c27837..37443369b 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -917,12 +917,6 @@ struct packed_ref_cache {\n struct files_ref_store {\n \tstruct ref_store base;\n \n-\t/*\n-\t * The name of the submodule represented by this object, or\n-\t * NULL if it represents the main repository's reference\n-\t * store:\n-\t */\n-\tconst char *submodule;\n \tchar *gitdir;\n \tchar *gitcommondir;\n \tchar *packed_refs_path;\n@@ -982,22 +976,14 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *submodule)\n+static struct ref_store *files_ref_store_create(const char *gitdir)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n \tstruct strbuf sb = STRBUF_INIT;\n-\tconst char *gitdir = get_git_dir();\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\tif (submodule) {\n-\t\trefs->submodule = xstrdup(submodule);\n-\t\trefs->packed_refs_path = git_pathdup_submodule(\n-\t\t\trefs->submodule, \"packed-refs\");\n-\t\treturn ref_store;\n-\t}\n-\n \trefs->gitdir = xstrdup(gitdir);\n \tget_common_dir_noenv(&sb, gitdir);\n \trefs->gitcommondir = strbuf_detach(&sb, NULL);\n@@ -1014,8 +1000,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\tif (refs->submodule)\n-\t\tdie(\"BUG: %s called for a submodule\", caller);\n+\t/* This function is to be deleted in the next patch */\n }\n \n /*\n@@ -1206,11 +1191,6 @@ static void files_refname_path(struct files_ref_store *refs,\n \t\t\t       struct strbuf *sb,\n \t\t\t       const char *refname)\n {\n-\tif (refs->submodule) {\n-\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n-\t\treturn;\n-\t}\n-\n \tswitch (ref_type(refname)) {\n \tcase REF_TYPE_PER_WORKTREE:\n \tcase REF_TYPE_PSEUDOREF:\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f732473e1..dfa181792 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -482,12 +482,11 @@ struct ref_store;\n /* refs backends */\n \n /*\n- * Initialize the ref_store for the specified submodule, or for the\n- * main repository if submodule == NULL. These functions should call\n- * base_ref_store_init() to initialize the shared part of the\n- * ref_store and to record the ref_store for later lookup.\n+ * Initialize the ref_store for the specified gitdir. These functions\n+ * should call base_ref_store_init() to initialize the shared part of\n+ * the ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *submodule);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312300","messageId":"20170222140450.30886-18-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 17/24] refs: rename get_ref_store() to get_submodule_ref_store() and make it public","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:43Z","receivedAt":"2017-02-22T14:07:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is intended to replace *_submodule() refs API. It provides\na ref store for a specific submodule, which can be operated on by a new\nset of refs API.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 12 ++++++++----\n refs.h               | 11 +++++++++++\n refs/refs-internal.h | 12 ------------\n 3 files changed, 19 insertions(+), 16 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2dc97891a..7843d3085 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1171,7 +1171,7 @@ int head_ref(each_ref_fn fn, void *cb_data)\n static int do_for_each_ref(const char *submodule, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(submodule);\n+\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1344,10 +1344,10 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \t\t/* We need to strip off one or more trailing slashes */\n \t\tchar *stripped = xmemdupz(submodule, len);\n \n-\t\trefs = get_ref_store(stripped);\n+\t\trefs = get_submodule_ref_store(stripped);\n \t\tfree(stripped);\n \t} else {\n-\t\trefs = get_ref_store(submodule);\n+\t\trefs = get_submodule_ref_store(submodule);\n \t}\n \n \tif (!refs)\n@@ -1468,13 +1468,17 @@ static void register_submodule_ref_store(struct ref_store *refs,\n \t\t    submodule);\n }\n \n-struct ref_store *get_ref_store(const char *submodule)\n+struct ref_store *get_submodule_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \tint ret;\n \n \tif (!submodule || !*submodule) {\n+\t\t/*\n+\t\t * FIXME: This case is ideally not allowed. But that\n+\t\t * can't happen until we clean up all the callers.\n+\t\t */\n \t\treturn get_main_ref_store();\n \t}\n \ndiff --git a/refs.h b/refs.h\nindex 29013cb7e..2efba6ba9 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -561,5 +561,16 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int ref_storage_backend_exists(const char *name);\n \n struct ref_store *get_main_ref_store(void);\n+/*\n+ * Return the ref_store instance for the specified submodule. For the\n+ * main repository, use submodule==NULL; such a call cannot fail. For\n+ * a submodule, the submodule must exist and be a nonbare repository,\n+ * otherwise return NULL. If the requested reference store has not yet\n+ * been initialized, initialize it first.\n+ *\n+ * For backwards compatibility, submodule==\"\" is treated the same as\n+ * submodule==NULL.\n+ */\n+struct ref_store *get_submodule_ref_store(const char *submodule);\n \n #endif /* REFS_H */\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 0cca280b5..f20dde39e 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -646,18 +646,6 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-/*\n- * Return the ref_store instance for the specified submodule. For the\n- * main repository, use submodule==NULL; such a call cannot fail. For\n- * a submodule, the submodule must exist and be a nonbare repository,\n- * otherwise return NULL. If the requested reference store has not yet\n- * been initialized, initialize it first.\n- *\n- * For backwards compatibility, submodule==\"\" is treated the same as\n- * submodule==NULL.\n- */\n-struct ref_store *get_ref_store(const char *submodule);\n-\n const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312301","messageId":"20170222140450.30886-19-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 18/24] refs: add new ref-store api","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:44Z","receivedAt":"2017-02-22T14:07:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is not meant to cover all existing API. It adds enough to test ref\nstores with the new test program test-ref-store, coming soon and to be\nused by files-backend.c.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 235 +++++++++++++++++++++++++++++++++++++--------------\n refs.h               |  71 ++++++++++++++++\n refs/files-backend.c |  13 +--\n refs/refs-internal.h |  31 +------\n 4 files changed, 253 insertions(+), 97 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 7843d3085..9137ac283 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -185,13 +185,20 @@ struct ref_filter {\n \tvoid *cb_data;\n };\n \n-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)\n+int refs_read_ref_full(struct ref_store *refs, const char *refname,\n+\t\t       int resolve_flags, unsigned char *sha1, int *flags)\n {\n-\tif (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))\n+\tif (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags))\n \t\treturn 0;\n \treturn -1;\n }\n \n+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)\n+{\n+\treturn refs_read_ref_full(get_main_ref_store(), refname,\n+\t\t\t\t  resolve_flags, sha1, flags);\n+}\n+\n int read_ref(const char *refname, unsigned char *sha1)\n {\n \treturn read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);\n@@ -286,34 +293,52 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li\n \tfor_each_rawref(warn_if_dangling_symref, &data);\n }\n \n+int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/tags/\", fn, cb_data);\n+}\n+\n int for_each_tag_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/tags/\", fn, cb_data);\n+\treturn refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/tags/\", fn, cb_data);\n+\treturn refs_for_each_tag_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t     fn, cb_data);\n+}\n+\n+int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/heads/\", fn, cb_data);\n }\n \n int for_each_branch_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/heads/\", fn, cb_data);\n+\treturn refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/heads/\", fn, cb_data);\n+\treturn refs_for_each_branch_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t\tfn, cb_data);\n+}\n+\n+int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/remotes/\", fn, cb_data);\n }\n \n int for_each_remote_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/remotes/\", fn, cb_data);\n+\treturn refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/remotes/\", fn, cb_data);\n+\treturn refs_for_each_remote_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t\tfn, cb_data);\n }\n \n int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n@@ -1120,14 +1145,17 @@ const char *find_descendant_ref(const char *dirname,\n \treturn NULL;\n }\n \n-int rename_ref_available(const char *old_refname, const char *new_refname)\n+int refs_rename_ref_available(struct ref_store *refs,\n+\t\t\t      const char *old_refname,\n+\t\t\t      const char *new_refname)\n {\n \tstruct string_list skip = STRING_LIST_INIT_NODUP;\n \tstruct strbuf err = STRBUF_INIT;\n \tint ok;\n \n \tstring_list_insert(&skip, old_refname);\n-\tok = !verify_refname_available(new_refname, NULL, &skip, &err);\n+\tok = !refs_verify_refname_available(refs, new_refname,\n+\t\t\t\t\t    NULL, &skip, &err);\n \tif (!ok)\n \t\terror(\"%s\", err.buf);\n \n@@ -1168,10 +1196,9 @@ int head_ref(each_ref_fn fn, void *cb_data)\n  * non-zero value, stop the iteration and return that value;\n  * otherwise, return 0.\n  */\n-static int do_for_each_ref(const char *submodule, const char *prefix,\n+static int do_for_each_ref(struct ref_store *refs, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1183,19 +1210,30 @@ static int do_for_each_ref(const char *submodule, const char *prefix,\n \treturn do_for_each_ref_iterator(iter, fn, cb_data);\n }\n \n+int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(refs, \"\", fn, 0, 0, cb_data);\n+}\n+\n int for_each_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"\", fn, 0, 0, cb_data);\n+\treturn refs_for_each_ref(get_main_ref_store(), 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, \"\", fn, 0, 0, cb_data);\n+\treturn refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);\n+}\n+\n+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,\n+\t\t\t each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);\n }\n \n int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);\n+\treturn refs_for_each_ref_in(get_main_ref_store(), prefix, fn, cb_data);\n }\n \n int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)\n@@ -1204,19 +1242,23 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig\n \n \tif (broken)\n \t\tflag = DO_FOR_EACH_INCLUDE_BROKEN;\n-\treturn do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);\n+\treturn do_for_each_ref(get_main_ref_store(),\n+\t\t\t       prefix, fn, 0, flag, cb_data);\n }\n \n int for_each_ref_in_submodule(const char *submodule, const char *prefix,\n-\t\teach_ref_fn fn, void *cb_data)\n+\t\t\t      each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);\n+\treturn refs_for_each_ref_in(get_submodule_ref_store(submodule),\n+\t\t\t\t    prefix, fn, cb_data);\n }\n \n int for_each_replace_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, git_replace_ref_base, fn,\n-\t\t\t       strlen(git_replace_ref_base), 0, cb_data);\n+\treturn do_for_each_ref(get_main_ref_store(),\n+\t\t\t       git_replace_ref_base, fn,\n+\t\t\t       strlen(git_replace_ref_base),\n+\t\t\t       0, cb_data);\n }\n \n int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n@@ -1224,19 +1266,25 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\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+\tret = do_for_each_ref(get_main_ref_store(),\n+\t\t\t      buf.buf, fn, 0, 0, cb_data);\n \tstrbuf_release(&buf);\n \treturn ret;\n }\n \n-int for_each_rawref(each_ref_fn fn, void *cb_data)\n+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"\", fn, 0,\n+\treturn do_for_each_ref(refs, \"\", fn, 0,\n \t\t\t       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n }\n \n+int for_each_rawref(each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_rawref(get_main_ref_store(), fn, cb_data);\n+}\n+\n /* This function needs to return a meaningful errno on failure */\n-const char *resolve_ref_recursively(struct ref_store *refs,\n+const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    unsigned char *sha1, int *flags)\n@@ -1323,7 +1371,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n+\treturn refs_resolve_ref_unsafe(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1353,7 +1401,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \tif (!refs)\n \t\treturn -1;\n \n-\tif (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||\n+\tif (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) ||\n \t    is_null_sha1(sha1))\n \t\treturn -1;\n \treturn 0;\n@@ -1511,27 +1559,42 @@ void base_ref_store_init(struct ref_store *refs,\n }\n \n /* backend functions */\n+int refs_pack_refs(struct ref_store *refs, unsigned int flags)\n+{\n+\treturn refs->be->pack_refs(refs, flags);\n+}\n+\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_pack_refs(get_main_ref_store(), flags);\n+}\n \n-\treturn refs->be->pack_refs(refs, flags);\n+int refs_peel_ref(struct ref_store *refs, const char *refname,\n+\t\t  unsigned char *sha1)\n+{\n+\treturn refs->be->peel_ref(refs, refname, sha1);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_peel_ref(get_main_ref_store(), refname, sha1);\n+}\n \n-\treturn refs->be->peel_ref(refs, refname, sha1);\n+int refs_create_symref(struct ref_store *refs,\n+\t\t       const char *ref_target,\n+\t\t       const char *refs_heads_master,\n+\t\t       const char *logmsg)\n+{\n+\treturn refs->be->create_symref(refs, ref_target,\n+\t\t\t\t       refs_heads_master,\n+\t\t\t\t       logmsg);\n }\n \n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n-\treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n-\t\t\t\t       logmsg);\n+\treturn refs_create_symref(get_main_ref_store(), ref_target,\n+\t\t\t\t  refs_heads_master, logmsg);\n }\n \n int ref_transaction_commit(struct ref_transaction *transaction,\n@@ -1542,19 +1605,17 @@ int ref_transaction_commit(struct ref_transaction *transaction,\n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n \n-int verify_refname_available(const char *refname,\n-\t\t\t     const struct string_list *extra,\n-\t\t\t     const struct string_list *skip,\n-\t\t\t     struct strbuf *err)\n+int refs_verify_refname_available(struct ref_store *refs,\n+\t\t\t\t  const char *refname,\n+\t\t\t\t  const struct string_list *extra,\n+\t\t\t\t  const struct string_list *skip,\n+\t\t\t\t  struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n-int for_each_reflog(each_ref_fn fn, void *cb_data)\n+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1562,43 +1623,84 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n \treturn do_for_each_ref_iterator(iter, fn, cb_data);\n }\n \n-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n-\t\t\t\tvoid *cb_data)\n+int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_for_each_reflog(get_main_ref_store(), fn, cb_data);\n+}\n \n+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     each_reflog_ent_fn fn,\n+\t\t\t\t     void *cb_data)\n+{\n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n }\n \n+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n+\t\t\t\tvoid *cb_data)\n+{\n+\treturn refs_for_each_reflog_ent_reverse(get_main_ref_store(),\n+\t\t\t\t\t\trefname, fn, cb_data);\n+}\n+\n+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,\n+\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n+{\n+\treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n+}\n+\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_for_each_reflog_ent(get_main_ref_store(), refname,\n+\t\t\t\t\tfn, cb_data);\n+}\n \n-\treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n+int refs_reflog_exists(struct ref_store *refs, const char *refname)\n+{\n+\treturn refs->be->reflog_exists(refs, refname);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_reflog_exists(get_main_ref_store(), refname);\n+}\n \n-\treturn refs->be->reflog_exists(refs, refname);\n+int refs_create_reflog(struct ref_store *refs, const char *refname,\n+\t\t       int force_create, struct strbuf *err)\n+{\n+\treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_create_reflog(get_main_ref_store(), refname,\n+\t\t\t\t  force_create, err);\n+}\n \n-\treturn refs->be->create_reflog(refs, refname, force_create, err);\n+int refs_delete_reflog(struct ref_store *refs, const char *refname)\n+{\n+\treturn refs->be->delete_reflog(refs, refname);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_delete_reflog(get_main_ref_store(), refname);\n+}\n \n-\treturn refs->be->delete_reflog(refs, refname);\n+int refs_reflog_expire(struct ref_store *refs,\n+\t\t       const char *refname, const unsigned char *sha1,\n+\t\t       unsigned int flags,\n+\t\t       reflog_expiry_prepare_fn prepare_fn,\n+\t\t       reflog_expiry_should_prune_fn should_prune_fn,\n+\t\t       reflog_expiry_cleanup_fn cleanup_fn,\n+\t\t       void *policy_cb_data)\n+{\n+\treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n+\t\t\t\t       prepare_fn, should_prune_fn,\n+\t\t\t\t       cleanup_fn, policy_cb_data);\n }\n \n int reflog_expire(const char *refname, const unsigned char *sha1,\n@@ -1608,11 +1710,10 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n-\treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n-\t\t\t\t       prepare_fn, should_prune_fn,\n-\t\t\t\t       cleanup_fn, policy_cb_data);\n+\treturn refs_reflog_expire(get_main_ref_store(),\n+\t\t\t\t  refname, sha1, flags,\n+\t\t\t\t  prepare_fn, should_prune_fn,\n+\t\t\t\t  cleanup_fn, policy_cb_data);\n }\n \n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n@@ -1623,16 +1724,24 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n-int delete_refs(struct string_list *refnames, unsigned int flags)\n+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,\n+\t\t     unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n-int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n+int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_delete_refs(get_main_ref_store(), refnames, flags);\n+}\n \n+int refs_rename_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg)\n+{\n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\n+\n+int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n+{\n+\treturn refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);\n+}\ndiff --git a/refs.h b/refs.h\nindex 2efba6ba9..70d4eb87c 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -58,16 +58,47 @@ struct string_list;\n #define RESOLVE_REF_NO_RECURSE 0x02\n #define RESOLVE_REF_ALLOW_BAD_NAME 0x04\n \n+const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n+\t\t\t\t    const char *refname,\n+\t\t\t\t    int resolve_flags,\n+\t\t\t\t    unsigned char *sha1,\n+\t\t\t\t    int *flags);\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags);\n \n char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t     unsigned char *sha1, int *flags);\n \n+int refs_read_ref_full(struct ref_store *refs, const char *refname,\n+\t\t       int resolve_flags, unsigned char *sha1, int *flags);\n int read_ref_full(const char *refname, int resolve_flags,\n \t\t  unsigned char *sha1, int *flags);\n int read_ref(const char *refname, unsigned char *sha1);\n \n+/*\n+ * Return 0 if a reference named refname could be created without\n+ * conflicting with the name of an existing reference. Otherwise,\n+ * return a negative value and write an explanation to err. If extras\n+ * is non-NULL, it is a list of additional refnames with which refname\n+ * is not allowed to conflict. If skip is non-NULL, ignore potential\n+ * conflicts with refs in skip (e.g., because they are scheduled for\n+ * deletion in the same operation). Behavior is undefined if the same\n+ * name is listed in both extras and skip.\n+ *\n+ * Two reference names conflict if one of them exactly matches the\n+ * leading components of the other; e.g., \"foo/bar\" conflicts with\n+ * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n+ * \"foo/barbados\".\n+ *\n+ * extras and skip must be sorted.\n+ */\n+\n+int refs_verify_refname_available(struct ref_store *refs,\n+\t\t\t\t  const char *refname,\n+\t\t\t\t  const struct string_list *extra,\n+\t\t\t\t  const struct string_list *skip,\n+\t\t\t\t  struct strbuf *err);\n+\n int ref_exists(const char *refname);\n \n int should_autocreate_reflog(const char *refname);\n@@ -84,6 +115,8 @@ extern int refs_init_db(struct strbuf *err);\n  * Symbolic references are considered unpeelable, even if they\n  * ultimately resolve to a peelable tag.\n  */\n+int refs_peel_ref(struct ref_store *refs, const char *refname,\n+\t\t  unsigned char *sha1);\n int peel_ref(const char *refname, unsigned char *sha1);\n \n /**\n@@ -196,6 +229,17 @@ typedef int each_ref_fn(const char *refname,\n  * modifies the reference also returns a nonzero value to immediately\n  * stop the iteration.\n  */\n+int refs_for_each_ref(struct ref_store *refs,\n+\t\t      each_ref_fn fn, void *cb_data);\n+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,\n+\t\t\t each_ref_fn fn, void *cb_data);\n+int refs_for_each_tag_ref(struct ref_store *refs,\n+\t\t\t  each_ref_fn fn, void *cb_data);\n+int refs_for_each_branch_ref(struct ref_store *refs,\n+\t\t\t     each_ref_fn fn, void *cb_data);\n+int refs_for_each_remote_ref(struct ref_store *refs,\n+\t\t\t     each_ref_fn fn, void *cb_data);\n+\n int head_ref(each_ref_fn fn, void *cb_data);\n int for_each_ref(each_ref_fn fn, void *cb_data);\n int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);\n@@ -225,6 +269,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data);\n int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);\n \n /* can be used to learn about broken ref and symref */\n+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_rawref(each_ref_fn fn, void *cb_data);\n \n static inline const char *has_glob_specials(const char *pattern)\n@@ -248,6 +293,7 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,\n  * Write a packed-refs file for the current repository.\n  * flags: Combination of the above PACK_REFS_* flags.\n  */\n+int refs_pack_refs(struct ref_store *refs, unsigned int flags);\n int pack_refs(unsigned int flags);\n \n /*\n@@ -263,6 +309,8 @@ int pack_refs(unsigned int flags);\n /*\n  * Setup reflog before using. Fill in err and return -1 on failure.\n  */\n+int refs_create_reflog(struct ref_store *refs, const char *refname,\n+\t\t       int force_create, struct strbuf *err);\n int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);\n \n /** Reads log for the value of ref during at_time. **/\n@@ -272,6 +320,7 @@ int read_ref_at(const char *refname, unsigned int flags,\n \t\tunsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);\n \n /** Check if a particular reflog exists */\n+int refs_reflog_exists(struct ref_store *refs, const char *refname);\n int reflog_exists(const char *refname);\n \n /*\n@@ -290,9 +339,12 @@ int delete_ref(const char *refname, const unsigned char *old_sha1,\n  * an all-or-nothing transaction). flags is passed through to\n  * ref_transaction_delete().\n  */\n+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,\n+\t\t     unsigned int flags);\n int delete_refs(struct string_list *refnames, unsigned int flags);\n \n /** Delete a reflog */\n+int refs_delete_reflog(struct ref_store *refs, const char *refname);\n int delete_reflog(const char *refname);\n \n /* iterate over reflog entries */\n@@ -301,6 +353,12 @@ typedef int each_reflog_ent_fn(\n \t\tconst char *committer, unsigned long timestamp,\n \t\tint tz, const char *msg, void *cb_data);\n \n+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,\n+\t\t\t     each_reflog_ent_fn fn, void *cb_data);\n+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     each_reflog_ent_fn fn,\n+\t\t\t\t     void *cb_data);\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);\n \n@@ -308,6 +366,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void\n  * Calls the specified function for each reflog file until it returns nonzero,\n  * and returns the value\n  */\n+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_reflog(each_ref_fn fn, void *cb_data);\n \n #define REFNAME_ALLOW_ONELEVEL 1\n@@ -328,8 +387,12 @@ const char *prettify_refname(const char *refname);\n char *shorten_unambiguous_ref(const char *refname, int strict);\n \n /** rename ref, return 0 on success **/\n+int refs_rename_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg);\n int rename_ref(const char *oldref, const char *newref, const char *logmsg);\n \n+int refs_create_symref(struct ref_store *refs, const char *refname,\n+\t\t       const char *target, const char *logmsg);\n int create_symref(const char *refname, const char *target, const char *logmsg);\n \n /*\n@@ -551,6 +614,14 @@ typedef void reflog_expiry_cleanup_fn(void *cb_data);\n  * enum expire_reflog_flags. The three function pointers are described\n  * above. On success, return zero.\n  */\n+int refs_reflog_expire(struct ref_store *refs,\n+\t\t       const char *refname,\n+\t\t       const unsigned char *sha1,\n+\t\t       unsigned int flags,\n+\t\t       reflog_expiry_prepare_fn prepare_fn,\n+\t\t       reflog_expiry_should_prune_fn should_prune_fn,\n+\t\t       reflog_expiry_cleanup_fn cleanup_fn,\n+\t\t       void *policy_cb_data);\n int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  unsigned int flags,\n \t\t  reflog_expiry_prepare_fn prepare_fn,\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 474d1027c..dafddefd3 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1320,7 +1320,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \t\t\t\t\t create_dir_entry(refs, refname.buf,\n \t\t\t\t\t\t\t  refname.len, 1));\n \t\t} else {\n-\t\t\tif (!resolve_ref_recursively(&refs->base,\n+\t\t\tif (!refs_resolve_ref_unsafe(&refs->base,\n \t\t\t\t\t\t     refname.buf,\n \t\t\t\t\t\t     RESOLVE_REF_READING,\n \t\t\t\t\t\t     sha1, &flag)) {\n@@ -1629,7 +1629,8 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t\t * another reference such as \"refs/foo\". There is no\n \t\t * reason to expect this error to be transitory.\n \t\t */\n-\t\tif (verify_refname_available(refname, extras, skip, err)) {\n+\t\tif (refs_verify_refname_available(&refs->base, refname,\n+\t\t\t\t\t\t  extras, skip, err)) {\n \t\t\tif (mustexist) {\n \t\t\t\t/*\n \t\t\t\t * To the user the relevant error is\n@@ -2671,7 +2672,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (flag & REF_ISSYMREF)\n \t\treturn error(\"refname %s is a symbolic ref, renaming it is not supported\",\n \t\t\toldrefname);\n-\tif (!rename_ref_available(oldrefname, newrefname))\n+\tif (!refs_rename_ref_available(&refs->base, oldrefname, newrefname))\n \t\treturn 1;\n \n \tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n@@ -4055,9 +4056,9 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\tif ((update->flags & REF_HAVE_OLD) &&\n \t\t    !is_null_sha1(update->old_sha1))\n \t\t\tdie(\"BUG: initial ref transaction with old_sha1 set\");\n-\t\tif (verify_refname_available(update->refname,\n-\t\t\t\t\t     &affected_refnames, NULL,\n-\t\t\t\t\t     err)) {\n+\t\tif (refs_verify_refname_available(&refs->base, update->refname,\n+\t\t\t\t\t\t  &affected_refnames, NULL,\n+\t\t\t\t\t\t  err)) {\n \t\t\tret = TRANSACTION_NAME_CONFLICT;\n \t\t\tgoto cleanup;\n \t\t}\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f20dde39e..5f26208c2 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -112,28 +112,6 @@ enum peel_status {\n enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);\n \n /*\n- * Return 0 if a reference named refname could be created without\n- * conflicting with the name of an existing reference. Otherwise,\n- * return a negative value and write an explanation to err. If extras\n- * is non-NULL, it is a list of additional refnames with which refname\n- * is not allowed to conflict. If skip is non-NULL, ignore potential\n- * conflicts with refs in skip (e.g., because they are scheduled for\n- * deletion in the same operation). Behavior is undefined if the same\n- * name is listed in both extras and skip.\n- *\n- * Two reference names conflict if one of them exactly matches the\n- * leading components of the other; e.g., \"foo/bar\" conflicts with\n- * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n- * \"foo/barbados\".\n- *\n- * extras and skip must be sorted.\n- */\n-int verify_refname_available(const char *newname,\n-\t\t\t     const struct string_list *extras,\n-\t\t\t     const struct string_list *skip,\n-\t\t\t     struct strbuf *err);\n-\n-/*\n  * Copy the reflog message msg to buf, which has been allocated sufficiently\n  * large, while cleaning up the whitespaces.  Especially, convert LF to space,\n  * because reflog file is one line per entry.\n@@ -252,7 +230,9 @@ const char *find_descendant_ref(const char *dirname,\n  * processes (though rename_ref() catches some races that might get by\n  * this check).\n  */\n-int rename_ref_available(const char *old_refname, const char *new_refname);\n+int refs_rename_ref_available(struct ref_store *refs,\n+\t\t\t      const char *old_refname,\n+\t\t\t      const char *new_refname);\n \n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define SYMREF_MAXDEPTH 5\n@@ -646,9 +626,4 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-const char *resolve_ref_recursively(struct ref_store *refs,\n-\t\t\t\t    const char *refname,\n-\t\t\t\t    int resolve_flags,\n-\t\t\t\t    unsigned char *sha1, int *flags);\n-\n #endif /* REFS_REFS_INTERNAL_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312302","messageId":"20170222140450.30886-20-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 19/24] refs: new transaction related ref-store api","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:45Z","receivedAt":"2017-02-22T14:08:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The transaction struct now takes a ref store at creation and will\noperate on that ref store alone.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 54 ++++++++++++++++++++++++++++++++++++++++------------\n refs.h               |  8 ++++++++\n refs/refs-internal.h |  1 +\n 3 files changed, 51 insertions(+), 12 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 9137ac283..851b5e125 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -618,16 +618,19 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1\n \treturn 0;\n }\n \n-int delete_ref(const char *refname, const unsigned char *old_sha1,\n-\t       unsigned int flags)\n+int refs_delete_ref(struct ref_store *refs, const char *refname,\n+\t\t    const unsigned char *old_sha1,\n+\t\t    unsigned int flags)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tif (ref_type(refname) == REF_TYPE_PSEUDOREF)\n+\tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n+\t\tassert(refs == get_main_ref_store());\n \t\treturn delete_pseudoref(refname, old_sha1);\n+\t}\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = ref_store_transaction_begin(refs, &err);\n \tif (!transaction ||\n \t    ref_transaction_delete(transaction, refname, old_sha1,\n \t\t\t\t   flags, NULL, &err) ||\n@@ -642,6 +645,13 @@ int delete_ref(const char *refname, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n+int delete_ref(const char *refname, const unsigned char *old_sha1,\n+\t       unsigned int flags)\n+{\n+\treturn refs_delete_ref(get_main_ref_store(), refname,\n+\t\t\t       old_sha1, flags);\n+}\n+\n int copy_reflog_msg(char *buf, const char *msg)\n {\n \tchar *cp = buf;\n@@ -801,11 +811,20 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,\n \treturn 1;\n }\n \n-struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n+\t\t\t\t\t\t    struct strbuf *err)\n {\n+\tstruct ref_transaction *tr;\n \tassert(err);\n \n-\treturn xcalloc(1, sizeof(struct ref_transaction));\n+\ttr = xcalloc(1, sizeof(struct ref_transaction));\n+\ttr->ref_store = refs;\n+\treturn tr;\n+}\n+\n+struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+{\n+\treturn ref_store_transaction_begin(get_main_ref_store(), err);\n }\n \n void ref_transaction_free(struct ref_transaction *transaction)\n@@ -922,18 +941,20 @@ int update_ref_oid(const char *msg, const char *refname,\n \t\told_oid ? old_oid->hash : NULL, flags, onerr);\n }\n \n-int update_ref(const char *msg, const char *refname,\n-\t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n-\t       unsigned int flags, enum action_on_err onerr)\n+int refs_update_ref(struct ref_store *refs, const char *msg,\n+\t\t    const char *refname, const unsigned char *new_sha1,\n+\t\t    const unsigned char *old_sha1, unsigned int flags,\n+\t\t    enum action_on_err onerr)\n {\n \tstruct ref_transaction *t = NULL;\n \tstruct strbuf err = STRBUF_INIT;\n \tint ret = 0;\n \n \tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n+\t\tassert(refs == get_main_ref_store());\n \t\tret = write_pseudoref(refname, new_sha1, old_sha1, &err);\n \t} else {\n-\t\tt = ref_transaction_begin(&err);\n+\t\tt = ref_store_transaction_begin(refs, &err);\n \t\tif (!t ||\n \t\t    ref_transaction_update(t, refname, new_sha1, old_sha1,\n \t\t\t\t\t   flags, msg, &err) ||\n@@ -964,6 +985,15 @@ int update_ref(const char *msg, const char *refname,\n \treturn 0;\n }\n \n+int update_ref(const char *msg, const char *refname,\n+\t       const unsigned char *new_sha1,\n+\t       const unsigned char *old_sha1,\n+\t       unsigned int flags, enum action_on_err onerr)\n+{\n+\treturn refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,\n+\t\t\t       old_sha1, flags, onerr);\n+}\n+\n char *shorten_unambiguous_ref(const char *refname, int strict)\n {\n \tint i;\n@@ -1600,7 +1630,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\tstruct ref_store *refs = transaction->ref_store;\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1719,7 +1749,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\tstruct ref_store *refs = transaction->ref_store;\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\ndiff --git a/refs.h b/refs.h\nindex 70d4eb87c..342cecd23 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -330,6 +330,9 @@ int reflog_exists(const char *refname);\n  * exists, regardless of its old value. It is an error for old_sha1 to\n  * be NULL_SHA1. flags is passed through to ref_transaction_delete().\n  */\n+int refs_delete_ref(struct ref_store *refs, const char *refname,\n+\t\t    const unsigned char *old_sha1,\n+\t\t    unsigned int flags);\n int delete_ref(const char *refname, const unsigned char *old_sha1,\n \t       unsigned int flags);\n \n@@ -414,6 +417,8 @@ enum action_on_err {\n  * Begin a reference transaction.  The reference transaction must\n  * be freed by calling ref_transaction_free().\n  */\n+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n+\t\t\t\t\t\t    struct strbuf *err);\n struct ref_transaction *ref_transaction_begin(struct strbuf *err);\n \n /*\n@@ -548,6 +553,9 @@ void ref_transaction_free(struct ref_transaction *transaction);\n  * ref_transaction_update(). Handle errors as requested by the `onerr`\n  * argument.\n  */\n+int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,\n+\t\t    const unsigned char *new_sha1, const unsigned char *old_sha1,\n+\t\t    unsigned int flags, enum action_on_err onerr);\n int update_ref(const char *msg, const char *refname,\n \t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n \t       unsigned int flags, enum action_on_err onerr);\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 5f26208c2..690498698 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -200,6 +200,7 @@ enum ref_transaction_state {\n  * as atomically as possible.  This structure is opaque to callers.\n  */\n struct ref_transaction {\n+\tstruct ref_store *ref_store;\n \tstruct ref_update **updates;\n \tsize_t alloc;\n \tsize_t nr;\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312303","messageId":"20170222140450.30886-21-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 20/24] files-backend: avoid ref api targetting main ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:46Z","receivedAt":"2017-02-22T14:08:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A small step towards making files-backend works as a non-main ref store\nusing the newly added store-aware API.\n\nFor the record, `join` and `nm` on refs.o and files-backend.o tell me\nthat files-backend no longer uses functions that defaults to\nget_main_ref_store().\n\nI'm not yet comfortable at the idea of removing\nfiles_assert_main_repository() (or converting REF_STORE_MAIN to\nREF_STORE_WRITE). More staring and testing is required before that can\nhappen. Well, except peel_ref(). I'm pretty sure that function is safe.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 85 ++++++++++++++++++++++++++++++----------------------\n 1 file changed, 49 insertions(+), 36 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex dafddefd3..09c280fd3 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1836,8 +1836,6 @@ static int files_peel_ref(struct ref_store *ref_store,\n \tint flag;\n \tunsigned char base[20];\n \n-\tfiles_assert_main_repository(refs, \"peel_ref\");\n-\n \tif (current_ref_iter && current_ref_iter->refname == refname) {\n \t\tstruct object_id peeled;\n \n@@ -1847,7 +1845,8 @@ static int files_peel_ref(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tif (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))\n+\tif (refs_read_ref_full(ref_store, refname,\n+\t\t\t       RESOLVE_REF_READING, base, &flag))\n \t\treturn -1;\n \n \t/*\n@@ -2017,15 +2016,15 @@ static struct ref_iterator *files_ref_iterator_begin(\n  * on success. On error, write an error message to err, set errno, and\n  * return a negative value.\n  */\n-static int verify_lock(struct ref_lock *lock,\n+static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,\n \t\t       const unsigned char *old_sha1, int mustexist,\n \t\t       struct strbuf *err)\n {\n \tassert(err);\n \n-\tif (read_ref_full(lock->ref_name,\n-\t\t\t  mustexist ? RESOLVE_REF_READING : 0,\n-\t\t\t  lock->old_oid.hash, NULL)) {\n+\tif (refs_read_ref_full(ref_store, lock->ref_name,\n+\t\t\t       mustexist ? RESOLVE_REF_READING : 0,\n+\t\t\t       lock->old_oid.hash, NULL)) {\n \t\tif (old_sha1) {\n \t\t\tint save_errno = errno;\n \t\t\tstrbuf_addf(err, \"can't verify ref '%s'\", lock->ref_name);\n@@ -2094,8 +2093,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n \tfiles_refname_path(refs, &ref_file, refname);\n-\tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\tlock->old_oid.hash, type);\n+\tresolved = !!refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t     refname, resolve_flags,\n+\t\t\t\t\t     lock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n \t\t/*\n \t\t * we are trying to lock foo but we used to\n@@ -2112,8 +2112,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\t\t\t\t    refname);\n \t\t\tgoto error_return;\n \t\t}\n-\t\tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\t\tlock->old_oid.hash, type);\n+\t\tresolved = !!refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t\t     refname, resolve_flags,\n+\t\t\t\t\t\t     lock->old_oid.hash, type);\n \t}\n \tif (!resolved) {\n \t\tlast_errno = errno;\n@@ -2151,7 +2152,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\tgoto error_return;\n \t}\n \n-\tif (verify_lock(lock, old_sha1, mustexist, err)) {\n+\tif (verify_lock(&refs->base, lock, old_sha1, mustexist, err)) {\n \t\tlast_errno = errno;\n \t\tgoto error_return;\n \t}\n@@ -2406,7 +2407,7 @@ static void try_remove_empty_parents(struct files_ref_store *refs,\n }\n \n /* make sure nobody touched the ref, and unlink */\n-static void prune_ref(struct ref_to_prune *r)\n+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -2414,7 +2415,7 @@ static void prune_ref(struct ref_to_prune *r)\n \tif (check_refname_format(r->name, 0))\n \t\treturn;\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = ref_store_transaction_begin(&refs->base, &err);\n \tif (!transaction ||\n \t    ref_transaction_delete(transaction, r->name, r->sha1,\n \t\t\t\t   REF_ISPRUNING | REF_NODEREF, NULL, &err) ||\n@@ -2428,10 +2429,10 @@ static void prune_ref(struct ref_to_prune *r)\n \tstrbuf_release(&err);\n }\n \n-static void prune_refs(struct ref_to_prune *r)\n+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \twhile (r) {\n-\t\tprune_ref(r);\n+\t\tprune_ref(refs, r);\n \t\tr = r->next;\n \t}\n }\n@@ -2455,7 +2456,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n \tif (commit_packed_refs(refs))\n \t\tdie_errno(\"unable to overwrite old ref-pack file\");\n \n-\tprune_refs(cbdata.ref_to_prune);\n+\tprune_refs(refs, cbdata.ref_to_prune);\n \treturn 0;\n }\n \n@@ -2547,7 +2548,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \tfor (i = 0; i < refnames->nr; i++) {\n \t\tconst char *refname = refnames->items[i].string;\n \n-\t\tif (delete_ref(refname, NULL, flags))\n+\t\tif (refs_delete_ref(&refs->base, refname, NULL, flags))\n \t\t\tresult |= error(_(\"could not remove reference %s\"), refname);\n \t}\n \n@@ -2665,8 +2666,9 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tif (log && S_ISLNK(loginfo.st_mode))\n \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n \n-\tif (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n-\t\t\t\torig_sha1, &flag))\n+\tif (!refs_resolve_ref_unsafe(&refs->base, oldrefname,\n+\t\t\t\t     RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\t\t\t\t     orig_sha1, &flag))\n \t\treturn error(\"refname %s not found\", oldrefname);\n \n \tif (flag & REF_ISSYMREF)\n@@ -2684,7 +2686,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\treturn error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n-\tif (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {\n+\tif (refs_delete_ref(&refs->base, oldrefname, orig_sha1, REF_NODEREF)) {\n \t\terror(\"unable to delete old %s\", oldrefname);\n \t\tgoto rollback;\n \t}\n@@ -2696,9 +2698,10 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t * the safety anyway; we want to delete the reference whatever\n \t * its current value.\n \t */\n-\tif (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n-\t\t\t   sha1, NULL) &&\n-\t    delete_ref(newrefname, NULL, REF_NODEREF)) {\n+\tif (!refs_read_ref_full(&refs->base, newrefname,\n+\t\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\t\t\t\tsha1, NULL) &&\n+\t    refs_delete_ref(&refs->base, newrefname, NULL, REF_NODEREF)) {\n \t\tif (errno == EISDIR) {\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n@@ -3054,8 +3057,9 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tint head_flag;\n \t\tconst char *head_ref;\n \n-\t\thead_ref = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n-\t\t\t\t\t      head_sha1, &head_flag);\n+\t\thead_ref = refs_resolve_ref_unsafe(&refs->base, \"HEAD\",\n+\t\t\t\t\t\t   RESOLVE_REF_READING,\n+\t\t\t\t\t\t   head_sha1, &head_flag);\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n@@ -3099,7 +3103,9 @@ static void update_symref_reflog(struct files_ref_store *refs,\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n-\tif (logmsg && !read_ref(target, new_sha1) &&\n+\tif (logmsg &&\n+\t    !refs_read_ref_full(&refs->base, target,\n+\t\t\t\tRESOLVE_REF_READING, new_sha1, NULL) &&\n \t    files_log_ref_write(refs, refname, lock->old_oid.hash,\n \t\t\t\tnew_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n@@ -3403,6 +3409,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n struct files_reflog_iterator {\n \tstruct ref_iterator base;\n \n+\tstruct ref_store *ref_store;\n \tstruct dir_iterator *dir_iterator;\n \tstruct object_id oid;\n };\n@@ -3424,8 +3431,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)\n \t\tif (ends_with(diter->basename, \".lock\"))\n \t\t\tcontinue;\n \n-\t\tif (read_ref_full(diter->relative_path, 0,\n-\t\t\t\t  iter->oid.hash, &flags)) {\n+\t\tif (refs_read_ref_full(iter->ref_store,\n+\t\t\t\t       diter->relative_path, 0,\n+\t\t\t\t       iter->oid.hash, &flags)) {\n \t\t\terror(\"bad ref for %s\", diter->path.buf);\n \t\t\tcontinue;\n \t\t}\n@@ -3479,6 +3487,7 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n \tfiles_reflog_path(refs, &sb, NULL);\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\titer->ref_store = ref_store;\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n@@ -3718,8 +3727,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \t\t\t * the transaction, so we have to read it here\n \t\t\t * to record and possibly check old_sha1:\n \t\t\t */\n-\t\t\tif (read_ref_full(referent.buf, 0,\n-\t\t\t\t\t  lock->old_oid.hash, NULL)) {\n+\t\t\tif (refs_read_ref_full(&refs->base,\n+\t\t\t\t\t       referent.buf, 0,\n+\t\t\t\t\t       lock->old_oid.hash, NULL)) {\n \t\t\t\tif (update->flags & REF_HAVE_OLD) {\n \t\t\t\t\tstrbuf_addf(err, \"cannot lock ref '%s': \"\n \t\t\t\t\t\t    \"error reading reference\",\n@@ -3873,8 +3883,10 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t * head_ref within the transaction, then split_head_update()\n \t * arranges for the reflog of HEAD to be updated, too.\n \t */\n-\thead_ref = resolve_refdup(\"HEAD\", RESOLVE_REF_NO_RECURSE,\n-\t\t\t\t  head_oid.hash, &head_type);\n+\thead_ref = (char *)refs_resolve_ref_unsafe(ref_store, \"HEAD\",\n+\t\t\t\t\t\t   RESOLVE_REF_NO_RECURSE,\n+\t\t\t\t\t\t   head_oid.hash, &head_type);\n+\thead_ref = xstrdup_or_null(head_ref);\n \n \tif (head_ref && !(head_type & REF_ISSYMREF)) {\n \t\tfree(head_ref);\n@@ -4047,7 +4059,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t * so here we really only check that none of the references\n \t * that we are creating already exists.\n \t */\n-\tif (for_each_rawref(ref_present, &affected_refnames))\n+\tif (refs_for_each_rawref(&refs->base, ref_present,\n+\t\t\t\t &affected_refnames))\n \t\tdie(\"BUG: initial ref transaction called with existing refs\");\n \n \tfor (i = 0; i < transaction->nr; i++) {\n@@ -4166,7 +4179,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\tstrbuf_release(&err);\n \t\treturn -1;\n \t}\n-\tif (!reflog_exists(refname)) {\n+\tif (!refs_reflog_exists(ref_store, refname)) {\n \t\tunlock_ref(lock);\n \t\treturn 0;\n \t}\n@@ -4197,7 +4210,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t}\n \n \t(*prepare_fn)(refname, sha1, cb.policy_cb);\n-\tfor_each_reflog_ent(refname, expire_reflog_ent, &cb);\n+\trefs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);\n \t(*cleanup_fn)(cb.policy_cb);\n \n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312304","messageId":"20170222140450.30886-22-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 21/24] refs: delete pack_refs() in favor of refs_pack_refs()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:47Z","receivedAt":"2017-02-22T14:08:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It only has one caller, not worth keeping just for convenience.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/pack-refs.c | 2 +-\n refs.c              | 5 -----\n refs.h              | 1 -\n 3 files changed, 1 insertion(+), 7 deletions(-)\n\ndiff --git a/builtin/pack-refs.c b/builtin/pack-refs.c\nindex 39f9a55d1..b106a392a 100644\n--- a/builtin/pack-refs.c\n+++ b/builtin/pack-refs.c\n@@ -17,5 +17,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)\n \t};\n \tif (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))\n \t\tusage_with_options(pack_refs_usage, opts);\n-\treturn pack_refs(flags);\n+\treturn refs_pack_refs(get_main_ref_store(), flags);\n }\ndiff --git a/refs.c b/refs.c\nindex 851b5e125..6efe5957d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1594,11 +1594,6 @@ int refs_pack_refs(struct ref_store *refs, unsigned int flags)\n \treturn refs->be->pack_refs(refs, flags);\n }\n \n-int pack_refs(unsigned int flags)\n-{\n-\treturn refs_pack_refs(get_main_ref_store(), flags);\n-}\n-\n int refs_peel_ref(struct ref_store *refs, const char *refname,\n \t\t  unsigned char *sha1)\n {\ndiff --git a/refs.h b/refs.h\nindex 342cecd23..1af41eaef 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -294,7 +294,6 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,\n  * flags: Combination of the above PACK_REFS_* flags.\n  */\n int refs_pack_refs(struct ref_store *refs, unsigned int flags);\n-int pack_refs(unsigned int flags);\n \n /*\n  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312305","messageId":"20170222140450.30886-23-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 22/24] t/helper: add test-ref-store to test ref-store functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:48Z","receivedAt":"2017-02-22T14:08:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile                        |   1 +\n t/helper/.gitignore             |   1 +\n t/helper/test-ref-store.c (new) | 274 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 276 insertions(+)\n create mode 100644 t/helper/test-ref-store.c\n\ndiff --git a/Makefile b/Makefile\nindex 8e4081e06..d62d64623 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -624,6 +624,7 @@ TEST_PROGRAMS_NEED_X += test-parse-options\n TEST_PROGRAMS_NEED_X += test-path-utils\n TEST_PROGRAMS_NEED_X += test-prio-queue\n TEST_PROGRAMS_NEED_X += test-read-cache\n+TEST_PROGRAMS_NEED_X += test-ref-store\n TEST_PROGRAMS_NEED_X += test-regex\n TEST_PROGRAMS_NEED_X += test-revision-walking\n TEST_PROGRAMS_NEED_X += test-run-command\ndiff --git a/t/helper/.gitignore b/t/helper/.gitignore\nindex d6e8b3679..5f68aa8f8 100644\n--- a/t/helper/.gitignore\n+++ b/t/helper/.gitignore\n@@ -19,6 +19,7 @@\n /test-path-utils\n /test-prio-queue\n /test-read-cache\n+/test-ref-store\n /test-regex\n /test-revision-walking\n /test-run-command\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nnew file mode 100644\nindex 000000000..c4c670acd\n--- /dev/null\n+++ b/t/helper/test-ref-store.c\n@@ -0,0 +1,274 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+\n+static const char *notnull(const char *arg, const char *name)\n+{\n+\tif (!arg)\n+\t\tdie(\"%s required\", name);\n+\treturn arg;\n+}\n+\n+static unsigned int arg_flags(const char *arg, const char *name)\n+{\n+\treturn atoi(notnull(arg, name));\n+}\n+\n+static const char **get_store(const char **argv, struct ref_store **refs)\n+{\n+\tconst char *gitdir;\n+\n+\tif (!argv[0]) {\n+\t\tdie(\"ref store required\");\n+\t} else if (!strcmp(argv[0], \"main\")) {\n+\t\t*refs = get_main_ref_store();\n+\t} else if (skip_prefix(argv[0], \"submodule:\", &gitdir)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret;\n+\n+\t\tret = strbuf_git_path_submodule(&sb, gitdir, \"objects/\");\n+\t\tif (ret)\n+\t\t\tdie(\"strbuf_git_path_submodule failed: %d\", ret);\n+\t\tadd_to_alternates_memory(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\n+\t\t*refs = get_submodule_ref_store(gitdir);\n+\t} else\n+\t\tdie(\"unknown backend %s\", argv[0]);\n+\n+\tif (!*refs)\n+\t\tdie(\"no ref store\");\n+\n+\t/* consume store-specific optional arguments if needed */\n+\n+\treturn argv + 1;\n+}\n+\n+\n+static int cmd_pack_refs(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\n+\treturn refs_pack_refs(refs, flags);\n+}\n+\n+static int cmd_peel_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tunsigned char sha1[20];\n+\tint ret;\n+\n+\tret = refs_peel_ref(refs, refname, sha1);\n+\tif (!ret)\n+\t\tputs(sha1_to_hex(sha1));\n+\treturn ret;\n+}\n+\n+static int cmd_create_symref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *target = notnull(*argv++, \"target\");\n+\tconst char *logmsg = *argv++;\n+\n+\treturn refs_create_symref(refs, refname, target, logmsg);\n+}\n+\n+static int cmd_delete_refs(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tstruct string_list refnames = STRING_LIST_INIT_NODUP;\n+\n+\twhile (*argv)\n+\t\tstring_list_append(&refnames, *argv++);\n+\n+\treturn refs_delete_refs(refs, &refnames, flags);\n+}\n+\n+static int cmd_rename_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *oldref = notnull(*argv++, \"oldref\");\n+\tconst char *newref = notnull(*argv++, \"newref\");\n+\tconst char *logmsg = *argv++;\n+\n+\treturn refs_rename_ref(refs, oldref, newref, logmsg);\n+}\n+\n+static int each_ref(const char *refname, const struct object_id *oid,\n+\t\t    int flags, void *cb_data)\n+{\n+\tprintf(\"%s %s 0x%x\\n\", oid_to_hex(oid), refname, flags);\n+\treturn 0;\n+}\n+\n+static int cmd_for_each_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *prefix = notnull(*argv++, \"prefix\");\n+\n+\treturn refs_for_each_ref_in(refs, prefix, each_ref, NULL);\n+}\n+\n+static int cmd_resolve_ref(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned char sha1[20];\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tint resolve_flags = arg_flags(*argv++, \"resolve-flags\");\n+\tint flags;\n+\tconst char *ref;\n+\n+\tref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\t\t\t\t      sha1, &flags);\n+\tprintf(\"%s %s 0x%x\\n\", sha1_to_hex(sha1), ref, flags);\n+\treturn ref ? 0 : 1;\n+}\n+\n+static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n+\n+\tret = refs_verify_refname_available(refs, refname, NULL, NULL, &err);\n+\tif (err.len)\n+\t\tputs(err.buf);\n+\treturn ret;\n+}\n+\n+static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)\n+{\n+\treturn refs_for_each_reflog(refs, each_ref, NULL);\n+}\n+\n+static int each_reflog(unsigned char *old_sha1, unsigned char *new_sha1,\n+\t\t       const char *committer, unsigned long timestamp,\n+\t\t       int tz, const char *msg, void *cb_data)\n+{\n+\tprintf(\"%s %s %s %lu %d %s\\n\",\n+\t       sha1_to_hex(old_sha1), sha1_to_hex(new_sha1),\n+\t       committer, timestamp, tz, msg);\n+\treturn 0;\n+}\n+\n+static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_for_each_reflog_ent(refs, refname, each_reflog, refs);\n+}\n+\n+static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);\n+}\n+\n+static int cmd_reflog_exists(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn !refs_reflog_exists(refs, refname);\n+}\n+\n+static int cmd_create_reflog(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tint force_create = arg_flags(*argv++, \"force-create\");\n+\tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n+\n+\tret = refs_create_reflog(refs, refname, force_create, &err);\n+\tif (err.len)\n+\t\tputs(err.buf);\n+\treturn ret;\n+}\n+\n+static int cmd_delete_reflog(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_delete_reflog(refs, refname);\n+}\n+\n+static int cmd_reflog_expire(struct ref_store *refs, const char **argv)\n+{\n+\tdie(\"not supported yet\");\n+}\n+\n+static int cmd_delete_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tunsigned char old_sha1[20];\n+\n+\tif (get_sha1_hex(sha1_buf, old_sha1))\n+\t\tdie(\"not sha-1\");\n+\n+\treturn refs_delete_ref(refs, refname, old_sha1, flags);\n+}\n+\n+static int cmd_update_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *msg = notnull(*argv++, \"msg\");\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *new_sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tunsigned char old_sha1[20];\n+\tunsigned char new_sha1[20];\n+\n+\tif (get_sha1_hex(old_sha1_buf, old_sha1) ||\n+\t    get_sha1_hex(new_sha1_buf, new_sha1))\n+\t\tdie(\"not sha-1\");\n+\n+\treturn refs_update_ref(refs, msg, refname,\n+\t\t\t       new_sha1, old_sha1,\n+\t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n+}\n+\n+struct command {\n+\tconst char *name;\n+\tint (*func)(struct ref_store *refs, const char **argv);\n+};\n+\n+static struct command commands[] = {\n+\t{ \"pack-refs\", cmd_pack_refs },\n+\t{ \"peel-ref\", cmd_peel_ref },\n+\t{ \"create-symref\", cmd_create_symref },\n+\t{ \"delete-refs\", cmd_delete_refs },\n+\t{ \"rename-ref\", cmd_rename_ref },\n+\t{ \"for-each-ref\", cmd_for_each_ref },\n+\t{ \"resolve-ref\", cmd_resolve_ref },\n+\t{ \"verify-ref\", cmd_verify_ref },\n+\t{ \"for-each-reflog\", cmd_for_each_reflog },\n+\t{ \"for-each-reflog-ent\", cmd_for_each_reflog_ent },\n+\t{ \"for-each-reflog-ent-reverse\", cmd_for_each_reflog_ent_reverse },\n+\t{ \"reflog-exists\", cmd_reflog_exists },\n+\t{ \"create-reflog\", cmd_create_reflog },\n+\t{ \"delete-reflog\", cmd_delete_reflog },\n+\t{ \"reflog-expire\", cmd_reflog_expire },\n+\t/*\n+\t * backend transaction functions can't be tested separately\n+\t */\n+\t{ \"delete-ref\", cmd_delete_ref },\n+\t{ \"update-ref\", cmd_update_ref },\n+\t{ NULL, NULL }\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct ref_store *refs;\n+\tconst char *func;\n+\tstruct command *cmd;\n+\n+\targv = get_store(argv + 1, &refs);\n+\n+\tfunc = *argv++;\n+\tif (!func)\n+\t\tdie(\"ref function required\");\n+\tfor (cmd = commands; cmd->name; cmd++) {\n+\t\tif (!strcmp(func, cmd->name))\n+\t\t\treturn cmd->func(refs, argv);\n+\t}\n+\tdie(\"unknown function %s\", func);\n+\treturn 0;\n+}\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312306","messageId":"20170222140450.30886-24-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 23/24] t1405: some basic tests on main ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:49Z","receivedAt":"2017-02-22T14:08:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1405-main-ref-store.sh (new +x) | 123 +++++++++++++++++++++++++++++++++++++\n 1 file changed, 123 insertions(+)\n create mode 100755 t/t1405-main-ref-store.sh\n\ndiff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh\nnew file mode 100755\nindex 000000000..0999829f1\n--- /dev/null\n+++ b/t/t1405-main-ref-store.sh\n@@ -0,0 +1,123 @@\n+#!/bin/sh\n+\n+test_description='test main ref store api'\n+\n+. ./test-lib.sh\n+\n+RUN=\"test-ref-store main\"\n+\n+test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '\n+\ttest_commit one &&\n+\tN=`find .git/refs -type f | wc -l` &&\n+\ttest \"$N\" != 0 &&\n+\t$RUN pack-refs 3 &&\n+\tN=`find .git/refs -type f | wc -l`\n+'\n+\n+test_expect_success 'peel_ref(new-tag)' '\n+\tgit rev-parse HEAD >expected &&\n+\tgit tag -a -m new-tag new-tag HEAD &&\n+\t$RUN peel-ref refs/tags/new-tag >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create_symref(FOO, refs/heads/master)' '\n+\t$RUN create-symref FOO refs/heads/master nothing &&\n+\techo refs/heads/master >expected &&\n+\tgit symbolic-ref FOO >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '\n+\tgit rev-parse FOO -- &&\n+\tgit rev-parse refs/tags/new-tag -- &&\n+\t$RUN delete-refs 0 FOO refs/tags/new-tag &&\n+\ttest_must_fail git rev-parse FOO -- &&\n+\ttest_must_fail git rev-parse refs/tags/new-tag --\n+'\n+\n+test_expect_success 'rename_refs(master, new-master)' '\n+\tgit rev-parse master >expected &&\n+\t$RUN rename-ref refs/heads/master refs/heads/new-master &&\n+\tgit rev-parse new-master >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_commit recreate-master\n+'\n+\n+test_expect_success 'for_each_ref(refs/heads/)' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tmaster 0x0\n+\tnew-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'resolve_ref(new-master)' '\n+\tSHA1=`git rev-parse new-master` &&\n+\techo \"$SHA1 refs/heads/new-master 0x0\" >expected &&\n+\t$RUN resolve-ref refs/heads/new-master 0 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify_ref(new-master)' '\n+\t$RUN verify-ref refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_reflog()' '\n+\t$RUN for-each-reflog | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\trefs/heads/master 0x0\n+\trefs/heads/new-master 0x0\n+\tHEAD 0x1\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'for_each_reflog_ent()' '\n+\t$RUN for-each-reflog-ent HEAD >actual &&\n+\thead -n1 actual | grep one &&\n+\ttail -n2 actual | head -n1 | grep recreate-master\n+'\n+\n+test_expect_success 'for_each_reflog_ent_reverse()' '\n+\t$RUN for-each-reflog-ent-reverse HEAD >actual &&\n+\thead -n1 actual | grep recreate-master &&\n+\ttail -n2 actual | head -n1 | grep one\n+'\n+\n+test_expect_success 'reflog_exists(HEAD)' '\n+\t$RUN reflog-exists HEAD\n+'\n+\n+test_expect_success 'delete_reflog(HEAD)' '\n+\t$RUN delete-reflog HEAD &&\n+\t! test -f .git/logs/HEAD\n+'\n+\n+test_expect_success 'create-reflog(HEAD)' '\n+\t$RUN create-reflog HEAD 1 &&\n+\ttest -f .git/logs/HEAD\n+'\n+\n+test_expect_success 'delete_ref(refs/heads/foo)' '\n+\tgit checkout -b foo &&\n+\tFOO_SHA1=`git rev-parse foo` &&\n+\tgit checkout --detach &&\n+\ttest_commit bar-commit &&\n+\tgit checkout -b bar &&\n+\tBAR_SHA1=`git rev-parse bar` &&\n+\t$RUN update-ref updating refs/heads/foo $BAR_SHA1 $FOO_SHA1 0 &&\n+\techo $BAR_SHA1 >expected &&\n+\tgit rev-parse refs/heads/foo >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'delete_ref(refs/heads/foo)' '\n+\tSHA1=`git rev-parse foo` &&\n+\tgit checkout --detach &&\n+\t$RUN delete-ref refs/heads/foo $SHA1 0 &&\n+\ttest_must_fail git rev-parse refs/heads/foo --\n+'\n+\n+test_done\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312307","messageId":"20170222140450.30886-25-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-22T14:04:50Z","receivedAt":"2017-02-22T14:08:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n 1 file changed, 95 insertions(+)\n create mode 100755 t/t1406-submodule-ref-store.sh\n\ndiff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\nnew file mode 100755\nindex 000000000..3b30ba62f\n--- /dev/null\n+++ b/t/t1406-submodule-ref-store.sh\n@@ -0,0 +1,95 @@\n+#!/bin/sh\n+\n+test_description='test submodule ref store api'\n+\n+. ./test-lib.sh\n+\n+RUN=\"test-ref-store submodule:sub\"\n+\n+test_expect_success 'setup' '\n+\tgit init sub &&\n+\t(\n+\t\tcd sub &&\n+\t\ttest_commit first &&\n+\t\tgit checkout -b new-master\n+\t)\n+'\n+\n+test_expect_success 'pack_refs() not allowed' '\n+\ttest_must_fail $RUN pack-refs 3\n+'\n+\n+test_expect_success 'peel_ref(new-tag)' '\n+\tgit -C sub rev-parse HEAD >expected &&\n+\tgit -C sub tag -a -m new-tag new-tag HEAD &&\n+\t$RUN peel-ref refs/tags/new-tag >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create_symref() not allowed' '\n+\ttest_must_fail $RUN create-symref FOO refs/heads/master nothing\n+'\n+\n+test_expect_success 'delete_refs() not allowed' '\n+\ttest_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag\n+'\n+\n+test_expect_success 'rename_refs() not allowed' '\n+\ttest_must_fail $RUN rename-ref refs/heads/master refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_ref(refs/heads/)' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tmaster 0x0\n+\tnew-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'resolve_ref(master)' '\n+\tSHA1=`git -C sub rev-parse master` &&\n+\techo \"$SHA1 refs/heads/master 0x0\" >expected &&\n+\t$RUN resolve-ref refs/heads/master 0 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify_ref(new-master)' '\n+\t$RUN verify-ref refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_reflog()' '\n+\t$RUN for-each-reflog | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\trefs/heads/master 0x0\n+\trefs/heads/new-master 0x0\n+\tHEAD 0x1\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'for_each_reflog_ent()' '\n+\t$RUN for-each-reflog-ent HEAD >actual && cat actual &&\n+\thead -n1 actual | grep first &&\n+\ttail -n2 actual | head -n1 | grep master.to.new\n+'\n+\n+test_expect_success 'for_each_reflog_ent_reverse()' '\n+\t$RUN for-each-reflog-ent-reverse HEAD >actual &&\n+\thead -n1 actual | grep master.to.new &&\n+\ttail -n2 actual | head -n1 | grep first\n+'\n+\n+test_expect_success 'reflog_exists(HEAD)' '\n+\t$RUN reflog-exists HEAD\n+'\n+\n+test_expect_success 'delete_reflog() not allowed' '\n+\ttest_must_fail $RUN delete-reflog HEAD\n+'\n+\n+test_expect_success 'create-reflog() not allowed' '\n+\ttest_must_fail $RUN create-reflog HEAD 1\n+'\n+\n+test_done\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"312317","messageId":"xmqqlgsycfeu.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"Re: [PATCH v5 00/24] Remove submodule from files-backend.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-22T17:18:49Z","receivedAt":"2017-02-22T17:28:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> v5 goes a bit longer than v4, basically:\n>\n>  - files_path() is broken down into three smaller functions,\n>    files_{packed_refs,reflog,refname}_path().\n>\n>  - most of store-based api is added because..\n>\n>  - test-ref-store.c is added with t1405 and t1406 for some basic tests\n>    I'm not aimimg for complete ref store coverage. But we can continue\n>    to improve from there.\n>\n>  - refs_store_init() now takes a \"permission\" flag, like open().\n>    Operations are allowed or forbidden based on this flag. The\n>    submodule_allowed flag is killed. files_assert_main.. remains.\n>\n>  - get_*_ref_store() remain public api because it's used by\n>    test-ref-store.c and pack-refs.c.\n>\n>  - files-backend.c should now make no function calls that implicitly\n>    target the main store. But this will have to be tested more to be\n>    sure. I'm tempted to add a tracing backend just for this purpose.\n\nOK.\n\n> Junio, if you take this on 'pu', you'll have to kick my other two\n> series out (they should not even compile). I'm not resending them\n> until I get a \"looks mostly ok\" from Michael. No point in updating\n> them when this series keeps moving.\n\nThanks for a note.\n\n"},{"id":"312326","messageId":"CAGZ79kZTr924RrvG6RsasmhT1yswN875XShtmrrcJ8ztaAGJUw@mail.gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-2-pclouds@gmail.com","subject":"Re: [PATCH v5 01/24] refs.h: add forward declaration for structs used in this file","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-02-22T18:18:16Z","receivedAt":"2017-02-22T18:18:23Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Feb 22, 2017 at 6:04 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.h | 6 +++++-\n>  1 file changed, 5 insertions(+), 1 deletion(-)\n>\n> diff --git a/refs.h b/refs.h\n> index 9fbff90e7..c494b641a 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -1,6 +1,11 @@\n>  #ifndef REFS_H\n>  #define REFS_H\n>\n> +struct object_id;\n> +struct ref_transaction;\n> +struct strbuf;\n> +struct string_list;\n> +\n>  /*\n>   * Resolve a reference, recursively following symbolic refererences.\n>   *\n> @@ -144,7 +149,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n>   * `ref_transaction_commit` is called.  So `ref_transaction_verify`\n>   * won't report a verification failure until the commit is attempted.\n>   */\n> -struct ref_transaction;\n\nLeaving the detailed comment about ref_transaction dangling?\nI can understand if you don't want to move it with the declaration,\nas you want all declarations terse in a few lines.\nMaybe move the comment to be part of the first large comment\n(The one that you can see in the first hunk, starting with\n\" * Resolve a reference, recursively following\")\n\nMaybe Michael has a better idea how to make this comment\nmore accessible to the casual refs-reader.\n\nThanks,\nStefan\n"},{"id":"312340","messageId":"xmqqd1eaaqe3.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"xmqqlgsycfeu.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 00/24] Remove submodule from files-backend.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-22T21:04:36Z","receivedAt":"2017-02-22T21:06:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> v5 goes a bit longer than v4, basically:\n>>\n>>  - files_path() is broken down into three smaller functions,\n>>    files_{packed_refs,reflog,refname}_path().\n>>\n>>  - most of store-based api is added because..\n>>\n>>  - test-ref-store.c is added with t1405 and t1406 for some basic tests\n>>    I'm not aimimg for complete ref store coverage. But we can continue\n>>    to improve from there.\n>>\n>>  - refs_store_init() now takes a \"permission\" flag, like open().\n>>    Operations are allowed or forbidden based on this flag. The\n>>    submodule_allowed flag is killed. files_assert_main.. remains.\n>>\n>>  - get_*_ref_store() remain public api because it's used by\n>>    test-ref-store.c and pack-refs.c.\n>>\n>>  - files-backend.c should now make no function calls that implicitly\n>>    target the main store. But this will have to be tested more to be\n>>    sure. I'm tempted to add a tracing backend just for this purpose.\n>\n> OK.\n>\n>> Junio, if you take this on 'pu', you'll have to kick my other two\n>> series out (they should not even compile). I'm not resending them\n>> until I get a \"looks mostly ok\" from Michael. No point in updating\n>> them when this series keeps moving.\n>\n> Thanks for a note.\n\nAs this round seems to have added conflicts with another topic that\nis already in flight that the previous round did not conflict with,\nI'll eject all three for now until this one solidifies a bit more.\n"},{"id":"312388","messageId":"CACsJy8C7XaRfKnP4zUOTaOtOoLk7pmxt_obHEYOpHoHWaiC+KA@mail.gmail.com","threadId":"45126","inReplyTo":"CAGZ79kZTr924RrvG6RsasmhT1yswN875XShtmrrcJ8ztaAGJUw@mail.gmail.com","subject":"Re: [PATCH v5 01/24] refs.h: add forward declaration for structs used in this file","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-02-23T09:26:38Z","receivedAt":"2017-02-23T09:35:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Feb 23, 2017 at 1:18 AM, Stefan Beller <sbeller@google.com> wrote:\n> On Wed, Feb 22, 2017 at 6:04 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs.h | 6 +++++-\n>>  1 file changed, 5 insertions(+), 1 deletion(-)\n>>\n>> diff --git a/refs.h b/refs.h\n>> index 9fbff90e7..c494b641a 100644\n>> --- a/refs.h\n>> +++ b/refs.h\n>> @@ -1,6 +1,11 @@\n>>  #ifndef REFS_H\n>>  #define REFS_H\n>>\n>> +struct object_id;\n>> +struct ref_transaction;\n>> +struct strbuf;\n>> +struct string_list;\n>> +\n>>  /*\n>>   * Resolve a reference, recursively following symbolic refererences.\n>>   *\n>> @@ -144,7 +149,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n>>   * `ref_transaction_commit` is called.  So `ref_transaction_verify`\n>>   * won't report a verification failure until the commit is attempted.\n>>   */\n>> -struct ref_transaction;\n>\n> Leaving the detailed comment about ref_transaction dangling?\n> I can understand if you don't want to move it with the declaration,\n> as you want all declarations terse in a few lines.\n> Maybe move the comment to be part of the first large comment\n> (The one that you can see in the first hunk, starting with\n> \" * Resolve a reference, recursively following\")\n\nI thought the comment block covered the following declarations too,\nnot just ref_transaction. But on second read it's not that.\nTransaction functions are way down below. I'll probably move\nref_transaction back where it was.\n-- \nDuy\n"},{"id":"312853","messageId":"9074fcd9-2b80-8927-19d2-1659eb0e327c@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-5-pclouds@gmail.com","subject":"Re: [PATCH v5 04/24] files-backend: convert git_path() to strbuf_git_path()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T17:06:17Z","receivedAt":"2017-02-28T17:07:23Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> git_path() and friends are going to be killed in files-backend.c in near\n> future. And because there's a risk with overwriting buffer in\n> git_path(), let's convert them all to strbuf_git_path(). We'll have\n> easier time killing/converting strbuf_git_path() then because we won't\n> have to worry about memory management again.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 139 +++++++++++++++++++++++++++++++++++++++------------\n>  1 file changed, 106 insertions(+), 33 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 4676525de..435db1293 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> [...]\n> @@ -2586,9 +2603,15 @@ static int files_rename_ref(struct ref_store *ref_store,\n>  \tint flag = 0, logmoved = 0;\n>  \tstruct ref_lock *lock;\n>  \tstruct stat loginfo;\n> -\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n> +\tstruct strbuf sb_oldref = STRBUF_INIT;\n> +\tstruct strbuf sb_newref = STRBUF_INIT;\n> +\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n> +\tint log, ret;\n>  \tstruct strbuf err = STRBUF_INIT;\n>  \n> +\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n> +\tlog = !lstat(sb_oldref.buf, &loginfo);\n> +\tstrbuf_release(&sb_oldref);\n>  \tif (log && S_ISLNK(loginfo.st_mode))\n>  \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n>  \n> @@ -2602,7 +2625,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n>  \tif (!rename_ref_available(oldrefname, newrefname))\n>  \t\treturn 1;\n>  \n> -\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n> +\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n> +\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n> +\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n> +\tstrbuf_release(&sb_oldref);\n> +\tstrbuf_release(&tmp_renamed_log);\n> +\tif (ret)\n>  \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n>  \t\t\toldrefname, strerror(errno));\n>  \n> @@ -2681,13 +2709,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n>  \tlog_all_ref_updates = flag;\n>  \n>   rollbacklog:\n> -\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n> +\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n> +\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n> +\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n>  \t\terror(\"unable to restore logfile %s from %s: %s\",\n>  \t\t\toldrefname, newrefname, strerror(errno));\n> +\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n>  \tif (!logmoved && log &&\n> -\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n> +\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n>  \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n>  \t\t\toldrefname, strerror(errno));\n\nIt feels like you're writing, releasing, re-writing these strbufs more\nthan necessary. Maybe it would be clearer to set them once, and on\nerrors set `ret = error()` then jump to a label here...\n\n> +\tstrbuf_release(&sb_newref);\n> +\tstrbuf_release(&sb_oldref);\n> +\tstrbuf_release(&tmp_renamed_log);\n>  \n\n...and change this to `return ret`?\n\n>  \treturn 1;\n>  }\n> [...]\n> @@ -4108,18 +4171,28 @@ static int files_reflog_expire(struct ref_store *ref_store,\n>  \n>  static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n>  {\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\n>  \t/* Check validity (but we don't need the result): */\n>  \tfiles_downcast(ref_store, 0, \"init_db\");\n>  \n>  \t/*\n>  \t * Create .git/refs/{heads,tags}\n>  \t */\n> -\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n> -\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n> +\tstrbuf_git_path(&sb, \"refs/heads\");\n> +\tsafe_create_dir(sb.buf, 1);\n> +\tstrbuf_reset(&sb);\n> +\tstrbuf_git_path(&sb, \"refs/tags\");\n> +\tsafe_create_dir(sb.buf, 1);\n> +\tstrbuf_reset(&sb);\n>  \tif (get_shared_repository()) {\n> -\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n> -\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n> +\t\tstrbuf_git_path(&sb, \"refs/heads\");\n> +\t\tadjust_shared_perm(sb.buf);\n> +\t\tstrbuf_reset(&sb);\n> +\t\tstrbuf_git_path(&sb, \"refs/tags\");\n> +\t\tadjust_shared_perm(sb.buf);\n>  \t}\n> +\tstrbuf_release(&sb);\n>  \treturn 0;\n>  }\n\nIt looks to me like `safe_create_dir()` already has the ability to\n`adjust_shared_perm()`, or am I missing something? (I realize that this\nis preexisting code.)\n\nMichael\n\n"},{"id":"312854","messageId":"cb86192d-8644-bed3-1e73-821931589e79@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-6-pclouds@gmail.com","subject":"Re: [PATCH v5 05/24] files-backend: move \"logs/\" out of TMP_RENAMED_LOG","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T17:19:19Z","receivedAt":"2017-02-28T17:31:48Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> This makes reflog path building consistent, always in the form of\n> \n>     strbuf_git_path(sb, \"logs/%s\", refname);\n> \n> It reduces the mental workload a bit in the next patch when that\n> function call is converted.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 12 ++++++------\n>  1 file changed, 6 insertions(+), 6 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 435db1293..69946b0de 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -2513,7 +2513,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n>   * IOW, to avoid cross device rename errors, the temporary renamed log must\n>   * live into logs/refs.\n>   */\n> -#define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n> +#define TMP_RENAMED_LOG  \"refs/.tmp-renamed-log\"\n\nThe constant name feels a little bit misleading now that it is not the\nname of a logfile but rather a reference name. OTOH \"tmp-renamed-log\" is\n*in* the reference name so I guess it's not really wrong.\n\n>  struct rename_cb {\n>  \tconst char *tmp_renamed_log;\n> @@ -2549,7 +2549,7 @@ static int rename_tmp_log(const char *newrefname)\n>  \tint ret;\n>  \n>  \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n> -\tstrbuf_git_path(&tmp, TMP_RENAMED_LOG);\n> +\tstrbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n>  \tcb.tmp_renamed_log = tmp.buf;\n>  \tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n>  \tif (ret) {\n> @@ -2626,12 +2626,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n>  \t\treturn 1;\n>  \n>  \tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n> -\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n> +\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n>  \tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n>  \tstrbuf_release(&sb_oldref);\n>  \tstrbuf_release(&tmp_renamed_log);\n>  \tif (ret)\n> -\t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n> +\t\treturn error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n>  \t\t\toldrefname, strerror(errno));\n\nIt seems like it would be preferable to use `sb_oldref.buf` and\n`tmp.buf` when building the error message. But I guess that `tmp.buf`\nmight include some path preceding \"logs/\" that is unwanted in the error\nmessage? But it's a shame to hardcode the file naming scheme here again.\n\nMaybe we *do* want the path in the error message?\n\nIt just occurred to me: this temporary logfile lives in the main\nrepository, right? What if a worktree reference is being renamed? Part\nof the advertised use of worktrees is that the worktree might live far\nfrom the main directory, or even on removable media. But it's not\npossible to rename files across partitions. Maybe this will come out in\nthe wash once worktrees are ref_stores themselves.\n\nFor that matter, what if a user tries to rename a worktree ref into a\ncommon ref or vice versa?\n\n>  \tif (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {\n> [...]\n\nMichael\n\n"},{"id":"312856","messageId":"7e5ef9e7-bd90-1917-d8eb-c6310c2744ab@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-25-pclouds@gmail.com","subject":"Re: [PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T17:34:17Z","receivedAt":"2017-02-28T17:38:11Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n>  1 file changed, 95 insertions(+)\n>  create mode 100755 t/t1406-submodule-ref-store.sh\n> \n> diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\n> new file mode 100755\n> index 000000000..3b30ba62f\n> --- /dev/null\n> +++ b/t/t1406-submodule-ref-store.sh\n> [...]\n\nI haven't actually read this far in the patch series, but I noticed that\na test in this file fails:\n\n\nt1406-submodule-ref-store.sh                     (Wstat: 256 Tests: 15\nFailed: 1)\n  Failed test:  10\n  Non-zero exit status: 1\n\nI didn't have time to look into it more; let me know if you can't\nreproduce it.\n\nMichael\n\n"},{"id":"312857","messageId":"beef528b-66cd-e104-4c02-b07ddfa7e6d7@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-8-pclouds@gmail.com","subject":"Re: [PATCH v5 07/24] files-backend: add and use files_refname_path()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T17:41:14Z","receivedAt":"2017-02-28T17:42:17Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> Keep repo-related path handling in one place. This will make it easier\n> to add submodule/multiworktree support later.\n> \n> This automatically adds the \"if submodule then use the submodule version\n> of git_path\" to other call sites too. But it does not mean those\n> operations are sumodule-ready. Not yet.\n\ns/sumodule/submodule/\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 45 +++++++++++++++++++++++++--------------------\n>  1 file changed, 25 insertions(+), 20 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 7b4ea4c56..72f4e1746 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -1180,6 +1180,18 @@ static void files_reflog_path(struct files_ref_store *refs,\n>  \tstrbuf_git_path(sb, \"logs/%s\", refname);\n>  }\n>  \n> +static void files_refname_path(struct files_ref_store *refs,\n> +\t\t\t       struct strbuf *sb,\n> +\t\t\t       const char *refname)\n> +{\n> +\tif (refs->submodule) {\n> +\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n> +\t\treturn;\n> +\t}\n> +\n> +\tstrbuf_git_path(sb, \"%s\", refname);\n> +}\n\nMaybe it's just me, but I find it odd to exit early here when the first\nexit isn't due to an error. For me, structuring this like `if ()\ncall1(); else call2();` would make it clearer that the two code paths\nare equally-valid alternatives, and either one or the other will be\nexecuted.\n\nI had the same feeling when I read `files_reflog_path()`\n\n>  /*\n>   * Get the packed_ref_cache for the specified files_ref_store,\n>   * creating it if necessary.\n> @@ -1251,10 +1263,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n>  \tsize_t path_baselen;\n>  \tint err = 0;\n>  \n> -\tif (refs->submodule)\n> -\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n> -\telse\n> -\t\tstrbuf_git_path(&path, \"%s\", dirname);\n> +\tfiles_refname_path(refs, &path, dirname);\n\nIt's so nice to see these ugly `if (refs->submodule)` code blocks\ndisappearing!\n\n> [...]\n\nMichael\n\n"},{"id":"312860","messageId":"0bef1e49-e96b-1666-9b88-f4262c2aeeba@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-10-pclouds@gmail.com","subject":"Re: [PATCH v5 09/24] refs.c: introduce get_main_ref_store()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T17:51:56Z","receivedAt":"2017-02-28T17:52:43Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c | 16 ++++++++++++----\n>  1 file changed, 12 insertions(+), 4 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index 81b64b4ed..dab1a21ac 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -1456,15 +1456,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n>  \treturn refs;\n>  }\n>  \n> +static struct ref_store *get_main_ref_store(void)\n> +{\n> +\tstruct ref_store *refs;\n> +\n> +\tif (main_ref_store)\n> +\t\treturn main_ref_store;\n> +\n> +\trefs = ref_store_init(NULL);\n> +\treturn refs;\n\nUnnecessary temporary variable?\n\n> [...]\n\nMichael\n\n\n"},{"id":"312861","messageId":"5c4a292e-ab86-ffe0-ba8d-c0fe1b527ec1@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-9-pclouds@gmail.com","subject":"Re: [PATCH v5 08/24] files-backend: remove the use of git_path()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T17:50:08Z","receivedAt":"2017-02-28T17:53:26Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\n> deciding what goes where (*). The end goal is to pass $GIT_DIR only. A\n> refs \"view\" of a linked worktree is a logical ref store that combines\n> two files backends together.\n> \n> (*) Not entirely true since strbuf_git_path_submodule() still does path\n> translation underneath. But that's for another patch.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 43 ++++++++++++++++++++++++++++++++++++++-----\n>  1 file changed, 38 insertions(+), 5 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 72f4e1746..a390eaadf 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -923,7 +923,8 @@ struct files_ref_store {\n>  \t * store:\n>  \t */\n>  \tconst char *submodule;\n> -\n> +\tchar *gitdir;\n> +\tchar *gitcommondir;\n>  \tchar *packed_refs_path;\n>  \n>  \tstruct ref_entry *loose;\n> @@ -985,6 +986,8 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n>  {\n>  \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n>  \tstruct ref_store *ref_store = (struct ref_store *)refs;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\tconst char *gitdir = get_git_dir();\n>  \n>  \tbase_ref_store_init(ref_store, &refs_be_files);\n>  \n> @@ -995,7 +998,11 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n>  \t\treturn ref_store;\n>  \t}\n>  \n> -\trefs->packed_refs_path = git_pathdup(\"packed-refs\");\n> +\trefs->gitdir = xstrdup(gitdir);\n> +\tget_common_dir_noenv(&sb, gitdir);\n> +\trefs->gitcommondir = strbuf_detach(&sb, NULL);\n> +\tstrbuf_addf(&sb, \"%s/packed-refs\", refs->gitcommondir);\n> +\trefs->packed_refs_path = strbuf_detach(&sb, NULL);\n\n`git_path()` and friends avoid adding an extra `/` if `git_dir()`\nalready ends in a slash or if it is the empty string. Here you don't\nhave that functionality. Is that intentional?\n\nSame thing below, too.\n\n>  \n>  \treturn ref_store;\n>  }\n> @@ -1173,11 +1180,26 @@ static void files_reflog_path(struct files_ref_store *refs,\n>  \t\t\t      const char *refname)\n>  {\n>  \tif (!refname) {\n> -\t\tstrbuf_git_path(sb, \"logs\");\n> +\t\t/*\n> +\t\t * FIXME: of course this is wrong in multi worktree\n> +\t\t * setting. To be fixed real soon.\n> +\t\t */\n> +\t\tstrbuf_addf(sb, \"%s/logs\", refs->gitcommondir);\n>  \t\treturn;\n>  \t}\n>  \n> -\tstrbuf_git_path(sb, \"logs/%s\", refname);\n> +\tswitch (ref_type(refname)) {\n> +\tcase REF_TYPE_PER_WORKTREE:\n> +\tcase REF_TYPE_PSEUDOREF:\n> +\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n> +\t\tbreak;\n> +\tcase REF_TYPE_NORMAL:\n> +\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n> +\t\t    ref_type(refname), refname);\n> +\t}\n>  }\n>  \n>  static void files_refname_path(struct files_ref_store *refs,\n> @@ -1189,7 +1211,18 @@ static void files_refname_path(struct files_ref_store *refs,\n>  \t\treturn;\n>  \t}\n>  \n> -\tstrbuf_git_path(sb, \"%s\", refname);\n> +\tswitch (ref_type(refname)) {\n> +\tcase REF_TYPE_PER_WORKTREE:\n> +\tcase REF_TYPE_PSEUDOREF:\n> +\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n> +\t\tbreak;\n> +\tcase REF_TYPE_NORMAL:\n> +\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n> +\t\t    ref_type(refname), refname);\n> +\t}\n>  }\n>  \n>  /*\n> \n\nMichael\n\n"},{"id":"312862","messageId":"92ff53b6-8a85-9be8-e14f-18eb1fcaf732@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-13-pclouds@gmail.com","subject":"Re: [PATCH v5 12/24] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T18:03:45Z","receivedAt":"2017-02-28T18:05:09Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> This is the last function in this code (besides public API) that takes\n> submodule argument and handles both main/submodule cases. Break it down,\n> move main store registration in get_main_ref_store() and keep the rest\n> in register_submodule_ref_store().\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c | 50 ++++++++++++++++++++++++++------------------------\n>  1 file changed, 26 insertions(+), 24 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index c284cb4f4..6adc38e42 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -1412,29 +1412,6 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n>  }\n>  \n>  /*\n> - * Register the specified ref_store to be the one that should be used\n> - * for submodule (or the main repository if submodule is NULL). It is\n> - * a fatal error to call this function twice for the same submodule.\n> - */\n> -static void register_ref_store(struct ref_store *refs, const char *submodule)\n> -{\n> -\tif (!submodule) {\n> -\t\tif (main_ref_store)\n> -\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n> -\n> -\t\tmain_ref_store = refs;\n> -\t} else {\n> -\t\tif (!submodule_ref_stores.tablesize)\n> -\t\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n> -\n> -\t\tif (hashmap_put(&submodule_ref_stores,\n> -\t\t\t\talloc_submodule_hash_entry(submodule, refs)))\n> -\t\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n> -\t\t\t    submodule);\n> -\t}\n> -}\n> -\n> -/*\n>   * Create, record, and return a ref_store instance for the specified\n>   * submodule (or the main repository if submodule is NULL).\n>   */\n> @@ -1448,7 +1425,6 @@ static struct ref_store *ref_store_init(const char *submodule)\n>  \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n>  \n>  \trefs = be->init(submodule);\n> -\tregister_ref_store(refs, submodule);\n>  \treturn refs;\n>  }\n>  \n> @@ -1460,9 +1436,32 @@ static struct ref_store *get_main_ref_store(void)\n>  \t\treturn main_ref_store;\n>  \n>  \trefs = ref_store_init(NULL);\n> +\tif (refs) {\n> +\t\tif (main_ref_store)\n> +\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n> +\n> +\t\tmain_ref_store = refs;\n> +\t}\n>  \treturn refs;\n\nSuperfluous test: I don't think `ref_store_init()` ever returns NULL.\nThis also implies that you could check `main_ref_store` before calling\n`ref_store_init()`, and eliminate a temporary.\n\n>  }\n>  \n> +/*\n> + * Register the specified ref_store to be the one that should be used\n> + * for submodule. It is a fatal error to call this function twice for\n> + * the same submodule.\n> + */\n> +static void register_submodule_ref_store(struct ref_store *refs,\n> +\t\t\t\t\t const char *submodule)\n> +{\n> +\tif (!submodule_ref_stores.tablesize)\n> +\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n> +\n> +\tif (hashmap_put(&submodule_ref_stores,\n> +\t\t\talloc_submodule_hash_entry(submodule, refs)))\n> +\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n> +\t\t    submodule);\n> +}\n> +\n>  struct ref_store *get_ref_store(const char *submodule)\n>  {\n>  \tstruct strbuf submodule_sb = STRBUF_INIT;\n> @@ -1480,6 +1479,9 @@ struct ref_store *get_ref_store(const char *submodule)\n>  \tif (is_nonbare_repository_dir(&submodule_sb))\n>  \t\trefs = ref_store_init(submodule);\n>  \tstrbuf_release(&submodule_sb);\n> +\n> +\tif (refs)\n\nI think `refs` should always be non-NULL here for the same reason.\n\n> +\t\tregister_submodule_ref_store(refs, submodule);\n>  \treturn refs;\n>  }\n\nMichael\n\n"},{"id":"312863","messageId":"a6e3f43b-a97c-6aa4-d80d-de342049df9f@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-14-pclouds@gmail.com","subject":"Re: [PATCH v5 13/24] refs.c: make get_main_ref_store() public and use it","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T18:06:58Z","receivedAt":"2017-02-28T18:09:48Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> get_ref_store() will soon be renamed to get_submodule_ref_store().\n> Together with future get_worktree_ref_store(), the three functions\n> provide an appropriate ref store for different operation modes. New APIs\n> will be added to operate directly on ref stores.\n\nNice.\n\nMichael\n\n"},{"id":"312865","messageId":"c525b1cf-3bb0-452c-8a8d-b07c58f00c84@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"Re: [PATCH v5 00/24] Remove submodule from files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T18:20:18Z","receivedAt":"2017-02-28T18:21:30Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> v5 goes a bit longer than v4, basically:\n\nI've read through patch 14/24 so far, and they all look good except for\nthe mostly superficial comments that I have sent so far. I like the way\nthis is heading!\n\nI'll try to continue tomorrow.\n\nMichael\n\n"},{"id":"312866","messageId":"72093fc6-806e-506e-5c75-206f864c365e@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-15-pclouds@gmail.com","subject":"Re: [PATCH v5 14/24] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-02-28T18:14:48Z","receivedAt":"2017-02-28T18:24:58Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> refs is learning to avoid path rewriting that is done by\n> strbuf_git_path_submodule(). Factor out this code so it could be reused\n> by refs*\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  path.c      | 34 +++++++---------------------------\n>  submodule.c | 31 +++++++++++++++++++++++++++++++\n>  submodule.h |  1 +\n>  3 files changed, 39 insertions(+), 27 deletions(-)\n> \n> diff --git a/path.c b/path.c\n> index efcedafba..3451d2916 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -475,35 +475,16 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n>  static int do_submodule_path(struct strbuf *buf, const char *path,\n>  \t\t\t     const char *fmt, va_list args)\n>  {\n> -\tconst char *git_dir;\n>  \tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n>  \tstruct strbuf git_submodule_dir = STRBUF_INIT;\n> -\tconst struct submodule *sub;\n> -\tint err = 0;\n> +\tint ret;\n>  \n> -\tstrbuf_addstr(buf, path);\n> -\tstrbuf_complete(buf, '/');\n> -\tstrbuf_addstr(buf, \".git\");\n> -\n> -\tgit_dir = read_gitfile(buf->buf);\n> -\tif (git_dir) {\n> -\t\tstrbuf_reset(buf);\n> -\t\tstrbuf_addstr(buf, git_dir);\n> -\t}\n> -\tif (!is_git_directory(buf->buf)) {\n> -\t\tgitmodules_config();\n> -\t\tsub = submodule_from_path(null_sha1, path);\n> -\t\tif (!sub) {\n> -\t\t\terr = SUBMODULE_PATH_ERR_NOT_CONFIGURED;\n\nI didn't read this patch too carefully, but where the old code used the\nconstant `SUBMODULE_PATH_ERR_NOT_CONFIGURED`, the new code returns -1.\nIn fact, now the constant is totally unused. It looks like there's some\nmore cleanup that could happen.\n\n> -\t\t\tgoto cleanup;\n> -\t\t}\n> -\t\tstrbuf_reset(buf);\n> -\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n> -\t}\n> -\n> -\tstrbuf_addch(buf, '/');\n> -\tstrbuf_addbuf(&git_submodule_dir, buf);\n> +\tret = submodule_to_gitdir(&git_submodule_dir, path);\n> +\tif (ret)\n> +\t\tgoto cleanup;\n>  \n> +\tstrbuf_complete(&git_submodule_dir, '/');\n> +\tstrbuf_addbuf(buf, &git_submodule_dir);\n>  \tstrbuf_vaddf(buf, fmt, args);\n>  \n>  \tif (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))\n> @@ -514,8 +495,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,\n>  cleanup:\n>  \tstrbuf_release(&git_submodule_dir);\n>  \tstrbuf_release(&git_submodule_common_dir);\n> -\n> -\treturn err;\n> +\treturn ret;\n>  }\n>  \n>  char *git_pathdup_submodule(const char *path, const char *fmt, ...)\n> diff --git a/submodule.c b/submodule.c\n> index 3b98766a6..36b8d1d11 100644\n> --- a/submodule.c\n> +++ b/submodule.c\n> @@ -1514,3 +1514,34 @@ void absorb_git_dir_into_superproject(const char *prefix,\n>  \t\tstrbuf_release(&sb);\n>  \t}\n>  }\n> +\n> +int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n> +{\n> +\tconst struct submodule *sub;\n> +\tconst char *git_dir;\n> +\tint ret = 0;\n> +\n> +\tstrbuf_reset(buf);\n> +\tstrbuf_addstr(buf, submodule);\n> +\tstrbuf_complete(buf, '/');\n> +\tstrbuf_addstr(buf, \".git\");\n> +\n> +\tgit_dir = read_gitfile(buf->buf);\n> +\tif (git_dir) {\n> +\t\tstrbuf_reset(buf);\n> +\t\tstrbuf_addstr(buf, git_dir);\n> +\t}\n> +\tif (!is_git_directory(buf->buf)) {\n> +\t\tgitmodules_config();\n> +\t\tsub = submodule_from_path(null_sha1, submodule);\n> +\t\tif (!sub) {\n> +\t\t\tret = -1;\n> +\t\t\tgoto cleanup;\n> +\t\t}\n> +\t\tstrbuf_reset(buf);\n> +\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n> +\t}\n> +\n> +cleanup:\n> +\treturn ret;\n> +}\n> diff --git a/submodule.h b/submodule.h\n> index 05ab674f0..fc3d0303e 100644\n> --- a/submodule.h\n> +++ b/submodule.h\n> @@ -81,6 +81,7 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n>  \t\t\t\t    int dry_run);\n>  extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n>  extern int parallel_submodules(void);\n> +int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n\nA docstring is always nice :-)\n\n>  \n>  /*\n>   * Prepare the \"env_array\" parameter of a \"struct child_process\" for executing\n> \n\nMichael\n\n"},{"id":"312892","messageId":"xmqqinnujaw6.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"c525b1cf-3bb0-452c-8a8d-b07c58f00c84@alum.mit.edu","subject":"Re: [PATCH v5 00/24] Remove submodule from files-backend.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-02-28T20:52:41Z","receivedAt":"2017-02-28T20:53:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n>> v5 goes a bit longer than v4, basically:\n>\n> I've read through patch 14/24 so far, and they all look good except for\n> the mostly superficial comments that I have sent so far. I like the way\n> this is heading!\n>\n> I'll try to continue tomorrow.\n>\n> Michael\n\nThanks.\n"},{"id":"312943","messageId":"CACsJy8BvKvo8_bRHH++dp53DaPbDVkKU0EUQC7ofYvBo+Tec0Q@mail.gmail.com","threadId":"45126","inReplyTo":"92ff53b6-8a85-9be8-e14f-18eb1fcaf732@alum.mit.edu","subject":"Re: [PATCH v5 12/24] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-01T12:00:50Z","receivedAt":"2017-03-01T12:03:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 1:03 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>>  struct ref_store *get_ref_store(const char *submodule)\n>>  {\n>>       struct strbuf submodule_sb = STRBUF_INIT;\n>> @@ -1480,6 +1479,9 @@ struct ref_store *get_ref_store(const char *submodule)\n>>       if (is_nonbare_repository_dir(&submodule_sb))\n>>               refs = ref_store_init(submodule);\n>>       strbuf_release(&submodule_sb);\n>> +\n>> +     if (refs)\n>\n> I think `refs` should always be non-NULL here for the same reason.\n\nThat's true if is_nonbar_repo... returns true. If it's false (e.g.\nuninitialized submodule) then refs remains NULL from before (I didn't\nknow about this until I hit a segfault in rev-list in another series)\n-- \nDuy\n"},{"id":"312944","messageId":"CACsJy8ArGqrRCsVgpumu1u4+1_2chWARgLG_2Kmpq5p-nmcWfg@mail.gmail.com","threadId":"45126","inReplyTo":"0bef1e49-e96b-1666-9b88-f4262c2aeeba@alum.mit.edu","subject":"Re: [PATCH v5 09/24] refs.c: introduce get_main_ref_store()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-01T12:06:04Z","receivedAt":"2017-03-01T12:07:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 12:51 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs.c | 16 ++++++++++++----\n>>  1 file changed, 12 insertions(+), 4 deletions(-)\n>>\n>> diff --git a/refs.c b/refs.c\n>> index 81b64b4ed..dab1a21ac 100644\n>> --- a/refs.c\n>> +++ b/refs.c\n>> @@ -1456,15 +1456,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n>>       return refs;\n>>  }\n>>\n>> +static struct ref_store *get_main_ref_store(void)\n>> +{\n>> +     struct ref_store *refs;\n>> +\n>> +     if (main_ref_store)\n>> +             return main_ref_store;\n>> +\n>> +     refs = ref_store_init(NULL);\n>> +     return refs;\n>\n> Unnecessary temporary variable?\n\nThat's the price for doing too much mechanical conversion :-P On\nsecond read, I think I did that on purpose (or by luck) because the\n\"kill register_ref_store\" patch needs this intermediate \"refs\" so we\ncan save it to main_ref_store. Cleaner future patch for the minor\nuncleanliness in the current one.\n-- \nDuy\n"},{"id":"312945","messageId":"f0821bd8-dda5-1b11-e66f-8fa4ab169778@alum.mit.edu","threadId":"45126","inReplyTo":"CACsJy8BvKvo8_bRHH++dp53DaPbDVkKU0EUQC7ofYvBo+Tec0Q@mail.gmail.com","subject":"Re: [PATCH v5 12/24] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-01T12:31:29Z","receivedAt":"2017-03-01T12:33:27Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/01/2017 01:00 PM, Duy Nguyen wrote:\n> On Wed, Mar 1, 2017 at 1:03 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>>>  struct ref_store *get_ref_store(const char *submodule)\n>>>  {\n>>>       struct strbuf submodule_sb = STRBUF_INIT;\n>>> @@ -1480,6 +1479,9 @@ struct ref_store *get_ref_store(const char *submodule)\n>>>       if (is_nonbare_repository_dir(&submodule_sb))\n>>>               refs = ref_store_init(submodule);\n>>>       strbuf_release(&submodule_sb);\n>>> +\n>>> +     if (refs)\n>>\n>> I think `refs` should always be non-NULL here for the same reason.\n> \n> That's true if is_nonbar_repo... returns true. If it's false (e.g.\n> uninitialized submodule) then refs remains NULL from before (I didn't\n> know about this until I hit a segfault in rev-list in another series)\n\nOh, yes, true.\n\nBut given that, I think the code would be clearer if the two calls were\nin the same if; i.e.,\n\n \trefs = lookup_submodule_ref_store(submodule);\n \tif (refs)\n \t\treturn refs;\n\n \tstrbuf_addstr(&submodule_sb, submodule);\n \tif (is_nonbare_repository_dir(&submodule_sb)) {\n \t\trefs = ref_store_init(submodule);\n\t\tregister_submodule_ref_store(refs, submodule);\n\t}\n \tstrbuf_release(&submodule_sb);\n\n \treturn refs;\n\nor even the `if (!is_nonbare_repository_dir(...)) goto cleanup;` pattern\nto make it clearer that this is an error return.\n\nMichael\n\n"},{"id":"312946","messageId":"CACsJy8BOY8jsCrDYESJ6Gwy_p=J=c8m1qe64w18HX1Cv2AaYnA@mail.gmail.com","threadId":"45126","inReplyTo":"7e5ef9e7-bd90-1917-d8eb-c6310c2744ab@alum.mit.edu","subject":"Re: [PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-01T12:34:50Z","receivedAt":"2017-03-01T12:35:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 12:34 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n>>  1 file changed, 95 insertions(+)\n>>  create mode 100755 t/t1406-submodule-ref-store.sh\n>>\n>> diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\n>> new file mode 100755\n>> index 000000000..3b30ba62f\n>> --- /dev/null\n>> +++ b/t/t1406-submodule-ref-store.sh\n>> [...]\n>\n> I haven't actually read this far in the patch series, but I noticed that\n> a test in this file fails:\n>\n>\n> t1406-submodule-ref-store.sh                     (Wstat: 256 Tests: 15\n> Failed: 1)\n>   Failed test:  10\n>   Non-zero exit status: 1\n>\n> I didn't have time to look into it more; let me know if you can't\n> reproduce it.\n\nFantastic. No I couldn't reproduce it, even --valgrind did not\ncomplain. First step maybe just pushing your branch somewhere so I can\ntry out if you're applying the patches via mail (maybe there's some\nchanges in the base that affect this). .Otherwise /t1406-* -v -i might\nbe enough clue for me to dig in, I hope.\n-- \nDuy\n"},{"id":"312948","messageId":"8f4ec174-9060-0896-5135-bb50229fe149@alum.mit.edu","threadId":"45126","inReplyTo":"CACsJy8BOY8jsCrDYESJ6Gwy_p=J=c8m1qe64w18HX1Cv2AaYnA@mail.gmail.com","subject":"Re: [PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-01T15:11:25Z","receivedAt":"2017-03-01T15:24:18Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/01/2017 01:34 PM, Duy Nguyen wrote:\n> On Wed, Mar 1, 2017 at 12:34 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>>> ---\n>>>  t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n>>>  1 file changed, 95 insertions(+)\n>>>  create mode 100755 t/t1406-submodule-ref-store.sh\n>>>\n>>> diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\n>>> new file mode 100755\n>>> index 000000000..3b30ba62f\n>>> --- /dev/null\n>>> +++ b/t/t1406-submodule-ref-store.sh\n>>> [...]\n>>\n>> I haven't actually read this far in the patch series, but I noticed that\n>> a test in this file fails:\n>>\n>>\n>> t1406-submodule-ref-store.sh                     (Wstat: 256 Tests: 15\n>> Failed: 1)\n>>   Failed test:  10\n>>   Non-zero exit status: 1\n>>\n>> I didn't have time to look into it more; let me know if you can't\n>> reproduce it.\n> \n> Fantastic. No I couldn't reproduce it, even --valgrind did not\n> complain. First step maybe just pushing your branch somewhere so I can\n> try out if you're applying the patches via mail (maybe there's some\n> changes in the base that affect this). .Otherwise /t1406-* -v -i might\n> be enough clue for me to dig in, I hope.\n\nI'm testing c5302654930070135eec9bc1b4ef99b14e0f28ee from Junio's GitHub\nfork.\n\nUnfortunately, the test succeeds (every time) when I run just `t1406-*`\nor with `-d` or `-i` options, but fails (every time) when run as part of\nthe whole test suite, so it's a bit tricky to dig deeper.\n\nBy trial and error, I found that the test succeeds if I comment out the\n\"for_each_reflog()\" test. By having that test write its results to\n`/tmp` where they won't be deleted, I found that the problem is that the\n`actual` results are not sorted correctly:\n\n    refs/heads/new-master 0x0\n    refs/heads/master 0x0\n    HEAD 0x1\n\nI don't know why it's so Heisenbergish.\n\nMichael\n\n"},{"id":"313040","messageId":"CACsJy8DyjWC5WUE7=dWK-mi_piGZJf7L1B0t5RUROvqDS+aP5g@mail.gmail.com","threadId":"45126","inReplyTo":"8f4ec174-9060-0896-5135-bb50229fe149@alum.mit.edu","subject":"Re: [PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-02T06:13:23Z","receivedAt":"2017-03-02T06:13:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 10:11 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> By trial and error, I found that the test succeeds if I comment out the\n> \"for_each_reflog()\" test. By having that test write its results to\n> `/tmp` where they won't be deleted, I found that the problem is that the\n> `actual` results are not sorted correctly:\n>\n>     refs/heads/new-master 0x0\n>     refs/heads/master 0x0\n>     HEAD 0x1\n>\n> I don't know why it's so Heisenbergish.\n\nIt happens consistently on my other laptop. And yes it looks like\nsorting order problem, probably because of the underlying file system.\nI did wonder about that at some point but never asked. We do not\nguarantee any sorting order in the for-each api, do we?\n-- \nDuy\n"},{"id":"313044","messageId":"51f2ba5a-38c9-17eb-e845-3a84b65c7029@alum.mit.edu","threadId":"45126","inReplyTo":"CACsJy8DyjWC5WUE7=dWK-mi_piGZJf7L1B0t5RUROvqDS+aP5g@mail.gmail.com","subject":"Re: [PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-02T08:16:45Z","receivedAt":"2017-03-02T08:17:41Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/02/2017 07:13 AM, Duy Nguyen wrote:\n> On Wed, Mar 1, 2017 at 10:11 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> By trial and error, I found that the test succeeds if I comment out the\n>> \"for_each_reflog()\" test. By having that test write its results to\n>> `/tmp` where they won't be deleted, I found that the problem is that the\n>> `actual` results are not sorted correctly:\n>>\n>>     refs/heads/new-master 0x0\n>>     refs/heads/master 0x0\n>>     HEAD 0x1\n>>\n>> I don't know why it's so Heisenbergish.\n> \n> It happens consistently on my other laptop. And yes it looks like\n> sorting order problem, probably because of the underlying file system.\n> I did wonder about that at some point but never asked. We do not\n> guarantee any sorting order in the for-each api, do we?\n\nThe for-each-ref iteration is defined to be sorted by refname, but it's\ntrue that the for-each-reflog order is undefined. So it was wrong for me\nto say that the results are not sorted correctly. The problem is that\nthe actual output is ordered differently than the \"expected\" results,\nwhich the test incorrectly considers to be an error.\n\nMichael\n\n"},{"id":"313062","messageId":"CACsJy8B-xi0Dob=KkHv2zMZ=ffca5qSo39VLGC1wPN6uXK6SGQ@mail.gmail.com","threadId":"45126","inReplyTo":"9074fcd9-2b80-8927-19d2-1659eb0e327c@alum.mit.edu","subject":"Re: [PATCH v5 04/24] files-backend: convert git_path() to strbuf_git_path()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-02T12:52:34Z","receivedAt":"2017-03-02T13:00:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 12:06 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> @@ -2681,13 +2709,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n>>       log_all_ref_updates = flag;\n>>\n>>   rollbacklog:\n>> -     if (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n>> +     strbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n>> +     strbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n>> +     if (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n>>               error(\"unable to restore logfile %s from %s: %s\",\n>>                       oldrefname, newrefname, strerror(errno));\n>> +     strbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n>>       if (!logmoved && log &&\n>> -         rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n>> +         rename(tmp_renamed_log.buf, sb_oldref.buf))\n>>               error(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n>>                       oldrefname, strerror(errno));\n>\n> It feels like you're writing, releasing, re-writing these strbufs more\n> than necessary. Maybe it would be clearer to set them once, and on\n> errors set `ret = error()` then jump to a label here...\n>\n>> +     strbuf_release(&sb_newref);\n>> +     strbuf_release(&sb_oldref);\n>> +     strbuf_release(&tmp_renamed_log);\n>>\n>\n> ...and change this to `return ret`?\n\nIf that's ok with you, will do. I kept the patch dumb so the reader\ndoes not have to keep track of many things when they check if there\nare any behavior changes.\n\n>>  static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n>>  {\n>> +     struct strbuf sb = STRBUF_INIT;\n>> +\n>>       /* Check validity (but we don't need the result): */\n>>       files_downcast(ref_store, 0, \"init_db\");\n>>\n>>       /*\n>>        * Create .git/refs/{heads,tags}\n>>        */\n>> -     safe_create_dir(git_path(\"refs/heads\"), 1);\n>> -     safe_create_dir(git_path(\"refs/tags\"), 1);\n>> +     strbuf_git_path(&sb, \"refs/heads\");\n>> +     safe_create_dir(sb.buf, 1);\n>> +     strbuf_reset(&sb);\n>> +     strbuf_git_path(&sb, \"refs/tags\");\n>> +     safe_create_dir(sb.buf, 1);\n>> +     strbuf_reset(&sb);\n>>       if (get_shared_repository()) {\n>> -             adjust_shared_perm(git_path(\"refs/heads\"));\n>> -             adjust_shared_perm(git_path(\"refs/tags\"));\n>> +             strbuf_git_path(&sb, \"refs/heads\");\n>> +             adjust_shared_perm(sb.buf);\n>> +             strbuf_reset(&sb);\n>> +             strbuf_git_path(&sb, \"refs/tags\");\n>> +             adjust_shared_perm(sb.buf);\n>>       }\n>> +     strbuf_release(&sb);\n>>       return 0;\n>>  }\n>\n> It looks to me like `safe_create_dir()` already has the ability to\n> `adjust_shared_perm()`, or am I missing something? (I realize that this\n> is preexisting code.)\n\nYeah you're right. I guess this code was written when\nsafe_create_dir() didn't do that. That certainly shortens this patch.\n-- \nDuy\n"},{"id":"313063","messageId":"CACsJy8DkZFMtBST9pmBO=r71FUaZeMKweJDQ2xuMAV-APFVWFw@mail.gmail.com","threadId":"45126","inReplyTo":"51f2ba5a-38c9-17eb-e845-3a84b65c7029@alum.mit.edu","subject":"Re: [PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-02T12:38:49Z","receivedAt":"2017-03-02T13:15:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 2, 2017 at 3:16 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> The for-each-ref iteration is defined to be sorted by refname, but it's\n> true that the for-each-reflog order is undefined.\n\nThanks. I'm going to add a few lines about this near for_each_ref and\nfor_each_reflog() then, assuming that this is the desired behavior,\nnot a bug. (Another patch, because I'm not happy until it gets 30+)\n-- \nDuy\n"},{"id":"313064","messageId":"CACsJy8DhMdtzmK6-1MmTKf_SpyeR23CJ0kU=LGHOsMXw+90yBw@mail.gmail.com","threadId":"45126","inReplyTo":"5c4a292e-ab86-ffe0-ba8d-c0fe1b527ec1@alum.mit.edu","subject":"Re: [PATCH v5 08/24] files-backend: remove the use of git_path()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-02T12:43:11Z","receivedAt":"2017-03-02T13:15:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 12:50 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> @@ -995,7 +998,11 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n>>               return ref_store;\n>>       }\n>>\n>> -     refs->packed_refs_path = git_pathdup(\"packed-refs\");\n>> +     refs->gitdir = xstrdup(gitdir);\n>> +     get_common_dir_noenv(&sb, gitdir);\n>> +     refs->gitcommondir = strbuf_detach(&sb, NULL);\n>> +     strbuf_addf(&sb, \"%s/packed-refs\", refs->gitcommondir);\n>> +     refs->packed_refs_path = strbuf_detach(&sb, NULL);\n>\n> `git_path()` and friends avoid adding an extra `/` if `git_dir()`\n> already ends in a slash or if it is the empty string. Here you don't\n> have that functionality. Is that intentional?\n\nKind of. I noticed that behavior but the thinking was, this $GIT_DIR\nthing is going to get replaced soon anyway, and because the the uses\nof these paths are very clear (in three groups) that avoiding\nredundant slashes does not buy us anything extra, it's just more code.\n-- \nDuy\n"},{"id":"313065","messageId":"CACsJy8AoZt-po5R7+1qPBqTzJ6aiVkKif9Lyp9y0ZwRM09Tj-Q@mail.gmail.com","threadId":"45126","inReplyTo":"cb86192d-8644-bed3-1e73-821931589e79@alum.mit.edu","subject":"Re: [PATCH v5 05/24] files-backend: move \"logs/\" out of TMP_RENAMED_LOG","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-02T13:07:13Z","receivedAt":"2017-03-02T13:15:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 12:19 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> @@ -2513,7 +2513,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n>>   * IOW, to avoid cross device rename errors, the temporary renamed log must\n>>   * live into logs/refs.\n>>   */\n>> -#define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n>> +#define TMP_RENAMED_LOG  \"refs/.tmp-renamed-log\"\n>\n> The constant name feels a little bit misleading now that it is not the\n> name of a logfile but rather a reference name. OTOH \"tmp-renamed-log\" is\n> *in* the reference name so I guess it's not really wrong.\n\nHeh.. I had a similar internal debate and almost renamed it to\ntmp_renamed_refname. But then it's technically not a valid ref name\neither (starting with a leading dot). My lazy side came in and\ndeclared that doing nothing was always the right way.\n\n>>  struct rename_cb {\n>>       const char *tmp_renamed_log;\n>> @@ -2549,7 +2549,7 @@ static int rename_tmp_log(const char *newrefname)\n>>       int ret;\n>>\n>>       strbuf_git_path(&path, \"logs/%s\", newrefname);\n>> -     strbuf_git_path(&tmp, TMP_RENAMED_LOG);\n>> +     strbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n>>       cb.tmp_renamed_log = tmp.buf;\n>>       ret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n>>       if (ret) {\n>> @@ -2626,12 +2626,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n>>               return 1;\n>>\n>>       strbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n>> -     strbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n>> +     strbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n>>       ret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n>>       strbuf_release(&sb_oldref);\n>>       strbuf_release(&tmp_renamed_log);\n>>       if (ret)\n>> -             return error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n>> +             return error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n>>                       oldrefname, strerror(errno));\n>\n> It seems like it would be preferable to use `sb_oldref.buf` and\n> `tmp.buf` when building the error message. But I guess that `tmp.buf`\n> might include some path preceding \"logs/\" that is unwanted in the error\n> message? But it's a shame to hardcode the file naming scheme here again.\n>\n> Maybe we *do* want the path in the error message?\n\nIt's an error, every piece of details matters. So yeah I'm inclined we\nshould print full path.\n\n> It just occurred to me: this temporary logfile lives in the main\n> repository, right? What if a worktree reference is being renamed? Part\n> of the advertised use of worktrees is that the worktree might live far\n> from the main directory, or even on removable media. But it's not\n> possible to rename files across partitions. Maybe this will come out in\n> the wash once worktrees are ref_stores themselves.\n\nThe actual working directory may be separated, but all the things that\nbelong to .git (even of a linked worktree) stay in the main worktree's\n.git directory. And I don't think we ever support having a .git\ndirectory on multiple partitions. You can rename refs freely even when\nthe worktree is on a detached removable drive.\n\n> For that matter, what if a user tries to rename a worktree ref into a\n> common ref or vice versa?\n\nInteresting. It should work, it's just a\nrename(\".git/worktrees/blah/refs/bisect/good\",\n\".git/refs/heads/saved\") after the path translation done by\ngit_path().\n-- \nDuy\n"},{"id":"313066","messageId":"CACsJy8CC=DSHyOk2qpdprUTt_3HGK7Wi0VSs4N-0wTUQ-xR8Lw@mail.gmail.com","threadId":"45126","inReplyTo":"beef528b-66cd-e104-4c02-b07ddfa7e6d7@alum.mit.edu","subject":"Re: [PATCH v5 07/24] files-backend: add and use files_refname_path()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-02T12:46:57Z","receivedAt":"2017-03-02T13:16:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 1, 2017 at 12:41 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n>> Keep repo-related path handling in one place. This will make it easier\n>> to add submodule/multiworktree support later.\n>>\n>> This automatically adds the \"if submodule then use the submodule version\n>> of git_path\" to other call sites too. But it does not mean those\n>> operations are sumodule-ready. Not yet.\n>\n> s/sumodule/submodule/\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs/files-backend.c | 45 +++++++++++++++++++++++++--------------------\n>>  1 file changed, 25 insertions(+), 20 deletions(-)\n>>\n>> diff --git a/refs/files-backend.c b/refs/files-backend.c\n>> index 7b4ea4c56..72f4e1746 100644\n>> --- a/refs/files-backend.c\n>> +++ b/refs/files-backend.c\n>> @@ -1180,6 +1180,18 @@ static void files_reflog_path(struct files_ref_store *refs,\n>>       strbuf_git_path(sb, \"logs/%s\", refname);\n>>  }\n>>\n>> +static void files_refname_path(struct files_ref_store *refs,\n>> +                            struct strbuf *sb,\n>> +                            const char *refname)\n>> +{\n>> +     if (refs->submodule) {\n>> +             strbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n>> +             return;\n>> +     }\n>> +\n>> +     strbuf_git_path(sb, \"%s\", refname);\n>> +}\n>\n> Maybe it's just me, but I find it odd to exit early here when the first\n> exit isn't due to an error. For me, structuring this like `if ()\n> call1(); else call2();` would make it clearer that the two code paths\n> are equally-valid alternatives, and either one or the other will be\n> executed.\n\nIts original version probably looked better. This is another case of\nfuture patches influencing back the past ones: I structure the patch\nso that in future we mostly add lines, or delete whole (in this case,\nI believe), not modify a lot of lines. I think the readability does\nnot degrade too much though, so it's probably ok.\n-- \nDuy\n"},{"id":"313176","messageId":"aef3916b-6c10-6fbd-7f5c-4693f0cbd700@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-16-pclouds@gmail.com","subject":"Re: [PATCH v5 15/24] refs: move submodule code out of files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-03T14:32:07Z","receivedAt":"2017-03-03T14:36:06Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> files-backend is now initialized with a $GIT_DIR. Converting a submodule\n> path to where real submodule gitdir is located is done in get_ref_store().\n> \n> This gives a slight performance improvement for submodules since we\n> don't convert submodule path to gitdir at every backend call like\n> before. We pay that once at ref-store creation.\n> \n> More cleanup in files_downcast() and files_assert_main_repository()\n> follows shortly. It's separate to keep noises from this patch.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c               | 20 ++++++++++++++------\n>  refs/files-backend.c | 24 ++----------------------\n>  refs/refs-internal.h |  9 ++++-----\n>  3 files changed, 20 insertions(+), 33 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index 562834fc0..67acae60c 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -9,6 +9,7 @@\n>  #include \"refs/refs-internal.h\"\n>  #include \"object.h\"\n>  #include \"tag.h\"\n> +#include \"submodule.h\"\n>  \n>  /*\n>   * List of all available backends\n> @@ -1413,9 +1414,9 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n>  \n>  /*\n>   * Create, record, and return a ref_store instance for the specified\n> - * submodule (or the main repository if submodule is NULL).\n> + * gitdir.\n>   */\n> -static struct ref_store *ref_store_init(const char *submodule)\n> +static struct ref_store *ref_store_init(const char *gitdir)\n>  {\n>  \tconst char *be_name = \"files\";\n>  \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n> @@ -1424,7 +1425,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n>  \tif (!be)\n>  \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n>  \n> -\trefs = be->init(submodule);\n> +\trefs = be->init(gitdir);\n>  \treturn refs;\n>  }\n>  \n> @@ -1435,7 +1436,7 @@ struct ref_store *get_main_ref_store(void)\n>  \tif (main_ref_store)\n>  \t\treturn main_ref_store;\n>  \n> -\trefs = ref_store_init(NULL);\n> +\trefs = ref_store_init(get_git_dir());\n>  \tif (refs) {\n>  \t\tif (main_ref_store)\n>  \t\t\tdie(\"BUG: main_ref_store initialized twice\");\n> @@ -1466,6 +1467,7 @@ struct ref_store *get_ref_store(const char *submodule)\n>  {\n>  \tstruct strbuf submodule_sb = STRBUF_INIT;\n>  \tstruct ref_store *refs;\n> +\tint ret;\n>  \n>  \tif (!submodule || !*submodule) {\n>  \t\treturn get_main_ref_store();\n> @@ -1476,8 +1478,14 @@ struct ref_store *get_ref_store(const char *submodule)\n>  \t\treturn refs;\n>  \n>  \tstrbuf_addstr(&submodule_sb, submodule);\n> -\tif (is_nonbare_repository_dir(&submodule_sb))\n> -\t\trefs = ref_store_init(submodule);\n> +\tret = is_nonbare_repository_dir(&submodule_sb);\n> +\tstrbuf_release(&submodule_sb);\n> +\tif (!ret)\n> +\t\treturn refs;\n\n`refs` is always NULL here, right? Then it would be more transparent to\nreturn NULL. Or maybe use the `goto cleanup` pattern, which makes it\nclearer which are error-handling paths (and lets you get avoid the\ntemporary variable `ret`)).\n\n> +\n> +\tret = submodule_to_gitdir(&submodule_sb, submodule);\n> +\tif (!ret)\n> +\t\trefs = ref_store_init(submodule_sb.buf);\n>  \tstrbuf_release(&submodule_sb);\n>  \n>  \tif (refs)\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index d80c27837..37443369b 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -917,12 +917,6 @@ struct packed_ref_cache {\n>  struct files_ref_store {\n>  \tstruct ref_store base;\n>  \n> -\t/*\n> -\t * The name of the submodule represented by this object, or\n> -\t * NULL if it represents the main repository's reference\n> -\t * store:\n> -\t */\n> -\tconst char *submodule;\n>  \tchar *gitdir;\n>  \tchar *gitcommondir;\n>  \tchar *packed_refs_path;\n> @@ -982,22 +976,14 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n>   * Create a new submodule ref cache and add it to the internal\n>   * set of caches.\n>   */\n> -static struct ref_store *files_ref_store_create(const char *submodule)\n> +static struct ref_store *files_ref_store_create(const char *gitdir)\n>  {\n>  \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n>  \tstruct ref_store *ref_store = (struct ref_store *)refs;\n>  \tstruct strbuf sb = STRBUF_INIT;\n> -\tconst char *gitdir = get_git_dir();\n>  \n>  \tbase_ref_store_init(ref_store, &refs_be_files);\n>  \n> -\tif (submodule) {\n> -\t\trefs->submodule = xstrdup(submodule);\n> -\t\trefs->packed_refs_path = git_pathdup_submodule(\n> -\t\t\trefs->submodule, \"packed-refs\");\n> -\t\treturn ref_store;\n> -\t}\n> -\n>  \trefs->gitdir = xstrdup(gitdir);\n>  \tget_common_dir_noenv(&sb, gitdir);\n>  \trefs->gitcommondir = strbuf_detach(&sb, NULL);\n> @@ -1014,8 +1000,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n>  static void files_assert_main_repository(struct files_ref_store *refs,\n>  \t\t\t\t\t const char *caller)\n>  {\n> -\tif (refs->submodule)\n> -\t\tdie(\"BUG: %s called for a submodule\", caller);\n> +\t/* This function is to be deleted in the next patch */\n\nI don't think the above comment is correct anymore. Possibly the commit\nlog message is also out of date too, but I haven't read far enough ahead\nto know.\n\n> [...]\n\nMichael\n\n"},{"id":"313179","messageId":"8fafd49f-71a6-ee97-6a69-c23e23c5d515@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-17-pclouds@gmail.com","subject":"Re: [PATCH v5 16/24] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-03T14:49:13Z","receivedAt":"2017-03-03T15:10:48Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> files-backend.c is unlearning submodules. Instead of having a specific\n> check for submodules to see what operation is allowed, files backend\n> now takes a set of flags at init. Each operation will check if the\n> required flags is present before performing.\n> \n> For now we have four flags: read, write and odb access. Main ref store\n> has all flags, obviously, while submodule stores are read-only and have\n> access to odb (*).\n\nI'm surprised that you have implemented a \"read\" flag. Do you expect\never to support ref-stores that are unreadable? In other words, couldn't\n\"read\" just be assumed for any ref store?\n\n> The \"main\" flag stays because many functions in the backend calls\n> frontend ones without a ref store, so these functions always target the\n> main ref store. Ideally the flag should be gone after ref-store-aware\n> api is in place and used by backends.\n> \n> (*) Submodule code needs for_each_ref. Try take REF_STORE_ODB flag\n> out. At least t3404 would fail. The \"have access to odb\" in submodule is\n> a bit hacky since we don't know from he whether add_submodule_odb() has\n> been called.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c               | 15 ++++++---\n>  refs/files-backend.c | 86 +++++++++++++++++++++++++++++++++-------------------\n>  refs/refs-internal.h |  9 +++++-\n>  3 files changed, 73 insertions(+), 37 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index 67acae60c..2dc97891a 100644\n> --- a/refs.c\n> +++ b/refs.c\n> [...]\n> @@ -1821,10 +1829,14 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n>  static int files_peel_ref(struct ref_store *ref_store,\n>  \t\t\t  const char *refname, unsigned char *sha1)\n>  {\n> -\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n> +\tstruct files_ref_store *refs =\n> +\t\tfiles_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,\n> +\t\t\t       \"peel_ref\");\n>  \tint flag;\n>  \tunsigned char base[20];\n>  \n> +\tfiles_assert_main_repository(refs, \"peel_ref\");\n\nInstead of this call, couldn't you add `REF_STORE_MAIN` to the flags\npassed to `files_downcase()`?\n\n> +\n>  \tif (current_ref_iter && current_ref_iter->refname == refname) {\n>  \t\tstruct object_id peeled;\n>  \n> [...]\n\nMichael\n\n"},{"id":"313188","messageId":"f837bec6-301c-b9a3-83f4-99d03ca10432@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-20-pclouds@gmail.com","subject":"Re: [PATCH v5 19/24] refs: new transaction related ref-store api","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-03T15:48:37Z","receivedAt":"2017-03-03T15:51:32Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> The transaction struct now takes a ref store at creation and will\n> operate on that ref store alone.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c               | 54 ++++++++++++++++++++++++++++++++++++++++------------\n>  refs.h               |  8 ++++++++\n>  refs/refs-internal.h |  1 +\n>  3 files changed, 51 insertions(+), 12 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index 9137ac283..851b5e125 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -618,16 +618,19 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1\n>  \treturn 0;\n>  }\n>  \n> -int delete_ref(const char *refname, const unsigned char *old_sha1,\n> -\t       unsigned int flags)\n> +int refs_delete_ref(struct ref_store *refs, const char *refname,\n> +\t\t    const unsigned char *old_sha1,\n> +\t\t    unsigned int flags)\n>  {\n>  \tstruct ref_transaction *transaction;\n>  \tstruct strbuf err = STRBUF_INIT;\n>  \n> -\tif (ref_type(refname) == REF_TYPE_PSEUDOREF)\n> +\tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n> +\t\tassert(refs == get_main_ref_store());\n\nHmmm, I would have expected this to be checked via `(refs.flags &\nREF_STORE_MAIN)`. I guess this is temporary; once we have compound ref\nstores either version will need to be changed again. So I don't see a\nstrong argument for one vs. the other.\n\nIt might be more natural to make `delete_pseudoref()` take a `refs`\nargument and do the check internally.\n\n(Same comments below where `write_pseudoref()` is called.)\n\nMichael\n\n>  \t\treturn delete_pseudoref(refname, old_sha1);\n> +\t}\n>  \n> -\ttransaction = ref_transaction_begin(&err);\n> +\ttransaction = ref_store_transaction_begin(refs, &err);\n>  \tif (!transaction ||\n>  \t    ref_transaction_delete(transaction, refname, old_sha1,\n>  \t\t\t\t   flags, NULL, &err) ||\n> @@ -642,6 +645,13 @@ int delete_ref(const char *refname, const unsigned char *old_sha1,\n>  \treturn 0;\n>  }\n>  \n> +int delete_ref(const char *refname, const unsigned char *old_sha1,\n> +\t       unsigned int flags)\n> +{\n> +\treturn refs_delete_ref(get_main_ref_store(), refname,\n> +\t\t\t       old_sha1, flags);\n> +}\n> +\n>  int copy_reflog_msg(char *buf, const char *msg)\n>  {\n>  \tchar *cp = buf;\n> @@ -801,11 +811,20 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,\n>  \treturn 1;\n>  }\n>  \n> -struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n> +struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n> +\t\t\t\t\t\t    struct strbuf *err)\n>  {\n> +\tstruct ref_transaction *tr;\n>  \tassert(err);\n>  \n> -\treturn xcalloc(1, sizeof(struct ref_transaction));\n> +\ttr = xcalloc(1, sizeof(struct ref_transaction));\n> +\ttr->ref_store = refs;\n> +\treturn tr;\n> +}\n> +\n> +struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n> +{\n> +\treturn ref_store_transaction_begin(get_main_ref_store(), err);\n>  }\n>  \n>  void ref_transaction_free(struct ref_transaction *transaction)\n> @@ -922,18 +941,20 @@ int update_ref_oid(const char *msg, const char *refname,\n>  \t\told_oid ? old_oid->hash : NULL, flags, onerr);\n>  }\n>  \n> -int update_ref(const char *msg, const char *refname,\n> -\t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n> -\t       unsigned int flags, enum action_on_err onerr)\n> +int refs_update_ref(struct ref_store *refs, const char *msg,\n> +\t\t    const char *refname, const unsigned char *new_sha1,\n> +\t\t    const unsigned char *old_sha1, unsigned int flags,\n> +\t\t    enum action_on_err onerr)\n>  {\n>  \tstruct ref_transaction *t = NULL;\n>  \tstruct strbuf err = STRBUF_INIT;\n>  \tint ret = 0;\n>  \n>  \tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n> +\t\tassert(refs == get_main_ref_store());\n>  \t\tret = write_pseudoref(refname, new_sha1, old_sha1, &err);\n>  \t} else {\n> -\t\tt = ref_transaction_begin(&err);\n> +\t\tt = ref_store_transaction_begin(refs, &err);\n>  \t\tif (!t ||\n>  \t\t    ref_transaction_update(t, refname, new_sha1, old_sha1,\n>  \t\t\t\t\t   flags, msg, &err) ||\n> @@ -964,6 +985,15 @@ int update_ref(const char *msg, const char *refname,\n>  \treturn 0;\n>  }\n>  \n> +int update_ref(const char *msg, const char *refname,\n> +\t       const unsigned char *new_sha1,\n> +\t       const unsigned char *old_sha1,\n> +\t       unsigned int flags, enum action_on_err onerr)\n> +{\n> +\treturn refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,\n> +\t\t\t       old_sha1, flags, onerr);\n> +}\n> +\n>  char *shorten_unambiguous_ref(const char *refname, int strict)\n>  {\n>  \tint i;\n> @@ -1600,7 +1630,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n>  int ref_transaction_commit(struct ref_transaction *transaction,\n>  \t\t\t   struct strbuf *err)\n>  {\n> -\tstruct ref_store *refs = get_main_ref_store();\n> +\tstruct ref_store *refs = transaction->ref_store;\n>  \n>  \treturn refs->be->transaction_commit(refs, transaction, err);\n>  }\n> @@ -1719,7 +1749,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n>  int initial_ref_transaction_commit(struct ref_transaction *transaction,\n>  \t\t\t\t   struct strbuf *err)\n>  {\n> -\tstruct ref_store *refs = get_main_ref_store();\n> +\tstruct ref_store *refs = transaction->ref_store;\n>  \n>  \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n>  }\n> diff --git a/refs.h b/refs.h\n> index 70d4eb87c..342cecd23 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -330,6 +330,9 @@ int reflog_exists(const char *refname);\n>   * exists, regardless of its old value. It is an error for old_sha1 to\n>   * be NULL_SHA1. flags is passed through to ref_transaction_delete().\n>   */\n> +int refs_delete_ref(struct ref_store *refs, const char *refname,\n> +\t\t    const unsigned char *old_sha1,\n> +\t\t    unsigned int flags);\n>  int delete_ref(const char *refname, const unsigned char *old_sha1,\n>  \t       unsigned int flags);\n>  \n> @@ -414,6 +417,8 @@ enum action_on_err {\n>   * Begin a reference transaction.  The reference transaction must\n>   * be freed by calling ref_transaction_free().\n>   */\n> +struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n> +\t\t\t\t\t\t    struct strbuf *err);\n>  struct ref_transaction *ref_transaction_begin(struct strbuf *err);\n>  \n>  /*\n> @@ -548,6 +553,9 @@ void ref_transaction_free(struct ref_transaction *transaction);\n>   * ref_transaction_update(). Handle errors as requested by the `onerr`\n>   * argument.\n>   */\n> +int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,\n> +\t\t    const unsigned char *new_sha1, const unsigned char *old_sha1,\n> +\t\t    unsigned int flags, enum action_on_err onerr);\n>  int update_ref(const char *msg, const char *refname,\n>  \t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n>  \t       unsigned int flags, enum action_on_err onerr);\n> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n> index 5f26208c2..690498698 100644\n> --- a/refs/refs-internal.h\n> +++ b/refs/refs-internal.h\n> @@ -200,6 +200,7 @@ enum ref_transaction_state {\n>   * as atomically as possible.  This structure is opaque to callers.\n>   */\n>  struct ref_transaction {\n> +\tstruct ref_store *ref_store;\n>  \tstruct ref_update **updates;\n>  \tsize_t alloc;\n>  \tsize_t nr;\n> \n\n"},{"id":"313190","messageId":"07b603ad-386d-56a8-8f0b-9721006313c7@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-21-pclouds@gmail.com","subject":"Re: [PATCH v5 20/24] files-backend: avoid ref api targetting main ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-03T16:03:13Z","receivedAt":"2017-03-03T16:08:36Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> A small step towards making files-backend works as a non-main ref store\n> using the newly added store-aware API.\n> \n> For the record, `join` and `nm` on refs.o and files-backend.o tell me\n> that files-backend no longer uses functions that defaults to\n> get_main_ref_store().\n\nNice!\n\n> I'm not yet comfortable at the idea of removing\n> files_assert_main_repository() (or converting REF_STORE_MAIN to\n> REF_STORE_WRITE). More staring and testing is required before that can\n> happen. Well, except peel_ref(). I'm pretty sure that function is safe.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 85 ++++++++++++++++++++++++++++++----------------------\n>  1 file changed, 49 insertions(+), 36 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index dafddefd3..09c280fd3 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> [...]\n> @@ -3873,8 +3883,10 @@ static int files_transaction_commit(struct ref_store *ref_store,\n>  \t * head_ref within the transaction, then split_head_update()\n>  \t * arranges for the reflog of HEAD to be updated, too.\n>  \t */\n> -\thead_ref = resolve_refdup(\"HEAD\", RESOLVE_REF_NO_RECURSE,\n> -\t\t\t\t  head_oid.hash, &head_type);\n> +\thead_ref = (char *)refs_resolve_ref_unsafe(ref_store, \"HEAD\",\n> +\t\t\t\t\t\t   RESOLVE_REF_NO_RECURSE,\n> +\t\t\t\t\t\t   head_oid.hash, &head_type);\n> +\thead_ref = xstrdup_or_null(head_ref);\n\nIf you combine the last two statements, you can avoid the ugly cast.\n\n> [...]\n\nMichael\n"},{"id":"313194","messageId":"7769af97-3dc7-f23b-c7c1-1d6b227a2f83@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-24-pclouds@gmail.com","subject":"Re: [PATCH v5 23/24] t1405: some basic tests on main ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-03T16:43:08Z","receivedAt":"2017-03-03T16:43:20Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  t/t1405-main-ref-store.sh (new +x) | 123 +++++++++++++++++++++++++++++++++++++\n>  1 file changed, 123 insertions(+)\n>  create mode 100755 t/t1405-main-ref-store.sh\n> \n> diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh\n> new file mode 100755\n> index 000000000..0999829f1\n> --- /dev/null\n> +++ b/t/t1405-main-ref-store.sh\n> @@ -0,0 +1,123 @@\n> +#!/bin/sh\n> +\n> +test_description='test main ref store api'\n> +\n> +. ./test-lib.sh\n> +\n> +RUN=\"test-ref-store main\"\n> +\n> +test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '\n> +\ttest_commit one &&\n> +\tN=`find .git/refs -type f | wc -l` &&\n\nI think we prefer $() to ``. (Same throughout this file and also t1406\nin the next commit.)\n\nIt's notable that these tests grep around the filesystem, so they won't\nbe applicable to future refs backends. Of course, \"pack-refs\" is\nintrinsically only applicable to the files backend, so for this test\nit's not surprising. But some tests could conceivably be written in a\ngeneric way, so that they should pass for any refs backend.\n\nJust food for thought; no need to change anything now.\n\n> +\ttest \"$N\" != 0 &&\n> +\t$RUN pack-refs 3 &&\n> +\tN=`find .git/refs -type f | wc -l`\n\nDidn't you mean to test the final `$N`?\n\n> +'\n> +\n> +test_expect_success 'peel_ref(new-tag)' '\n> +\tgit rev-parse HEAD >expected &&\n> +\tgit tag -a -m new-tag new-tag HEAD &&\n> +\t$RUN peel-ref refs/tags/new-tag >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'create_symref(FOO, refs/heads/master)' '\n> +\t$RUN create-symref FOO refs/heads/master nothing &&\n> +\techo refs/heads/master >expected &&\n> +\tgit symbolic-ref FOO >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '\n> +\tgit rev-parse FOO -- &&\n> +\tgit rev-parse refs/tags/new-tag -- &&\n> +\t$RUN delete-refs 0 FOO refs/tags/new-tag &&\n> +\ttest_must_fail git rev-parse FOO -- &&\n> +\ttest_must_fail git rev-parse refs/tags/new-tag --\n> +'\n> +\n> +test_expect_success 'rename_refs(master, new-master)' '\n> +\tgit rev-parse master >expected &&\n> +\t$RUN rename-ref refs/heads/master refs/heads/new-master &&\n> +\tgit rev-parse new-master >actual &&\n> +\ttest_cmp expected actual &&\n> +\ttest_commit recreate-master\n> +'\n\nIsn't HEAD set to `refs/heads/master` prior to this test? If so, then I\nthink it should become detached when you rename `refs/heads/master`. Or\nmaybe it should be changed to point at `refs/heads/new-master`, I can't\nremember. In either case, it might be useful for the test to check that\nthe behavior matches the status quo, so we notice if the behavior ever\nchanges inadvertently.\n\n> +\n> +test_expect_success 'for_each_ref(refs/heads/)' '\n> +\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n> +\tcat >expected <<-\\EOF &&\n> +\tmaster 0x0\n> +\tnew-master 0x0\n> +\tEOF\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'resolve_ref(new-master)' '\n> +\tSHA1=`git rev-parse new-master` &&\n> +\techo \"$SHA1 refs/heads/new-master 0x0\" >expected &&\n> +\t$RUN resolve-ref refs/heads/new-master 0 >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'verify_ref(new-master)' '\n> +\t$RUN verify-ref refs/heads/new-master\n> +'\n> +\n> +test_expect_success 'for_each_reflog()' '\n> +\t$RUN for-each-reflog | cut -c 42- >actual &&\n> +\tcat >expected <<-\\EOF &&\n> +\trefs/heads/master 0x0\n> +\trefs/heads/new-master 0x0\n> +\tHEAD 0x1\n> +\tEOF\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'for_each_reflog_ent()' '\n> +\t$RUN for-each-reflog-ent HEAD >actual &&\n> +\thead -n1 actual | grep one &&\n> +\ttail -n2 actual | head -n1 | grep recreate-master\n> +'\n> +\n> +test_expect_success 'for_each_reflog_ent_reverse()' '\n> +\t$RUN for-each-reflog-ent-reverse HEAD >actual &&\n> +\thead -n1 actual | grep recreate-master &&\n> +\ttail -n2 actual | head -n1 | grep one\n> +'\n> +\n> +test_expect_success 'reflog_exists(HEAD)' '\n> +\t$RUN reflog-exists HEAD\n> +'\n> +\n> +test_expect_success 'delete_reflog(HEAD)' '\n> +\t$RUN delete-reflog HEAD &&\n> +\t! test -f .git/logs/HEAD\n\nMaybe also test the final state using `reflog-exists` to make sure that\nfunction can also return false?\n\n> +'\n> +\n> +test_expect_success 'create-reflog(HEAD)' '\n> +\t$RUN create-reflog HEAD 1 &&\n> +\ttest -f .git/logs/HEAD\n> +'\n> +\n> +test_expect_success 'delete_ref(refs/heads/foo)' '\n> +\tgit checkout -b foo &&\n> +\tFOO_SHA1=`git rev-parse foo` &&\n> +\tgit checkout --detach &&\n> +\ttest_commit bar-commit &&\n> +\tgit checkout -b bar &&\n> +\tBAR_SHA1=`git rev-parse bar` &&\n> +\t$RUN update-ref updating refs/heads/foo $BAR_SHA1 $FOO_SHA1 0 &&\n> +\techo $BAR_SHA1 >expected &&\n> +\tgit rev-parse refs/heads/foo >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'delete_ref(refs/heads/foo)' '\n> +\tSHA1=`git rev-parse foo` &&\n> +\tgit checkout --detach &&\n> +\t$RUN delete-ref refs/heads/foo $SHA1 0 &&\n> +\ttest_must_fail git rev-parse refs/heads/foo --\n> +'\n\nThe last two tests have the same name.\n\nYou might also want to test these functions when the old oid is\nincorrect or when the reference is missing.\n\n> +test_done\n\nThanks for all the new tests!\n\nMichael\n\n"},{"id":"313195","messageId":"22f499da-4ae9-dd93-3862-3287f24c5645@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"Re: [PATCH v5 00/24] Remove submodule from files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-03T16:54:42Z","receivedAt":"2017-03-03T16:56:30Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> v5 goes a bit longer than v4, basically:\n\nI've reviewed all of the patches and left a bunch of comments, mostly\nsuperficial. I'm very happy about the way it's going, and especially\nwant to say how much I appreciate that you've done so much legwork and\nadded so many tests.\n\nMichael\n\n"},{"id":"313198","messageId":"eb088421-11c1-a6d7-398e-b2b8b4d785e3@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-25-pclouds@gmail.com","subject":"Re: [PATCH v5 24/24] t1406: new tests for submodule ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-03T16:51:29Z","receivedAt":"2017-03-03T17:02:10Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n>  1 file changed, 95 insertions(+)\n>  create mode 100755 t/t1406-submodule-ref-store.sh\n\nI wonder if you could reduce some of the repetition between this test\nfile and t1405, at least for the functions that should work for both the\nmain repository and for submodules? For example, if you were to define\n\n    GIT='git'\n\nin one case and\n\n    GIT='git -C sub'\n\nin the other, then maybe some of the setup code could be made to look\nalike? Then the division could be one test file for the functions that\nwork for both main and submodules, and a second for the functions that\nonly work for main. Just a thought...\n\nMichael\n\n"},{"id":"313338","messageId":"CACsJy8CfB=ZwVuYJd8QfFM5PqFHKfHLZ8RqDmZ7=R-=PdOJbjw@mail.gmail.com","threadId":"45126","inReplyTo":"7769af97-3dc7-f23b-c7c1-1d6b227a2f83@alum.mit.edu","subject":"Re: [PATCH v5 23/24] t1405: some basic tests on main ref store","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-06T12:30:43Z","receivedAt":"2017-03-06T12:32:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 3, 2017 at 11:43 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> It's notable that these tests grep around the filesystem, so they won't\n> be applicable to future refs backends. Of course, \"pack-refs\" is\n> intrinsically only applicable to the files backend, so for this test\n> it's not surprising. But some tests could conceivably be written in a\n> generic way, so that they should pass for any refs backend.\n>\n> Just food for thought; no need to change anything now.\n\nI'm a bit on the fence about this. On one hand I think there is room\nto backend-specific tests (and this one is more about files backend,\nwe just don't have any direct way of getting the backend except\nthrough get_main_ref_store()).\n\nOn the other hand, I can see a need for verifying refs behavior across\nbackends. Submodule backend is unfortunately a bad fit (and probably\nworktree backend too in early phase) because it cannot fully replace\nfiles backend. lmdb does. I guess these tests will have some more\nrestructuring when lmdb joins the party.\n\nI imagine we could have something like \"ref_expect_success\n[backend,[backend..]] <title> <body>\", which makes it easier to\nexercise a new backend with the same test, or we could add\nbackend-specific tests as well. Not sure how to do it yet (the devil\nwill be in the body, I think, like dealing with \"git -C sub\" for\nsubmodules). Probably won't do in this series anyway.\n\n>> +test_expect_success 'rename_refs(master, new-master)' '\n>> +     git rev-parse master >expected &&\n>> +     $RUN rename-ref refs/heads/master refs/heads/new-master &&\n>> +     git rev-parse new-master >actual &&\n>> +     test_cmp expected actual &&\n>> +     test_commit recreate-master\n>> +'\n>\n> Isn't HEAD set to `refs/heads/master` prior to this test? If so, then I\n> think it should become detached when you rename `refs/heads/master`. Or\n> maybe it should be changed to point at `refs/heads/new-master`, I can't\n> remember. In either case, it might be useful for the test to check that\n> the behavior matches the status quo, so we notice if the behavior ever\n> changes inadvertently.\n\nYou had me worried a bit there, that I broke something. Yes we rename\nHEAD too. No it's not the backend's job. It's done separately by\nbuiltin/branch.c. Probably a good thing because I don't the backend\nshould know about HEAD's special semantics. Front-end might though.\n\n>> +test_expect_success 'delete_ref(refs/heads/foo)' '\n>> +     SHA1=`git rev-parse foo` &&\n>> +     git checkout --detach &&\n>> +     $RUN delete-ref refs/heads/foo $SHA1 0 &&\n>> +     test_must_fail git rev-parse refs/heads/foo --\n>> +'\n>\n> The last two tests have the same name.\n>\n> You might also want to test these functions when the old oid is\n> incorrect or when the reference is missing.\n\nI will. But you probably noticed that I didn't cover refs behavior\nextensively. I don't know this area well enough to write a conformant\ntest suite. But I think that's ok. We have something to start\nimproving now.\n-- \nDuy\n"},{"id":"313637","messageId":"984307d7-8e1f-fa7c-3d6d-6440954334c3@alum.mit.edu","threadId":"45126","inReplyTo":"20170222140450.30886-8-pclouds@gmail.com","subject":"Re: [PATCH v5 07/24] files-backend: add and use files_refname_path()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-09T12:24:36Z","receivedAt":"2017-03-09T12:25:24Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/22/2017 03:04 PM, Nguyễn Thái Ngọc Duy wrote:\n> Keep repo-related path handling in one place. This will make it easier\n> to add submodule/multiworktree support later.\n> \n> This automatically adds the \"if submodule then use the submodule version\n> of git_path\" to other call sites too. But it does not mean those\n> operations are sumodule-ready. Not yet.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 45 +++++++++++++++++++++++++--------------------\n>  1 file changed, 25 insertions(+), 20 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 7b4ea4c56..72f4e1746 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> [...]\n> @@ -1251,10 +1263,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n>  \tsize_t path_baselen;\n>  \tint err = 0;\n>  \n> -\tif (refs->submodule)\n> -\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n> -\telse\n> -\t\tstrbuf_git_path(&path, \"%s\", dirname);\n> +\tfiles_refname_path(refs, &path, dirname);\n>  \tpath_baselen = path.len;\n>  \n>  \tif (err) {\n\nI just noticed another thing. After this change, `err` is never set, so\nthe `if (err)` block (and `err` itself) can be deleted.\n\n> [...]\n\nMichael\n\n"},{"id":"314437","messageId":"20170318020337.22767-8-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 07/27] files-backend: move \"logs/\" out of TMP_RENAMED_LOG","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:17Z","receivedAt":"2017-03-18T02:05:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes reflog path building consistent, always in the form of\n\n    strbuf_git_path(sb, \"logs/%s\", refname);\n\nIt reduces the mental workload a bit in the next patch when that\nfunction call is converted.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 54e698dcea..6a84e56db8 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2513,7 +2513,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n  * IOW, to avoid cross device rename errors, the temporary renamed log must\n  * live into logs/refs.\n  */\n-#define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n+#define TMP_RENAMED_LOG  \"refs/.tmp-renamed-log\"\n \n struct rename_cb {\n \tconst char *tmp_renamed_log;\n@@ -2549,7 +2549,7 @@ static int rename_tmp_log(const char *newrefname)\n \tint ret;\n \n \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp, TMP_RENAMED_LOG);\n+\tstrbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n \tcb.tmp_renamed_log = tmp.buf;\n \tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n@@ -2611,7 +2611,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \n \tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n \tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n \n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tif (log && S_ISLNK(loginfo.st_mode)) {\n@@ -2636,7 +2636,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t}\n \n \tif (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {\n-\t\tret = error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n+\t\tret = error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\t    oldrefname, strerror(errno));\n \t\tgoto out;\n \t}\n@@ -2722,7 +2722,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\toldrefname, newrefname, strerror(errno));\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n-\t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n+\t\terror(\"unable to restore logfile %s from logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \tret = 1;\n  out:\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314438","messageId":"20170318020337.22767-6-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 05/27] files-backend: make sure files_rename_ref() always reach the end","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:15Z","receivedAt":"2017-03-18T02:05:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a no-op patch. It prepares the function so that we can release\nresources (to be added later in this function) before we return.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 42 ++++++++++++++++++++++++++++--------------\n 1 file changed, 28 insertions(+), 14 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 6e6962151a..6282e5868f 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2588,23 +2588,34 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tstruct stat loginfo;\n \tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n \tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n \n-\tif (log && S_ISLNK(loginfo.st_mode))\n-\t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n+\tif (log && S_ISLNK(loginfo.st_mode)) {\n+\t\tret = error(\"reflog for %s is a symlink\", oldrefname);\n+\t\tgoto out;\n+\t}\n \n \tif (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n-\t\t\t\torig_sha1, &flag))\n-\t\treturn error(\"refname %s not found\", oldrefname);\n+\t\t\t\torig_sha1, &flag)) {\n+\t\tret = error(\"refname %s not found\", oldrefname);\n+\t\tgoto out;\n+\t}\n \n-\tif (flag & REF_ISSYMREF)\n-\t\treturn error(\"refname %s is a symbolic ref, renaming it is not supported\",\n-\t\t\toldrefname);\n-\tif (!rename_ref_available(oldrefname, newrefname))\n-\t\treturn 1;\n+\tif (flag & REF_ISSYMREF) {\n+\t\tret = error(\"refname %s is a symbolic ref, renaming it is not supported\",\n+\t\t\t    oldrefname);\n+\t\tgoto out;\n+\t}\n+\tif (!rename_ref_available(oldrefname, newrefname)) {\n+\t\tret = 1;\n+\t\tgoto out;\n+\t}\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n-\t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n-\t\t\toldrefname, strerror(errno));\n+\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG))) {\n+\t\tret = error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n+\t\t\t    oldrefname, strerror(errno));\n+\t\tgoto out;\n+\t}\n \n \tif (delete_ref(logmsg, oldrefname, orig_sha1, REF_NODEREF)) {\n \t\terror(\"unable to delete old %s\", oldrefname);\n@@ -2660,7 +2671,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto rollback;\n \t}\n \n-\treturn 0;\n+\tret = 0;\n+\tgoto out;\n \n  rollback:\n \tlock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,\n@@ -2689,7 +2701,9 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n-\treturn 1;\n+\tret = 1;\n+ out:\n+\treturn ret;\n }\n \n static int close_ref(struct ref_lock *lock)\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314439","messageId":"20170318020337.22767-11-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 10/27] files-backend: remove the use of git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:20Z","receivedAt":"2017-03-18T02:05:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\ndeciding what goes where (*). The end goal is to pass $GIT_DIR only. A\nrefs \"view\" of a linked worktree is a logical ref store that combines\ntwo files backends together.\n\n(*) Not entirely true since strbuf_git_path_submodule() still does path\ntranslation underneath. But that's for another patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 43 ++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 38 insertions(+), 5 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 1a13fb5e2b..43d8ddac93 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -923,7 +923,8 @@ struct files_ref_store {\n \t * store:\n \t */\n \tconst char *submodule;\n-\n+\tchar *gitdir;\n+\tchar *gitcommondir;\n \tchar *packed_refs_path;\n \n \tstruct ref_entry *loose;\n@@ -985,6 +986,8 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *gitdir = get_git_dir();\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n@@ -995,7 +998,11 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \t\treturn ref_store;\n \t}\n \n-\trefs->packed_refs_path = git_pathdup(\"packed-refs\");\n+\trefs->gitdir = xstrdup(gitdir);\n+\tget_common_dir_noenv(&sb, gitdir);\n+\trefs->gitcommondir = strbuf_detach(&sb, NULL);\n+\tstrbuf_addf(&sb, \"%s/packed-refs\", refs->gitcommondir);\n+\trefs->packed_refs_path = strbuf_detach(&sb, NULL);\n \n \treturn ref_store;\n }\n@@ -1173,11 +1180,26 @@ static void files_reflog_path(struct files_ref_store *refs,\n \t\t\t      const char *refname)\n {\n \tif (!refname) {\n-\t\tstrbuf_git_path(sb, \"logs\");\n+\t\t/*\n+\t\t * FIXME: of course this is wrong in multi worktree\n+\t\t * setting. To be fixed real soon.\n+\t\t */\n+\t\tstrbuf_addf(sb, \"%s/logs\", refs->gitcommondir);\n \t\treturn;\n \t}\n \n-\tstrbuf_git_path(sb, \"logs/%s\", refname);\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_PER_WORKTREE:\n+\tcase REF_TYPE_PSEUDOREF:\n+\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n+\t\tbreak;\n+\tcase REF_TYPE_NORMAL:\n+\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n+\t\t    ref_type(refname), refname);\n+\t}\n }\n \n static void files_refname_path(struct files_ref_store *refs,\n@@ -1189,7 +1211,18 @@ static void files_refname_path(struct files_ref_store *refs,\n \t\treturn;\n \t}\n \n-\tstrbuf_git_path(sb, \"%s\", refname);\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_PER_WORKTREE:\n+\tcase REF_TYPE_PSEUDOREF:\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n+\t\tbreak;\n+\tcase REF_TYPE_NORMAL:\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n+\t\t    ref_type(refname), refname);\n+\t}\n }\n \n /*\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314440","messageId":"20170318020337.22767-10-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 09/27] files-backend: add and use files_refname_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:19Z","receivedAt":"2017-03-18T02:05:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nThis automatically adds the \"if submodule then use the submodule version\nof git_path\" to other call sites too. But it does not mean those\noperations are submodule-ready. Not yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 47 +++++++++++++++++++++++------------------------\n 1 file changed, 23 insertions(+), 24 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 64d1ab3fe8..1a13fb5e2b 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1180,6 +1180,18 @@ static void files_reflog_path(struct files_ref_store *refs,\n \tstrbuf_git_path(sb, \"logs/%s\", refname);\n }\n \n+static void files_refname_path(struct files_ref_store *refs,\n+\t\t\t       struct strbuf *sb,\n+\t\t\t       const char *refname)\n+{\n+\tif (refs->submodule) {\n+\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_git_path(sb, \"%s\", refname);\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n@@ -1249,19 +1261,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tstruct strbuf refname;\n \tstruct strbuf path = STRBUF_INIT;\n \tsize_t path_baselen;\n-\tint err = 0;\n \n-\tif (refs->submodule)\n-\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n-\telse\n-\t\tstrbuf_git_path(&path, \"%s\", dirname);\n+\tfiles_refname_path(refs, &path, dirname);\n \tpath_baselen = path.len;\n \n-\tif (err) {\n-\t\tstrbuf_release(&path);\n-\t\treturn;\n-\t}\n-\n \td = opendir(path.buf);\n \tif (!d) {\n \t\tstrbuf_release(&path);\n@@ -1396,10 +1399,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(&sb_path, refs->submodule, \"%s\", refname);\n-\telse\n-\t\tstrbuf_git_path(&sb_path, \"%s\", refname);\n+\tfiles_refname_path(refs, &sb_path, refname);\n \n \tpath = sb_path.buf;\n \n@@ -1587,7 +1587,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_refname_path(refs, &ref_file, refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -2059,7 +2059,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_refname_path(refs, &ref_file, refname);\n \tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\tlock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n@@ -2358,7 +2358,7 @@ static void try_remove_empty_parents(struct files_ref_store *refs,\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tfiles_refname_path(refs, &sb, buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n@@ -2675,7 +2675,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tstrbuf_git_path(&path, \"%s\", newrefname);\n+\t\t\tfiles_refname_path(refs, &path, newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -3909,7 +3909,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n \t\t\t\tstrbuf_reset(&sb);\n-\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tfiles_refname_path(refs, &sb, lock->ref_name);\n \t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n@@ -4209,19 +4209,18 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"init_db\");\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tfiles_refname_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \n \tstrbuf_reset(&sb);\n-\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tfiles_refname_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \n \tstrbuf_release(&sb);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314441","messageId":"20170318020337.22767-7-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 06/27] files-backend: convert git_path() to strbuf_git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:16Z","receivedAt":"2017-03-18T02:05:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git_path() and friends are going to be killed in files-backend.c in near\nfuture. And because there's a risk with overwriting buffer in\ngit_path(), let's convert them all to strbuf_git_path(). We'll have\neasier time killing/converting strbuf_git_path() then because we won't\nhave to worry about memory management again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 130 ++++++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 97 insertions(+), 33 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 6282e5868f..54e698dcea 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2319,6 +2319,7 @@ enum {\n static void try_remove_empty_parents(const char *refname, unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \tchar *p, *q;\n \tint i;\n \n@@ -2340,14 +2341,19 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) &&\n-\t\t    rmdir(git_path(\"%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&\n-\t\t    rmdir(git_path(\"logs/%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n \tstrbuf_release(&buf);\n+\tstrbuf_release(&sb);\n }\n \n /* make sure nobody touched the ref, and unlink */\n@@ -2509,11 +2515,16 @@ static int files_delete_refs(struct ref_store *ref_store,\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-static int rename_tmp_log_callback(const char *path, void *cb)\n+struct rename_cb {\n+\tconst char *tmp_renamed_log;\n+\tint true_errno;\n+};\n+\n+static int rename_tmp_log_callback(const char *path, void *cb_data)\n {\n-\tint *true_errno = cb;\n+\tstruct rename_cb *cb = cb_data;\n \n-\tif (rename(git_path(TMP_RENAMED_LOG), path)) {\n+\tif (rename(cb->tmp_renamed_log, path)) {\n \t\t/*\n \t\t * rename(a, b) when b is an existing directory ought\n \t\t * to result in ISDIR, but Solaris 5.8 gives ENOTDIR.\n@@ -2521,7 +2532,7 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \t\t * but report EISDIR to raceproof_create_file() so\n \t\t * that it knows to retry.\n \t\t */\n-\t\t*true_errno = errno;\n+\t\tcb->true_errno = errno;\n \t\tif (errno == ENOTDIR)\n \t\t\terrno = EISDIR;\n \t\treturn -1;\n@@ -2532,20 +2543,26 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \n static int rename_tmp_log(const char *newrefname)\n {\n-\tchar *path = git_pathdup(\"logs/%s\", newrefname);\n-\tint ret, true_errno;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tstruct rename_cb cb;\n+\tint ret;\n \n-\tret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);\n+\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&tmp, TMP_RENAMED_LOG);\n+\tcb.tmp_renamed_log = tmp.buf;\n+\tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n \t\tif (errno == EISDIR)\n-\t\t\terror(\"directory not empty: %s\", path);\n+\t\t\terror(\"directory not empty: %s\", path.buf);\n \t\telse\n \t\t\terror(\"unable to move logfile %s to %s: %s\",\n-\t\t\t      git_path(TMP_RENAMED_LOG), path,\n-\t\t\t      strerror(true_errno));\n+\t\t\t      tmp.buf, path.buf,\n+\t\t\t      strerror(cb.true_errno));\n \t}\n \n-\tfree(path);\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&tmp);\n \treturn ret;\n }\n \n@@ -2586,10 +2603,17 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n+\tstruct strbuf sb_oldref = STRBUF_INIT;\n+\tstruct strbuf sb_newref = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n+\tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n-\tint ret;\n \n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\n+\tlog = !lstat(sb_oldref.buf, &loginfo);\n \tif (log && S_ISLNK(loginfo.st_mode)) {\n \t\tret = error(\"reflog for %s is a symlink\", oldrefname);\n \t\tgoto out;\n@@ -2611,7 +2635,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG))) {\n+\tif (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {\n \t\tret = error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n \t\t\t    oldrefname, strerror(errno));\n \t\tgoto out;\n@@ -2693,16 +2717,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n+\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n+\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n-\n \tret = 1;\n  out:\n+\tstrbuf_release(&sb_newref);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n+\n \treturn ret;\n }\n \n@@ -2875,18 +2902,24 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \tresult = log_ref_write_fd(logfd, old_sha1, new_sha1,\n \t\t\t\t  git_committer_info(0), msg);\n \tif (result) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\tclose(logfd);\n \t\treturn -1;\n \t}\n \tif (close(logfd)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \treturn 0;\n@@ -3106,22 +3139,32 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n+\tint ret;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \n-\treturn !lstat(git_path(\"logs/%s\", refname), &st) &&\n-\t\tS_ISREG(st.st_mode);\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \n-\treturn remove_path(git_path(\"logs/%s\", refname));\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = remove_path(sb.buf);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)\n@@ -3177,7 +3220,9 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3283,7 +3328,9 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3365,12 +3412,15 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n {\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\titer->dir_iterator = dir_iterator_begin(git_path(\"logs\"));\n+\tstrbuf_git_path(&sb, \"logs\");\n+\titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n \n@@ -3708,6 +3758,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tchar *head_ref = NULL;\n \tint head_type;\n \tstruct object_id head_oid;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tassert(err);\n \n@@ -3829,7 +3880,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\tif (!(update->type & REF_ISPACKED) ||\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n-\t\t\t\tif (unlink_or_msg(git_path(\"%s\", lock->ref_name), err)) {\n+\t\t\t\tstrbuf_reset(&sb);\n+\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n \t\t\t\t}\n@@ -3849,7 +3902,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t/* Delete the reflogs of any references that were deleted: */\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n-\t\tif (!unlink_or_warn(git_path(\"logs/%s\", ref_to_delete->string)))\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tif (!unlink_or_warn(sb.buf))\n \t\t\ttry_remove_empty_parents(ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n \t}\n@@ -3857,6 +3912,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tclear_loose_ref_cache(refs);\n \n cleanup:\n+\tstrbuf_release(&sb);\n \ttransaction->state = REF_TRANSACTION_CLOSED;\n \n \tfor (i = 0; i < transaction->nr; i++) {\n@@ -4123,14 +4179,22 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"init_db\");\n \n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tsafe_create_dir(sb.buf, 1);\n+\n+\tstrbuf_reset(&sb);\n+\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tsafe_create_dir(sb.buf, 1);\n+\n+\tstrbuf_release(&sb);\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314442","messageId":"20170318020337.22767-16-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 15/27] refs.c: make get_main_ref_store() public and use it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:25Z","receivedAt":"2017-03-18T02:05:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"get_ref_store() will soon be renamed to get_submodule_ref_store().\nTogether with future get_worktree_ref_store(), the three functions\nprovide an appropriate ref store for different operation modes. New APIs\nwill be added to operate directly on ref stores.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 36 ++++++++++++++++++------------------\n refs.h               |  3 +++\n refs/files-backend.c |  2 +-\n 3 files changed, 22 insertions(+), 19 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 11c460c5e9..404d3b62b5 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1314,7 +1314,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n /* backend functions */\n int refs_init_db(struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->init_db(refs, err);\n }\n@@ -1322,7 +1322,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_ref_store(NULL), refname,\n+\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1428,7 +1428,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n-static struct ref_store *get_main_ref_store(void)\n+struct ref_store *get_main_ref_store(void)\n {\n \tstruct ref_store *refs;\n \n@@ -1491,14 +1491,14 @@ void base_ref_store_init(struct ref_store *refs,\n /* backend functions */\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->pack_refs(refs, flags);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->peel_ref(refs, refname, sha1);\n }\n@@ -1506,7 +1506,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n \t\t\t\t       logmsg);\n@@ -1515,7 +1515,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1525,14 +1525,14 @@ int verify_refname_available(const char *refname,\n \t\t\t     const struct string_list *skip,\n \t\t\t     struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1543,7 +1543,7 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n \t\t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n@@ -1552,14 +1552,14 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_exists(refs, refname);\n }\n@@ -1567,14 +1567,14 @@ int reflog_exists(const char *refname)\n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_reflog(refs, refname);\n }\n@@ -1586,7 +1586,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n \t\t\t\t       prepare_fn, should_prune_fn,\n@@ -1596,21 +1596,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\ndiff --git a/refs.h b/refs.h\nindex 2d6b6263fc..a6cd12267f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -2,6 +2,7 @@\n #define REFS_H\n \n struct object_id;\n+struct ref_store;\n struct strbuf;\n struct string_list;\n \n@@ -560,4 +561,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \n int ref_storage_backend_exists(const char *name);\n \n+struct ref_store *get_main_ref_store(void);\n+\n #endif /* REFS_H */\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 43d8ddac93..351934d36e 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3166,7 +3166,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(), 0, \"set_head_symref\");\n \n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314443","messageId":"20170318020337.22767-9-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 08/27] files-backend: add and use files_reflog_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:18Z","receivedAt":"2017-03-18T02:05:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 142 +++++++++++++++++++++++++++++++--------------------\n 1 file changed, 86 insertions(+), 56 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 6a84e56db8..64d1ab3fe8 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,7 +165,8 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n@@ -1167,6 +1168,18 @@ static const char *files_packed_refs_path(struct files_ref_store *refs)\n \treturn refs->packed_refs_path;\n }\n \n+static void files_reflog_path(struct files_ref_store *refs,\n+\t\t\t      struct strbuf *sb,\n+\t\t\t      const char *refname)\n+{\n+\tif (!refname) {\n+\t\tstrbuf_git_path(sb, \"logs\");\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_git_path(sb, \"logs/%s\", refname);\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n@@ -2316,7 +2329,9 @@ enum {\n  * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or\n  * REMOVE_EMPTY_PARENTS_REFLOG.\n  */\n-static void try_remove_empty_parents(const char *refname, unsigned int flags)\n+static void try_remove_empty_parents(struct files_ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -2348,7 +2363,7 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tfiles_reflog_path(refs, &sb, buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n@@ -2541,15 +2556,15 @@ static int rename_tmp_log_callback(const char *path, void *cb_data)\n \t}\n }\n \n-static int rename_tmp_log(const char *newrefname)\n+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n {\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf tmp = STRBUF_INIT;\n \tstruct rename_cb cb;\n \tint ret;\n \n-\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n+\tfiles_reflog_path(refs, &path, newrefname);\n+\tfiles_reflog_path(refs, &tmp, TMP_RENAMED_LOG);\n \tcb.tmp_renamed_log = tmp.buf;\n \tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n@@ -2609,9 +2624,9 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n+\tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n+\tfiles_reflog_path(refs, &sb_newref, newrefname);\n+\tfiles_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tif (log && S_ISLNK(loginfo.st_mode)) {\n@@ -2674,7 +2689,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t}\n \t}\n \n-\tif (log && rename_tmp_log(newrefname))\n+\tif (log && rename_tmp_log(refs, newrefname))\n \t\tgoto rollback;\n \n \tlogmoved = log;\n@@ -2789,10 +2804,15 @@ static int open_or_create_logfile(const char *path, void *cb)\n  * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and\n  * return -1.\n  */\n-static int log_ref_setup(const char *refname, int force_create,\n+static int log_ref_setup(struct files_ref_store *refs,\n+\t\t\t const char *refname, int force_create,\n \t\t\t int *logfd, struct strbuf *err)\n {\n-\tchar *logfile = git_pathdup(\"logs/%s\", refname);\n+\tstruct strbuf logfile_sb = STRBUF_INIT;\n+\tchar *logfile;\n+\n+\tfiles_reflog_path(refs, &logfile_sb, refname);\n+\tlogfile = strbuf_detach(&logfile_sb, NULL);\n \n \tif (force_create || should_autocreate_reflog(refname)) {\n \t\tif (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {\n@@ -2842,12 +2862,11 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname, int force_create,\n \t\t\t       struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n \tint fd;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"create_reflog\");\n-\n-\tif (log_ref_setup(refname, force_create, &fd, err))\n+\tif (log_ref_setup(refs, refname, force_create, &fd, err))\n \t\treturn -1;\n \n \tif (fd >= 0)\n@@ -2882,7 +2901,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\tconst char *refname, const unsigned char *old_sha1,\n \t\t\tconst unsigned char *new_sha1, const char *msg,\n \t\t\tint flags, struct strbuf *err)\n {\n@@ -2891,7 +2911,8 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \tif (log_all_ref_updates == LOG_REFS_UNSET)\n \t\tlog_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;\n \n-\tresult = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,\n+\tresult = log_ref_setup(refs, refname,\n+\t\t\t       flags & REF_FORCE_CREATE_REFLOG,\n \t\t\t       &logfd, err);\n \n \tif (result)\n@@ -2905,7 +2926,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_reflog_path(refs, &sb, refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2916,7 +2937,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_reflog_path(refs, &sb, refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2977,7 +2998,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \tfiles_assert_main_repository(refs, \"commit_ref_update\");\n \n \tclear_loose_ref_cache(refs);\n-\tif (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,\n+\tif (files_log_ref_write(refs, lock->ref_name,\n+\t\t\t\tlock->old_oid.hash, sha1,\n \t\t\t\tlogmsg, 0, err)) {\n \t\tchar *old_msg = strbuf_detach(err, NULL);\n \t\tstrbuf_addf(err, \"cannot update the ref '%s': %s\",\n@@ -3009,8 +3031,9 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n-\t\t\tif (files_log_ref_write(\"HEAD\", lock->old_oid.hash, sha1,\n-\t\t\t\t\t  logmsg, 0, &log_err)) {\n+\t\t\tif (files_log_ref_write(refs, \"HEAD\",\n+\t\t\t\t\t\tlock->old_oid.hash, sha1,\n+\t\t\t\t\t\tlogmsg, 0, &log_err)) {\n \t\t\t\terror(\"%s\", log_err.buf);\n \t\t\t\tstrbuf_release(&log_err);\n \t\t\t}\n@@ -3042,24 +3065,26 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)\n \treturn ret;\n }\n \n-static void update_symref_reflog(struct ref_lock *lock, const char *refname,\n+static void update_symref_reflog(struct files_ref_store *refs,\n+\t\t\t\t struct ref_lock *lock, const char *refname,\n \t\t\t\t const char *target, const char *logmsg)\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n \tif (logmsg && !read_ref(target, new_sha1) &&\n-\t    files_log_ref_write(refname, lock->old_oid.hash, new_sha1,\n-\t\t\t\tlogmsg, 0, &err)) {\n+\t    files_log_ref_write(refs, refname, lock->old_oid.hash,\n+\t\t\t\tnew_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t}\n }\n \n-static int create_symref_locked(struct ref_lock *lock, const char *refname,\n+static int create_symref_locked(struct files_ref_store *refs,\n+\t\t\t\tstruct ref_lock *lock, const char *refname,\n \t\t\t\tconst char *target, const char *logmsg)\n {\n \tif (prefer_symlink_refs && !create_ref_symlink(lock, target)) {\n-\t\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\t\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \t\treturn 0;\n \t}\n \n@@ -3067,7 +3092,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,\n \t\treturn error(\"unable to fdopen %s: %s\",\n \t\t\t     lock->lk->tempfile.filename.buf, strerror(errno));\n \n-\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \n \t/* no error check; commit_ref will check ferror */\n \tfprintf(lock->lk->tempfile.fp, \"ref: %s\\n\", target);\n@@ -3096,13 +3121,20 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\treturn -1;\n \t}\n \n-\tret = create_symref_locked(lock, refname, target, logmsg);\n+\tret = create_symref_locked(refs, lock, refname, target, logmsg);\n \tunlock_ref(lock);\n \treturn ret;\n }\n \n int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)\n {\n+\t/*\n+\t * FIXME: this obviously will not work well for future refs\n+\t * backends. This function needs to die.\n+\t */\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3129,7 +3161,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n \tlock->lk = &head_lock;\n \tlock->ref_name = xstrdup(head_rel);\n \n-\tret = create_symref_locked(lock, head_rel, target, logmsg);\n+\tret = create_symref_locked(refs, lock, head_rel, target, logmsg);\n \n \tunlock_ref(lock); /* will free lock */\n \tstrbuf_release(&head_path);\n@@ -3139,14 +3171,13 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3155,13 +3186,12 @@ static int files_reflog_exists(struct ref_store *ref_store,\n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tret = remove_path(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3212,15 +3242,14 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     each_reflog_ent_fn fn,\n \t\t\t\t\t     void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n \tint ret = 0, at_tail = 1;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3321,14 +3350,13 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     const char *refname,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3410,15 +3438,14 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n \n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n-\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\tstrbuf_git_path(&sb, \"logs\");\n+\tfiles_reflog_path(refs, &sb, NULL);\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n@@ -3843,7 +3870,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t\tif (update->flags & REF_NEEDS_COMMIT ||\n \t\t    update->flags & REF_LOG_ONLY) {\n-\t\t\tif (files_log_ref_write(lock->ref_name,\n+\t\t\tif (files_log_ref_write(refs,\n+\t\t\t\t\t\tlock->ref_name,\n \t\t\t\t\t\tlock->old_oid.hash,\n \t\t\t\t\t\tupdate->new_sha1,\n \t\t\t\t\t\tupdate->msg, update->flags,\n@@ -3903,9 +3931,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t/* Delete the reflogs of any references that were deleted: */\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tfiles_reflog_path(refs, &sb, ref_to_delete->string);\n \t\tif (!unlink_or_warn(sb.buf))\n-\t\t\ttry_remove_empty_parents(ref_to_delete->string,\n+\t\t\ttry_remove_empty_parents(refs, ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n \t}\n \n@@ -3929,7 +3957,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t * can only work because we have already\n \t\t\t * removed the lockfile.)\n \t\t\t */\n-\t\t\ttry_remove_empty_parents(update->refname,\n+\t\t\ttry_remove_empty_parents(refs, update->refname,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REF);\n \t\t}\n \t}\n@@ -4080,6 +4108,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n+\tstruct strbuf log_file_sb = STRBUF_INIT;\n \tchar *log_file;\n \tint status = 0;\n \tint type;\n@@ -4108,7 +4137,8 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tlog_file = git_pathdup(\"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &log_file_sb, refname);\n+\tlog_file = strbuf_detach(&log_file_sb, NULL);\n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n \t\t/*\n \t\t * Even though holding $GIT_DIR/logs/$reflog.lock has\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314444","messageId":"20170318020337.22767-13-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 12/27] refs: rename lookup_ref_store() to lookup_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:22Z","receivedAt":"2017-03-18T02:05:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With get_main_ref_store() being used inside get_ref_store(),\nlookup_ref_store() is only used for submodule code path. Rename to\nreflect that and delete dead code.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 13 ++++---------\n 1 file changed, 4 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2637353b72..818d9f0ce9 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1395,17 +1395,13 @@ static struct ref_store *main_ref_store;\n static struct hashmap submodule_ref_stores;\n \n /*\n- * Return the ref_store instance for the specified submodule (or the\n- * main repository if submodule is NULL). If that ref_store hasn't\n- * been initialized yet, return NULL.\n+ * Return the ref_store instance for the specified submodule. If that\n+ * ref_store hasn't been initialized yet, return NULL.\n  */\n-static struct ref_store *lookup_ref_store(const char *submodule)\n+static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n {\n \tstruct submodule_hash_entry *entry;\n \n-\tif (!submodule)\n-\t\treturn main_ref_store;\n-\n \tif (!submodule_ref_stores.tablesize)\n \t\t/* It's initialized on demand in register_ref_store(). */\n \t\treturn NULL;\n@@ -1474,7 +1470,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n \t} else {\n-\t\trefs = lookup_ref_store(submodule);\n+\t\trefs = lookup_submodule_ref_store(submodule);\n \n \t\tif (!refs) {\n \t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1485,7 +1481,6 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\t\tstrbuf_release(&submodule_sb);\n \t\t}\n \t}\n-\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314445","messageId":"20170318020337.22767-12-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 11/27] refs.c: introduce get_main_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:21Z","receivedAt":"2017-03-18T02:06:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex e7606716dd..2637353b72 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1456,15 +1456,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n+static struct ref_store *get_main_ref_store(void)\n+{\n+\tstruct ref_store *refs;\n+\n+\tif (main_ref_store)\n+\t\treturn main_ref_store;\n+\n+\trefs = ref_store_init(NULL);\n+\treturn refs;\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n-\t\trefs = lookup_ref_store(NULL);\n-\n-\t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL);\n+\t\treturn get_main_ref_store();\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314446","messageId":"20170318020337.22767-15-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 14/27] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:24Z","receivedAt":"2017-03-18T02:06:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the last function in this code (besides public API) that takes\nsubmodule argument and handles both main/submodule cases. Break it down,\nmove main store registration in get_main_ref_store() and keep the rest\nin register_submodule_ref_store().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 43 +++++++++++++++++++------------------------\n 1 file changed, 19 insertions(+), 24 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f458423301..11c460c5e9 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1412,29 +1412,6 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n }\n \n /*\n- * Register the specified ref_store to be the one that should be used\n- * for submodule (or the main repository if submodule is NULL). It is\n- * a fatal error to call this function twice for the same submodule.\n- */\n-static void register_ref_store(struct ref_store *refs, const char *submodule)\n-{\n-\tif (!submodule) {\n-\t\tif (main_ref_store)\n-\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n-\n-\t\tmain_ref_store = refs;\n-\t} else {\n-\t\tif (!submodule_ref_stores.tablesize)\n-\t\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n-\n-\t\tif (hashmap_put(&submodule_ref_stores,\n-\t\t\t\talloc_submodule_hash_entry(submodule, refs)))\n-\t\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n-\t\t\t    submodule);\n-\t}\n-}\n-\n-/*\n  * Create, record, and return a ref_store instance for the specified\n  * submodule (or the main repository if submodule is NULL).\n  */\n@@ -1448,7 +1425,6 @@ static struct ref_store *ref_store_init(const char *submodule)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n \trefs = be->init(submodule);\n-\tregister_ref_store(refs, submodule);\n \treturn refs;\n }\n \n@@ -1460,9 +1436,27 @@ static struct ref_store *get_main_ref_store(void)\n \t\treturn main_ref_store;\n \n \trefs = ref_store_init(NULL);\n+\tmain_ref_store = refs;\n \treturn refs;\n }\n \n+/*\n+ * Register the specified ref_store to be the one that should be used\n+ * for submodule. It is a fatal error to call this function twice for\n+ * the same submodule.\n+ */\n+static void register_submodule_ref_store(struct ref_store *refs,\n+\t\t\t\t\t const char *submodule)\n+{\n+\tif (!submodule_ref_stores.tablesize)\n+\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n+\n+\tif (hashmap_put(&submodule_ref_stores,\n+\t\t\talloc_submodule_hash_entry(submodule, refs)))\n+\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n+\t\t    submodule);\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1484,6 +1478,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn NULL;\n \n \trefs = ref_store_init(submodule);\n+\tregister_submodule_ref_store(refs, submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314447","messageId":"20170318020337.22767-14-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 13/27] refs.c: flatten get_ref_store() a bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:23Z","receivedAt":"2017-03-18T02:06:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This helps the future changes in this code. And because get_ref_store()\nis destined to become get_submodule_ref_store(), the \"get main store\"\ncode path will be removed eventually. After this the patch to delete\nthat code will be cleaner.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 23 +++++++++++++----------\n 1 file changed, 13 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 818d9f0ce9..f458423301 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1465,22 +1465,25 @@ static struct ref_store *get_main_ref_store(void)\n \n struct ref_store *get_ref_store(const char *submodule)\n {\n+\tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n+\tint ret;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n-\t} else {\n-\t\trefs = lookup_submodule_ref_store(submodule);\n+\t}\n \n-\t\tif (!refs) {\n-\t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n+\trefs = lookup_submodule_ref_store(submodule);\n+\tif (refs)\n+\t\treturn refs;\n \n-\t\t\tstrbuf_addstr(&submodule_sb, submodule);\n-\t\t\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\t\t\trefs = ref_store_init(submodule);\n-\t\t\tstrbuf_release(&submodule_sb);\n-\t\t}\n-\t}\n+\tstrbuf_addstr(&submodule_sb, submodule);\n+\tret = is_nonbare_repository_dir(&submodule_sb);\n+\tstrbuf_release(&submodule_sb);\n+\tif (!ret)\n+\t\treturn NULL;\n+\n+\trefs = ref_store_init(submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314448","messageId":"20170318020337.22767-24-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 23/27] refs: delete pack_refs() in favor of refs_pack_refs()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:33Z","receivedAt":"2017-03-18T02:07:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It only has one caller, not worth keeping just for convenience.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/pack-refs.c | 2 +-\n refs.c              | 5 -----\n refs.h              | 1 -\n 3 files changed, 1 insertion(+), 7 deletions(-)\n\ndiff --git a/builtin/pack-refs.c b/builtin/pack-refs.c\nindex 39f9a55d16..b106a392a4 100644\n--- a/builtin/pack-refs.c\n+++ b/builtin/pack-refs.c\n@@ -17,5 +17,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)\n \t};\n \tif (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))\n \t\tusage_with_options(pack_refs_usage, opts);\n-\treturn pack_refs(flags);\n+\treturn refs_pack_refs(get_main_ref_store(), flags);\n }\ndiff --git a/refs.c b/refs.c\nindex 5ea28ae128..77a39f8b17 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1605,11 +1605,6 @@ int refs_pack_refs(struct ref_store *refs, unsigned int flags)\n \treturn refs->be->pack_refs(refs, flags);\n }\n \n-int pack_refs(unsigned int flags)\n-{\n-\treturn refs_pack_refs(get_main_ref_store(), flags);\n-}\n-\n int refs_peel_ref(struct ref_store *refs, const char *refname,\n \t\t  unsigned char *sha1)\n {\ndiff --git a/refs.h b/refs.h\nindex 37f4aa8bd5..1a07f9d86f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -297,7 +297,6 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,\n  * flags: Combination of the above PACK_REFS_* flags.\n  */\n int refs_pack_refs(struct ref_store *refs, unsigned int flags);\n-int pack_refs(unsigned int flags);\n \n /*\n  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314449","messageId":"20170318020337.22767-18-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 17/27] refs: move submodule code out of files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:27Z","receivedAt":"2017-03-18T02:07:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend is now initialized with a $GIT_DIR. Converting a submodule\npath to where real submodule gitdir is located is done in get_ref_store().\n\nThis gives a slight performance improvement for submodules since we\ndon't convert submodule path to gitdir at every backend call like\nbefore. We pay that once at ref-store creation.\n\nMore cleanup in files_downcast() and files_assert_main_repository()\nfollows shortly. It's separate to keep noises from this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 19 ++++++++++++++-----\n refs/files-backend.c | 24 ++----------------------\n refs/refs-internal.h |  9 ++++-----\n 3 files changed, 20 insertions(+), 32 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 404d3b62b5..fe724869b8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -9,6 +9,7 @@\n #include \"refs/refs-internal.h\"\n #include \"object.h\"\n #include \"tag.h\"\n+#include \"submodule.h\"\n \n /*\n  * List of all available backends\n@@ -1413,9 +1414,9 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n \n /*\n  * Create, record, and return a ref_store instance for the specified\n- * submodule (or the main repository if submodule is NULL).\n+ * gitdir.\n  */\n-static struct ref_store *ref_store_init(const char *submodule)\n+static struct ref_store *ref_store_init(const char *gitdir)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1424,7 +1425,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(submodule);\n+\trefs = be->init(gitdir);\n \treturn refs;\n }\n \n@@ -1435,7 +1436,7 @@ struct ref_store *get_main_ref_store(void)\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\trefs = ref_store_init(NULL);\n+\trefs = ref_store_init(get_git_dir());\n \tmain_ref_store = refs;\n \treturn refs;\n }\n@@ -1477,8 +1478,16 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!ret)\n \t\treturn NULL;\n \n-\trefs = ref_store_init(submodule);\n+\tret = submodule_to_gitdir(&submodule_sb, submodule);\n+\tif (ret) {\n+\t\tstrbuf_release(&submodule_sb);\n+\t\treturn NULL;\n+\t}\n+\n+\trefs = ref_store_init(submodule_sb.buf);\n \tregister_submodule_ref_store(refs, submodule);\n+\n+\tstrbuf_release(&submodule_sb);\n \treturn refs;\n }\n \ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 351934d36e..db335e4ca6 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -917,12 +917,6 @@ struct packed_ref_cache {\n struct files_ref_store {\n \tstruct ref_store base;\n \n-\t/*\n-\t * The name of the submodule represented by this object, or\n-\t * NULL if it represents the main repository's reference\n-\t * store:\n-\t */\n-\tconst char *submodule;\n \tchar *gitdir;\n \tchar *gitcommondir;\n \tchar *packed_refs_path;\n@@ -982,22 +976,14 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *submodule)\n+static struct ref_store *files_ref_store_create(const char *gitdir)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n \tstruct strbuf sb = STRBUF_INIT;\n-\tconst char *gitdir = get_git_dir();\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\tif (submodule) {\n-\t\trefs->submodule = xstrdup(submodule);\n-\t\trefs->packed_refs_path = git_pathdup_submodule(\n-\t\t\trefs->submodule, \"packed-refs\");\n-\t\treturn ref_store;\n-\t}\n-\n \trefs->gitdir = xstrdup(gitdir);\n \tget_common_dir_noenv(&sb, gitdir);\n \trefs->gitcommondir = strbuf_detach(&sb, NULL);\n@@ -1014,8 +1000,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\tif (refs->submodule)\n-\t\tdie(\"BUG: %s called for a submodule\", caller);\n+\t/* This function is to be deleted in the next patch */\n }\n \n /*\n@@ -1206,11 +1191,6 @@ static void files_refname_path(struct files_ref_store *refs,\n \t\t\t       struct strbuf *sb,\n \t\t\t       const char *refname)\n {\n-\tif (refs->submodule) {\n-\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n-\t\treturn;\n-\t}\n-\n \tswitch (ref_type(refname)) {\n \tcase REF_TYPE_PER_WORKTREE:\n \tcase REF_TYPE_PSEUDOREF:\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f732473e1d..dfa1817929 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -482,12 +482,11 @@ struct ref_store;\n /* refs backends */\n \n /*\n- * Initialize the ref_store for the specified submodule, or for the\n- * main repository if submodule == NULL. These functions should call\n- * base_ref_store_init() to initialize the shared part of the\n- * ref_store and to record the ref_store for later lookup.\n+ * Initialize the ref_store for the specified gitdir. These functions\n+ * should call base_ref_store_init() to initialize the shared part of\n+ * the ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *submodule);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314450","messageId":"20170318020337.22767-17-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 16/27] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:26Z","receivedAt":"2017-03-18T02:07:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"refs is learning to avoid path rewriting that is done by\nstrbuf_git_path_submodule(). Factor out this code so it could be reused\nby refs*\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n path.c      | 35 +++++++----------------------------\n submodule.c | 36 ++++++++++++++++++++++++++++++++++++\n submodule.h |  1 +\n 3 files changed, 44 insertions(+), 28 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex efcedafba6..03e7e33b6e 100644\n--- a/path.c\n+++ b/path.c\n@@ -471,39 +471,19 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n }\n \n /* Returns 0 on success, negative on failure. */\n-#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1\n static int do_submodule_path(struct strbuf *buf, const char *path,\n \t\t\t     const char *fmt, va_list args)\n {\n-\tconst char *git_dir;\n \tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n \tstruct strbuf git_submodule_dir = STRBUF_INIT;\n-\tconst struct submodule *sub;\n-\tint err = 0;\n+\tint ret;\n \n-\tstrbuf_addstr(buf, path);\n-\tstrbuf_complete(buf, '/');\n-\tstrbuf_addstr(buf, \".git\");\n-\n-\tgit_dir = read_gitfile(buf->buf);\n-\tif (git_dir) {\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, git_dir);\n-\t}\n-\tif (!is_git_directory(buf->buf)) {\n-\t\tgitmodules_config();\n-\t\tsub = submodule_from_path(null_sha1, path);\n-\t\tif (!sub) {\n-\t\t\terr = SUBMODULE_PATH_ERR_NOT_CONFIGURED;\n-\t\t\tgoto cleanup;\n-\t\t}\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n-\t}\n-\n-\tstrbuf_addch(buf, '/');\n-\tstrbuf_addbuf(&git_submodule_dir, buf);\n+\tret = submodule_to_gitdir(&git_submodule_dir, path);\n+\tif (ret)\n+\t\tgoto cleanup;\n \n+\tstrbuf_complete(&git_submodule_dir, '/');\n+\tstrbuf_addbuf(buf, &git_submodule_dir);\n \tstrbuf_vaddf(buf, fmt, args);\n \n \tif (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))\n@@ -514,8 +494,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,\n cleanup:\n \tstrbuf_release(&git_submodule_dir);\n \tstrbuf_release(&git_submodule_common_dir);\n-\n-\treturn err;\n+\treturn ret;\n }\n \n char *git_pathdup_submodule(const char *path, const char *fmt, ...)\ndiff --git a/submodule.c b/submodule.c\nindex 3200b7bb2b..3c445f274e 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1596,3 +1596,39 @@ const char *get_superproject_working_tree(void)\n \n \treturn ret;\n }\n+\n+/*\n+ * Given a submodule path (as in the index), return the repository\n+ * path of that submodule in 'buf'. Return -1 on error or when the\n+ * submodule is not initialized.\n+ */\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n+{\n+\tconst struct submodule *sub;\n+\tconst char *git_dir;\n+\tint ret = 0;\n+\n+\tstrbuf_reset(buf);\n+\tstrbuf_addstr(buf, submodule);\n+\tstrbuf_complete(buf, '/');\n+\tstrbuf_addstr(buf, \".git\");\n+\n+\tgit_dir = read_gitfile(buf->buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, git_dir);\n+\t}\n+\tif (!is_git_directory(buf->buf)) {\n+\t\tgitmodules_config();\n+\t\tsub = submodule_from_path(null_sha1, submodule);\n+\t\tif (!sub) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n+\t}\n+\n+cleanup:\n+\treturn ret;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex c8a0c9cb29..bddbba7f32 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -81,6 +81,7 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n \t\t\t\t    int dry_run);\n extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n extern int parallel_submodules(void);\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n \n /*\n  * Prepare the \"env_array\" parameter of a \"struct child_process\" for executing\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314451","messageId":"20170318020337.22767-27-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 26/27] t1406: new tests for submodule ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:36Z","receivedAt":"2017-03-18T02:07:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n 1 file changed, 95 insertions(+)\n create mode 100755 t/t1406-submodule-ref-store.sh\n\ndiff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\nnew file mode 100755\nindex 0000000000..22214ebd32\n--- /dev/null\n+++ b/t/t1406-submodule-ref-store.sh\n@@ -0,0 +1,95 @@\n+#!/bin/sh\n+\n+test_description='test submodule ref store api'\n+\n+. ./test-lib.sh\n+\n+RUN=\"test-ref-store submodule:sub\"\n+\n+test_expect_success 'setup' '\n+\tgit init sub &&\n+\t(\n+\t\tcd sub &&\n+\t\ttest_commit first &&\n+\t\tgit checkout -b new-master\n+\t)\n+'\n+\n+test_expect_success 'pack_refs() not allowed' '\n+\ttest_must_fail $RUN pack-refs 3\n+'\n+\n+test_expect_success 'peel_ref(new-tag)' '\n+\tgit -C sub rev-parse HEAD >expected &&\n+\tgit -C sub tag -a -m new-tag new-tag HEAD &&\n+\t$RUN peel-ref refs/tags/new-tag >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create_symref() not allowed' '\n+\ttest_must_fail $RUN create-symref FOO refs/heads/master nothing\n+'\n+\n+test_expect_success 'delete_refs() not allowed' '\n+\ttest_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag\n+'\n+\n+test_expect_success 'rename_refs() not allowed' '\n+\ttest_must_fail $RUN rename-ref refs/heads/master refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_ref(refs/heads/)' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tmaster 0x0\n+\tnew-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'resolve_ref(master)' '\n+\tSHA1=`git -C sub rev-parse master` &&\n+\techo \"$SHA1 refs/heads/master 0x0\" >expected &&\n+\t$RUN resolve-ref refs/heads/master 0 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify_ref(new-master)' '\n+\t$RUN verify-ref refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_reflog()' '\n+\t$RUN for-each-reflog | sort | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tHEAD 0x1\n+\trefs/heads/master 0x0\n+\trefs/heads/new-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'for_each_reflog_ent()' '\n+\t$RUN for-each-reflog-ent HEAD >actual && cat actual &&\n+\thead -n1 actual | grep first &&\n+\ttail -n2 actual | head -n1 | grep master.to.new\n+'\n+\n+test_expect_success 'for_each_reflog_ent_reverse()' '\n+\t$RUN for-each-reflog-ent-reverse HEAD >actual &&\n+\thead -n1 actual | grep master.to.new &&\n+\ttail -n2 actual | head -n1 | grep first\n+'\n+\n+test_expect_success 'reflog_exists(HEAD)' '\n+\t$RUN reflog-exists HEAD\n+'\n+\n+test_expect_success 'delete_reflog() not allowed' '\n+\ttest_must_fail $RUN delete-reflog HEAD\n+'\n+\n+test_expect_success 'create-reflog() not allowed' '\n+\ttest_must_fail $RUN create-reflog HEAD 1\n+'\n+\n+test_done\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314452","messageId":"20170318020337.22767-23-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 22/27] files-backend: avoid ref api targetting main ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:32Z","receivedAt":"2017-03-18T02:07:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A small step towards making files-backend works as a non-main ref store\nusing the newly added store-aware API.\n\nFor the record, `join` and `nm` on refs.o and files-backend.o tell me\nthat files-backend no longer uses functions that defaults to\nget_main_ref_store().\n\nI'm not yet comfortable at the idea of removing\nfiles_assert_main_repository() (or converting REF_STORE_MAIN to\nREF_STORE_WRITE). More staring and testing is required before that can\nhappen. Well, except peel_ref(). I'm pretty sure that function is safe.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 84 ++++++++++++++++++++++++++++++----------------------\n 1 file changed, 49 insertions(+), 35 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 46f791a516..4242486118 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1830,8 +1830,6 @@ static int files_peel_ref(struct ref_store *ref_store,\n \tint flag;\n \tunsigned char base[20];\n \n-\tfiles_assert_main_repository(refs, \"peel_ref\");\n-\n \tif (current_ref_iter && current_ref_iter->refname == refname) {\n \t\tstruct object_id peeled;\n \n@@ -1841,7 +1839,8 @@ static int files_peel_ref(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tif (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))\n+\tif (refs_read_ref_full(ref_store, refname,\n+\t\t\t       RESOLVE_REF_READING, base, &flag))\n \t\treturn -1;\n \n \t/*\n@@ -2011,15 +2010,15 @@ static struct ref_iterator *files_ref_iterator_begin(\n  * on success. On error, write an error message to err, set errno, and\n  * return a negative value.\n  */\n-static int verify_lock(struct ref_lock *lock,\n+static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,\n \t\t       const unsigned char *old_sha1, int mustexist,\n \t\t       struct strbuf *err)\n {\n \tassert(err);\n \n-\tif (read_ref_full(lock->ref_name,\n-\t\t\t  mustexist ? RESOLVE_REF_READING : 0,\n-\t\t\t  lock->old_oid.hash, NULL)) {\n+\tif (refs_read_ref_full(ref_store, lock->ref_name,\n+\t\t\t       mustexist ? RESOLVE_REF_READING : 0,\n+\t\t\t       lock->old_oid.hash, NULL)) {\n \t\tif (old_sha1) {\n \t\t\tint save_errno = errno;\n \t\t\tstrbuf_addf(err, \"can't verify ref '%s'\", lock->ref_name);\n@@ -2088,8 +2087,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n \tfiles_refname_path(refs, &ref_file, refname);\n-\tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\tlock->old_oid.hash, type);\n+\tresolved = !!refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t     refname, resolve_flags,\n+\t\t\t\t\t     lock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n \t\t/*\n \t\t * we are trying to lock foo but we used to\n@@ -2106,8 +2106,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\t\t\t\t    refname);\n \t\t\tgoto error_return;\n \t\t}\n-\t\tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\t\tlock->old_oid.hash, type);\n+\t\tresolved = !!refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t\t     refname, resolve_flags,\n+\t\t\t\t\t\t     lock->old_oid.hash, type);\n \t}\n \tif (!resolved) {\n \t\tlast_errno = errno;\n@@ -2145,7 +2146,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\tgoto error_return;\n \t}\n \n-\tif (verify_lock(lock, old_sha1, mustexist, err)) {\n+\tif (verify_lock(&refs->base, lock, old_sha1, mustexist, err)) {\n \t\tlast_errno = errno;\n \t\tgoto error_return;\n \t}\n@@ -2400,7 +2401,7 @@ static void try_remove_empty_parents(struct files_ref_store *refs,\n }\n \n /* make sure nobody touched the ref, and unlink */\n-static void prune_ref(struct ref_to_prune *r)\n+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -2408,7 +2409,7 @@ static void prune_ref(struct ref_to_prune *r)\n \tif (check_refname_format(r->name, 0))\n \t\treturn;\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = ref_store_transaction_begin(&refs->base, &err);\n \tif (!transaction ||\n \t    ref_transaction_delete(transaction, r->name, r->sha1,\n \t\t\t\t   REF_ISPRUNING | REF_NODEREF, NULL, &err) ||\n@@ -2422,10 +2423,10 @@ static void prune_ref(struct ref_to_prune *r)\n \tstrbuf_release(&err);\n }\n \n-static void prune_refs(struct ref_to_prune *r)\n+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \twhile (r) {\n-\t\tprune_ref(r);\n+\t\tprune_ref(refs, r);\n \t\tr = r->next;\n \t}\n }\n@@ -2449,7 +2450,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n \tif (commit_packed_refs(refs))\n \t\tdie_errno(\"unable to overwrite old ref-pack file\");\n \n-\tprune_refs(cbdata.ref_to_prune);\n+\tprune_refs(refs, cbdata.ref_to_prune);\n \treturn 0;\n }\n \n@@ -2541,7 +2542,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \tfor (i = 0; i < refnames->nr; i++) {\n \t\tconst char *refname = refnames->items[i].string;\n \n-\t\tif (delete_ref(NULL, refname, NULL, flags))\n+\t\tif (refs_delete_ref(&refs->base, NULL, refname, NULL, flags))\n \t\t\tresult |= error(_(\"could not remove reference %s\"), refname);\n \t}\n \n@@ -2663,7 +2664,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\tif (!refs_resolve_ref_unsafe(&refs->base, oldrefname,\n+\t\t\t\t     RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\torig_sha1, &flag)) {\n \t\tret = error(\"refname %s not found\", oldrefname);\n \t\tgoto out;\n@@ -2685,7 +2687,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (delete_ref(logmsg, oldrefname, orig_sha1, REF_NODEREF)) {\n+\tif (refs_delete_ref(&refs->base, logmsg, oldrefname,\n+\t\t\t    orig_sha1, REF_NODEREF)) {\n \t\terror(\"unable to delete old %s\", oldrefname);\n \t\tgoto rollback;\n \t}\n@@ -2697,9 +2700,11 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t * the safety anyway; we want to delete the reference whatever\n \t * its current value.\n \t */\n-\tif (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n-\t\t\t   sha1, NULL) &&\n-\t    delete_ref(NULL, newrefname, NULL, REF_NODEREF)) {\n+\tif (!refs_read_ref_full(&refs->base, newrefname,\n+\t\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\t\t\t\tsha1, NULL) &&\n+\t    refs_delete_ref(&refs->base, NULL, newrefname,\n+\t\t\t    NULL, REF_NODEREF)) {\n \t\tif (errno == EISDIR) {\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n@@ -3055,8 +3060,9 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tint head_flag;\n \t\tconst char *head_ref;\n \n-\t\thead_ref = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n-\t\t\t\t\t      head_sha1, &head_flag);\n+\t\thead_ref = refs_resolve_ref_unsafe(&refs->base, \"HEAD\",\n+\t\t\t\t\t\t   RESOLVE_REF_READING,\n+\t\t\t\t\t\t   head_sha1, &head_flag);\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n@@ -3100,7 +3106,9 @@ static void update_symref_reflog(struct files_ref_store *refs,\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n-\tif (logmsg && !read_ref(target, new_sha1) &&\n+\tif (logmsg &&\n+\t    !refs_read_ref_full(&refs->base, target,\n+\t\t\t\tRESOLVE_REF_READING, new_sha1, NULL) &&\n \t    files_log_ref_write(refs, refname, lock->old_oid.hash,\n \t\t\t\tnew_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n@@ -3405,6 +3413,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n struct files_reflog_iterator {\n \tstruct ref_iterator base;\n \n+\tstruct ref_store *ref_store;\n \tstruct dir_iterator *dir_iterator;\n \tstruct object_id oid;\n };\n@@ -3426,8 +3435,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)\n \t\tif (ends_with(diter->basename, \".lock\"))\n \t\t\tcontinue;\n \n-\t\tif (read_ref_full(diter->relative_path, 0,\n-\t\t\t\t  iter->oid.hash, &flags)) {\n+\t\tif (refs_read_ref_full(iter->ref_store,\n+\t\t\t\t       diter->relative_path, 0,\n+\t\t\t\t       iter->oid.hash, &flags)) {\n \t\t\terror(\"bad ref for %s\", diter->path.buf);\n \t\t\tcontinue;\n \t\t}\n@@ -3481,6 +3491,7 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n \tfiles_reflog_path(refs, &sb, NULL);\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\titer->ref_store = ref_store;\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n@@ -3720,8 +3731,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \t\t\t * the transaction, so we have to read it here\n \t\t\t * to record and possibly check old_sha1:\n \t\t\t */\n-\t\t\tif (read_ref_full(referent.buf, 0,\n-\t\t\t\t\t  lock->old_oid.hash, NULL)) {\n+\t\t\tif (refs_read_ref_full(&refs->base,\n+\t\t\t\t\t       referent.buf, 0,\n+\t\t\t\t\t       lock->old_oid.hash, NULL)) {\n \t\t\t\tif (update->flags & REF_HAVE_OLD) {\n \t\t\t\t\tstrbuf_addf(err, \"cannot lock ref '%s': \"\n \t\t\t\t\t\t    \"error reading reference\",\n@@ -3875,8 +3887,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t * head_ref within the transaction, then split_head_update()\n \t * arranges for the reflog of HEAD to be updated, too.\n \t */\n-\thead_ref = resolve_refdup(\"HEAD\", RESOLVE_REF_NO_RECURSE,\n-\t\t\t\t  head_oid.hash, &head_type);\n+\thead_ref = refs_resolve_refdup(ref_store, \"HEAD\",\n+\t\t\t\t       RESOLVE_REF_NO_RECURSE,\n+\t\t\t\t       head_oid.hash, &head_type);\n \n \tif (head_ref && !(head_type & REF_ISSYMREF)) {\n \t\tfree(head_ref);\n@@ -4049,7 +4062,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t * so here we really only check that none of the references\n \t * that we are creating already exists.\n \t */\n-\tif (for_each_rawref(ref_present, &affected_refnames))\n+\tif (refs_for_each_rawref(&refs->base, ref_present,\n+\t\t\t\t &affected_refnames))\n \t\tdie(\"BUG: initial ref transaction called with existing refs\");\n \n \tfor (i = 0; i < transaction->nr; i++) {\n@@ -4168,7 +4182,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\tstrbuf_release(&err);\n \t\treturn -1;\n \t}\n-\tif (!reflog_exists(refname)) {\n+\tif (!refs_reflog_exists(ref_store, refname)) {\n \t\tunlock_ref(lock);\n \t\treturn 0;\n \t}\n@@ -4199,7 +4213,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t}\n \n \t(*prepare_fn)(refname, sha1, cb.policy_cb);\n-\tfor_each_reflog_ent(refname, expire_reflog_ent, &cb);\n+\trefs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);\n \t(*cleanup_fn)(cb.policy_cb);\n \n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314453","messageId":"20170318020337.22767-28-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 27/27] refs.h: add a note about sorting order of for_each_ref_*","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:37Z","receivedAt":"2017-03-18T02:07:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.h                         | 4 ++--\n t/t1405-main-ref-store.sh      | 6 ++++++\n t/t1406-submodule-ref-store.sh | 6 ++++++\n 3 files changed, 14 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.h b/refs.h\nindex 1a07f9d86f..49e97d7d5f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -230,7 +230,7 @@ typedef int each_ref_fn(const char *refname,\n  * it is not safe to modify references while an iteration is in\n  * progress, unless the same callback function invocation that\n  * modifies the reference also returns a nonzero value to immediately\n- * stop the iteration.\n+ * stop the iteration. Returned references are sorted.\n  */\n int refs_for_each_ref(struct ref_store *refs,\n \t\t      each_ref_fn fn, void *cb_data);\n@@ -370,7 +370,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void\n \n /*\n  * Calls the specified function for each reflog file until it returns nonzero,\n- * and returns the value\n+ * and returns the value. Reflog file order is unspecified.\n  */\n int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_reflog(each_ref_fn fn, void *cb_data);\ndiff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh\nindex 77e1c130c2..490521f8cb 100755\n--- a/t/t1405-main-ref-store.sh\n+++ b/t/t1405-main-ref-store.sh\n@@ -53,6 +53,12 @@ test_expect_success 'for_each_ref(refs/heads/)' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'for_each_ref() is sorted' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tsort actual > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'resolve_ref(new-master)' '\n \tSHA1=`git rev-parse new-master` &&\n \techo \"$SHA1 refs/heads/new-master 0x0\" >expected &&\ndiff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\nindex 22214ebd32..13b5454c56 100755\n--- a/t/t1406-submodule-ref-store.sh\n+++ b/t/t1406-submodule-ref-store.sh\n@@ -47,6 +47,12 @@ test_expect_success 'for_each_ref(refs/heads/)' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'for_each_ref() is sorted' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tsort actual > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'resolve_ref(master)' '\n \tSHA1=`git -C sub rev-parse master` &&\n \techo \"$SHA1 refs/heads/master 0x0\" >expected &&\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314454","messageId":"20170318020337.22767-26-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 25/27] t1405: some basic tests on main ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:35Z","receivedAt":"2017-03-18T02:07:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1405-main-ref-store.sh (new +x) | 123 +++++++++++++++++++++++++++++++++++++\n 1 file changed, 123 insertions(+)\n create mode 100755 t/t1405-main-ref-store.sh\n\ndiff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh\nnew file mode 100755\nindex 0000000000..77e1c130c2\n--- /dev/null\n+++ b/t/t1405-main-ref-store.sh\n@@ -0,0 +1,123 @@\n+#!/bin/sh\n+\n+test_description='test main ref store api'\n+\n+. ./test-lib.sh\n+\n+RUN=\"test-ref-store main\"\n+\n+test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '\n+\ttest_commit one &&\n+\tN=`find .git/refs -type f | wc -l` &&\n+\ttest \"$N\" != 0 &&\n+\t$RUN pack-refs 3 &&\n+\tN=`find .git/refs -type f | wc -l`\n+'\n+\n+test_expect_success 'peel_ref(new-tag)' '\n+\tgit rev-parse HEAD >expected &&\n+\tgit tag -a -m new-tag new-tag HEAD &&\n+\t$RUN peel-ref refs/tags/new-tag >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create_symref(FOO, refs/heads/master)' '\n+\t$RUN create-symref FOO refs/heads/master nothing &&\n+\techo refs/heads/master >expected &&\n+\tgit symbolic-ref FOO >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '\n+\tgit rev-parse FOO -- &&\n+\tgit rev-parse refs/tags/new-tag -- &&\n+\t$RUN delete-refs 0 FOO refs/tags/new-tag &&\n+\ttest_must_fail git rev-parse FOO -- &&\n+\ttest_must_fail git rev-parse refs/tags/new-tag --\n+'\n+\n+test_expect_success 'rename_refs(master, new-master)' '\n+\tgit rev-parse master >expected &&\n+\t$RUN rename-ref refs/heads/master refs/heads/new-master &&\n+\tgit rev-parse new-master >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_commit recreate-master\n+'\n+\n+test_expect_success 'for_each_ref(refs/heads/)' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tmaster 0x0\n+\tnew-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'resolve_ref(new-master)' '\n+\tSHA1=`git rev-parse new-master` &&\n+\techo \"$SHA1 refs/heads/new-master 0x0\" >expected &&\n+\t$RUN resolve-ref refs/heads/new-master 0 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify_ref(new-master)' '\n+\t$RUN verify-ref refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_reflog()' '\n+\t$RUN for-each-reflog | sort | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tHEAD 0x1\n+\trefs/heads/master 0x0\n+\trefs/heads/new-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'for_each_reflog_ent()' '\n+\t$RUN for-each-reflog-ent HEAD >actual &&\n+\thead -n1 actual | grep one &&\n+\ttail -n2 actual | head -n1 | grep recreate-master\n+'\n+\n+test_expect_success 'for_each_reflog_ent_reverse()' '\n+\t$RUN for-each-reflog-ent-reverse HEAD >actual &&\n+\thead -n1 actual | grep recreate-master &&\n+\ttail -n2 actual | head -n1 | grep one\n+'\n+\n+test_expect_success 'reflog_exists(HEAD)' '\n+\t$RUN reflog-exists HEAD\n+'\n+\n+test_expect_success 'delete_reflog(HEAD)' '\n+\t$RUN delete-reflog HEAD &&\n+\t! test -f .git/logs/HEAD\n+'\n+\n+test_expect_success 'create-reflog(HEAD)' '\n+\t$RUN create-reflog HEAD 1 &&\n+\ttest -f .git/logs/HEAD\n+'\n+\n+test_expect_success 'delete_ref(refs/heads/foo)' '\n+\tgit checkout -b foo &&\n+\tFOO_SHA1=`git rev-parse foo` &&\n+\tgit checkout --detach &&\n+\ttest_commit bar-commit &&\n+\tgit checkout -b bar &&\n+\tBAR_SHA1=`git rev-parse bar` &&\n+\t$RUN update-ref updating refs/heads/foo $BAR_SHA1 $FOO_SHA1 0 &&\n+\techo $BAR_SHA1 >expected &&\n+\tgit rev-parse refs/heads/foo >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'delete_ref(refs/heads/foo)' '\n+\tSHA1=`git rev-parse foo` &&\n+\tgit checkout --detach &&\n+\t$RUN delete-ref msg refs/heads/foo $SHA1 0 &&\n+\ttest_must_fail git rev-parse refs/heads/foo --\n+'\n+\n+test_done\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314455","messageId":"20170318020337.22767-25-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 24/27] t/helper: add test-ref-store to test ref-store functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:34Z","receivedAt":"2017-03-18T02:07:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile                        |   1 +\n t/helper/.gitignore             |   1 +\n t/helper/test-ref-store.c (new) | 275 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 277 insertions(+)\n create mode 100644 t/helper/test-ref-store.c\n\ndiff --git a/Makefile b/Makefile\nindex a5a11e721a..5f3844e33e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -622,6 +622,7 @@ TEST_PROGRAMS_NEED_X += test-parse-options\n TEST_PROGRAMS_NEED_X += test-path-utils\n TEST_PROGRAMS_NEED_X += test-prio-queue\n TEST_PROGRAMS_NEED_X += test-read-cache\n+TEST_PROGRAMS_NEED_X += test-ref-store\n TEST_PROGRAMS_NEED_X += test-regex\n TEST_PROGRAMS_NEED_X += test-revision-walking\n TEST_PROGRAMS_NEED_X += test-run-command\ndiff --git a/t/helper/.gitignore b/t/helper/.gitignore\nindex d6e8b36798..5f68aa8f8a 100644\n--- a/t/helper/.gitignore\n+++ b/t/helper/.gitignore\n@@ -19,6 +19,7 @@\n /test-path-utils\n /test-prio-queue\n /test-read-cache\n+/test-ref-store\n /test-regex\n /test-revision-walking\n /test-run-command\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nnew file mode 100644\nindex 0000000000..57849ee59a\n--- /dev/null\n+++ b/t/helper/test-ref-store.c\n@@ -0,0 +1,275 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+\n+static const char *notnull(const char *arg, const char *name)\n+{\n+\tif (!arg)\n+\t\tdie(\"%s required\", name);\n+\treturn arg;\n+}\n+\n+static unsigned int arg_flags(const char *arg, const char *name)\n+{\n+\treturn atoi(notnull(arg, name));\n+}\n+\n+static const char **get_store(const char **argv, struct ref_store **refs)\n+{\n+\tconst char *gitdir;\n+\n+\tif (!argv[0]) {\n+\t\tdie(\"ref store required\");\n+\t} else if (!strcmp(argv[0], \"main\")) {\n+\t\t*refs = get_main_ref_store();\n+\t} else if (skip_prefix(argv[0], \"submodule:\", &gitdir)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret;\n+\n+\t\tret = strbuf_git_path_submodule(&sb, gitdir, \"objects/\");\n+\t\tif (ret)\n+\t\t\tdie(\"strbuf_git_path_submodule failed: %d\", ret);\n+\t\tadd_to_alternates_memory(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\n+\t\t*refs = get_submodule_ref_store(gitdir);\n+\t} else\n+\t\tdie(\"unknown backend %s\", argv[0]);\n+\n+\tif (!*refs)\n+\t\tdie(\"no ref store\");\n+\n+\t/* consume store-specific optional arguments if needed */\n+\n+\treturn argv + 1;\n+}\n+\n+\n+static int cmd_pack_refs(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\n+\treturn refs_pack_refs(refs, flags);\n+}\n+\n+static int cmd_peel_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tunsigned char sha1[20];\n+\tint ret;\n+\n+\tret = refs_peel_ref(refs, refname, sha1);\n+\tif (!ret)\n+\t\tputs(sha1_to_hex(sha1));\n+\treturn ret;\n+}\n+\n+static int cmd_create_symref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *target = notnull(*argv++, \"target\");\n+\tconst char *logmsg = *argv++;\n+\n+\treturn refs_create_symref(refs, refname, target, logmsg);\n+}\n+\n+static int cmd_delete_refs(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tstruct string_list refnames = STRING_LIST_INIT_NODUP;\n+\n+\twhile (*argv)\n+\t\tstring_list_append(&refnames, *argv++);\n+\n+\treturn refs_delete_refs(refs, &refnames, flags);\n+}\n+\n+static int cmd_rename_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *oldref = notnull(*argv++, \"oldref\");\n+\tconst char *newref = notnull(*argv++, \"newref\");\n+\tconst char *logmsg = *argv++;\n+\n+\treturn refs_rename_ref(refs, oldref, newref, logmsg);\n+}\n+\n+static int each_ref(const char *refname, const struct object_id *oid,\n+\t\t    int flags, void *cb_data)\n+{\n+\tprintf(\"%s %s 0x%x\\n\", oid_to_hex(oid), refname, flags);\n+\treturn 0;\n+}\n+\n+static int cmd_for_each_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *prefix = notnull(*argv++, \"prefix\");\n+\n+\treturn refs_for_each_ref_in(refs, prefix, each_ref, NULL);\n+}\n+\n+static int cmd_resolve_ref(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned char sha1[20];\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tint resolve_flags = arg_flags(*argv++, \"resolve-flags\");\n+\tint flags;\n+\tconst char *ref;\n+\n+\tref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\t\t\t\t      sha1, &flags);\n+\tprintf(\"%s %s 0x%x\\n\", sha1_to_hex(sha1), ref, flags);\n+\treturn ref ? 0 : 1;\n+}\n+\n+static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n+\n+\tret = refs_verify_refname_available(refs, refname, NULL, NULL, &err);\n+\tif (err.len)\n+\t\tputs(err.buf);\n+\treturn ret;\n+}\n+\n+static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)\n+{\n+\treturn refs_for_each_reflog(refs, each_ref, NULL);\n+}\n+\n+static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,\n+\t\t       const char *committer, unsigned long timestamp,\n+\t\t       int tz, const char *msg, void *cb_data)\n+{\n+\tprintf(\"%s %s %s %lu %d %s\\n\",\n+\t       oid_to_hex(old_oid), oid_to_hex(new_oid),\n+\t       committer, timestamp, tz, msg);\n+\treturn 0;\n+}\n+\n+static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_for_each_reflog_ent(refs, refname, each_reflog, refs);\n+}\n+\n+static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);\n+}\n+\n+static int cmd_reflog_exists(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn !refs_reflog_exists(refs, refname);\n+}\n+\n+static int cmd_create_reflog(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tint force_create = arg_flags(*argv++, \"force-create\");\n+\tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n+\n+\tret = refs_create_reflog(refs, refname, force_create, &err);\n+\tif (err.len)\n+\t\tputs(err.buf);\n+\treturn ret;\n+}\n+\n+static int cmd_delete_reflog(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_delete_reflog(refs, refname);\n+}\n+\n+static int cmd_reflog_expire(struct ref_store *refs, const char **argv)\n+{\n+\tdie(\"not supported yet\");\n+}\n+\n+static int cmd_delete_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *msg = notnull(*argv++, \"msg\");\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tunsigned char old_sha1[20];\n+\n+\tif (get_sha1_hex(sha1_buf, old_sha1))\n+\t\tdie(\"not sha-1\");\n+\n+\treturn refs_delete_ref(refs, msg, refname, old_sha1, flags);\n+}\n+\n+static int cmd_update_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *msg = notnull(*argv++, \"msg\");\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *new_sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tunsigned char old_sha1[20];\n+\tunsigned char new_sha1[20];\n+\n+\tif (get_sha1_hex(old_sha1_buf, old_sha1) ||\n+\t    get_sha1_hex(new_sha1_buf, new_sha1))\n+\t\tdie(\"not sha-1\");\n+\n+\treturn refs_update_ref(refs, msg, refname,\n+\t\t\t       new_sha1, old_sha1,\n+\t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n+}\n+\n+struct command {\n+\tconst char *name;\n+\tint (*func)(struct ref_store *refs, const char **argv);\n+};\n+\n+static struct command commands[] = {\n+\t{ \"pack-refs\", cmd_pack_refs },\n+\t{ \"peel-ref\", cmd_peel_ref },\n+\t{ \"create-symref\", cmd_create_symref },\n+\t{ \"delete-refs\", cmd_delete_refs },\n+\t{ \"rename-ref\", cmd_rename_ref },\n+\t{ \"for-each-ref\", cmd_for_each_ref },\n+\t{ \"resolve-ref\", cmd_resolve_ref },\n+\t{ \"verify-ref\", cmd_verify_ref },\n+\t{ \"for-each-reflog\", cmd_for_each_reflog },\n+\t{ \"for-each-reflog-ent\", cmd_for_each_reflog_ent },\n+\t{ \"for-each-reflog-ent-reverse\", cmd_for_each_reflog_ent_reverse },\n+\t{ \"reflog-exists\", cmd_reflog_exists },\n+\t{ \"create-reflog\", cmd_create_reflog },\n+\t{ \"delete-reflog\", cmd_delete_reflog },\n+\t{ \"reflog-expire\", cmd_reflog_expire },\n+\t/*\n+\t * backend transaction functions can't be tested separately\n+\t */\n+\t{ \"delete-ref\", cmd_delete_ref },\n+\t{ \"update-ref\", cmd_update_ref },\n+\t{ NULL, NULL }\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct ref_store *refs;\n+\tconst char *func;\n+\tstruct command *cmd;\n+\n+\targv = get_store(argv + 1, &refs);\n+\n+\tfunc = *argv++;\n+\tif (!func)\n+\t\tdie(\"ref function required\");\n+\tfor (cmd = commands; cmd->name; cmd++) {\n+\t\tif (!strcmp(func, cmd->name))\n+\t\t\treturn cmd->func(refs, argv);\n+\t}\n+\tdie(\"unknown function %s\", func);\n+\treturn 0;\n+}\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314456","messageId":"20170318020337.22767-20-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 19/27] refs: rename get_ref_store() to get_submodule_ref_store() and make it public","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:29Z","receivedAt":"2017-03-18T02:14:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is intended to replace *_submodule() refs API. It provides\na ref store for a specific submodule, which can be operated on by a new\nset of refs API.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 12 ++++++++----\n refs.h               | 11 +++++++++++\n refs/refs-internal.h | 12 ------------\n 3 files changed, 19 insertions(+), 16 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 305ab71249..b179fb3106 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1171,7 +1171,7 @@ int head_ref(each_ref_fn fn, void *cb_data)\n static int do_for_each_ref(const char *submodule, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(submodule);\n+\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1344,10 +1344,10 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \t\t/* We need to strip off one or more trailing slashes */\n \t\tchar *stripped = xmemdupz(submodule, len);\n \n-\t\trefs = get_ref_store(stripped);\n+\t\trefs = get_submodule_ref_store(stripped);\n \t\tfree(stripped);\n \t} else {\n-\t\trefs = get_ref_store(submodule);\n+\t\trefs = get_submodule_ref_store(submodule);\n \t}\n \n \tif (!refs)\n@@ -1463,13 +1463,17 @@ static void register_submodule_ref_store(struct ref_store *refs,\n \t\t    submodule);\n }\n \n-struct ref_store *get_ref_store(const char *submodule)\n+struct ref_store *get_submodule_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \tint ret;\n \n \tif (!submodule || !*submodule) {\n+\t\t/*\n+\t\t * FIXME: This case is ideally not allowed. But that\n+\t\t * can't happen until we clean up all the callers.\n+\t\t */\n \t\treturn get_main_ref_store();\n \t}\n \ndiff --git a/refs.h b/refs.h\nindex a6cd12267f..e6d8f67895 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -562,5 +562,16 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int ref_storage_backend_exists(const char *name);\n \n struct ref_store *get_main_ref_store(void);\n+/*\n+ * Return the ref_store instance for the specified submodule. For the\n+ * main repository, use submodule==NULL; such a call cannot fail. For\n+ * a submodule, the submodule must exist and be a nonbare repository,\n+ * otherwise return NULL. If the requested reference store has not yet\n+ * been initialized, initialize it first.\n+ *\n+ * For backwards compatibility, submodule==\"\" is treated the same as\n+ * submodule==NULL.\n+ */\n+struct ref_store *get_submodule_ref_store(const char *submodule);\n \n #endif /* REFS_H */\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 0cca280b5c..f20dde39ee 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -646,18 +646,6 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-/*\n- * Return the ref_store instance for the specified submodule. For the\n- * main repository, use submodule==NULL; such a call cannot fail. For\n- * a submodule, the submodule must exist and be a nonbare repository,\n- * otherwise return NULL. If the requested reference store has not yet\n- * been initialized, initialize it first.\n- *\n- * For backwards compatibility, submodule==\"\" is treated the same as\n- * submodule==NULL.\n- */\n-struct ref_store *get_ref_store(const char *submodule);\n-\n const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314458","messageId":"20170318020337.22767-22-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 21/27] refs: new transaction related ref-store api","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:31Z","receivedAt":"2017-03-18T02:14:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The transaction struct now takes a ref store at creation and will\noperate on that ref store alone.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 55 ++++++++++++++++++++++++++++++++++++++++------------\n refs.h               |  9 +++++++++\n refs/refs-internal.h |  1 +\n 3 files changed, 53 insertions(+), 12 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex a3fc60ef61..5ea28ae128 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -630,16 +630,20 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1\n \treturn 0;\n }\n \n-int delete_ref(const char *msg, const char *refname,\n-\t       const unsigned char *old_sha1, unsigned int flags)\n+int refs_delete_ref(struct ref_store *refs, const char *msg,\n+\t\t    const char *refname,\n+\t\t    const unsigned char *old_sha1,\n+\t\t    unsigned int flags)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tif (ref_type(refname) == REF_TYPE_PSEUDOREF)\n+\tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n+\t\tassert(refs == get_main_ref_store());\n \t\treturn delete_pseudoref(refname, old_sha1);\n+\t}\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = ref_store_transaction_begin(refs, &err);\n \tif (!transaction ||\n \t    ref_transaction_delete(transaction, refname, old_sha1,\n \t\t\t\t   flags, msg, &err) ||\n@@ -654,6 +658,13 @@ int delete_ref(const char *msg, const char *refname,\n \treturn 0;\n }\n \n+int delete_ref(const char *msg, const char *refname,\n+\t       const unsigned char *old_sha1, unsigned int flags)\n+{\n+\treturn refs_delete_ref(get_main_ref_store(), msg, refname,\n+\t\t\t       old_sha1, flags);\n+}\n+\n int copy_reflog_msg(char *buf, const char *msg)\n {\n \tchar *cp = buf;\n@@ -813,11 +824,20 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,\n \treturn 1;\n }\n \n-struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n+\t\t\t\t\t\t    struct strbuf *err)\n {\n+\tstruct ref_transaction *tr;\n \tassert(err);\n \n-\treturn xcalloc(1, sizeof(struct ref_transaction));\n+\ttr = xcalloc(1, sizeof(struct ref_transaction));\n+\ttr->ref_store = refs;\n+\treturn tr;\n+}\n+\n+struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+{\n+\treturn ref_store_transaction_begin(get_main_ref_store(), err);\n }\n \n void ref_transaction_free(struct ref_transaction *transaction)\n@@ -934,18 +954,20 @@ int update_ref_oid(const char *msg, const char *refname,\n \t\told_oid ? old_oid->hash : NULL, flags, onerr);\n }\n \n-int update_ref(const char *msg, const char *refname,\n-\t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n-\t       unsigned int flags, enum action_on_err onerr)\n+int refs_update_ref(struct ref_store *refs, const char *msg,\n+\t\t    const char *refname, const unsigned char *new_sha1,\n+\t\t    const unsigned char *old_sha1, unsigned int flags,\n+\t\t    enum action_on_err onerr)\n {\n \tstruct ref_transaction *t = NULL;\n \tstruct strbuf err = STRBUF_INIT;\n \tint ret = 0;\n \n \tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n+\t\tassert(refs == get_main_ref_store());\n \t\tret = write_pseudoref(refname, new_sha1, old_sha1, &err);\n \t} else {\n-\t\tt = ref_transaction_begin(&err);\n+\t\tt = ref_store_transaction_begin(refs, &err);\n \t\tif (!t ||\n \t\t    ref_transaction_update(t, refname, new_sha1, old_sha1,\n \t\t\t\t\t   flags, msg, &err) ||\n@@ -976,6 +998,15 @@ int update_ref(const char *msg, const char *refname,\n \treturn 0;\n }\n \n+int update_ref(const char *msg, const char *refname,\n+\t       const unsigned char *new_sha1,\n+\t       const unsigned char *old_sha1,\n+\t       unsigned int flags, enum action_on_err onerr)\n+{\n+\treturn refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,\n+\t\t\t       old_sha1, flags, onerr);\n+}\n+\n char *shorten_unambiguous_ref(const char *refname, int strict)\n {\n \tint i;\n@@ -1610,7 +1641,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\tstruct ref_store *refs = transaction->ref_store;\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1729,7 +1760,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\tstruct ref_store *refs = transaction->ref_store;\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\ndiff --git a/refs.h b/refs.h\nindex eaa31e8193..37f4aa8bd5 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -333,6 +333,10 @@ int reflog_exists(const char *refname);\n  * exists, regardless of its old value. It is an error for old_sha1 to\n  * be NULL_SHA1. flags is passed through to ref_transaction_delete().\n  */\n+int refs_delete_ref(struct ref_store *refs, const char *msg,\n+\t\t    const char *refname,\n+\t\t    const unsigned char *old_sha1,\n+\t\t    unsigned int flags);\n int delete_ref(const char *msg, const char *refname,\n \t       const unsigned char *old_sha1, unsigned int flags);\n \n@@ -418,6 +422,8 @@ enum action_on_err {\n  * Begin a reference transaction.  The reference transaction must\n  * be freed by calling ref_transaction_free().\n  */\n+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n+\t\t\t\t\t\t    struct strbuf *err);\n struct ref_transaction *ref_transaction_begin(struct strbuf *err);\n \n /*\n@@ -552,6 +558,9 @@ void ref_transaction_free(struct ref_transaction *transaction);\n  * ref_transaction_update(). Handle errors as requested by the `onerr`\n  * argument.\n  */\n+int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,\n+\t\t    const unsigned char *new_sha1, const unsigned char *old_sha1,\n+\t\t    unsigned int flags, enum action_on_err onerr);\n int update_ref(const char *msg, const char *refname,\n \t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n \t       unsigned int flags, enum action_on_err onerr);\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 5f26208c2c..690498698e 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -200,6 +200,7 @@ enum ref_transaction_state {\n  * as atomically as possible.  This structure is opaque to callers.\n  */\n struct ref_transaction {\n+\tstruct ref_store *ref_store;\n \tstruct ref_update **updates;\n \tsize_t alloc;\n \tsize_t nr;\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314459","messageId":"20170318020337.22767-19-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 18/27] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:28Z","receivedAt":"2017-03-18T02:16:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend.c is unlearning submodules. Instead of having a specific\ncheck for submodules to see what operation is allowed, files backend\nnow takes a set of flags at init. Each operation will check if the\nrequired flags is present before performing.\n\nFor now we have four flags: read, write and odb access. Main ref store\nhas all flags, obviously, while submodule stores are read-only and have\naccess to odb (*).\n\nThe \"main\" flag stays because many functions in the backend calls\nfrontend ones without a ref store, so these functions always target the\nmain ref store. Ideally the flag should be gone after ref-store-aware\napi is in place and used by backends.\n\n(*) Submodule code needs for_each_ref. Try take REF_STORE_ODB flag\nout. At least t3404 would fail. The \"have access to odb\" in submodule is\na bit hacky since we don't know from he whether add_submodule_odb() has\nbeen called.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 15 ++++++---\n refs/files-backend.c | 86 +++++++++++++++++++++++++++++++++-------------------\n refs/refs-internal.h |  9 +++++-\n 3 files changed, 73 insertions(+), 37 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex fe724869b8..305ab71249 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1416,7 +1416,8 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n  * Create, record, and return a ref_store instance for the specified\n  * gitdir.\n  */\n-static struct ref_store *ref_store_init(const char *gitdir)\n+static struct ref_store *ref_store_init(const char *gitdir,\n+\t\t\t\t\tunsigned int flags)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1425,7 +1426,7 @@ static struct ref_store *ref_store_init(const char *gitdir)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(gitdir);\n+\trefs = be->init(gitdir, flags);\n \treturn refs;\n }\n \n@@ -1436,7 +1437,11 @@ struct ref_store *get_main_ref_store(void)\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\trefs = ref_store_init(get_git_dir());\n+\trefs = ref_store_init(get_git_dir(),\n+\t\t\t      (REF_STORE_READ |\n+\t\t\t       REF_STORE_WRITE |\n+\t\t\t       REF_STORE_ODB |\n+\t\t\t       REF_STORE_MAIN));\n \tmain_ref_store = refs;\n \treturn refs;\n }\n@@ -1484,7 +1489,9 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn NULL;\n \t}\n \n-\trefs = ref_store_init(submodule_sb.buf);\n+\t/* pretend that add_submodule_odb() has been called */\n+\trefs = ref_store_init(submodule_sb.buf,\n+\t\t\t      REF_STORE_READ | REF_STORE_ODB);\n \tregister_submodule_ref_store(refs, submodule);\n \n \tstrbuf_release(&submodule_sb);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex db335e4ca6..7d8d4dcc16 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -916,6 +916,7 @@ struct packed_ref_cache {\n  */\n struct files_ref_store {\n \tstruct ref_store base;\n+\tunsigned int store_flags;\n \n \tchar *gitdir;\n \tchar *gitcommondir;\n@@ -976,13 +977,15 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *gitdir)\n+static struct ref_store *files_ref_store_create(const char *gitdir,\n+\t\t\t\t\t\tunsigned int flags)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n+\trefs->store_flags = flags;\n \n \trefs->gitdir = xstrdup(gitdir);\n \tget_common_dir_noenv(&sb, gitdir);\n@@ -994,13 +997,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n }\n \n /*\n- * Die if refs is for a submodule (i.e., not for the main repository).\n- * caller is used in any necessary error messages.\n+ * Die if refs is not the main ref store. caller is used in any\n+ * necessary error messages.\n  */\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\t/* This function is to be deleted in the next patch */\n+\tif (refs->store_flags & REF_STORE_MAIN)\n+\t\treturn;\n+\n+\tdie(\"BUG: unallowed operation (%s), only works \"\n+\t    \"on main ref store\\n\", caller);\n }\n \n /*\n@@ -1009,9 +1016,9 @@ static void files_assert_main_repository(struct files_ref_store *refs,\n  * files_ref_store is for a submodule (i.e., not for the main\n  * repository). caller is used in any necessary error messages.\n  */\n-static struct files_ref_store *files_downcast(\n-\t\tstruct ref_store *ref_store, int submodule_allowed,\n-\t\tconst char *caller)\n+static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n+\t\t\t\t\t      unsigned int required_flags,\n+\t\t\t\t\t      const char *caller)\n {\n \tstruct files_ref_store *refs;\n \n@@ -1021,8 +1028,9 @@ static struct files_ref_store *files_downcast(\n \n \trefs = (struct files_ref_store *)ref_store;\n \n-\tif (!submodule_allowed)\n-\t\tfiles_assert_main_repository(refs, caller);\n+\tif ((refs->store_flags & required_flags) != required_flags)\n+\t\tdie(\"BUG: unallowed operation (%s), requires %x, has %x\\n\",\n+\t\t    caller, required_flags, refs->store_flags);\n \n \treturn refs;\n }\n@@ -1398,7 +1406,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t\t\t      struct strbuf *referent, unsigned int *type)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"read_raw_ref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"read_raw_ref\");\n \tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n \tconst char *path;\n@@ -1815,10 +1823,14 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n static int files_peel_ref(struct ref_store *ref_store,\n \t\t\t  const char *refname, unsigned char *sha1)\n {\n-\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,\n+\t\t\t       \"peel_ref\");\n \tint flag;\n \tunsigned char base[20];\n \n+\tfiles_assert_main_repository(refs, \"peel_ref\");\n+\n \tif (current_ref_iter && current_ref_iter->refname == refname) {\n \t\tstruct object_id peeled;\n \n@@ -1923,21 +1935,23 @@ static struct ref_iterator *files_ref_iterator_begin(\n \t\tstruct ref_store *ref_store,\n \t\tconst char *prefix, unsigned int flags)\n {\n-\tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n+\tstruct files_ref_store *refs;\n \tstruct ref_dir *loose_dir, *packed_dir;\n \tstruct ref_iterator *loose_iter, *packed_iter;\n \tstruct files_ref_iterator *iter;\n \tstruct ref_iterator *ref_iterator;\n \n-\tif (!refs)\n-\t\treturn empty_ref_iterator_begin();\n-\n \tif (ref_paranoia < 0)\n \t\tref_paranoia = git_env_bool(\"GIT_REF_PARANOIA\", 0);\n \tif (ref_paranoia)\n \t\tflags |= DO_FOR_EACH_INCLUDE_BROKEN;\n \n+\trefs = files_downcast(ref_store,\n+\t\t\t      REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),\n+\t\t\t      \"ref_iterator_begin\");\n+\tif (!refs)\n+\t\treturn empty_ref_iterator_begin();\n+\n \titer = xcalloc(1, sizeof(*iter));\n \tref_iterator = &iter->base;\n \tbase_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);\n@@ -2418,7 +2432,8 @@ static void prune_refs(struct ref_to_prune *r)\n static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"pack_refs\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,\n+\t\t\t       \"pack_refs\");\n \tstruct pack_refs_cb_data cbdata;\n \n \tmemset(&cbdata, 0, sizeof(cbdata));\n@@ -2497,7 +2512,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \t\t\t     struct string_list *refnames, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_refs\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"delete_refs\");\n \tstruct strbuf err = STRBUF_INIT;\n \tint i, result = 0;\n \n@@ -2601,7 +2616,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,\n \t\t\t\t\t  struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"verify_refname_available\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"verify_refname_available\");\n \tstruct ref_dir *packed_refs = get_packed_refs(refs);\n \tstruct ref_dir *loose_refs = get_loose_refs(refs);\n \n@@ -2626,7 +2641,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"rename_ref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"rename_ref\");\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n@@ -2876,7 +2891,7 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"create_reflog\");\n \tint fd;\n \n \tif (log_ref_setup(refs, refname, force_create, &fd, err))\n@@ -3120,7 +3135,7 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\t\t       const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_symref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"create_symref\");\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_lock *lock;\n \tint ret;\n@@ -3146,7 +3161,9 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_main_ref_store(), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(),\n+\t\t\t       REF_STORE_WRITE,\n+\t\t\t       \"set_head_symref\");\n \n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n@@ -3185,7 +3202,7 @@ static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n@@ -3200,7 +3217,7 @@ static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n@@ -3256,7 +3273,8 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n@@ -3364,7 +3382,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n@@ -3452,7 +3471,8 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -3790,7 +3810,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE,\n+\t\t\t       \"ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list refs_to_delete = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *ref_to_delete;\n@@ -3995,7 +4016,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"initial_ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE,\n+\t\t\t       \"initial_ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list affected_refnames = STRING_LIST_INIT_NODUP;\n \n@@ -4117,7 +4139,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\t       void *policy_cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_expire\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"reflog_expire\");\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n@@ -4223,7 +4245,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"init_db\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n \t/*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex dfa1817929..0cca280b5c 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -481,12 +481,19 @@ struct ref_store;\n \n /* refs backends */\n \n+/* ref_store_init flags */\n+#define REF_STORE_READ\t\t(1 << 0)\n+#define REF_STORE_WRITE\t\t(1 << 1) /* can perform update operations */\n+#define REF_STORE_ODB\t\t(1 << 2) /* has access to object database */\n+#define REF_STORE_MAIN\t\t(1 << 3)\n+\n /*\n  * Initialize the ref_store for the specified gitdir. These functions\n  * should call base_ref_store_init() to initialize the shared part of\n  * the ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir,\n+\t\t\t\t\t    unsigned int flags);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314460","messageId":"20170318020337.22767-21-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 20/27] refs: add new ref-store api","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:30Z","receivedAt":"2017-03-18T02:30:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is not meant to cover all existing API. It adds enough to test ref\nstores with the new test program test-ref-store, coming soon and to be\nused by files-backend.c.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 251 ++++++++++++++++++++++++++++++++++++++-------------\n refs.h               |  74 +++++++++++++++\n refs/files-backend.c |  13 +--\n refs/refs-internal.h |  31 +------\n 4 files changed, 270 insertions(+), 99 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex b179fb3106..a3fc60ef61 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -171,11 +171,23 @@ int refname_is_safe(const char *refname)\n \treturn 1;\n }\n \n+char *refs_resolve_refdup(struct ref_store *refs,\n+\t\t\t  const char *refname, int resolve_flags,\n+\t\t\t  unsigned char *sha1, int *flags)\n+{\n+\tconst char *result;\n+\n+\tresult = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\t\t\t\t\t sha1, flags);\n+\treturn xstrdup_or_null(result);\n+}\n+\n char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t     unsigned char *sha1, int *flags)\n {\n-\treturn xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\t\t  sha1, flags));\n+\treturn refs_resolve_refdup(get_main_ref_store(),\n+\t\t\t\t   refname, resolve_flags,\n+\t\t\t\t   sha1, flags);\n }\n \n /* The argument to filter_refs */\n@@ -185,13 +197,20 @@ struct ref_filter {\n \tvoid *cb_data;\n };\n \n-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)\n+int refs_read_ref_full(struct ref_store *refs, const char *refname,\n+\t\t       int resolve_flags, unsigned char *sha1, int *flags)\n {\n-\tif (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))\n+\tif (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags))\n \t\treturn 0;\n \treturn -1;\n }\n \n+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)\n+{\n+\treturn refs_read_ref_full(get_main_ref_store(), refname,\n+\t\t\t\t  resolve_flags, sha1, flags);\n+}\n+\n int read_ref(const char *refname, unsigned char *sha1)\n {\n \treturn read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);\n@@ -286,34 +305,52 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li\n \tfor_each_rawref(warn_if_dangling_symref, &data);\n }\n \n+int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/tags/\", fn, cb_data);\n+}\n+\n int for_each_tag_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/tags/\", fn, cb_data);\n+\treturn refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/tags/\", fn, cb_data);\n+\treturn refs_for_each_tag_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t     fn, cb_data);\n+}\n+\n+int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/heads/\", fn, cb_data);\n }\n \n int for_each_branch_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/heads/\", fn, cb_data);\n+\treturn refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/heads/\", fn, cb_data);\n+\treturn refs_for_each_branch_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t\tfn, cb_data);\n+}\n+\n+int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/remotes/\", fn, cb_data);\n }\n \n int for_each_remote_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/remotes/\", fn, cb_data);\n+\treturn refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/remotes/\", fn, cb_data);\n+\treturn refs_for_each_remote_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t\tfn, cb_data);\n }\n \n int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n@@ -1120,14 +1157,17 @@ const char *find_descendant_ref(const char *dirname,\n \treturn NULL;\n }\n \n-int rename_ref_available(const char *old_refname, const char *new_refname)\n+int refs_rename_ref_available(struct ref_store *refs,\n+\t\t\t      const char *old_refname,\n+\t\t\t      const char *new_refname)\n {\n \tstruct string_list skip = STRING_LIST_INIT_NODUP;\n \tstruct strbuf err = STRBUF_INIT;\n \tint ok;\n \n \tstring_list_insert(&skip, old_refname);\n-\tok = !verify_refname_available(new_refname, NULL, &skip, &err);\n+\tok = !refs_verify_refname_available(refs, new_refname,\n+\t\t\t\t\t    NULL, &skip, &err);\n \tif (!ok)\n \t\terror(\"%s\", err.buf);\n \n@@ -1168,10 +1208,9 @@ int head_ref(each_ref_fn fn, void *cb_data)\n  * non-zero value, stop the iteration and return that value;\n  * otherwise, return 0.\n  */\n-static int do_for_each_ref(const char *submodule, const char *prefix,\n+static int do_for_each_ref(struct ref_store *refs, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1183,19 +1222,30 @@ static int do_for_each_ref(const char *submodule, const char *prefix,\n \treturn do_for_each_ref_iterator(iter, fn, cb_data);\n }\n \n+int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(refs, \"\", fn, 0, 0, cb_data);\n+}\n+\n int for_each_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"\", fn, 0, 0, cb_data);\n+\treturn refs_for_each_ref(get_main_ref_store(), 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, \"\", fn, 0, 0, cb_data);\n+\treturn refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);\n+}\n+\n+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,\n+\t\t\t each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);\n }\n \n int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);\n+\treturn refs_for_each_ref_in(get_main_ref_store(), prefix, fn, cb_data);\n }\n \n int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)\n@@ -1204,19 +1254,23 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig\n \n \tif (broken)\n \t\tflag = DO_FOR_EACH_INCLUDE_BROKEN;\n-\treturn do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);\n+\treturn do_for_each_ref(get_main_ref_store(),\n+\t\t\t       prefix, fn, 0, flag, cb_data);\n }\n \n int for_each_ref_in_submodule(const char *submodule, const char *prefix,\n-\t\teach_ref_fn fn, void *cb_data)\n+\t\t\t      each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);\n+\treturn refs_for_each_ref_in(get_submodule_ref_store(submodule),\n+\t\t\t\t    prefix, fn, cb_data);\n }\n \n int for_each_replace_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, git_replace_ref_base, fn,\n-\t\t\t       strlen(git_replace_ref_base), 0, cb_data);\n+\treturn do_for_each_ref(get_main_ref_store(),\n+\t\t\t       git_replace_ref_base, fn,\n+\t\t\t       strlen(git_replace_ref_base),\n+\t\t\t       0, cb_data);\n }\n \n int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n@@ -1224,19 +1278,25 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\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+\tret = do_for_each_ref(get_main_ref_store(),\n+\t\t\t      buf.buf, fn, 0, 0, cb_data);\n \tstrbuf_release(&buf);\n \treturn ret;\n }\n \n-int for_each_rawref(each_ref_fn fn, void *cb_data)\n+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"\", fn, 0,\n+\treturn do_for_each_ref(refs, \"\", fn, 0,\n \t\t\t       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n }\n \n+int for_each_rawref(each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_rawref(get_main_ref_store(), fn, cb_data);\n+}\n+\n /* This function needs to return a meaningful errno on failure */\n-const char *resolve_ref_recursively(struct ref_store *refs,\n+const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    unsigned char *sha1, int *flags)\n@@ -1323,7 +1383,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n+\treturn refs_resolve_ref_unsafe(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1353,7 +1413,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \tif (!refs)\n \t\treturn -1;\n \n-\tif (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||\n+\tif (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) ||\n \t    is_null_sha1(sha1))\n \t\treturn -1;\n \treturn 0;\n@@ -1509,27 +1569,42 @@ void base_ref_store_init(struct ref_store *refs,\n }\n \n /* backend functions */\n+int refs_pack_refs(struct ref_store *refs, unsigned int flags)\n+{\n+\treturn refs->be->pack_refs(refs, flags);\n+}\n+\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_pack_refs(get_main_ref_store(), flags);\n+}\n \n-\treturn refs->be->pack_refs(refs, flags);\n+int refs_peel_ref(struct ref_store *refs, const char *refname,\n+\t\t  unsigned char *sha1)\n+{\n+\treturn refs->be->peel_ref(refs, refname, sha1);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_peel_ref(get_main_ref_store(), refname, sha1);\n+}\n \n-\treturn refs->be->peel_ref(refs, refname, sha1);\n+int refs_create_symref(struct ref_store *refs,\n+\t\t       const char *ref_target,\n+\t\t       const char *refs_heads_master,\n+\t\t       const char *logmsg)\n+{\n+\treturn refs->be->create_symref(refs, ref_target,\n+\t\t\t\t       refs_heads_master,\n+\t\t\t\t       logmsg);\n }\n \n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n-\treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n-\t\t\t\t       logmsg);\n+\treturn refs_create_symref(get_main_ref_store(), ref_target,\n+\t\t\t\t  refs_heads_master, logmsg);\n }\n \n int ref_transaction_commit(struct ref_transaction *transaction,\n@@ -1540,19 +1615,17 @@ int ref_transaction_commit(struct ref_transaction *transaction,\n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n \n-int verify_refname_available(const char *refname,\n-\t\t\t     const struct string_list *extra,\n-\t\t\t     const struct string_list *skip,\n-\t\t\t     struct strbuf *err)\n+int refs_verify_refname_available(struct ref_store *refs,\n+\t\t\t\t  const char *refname,\n+\t\t\t\t  const struct string_list *extra,\n+\t\t\t\t  const struct string_list *skip,\n+\t\t\t\t  struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n-int for_each_reflog(each_ref_fn fn, void *cb_data)\n+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1560,43 +1633,84 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n \treturn do_for_each_ref_iterator(iter, fn, cb_data);\n }\n \n-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n-\t\t\t\tvoid *cb_data)\n+int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_for_each_reflog(get_main_ref_store(), fn, cb_data);\n+}\n \n+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     each_reflog_ent_fn fn,\n+\t\t\t\t     void *cb_data)\n+{\n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n }\n \n+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n+\t\t\t\tvoid *cb_data)\n+{\n+\treturn refs_for_each_reflog_ent_reverse(get_main_ref_store(),\n+\t\t\t\t\t\trefname, fn, cb_data);\n+}\n+\n+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,\n+\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n+{\n+\treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n+}\n+\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_for_each_reflog_ent(get_main_ref_store(), refname,\n+\t\t\t\t\tfn, cb_data);\n+}\n \n-\treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n+int refs_reflog_exists(struct ref_store *refs, const char *refname)\n+{\n+\treturn refs->be->reflog_exists(refs, refname);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_reflog_exists(get_main_ref_store(), refname);\n+}\n \n-\treturn refs->be->reflog_exists(refs, refname);\n+int refs_create_reflog(struct ref_store *refs, const char *refname,\n+\t\t       int force_create, struct strbuf *err)\n+{\n+\treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_create_reflog(get_main_ref_store(), refname,\n+\t\t\t\t  force_create, err);\n+}\n \n-\treturn refs->be->create_reflog(refs, refname, force_create, err);\n+int refs_delete_reflog(struct ref_store *refs, const char *refname)\n+{\n+\treturn refs->be->delete_reflog(refs, refname);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_delete_reflog(get_main_ref_store(), refname);\n+}\n \n-\treturn refs->be->delete_reflog(refs, refname);\n+int refs_reflog_expire(struct ref_store *refs,\n+\t\t       const char *refname, const unsigned char *sha1,\n+\t\t       unsigned int flags,\n+\t\t       reflog_expiry_prepare_fn prepare_fn,\n+\t\t       reflog_expiry_should_prune_fn should_prune_fn,\n+\t\t       reflog_expiry_cleanup_fn cleanup_fn,\n+\t\t       void *policy_cb_data)\n+{\n+\treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n+\t\t\t\t       prepare_fn, should_prune_fn,\n+\t\t\t\t       cleanup_fn, policy_cb_data);\n }\n \n int reflog_expire(const char *refname, const unsigned char *sha1,\n@@ -1606,11 +1720,10 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n-\treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n-\t\t\t\t       prepare_fn, should_prune_fn,\n-\t\t\t\t       cleanup_fn, policy_cb_data);\n+\treturn refs_reflog_expire(get_main_ref_store(),\n+\t\t\t\t  refname, sha1, flags,\n+\t\t\t\t  prepare_fn, should_prune_fn,\n+\t\t\t\t  cleanup_fn, policy_cb_data);\n }\n \n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n@@ -1621,16 +1734,24 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n-int delete_refs(struct string_list *refnames, unsigned int flags)\n+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,\n+\t\t     unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n-int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n+int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_delete_refs(get_main_ref_store(), refnames, flags);\n+}\n \n+int refs_rename_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg)\n+{\n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\n+\n+int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n+{\n+\treturn refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);\n+}\ndiff --git a/refs.h b/refs.h\nindex e6d8f67895..eaa31e8193 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -57,16 +57,50 @@ struct string_list;\n #define RESOLVE_REF_NO_RECURSE 0x02\n #define RESOLVE_REF_ALLOW_BAD_NAME 0x04\n \n+const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n+\t\t\t\t    const char *refname,\n+\t\t\t\t    int resolve_flags,\n+\t\t\t\t    unsigned char *sha1,\n+\t\t\t\t    int *flags);\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags);\n \n+char *refs_resolve_refdup(struct ref_store *refs,\n+\t\t\t  const char *refname, int resolve_flags,\n+\t\t\t  unsigned char *sha1, int *flags);\n char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t     unsigned char *sha1, int *flags);\n \n+int refs_read_ref_full(struct ref_store *refs, const char *refname,\n+\t\t       int resolve_flags, unsigned char *sha1, int *flags);\n int read_ref_full(const char *refname, int resolve_flags,\n \t\t  unsigned char *sha1, int *flags);\n int read_ref(const char *refname, unsigned char *sha1);\n \n+/*\n+ * Return 0 if a reference named refname could be created without\n+ * conflicting with the name of an existing reference. Otherwise,\n+ * return a negative value and write an explanation to err. If extras\n+ * is non-NULL, it is a list of additional refnames with which refname\n+ * is not allowed to conflict. If skip is non-NULL, ignore potential\n+ * conflicts with refs in skip (e.g., because they are scheduled for\n+ * deletion in the same operation). Behavior is undefined if the same\n+ * name is listed in both extras and skip.\n+ *\n+ * Two reference names conflict if one of them exactly matches the\n+ * leading components of the other; e.g., \"foo/bar\" conflicts with\n+ * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n+ * \"foo/barbados\".\n+ *\n+ * extras and skip must be sorted.\n+ */\n+\n+int refs_verify_refname_available(struct ref_store *refs,\n+\t\t\t\t  const char *refname,\n+\t\t\t\t  const struct string_list *extra,\n+\t\t\t\t  const struct string_list *skip,\n+\t\t\t\t  struct strbuf *err);\n+\n int ref_exists(const char *refname);\n \n int should_autocreate_reflog(const char *refname);\n@@ -83,6 +117,8 @@ extern int refs_init_db(struct strbuf *err);\n  * Symbolic references are considered unpeelable, even if they\n  * ultimately resolve to a peelable tag.\n  */\n+int refs_peel_ref(struct ref_store *refs, const char *refname,\n+\t\t  unsigned char *sha1);\n int peel_ref(const char *refname, unsigned char *sha1);\n \n /**\n@@ -196,6 +232,17 @@ typedef int each_ref_fn(const char *refname,\n  * modifies the reference also returns a nonzero value to immediately\n  * stop the iteration.\n  */\n+int refs_for_each_ref(struct ref_store *refs,\n+\t\t      each_ref_fn fn, void *cb_data);\n+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,\n+\t\t\t each_ref_fn fn, void *cb_data);\n+int refs_for_each_tag_ref(struct ref_store *refs,\n+\t\t\t  each_ref_fn fn, void *cb_data);\n+int refs_for_each_branch_ref(struct ref_store *refs,\n+\t\t\t     each_ref_fn fn, void *cb_data);\n+int refs_for_each_remote_ref(struct ref_store *refs,\n+\t\t\t     each_ref_fn fn, void *cb_data);\n+\n int head_ref(each_ref_fn fn, void *cb_data);\n int for_each_ref(each_ref_fn fn, void *cb_data);\n int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);\n@@ -225,6 +272,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data);\n int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);\n \n /* can be used to learn about broken ref and symref */\n+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_rawref(each_ref_fn fn, void *cb_data);\n \n static inline const char *has_glob_specials(const char *pattern)\n@@ -248,6 +296,7 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,\n  * Write a packed-refs file for the current repository.\n  * flags: Combination of the above PACK_REFS_* flags.\n  */\n+int refs_pack_refs(struct ref_store *refs, unsigned int flags);\n int pack_refs(unsigned int flags);\n \n /*\n@@ -263,6 +312,8 @@ int pack_refs(unsigned int flags);\n /*\n  * Setup reflog before using. Fill in err and return -1 on failure.\n  */\n+int refs_create_reflog(struct ref_store *refs, const char *refname,\n+\t\t       int force_create, struct strbuf *err);\n int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);\n \n /** Reads log for the value of ref during at_time. **/\n@@ -272,6 +323,7 @@ int read_ref_at(const char *refname, unsigned int flags,\n \t\tunsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);\n \n /** Check if a particular reflog exists */\n+int refs_reflog_exists(struct ref_store *refs, const char *refname);\n int reflog_exists(const char *refname);\n \n /*\n@@ -290,9 +342,12 @@ int delete_ref(const char *msg, const char *refname,\n  * an all-or-nothing transaction). flags is passed through to\n  * ref_transaction_delete().\n  */\n+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,\n+\t\t     unsigned int flags);\n int delete_refs(struct string_list *refnames, unsigned int flags);\n \n /** Delete a reflog */\n+int refs_delete_reflog(struct ref_store *refs, const char *refname);\n int delete_reflog(const char *refname);\n \n /* iterate over reflog entries */\n@@ -301,6 +356,12 @@ typedef int each_reflog_ent_fn(\n \t\tconst char *committer, unsigned long timestamp,\n \t\tint tz, const char *msg, void *cb_data);\n \n+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,\n+\t\t\t     each_reflog_ent_fn fn, void *cb_data);\n+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     each_reflog_ent_fn fn,\n+\t\t\t\t     void *cb_data);\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);\n \n@@ -308,6 +369,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void\n  * Calls the specified function for each reflog file until it returns nonzero,\n  * and returns the value\n  */\n+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_reflog(each_ref_fn fn, void *cb_data);\n \n #define REFNAME_ALLOW_ONELEVEL 1\n@@ -328,8 +390,12 @@ const char *prettify_refname(const char *refname);\n char *shorten_unambiguous_ref(const char *refname, int strict);\n \n /** rename ref, return 0 on success **/\n+int refs_rename_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg);\n int rename_ref(const char *oldref, const char *newref, const char *logmsg);\n \n+int refs_create_symref(struct ref_store *refs, const char *refname,\n+\t\t       const char *target, const char *logmsg);\n int create_symref(const char *refname, const char *target, const char *logmsg);\n \n /*\n@@ -552,6 +618,14 @@ typedef void reflog_expiry_cleanup_fn(void *cb_data);\n  * enum expire_reflog_flags. The three function pointers are described\n  * above. On success, return zero.\n  */\n+int refs_reflog_expire(struct ref_store *refs,\n+\t\t       const char *refname,\n+\t\t       const unsigned char *sha1,\n+\t\t       unsigned int flags,\n+\t\t       reflog_expiry_prepare_fn prepare_fn,\n+\t\t       reflog_expiry_should_prune_fn should_prune_fn,\n+\t\t       reflog_expiry_cleanup_fn cleanup_fn,\n+\t\t       void *policy_cb_data);\n int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  unsigned int flags,\n \t\t  reflog_expiry_prepare_fn prepare_fn,\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 7d8d4dcc16..46f791a516 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1314,7 +1314,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \t\t\t\t\t create_dir_entry(refs, refname.buf,\n \t\t\t\t\t\t\t  refname.len, 1));\n \t\t} else {\n-\t\t\tif (!resolve_ref_recursively(&refs->base,\n+\t\t\tif (!refs_resolve_ref_unsafe(&refs->base,\n \t\t\t\t\t\t     refname.buf,\n \t\t\t\t\t\t     RESOLVE_REF_READING,\n \t\t\t\t\t\t     sha1, &flag)) {\n@@ -1623,7 +1623,8 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t\t * another reference such as \"refs/foo\". There is no\n \t\t * reason to expect this error to be transitory.\n \t\t */\n-\t\tif (verify_refname_available(refname, extras, skip, err)) {\n+\t\tif (refs_verify_refname_available(&refs->base, refname,\n+\t\t\t\t\t\t  extras, skip, err)) {\n \t\t\tif (mustexist) {\n \t\t\t\t/*\n \t\t\t\t * To the user the relevant error is\n@@ -2673,7 +2674,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    oldrefname);\n \t\tgoto out;\n \t}\n-\tif (!rename_ref_available(oldrefname, newrefname)) {\n+\tif (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {\n \t\tret = 1;\n \t\tgoto out;\n \t}\n@@ -4057,9 +4058,9 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\tif ((update->flags & REF_HAVE_OLD) &&\n \t\t    !is_null_sha1(update->old_sha1))\n \t\t\tdie(\"BUG: initial ref transaction with old_sha1 set\");\n-\t\tif (verify_refname_available(update->refname,\n-\t\t\t\t\t     &affected_refnames, NULL,\n-\t\t\t\t\t     err)) {\n+\t\tif (refs_verify_refname_available(&refs->base, update->refname,\n+\t\t\t\t\t\t  &affected_refnames, NULL,\n+\t\t\t\t\t\t  err)) {\n \t\t\tret = TRANSACTION_NAME_CONFLICT;\n \t\t\tgoto cleanup;\n \t\t}\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f20dde39ee..5f26208c2c 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -112,28 +112,6 @@ enum peel_status {\n enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);\n \n /*\n- * Return 0 if a reference named refname could be created without\n- * conflicting with the name of an existing reference. Otherwise,\n- * return a negative value and write an explanation to err. If extras\n- * is non-NULL, it is a list of additional refnames with which refname\n- * is not allowed to conflict. If skip is non-NULL, ignore potential\n- * conflicts with refs in skip (e.g., because they are scheduled for\n- * deletion in the same operation). Behavior is undefined if the same\n- * name is listed in both extras and skip.\n- *\n- * Two reference names conflict if one of them exactly matches the\n- * leading components of the other; e.g., \"foo/bar\" conflicts with\n- * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n- * \"foo/barbados\".\n- *\n- * extras and skip must be sorted.\n- */\n-int verify_refname_available(const char *newname,\n-\t\t\t     const struct string_list *extras,\n-\t\t\t     const struct string_list *skip,\n-\t\t\t     struct strbuf *err);\n-\n-/*\n  * Copy the reflog message msg to buf, which has been allocated sufficiently\n  * large, while cleaning up the whitespaces.  Especially, convert LF to space,\n  * because reflog file is one line per entry.\n@@ -252,7 +230,9 @@ const char *find_descendant_ref(const char *dirname,\n  * processes (though rename_ref() catches some races that might get by\n  * this check).\n  */\n-int rename_ref_available(const char *old_refname, const char *new_refname);\n+int refs_rename_ref_available(struct ref_store *refs,\n+\t\t\t      const char *old_refname,\n+\t\t\t      const char *new_refname);\n \n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define SYMREF_MAXDEPTH 5\n@@ -646,9 +626,4 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-const char *resolve_ref_recursively(struct ref_store *refs,\n-\t\t\t\t    const char *refname,\n-\t\t\t\t    int resolve_flags,\n-\t\t\t\t    unsigned char *sha1, int *flags);\n-\n #endif /* REFS_REFS_INTERNAL_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314461","messageId":"20170318020337.22767-5-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 04/27] files-backend: add and use files_packed_refs_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:14Z","receivedAt":"2017-03-18T02:32:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 29 ++++++++++++++++++-----------\n 1 file changed, 18 insertions(+), 11 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 0ef80c51cc..6e6962151a 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -923,6 +923,8 @@ struct files_ref_store {\n \t */\n \tconst char *submodule;\n \n+\tchar *packed_refs_path;\n+\n \tstruct ref_entry *loose;\n \tstruct packed_ref_cache *packed;\n };\n@@ -985,7 +987,14 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\trefs->submodule = xstrdup_or_null(submodule);\n+\tif (submodule) {\n+\t\trefs->submodule = xstrdup(submodule);\n+\t\trefs->packed_refs_path = git_pathdup_submodule(\n+\t\t\trefs->submodule, \"packed-refs\");\n+\t\treturn ref_store;\n+\t}\n+\n+\trefs->packed_refs_path = git_pathdup(\"packed-refs\");\n \n \treturn ref_store;\n }\n@@ -1153,19 +1162,18 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n \tstrbuf_release(&line);\n }\n \n+static const char *files_packed_refs_path(struct files_ref_store *refs)\n+{\n+\treturn refs->packed_refs_path;\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n  */\n static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n {\n-\tchar *packed_refs_file;\n-\n-\tif (refs->submodule)\n-\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n-\t\t\t\t\t\t\t \"packed-refs\");\n-\telse\n-\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n+\tconst char *packed_refs_file = files_packed_refs_path(refs);\n \n \tif (refs->packed &&\n \t    !stat_validity_check(&refs->packed->validity, packed_refs_file))\n@@ -1184,7 +1192,6 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref\n \t\t\tfclose(f);\n \t\t}\n \t}\n-\tfree(packed_refs_file);\n \treturn refs->packed;\n }\n \n@@ -2160,7 +2167,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t}\n \n \tif (hold_lock_file_for_update_timeout(\n-\t\t\t    &packlock, git_path(\"packed-refs\"),\n+\t\t\t    &packlock, files_packed_refs_path(refs),\n \t\t\t    flags, timeout_value) < 0)\n \t\treturn -1;\n \t/*\n@@ -2426,7 +2433,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n \t\treturn 0; /* no refname exists in packed refs */\n \n \tif (lock_packed_refs(refs, 0)) {\n-\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n+\t\tunable_to_lock_message(files_packed_refs_path(refs), errno, err);\n \t\treturn -1;\n \t}\n \tpacked = get_packed_refs(refs);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314462","messageId":"20170318020337.22767-3-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 02/27] files-backend: make files_log_ref_write() static","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:12Z","receivedAt":"2017-03-18T02:34:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\nbut probably never used outside refs-internal.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 +++\n refs/refs-internal.h | 4 ----\n 2 files changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 50188e92f9..0a6d2bf6bc 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err);\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex fa93c9a32e..f732473e1d 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -228,10 +228,6 @@ struct ref_transaction {\n \tenum ref_transaction_state state;\n };\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err);\n-\n /*\n  * Check for entries in extras that are within the specified\n  * directory, where dirname is a reference directory name including\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314463","messageId":"20170318020337.22767-1-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170222140450.30886-1-pclouds@gmail.com","subject":"[PATCH v6 00/27] Remove submodule from files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:10Z","receivedAt":"2017-03-18T02:37:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v6 should address all Micheal's comments in v5, except a few that I\nreplied back. The interdiff looks a bit messy due to some new\nrefactoring (05/27) in files_rename_ref(), but it makes the final\noutput easier to read.\n\n03/27 and 27/27 are also new patches that are not really necessary but\nnice to have.\n\nThe series is rebased on latest master and does not depend on any\nother topics, since they all have graduated to master.\n\ndiff --git a/path.c b/path.c\nindex 3451d2916f..03e7e33b6e 100644\n--- a/path.c\n+++ b/path.c\n@@ -471,7 +471,6 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n }\n \n /* Returns 0 on success, negative on failure. */\n-#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1\n static int do_submodule_path(struct strbuf *buf, const char *path,\n \t\t\t     const char *fmt, va_list args)\n {\ndiff --git a/refs.c b/refs.c\nindex 4e4945bd1c..24c7113d36 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -171,11 +171,23 @@ int refname_is_safe(const char *refname)\n \treturn 1;\n }\n \n+char *refs_resolve_refdup(struct ref_store *refs,\n+\t\t\t  const char *refname, int resolve_flags,\n+\t\t\t  unsigned char *sha1, int *flags)\n+{\n+\tconst char *result;\n+\n+\tresult = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\t\t\t\t\t sha1, flags);\n+\treturn xstrdup_or_null(result);\n+}\n+\n char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t     unsigned char *sha1, int *flags)\n {\n-\treturn xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\t\t  sha1, flags));\n+\treturn refs_resolve_refdup(get_main_ref_store(),\n+\t\t\t\t   refname, resolve_flags,\n+\t\t\t\t   sha1, flags);\n }\n \n /* The argument to filter_refs */\n@@ -1521,12 +1533,7 @@ struct ref_store *get_main_ref_store(void)\n \t\t\t       REF_STORE_WRITE |\n \t\t\t       REF_STORE_ODB |\n \t\t\t       REF_STORE_MAIN));\n-\tif (refs) {\n-\t\tif (main_ref_store)\n-\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n-\n-\t\tmain_ref_store = refs;\n-\t}\n+\tmain_ref_store = refs;\n \treturn refs;\n }\n \n@@ -1569,17 +1576,20 @@ struct ref_store *get_submodule_ref_store(const char *submodule)\n \tret = is_nonbare_repository_dir(&submodule_sb);\n \tstrbuf_release(&submodule_sb);\n \tif (!ret)\n-\t\treturn refs;\n+\t\treturn NULL;\n \n \tret = submodule_to_gitdir(&submodule_sb, submodule);\n-\tif (!ret)\n-\t\t/* pretend that add_submodule_odb() has been called */\n-\t\trefs = ref_store_init(submodule_sb.buf,\n-\t\t\t\t      REF_STORE_READ | REF_STORE_ODB);\n-\tstrbuf_release(&submodule_sb);\n+\tif (ret) {\n+\t\tstrbuf_release(&submodule_sb);\n+\t\treturn NULL;\n+\t}\n \n-\tif (refs)\n-\t\tregister_submodule_ref_store(refs, submodule);\n+\t/* pretend that add_submodule_odb() has been called */\n+\trefs = ref_store_init(submodule_sb.buf,\n+\t\t\t      REF_STORE_READ | REF_STORE_ODB);\n+\tregister_submodule_ref_store(refs, submodule);\n+\n+\tstrbuf_release(&submodule_sb);\n \treturn refs;\n }\n \ndiff --git a/refs.h b/refs.h\nindex 3fc140c16f..4f9983816a 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -3,7 +3,6 @@\n \n struct object_id;\n struct ref_store;\n-struct ref_transaction;\n struct strbuf;\n struct string_list;\n \n@@ -66,6 +65,9 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags);\n \n+char *refs_resolve_refdup(struct ref_store *refs,\n+\t\t\t  const char *refname, int resolve_flags,\n+\t\t\t  unsigned char *sha1, int *flags);\n char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t     unsigned char *sha1, int *flags);\n \n@@ -183,6 +185,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n  * `ref_transaction_commit` is called.  So `ref_transaction_verify`\n  * won't report a verification failure until the commit is attempted.\n  */\n+struct ref_transaction;\n \n /*\n  * Bit values set in the flags argument passed to each_ref_fn() and\n@@ -227,7 +230,7 @@ typedef int each_ref_fn(const char *refname,\n  * it is not safe to modify references while an iteration is in\n  * progress, unless the same callback function invocation that\n  * modifies the reference also returns a nonzero value to immediately\n- * stop the iteration.\n+ * stop the iteration. Returned references are sorted.\n  */\n int refs_for_each_ref(struct ref_store *refs,\n \t\t      each_ref_fn fn, void *cb_data);\n@@ -367,7 +370,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void\n \n /*\n  * Calls the specified function for each reflog file until it returns nonzero,\n- * and returns the value\n+ * and returns the value. Reflog file order is unspecified.\n  */\n int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_reflog(each_ref_fn fn, void *cb_data);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 89acf38e36..04298ba9fa 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1282,16 +1282,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tstruct strbuf refname;\n \tstruct strbuf path = STRBUF_INIT;\n \tsize_t path_baselen;\n-\tint err = 0;\n \n \tfiles_refname_path(refs, &path, dirname);\n \tpath_baselen = path.len;\n \n-\tif (err) {\n-\t\tstrbuf_release(&path);\n-\t\treturn;\n-\t}\n-\n \td = opendir(path.buf);\n \tif (!d) {\n \t\tstrbuf_release(&path);\n@@ -2661,30 +2655,37 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tstruct strbuf err = STRBUF_INIT;\n \n \tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n+\tfiles_reflog_path(refs, &sb_newref, newrefname);\n+\tfiles_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n+\n \tlog = !lstat(sb_oldref.buf, &loginfo);\n-\tstrbuf_release(&sb_oldref);\n-\tif (log && S_ISLNK(loginfo.st_mode))\n-\t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n+\tif (log && S_ISLNK(loginfo.st_mode)) {\n+\t\tret = error(\"reflog for %s is a symlink\", oldrefname);\n+\t\tgoto out;\n+\t}\n \n \tif (!refs_resolve_ref_unsafe(&refs->base, oldrefname,\n \t\t\t\t     RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n-\t\t\t\t     orig_sha1, &flag))\n-\t\treturn error(\"refname %s not found\", oldrefname);\n+\t\t\t\torig_sha1, &flag)) {\n+\t\tret = error(\"refname %s not found\", oldrefname);\n+\t\tgoto out;\n+\t}\n \n-\tif (flag & REF_ISSYMREF)\n-\t\treturn error(\"refname %s is a symbolic ref, renaming it is not supported\",\n-\t\t\toldrefname);\n-\tif (!refs_rename_ref_available(&refs->base, oldrefname, newrefname))\n-\t\treturn 1;\n+\tif (flag & REF_ISSYMREF) {\n+\t\tret = error(\"refname %s is a symbolic ref, renaming it is not supported\",\n+\t\t\t    oldrefname);\n+\t\tgoto out;\n+\t}\n+\tif (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {\n+\t\tret = 1;\n+\t\tgoto out;\n+\t}\n \n-\tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n-\tfiles_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n-\tret = log && rename(sb_oldref.buf, tmp_renamed_log.buf);\n-\tstrbuf_release(&sb_oldref);\n-\tstrbuf_release(&tmp_renamed_log);\n-\tif (ret)\n-\t\treturn error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n-\t\t\toldrefname, strerror(errno));\n+\tif (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {\n+\t\tret = error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n+\t\t\t    oldrefname, strerror(errno));\n+\t\tgoto out;\n+\t}\n \n \tif (refs_delete_ref(&refs->base, logmsg, oldrefname,\n \t\t\t    orig_sha1, REF_NODEREF)) {\n@@ -2743,7 +2744,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto rollback;\n \t}\n \n-\treturn 0;\n+\tret = 0;\n+\tgoto out;\n \n  rollback:\n \tlock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,\n@@ -2764,21 +2766,20 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tfiles_reflog_path(refs, &sb_newref, newrefname);\n-\tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n \tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n-\tfiles_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n+\tret = 1;\n+ out:\n \tstrbuf_release(&sb_newref);\n \tstrbuf_release(&sb_oldref);\n \tstrbuf_release(&tmp_renamed_log);\n \n-\treturn 1;\n+\treturn ret;\n }\n \n static int close_ref(struct ref_lock *lock)\n@@ -3885,10 +3886,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t * head_ref within the transaction, then split_head_update()\n \t * arranges for the reflog of HEAD to be updated, too.\n \t */\n-\thead_ref = (char *)refs_resolve_ref_unsafe(ref_store, \"HEAD\",\n-\t\t\t\t\t\t   RESOLVE_REF_NO_RECURSE,\n-\t\t\t\t\t\t   head_oid.hash, &head_type);\n-\thead_ref = xstrdup_or_null(head_ref);\n+\thead_ref = refs_resolve_refdup(ref_store, \"HEAD\",\n+\t\t\t\t       RESOLVE_REF_NO_RECURSE,\n+\t\t\t\t       head_oid.hash, &head_type);\n \n \tif (head_ref && !(head_type & REF_ISSYMREF)) {\n \t\tfree(head_ref);\n@@ -4267,17 +4267,11 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n \t */\n \tfiles_refname_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n+\n \tstrbuf_reset(&sb);\n \tfiles_refname_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n-\tstrbuf_reset(&sb);\n-\tif (get_shared_repository()) {\n-\t\tfiles_refname_path(refs, &sb, \"refs/heads\");\n-\t\tadjust_shared_perm(sb.buf);\n-\t\tstrbuf_reset(&sb);\n-\t\tfiles_refname_path(refs, &sb, \"refs/tags\");\n-\t\tadjust_shared_perm(sb.buf);\n-\t}\n+\n \tstrbuf_release(&sb);\n \treturn 0;\n }\ndiff --git a/submodule.c b/submodule.c\nindex 36b8d1d113..193243102d 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1515,6 +1515,11 @@ void absorb_git_dir_into_superproject(const char *prefix,\n \t}\n }\n \n+/*\n+ * Given a submodule path (as in the index), return the repository\n+ * path of that submodule in 'buf'. Return -1 on error or when the\n+ * submodule is not initialized.\n+ */\n int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n {\n \tconst struct submodule *sub;\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex c4c670acd3..4cecf6b42e 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -195,6 +195,7 @@ static int cmd_reflog_expire(struct ref_store *refs, const char **argv)\n \n static int cmd_delete_ref(struct ref_store *refs, const char **argv)\n {\n+\tconst char *msg = notnull(*argv++, \"msg\");\n \tconst char *refname = notnull(*argv++, \"refname\");\n \tconst char *sha1_buf = notnull(*argv++, \"old-sha1\");\n \tunsigned int flags = arg_flags(*argv++, \"flags\");\n@@ -203,7 +204,7 @@ static int cmd_delete_ref(struct ref_store *refs, const char **argv)\n \tif (get_sha1_hex(sha1_buf, old_sha1))\n \t\tdie(\"not sha-1\");\n \n-\treturn refs_delete_ref(refs, refname, old_sha1, flags);\n+\treturn refs_delete_ref(refs, msg, refname, old_sha1, flags);\n }\n \n static int cmd_update_ref(struct ref_store *refs, const char **argv)\ndiff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh\nindex 0999829f1f..490521f8cb 100755\n--- a/t/t1405-main-ref-store.sh\n+++ b/t/t1405-main-ref-store.sh\n@@ -53,6 +53,12 @@ test_expect_success 'for_each_ref(refs/heads/)' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'for_each_ref() is sorted' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tsort actual > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'resolve_ref(new-master)' '\n \tSHA1=`git rev-parse new-master` &&\n \techo \"$SHA1 refs/heads/new-master 0x0\" >expected &&\n@@ -65,11 +71,11 @@ test_expect_success 'verify_ref(new-master)' '\n '\n \n test_expect_success 'for_each_reflog()' '\n-\t$RUN for-each-reflog | cut -c 42- >actual &&\n+\t$RUN for-each-reflog | sort | cut -c 42- >actual &&\n \tcat >expected <<-\\EOF &&\n+\tHEAD 0x1\n \trefs/heads/master 0x0\n \trefs/heads/new-master 0x0\n-\tHEAD 0x1\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -116,7 +122,7 @@ test_expect_success 'delete_ref(refs/heads/foo)' '\n test_expect_success 'delete_ref(refs/heads/foo)' '\n \tSHA1=`git rev-parse foo` &&\n \tgit checkout --detach &&\n-\t$RUN delete-ref refs/heads/foo $SHA1 0 &&\n+\t$RUN delete-ref msg refs/heads/foo $SHA1 0 &&\n \ttest_must_fail git rev-parse refs/heads/foo --\n '\n \ndiff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\nindex 3b30ba62f2..13b5454c56 100755\n--- a/t/t1406-submodule-ref-store.sh\n+++ b/t/t1406-submodule-ref-store.sh\n@@ -47,6 +47,12 @@ test_expect_success 'for_each_ref(refs/heads/)' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'for_each_ref() is sorted' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tsort actual > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'resolve_ref(master)' '\n \tSHA1=`git -C sub rev-parse master` &&\n \techo \"$SHA1 refs/heads/master 0x0\" >expected &&\n@@ -59,11 +65,11 @@ test_expect_success 'verify_ref(new-master)' '\n '\n \n test_expect_success 'for_each_reflog()' '\n-\t$RUN for-each-reflog | cut -c 42- >actual &&\n+\t$RUN for-each-reflog | sort | cut -c 42- >actual &&\n \tcat >expected <<-\\EOF &&\n+\tHEAD 0x1\n \trefs/heads/master 0x0\n \trefs/heads/new-master 0x0\n-\tHEAD 0x1\n \tEOF\n \ttest_cmp expected actual\n '\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314464","messageId":"20170318020337.22767-2-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 01/27] refs.h: add forward declaration for structs used in this file","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:11Z","receivedAt":"2017-03-18T03:00:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.h | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/refs.h b/refs.h\nindex 3df0d45ebb..2d6b6263fc 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -1,6 +1,10 @@\n #ifndef REFS_H\n #define REFS_H\n \n+struct object_id;\n+struct strbuf;\n+struct string_list;\n+\n /*\n  * Resolve a reference, recursively following symbolic refererences.\n  *\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314468","messageId":"20170318020337.22767-4-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v6 03/27] files-backend: delete dead code in files_init_db()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-18T02:03:13Z","receivedAt":"2017-03-18T04:55:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"safe_create_dir() can do adjust_shared_perm() internally, and init-db\nhas always created 'refs' in shared mode since the beginning,\naf6e277c5e (git-init-db: initialize shared repositories with --shared -\n2005-12-22). So this code looks like extra adjust_shared_perm calls are\nunnecessary.\n\nAnd they are. But let's see why there are here in the first place.\n\nThis code was added in 6fb5acfd8f (refs: add methods to init refs db -\n2016-09-04). From the diff alone this looks like a faithful refactored\ncode from init-db.c. But there is a subtle difference:\n\nBetween the safe_create_dir() block and adjust_shared_perm() block in\nthe old init-db.c, we may copy/recreate directories from the repo\ntemplate. So it makes sense that adjust_shared_perm() is re-executed\nthen to fix potential permission screwups.\n\nAfter 6fb5acfd8f, refs dirs are created after template is copied. Nobody\nwill change directory permission again. So the extra adjust_shared_perm()\nis redudant. Delete them.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 4 ----\n 1 file changed, 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 0a6d2bf6bc..0ef80c51cc 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -4110,10 +4110,6 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n \t */\n \tsafe_create_dir(git_path(\"refs/heads\"), 1);\n \tsafe_create_dir(git_path(\"refs/tags\"), 1);\n-\tif (get_shared_repository()) {\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n-\t}\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"314596","messageId":"a949cc96-34a4-fb5d-06c6-e124f6c025aa@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-3-pclouds@gmail.com","subject":"Re: [PATCH v6 02/27] files-backend: make files_log_ref_write() static","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-19T19:10:37Z","receivedAt":"2017-03-19T19:13:02Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\n> but probably never used outside refs-internal.c\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 3 +++\n>  refs/refs-internal.h | 4 ----\n>  2 files changed, 3 insertions(+), 4 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 50188e92f9..0a6d2bf6bc 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n>  \t\t\t\t\t  const char *dirname, size_t len,\n>  \t\t\t\t\t  int incomplete);\n>  static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n> +static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n> +\t\t\t       const unsigned char *new_sha1, const char *msg,\n> +\t\t\t       int flags, struct strbuf *err);\n>  \n>  static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n>  {\n> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n> index fa93c9a32e..f732473e1d 100644\n> --- a/refs/refs-internal.h\n> +++ b/refs/refs-internal.h\n> @@ -228,10 +228,6 @@ struct ref_transaction {\n>  \tenum ref_transaction_state state;\n>  };\n>  \n> -int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n> -\t\t\tconst unsigned char *new_sha1, const char *msg,\n> -\t\t\tint flags, struct strbuf *err);\n> -\n>  /*\n>   * Check for entries in extras that are within the specified\n>   * directory, where dirname is a reference directory name including\n> \n\nYou changed the declaration, but the definition still makes the function\nnon-static.\n\nMichael\n\n"},{"id":"314597","messageId":"fe71b052-d0ef-061e-4adb-405cc87428de@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-4-pclouds@gmail.com","subject":"Re: [PATCH v6 03/27] files-backend: delete dead code in files_init_db()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-19T19:11:27Z","receivedAt":"2017-03-19T19:17:38Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> safe_create_dir() can do adjust_shared_perm() internally, and init-db\n> has always created 'refs' in shared mode since the beginning,\n> af6e277c5e (git-init-db: initialize shared repositories with --shared -\n> 2005-12-22). So this code looks like extra adjust_shared_perm calls are\n> unnecessary.\n> \n> And they are. But let's see why there are here in the first place.\n> \n> This code was added in 6fb5acfd8f (refs: add methods to init refs db -\n> 2016-09-04). From the diff alone this looks like a faithful refactored\n> code from init-db.c. But there is a subtle difference:\n> \n> Between the safe_create_dir() block and adjust_shared_perm() block in\n> the old init-db.c, we may copy/recreate directories from the repo\n> template. So it makes sense that adjust_shared_perm() is re-executed\n> then to fix potential permission screwups.\n> \n> After 6fb5acfd8f, refs dirs are created after template is copied. Nobody\n> will change directory permission again. So the extra adjust_shared_perm()\n> is redudant. Delete them.\n\nLGTM. Thanks for the careful research.\n\nMichael\n\n\n"},{"id":"314604","messageId":"f90afe6c-534e-80ec-cb58-95bcb3172d86@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-10-pclouds@gmail.com","subject":"Re: [PATCH v6 09/27] files-backend: add and use files_refname_path()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-19T20:32:54Z","receivedAt":"2017-03-19T20:33:56Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> Keep repo-related path handling in one place. This will make it easier\n> to add submodule/multiworktree support later.\n> \n> This automatically adds the \"if submodule then use the submodule version\n> of git_path\" to other call sites too. But it does not mean those\n> operations are submodule-ready. Not yet.\n\nMaybe `files_loose_ref_path()` would be a more descriptive name for the\nnew function. But I can live with it either way.\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs/files-backend.c | 47 +++++++++++++++++++++++------------------------\n>  1 file changed, 23 insertions(+), 24 deletions(-)\n> \n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 64d1ab3fe8..1a13fb5e2b 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -1180,6 +1180,18 @@ static void files_reflog_path(struct files_ref_store *refs,\n>  \tstrbuf_git_path(sb, \"logs/%s\", refname);\n>  }\n>  \n> +static void files_refname_path(struct files_ref_store *refs,\n> +\t\t\t       struct strbuf *sb,\n> +\t\t\t       const char *refname)\n> +{\n> +\tif (refs->submodule) {\n> +\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n> +\t\treturn;\n> +\t}\n> +\n> +\tstrbuf_git_path(sb, \"%s\", refname);\n> +}\n> +\n>  /*\n>   * Get the packed_ref_cache for the specified files_ref_store,\n>   * creating it if necessary.\n> @@ -1249,19 +1261,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n>  \tstruct strbuf refname;\n>  \tstruct strbuf path = STRBUF_INIT;\n>  \tsize_t path_baselen;\n> -\tint err = 0;\n>  \n> -\tif (refs->submodule)\n> -\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n> -\telse\n> -\t\tstrbuf_git_path(&path, \"%s\", dirname);\n> +\tfiles_refname_path(refs, &path, dirname);\n>  \tpath_baselen = path.len;\n>  \n> -\tif (err) {\n> -\t\tstrbuf_release(&path);\n> -\t\treturn;\n> -\t}\n> -\n>  \td = opendir(path.buf);\n>  \tif (!d) {\n>  \t\tstrbuf_release(&path);\n\nThe old code in the hunk above went to the trouble of handling errors\nfrom `strbuf_git_path_submodule()`, which I think can happen if the\nsubmodule path doesn't actually point at a directory that looks like a\nGit repository. Your new code doesn't handle such an error.\n\nIt seems clear that `read_loose_refs()` is to late a place to be dealing\nwith such errors, and indeed it seems like the check\n`is_nonbare_repository_dir()` in `get_ref_store()` should make such\nerrors impossible, so I think your change is OK. If you agree, it might\nbe appropriate to mention that reasoning in the commit message.\n\n> [...]\n\nMichael\n\n"},{"id":"314605","messageId":"dda96cdb-ec10-cb46-96e9-576062d90841@ramsayjones.plus.com","threadId":"45126","inReplyTo":"a949cc96-34a4-fb5d-06c6-e124f6c025aa@alum.mit.edu","subject":"Re: [PATCH v6 02/27] files-backend: make files_log_ref_write() static","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2017-03-19T20:35:01Z","receivedAt":"2017-03-19T20:35:59Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 19/03/17 19:10, Michael Haggerty wrote:\n> On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n>> Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\n>> but probably never used outside refs-internal.c\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs/files-backend.c | 3 +++\n>>  refs/refs-internal.h | 4 ----\n>>  2 files changed, 3 insertions(+), 4 deletions(-)\n>>\n>> diff --git a/refs/files-backend.c b/refs/files-backend.c\n>> index 50188e92f9..0a6d2bf6bc 100644\n>> --- a/refs/files-backend.c\n>> +++ b/refs/files-backend.c\n>> @@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n>>  \t\t\t\t\t  const char *dirname, size_t len,\n>>  \t\t\t\t\t  int incomplete);\n>>  static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n>> +static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n>> +\t\t\t       const unsigned char *new_sha1, const char *msg,\n>> +\t\t\t       int flags, struct strbuf *err);\n>>  \n>>  static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n>>  {\n>> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n>> index fa93c9a32e..f732473e1d 100644\n>> --- a/refs/refs-internal.h\n>> +++ b/refs/refs-internal.h\n>> @@ -228,10 +228,6 @@ struct ref_transaction {\n>>  \tenum ref_transaction_state state;\n>>  };\n>>  \n>> -int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n>> -\t\t\tconst unsigned char *new_sha1, const char *msg,\n>> -\t\t\tint flags, struct strbuf *err);\n>> -\n>>  /*\n>>   * Check for entries in extras that are within the specified\n>>   * directory, where dirname is a reference directory name including\n>>\n> \n> You changed the declaration, but the definition still makes the function\n> non-static.\n\nActually, this is a static symbol, since it's 'static-ness' derives\nfrom the above forward declaration.\n\nHowever, as I said in a previous email, this tickles sparse to issue\na warning. In addition, simply adding 'static' to the definition\n(ie simply _don't_ do the forward declaration) is less work! :-D\n\nATB,\nRamsay Jones\n\n\n"},{"id":"314606","messageId":"511a2789-0f4e-2e65-94a9-eaaff858aa76@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-12-pclouds@gmail.com","subject":"Re: [PATCH v6 11/27] refs.c: introduce get_main_ref_store()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-19T20:38:19Z","receivedAt":"2017-03-19T20:38:29Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c | 16 ++++++++++++----\n>  1 file changed, 12 insertions(+), 4 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index e7606716dd..2637353b72 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -1456,15 +1456,23 @@ static struct ref_store *ref_store_init(const char *submodule)\n>  \treturn refs;\n>  }\n>  \n> +static struct ref_store *get_main_ref_store(void)\n> +{\n> +\tstruct ref_store *refs;\n> +\n> +\tif (main_ref_store)\n> +\t\treturn main_ref_store;\n> +\n> +\trefs = ref_store_init(NULL);\n> +\treturn refs;\n> +}\n> +\n\nI still [1] think that `refs` here is an unnecessary temporary variable.\nAnd even after your \"kill register_ref_store\" patch I think it is\nsuperfluous and the function could look like\n\nstatic struct ref_store *get_main_ref_store(void)\n{\n\tif (!main_ref_store)\n\t\tmain_ref_store = ref_store_init(NULL);\n\n\treturn main_ref_store;\n}\n\n> [...]\n\nMichael\n\n[1]\nhttp://public-inbox.org/git/0bef1e49-e96b-1666-9b88-f4262c2aeeba@alum.mit.edu/\n"},{"id":"314607","messageId":"45b2b743-4eb0-3511-a48f-1a4725d67c36@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-17-pclouds@gmail.com","subject":"Re: [PATCH v6 16/27] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-19T20:47:48Z","receivedAt":"2017-03-19T20:50:54Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> refs is learning to avoid path rewriting that is done by\n> strbuf_git_path_submodule(). Factor out this code so it could be reused\n> by refs*\n\nIs the \"*\" on the previous line is a typo, or did you want to add a\nfootnote, or ...?\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  path.c      | 35 +++++++----------------------------\n>  submodule.c | 36 ++++++++++++++++++++++++++++++++++++\n>  submodule.h |  1 +\n>  3 files changed, 44 insertions(+), 28 deletions(-)\n> \n> [...]\n> diff --git a/submodule.c b/submodule.c\n> index 3200b7bb2b..3c445f274e 100644\n> --- a/submodule.c\n> +++ b/submodule.c\n> @@ -1596,3 +1596,39 @@ const char *get_superproject_working_tree(void)\n>  \n>  \treturn ret;\n>  }\n> +\n> +/*\n> + * Given a submodule path (as in the index), return the repository\n> + * path of that submodule in 'buf'. Return -1 on error or when the\n> + * submodule is not initialized.\n> + */\n\nThanks for writing a docstring, but given that this is a public\nfunction, I think it is preferred to put the docstring in the header file.\n\n> [...]\n\nMichael\n\n"},{"id":"314608","messageId":"3738362d-6c76-cba7-824a-d689bbe290c6@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-18-pclouds@gmail.com","subject":"Re: [PATCH v6 17/27] refs: move submodule code out of files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-19T21:05:23Z","receivedAt":"2017-03-19T21:06:12Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> files-backend is now initialized with a $GIT_DIR. Converting a submodule\n> path to where real submodule gitdir is located is done in get_ref_store().\n> \n> This gives a slight performance improvement for submodules since we\n> don't convert submodule path to gitdir at every backend call like\n> before. We pay that once at ref-store creation.\n> \n> More cleanup in files_downcast() and files_assert_main_repository()\n> follows shortly. It's separate to keep noises from this patch.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c               | 19 ++++++++++++++-----\n>  refs/files-backend.c | 24 ++----------------------\n>  refs/refs-internal.h |  9 ++++-----\n>  3 files changed, 20 insertions(+), 32 deletions(-)\n> \n> [...]\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 351934d36e..db335e4ca6 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> [...]\n> @@ -1014,8 +1000,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n>  static void files_assert_main_repository(struct files_ref_store *refs,\n>  \t\t\t\t\t const char *caller)\n>  {\n> -\tif (refs->submodule)\n> -\t\tdie(\"BUG: %s called for a submodule\", caller);\n> +\t/* This function is to be deleted in the next patch */\n\nActually, the function survives the next patch. But the incorrect\ncomment doesn't ;-)\n\nIdeally, the following patch would precede this one; then this function\nwouldn't have to be disabled between the two patches.\n\nBut given that it's only a consistency check, I suppose that we can live\nwith the disappearance of this check for one commit. The comment could\nmaybe be corrected, though.\n\n> [...]\n> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n> index f732473e1d..dfa1817929 100644\n> --- a/refs/refs-internal.h\n> +++ b/refs/refs-internal.h\n> @@ -482,12 +482,11 @@ struct ref_store;\n>  /* refs backends */\n>  \n>  /*\n> - * Initialize the ref_store for the specified submodule, or for the\n> - * main repository if submodule == NULL. These functions should call\n> - * base_ref_store_init() to initialize the shared part of the\n> - * ref_store and to record the ref_store for later lookup.\n> + * Initialize the ref_store for the specified gitdir. These functions\n> + * should call base_ref_store_init() to initialize the shared part of\n> + * the ref_store and to record the ref_store for later lookup.\n\nMaybe mention that the function will make its own copy of `gitdir`?\n\n>   */\n> -typedef struct ref_store *ref_store_init_fn(const char *submodule);\n> +typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n>  \n>  typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n\nMichael\n\n"},{"id":"314610","messageId":"004082a0-871f-cd41-edc5-522b5c2b3b58@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-19-pclouds@gmail.com","subject":"Re: [PATCH v6 18/27] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-19T21:18:25Z","receivedAt":"2017-03-19T21:18:42Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> files-backend.c is unlearning submodules. Instead of having a specific\n> check for submodules to see what operation is allowed, files backend\n> now takes a set of flags at init. Each operation will check if the\n> required flags is present before performing.\n> \n> For now we have four flags: read, write and odb access. Main ref store\n> has all flags, obviously, while submodule stores are read-only and have\n> access to odb (*).\n> \n> The \"main\" flag stays because many functions in the backend calls\n> frontend ones without a ref store, so these functions always target the\n> main ref store. Ideally the flag should be gone after ref-store-aware\n> api is in place and used by backends.\n> \n> (*) Submodule code needs for_each_ref. Try take REF_STORE_ODB flag\n> out. At least t3404 would fail. The \"have access to odb\" in submodule is\n> a bit hacky since we don't know from he whether add_submodule_odb() has\n> been called.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c               | 15 ++++++---\n>  refs/files-backend.c | 86 +++++++++++++++++++++++++++++++++-------------------\n>  refs/refs-internal.h |  9 +++++-\n>  3 files changed, 73 insertions(+), 37 deletions(-)\n> \n> [...]\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index db335e4ca6..7d8d4dcc16 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> [...]\n> @@ -1923,21 +1935,23 @@ static struct ref_iterator *files_ref_iterator_begin(\n>  \t\tstruct ref_store *ref_store,\n>  \t\tconst char *prefix, unsigned int flags)\n>  {\n> -\tstruct files_ref_store *refs =\n> -\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n> +\tstruct files_ref_store *refs;\n>  \tstruct ref_dir *loose_dir, *packed_dir;\n>  \tstruct ref_iterator *loose_iter, *packed_iter;\n>  \tstruct files_ref_iterator *iter;\n>  \tstruct ref_iterator *ref_iterator;\n>  \n> -\tif (!refs)\n> -\t\treturn empty_ref_iterator_begin();\n> -\n>  \tif (ref_paranoia < 0)\n>  \t\tref_paranoia = git_env_bool(\"GIT_REF_PARANOIA\", 0);\n>  \tif (ref_paranoia)\n>  \t\tflags |= DO_FOR_EACH_INCLUDE_BROKEN;\n>  \n> +\trefs = files_downcast(ref_store,\n> +\t\t\t      REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),\n> +\t\t\t      \"ref_iterator_begin\");\n> +\tif (!refs)\n> +\t\treturn empty_ref_iterator_begin();\n> +\n\nI realize that this `if (!refs)` check existed in the old code, but\nisn't it pointless? If `refs` were NULL, `files_downcast()` would have\nalready segfaulted, I think.\n\n> [...]\n> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n> index dfa1817929..0cca280b5c 100644\n> --- a/refs/refs-internal.h\n> +++ b/refs/refs-internal.h\n> @@ -481,12 +481,19 @@ struct ref_store;\n>  \n>  /* refs backends */\n>  \n> +/* ref_store_init flags */\n> +#define REF_STORE_READ\t\t(1 << 0)\n\nI asked [1] in reply to v5 whether `REF_STORE_READ` is really necessary\nbut I don't think you replied. Surely a reference store that we can't\nread would be useless? Can't we just assume that any `ref_store` is\nreadable and drop this constant?\n\n> +#define REF_STORE_WRITE\t\t(1 << 1) /* can perform update operations */\n> +#define REF_STORE_ODB\t\t(1 << 2) /* has access to object database */\n> +#define REF_STORE_MAIN\t\t(1 << 3)\n> +\n>  /*\n>   * Initialize the ref_store for the specified gitdir. These functions\n>   * should call base_ref_store_init() to initialize the shared part of\n>   * the ref_store and to record the ref_store for later lookup.\n>   */\n> -typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n> +typedef struct ref_store *ref_store_init_fn(const char *gitdir,\n> +\t\t\t\t\t    unsigned int flags);\n>  \n>  typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n\nMichael\n\n[1]\nhttp://public-inbox.org/git/8fafd49f-71a6-ee97-6a69-c23e23c5d515@alum.mit.edu/\n\n"},{"id":"314628","messageId":"37e76a4d-e197-f9b1-2546-d207656149a6@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-27-pclouds@gmail.com","subject":"Re: [PATCH v6 26/27] t1406: new tests for submodule ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-20T05:27:05Z","receivedAt":"2017-03-20T05:27:19Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n>  1 file changed, 95 insertions(+)\n>  create mode 100755 t/t1406-submodule-ref-store.sh\n> \n> diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\n> new file mode 100755\n> index 0000000000..22214ebd32\n> --- /dev/null\n> +++ b/t/t1406-submodule-ref-store.sh\n> @@ -0,0 +1,95 @@\n> +#!/bin/sh\n> +\n> +test_description='test submodule ref store api'\n> +\n> +. ./test-lib.sh\n> +\n> +RUN=\"test-ref-store submodule:sub\"\n> +\n> +test_expect_success 'setup' '\n> +\tgit init sub &&\n> +\t(\n> +\t\tcd sub &&\n> +\t\ttest_commit first &&\n> +\t\tgit checkout -b new-master\n> +\t)\n> +'\n> +\n> +test_expect_success 'pack_refs() not allowed' '\n> +\ttest_must_fail $RUN pack-refs 3\n> +'\n\nIt's a little bit unfortunate but probably OK that this test and its\nanalog in t1405 hard-code internal numerical constants. At least in\nt1405 the names of the constants appear in the test name, meaning that\nanybody changing the constants' values in the future will probably find\nthe \"3\" in that file. Would you do the same here, please, or at least\nmention the constant names in a comment here?\n\n> [...]\n\nMichael\n\n"},{"id":"314629","messageId":"9f6d9544-a869-3d89-4025-345fac1ac70c@alum.mit.edu","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"Re: [PATCH v6 00/27] Remove submodule from files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-20T05:37:02Z","receivedAt":"2017-03-20T05:37:13Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n> v6 should address all Micheal's comments in v5, except a few that I\n> replied back. The interdiff looks a bit messy due to some new\n> refactoring (05/27) in files_rename_ref(), but it makes the final\n> output easier to read.\n> \n> 03/27 and 27/27 are also new patches that are not really necessary but\n> nice to have.\n> \n> The series is rebased on latest master and does not depend on any\n> other topics, since they all have graduated to master.\n\nThanks for the new version of this patch series. I left a few optional\ncomments, but nothing serious. With or without the suggested changes, I\nthink it's ready to go. Thanks for working on this!\n\nReviewed-by: Michael Haggerty <mhagger@alum.mit.edu>\n\nMichael\n\n"},{"id":"314639","messageId":"CACsJy8A6mZGjNdFbhKgWAR=XSK+2mO-HG7Vayp7VODRJwu1ixA@mail.gmail.com","threadId":"45126","inReplyTo":"37e76a4d-e197-f9b1-2546-d207656149a6@alum.mit.edu","subject":"Re: [PATCH v6 26/27] t1406: new tests for submodule ref store","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-20T12:05:21Z","receivedAt":"2017-03-20T12:08:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 20, 2017 at 12:27 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n>>  1 file changed, 95 insertions(+)\n>>  create mode 100755 t/t1406-submodule-ref-store.sh\n>>\n>> diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\n>> new file mode 100755\n>> index 0000000000..22214ebd32\n>> --- /dev/null\n>> +++ b/t/t1406-submodule-ref-store.sh\n>> @@ -0,0 +1,95 @@\n>> +#!/bin/sh\n>> +\n>> +test_description='test submodule ref store api'\n>> +\n>> +. ./test-lib.sh\n>> +\n>> +RUN=\"test-ref-store submodule:sub\"\n>> +\n>> +test_expect_success 'setup' '\n>> +     git init sub &&\n>> +     (\n>> +             cd sub &&\n>> +             test_commit first &&\n>> +             git checkout -b new-master\n>> +     )\n>> +'\n>> +\n>> +test_expect_success 'pack_refs() not allowed' '\n>> +     test_must_fail $RUN pack-refs 3\n>> +'\n>\n> It's a little bit unfortunate but probably OK that this test and its\n> analog in t1405 hard-code internal numerical constants. At least in\n> t1405 the names of the constants appear in the test name, meaning that\n> anybody changing the constants' values in the future will probably find\n> the \"3\" in that file.\n\nI noticed that too, but was not motivated enough to do actual\nenum/define <-> string mapping in test-ref-store.\n\n> Would you do the same here, please, or at least\n> mention the constant names in a comment here?\n\nWill do (and hopefully will make test-ref-store take names instead of\nnumbers at a later point, or you'll get annoyed faster and do it ;-)\n-- \nDuy\n"},{"id":"314644","messageId":"CACsJy8CHjSMV1qOAB-7-F562jx+3XKtQOjuPOFz0wu-pYR3aeA@mail.gmail.com","threadId":"45126","inReplyTo":"3738362d-6c76-cba7-824a-d689bbe290c6@alum.mit.edu","subject":"Re: [PATCH v6 17/27] refs: move submodule code out of files-backend.c","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-20T12:09:48Z","receivedAt":"2017-03-20T12:40:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 20, 2017 at 4:05 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> [...]\n>> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n>> index f732473e1d..dfa1817929 100644\n>> --- a/refs/refs-internal.h\n>> +++ b/refs/refs-internal.h\n>> @@ -482,12 +482,11 @@ struct ref_store;\n>>  /* refs backends */\n>>\n>>  /*\n>> - * Initialize the ref_store for the specified submodule, or for the\n>> - * main repository if submodule == NULL. These functions should call\n>> - * base_ref_store_init() to initialize the shared part of the\n>> - * ref_store and to record the ref_store for later lookup.\n>> + * Initialize the ref_store for the specified gitdir. These functions\n>> + * should call base_ref_store_init() to initialize the shared part of\n>> + * the ref_store and to record the ref_store for later lookup.\n>\n> Maybe mention that the function will make its own copy of `gitdir`?\n\nI would think that's the default/sane behavior and not need to be\nmentioned? A function that keeps a pointer even after it exits, now\nthat's something that must be documented.\n-- \nDuy\n"},{"id":"314645","messageId":"CACsJy8CC-BDHPLnE8DAXCxpttwgEsrQU9gzzc=PGsmFnh=XNpw@mail.gmail.com","threadId":"45126","inReplyTo":"45b2b743-4eb0-3511-a48f-1a4725d67c36@alum.mit.edu","subject":"Re: [PATCH v6 16/27] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-20T12:11:05Z","receivedAt":"2017-03-20T12:43:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 20, 2017 at 3:47 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 03/18/2017 03:03 AM, Nguyễn Thái Ngọc Duy wrote:\n>> refs is learning to avoid path rewriting that is done by\n>> strbuf_git_path_submodule(). Factor out this code so it could be reused\n>> by refs*\n>\n> Is the \"*\" on the previous line is a typo, or did you want to add a\n> footnote, or ...?\n\nIt's a wildcard for \"all refs functions\" :D Will rephrase.\n-- \nDuy\n"},{"id":"314652","messageId":"77d37131-535d-c4ed-0785-1b7cf91f01ad@alum.mit.edu","threadId":"45126","inReplyTo":"CACsJy8CHjSMV1qOAB-7-F562jx+3XKtQOjuPOFz0wu-pYR3aeA@mail.gmail.com","subject":"Re: [PATCH v6 17/27] refs: move submodule code out of files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-20T14:29:04Z","receivedAt":"2017-03-20T14:29:23Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/20/2017 01:09 PM, Duy Nguyen wrote:\n> On Mon, Mar 20, 2017 at 4:05 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>>> [...]\n>>> diff --git a/refs/refs-internal.h b/refs/refs-internal.h\n>>> index f732473e1d..dfa1817929 100644\n>>> --- a/refs/refs-internal.h\n>>> +++ b/refs/refs-internal.h\n>>> @@ -482,12 +482,11 @@ struct ref_store;\n>>>  /* refs backends */\n>>>\n>>>  /*\n>>> - * Initialize the ref_store for the specified submodule, or for the\n>>> - * main repository if submodule == NULL. These functions should call\n>>> - * base_ref_store_init() to initialize the shared part of the\n>>> - * ref_store and to record the ref_store for later lookup.\n>>> + * Initialize the ref_store for the specified gitdir. These functions\n>>> + * should call base_ref_store_init() to initialize the shared part of\n>>> + * the ref_store and to record the ref_store for later lookup.\n>>\n>> Maybe mention that the function will make its own copy of `gitdir`?\n> \n> I would think that's the default/sane behavior and not need to be\n> mentioned? A function that keeps a pointer even after it exits, now\n> that's something that must be documented.\n\nI guess you're right, and I hope that everybody really documents such cases.\n\nMichael\n\n"},{"id":"314658","messageId":"xmqqmvcgx7ud.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"9f6d9544-a869-3d89-4025-345fac1ac70c@alum.mit.edu","subject":"Re: [PATCH v6 00/27] Remove submodule from files-backend.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-03-20T15:53:46Z","receivedAt":"2017-03-20T16:09:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> Thanks for the new version of this patch series. I left a few optional\n> comments, but nothing serious. With or without the suggested changes, I\n> think it's ready to go. Thanks for working on this!\n>\n> Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>\n\nThanks for reviewing.  Judging from the exchanges between you two,\nit appears that the final reroll would mostly be uncontroversial\nkind that we can finally merge down to 'next' and then to 'master'\nfairly quickly ;-)\n\nThanks, Duy, for working on this.  The end-result looks much nicer.\n\n\n"},{"id":"314900","messageId":"20170322133735.ozjbncfm5tchygoc@sigill.intra.peff.net","threadId":"45126","inReplyTo":"20170322133412.e2oqmqekreyuf4gr@sigill.intra.peff.net","subject":"Re: [PATCH v6 24/27] t/helper: add test-ref-store to test ref-store functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-03-22T13:37:35Z","receivedAt":"2017-03-22T13:37:51Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 22, 2017 at 09:34:12AM -0400, Jeff King wrote:\n\n> On Sat, Mar 18, 2017 at 09:03:34AM +0700, Nguyễn Thái Ngọc Duy wrote:\n> \n> > diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> > new file mode 100644\n> \n> When nd/files-backend-git-dir is merged to 'next', the new tests in\n> t1405/t1406 break. They need this on top (which should probably just be\n> squashed into this commit when you re-roll).\n> \n> -- >8 --\n> Subject: [PATCH] test-ref-store: setup git directory\n> \n> Without setting up the git directory, we rely on the \".git\"\n> fallback in setup_git_env(). This will cause us to abort\n> once b1ef400ee (setup_git_env: avoid blind fall-back to\n> \".git\", 2016-10-20) is merged.\n\nAfter posting this, it occurred to me that \"pu\" should be showing the\nsame failure, but it doesn't. The reason is that e9e167145 (worktree.c:\nkill parse_ref() in favor of refs_resolve_ref_unsafe(), 2017-03-18) from\nnd/worktree-kill-parse-ref sneaks in the setup call.\n\nWe should move it back to the addition of test-ref-store, though, since\nnd/files-backend-git-dir may graduate separately.\n\n-Peff\n"},{"id":"314901","messageId":"20170322133412.e2oqmqekreyuf4gr@sigill.intra.peff.net","threadId":"45126","inReplyTo":"20170318020337.22767-25-pclouds@gmail.com","subject":"Re: [PATCH v6 24/27] t/helper: add test-ref-store to test ref-store functions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-03-22T13:34:12Z","receivedAt":"2017-03-22T13:41:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Mar 18, 2017 at 09:03:34AM +0700, Nguyễn Thái Ngọc Duy wrote:\n\n> diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n> new file mode 100644\n\nWhen nd/files-backend-git-dir is merged to 'next', the new tests in\nt1405/t1406 break. They need this on top (which should probably just be\nsquashed into this commit when you re-roll).\n\n-- >8 --\nSubject: [PATCH] test-ref-store: setup git directory\n\nWithout setting up the git directory, we rely on the \".git\"\nfallback in setup_git_env(). This will cause us to abort\nonce b1ef400ee (setup_git_env: avoid blind fall-back to\n\".git\", 2016-10-20) is merged.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n t/helper/test-ref-store.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 57849ee59..2d84c45ff 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -261,6 +261,8 @@ int cmd_main(int argc, const char **argv)\n \tconst char *func;\n \tstruct command *cmd;\n \n+\tsetup_git_directory();\n+\n \targv = get_store(argv + 1, &refs);\n \n \tfunc = *argv++;\n-- \n2.12.1.788.g3cf5b5ec6\n\n"},{"id":"315382","messageId":"CACsJy8Bkzf8okX6oroHoXocBxfKvZQP469K_=fU==Ekv2DTxUQ@mail.gmail.com","threadId":"45126","inReplyTo":"20170322133735.ozjbncfm5tchygoc@sigill.intra.peff.net","subject":"Re: [PATCH v6 24/27] t/helper: add test-ref-store to test ref-store functions","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-25T11:54:18Z","receivedAt":"2017-03-25T11:54:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 22, 2017 at 8:37 PM, Jeff King <peff@peff.net> wrote:\n> On Wed, Mar 22, 2017 at 09:34:12AM -0400, Jeff King wrote:\n>\n>> On Sat, Mar 18, 2017 at 09:03:34AM +0700, Nguyễn Thái Ngọc Duy wrote:\n>>\n>> > diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\n>> > new file mode 100644\n>>\n>> When nd/files-backend-git-dir is merged to 'next', the new tests in\n>> t1405/t1406 break. They need this on top (which should probably just be\n>> squashed into this commit when you re-roll).\n>>\n>> -- >8 --\n>> Subject: [PATCH] test-ref-store: setup git directory\n>>\n>> Without setting up the git directory, we rely on the \".git\"\n>> fallback in setup_git_env(). This will cause us to abort\n>> once b1ef400ee (setup_git_env: avoid blind fall-back to\n>> \".git\", 2016-10-20) is merged.\n>\n> After posting this, it occurred to me that \"pu\" should be showing the\n> same failure, but it doesn't. The reason is that e9e167145 (worktree.c:\n> kill parse_ref() in favor of refs_resolve_ref_unsafe(), 2017-03-18) from\n> nd/worktree-kill-parse-ref sneaks in the setup call.\n>\n> We should move it back to the addition of test-ref-store, though, since\n> nd/files-backend-git-dir may graduate separately.\n\nRight. I needed that setup_git_directory() for the third ref store.\nBut now that I think about it, I need it for the first two as well\nbecause git_path() is always involved. Will fix (since it looks like\nfiles-backend-git-dir is not merged to next yet).\n-- \nDuy\n"},{"id":"315417","messageId":"CACsJy8CpUzRjZG7V8a6XvzcjxhwTnAfpwYed3OSLrygUPOK66g@mail.gmail.com","threadId":"45126","inReplyTo":"004082a0-871f-cd41-edc5-522b5c2b3b58@alum.mit.edu","subject":"Re: [PATCH v6 18/27] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:16:56Z","receivedAt":"2017-03-26T02:18:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 20, 2017 at 4:18 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> +/* ref_store_init flags */\n>> +#define REF_STORE_READ               (1 << 0)\n>\n> I asked [1] in reply to v5 whether `REF_STORE_READ` is really necessary\n> but I don't think you replied. Surely a reference store that we can't\n> read would be useless? Can't we just assume that any `ref_store` is\n> readable and drop this constant?\n\nI deleted it then I realized many lines like these\n\n    files_downcast(ref_store, REF_STORE_READ, \"read_raw_ref\");\n\nwould become\n\n    files_downcast(ref_store, 0, \"read_raw_ref\");\n\nI think for newcomers, the former gives a better hint what the second\nargument is than a plain zero in the latter, so I'm inclined to keep\nit.\n-- \nDuy\n"},{"id":"315419","messageId":"20170326024241.31984-1-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170318020337.22767-1-pclouds@gmail.com","subject":"[PATCH v7 00/28] Remove submodule from files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:13Z","receivedAt":"2017-03-26T02:42:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v7 is mostly about style changes except the one bug in\ntest-ref-store.c, missing setup_git_directory().\n\nThere's one new patch, 03/28, which maps to the \"if (!refs)\" deletion\nin the interdiff.\n\nThe one comment from v6 I haven't addressed in v7 is whether to delete\nREF_STORE_READ. But if it is deleted, I think I'm doing it in\nnd/worktree-kill-parse-ref, which is partly about cleanup refs code\nanyway, to avoid another re-roll in this series.\n\ndiff --git a/refs.c b/refs.c\nindex 77a39f8b17..ec1f563824 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1523,18 +1523,15 @@ static struct ref_store *ref_store_init(const char *gitdir,\n \n struct ref_store *get_main_ref_store(void)\n {\n-\tstruct ref_store *refs;\n-\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\trefs = ref_store_init(get_git_dir(),\n-\t\t\t      (REF_STORE_READ |\n-\t\t\t       REF_STORE_WRITE |\n-\t\t\t       REF_STORE_ODB |\n-\t\t\t       REF_STORE_MAIN));\n-\tmain_ref_store = refs;\n-\treturn refs;\n+\tmain_ref_store = ref_store_init(get_git_dir(),\n+\t\t\t\t\t(REF_STORE_READ |\n+\t\t\t\t\t REF_STORE_WRITE |\n+\t\t\t\t\t REF_STORE_ODB |\n+\t\t\t\t\t REF_STORE_MAIN));\n+\treturn main_ref_store;\n }\n \n /*\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 4242486118..a5b405436f 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1195,9 +1195,9 @@ static void files_reflog_path(struct files_ref_store *refs,\n \t}\n }\n \n-static void files_refname_path(struct files_ref_store *refs,\n-\t\t\t       struct strbuf *sb,\n-\t\t\t       const char *refname)\n+static void files_ref_path(struct files_ref_store *refs,\n+\t\t\t   struct strbuf *sb,\n+\t\t\t   const char *refname)\n {\n \tswitch (ref_type(refname)) {\n \tcase REF_TYPE_PER_WORKTREE:\n@@ -1283,7 +1283,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tstruct strbuf path = STRBUF_INIT;\n \tsize_t path_baselen;\n \n-\tfiles_refname_path(refs, &path, dirname);\n+\tfiles_ref_path(refs, &path, dirname);\n \tpath_baselen = path.len;\n \n \td = opendir(path.buf);\n@@ -1420,7 +1420,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tfiles_refname_path(refs, &sb_path, refname);\n+\tfiles_ref_path(refs, &sb_path, refname);\n \n \tpath = sb_path.buf;\n \n@@ -1608,7 +1608,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tfiles_refname_path(refs, &ref_file, refname);\n+\tfiles_ref_path(refs, &ref_file, refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -1949,8 +1949,6 @@ static struct ref_iterator *files_ref_iterator_begin(\n \trefs = files_downcast(ref_store,\n \t\t\t      REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),\n \t\t\t      \"ref_iterator_begin\");\n-\tif (!refs)\n-\t\treturn empty_ref_iterator_begin();\n \n \titer = xcalloc(1, sizeof(*iter));\n \tref_iterator = &iter->base;\n@@ -2086,7 +2084,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tfiles_refname_path(refs, &ref_file, refname);\n+\tfiles_ref_path(refs, &ref_file, refname);\n \tresolved = !!refs_resolve_ref_unsafe(&refs->base,\n \t\t\t\t\t     refname, resolve_flags,\n \t\t\t\t\t     lock->old_oid.hash, type);\n@@ -2387,7 +2385,7 @@ static void try_remove_empty_parents(struct files_ref_store *refs,\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n \n \t\tstrbuf_reset(&sb);\n-\t\tfiles_refname_path(refs, &sb, buf.buf);\n+\t\tfiles_ref_path(refs, &sb, buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n@@ -2709,7 +2707,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tfiles_refname_path(refs, &path, newrefname);\n+\t\t\tfiles_ref_path(refs, &path, newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -2935,10 +2933,10 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-int files_log_ref_write(struct files_ref_store *refs,\n-\t\t\tconst char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err)\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err)\n {\n \tint logfd, result;\n \n@@ -3957,7 +3955,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n \t\t\t\tstrbuf_reset(&sb);\n-\t\t\t\tfiles_refname_path(refs, &sb, lock->ref_name);\n+\t\t\t\tfiles_ref_path(refs, &sb, lock->ref_name);\n \t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n@@ -4266,11 +4264,11 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tfiles_refname_path(refs, &sb, \"refs/heads\");\n+\tfiles_ref_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \n \tstrbuf_reset(&sb);\n-\tfiles_refname_path(refs, &sb, \"refs/tags\");\n+\tfiles_ref_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \n \tstrbuf_release(&sb);\ndiff --git a/submodule.c b/submodule.c\nindex 3c445f274e..a31f68812c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1597,11 +1597,6 @@ const char *get_superproject_working_tree(void)\n \treturn ret;\n }\n \n-/*\n- * Given a submodule path (as in the index), return the repository\n- * path of that submodule in 'buf'. Return -1 on error or when the\n- * submodule is not initialized.\n- */\n int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n {\n \tconst struct submodule *sub;\ndiff --git a/submodule.h b/submodule.h\nindex bddbba7f32..fce2fb64d2 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -81,6 +81,11 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n \t\t\t\t    int dry_run);\n extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n extern int parallel_submodules(void);\n+/*\n+ * Given a submodule path (as in the index), return the repository\n+ * path of that submodule in 'buf'. Return -1 on error or when the\n+ * submodule is not initialized.\n+ */\n int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n \n /*\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 57849ee59a..2d84c45ffe 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -261,6 +261,8 @@ int cmd_main(int argc, const char **argv)\n \tconst char *func;\n \tstruct command *cmd;\n \n+\tsetup_git_directory();\n+\n \targv = get_store(argv + 1, &refs);\n \n \tfunc = *argv++;\n\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315420","messageId":"20170326024241.31984-2-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 01/28] refs.h: add forward declaration for structs used in this file","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:14Z","receivedAt":"2017-03-26T02:43:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.h | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/refs.h b/refs.h\nindex 3df0d45ebb..2d6b6263fc 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -1,6 +1,10 @@\n #ifndef REFS_H\n #define REFS_H\n \n+struct object_id;\n+struct strbuf;\n+struct string_list;\n+\n /*\n  * Resolve a reference, recursively following symbolic refererences.\n  *\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315421","messageId":"20170326024241.31984-3-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 02/28] files-backend: make files_log_ref_write() static","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:15Z","receivedAt":"2017-03-26T02:43:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Created in 5f3c3a4e6f (files_log_ref_write: new function - 2015-11-10)\nbut probably never used outside refs-internal.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 9 ++++++---\n refs/refs-internal.h | 4 ----\n 2 files changed, 6 insertions(+), 7 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 50188e92f9..42c0cc14f3 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,6 +165,9 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err);\n \n static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n {\n@@ -2831,9 +2834,9 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err)\n+static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+\t\t\t       const unsigned char *new_sha1, const char *msg,\n+\t\t\t       int flags, struct strbuf *err)\n {\n \tint logfd, result;\n \ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex fa93c9a32e..f732473e1d 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -228,10 +228,6 @@ struct ref_transaction {\n \tenum ref_transaction_state state;\n };\n \n-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n-\t\t\tconst unsigned char *new_sha1, const char *msg,\n-\t\t\tint flags, struct strbuf *err);\n-\n /*\n  * Check for entries in extras that are within the specified\n  * directory, where dirname is a reference directory name including\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315422","messageId":"20170326024241.31984-5-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 04/28] files-backend: delete dead code in files_init_db()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:17Z","receivedAt":"2017-03-26T02:43:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"safe_create_dir() can do adjust_shared_perm() internally, and init-db\nhas always created 'refs' in shared mode since the beginning,\naf6e277c5e (git-init-db: initialize shared repositories with --shared -\n2005-12-22). So this code looks like extra adjust_shared_perm calls are\nunnecessary.\n\nAnd they are. But let's see why there are here in the first place.\n\nThis code was added in 6fb5acfd8f (refs: add methods to init refs db -\n2016-09-04). From the diff alone this looks like a faithful refactored\ncode from init-db.c. But there is a subtle difference:\n\nBetween the safe_create_dir() block and adjust_shared_perm() block in\nthe old init-db.c, we may copy/recreate directories from the repo\ntemplate. So it makes sense that adjust_shared_perm() is re-executed\nthen to fix potential permission screwups.\n\nAfter 6fb5acfd8f, refs dirs are created after template is copied. Nobody\nwill change directory permission again. So the extra adjust_shared_perm()\nis redudant. Delete them.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 4 ----\n 1 file changed, 4 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex caeb8188fd..9a9dc4e50c 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -4107,10 +4107,6 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n \t */\n \tsafe_create_dir(git_path(\"refs/heads\"), 1);\n \tsafe_create_dir(git_path(\"refs/tags\"), 1);\n-\tif (get_shared_repository()) {\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n-\t}\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315423","messageId":"20170326024241.31984-4-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 03/28] files-backend.c: delete dead code in files_ref_iterator_begin()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:16Z","receivedAt":"2017-03-26T02:43:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's not in the diff context, but files_downcast() is called before this\ncheck. If \"refs\" is NULL, we would have segfaulted before reaching the\ncheck here. And we should never see NULL refs in backend code (frontend\nshould have caught it).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 ---\n 1 file changed, 3 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 42c0cc14f3..caeb8188fd 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1897,9 +1897,6 @@ static struct ref_iterator *files_ref_iterator_begin(\n \tstruct files_ref_iterator *iter;\n \tstruct ref_iterator *ref_iterator;\n \n-\tif (!refs)\n-\t\treturn empty_ref_iterator_begin();\n-\n \tif (ref_paranoia < 0)\n \t\tref_paranoia = git_env_bool(\"GIT_REF_PARANOIA\", 0);\n \tif (ref_paranoia)\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315424","messageId":"20170326024241.31984-6-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 05/28] files-backend: add and use files_packed_refs_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:18Z","receivedAt":"2017-03-26T02:43:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 29 ++++++++++++++++++-----------\n 1 file changed, 18 insertions(+), 11 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 9a9dc4e50c..f17daddffa 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -923,6 +923,8 @@ struct files_ref_store {\n \t */\n \tconst char *submodule;\n \n+\tchar *packed_refs_path;\n+\n \tstruct ref_entry *loose;\n \tstruct packed_ref_cache *packed;\n };\n@@ -985,7 +987,14 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\trefs->submodule = xstrdup_or_null(submodule);\n+\tif (submodule) {\n+\t\trefs->submodule = xstrdup(submodule);\n+\t\trefs->packed_refs_path = git_pathdup_submodule(\n+\t\t\trefs->submodule, \"packed-refs\");\n+\t\treturn ref_store;\n+\t}\n+\n+\trefs->packed_refs_path = git_pathdup(\"packed-refs\");\n \n \treturn ref_store;\n }\n@@ -1153,19 +1162,18 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n \tstrbuf_release(&line);\n }\n \n+static const char *files_packed_refs_path(struct files_ref_store *refs)\n+{\n+\treturn refs->packed_refs_path;\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n  */\n static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)\n {\n-\tchar *packed_refs_file;\n-\n-\tif (refs->submodule)\n-\t\tpacked_refs_file = git_pathdup_submodule(refs->submodule,\n-\t\t\t\t\t\t\t \"packed-refs\");\n-\telse\n-\t\tpacked_refs_file = git_pathdup(\"packed-refs\");\n+\tconst char *packed_refs_file = files_packed_refs_path(refs);\n \n \tif (refs->packed &&\n \t    !stat_validity_check(&refs->packed->validity, packed_refs_file))\n@@ -1184,7 +1192,6 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref\n \t\t\tfclose(f);\n \t\t}\n \t}\n-\tfree(packed_refs_file);\n \treturn refs->packed;\n }\n \n@@ -2157,7 +2164,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)\n \t}\n \n \tif (hold_lock_file_for_update_timeout(\n-\t\t\t    &packlock, git_path(\"packed-refs\"),\n+\t\t\t    &packlock, files_packed_refs_path(refs),\n \t\t\t    flags, timeout_value) < 0)\n \t\treturn -1;\n \t/*\n@@ -2423,7 +2430,7 @@ static int repack_without_refs(struct files_ref_store *refs,\n \t\treturn 0; /* no refname exists in packed refs */\n \n \tif (lock_packed_refs(refs, 0)) {\n-\t\tunable_to_lock_message(git_path(\"packed-refs\"), errno, err);\n+\t\tunable_to_lock_message(files_packed_refs_path(refs), errno, err);\n \t\treturn -1;\n \t}\n \tpacked = get_packed_refs(refs);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315425","messageId":"20170326024241.31984-7-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 06/28] files-backend: make sure files_rename_ref() always reach the end","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:19Z","receivedAt":"2017-03-26T02:44:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a no-op patch. It prepares the function so that we can release\nresources (to be added later in this function) before we return.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 42 ++++++++++++++++++++++++++++--------------\n 1 file changed, 28 insertions(+), 14 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex f17daddffa..6d0fcc88f9 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2585,23 +2585,34 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tstruct stat loginfo;\n \tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n \tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n \n-\tif (log && S_ISLNK(loginfo.st_mode))\n-\t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n+\tif (log && S_ISLNK(loginfo.st_mode)) {\n+\t\tret = error(\"reflog for %s is a symlink\", oldrefname);\n+\t\tgoto out;\n+\t}\n \n \tif (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n-\t\t\t\torig_sha1, &flag))\n-\t\treturn error(\"refname %s not found\", oldrefname);\n+\t\t\t\torig_sha1, &flag)) {\n+\t\tret = error(\"refname %s not found\", oldrefname);\n+\t\tgoto out;\n+\t}\n \n-\tif (flag & REF_ISSYMREF)\n-\t\treturn error(\"refname %s is a symbolic ref, renaming it is not supported\",\n-\t\t\toldrefname);\n-\tif (!rename_ref_available(oldrefname, newrefname))\n-\t\treturn 1;\n+\tif (flag & REF_ISSYMREF) {\n+\t\tret = error(\"refname %s is a symbolic ref, renaming it is not supported\",\n+\t\t\t    oldrefname);\n+\t\tgoto out;\n+\t}\n+\tif (!rename_ref_available(oldrefname, newrefname)) {\n+\t\tret = 1;\n+\t\tgoto out;\n+\t}\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n-\t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n-\t\t\toldrefname, strerror(errno));\n+\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG))) {\n+\t\tret = error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n+\t\t\t    oldrefname, strerror(errno));\n+\t\tgoto out;\n+\t}\n \n \tif (delete_ref(logmsg, oldrefname, orig_sha1, REF_NODEREF)) {\n \t\terror(\"unable to delete old %s\", oldrefname);\n@@ -2657,7 +2668,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto rollback;\n \t}\n \n-\treturn 0;\n+\tret = 0;\n+\tgoto out;\n \n  rollback:\n \tlock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,\n@@ -2686,7 +2698,9 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \n-\treturn 1;\n+\tret = 1;\n+ out:\n+\treturn ret;\n }\n \n static int close_ref(struct ref_lock *lock)\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315426","messageId":"20170326024241.31984-9-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 08/28] files-backend: move \"logs/\" out of TMP_RENAMED_LOG","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:21Z","receivedAt":"2017-03-26T02:44:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes reflog path building consistent, always in the form of\n\n    strbuf_git_path(sb, \"logs/%s\", refname);\n\nIt reduces the mental workload a bit in the next patch when that\nfunction call is converted.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 5f9b0ab9bc..3e0bafcaf9 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2510,7 +2510,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n  * IOW, to avoid cross device rename errors, the temporary renamed log must\n  * live into logs/refs.\n  */\n-#define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n+#define TMP_RENAMED_LOG  \"refs/.tmp-renamed-log\"\n \n struct rename_cb {\n \tconst char *tmp_renamed_log;\n@@ -2546,7 +2546,7 @@ static int rename_tmp_log(const char *newrefname)\n \tint ret;\n \n \tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp, TMP_RENAMED_LOG);\n+\tstrbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n \tcb.tmp_renamed_log = tmp.buf;\n \tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n@@ -2608,7 +2608,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \n \tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n \tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n \n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tif (log && S_ISLNK(loginfo.st_mode)) {\n@@ -2633,7 +2633,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t}\n \n \tif (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {\n-\t\tret = error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n+\t\tret = error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\t    oldrefname, strerror(errno));\n \t\tgoto out;\n \t}\n@@ -2719,7 +2719,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\toldrefname, newrefname, strerror(errno));\n \tif (!logmoved && log &&\n \t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n-\t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n+\t\terror(\"unable to restore logfile %s from logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n \tret = 1;\n  out:\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315427","messageId":"20170326024241.31984-8-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 07/28] files-backend: convert git_path() to strbuf_git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:20Z","receivedAt":"2017-03-26T02:44:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"git_path() and friends are going to be killed in files-backend.c in near\nfuture. And because there's a risk with overwriting buffer in\ngit_path(), let's convert them all to strbuf_git_path(). We'll have\neasier time killing/converting strbuf_git_path() then because we won't\nhave to worry about memory management again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 130 ++++++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 97 insertions(+), 33 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 6d0fcc88f9..5f9b0ab9bc 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -2316,6 +2316,7 @@ enum {\n static void try_remove_empty_parents(const char *refname, unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n \tchar *p, *q;\n \tint i;\n \n@@ -2337,14 +2338,19 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\tif (q == p)\n \t\t\tbreak;\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) &&\n-\t\t    rmdir(git_path(\"%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n-\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&\n-\t\t    rmdir(git_path(\"logs/%s\", buf.buf)))\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n \tstrbuf_release(&buf);\n+\tstrbuf_release(&sb);\n }\n \n /* make sure nobody touched the ref, and unlink */\n@@ -2506,11 +2512,16 @@ static int files_delete_refs(struct ref_store *ref_store,\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-static int rename_tmp_log_callback(const char *path, void *cb)\n+struct rename_cb {\n+\tconst char *tmp_renamed_log;\n+\tint true_errno;\n+};\n+\n+static int rename_tmp_log_callback(const char *path, void *cb_data)\n {\n-\tint *true_errno = cb;\n+\tstruct rename_cb *cb = cb_data;\n \n-\tif (rename(git_path(TMP_RENAMED_LOG), path)) {\n+\tif (rename(cb->tmp_renamed_log, path)) {\n \t\t/*\n \t\t * rename(a, b) when b is an existing directory ought\n \t\t * to result in ISDIR, but Solaris 5.8 gives ENOTDIR.\n@@ -2518,7 +2529,7 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \t\t * but report EISDIR to raceproof_create_file() so\n \t\t * that it knows to retry.\n \t\t */\n-\t\t*true_errno = errno;\n+\t\tcb->true_errno = errno;\n \t\tif (errno == ENOTDIR)\n \t\t\terrno = EISDIR;\n \t\treturn -1;\n@@ -2529,20 +2540,26 @@ static int rename_tmp_log_callback(const char *path, void *cb)\n \n static int rename_tmp_log(const char *newrefname)\n {\n-\tchar *path = git_pathdup(\"logs/%s\", newrefname);\n-\tint ret, true_errno;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tstruct rename_cb cb;\n+\tint ret;\n \n-\tret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);\n+\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&tmp, TMP_RENAMED_LOG);\n+\tcb.tmp_renamed_log = tmp.buf;\n+\tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n \t\tif (errno == EISDIR)\n-\t\t\terror(\"directory not empty: %s\", path);\n+\t\t\terror(\"directory not empty: %s\", path.buf);\n \t\telse\n \t\t\terror(\"unable to move logfile %s to %s: %s\",\n-\t\t\t      git_path(TMP_RENAMED_LOG), path,\n-\t\t\t      strerror(true_errno));\n+\t\t\t      tmp.buf, path.buf,\n+\t\t\t      strerror(cb.true_errno));\n \t}\n \n-\tfree(path);\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&tmp);\n \treturn ret;\n }\n \n@@ -2583,10 +2600,17 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n+\tstruct strbuf sb_oldref = STRBUF_INIT;\n+\tstruct strbuf sb_newref = STRBUF_INIT;\n+\tstruct strbuf tmp_renamed_log = STRBUF_INIT;\n+\tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n-\tint ret;\n \n+\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n+\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n+\tstrbuf_git_path(&tmp_renamed_log, TMP_RENAMED_LOG);\n+\n+\tlog = !lstat(sb_oldref.buf, &loginfo);\n \tif (log && S_ISLNK(loginfo.st_mode)) {\n \t\tret = error(\"reflog for %s is a symlink\", oldrefname);\n \t\tgoto out;\n@@ -2608,7 +2632,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG))) {\n+\tif (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {\n \t\tret = error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n \t\t\t    oldrefname, strerror(errno));\n \t\tgoto out;\n@@ -2690,16 +2714,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n+\tif (logmoved && rename(sb_newref.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n \t\t\toldrefname, newrefname, strerror(errno));\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n+\t    rename(tmp_renamed_log.buf, sb_oldref.buf))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n \t\t\toldrefname, strerror(errno));\n-\n \tret = 1;\n  out:\n+\tstrbuf_release(&sb_newref);\n+\tstrbuf_release(&sb_oldref);\n+\tstrbuf_release(&tmp_renamed_log);\n+\n \treturn ret;\n }\n \n@@ -2872,18 +2899,24 @@ static int files_log_ref_write(const char *refname, const unsigned char *old_sha\n \tresult = log_ref_write_fd(logfd, old_sha1, new_sha1,\n \t\t\t\t  git_committer_info(0), msg);\n \tif (result) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\tclose(logfd);\n \t\treturn -1;\n \t}\n \tif (close(logfd)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n+\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n-\t\t\t    git_path(\"logs/%s\", refname), strerror(save_errno));\n+\t\t\t    sb.buf, strerror(save_errno));\n+\t\tstrbuf_release(&sb);\n \t\treturn -1;\n \t}\n \treturn 0;\n@@ -3103,22 +3136,32 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n+\tint ret;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \n-\treturn !lstat(git_path(\"logs/%s\", refname), &st) &&\n-\t\tS_ISREG(st.st_mode);\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint ret;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \n-\treturn remove_path(git_path(\"logs/%s\", refname));\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tret = remove_path(sb.buf);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n }\n \n static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)\n@@ -3174,7 +3217,9 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3280,7 +3325,9 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \n-\tlogfp = fopen(git_path(\"logs/%s\", refname), \"r\");\n+\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tlogfp = fopen(sb.buf, \"r\");\n+\tstrbuf_release(&sb);\n \tif (!logfp)\n \t\treturn -1;\n \n@@ -3362,12 +3409,15 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n {\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\titer->dir_iterator = dir_iterator_begin(git_path(\"logs\"));\n+\tstrbuf_git_path(&sb, \"logs\");\n+\titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n \n@@ -3705,6 +3755,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tchar *head_ref = NULL;\n \tint head_type;\n \tstruct object_id head_oid;\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tassert(err);\n \n@@ -3826,7 +3877,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\tif (!(update->type & REF_ISPACKED) ||\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n-\t\t\t\tif (unlink_or_msg(git_path(\"%s\", lock->ref_name), err)) {\n+\t\t\t\tstrbuf_reset(&sb);\n+\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n \t\t\t\t}\n@@ -3846,7 +3899,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t/* Delete the reflogs of any references that were deleted: */\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n-\t\tif (!unlink_or_warn(git_path(\"logs/%s\", ref_to_delete->string)))\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tif (!unlink_or_warn(sb.buf))\n \t\t\ttry_remove_empty_parents(ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n \t}\n@@ -3854,6 +3909,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \tclear_loose_ref_cache(refs);\n \n cleanup:\n+\tstrbuf_release(&sb);\n \ttransaction->state = REF_TRANSACTION_CLOSED;\n \n \tfor (i = 0; i < transaction->nr; i++) {\n@@ -4120,14 +4176,22 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n \t/* Check validity (but we don't need the result): */\n \tfiles_downcast(ref_store, 0, \"init_db\");\n \n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tsafe_create_dir(sb.buf, 1);\n+\n+\tstrbuf_reset(&sb);\n+\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tsafe_create_dir(sb.buf, 1);\n+\n+\tstrbuf_release(&sb);\n \treturn 0;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315428","messageId":"20170326024241.31984-11-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 10/28] files-backend: add and use files_ref_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:23Z","receivedAt":"2017-03-26T02:44:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nThis automatically adds the \"if submodule then use the submodule version\nof git_path\" to other call sites too. But it does not mean those\noperations are submodule-ready. Not yet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 47 +++++++++++++++++++++++------------------------\n 1 file changed, 23 insertions(+), 24 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex b0e0df9f66..741e52d143 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1180,6 +1180,18 @@ static void files_reflog_path(struct files_ref_store *refs,\n \tstrbuf_git_path(sb, \"logs/%s\", refname);\n }\n \n+static void files_ref_path(struct files_ref_store *refs,\n+\t\t\t   struct strbuf *sb,\n+\t\t\t   const char *refname)\n+{\n+\tif (refs->submodule) {\n+\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_git_path(sb, \"%s\", refname);\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n@@ -1249,19 +1261,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \tstruct strbuf refname;\n \tstruct strbuf path = STRBUF_INIT;\n \tsize_t path_baselen;\n-\tint err = 0;\n \n-\tif (refs->submodule)\n-\t\terr = strbuf_git_path_submodule(&path, refs->submodule, \"%s\", dirname);\n-\telse\n-\t\tstrbuf_git_path(&path, \"%s\", dirname);\n+\tfiles_ref_path(refs, &path, dirname);\n \tpath_baselen = path.len;\n \n-\tif (err) {\n-\t\tstrbuf_release(&path);\n-\t\treturn;\n-\t}\n-\n \td = opendir(path.buf);\n \tif (!d) {\n \t\tstrbuf_release(&path);\n@@ -1396,10 +1399,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t*type = 0;\n \tstrbuf_reset(&sb_path);\n \n-\tif (refs->submodule)\n-\t\tstrbuf_git_path_submodule(&sb_path, refs->submodule, \"%s\", refname);\n-\telse\n-\t\tstrbuf_git_path(&sb_path, \"%s\", refname);\n+\tfiles_ref_path(refs, &sb_path, refname);\n \n \tpath = sb_path.buf;\n \n@@ -1587,7 +1587,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t*lock_p = lock = xcalloc(1, sizeof(*lock));\n \n \tlock->ref_name = xstrdup(refname);\n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_ref_path(refs, &ref_file, refname);\n \n retry:\n \tswitch (safe_create_leading_directories(ref_file.buf)) {\n@@ -2056,7 +2056,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \tif (flags & REF_DELETING)\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n-\tstrbuf_git_path(&ref_file, \"%s\", refname);\n+\tfiles_ref_path(refs, &ref_file, refname);\n \tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\tlock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n@@ -2355,7 +2355,7 @@ static void try_remove_empty_parents(struct files_ref_store *refs,\n \t\tstrbuf_setlen(&buf, q - buf.buf);\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"%s\", buf.buf);\n+\t\tfiles_ref_path(refs, &sb, buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n@@ -2672,7 +2672,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n \n-\t\t\tstrbuf_git_path(&path, \"%s\", newrefname);\n+\t\t\tfiles_ref_path(refs, &path, newrefname);\n \t\t\tresult = remove_empty_directories(&path);\n \t\t\tstrbuf_release(&path);\n \n@@ -3906,7 +3906,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t    update->type & REF_ISSYMREF) {\n \t\t\t\t/* It is a loose reference. */\n \t\t\t\tstrbuf_reset(&sb);\n-\t\t\t\tstrbuf_git_path(&sb, \"%s\", lock->ref_name);\n+\t\t\t\tfiles_ref_path(refs, &sb, lock->ref_name);\n \t\t\t\tif (unlink_or_msg(sb.buf, err)) {\n \t\t\t\t\tret = TRANSACTION_GENERIC_ERROR;\n \t\t\t\t\tgoto cleanup;\n@@ -4206,19 +4206,18 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"init_db\");\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tstrbuf_git_path(&sb, \"refs/heads\");\n+\tfiles_ref_path(refs, &sb, \"refs/heads\");\n \tsafe_create_dir(sb.buf, 1);\n \n \tstrbuf_reset(&sb);\n-\tstrbuf_git_path(&sb, \"refs/tags\");\n+\tfiles_ref_path(refs, &sb, \"refs/tags\");\n \tsafe_create_dir(sb.buf, 1);\n \n \tstrbuf_release(&sb);\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315429","messageId":"20170326024241.31984-10-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 09/28] files-backend: add and use files_reflog_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:22Z","receivedAt":"2017-03-26T02:44:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Keep repo-related path handling in one place. This will make it easier\nto add submodule/multiworktree support later.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 142 +++++++++++++++++++++++++++++++--------------------\n 1 file changed, 86 insertions(+), 56 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 3e0bafcaf9..b0e0df9f66 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -165,7 +165,8 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,\n \t\t\t\t\t  const char *dirname, size_t len,\n \t\t\t\t\t  int incomplete);\n static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);\n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err);\n \n@@ -1167,6 +1168,18 @@ static const char *files_packed_refs_path(struct files_ref_store *refs)\n \treturn refs->packed_refs_path;\n }\n \n+static void files_reflog_path(struct files_ref_store *refs,\n+\t\t\t      struct strbuf *sb,\n+\t\t\t      const char *refname)\n+{\n+\tif (!refname) {\n+\t\tstrbuf_git_path(sb, \"logs\");\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_git_path(sb, \"logs/%s\", refname);\n+}\n+\n /*\n  * Get the packed_ref_cache for the specified files_ref_store,\n  * creating it if necessary.\n@@ -2313,7 +2326,9 @@ enum {\n  * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or\n  * REMOVE_EMPTY_PARENTS_REFLOG.\n  */\n-static void try_remove_empty_parents(const char *refname, unsigned int flags)\n+static void try_remove_empty_parents(struct files_ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     unsigned int flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -2345,7 +2360,7 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REF;\n \n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"logs/%s\", buf.buf);\n+\t\tfiles_reflog_path(refs, &sb, buf.buf);\n \t\tif ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))\n \t\t\tflags &= ~REMOVE_EMPTY_PARENTS_REFLOG;\n \t}\n@@ -2538,15 +2553,15 @@ static int rename_tmp_log_callback(const char *path, void *cb_data)\n \t}\n }\n \n-static int rename_tmp_log(const char *newrefname)\n+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)\n {\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf tmp = STRBUF_INIT;\n \tstruct rename_cb cb;\n \tint ret;\n \n-\tstrbuf_git_path(&path, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp, \"logs/%s\", TMP_RENAMED_LOG);\n+\tfiles_reflog_path(refs, &path, newrefname);\n+\tfiles_reflog_path(refs, &tmp, TMP_RENAMED_LOG);\n \tcb.tmp_renamed_log = tmp.buf;\n \tret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);\n \tif (ret) {\n@@ -2606,9 +2621,9 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tint log, ret;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tstrbuf_git_path(&sb_oldref, \"logs/%s\", oldrefname);\n-\tstrbuf_git_path(&sb_newref, \"logs/%s\", newrefname);\n-\tstrbuf_git_path(&tmp_renamed_log, \"logs/%s\", TMP_RENAMED_LOG);\n+\tfiles_reflog_path(refs, &sb_oldref, oldrefname);\n+\tfiles_reflog_path(refs, &sb_newref, newrefname);\n+\tfiles_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);\n \n \tlog = !lstat(sb_oldref.buf, &loginfo);\n \tif (log && S_ISLNK(loginfo.st_mode)) {\n@@ -2671,7 +2686,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t}\n \t}\n \n-\tif (log && rename_tmp_log(newrefname))\n+\tif (log && rename_tmp_log(refs, newrefname))\n \t\tgoto rollback;\n \n \tlogmoved = log;\n@@ -2786,10 +2801,15 @@ static int open_or_create_logfile(const char *path, void *cb)\n  * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and\n  * return -1.\n  */\n-static int log_ref_setup(const char *refname, int force_create,\n+static int log_ref_setup(struct files_ref_store *refs,\n+\t\t\t const char *refname, int force_create,\n \t\t\t int *logfd, struct strbuf *err)\n {\n-\tchar *logfile = git_pathdup(\"logs/%s\", refname);\n+\tstruct strbuf logfile_sb = STRBUF_INIT;\n+\tchar *logfile;\n+\n+\tfiles_reflog_path(refs, &logfile_sb, refname);\n+\tlogfile = strbuf_detach(&logfile_sb, NULL);\n \n \tif (force_create || should_autocreate_reflog(refname)) {\n \t\tif (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {\n@@ -2839,12 +2859,11 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname, int force_create,\n \t\t\t       struct strbuf *err)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n \tint fd;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"create_reflog\");\n-\n-\tif (log_ref_setup(refname, force_create, &fd, err))\n+\tif (log_ref_setup(refs, refname, force_create, &fd, err))\n \t\treturn -1;\n \n \tif (fd >= 0)\n@@ -2879,7 +2898,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n-static int files_log_ref_write(const char *refname, const unsigned char *old_sha1,\n+static int files_log_ref_write(struct files_ref_store *refs,\n+\t\t\t       const char *refname, const unsigned char *old_sha1,\n \t\t\t       const unsigned char *new_sha1, const char *msg,\n \t\t\t       int flags, struct strbuf *err)\n {\n@@ -2888,7 +2908,8 @@ static int files_log_ref_write(const char *refname, const unsigned char *old_sha\n \tif (log_all_ref_updates == LOG_REFS_UNSET)\n \t\tlog_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;\n \n-\tresult = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,\n+\tresult = log_ref_setup(refs, refname,\n+\t\t\t       flags & REF_FORCE_CREATE_REFLOG,\n \t\t\t       &logfd, err);\n \n \tif (result)\n@@ -2902,7 +2923,7 @@ static int files_log_ref_write(const char *refname, const unsigned char *old_sha\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_reflog_path(refs, &sb, refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2913,7 +2934,7 @@ static int files_log_ref_write(const char *refname, const unsigned char *old_sha\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tint save_errno = errno;\n \n-\t\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\t\tfiles_reflog_path(refs, &sb, refname);\n \t\tstrbuf_addf(err, \"unable to append to '%s': %s\",\n \t\t\t    sb.buf, strerror(save_errno));\n \t\tstrbuf_release(&sb);\n@@ -2974,7 +2995,8 @@ static int commit_ref_update(struct files_ref_store *refs,\n \tfiles_assert_main_repository(refs, \"commit_ref_update\");\n \n \tclear_loose_ref_cache(refs);\n-\tif (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,\n+\tif (files_log_ref_write(refs, lock->ref_name,\n+\t\t\t\tlock->old_oid.hash, sha1,\n \t\t\t\tlogmsg, 0, err)) {\n \t\tchar *old_msg = strbuf_detach(err, NULL);\n \t\tstrbuf_addf(err, \"cannot update the ref '%s': %s\",\n@@ -3006,8 +3028,9 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n-\t\t\tif (files_log_ref_write(\"HEAD\", lock->old_oid.hash, sha1,\n-\t\t\t\t\t  logmsg, 0, &log_err)) {\n+\t\t\tif (files_log_ref_write(refs, \"HEAD\",\n+\t\t\t\t\t\tlock->old_oid.hash, sha1,\n+\t\t\t\t\t\tlogmsg, 0, &log_err)) {\n \t\t\t\terror(\"%s\", log_err.buf);\n \t\t\t\tstrbuf_release(&log_err);\n \t\t\t}\n@@ -3039,24 +3062,26 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)\n \treturn ret;\n }\n \n-static void update_symref_reflog(struct ref_lock *lock, const char *refname,\n+static void update_symref_reflog(struct files_ref_store *refs,\n+\t\t\t\t struct ref_lock *lock, const char *refname,\n \t\t\t\t const char *target, const char *logmsg)\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n \tif (logmsg && !read_ref(target, new_sha1) &&\n-\t    files_log_ref_write(refname, lock->old_oid.hash, new_sha1,\n-\t\t\t\tlogmsg, 0, &err)) {\n+\t    files_log_ref_write(refs, refname, lock->old_oid.hash,\n+\t\t\t\tnew_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t}\n }\n \n-static int create_symref_locked(struct ref_lock *lock, const char *refname,\n+static int create_symref_locked(struct files_ref_store *refs,\n+\t\t\t\tstruct ref_lock *lock, const char *refname,\n \t\t\t\tconst char *target, const char *logmsg)\n {\n \tif (prefer_symlink_refs && !create_ref_symlink(lock, target)) {\n-\t\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\t\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \t\treturn 0;\n \t}\n \n@@ -3064,7 +3089,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,\n \t\treturn error(\"unable to fdopen %s: %s\",\n \t\t\t     lock->lk->tempfile.filename.buf, strerror(errno));\n \n-\tupdate_symref_reflog(lock, refname, target, logmsg);\n+\tupdate_symref_reflog(refs, lock, refname, target, logmsg);\n \n \t/* no error check; commit_ref will check ferror */\n \tfprintf(lock->lk->tempfile.fp, \"ref: %s\\n\", target);\n@@ -3093,13 +3118,20 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\treturn -1;\n \t}\n \n-\tret = create_symref_locked(lock, refname, target, logmsg);\n+\tret = create_symref_locked(refs, lock, refname, target, logmsg);\n \tunlock_ref(lock);\n \treturn ret;\n }\n \n int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)\n {\n+\t/*\n+\t * FIXME: this obviously will not work well for future refs\n+\t * backends. This function needs to die.\n+\t */\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n \tstruct strbuf head_path = STRBUF_INIT;\n@@ -3126,7 +3158,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n \tlock->lk = &head_lock;\n \tlock->ref_name = xstrdup(head_rel);\n \n-\tret = create_symref_locked(lock, head_rel, target, logmsg);\n+\tret = create_symref_locked(refs, lock, head_rel, target, logmsg);\n \n \tunlock_ref(lock); /* will free lock */\n \tstrbuf_release(&head_path);\n@@ -3136,14 +3168,13 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3152,13 +3183,12 @@ static int files_reflog_exists(struct ref_store *ref_store,\n static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tret = remove_path(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -3209,15 +3239,14 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     each_reflog_ent_fn fn,\n \t\t\t\t\t     void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n \tint ret = 0, at_tail = 1;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3318,14 +3347,13 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     const char *refname,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n-\n-\tstrbuf_git_path(&sb, \"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &sb, refname);\n \tlogfp = fopen(sb.buf, \"r\");\n \tstrbuf_release(&sb);\n \tif (!logfp)\n@@ -3407,15 +3435,14 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n \n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n \n-\t/* Check validity (but we don't need the result): */\n-\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n-\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n-\tstrbuf_git_path(&sb, \"logs\");\n+\tfiles_reflog_path(refs, &sb, NULL);\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n@@ -3840,7 +3867,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \n \t\tif (update->flags & REF_NEEDS_COMMIT ||\n \t\t    update->flags & REF_LOG_ONLY) {\n-\t\t\tif (files_log_ref_write(lock->ref_name,\n+\t\t\tif (files_log_ref_write(refs,\n+\t\t\t\t\t\tlock->ref_name,\n \t\t\t\t\t\tlock->old_oid.hash,\n \t\t\t\t\t\tupdate->new_sha1,\n \t\t\t\t\t\tupdate->msg, update->flags,\n@@ -3900,9 +3928,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t/* Delete the reflogs of any references that were deleted: */\n \tfor_each_string_list_item(ref_to_delete, &refs_to_delete) {\n \t\tstrbuf_reset(&sb);\n-\t\tstrbuf_git_path(&sb, \"logs/%s\", ref_to_delete->string);\n+\t\tfiles_reflog_path(refs, &sb, ref_to_delete->string);\n \t\tif (!unlink_or_warn(sb.buf))\n-\t\t\ttry_remove_empty_parents(ref_to_delete->string,\n+\t\t\ttry_remove_empty_parents(refs, ref_to_delete->string,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REFLOG);\n \t}\n \n@@ -3926,7 +3954,7 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t * can only work because we have already\n \t\t\t * removed the lockfile.)\n \t\t\t */\n-\t\t\ttry_remove_empty_parents(update->refname,\n+\t\t\ttry_remove_empty_parents(refs, update->refname,\n \t\t\t\t\t\t REMOVE_EMPTY_PARENTS_REF);\n \t\t}\n \t}\n@@ -4077,6 +4105,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n+\tstruct strbuf log_file_sb = STRBUF_INIT;\n \tchar *log_file;\n \tint status = 0;\n \tint type;\n@@ -4105,7 +4134,8 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tlog_file = git_pathdup(\"logs/%s\", refname);\n+\tfiles_reflog_path(refs, &log_file_sb, refname);\n+\tlog_file = strbuf_detach(&log_file_sb, NULL);\n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n \t\t/*\n \t\t * Even though holding $GIT_DIR/logs/$reflog.lock has\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315430","messageId":"20170326024241.31984-14-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 13/28] refs: rename lookup_ref_store() to lookup_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:26Z","receivedAt":"2017-03-26T02:44:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With get_main_ref_store() being used inside get_ref_store(),\nlookup_ref_store() is only used for submodule code path. Rename to\nreflect that and delete dead code.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 13 ++++---------\n 1 file changed, 4 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 8aa33af4e8..a4a1a4ccfd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1395,17 +1395,13 @@ static struct ref_store *main_ref_store;\n static struct hashmap submodule_ref_stores;\n \n /*\n- * Return the ref_store instance for the specified submodule (or the\n- * main repository if submodule is NULL). If that ref_store hasn't\n- * been initialized yet, return NULL.\n+ * Return the ref_store instance for the specified submodule. If that\n+ * ref_store hasn't been initialized yet, return NULL.\n  */\n-static struct ref_store *lookup_ref_store(const char *submodule)\n+static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n {\n \tstruct submodule_hash_entry *entry;\n \n-\tif (!submodule)\n-\t\treturn main_ref_store;\n-\n \tif (!submodule_ref_stores.tablesize)\n \t\t/* It's initialized on demand in register_ref_store(). */\n \t\treturn NULL;\n@@ -1471,7 +1467,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n \t} else {\n-\t\trefs = lookup_ref_store(submodule);\n+\t\trefs = lookup_submodule_ref_store(submodule);\n \n \t\tif (!refs) {\n \t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n@@ -1482,7 +1478,6 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\t\tstrbuf_release(&submodule_sb);\n \t\t}\n \t}\n-\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315431","messageId":"20170326024241.31984-16-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 15/28] refs.c: kill register_ref_store(), add register_submodule_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:28Z","receivedAt":"2017-03-26T02:44:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the last function in this code (besides public API) that takes\nsubmodule argument and handles both main/submodule cases. Break it down,\nmove main store registration in get_main_ref_store() and keep the rest\nin register_submodule_ref_store().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 45 ++++++++++++++++++++-------------------------\n 1 file changed, 20 insertions(+), 25 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 66dc84787d..87f64271ac 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1412,29 +1412,6 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n }\n \n /*\n- * Register the specified ref_store to be the one that should be used\n- * for submodule (or the main repository if submodule is NULL). It is\n- * a fatal error to call this function twice for the same submodule.\n- */\n-static void register_ref_store(struct ref_store *refs, const char *submodule)\n-{\n-\tif (!submodule) {\n-\t\tif (main_ref_store)\n-\t\t\tdie(\"BUG: main_ref_store initialized twice\");\n-\n-\t\tmain_ref_store = refs;\n-\t} else {\n-\t\tif (!submodule_ref_stores.tablesize)\n-\t\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n-\n-\t\tif (hashmap_put(&submodule_ref_stores,\n-\t\t\t\talloc_submodule_hash_entry(submodule, refs)))\n-\t\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n-\t\t\t    submodule);\n-\t}\n-}\n-\n-/*\n  * Create, record, and return a ref_store instance for the specified\n  * submodule (or the main repository if submodule is NULL).\n  */\n@@ -1448,7 +1425,6 @@ static struct ref_store *ref_store_init(const char *submodule)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n \trefs = be->init(submodule);\n-\tregister_ref_store(refs, submodule);\n \treturn refs;\n }\n \n@@ -1457,7 +1433,25 @@ static struct ref_store *get_main_ref_store(void)\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\treturn ref_store_init(NULL);\n+\tmain_ref_store = ref_store_init(NULL);\n+\treturn main_ref_store;\n+}\n+\n+/*\n+ * Register the specified ref_store to be the one that should be used\n+ * for submodule. It is a fatal error to call this function twice for\n+ * the same submodule.\n+ */\n+static void register_submodule_ref_store(struct ref_store *refs,\n+\t\t\t\t\t const char *submodule)\n+{\n+\tif (!submodule_ref_stores.tablesize)\n+\t\thashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);\n+\n+\tif (hashmap_put(&submodule_ref_stores,\n+\t\t\talloc_submodule_hash_entry(submodule, refs)))\n+\t\tdie(\"BUG: ref_store for submodule '%s' initialized twice\",\n+\t\t    submodule);\n }\n \n struct ref_store *get_ref_store(const char *submodule)\n@@ -1481,6 +1475,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn NULL;\n \n \trefs = ref_store_init(submodule);\n+\tregister_submodule_ref_store(refs, submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315432","messageId":"20170326024241.31984-12-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 11/28] files-backend: remove the use of git_path()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:24Z","receivedAt":"2017-03-26T02:44:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given $GIT_DIR and $GIT_COMMON_DIR, files-backend is now in charge of\ndeciding what goes where (*). The end goal is to pass $GIT_DIR only. A\nrefs \"view\" of a linked worktree is a logical ref store that combines\ntwo files backends together.\n\n(*) Not entirely true since strbuf_git_path_submodule() still does path\ntranslation underneath. But that's for another patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 43 ++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 38 insertions(+), 5 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 741e52d143..9676cd32e4 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -923,7 +923,8 @@ struct files_ref_store {\n \t * store:\n \t */\n \tconst char *submodule;\n-\n+\tchar *gitdir;\n+\tchar *gitcommondir;\n \tchar *packed_refs_path;\n \n \tstruct ref_entry *loose;\n@@ -985,6 +986,8 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *gitdir = get_git_dir();\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n@@ -995,7 +998,11 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n \t\treturn ref_store;\n \t}\n \n-\trefs->packed_refs_path = git_pathdup(\"packed-refs\");\n+\trefs->gitdir = xstrdup(gitdir);\n+\tget_common_dir_noenv(&sb, gitdir);\n+\trefs->gitcommondir = strbuf_detach(&sb, NULL);\n+\tstrbuf_addf(&sb, \"%s/packed-refs\", refs->gitcommondir);\n+\trefs->packed_refs_path = strbuf_detach(&sb, NULL);\n \n \treturn ref_store;\n }\n@@ -1173,11 +1180,26 @@ static void files_reflog_path(struct files_ref_store *refs,\n \t\t\t      const char *refname)\n {\n \tif (!refname) {\n-\t\tstrbuf_git_path(sb, \"logs\");\n+\t\t/*\n+\t\t * FIXME: of course this is wrong in multi worktree\n+\t\t * setting. To be fixed real soon.\n+\t\t */\n+\t\tstrbuf_addf(sb, \"%s/logs\", refs->gitcommondir);\n \t\treturn;\n \t}\n \n-\tstrbuf_git_path(sb, \"logs/%s\", refname);\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_PER_WORKTREE:\n+\tcase REF_TYPE_PSEUDOREF:\n+\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n+\t\tbreak;\n+\tcase REF_TYPE_NORMAL:\n+\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n+\t\t    ref_type(refname), refname);\n+\t}\n }\n \n static void files_ref_path(struct files_ref_store *refs,\n@@ -1189,7 +1211,18 @@ static void files_ref_path(struct files_ref_store *refs,\n \t\treturn;\n \t}\n \n-\tstrbuf_git_path(sb, \"%s\", refname);\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_PER_WORKTREE:\n+\tcase REF_TYPE_PSEUDOREF:\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n+\t\tbreak;\n+\tcase REF_TYPE_NORMAL:\n+\t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: unknown ref type %d of ref %s\",\n+\t\t    ref_type(refname), refname);\n+\t}\n }\n \n /*\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315433","messageId":"20170326024241.31984-17-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 16/28] refs.c: make get_main_ref_store() public and use it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:29Z","receivedAt":"2017-03-26T02:44:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"get_ref_store() will soon be renamed to get_submodule_ref_store().\nTogether with future get_worktree_ref_store(), the three functions\nprovide an appropriate ref store for different operation modes. New APIs\nwill be added to operate directly on ref stores.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 36 ++++++++++++++++++------------------\n refs.h               |  3 +++\n refs/files-backend.c |  2 +-\n 3 files changed, 22 insertions(+), 19 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 87f64271ac..1f4c1a2347 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1314,7 +1314,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,\n /* backend functions */\n int refs_init_db(struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->init_db(refs, err);\n }\n@@ -1322,7 +1322,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_ref_store(NULL), refname,\n+\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1428,7 +1428,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n-static struct ref_store *get_main_ref_store(void)\n+struct ref_store *get_main_ref_store(void)\n {\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n@@ -1488,14 +1488,14 @@ void base_ref_store_init(struct ref_store *refs,\n /* backend functions */\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->pack_refs(refs, flags);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->peel_ref(refs, refname, sha1);\n }\n@@ -1503,7 +1503,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n \t\t\t\t       logmsg);\n@@ -1512,7 +1512,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1522,14 +1522,14 @@ int verify_refname_available(const char *refname,\n \t\t\t     const struct string_list *skip,\n \t\t\t     struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1540,7 +1540,7 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n \t\t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n@@ -1549,14 +1549,14 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_exists(refs, refname);\n }\n@@ -1564,14 +1564,14 @@ int reflog_exists(const char *refname)\n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_reflog(refs, refname);\n }\n@@ -1583,7 +1583,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n \t\t\t\t       prepare_fn, should_prune_fn,\n@@ -1593,21 +1593,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n-\tstruct ref_store *refs = get_ref_store(NULL);\n+\tstruct ref_store *refs = get_main_ref_store();\n \n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\ndiff --git a/refs.h b/refs.h\nindex 2d6b6263fc..a6cd12267f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -2,6 +2,7 @@\n #define REFS_H\n \n struct object_id;\n+struct ref_store;\n struct strbuf;\n struct string_list;\n \n@@ -560,4 +561,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \n int ref_storage_backend_exists(const char *name);\n \n+struct ref_store *get_main_ref_store(void);\n+\n #endif /* REFS_H */\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 9676cd32e4..b62f374f9c 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -3163,7 +3163,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_ref_store(NULL), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(), 0, \"set_head_symref\");\n \n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315434","messageId":"20170326024241.31984-18-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 17/28] path.c: move some code out of strbuf_git_path_submodule()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:30Z","receivedAt":"2017-03-26T02:44:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"refs is learning to avoid path rewriting that is done by\nstrbuf_git_path_submodule(). Factor out this code so it could be reused\nby refs_* functions.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n path.c      | 35 +++++++----------------------------\n submodule.c | 31 +++++++++++++++++++++++++++++++\n submodule.h |  6 ++++++\n 3 files changed, 44 insertions(+), 28 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex efcedafba6..03e7e33b6e 100644\n--- a/path.c\n+++ b/path.c\n@@ -471,39 +471,19 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)\n }\n \n /* Returns 0 on success, negative on failure. */\n-#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1\n static int do_submodule_path(struct strbuf *buf, const char *path,\n \t\t\t     const char *fmt, va_list args)\n {\n-\tconst char *git_dir;\n \tstruct strbuf git_submodule_common_dir = STRBUF_INIT;\n \tstruct strbuf git_submodule_dir = STRBUF_INIT;\n-\tconst struct submodule *sub;\n-\tint err = 0;\n+\tint ret;\n \n-\tstrbuf_addstr(buf, path);\n-\tstrbuf_complete(buf, '/');\n-\tstrbuf_addstr(buf, \".git\");\n-\n-\tgit_dir = read_gitfile(buf->buf);\n-\tif (git_dir) {\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_addstr(buf, git_dir);\n-\t}\n-\tif (!is_git_directory(buf->buf)) {\n-\t\tgitmodules_config();\n-\t\tsub = submodule_from_path(null_sha1, path);\n-\t\tif (!sub) {\n-\t\t\terr = SUBMODULE_PATH_ERR_NOT_CONFIGURED;\n-\t\t\tgoto cleanup;\n-\t\t}\n-\t\tstrbuf_reset(buf);\n-\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n-\t}\n-\n-\tstrbuf_addch(buf, '/');\n-\tstrbuf_addbuf(&git_submodule_dir, buf);\n+\tret = submodule_to_gitdir(&git_submodule_dir, path);\n+\tif (ret)\n+\t\tgoto cleanup;\n \n+\tstrbuf_complete(&git_submodule_dir, '/');\n+\tstrbuf_addbuf(buf, &git_submodule_dir);\n \tstrbuf_vaddf(buf, fmt, args);\n \n \tif (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))\n@@ -514,8 +494,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,\n cleanup:\n \tstrbuf_release(&git_submodule_dir);\n \tstrbuf_release(&git_submodule_common_dir);\n-\n-\treturn err;\n+\treturn ret;\n }\n \n char *git_pathdup_submodule(const char *path, const char *fmt, ...)\ndiff --git a/submodule.c b/submodule.c\nindex 3200b7bb2b..a31f68812c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1596,3 +1596,34 @@ const char *get_superproject_working_tree(void)\n \n \treturn ret;\n }\n+\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n+{\n+\tconst struct submodule *sub;\n+\tconst char *git_dir;\n+\tint ret = 0;\n+\n+\tstrbuf_reset(buf);\n+\tstrbuf_addstr(buf, submodule);\n+\tstrbuf_complete(buf, '/');\n+\tstrbuf_addstr(buf, \".git\");\n+\n+\tgit_dir = read_gitfile(buf->buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_addstr(buf, git_dir);\n+\t}\n+\tif (!is_git_directory(buf->buf)) {\n+\t\tgitmodules_config();\n+\t\tsub = submodule_from_path(null_sha1, submodule);\n+\t\tif (!sub) {\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t\tstrbuf_reset(buf);\n+\t\tstrbuf_git_path(buf, \"%s/%s\", \"modules\", sub->name);\n+\t}\n+\n+cleanup:\n+\treturn ret;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex c8a0c9cb29..fce2fb64d2 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -81,6 +81,12 @@ extern int push_unpushed_submodules(struct sha1_array *commits,\n \t\t\t\t    int dry_run);\n extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n extern int parallel_submodules(void);\n+/*\n+ * Given a submodule path (as in the index), return the repository\n+ * path of that submodule in 'buf'. Return -1 on error or when the\n+ * submodule is not initialized.\n+ */\n+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);\n \n /*\n  * Prepare the \"env_array\" parameter of a \"struct child_process\" for executing\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315435","messageId":"20170326024241.31984-13-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 12/28] refs.c: introduce get_main_ref_store()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:25Z","receivedAt":"2017-03-26T02:44:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex e7606716dd..8aa33af4e8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1456,15 +1456,20 @@ static struct ref_store *ref_store_init(const char *submodule)\n \treturn refs;\n }\n \n+static struct ref_store *get_main_ref_store(void)\n+{\n+\tif (main_ref_store)\n+\t\treturn main_ref_store;\n+\n+\treturn ref_store_init(NULL);\n+}\n+\n struct ref_store *get_ref_store(const char *submodule)\n {\n \tstruct ref_store *refs;\n \n \tif (!submodule || !*submodule) {\n-\t\trefs = lookup_ref_store(NULL);\n-\n-\t\tif (!refs)\n-\t\t\trefs = ref_store_init(NULL);\n+\t\treturn get_main_ref_store();\n \t} else {\n \t\trefs = lookup_ref_store(submodule);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315436","messageId":"20170326024241.31984-15-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 14/28] refs.c: flatten get_ref_store() a bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:27Z","receivedAt":"2017-03-26T02:44:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This helps the future changes in this code. And because get_ref_store()\nis destined to become get_submodule_ref_store(), the \"get main store\"\ncode path will be removed eventually. After this the patch to delete\nthat code will be cleaner.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 23 +++++++++++++----------\n 1 file changed, 13 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex a4a1a4ccfd..66dc84787d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1462,22 +1462,25 @@ static struct ref_store *get_main_ref_store(void)\n \n struct ref_store *get_ref_store(const char *submodule)\n {\n+\tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n+\tint ret;\n \n \tif (!submodule || !*submodule) {\n \t\treturn get_main_ref_store();\n-\t} else {\n-\t\trefs = lookup_submodule_ref_store(submodule);\n+\t}\n \n-\t\tif (!refs) {\n-\t\t\tstruct strbuf submodule_sb = STRBUF_INIT;\n+\trefs = lookup_submodule_ref_store(submodule);\n+\tif (refs)\n+\t\treturn refs;\n \n-\t\t\tstrbuf_addstr(&submodule_sb, submodule);\n-\t\t\tif (is_nonbare_repository_dir(&submodule_sb))\n-\t\t\t\trefs = ref_store_init(submodule);\n-\t\t\tstrbuf_release(&submodule_sb);\n-\t\t}\n-\t}\n+\tstrbuf_addstr(&submodule_sb, submodule);\n+\tret = is_nonbare_repository_dir(&submodule_sb);\n+\tstrbuf_release(&submodule_sb);\n+\tif (!ret)\n+\t\treturn NULL;\n+\n+\trefs = ref_store_init(submodule);\n \treturn refs;\n }\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315437","messageId":"20170326024241.31984-19-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 18/28] refs: move submodule code out of files-backend.c","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:31Z","receivedAt":"2017-03-26T02:45:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend is now initialized with a $GIT_DIR. Converting a submodule\npath to where real submodule gitdir is located is done in get_ref_store().\n\nThis gives a slight performance improvement for submodules since we\ndon't convert submodule path to gitdir at every backend call like\nbefore. We pay that once at ref-store creation.\n\nMore cleanup in files_downcast() and files_assert_main_repository()\nfollows shortly. It's separate to keep noises from this patch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 19 ++++++++++++++-----\n refs/files-backend.c | 24 ++----------------------\n refs/refs-internal.h |  9 ++++-----\n 3 files changed, 20 insertions(+), 32 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 1f4c1a2347..d72b48a430 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -9,6 +9,7 @@\n #include \"refs/refs-internal.h\"\n #include \"object.h\"\n #include \"tag.h\"\n+#include \"submodule.h\"\n \n /*\n  * List of all available backends\n@@ -1413,9 +1414,9 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n \n /*\n  * Create, record, and return a ref_store instance for the specified\n- * submodule (or the main repository if submodule is NULL).\n+ * gitdir.\n  */\n-static struct ref_store *ref_store_init(const char *submodule)\n+static struct ref_store *ref_store_init(const char *gitdir)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1424,7 +1425,7 @@ static struct ref_store *ref_store_init(const char *submodule)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(submodule);\n+\trefs = be->init(gitdir);\n \treturn refs;\n }\n \n@@ -1433,7 +1434,7 @@ struct ref_store *get_main_ref_store(void)\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\tmain_ref_store = ref_store_init(NULL);\n+\tmain_ref_store = ref_store_init(get_git_dir());\n \treturn main_ref_store;\n }\n \n@@ -1474,8 +1475,16 @@ struct ref_store *get_ref_store(const char *submodule)\n \tif (!ret)\n \t\treturn NULL;\n \n-\trefs = ref_store_init(submodule);\n+\tret = submodule_to_gitdir(&submodule_sb, submodule);\n+\tif (ret) {\n+\t\tstrbuf_release(&submodule_sb);\n+\t\treturn NULL;\n+\t}\n+\n+\trefs = ref_store_init(submodule_sb.buf);\n \tregister_submodule_ref_store(refs, submodule);\n+\n+\tstrbuf_release(&submodule_sb);\n \treturn refs;\n }\n \ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex b62f374f9c..490f05a6f4 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -917,12 +917,6 @@ struct packed_ref_cache {\n struct files_ref_store {\n \tstruct ref_store base;\n \n-\t/*\n-\t * The name of the submodule represented by this object, or\n-\t * NULL if it represents the main repository's reference\n-\t * store:\n-\t */\n-\tconst char *submodule;\n \tchar *gitdir;\n \tchar *gitcommondir;\n \tchar *packed_refs_path;\n@@ -982,22 +976,14 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *submodule)\n+static struct ref_store *files_ref_store_create(const char *gitdir)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n \tstruct strbuf sb = STRBUF_INIT;\n-\tconst char *gitdir = get_git_dir();\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n \n-\tif (submodule) {\n-\t\trefs->submodule = xstrdup(submodule);\n-\t\trefs->packed_refs_path = git_pathdup_submodule(\n-\t\t\trefs->submodule, \"packed-refs\");\n-\t\treturn ref_store;\n-\t}\n-\n \trefs->gitdir = xstrdup(gitdir);\n \tget_common_dir_noenv(&sb, gitdir);\n \trefs->gitcommondir = strbuf_detach(&sb, NULL);\n@@ -1014,8 +1000,7 @@ static struct ref_store *files_ref_store_create(const char *submodule)\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\tif (refs->submodule)\n-\t\tdie(\"BUG: %s called for a submodule\", caller);\n+\t/* This function is to be fixed up in the next patch */\n }\n \n /*\n@@ -1206,11 +1191,6 @@ static void files_ref_path(struct files_ref_store *refs,\n \t\t\t   struct strbuf *sb,\n \t\t\t   const char *refname)\n {\n-\tif (refs->submodule) {\n-\t\tstrbuf_git_path_submodule(sb, refs->submodule, \"%s\", refname);\n-\t\treturn;\n-\t}\n-\n \tswitch (ref_type(refname)) {\n \tcase REF_TYPE_PER_WORKTREE:\n \tcase REF_TYPE_PSEUDOREF:\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f732473e1d..dfa1817929 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -482,12 +482,11 @@ struct ref_store;\n /* refs backends */\n \n /*\n- * Initialize the ref_store for the specified submodule, or for the\n- * main repository if submodule == NULL. These functions should call\n- * base_ref_store_init() to initialize the shared part of the\n- * ref_store and to record the ref_store for later lookup.\n+ * Initialize the ref_store for the specified gitdir. These functions\n+ * should call base_ref_store_init() to initialize the shared part of\n+ * the ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *submodule);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315438","messageId":"20170326024241.31984-20-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 19/28] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:32Z","receivedAt":"2017-03-26T02:45:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"files-backend.c is unlearning submodules. Instead of having a specific\ncheck for submodules to see what operation is allowed, files backend\nnow takes a set of flags at init. Each operation will check if the\nrequired flags is present before performing.\n\nFor now we have four flags: read, write and odb access. Main ref store\nhas all flags, obviously, while submodule stores are read-only and have\naccess to odb (*).\n\nThe \"main\" flag stays because many functions in the backend calls\nfrontend ones without a ref store, so these functions always target the\nmain ref store. Ideally the flag should be gone after ref-store-aware\napi is in place and used by backends.\n\n(*) Submodule code needs for_each_ref. Try take REF_STORE_ODB flag\nout. At least t3404 would fail. The \"have access to odb\" in submodule is\na bit hacky since we don't know from he whether add_submodule_odb() has\nbeen called.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 15 +++++++---\n refs/files-backend.c | 81 +++++++++++++++++++++++++++++++++-------------------\n refs/refs-internal.h |  9 +++++-\n 3 files changed, 71 insertions(+), 34 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex d72b48a430..241b4227b2 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1416,7 +1416,8 @@ static struct ref_store *lookup_submodule_ref_store(const char *submodule)\n  * Create, record, and return a ref_store instance for the specified\n  * gitdir.\n  */\n-static struct ref_store *ref_store_init(const char *gitdir)\n+static struct ref_store *ref_store_init(const char *gitdir,\n+\t\t\t\t\tunsigned int flags)\n {\n \tconst char *be_name = \"files\";\n \tstruct ref_storage_be *be = find_ref_storage_backend(be_name);\n@@ -1425,7 +1426,7 @@ static struct ref_store *ref_store_init(const char *gitdir)\n \tif (!be)\n \t\tdie(\"BUG: reference backend %s is unknown\", be_name);\n \n-\trefs = be->init(gitdir);\n+\trefs = be->init(gitdir, flags);\n \treturn refs;\n }\n \n@@ -1434,7 +1435,11 @@ struct ref_store *get_main_ref_store(void)\n \tif (main_ref_store)\n \t\treturn main_ref_store;\n \n-\tmain_ref_store = ref_store_init(get_git_dir());\n+\tmain_ref_store = ref_store_init(get_git_dir(),\n+\t\t\t\t\t(REF_STORE_READ |\n+\t\t\t\t\t REF_STORE_WRITE |\n+\t\t\t\t\t REF_STORE_ODB |\n+\t\t\t\t\t REF_STORE_MAIN));\n \treturn main_ref_store;\n }\n \n@@ -1481,7 +1486,9 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn NULL;\n \t}\n \n-\trefs = ref_store_init(submodule_sb.buf);\n+\t/* pretend that add_submodule_odb() has been called */\n+\trefs = ref_store_init(submodule_sb.buf,\n+\t\t\t      REF_STORE_READ | REF_STORE_ODB);\n \tregister_submodule_ref_store(refs, submodule);\n \n \tstrbuf_release(&submodule_sb);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 490f05a6f4..d97a924860 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -916,6 +916,7 @@ struct packed_ref_cache {\n  */\n struct files_ref_store {\n \tstruct ref_store base;\n+\tunsigned int store_flags;\n \n \tchar *gitdir;\n \tchar *gitcommondir;\n@@ -976,13 +977,15 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)\n  * Create a new submodule ref cache and add it to the internal\n  * set of caches.\n  */\n-static struct ref_store *files_ref_store_create(const char *gitdir)\n+static struct ref_store *files_ref_store_create(const char *gitdir,\n+\t\t\t\t\t\tunsigned int flags)\n {\n \tstruct files_ref_store *refs = xcalloc(1, sizeof(*refs));\n \tstruct ref_store *ref_store = (struct ref_store *)refs;\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tbase_ref_store_init(ref_store, &refs_be_files);\n+\trefs->store_flags = flags;\n \n \trefs->gitdir = xstrdup(gitdir);\n \tget_common_dir_noenv(&sb, gitdir);\n@@ -994,13 +997,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n }\n \n /*\n- * Die if refs is for a submodule (i.e., not for the main repository).\n- * caller is used in any necessary error messages.\n+ * Die if refs is not the main ref store. caller is used in any\n+ * necessary error messages.\n  */\n static void files_assert_main_repository(struct files_ref_store *refs,\n \t\t\t\t\t const char *caller)\n {\n-\t/* This function is to be fixed up in the next patch */\n+\tif (refs->store_flags & REF_STORE_MAIN)\n+\t\treturn;\n+\n+\tdie(\"BUG: unallowed operation (%s), only works \"\n+\t    \"on main ref store\\n\", caller);\n }\n \n /*\n@@ -1009,9 +1016,9 @@ static void files_assert_main_repository(struct files_ref_store *refs,\n  * files_ref_store is for a submodule (i.e., not for the main\n  * repository). caller is used in any necessary error messages.\n  */\n-static struct files_ref_store *files_downcast(\n-\t\tstruct ref_store *ref_store, int submodule_allowed,\n-\t\tconst char *caller)\n+static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n+\t\t\t\t\t      unsigned int required_flags,\n+\t\t\t\t\t      const char *caller)\n {\n \tstruct files_ref_store *refs;\n \n@@ -1021,8 +1028,9 @@ static struct files_ref_store *files_downcast(\n \n \trefs = (struct files_ref_store *)ref_store;\n \n-\tif (!submodule_allowed)\n-\t\tfiles_assert_main_repository(refs, caller);\n+\tif ((refs->store_flags & required_flags) != required_flags)\n+\t\tdie(\"BUG: unallowed operation (%s), requires %x, has %x\\n\",\n+\t\t    caller, required_flags, refs->store_flags);\n \n \treturn refs;\n }\n@@ -1398,7 +1406,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,\n \t\t\t      struct strbuf *referent, unsigned int *type)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"read_raw_ref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"read_raw_ref\");\n \tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n \tconst char *path;\n@@ -1815,10 +1823,14 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)\n static int files_peel_ref(struct ref_store *ref_store,\n \t\t\t  const char *refname, unsigned char *sha1)\n {\n-\tstruct files_ref_store *refs = files_downcast(ref_store, 0, \"peel_ref\");\n+\tstruct files_ref_store *refs =\n+\t\tfiles_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,\n+\t\t\t       \"peel_ref\");\n \tint flag;\n \tunsigned char base[20];\n \n+\tfiles_assert_main_repository(refs, \"peel_ref\");\n+\n \tif (current_ref_iter && current_ref_iter->refname == refname) {\n \t\tstruct object_id peeled;\n \n@@ -1923,8 +1935,7 @@ static struct ref_iterator *files_ref_iterator_begin(\n \t\tstruct ref_store *ref_store,\n \t\tconst char *prefix, unsigned int flags)\n {\n-\tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"ref_iterator_begin\");\n+\tstruct files_ref_store *refs;\n \tstruct ref_dir *loose_dir, *packed_dir;\n \tstruct ref_iterator *loose_iter, *packed_iter;\n \tstruct files_ref_iterator *iter;\n@@ -1935,6 +1946,10 @@ static struct ref_iterator *files_ref_iterator_begin(\n \tif (ref_paranoia)\n \t\tflags |= DO_FOR_EACH_INCLUDE_BROKEN;\n \n+\trefs = files_downcast(ref_store,\n+\t\t\t      REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),\n+\t\t\t      \"ref_iterator_begin\");\n+\n \titer = xcalloc(1, sizeof(*iter));\n \tref_iterator = &iter->base;\n \tbase_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);\n@@ -2415,7 +2430,8 @@ static void prune_refs(struct ref_to_prune *r)\n static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"pack_refs\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,\n+\t\t\t       \"pack_refs\");\n \tstruct pack_refs_cb_data cbdata;\n \n \tmemset(&cbdata, 0, sizeof(cbdata));\n@@ -2494,7 +2510,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \t\t\t     struct string_list *refnames, unsigned int flags)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_refs\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"delete_refs\");\n \tstruct strbuf err = STRBUF_INIT;\n \tint i, result = 0;\n \n@@ -2598,7 +2614,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,\n \t\t\t\t\t  struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 1, \"verify_refname_available\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"verify_refname_available\");\n \tstruct ref_dir *packed_refs = get_packed_refs(refs);\n \tstruct ref_dir *loose_refs = get_loose_refs(refs);\n \n@@ -2623,7 +2639,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"rename_ref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"rename_ref\");\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n@@ -2873,7 +2889,7 @@ static int files_create_reflog(struct ref_store *ref_store,\n \t\t\t       struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_reflog\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"create_reflog\");\n \tint fd;\n \n \tif (log_ref_setup(refs, refname, force_create, &fd, err))\n@@ -3117,7 +3133,7 @@ static int files_create_symref(struct ref_store *ref_store,\n \t\t\t       const char *logmsg)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"create_symref\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"create_symref\");\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_lock *lock;\n \tint ret;\n@@ -3143,7 +3159,9 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char\n \t * backends. This function needs to die.\n \t */\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(get_main_ref_store(), 0, \"set_head_symref\");\n+\t\tfiles_downcast(get_main_ref_store(),\n+\t\t\t       REF_STORE_WRITE,\n+\t\t\t       \"set_head_symref\");\n \n \tstatic struct lock_file head_lock;\n \tstruct ref_lock *lock;\n@@ -3182,7 +3200,7 @@ static int files_reflog_exists(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_exists\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ, \"reflog_exists\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct stat st;\n \tint ret;\n@@ -3197,7 +3215,7 @@ static int files_delete_reflog(struct ref_store *ref_store,\n \t\t\t       const char *refname)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"delete_reflog\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"delete_reflog\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n@@ -3253,7 +3271,8 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,\n \t\t\t\t\t     void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent_reverse\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"for_each_reflog_ent_reverse\");\n \tstruct strbuf sb = STRBUF_INIT;\n \tFILE *logfp;\n \tlong pos;\n@@ -3361,7 +3380,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n \t\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"for_each_reflog_ent\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"for_each_reflog_ent\");\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n@@ -3449,7 +3469,8 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {\n static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_iterator_begin\");\n+\t\tfiles_downcast(ref_store, REF_STORE_READ,\n+\t\t\t       \"reflog_iterator_begin\");\n \tstruct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));\n \tstruct ref_iterator *ref_iterator = &iter->base;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -3787,7 +3808,8 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE,\n+\t\t\t       \"ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list refs_to_delete = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *ref_to_delete;\n@@ -3992,7 +4014,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\t\t\t\t    struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"initial_ref_transaction_commit\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE,\n+\t\t\t       \"initial_ref_transaction_commit\");\n \tint ret = 0, i;\n \tstruct string_list affected_refnames = STRING_LIST_INIT_NODUP;\n \n@@ -4114,7 +4137,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\t\t       void *policy_cb_data)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"reflog_expire\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"reflog_expire\");\n \tstatic struct lock_file reflog_lock;\n \tstruct expire_reflog_cb cb;\n \tstruct ref_lock *lock;\n@@ -4220,7 +4243,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n static int files_init_db(struct ref_store *ref_store, struct strbuf *err)\n {\n \tstruct files_ref_store *refs =\n-\t\tfiles_downcast(ref_store, 0, \"init_db\");\n+\t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"init_db\");\n \tstruct strbuf sb = STRBUF_INIT;\n \n \t/*\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex dfa1817929..0cca280b5c 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -481,12 +481,19 @@ struct ref_store;\n \n /* refs backends */\n \n+/* ref_store_init flags */\n+#define REF_STORE_READ\t\t(1 << 0)\n+#define REF_STORE_WRITE\t\t(1 << 1) /* can perform update operations */\n+#define REF_STORE_ODB\t\t(1 << 2) /* has access to object database */\n+#define REF_STORE_MAIN\t\t(1 << 3)\n+\n /*\n  * Initialize the ref_store for the specified gitdir. These functions\n  * should call base_ref_store_init() to initialize the shared part of\n  * the ref_store and to record the ref_store for later lookup.\n  */\n-typedef struct ref_store *ref_store_init_fn(const char *gitdir);\n+typedef struct ref_store *ref_store_init_fn(const char *gitdir,\n+\t\t\t\t\t    unsigned int flags);\n \n typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);\n \n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315439","messageId":"20170326024241.31984-21-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 20/28] refs: rename get_ref_store() to get_submodule_ref_store() and make it public","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:33Z","receivedAt":"2017-03-26T02:45:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is intended to replace *_submodule() refs API. It provides\na ref store for a specific submodule, which can be operated on by a new\nset of refs API.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 12 ++++++++----\n refs.h               | 11 +++++++++++\n refs/refs-internal.h | 12 ------------\n 3 files changed, 19 insertions(+), 16 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 241b4227b2..100a925bcc 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1171,7 +1171,7 @@ int head_ref(each_ref_fn fn, void *cb_data)\n static int do_for_each_ref(const char *submodule, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_ref_store(submodule);\n+\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1344,10 +1344,10 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \t\t/* We need to strip off one or more trailing slashes */\n \t\tchar *stripped = xmemdupz(submodule, len);\n \n-\t\trefs = get_ref_store(stripped);\n+\t\trefs = get_submodule_ref_store(stripped);\n \t\tfree(stripped);\n \t} else {\n-\t\trefs = get_ref_store(submodule);\n+\t\trefs = get_submodule_ref_store(submodule);\n \t}\n \n \tif (!refs)\n@@ -1460,13 +1460,17 @@ static void register_submodule_ref_store(struct ref_store *refs,\n \t\t    submodule);\n }\n \n-struct ref_store *get_ref_store(const char *submodule)\n+struct ref_store *get_submodule_ref_store(const char *submodule)\n {\n \tstruct strbuf submodule_sb = STRBUF_INIT;\n \tstruct ref_store *refs;\n \tint ret;\n \n \tif (!submodule || !*submodule) {\n+\t\t/*\n+\t\t * FIXME: This case is ideally not allowed. But that\n+\t\t * can't happen until we clean up all the callers.\n+\t\t */\n \t\treturn get_main_ref_store();\n \t}\n \ndiff --git a/refs.h b/refs.h\nindex a6cd12267f..e6d8f67895 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -562,5 +562,16 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int ref_storage_backend_exists(const char *name);\n \n struct ref_store *get_main_ref_store(void);\n+/*\n+ * Return the ref_store instance for the specified submodule. For the\n+ * main repository, use submodule==NULL; such a call cannot fail. For\n+ * a submodule, the submodule must exist and be a nonbare repository,\n+ * otherwise return NULL. If the requested reference store has not yet\n+ * been initialized, initialize it first.\n+ *\n+ * For backwards compatibility, submodule==\"\" is treated the same as\n+ * submodule==NULL.\n+ */\n+struct ref_store *get_submodule_ref_store(const char *submodule);\n \n #endif /* REFS_H */\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 0cca280b5c..f20dde39ee 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -646,18 +646,6 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-/*\n- * Return the ref_store instance for the specified submodule. For the\n- * main repository, use submodule==NULL; such a call cannot fail. For\n- * a submodule, the submodule must exist and be a nonbare repository,\n- * otherwise return NULL. If the requested reference store has not yet\n- * been initialized, initialize it first.\n- *\n- * For backwards compatibility, submodule==\"\" is treated the same as\n- * submodule==NULL.\n- */\n-struct ref_store *get_ref_store(const char *submodule);\n-\n const char *resolve_ref_recursively(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315440","messageId":"20170326024241.31984-23-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 22/28] refs: new transaction related ref-store api","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:35Z","receivedAt":"2017-03-26T02:45:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The transaction struct now takes a ref store at creation and will\noperate on that ref store alone.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 55 ++++++++++++++++++++++++++++++++++++++++------------\n refs.h               |  9 +++++++++\n refs/refs-internal.h |  1 +\n 3 files changed, 53 insertions(+), 12 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c103f90b35..50ea24bd75 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -630,16 +630,20 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1\n \treturn 0;\n }\n \n-int delete_ref(const char *msg, const char *refname,\n-\t       const unsigned char *old_sha1, unsigned int flags)\n+int refs_delete_ref(struct ref_store *refs, const char *msg,\n+\t\t    const char *refname,\n+\t\t    const unsigned char *old_sha1,\n+\t\t    unsigned int flags)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tif (ref_type(refname) == REF_TYPE_PSEUDOREF)\n+\tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n+\t\tassert(refs == get_main_ref_store());\n \t\treturn delete_pseudoref(refname, old_sha1);\n+\t}\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = ref_store_transaction_begin(refs, &err);\n \tif (!transaction ||\n \t    ref_transaction_delete(transaction, refname, old_sha1,\n \t\t\t\t   flags, msg, &err) ||\n@@ -654,6 +658,13 @@ int delete_ref(const char *msg, const char *refname,\n \treturn 0;\n }\n \n+int delete_ref(const char *msg, const char *refname,\n+\t       const unsigned char *old_sha1, unsigned int flags)\n+{\n+\treturn refs_delete_ref(get_main_ref_store(), msg, refname,\n+\t\t\t       old_sha1, flags);\n+}\n+\n int copy_reflog_msg(char *buf, const char *msg)\n {\n \tchar *cp = buf;\n@@ -813,11 +824,20 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,\n \treturn 1;\n }\n \n-struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n+\t\t\t\t\t\t    struct strbuf *err)\n {\n+\tstruct ref_transaction *tr;\n \tassert(err);\n \n-\treturn xcalloc(1, sizeof(struct ref_transaction));\n+\ttr = xcalloc(1, sizeof(struct ref_transaction));\n+\ttr->ref_store = refs;\n+\treturn tr;\n+}\n+\n+struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+{\n+\treturn ref_store_transaction_begin(get_main_ref_store(), err);\n }\n \n void ref_transaction_free(struct ref_transaction *transaction)\n@@ -934,18 +954,20 @@ int update_ref_oid(const char *msg, const char *refname,\n \t\told_oid ? old_oid->hash : NULL, flags, onerr);\n }\n \n-int update_ref(const char *msg, const char *refname,\n-\t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n-\t       unsigned int flags, enum action_on_err onerr)\n+int refs_update_ref(struct ref_store *refs, const char *msg,\n+\t\t    const char *refname, const unsigned char *new_sha1,\n+\t\t    const unsigned char *old_sha1, unsigned int flags,\n+\t\t    enum action_on_err onerr)\n {\n \tstruct ref_transaction *t = NULL;\n \tstruct strbuf err = STRBUF_INIT;\n \tint ret = 0;\n \n \tif (ref_type(refname) == REF_TYPE_PSEUDOREF) {\n+\t\tassert(refs == get_main_ref_store());\n \t\tret = write_pseudoref(refname, new_sha1, old_sha1, &err);\n \t} else {\n-\t\tt = ref_transaction_begin(&err);\n+\t\tt = ref_store_transaction_begin(refs, &err);\n \t\tif (!t ||\n \t\t    ref_transaction_update(t, refname, new_sha1, old_sha1,\n \t\t\t\t\t   flags, msg, &err) ||\n@@ -976,6 +998,15 @@ int update_ref(const char *msg, const char *refname,\n \treturn 0;\n }\n \n+int update_ref(const char *msg, const char *refname,\n+\t       const unsigned char *new_sha1,\n+\t       const unsigned char *old_sha1,\n+\t       unsigned int flags, enum action_on_err onerr)\n+{\n+\treturn refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,\n+\t\t\t       old_sha1, flags, onerr);\n+}\n+\n char *shorten_unambiguous_ref(const char *refname, int strict)\n {\n \tint i;\n@@ -1607,7 +1638,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n int ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\tstruct ref_store *refs = transaction->ref_store;\n \n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n@@ -1726,7 +1757,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\tstruct ref_store *refs = transaction->ref_store;\n \n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\ndiff --git a/refs.h b/refs.h\nindex eaa31e8193..37f4aa8bd5 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -333,6 +333,10 @@ int reflog_exists(const char *refname);\n  * exists, regardless of its old value. It is an error for old_sha1 to\n  * be NULL_SHA1. flags is passed through to ref_transaction_delete().\n  */\n+int refs_delete_ref(struct ref_store *refs, const char *msg,\n+\t\t    const char *refname,\n+\t\t    const unsigned char *old_sha1,\n+\t\t    unsigned int flags);\n int delete_ref(const char *msg, const char *refname,\n \t       const unsigned char *old_sha1, unsigned int flags);\n \n@@ -418,6 +422,8 @@ enum action_on_err {\n  * Begin a reference transaction.  The reference transaction must\n  * be freed by calling ref_transaction_free().\n  */\n+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,\n+\t\t\t\t\t\t    struct strbuf *err);\n struct ref_transaction *ref_transaction_begin(struct strbuf *err);\n \n /*\n@@ -552,6 +558,9 @@ void ref_transaction_free(struct ref_transaction *transaction);\n  * ref_transaction_update(). Handle errors as requested by the `onerr`\n  * argument.\n  */\n+int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,\n+\t\t    const unsigned char *new_sha1, const unsigned char *old_sha1,\n+\t\t    unsigned int flags, enum action_on_err onerr);\n int update_ref(const char *msg, const char *refname,\n \t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n \t       unsigned int flags, enum action_on_err onerr);\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 5f26208c2c..690498698e 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -200,6 +200,7 @@ enum ref_transaction_state {\n  * as atomically as possible.  This structure is opaque to callers.\n  */\n struct ref_transaction {\n+\tstruct ref_store *ref_store;\n \tstruct ref_update **updates;\n \tsize_t alloc;\n \tsize_t nr;\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315441","messageId":"20170326024241.31984-25-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 24/28] refs: delete pack_refs() in favor of refs_pack_refs()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:37Z","receivedAt":"2017-03-26T02:45:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It only has one caller, not worth keeping just for convenience.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/pack-refs.c | 2 +-\n refs.c              | 5 -----\n refs.h              | 1 -\n 3 files changed, 1 insertion(+), 7 deletions(-)\n\ndiff --git a/builtin/pack-refs.c b/builtin/pack-refs.c\nindex 39f9a55d16..b106a392a4 100644\n--- a/builtin/pack-refs.c\n+++ b/builtin/pack-refs.c\n@@ -17,5 +17,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)\n \t};\n \tif (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))\n \t\tusage_with_options(pack_refs_usage, opts);\n-\treturn pack_refs(flags);\n+\treturn refs_pack_refs(get_main_ref_store(), flags);\n }\ndiff --git a/refs.c b/refs.c\nindex 50ea24bd75..ec1f563824 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1602,11 +1602,6 @@ int refs_pack_refs(struct ref_store *refs, unsigned int flags)\n \treturn refs->be->pack_refs(refs, flags);\n }\n \n-int pack_refs(unsigned int flags)\n-{\n-\treturn refs_pack_refs(get_main_ref_store(), flags);\n-}\n-\n int refs_peel_ref(struct ref_store *refs, const char *refname,\n \t\t  unsigned char *sha1)\n {\ndiff --git a/refs.h b/refs.h\nindex 37f4aa8bd5..1a07f9d86f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -297,7 +297,6 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,\n  * flags: Combination of the above PACK_REFS_* flags.\n  */\n int refs_pack_refs(struct ref_store *refs, unsigned int flags);\n-int pack_refs(unsigned int flags);\n \n /*\n  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315442","messageId":"20170326024241.31984-24-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 23/28] files-backend: avoid ref api targetting main ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:36Z","receivedAt":"2017-03-26T02:45:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A small step towards making files-backend works as a non-main ref store\nusing the newly added store-aware API.\n\nFor the record, `join` and `nm` on refs.o and files-backend.o tell me\nthat files-backend no longer uses functions that defaults to\nget_main_ref_store().\n\nI'm not yet comfortable at the idea of removing\nfiles_assert_main_repository() (or converting REF_STORE_MAIN to\nREF_STORE_WRITE). More staring and testing is required before that can\nhappen. Well, except peel_ref(). I'm pretty sure that function is safe.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 84 ++++++++++++++++++++++++++++++----------------------\n 1 file changed, 49 insertions(+), 35 deletions(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex dec8540a0f..a5b405436f 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1830,8 +1830,6 @@ static int files_peel_ref(struct ref_store *ref_store,\n \tint flag;\n \tunsigned char base[20];\n \n-\tfiles_assert_main_repository(refs, \"peel_ref\");\n-\n \tif (current_ref_iter && current_ref_iter->refname == refname) {\n \t\tstruct object_id peeled;\n \n@@ -1841,7 +1839,8 @@ static int files_peel_ref(struct ref_store *ref_store,\n \t\treturn 0;\n \t}\n \n-\tif (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))\n+\tif (refs_read_ref_full(ref_store, refname,\n+\t\t\t       RESOLVE_REF_READING, base, &flag))\n \t\treturn -1;\n \n \t/*\n@@ -2009,15 +2008,15 @@ static struct ref_iterator *files_ref_iterator_begin(\n  * on success. On error, write an error message to err, set errno, and\n  * return a negative value.\n  */\n-static int verify_lock(struct ref_lock *lock,\n+static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,\n \t\t       const unsigned char *old_sha1, int mustexist,\n \t\t       struct strbuf *err)\n {\n \tassert(err);\n \n-\tif (read_ref_full(lock->ref_name,\n-\t\t\t  mustexist ? RESOLVE_REF_READING : 0,\n-\t\t\t  lock->old_oid.hash, NULL)) {\n+\tif (refs_read_ref_full(ref_store, lock->ref_name,\n+\t\t\t       mustexist ? RESOLVE_REF_READING : 0,\n+\t\t\t       lock->old_oid.hash, NULL)) {\n \t\tif (old_sha1) {\n \t\t\tint save_errno = errno;\n \t\t\tstrbuf_addf(err, \"can't verify ref '%s'\", lock->ref_name);\n@@ -2086,8 +2085,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;\n \n \tfiles_ref_path(refs, &ref_file, refname);\n-\tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\tlock->old_oid.hash, type);\n+\tresolved = !!refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t     refname, resolve_flags,\n+\t\t\t\t\t     lock->old_oid.hash, type);\n \tif (!resolved && errno == EISDIR) {\n \t\t/*\n \t\t * we are trying to lock foo but we used to\n@@ -2104,8 +2104,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\t\t\t\t    refname);\n \t\t\tgoto error_return;\n \t\t}\n-\t\tresolved = !!resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\t\tlock->old_oid.hash, type);\n+\t\tresolved = !!refs_resolve_ref_unsafe(&refs->base,\n+\t\t\t\t\t\t     refname, resolve_flags,\n+\t\t\t\t\t\t     lock->old_oid.hash, type);\n \t}\n \tif (!resolved) {\n \t\tlast_errno = errno;\n@@ -2143,7 +2144,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,\n \t\tgoto error_return;\n \t}\n \n-\tif (verify_lock(lock, old_sha1, mustexist, err)) {\n+\tif (verify_lock(&refs->base, lock, old_sha1, mustexist, err)) {\n \t\tlast_errno = errno;\n \t\tgoto error_return;\n \t}\n@@ -2398,7 +2399,7 @@ static void try_remove_empty_parents(struct files_ref_store *refs,\n }\n \n /* make sure nobody touched the ref, and unlink */\n-static void prune_ref(struct ref_to_prune *r)\n+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n@@ -2406,7 +2407,7 @@ static void prune_ref(struct ref_to_prune *r)\n \tif (check_refname_format(r->name, 0))\n \t\treturn;\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = ref_store_transaction_begin(&refs->base, &err);\n \tif (!transaction ||\n \t    ref_transaction_delete(transaction, r->name, r->sha1,\n \t\t\t\t   REF_ISPRUNING | REF_NODEREF, NULL, &err) ||\n@@ -2420,10 +2421,10 @@ static void prune_ref(struct ref_to_prune *r)\n \tstrbuf_release(&err);\n }\n \n-static void prune_refs(struct ref_to_prune *r)\n+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)\n {\n \twhile (r) {\n-\t\tprune_ref(r);\n+\t\tprune_ref(refs, r);\n \t\tr = r->next;\n \t}\n }\n@@ -2447,7 +2448,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)\n \tif (commit_packed_refs(refs))\n \t\tdie_errno(\"unable to overwrite old ref-pack file\");\n \n-\tprune_refs(cbdata.ref_to_prune);\n+\tprune_refs(refs, cbdata.ref_to_prune);\n \treturn 0;\n }\n \n@@ -2539,7 +2540,7 @@ static int files_delete_refs(struct ref_store *ref_store,\n \tfor (i = 0; i < refnames->nr; i++) {\n \t\tconst char *refname = refnames->items[i].string;\n \n-\t\tif (delete_ref(NULL, refname, NULL, flags))\n+\t\tif (refs_delete_ref(&refs->base, NULL, refname, NULL, flags))\n \t\t\tresult |= error(_(\"could not remove reference %s\"), refname);\n \t}\n \n@@ -2661,7 +2662,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\tif (!refs_resolve_ref_unsafe(&refs->base, oldrefname,\n+\t\t\t\t     RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\torig_sha1, &flag)) {\n \t\tret = error(\"refname %s not found\", oldrefname);\n \t\tgoto out;\n@@ -2683,7 +2685,8 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (delete_ref(logmsg, oldrefname, orig_sha1, REF_NODEREF)) {\n+\tif (refs_delete_ref(&refs->base, logmsg, oldrefname,\n+\t\t\t    orig_sha1, REF_NODEREF)) {\n \t\terror(\"unable to delete old %s\", oldrefname);\n \t\tgoto rollback;\n \t}\n@@ -2695,9 +2698,11 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t * the safety anyway; we want to delete the reference whatever\n \t * its current value.\n \t */\n-\tif (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n-\t\t\t   sha1, NULL) &&\n-\t    delete_ref(NULL, newrefname, NULL, REF_NODEREF)) {\n+\tif (!refs_read_ref_full(&refs->base, newrefname,\n+\t\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\t\t\t\tsha1, NULL) &&\n+\t    refs_delete_ref(&refs->base, NULL, newrefname,\n+\t\t\t    NULL, REF_NODEREF)) {\n \t\tif (errno == EISDIR) {\n \t\t\tstruct strbuf path = STRBUF_INIT;\n \t\t\tint result;\n@@ -3053,8 +3058,9 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\tint head_flag;\n \t\tconst char *head_ref;\n \n-\t\thead_ref = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n-\t\t\t\t\t      head_sha1, &head_flag);\n+\t\thead_ref = refs_resolve_ref_unsafe(&refs->base, \"HEAD\",\n+\t\t\t\t\t\t   RESOLVE_REF_READING,\n+\t\t\t\t\t\t   head_sha1, &head_flag);\n \t\tif (head_ref && (head_flag & REF_ISSYMREF) &&\n \t\t    !strcmp(head_ref, lock->ref_name)) {\n \t\t\tstruct strbuf log_err = STRBUF_INIT;\n@@ -3098,7 +3104,9 @@ static void update_symref_reflog(struct files_ref_store *refs,\n {\n \tstruct strbuf err = STRBUF_INIT;\n \tunsigned char new_sha1[20];\n-\tif (logmsg && !read_ref(target, new_sha1) &&\n+\tif (logmsg &&\n+\t    !refs_read_ref_full(&refs->base, target,\n+\t\t\t\tRESOLVE_REF_READING, new_sha1, NULL) &&\n \t    files_log_ref_write(refs, refname, lock->old_oid.hash,\n \t\t\t\tnew_sha1, logmsg, 0, &err)) {\n \t\terror(\"%s\", err.buf);\n@@ -3403,6 +3411,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,\n struct files_reflog_iterator {\n \tstruct ref_iterator base;\n \n+\tstruct ref_store *ref_store;\n \tstruct dir_iterator *dir_iterator;\n \tstruct object_id oid;\n };\n@@ -3424,8 +3433,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)\n \t\tif (ends_with(diter->basename, \".lock\"))\n \t\t\tcontinue;\n \n-\t\tif (read_ref_full(diter->relative_path, 0,\n-\t\t\t\t  iter->oid.hash, &flags)) {\n+\t\tif (refs_read_ref_full(iter->ref_store,\n+\t\t\t\t       diter->relative_path, 0,\n+\t\t\t\t       iter->oid.hash, &flags)) {\n \t\t\terror(\"bad ref for %s\", diter->path.buf);\n \t\t\tcontinue;\n \t\t}\n@@ -3479,6 +3489,7 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st\n \tbase_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);\n \tfiles_reflog_path(refs, &sb, NULL);\n \titer->dir_iterator = dir_iterator_begin(sb.buf);\n+\titer->ref_store = ref_store;\n \tstrbuf_release(&sb);\n \treturn ref_iterator;\n }\n@@ -3718,8 +3729,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,\n \t\t\t * the transaction, so we have to read it here\n \t\t\t * to record and possibly check old_sha1:\n \t\t\t */\n-\t\t\tif (read_ref_full(referent.buf, 0,\n-\t\t\t\t\t  lock->old_oid.hash, NULL)) {\n+\t\t\tif (refs_read_ref_full(&refs->base,\n+\t\t\t\t\t       referent.buf, 0,\n+\t\t\t\t\t       lock->old_oid.hash, NULL)) {\n \t\t\t\tif (update->flags & REF_HAVE_OLD) {\n \t\t\t\t\tstrbuf_addf(err, \"cannot lock ref '%s': \"\n \t\t\t\t\t\t    \"error reading reference\",\n@@ -3873,8 +3885,9 @@ static int files_transaction_commit(struct ref_store *ref_store,\n \t * head_ref within the transaction, then split_head_update()\n \t * arranges for the reflog of HEAD to be updated, too.\n \t */\n-\thead_ref = resolve_refdup(\"HEAD\", RESOLVE_REF_NO_RECURSE,\n-\t\t\t\t  head_oid.hash, &head_type);\n+\thead_ref = refs_resolve_refdup(ref_store, \"HEAD\",\n+\t\t\t\t       RESOLVE_REF_NO_RECURSE,\n+\t\t\t\t       head_oid.hash, &head_type);\n \n \tif (head_ref && !(head_type & REF_ISSYMREF)) {\n \t\tfree(head_ref);\n@@ -4047,7 +4060,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t * so here we really only check that none of the references\n \t * that we are creating already exists.\n \t */\n-\tif (for_each_rawref(ref_present, &affected_refnames))\n+\tif (refs_for_each_rawref(&refs->base, ref_present,\n+\t\t\t\t &affected_refnames))\n \t\tdie(\"BUG: initial ref transaction called with existing refs\");\n \n \tfor (i = 0; i < transaction->nr; i++) {\n@@ -4166,7 +4180,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t\tstrbuf_release(&err);\n \t\treturn -1;\n \t}\n-\tif (!reflog_exists(refname)) {\n+\tif (!refs_reflog_exists(ref_store, refname)) {\n \t\tunlock_ref(lock);\n \t\treturn 0;\n \t}\n@@ -4197,7 +4211,7 @@ static int files_reflog_expire(struct ref_store *ref_store,\n \t}\n \n \t(*prepare_fn)(refname, sha1, cb.policy_cb);\n-\tfor_each_reflog_ent(refname, expire_reflog_ent, &cb);\n+\trefs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);\n \t(*cleanup_fn)(cb.policy_cb);\n \n \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315443","messageId":"20170326024241.31984-22-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 21/28] refs: add new ref-store api","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:34Z","receivedAt":"2017-03-26T02:45:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is not meant to cover all existing API. It adds enough to test ref\nstores with the new test program test-ref-store, coming soon and to be\nused by files-backend.c.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c               | 251 ++++++++++++++++++++++++++++++++++++++-------------\n refs.h               |  74 +++++++++++++++\n refs/files-backend.c |  13 +--\n refs/refs-internal.h |  31 +------\n 4 files changed, 270 insertions(+), 99 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 100a925bcc..c103f90b35 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -171,11 +171,23 @@ int refname_is_safe(const char *refname)\n \treturn 1;\n }\n \n+char *refs_resolve_refdup(struct ref_store *refs,\n+\t\t\t  const char *refname, int resolve_flags,\n+\t\t\t  unsigned char *sha1, int *flags)\n+{\n+\tconst char *result;\n+\n+\tresult = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\t\t\t\t\t sha1, flags);\n+\treturn xstrdup_or_null(result);\n+}\n+\n char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t     unsigned char *sha1, int *flags)\n {\n-\treturn xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,\n-\t\t\t\t\t\t  sha1, flags));\n+\treturn refs_resolve_refdup(get_main_ref_store(),\n+\t\t\t\t   refname, resolve_flags,\n+\t\t\t\t   sha1, flags);\n }\n \n /* The argument to filter_refs */\n@@ -185,13 +197,20 @@ struct ref_filter {\n \tvoid *cb_data;\n };\n \n-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)\n+int refs_read_ref_full(struct ref_store *refs, const char *refname,\n+\t\t       int resolve_flags, unsigned char *sha1, int *flags)\n {\n-\tif (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))\n+\tif (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags))\n \t\treturn 0;\n \treturn -1;\n }\n \n+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)\n+{\n+\treturn refs_read_ref_full(get_main_ref_store(), refname,\n+\t\t\t\t  resolve_flags, sha1, flags);\n+}\n+\n int read_ref(const char *refname, unsigned char *sha1)\n {\n \treturn read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);\n@@ -286,34 +305,52 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li\n \tfor_each_rawref(warn_if_dangling_symref, &data);\n }\n \n+int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/tags/\", fn, cb_data);\n+}\n+\n int for_each_tag_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/tags/\", fn, cb_data);\n+\treturn refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/tags/\", fn, cb_data);\n+\treturn refs_for_each_tag_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t     fn, cb_data);\n+}\n+\n+int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/heads/\", fn, cb_data);\n }\n \n int for_each_branch_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/heads/\", fn, cb_data);\n+\treturn refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/heads/\", fn, cb_data);\n+\treturn refs_for_each_branch_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t\tfn, cb_data);\n+}\n+\n+int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_ref_in(refs, \"refs/remotes/\", fn, cb_data);\n }\n \n int for_each_remote_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in(\"refs/remotes/\", fn, cb_data);\n+\treturn refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);\n }\n \n int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n-\treturn for_each_ref_in_submodule(submodule, \"refs/remotes/\", fn, cb_data);\n+\treturn refs_for_each_remote_ref(get_submodule_ref_store(submodule),\n+\t\t\t\t\tfn, cb_data);\n }\n \n int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n@@ -1120,14 +1157,17 @@ const char *find_descendant_ref(const char *dirname,\n \treturn NULL;\n }\n \n-int rename_ref_available(const char *old_refname, const char *new_refname)\n+int refs_rename_ref_available(struct ref_store *refs,\n+\t\t\t      const char *old_refname,\n+\t\t\t      const char *new_refname)\n {\n \tstruct string_list skip = STRING_LIST_INIT_NODUP;\n \tstruct strbuf err = STRBUF_INIT;\n \tint ok;\n \n \tstring_list_insert(&skip, old_refname);\n-\tok = !verify_refname_available(new_refname, NULL, &skip, &err);\n+\tok = !refs_verify_refname_available(refs, new_refname,\n+\t\t\t\t\t    NULL, &skip, &err);\n \tif (!ok)\n \t\terror(\"%s\", err.buf);\n \n@@ -1168,10 +1208,9 @@ int head_ref(each_ref_fn fn, void *cb_data)\n  * non-zero value, stop the iteration and return that value;\n  * otherwise, return 0.\n  */\n-static int do_for_each_ref(const char *submodule, const char *prefix,\n+static int do_for_each_ref(struct ref_store *refs, const char *prefix,\n \t\t\t   each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n-\tstruct ref_store *refs = get_submodule_ref_store(submodule);\n \tstruct ref_iterator *iter;\n \n \tif (!refs)\n@@ -1183,19 +1222,30 @@ static int do_for_each_ref(const char *submodule, const char *prefix,\n \treturn do_for_each_ref_iterator(iter, fn, cb_data);\n }\n \n+int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(refs, \"\", fn, 0, 0, cb_data);\n+}\n+\n int for_each_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"\", fn, 0, 0, cb_data);\n+\treturn refs_for_each_ref(get_main_ref_store(), 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, \"\", fn, 0, 0, cb_data);\n+\treturn refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);\n+}\n+\n+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,\n+\t\t\t each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);\n }\n \n int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);\n+\treturn refs_for_each_ref_in(get_main_ref_store(), prefix, fn, cb_data);\n }\n \n int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)\n@@ -1204,19 +1254,23 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig\n \n \tif (broken)\n \t\tflag = DO_FOR_EACH_INCLUDE_BROKEN;\n-\treturn do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);\n+\treturn do_for_each_ref(get_main_ref_store(),\n+\t\t\t       prefix, fn, 0, flag, cb_data);\n }\n \n int for_each_ref_in_submodule(const char *submodule, const char *prefix,\n-\t\teach_ref_fn fn, void *cb_data)\n+\t\t\t      each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);\n+\treturn refs_for_each_ref_in(get_submodule_ref_store(submodule),\n+\t\t\t\t    prefix, fn, cb_data);\n }\n \n int for_each_replace_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, git_replace_ref_base, fn,\n-\t\t\t       strlen(git_replace_ref_base), 0, cb_data);\n+\treturn do_for_each_ref(get_main_ref_store(),\n+\t\t\t       git_replace_ref_base, fn,\n+\t\t\t       strlen(git_replace_ref_base),\n+\t\t\t       0, cb_data);\n }\n \n int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n@@ -1224,19 +1278,25 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\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+\tret = do_for_each_ref(get_main_ref_store(),\n+\t\t\t      buf.buf, fn, 0, 0, cb_data);\n \tstrbuf_release(&buf);\n \treturn ret;\n }\n \n-int for_each_rawref(each_ref_fn fn, void *cb_data)\n+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(NULL, \"\", fn, 0,\n+\treturn do_for_each_ref(refs, \"\", fn, 0,\n \t\t\t       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n }\n \n+int for_each_rawref(each_ref_fn fn, void *cb_data)\n+{\n+\treturn refs_for_each_rawref(get_main_ref_store(), fn, cb_data);\n+}\n+\n /* This function needs to return a meaningful errno on failure */\n-const char *resolve_ref_recursively(struct ref_store *refs,\n+const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n \t\t\t\t    const char *refname,\n \t\t\t\t    int resolve_flags,\n \t\t\t\t    unsigned char *sha1, int *flags)\n@@ -1323,7 +1383,7 @@ int refs_init_db(struct strbuf *err)\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n-\treturn resolve_ref_recursively(get_main_ref_store(), refname,\n+\treturn refs_resolve_ref_unsafe(get_main_ref_store(), refname,\n \t\t\t\t       resolve_flags, sha1, flags);\n }\n \n@@ -1353,7 +1413,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,\n \tif (!refs)\n \t\treturn -1;\n \n-\tif (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||\n+\tif (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) ||\n \t    is_null_sha1(sha1))\n \t\treturn -1;\n \treturn 0;\n@@ -1506,27 +1566,42 @@ void base_ref_store_init(struct ref_store *refs,\n }\n \n /* backend functions */\n+int refs_pack_refs(struct ref_store *refs, unsigned int flags)\n+{\n+\treturn refs->be->pack_refs(refs, flags);\n+}\n+\n int pack_refs(unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_pack_refs(get_main_ref_store(), flags);\n+}\n \n-\treturn refs->be->pack_refs(refs, flags);\n+int refs_peel_ref(struct ref_store *refs, const char *refname,\n+\t\t  unsigned char *sha1)\n+{\n+\treturn refs->be->peel_ref(refs, refname, sha1);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_peel_ref(get_main_ref_store(), refname, sha1);\n+}\n \n-\treturn refs->be->peel_ref(refs, refname, sha1);\n+int refs_create_symref(struct ref_store *refs,\n+\t\t       const char *ref_target,\n+\t\t       const char *refs_heads_master,\n+\t\t       const char *logmsg)\n+{\n+\treturn refs->be->create_symref(refs, ref_target,\n+\t\t\t\t       refs_heads_master,\n+\t\t\t\t       logmsg);\n }\n \n int create_symref(const char *ref_target, const char *refs_heads_master,\n \t\t  const char *logmsg)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n-\treturn refs->be->create_symref(refs, ref_target, refs_heads_master,\n-\t\t\t\t       logmsg);\n+\treturn refs_create_symref(get_main_ref_store(), ref_target,\n+\t\t\t\t  refs_heads_master, logmsg);\n }\n \n int ref_transaction_commit(struct ref_transaction *transaction,\n@@ -1537,19 +1612,17 @@ int ref_transaction_commit(struct ref_transaction *transaction,\n \treturn refs->be->transaction_commit(refs, transaction, err);\n }\n \n-int verify_refname_available(const char *refname,\n-\t\t\t     const struct string_list *extra,\n-\t\t\t     const struct string_list *skip,\n-\t\t\t     struct strbuf *err)\n+int refs_verify_refname_available(struct ref_store *refs,\n+\t\t\t\t  const char *refname,\n+\t\t\t\t  const struct string_list *extra,\n+\t\t\t\t  const struct string_list *skip,\n+\t\t\t\t  struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n \treturn refs->be->verify_refname_available(refs, refname, extra, skip, err);\n }\n \n-int for_each_reflog(each_ref_fn fn, void *cb_data)\n+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n \tstruct ref_iterator *iter;\n \n \titer = refs->be->reflog_iterator_begin(refs);\n@@ -1557,43 +1630,84 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n \treturn do_for_each_ref_iterator(iter, fn, cb_data);\n }\n \n-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n-\t\t\t\tvoid *cb_data)\n+int for_each_reflog(each_ref_fn fn, void *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_for_each_reflog(get_main_ref_store(), fn, cb_data);\n+}\n \n+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     each_reflog_ent_fn fn,\n+\t\t\t\t     void *cb_data)\n+{\n \treturn refs->be->for_each_reflog_ent_reverse(refs, refname,\n \t\t\t\t\t\t     fn, cb_data);\n }\n \n+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,\n+\t\t\t\tvoid *cb_data)\n+{\n+\treturn refs_for_each_reflog_ent_reverse(get_main_ref_store(),\n+\t\t\t\t\t\trefname, fn, cb_data);\n+}\n+\n+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,\n+\t\t\t     each_reflog_ent_fn fn, void *cb_data)\n+{\n+\treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n+}\n+\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,\n \t\t\tvoid *cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_for_each_reflog_ent(get_main_ref_store(), refname,\n+\t\t\t\t\tfn, cb_data);\n+}\n \n-\treturn refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);\n+int refs_reflog_exists(struct ref_store *refs, const char *refname)\n+{\n+\treturn refs->be->reflog_exists(refs, refname);\n }\n \n int reflog_exists(const char *refname)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_reflog_exists(get_main_ref_store(), refname);\n+}\n \n-\treturn refs->be->reflog_exists(refs, refname);\n+int refs_create_reflog(struct ref_store *refs, const char *refname,\n+\t\t       int force_create, struct strbuf *err)\n+{\n+\treturn refs->be->create_reflog(refs, refname, force_create, err);\n }\n \n int safe_create_reflog(const char *refname, int force_create,\n \t\t       struct strbuf *err)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_create_reflog(get_main_ref_store(), refname,\n+\t\t\t\t  force_create, err);\n+}\n \n-\treturn refs->be->create_reflog(refs, refname, force_create, err);\n+int refs_delete_reflog(struct ref_store *refs, const char *refname)\n+{\n+\treturn refs->be->delete_reflog(refs, refname);\n }\n \n int delete_reflog(const char *refname)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_delete_reflog(get_main_ref_store(), refname);\n+}\n \n-\treturn refs->be->delete_reflog(refs, refname);\n+int refs_reflog_expire(struct ref_store *refs,\n+\t\t       const char *refname, const unsigned char *sha1,\n+\t\t       unsigned int flags,\n+\t\t       reflog_expiry_prepare_fn prepare_fn,\n+\t\t       reflog_expiry_should_prune_fn should_prune_fn,\n+\t\t       reflog_expiry_cleanup_fn cleanup_fn,\n+\t\t       void *policy_cb_data)\n+{\n+\treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n+\t\t\t\t       prepare_fn, should_prune_fn,\n+\t\t\t\t       cleanup_fn, policy_cb_data);\n }\n \n int reflog_expire(const char *refname, const unsigned char *sha1,\n@@ -1603,11 +1717,10 @@ int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  reflog_expiry_cleanup_fn cleanup_fn,\n \t\t  void *policy_cb_data)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n-\treturn refs->be->reflog_expire(refs, refname, sha1, flags,\n-\t\t\t\t       prepare_fn, should_prune_fn,\n-\t\t\t\t       cleanup_fn, policy_cb_data);\n+\treturn refs_reflog_expire(get_main_ref_store(),\n+\t\t\t\t  refname, sha1, flags,\n+\t\t\t\t  prepare_fn, should_prune_fn,\n+\t\t\t\t  cleanup_fn, policy_cb_data);\n }\n \n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n@@ -1618,16 +1731,24 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \treturn refs->be->initial_transaction_commit(refs, transaction, err);\n }\n \n-int delete_refs(struct string_list *refnames, unsigned int flags)\n+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,\n+\t\t     unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n-\n \treturn refs->be->delete_refs(refs, refnames, flags);\n }\n \n-int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n+int delete_refs(struct string_list *refnames, unsigned int flags)\n {\n-\tstruct ref_store *refs = get_main_ref_store();\n+\treturn refs_delete_refs(get_main_ref_store(), refnames, flags);\n+}\n \n+int refs_rename_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg)\n+{\n \treturn refs->be->rename_ref(refs, oldref, newref, logmsg);\n }\n+\n+int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n+{\n+\treturn refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);\n+}\ndiff --git a/refs.h b/refs.h\nindex e6d8f67895..eaa31e8193 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -57,16 +57,50 @@ struct string_list;\n #define RESOLVE_REF_NO_RECURSE 0x02\n #define RESOLVE_REF_ALLOW_BAD_NAME 0x04\n \n+const char *refs_resolve_ref_unsafe(struct ref_store *refs,\n+\t\t\t\t    const char *refname,\n+\t\t\t\t    int resolve_flags,\n+\t\t\t\t    unsigned char *sha1,\n+\t\t\t\t    int *flags);\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags);\n \n+char *refs_resolve_refdup(struct ref_store *refs,\n+\t\t\t  const char *refname, int resolve_flags,\n+\t\t\t  unsigned char *sha1, int *flags);\n char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t     unsigned char *sha1, int *flags);\n \n+int refs_read_ref_full(struct ref_store *refs, const char *refname,\n+\t\t       int resolve_flags, unsigned char *sha1, int *flags);\n int read_ref_full(const char *refname, int resolve_flags,\n \t\t  unsigned char *sha1, int *flags);\n int read_ref(const char *refname, unsigned char *sha1);\n \n+/*\n+ * Return 0 if a reference named refname could be created without\n+ * conflicting with the name of an existing reference. Otherwise,\n+ * return a negative value and write an explanation to err. If extras\n+ * is non-NULL, it is a list of additional refnames with which refname\n+ * is not allowed to conflict. If skip is non-NULL, ignore potential\n+ * conflicts with refs in skip (e.g., because they are scheduled for\n+ * deletion in the same operation). Behavior is undefined if the same\n+ * name is listed in both extras and skip.\n+ *\n+ * Two reference names conflict if one of them exactly matches the\n+ * leading components of the other; e.g., \"foo/bar\" conflicts with\n+ * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n+ * \"foo/barbados\".\n+ *\n+ * extras and skip must be sorted.\n+ */\n+\n+int refs_verify_refname_available(struct ref_store *refs,\n+\t\t\t\t  const char *refname,\n+\t\t\t\t  const struct string_list *extra,\n+\t\t\t\t  const struct string_list *skip,\n+\t\t\t\t  struct strbuf *err);\n+\n int ref_exists(const char *refname);\n \n int should_autocreate_reflog(const char *refname);\n@@ -83,6 +117,8 @@ extern int refs_init_db(struct strbuf *err);\n  * Symbolic references are considered unpeelable, even if they\n  * ultimately resolve to a peelable tag.\n  */\n+int refs_peel_ref(struct ref_store *refs, const char *refname,\n+\t\t  unsigned char *sha1);\n int peel_ref(const char *refname, unsigned char *sha1);\n \n /**\n@@ -196,6 +232,17 @@ typedef int each_ref_fn(const char *refname,\n  * modifies the reference also returns a nonzero value to immediately\n  * stop the iteration.\n  */\n+int refs_for_each_ref(struct ref_store *refs,\n+\t\t      each_ref_fn fn, void *cb_data);\n+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,\n+\t\t\t each_ref_fn fn, void *cb_data);\n+int refs_for_each_tag_ref(struct ref_store *refs,\n+\t\t\t  each_ref_fn fn, void *cb_data);\n+int refs_for_each_branch_ref(struct ref_store *refs,\n+\t\t\t     each_ref_fn fn, void *cb_data);\n+int refs_for_each_remote_ref(struct ref_store *refs,\n+\t\t\t     each_ref_fn fn, void *cb_data);\n+\n int head_ref(each_ref_fn fn, void *cb_data);\n int for_each_ref(each_ref_fn fn, void *cb_data);\n int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);\n@@ -225,6 +272,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data);\n int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);\n \n /* can be used to learn about broken ref and symref */\n+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_rawref(each_ref_fn fn, void *cb_data);\n \n static inline const char *has_glob_specials(const char *pattern)\n@@ -248,6 +296,7 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,\n  * Write a packed-refs file for the current repository.\n  * flags: Combination of the above PACK_REFS_* flags.\n  */\n+int refs_pack_refs(struct ref_store *refs, unsigned int flags);\n int pack_refs(unsigned int flags);\n \n /*\n@@ -263,6 +312,8 @@ int pack_refs(unsigned int flags);\n /*\n  * Setup reflog before using. Fill in err and return -1 on failure.\n  */\n+int refs_create_reflog(struct ref_store *refs, const char *refname,\n+\t\t       int force_create, struct strbuf *err);\n int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);\n \n /** Reads log for the value of ref during at_time. **/\n@@ -272,6 +323,7 @@ int read_ref_at(const char *refname, unsigned int flags,\n \t\tunsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);\n \n /** Check if a particular reflog exists */\n+int refs_reflog_exists(struct ref_store *refs, const char *refname);\n int reflog_exists(const char *refname);\n \n /*\n@@ -290,9 +342,12 @@ int delete_ref(const char *msg, const char *refname,\n  * an all-or-nothing transaction). flags is passed through to\n  * ref_transaction_delete().\n  */\n+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,\n+\t\t     unsigned int flags);\n int delete_refs(struct string_list *refnames, unsigned int flags);\n \n /** Delete a reflog */\n+int refs_delete_reflog(struct ref_store *refs, const char *refname);\n int delete_reflog(const char *refname);\n \n /* iterate over reflog entries */\n@@ -301,6 +356,12 @@ typedef int each_reflog_ent_fn(\n \t\tconst char *committer, unsigned long timestamp,\n \t\tint tz, const char *msg, void *cb_data);\n \n+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,\n+\t\t\t     each_reflog_ent_fn fn, void *cb_data);\n+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,\n+\t\t\t\t     const char *refname,\n+\t\t\t\t     each_reflog_ent_fn fn,\n+\t\t\t\t     void *cb_data);\n int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);\n int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);\n \n@@ -308,6 +369,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void\n  * Calls the specified function for each reflog file until it returns nonzero,\n  * and returns the value\n  */\n+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_reflog(each_ref_fn fn, void *cb_data);\n \n #define REFNAME_ALLOW_ONELEVEL 1\n@@ -328,8 +390,12 @@ const char *prettify_refname(const char *refname);\n char *shorten_unambiguous_ref(const char *refname, int strict);\n \n /** rename ref, return 0 on success **/\n+int refs_rename_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg);\n int rename_ref(const char *oldref, const char *newref, const char *logmsg);\n \n+int refs_create_symref(struct ref_store *refs, const char *refname,\n+\t\t       const char *target, const char *logmsg);\n int create_symref(const char *refname, const char *target, const char *logmsg);\n \n /*\n@@ -552,6 +618,14 @@ typedef void reflog_expiry_cleanup_fn(void *cb_data);\n  * enum expire_reflog_flags. The three function pointers are described\n  * above. On success, return zero.\n  */\n+int refs_reflog_expire(struct ref_store *refs,\n+\t\t       const char *refname,\n+\t\t       const unsigned char *sha1,\n+\t\t       unsigned int flags,\n+\t\t       reflog_expiry_prepare_fn prepare_fn,\n+\t\t       reflog_expiry_should_prune_fn should_prune_fn,\n+\t\t       reflog_expiry_cleanup_fn cleanup_fn,\n+\t\t       void *policy_cb_data);\n int reflog_expire(const char *refname, const unsigned char *sha1,\n \t\t  unsigned int flags,\n \t\t  reflog_expiry_prepare_fn prepare_fn,\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex d97a924860..dec8540a0f 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1314,7 +1314,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)\n \t\t\t\t\t create_dir_entry(refs, refname.buf,\n \t\t\t\t\t\t\t  refname.len, 1));\n \t\t} else {\n-\t\t\tif (!resolve_ref_recursively(&refs->base,\n+\t\t\tif (!refs_resolve_ref_unsafe(&refs->base,\n \t\t\t\t\t\t     refname.buf,\n \t\t\t\t\t\t     RESOLVE_REF_READING,\n \t\t\t\t\t\t     sha1, &flag)) {\n@@ -1623,7 +1623,8 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \t\t * another reference such as \"refs/foo\". There is no\n \t\t * reason to expect this error to be transitory.\n \t\t */\n-\t\tif (verify_refname_available(refname, extras, skip, err)) {\n+\t\tif (refs_verify_refname_available(&refs->base, refname,\n+\t\t\t\t\t\t  extras, skip, err)) {\n \t\t\tif (mustexist) {\n \t\t\t\t/*\n \t\t\t\t * To the user the relevant error is\n@@ -2671,7 +2672,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\t\t    oldrefname);\n \t\tgoto out;\n \t}\n-\tif (!rename_ref_available(oldrefname, newrefname)) {\n+\tif (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {\n \t\tret = 1;\n \t\tgoto out;\n \t}\n@@ -4055,9 +4056,9 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,\n \t\tif ((update->flags & REF_HAVE_OLD) &&\n \t\t    !is_null_sha1(update->old_sha1))\n \t\t\tdie(\"BUG: initial ref transaction with old_sha1 set\");\n-\t\tif (verify_refname_available(update->refname,\n-\t\t\t\t\t     &affected_refnames, NULL,\n-\t\t\t\t\t     err)) {\n+\t\tif (refs_verify_refname_available(&refs->base, update->refname,\n+\t\t\t\t\t\t  &affected_refnames, NULL,\n+\t\t\t\t\t\t  err)) {\n \t\t\tret = TRANSACTION_NAME_CONFLICT;\n \t\t\tgoto cleanup;\n \t\t}\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex f20dde39ee..5f26208c2c 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -112,28 +112,6 @@ enum peel_status {\n enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);\n \n /*\n- * Return 0 if a reference named refname could be created without\n- * conflicting with the name of an existing reference. Otherwise,\n- * return a negative value and write an explanation to err. If extras\n- * is non-NULL, it is a list of additional refnames with which refname\n- * is not allowed to conflict. If skip is non-NULL, ignore potential\n- * conflicts with refs in skip (e.g., because they are scheduled for\n- * deletion in the same operation). Behavior is undefined if the same\n- * name is listed in both extras and skip.\n- *\n- * Two reference names conflict if one of them exactly matches the\n- * leading components of the other; e.g., \"foo/bar\" conflicts with\n- * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n- * \"foo/barbados\".\n- *\n- * extras and skip must be sorted.\n- */\n-int verify_refname_available(const char *newname,\n-\t\t\t     const struct string_list *extras,\n-\t\t\t     const struct string_list *skip,\n-\t\t\t     struct strbuf *err);\n-\n-/*\n  * Copy the reflog message msg to buf, which has been allocated sufficiently\n  * large, while cleaning up the whitespaces.  Especially, convert LF to space,\n  * because reflog file is one line per entry.\n@@ -252,7 +230,9 @@ const char *find_descendant_ref(const char *dirname,\n  * processes (though rename_ref() catches some races that might get by\n  * this check).\n  */\n-int rename_ref_available(const char *old_refname, const char *new_refname);\n+int refs_rename_ref_available(struct ref_store *refs,\n+\t\t\t      const char *old_refname,\n+\t\t\t      const char *new_refname);\n \n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define SYMREF_MAXDEPTH 5\n@@ -646,9 +626,4 @@ struct ref_store {\n void base_ref_store_init(struct ref_store *refs,\n \t\t\t const struct ref_storage_be *be);\n \n-const char *resolve_ref_recursively(struct ref_store *refs,\n-\t\t\t\t    const char *refname,\n-\t\t\t\t    int resolve_flags,\n-\t\t\t\t    unsigned char *sha1, int *flags);\n-\n #endif /* REFS_REFS_INTERNAL_H */\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315444","messageId":"20170326024241.31984-27-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 26/28] t1405: some basic tests on main ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:39Z","receivedAt":"2017-03-26T02:45:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1405-main-ref-store.sh (new +x) | 123 +++++++++++++++++++++++++++++++++++++\n 1 file changed, 123 insertions(+)\n create mode 100755 t/t1405-main-ref-store.sh\n\ndiff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh\nnew file mode 100755\nindex 0000000000..77e1c130c2\n--- /dev/null\n+++ b/t/t1405-main-ref-store.sh\n@@ -0,0 +1,123 @@\n+#!/bin/sh\n+\n+test_description='test main ref store api'\n+\n+. ./test-lib.sh\n+\n+RUN=\"test-ref-store main\"\n+\n+test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '\n+\ttest_commit one &&\n+\tN=`find .git/refs -type f | wc -l` &&\n+\ttest \"$N\" != 0 &&\n+\t$RUN pack-refs 3 &&\n+\tN=`find .git/refs -type f | wc -l`\n+'\n+\n+test_expect_success 'peel_ref(new-tag)' '\n+\tgit rev-parse HEAD >expected &&\n+\tgit tag -a -m new-tag new-tag HEAD &&\n+\t$RUN peel-ref refs/tags/new-tag >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create_symref(FOO, refs/heads/master)' '\n+\t$RUN create-symref FOO refs/heads/master nothing &&\n+\techo refs/heads/master >expected &&\n+\tgit symbolic-ref FOO >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '\n+\tgit rev-parse FOO -- &&\n+\tgit rev-parse refs/tags/new-tag -- &&\n+\t$RUN delete-refs 0 FOO refs/tags/new-tag &&\n+\ttest_must_fail git rev-parse FOO -- &&\n+\ttest_must_fail git rev-parse refs/tags/new-tag --\n+'\n+\n+test_expect_success 'rename_refs(master, new-master)' '\n+\tgit rev-parse master >expected &&\n+\t$RUN rename-ref refs/heads/master refs/heads/new-master &&\n+\tgit rev-parse new-master >actual &&\n+\ttest_cmp expected actual &&\n+\ttest_commit recreate-master\n+'\n+\n+test_expect_success 'for_each_ref(refs/heads/)' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tmaster 0x0\n+\tnew-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'resolve_ref(new-master)' '\n+\tSHA1=`git rev-parse new-master` &&\n+\techo \"$SHA1 refs/heads/new-master 0x0\" >expected &&\n+\t$RUN resolve-ref refs/heads/new-master 0 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify_ref(new-master)' '\n+\t$RUN verify-ref refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_reflog()' '\n+\t$RUN for-each-reflog | sort | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tHEAD 0x1\n+\trefs/heads/master 0x0\n+\trefs/heads/new-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'for_each_reflog_ent()' '\n+\t$RUN for-each-reflog-ent HEAD >actual &&\n+\thead -n1 actual | grep one &&\n+\ttail -n2 actual | head -n1 | grep recreate-master\n+'\n+\n+test_expect_success 'for_each_reflog_ent_reverse()' '\n+\t$RUN for-each-reflog-ent-reverse HEAD >actual &&\n+\thead -n1 actual | grep recreate-master &&\n+\ttail -n2 actual | head -n1 | grep one\n+'\n+\n+test_expect_success 'reflog_exists(HEAD)' '\n+\t$RUN reflog-exists HEAD\n+'\n+\n+test_expect_success 'delete_reflog(HEAD)' '\n+\t$RUN delete-reflog HEAD &&\n+\t! test -f .git/logs/HEAD\n+'\n+\n+test_expect_success 'create-reflog(HEAD)' '\n+\t$RUN create-reflog HEAD 1 &&\n+\ttest -f .git/logs/HEAD\n+'\n+\n+test_expect_success 'delete_ref(refs/heads/foo)' '\n+\tgit checkout -b foo &&\n+\tFOO_SHA1=`git rev-parse foo` &&\n+\tgit checkout --detach &&\n+\ttest_commit bar-commit &&\n+\tgit checkout -b bar &&\n+\tBAR_SHA1=`git rev-parse bar` &&\n+\t$RUN update-ref updating refs/heads/foo $BAR_SHA1 $FOO_SHA1 0 &&\n+\techo $BAR_SHA1 >expected &&\n+\tgit rev-parse refs/heads/foo >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'delete_ref(refs/heads/foo)' '\n+\tSHA1=`git rev-parse foo` &&\n+\tgit checkout --detach &&\n+\t$RUN delete-ref msg refs/heads/foo $SHA1 0 &&\n+\ttest_must_fail git rev-parse refs/heads/foo --\n+'\n+\n+test_done\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315445","messageId":"20170326024241.31984-29-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 28/28] refs.h: add a note about sorting order of for_each_ref_*","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:41Z","receivedAt":"2017-03-26T02:46:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.h                         | 4 ++--\n t/t1405-main-ref-store.sh      | 6 ++++++\n t/t1406-submodule-ref-store.sh | 6 ++++++\n 3 files changed, 14 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.h b/refs.h\nindex 1a07f9d86f..49e97d7d5f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -230,7 +230,7 @@ typedef int each_ref_fn(const char *refname,\n  * it is not safe to modify references while an iteration is in\n  * progress, unless the same callback function invocation that\n  * modifies the reference also returns a nonzero value to immediately\n- * stop the iteration.\n+ * stop the iteration. Returned references are sorted.\n  */\n int refs_for_each_ref(struct ref_store *refs,\n \t\t      each_ref_fn fn, void *cb_data);\n@@ -370,7 +370,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void\n \n /*\n  * Calls the specified function for each reflog file until it returns nonzero,\n- * and returns the value\n+ * and returns the value. Reflog file order is unspecified.\n  */\n int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);\n int for_each_reflog(each_ref_fn fn, void *cb_data);\ndiff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh\nindex 77e1c130c2..490521f8cb 100755\n--- a/t/t1405-main-ref-store.sh\n+++ b/t/t1405-main-ref-store.sh\n@@ -53,6 +53,12 @@ test_expect_success 'for_each_ref(refs/heads/)' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'for_each_ref() is sorted' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tsort actual > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'resolve_ref(new-master)' '\n \tSHA1=`git rev-parse new-master` &&\n \techo \"$SHA1 refs/heads/new-master 0x0\" >expected &&\ndiff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\nindex 22214ebd32..13b5454c56 100755\n--- a/t/t1406-submodule-ref-store.sh\n+++ b/t/t1406-submodule-ref-store.sh\n@@ -47,6 +47,12 @@ test_expect_success 'for_each_ref(refs/heads/)' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'for_each_ref() is sorted' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tsort actual > expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'resolve_ref(master)' '\n \tSHA1=`git -C sub rev-parse master` &&\n \techo \"$SHA1 refs/heads/master 0x0\" >expected &&\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315446","messageId":"20170326024241.31984-26-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 25/28] t/helper: add test-ref-store to test ref-store functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:38Z","receivedAt":"2017-03-26T02:46:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile                        |   1 +\n t/helper/.gitignore             |   1 +\n t/helper/test-ref-store.c (new) | 277 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 279 insertions(+)\n create mode 100644 t/helper/test-ref-store.c\n\ndiff --git a/Makefile b/Makefile\nindex a5a11e721a..5f3844e33e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -622,6 +622,7 @@ TEST_PROGRAMS_NEED_X += test-parse-options\n TEST_PROGRAMS_NEED_X += test-path-utils\n TEST_PROGRAMS_NEED_X += test-prio-queue\n TEST_PROGRAMS_NEED_X += test-read-cache\n+TEST_PROGRAMS_NEED_X += test-ref-store\n TEST_PROGRAMS_NEED_X += test-regex\n TEST_PROGRAMS_NEED_X += test-revision-walking\n TEST_PROGRAMS_NEED_X += test-run-command\ndiff --git a/t/helper/.gitignore b/t/helper/.gitignore\nindex d6e8b36798..5f68aa8f8a 100644\n--- a/t/helper/.gitignore\n+++ b/t/helper/.gitignore\n@@ -19,6 +19,7 @@\n /test-path-utils\n /test-prio-queue\n /test-read-cache\n+/test-ref-store\n /test-regex\n /test-revision-walking\n /test-run-command\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nnew file mode 100644\nindex 0000000000..2d84c45ffe\n--- /dev/null\n+++ b/t/helper/test-ref-store.c\n@@ -0,0 +1,277 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+\n+static const char *notnull(const char *arg, const char *name)\n+{\n+\tif (!arg)\n+\t\tdie(\"%s required\", name);\n+\treturn arg;\n+}\n+\n+static unsigned int arg_flags(const char *arg, const char *name)\n+{\n+\treturn atoi(notnull(arg, name));\n+}\n+\n+static const char **get_store(const char **argv, struct ref_store **refs)\n+{\n+\tconst char *gitdir;\n+\n+\tif (!argv[0]) {\n+\t\tdie(\"ref store required\");\n+\t} else if (!strcmp(argv[0], \"main\")) {\n+\t\t*refs = get_main_ref_store();\n+\t} else if (skip_prefix(argv[0], \"submodule:\", &gitdir)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tint ret;\n+\n+\t\tret = strbuf_git_path_submodule(&sb, gitdir, \"objects/\");\n+\t\tif (ret)\n+\t\t\tdie(\"strbuf_git_path_submodule failed: %d\", ret);\n+\t\tadd_to_alternates_memory(sb.buf);\n+\t\tstrbuf_release(&sb);\n+\n+\t\t*refs = get_submodule_ref_store(gitdir);\n+\t} else\n+\t\tdie(\"unknown backend %s\", argv[0]);\n+\n+\tif (!*refs)\n+\t\tdie(\"no ref store\");\n+\n+\t/* consume store-specific optional arguments if needed */\n+\n+\treturn argv + 1;\n+}\n+\n+\n+static int cmd_pack_refs(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\n+\treturn refs_pack_refs(refs, flags);\n+}\n+\n+static int cmd_peel_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tunsigned char sha1[20];\n+\tint ret;\n+\n+\tret = refs_peel_ref(refs, refname, sha1);\n+\tif (!ret)\n+\t\tputs(sha1_to_hex(sha1));\n+\treturn ret;\n+}\n+\n+static int cmd_create_symref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *target = notnull(*argv++, \"target\");\n+\tconst char *logmsg = *argv++;\n+\n+\treturn refs_create_symref(refs, refname, target, logmsg);\n+}\n+\n+static int cmd_delete_refs(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tstruct string_list refnames = STRING_LIST_INIT_NODUP;\n+\n+\twhile (*argv)\n+\t\tstring_list_append(&refnames, *argv++);\n+\n+\treturn refs_delete_refs(refs, &refnames, flags);\n+}\n+\n+static int cmd_rename_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *oldref = notnull(*argv++, \"oldref\");\n+\tconst char *newref = notnull(*argv++, \"newref\");\n+\tconst char *logmsg = *argv++;\n+\n+\treturn refs_rename_ref(refs, oldref, newref, logmsg);\n+}\n+\n+static int each_ref(const char *refname, const struct object_id *oid,\n+\t\t    int flags, void *cb_data)\n+{\n+\tprintf(\"%s %s 0x%x\\n\", oid_to_hex(oid), refname, flags);\n+\treturn 0;\n+}\n+\n+static int cmd_for_each_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *prefix = notnull(*argv++, \"prefix\");\n+\n+\treturn refs_for_each_ref_in(refs, prefix, each_ref, NULL);\n+}\n+\n+static int cmd_resolve_ref(struct ref_store *refs, const char **argv)\n+{\n+\tunsigned char sha1[20];\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tint resolve_flags = arg_flags(*argv++, \"resolve-flags\");\n+\tint flags;\n+\tconst char *ref;\n+\n+\tref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,\n+\t\t\t\t      sha1, &flags);\n+\tprintf(\"%s %s 0x%x\\n\", sha1_to_hex(sha1), ref, flags);\n+\treturn ref ? 0 : 1;\n+}\n+\n+static int cmd_verify_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n+\n+\tret = refs_verify_refname_available(refs, refname, NULL, NULL, &err);\n+\tif (err.len)\n+\t\tputs(err.buf);\n+\treturn ret;\n+}\n+\n+static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)\n+{\n+\treturn refs_for_each_reflog(refs, each_ref, NULL);\n+}\n+\n+static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,\n+\t\t       const char *committer, unsigned long timestamp,\n+\t\t       int tz, const char *msg, void *cb_data)\n+{\n+\tprintf(\"%s %s %s %lu %d %s\\n\",\n+\t       oid_to_hex(old_oid), oid_to_hex(new_oid),\n+\t       committer, timestamp, tz, msg);\n+\treturn 0;\n+}\n+\n+static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_for_each_reflog_ent(refs, refname, each_reflog, refs);\n+}\n+\n+static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);\n+}\n+\n+static int cmd_reflog_exists(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn !refs_reflog_exists(refs, refname);\n+}\n+\n+static int cmd_create_reflog(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tint force_create = arg_flags(*argv++, \"force-create\");\n+\tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n+\n+\tret = refs_create_reflog(refs, refname, force_create, &err);\n+\tif (err.len)\n+\t\tputs(err.buf);\n+\treturn ret;\n+}\n+\n+static int cmd_delete_reflog(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\n+\treturn refs_delete_reflog(refs, refname);\n+}\n+\n+static int cmd_reflog_expire(struct ref_store *refs, const char **argv)\n+{\n+\tdie(\"not supported yet\");\n+}\n+\n+static int cmd_delete_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *msg = notnull(*argv++, \"msg\");\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tunsigned char old_sha1[20];\n+\n+\tif (get_sha1_hex(sha1_buf, old_sha1))\n+\t\tdie(\"not sha-1\");\n+\n+\treturn refs_delete_ref(refs, msg, refname, old_sha1, flags);\n+}\n+\n+static int cmd_update_ref(struct ref_store *refs, const char **argv)\n+{\n+\tconst char *msg = notnull(*argv++, \"msg\");\n+\tconst char *refname = notnull(*argv++, \"refname\");\n+\tconst char *new_sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tconst char *old_sha1_buf = notnull(*argv++, \"old-sha1\");\n+\tunsigned int flags = arg_flags(*argv++, \"flags\");\n+\tunsigned char old_sha1[20];\n+\tunsigned char new_sha1[20];\n+\n+\tif (get_sha1_hex(old_sha1_buf, old_sha1) ||\n+\t    get_sha1_hex(new_sha1_buf, new_sha1))\n+\t\tdie(\"not sha-1\");\n+\n+\treturn refs_update_ref(refs, msg, refname,\n+\t\t\t       new_sha1, old_sha1,\n+\t\t\t       flags, UPDATE_REFS_DIE_ON_ERR);\n+}\n+\n+struct command {\n+\tconst char *name;\n+\tint (*func)(struct ref_store *refs, const char **argv);\n+};\n+\n+static struct command commands[] = {\n+\t{ \"pack-refs\", cmd_pack_refs },\n+\t{ \"peel-ref\", cmd_peel_ref },\n+\t{ \"create-symref\", cmd_create_symref },\n+\t{ \"delete-refs\", cmd_delete_refs },\n+\t{ \"rename-ref\", cmd_rename_ref },\n+\t{ \"for-each-ref\", cmd_for_each_ref },\n+\t{ \"resolve-ref\", cmd_resolve_ref },\n+\t{ \"verify-ref\", cmd_verify_ref },\n+\t{ \"for-each-reflog\", cmd_for_each_reflog },\n+\t{ \"for-each-reflog-ent\", cmd_for_each_reflog_ent },\n+\t{ \"for-each-reflog-ent-reverse\", cmd_for_each_reflog_ent_reverse },\n+\t{ \"reflog-exists\", cmd_reflog_exists },\n+\t{ \"create-reflog\", cmd_create_reflog },\n+\t{ \"delete-reflog\", cmd_delete_reflog },\n+\t{ \"reflog-expire\", cmd_reflog_expire },\n+\t/*\n+\t * backend transaction functions can't be tested separately\n+\t */\n+\t{ \"delete-ref\", cmd_delete_ref },\n+\t{ \"update-ref\", cmd_update_ref },\n+\t{ NULL, NULL }\n+};\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tstruct ref_store *refs;\n+\tconst char *func;\n+\tstruct command *cmd;\n+\n+\tsetup_git_directory();\n+\n+\targv = get_store(argv + 1, &refs);\n+\n+\tfunc = *argv++;\n+\tif (!func)\n+\t\tdie(\"ref function required\");\n+\tfor (cmd = commands; cmd->name; cmd++) {\n+\t\tif (!strcmp(func, cmd->name))\n+\t\t\treturn cmd->func(refs, argv);\n+\t}\n+\tdie(\"unknown function %s\", func);\n+\treturn 0;\n+}\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315447","messageId":"20170326024241.31984-28-pclouds@gmail.com","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"[PATCH v7 27/28] t1406: new tests for submodule ref store","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2017-03-26T02:42:40Z","receivedAt":"2017-03-26T02:46:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1406-submodule-ref-store.sh (new +x) | 95 +++++++++++++++++++++++++++++++++\n 1 file changed, 95 insertions(+)\n create mode 100755 t/t1406-submodule-ref-store.sh\n\ndiff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh\nnew file mode 100755\nindex 0000000000..22214ebd32\n--- /dev/null\n+++ b/t/t1406-submodule-ref-store.sh\n@@ -0,0 +1,95 @@\n+#!/bin/sh\n+\n+test_description='test submodule ref store api'\n+\n+. ./test-lib.sh\n+\n+RUN=\"test-ref-store submodule:sub\"\n+\n+test_expect_success 'setup' '\n+\tgit init sub &&\n+\t(\n+\t\tcd sub &&\n+\t\ttest_commit first &&\n+\t\tgit checkout -b new-master\n+\t)\n+'\n+\n+test_expect_success 'pack_refs() not allowed' '\n+\ttest_must_fail $RUN pack-refs 3\n+'\n+\n+test_expect_success 'peel_ref(new-tag)' '\n+\tgit -C sub rev-parse HEAD >expected &&\n+\tgit -C sub tag -a -m new-tag new-tag HEAD &&\n+\t$RUN peel-ref refs/tags/new-tag >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create_symref() not allowed' '\n+\ttest_must_fail $RUN create-symref FOO refs/heads/master nothing\n+'\n+\n+test_expect_success 'delete_refs() not allowed' '\n+\ttest_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag\n+'\n+\n+test_expect_success 'rename_refs() not allowed' '\n+\ttest_must_fail $RUN rename-ref refs/heads/master refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_ref(refs/heads/)' '\n+\t$RUN for-each-ref refs/heads/ | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tmaster 0x0\n+\tnew-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'resolve_ref(master)' '\n+\tSHA1=`git -C sub rev-parse master` &&\n+\techo \"$SHA1 refs/heads/master 0x0\" >expected &&\n+\t$RUN resolve-ref refs/heads/master 0 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify_ref(new-master)' '\n+\t$RUN verify-ref refs/heads/new-master\n+'\n+\n+test_expect_success 'for_each_reflog()' '\n+\t$RUN for-each-reflog | sort | cut -c 42- >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tHEAD 0x1\n+\trefs/heads/master 0x0\n+\trefs/heads/new-master 0x0\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'for_each_reflog_ent()' '\n+\t$RUN for-each-reflog-ent HEAD >actual && cat actual &&\n+\thead -n1 actual | grep first &&\n+\ttail -n2 actual | head -n1 | grep master.to.new\n+'\n+\n+test_expect_success 'for_each_reflog_ent_reverse()' '\n+\t$RUN for-each-reflog-ent-reverse HEAD >actual &&\n+\thead -n1 actual | grep master.to.new &&\n+\ttail -n2 actual | head -n1 | grep first\n+'\n+\n+test_expect_success 'reflog_exists(HEAD)' '\n+\t$RUN reflog-exists HEAD\n+'\n+\n+test_expect_success 'delete_reflog() not allowed' '\n+\ttest_must_fail $RUN delete-reflog HEAD\n+'\n+\n+test_expect_success 'create-reflog() not allowed' '\n+\ttest_must_fail $RUN create-reflog HEAD 1\n+'\n+\n+test_done\n-- \n2.11.0.157.gd943d85\n\n"},{"id":"315660","messageId":"a44dfc2e-8196-a782-be69-682f252d2592@alum.mit.edu","threadId":"45126","inReplyTo":"CACsJy8CpUzRjZG7V8a6XvzcjxhwTnAfpwYed3OSLrygUPOK66g@mail.gmail.com","subject":"Re: [PATCH v6 18/27] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-03-29T10:55:42Z","receivedAt":"2017-03-29T10:58:36Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/26/2017 04:16 AM, Duy Nguyen wrote:\n> On Mon, Mar 20, 2017 at 4:18 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>>> +/* ref_store_init flags */\n>>> +#define REF_STORE_READ               (1 << 0)\n>>\n>> I asked [1] in reply to v5 whether `REF_STORE_READ` is really necessary\n>> but I don't think you replied. Surely a reference store that we can't\n>> read would be useless? Can't we just assume that any `ref_store` is\n>> readable and drop this constant?\n> \n> I deleted it then I realized many lines like these\n> \n>     files_downcast(ref_store, REF_STORE_READ, \"read_raw_ref\");\n> \n> would become\n> \n>     files_downcast(ref_store, 0, \"read_raw_ref\");\n> \n> I think for newcomers, the former gives a better hint what the second\n> argument is than a plain zero in the latter, so I'm inclined to keep\n> it.\n\nThat's OK with me; I don't feel strongly about it.\n\nMichael\n\n"},{"id":"315981","messageId":"4e4fd98d-055d-6e2b-845d-51d3f1c2856f@alum.mit.edu","threadId":"45126","inReplyTo":"20170326024241.31984-20-pclouds@gmail.com","subject":"Re: [PATCH v7 19/28] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-04-01T04:02:37Z","receivedAt":"2017-04-01T04:02:50Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/26/2017 04:42 AM, Nguyễn Thái Ngọc Duy wrote:\n> files-backend.c is unlearning submodules. Instead of having a specific\n> check for submodules to see what operation is allowed, files backend\n> now takes a set of flags at init. Each operation will check if the\n> required flags is present before performing.\n> \n> For now we have four flags: read, write and odb access. Main ref store\n> has all flags, obviously, while submodule stores are read-only and have\n> access to odb (*).\n> \n> The \"main\" flag stays because many functions in the backend calls\n> frontend ones without a ref store, so these functions always target the\n> main ref store. Ideally the flag should be gone after ref-store-aware\n> api is in place and used by backends.\n> \n> (*) Submodule code needs for_each_ref. Try take REF_STORE_ODB flag\n> out. At least t3404 would fail. The \"have access to odb\" in submodule is\n> a bit hacky since we don't know from he whether add_submodule_odb() has\n> been called.\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  refs.c               | 15 +++++++---\n>  refs/files-backend.c | 81 +++++++++++++++++++++++++++++++++-------------------\n>  refs/refs-internal.h |  9 +++++-\n>  3 files changed, 71 insertions(+), 34 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index d72b48a430..241b4227b2 100644\n> --- a/refs.c\n> +++ b/refs.c\n> [...]\n> @@ -1481,7 +1486,9 @@ struct ref_store *get_ref_store(const char *submodule)\n>  \t\treturn NULL;\n>  \t}\n>  \n> -\trefs = ref_store_init(submodule_sb.buf);\n> +\t/* pretend that add_submodule_odb() has been called */\n\nThe word \"pretend\" implies that the thing that follows is not true,\nwhereas we hope that it *is* true. It would be better to say \"assume\".\n\n> +\trefs = ref_store_init(submodule_sb.buf,\n> +\t\t\t      REF_STORE_READ | REF_STORE_ODB);\n>  \tregister_submodule_ref_store(refs, submodule);\n>  \n>  \tstrbuf_release(&submodule_sb);\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 490f05a6f4..d97a924860 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> [...]\n> @@ -994,13 +997,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n>  }\n>  \n>  /*\n> - * Die if refs is for a submodule (i.e., not for the main repository).\n> - * caller is used in any necessary error messages.\n> + * Die if refs is not the main ref store. caller is used in any\n> + * necessary error messages.\n>   */\n>  static void files_assert_main_repository(struct files_ref_store *refs,\n>  \t\t\t\t\t const char *caller)\n>  {\n> -\t/* This function is to be fixed up in the next patch */\n> +\tif (refs->store_flags & REF_STORE_MAIN)\n> +\t\treturn;\n> +\n> +\tdie(\"BUG: unallowed operation (%s), only works \"\n> +\t    \"on main ref store\\n\", caller);\n\n\"Unallowed\" isn't really a word; one would say \"disallowed\". But it\nmight sound better to say\n\n    BUG: operation %s only allowed for main ref store\n\n>  }\n>  \n>  /*\n> @@ -1009,9 +1016,9 @@ static void files_assert_main_repository(struct files_ref_store *refs,\n>   * files_ref_store is for a submodule (i.e., not for the main\n>   * repository). caller is used in any necessary error messages.\n>   */\n> -static struct files_ref_store *files_downcast(\n> -\t\tstruct ref_store *ref_store, int submodule_allowed,\n> -\t\tconst char *caller)\n> +static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n> +\t\t\t\t\t      unsigned int required_flags,\n> +\t\t\t\t\t      const char *caller)\n\nThe docstring for this function needs to be updated; it still talks\nabout the old `submodule_allowed` parameter.\n\n>  {\n>  \tstruct files_ref_store *refs;\n>  \n> @@ -1021,8 +1028,9 @@ static struct files_ref_store *files_downcast(\n>  \n>  \trefs = (struct files_ref_store *)ref_store;\n>  \n> -\tif (!submodule_allowed)\n> -\t\tfiles_assert_main_repository(refs, caller);\n> +\tif ((refs->store_flags & required_flags) != required_flags)\n> +\t\tdie(\"BUG: unallowed operation (%s), requires %x, has %x\\n\",\n> +\t\t    caller, required_flags, refs->store_flags);\n\nSame comment about \"unallowed\". Maybe\n\n    BUG: operation %s requires abilities 0x%x but only have 0x%x\n\n>  \n>  \treturn refs;\n>  }\n> [...]\n\nMichael\n\n"},{"id":"315984","messageId":"07e96aa2-6733-474a-118b-05c533f49592@alum.mit.edu","threadId":"45126","inReplyTo":"20170326024241.31984-23-pclouds@gmail.com","subject":"Re: [PATCH v7 22/28] refs: new transaction related ref-store api","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-04-01T04:54:33Z","receivedAt":"2017-04-01T04:54:45Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/26/2017 04:42 AM, Nguyễn Thái Ngọc Duy wrote:\n> The transaction struct now takes a ref store at creation and will\n> operate on that ref store alone.\n\nHaving worked downstream of this patch series for a while, I started to\nwonder why a `ref_store` has to be baked into a `ref_transaction` at\ncreation. I haven't noticed any technical reason why it must be so.\n\nIf we expected to need to virtualize `ref_transaction_begin()` sometime,\nthen of course your design would be preferable.\n\nIt surprised be, because until now `ref_transaction` has been a plain\nold data structure unconnected with a particular `ref_store`. I would\nhave expected `ref_transaction_commit()` to gain a more general sibling,\n`refs_ref_transaction_commit()` [*], that takes an additional `ref_store\n*` object argument, and for `ref_transaction_commit()` to call that\nfunction. It would feel slightly more natural to me, given that\n`transaction_commit` is a virtual method of the `ref_store` class, for\n`refs_ref_transaction_commit()` to take an explicit `refs` pointer\nrather than having that pointer buried in the `ref_transaction`.\n\nI think this is mostly an aesthetic issue (about which opinions can\nlegitimately differ) rather than a concrete problem. I haven't yet had\nany difficulties working with your interface. But I wanted to mention my\nobservation anyway. As far as I'm concerned, you don't need to change it.\n\n> [...]\n\nMichael\n\n[*] The name could obviously be improved, but you get the idea.\n\n"},{"id":"315985","messageId":"f86b6e02-43cc-c3eb-5383-ed57f2a2d1ef@alum.mit.edu","threadId":"45126","inReplyTo":"20170326024241.31984-24-pclouds@gmail.com","subject":"Re: [PATCH v7 23/28] files-backend: avoid ref api targetting main ref store","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-04-01T05:05:30Z","receivedAt":"2017-04-01T05:05:44Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Nits: s/targetting/targeting/ in the subject line.\n\nOn 03/26/2017 04:42 AM, Nguyễn Thái Ngọc Duy wrote:\n> A small step towards making files-backend works as a non-main ref store\n> using the newly added store-aware API.\n\ns/works/work/\n\n> For the record, `join` and `nm` on refs.o and files-backend.o tell me\n> that files-backend no longer uses functions that defaults to\n> get_main_ref_store().\n\ns/defaults/default/\n\n> I'm not yet comfortable at the idea of removing\n> files_assert_main_repository() (or converting REF_STORE_MAIN to\n> REF_STORE_WRITE). More staring and testing is required before that can\n> happen. Well, except peel_ref(). I'm pretty sure that function is safe.\n> [...]\n\nMichael\n\n"},{"id":"315986","messageId":"24c2d384-b719-6a36-5b65-8edf86191fdd@alum.mit.edu","threadId":"45126","inReplyTo":"20170326024241.31984-1-pclouds@gmail.com","subject":"Re: [PATCH v7 00/28] Remove submodule from files-backend.c","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2017-04-01T05:09:54Z","receivedAt":"2017-04-01T05:10:07Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 03/26/2017 04:42 AM, Nguyễn Thái Ngọc Duy wrote:\n> v7 is mostly about style changes except the one bug in\n> test-ref-store.c, missing setup_git_directory().\n> \n> There's one new patch, 03/28, which maps to the \"if (!refs)\" deletion\n> in the interdiff.\n> \n> The one comment from v6 I haven't addressed in v7 is whether to delete\n> REF_STORE_READ. But if it is deleted, I think I'm doing it in\n> nd/worktree-kill-parse-ref, which is partly about cleanup refs code\n> anyway, to avoid another re-roll in this series.\n\nI reviewed v7 pretty carefully, and send a couple of minor comments. But\nwith or without changes, it looks good to me and the whole series is\n\nReviewed-by: Michael Haggerty <mhagger@alum.mit.edu>\n\nThanks!\n\nMichael\n\n"},{"id":"316347","messageId":"CACsJy8AHGmbPrV50u0XbgGFyYxvE-guk+vt6aUiNM3RtdDj=_A@mail.gmail.com","threadId":"45126","inReplyTo":"07e96aa2-6733-474a-118b-05c533f49592@alum.mit.edu","subject":"Re: [PATCH v7 22/28] refs: new transaction related ref-store api","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-04-07T12:29:31Z","receivedAt":"2017-04-07T12:30:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Apr 1, 2017 at 11:54 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 03/26/2017 04:42 AM, Nguyễn Thái Ngọc Duy wrote:\n>> The transaction struct now takes a ref store at creation and will\n>> operate on that ref store alone.\n>\n> Having worked downstream of this patch series for a while, I started to\n> wonder why a `ref_store` has to be baked into a `ref_transaction` at\n> creation. I haven't noticed any technical reason why it must be so.\n>\n> If we expected to need to virtualize `ref_transaction_begin()` sometime,\n> then of course your design would be preferable.\n>\n> It surprised be, because until now `ref_transaction` has been a plain\n> old data structure unconnected with a particular `ref_store`. I would\n> have expected `ref_transaction_commit()` to gain a more general sibling,\n> `refs_ref_transaction_commit()` [*], that takes an additional `ref_store\n> *` object argument, and for `ref_transaction_commit()` to call that\n> function.\n\nI almost went with that, but the question that stopped me was, \"what\nif people do X on ref store A, then Y on ref store B, using the same\ntransaction?\". I suppose we could even support that (which basically\nturns one transaction into two), or check for same ref store and\nreject. It's too complicated though. So I went a safe way, making sure\npeople won't be able to change ref store half way.\n\n> It would feel slightly more natural to me, given that\n> `transaction_commit` is a virtual method of the `ref_store` class, for\n> `refs_ref_transaction_commit()` to take an explicit `refs` pointer\n> rather than having that pointer buried in the `ref_transaction`.\n>\n> I think this is mostly an aesthetic issue (about which opinions can\n> legitimately differ) rather than a concrete problem. I haven't yet had\n> any difficulties working with your interface. But I wanted to mention my\n> observation anyway. As far as I'm concerned, you don't need to change it.\n>\n>> [...]\n>\n> Michael\n>\n> [*] The name could obviously be improved, but you get the idea.\n>\n\n\n\n-- \nDuy\n"},{"id":"316348","messageId":"CACsJy8ByRdcXUS57pqSH4Gmr6FLSfK1yNnq7u5wVzh=7hj0vBA@mail.gmail.com","threadId":"45126","inReplyTo":"4e4fd98d-055d-6e2b-845d-51d3f1c2856f@alum.mit.edu","subject":"Re: [PATCH v7 19/28] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-04-07T12:41:17Z","receivedAt":"2017-04-07T12:41:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I'll mark this mail and do a follow-up patch once this topic graduates\nto master. It's less review burden and mail traffic.\n\nOn Sat, Apr 1, 2017 at 11:02 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 03/26/2017 04:42 AM, Nguyễn Thái Ngọc Duy wrote:\n>> files-backend.c is unlearning submodules. Instead of having a specific\n>> check for submodules to see what operation is allowed, files backend\n>> now takes a set of flags at init. Each operation will check if the\n>> required flags is present before performing.\n>>\n>> For now we have four flags: read, write and odb access. Main ref store\n>> has all flags, obviously, while submodule stores are read-only and have\n>> access to odb (*).\n>>\n>> The \"main\" flag stays because many functions in the backend calls\n>> frontend ones without a ref store, so these functions always target the\n>> main ref store. Ideally the flag should be gone after ref-store-aware\n>> api is in place and used by backends.\n>>\n>> (*) Submodule code needs for_each_ref. Try take REF_STORE_ODB flag\n>> out. At least t3404 would fail. The \"have access to odb\" in submodule is\n>> a bit hacky since we don't know from he whether add_submodule_odb() has\n>> been called.\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  refs.c               | 15 +++++++---\n>>  refs/files-backend.c | 81 +++++++++++++++++++++++++++++++++-------------------\n>>  refs/refs-internal.h |  9 +++++-\n>>  3 files changed, 71 insertions(+), 34 deletions(-)\n>>\n>> diff --git a/refs.c b/refs.c\n>> index d72b48a430..241b4227b2 100644\n>> --- a/refs.c\n>> +++ b/refs.c\n>> [...]\n>> @@ -1481,7 +1486,9 @@ struct ref_store *get_ref_store(const char *submodule)\n>>               return NULL;\n>>       }\n>>\n>> -     refs = ref_store_init(submodule_sb.buf);\n>> +     /* pretend that add_submodule_odb() has been called */\n>\n> The word \"pretend\" implies that the thing that follows is not true,\n> whereas we hope that it *is* true. It would be better to say \"assume\".\n>\n>> +     refs = ref_store_init(submodule_sb.buf,\n>> +                           REF_STORE_READ | REF_STORE_ODB);\n>>       register_submodule_ref_store(refs, submodule);\n>>\n>>       strbuf_release(&submodule_sb);\n>> diff --git a/refs/files-backend.c b/refs/files-backend.c\n>> index 490f05a6f4..d97a924860 100644\n>> --- a/refs/files-backend.c\n>> +++ b/refs/files-backend.c\n>> [...]\n>> @@ -994,13 +997,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir)\n>>  }\n>>\n>>  /*\n>> - * Die if refs is for a submodule (i.e., not for the main repository).\n>> - * caller is used in any necessary error messages.\n>> + * Die if refs is not the main ref store. caller is used in any\n>> + * necessary error messages.\n>>   */\n>>  static void files_assert_main_repository(struct files_ref_store *refs,\n>>                                        const char *caller)\n>>  {\n>> -     /* This function is to be fixed up in the next patch */\n>> +     if (refs->store_flags & REF_STORE_MAIN)\n>> +             return;\n>> +\n>> +     die(\"BUG: unallowed operation (%s), only works \"\n>> +         \"on main ref store\\n\", caller);\n>\n> \"Unallowed\" isn't really a word; one would say \"disallowed\". But it\n> might sound better to say\n>\n>     BUG: operation %s only allowed for main ref store\n>\n>>  }\n>>\n>>  /*\n>> @@ -1009,9 +1016,9 @@ static void files_assert_main_repository(struct files_ref_store *refs,\n>>   * files_ref_store is for a submodule (i.e., not for the main\n>>   * repository). caller is used in any necessary error messages.\n>>   */\n>> -static struct files_ref_store *files_downcast(\n>> -             struct ref_store *ref_store, int submodule_allowed,\n>> -             const char *caller)\n>> +static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n>> +                                           unsigned int required_flags,\n>> +                                           const char *caller)\n>\n> The docstring for this function needs to be updated; it still talks\n> about the old `submodule_allowed` parameter.\n>\n>>  {\n>>       struct files_ref_store *refs;\n>>\n>> @@ -1021,8 +1028,9 @@ static struct files_ref_store *files_downcast(\n>>\n>>       refs = (struct files_ref_store *)ref_store;\n>>\n>> -     if (!submodule_allowed)\n>> -             files_assert_main_repository(refs, caller);\n>> +     if ((refs->store_flags & required_flags) != required_flags)\n>> +             die(\"BUG: unallowed operation (%s), requires %x, has %x\\n\",\n>> +                 caller, required_flags, refs->store_flags);\n>\n> Same comment about \"unallowed\". Maybe\n>\n>     BUG: operation %s requires abilities 0x%x but only have 0x%x\n>\n>>\n>>       return refs;\n>>  }\n>> [...]\n>\n> Michael\n>\n\n\n\n-- \nDuy\n"},{"id":"316553","messageId":"xmqqmvbnqrbk.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"24c2d384-b719-6a36-5b65-8edf86191fdd@alum.mit.edu","subject":"Re: [PATCH v7 00/28] Remove submodule from files-backend.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-04-11T08:30:39Z","receivedAt":"2017-04-11T08:30:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> On 03/26/2017 04:42 AM, Nguyễn Thái Ngọc Duy wrote:\n>> v7 is mostly about style changes except the one bug in\n>> test-ref-store.c, missing setup_git_directory().\n>> \n>> There's one new patch, 03/28, which maps to the \"if (!refs)\" deletion\n>> in the interdiff.\n>> \n>> The one comment from v6 I haven't addressed in v7 is whether to delete\n>> REF_STORE_READ. But if it is deleted, I think I'm doing it in\n>> nd/worktree-kill-parse-ref, which is partly about cleanup refs code\n>> anyway, to avoid another re-roll in this series.\n>\n> I reviewed v7 pretty carefully, and send a couple of minor comments. But\n> with or without changes, it looks good to me and the whole series is\n>\n> Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>\n>\n> Thanks!\n\nThanks.  The \"What's cooking\" report at the end of the last month\nmarked this topic to wait for a few days for comments before moving\nto 'next'.  Now it has been more than a few days, so I'll move it to\nthe normal \"will merge to 'next'\" category.\n\n"},{"id":"316810","messageId":"xmqq8tn3nu8x.fsf@gitster.mtv.corp.google.com","threadId":"45126","inReplyTo":"CACsJy8ByRdcXUS57pqSH4Gmr6FLSfK1yNnq7u5wVzh=7hj0vBA@mail.gmail.com","subject":"Re: [PATCH v7 19/28] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-04-14T10:44:46Z","receivedAt":"2017-04-14T10:44:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> I'll mark this mail and do a follow-up patch once this topic graduates\n> to master. It's less review burden and mail traffic.\n\nI actually do not mind a single replacement patch.\n"},{"id":"316814","messageId":"20170414130251.GA29908@ash","threadId":"45126","inReplyTo":"xmqq8tn3nu8x.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 19/28] files-backend: replace submodule_allowed check in files_downcast()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2017-04-14T13:02:51Z","receivedAt":"2017-04-14T13:03:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Apr 14, 2017 at 03:44:46AM -0700, Junio C Hamano wrote:\n> Duy Nguyen <pclouds@gmail.com> writes:\n> \n> > I'll mark this mail and do a follow-up patch once this topic graduates\n> > to master. It's less review burden and mail traffic.\n> \n> I actually do not mind a single replacement patch.\n\nThanks. Here's the fixup patch\n\n-- 8< --\nFrom c572f11f9c653f6f18d51a56b31cc5d966f9b242 Mon Sep 17 00:00:00 2001\nFrom: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?=\n <pclouds@gmail.com>\nDate: Fri, 14 Apr 2017 19:54:31 +0700\nSubject: [PATCH] fixup! files-backend: replace submodule_allowed check in\n files_downcast()\n\n---\n refs.c               |  2 +-\n refs/files-backend.c | 10 ++++------\n 2 files changed, 5 insertions(+), 7 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 241b4227b2..c7bffac06d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1486,7 +1486,7 @@ struct ref_store *get_ref_store(const char *submodule)\n \t\treturn NULL;\n \t}\n \n-\t/* pretend that add_submodule_odb() has been called */\n+\t/* assume that add_submodule_odb() has been called */\n \trefs = ref_store_init(submodule_sb.buf,\n \t\t\t      REF_STORE_READ | REF_STORE_ODB);\n \tregister_submodule_ref_store(refs, submodule);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex d97a924860..3faf5a0635 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1006,15 +1006,13 @@ static void files_assert_main_repository(struct files_ref_store *refs,\n \tif (refs->store_flags & REF_STORE_MAIN)\n \t\treturn;\n \n-\tdie(\"BUG: unallowed operation (%s), only works \"\n-\t    \"on main ref store\\n\", caller);\n+\tdie(\"BUG: operation %s is only allowed for main ref store\", caller);\n }\n \n /*\n  * Downcast ref_store to files_ref_store. Die if ref_store is not a\n- * files_ref_store. If submodule_allowed is not true, then also die if\n- * files_ref_store is for a submodule (i.e., not for the main\n- * repository). caller is used in any necessary error messages.\n+ * files_ref_store. Die if the operation is not allowed on this particular\n+ * store. \"caller\" is used in any necessary error messages.\n  */\n static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n \t\t\t\t\t      unsigned int required_flags,\n@@ -1029,7 +1027,7 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n \trefs = (struct files_ref_store *)ref_store;\n \n \tif ((refs->store_flags & required_flags) != required_flags)\n-\t\tdie(\"BUG: unallowed operation (%s), requires %x, has %x\\n\",\n+\t\tdie(\"BUG: operation %s requires abilities 0x%x but have 0x%x\",\n \t\t    caller, required_flags, refs->store_flags);\n \n \treturn refs;\n-- \n2.11.0.157.gd943d85\n\n-- 8< --\n"}]}