{"thread":{"id":"38107","subject":"[PATCHv3 00/13] the refs-transactions-reflog series","startedAt":"2014-12-04T08:29:10Z","lastAt":"2014-12-04T21:13:38Z","messageCount":25,"participants":["Stefan Beller","Torsten Bögershausen","Andreas Schwab","Michael Haggerty","Jonathan Nieder","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"253026","messageId":"1417681763-32334-1-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":null,"subject":"[PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:10Z","receivedAt":"2014-12-04T08:29:10Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This is the whole refs-transactions-reflog series[1],\nwhich was in discussion for a bit already. It applies to origin/master.\n\nThe idea is to have the reflog being part of the transactions, which\nthe refs are already using, so the we're moving towards a database\nlike API in the long run. This makes git easier to maintain as well\nas opening the possibility to replace the backend with a real database.\n\nIf you've followed the topic a bit, start reading at patch\n\"[PATCH 06/13] refs.c: add a transaction function to append a reflog\"\nas the first 5 patches have been discussed a lot separately and can be found in\norigin/pu already[2].\nThe first two patches are deduplicating code.\nThe third patch is ripping some code out of log_ref_write and introduces\nlog_ref_write_fd, which does the actual writing.\nThe patches 4+5 are renaming variables for clarity.\n\nThe patch 6 and 7 are the entree in this series. Patch 6 adds two functions to\nthe refs API: transaction_truncate_reflog and transaction_update_reflog. Both\nfunctions do not affect the files directy, but rather become effective once\ntransaction_commit function is called. The transaction_truncate_reflog will\nwipe the reflog, which can be used for rebuilding the reflog, whereas the\ntransaction_update_reflog function will just append one entry to the reflog.\nThe patch 7 will make use of the functions introduced in patch 6. The command\ngit reflog expire will then use the reflog transactions.\n\nPatch 8 is renaming log_ref_setup to create_reflog and should not\n\nPatches 9-12 are removing functions from the public refs API, which are unused then.\n\nPatch 13 is adding new functionality again, you can finally delete broken refs without\nhaving to know the details on git.\n\n[1] http://comments.gmane.org/gmane.comp.version-control.git/259712 or\n    http://www.spinics.net/lists/git/msg242502.html \n\n[2] patch 1 + 2:\n        origin/sb/ref-transaction-unify-to-update\n    patch 3:\n        origin/sb/log-ref-write-fd\n    patch 4 + 5:\n        origin/sb/ref-transaction-reflog\n    patch 1-5 is unchanged in this series.\n\n\nRonnie Sahlberg (9):\n  refs.c: make ref_transaction_create a wrapper for\n    ref_transaction_update\n  refs.c: make ref_transaction_delete a wrapper for\n    ref_transaction_update\n  refs.c: add a function to append a reflog entry to a fd\n  refs.c: rename the transaction functions\n  reflog.c: use a reflog transaction when writing during expire\n  refs.c: rename log_ref_setup to create_reflog\n  refs.c: remove unlock_ref/close_ref/commit_ref from the refs api\n  refs.c: remove lock_any_ref_for_update\n  refs.c: allow deleting refs with a broken sha1\n\nStefan Beller (4):\n  refs.c: rename transaction.updates to transaction.ref_updates\n  refs.c: add a transaction function to truncate or append a reflog\n    entry\n  refs.c: don't expose the internal struct ref_lock in the header file\n  refs.c: use a bit for ref_update have_old\n\n branch.c               |  13 +-\n builtin/branch.c       |   5 +-\n builtin/checkout.c     |   8 +-\n builtin/commit.c       |  10 +-\n builtin/fetch.c        |  12 +-\n builtin/receive-pack.c |  13 +-\n builtin/reflog.c       |  84 +++++------\n builtin/replace.c      |  10 +-\n builtin/tag.c          |  10 +-\n builtin/update-ref.c   |  26 ++--\n cache.h                |   7 +\n fast-import.c          |  22 +--\n refs.c                 | 371 +++++++++++++++++++++++++++++++------------------\n refs.h                 |  95 ++++++-------\n sequencer.c            |  12 +-\n t/t3200-branch.sh      |   8 ++\n walker.c               |  10 +-\n 17 files changed, 408 insertions(+), 308 deletions(-)\n\n-- \n2.2.0\n"},{"id":"253027","messageId":"1417681763-32334-2-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 01/13] refs.c: make ref_transaction_create a wrapper for ref_transaction_update","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:11Z","receivedAt":"2014-12-04T08:29:11Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nThe ref_transaction_update function can already be used to create refs by\npassing null_sha1 as the old_sha1 parameter. Simplify by replacing\ntransaction_create with a thin wrapper.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nReviewed-by: Michael Haggerty <mhagger@alum.mit.edu>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n\nNotes:\n    origin/sb/ref-transaction-unify-to-update\n    as well as\n    origin/sb/ref-transaction-reflog\n    \n    no changes since sending them last time\n\n refs.c | 27 ++-------------------------\n 1 file changed, 2 insertions(+), 25 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 5ff457e..005eb18 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3623,31 +3623,8 @@ int ref_transaction_create(struct ref_transaction *transaction,\n \t\t\t   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: create called for transaction that is not open\");\n-\n-\tif (!new_sha1 || is_null_sha1(new_sha1))\n-\t\tdie(\"BUG: create ref with null new_sha1\");\n-\n-\tif (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {\n-\t\tstrbuf_addf(err, \"refusing to create ref with bad name %s\",\n-\t\t\t    refname);\n-\t\treturn -1;\n-\t}\n-\n-\tupdate = add_update(transaction, refname);\n-\n-\thashcpy(update->new_sha1, new_sha1);\n-\thashclr(update->old_sha1);\n-\tupdate->flags = flags;\n-\tupdate->have_old = 1;\n-\tif (msg)\n-\t\tupdate->msg = xstrdup(msg);\n-\treturn 0;\n+\treturn ref_transaction_update(transaction, refname, new_sha1,\n+\t\t\t\t      null_sha1, flags, 1, msg, err);\n }\n \n int ref_transaction_delete(struct ref_transaction *transaction,\n-- \n2.2.0\n"},{"id":"253028","messageId":"1417681763-32334-3-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 02/13] refs.c: make ref_transaction_delete a wrapper for ref_transaction_update","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:12Z","receivedAt":"2014-12-04T08:29:12Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nReviewed-by: Michael Haggerty <mhagger@alum.mit.edu>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n\nNotes:\n    origin/sb/ref-transaction-unify-to-update\n    as well as\n    origin/sb/ref-transaction-reflog\n    \n    no changes since sending last time.\n\n refs.c | 22 ++--------------------\n refs.h |  2 +-\n 2 files changed, 3 insertions(+), 21 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 005eb18..05cb299 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3633,26 +3633,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,\n \t\t\t   int flags, int have_old, 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: delete called for transaction that is not open\");\n-\n-\tif (have_old && !old_sha1)\n-\t\tdie(\"BUG: have_old is true but old_sha1 is NULL\");\n-\n-\tupdate = add_update(transaction, refname);\n-\tupdate->flags = flags;\n-\tupdate->have_old = have_old;\n-\tif (have_old) {\n-\t\tassert(!is_null_sha1(old_sha1));\n-\t\thashcpy(update->old_sha1, old_sha1);\n-\t}\n-\tif (msg)\n-\t\tupdate->msg = xstrdup(msg);\n-\treturn 0;\n+\treturn ref_transaction_update(transaction, refname, null_sha1,\n+\t\t\t\t      old_sha1, flags, have_old, msg, err);\n }\n \n int update_ref(const char *action, const char *refname,\ndiff --git a/refs.h b/refs.h\nindex 2bc3556..7d675b7 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -283,7 +283,7 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);\n \n /*\n  * Add a reference update to transaction.  new_sha1 is the value that\n- * the reference should have after the update, or zeros if it should\n+ * the reference should have after the update, or null_sha1 if it should\n  * be deleted.  If have_old is true, then old_sha1 holds the value\n  * that the reference should have had before the update, or zeros if\n  * it must not have existed beforehand.\n-- \n2.2.0\n"},{"id":"253040","messageId":"1417681763-32334-4-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 03/13] refs.c: add a function to append a reflog entry to a fd","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:13Z","receivedAt":"2014-12-04T08:29:13Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nBreak out the code to create the string and writing it to the file\ndescriptor from log_ref_write and add it into a dedicated function\nlog_ref_write_fd. For now this is only used from log_ref_write,\nbut later on we will call this function from reflog transactions too,\nwhich means that we will end up with only a single place,\nwhere we write a reflog entry to a file instead of the current two\nplaces (log_ref_write and builtin/reflog.c).\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n\nNotes:\n    being part of origin/sb/log-ref-write-fd\n    \n    no changes since sending last time.\n\n refs.c | 48 ++++++++++++++++++++++++++++++------------------\n 1 file changed, 30 insertions(+), 18 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 05cb299..150c980 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2990,15 +2990,37 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)\n \treturn 0;\n }\n \n+static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n+\t\t\t    const unsigned char *new_sha1,\n+\t\t\t    const char *committer, const char *msg)\n+{\n+\tint msglen, written;\n+\tunsigned maxlen, len;\n+\tchar *logrec;\n+\n+\tmsglen = msg ? strlen(msg) : 0;\n+\tmaxlen = strlen(committer) + msglen + 100;\n+\tlogrec = xmalloc(maxlen);\n+\tlen = sprintf(logrec, \"%s %s %s\\n\",\n+\t\t      sha1_to_hex(old_sha1),\n+\t\t      sha1_to_hex(new_sha1),\n+\t\t      committer);\n+\tif (msglen)\n+\t\tlen += copy_msg(logrec + len - 1, msg) - 1;\n+\n+\twritten = len <= maxlen ? write_in_full(fd, logrec, len) : -1;\n+\tfree(logrec);\n+\tif (written != len)\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n 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 {\n-\tint logfd, result, written, oflags = O_APPEND | O_WRONLY;\n-\tunsigned maxlen, len;\n-\tint msglen;\n+\tint logfd, result, oflags = O_APPEND | O_WRONLY;\n \tchar log_file[PATH_MAX];\n-\tchar *logrec;\n-\tconst char *committer;\n \n \tif (log_all_ref_updates < 0)\n \t\tlog_all_ref_updates = !is_bare_repository();\n@@ -3010,19 +3032,9 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,\n \tlogfd = open(log_file, oflags);\n \tif (logfd < 0)\n \t\treturn 0;\n-\tmsglen = msg ? strlen(msg) : 0;\n-\tcommitter = git_committer_info(0);\n-\tmaxlen = strlen(committer) + msglen + 100;\n-\tlogrec = xmalloc(maxlen);\n-\tlen = sprintf(logrec, \"%s %s %s\\n\",\n-\t\t      sha1_to_hex(old_sha1),\n-\t\t      sha1_to_hex(new_sha1),\n-\t\t      committer);\n-\tif (msglen)\n-\t\tlen += copy_msg(logrec + len - 1, msg) - 1;\n-\twritten = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;\n-\tfree(logrec);\n-\tif (written != len) {\n+\tresult = log_ref_write_fd(logfd, old_sha1, new_sha1,\n+\t\t\t\t  git_committer_info(0), msg);\n+\tif (result) {\n \t\tint save_errno = errno;\n \t\tclose(logfd);\n \t\terror(\"Unable to append to %s\", log_file);\n-- \n2.2.0\n"},{"id":"253030","messageId":"1417681763-32334-5-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 04/13] refs.c: rename the transaction functions","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:14Z","receivedAt":"2014-12-04T08:29:14Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nRename the transaction functions. Remove the leading ref_ from the\nnames and append _ref to the names for functions that create/delete/\nupdate sha1 refs.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n\nNotes:\n    remotes/origin/sb/ref-transaction-reflog\n    \n    no changes since last review\n    \n    This commit can be reproduced via\n    find . -name \"*.[ch]\" -print | xargs sed -i ' \\\n            s/REF_TRANSACTION_OPEN/TRANSACTION_OPEN/; \\\n            s/REF_TRANSACTION_CLOSED/TRANSACTION_CLOSED/; \\\n            s/ref_transaction_begin/transaction_begin/; \\\n            s/ref_transaction_commit/transaction_commit/; \\\n            s/ref_transaction_create/transaction_create_ref/; \\\n            s/ref_transaction_delete/transaction_delete_ref/; \\\n            s/ref_transaction_free/transaction_free/; \\\n            s/ref_transaction_update/transaction_update_ref/; \\\n            s/ref_transaction/transaction/'\n    modulo white space changes for alignment.\n\n branch.c               | 13 +++++----\n builtin/commit.c       | 10 +++----\n builtin/fetch.c        | 12 ++++----\n builtin/receive-pack.c | 13 ++++-----\n builtin/replace.c      | 10 +++----\n builtin/tag.c          | 10 +++----\n builtin/update-ref.c   | 26 ++++++++---------\n fast-import.c          | 22 +++++++-------\n refs.c                 | 78 +++++++++++++++++++++++++-------------------------\n refs.h                 | 36 +++++++++++------------\n sequencer.c            | 12 ++++----\n walker.c               | 10 +++----\n 12 files changed, 126 insertions(+), 126 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 4bab55a..c8462de 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -279,16 +279,17 @@ void create_branch(const char *head,\n \t\tlog_all_ref_updates = 1;\n \n \tif (!dont_change_ref) {\n-\t\tstruct ref_transaction *transaction;\n+\t\tstruct transaction *transaction;\n \t\tstruct strbuf err = STRBUF_INIT;\n \n-\t\ttransaction = ref_transaction_begin(&err);\n+\t\ttransaction = transaction_begin(&err);\n \t\tif (!transaction ||\n-\t\t    ref_transaction_update(transaction, ref.buf, sha1,\n-\t\t\t\t\t   null_sha1, 0, !forcing, msg, &err) ||\n-\t\t    ref_transaction_commit(transaction, &err))\n+\t\t    transaction_update_ref(transaction, ref.buf, sha1,\n+\t\t\t\t\t   null_sha1, 0, !forcing, msg,\n+\t\t\t\t\t   &err) ||\n+\t\t    transaction_commit(transaction, &err))\n \t\t\tdie(\"%s\", err.buf);\n-\t\tref_transaction_free(transaction);\n+\t\ttransaction_free(transaction);\n \t\tstrbuf_release(&err);\n \t}\n \ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex e108c53..f50b7df 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1673,7 +1673,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstruct stat statbuf;\n \tstruct commit *current_head = NULL;\n \tstruct commit_extra_header *extra = NULL;\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n@@ -1804,17 +1804,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n \tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\", sha1,\n+\t    transaction_update_ref(transaction, \"HEAD\", sha1,\n \t\t\t\t   current_head\n \t\t\t\t   ? current_head->object.sha1 : NULL,\n \t\t\t\t   0, !!current_head, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\t    transaction_commit(transaction, &err)) {\n \t\trollback_index_files();\n \t\tdie(\"%s\", err.buf);\n \t}\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \n \tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n \tunlink(git_path(\"REVERT_HEAD\"));\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7b84d35..0be0b09 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -404,7 +404,7 @@ static int s_update_ref(const char *action,\n {\n \tchar msg[1024];\n \tchar *rla = getenv(\"GIT_REFLOG_ACTION\");\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \tint ret, df_conflict = 0;\n \n@@ -414,23 +414,23 @@ static int s_update_ref(const char *action,\n \t\trla = default_rla.buf;\n \tsnprintf(msg, sizeof(msg), \"%s: %s\", rla, action);\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_update(transaction, ref->name, ref->new_sha1,\n+\t    transaction_update_ref(transaction, ref->name, ref->new_sha1,\n \t\t\t\t   ref->old_sha1, 0, check_old, msg, &err))\n \t\tgoto fail;\n \n-\tret = ref_transaction_commit(transaction, &err);\n+\tret = transaction_commit(transaction, &err);\n \tif (ret) {\n \t\tdf_conflict = (ret == TRANSACTION_NAME_CONFLICT);\n \t\tgoto fail;\n \t}\n \n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \tstrbuf_release(&err);\n \treturn 0;\n fail:\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \terror(\"%s\", err.buf);\n \tstrbuf_release(&err);\n \treturn df_conflict ? STORE_REF_ERROR_DF_CONFLICT\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 32fc540..397abc9 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -838,26 +838,25 @@ static const char *update(struct command *cmd, struct shallow_info *si)\n \t}\n \telse {\n \t\tstruct strbuf err = STRBUF_INIT;\n-\t\tstruct ref_transaction *transaction;\n+\t\tstruct transaction *transaction;\n \n \t\tif (shallow_update && si->shallow_ref[cmd->index] &&\n \t\t    update_shallow_ref(cmd, si))\n \t\t\treturn \"shallow error\";\n \n-\t\ttransaction = ref_transaction_begin(&err);\n+\t\ttransaction = transaction_begin(&err);\n \t\tif (!transaction ||\n-\t\t    ref_transaction_update(transaction, namespaced_name,\n+\t\t    transaction_update_ref(transaction, namespaced_name,\n \t\t\t\t\t   new_sha1, old_sha1, 0, 1, \"push\",\n \t\t\t\t\t   &err) ||\n-\t\t    ref_transaction_commit(transaction, &err)) {\n-\t\t\tref_transaction_free(transaction);\n-\n+\t\t    transaction_commit(transaction, &err)) {\n+\t\t\ttransaction_free(transaction);\n \t\t\trp_error(\"%s\", err.buf);\n \t\t\tstrbuf_release(&err);\n \t\t\treturn \"failed to update ref\";\n \t\t}\n \n-\t\tref_transaction_free(transaction);\n+\t\ttransaction_free(transaction);\n \t\tstrbuf_release(&err);\n \t\treturn NULL; /* good */\n \t}\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 85d39b5..5a7ab1f 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -155,7 +155,7 @@ static int replace_object_sha1(const char *object_ref,\n \tunsigned char prev[20];\n \tenum object_type obj_type, repl_type;\n \tchar ref[PATH_MAX];\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tobj_type = sha1_object_info(object, NULL);\n@@ -169,14 +169,14 @@ static int replace_object_sha1(const char *object_ref,\n \n \tcheck_ref_valid(object, prev, ref, sizeof(ref), force);\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_update(transaction, ref, repl, prev,\n+\t    transaction_update_ref(transaction, ref, repl, prev,\n \t\t\t\t   0, 1, NULL, &err) ||\n-\t    ref_transaction_commit(transaction, &err))\n+\t    transaction_commit(transaction, &err))\n \t\tdie(\"%s\", err.buf);\n \n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \treturn 0;\n }\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex e633f4e..5f3554b 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -583,7 +583,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tconst char *msgfile = NULL, *keyid = NULL;\n \tstruct msg_arg msg = { 0, STRBUF_INIT };\n \tstruct commit_list *with_commit = NULL;\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list tag names\"), 'l'),\n@@ -730,13 +730,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tif (annotate)\n \t\tcreate_tag(object, tag, &buf, &opt, prev, object);\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_update(transaction, ref.buf, object, prev,\n+\t    transaction_update_ref(transaction, ref.buf, object, prev,\n \t\t\t\t   0, 1, NULL, &err) ||\n-\t    ref_transaction_commit(transaction, &err))\n+\t    transaction_commit(transaction, &err))\n \t\tdie(\"%s\", err.buf);\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \tif (force && !is_null_sha1(prev) && hashcmp(prev, object))\n \t\tprintf(_(\"Updated tag '%s' (was %s)\\n\"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));\n \ndiff --git a/builtin/update-ref.c b/builtin/update-ref.c\nindex 6c9be05..af08dd9 100644\n--- a/builtin/update-ref.c\n+++ b/builtin/update-ref.c\n@@ -175,7 +175,7 @@ static int parse_next_sha1(struct strbuf *input, const char **next,\n  * depending on how line_termination is set.\n  */\n \n-static const char *parse_cmd_update(struct ref_transaction *transaction,\n+static const char *parse_cmd_update(struct transaction *transaction,\n \t\t\t\t    struct strbuf *input, const char *next)\n {\n \tstruct strbuf err = STRBUF_INIT;\n@@ -198,7 +198,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,\n \tif (*next != line_termination)\n \t\tdie(\"update %s: extra input: %s\", refname, next);\n \n-\tif (ref_transaction_update(transaction, refname, new_sha1, old_sha1,\n+\tif (transaction_update_ref(transaction, refname, new_sha1, old_sha1,\n \t\t\t\t   update_flags, have_old, msg, &err))\n \t\tdie(\"%s\", err.buf);\n \n@@ -209,7 +209,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,\n \treturn next;\n }\n \n-static const char *parse_cmd_create(struct ref_transaction *transaction,\n+static const char *parse_cmd_create(struct transaction *transaction,\n \t\t\t\t    struct strbuf *input, const char *next)\n {\n \tstruct strbuf err = STRBUF_INIT;\n@@ -229,7 +229,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,\n \tif (*next != line_termination)\n \t\tdie(\"create %s: extra input: %s\", refname, next);\n \n-\tif (ref_transaction_create(transaction, refname, new_sha1,\n+\tif (transaction_create_ref(transaction, refname, new_sha1,\n \t\t\t\t   update_flags, msg, &err))\n \t\tdie(\"%s\", err.buf);\n \n@@ -240,7 +240,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,\n \treturn next;\n }\n \n-static const char *parse_cmd_delete(struct ref_transaction *transaction,\n+static const char *parse_cmd_delete(struct transaction *transaction,\n \t\t\t\t    struct strbuf *input, const char *next)\n {\n \tstruct strbuf err = STRBUF_INIT;\n@@ -264,7 +264,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,\n \tif (*next != line_termination)\n \t\tdie(\"delete %s: extra input: %s\", refname, next);\n \n-\tif (ref_transaction_delete(transaction, refname, old_sha1,\n+\tif (transaction_delete_ref(transaction, refname, old_sha1,\n \t\t\t\t   update_flags, have_old, msg, &err))\n \t\tdie(\"%s\", err.buf);\n \n@@ -275,7 +275,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,\n \treturn next;\n }\n \n-static const char *parse_cmd_verify(struct ref_transaction *transaction,\n+static const char *parse_cmd_verify(struct transaction *transaction,\n \t\t\t\t    struct strbuf *input, const char *next)\n {\n \tstruct strbuf err = STRBUF_INIT;\n@@ -300,7 +300,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,\n \tif (*next != line_termination)\n \t\tdie(\"verify %s: extra input: %s\", refname, next);\n \n-\tif (ref_transaction_update(transaction, refname, new_sha1, old_sha1,\n+\tif (transaction_update_ref(transaction, refname, new_sha1, old_sha1,\n \t\t\t\t   update_flags, have_old, msg, &err))\n \t\tdie(\"%s\", err.buf);\n \n@@ -320,7 +320,7 @@ static const char *parse_cmd_option(struct strbuf *input, const char *next)\n \treturn next + 8;\n }\n \n-static void update_refs_stdin(struct ref_transaction *transaction)\n+static void update_refs_stdin(struct transaction *transaction)\n {\n \tstruct strbuf input = STRBUF_INIT;\n \tconst char *next;\n@@ -376,9 +376,9 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)\n \n \tif (read_stdin) {\n \t\tstruct strbuf err = STRBUF_INIT;\n-\t\tstruct ref_transaction *transaction;\n+\t\tstruct transaction *transaction;\n \n-\t\ttransaction = ref_transaction_begin(&err);\n+\t\ttransaction = transaction_begin(&err);\n \t\tif (!transaction)\n \t\t\tdie(\"%s\", err.buf);\n \t\tif (delete || no_deref || argc > 0)\n@@ -386,9 +386,9 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)\n \t\tif (end_null)\n \t\t\tline_termination = '\\0';\n \t\tupdate_refs_stdin(transaction);\n-\t\tif (ref_transaction_commit(transaction, &err))\n+\t\tif (transaction_commit(transaction, &err))\n \t\t\tdie(\"%s\", err.buf);\n-\t\tref_transaction_free(transaction);\n+\t\ttransaction_free(transaction);\n \t\tstrbuf_release(&err);\n \t\treturn 0;\n \t}\ndiff --git a/fast-import.c b/fast-import.c\nindex d0bd285..152d944 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -1687,7 +1687,7 @@ found_entry:\n static int update_branch(struct branch *b)\n {\n \tstatic const char *msg = \"fast-import\";\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tunsigned char old_sha1[20];\n \tstruct strbuf err = STRBUF_INIT;\n \n@@ -1713,17 +1713,17 @@ static int update_branch(struct branch *b)\n \t\t\treturn -1;\n \t\t}\n \t}\n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_update(transaction, b->name, b->sha1, old_sha1,\n+\t    transaction_update_ref(transaction, b->name, b->sha1, old_sha1,\n \t\t\t\t   0, 1, msg, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n-\t\tref_transaction_free(transaction);\n+\t    transaction_commit(transaction, &err)) {\n+\t\ttransaction_free(transaction);\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t\treturn -1;\n \t}\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \tstrbuf_release(&err);\n \treturn 0;\n }\n@@ -1745,9 +1745,9 @@ static void dump_tags(void)\n \tstruct tag *t;\n \tstruct strbuf ref_name = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction) {\n \t\tfailure |= error(\"%s\", err.buf);\n \t\tgoto cleanup;\n@@ -1756,17 +1756,17 @@ static void dump_tags(void)\n \t\tstrbuf_reset(&ref_name);\n \t\tstrbuf_addf(&ref_name, \"refs/tags/%s\", t->name);\n \n-\t\tif (ref_transaction_update(transaction, ref_name.buf, t->sha1,\n+\t\tif (transaction_update_ref(transaction, ref_name.buf, t->sha1,\n \t\t\t\t\t   NULL, 0, 0, msg, &err)) {\n \t\t\tfailure |= error(\"%s\", err.buf);\n \t\t\tgoto cleanup;\n \t\t}\n \t}\n-\tif (ref_transaction_commit(transaction, &err))\n+\tif (transaction_commit(transaction, &err))\n \t\tfailure |= error(\"%s\", err.buf);\n \n  cleanup:\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \tstrbuf_release(&ref_name);\n \tstrbuf_release(&err);\n }\ndiff --git a/refs.c b/refs.c\nindex 150c980..f0f0d23 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -26,7 +26,7 @@ static unsigned char refname_disposition[256] = {\n };\n \n /*\n- * Used as a flag to ref_transaction_delete when a loose ref is being\n+ * Used as a flag to transaction_delete_ref when a loose ref is being\n  * pruned.\n  */\n #define REF_ISPRUNING\t0x0100\n@@ -2533,23 +2533,23 @@ static void try_remove_empty_parents(char *name)\n /* make sure nobody touched the ref, and unlink */\n static void prune_ref(struct ref_to_prune *r)\n {\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (check_refname_format(r->name, 0))\n \t\treturn;\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_delete(transaction, r->name, r->sha1,\n+\t    transaction_delete_ref(transaction, r->name, r->sha1,\n \t\t\t\t   REF_ISPRUNING, 1, NULL, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n-\t\tref_transaction_free(transaction);\n+\t    transaction_commit(transaction, &err)) {\n+\t\ttransaction_free(transaction);\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&err);\n \t\treturn;\n \t}\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \tstrbuf_release(&err);\n \ttry_remove_empty_parents(r->name);\n }\n@@ -2711,20 +2711,20 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)\n \n int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n {\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_delete(transaction, refname, sha1, delopt,\n+\t    transaction_delete_ref(transaction, refname, sha1, delopt,\n \t\t\t\t   sha1 && !is_null_sha1(sha1), NULL, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\t    transaction_commit(transaction, &err)) {\n \t\terror(\"%s\", err.buf);\n-\t\tref_transaction_free(transaction);\n+\t\ttransaction_free(transaction);\n \t\tstrbuf_release(&err);\n \t\treturn 1;\n \t}\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \tstrbuf_release(&err);\n \treturn 0;\n }\n@@ -3543,9 +3543,9 @@ struct ref_update {\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+enum transaction_state {\n+\tTRANSACTION_OPEN   = 0,\n+\tTRANSACTION_CLOSED = 1\n };\n \n /*\n@@ -3553,21 +3553,21 @@ enum ref_transaction_state {\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+struct transaction {\n \tstruct ref_update **updates;\n \tsize_t alloc;\n \tsize_t nr;\n-\tenum ref_transaction_state state;\n+\tenum transaction_state state;\n };\n \n-struct ref_transaction *ref_transaction_begin(struct strbuf *err)\n+struct transaction *transaction_begin(struct strbuf *err)\n {\n \tassert(err);\n \n-\treturn xcalloc(1, sizeof(struct ref_transaction));\n+\treturn xcalloc(1, sizeof(struct transaction));\n }\n \n-void ref_transaction_free(struct ref_transaction *transaction)\n+void transaction_free(struct transaction *transaction)\n {\n \tint i;\n \n@@ -3582,7 +3582,7 @@ void ref_transaction_free(struct ref_transaction *transaction)\n \tfree(transaction);\n }\n \n-static struct ref_update *add_update(struct ref_transaction *transaction,\n+static struct ref_update *add_update(struct transaction *transaction,\n \t\t\t\t     const char *refname)\n {\n \tsize_t len = strlen(refname);\n@@ -3594,7 +3594,7 @@ static struct ref_update *add_update(struct ref_transaction *transaction,\n \treturn update;\n }\n \n-int ref_transaction_update(struct ref_transaction *transaction,\n+int transaction_update_ref(struct 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@@ -3605,7 +3605,7 @@ int ref_transaction_update(struct ref_transaction *transaction,\n \n \tassert(err);\n \n-\tif (transaction->state != REF_TRANSACTION_OPEN)\n+\tif (transaction->state != TRANSACTION_OPEN)\n \t\tdie(\"BUG: update called for transaction that is not open\");\n \n \tif (have_old && !old_sha1)\n@@ -3629,23 +3629,23 @@ int ref_transaction_update(struct ref_transaction *transaction,\n \treturn 0;\n }\n \n-int ref_transaction_create(struct ref_transaction *transaction,\n+int transaction_create_ref(struct transaction *transaction,\n \t\t\t   const char *refname,\n \t\t\t   const unsigned char *new_sha1,\n \t\t\t   int flags, const char *msg,\n \t\t\t   struct strbuf *err)\n {\n-\treturn ref_transaction_update(transaction, refname, new_sha1,\n+\treturn transaction_update_ref(transaction, refname, new_sha1,\n \t\t\t\t      null_sha1, flags, 1, msg, err);\n }\n \n-int ref_transaction_delete(struct ref_transaction *transaction,\n+int transaction_delete_ref(struct transaction *transaction,\n \t\t\t   const char *refname,\n \t\t\t   const unsigned char *old_sha1,\n \t\t\t   int flags, int have_old, const char *msg,\n \t\t\t   struct strbuf *err)\n {\n-\treturn ref_transaction_update(transaction, refname, null_sha1,\n+\treturn transaction_update_ref(transaction, refname, null_sha1,\n \t\t\t\t      old_sha1, flags, have_old, msg, err);\n }\n \n@@ -3653,17 +3653,17 @@ int update_ref(const char *action, const char *refname,\n \t       const unsigned char *sha1, const unsigned char *oldval,\n \t       int flags, enum action_on_err onerr)\n {\n-\tstruct ref_transaction *t;\n+\tstruct transaction *t;\n \tstruct strbuf err = STRBUF_INIT;\n \n-\tt = ref_transaction_begin(&err);\n+\tt = transaction_begin(&err);\n \tif (!t ||\n-\t    ref_transaction_update(t, refname, sha1, oldval, flags,\n+\t    transaction_update_ref(t, refname, sha1, oldval, flags,\n \t\t\t\t   !!oldval, action, &err) ||\n-\t    ref_transaction_commit(t, &err)) {\n+\t    transaction_commit(t, &err)) {\n \t\tconst char *str = \"update_ref failed for ref '%s': %s\";\n \n-\t\tref_transaction_free(t);\n+\t\ttransaction_free(t);\n \t\tswitch (onerr) {\n \t\tcase UPDATE_REFS_MSG_ON_ERR:\n \t\t\terror(str, refname, err.buf);\n@@ -3678,7 +3678,7 @@ int update_ref(const char *action, const char *refname,\n \t\treturn 1;\n \t}\n \tstrbuf_release(&err);\n-\tref_transaction_free(t);\n+\ttransaction_free(t);\n \treturn 0;\n }\n \n@@ -3706,8 +3706,8 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,\n \treturn 0;\n }\n \n-int ref_transaction_commit(struct ref_transaction *transaction,\n-\t\t\t   struct strbuf *err)\n+int transaction_commit(struct transaction *transaction,\n+\t\t       struct strbuf *err)\n {\n \tint ret = 0, delnum = 0, i;\n \tconst char **delnames;\n@@ -3716,11 +3716,11 @@ int ref_transaction_commit(struct ref_transaction *transaction,\n \n \tassert(err);\n \n-\tif (transaction->state != REF_TRANSACTION_OPEN)\n+\tif (transaction->state != TRANSACTION_OPEN)\n \t\tdie(\"BUG: commit called for transaction that is not open\");\n \n \tif (!n) {\n-\t\ttransaction->state = REF_TRANSACTION_CLOSED;\n+\t\ttransaction->state = TRANSACTION_CLOSED;\n \t\treturn 0;\n \t}\n \n@@ -3799,7 +3799,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,\n \tclear_loose_ref_cache(&ref_cache);\n \n cleanup:\n-\ttransaction->state = REF_TRANSACTION_CLOSED;\n+\ttransaction->state = TRANSACTION_CLOSED;\n \n \tfor (i = 0; i < n; i++)\n \t\tif (updates[i]->lock)\ndiff --git a/refs.h b/refs.h\nindex 7d675b7..556adfd 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -11,22 +11,22 @@ struct ref_lock {\n };\n \n /*\n- * A ref_transaction represents a collection of ref updates\n+ * A transaction represents a collection of ref updates\n  * that should succeed or fail together.\n  *\n  * Calling sequence\n  * ----------------\n- * - Allocate and initialize a `struct ref_transaction` by calling\n- *   `ref_transaction_begin()`.\n+ * - Allocate and initialize a `struct transaction` by calling\n+ *   `transaction_begin()`.\n  *\n  * - List intended ref updates by calling functions like\n- *   `ref_transaction_update()` and `ref_transaction_create()`.\n+ *   `transaction_update_ref()` and `transaction_create_ref()`.\n  *\n- * - Call `ref_transaction_commit()` to execute the transaction.\n+ * - Call `transaction_commit()` to execute the transaction.\n  *   If this succeeds, the ref updates will have taken place and\n  *   the transaction cannot be rolled back.\n  *\n- * - At any time call `ref_transaction_free()` to discard the\n+ * - At any time call `transaction_free()` to discard the\n  *   transaction and free associated resources.  In particular,\n  *   this rolls back the transaction if it has not been\n  *   successfully committed.\n@@ -42,7 +42,7 @@ struct ref_lock {\n  * The message is appended to err without first clearing err.\n  * err will not be '\\n' terminated.\n  */\n-struct ref_transaction;\n+struct transaction;\n \n /*\n  * Bit values set in the flags argument passed to each_ref_fn():\n@@ -181,8 +181,8 @@ extern int is_branch(const char *refname);\n extern int peel_ref(const char *refname, unsigned char *sha1);\n \n /*\n- * Flags controlling lock_any_ref_for_update(), ref_transaction_update(),\n- * ref_transaction_create(), etc.\n+ * Flags controlling lock_any_ref_for_update(), transaction_update_ref(),\n+ * transaction_create_ref(), etc.\n  * REF_NODEREF: act on the ref directly, instead of dereferencing\n  *              symbolic references.\n  * REF_DELETING: tolerate broken refs\n@@ -269,13 +269,13 @@ enum action_on_err {\n \n /*\n  * Begin a reference transaction.  The reference transaction must\n- * be freed by calling ref_transaction_free().\n+ * be freed by calling transaction_free().\n  */\n-struct ref_transaction *ref_transaction_begin(struct strbuf *err);\n+struct transaction *transaction_begin(struct strbuf *err);\n \n /*\n  * The following functions add a reference check or update to a\n- * ref_transaction.  In all of them, refname is the name of the\n+ * transaction.  In all of them, refname is the name of the\n  * reference to be affected.  The functions make internal copies of\n  * refname and msg, so the caller retains ownership of these parameters.\n  * flags can be REF_NODEREF; it is passed to update_ref_lock().\n@@ -291,7 +291,7 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);\n  * means that the transaction as a whole has failed and will need to be\n  * rolled back.\n  */\n-int ref_transaction_update(struct ref_transaction *transaction,\n+int transaction_update_ref(struct 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@@ -307,7 +307,7 @@ int ref_transaction_update(struct ref_transaction *transaction,\n  * means that the transaction as a whole has failed and will need to be\n  * rolled back.\n  */\n-int ref_transaction_create(struct ref_transaction *transaction,\n+int transaction_create_ref(struct transaction *transaction,\n \t\t\t   const char *refname,\n \t\t\t   const unsigned char *new_sha1,\n \t\t\t   int flags, const char *msg,\n@@ -321,7 +321,7 @@ int ref_transaction_create(struct ref_transaction *transaction,\n  * means that the transaction as a whole has failed and will need to be\n  * rolled back.\n  */\n-int ref_transaction_delete(struct ref_transaction *transaction,\n+int transaction_delete_ref(struct transaction *transaction,\n \t\t\t   const char *refname,\n \t\t\t   const unsigned char *old_sha1,\n \t\t\t   int flags, int have_old, const char *msg,\n@@ -337,13 +337,13 @@ int ref_transaction_delete(struct ref_transaction *transaction,\n #define TRANSACTION_NAME_CONFLICT -1\n /* All other errors. */\n #define TRANSACTION_GENERIC_ERROR -2\n-int ref_transaction_commit(struct ref_transaction *transaction,\n-\t\t\t   struct strbuf *err);\n+int transaction_commit(struct transaction *transaction,\n+\t\t       struct strbuf *err);\n \n /*\n  * Free an existing transaction and all associated data.\n  */\n-void ref_transaction_free(struct ref_transaction *transaction);\n+void transaction_free(struct transaction *transaction);\n \n /** Lock a ref and then write its file */\n int update_ref(const char *action, const char *refname,\ndiff --git a/sequencer.c b/sequencer.c\nindex a03d4fa..f888005 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -238,7 +238,7 @@ static int error_dirty_index(struct replay_opts *opts)\n static int fast_forward_to(const unsigned char *to, const unsigned char *from,\n \t\t\tint unborn, struct replay_opts *opts)\n {\n-\tstruct ref_transaction *transaction;\n+\tstruct transaction *transaction;\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n \n@@ -248,13 +248,13 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,\n \n \tstrbuf_addf(&sb, \"%s: fast-forward\", action_name(opts));\n \n-\ttransaction = ref_transaction_begin(&err);\n+\ttransaction = transaction_begin(&err);\n \tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\",\n+\t    transaction_update_ref(transaction, \"HEAD\",\n \t\t\t\t   to, unborn ? null_sha1 : from,\n \t\t\t\t   0, 1, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n-\t\tref_transaction_free(transaction);\n+\t    transaction_commit(transaction, &err)) {\n+\t\ttransaction_free(transaction);\n \t\terror(\"%s\", err.buf);\n \t\tstrbuf_release(&sb);\n \t\tstrbuf_release(&err);\n@@ -263,7 +263,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,\n \n \tstrbuf_release(&sb);\n \tstrbuf_release(&err);\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \treturn 0;\n }\n \ndiff --git a/walker.c b/walker.c\nindex f149371..f1d5e9b 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -253,7 +253,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,\n {\n \tstruct strbuf refname = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n-\tstruct ref_transaction *transaction = NULL;\n+\tstruct transaction *transaction = NULL;\n \tunsigned char *sha1 = xmalloc(targets * 20);\n \tchar *msg = NULL;\n \tint i, ret = -1;\n@@ -261,7 +261,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,\n \tsave_commit_buffer = 0;\n \n \tif (write_ref) {\n-\t\ttransaction = ref_transaction_begin(&err);\n+\t\ttransaction = transaction_begin(&err);\n \t\tif (!transaction) {\n \t\t\terror(\"%s\", err.buf);\n \t\t\tgoto done;\n@@ -298,7 +298,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,\n \t\t\tcontinue;\n \t\tstrbuf_reset(&refname);\n \t\tstrbuf_addf(&refname, \"refs/%s\", write_ref[i]);\n-\t\tif (ref_transaction_update(transaction, refname.buf,\n+\t\tif (transaction_update_ref(transaction, refname.buf,\n \t\t\t\t\t   &sha1[20 * i], NULL, 0, 0,\n \t\t\t\t\t   msg ? msg : \"fetch (unknown)\",\n \t\t\t\t\t   &err)) {\n@@ -306,7 +306,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,\n \t\t\tgoto done;\n \t\t}\n \t}\n-\tif (ref_transaction_commit(transaction, &err)) {\n+\tif (transaction_commit(transaction, &err)) {\n \t\terror(\"%s\", err.buf);\n \t\tgoto done;\n \t}\n@@ -314,7 +314,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,\n \tret = 0;\n \n done:\n-\tref_transaction_free(transaction);\n+\ttransaction_free(transaction);\n \tfree(msg);\n \tfree(sha1);\n \tstrbuf_release(&err);\n-- \n2.2.0\n"},{"id":"253029","messageId":"1417681763-32334-6-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 05/13] refs.c: rename transaction.updates to transaction.ref_updates","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:15Z","receivedAt":"2014-12-04T08:29:15Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"The updates are only holding refs not reflogs, so express it to the reader.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n\nNotes:\n    remotes/origin/sb/ref-transaction-reflog\n    \n    no changes since last review\n\n refs.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f0f0d23..58de60c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3554,7 +3554,7 @@ enum transaction_state {\n  * as atomically as possible.  This structure is opaque to callers.\n  */\n struct transaction {\n-\tstruct ref_update **updates;\n+\tstruct ref_update **ref_updates;\n \tsize_t alloc;\n \tsize_t nr;\n \tenum transaction_state state;\n@@ -3575,10 +3575,10 @@ void transaction_free(struct transaction *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\tfree(transaction->ref_updates[i]->msg);\n+\t\tfree(transaction->ref_updates[i]);\n \t}\n-\tfree(transaction->updates);\n+\tfree(transaction->ref_updates);\n \tfree(transaction);\n }\n \n@@ -3589,8 +3589,8 @@ static struct ref_update *add_update(struct transaction *transaction,\n \tstruct ref_update *update = xcalloc(1, sizeof(*update) + len + 1);\n \n \tstrcpy((char *)update->refname, refname);\n-\tALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);\n-\ttransaction->updates[transaction->nr++] = update;\n+\tALLOC_GROW(transaction->ref_updates, transaction->nr + 1, transaction->alloc);\n+\ttransaction->ref_updates[transaction->nr++] = update;\n \treturn update;\n }\n \n@@ -3712,7 +3712,7 @@ int transaction_commit(struct transaction *transaction,\n \tint ret = 0, delnum = 0, i;\n \tconst char **delnames;\n \tint n = transaction->nr;\n-\tstruct ref_update **updates = transaction->updates;\n+\tstruct ref_update **updates = transaction->ref_updates;\n \n \tassert(err);\n \n-- \n2.2.0\n"},{"id":"253039","messageId":"1417681763-32334-7-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 06/13] refs.c: add a transaction function to truncate or append a reflog entry","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:16Z","receivedAt":"2014-12-04T08:29:16Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This patch introduces two transaction functions for dealing with reflog\nchanges. The first function transaction_truncate_reflog can be used,\nwhen a rebuilding of the reflog is desired, e.g. on reflog expire.\nThe transaction_update_reflog function can be used to amend a line to the\nreflog.\n\nWe cannot handle reflogs the same way we handle refs because\nof naming conflicts in the file system.\nIf renaming a ref from foo/foo to foo or the other way round,\nwe need to be careful as we need the basic foo/ from being a\ndirectory to be a file or vice versa. When handling the refs\nthis can be solved easily by first recording all we want into\nthe packed refs file and then deleting all the loose refs. By\ndoing it that way, we always have a valid state on disk.\n\nWe don't have an equivalent of a packed refs file when dealing\nwith reflog updates, but the API for updating the refs turned\nout to be conveniant, so let's introduce an intermediate file\noutside the $GIT_DIR/logs/refs/heads/ directory helping us\navoiding this naming conflict for the reflogs. The intermediate\nlock file, in which we build up the new reflog will live under\n$GIT_DIR/logs/lock/refs/heads/ so the files\n        $GIT_DIR/logs/lock/refs/heads/foo.lock and\n        $GIT_DIR/logs/lock/refs/heads/foo/foo.lock\ndo not collide.\n\nWhen using transaction_update_reflog, only write to the reflog iff\nmsg is non-NULL.\n\nDuring one transaction we allow to make multiple reflog updates to the\nsame ref. This means we only need to lock the reflog once, during the first\nupdate that touches the reflog, and that all further updates can just write the\nreflog entry since the reflog is already locked.\n\nThis allows us to write code such as:\n\n  t = transaction_begin()\n  transaction_truncate_reflog(t, \"foo\");\n  loop-over-something...\n     if (want_reflog_entry(...))\n         transaction_reflog_update(t, \"foo\", 0, <message>);\n  transaction_commit(t)\n\nwhere we first truncate the reflog and then build the new content one line\nat a time.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n\nNotes:\n    This is a complete rewrite of this patch, lots of design ideas\n    have been born in fruitful discussions with Jonathan.\n    \n    Personally I am not yet happy about the file copying in\n    transaction_update_reflog.\n\n refs.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--\n refs.h |  20 +++++++++++\n 2 files changed, 146 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 58de60c..c411af9 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3557,6 +3557,12 @@ struct transaction {\n \tstruct ref_update **ref_updates;\n \tsize_t alloc;\n \tsize_t nr;\n+\n+\t/*\n+\t * Sorted list of reflogs to be committed,\n+\t * the util points to the lock_file.\n+\t */\n+\tstruct string_list reflog_updates;\n \tenum transaction_state state;\n };\n \n@@ -3564,7 +3570,10 @@ struct transaction *transaction_begin(struct strbuf *err)\n {\n \tassert(err);\n \n-\treturn xcalloc(1, sizeof(struct transaction));\n+\tstruct transaction *ret = xcalloc(1, sizeof(struct transaction));\n+\tret->reflog_updates.strdup_strings = 1;\n+\n+\treturn ret;\n }\n \n void transaction_free(struct transaction *transaction)\n@@ -3629,6 +3638,113 @@ int transaction_update_ref(struct transaction *transaction,\n \treturn 0;\n }\n \n+int transaction_truncate_reflog(struct transaction *transaction,\n+\t\t\t\tconst char *refname,\n+\t\t\t\tstruct strbuf *err)\n+{\n+\tstruct lock_file *lock;\n+\tstruct string_list_item *item;\n+\n+\tif (transaction->state != TRANSACTION_OPEN)\n+\t\tdie(\"BUG: update_reflog called for transaction that is not open\");\n+\n+\titem = string_list_insert(&transaction->reflog_updates, refname);\n+\tif (item->util) {\n+\t\tlock = item->util;\n+\t\tif (lseek(lock->fd, 0, SEEK_SET) < 0 ||\n+\t\t\tftruncate(lock->fd, 0)) {\n+\t\t\tstrbuf_addf(err, \"cannot truncate reflog '%s': %s\",\n+\t\t\t\t    refname, strerror(errno));\n+\t\t\tgoto failure;\n+\t\t}\n+\n+\t} else {\n+\t\tchar *path = git_path(\"logs/locks/%s\", refname);\n+\t\titem->util = xcalloc(1, sizeof(struct lock_file));\n+\t\tlock = item->util;\n+\t\tif (safe_create_leading_directories(path)) {\n+\t\t\tstrbuf_addf(err, \"could not create leading directories of '%s': %s\",\n+\t\t\t\t    path, strerror(errno));\n+\t\t\tgoto failure;\n+\t\t}\n+\n+\t\tif (hold_lock_file_for_update(lock, path, 0) < 0) {\n+\t\t\tunable_to_lock_message(path, errno, err);\n+\t\t\tgoto failure;\n+\t\t}\n+\t\t/* The file is empty, no need to truncate. */\n+\t}\n+\treturn 0;\n+failure:\n+\ttransaction->state = TRANSACTION_CLOSED;\n+\treturn -1;\n+}\n+\n+\n+int transaction_update_reflog(struct 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      const char *email,\n+\t\t\t      unsigned long timestamp, int tz,\n+\t\t\t      const char *msg,\n+\t\t\t      struct strbuf *err)\n+{\n+\tstruct lock_file *lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct string_list_item *item;\n+\n+\tif (transaction->state != TRANSACTION_OPEN)\n+\t\tdie(\"BUG: update_reflog called for transaction that is not open\");\n+\n+\titem = string_list_insert(&transaction->reflog_updates, refname);\n+\tif (!item->util) {\n+\t\tchar buf[1024];\n+\t\tint infd, nread;\n+\t\tchar *path = git_path(\"logs/locks/%s\", refname);\n+\t\titem->util = xcalloc(1, sizeof(struct lock_file));\n+\t\tlock = item->util;\n+\t\tif (safe_create_leading_directories(path)) {\n+\t\t\tstrbuf_addf(err, \"could not create leading directories of '%s': %s\",\n+\t\t\t\t    path, strerror(errno));\n+\t\t\tgoto failure;\n+\t\t}\n+\n+\t\tif (hold_lock_file_for_update(lock, path, 0) < 0) {\n+\t\t\tunable_to_lock_message(path, errno, err);\n+\t\t\tgoto failure;\n+\t\t}\n+\n+\t\t/* copy existing reflog over */\n+\t\tinfd = open(git_path(\"logs/%s\", refname), O_RDONLY);\n+\t\twhile ((nread = read(infd, buf, 1024)) > 0)\n+\t\t\twrite_in_full(lock->fd, buf, nread);\n+\t\tif (nread < 0) {\n+\t\t\tstrbuf_addf(err, \"could not read reflog '%s': %s\", refname, strerror(errno));\n+\t\t\tgoto failure;\n+\t\t}\n+\t\tclose(infd);\n+\t}\n+\tlock = item->util;\n+\n+\tif (email)\n+\t\tstrbuf_addf(&buf, \"%s %lu %+05d\", email, timestamp, tz);\n+\n+\tif (msg &&\n+\t    log_ref_write_fd(lock->fd, old_sha1, new_sha1, buf.buf, msg)) {\n+\t\tstrbuf_addf(err, \"Could not write to reflog: %s. %s\",\n+\t\t\t    refname, strerror(errno));\n+\t\tgoto failure;\n+\t}\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+failure:\n+\tstrbuf_release(&buf);\n+\ttransaction->state = TRANSACTION_CLOSED;\n+\treturn -1;\n+}\n+\n int transaction_create_ref(struct transaction *transaction,\n \t\t\t   const char *refname,\n \t\t\t   const unsigned char *new_sha1,\n@@ -3713,13 +3829,14 @@ int transaction_commit(struct transaction *transaction,\n \tconst char **delnames;\n \tint n = transaction->nr;\n \tstruct ref_update **updates = transaction->ref_updates;\n+\tstruct string_list_item *item;\n \n \tassert(err);\n \n \tif (transaction->state != TRANSACTION_OPEN)\n \t\tdie(\"BUG: commit called for transaction that is not open\");\n \n-\tif (!n) {\n+\tif (!n && !transaction->reflog_updates.nr) {\n \t\ttransaction->state = TRANSACTION_CLOSED;\n \t\treturn 0;\n \t}\n@@ -3796,6 +3913,13 @@ int transaction_commit(struct transaction *transaction,\n \t}\n \tfor (i = 0; i < delnum; i++)\n \t\tunlink_or_warn(git_path(\"logs/%s\", delnames[i]));\n+\n+\t/* Commit all reflog updates*/\n+\tfor_each_string_list_item(item, &transaction->reflog_updates) {\n+\t\tstruct lock_file *lock = item->util;\n+\t\tcommit_lock_file_to(lock, git_path(\"logs/%s\", item->string));\n+\t}\n+\n \tclear_loose_ref_cache(&ref_cache);\n \n cleanup:\ndiff --git a/refs.h b/refs.h\nindex 556adfd..1afc72c 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -328,6 +328,26 @@ int transaction_delete_ref(struct transaction *transaction,\n \t\t\t   struct strbuf *err);\n \n /*\n+ * Truncate the reflog for the given refname.\n+ */\n+int transaction_truncate_reflog(struct transaction *transaction,\n+\t\t\t\tconst char *refname,\n+\t\t\t\tstruct strbuf *err);\n+\n+/*\n+ * Append a reflog entry for refname. If msg is NULL no update will be\n+ * written to the log.\n+ */\n+int transaction_update_reflog(struct 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      const char *email,\n+\t\t\t      unsigned long timestamp, int tz,\n+\t\t\t      const char *msg,\n+\t\t\t      struct strbuf *err);\n+\n+/*\n  * Commit all of the changes that have been queued in transaction, as\n  * atomically as possible.\n  *\n-- \n2.2.0\n"},{"id":"253031","messageId":"1417681763-32334-8-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 07/13] reflog.c: use a reflog transaction when writing during expire","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:17Z","receivedAt":"2014-12-04T08:29:17Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nUse a transaction for all updates during expire_reflog.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n\nNotes:\n    Jonathan writes:\n    > This doesn't match the signature of each_reflog_ent_fn.  Would putting\n    > err in the cb_data struct work?\n    \n    That's what I do in this update.\n\n builtin/reflog.c | 84 ++++++++++++++++++++++++--------------------------------\n 1 file changed, 36 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 2d85d26..e13427c 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -33,7 +33,8 @@ struct cmd_reflog_expire_cb {\n };\n \n struct expire_reflog_cb {\n-\tFILE *newlog;\n+\tstruct transaction *t;\n+\tconst char *refname;\n \tenum {\n \t\tUE_NORMAL,\n \t\tUE_ALWAYS,\n@@ -43,6 +44,7 @@ struct expire_reflog_cb {\n \tunsigned long mark_limit;\n \tstruct cmd_reflog_expire_cb *cmd;\n \tunsigned char last_kept_sha1[20];\n+\tstruct strbuf *err;\n };\n \n struct collected_reflog {\n@@ -316,20 +318,18 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,\n \tif (cb->cmd->recno && --(cb->cmd->recno) == 0)\n \t\tgoto prune;\n \n-\tif (cb->newlog) {\n-\t\tchar sign = (tz < 0) ? '-' : '+';\n-\t\tint zone = (tz < 0) ? (-tz) : tz;\n-\t\tfprintf(cb->newlog, \"%s %s %s %lu %c%04d\\t%s\",\n-\t\t\tsha1_to_hex(osha1), sha1_to_hex(nsha1),\n-\t\t\temail, timestamp, sign, zone,\n-\t\t\tmessage);\n+\tif (cb->t) {\n+\t\tif (transaction_update_reflog(cb->t, cb->refname, nsha1, osha1,\n+\t\t\t\t\t      email, timestamp, tz, message,\n+\t\t\t\t\t      cb->err))\n+\t\t\treturn -1;\n \t\thashcpy(cb->last_kept_sha1, nsha1);\n \t}\n \tif (cb->cmd->verbose)\n \t\tprintf(\"keep %s\", message);\n \treturn 0;\n  prune:\n-\tif (!cb->newlog)\n+\tif (!cb->t)\n \t\tprintf(\"would prune %s\", message);\n \telse if (cb->cmd->verbose)\n \t\tprintf(\"prune %s\", message);\n@@ -353,29 +353,26 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n {\n \tstruct cmd_reflog_expire_cb *cmd = cb_data;\n \tstruct expire_reflog_cb cb;\n-\tstruct ref_lock *lock;\n-\tchar *log_file, *newlog_path = NULL;\n \tstruct commit *tip_commit;\n \tstruct commit_list *tips;\n+\tstruct strbuf err = STRBUF_INIT;\n \tint status = 0;\n \n \tmemset(&cb, 0, sizeof(cb));\n+\tcb.refname = ref;\n+\tcb.err = &err;\n \n-\t/*\n-\t * we take the lock for the ref itself to prevent it from\n-\t * getting updated.\n-\t */\n-\tlock = lock_any_ref_for_update(ref, sha1, 0, NULL);\n-\tif (!lock)\n-\t\treturn error(\"cannot lock ref '%s'\", ref);\n-\tlog_file = git_pathdup(\"logs/%s\", ref);\n \tif (!reflog_exists(ref))\n \t\tgoto finish;\n-\tif (!cmd->dry_run) {\n-\t\tnewlog_path = git_pathdup(\"logs/%s.lock\", ref);\n-\t\tcb.newlog = fopen(newlog_path, \"w\");\n+\tcb.t = transaction_begin(&err);\n+\tif (!cb.t) {\n+\t\tstatus |= error(\"%s\", err.buf);\n+\t\tgoto cleanup;\n+\t}\n+\tif (transaction_truncate_reflog(cb.t, cb.refname, &err)) {\n+\t\tstatus |= error(\"%s\", err.buf);\n+\t\tgoto cleanup;\n \t}\n-\n \tcb.cmd = cmd;\n \n \tif (!cmd->expire_unreachable || !strcmp(ref, \"HEAD\")) {\n@@ -407,7 +404,10 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \t\tmark_reachable(&cb);\n \t}\n \n-\tfor_each_reflog_ent(ref, expire_reflog_ent, &cb);\n+\tif (for_each_reflog_ent(ref, expire_reflog_ent, &cb)) {\n+\t\tstatus |= error(\"%s\", err.buf);\n+\t\tgoto cleanup;\n+\t}\n \n \tif (cb.unreachable_expire_kind != UE_ALWAYS) {\n \t\tif (cb.unreachable_expire_kind == UE_HEAD) {\n@@ -420,32 +420,20 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \t\t}\n \t}\n  finish:\n-\tif (cb.newlog) {\n-\t\tif (fclose(cb.newlog)) {\n-\t\t\tstatus |= error(\"%s: %s\", strerror(errno),\n-\t\t\t\t\tnewlog_path);\n-\t\t\tunlink(newlog_path);\n-\t\t} else if (cmd->updateref &&\n-\t\t\t(write_in_full(lock->lock_fd,\n-\t\t\t\tsha1_to_hex(cb.last_kept_sha1), 40) != 40 ||\n-\t\t\t write_str_in_full(lock->lock_fd, \"\\n\") != 1 ||\n-\t\t\t close_ref(lock) < 0)) {\n-\t\t\tstatus |= error(\"Couldn't write %s\",\n-\t\t\t\t\tlock->lk->filename.buf);\n-\t\t\tunlink(newlog_path);\n-\t\t} else if (rename(newlog_path, log_file)) {\n-\t\t\tstatus |= error(\"cannot rename %s to %s\",\n-\t\t\t\t\tnewlog_path, log_file);\n-\t\t\tunlink(newlog_path);\n-\t\t} else if (cmd->updateref && commit_ref(lock)) {\n-\t\t\tstatus |= error(\"Couldn't set %s\", lock->ref_name);\n-\t\t} else {\n-\t\t\tadjust_shared_perm(log_file);\n+\tif (!cmd->dry_run) {\n+\t\tif (cmd->updateref &&\n+\t\t    transaction_update_ref(cb.t, cb.refname,\n+\t\t\t\t\t   cb.last_kept_sha1, sha1,\n+\t\t\t\t\t   0, 1, NULL, &err)) {\n+\t\t\tstatus |= error(\"%s\", err.buf);\n+\t\t\tgoto cleanup;\n \t\t}\n+\t\tif (transaction_commit(cb.t, &err))\n+\t\t\tstatus |= error(\"%s\", err.buf);\n \t}\n-\tfree(newlog_path);\n-\tfree(log_file);\n-\tunlock_ref(lock);\n+ cleanup:\n+\ttransaction_free(cb.t);\n+\tstrbuf_release(&err);\n \treturn status;\n }\n \n-- \n2.2.0\n"},{"id":"253032","messageId":"1417681763-32334-9-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 08/13] refs.c: rename log_ref_setup to create_reflog","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:18Z","receivedAt":"2014-12-04T08:29:18Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nlog_ref_setup is used to do several semi-related things:\n* Sometimes it will create a new reflog including missing parent\n  directories and cleaning up any conflicting stale directories\n  in the path.\n* Fill in a filename buffer for the full path to the reflog.\n* Unconditionally re-adjust the permissions for the file.\n\nThis function is only called from two places: In checkout.c, where\nit is always used to create a reflog and in refs.c log_ref_write,\nwhere it is used to create a reflog sometimes, and sometimes just\nto fill in the filename.\n\nRename log_ref_setup to create_reflog and change it to only take the\nrefname as an argument to make its signature similar to delete_reflog\nand reflog_exists. Change create_reflog to ignore log_all_ref_updates\nand \"unconditionally\" create the reflog when called. Since checkout.c\nalways wants to create a reflog we can call create_reflog directly and\navoid the temp-and-log_all_ref_update dance.\n\nIn log_ref_write, only call create_reflog, iff we want to create a reflog\nand if the reflog does not yet exist. This means that for the common case\nwhere the log already exists we now only need to perform a single lstat()\ninstead of a open(O_CREAT)+lstat()+close().\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n builtin/checkout.c |  8 +-------\n refs.c             | 22 +++++++++++++---------\n refs.h             |  8 +++-----\n 3 files changed, 17 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5410dac..8550b6d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -587,19 +587,13 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \tif (opts->new_branch) {\n \t\tif (opts->new_orphan_branch) {\n \t\t\tif (opts->new_branch_log && !log_all_ref_updates) {\n-\t\t\t\tint temp;\n-\t\t\t\tchar log_file[PATH_MAX];\n \t\t\t\tchar *ref_name = mkpath(\"refs/heads/%s\", opts->new_orphan_branch);\n \n-\t\t\t\ttemp = log_all_ref_updates;\n-\t\t\t\tlog_all_ref_updates = 1;\n-\t\t\t\tif (log_ref_setup(ref_name, log_file, sizeof(log_file))) {\n+\t\t\t\tif (create_reflog(ref_name)) {\n \t\t\t\t\tfprintf(stderr, _(\"Can not do reflog for '%s'\\n\"),\n \t\t\t\t\t    opts->new_orphan_branch);\n-\t\t\t\t\tlog_all_ref_updates = temp;\n \t\t\t\t\treturn;\n \t\t\t\t}\n-\t\t\t\tlog_all_ref_updates = temp;\n \t\t\t}\n \t\t}\n \t\telse\ndiff --git a/refs.c b/refs.c\nindex c411af9..bdd3f75 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2941,16 +2941,16 @@ static int copy_msg(char *buf, const char *msg)\n }\n \n /* This function must set a meaningful errno on failure */\n-int log_ref_setup(const char *refname, char *logfile, int bufsize)\n+int create_reflog(const char *refname)\n {\n \tint logfd, oflags = O_APPEND | O_WRONLY;\n+\tchar logfile[PATH_MAX];\n \n-\tgit_snpath(logfile, bufsize, \"logs/%s\", refname);\n-\tif (log_all_ref_updates &&\n-\t    (starts_with(refname, \"refs/heads/\") ||\n-\t     starts_with(refname, \"refs/remotes/\") ||\n-\t     starts_with(refname, \"refs/notes/\") ||\n-\t     !strcmp(refname, \"HEAD\"))) {\n+\tgit_snpath(logfile, sizeof(logfile), \"logs/%s\", refname);\n+\tif (starts_with(refname, \"refs/heads/\") ||\n+\t    starts_with(refname, \"refs/remotes/\") ||\n+\t    starts_with(refname, \"refs/notes/\") ||\n+\t    !strcmp(refname, \"HEAD\")) {\n \t\tif (safe_create_leading_directories(logfile) < 0) {\n \t\t\tint save_errno = errno;\n \t\t\terror(\"unable to create directory for %s\", logfile);\n@@ -3019,16 +3019,20 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n 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 {\n-\tint logfd, result, oflags = O_APPEND | O_WRONLY;\n+\tint logfd, result = 0, oflags = O_APPEND | O_WRONLY;\n \tchar log_file[PATH_MAX];\n \n \tif (log_all_ref_updates < 0)\n \t\tlog_all_ref_updates = !is_bare_repository();\n \n-\tresult = log_ref_setup(refname, log_file, sizeof(log_file));\n+\tif (log_all_ref_updates && !reflog_exists(refname))\n+\t\tresult = create_reflog(refname);\n+\n \tif (result)\n \t\treturn result;\n \n+\tgit_snpath(log_file, sizeof(log_file), \"logs/%s\", refname);\n+\n \tlogfd = open(log_file, oflags);\n \tif (logfd < 0)\n \t\treturn 0;\ndiff --git a/refs.h b/refs.h\nindex 1afc72c..0c75217 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -207,11 +207,6 @@ extern int commit_ref(struct ref_lock *lock);\n /** Release any lock taken but not written. **/\n extern void unlock_ref(struct ref_lock *lock);\n \n-/*\n- * Setup reflog before using. Set errno to something meaningful on failure.\n- */\n-int log_ref_setup(const char *refname, char *logfile, int bufsize);\n-\n /** Reads log for the value of ref during at_time. **/\n extern int read_ref_at(const char *refname, unsigned int flags,\n \t\t       unsigned long at_time, int cnt,\n@@ -221,6 +216,9 @@ extern int read_ref_at(const char *refname, unsigned int flags,\n /** Check if a particular reflog exists */\n extern int reflog_exists(const char *refname);\n \n+/** Create reflog. Set errno to something meaningful on failure. */\n+extern int create_reflog(const char *refname);\n+\n /** Delete a reflog */\n extern int delete_reflog(const char *refname);\n \n-- \n2.2.0\n"},{"id":"253033","messageId":"1417681763-32334-10-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 09/13] refs.c: remove unlock_ref/close_ref/commit_ref from the refs api","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:19Z","receivedAt":"2014-12-04T08:29:19Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nunlock|close|commit_ref can be made static since there are no more external\ncallers.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n refs.c | 24 ++++++++++++------------\n refs.h |  9 ---------\n 2 files changed, 12 insertions(+), 21 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex bdd3f75..1468c00 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2090,6 +2090,16 @@ int refname_match(const char *abbrev_name, const char *full_name)\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+\tif (lock->lk)\n+\t\trollback_lock_file(lock->lk);\n+\tfree(lock->ref_name);\n+\tfree(lock->orig_ref_name);\n+\tfree(lock);\n+}\n+\n /* This function should make sure errno is meaningful on error */\n static struct ref_lock *verify_lock(struct ref_lock *lock,\n \tconst unsigned char *old_sha1, int mustexist)\n@@ -2888,7 +2898,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms\n \treturn 1;\n }\n \n-int close_ref(struct ref_lock *lock)\n+static int close_ref(struct ref_lock *lock)\n {\n \tif (close_lock_file(lock->lk))\n \t\treturn -1;\n@@ -2896,7 +2906,7 @@ int close_ref(struct ref_lock *lock)\n \treturn 0;\n }\n \n-int commit_ref(struct ref_lock *lock)\n+static int commit_ref(struct ref_lock *lock)\n {\n \tif (commit_lock_file(lock->lk))\n \t\treturn -1;\n@@ -2904,16 +2914,6 @@ int commit_ref(struct ref_lock *lock)\n \treturn 0;\n }\n \n-void unlock_ref(struct ref_lock *lock)\n-{\n-\t/* Do not free lock->lk -- atexit() still looks at them */\n-\tif (lock->lk)\n-\t\trollback_lock_file(lock->lk);\n-\tfree(lock->ref_name);\n-\tfree(lock->orig_ref_name);\n-\tfree(lock);\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,\ndiff --git a/refs.h b/refs.h\nindex 0c75217..fdbdea6 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -198,15 +198,6 @@ extern struct ref_lock *lock_any_ref_for_update(const char *refname,\n \t\t\t\t\t\tconst unsigned char *old_sha1,\n \t\t\t\t\t\tint flags, int *type_p);\n \n-/** Close the file descriptor owned by a lock and return the status */\n-extern int close_ref(struct ref_lock *lock);\n-\n-/** Close and commit the ref locked by the lock */\n-extern int commit_ref(struct ref_lock *lock);\n-\n-/** Release any lock taken but not written. **/\n-extern void unlock_ref(struct ref_lock *lock);\n-\n /** Reads log for the value of ref during at_time. **/\n extern int read_ref_at(const char *refname, unsigned int flags,\n \t\t       unsigned long at_time, int cnt,\n-- \n2.2.0\n"},{"id":"253036","messageId":"1417681763-32334-11-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 10/13] refs.c: remove lock_any_ref_for_update","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:20Z","receivedAt":"2014-12-04T08:29:20Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nNo one is using this function so we can delete it.\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n refs.c | 7 -------\n refs.h | 9 +--------\n 2 files changed, 1 insertion(+), 15 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 1468c00..796b7cc 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2346,13 +2346,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n \treturn NULL;\n }\n \n-struct ref_lock *lock_any_ref_for_update(const char *refname,\n-\t\t\t\t\t const unsigned char *old_sha1,\n-\t\t\t\t\t int flags, int *type_p)\n-{\n-\treturn lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);\n-}\n-\n /*\n  * Write an entry to the packed-refs file for the specified refname.\n  * If peeled is non-NULL, write it as the entry's peeled value.\ndiff --git a/refs.h b/refs.h\nindex fdbdea6..166aab8 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -181,8 +181,7 @@ extern int is_branch(const char *refname);\n extern int peel_ref(const char *refname, unsigned char *sha1);\n \n /*\n- * Flags controlling lock_any_ref_for_update(), transaction_update_ref(),\n- * transaction_create_ref(), etc.\n+ * Flags controlling transaction_update_ref(), transaction_create_ref(), etc.\n  * REF_NODEREF: act on the ref directly, instead of dereferencing\n  *              symbolic references.\n  * REF_DELETING: tolerate broken refs\n@@ -191,12 +190,6 @@ extern int peel_ref(const char *refname, unsigned char *sha1);\n  */\n #define REF_NODEREF\t0x01\n #define REF_DELETING\t0x02\n-/*\n- * This function sets errno to something meaningful on failure.\n- */\n-extern struct ref_lock *lock_any_ref_for_update(const char *refname,\n-\t\t\t\t\t\tconst unsigned char *old_sha1,\n-\t\t\t\t\t\tint flags, int *type_p);\n \n /** Reads log for the value of ref during at_time. **/\n extern int read_ref_at(const char *refname, unsigned int flags,\n-- \n2.2.0\n"},{"id":"253037","messageId":"1417681763-32334-12-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 11/13] refs.c: don't expose the internal struct ref_lock in the header file","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:21Z","receivedAt":"2014-12-04T08:29:21Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Now the struct ref_lock is used completely internally, so let's\nremove it from the header file.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n\nNotes:\n    This patch is brand new in this series,\n    so digest it while it's hot!\n\n refs.c | 9 +++++++++\n refs.h | 9 ---------\n 2 files changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 796b7cc..b54b5b3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -6,6 +6,15 @@\n #include \"dir.h\"\n #include \"string-list.h\"\n \n+struct ref_lock {\n+\tchar *ref_name;\n+\tchar *orig_ref_name;\n+\tstruct lock_file *lk;\n+\tunsigned char old_sha1[20];\n+\tint lock_fd;\n+\tint force_write;\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 166aab8..cfc8d90 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -1,15 +1,6 @@\n #ifndef REFS_H\n #define REFS_H\n \n-struct ref_lock {\n-\tchar *ref_name;\n-\tchar *orig_ref_name;\n-\tstruct lock_file *lk;\n-\tunsigned char old_sha1[20];\n-\tint lock_fd;\n-\tint force_write;\n-};\n-\n /*\n  * A transaction represents a collection of ref updates\n  * that should succeed or fail together.\n-- \n2.2.0\n"},{"id":"253034","messageId":"1417681763-32334-13-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 12/13] refs.c: use a bit for ref_update have_old","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:22Z","receivedAt":"2014-12-04T08:29:22Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Signed-off-by: Stefan Beller <sbeller@google.com>\n---\n\nNotes:\n    Also a patch, which hasn't been posted on the mailing list before.\n\n refs.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex b54b5b3..2942227 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3532,7 +3532,7 @@ struct ref_update {\n \tunsigned char new_sha1[20];\n \tunsigned char old_sha1[20];\n \tint flags; /* REF_NODEREF? */\n-\tint have_old; /* 1 if old_sha1 is valid, 0 otherwise */\n+\tint have_old:1; /* 1 if old_sha1 is valid, 0 otherwise */\n \tstruct ref_lock *lock;\n \tint type;\n \tchar *msg;\n-- \n2.2.0\n"},{"id":"253035","messageId":"1417681763-32334-14-git-send-email-sbeller@google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"[PATCH 13/13] refs.c: allow deleting refs with a broken sha1","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T08:29:23Z","receivedAt":"2014-12-04T08:29:23Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"From: Ronnie Sahlberg <sahlberg@google.com>\n\nAdd back support to make it possible to delete refs that have a broken\nsha1.\n\nAdd new internal flags REF_ALLOW_BROKEN and RESOLVE_REF_ALLOW_BAD_SHA1\nto pass intent from branch.c that we are willing to allow\nresolve_ref_unsafe and lock_ref_sha1_basic to allow broken refs.\nSince these refs can not actually be resolved to a sha1, they instead resolve\nto null_sha1 when these flags are used.\n\nFor example, the ref:\n\n   echo \"Broken ref\" > .git/refs/heads/foo-broken-1\n\ncan now be deleted using git branch -d foo-broken-1\n\nSigned-off-by: Ronnie Sahlberg <sahlberg@google.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n builtin/branch.c  | 5 +++--\n cache.h           | 7 +++++++\n refs.c            | 6 ++++++\n refs.h            | 6 ++++--\n t/t3200-branch.sh | 8 ++++++++\n 5 files changed, 28 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 3b79c50..04f57d4 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -238,7 +238,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n \t\ttarget = resolve_ref_unsafe(name,\n \t\t\t\t\t    RESOLVE_REF_READING\n \t\t\t\t\t    | RESOLVE_REF_NO_RECURSE\n-\t\t\t\t\t    | RESOLVE_REF_ALLOW_BAD_NAME,\n+\t\t\t\t\t    | RESOLVE_REF_ALLOW_BAD_NAME\n+\t\t\t\t\t    | RESOLVE_REF_ALLOW_BAD_SHA1,\n \t\t\t\t\t    sha1, &flags);\n \t\tif (!target) {\n \t\t\terror(remote_branch\n@@ -255,7 +256,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (delete_ref(name, sha1, REF_NODEREF)) {\n+\t\tif (delete_ref(name, sha1, REF_NODEREF|REF_ALLOW_BROKEN)) {\n \t\t\terror(remote_branch\n \t\t\t      ? _(\"Error deleting remote branch '%s'\")\n \t\t\t      : _(\"Error deleting branch '%s'\"),\ndiff --git a/cache.h b/cache.h\nindex 99ed096..61e61af 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1000,10 +1000,17 @@ extern int read_ref(const char *refname, unsigned char *sha1);\n  * resolved. The function returns NULL for such ref names.\n  * Caps and underscores refers to the special refs, such as HEAD,\n  * FETCH_HEAD and friends, that all live outside of the refs/ directory.\n+ *\n+ * RESOLVE_REF_ALLOW_BAD_SHA1 when this flag is set and the ref contains\n+ * an invalid sha1, resolve_ref_unsafe will clear the sha1 argument,\n+ * set the REF_ISBROKEN flag and return the refname.\n+ * This allows using resolve_ref_unsafe to check for existence of such\n+ * broken refs.\n  */\n #define RESOLVE_REF_READING 0x01\n #define RESOLVE_REF_NO_RECURSE 0x02\n #define RESOLVE_REF_ALLOW_BAD_NAME 0x04\n+#define RESOLVE_REF_ALLOW_BAD_SHA1 0x08\n extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);\n extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);\n \ndiff --git a/refs.c b/refs.c\nindex 2942227..759536c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1587,6 +1587,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned\n \t\t\t    (buffer[40] != '\\0' && !isspace(buffer[40]))) {\n \t\t\t\tif (flags)\n \t\t\t\t\t*flags |= REF_ISBROKEN;\n+\t\t\t\tif (resolve_flags & RESOLVE_REF_ALLOW_BAD_SHA1) {\n+\t\t\t\t\thashclr(sha1);\n+\t\t\t\t\treturn refname;\n+\t\t\t\t}\n \t\t\t\terrno = EINVAL;\n \t\t\t\treturn NULL;\n \t\t\t}\n@@ -2268,6 +2272,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n \t\tif (flags & REF_NODEREF)\n \t\t\tresolve_flags |= RESOLVE_REF_NO_RECURSE;\n \t}\n+\tif (flags & REF_ALLOW_BROKEN)\n+\t\tresolve_flags |= RESOLVE_REF_ALLOW_BAD_SHA1;\n \n \trefname = resolve_ref_unsafe(refname, resolve_flags,\n \t\t\t\t     lock->old_sha1, &type);\ndiff --git a/refs.h b/refs.h\nindex cfc8d90..735f59f 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -176,11 +176,13 @@ extern int peel_ref(const char *refname, unsigned char *sha1);\n  * REF_NODEREF: act on the ref directly, instead of dereferencing\n  *              symbolic references.\n  * REF_DELETING: tolerate broken refs\n+ * REF_ALLOW_BROKEN: allow locking refs that are broken.\n  *\n  * Flags >= 0x100 are reserved for internal use.\n  */\n-#define REF_NODEREF\t0x01\n-#define REF_DELETING\t0x02\n+#define REF_NODEREF\t\t0x01\n+#define REF_DELETING\t\t0x02\n+#define REF_ALLOW_BROKEN\t0x04\n \n /** Reads log for the value of ref during at_time. **/\n extern int read_ref_at(const char *refname, unsigned int flags,\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 432921b..fa7d7bd 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -163,6 +163,14 @@ test_expect_success 'git branch --list -d t should fail' '\n \ttest_path_is_missing .git/refs/heads/t\n '\n \n+test_expect_failure 'git branch -d can delete ref with broken sha1' '\n+\techo \"pointing nowhere\" >.git/refs/heads/brokensha1 &&\n+\ttest_when_finished \"rm -f .git/refs/heads/brokensha1\" &&\n+\tgit branch -d brokensha1 &&\n+\tgit branch >output &&\n+\t! grep brokensha1 output\n+'\n+\n test_expect_success 'git branch --column' '\n \tCOLUMNS=81 git branch --column=column >actual &&\n \tcat >expected <<\\EOF &&\n-- \n2.2.0\n"},{"id":"253058","messageId":"5480875D.6050803@web.de","threadId":"38107","inReplyTo":"1417681763-32334-13-git-send-email-sbeller@google.com","subject":"Re: [PATCH 12/13] refs.c: use a bit for ref_update have_old","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2014-12-04T16:10:05Z","receivedAt":"2014-12-04T16:10:05Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 12/04/2014 09:29 AM, Stefan Beller wrote:\nSomewhat short commit message, especially the motivation is missing.\n\nWhat do we gain with this patch ?\nIn the struct the compiler needs to layout 2*20 bytes for the sha's.\nIt follows an int, typically 4 bytes.\nIt follows another int, now with one bit.\nThen we have the pointer \"struct ref_lock *lock\",\nwhich needs to be aligned on 4 byte boundary for a 32 bit processor,\nor an 8 byte boundary for a 64 bit machine.\n\nOur \"1 bit int\" is padded with 31 bits.\nWe do not gain anything in memory consumption. (unless we declare int flags\nto be 31 bits, and the compiler may join \"have_old\" and \"flags\" together\ninto one int in memory.\n\nBut there is a price to pay:\nThe generated code to fiddle out the bits from an int becomes more complicated.\nYou need to fetch the memory from one int, mask and shift.\nSome processors can do this, out of my mind some ARM can, some can not.\n\nWe need to run the compiler to look at the generated code of course.\n \n\n\n> Signed-off-by: Stefan Beller <sbeller@google.com>\n> ---\n> \n> Notes:\n>     Also a patch, which hasn't been posted on the mailing list before.\n> \n>  refs.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n> \n> diff --git a/refs.c b/refs.c\n> index b54b5b3..2942227 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -3532,7 +3532,7 @@ struct ref_update {\n>  \tunsigned char new_sha1[20];\n>  \tunsigned char old_sha1[20];\n>  \tint flags; /* REF_NODEREF? */\n> -\tint have_old; /* 1 if old_sha1 is valid, 0 otherwise */\n> +\tint have_old:1; /* 1 if old_sha1 is valid, 0 otherwise */\n>  \tstruct ref_lock *lock;\n>  \tint type;\n>  \tchar *msg;\n> \n"},{"id":"253061","messageId":"87wq67ic46.fsf@igel.home","threadId":"38107","inReplyTo":"1417681763-32334-13-git-send-email-sbeller@google.com","subject":"Re: [PATCH 12/13] refs.c: use a bit for ref_update have_old","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2014-12-04T17:00:25Z","receivedAt":"2014-12-04T17:00:25Z","isPatch":true,"sender":{"key":"schwab@linux-m68k.org","avatar":"https://avatars.githubusercontent.com/u/2175493?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> diff --git a/refs.c b/refs.c\n> index b54b5b3..2942227 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -3532,7 +3532,7 @@ struct ref_update {\n>  \tunsigned char new_sha1[20];\n>  \tunsigned char old_sha1[20];\n>  \tint flags; /* REF_NODEREF? */\n> -\tint have_old; /* 1 if old_sha1 is valid, 0 otherwise */\n> +\tint have_old:1; /* 1 if old_sha1 is valid, 0 otherwise */\n\nA signed one-bit field cannot store a value of 1.\n\nAndreas.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n\"And now for something completely different.\"\n"},{"id":"253062","messageId":"54809577.4080302@alum.mit.edu","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2014-12-04T17:10:15Z","receivedAt":"2014-12-04T17:10:15Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/04/2014 09:29 AM, Stefan Beller wrote:\n> This is the whole refs-transactions-reflog series[1],\n> which was in discussion for a bit already. It applies to origin/master.\n\nI am still unhappy with the approach of this series, for the reasons\nthat I explained earlier [1]. In short, I think that the abstraction\nlevel is wrong. In my opinion, consumers of the refs API should barely\neven have to *know* about reflogs, let alone implement reflog expiration\nthemselves.\n\nOf course, reflog expiration *should* be done atomically. But that is\nthe business of the refs module; callers shouldn't have to do all the\ncomplicated work of building the transaction themselves.\n\nI spent the day working on an alternate proposal, to convert\nexpire_reflogs() to take callback functions that decide *what* to\nexpire, but which itself does the work of acquiring locks, iterating\nthrough the reflog entries, rewriting the file, and overwriting the old\nfile with the new one. The goal is to move this function into refs.c and\nmake builtin/reflog.c much simpler--it will only have to implement the\ncallbacks that determine the expiration policy.\n\nI'm almost done with the series but won't be able to finish it until\ntomorrow. For those who are impatient, here is my work in progress [2].\n\nMichael\n\n[1]\nhttp://thread.gmane.org/gmane.comp.version-control.git/259712/focus=259770\n[2] https://github.com/mhagger/git, branch \"reflog-expire-api\".\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"253065","messageId":"20141204175343.GA9992@google.com","threadId":"38107","inReplyTo":"54809577.4080302@alum.mit.edu","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-12-04T17:53:43Z","receivedAt":"2014-12-04T17:53:43Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nMichael Haggerty wrote:\n> On 12/04/2014 09:29 AM, Stefan Beller wrote:\n\n>> This is the whole refs-transactions-reflog series[1],\n>> which was in discussion for a bit already. It applies to origin/master.\n>\n> I am still unhappy with the approach of this series, for the reasons\n> that I explained earlier [1]. In short, I think that the abstraction\n> level is wrong. In my opinion, consumers of the refs API should barely\n> even have to *know* about reflogs, let alone implement reflog expiration\n> themselves.\n\nWould it make sense to propose competing documentation patches (to\nDocumentation/technical/api-ref-transactions.txt, or to refs.h), so\nwe can work out the API that way?\n\nI don't think the API questions that we're talking about would end up\naffecting the details of how the files backend implements them too\nmuch.\n\nJonathan\n"},{"id":"253070","messageId":"20141204181404.GD9992@google.com","threadId":"38107","inReplyTo":"54809577.4080302@alum.mit.edu","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-12-04T18:14:04Z","receivedAt":"2014-12-04T18:14:04Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Michael Haggerty wrote:\n\n> I am still unhappy with the approach of this series, for the reasons\n> that I explained earlier [1]. In short, I think that the abstraction\n> level is wrong. In my opinion, consumers of the refs API should barely\n> even have to *know* about reflogs, let alone implement reflog expiration\n> themselves.\n\nOkay, now returning to the substance of this comment.  This is\nrevisiting themes from [3], so my opinions are probably not a\nsurprise.\n\nI think that the API changes that you and Stefan are proposing are\nconsistent and could both go in.\n\nYou suggested refactoring expire_reflogs() to use a callback that\ndecides what to expire.  Then it doesn't have to care that the\nexpiration happens by creating a new reflog and copying over the\nreflog entries that are not being expired.  The result is a clean\nreflog expiration API.\n\nThe ref-transaction-reflog series allows those low-level steps to be\npart of a ref transaction.  Any ref backend (the current files-based\nbackend or a future other one) would get a chance to reimplement those\nlow-level steps, which are part of what happens during ref updates and\nreflog deletion.  The goal is for all reflog updates to use the\ntransaction API, so that new ref/reflog backends only need to\nimplement the transaction functions.\n\nSo *both* are making good changes, with different goals.\n\nThe implementation of the reflog expiration API can use the ref\ntransaction API.\n\n> Of course, reflog expiration *should* be done atomically. But that is\n> the business of the refs module; callers shouldn't have to do all the\n> complicated work of building the transaction themselves.\n\nI don't understand this comment.  After the ref-transaction-reflog\nseries, a transaction_update_ref() call still takes care of the\ncorresponding reflog update without the caller having to worry about\nit.\n\nThanks for looking it over,\nJonathan\n\n> [1] http://thread.gmane.org/gmane.comp.version-control.git/259712/focus=259770\n[3] http://thread.gmane.org/gmane.comp.version-control.git/259939/focus=259967\n"},{"id":"253072","messageId":"20141204183231.GA2649@google.com","threadId":"38107","inReplyTo":"20141204181404.GD9992@google.com","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T18:32:31Z","receivedAt":"2014-12-04T18:32:31Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Dec 04, 2014 at 10:14:04AM -0800, Jonathan Nieder wrote:\n> Michael Haggerty wrote:\n> \n> > I am still unhappy with the approach of this series, for the reasons\n> > that I explained earlier [1]. In short, I think that the abstraction\n> > level is wrong. In my opinion, consumers of the refs API should barely\n> > even have to *know* about reflogs, let alone implement reflog expiration\n> > themselves.\n> \nOk, what is a consumer for you? In my understanding the builtin/reflog.c is\na consumer of the refs API, so there we want to see clean code just telling the\nrefs backend to do its thing.\n\nI think Jonathan made a good point by saying our patch series have \ndifferent goals.\n\nSo I really like the code, which leads to \n\n    reflog_expiry_prepare(refname, sha1, cb.policy_cb);\n    for_each_reflog_ent(refname, expire_reflog_ent, &cb);\n    reflog_expiry_cleanup(cb.policy_cb);\n\nbut look at the surrounding code:\n    \n\tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n\t\tif (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0)\n\t\t\t...\n\t}\n\n\n\tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n\t\tif (close_lock_file(&reflog_lock)) {\n\t\t\t...\n\t\t}\n\t}\n\nThat part should also go into the refs.c backend, so in the expire_reflog\nfunction you can just write:\n\n\ttransaction_begin(...)  // This does all the hold_lock_file_for_update magic \n\t\t\t\t// lines 457-464 in mhagger/reflog-expire-api\n\n \treflog_expiry_prepare(refname, sha1, cb.policy_cb);\n\tfor_each_reflog_ent(refname, expire_reflog_ent, &cb);\n\treflog_expiry_cleanup(cb.policy_cb);\n\n\ttransaction_commit(...) // This does all the close_lock_file/rename/write_in_full\n\t\t\t\t// lines 470-488 in mhagger/reflog-expire-api\n\n\n> \n> So *both* are making good changes, with different goals.\n\nIf you want to I can rebase the reflog series on top of yours to show\nthey can work together quite nicely.\n\nThanks for your draft series and feedback,\nStefan\n"},{"id":"253073","messageId":"xmqqtx1btg6d.fsf@gitster.dls.corp.google.com","threadId":"38107","inReplyTo":"54809577.4080302@alum.mit.edu","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-04T18:37:14Z","receivedAt":"2014-12-04T18:37:14Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> ... an alternate proposal, to convert\n> expire_reflogs() to take callback functions that decide *what* to\n> expire, but which itself does the work of acquiring locks, iterating\n> through the reflog entries, rewriting the file, and overwriting the old\n> file with the new one. The goal is to move this function into refs.c and\n> make builtin/reflog.c much simpler--it will only have to implement the\n> callbacks that determine the expiration policy.\n\nIt sounds like a very sensible approach to me.\n"},{"id":"253074","messageId":"xmqqppbztfzj.fsf@gitster.dls.corp.google.com","threadId":"38107","inReplyTo":"1417681763-32334-1-git-send-email-sbeller@google.com","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-04T18:41:20Z","receivedAt":"2014-12-04T18:41:20Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> This is the whole refs-transactions-reflog series[1],\n> which was in discussion for a bit already. It applies to origin/master.\n>\n> The idea is to have the reflog being part of the transactions, which\n> the refs are already using, so the we're moving towards a database\n> like API in the long run. This makes git easier to maintain as well\n> as opening the possibility to replace the backend with a real database.\n>\n> If you've followed the topic a bit, start reading at patch\n> \"[PATCH 06/13] refs.c: add a transaction function to append a reflog\"\n> as the first 5 patches have been discussed a lot separately and\n> can be found in origin/pu already[2].\n\n> The first two patches are deduplicating code.\n> The third patch is ripping some code out of log_ref_write and introduces\n> log_ref_write_fd, which does the actual writing.\n> The patches 4+5 are renaming variables for clarity.\n\nThanks.  It seems that we have a bit of hashing out the approaches\nthat is necessary between Michael's and this one.  I'll refrain from\npicking this up for today (even though I would read it through as\ntime permits).  It might turn out to be necessary to drop those five\nearly patches from my tree when this series gets rerolled to take\ninput from the alternative Michael's working on, but that droppage\ndoes not have to happen today.\n"},{"id":"253075","messageId":"CAGZ79ka9swMKvYpSLC20kj0bj=QWS7syVDtQcOmuT5AC3SO+gw@mail.gmail.com","threadId":"38107","inReplyTo":"xmqqppbztfzj.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2014-12-04T18:49:09Z","receivedAt":"2014-12-04T18:49:09Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Dec 4, 2014 at 10:41 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Stefan Beller <sbeller@google.com> writes:\n>\n>> This is the whole refs-transactions-reflog series[1],\n>> which was in discussion for a bit already. It applies to origin/master.\n>>\n>> The idea is to have the reflog being part of the transactions, which\n>> the refs are already using, so the we're moving towards a database\n>> like API in the long run. This makes git easier to maintain as well\n>> as opening the possibility to replace the backend with a real database.\n>>\n>> If you've followed the topic a bit, start reading at patch\n>> \"[PATCH 06/13] refs.c: add a transaction function to append a reflog\"\n>> as the first 5 patches have been discussed a lot separately and\n>> can be found in origin/pu already[2].\n>\n>> The first two patches are deduplicating code.\n>> The third patch is ripping some code out of log_ref_write and introduces\n>> log_ref_write_fd, which does the actual writing.\n>> The patches 4+5 are renaming variables for clarity.\n>\n> Thanks.  It seems that we have a bit of hashing out the approaches\n> that is necessary between Michael's and this one.  I'll refrain from\n> picking this up for today (even though I would read it through as\n> time permits).  It might turn out to be necessary to drop those five\n> early patches from my tree when this series gets rerolled to take\n> input from the alternative Michael's working on, but that droppage\n> does not have to happen today.\n>\n\nOk, let's see how Michaels series evolves and if I can base the\ntransactions series\non that afterwards. Michael has picked up 3 out the 5 patches to get\nhis series started,\nso maybe we can resolve it nicely.\n"},{"id":"253080","messageId":"20141204192720.GB16345@google.com","threadId":"38107","inReplyTo":"CAGZ79ka9swMKvYpSLC20kj0bj=QWS7syVDtQcOmuT5AC3SO+gw@mail.gmail.com","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-12-04T19:27:20Z","receivedAt":"2014-12-04T19:27:20Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Stefan Beller wrote:\n\n> Ok, let's see how Michaels series evolves and if I can base the\n> transactions series\n> on that\n\nThat sounds good to me, too, FWIW.  That way we don't have to fuss\nwith conflicts in refs.c. :)\n\nThanks,\nJonathan\n"},{"id":"253098","messageId":"5480CE82.9000408@alum.mit.edu","threadId":"38107","inReplyTo":"20141204183231.GA2649@google.com","subject":"Re: [PATCHv3 00/13] the refs-transactions-reflog series","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2014-12-04T21:13:38Z","receivedAt":"2014-12-04T21:13:38Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/04/2014 07:32 PM, Stefan Beller wrote:\n> On Thu, Dec 04, 2014 at 10:14:04AM -0800, Jonathan Nieder wrote:\n>> Michael Haggerty wrote:\n>>\n>>> I am still unhappy with the approach of this series, for the reasons\n>>> that I explained earlier [1]. In short, I think that the abstraction\n>>> level is wrong. In my opinion, consumers of the refs API should barely\n>>> even have to *know* about reflogs, let alone implement reflog expiration\n>>> themselves.\n>>\n> Ok, what is a consumer for you? In my understanding the builtin/reflog.c is\n> a consumer of the refs API, so there we want to see clean code just telling the\n> refs backend to do its thing.\n> \n> I think Jonathan made a good point by saying our patch series have \n> different goals.\n> \n> So I really like the code, which leads to \n> \n>     reflog_expiry_prepare(refname, sha1, cb.policy_cb);\n>     for_each_reflog_ent(refname, expire_reflog_ent, &cb);\n>     reflog_expiry_cleanup(cb.policy_cb);\n> \n> but look at the surrounding code:\n>     \n> \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n> \t\tif (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0)\n> \t\t\t...\n> \t}\n> \n> \n> \tif (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {\n> \t\tif (close_lock_file(&reflog_lock)) {\n> \t\t\t...\n> \t\t}\n> \t}\n> \n> That part should also go into the refs.c backend, so in the expire_reflog\n> function you can just write:\n> \n> \ttransaction_begin(...)  // This does all the hold_lock_file_for_update magic \n> \t\t\t\t// lines 457-464 in mhagger/reflog-expire-api\n> \n>  \treflog_expiry_prepare(refname, sha1, cb.policy_cb);\n> \tfor_each_reflog_ent(refname, expire_reflog_ent, &cb);\n> \treflog_expiry_cleanup(cb.policy_cb);\n> \n> \ttransaction_commit(...) // This does all the close_lock_file/rename/write_in_full\n> \t\t\t\t// lines 470-488 in mhagger/reflog-expire-api\n\nI'm pleasantly surprised that you guys have already looked at my work in\nprogress. I wish I had had more time earlier today to explain what is\nstill to be done:\n\nThe whole point of all of the refactoring is to move expire_reflog()\n(and supporting functions like expire_reflog_ent()) to refs.c. The\n\"surrounding code\" that you mention above would be moved there and would\n*not* need to be done by callers.\n\nexpire_reflog() will gain three callback function pointers as\nparameters. The caller (in this case reflog.c) will pass pointers to\nreflog_expiry_prepare(), reflog_expiry_cleanup(), and\nshould_expire_reflog_ent() into expire_reflog().\n\nThere is no need to wrap the code in a transaction, because the\n\"surrounding code\" that you mentioned implements all the \"transaction\"\nthat is needed! There is no need to complicated the *ref_transaction*\ninterface to allow arbitrary reflog updates when all we need is this one\nvery special case, plus of course the reflog appends that (I claim)\nshould happen implicitly whenever a reference is updated [1].\n\n>> So *both* are making good changes, with different goals.\n> \n> If you want to I can rebase the reflog series on top of yours to show\n> they can work together quite nicely.\n\nFeel free to do what you want, but I really don't think we will ever\nneed transactions to handle generic reflog updates.\n\nMeanwhile I'll try to get my series finished, including API docs as\nJonathan requested. I hope the code will be more convincing than my\nprose :-)\n\nMichael\n\n[1] Of course, only for references that have reflogs enabled.\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"}]}