{"thread":{"id":"40568","subject":"[PATCH v4 00/26] refs backend pre-vtable patches","startedAt":"2015-10-15T19:46:24Z","lastAt":"2015-10-21T19:47:40Z","messageCount":35,"participants":["David Turner","Michael Haggerty","Junio C Hamano"],"isPatch":true,"patchVersion":4,"patchTotal":26},"messages":[{"id":"271747","messageId":"1444938410-2345-1-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":null,"subject":"[PATCH v4 00/26] refs backend pre-vtable patches","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:24Z","receivedAt":"2015-10-15T19:46:24Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Michael and Junio want to split the refs-backend patches into multiple\nsections: pre-vtable, vtable, and maybe lmdb.\n\nThis is the pre-vtable section.  It includes multiple fixes suggested\nby Michael:\n\n1. When we move chunks around between files, make sure we don't cut\nnew versions and paste old versions!\n\n2. Some rearrangement of patches\n\n3. Made fewer things public, since they are really only needed for\nfiles-backend optimizations.\n\n4. Comment fixes (and moving doc-comments to refs.h when functions\nbecome public)\n\nAlso, on my own, I moved Jeff's repository format patch into this\nchunk, since it should be relatively non-controversial.\n\nDavid Turner (9):\n  refs: make repack_without_refs and is_branch public\n  refs: move transaction functions into common code\n  refs.c: move refname_is_safe to the common code\n  refs.c: move copy_msg to the common code\n  refs.c: move peel_object to the common code\n  refs.c: move should_autocreate_reflog to common code\n  initdb: move safe_create_dir into common code\n  refs: make files_log_ref_write functions public\n  refs: break out ref conflict checks\n\nJeff King (1):\n  introduce \"extensions\" form of core.repositoryformatversion\n\nRonnie Sahlberg (16):\n  refs.c: create a public version of verify_refname_available\n  refs-be-files.c: rename refs to refs-be-files\n  refs.c: add a new refs.c file to hold all common refs code\n  refs.c: move update_ref to refs.c\n  refs.c: move delete_pseudoref and delete_ref to the common code\n  refs.c: move read_ref_at to the common refs file\n  refs.c: move the hidden refs functions to the common code\n  refs.c: move dwim and friend functions to the common refs code\n  refs.c: move warn_if_dangling_symref* to the common code\n  refs.c: move read_ref, read_ref_full and ref_exists to the common code\n  refs.c: move resolve_refdup to common\n  refs.c: move check_refname_format to the common code\n  refs.c: move is_branch to the common code\n  refs.c: move prettify_refname to the common code\n  refs.c: move ref iterators to the common code\n  refs.c: move head_ref_namespaced to the common code\n\n Documentation/technical/repository-version.txt |   81 +\n Makefile                                       |    1 +\n builtin/init-db.c                              |   12 -\n cache.h                                        |   11 +\n path.c                                         |   12 +\n refs-be-files.c                                | 3513 ++++++++++++++++\n refs.c                                         | 5172 ++++--------------------\n refs.h                                         |  187 +-\n setup.c                                        |   37 +-\n t/t1302-repo-version.sh                        |   38 +\n 10 files changed, 4645 insertions(+), 4419 deletions(-)\n create mode 100644 Documentation/technical/repository-version.txt\n create mode 100644 refs-be-files.c\n\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271771","messageId":"1444938410-2345-2-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 01/26] refs.c: create a public version of verify_refname_available","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:25Z","receivedAt":"2015-10-15T19:46:25Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nCreate a public version of verify_refname_available that backends can\nprovide.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs.c | 65 ++++++++++++++++++++++++++++++-----------------------------------\n refs.h | 20 ++++++++++++++++++++\n 2 files changed, 50 insertions(+), 35 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 132eff5..fe71ea0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -279,7 +279,7 @@ struct ref_dir {\n  * presence of an empty subdirectory does not block the creation of a\n  * similarly-named reference.  (The fact that reference names with the\n  * same leading components can conflict *with each other* is a\n- * separate issue that is regulated by verify_refname_available().)\n+ * separate issue that is regulated by verify_refname_available_dir().)\n  *\n  * Please note that the name field contains the fully-qualified\n  * reference (or subdirectory) name.  Space could be saved by only\n@@ -897,25 +897,13 @@ static int nonmatching_ref_fn(struct ref_entry *entry, void *vdata)\n /*\n  * Return 0 if a reference named refname could be created without\n  * conflicting with the name of an existing reference in dir.\n- * Otherwise, return a negative value and write an explanation to err.\n- * If extras is non-NULL, it is a list of additional refnames with\n- * which refname is not allowed to conflict. If skip is non-NULL,\n- * ignore potential conflicts with refs in skip (e.g., because they\n- * are scheduled for deletion in the same operation). Behavior is\n- * undefined if the same 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., \"refs/foo/bar\" conflicts\n- * with both \"refs/foo\" and with \"refs/foo/bar/baz\" but not with\n- * \"refs/foo/bar\" or \"refs/foo/barbados\".\n- *\n- * extras and skip must be sorted.\n+ * See verify_refname_available for details.\n  */\n-static int verify_refname_available(const char *refname,\n-\t\t\t\t    const struct string_list *extras,\n-\t\t\t\t    const struct string_list *skip,\n-\t\t\t\t    struct ref_dir *dir,\n-\t\t\t\t    struct strbuf *err)\n+static int verify_refname_available_dir(const char *refname,\n+\t\t\t\t\tconst struct string_list *extras,\n+\t\t\t\t\tconst struct string_list *skip,\n+\t\t\t\t\tstruct ref_dir *dir,\n+\t\t\t\t\tstruct strbuf *err)\n {\n \tconst char *slash;\n \tint pos;\n@@ -2464,9 +2452,12 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n \t\t */\n \t\tstrbuf_git_path(&orig_ref_file, \"%s\", orig_refname);\n \t\tif (remove_empty_directories(&orig_ref_file)) {\n+\t\t\tstruct ref_dir *loose_refs;\n+\t\t\tloose_refs = get_loose_refs(&ref_cache);\n \t\t\tlast_errno = errno;\n-\t\t\tif (!verify_refname_available(orig_refname, extras, skip,\n-\t\t\t\t\t\t      get_loose_refs(&ref_cache), err))\n+\t\t\tif (!verify_refname_available_dir(orig_refname, extras,\n+\t\t\t\t\t\t\t  skip, loose_refs,\n+\t\t\t\t\t\t\t  err))\n \t\t\t\tstrbuf_addf(err, \"there are still refs under '%s'\",\n \t\t\t\t\t    orig_refname);\n \t\t\tgoto error_return;\n@@ -2479,8 +2470,9 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n \tif (!refname) {\n \t\tlast_errno = errno;\n \t\tif (last_errno != ENOTDIR ||\n-\t\t    !verify_refname_available(orig_refname, extras, skip,\n-\t\t\t\t\t      get_loose_refs(&ref_cache), err))\n+\t\t    !verify_refname_available_dir(orig_refname, extras, skip,\n+\t\t\t\t\t\t  get_loose_refs(&ref_cache),\n+\t\t\t\t\t\t  err))\n \t\t\tstrbuf_addf(err, \"unable to resolve reference %s: %s\",\n \t\t\t\t    orig_refname, strerror(last_errno));\n \n@@ -2493,8 +2485,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n \t * our refname.\n \t */\n \tif (is_null_oid(&lock->old_oid) &&\n-\t    verify_refname_available(refname, extras, skip,\n-\t\t\t\t     get_packed_refs(&ref_cache), err)) {\n+\t    verify_refname_available_dir(refname, extras, skip,\n+\t\t\t\t\t get_packed_refs(&ref_cache), err)) {\n \t\tlast_errno = ENOTDIR;\n \t\tgoto error_return;\n \t}\n@@ -3127,10 +3119,7 @@ static int rename_ref_available(const char *oldname, const char *newname)\n \tint ret;\n \n \tstring_list_insert(&skip, oldname);\n-\tret = !verify_refname_available(newname, NULL, &skip,\n-\t\t\t\t\tget_packed_refs(&ref_cache), &err)\n-\t\t&& !verify_refname_available(newname, NULL, &skip,\n-\t\t\t\t\t     get_loose_refs(&ref_cache), &err);\n+\tret = !verify_refname_available(newname, NULL, &skip, &err);\n \tif (!ret)\n \t\terror(\"%s\", err.buf);\n \n@@ -3299,6 +3288,17 @@ static int should_autocreate_reflog(const char *refname)\n \t\t!strcmp(refname, \"HEAD\");\n }\n \n+int verify_refname_available(const char *newname, struct string_list *extra,\n+\t\t\t     struct string_list *skip, struct strbuf *err)\n+{\n+\tstruct ref_dir *packed_refs = get_packed_refs(&ref_cache);\n+\tstruct ref_dir *loose_refs = get_loose_refs(&ref_cache);\n+\treturn verify_refname_available_dir(newname, extra, skip,\n+\t\t\t\t\t    packed_refs, err) ||\n+\t\tverify_refname_available_dir(newname, extra, skip,\n+\t\t\t\t\t     loose_refs, err);\n+}\n+\n /*\n  * Create a reflog for a ref.  If force_create = 0, the reflog will\n  * only be created for certain refs (those for which\n@@ -4334,8 +4334,6 @@ static int ref_present(const char *refname,\n int initial_ref_transaction_commit(struct ref_transaction *transaction,\n \t\t\t\t   struct strbuf *err)\n {\n-\tstruct ref_dir *loose_refs = get_loose_refs(&ref_cache);\n-\tstruct ref_dir *packed_refs = get_packed_refs(&ref_cache);\n \tint ret = 0, i;\n \tint n = transaction->nr;\n \tstruct ref_update **updates = transaction->updates;\n@@ -4378,10 +4376,7 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,\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     loose_refs, err) ||\n-\t\t    verify_refname_available(update->refname,\n-\t\t\t\t\t     &affected_refnames, NULL,\n-\t\t\t\t\t     packed_refs, err)) {\n+\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.h b/refs.h\nindex 6d30c98..7367a7f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -218,6 +218,26 @@ extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct st\n int pack_refs(unsigned int flags);\n \n /*\n+ * Return 0 if a reference named refname could be created without\n+ * conflicting with the name of an existing reference.\n+ * Otherwise, return a negative value and write an explanation to err.\n+ * If extras is non-NULL, it is a list of additional refnames with\n+ * which refname is not allowed to conflict. If skip is non-NULL,\n+ * ignore potential conflicts with refs in skip (e.g., because they\n+ * are scheduled for deletion in the same operation). Behavior is\n+ * undefined if the same 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+ * skip must be sorted.\n+ */\n+int verify_refname_available(const char *newname, struct string_list *extra,\n+\t\t\t     struct string_list *skip, struct strbuf *err);\n+\n+/*\n  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.\n  * REF_NODEREF: act on the ref directly, instead of dereferencing\n  *              symbolic references.\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271750","messageId":"1444938410-2345-3-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 02/26] refs: make repack_without_refs and is_branch public","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:26Z","receivedAt":"2015-10-15T19:46:26Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"is_branch was already non-static, but this patch declares it in the\nheader.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs.c | 5 +++--\n refs.h | 2 ++\n 2 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex fe71ea0..84abc82 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2816,8 +2816,9 @@ int pack_refs(unsigned int flags)\n \n /*\n  * Rewrite the packed-refs file, omitting any refs listed in\n- * 'refnames'. On error, leave packed-refs unchanged, write an error\n- * message to 'err', and return a nonzero value.\n+ * 'refnames'. On error, packed-refs will be unchanged, the return\n+ * value is nonzero, and a message about the error is written to the\n+ * 'err' strbuf.\n  *\n  * The refs in 'refnames' needn't be sorted. `err` must not be NULL.\n  */\ndiff --git a/refs.h b/refs.h\nindex 7367a7f..8408bef 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -237,6 +237,8 @@ int pack_refs(unsigned int flags);\n int verify_refname_available(const char *newname, struct string_list *extra,\n \t\t\t     struct string_list *skip, struct strbuf *err);\n \n+extern int is_branch(const char *refname);\n+\n /*\n  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.\n  * REF_NODEREF: act on the ref directly, instead of dereferencing\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271748","messageId":"1444938410-2345-4-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 03/26] refs-be-files.c: rename refs to refs-be-files","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:27Z","receivedAt":"2015-10-15T19:46:27Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nRename refs.c to refs-be-files.c to indicate that this file now\nholds the implementation for the files based refs backend.\nA smaller portion of the code in this file is backend agnostic and will\nbe moved to a a new refs.c file that will hold all the common refs code\nthat is shared across all backends.\n\nA second reason for first moving all the code to the new file and then\nmove the backend agnostic code back to refs.c instead of the other way\naround is because the code that will eventually remain in this new\nrefs-be-files.c file is so entangled that it would then be very\ndifficult to break the split up into small independent patches/chunks.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile                  | 2 +-\n refs.c => refs-be-files.c | 0\n 2 files changed, 1 insertion(+), 1 deletion(-)\n rename refs.c => refs-be-files.c (100%)\n\ndiff --git a/Makefile b/Makefile\nindex 0d9f5dd..173b9d4 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -762,7 +762,7 @@ LIB_OBJS += quote.o\n LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n LIB_OBJS += reflog-walk.o\n-LIB_OBJS += refs.o\n+LIB_OBJS += refs-be-files.o\n LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace_object.o\ndiff --git a/refs.c b/refs-be-files.c\nsimilarity index 100%\nrename from refs.c\nrename to refs-be-files.c\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271768","messageId":"1444938410-2345-5-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 04/26] refs.c: add a new refs.c file to hold all common refs code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:28Z","receivedAt":"2015-10-15T19:46:28Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nCreate a new refs.c file that will be used to hold all the refs\ncode that is backend agnostic and will be shared across all backends.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile | 1 +\n refs.c   | 3 +++\n 2 files changed, 4 insertions(+)\n create mode 100644 refs.c\n\ndiff --git a/Makefile b/Makefile\nindex 173b9d4..b37eae3 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -763,6 +763,7 @@ LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs-be-files.o\n+LIB_OBJS += refs.o\n LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace_object.o\ndiff --git a/refs.c b/refs.c\nnew file mode 100644\nindex 0000000..77492ff\n--- /dev/null\n+++ b/refs.c\n@@ -0,0 +1,3 @@\n+/*\n+ * Common refs code for all backends.\n+ */\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271773","messageId":"1444938410-2345-6-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 05/26] refs.c: move update_ref to refs.c","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:29Z","receivedAt":"2015-10-15T19:46:29Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nMove update_ref() to the refs.c file since this function does not\ncontain any backend specific code.  Move the ref classifier functions\nas well, since update_ref depends on them.\n\nBased on Ronnie Sahlberg's patch\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 117 +-------------------------------------------------------\n refs.c          | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 117 insertions(+), 116 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 84abc82..72a9bc4 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -2675,8 +2675,6 @@ struct pack_refs_cb_data {\n \tstruct ref_to_prune *ref_to_prune;\n };\n \n-static int is_per_worktree_ref(const char *refname);\n-\n /*\n  * An each_ref_entry_fn that is run over loose references only.  If\n  * the loose reference can be packed, add an entry in the packed ref\n@@ -2691,7 +2689,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)\n \tint is_tag_ref = starts_with(entry->name, \"refs/tags/\");\n \n \t/* Do not pack per-worktree refs: */\n-\tif (is_per_worktree_ref(entry->name))\n+\tif (ref_type(entry->name) == REF_TYPE_PER_WORKTREE)\n \t\treturn 0;\n \n \t/* ALWAYS pack tags */\n@@ -2887,77 +2885,6 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)\n \treturn 0;\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;\n-\n-\tfor (c = refname; *c; c++) {\n-\t\tif (!isupper(*c) && *c != '-' && *c != '_')\n-\t\t\treturn 0;\n-\t}\n-\n-\treturn 1;\n-}\n-\n-enum ref_type ref_type(const char *refname)\n-{\n-\tif (is_per_worktree_ref(refname))\n-\t\treturn REF_TYPE_PER_WORKTREE;\n-\tif (is_pseudoref_syntax(refname))\n-\t\treturn REF_TYPE_PSEUDOREF;\n-       return REF_TYPE_NORMAL;\n-}\n-\n-static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,\n-\t\t\t   const unsigned char *old_sha1, struct strbuf *err)\n-{\n-\tconst char *filename;\n-\tint fd;\n-\tstatic struct lock_file lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint ret = -1;\n-\n-\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(sha1));\n-\n-\tfilename = git_path(\"%s\", pseudoref);\n-\tfd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);\n-\tif (fd < 0) {\n-\t\tstrbuf_addf(err, \"Could not open '%s' for writing: %s\",\n-\t\t\t    filename, strerror(errno));\n-\t\treturn -1;\n-\t}\n-\n-\tif (old_sha1) {\n-\t\tunsigned char actual_old_sha1[20];\n-\n-\t\tif (read_ref(pseudoref, actual_old_sha1))\n-\t\t\tdie(\"could not read ref '%s'\", pseudoref);\n-\t\tif (hashcmp(actual_old_sha1, old_sha1)) {\n-\t\t\tstrbuf_addf(err, \"Unexpected sha1 when writing %s\", pseudoref);\n-\t\t\trollback_lock_file(&lock);\n-\t\t\tgoto done;\n-\t\t}\n-\t}\n-\n-\tif (write_in_full(fd, buf.buf, buf.len) != buf.len) {\n-\t\tstrbuf_addf(err, \"Could not write to '%s'\", filename);\n-\t\trollback_lock_file(&lock);\n-\t\tgoto done;\n-\t}\n-\n-\tcommit_lock_file(&lock);\n-\tret = 0;\n-done:\n-\tstrbuf_release(&buf);\n-\treturn ret;\n-}\n-\n static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)\n {\n \tstatic struct lock_file lock;\n@@ -4106,48 +4033,6 @@ int ref_transaction_verify(struct ref_transaction *transaction,\n \t\t\t\t      flags, NULL, err);\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-{\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\tret = write_pseudoref(refname, new_sha1, old_sha1, &err);\n-\t} else {\n-\t\tt = ref_transaction_begin(&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-\t\t    ref_transaction_commit(t, &err)) {\n-\t\t\tret = 1;\n-\t\t\tref_transaction_free(t);\n-\t\t}\n-\t}\n-\tif (ret) {\n-\t\tconst char *str = \"update_ref failed for ref '%s': %s\";\n-\n-\t\tswitch (onerr) {\n-\t\tcase UPDATE_REFS_MSG_ON_ERR:\n-\t\t\terror(str, refname, err.buf);\n-\t\t\tbreak;\n-\t\tcase UPDATE_REFS_DIE_ON_ERR:\n-\t\t\tdie(str, refname, err.buf);\n-\t\t\tbreak;\n-\t\tcase UPDATE_REFS_QUIET_ON_ERR:\n-\t\t\tbreak;\n-\t\t}\n-\t\tstrbuf_release(&err);\n-\t\treturn 1;\n-\t}\n-\tstrbuf_release(&err);\n-\tif (t)\n-\t\tref_transaction_free(t);\n-\treturn 0;\n-}\n-\n static int ref_update_reject_duplicates(struct string_list *refnames,\n \t\t\t\t\tstruct strbuf *err)\n {\ndiff --git a/refs.c b/refs.c\nindex 77492ff..610fab0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1,3 +1,119 @@\n /*\n  * Common refs code for all backends.\n  */\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"lockfile.h\"\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;\n+\n+\tfor (c = refname; *c; c++) {\n+\t\tif (!isupper(*c) && *c != '-' && *c != '_')\n+\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+enum ref_type ref_type(const char *refname)\n+{\n+\tif (is_per_worktree_ref(refname))\n+\t\treturn REF_TYPE_PER_WORKTREE;\n+\tif (is_pseudoref_syntax(refname))\n+\t\treturn REF_TYPE_PSEUDOREF;\n+       return REF_TYPE_NORMAL;\n+}\n+\n+static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,\n+\t\t\t   const unsigned char *old_sha1, struct strbuf *err)\n+{\n+\tconst char *filename;\n+\tint fd;\n+\tstatic struct lock_file lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint ret = -1;\n+\n+\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(sha1));\n+\n+\tfilename = git_path(\"%s\", pseudoref);\n+\tfd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);\n+\tif (fd < 0) {\n+\t\tstrbuf_addf(err, \"Could not open '%s' for writing: %s\",\n+\t\t\t    filename, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\tif (old_sha1) {\n+\t\tunsigned char actual_old_sha1[20];\n+\n+\t\tif (read_ref(pseudoref, actual_old_sha1))\n+\t\t\tdie(\"could not read ref '%s'\", pseudoref);\n+\t\tif (hashcmp(actual_old_sha1, old_sha1)) {\n+\t\t\tstrbuf_addf(err, \"Unexpected sha1 when writing %s\", pseudoref);\n+\t\t\trollback_lock_file(&lock);\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tif (write_in_full(fd, buf.buf, buf.len) != buf.len) {\n+\t\tstrbuf_addf(err, \"Could not write to '%s'\", filename);\n+\t\trollback_lock_file(&lock);\n+\t\tgoto done;\n+\t}\n+\n+\tcommit_lock_file(&lock);\n+\tret = 0;\n+done:\n+\tstrbuf_release(&buf);\n+\treturn ret;\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+{\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\tret = write_pseudoref(refname, new_sha1, old_sha1, &err);\n+\t} else {\n+\t\tt = ref_transaction_begin(&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+\t\t    ref_transaction_commit(t, &err)) {\n+\t\t\tret = 1;\n+\t\t\tref_transaction_free(t);\n+\t\t}\n+\t}\n+\tif (ret) {\n+\t\tconst char *str = \"update_ref failed for ref '%s': %s\";\n+\n+\t\tswitch (onerr) {\n+\t\tcase UPDATE_REFS_MSG_ON_ERR:\n+\t\t\terror(str, refname, err.buf);\n+\t\t\tbreak;\n+\t\tcase UPDATE_REFS_DIE_ON_ERR:\n+\t\t\tdie(str, refname, err.buf);\n+\t\t\tbreak;\n+\t\tcase UPDATE_REFS_QUIET_ON_ERR:\n+\t\t\tbreak;\n+\t\t}\n+\t\tstrbuf_release(&err);\n+\t\treturn 1;\n+\t}\n+\tstrbuf_release(&err);\n+\tif (t)\n+\t\tref_transaction_free(t);\n+\treturn 0;\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271770","messageId":"1444938410-2345-7-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 06/26] refs.c: move delete_pseudoref and delete_ref to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:30Z","receivedAt":"2015-10-15T19:46:30Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nMove delete_pseudoref() and delete_ref() to the refs.c file since\nthese functions do not contain any backend specific code.  We can't\nmove delete_refs yet because it depends on the files-backend-specific\nrepack_without_refs.\n\nBased on a patch by Ronnie Sahlberg.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 56 --------------------------------------------------------\n refs.c          | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 57 insertions(+), 56 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 72a9bc4..d969066 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -2885,62 +2885,6 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)\n \treturn 0;\n }\n \n-static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)\n-{\n-\tstatic struct lock_file lock;\n-\tconst char *filename;\n-\n-\tfilename = git_path(\"%s\", pseudoref);\n-\n-\tif (old_sha1 && !is_null_sha1(old_sha1)) {\n-\t\tint fd;\n-\t\tunsigned char actual_old_sha1[20];\n-\n-\t\tfd = hold_lock_file_for_update(&lock, filename,\n-\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n-\t\tif (fd < 0)\n-\t\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n-\t\tif (read_ref(pseudoref, actual_old_sha1))\n-\t\t\tdie(\"could not read ref '%s'\", pseudoref);\n-\t\tif (hashcmp(actual_old_sha1, old_sha1)) {\n-\t\t\twarning(\"Unexpected sha1 when deleting %s\", pseudoref);\n-\t\t\trollback_lock_file(&lock);\n-\t\t\treturn -1;\n-\t\t}\n-\n-\t\tunlink(filename);\n-\t\trollback_lock_file(&lock);\n-\t} else {\n-\t\tunlink(filename);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-int delete_ref(const char *refname, const unsigned char *old_sha1,\n-\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-\t\treturn delete_pseudoref(refname, old_sha1);\n-\n-\ttransaction = ref_transaction_begin(&err);\n-\tif (!transaction ||\n-\t    ref_transaction_delete(transaction, refname, old_sha1,\n-\t\t\t\t   flags, NULL, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n-\t\terror(\"%s\", err.buf);\n-\t\tref_transaction_free(transaction);\n-\t\tstrbuf_release(&err);\n-\t\treturn 1;\n-\t}\n-\tref_transaction_free(transaction);\n-\tstrbuf_release(&err);\n-\treturn 0;\n-}\n-\n int delete_refs(struct string_list *refnames)\n {\n \tstruct strbuf err = STRBUF_INIT;\ndiff --git a/refs.c b/refs.c\nindex 610fab0..f4f0677 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -117,3 +117,60 @@ int update_ref(const char *msg, const char *refname,\n \t\tref_transaction_free(t);\n \treturn 0;\n }\n+\n+\n+static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)\n+{\n+\tstatic struct lock_file lock;\n+\tconst char *filename;\n+\n+\tfilename = git_path(\"%s\", pseudoref);\n+\n+\tif (old_sha1 && !is_null_sha1(old_sha1)) {\n+\t\tint fd;\n+\t\tunsigned char actual_old_sha1[20];\n+\n+\t\tfd = hold_lock_file_for_update(&lock, filename,\n+\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n+\t\tif (fd < 0)\n+\t\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n+\t\tif (read_ref(pseudoref, actual_old_sha1))\n+\t\t\tdie(\"could not read ref '%s'\", pseudoref);\n+\t\tif (hashcmp(actual_old_sha1, old_sha1)) {\n+\t\t\twarning(\"Unexpected sha1 when deleting %s\", pseudoref);\n+\t\t\trollback_lock_file(&lock);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tunlink(filename);\n+\t\trollback_lock_file(&lock);\n+\t} else {\n+\t\tunlink(filename);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int delete_ref(const char *refname, const unsigned char *old_sha1,\n+\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+\t\treturn delete_pseudoref(refname, old_sha1);\n+\n+\ttransaction = ref_transaction_begin(&err);\n+\tif (!transaction ||\n+\t    ref_transaction_delete(transaction, refname, old_sha1,\n+\t\t\t\t   flags, NULL, &err) ||\n+\t    ref_transaction_commit(transaction, &err)) {\n+\t\terror(\"%s\", err.buf);\n+\t\tref_transaction_free(transaction);\n+\t\tstrbuf_release(&err);\n+\t\treturn 1;\n+\t}\n+\tref_transaction_free(transaction);\n+\tstrbuf_release(&err);\n+\treturn 0;\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271749","messageId":"1444938410-2345-8-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 07/26] refs.c: move read_ref_at to the common refs file","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:31Z","receivedAt":"2015-10-15T19:46:31Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nMove read_ref_at() to the refs.c file since this function does not\ncontain any backend specific code.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 118 --------------------------------------------------------\n refs.c          | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 118 insertions(+), 118 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex d969066..04c3206 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -3470,124 +3470,6 @@ int create_symref(const char *ref_target, const char *refs_heads_master,\n \treturn 0;\n }\n \n-struct read_ref_at_cb {\n-\tconst char *refname;\n-\tunsigned long at_time;\n-\tint cnt;\n-\tint reccnt;\n-\tunsigned char *sha1;\n-\tint found_it;\n-\n-\tunsigned char osha1[20];\n-\tunsigned char nsha1[20];\n-\tint tz;\n-\tunsigned long date;\n-\tchar **msg;\n-\tunsigned long *cutoff_time;\n-\tint *cutoff_tz;\n-\tint *cutoff_cnt;\n-};\n-\n-static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,\n-\t\tconst char *email, unsigned long timestamp, int tz,\n-\t\tconst char *message, void *cb_data)\n-{\n-\tstruct read_ref_at_cb *cb = cb_data;\n-\n-\tcb->reccnt++;\n-\tcb->tz = tz;\n-\tcb->date = timestamp;\n-\n-\tif (timestamp <= cb->at_time || cb->cnt == 0) {\n-\t\tif (cb->msg)\n-\t\t\t*cb->msg = xstrdup(message);\n-\t\tif (cb->cutoff_time)\n-\t\t\t*cb->cutoff_time = timestamp;\n-\t\tif (cb->cutoff_tz)\n-\t\t\t*cb->cutoff_tz = tz;\n-\t\tif (cb->cutoff_cnt)\n-\t\t\t*cb->cutoff_cnt = cb->reccnt - 1;\n-\t\t/*\n-\t\t * we have not yet updated cb->[n|o]sha1 so they still\n-\t\t * hold the values for the previous record.\n-\t\t */\n-\t\tif (!is_null_sha1(cb->osha1)) {\n-\t\t\thashcpy(cb->sha1, nsha1);\n-\t\t\tif (hashcmp(cb->osha1, nsha1))\n-\t\t\t\twarning(\"Log for ref %s has gap after %s.\",\n-\t\t\t\t\tcb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));\n-\t\t}\n-\t\telse if (cb->date == cb->at_time)\n-\t\t\thashcpy(cb->sha1, nsha1);\n-\t\telse if (hashcmp(nsha1, cb->sha1))\n-\t\t\twarning(\"Log for ref %s unexpectedly ended on %s.\",\n-\t\t\t\tcb->refname, show_date(cb->date, cb->tz,\n-\t\t\t\t\t\t       DATE_MODE(RFC2822)));\n-\t\thashcpy(cb->osha1, osha1);\n-\t\thashcpy(cb->nsha1, nsha1);\n-\t\tcb->found_it = 1;\n-\t\treturn 1;\n-\t}\n-\thashcpy(cb->osha1, osha1);\n-\thashcpy(cb->nsha1, nsha1);\n-\tif (cb->cnt > 0)\n-\t\tcb->cnt--;\n-\treturn 0;\n-}\n-\n-static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,\n-\t\t\t\t  const char *email, unsigned long timestamp,\n-\t\t\t\t  int tz, const char *message, void *cb_data)\n-{\n-\tstruct read_ref_at_cb *cb = cb_data;\n-\n-\tif (cb->msg)\n-\t\t*cb->msg = xstrdup(message);\n-\tif (cb->cutoff_time)\n-\t\t*cb->cutoff_time = timestamp;\n-\tif (cb->cutoff_tz)\n-\t\t*cb->cutoff_tz = tz;\n-\tif (cb->cutoff_cnt)\n-\t\t*cb->cutoff_cnt = cb->reccnt;\n-\thashcpy(cb->sha1, osha1);\n-\tif (is_null_sha1(cb->sha1))\n-\t\thashcpy(cb->sha1, nsha1);\n-\t/* We just want the first entry */\n-\treturn 1;\n-}\n-\n-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,\n-\t\tunsigned char *sha1, char **msg,\n-\t\tunsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)\n-{\n-\tstruct read_ref_at_cb cb;\n-\n-\tmemset(&cb, 0, sizeof(cb));\n-\tcb.refname = refname;\n-\tcb.at_time = at_time;\n-\tcb.cnt = cnt;\n-\tcb.msg = msg;\n-\tcb.cutoff_time = cutoff_time;\n-\tcb.cutoff_tz = cutoff_tz;\n-\tcb.cutoff_cnt = cutoff_cnt;\n-\tcb.sha1 = sha1;\n-\n-\tfor_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);\n-\n-\tif (!cb.reccnt) {\n-\t\tif (flags & GET_SHA1_QUIETLY)\n-\t\t\texit(128);\n-\t\telse\n-\t\t\tdie(\"Log for %s is empty.\", refname);\n-\t}\n-\tif (cb.found_it)\n-\t\treturn 0;\n-\n-\tfor_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);\n-\n-\treturn 1;\n-}\n-\n int reflog_exists(const char *refname)\n {\n \tstruct stat st;\ndiff --git a/refs.c b/refs.c\nindex f4f0677..8245118 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -174,3 +174,121 @@ int delete_ref(const char *refname, const unsigned char *old_sha1,\n \tstrbuf_release(&err);\n \treturn 0;\n }\n+\n+struct read_ref_at_cb {\n+\tconst char *refname;\n+\tunsigned long at_time;\n+\tint cnt;\n+\tint reccnt;\n+\tunsigned char *sha1;\n+\tint found_it;\n+\n+\tunsigned char osha1[20];\n+\tunsigned char nsha1[20];\n+\tint tz;\n+\tunsigned long date;\n+\tchar **msg;\n+\tunsigned long *cutoff_time;\n+\tint *cutoff_tz;\n+\tint *cutoff_cnt;\n+};\n+\n+static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,\n+\t\tconst char *email, unsigned long timestamp, int tz,\n+\t\tconst char *message, void *cb_data)\n+{\n+\tstruct read_ref_at_cb *cb = cb_data;\n+\n+\tcb->reccnt++;\n+\tcb->tz = tz;\n+\tcb->date = timestamp;\n+\n+\tif (timestamp <= cb->at_time || cb->cnt == 0) {\n+\t\tif (cb->msg)\n+\t\t\t*cb->msg = xstrdup(message);\n+\t\tif (cb->cutoff_time)\n+\t\t\t*cb->cutoff_time = timestamp;\n+\t\tif (cb->cutoff_tz)\n+\t\t\t*cb->cutoff_tz = tz;\n+\t\tif (cb->cutoff_cnt)\n+\t\t\t*cb->cutoff_cnt = cb->reccnt - 1;\n+\t\t/*\n+\t\t * we have not yet updated cb->[n|o]sha1 so they still\n+\t\t * hold the values for the previous record.\n+\t\t */\n+\t\tif (!is_null_sha1(cb->osha1)) {\n+\t\t\thashcpy(cb->sha1, nsha1);\n+\t\t\tif (hashcmp(cb->osha1, nsha1))\n+\t\t\t\twarning(\"Log for ref %s has gap after %s.\",\n+\t\t\t\t\tcb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));\n+\t\t}\n+\t\telse if (cb->date == cb->at_time)\n+\t\t\thashcpy(cb->sha1, nsha1);\n+\t\telse if (hashcmp(nsha1, cb->sha1))\n+\t\t\twarning(\"Log for ref %s unexpectedly ended on %s.\",\n+\t\t\t\tcb->refname, show_date(cb->date, cb->tz,\n+\t\t\t\t\t\t       DATE_MODE(RFC2822)));\n+\t\thashcpy(cb->osha1, osha1);\n+\t\thashcpy(cb->nsha1, nsha1);\n+\t\tcb->found_it = 1;\n+\t\treturn 1;\n+\t}\n+\thashcpy(cb->osha1, osha1);\n+\thashcpy(cb->nsha1, nsha1);\n+\tif (cb->cnt > 0)\n+\t\tcb->cnt--;\n+\treturn 0;\n+}\n+\n+static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,\n+\t\t\t\t  const char *email, unsigned long timestamp,\n+\t\t\t\t  int tz, const char *message, void *cb_data)\n+{\n+\tstruct read_ref_at_cb *cb = cb_data;\n+\n+\tif (cb->msg)\n+\t\t*cb->msg = xstrdup(message);\n+\tif (cb->cutoff_time)\n+\t\t*cb->cutoff_time = timestamp;\n+\tif (cb->cutoff_tz)\n+\t\t*cb->cutoff_tz = tz;\n+\tif (cb->cutoff_cnt)\n+\t\t*cb->cutoff_cnt = cb->reccnt;\n+\thashcpy(cb->sha1, osha1);\n+\tif (is_null_sha1(cb->sha1))\n+\t\thashcpy(cb->sha1, nsha1);\n+\t/* We just want the first entry */\n+\treturn 1;\n+}\n+\n+int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,\n+\t\tunsigned char *sha1, char **msg,\n+\t\tunsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)\n+{\n+\tstruct read_ref_at_cb cb;\n+\n+\tmemset(&cb, 0, sizeof(cb));\n+\tcb.refname = refname;\n+\tcb.at_time = at_time;\n+\tcb.cnt = cnt;\n+\tcb.msg = msg;\n+\tcb.cutoff_time = cutoff_time;\n+\tcb.cutoff_tz = cutoff_tz;\n+\tcb.cutoff_cnt = cutoff_cnt;\n+\tcb.sha1 = sha1;\n+\n+\tfor_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);\n+\n+\tif (!cb.reccnt) {\n+\t\tif (flags & GET_SHA1_QUIETLY)\n+\t\t\texit(128);\n+\t\telse\n+\t\t\tdie(\"Log for %s is empty.\", refname);\n+\t}\n+\tif (cb.found_it)\n+\t\treturn 0;\n+\n+\tfor_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);\n+\n+\treturn 1;\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271767","messageId":"1444938410-2345-9-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 08/26] refs.c: move the hidden refs functions to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:32Z","receivedAt":"2015-10-15T19:46:32Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nMove the hidden refs functions to the refs.c file since these\nfunctions do not contain any backend specific code.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 51 ---------------------------------------------------\n refs.c          | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 51 insertions(+), 51 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 04c3206..dad23c7 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -4215,57 +4215,6 @@ char *shorten_unambiguous_ref(const char *refname, int strict)\n \treturn xstrdup(refname);\n }\n \n-static struct string_list *hide_refs;\n-\n-int parse_hide_refs_config(const char *var, const char *value, const char *section)\n-{\n-\tif (!strcmp(\"transfer.hiderefs\", var) ||\n-\t    /* NEEDSWORK: use parse_config_key() once both are merged */\n-\t    (starts_with(var, section) && var[strlen(section)] == '.' &&\n-\t     !strcmp(var + strlen(section), \".hiderefs\"))) {\n-\t\tchar *ref;\n-\t\tint len;\n-\n-\t\tif (!value)\n-\t\t\treturn config_error_nonbool(var);\n-\t\tref = xstrdup(value);\n-\t\tlen = strlen(ref);\n-\t\twhile (len && ref[len - 1] == '/')\n-\t\t\tref[--len] = '\\0';\n-\t\tif (!hide_refs) {\n-\t\t\thide_refs = xcalloc(1, sizeof(*hide_refs));\n-\t\t\thide_refs->strdup_strings = 1;\n-\t\t}\n-\t\tstring_list_append(hide_refs, ref);\n-\t}\n-\treturn 0;\n-}\n-\n-int ref_is_hidden(const char *refname)\n-{\n-\tint i;\n-\n-\tif (!hide_refs)\n-\t\treturn 0;\n-\tfor (i = hide_refs->nr - 1; i >= 0; i--) {\n-\t\tconst char *match = hide_refs->items[i].string;\n-\t\tint neg = 0;\n-\t\tint len;\n-\n-\t\tif (*match == '!') {\n-\t\t\tneg = 1;\n-\t\t\tmatch++;\n-\t\t}\n-\n-\t\tif (!starts_with(refname, match))\n-\t\t\tcontinue;\n-\t\tlen = strlen(match);\n-\t\tif (!refname[len] || refname[len] == '/')\n-\t\t\treturn !neg;\n-\t}\n-\treturn 0;\n-}\n-\n struct expire_reflog_cb {\n \tunsigned int flags;\n \treflog_expiry_should_prune_fn *should_prune_fn;\ndiff --git a/refs.c b/refs.c\nindex 8245118..6e5c8f8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -292,3 +292,54 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,\n \n \treturn 1;\n }\n+\n+static struct string_list *hide_refs;\n+\n+int parse_hide_refs_config(const char *var, const char *value, const char *section)\n+{\n+\tif (!strcmp(\"transfer.hiderefs\", var) ||\n+\t    /* NEEDSWORK: use parse_config_key() once both are merged */\n+\t    (starts_with(var, section) && var[strlen(section)] == '.' &&\n+\t     !strcmp(var + strlen(section), \".hiderefs\"))) {\n+\t\tchar *ref;\n+\t\tint len;\n+\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\tref = xstrdup(value);\n+\t\tlen = strlen(ref);\n+\t\twhile (len && ref[len - 1] == '/')\n+\t\t\tref[--len] = '\\0';\n+\t\tif (!hide_refs) {\n+\t\t\thide_refs = xcalloc(1, sizeof(*hide_refs));\n+\t\t\thide_refs->strdup_strings = 1;\n+\t\t}\n+\t\tstring_list_append(hide_refs, ref);\n+\t}\n+\treturn 0;\n+}\n+\n+int ref_is_hidden(const char *refname)\n+{\n+\tint i;\n+\n+\tif (!hide_refs)\n+\t\treturn 0;\n+\tfor (i = hide_refs->nr - 1; i >= 0; i--) {\n+\t\tconst char *match = hide_refs->items[i].string;\n+\t\tint neg = 0;\n+\t\tint len;\n+\n+\t\tif (*match == '!') {\n+\t\t\tneg = 1;\n+\t\t\tmatch++;\n+\t\t}\n+\n+\t\tif (!starts_with(refname, match))\n+\t\t\tcontinue;\n+\t\tlen = strlen(match);\n+\t\tif (!refname[len] || refname[len] == '/')\n+\t\t\treturn !neg;\n+\t}\n+\treturn 0;\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271772","messageId":"1444938410-2345-10-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 09/26] refs.c: move dwim and friend functions to the common refs code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:33Z","receivedAt":"2015-10-15T19:46:33Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nThese functions do not contain any backend specific code so we move\nthem to the common code and share across all backends.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 203 --------------------------------------------------------\n refs.c          | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 203 insertions(+), 203 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex dad23c7..3609fb7 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -2247,30 +2247,6 @@ const char *prettify_refname(const char *name)\n \t\t0);\n }\n \n-static const char *ref_rev_parse_rules[] = {\n-\t\"%.*s\",\n-\t\"refs/%.*s\",\n-\t\"refs/tags/%.*s\",\n-\t\"refs/heads/%.*s\",\n-\t\"refs/remotes/%.*s\",\n-\t\"refs/remotes/%.*s/HEAD\",\n-\tNULL\n-};\n-\n-int refname_match(const char *abbrev_name, const char *full_name)\n-{\n-\tconst char **p;\n-\tconst int abbrev_name_len = strlen(abbrev_name);\n-\n-\tfor (p = ref_rev_parse_rules; *p; p++) {\n-\t\tif (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) {\n-\t\t\treturn 1;\n-\t\t}\n-\t}\n-\n-\treturn 0;\n-}\n-\n static void unlock_ref(struct ref_lock *lock)\n {\n \t/* Do not free lock->lk -- atexit() still looks at them */\n@@ -2323,92 +2299,6 @@ static int remove_empty_directories(struct strbuf *path)\n }\n \n /*\n- * *string and *len will only be substituted, and *string returned (for\n- * later free()ing) if the string passed in is a magic short-hand form\n- * to name a branch.\n- */\n-static char *substitute_branch_name(const char **string, int *len)\n-{\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint ret = interpret_branch_name(*string, *len, &buf);\n-\n-\tif (ret == *len) {\n-\t\tsize_t size;\n-\t\t*string = strbuf_detach(&buf, &size);\n-\t\t*len = size;\n-\t\treturn (char *)*string;\n-\t}\n-\n-\treturn NULL;\n-}\n-\n-int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)\n-{\n-\tchar *last_branch = substitute_branch_name(&str, &len);\n-\tconst char **p, *r;\n-\tint refs_found = 0;\n-\n-\t*ref = NULL;\n-\tfor (p = ref_rev_parse_rules; *p; p++) {\n-\t\tchar fullref[PATH_MAX];\n-\t\tunsigned char sha1_from_ref[20];\n-\t\tunsigned char *this_result;\n-\t\tint flag;\n-\n-\t\tthis_result = refs_found ? sha1_from_ref : sha1;\n-\t\tmksnpath(fullref, sizeof(fullref), *p, len, str);\n-\t\tr = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,\n-\t\t\t\t       this_result, &flag);\n-\t\tif (r) {\n-\t\t\tif (!refs_found++)\n-\t\t\t\t*ref = xstrdup(r);\n-\t\t\tif (!warn_ambiguous_refs)\n-\t\t\t\tbreak;\n-\t\t} else if ((flag & REF_ISSYMREF) && strcmp(fullref, \"HEAD\")) {\n-\t\t\twarning(\"ignoring dangling symref %s.\", fullref);\n-\t\t} else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) {\n-\t\t\twarning(\"ignoring broken ref %s.\", fullref);\n-\t\t}\n-\t}\n-\tfree(last_branch);\n-\treturn refs_found;\n-}\n-\n-int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n-{\n-\tchar *last_branch = substitute_branch_name(&str, &len);\n-\tconst char **p;\n-\tint logs_found = 0;\n-\n-\t*log = NULL;\n-\tfor (p = ref_rev_parse_rules; *p; p++) {\n-\t\tunsigned char hash[20];\n-\t\tchar path[PATH_MAX];\n-\t\tconst char *ref, *it;\n-\n-\t\tmksnpath(path, sizeof(path), *p, len, str);\n-\t\tref = resolve_ref_unsafe(path, RESOLVE_REF_READING,\n-\t\t\t\t\t hash, NULL);\n-\t\tif (!ref)\n-\t\t\tcontinue;\n-\t\tif (reflog_exists(path))\n-\t\t\tit = path;\n-\t\telse if (strcmp(ref, path) && reflog_exists(ref))\n-\t\t\tit = ref;\n-\t\telse\n-\t\t\tcontinue;\n-\t\tif (!logs_found++) {\n-\t\t\t*log = xstrdup(it);\n-\t\t\thashcpy(sha1, hash);\n-\t\t}\n-\t\tif (!warn_ambiguous_refs)\n-\t\t\tbreak;\n-\t}\n-\tfree(last_branch);\n-\treturn logs_found;\n-}\n-\n-/*\n  * Locks a ref returning the lock on success and NULL on failure.\n  * On failure errno is set to something meaningful.\n  */\n@@ -4122,99 +4012,6 @@ cleanup:\n \treturn ret;\n }\n \n-char *shorten_unambiguous_ref(const char *refname, int strict)\n-{\n-\tint i;\n-\tstatic char **scanf_fmts;\n-\tstatic int nr_rules;\n-\tchar *short_name;\n-\n-\tif (!nr_rules) {\n-\t\t/*\n-\t\t * Pre-generate scanf formats from ref_rev_parse_rules[].\n-\t\t * Generate a format suitable for scanf from a\n-\t\t * ref_rev_parse_rules rule by interpolating \"%s\" at the\n-\t\t * location of the \"%.*s\".\n-\t\t */\n-\t\tsize_t total_len = 0;\n-\t\tsize_t offset = 0;\n-\n-\t\t/* the rule list is NULL terminated, count them first */\n-\t\tfor (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)\n-\t\t\t/* -2 for strlen(\"%.*s\") - strlen(\"%s\"); +1 for NUL */\n-\t\t\ttotal_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;\n-\n-\t\tscanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);\n-\n-\t\toffset = 0;\n-\t\tfor (i = 0; i < nr_rules; i++) {\n-\t\t\tassert(offset < total_len);\n-\t\t\tscanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset;\n-\t\t\toffset += snprintf(scanf_fmts[i], total_len - offset,\n-\t\t\t\t\t   ref_rev_parse_rules[i], 2, \"%s\") + 1;\n-\t\t}\n-\t}\n-\n-\t/* bail out if there are no rules */\n-\tif (!nr_rules)\n-\t\treturn xstrdup(refname);\n-\n-\t/* buffer for scanf result, at most refname must fit */\n-\tshort_name = xstrdup(refname);\n-\n-\t/* skip first rule, it will always match */\n-\tfor (i = nr_rules - 1; i > 0 ; --i) {\n-\t\tint j;\n-\t\tint rules_to_fail = i;\n-\t\tint short_name_len;\n-\n-\t\tif (1 != sscanf(refname, scanf_fmts[i], short_name))\n-\t\t\tcontinue;\n-\n-\t\tshort_name_len = strlen(short_name);\n-\n-\t\t/*\n-\t\t * in strict mode, all (except the matched one) rules\n-\t\t * must fail to resolve to a valid non-ambiguous ref\n-\t\t */\n-\t\tif (strict)\n-\t\t\trules_to_fail = nr_rules;\n-\n-\t\t/*\n-\t\t * check if the short name resolves to a valid ref,\n-\t\t * but use only rules prior to the matched one\n-\t\t */\n-\t\tfor (j = 0; j < rules_to_fail; j++) {\n-\t\t\tconst char *rule = ref_rev_parse_rules[j];\n-\t\t\tchar refname[PATH_MAX];\n-\n-\t\t\t/* skip matched rule */\n-\t\t\tif (i == j)\n-\t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * the short name is ambiguous, if it resolves\n-\t\t\t * (with this previous rule) to a valid ref\n-\t\t\t * read_ref() returns 0 on success\n-\t\t\t */\n-\t\t\tmksnpath(refname, sizeof(refname),\n-\t\t\t\t rule, short_name_len, short_name);\n-\t\t\tif (ref_exists(refname))\n-\t\t\t\tbreak;\n-\t\t}\n-\n-\t\t/*\n-\t\t * short name is non-ambiguous if all previous rules\n-\t\t * haven't resolved to a valid ref\n-\t\t */\n-\t\tif (j == rules_to_fail)\n-\t\t\treturn short_name;\n-\t}\n-\n-\tfree(short_name);\n-\treturn xstrdup(refname);\n-}\n-\n struct expire_reflog_cb {\n \tunsigned int flags;\n \treflog_expiry_should_prune_fn *should_prune_fn;\ndiff --git a/refs.c b/refs.c\nindex 6e5c8f8..53296e9 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -343,3 +343,206 @@ int ref_is_hidden(const char *refname)\n \t}\n \treturn 0;\n }\n+\n+static const char *ref_rev_parse_rules[] = {\n+\t\"%.*s\",\n+\t\"refs/%.*s\",\n+\t\"refs/tags/%.*s\",\n+\t\"refs/heads/%.*s\",\n+\t\"refs/remotes/%.*s\",\n+\t\"refs/remotes/%.*s/HEAD\",\n+\tNULL\n+};\n+\n+int refname_match(const char *abbrev_name, const char *full_name)\n+{\n+\tconst char **p;\n+\tconst int abbrev_name_len = strlen(abbrev_name);\n+\n+\tfor (p = ref_rev_parse_rules; *p; p++) {\n+\t\tif (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) {\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * *string and *len will only be substituted, and *string returned (for\n+ * later free()ing) if the string passed in is a magic short-hand form\n+ * to name a branch.\n+ */\n+static char *substitute_branch_name(const char **string, int *len)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint ret = interpret_branch_name(*string, *len, &buf);\n+\n+\tif (ret == *len) {\n+\t\tsize_t size;\n+\t\t*string = strbuf_detach(&buf, &size);\n+\t\t*len = size;\n+\t\treturn (char *)*string;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)\n+{\n+\tchar *last_branch = substitute_branch_name(&str, &len);\n+\tconst char **p, *r;\n+\tint refs_found = 0;\n+\n+\t*ref = NULL;\n+\tfor (p = ref_rev_parse_rules; *p; p++) {\n+\t\tchar fullref[PATH_MAX];\n+\t\tunsigned char sha1_from_ref[20];\n+\t\tunsigned char *this_result;\n+\t\tint flag;\n+\n+\t\tthis_result = refs_found ? sha1_from_ref : sha1;\n+\t\tmksnpath(fullref, sizeof(fullref), *p, len, str);\n+\t\tr = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,\n+\t\t\t\t       this_result, &flag);\n+\t\tif (r) {\n+\t\t\tif (!refs_found++)\n+\t\t\t\t*ref = xstrdup(r);\n+\t\t\tif (!warn_ambiguous_refs)\n+\t\t\t\tbreak;\n+\t\t} else if ((flag & REF_ISSYMREF) && strcmp(fullref, \"HEAD\")) {\n+\t\t\twarning(\"ignoring dangling symref %s.\", fullref);\n+\t\t} else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) {\n+\t\t\twarning(\"ignoring broken ref %s.\", fullref);\n+\t\t}\n+\t}\n+\tfree(last_branch);\n+\treturn refs_found;\n+}\n+\n+int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n+{\n+\tchar *last_branch = substitute_branch_name(&str, &len);\n+\tconst char **p;\n+\tint logs_found = 0;\n+\n+\t*log = NULL;\n+\tfor (p = ref_rev_parse_rules; *p; p++) {\n+\t\tunsigned char hash[20];\n+\t\tchar path[PATH_MAX];\n+\t\tconst char *ref, *it;\n+\n+\t\tmksnpath(path, sizeof(path), *p, len, str);\n+\t\tref = resolve_ref_unsafe(path, RESOLVE_REF_READING,\n+\t\t\t\t\t hash, NULL);\n+\t\tif (!ref)\n+\t\t\tcontinue;\n+\t\tif (reflog_exists(path))\n+\t\t\tit = path;\n+\t\telse if (strcmp(ref, path) && reflog_exists(ref))\n+\t\t\tit = ref;\n+\t\telse\n+\t\t\tcontinue;\n+\t\tif (!logs_found++) {\n+\t\t\t*log = xstrdup(it);\n+\t\t\thashcpy(sha1, hash);\n+\t\t}\n+\t\tif (!warn_ambiguous_refs)\n+\t\t\tbreak;\n+\t}\n+\tfree(last_branch);\n+\treturn logs_found;\n+}\n+\n+char *shorten_unambiguous_ref(const char *refname, int strict)\n+{\n+\tint i;\n+\tstatic char **scanf_fmts;\n+\tstatic int nr_rules;\n+\tchar *short_name;\n+\n+\tif (!nr_rules) {\n+\t\t/*\n+\t\t * Pre-generate scanf formats from ref_rev_parse_rules[].\n+\t\t * Generate a format suitable for scanf from a\n+\t\t * ref_rev_parse_rules rule by interpolating \"%s\" at the\n+\t\t * location of the \"%.*s\".\n+\t\t */\n+\t\tsize_t total_len = 0;\n+\t\tsize_t offset = 0;\n+\n+\t\t/* the rule list is NULL terminated, count them first */\n+\t\tfor (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)\n+\t\t\t/* -2 for strlen(\"%.*s\") - strlen(\"%s\"); +1 for NUL */\n+\t\t\ttotal_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;\n+\n+\t\tscanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);\n+\n+\t\toffset = 0;\n+\t\tfor (i = 0; i < nr_rules; i++) {\n+\t\t\tassert(offset < total_len);\n+\t\t\tscanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset;\n+\t\t\toffset += snprintf(scanf_fmts[i], total_len - offset,\n+\t\t\t\t\t   ref_rev_parse_rules[i], 2, \"%s\") + 1;\n+\t\t}\n+\t}\n+\n+\t/* bail out if there are no rules */\n+\tif (!nr_rules)\n+\t\treturn xstrdup(refname);\n+\n+\t/* buffer for scanf result, at most refname must fit */\n+\tshort_name = xstrdup(refname);\n+\n+\t/* skip first rule, it will always match */\n+\tfor (i = nr_rules - 1; i > 0 ; --i) {\n+\t\tint j;\n+\t\tint rules_to_fail = i;\n+\t\tint short_name_len;\n+\n+\t\tif (1 != sscanf(refname, scanf_fmts[i], short_name))\n+\t\t\tcontinue;\n+\n+\t\tshort_name_len = strlen(short_name);\n+\n+\t\t/*\n+\t\t * in strict mode, all (except the matched one) rules\n+\t\t * must fail to resolve to a valid non-ambiguous ref\n+\t\t */\n+\t\tif (strict)\n+\t\t\trules_to_fail = nr_rules;\n+\n+\t\t/*\n+\t\t * check if the short name resolves to a valid ref,\n+\t\t * but use only rules prior to the matched one\n+\t\t */\n+\t\tfor (j = 0; j < rules_to_fail; j++) {\n+\t\t\tconst char *rule = ref_rev_parse_rules[j];\n+\t\t\tchar refname[PATH_MAX];\n+\n+\t\t\t/* skip matched rule */\n+\t\t\tif (i == j)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * the short name is ambiguous, if it resolves\n+\t\t\t * (with this previous rule) to a valid ref\n+\t\t\t * read_ref() returns 0 on success\n+\t\t\t */\n+\t\t\tmksnpath(refname, sizeof(refname),\n+\t\t\t\t rule, short_name_len, short_name);\n+\t\t\tif (ref_exists(refname))\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\t/*\n+\t\t * short name is non-ambiguous if all previous rules\n+\t\t * haven't resolved to a valid ref\n+\t\t */\n+\t\tif (j == rules_to_fail)\n+\t\t\treturn short_name;\n+\t}\n+\n+\tfree(short_name);\n+\treturn xstrdup(refname);\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271751","messageId":"1444938410-2345-11-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 10/26] refs.c: move warn_if_dangling_symref* to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:34Z","receivedAt":"2015-10-15T19:46:34Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nThese functions do not use any backend specific code so we move\nthem to the common code.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 52 ----------------------------------------------------\n refs.c          | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 52 insertions(+), 52 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 3609fb7..9c53f54 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -1944,58 +1944,6 @@ int peel_ref(const char *refname, unsigned char *sha1)\n \treturn peel_object(base, sha1);\n }\n \n-struct warn_if_dangling_data {\n-\tFILE *fp;\n-\tconst char *refname;\n-\tconst struct string_list *refnames;\n-\tconst char *msg_fmt;\n-};\n-\n-static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,\n-\t\t\t\t   int flags, void *cb_data)\n-{\n-\tstruct warn_if_dangling_data *d = cb_data;\n-\tconst char *resolves_to;\n-\tstruct object_id junk;\n-\n-\tif (!(flags & REF_ISSYMREF))\n-\t\treturn 0;\n-\n-\tresolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);\n-\tif (!resolves_to\n-\t    || (d->refname\n-\t\t? strcmp(resolves_to, d->refname)\n-\t\t: !string_list_has_string(d->refnames, resolves_to))) {\n-\t\treturn 0;\n-\t}\n-\n-\tfprintf(d->fp, d->msg_fmt, refname);\n-\tfputc('\\n', d->fp);\n-\treturn 0;\n-}\n-\n-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)\n-{\n-\tstruct warn_if_dangling_data data;\n-\n-\tdata.fp = fp;\n-\tdata.refname = refname;\n-\tdata.refnames = NULL;\n-\tdata.msg_fmt = msg_fmt;\n-\tfor_each_rawref(warn_if_dangling_symref, &data);\n-}\n-\n-void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)\n-{\n-\tstruct warn_if_dangling_data data;\n-\n-\tdata.fp = fp;\n-\tdata.refname = NULL;\n-\tdata.refnames = refnames;\n-\tdata.msg_fmt = msg_fmt;\n-\tfor_each_rawref(warn_if_dangling_symref, &data);\n-}\n-\n /*\n  * Call fn for each reference in the specified ref_cache, omitting\n  * references not in the containing_dir of base.  fn is called for all\ndiff --git a/refs.c b/refs.c\nindex 53296e9..fab7603 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -546,3 +546,55 @@ char *shorten_unambiguous_ref(const char *refname, int strict)\n \tfree(short_name);\n \treturn xstrdup(refname);\n }\n+\n+struct warn_if_dangling_data {\n+\tFILE *fp;\n+\tconst char *refname;\n+\tconst struct string_list *refnames;\n+\tconst char *msg_fmt;\n+};\n+\n+static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,\n+\t\t\t\t   int flags, void *cb_data)\n+{\n+\tstruct warn_if_dangling_data *d = cb_data;\n+\tconst char *resolves_to;\n+\tstruct object_id junk;\n+\n+\tif (!(flags & REF_ISSYMREF))\n+\t\treturn 0;\n+\n+\tresolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);\n+\tif (!resolves_to\n+\t    || (d->refname\n+\t\t? strcmp(resolves_to, d->refname)\n+\t\t: !string_list_has_string(d->refnames, resolves_to))) {\n+\t\treturn 0;\n+\t}\n+\n+\tfprintf(d->fp, d->msg_fmt, refname);\n+\tfputc('\\n', d->fp);\n+\treturn 0;\n+}\n+\n+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)\n+{\n+\tstruct warn_if_dangling_data data;\n+\n+\tdata.fp = fp;\n+\tdata.refname = refname;\n+\tdata.refnames = NULL;\n+\tdata.msg_fmt = msg_fmt;\n+\tfor_each_rawref(warn_if_dangling_symref, &data);\n+}\n+\n+void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)\n+{\n+\tstruct warn_if_dangling_data data;\n+\n+\tdata.fp = fp;\n+\tdata.refname = NULL;\n+\tdata.refnames = refnames;\n+\tdata.msg_fmt = msg_fmt;\n+\tfor_each_rawref(warn_if_dangling_symref, &data);\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271769","messageId":"1444938410-2345-12-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 11/26] refs.c: move read_ref, read_ref_full and ref_exists to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:35Z","receivedAt":"2015-10-15T19:46:35Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nThese functions do not depend on the backend implementation so we\nmove them to the common code.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 18 ------------------\n refs.c          | 18 ++++++++++++++++++\n 2 files changed, 18 insertions(+), 18 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 9c53f54..728751a 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -1791,24 +1791,6 @@ 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-{\n-\tif (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))\n-\t\treturn 0;\n-\treturn -1;\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-}\n-\n-int ref_exists(const char *refname)\n-{\n-\tunsigned char sha1[20];\n-\treturn !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);\n-}\n-\n static int filter_refs(const char *refname, const struct object_id *oid,\n \t\t\t   int flags, void *data)\n {\ndiff --git a/refs.c b/refs.c\nindex fab7603..8a619a6 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -598,3 +598,21 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li\n \tdata.msg_fmt = msg_fmt;\n \tfor_each_rawref(warn_if_dangling_symref, &data);\n }\n+\n+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)\n+{\n+\tif (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))\n+\t\treturn 0;\n+\treturn -1;\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+}\n+\n+int ref_exists(const char *refname)\n+{\n+\tunsigned char sha1[20];\n+\treturn !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271753","messageId":"1444938410-2345-13-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 12/26] refs.c: move resolve_refdup to common","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:36Z","receivedAt":"2015-10-15T19:46:36Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nThis function can be shared across all refs backends so move it\nto the common code.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 7 -------\n refs.c          | 7 +++++++\n 2 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 728751a..589e10d 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -1777,13 +1777,6 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \treturn ret;\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-}\n-\n /* The argument to filter_refs */\n struct ref_filter {\n \tconst char *pattern;\ndiff --git a/refs.c b/refs.c\nindex 8a619a6..fcd5ddd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -616,3 +616,10 @@ int ref_exists(const char *refname)\n \tunsigned char sha1[20];\n \treturn !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);\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+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271765","messageId":"1444938410-2345-14-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 13/26] refs.c: move check_refname_format to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:37Z","receivedAt":"2015-10-15T19:46:37Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nThis function does not contain any backend specific code so we\nmove it to the common code.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 109 --------------------------------------------------------\n refs.c          | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 109 insertions(+), 109 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 589e10d..345bee7 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -14,27 +14,6 @@ struct ref_lock {\n };\n \n /*\n- * How to handle various characters in refnames:\n- * 0: An acceptable character for refs\n- * 1: End-of-component\n- * 2: ., look for a preceding . to reject .. in refs\n- * 3: {, look for a preceding @ to reject @{ in refs\n- * 4: A bad character: ASCII control characters, and\n- *    \":\", \"?\", \"[\", \"\\\", \"^\", \"~\", SP, or TAB\n- * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set\n- */\n-static unsigned char refname_disposition[256] = {\n-\t1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n-\t4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n-\t4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,\n-\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,\n-\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n-\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,\n-\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n-\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4\n-};\n-\n-/*\n  * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken\n  * refs (i.e., because the reference is about to be deleted anyway).\n  */\n@@ -69,94 +48,6 @@ static unsigned char refname_disposition[256] = {\n  * value to ref_update::flags\n  */\n \n-/*\n- * Try to read one refname component from the front of refname.\n- * Return the length of the component found, or -1 if the component is\n- * not legal.  It is legal if it is something reasonable to have under\n- * \".git/refs/\"; We do not like it if:\n- *\n- * - any path component of it begins with \".\", or\n- * - it has double dots \"..\", or\n- * - it has ASCII control characters, or\n- * - it has \":\", \"?\", \"[\", \"\\\", \"^\", \"~\", SP, or TAB anywhere, or\n- * - it has \"*\" anywhere unless REFNAME_REFSPEC_PATTERN is set, or\n- * - it ends with a \"/\", or\n- * - it ends with \".lock\", or\n- * - it contains a \"@{\" portion\n- */\n-static int check_refname_component(const char *refname, int *flags)\n-{\n-\tconst char *cp;\n-\tchar last = '\\0';\n-\n-\tfor (cp = refname; ; cp++) {\n-\t\tint ch = *cp & 255;\n-\t\tunsigned char disp = refname_disposition[ch];\n-\t\tswitch (disp) {\n-\t\tcase 1:\n-\t\t\tgoto out;\n-\t\tcase 2:\n-\t\t\tif (last == '.')\n-\t\t\t\treturn -1; /* Refname contains \"..\". */\n-\t\t\tbreak;\n-\t\tcase 3:\n-\t\t\tif (last == '@')\n-\t\t\t\treturn -1; /* Refname contains \"@{\". */\n-\t\t\tbreak;\n-\t\tcase 4:\n-\t\t\treturn -1;\n-\t\tcase 5:\n-\t\t\tif (!(*flags & REFNAME_REFSPEC_PATTERN))\n-\t\t\t\treturn -1; /* refspec can't be a pattern */\n-\n-\t\t\t/*\n-\t\t\t * Unset the pattern flag so that we only accept\n-\t\t\t * a single asterisk for one side of refspec.\n-\t\t\t */\n-\t\t\t*flags &= ~ REFNAME_REFSPEC_PATTERN;\n-\t\t\tbreak;\n-\t\t}\n-\t\tlast = ch;\n-\t}\n-out:\n-\tif (cp == refname)\n-\t\treturn 0; /* Component has zero length. */\n-\tif (refname[0] == '.')\n-\t\treturn -1; /* Component starts with '.'. */\n-\tif (cp - refname >= LOCK_SUFFIX_LEN &&\n-\t    !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))\n-\t\treturn -1; /* Refname ends with \".lock\". */\n-\treturn cp - refname;\n-}\n-\n-int check_refname_format(const char *refname, int flags)\n-{\n-\tint component_len, component_count = 0;\n-\n-\tif (!strcmp(refname, \"@\"))\n-\t\t/* Refname is a single character '@'. */\n-\t\treturn -1;\n-\n-\twhile (1) {\n-\t\t/* We are at the start of a path component. */\n-\t\tcomponent_len = check_refname_component(refname, &flags);\n-\t\tif (component_len <= 0)\n-\t\t\treturn -1;\n-\n-\t\tcomponent_count++;\n-\t\tif (refname[component_len] == '\\0')\n-\t\t\tbreak;\n-\t\t/* Skip to next component. */\n-\t\trefname += component_len + 1;\n-\t}\n-\n-\tif (refname[component_len - 1] == '.')\n-\t\treturn -1; /* Refname ends with '.'. */\n-\tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n-\t\treturn -1; /* Refname has only one component. */\n-\treturn 0;\n-}\n-\n struct ref_entry;\n \n /*\ndiff --git a/refs.c b/refs.c\nindex fcd5ddd..d79ba82 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -623,3 +623,112 @@ char *resolve_refdup(const char *refname, int resolve_flags,\n \treturn xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t\t\t  sha1, flags));\n }\n+\n+/*\n+ * How to handle various characters in refnames:\n+ * 0: An acceptable character for refs\n+ * 1: End-of-component\n+ * 2: ., look for a preceding . to reject .. in refs\n+ * 3: {, look for a preceding @ to reject @{ in refs\n+ * 4: A bad character: ASCII control characters, and\n+ *    \":\", \"?\", \"[\", \"\\\", \"^\", \"~\", SP, or TAB\n+ * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set\n+ */\n+static unsigned char refname_disposition[256] = {\n+\t1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n+\t4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n+\t4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,\n+\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,\n+\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n+\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,\n+\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n+\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4\n+};\n+\n+/*\n+ * Try to read one refname component from the front of refname.\n+ * Return the length of the component found, or -1 if the component is\n+ * not legal.  It is legal if it is something reasonable to have under\n+ * \".git/refs/\"; We do not like it if:\n+ *\n+ * - any path component of it begins with \".\", or\n+ * - it has double dots \"..\", or\n+ * - it has ASCII control characters, or\n+ * - it has \":\", \"?\", \"[\", \"\\\", \"^\", \"~\", SP, or TAB anywhere, or\n+ * - it has \"*\" anywhere unless REFNAME_REFSPEC_PATTERN is set, or\n+ * - it ends with a \"/\", or\n+ * - it ends with \".lock\", or\n+ * - it contains a \"@{\" portion\n+ */\n+static int check_refname_component(const char *refname, int *flags)\n+{\n+\tconst char *cp;\n+\tchar last = '\\0';\n+\n+\tfor (cp = refname; ; cp++) {\n+\t\tint ch = *cp & 255;\n+\t\tunsigned char disp = refname_disposition[ch];\n+\t\tswitch (disp) {\n+\t\tcase 1:\n+\t\t\tgoto out;\n+\t\tcase 2:\n+\t\t\tif (last == '.')\n+\t\t\t\treturn -1; /* Refname contains \"..\". */\n+\t\t\tbreak;\n+\t\tcase 3:\n+\t\t\tif (last == '@')\n+\t\t\t\treturn -1; /* Refname contains \"@{\". */\n+\t\t\tbreak;\n+\t\tcase 4:\n+\t\t\treturn -1;\n+\t\tcase 5:\n+\t\t\tif (!(*flags & REFNAME_REFSPEC_PATTERN))\n+\t\t\t\treturn -1; /* refspec can't be a pattern */\n+\n+\t\t\t/*\n+\t\t\t * Unset the pattern flag so that we only accept\n+\t\t\t * a single asterisk for one side of refspec.\n+\t\t\t */\n+\t\t\t*flags &= ~ REFNAME_REFSPEC_PATTERN;\n+\t\t\tbreak;\n+\t\t}\n+\t\tlast = ch;\n+\t}\n+out:\n+\tif (cp == refname)\n+\t\treturn 0; /* Component has zero length. */\n+\tif (refname[0] == '.')\n+\t\treturn -1; /* Component starts with '.'. */\n+\tif (cp - refname >= LOCK_SUFFIX_LEN &&\n+\t    !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))\n+\t\treturn -1; /* Refname ends with \".lock\". */\n+\treturn cp - refname;\n+}\n+\n+int check_refname_format(const char *refname, int flags)\n+{\n+\tint component_len, component_count = 0;\n+\n+\tif (!strcmp(refname, \"@\"))\n+\t\t/* Refname is a single character '@'. */\n+\t\treturn -1;\n+\n+\twhile (1) {\n+\t\t/* We are at the start of a path component. */\n+\t\tcomponent_len = check_refname_component(refname, &flags);\n+\t\tif (component_len <= 0)\n+\t\t\treturn -1;\n+\n+\t\tcomponent_count++;\n+\t\tif (refname[component_len] == '\\0')\n+\t\t\tbreak;\n+\t\t/* Skip to next component. */\n+\t\trefname += component_len + 1;\n+\t}\n+\n+\tif (refname[component_len - 1] == '.')\n+\t\treturn -1; /* Refname ends with '.'. */\n+\tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n+\t\treturn -1; /* Refname has only one component. */\n+\treturn 0;\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271754","messageId":"1444938410-2345-15-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 14/26] refs.c: move is_branch to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:38Z","receivedAt":"2015-10-15T19:46:38Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 5 -----\n refs.c          | 5 +++++\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 345bee7..5c6c743 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -3003,11 +3003,6 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,\n \treturn ret;\n }\n \n-int is_branch(const char *refname)\n-{\n-\treturn !strcmp(refname, \"HEAD\") || starts_with(refname, \"refs/heads/\");\n-}\n-\n /*\n  * Write sha1 into the open lockfile, then close the lockfile. On\n  * errors, rollback the lockfile, fill in *err and\ndiff --git a/refs.c b/refs.c\nindex d79ba82..f2f8052 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -732,3 +732,8 @@ int check_refname_format(const char *refname, int flags)\n \t\treturn -1; /* Refname has only one component. */\n \treturn 0;\n }\n+\n+int is_branch(const char *refname)\n+{\n+\treturn !strcmp(refname, \"HEAD\") || starts_with(refname, \"refs/heads/\");\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271764","messageId":"1444938410-2345-16-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 15/26] refs.c: move prettify_refname to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:39Z","receivedAt":"2015-10-15T19:46:39Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 9 ---------\n refs.c          | 9 +++++++++\n 2 files changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 5c6c743..c8b44ab 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -2052,15 +2052,6 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)\n \t\t\t       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n }\n \n-const char *prettify_refname(const char *name)\n-{\n-\treturn name + (\n-\t\tstarts_with(name, \"refs/heads/\") ? 11 :\n-\t\tstarts_with(name, \"refs/tags/\") ? 10 :\n-\t\tstarts_with(name, \"refs/remotes/\") ? 13 :\n-\t\t0);\n-}\n-\n static void unlock_ref(struct ref_lock *lock)\n {\n \t/* Do not free lock->lk -- atexit() still looks at them */\ndiff --git a/refs.c b/refs.c\nindex f2f8052..642a2ab 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -737,3 +737,12 @@ int is_branch(const char *refname)\n {\n \treturn !strcmp(refname, \"HEAD\") || starts_with(refname, \"refs/heads/\");\n }\n+\n+const char *prettify_refname(const char *name)\n+{\n+\treturn name + (\n+\t\tstarts_with(name, \"refs/heads/\") ? 11 :\n+\t\tstarts_with(name, \"refs/tags/\") ? 10 :\n+\t\tstarts_with(name, \"refs/remotes/\") ? 13 :\n+\t\t0);\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271752","messageId":"1444938410-2345-17-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 16/26] refs.c: move ref iterators to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:40Z","receivedAt":"2015-10-15T19:46:40Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 82 ---------------------------------------------------------\n refs.c          | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 81 insertions(+), 82 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex c8b44ab..14319cb 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -1668,23 +1668,6 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \treturn ret;\n }\n \n-/* The argument to filter_refs */\n-struct ref_filter {\n-\tconst char *pattern;\n-\teach_ref_fn *fn;\n-\tvoid *cb_data;\n-};\n-\n-static int filter_refs(const char *refname, const struct object_id *oid,\n-\t\t\t   int flags, void *data)\n-{\n-\tstruct ref_filter *filter = (struct ref_filter *)data;\n-\n-\tif (wildmatch(filter->pattern, refname, 0, NULL))\n-\t\treturn 0;\n-\treturn filter->fn(refname, oid, flags, filter->cb_data);\n-}\n-\n enum peel_status {\n \t/* object was peeled successfully: */\n \tPEEL_PEELED = 0,\n@@ -1950,37 +1933,6 @@ int for_each_ref_in_submodule(const char *submodule, const char *prefix,\n {\n \treturn do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, 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-}\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-}\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-}\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-}\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-}\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-}\n-\n int for_each_replace_ref(each_ref_fn fn, void *cb_data)\n {\n \treturn do_for_each_ref(&ref_cache, git_replace_ref_base, fn,\n@@ -2012,40 +1964,6 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n \treturn ret;\n }\n \n-int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n-\tconst char *prefix, void *cb_data)\n-{\n-\tstruct strbuf real_pattern = STRBUF_INIT;\n-\tstruct ref_filter filter;\n-\tint ret;\n-\n-\tif (!prefix && !starts_with(pattern, \"refs/\"))\n-\t\tstrbuf_addstr(&real_pattern, \"refs/\");\n-\telse if (prefix)\n-\t\tstrbuf_addstr(&real_pattern, prefix);\n-\tstrbuf_addstr(&real_pattern, pattern);\n-\n-\tif (!has_glob_specials(pattern)) {\n-\t\t/* Append implied '/' '*' if not present. */\n-\t\tstrbuf_complete(&real_pattern, '/');\n-\t\t/* No need to check for '*', there is none. */\n-\t\tstrbuf_addch(&real_pattern, '*');\n-\t}\n-\n-\tfilter.pattern = real_pattern.buf;\n-\tfilter.fn = fn;\n-\tfilter.cb_data = cb_data;\n-\tret = for_each_ref(filter_refs, &filter);\n-\n-\tstrbuf_release(&real_pattern);\n-\treturn ret;\n-}\n-\n-int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)\n-{\n-\treturn for_each_glob_ref_in(fn, pattern, NULL, cb_data);\n-}\n-\n int for_each_rawref(each_ref_fn fn, void *cb_data)\n {\n \treturn do_for_each_ref(&ref_cache, \"\", fn, 0,\ndiff --git a/refs.c b/refs.c\nindex 642a2ab..f7aa9b3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -746,3 +746,84 @@ const char *prettify_refname(const char *name)\n \t\tstarts_with(name, \"refs/remotes/\") ? 13 :\n \t\t0);\n }\n+\n+/* The argument to filter_refs */\n+struct ref_filter {\n+\tconst char *pattern;\n+\teach_ref_fn *fn;\n+\tvoid *cb_data;\n+};\n+\n+static int filter_refs(const char *refname, const struct object_id *oid, int flags,\n+\t\t       void *data)\n+{\n+\tstruct ref_filter *filter = (struct ref_filter *)data;\n+\n+\tif (wildmatch(filter->pattern, refname, 0, NULL))\n+\t\treturn 0;\n+\treturn filter->fn(refname, oid, flags, filter->cb_data);\n+}\n+\n+int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n+\tconst char *prefix, void *cb_data)\n+{\n+\tstruct strbuf real_pattern = STRBUF_INIT;\n+\tstruct ref_filter filter;\n+\tint ret;\n+\n+\tif (!prefix && !starts_with(pattern, \"refs/\"))\n+\t\tstrbuf_addstr(&real_pattern, \"refs/\");\n+\telse if (prefix)\n+\t\tstrbuf_addstr(&real_pattern, prefix);\n+\tstrbuf_addstr(&real_pattern, pattern);\n+\n+\tif (!has_glob_specials(pattern)) {\n+\t\t/* Append implied '/' '*' if not present. */\n+\t\tstrbuf_complete(&real_pattern, '/');\n+\t\t/* No need to check for '*', there is none. */\n+\t\tstrbuf_addch(&real_pattern, '*');\n+\t}\n+\n+\tfilter.pattern = real_pattern.buf;\n+\tfilter.fn = fn;\n+\tfilter.cb_data = cb_data;\n+\tret = for_each_ref(filter_refs, &filter);\n+\n+\tstrbuf_release(&real_pattern);\n+\treturn ret;\n+}\n+\n+int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)\n+{\n+\treturn for_each_glob_ref_in(fn, pattern, NULL, 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+}\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+}\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+}\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+}\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+}\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+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271760","messageId":"1444938410-2345-18-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 17/26] refs.c: move head_ref_namespaced to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:41Z","receivedAt":"2015-10-15T19:46:41Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 15 ---------------\n refs.c          | 15 +++++++++++++++\n 2 files changed, 15 insertions(+), 15 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 14319cb..ffc3813 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -1939,21 +1939,6 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)\n \t\t\t       strlen(git_replace_ref_base), 0, cb_data);\n }\n \n-int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n-{\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint ret = 0;\n-\tstruct object_id oid;\n-\tint flag;\n-\n-\tstrbuf_addf(&buf, \"%sHEAD\", get_git_namespace());\n-\tif (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))\n-\t\tret = fn(buf.buf, &oid, flag, cb_data);\n-\tstrbuf_release(&buf);\n-\n-\treturn ret;\n-}\n-\n int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)\n {\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/refs.c b/refs.c\nindex f7aa9b3..ae518a2 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -827,3 +827,18 @@ int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *c\n {\n \treturn for_each_ref_in_submodule(submodule, \"refs/remotes/\", fn, cb_data);\n }\n+\n+int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint ret = 0;\n+\tstruct object_id oid;\n+\tint flag;\n+\n+\tstrbuf_addf(&buf, \"%sHEAD\", get_git_namespace());\n+\tif (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))\n+\t\tret = fn(buf.buf, &oid, flag, cb_data);\n+\tstrbuf_release(&buf);\n+\n+\treturn ret;\n+}\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271757","messageId":"1444938410-2345-19-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 18/26] refs: move transaction functions into common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:42Z","receivedAt":"2015-10-15T19:46:42Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"The common ref code will build up a ref transaction.  Backends will\nthen commit it.  So the transaction creation and update functions should\nbe in the common code.  We also need to move the ref structs into\nthe common code so that alternate backends can access them.\n\nLater, we will modify struct ref_update to support alternate backends.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n refs-be-files.c | 198 --------------------------------------------------------\n refs.c          | 108 +++++++++++++++++++++++++++++++\n refs.h          |  91 +++++++++++++++++++++++++-\n 3 files changed, 198 insertions(+), 199 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex ffc3813..73eb1e2 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -13,41 +13,6 @@ struct ref_lock {\n \tstruct object_id old_oid;\n };\n \n-/*\n- * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken\n- * refs (i.e., because the reference is about to be deleted anyway).\n- */\n-#define REF_DELETING\t0x02\n-\n-/*\n- * Used as a flag in ref_update::flags when a loose ref is being\n- * pruned.\n- */\n-#define REF_ISPRUNING\t0x04\n-\n-/*\n- * Used as a flag in ref_update::flags when the reference should be\n- * updated to new_sha1.\n- */\n-#define REF_HAVE_NEW\t0x08\n-\n-/*\n- * Used as a flag in ref_update::flags when old_sha1 should be\n- * checked.\n- */\n-#define REF_HAVE_OLD\t0x10\n-\n-/*\n- * Used as a flag in ref_update::flags when the lockfile needs to be\n- * committed.\n- */\n-#define REF_NEEDS_COMMIT 0x20\n-\n-/*\n- * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a\n- * value to ref_update::flags\n- */\n-\n struct ref_entry;\n \n /*\n@@ -3289,169 +3254,6 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)\n \treturn retval;\n }\n \n-/**\n- * Information needed for a single ref update. Set new_sha1 to the new\n- * value or to null_sha1 to delete the ref. To check the old value\n- * while the ref is locked, set (flags & REF_HAVE_OLD) and set\n- * old_sha1 to the old value, or to null_sha1 to ensure the ref does\n- * not exist before update.\n- */\n-struct ref_update {\n-\t/*\n-\t * If (flags & REF_HAVE_NEW), set the reference to this value:\n-\t */\n-\tunsigned char new_sha1[20];\n-\t/*\n-\t * If (flags & REF_HAVE_OLD), check that the reference\n-\t * previously had this value:\n-\t */\n-\tunsigned char old_sha1[20];\n-\t/*\n-\t * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,\n-\t * REF_DELETING, and REF_ISPRUNING:\n-\t */\n-\tunsigned int flags;\n-\tstruct ref_lock *lock;\n-\tint type;\n-\tchar *msg;\n-\tconst char refname[FLEX_ARRAY];\n-};\n-\n-/*\n- * Transaction states.\n- * OPEN:   The transaction is in a valid state and can accept new updates.\n- *         An OPEN transaction can be committed.\n- * CLOSED: A closed transaction is no longer active and no other operations\n- *         than free can be used on it in this state.\n- *         A transaction can either become closed by successfully committing\n- *         an active transaction or if there is a failure while building\n- *         the transaction thus rendering it failed/inactive.\n- */\n-enum ref_transaction_state {\n-\tREF_TRANSACTION_OPEN   = 0,\n-\tREF_TRANSACTION_CLOSED = 1\n-};\n-\n-/*\n- * Data structure for holding a reference transaction, which can\n- * consist of checks and updates to multiple references, carried out\n- * as atomically as possible.  This structure is opaque to callers.\n- */\n-struct ref_transaction {\n-\tstruct ref_update **updates;\n-\tsize_t alloc;\n-\tsize_t nr;\n-\tenum ref_transaction_state state;\n-};\n-\n-struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n-{\n-\tassert(err);\n-\n-\treturn xcalloc(1, sizeof(struct ref_transaction));\n-}\n-\n-void ref_transaction_free(struct ref_transaction *transaction)\n-{\n-\tint i;\n-\n-\tif (!transaction)\n-\t\treturn;\n-\n-\tfor (i = 0; i < transaction->nr; i++) {\n-\t\tfree(transaction->updates[i]->msg);\n-\t\tfree(transaction->updates[i]);\n-\t}\n-\tfree(transaction->updates);\n-\tfree(transaction);\n-}\n-\n-static struct ref_update *add_update(struct ref_transaction *transaction,\n-\t\t\t\t     const char *refname)\n-{\n-\tsize_t len = strlen(refname) + 1;\n-\tstruct ref_update *update = xcalloc(1, sizeof(*update) + len);\n-\n-\tmemcpy((char *)update->refname, refname, len); /* includes NUL */\n-\tALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);\n-\ttransaction->updates[transaction->nr++] = update;\n-\treturn update;\n-}\n-\n-int ref_transaction_update(struct ref_transaction *transaction,\n-\t\t\t   const char *refname,\n-\t\t\t   const unsigned char *new_sha1,\n-\t\t\t   const unsigned char *old_sha1,\n-\t\t\t   unsigned int flags, const char *msg,\n-\t\t\t   struct strbuf *err)\n-{\n-\tstruct ref_update *update;\n-\n-\tassert(err);\n-\n-\tif (transaction->state != REF_TRANSACTION_OPEN)\n-\t\tdie(\"BUG: update called for transaction that is not open\");\n-\n-\tif (new_sha1 && !is_null_sha1(new_sha1) &&\n-\t    check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {\n-\t\tstrbuf_addf(err, \"refusing to update ref with bad name %s\",\n-\t\t\t    refname);\n-\t\treturn -1;\n-\t}\n-\n-\tupdate = add_update(transaction, refname);\n-\tif (new_sha1) {\n-\t\thashcpy(update->new_sha1, new_sha1);\n-\t\tflags |= REF_HAVE_NEW;\n-\t}\n-\tif (old_sha1) {\n-\t\thashcpy(update->old_sha1, old_sha1);\n-\t\tflags |= REF_HAVE_OLD;\n-\t}\n-\tupdate->flags = flags;\n-\tif (msg)\n-\t\tupdate->msg = xstrdup(msg);\n-\treturn 0;\n-}\n-\n-int ref_transaction_create(struct ref_transaction *transaction,\n-\t\t\t   const char *refname,\n-\t\t\t   const unsigned char *new_sha1,\n-\t\t\t   unsigned int flags, const char *msg,\n-\t\t\t   struct strbuf *err)\n-{\n-\tif (!new_sha1 || is_null_sha1(new_sha1))\n-\t\tdie(\"BUG: create called without valid new_sha1\");\n-\treturn ref_transaction_update(transaction, refname, new_sha1,\n-\t\t\t\t      null_sha1, flags, msg, err);\n-}\n-\n-int ref_transaction_delete(struct ref_transaction *transaction,\n-\t\t\t   const char *refname,\n-\t\t\t   const unsigned char *old_sha1,\n-\t\t\t   unsigned int flags, const char *msg,\n-\t\t\t   struct strbuf *err)\n-{\n-\tif (old_sha1 && is_null_sha1(old_sha1))\n-\t\tdie(\"BUG: delete called with old_sha1 set to zeros\");\n-\treturn ref_transaction_update(transaction, refname,\n-\t\t\t\t      null_sha1, old_sha1,\n-\t\t\t\t      flags, msg, err);\n-}\n-\n-int ref_transaction_verify(struct ref_transaction *transaction,\n-\t\t\t   const char *refname,\n-\t\t\t   const unsigned char *old_sha1,\n-\t\t\t   unsigned int flags,\n-\t\t\t   struct strbuf *err)\n-{\n-\tif (!old_sha1)\n-\t\tdie(\"BUG: verify called with old_sha1 set to NULL\");\n-\treturn ref_transaction_update(transaction, refname,\n-\t\t\t\t      NULL, old_sha1,\n-\t\t\t\t      flags, NULL, err);\n-}\n-\n static int ref_update_reject_duplicates(struct string_list *refnames,\n \t\t\t\t\tstruct strbuf *err)\n {\ndiff --git a/refs.c b/refs.c\nindex ae518a2..be08787 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -842,3 +842,111 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n \n \treturn ret;\n }\n+\n+struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+{\n+\tassert(err);\n+\n+\treturn xcalloc(1, sizeof(struct ref_transaction));\n+}\n+\n+void ref_transaction_free(struct ref_transaction *transaction)\n+{\n+\tint i;\n+\n+\tif (!transaction)\n+\t\treturn;\n+\n+\tfor (i = 0; i < transaction->nr; i++) {\n+\t\tfree(transaction->updates[i]->msg);\n+\t\tfree(transaction->updates[i]);\n+\t}\n+\tfree(transaction->updates);\n+\tfree(transaction);\n+}\n+\n+static struct ref_update *add_update(struct ref_transaction *transaction,\n+\t\t\t\t     const char *refname)\n+{\n+\tsize_t len = strlen(refname) + 1;\n+\tstruct ref_update *update = xcalloc(1, sizeof(*update) + len);\n+\n+\tmemcpy((char *)update->refname, refname, len); /* includes NUL */\n+\tALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);\n+\ttransaction->updates[transaction->nr++] = update;\n+\treturn update;\n+}\n+\n+int ref_transaction_update(struct ref_transaction *transaction,\n+\t\t\t   const char *refname,\n+\t\t\t   const unsigned char *new_sha1,\n+\t\t\t   const unsigned char *old_sha1,\n+\t\t\t   unsigned int flags, const char *msg,\n+\t\t\t   struct strbuf *err)\n+{\n+\tstruct ref_update *update;\n+\n+\tassert(err);\n+\n+\tif (transaction->state != REF_TRANSACTION_OPEN)\n+\t\tdie(\"BUG: update called for transaction that is not open\");\n+\n+\tif (new_sha1 && !is_null_sha1(new_sha1) &&\n+\t    check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {\n+\t\tstrbuf_addf(err, \"refusing to update ref with bad name %s\",\n+\t\t\t    refname);\n+\t\treturn -1;\n+\t}\n+\n+\tupdate = add_update(transaction, refname);\n+\tif (new_sha1) {\n+\t\thashcpy(update->new_sha1, new_sha1);\n+\t\tflags |= REF_HAVE_NEW;\n+\t}\n+\tif (old_sha1) {\n+\t\thashcpy(update->old_sha1, old_sha1);\n+\t\tflags |= REF_HAVE_OLD;\n+\t}\n+\tupdate->flags = flags;\n+\tif (msg)\n+\t\tupdate->msg = xstrdup(msg);\n+\treturn 0;\n+}\n+\n+int ref_transaction_create(struct ref_transaction *transaction,\n+\t\t\t   const char *refname,\n+\t\t\t   const unsigned char *new_sha1,\n+\t\t\t   unsigned int flags, const char *msg,\n+\t\t\t   struct strbuf *err)\n+{\n+\tif (!new_sha1 || is_null_sha1(new_sha1))\n+\t\tdie(\"BUG: create called without valid new_sha1\");\n+\treturn ref_transaction_update(transaction, refname, new_sha1,\n+\t\t\t\t      null_sha1, flags, msg, err);\n+}\n+\n+int ref_transaction_delete(struct ref_transaction *transaction,\n+\t\t\t   const char *refname,\n+\t\t\t   const unsigned char *old_sha1,\n+\t\t\t   unsigned int flags, const char *msg,\n+\t\t\t   struct strbuf *err)\n+{\n+\tif (old_sha1 && is_null_sha1(old_sha1))\n+\t\tdie(\"BUG: delete called with old_sha1 set to zeros\");\n+\treturn ref_transaction_update(transaction, refname,\n+\t\t\t\t      null_sha1, old_sha1,\n+\t\t\t\t      flags, msg, err);\n+}\n+\n+int ref_transaction_verify(struct ref_transaction *transaction,\n+\t\t\t   const char *refname,\n+\t\t\t   const unsigned char *old_sha1,\n+\t\t\t   unsigned int flags,\n+\t\t\t   struct strbuf *err)\n+{\n+\tif (!old_sha1)\n+\t\tdie(\"BUG: verify called with old_sha1 set to NULL\");\n+\treturn ref_transaction_update(transaction, refname,\n+\t\t\t\t      NULL, old_sha1,\n+\t\t\t\t      flags, NULL, err);\n+}\ndiff --git a/refs.h b/refs.h\nindex 8408bef..3d24c79 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -94,6 +94,84 @@ extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);\n extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n \n /*\n+ * Transaction states.\n+ * OPEN:   The transaction is in a valid state and can accept new updates.\n+ *         An OPEN transaction can be committed.\n+ * CLOSED: A closed transaction is no longer active and no other operations\n+ *         than free can be used on it in this state.\n+ *         A transaction can either become closed by successfully committing\n+ *         an active transaction or if there is a failure while building\n+ *         the transaction thus rendering it failed/inactive.\n+ */\n+enum ref_transaction_state {\n+\tREF_TRANSACTION_OPEN   = 0,\n+\tREF_TRANSACTION_CLOSED = 1\n+};\n+\n+/*\n+ * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken\n+ * refs (i.e., because the reference is about to be deleted anyway).\n+ */\n+#define REF_DELETING\t0x02\n+\n+/*\n+ * Used as a flag in ref_update::flags when a loose ref is being\n+ * pruned.\n+ */\n+#define REF_ISPRUNING\t0x04\n+\n+/*\n+ * Used as a flag in ref_update::flags when the reference should be\n+ * updated to new_sha1.\n+ */\n+#define REF_HAVE_NEW\t0x08\n+\n+/*\n+ * Used as a flag in ref_update::flags when old_sha1 should be\n+ * checked.\n+ */\n+#define REF_HAVE_OLD\t0x10\n+\n+/*\n+ * Used as a flag in ref_update::flags when the lockfile needs to be\n+ * committed.\n+ */\n+#define REF_NEEDS_COMMIT 0x20\n+\n+/*\n+ * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a\n+ * value to ref_update::flags\n+ */\n+\n+/**\n+ * Information needed for a single ref update. Set new_sha1 to the new\n+ * value or to null_sha1 to delete the ref. To check the old value\n+ * while the ref is locked, set (flags & REF_HAVE_OLD) and set\n+ * old_sha1 to the old value, or to null_sha1 to ensure the ref does\n+ * not exist before update.\n+ */\n+struct ref_update {\n+\t/*\n+\t * If (flags & REF_HAVE_NEW), set the reference to this value:\n+\t */\n+\tunsigned char new_sha1[20];\n+\t/*\n+\t * If (flags & REF_HAVE_OLD), check that the reference\n+\t * previously had this value:\n+\t */\n+\tunsigned char old_sha1[20];\n+\t/*\n+\t * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,\n+\t * REF_DELETING, and REF_ISPRUNING:\n+\t */\n+\tunsigned int flags;\n+\tstruct ref_lock *lock;\n+\tint type;\n+\tchar *msg;\n+\tconst char refname[FLEX_ARRAY];\n+};\n+\n+/*\n  * A ref_transaction represents a collection of ref updates\n  * that should succeed or fail together.\n  *\n@@ -125,7 +203,18 @@ extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n  * The message is appended to err without first clearing err.\n  * err will not be '\\n' terminated.\n  */\n-struct ref_transaction;\n+\n+/*\n+ * Data structure for holding a reference transaction, which can\n+ * consist of checks and updates to multiple references, carried out\n+ * as atomically as possible.  This structure is opaque to callers.\n+ */\n+struct ref_transaction {\n+\tstruct ref_update **updates;\n+\tsize_t alloc;\n+\tsize_t nr;\n+\tenum ref_transaction_state state;\n+};\n \n /*\n  * Bit values set in the flags argument passed to each_ref_fn():\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271755","messageId":"1444938410-2345-20-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 19/26] refs.c: move refname_is_safe to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:43Z","receivedAt":"2015-10-15T19:46:43Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"This function does not contain any backend specific code so we move it\nto the common code. This function might be used by other refs backends.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs-be-files.c | 33 ---------------------------------\n refs.c          | 24 ++++++++++++++++++++++++\n refs.h          | 11 +++++++++++\n 3 files changed, 35 insertions(+), 33 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 73eb1e2..8d966f1 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -196,39 +196,6 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)\n \treturn dir;\n }\n \n-/*\n- * Check if a refname is safe.\n- * For refs that start with \"refs/\" we consider it safe as long they do\n- * not try to resolve to outside of refs/.\n- *\n- * For all other refs we only consider them safe iff they only contain\n- * upper case characters and '_' (like \"HEAD\" AND \"MERGE_HEAD\", and not like\n- * \"config\").\n- */\n-static int refname_is_safe(const char *refname)\n-{\n-\tif (starts_with(refname, \"refs/\")) {\n-\t\tchar *buf;\n-\t\tint result;\n-\n-\t\tbuf = xmalloc(strlen(refname) + 1);\n-\t\t/*\n-\t\t * Does the refname try to escape refs/?\n-\t\t * For example: refs/foo/../bar is safe but refs/foo/../../bar\n-\t\t * is not.\n-\t\t */\n-\t\tresult = !normalize_path_copy(buf, refname + strlen(\"refs/\"));\n-\t\tfree(buf);\n-\t\treturn result;\n-\t}\n-\twhile (*refname) {\n-\t\tif (!isupper(*refname) && *refname != '_')\n-\t\t\treturn 0;\n-\t\trefname++;\n-\t}\n-\treturn 1;\n-}\n-\n static struct ref_entry *create_ref_entry(const char *refname,\n \t\t\t\t\t  const unsigned char *sha1, int flag,\n \t\t\t\t\t  int check_name)\ndiff --git a/refs.c b/refs.c\nindex be08787..3eb3a28 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -950,3 +950,27 @@ int ref_transaction_verify(struct ref_transaction *transaction,\n \t\t\t\t      NULL, old_sha1,\n \t\t\t\t      flags, NULL, err);\n }\n+\n+int refname_is_safe(const char *refname)\n+{\n+\tif (starts_with(refname, \"refs/\")) {\n+\t\tchar *buf;\n+\t\tint result;\n+\n+\t\tbuf = xmalloc(strlen(refname) + 1);\n+\t\t/*\n+\t\t * Does the refname try to escape refs/?\n+\t\t * For example: refs/foo/../bar is safe but refs/foo/../../bar\n+\t\t * is not.\n+\t\t */\n+\t\tresult = !normalize_path_copy(buf, refname + strlen(\"refs/\"));\n+\t\tfree(buf);\n+\t\treturn result;\n+\t}\n+\twhile (*refname) {\n+\t\tif (!isupper(*refname) && *refname != '_')\n+\t\t\treturn 0;\n+\t\trefname++;\n+\t}\n+\treturn 1;\n+}\ndiff --git a/refs.h b/refs.h\nindex 3d24c79..be94e18 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -396,6 +396,17 @@ extern int for_each_reflog(each_ref_fn, void *);\n  */\n extern int check_refname_format(const char *refname, int flags);\n \n+/*\n+ * Check if a refname is safe.\n+ * For refs that start with \"refs/\" we consider it safe as long they do\n+ * not try to resolve to outside of refs/.\n+ *\n+ * For all other refs we only consider them safe iff they only contain\n+ * upper case characters and '_' (like \"HEAD\" AND \"MERGE_HEAD\", and not like\n+ * \"config\").\n+ */\n+extern int refname_is_safe(const char *refname);\n+\n extern const char *prettify_refname(const char *refname);\n \n extern char *shorten_unambiguous_ref(const char *refname, int strict);\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271762","messageId":"1444938410-2345-21-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 20/26] refs.c: move copy_msg to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:44Z","receivedAt":"2015-10-15T19:46:44Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Rename copy_msg to copy_reflog_msg and make it public.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs-be-files.c | 28 +---------------------------\n refs.c          | 21 +++++++++++++++++++++\n refs.h          |  7 +++++++\n 3 files changed, 29 insertions(+), 27 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 8d966f1..4b6bf29 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -2654,32 +2654,6 @@ static int commit_ref(struct ref_lock *lock)\n \treturn 0;\n }\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- */\n-static int copy_msg(char *buf, const char *msg)\n-{\n-\tchar *cp = buf;\n-\tchar c;\n-\tint wasspace = 1;\n-\n-\t*cp++ = '\\t';\n-\twhile ((c = *msg++)) {\n-\t\tif (wasspace && isspace(c))\n-\t\t\tcontinue;\n-\t\twasspace = isspace(c);\n-\t\tif (wasspace)\n-\t\t\tc = ' ';\n-\t\t*cp++ = c;\n-\t}\n-\twhile (buf < cp && isspace(cp[-1]))\n-\t\tcp--;\n-\t*cp++ = '\\n';\n-\treturn cp - buf;\n-}\n-\n static int should_autocreate_reflog(const char *refname)\n {\n \tif (!log_all_ref_updates)\n@@ -2774,7 +2748,7 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \t\t\tsha1_to_hex(new_sha1),\n \t\t\tcommitter);\n \tif (msglen)\n-\t\tlen += copy_msg(logrec + len - 1, msg) - 1;\n+\t\tlen += copy_reflog_msg(logrec + len - 1, msg) - 1;\n \n \twritten = len <= maxlen ? write_in_full(fd, logrec, len) : -1;\n \tfree(logrec);\ndiff --git a/refs.c b/refs.c\nindex 3eb3a28..16280d5 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -828,6 +828,27 @@ int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *c\n \treturn for_each_ref_in_submodule(submodule, \"refs/remotes/\", fn, cb_data);\n }\n \n+int copy_reflog_msg(char *buf, const char *msg)\n+{\n+\tchar *cp = buf;\n+\tchar c;\n+\tint wasspace = 1;\n+\n+\t*cp++ = '\\t';\n+\twhile ((c = *msg++)) {\n+\t\tif (wasspace && isspace(c))\n+\t\t\tcontinue;\n+\t\twasspace = isspace(c);\n+\t\tif (wasspace)\n+\t\t\tc = ' ';\n+\t\t*cp++ = c;\n+\t}\n+\twhile (buf < cp && isspace(cp[-1]))\n+\t\tcp--;\n+\t*cp++ = '\\n';\n+\treturn cp - buf;\n+}\n+\n int head_ref_namespaced(each_ref_fn fn, void *cb_data)\n {\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/refs.h b/refs.h\nindex be94e18..fc0611d 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -576,6 +576,13 @@ enum ref_type {\n \n enum ref_type ref_type(const char *refname);\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+ */\n+int copy_reflog_msg(char *buf, const char *msg);\n+\n enum expire_reflog_flags {\n \tEXPIRE_REFLOGS_DRY_RUN = 1 << 0,\n \tEXPIRE_REFLOGS_UPDATE_REF = 1 << 1,\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271758","messageId":"1444938410-2345-22-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 21/26] refs.c: move peel_object to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:45Z","receivedAt":"2015-10-15T19:46:45Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"This function does not contain any backend specific code so we\nmove it to the common code.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs-be-files.c | 53 -----------------------------------------------------\n refs.c          | 23 +++++++++++++++++++++++\n refs.h          | 34 ++++++++++++++++++++++++++++++++++\n 3 files changed, 57 insertions(+), 53 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 4b6bf29..ef600d8 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -1600,59 +1600,6 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \treturn ret;\n }\n \n-enum peel_status {\n-\t/* object was peeled successfully: */\n-\tPEEL_PEELED = 0,\n-\n-\t/*\n-\t * object cannot be peeled because the named object (or an\n-\t * object referred to by a tag in the peel chain), does not\n-\t * exist.\n-\t */\n-\tPEEL_INVALID = -1,\n-\n-\t/* object cannot be peeled because it is not a tag: */\n-\tPEEL_NON_TAG = -2,\n-\n-\t/* ref_entry contains no peeled value because it is a symref: */\n-\tPEEL_IS_SYMREF = -3,\n-\n-\t/*\n-\t * ref_entry cannot be peeled because it is broken (i.e., the\n-\t * symbolic reference cannot even be resolved to an object\n-\t * name):\n-\t */\n-\tPEEL_BROKEN = -4\n-};\n-\n-/*\n- * Peel the named object; i.e., if the object is a tag, resolve the\n- * tag recursively until a non-tag is found.  If successful, store the\n- * result to sha1 and return PEEL_PEELED.  If the object is not a tag\n- * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,\n- * and leave sha1 unchanged.\n- */\n-static enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)\n-{\n-\tstruct object *o = lookup_unknown_object(name);\n-\n-\tif (o->type == OBJ_NONE) {\n-\t\tint type = sha1_object_info(name, NULL);\n-\t\tif (type < 0 || !object_as_type(o, type, 0))\n-\t\t\treturn PEEL_INVALID;\n-\t}\n-\n-\tif (o->type != OBJ_TAG)\n-\t\treturn PEEL_NON_TAG;\n-\n-\to = deref_tag_noverify(o);\n-\tif (!o)\n-\t\treturn PEEL_INVALID;\n-\n-\thashcpy(sha1, o->sha1);\n-\treturn PEEL_PEELED;\n-}\n-\n /*\n  * Peel the entry (if possible) and return its new peel_status.  If\n  * repeel is true, re-peel the entry even if there is an old peeled\ndiff --git a/refs.c b/refs.c\nindex 16280d5..e7f6c77 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -4,6 +4,8 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"lockfile.h\"\n+#include \"object.h\"\n+#include \"tag.h\"\n \n static int is_per_worktree_ref(const char *refname)\n {\n@@ -995,3 +997,24 @@ int refname_is_safe(const char *refname)\n \t}\n \treturn 1;\n }\n+\n+enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)\n+{\n+\tstruct object *o = lookup_unknown_object(name);\n+\n+\tif (o->type == OBJ_NONE) {\n+\t\tint type = sha1_object_info(name, NULL);\n+\t\tif (type < 0 || !object_as_type(o, type, 0))\n+\t\t\treturn PEEL_INVALID;\n+\t}\n+\n+\tif (o->type != OBJ_TAG)\n+\t\treturn PEEL_NON_TAG;\n+\n+\to = deref_tag_noverify(o);\n+\tif (!o)\n+\t\treturn PEEL_INVALID;\n+\n+\thashcpy(sha1, o->sha1);\n+\treturn PEEL_PEELED;\n+}\ndiff --git a/refs.h b/refs.h\nindex fc0611d..3fb480b 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -76,6 +76,40 @@ extern int is_branch(const char *refname);\n  */\n extern int peel_ref(const char *refname, unsigned char *sha1);\n \n+enum peel_status {\n+\t/* object was peeled successfully: */\n+\tPEEL_PEELED = 0,\n+\n+\t/*\n+\t * object cannot be peeled because the named object (or an\n+\t * object referred to by a tag in the peel chain), does not\n+\t * exist.\n+\t */\n+\tPEEL_INVALID = -1,\n+\n+\t/* object cannot be peeled because it is not a tag: */\n+\tPEEL_NON_TAG = -2,\n+\n+\t/* ref_entry contains no peeled value because it is a symref: */\n+\tPEEL_IS_SYMREF = -3,\n+\n+\t/*\n+\t * ref_entry cannot be peeled because it is broken (i.e., the\n+\t * symbolic reference cannot even be resolved to an object\n+\t * name):\n+\t */\n+\tPEEL_BROKEN = -4\n+};\n+\n+/*\n+ * Peel the named object; i.e., if the object is a tag, resolve the\n+ * tag recursively until a non-tag is found.  If successful, store the\n+ * result to sha1 and return PEEL_PEELED.  If the object is not a tag\n+ * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,\n+ * and leave sha1 unchanged.\n+ */\n+enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);\n+\n /**\n  * Resolve refname in the nested \"gitlink\" repository that is located\n  * at path.  If the resolution is successful, return 0 and set sha1 to\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271761","messageId":"1444938410-2345-23-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 22/26] refs.c: move should_autocreate_reflog to common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:46Z","receivedAt":"2015-10-15T19:46:46Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Signed-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs-be-files.c | 10 ----------\n refs.c          | 10 ++++++++++\n refs.h          |  2 ++\n 3 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex ef600d8..7c39afa 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -2601,16 +2601,6 @@ static int commit_ref(struct ref_lock *lock)\n \treturn 0;\n }\n \n-static int should_autocreate_reflog(const char *refname)\n-{\n-\tif (!log_all_ref_updates)\n-\t\treturn 0;\n-\treturn starts_with(refname, \"refs/heads/\") ||\n-\t\tstarts_with(refname, \"refs/remotes/\") ||\n-\t\tstarts_with(refname, \"refs/notes/\") ||\n-\t\t!strcmp(refname, \"HEAD\");\n-}\n-\n int verify_refname_available(const char *newname, struct string_list *extra,\n \t\t\t     struct string_list *skip, struct strbuf *err)\n {\ndiff --git a/refs.c b/refs.c\nindex e7f6c77..efc1c47 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -626,6 +626,16 @@ char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t\t\t\t\t  sha1, flags));\n }\n \n+int should_autocreate_reflog(const char *refname)\n+{\n+\tif (!log_all_ref_updates)\n+\t\treturn 0;\n+\treturn starts_with(refname, \"refs/heads/\") ||\n+\t\tstarts_with(refname, \"refs/remotes/\") ||\n+\t\tstarts_with(refname, \"refs/notes/\") ||\n+\t\t!strcmp(refname, \"HEAD\");\n+}\n+\n /*\n  * How to handle various characters in refnames:\n  * 0: An acceptable character for refs\ndiff --git a/refs.h b/refs.h\nindex 3fb480b..3fce9c9 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -58,6 +58,8 @@ extern const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n extern char *resolve_refdup(const char *refname, int resolve_flags,\n \t\t\t    unsigned char *sha1, int *flags);\n \n+extern int should_autocreate_reflog(const char *refname);\n+\n extern int read_ref_full(const char *refname, int resolve_flags,\n \t\t\t unsigned char *sha1, int *flags);\n extern int read_ref(const char *refname, unsigned char *sha1);\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271759","messageId":"1444938410-2345-24-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 23/26] initdb: move safe_create_dir into common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:47Z","receivedAt":"2015-10-15T19:46:47Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"In a moment, we'll create initdb functions for ref backends, and code\nfrom initdb that calls this function needs to move into the files\nbackend.  So this function needs to be public.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/init-db.c | 12 ------------\n cache.h           |  5 +++++\n path.c            | 12 ++++++++++++\n 3 files changed, 17 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex f59f407..07229d6 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -24,18 +24,6 @@ static int init_shared_repository = -1;\n static const char *init_db_template_dir;\n static const char *git_link;\n \n-static void safe_create_dir(const char *dir, int share)\n-{\n-\tif (mkdir(dir, 0777) < 0) {\n-\t\tif (errno != EEXIST) {\n-\t\t\tperror(dir);\n-\t\t\texit(1);\n-\t\t}\n-\t}\n-\telse if (share && adjust_shared_perm(dir))\n-\t\tdie(_(\"Could not make %s writable by group\"), dir);\n-}\n-\n static void copy_templates_1(struct strbuf *path, struct strbuf *template,\n \t\t\t     DIR *dir)\n {\ndiff --git a/cache.h b/cache.h\nindex 9a905a8..1d8a051 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1737,4 +1737,9 @@ void stat_validity_update(struct stat_validity *sv, int fd);\n int versioncmp(const char *s1, const char *s2);\n void sleep_millisec(int millisec);\n \n+/*\n+ * Create a directory and (if share is nonzero) adjust its permissions\n+ * according to the shared_repository setting.\n+ */\n+void safe_create_dir(const char *dir, int share);\n #endif /* CACHE_H */\ndiff --git a/path.c b/path.c\nindex 212695a..9e0283c 100644\n--- a/path.c\n+++ b/path.c\n@@ -723,6 +723,18 @@ int adjust_shared_perm(const char *path)\n \treturn 0;\n }\n \n+void safe_create_dir(const char *dir, int share)\n+{\n+\tif (mkdir(dir, 0777) < 0) {\n+\t\tif (errno != EEXIST) {\n+\t\t\tperror(dir);\n+\t\t\texit(1);\n+\t\t}\n+\t}\n+\telse if (share && adjust_shared_perm(dir))\n+\t\tdie(_(\"Could not make %s writable by group\"), dir);\n+}\n+\n static int have_same_root(const char *path1, const char *path2)\n {\n \tint is_abs1, is_abs2;\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271756","messageId":"1444938410-2345-25-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 24/26] refs: make files_log_ref_write functions public","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:48Z","receivedAt":"2015-10-15T19:46:48Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Because HEAD and stash are per-worktree, refs.c needs to go through\nthe files backend to write these refs.\n\nIn this patch, we make one files backend internal functions\npublic. Later, we will use this to handle reflog updates for\nper-worktree symbolic refs (HEAD).\n\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs-be-files.c | 8 ++++++++\n refs.h          | 5 +++++\n 2 files changed, 13 insertions(+)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 7c39afa..1bda3e4 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -2733,6 +2733,14 @@ static int 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+\treturn files_log_ref_write(refname, old_sha1, new_sha1, msg, flags,\n+\t\t\t\t   err);\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 \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);\ndiff --git a/refs.h b/refs.h\nindex 3fce9c9..7aed0a2 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -619,6 +619,11 @@ enum ref_type ref_type(const char *refname);\n  */\n int copy_reflog_msg(char *buf, const char *msg);\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 enum expire_reflog_flags {\n \tEXPIRE_REFLOGS_DRY_RUN = 1 << 0,\n \tEXPIRE_REFLOGS_UPDATE_REF = 1 << 1,\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271766","messageId":"1444938410-2345-26-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 25/26] refs: break out ref conflict checks","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:49Z","receivedAt":"2015-10-15T19:46:49Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Create new function verify_no_descendants, to hold one of the ref\nconflict checks used in verify_refname_available.  Multiple backends\nwill need this function, so it goes in the common code.\n\nrename_ref_available also moves to the common code, because alternate\nbackends might need it and it has no files-backend-specific code.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs-be-files.c | 49 ++++++++-----------------------------------------\n refs.c          | 38 ++++++++++++++++++++++++++++++++++++++\n refs.h          | 15 +++++++++++++++\n 3 files changed, 61 insertions(+), 41 deletions(-)\n\ndiff --git a/refs-be-files.c b/refs-be-files.c\nindex 1bda3e4..1f17348 100644\n--- a/refs-be-files.c\n+++ b/refs-be-files.c\n@@ -729,6 +729,7 @@ static int verify_refname_available_dir(const char *refname,\n \t\t\t\t\tstruct strbuf *err)\n {\n \tconst char *slash;\n+\tconst char *extra_refname;\n \tint pos;\n \tstruct strbuf dirname = STRBUF_INIT;\n \tint ret = -1;\n@@ -834,33 +835,15 @@ static int verify_refname_available_dir(const char *refname,\n \t\t}\n \t}\n \n-\tif (extras) {\n-\t\t/*\n-\t\t * Check for entries in extras that start with\n-\t\t * \"$refname/\". We do that by looking for the place\n-\t\t * where \"$refname/\" would be inserted in extras. If\n-\t\t * there is an entry at that position that starts with\n-\t\t * \"$refname/\" and is not in skip, then we have a\n-\t\t * conflict.\n-\t\t */\n-\t\tfor (pos = string_list_find_insert_index(extras, dirname.buf, 0);\n-\t\t     pos < extras->nr; pos++) {\n-\t\t\tconst char *extra_refname = extras->items[pos].string;\n-\n-\t\t\tif (!starts_with(extra_refname, dirname.buf))\n-\t\t\t\tbreak;\n-\n-\t\t\tif (!skip || !string_list_has_string(skip, extra_refname)) {\n-\t\t\t\tstrbuf_addf(err, \"cannot process '%s' and '%s' at the same time\",\n-\t\t\t\t\t    refname, extra_refname);\n-\t\t\t\tgoto cleanup;\n-\t\t\t}\n-\t\t}\n+\textra_refname = find_descendant_ref(dirname.buf, extras, skip);\n+\tif (extra_refname) {\n+\t\tstrbuf_addf(err,\n+\t\t\t    \"cannot process '%s' and '%s' at the same time\",\n+\t\t\t    refname, extra_refname);\n+\t} else {\n+\t\tret = 0;\n \t}\n \n-\t/* No conflicts were found */\n-\tret = 0;\n-\n cleanup:\n \tstrbuf_release(&dirname);\n \treturn ret;\n@@ -2461,22 +2444,6 @@ out:\n \treturn ret;\n }\n \n-static int rename_ref_available(const char *oldname, const char *newname)\n-{\n-\tstruct string_list skip = STRING_LIST_INIT_NODUP;\n-\tstruct strbuf err = STRBUF_INIT;\n-\tint ret;\n-\n-\tstring_list_insert(&skip, oldname);\n-\tret = !verify_refname_available(newname, NULL, &skip, &err);\n-\tif (!ret)\n-\t\terror(\"%s\", err.buf);\n-\n-\tstring_list_clear(&skip, 0);\n-\tstrbuf_release(&err);\n-\treturn ret;\n-}\n-\n static int write_ref_to_lockfile(struct ref_lock *lock,\n \t\t\t\t const unsigned char *sha1, struct strbuf *err);\n static int commit_ref_update(struct ref_lock *lock,\ndiff --git a/refs.c b/refs.c\nindex efc1c47..72d96ed 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1028,3 +1028,41 @@ enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)\n \thashcpy(sha1, o->sha1);\n \treturn PEEL_PEELED;\n }\n+\n+const char *find_descendant_ref(const char *dirname,\n+\t\t\t\tconst struct string_list *extras,\n+\t\t\t\tconst struct string_list *skip)\n+{\n+\tint pos;\n+\tif (!extras)\n+\t\treturn NULL;\n+\n+\t/* Look for the place where dirname would be inserted in extras. */\n+\tfor (pos = string_list_find_insert_index(extras, dirname, 0);\n+\t     pos < extras->nr; pos++) {\n+\t\tconst char *extra_refname = extras->items[pos].string;\n+\n+\t\tif (!starts_with(extra_refname, dirname))\n+\t\t\tbreak;\n+\n+\t\tif (!skip || !string_list_has_string(skip, extra_refname))\n+\t\t\treturn extra_refname;\n+\t}\n+\treturn NULL;\n+}\n+\n+int rename_ref_available(const char *oldname, const char *newname)\n+{\n+\tstruct string_list skip = STRING_LIST_INIT_NODUP;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tint ret;\n+\n+\tstring_list_insert(&skip, oldname);\n+\tret = !verify_refname_available(newname, NULL, &skip, &err);\n+\tif (!ret)\n+\t\terror(\"%s\", err.buf);\n+\n+\tstring_list_clear(&skip, 0);\n+\tstrbuf_release(&err);\n+\treturn ret;\n+}\ndiff --git a/refs.h b/refs.h\nindex 7aed0a2..69fa4df 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -362,6 +362,8 @@ int pack_refs(unsigned int flags);\n int verify_refname_available(const char *newname, struct string_list *extra,\n \t\t\t     struct string_list *skip, struct strbuf *err);\n \n+int rename_ref_available(const char *oldname, const char *newname);\n+\n extern int is_branch(const char *refname);\n \n /*\n@@ -623,6 +625,19 @@ 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+ * the trailing slash (e.g., \"refs/heads/master/\"). Ignore any\n+ * conflicting references that are found in skip. If there is a\n+ * conflicting reference, return its name.\n+ *\n+ * extras and skip must be sorted lists of reference names. skip can\n+ * be NULL; extras cannot.\n+ */\n+const char *find_descendant_ref(const char *dirname,\n+\t\t\t\tconst struct string_list *extras,\n+\t\t\t\tconst struct string_list *skip);\n \n enum expire_reflog_flags {\n \tEXPIRE_REFLOGS_DRY_RUN = 1 << 0,\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271763","messageId":"1444938410-2345-27-git-send-email-dturner@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-1-git-send-email-dturner@twopensource.com","subject":"[PATCH v4 26/26] introduce \"extensions\" form of core.repositoryformatversion","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-15T19:46:50Z","receivedAt":"2015-10-15T19:46:50Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nNormally we try to avoid bumps of the whole-repository\ncore.repositoryformatversion field. However, it is\nunavoidable if we want to safely change certain aspects of\ngit in a backwards-incompatible way (e.g., modifying the set\nof ref tips that we must traverse to generate a list of\nunreachable, safe-to-prune objects).\n\nIf we were to bump the repository version for every such\nchange, then any implementation understanding version `X`\nwould also have to understand `X-1`, `X-2`, and so forth,\neven though the incompatibilities may be in orthogonal parts\nof the system, and there is otherwise no reason we cannot\nimplement one without the other (or more importantly, that\nthe user cannot choose to use one feature without the other,\nweighing the tradeoff in compatibility only for that\nparticular feature).\n\nThis patch documents the existing repositoryformatversion\nstrategy and introduces a new format, \"1\", which lets a\nrepository specify that it must run with an arbitrary set of\nextensions. This can be used, for example:\n\n - to inform git that the objects should not be pruned based\n   only on the reachability of the ref tips (e.g, because it\n   has \"clone --shared\" children)\n\n - that the refs are stored in a format besides the usual\n   \"refs\" and \"packed-refs\" directories\n\nBecause we bump to format \"1\", and because format \"1\"\nrequires that a running git knows about any extensions\nmentioned, we know that older versions of the code will not\ndo something dangerous when confronted with these new\nformats.\n\nFor example, if the user chooses to use database storage for\nrefs, they may set the \"extensions.refbackend\" config to\n\"db\". Older versions of git will not understand format \"1\"\nand bail. Versions of git which understand \"1\" but do not\nknow about \"refbackend\", or which know about \"refbackend\"\nbut not about the \"db\" backend, will refuse to run. This is\nannoying, of course, but much better than the alternative of\nclaiming that there are no refs in the repository, or\nwriting to a location that other implementations will not\nread.\n\nNote that we are only defining the rules for format 1 here.\nWe do not ever write format 1 ourselves; it is a tool that\nis meant to be used by users and future extensions to\nprovide safety with older implementations.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/technical/repository-version.txt | 81 ++++++++++++++++++++++++++\n cache.h                                        |  6 ++\n setup.c                                        | 37 +++++++++++-\n t/t1302-repo-version.sh                        | 38 ++++++++++++\n 4 files changed, 159 insertions(+), 3 deletions(-)\n create mode 100644 Documentation/technical/repository-version.txt\n\ndiff --git a/Documentation/technical/repository-version.txt b/Documentation/technical/repository-version.txt\nnew file mode 100644\nindex 0000000..3d7106d\n--- /dev/null\n+++ b/Documentation/technical/repository-version.txt\n@@ -0,0 +1,81 @@\n+Git Repository Format Versions\n+==============================\n+\n+Every git repository is marked with a numeric version in the\n+`core.repositoryformatversion` key of its `config` file. This version\n+specifies the rules for operating on the on-disk repository data. An\n+implementation of git which does not understand a particular version\n+advertised by an on-disk repository MUST NOT operate on that repository;\n+doing so risks not only producing wrong results, but actually losing\n+data.\n+\n+Because of this rule, version bumps should be kept to an absolute\n+minimum. Instead, we generally prefer these strategies:\n+\n+  - bumping format version numbers of individual data files (e.g.,\n+    index, packfiles, etc). This restricts the incompatibilities only to\n+    those files.\n+\n+  - introducing new data that gracefully degrades when used by older\n+    clients (e.g., pack bitmap files are ignored by older clients, which\n+    simply do not take advantage of the optimization they provide).\n+\n+A whole-repository format version bump should only be part of a change\n+that cannot be independently versioned. For instance, if one were to\n+change the reachability rules for objects, or the rules for locking\n+refs, that would require a bump of the repository format version.\n+\n+Note that this applies only to accessing the repository's disk contents\n+directly. An older client which understands only format `0` may still\n+connect via `git://` to a repository using format `1`, as long as the\n+server process understands format `1`.\n+\n+The preferred strategy for rolling out a version bump (whether whole\n+repository or for a single file) is to teach git to read the new format,\n+and allow writing the new format with a config switch or command line\n+option (for experimentation or for those who do not care about backwards\n+compatibility with older gits). Then after a long period to allow the\n+reading capability to become common, we may switch to writing the new\n+format by default.\n+\n+The currently defined format versions are:\n+\n+Version `0`\n+-----------\n+\n+This is the format defined by the initial version of git, including but\n+not limited to the format of the repository directory, the repository\n+configuration file, and the object and ref storage. Specifying the\n+complete behavior of git is beyond the scope of this document.\n+\n+Version `1`\n+-----------\n+\n+This format is identical to version `0`, with the following exceptions:\n+\n+  1. When reading the `core.repositoryformatversion` variable, a git\n+     implementation which supports version 1 MUST also read any\n+     configuration keys found in the `extensions` section of the\n+     configuration file.\n+\n+  2. If a version-1 repository specifies any `extensions.*` keys that\n+     the running git has not implemented, the operation MUST NOT\n+     proceed. Similarly, if the value of any known key is not understood\n+     by the implementation, the operation MUST NOT proceed.\n+\n+Note that if no extensions are specified in the config file, then\n+`core.repositoryformatversion` SHOULD be set to `0` (setting it to `1`\n+provides no benefit, and makes the repository incompatible with older\n+implementations of git).\n+\n+This document will serve as the master list for extensions. Any\n+implementation wishing to define a new extension should make a note of\n+it here, in order to claim the name.\n+\n+The defined extensions are:\n+\n+`noop`\n+~~~~~~\n+\n+This extension does not change git's behavior at all. It is useful only\n+for testing format-1 compatibility.\ndiff --git a/cache.h b/cache.h\nindex 1d8a051..4b03cb3 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -696,7 +696,13 @@ extern char *notes_ref_name;\n \n extern int grafts_replace_parents;\n \n+/*\n+ * GIT_REPO_VERSION is the version we write by default. The\n+ * _READ variant is the highest number we know how to\n+ * handle.\n+ */\n #define GIT_REPO_VERSION 0\n+#define GIT_REPO_VERSION_READ 1\n extern int repository_format_version;\n extern int check_repository_format(void);\n \ndiff --git a/setup.c b/setup.c\nindex 2b64cbb..0c29469 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -5,6 +5,7 @@\n static int inside_git_dir = -1;\n static int inside_work_tree = -1;\n static int work_tree_config_is_bogus;\n+static struct string_list unknown_extensions = STRING_LIST_INIT_DUP;\n \n /*\n  * The input parameter must contain an absolute path, and it must already be\n@@ -349,10 +350,23 @@ void setup_work_tree(void)\n \n static int check_repo_format(const char *var, const char *value, void *cb)\n {\n+\tconst char *ext;\n+\n \tif (strcmp(var, \"core.repositoryformatversion\") == 0)\n \t\trepository_format_version = git_config_int(var, value);\n \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n \t\tshared_repository = git_config_perm(var, value);\n+\telse if (skip_prefix(var, \"extensions.\", &ext)) {\n+\t\t/*\n+\t\t * record any known extensions here; otherwise,\n+\t\t * we fall through to recording it as unknown, and\n+\t\t * check_repository_format will complain\n+\t\t */\n+\t\tif (!strcmp(ext, \"noop\"))\n+\t\t\t;\n+\t\telse\n+\t\t\tstring_list_append(&unknown_extensions, ext);\n+\t}\n \treturn 0;\n }\n \n@@ -363,6 +377,8 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n \tconfig_fn_t fn;\n \tint ret = 0;\n \n+\tstring_list_clear(&unknown_extensions, 0);\n+\n \tif (get_common_dir(&sb, gitdir))\n \t\tfn = check_repo_format;\n \telse\n@@ -380,16 +396,31 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n \t * is a good one.\n \t */\n \tgit_config_early(fn, NULL, repo_config);\n-\tif (GIT_REPO_VERSION < repository_format_version) {\n+\tif (GIT_REPO_VERSION_READ < repository_format_version) {\n \t\tif (!nongit_ok)\n \t\t\tdie (\"Expected git repo version <= %d, found %d\",\n-\t\t\t     GIT_REPO_VERSION, repository_format_version);\n+\t\t\t     GIT_REPO_VERSION_READ, repository_format_version);\n \t\twarning(\"Expected git repo version <= %d, found %d\",\n-\t\t\tGIT_REPO_VERSION, repository_format_version);\n+\t\t\tGIT_REPO_VERSION_READ, repository_format_version);\n \t\twarning(\"Please upgrade Git\");\n \t\t*nongit_ok = -1;\n \t\tret = -1;\n \t}\n+\n+\tif (repository_format_version >= 1 && unknown_extensions.nr) {\n+\t\tint i;\n+\n+\t\tif (!nongit_ok)\n+\t\t\tdie(\"unknown repository extension: %s\",\n+\t\t\t    unknown_extensions.items[0].string);\n+\n+\t\tfor (i = 0; i < unknown_extensions.nr; i++)\n+\t\t\twarning(\"unknown repository extension: %s\",\n+\t\t\t\tunknown_extensions.items[i].string);\n+\t\t*nongit_ok = -1;\n+\t\tret = -1;\n+\t}\n+\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh\nindex 0d9388a..8dd6fd7 100755\n--- a/t/t1302-repo-version.sh\n+++ b/t/t1302-repo-version.sh\n@@ -67,4 +67,42 @@ test_expect_success 'gitdir required mode' '\n \t)\n '\n \n+check_allow () {\n+\tgit rev-parse --git-dir >actual &&\n+\techo .git >expect &&\n+\ttest_cmp expect actual\n+}\n+\n+check_abort () {\n+\ttest_must_fail git rev-parse --git-dir\n+}\n+\n+# avoid git-config, since it cannot be trusted to run\n+# in a repository with a broken version\n+mkconfig () {\n+\techo '[core]' &&\n+\techo \"repositoryformatversion = $1\" &&\n+\tshift &&\n+\n+\tif test $# -gt 0; then\n+\t\techo '[extensions]' &&\n+\t\tfor i in \"$@\"; do\n+\t\t\techo \"$i\"\n+\t\tdone\n+\tfi\n+}\n+\n+while read outcome version extensions; do\n+\ttest_expect_success \"$outcome version=$version $extensions\" \"\n+\t\tmkconfig $version $extensions >.git/config &&\n+\t\tcheck_${outcome}\n+\t\"\n+done <<\\EOF\n+allow 0\n+allow 1\n+allow 1 noop\n+abort 1 no-such-extension\n+allow 0 no-such-extension\n+EOF\n+\n test_done\n-- \n2.4.2.644.g97b850b-twtrsrc\n"},{"id":"271795","messageId":"56209A81.1060706@alum.mit.edu","threadId":"40568","inReplyTo":"1444938410-2345-3-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH v4 02/26] refs: make repack_without_refs and is_branch public","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-10-16T06:34:41Z","receivedAt":"2015-10-16T06:34:41Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 10/15/2015 09:46 PM, David Turner wrote:\n> is_branch was already non-static, but this patch declares it in the\n> header.\n\nThe commit message no longer reflects the patch.\n\n> Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>\n> Signed-off-by: David Turner <dturner@twopensource.com>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  refs.c | 5 +++--\n>  refs.h | 2 ++\n>  2 files changed, 5 insertions(+), 2 deletions(-)\n> \n> diff --git a/refs.c b/refs.c\n> index fe71ea0..84abc82 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -2816,8 +2816,9 @@ int pack_refs(unsigned int flags)\n>  \n>  /*\n>   * Rewrite the packed-refs file, omitting any refs listed in\n> - * 'refnames'. On error, leave packed-refs unchanged, write an error\n> - * message to 'err', and return a nonzero value.\n> + * 'refnames'. On error, packed-refs will be unchanged, the return\n> + * value is nonzero, and a message about the error is written to the\n> + * 'err' strbuf.\n\n^^^ ?\n\nIt is preferable for docstrings to be written in imperative form, so in\nmy opinion this is a step backwards...\n\n...literally. Your \"new\" version comes from an older version of Git; it\nwas changed in\n\n    79e4d8a9b8 repack_without_refs(): make function private (2015-06-22)\n\nto the imperative form.\n\nAssuming you are using `git-format-patch` to prepare your patches, it is\nalways a good idea to read over the prepared email files before sending\nthem to the ML, to check for bloopers like this.\n\n>   *\n>   * The refs in 'refnames' needn't be sorted. `err` must not be NULL.\n>   */\n> diff --git a/refs.h b/refs.h\n> index 7367a7f..8408bef 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -237,6 +237,8 @@ int pack_refs(unsigned int flags);\n>  int verify_refname_available(const char *newname, struct string_list *extra,\n>  \t\t\t     struct string_list *skip, struct strbuf *err);\n>  \n> +extern int is_branch(const char *refname);\n> +\n>  /*\n>   * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.\n>   * REF_NODEREF: act on the ref directly, instead of dereferencing\n> \n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"271796","messageId":"56209B06.6000900@alum.mit.edu","threadId":"40568","inReplyTo":"1444938410-2345-4-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH v4 03/26] refs-be-files.c: rename refs to refs-be-files","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-10-16T06:36:54Z","receivedAt":"2015-10-16T06:36:54Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 10/15/2015 09:46 PM, David Turner wrote:\n> From: Ronnie Sahlberg <sahlberg@google.com>\n> \n> Rename refs.c to refs-be-files.c to indicate that this file now\n> holds the implementation for the files based refs backend.\n> A smaller portion of the code in this file is backend agnostic and will\n> be moved to a a new refs.c file that will hold all the common refs code\n\n\"a\" is repeated above.\n\n> that is shared across all backends.\n> \n> A second reason for first moving all the code to the new file and then\n> move the backend agnostic code back to refs.c instead of the other way\n> around is because the code that will eventually remain in this new\n> refs-be-files.c file is so entangled that it would then be very\n> difficult to break the split up into small independent patches/chunks.\n> [...]\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"271956","messageId":"1445296596.3418.31.camel@twopensource.com","threadId":"40568","inReplyTo":"56209A81.1060706@alum.mit.edu","subject":"Re: [PATCH v4 02/26] refs: make repack_without_refs and is_branch public","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-19T23:16:36Z","receivedAt":"2015-10-19T23:16:36Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"On Fri, 2015-10-16 at 08:34 +0200, Michael Haggerty wrote:\n> On 10/15/2015 09:46 PM, David Turner wrote:\n> > is_branch was already non-static, but this patch declares it in the\n> > header.\n> \n> The commit message no longer reflects the patch.\n> \n> > Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>\n> > Signed-off-by: David Turner <dturner@twopensource.com>\n> > Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> > ---\n> >  refs.c | 5 +++--\n> >  refs.h | 2 ++\n> >  2 files changed, 5 insertions(+), 2 deletions(-)\n> > \n> > diff --git a/refs.c b/refs.c\n> > index fe71ea0..84abc82 100644\n> > --- a/refs.c\n> > +++ b/refs.c\n> > @@ -2816,8 +2816,9 @@ int pack_refs(unsigned int flags)\n> >  \n> >  /*\n> >   * Rewrite the packed-refs file, omitting any refs listed in\n> > - * 'refnames'. On error, leave packed-refs unchanged, write an error\n> > - * message to 'err', and return a nonzero value.\n> > + * 'refnames'. On error, packed-refs will be unchanged, the return\n> > + * value is nonzero, and a message about the error is written to the\n> > + * 'err' strbuf.\n> \n> ^^^ ?\n> \n> It is preferable for docstrings to be written in imperative form, so in\n> my opinion this is a step backwards...\n> \n> ...literally. Your \"new\" version comes from an older version of Git; it\n> was changed in\n> \n>     79e4d8a9b8 repack_without_refs(): make function private (2015-06-22)\n> \n> to the imperative form.\n> \n> Assuming you are using `git-format-patch` to prepare your patches, it is\n> always a good idea to read over the prepared email files before sending\n> them to the ML, to check for bloopers like this.\n\nSorry about that one.  It's hard to keep track of what all of these\npatches do -- especially the ones that were rebases of Ronnie's.  I've\nfixed that and 03/26 as well.\n\nDo you have comments on any of the rest before I re-roll?  \n"},{"id":"272050","messageId":"1445454210.8302.13.camel@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-6-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH v4 05/26] refs.c: move update_ref to refs.c","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-21T19:03:30Z","receivedAt":"2015-10-21T19:03:30Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"On Thu, 2015-10-15 at 15:46 -0400, David Turner wrote:\n> From: Ronnie Sahlberg <sahlberg@google.com>\n> \n> Move update_ref() to the refs.c file since this function does not\n> contain any backend specific code.  Move the ref classifier functions\n\nand write_pseudoref (will fix on reroll).\n"},{"id":"293765","messageId":"1445454241.8302.14.camel@twopensource.com","threadId":"40568","inReplyTo":"1444938410-2345-7-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH v4 06/26] refs.c: move delete_pseudoref and delete_ref to the common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-21T19:04:01Z","receivedAt":"2015-10-21T19:04:01Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"On Thu, 2015-10-15 at 15:46 -0400, David Turner wrote:\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -117,3 +117,60 @@ int update_ref(const char *msg, const char *refname,\n>  \t\tref_transaction_free(t);\n>  \treturn 0;\n>  }\n> +\n> +\n> +static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)\n\nextra newline (will fix on reroll)\n\n"},{"id":"272054","messageId":"xmqq37x4vwon.fsf@gitster.mtv.corp.google.com","threadId":"40568","inReplyTo":"1444938410-2345-24-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH v4 23/26] initdb: move safe_create_dir into common code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-10-21T19:38:48Z","receivedAt":"2015-10-21T19:38:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Turner <dturner@twopensource.com> writes:\n\n> In a moment, we'll create initdb functions for ref backends, and code\n> from initdb that calls this function needs to move into the files\n> backend.  So this function needs to be public.\n\nOK, but unlike the static function, being in public interface part\ncan invite mistakes of using this for things outside $GIT_DIR [*1*].\nLet's have \"Never use this for working tree directories\" somewhere\nin its docstring.\n\nOther than that, this one, 24/26 and 25/26 looked fine to me.\n\nThanks.\n\n[Footnote]\n\n*1* Anything created by this function and everything underneath are\nrepository metadata and this function must not be used to do with\nanything with the working tree, as it is clear with the use of\nadjust_shared_perm().\n\n> diff --git a/cache.h b/cache.h\n> index 9a905a8..1d8a051 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -1737,4 +1737,9 @@ void stat_validity_update(struct stat_validity *sv, int fd);\n>  int versioncmp(const char *s1, const char *s2);\n>  void sleep_millisec(int millisec);\n>  \n> +/*\n> + * Create a directory and (if share is nonzero) adjust its permissions\n> + * according to the shared_repository setting.\n> + */\n> +void safe_create_dir(const char *dir, int share);\n>  #endif /* CACHE_H */\n> diff --git a/path.c b/path.c\n> index 212695a..9e0283c 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -723,6 +723,18 @@ int adjust_shared_perm(const char *path)\n>  \treturn 0;\n>  }\n>  \n> +void safe_create_dir(const char *dir, int share)\n> +{\n> +\tif (mkdir(dir, 0777) < 0) {\n> +\t\tif (errno != EEXIST) {\n> +\t\t\tperror(dir);\n> +\t\t\texit(1);\n> +\t\t}\n> +\t}\n> +\telse if (share && adjust_shared_perm(dir))\n> +\t\tdie(_(\"Could not make %s writable by group\"), dir);\n> +}\n> +\n>  static int have_same_root(const char *path1, const char *path2)\n>  {\n>  \tint is_abs1, is_abs2;\n"},{"id":"272055","messageId":"xmqqy4ewui1m.fsf@gitster.mtv.corp.google.com","threadId":"40568","inReplyTo":"1444938410-2345-27-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH v4 26/26] introduce \"extensions\" form of core.repositoryformatversion","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-10-21T19:40:21Z","receivedAt":"2015-10-21T19:40:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Turner <dturner@twopensource.com> writes:\n\n> From: Jeff King <peff@peff.net>\n\nI just made sure this is bit-for-bit identical with the first of the\ntwo patches I received from Peff and I locally have kept.\n\nRe-reading the two patches again, I do not see a reason why we should\nreject them.  I'll queue this and the other \"precious object\" one\nseparately.\n\nThanks.\n\n>\n> Normally we try to avoid bumps of the whole-repository\n> core.repositoryformatversion field. However, it is\n> unavoidable if we want to safely change certain aspects of\n> git in a backwards-incompatible way (e.g., modifying the set\n> of ref tips that we must traverse to generate a list of\n> unreachable, safe-to-prune objects).\n>\n> If we were to bump the repository version for every such\n> change, then any implementation understanding version `X`\n> would also have to understand `X-1`, `X-2`, and so forth,\n> even though the incompatibilities may be in orthogonal parts\n> of the system, and there is otherwise no reason we cannot\n> implement one without the other (or more importantly, that\n> the user cannot choose to use one feature without the other,\n> weighing the tradeoff in compatibility only for that\n> particular feature).\n>\n> This patch documents the existing repositoryformatversion\n> strategy and introduces a new format, \"1\", which lets a\n> repository specify that it must run with an arbitrary set of\n> extensions. This can be used, for example:\n>\n>  - to inform git that the objects should not be pruned based\n>    only on the reachability of the ref tips (e.g, because it\n>    has \"clone --shared\" children)\n>\n>  - that the refs are stored in a format besides the usual\n>    \"refs\" and \"packed-refs\" directories\n>\n> Because we bump to format \"1\", and because format \"1\"\n> requires that a running git knows about any extensions\n> mentioned, we know that older versions of the code will not\n> do something dangerous when confronted with these new\n> formats.\n>\n> For example, if the user chooses to use database storage for\n> refs, they may set the \"extensions.refbackend\" config to\n> \"db\". Older versions of git will not understand format \"1\"\n> and bail. Versions of git which understand \"1\" but do not\n> know about \"refbackend\", or which know about \"refbackend\"\n> but not about the \"db\" backend, will refuse to run. This is\n> annoying, of course, but much better than the alternative of\n> claiming that there are no refs in the repository, or\n> writing to a location that other implementations will not\n> read.\n>\n> Note that we are only defining the rules for format 1 here.\n> We do not ever write format 1 ourselves; it is a tool that\n> is meant to be used by users and future extensions to\n> provide safety with older implementations.\n>\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  Documentation/technical/repository-version.txt | 81 ++++++++++++++++++++++++++\n>  cache.h                                        |  6 ++\n>  setup.c                                        | 37 +++++++++++-\n>  t/t1302-repo-version.sh                        | 38 ++++++++++++\n>  4 files changed, 159 insertions(+), 3 deletions(-)\n>  create mode 100644 Documentation/technical/repository-version.txt\n>\n> diff --git a/Documentation/technical/repository-version.txt b/Documentation/technical/repository-version.txt\n> new file mode 100644\n> index 0000000..3d7106d\n> --- /dev/null\n> +++ b/Documentation/technical/repository-version.txt\n> @@ -0,0 +1,81 @@\n> +Git Repository Format Versions\n> +==============================\n> +\n> +Every git repository is marked with a numeric version in the\n> +`core.repositoryformatversion` key of its `config` file. This version\n> +specifies the rules for operating on the on-disk repository data. An\n> +implementation of git which does not understand a particular version\n> +advertised by an on-disk repository MUST NOT operate on that repository;\n> +doing so risks not only producing wrong results, but actually losing\n> +data.\n> +\n> +Because of this rule, version bumps should be kept to an absolute\n> +minimum. Instead, we generally prefer these strategies:\n> +\n> +  - bumping format version numbers of individual data files (e.g.,\n> +    index, packfiles, etc). This restricts the incompatibilities only to\n> +    those files.\n> +\n> +  - introducing new data that gracefully degrades when used by older\n> +    clients (e.g., pack bitmap files are ignored by older clients, which\n> +    simply do not take advantage of the optimization they provide).\n> +\n> +A whole-repository format version bump should only be part of a change\n> +that cannot be independently versioned. For instance, if one were to\n> +change the reachability rules for objects, or the rules for locking\n> +refs, that would require a bump of the repository format version.\n> +\n> +Note that this applies only to accessing the repository's disk contents\n> +directly. An older client which understands only format `0` may still\n> +connect via `git://` to a repository using format `1`, as long as the\n> +server process understands format `1`.\n> +\n> +The preferred strategy for rolling out a version bump (whether whole\n> +repository or for a single file) is to teach git to read the new format,\n> +and allow writing the new format with a config switch or command line\n> +option (for experimentation or for those who do not care about backwards\n> +compatibility with older gits). Then after a long period to allow the\n> +reading capability to become common, we may switch to writing the new\n> +format by default.\n> +\n> +The currently defined format versions are:\n> +\n> +Version `0`\n> +-----------\n> +\n> +This is the format defined by the initial version of git, including but\n> +not limited to the format of the repository directory, the repository\n> +configuration file, and the object and ref storage. Specifying the\n> +complete behavior of git is beyond the scope of this document.\n> +\n> +Version `1`\n> +-----------\n> +\n> +This format is identical to version `0`, with the following exceptions:\n> +\n> +  1. When reading the `core.repositoryformatversion` variable, a git\n> +     implementation which supports version 1 MUST also read any\n> +     configuration keys found in the `extensions` section of the\n> +     configuration file.\n> +\n> +  2. If a version-1 repository specifies any `extensions.*` keys that\n> +     the running git has not implemented, the operation MUST NOT\n> +     proceed. Similarly, if the value of any known key is not understood\n> +     by the implementation, the operation MUST NOT proceed.\n> +\n> +Note that if no extensions are specified in the config file, then\n> +`core.repositoryformatversion` SHOULD be set to `0` (setting it to `1`\n> +provides no benefit, and makes the repository incompatible with older\n> +implementations of git).\n> +\n> +This document will serve as the master list for extensions. Any\n> +implementation wishing to define a new extension should make a note of\n> +it here, in order to claim the name.\n> +\n> +The defined extensions are:\n> +\n> +`noop`\n> +~~~~~~\n> +\n> +This extension does not change git's behavior at all. It is useful only\n> +for testing format-1 compatibility.\n> diff --git a/cache.h b/cache.h\n> index 1d8a051..4b03cb3 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -696,7 +696,13 @@ extern char *notes_ref_name;\n>  \n>  extern int grafts_replace_parents;\n>  \n> +/*\n> + * GIT_REPO_VERSION is the version we write by default. The\n> + * _READ variant is the highest number we know how to\n> + * handle.\n> + */\n>  #define GIT_REPO_VERSION 0\n> +#define GIT_REPO_VERSION_READ 1\n>  extern int repository_format_version;\n>  extern int check_repository_format(void);\n>  \n> diff --git a/setup.c b/setup.c\n> index 2b64cbb..0c29469 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -5,6 +5,7 @@\n>  static int inside_git_dir = -1;\n>  static int inside_work_tree = -1;\n>  static int work_tree_config_is_bogus;\n> +static struct string_list unknown_extensions = STRING_LIST_INIT_DUP;\n>  \n>  /*\n>   * The input parameter must contain an absolute path, and it must already be\n> @@ -349,10 +350,23 @@ void setup_work_tree(void)\n>  \n>  static int check_repo_format(const char *var, const char *value, void *cb)\n>  {\n> +\tconst char *ext;\n> +\n>  \tif (strcmp(var, \"core.repositoryformatversion\") == 0)\n>  \t\trepository_format_version = git_config_int(var, value);\n>  \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n>  \t\tshared_repository = git_config_perm(var, value);\n> +\telse if (skip_prefix(var, \"extensions.\", &ext)) {\n> +\t\t/*\n> +\t\t * record any known extensions here; otherwise,\n> +\t\t * we fall through to recording it as unknown, and\n> +\t\t * check_repository_format will complain\n> +\t\t */\n> +\t\tif (!strcmp(ext, \"noop\"))\n> +\t\t\t;\n> +\t\telse\n> +\t\t\tstring_list_append(&unknown_extensions, ext);\n> +\t}\n>  \treturn 0;\n>  }\n>  \n> @@ -363,6 +377,8 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n>  \tconfig_fn_t fn;\n>  \tint ret = 0;\n>  \n> +\tstring_list_clear(&unknown_extensions, 0);\n> +\n>  \tif (get_common_dir(&sb, gitdir))\n>  \t\tfn = check_repo_format;\n>  \telse\n> @@ -380,16 +396,31 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n>  \t * is a good one.\n>  \t */\n>  \tgit_config_early(fn, NULL, repo_config);\n> -\tif (GIT_REPO_VERSION < repository_format_version) {\n> +\tif (GIT_REPO_VERSION_READ < repository_format_version) {\n>  \t\tif (!nongit_ok)\n>  \t\t\tdie (\"Expected git repo version <= %d, found %d\",\n> -\t\t\t     GIT_REPO_VERSION, repository_format_version);\n> +\t\t\t     GIT_REPO_VERSION_READ, repository_format_version);\n>  \t\twarning(\"Expected git repo version <= %d, found %d\",\n> -\t\t\tGIT_REPO_VERSION, repository_format_version);\n> +\t\t\tGIT_REPO_VERSION_READ, repository_format_version);\n>  \t\twarning(\"Please upgrade Git\");\n>  \t\t*nongit_ok = -1;\n>  \t\tret = -1;\n>  \t}\n> +\n> +\tif (repository_format_version >= 1 && unknown_extensions.nr) {\n> +\t\tint i;\n> +\n> +\t\tif (!nongit_ok)\n> +\t\t\tdie(\"unknown repository extension: %s\",\n> +\t\t\t    unknown_extensions.items[0].string);\n> +\n> +\t\tfor (i = 0; i < unknown_extensions.nr; i++)\n> +\t\t\twarning(\"unknown repository extension: %s\",\n> +\t\t\t\tunknown_extensions.items[i].string);\n> +\t\t*nongit_ok = -1;\n> +\t\tret = -1;\n> +\t}\n> +\n>  \tstrbuf_release(&sb);\n>  \treturn ret;\n>  }\n> diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh\n> index 0d9388a..8dd6fd7 100755\n> --- a/t/t1302-repo-version.sh\n> +++ b/t/t1302-repo-version.sh\n> @@ -67,4 +67,42 @@ test_expect_success 'gitdir required mode' '\n>  \t)\n>  '\n>  \n> +check_allow () {\n> +\tgit rev-parse --git-dir >actual &&\n> +\techo .git >expect &&\n> +\ttest_cmp expect actual\n> +}\n> +\n> +check_abort () {\n> +\ttest_must_fail git rev-parse --git-dir\n> +}\n> +\n> +# avoid git-config, since it cannot be trusted to run\n> +# in a repository with a broken version\n> +mkconfig () {\n> +\techo '[core]' &&\n> +\techo \"repositoryformatversion = $1\" &&\n> +\tshift &&\n> +\n> +\tif test $# -gt 0; then\n> +\t\techo '[extensions]' &&\n> +\t\tfor i in \"$@\"; do\n> +\t\t\techo \"$i\"\n> +\t\tdone\n> +\tfi\n> +}\n> +\n> +while read outcome version extensions; do\n> +\ttest_expect_success \"$outcome version=$version $extensions\" \"\n> +\t\tmkconfig $version $extensions >.git/config &&\n> +\t\tcheck_${outcome}\n> +\t\"\n> +done <<\\EOF\n> +allow 0\n> +allow 1\n> +allow 1 noop\n> +abort 1 no-such-extension\n> +allow 0 no-such-extension\n> +EOF\n> +\n>  test_done\n"},{"id":"272053","messageId":"1445456860.8302.15.camel@twopensource.com","threadId":"40568","inReplyTo":"xmqq37x4vwon.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 23/26] initdb: move safe_create_dir into common code","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-10-21T19:47:40Z","receivedAt":"2015-10-21T19:47:40Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"On Wed, 2015-10-21 at 12:38 -0700, Junio C Hamano wrote:\n> David Turner <dturner@twopensource.com> writes:\n> \n> > In a moment, we'll create initdb functions for ref backends, and code\n> > from initdb that calls this function needs to move into the files\n> > backend.  So this function needs to be public.\n> \n> OK, but unlike the static function, being in public interface part\n> can invite mistakes of using this for things outside $GIT_DIR [*1*].\n> Let's have \"Never use this for working tree directories\" somewhere\n> in its docstring.\n\nWill fix in the re-roll, thanks.\n"}]}