{"thread":{"id":"39944","subject":"[PATCH v2 0/5] pseudorefs","startedAt":"2015-07-27T20:08:35Z","lastAt":"2015-07-28T18:53:46Z","messageCount":11,"participants":["David Turner","Junio C Hamano","Eric Sunshine"],"isPatch":true,"patchVersion":2,"patchTotal":5},"messages":[{"id":"266854","messageId":"1438027720-23074-1-git-send-email-dturner@twopensource.com","threadId":"39944","inReplyTo":null,"subject":"[PATCH v2 0/5] pseudorefs","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-27T20:08:35Z","receivedAt":"2015-07-27T20:08:35Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"This version of the pseudorefs patch series is much simpler.  Instead\nof forbidding update_ref and delete_ref from updating pseudorefs,\nthese functions now just special-case pseudorefs.  So we can use\nupdate_ref to write pseudorefs in a rebase and sequencer, and\nwe don't need to rewrite so much code.\n\nIn addition, I made typo fixes suggested by Eric Sunshine and Philip\nOakley.\n"},{"id":"266855","messageId":"1438027720-23074-2-git-send-email-dturner@twopensource.com","threadId":"39944","inReplyTo":"1438027720-23074-1-git-send-email-dturner@twopensource.com","subject":"[PATCH 1/5] refs: Introduce pseudoref and per-worktree ref concepts","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-27T20:08:36Z","receivedAt":"2015-07-27T20:08:36Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Add glossary entries for both concepts.\n\nPseudorefs and per-worktree refs do not yet have special handling,\nbecause the files refs backend already handles them correctly.  Later,\nwe will make the LMDB backend call out to the files backend to handle\nper-worktree refs.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\n---\n Documentation/glossary-content.txt | 21 +++++++++++++++++++++\n refs.c                             | 23 +++++++++++++++++++++++\n refs.h                             |  2 ++\n 3 files changed, 46 insertions(+)\n\ndiff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt\nindex ab18f4b..67952f3 100644\n--- a/Documentation/glossary-content.txt\n+++ b/Documentation/glossary-content.txt\n@@ -411,6 +411,27 @@ exclude;;\n \tcore Git. Porcelains expose more of a <<def_SCM,SCM>>\n \tinterface than the <<def_plumbing,plumbing>>.\n \n+[[def_per_worktree_ref]]per-worktree ref::\n+\tRefs that are per-<<def_worktree,worktree>>, rather than\n+\tglobal.  This is presently only <<def_HEAD,HEAD>>, but might\n+\tlater include other unusual refs.\n+\n+[[def_pseudoref]]pseudoref::\n+\tPseudorefs are a class of files under `$GIT_DIR` which behave\n+\tlike refs for the purposes of rev-parse, but which are treated\n+\tspecially by git.  Psuedorefs both have names are all-caps,\n+\tand always start with a line consisting of a\n+\t<<def_sha1,SHA-1>> followed by whitespace.  So, HEAD is not a\n+\tpseudoref, because it is sometimes a symbolic ref.  They might\n+\toptionally some additional data.  `MERGE_HEAD` and\n+\t`CHERRY_PICK_HEAD` are examples.  Unlike\n+\t<<def_per_worktree_ref,per-worktree refs>>, these files cannot\n+\tbe symbolic refs, and never have reflogs.  They also cannot be\n+\tupdated through the normal ref update machinery.  Instead,\n+\tthey are updated by directly writing to the files.  However,\n+\tthey can be read as if they were refs, so `git rev-parse\n+\tMERGE_HEAD` will work.\n+\n [[def_pull]]pull::\n \tPulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and\n \t<<def_merge,merge>> it.  See also linkgit:git-pull[1].\ndiff --git a/refs.c b/refs.c\nindex 0b96ece..0d10b7b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2848,6 +2848,29 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)\n \treturn 0;\n }\n \n+int is_per_worktree_ref(const char *refname)\n+{\n+\treturn !strcmp(refname, \"HEAD\");\n+}\n+\n+static int is_pseudoref(const char *refname)\n+{\n+\tconst char *c;\n+\n+\tif (strchr(refname, '/'))\n+\t\treturn 0;\n+\n+\tif (is_per_worktree_ref(refname))\n+\t\treturn 0;\n+\n+\tfor (c = refname; *c; ++c) {\n+\t\tif (!isupper(*c) && *c != '-' && *c != '_')\n+\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n int delete_ref(const char *refname, const unsigned char *old_sha1,\n \t       unsigned int flags)\n {\ndiff --git a/refs.h b/refs.h\nindex e4e46c3..bd5526e 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -445,6 +445,8 @@ extern int parse_hide_refs_config(const char *var, const char *value, const char\n \n extern int ref_is_hidden(const char *);\n \n+int is_per_worktree_ref(const char *refname);\n+\n enum expire_reflog_flags {\n \tEXPIRE_REFLOGS_DRY_RUN = 1 << 0,\n \tEXPIRE_REFLOGS_UPDATE_REF = 1 << 1,\n-- \n2.0.4.315.gad8727a-twtrsrc\n"},{"id":"266858","messageId":"1438027720-23074-3-git-send-email-dturner@twopensource.com","threadId":"39944","inReplyTo":"1438027720-23074-1-git-send-email-dturner@twopensource.com","subject":"[PATCH 2/5] notes: replace pseudorefs with real refs","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-27T20:08:37Z","receivedAt":"2015-07-27T20:08:37Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"All-caps files like NOTES_MERGE_REF are pseudorefs, and thus are\nper-worktree.  We don't want multiple notes merges happening at once\n(in different worktrees), so we want to make these refs true refs.\n\nSo, we lowercase NOTES_MERGE_REF and friends.  That way, backends\nthat distinguish between pseudorefs and real refs can correctly\nhandle notes merges.\n\nThis will also enable us to prevent pseudorefs from being updated by\nthe ref update machinery e.g. git update-ref.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\n---\n Documentation/git-notes.txt           | 12 ++---\n builtin/notes.c                       | 38 ++++++++--------\n notes-merge.c                         | 10 ++--\n notes-merge.h                         |  8 ++--\n t/t3310-notes-merge-manual-resolve.sh | 86 +++++++++++++++++------------------\n t/t3311-notes-merge-fanout.sh         |  6 +--\n 6 files changed, 80 insertions(+), 80 deletions(-)\n\ndiff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt\nindex 851518d..82fa3fd 100644\n--- a/Documentation/git-notes.txt\n+++ b/Documentation/git-notes.txt\n@@ -103,7 +103,7 @@ merge::\n If conflicts arise and a strategy for automatically resolving\n conflicting notes (see the -s/--strategy option) is not given,\n the \"manual\" resolver is used. This resolver checks out the\n-conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),\n+conflicting notes in a special worktree (`.git/notes_merge_worktree`),\n and instructs the user to manually resolve the conflicts there.\n When done, the user can either finalize the merge with\n 'git notes merge --commit', or abort the merge with\n@@ -189,11 +189,11 @@ OPTIONS\n --commit::\n \tFinalize an in-progress 'git notes merge'. Use this option\n \twhen you have resolved the conflicts that 'git notes merge'\n-\tstored in .git/NOTES_MERGE_WORKTREE. This amends the partial\n+\tstored in .git/notes_merge_worktree. This amends the partial\n \tmerge commit created by 'git notes merge' (stored in\n-\t.git/NOTES_MERGE_PARTIAL) by adding the notes in\n-\t.git/NOTES_MERGE_WORKTREE. The notes ref stored in the\n-\t.git/NOTES_MERGE_REF symref is updated to the resulting commit.\n+\t.git/notes_merge_partial) by adding the notes in\n+\t.git/notes_merge_worktree. The notes ref stored in the\n+\t.git/notes_merge_ref symref is updated to the resulting commit.\n \n --abort::\n \tAbort/reset a in-progress 'git notes merge', i.e. a notes merge\n@@ -241,7 +241,7 @@ NOTES MERGE STRATEGIES\n \n The default notes merge strategy is \"manual\", which checks out\n conflicting notes in a special work tree for resolving notes conflicts\n-(`.git/NOTES_MERGE_WORKTREE`), and instructs the user to resolve the\n+(`.git/notes_merge_worktree`), and instructs the user to resolve the\n conflicts in that work tree.\n When done, the user can either finalize the merge with\n 'git notes merge --commit', or abort the merge with\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 63f95fc..e0b8a02 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -670,14 +670,14 @@ static int merge_abort(struct notes_merge_options *o)\n \tint ret = 0;\n \n \t/*\n-\t * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call\n-\t * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.\n+\t * Remove .git/notes_merge_partial and .git/notes_merge_ref, and call\n+\t * notes_merge_abort() to remove .git/notes_merge_worktree.\n \t */\n \n-\tif (delete_ref(\"NOTES_MERGE_PARTIAL\", NULL, 0))\n-\t\tret += error(\"Failed to delete ref NOTES_MERGE_PARTIAL\");\n-\tif (delete_ref(\"NOTES_MERGE_REF\", NULL, REF_NODEREF))\n-\t\tret += error(\"Failed to delete ref NOTES_MERGE_REF\");\n+\tif (delete_ref(\"notes_merge_partial\", NULL, 0))\n+\t\tret += error(\"Failed to delete ref notes_merge_partial\");\n+\tif (delete_ref(\"notes_merge_ref\", NULL, REF_NODEREF))\n+\t\tret += error(\"Failed to delete ref notes_merge_ref\");\n \tif (notes_merge_abort(o))\n \t\tret += error(\"Failed to remove 'git notes merge' worktree\");\n \treturn ret;\n@@ -694,16 +694,16 @@ static int merge_commit(struct notes_merge_options *o)\n \tint ret;\n \n \t/*\n-\t * Read partial merge result from .git/NOTES_MERGE_PARTIAL,\n-\t * and target notes ref from .git/NOTES_MERGE_REF.\n+\t * Read partial merge result from .git/notes_merge_partial,\n+\t * and target notes ref from .git/notes_merge_ref.\n \t */\n \n-\tif (get_sha1(\"NOTES_MERGE_PARTIAL\", sha1))\n-\t\tdie(\"Failed to read ref NOTES_MERGE_PARTIAL\");\n+\tif (get_sha1(\"notes_merge_partial\", sha1))\n+\t\tdie(\"Failed to read ref notes_merge_partial\");\n \telse if (!(partial = lookup_commit_reference(sha1)))\n-\t\tdie(\"Could not find commit from NOTES_MERGE_PARTIAL.\");\n+\t\tdie(\"Could not find commit from notes_merge_partial.\");\n \telse if (parse_commit(partial))\n-\t\tdie(\"Could not parse commit from NOTES_MERGE_PARTIAL.\");\n+\t\tdie(\"Could not parse commit from notes_merge_partial.\");\n \n \tif (partial->parents)\n \t\thashcpy(parent_sha1, partial->parents->item->object.sha1);\n@@ -711,12 +711,12 @@ static int merge_commit(struct notes_merge_options *o)\n \t\thashclr(parent_sha1);\n \n \tt = xcalloc(1, sizeof(struct notes_tree));\n-\tinit_notes(t, \"NOTES_MERGE_PARTIAL\", combine_notes_overwrite, 0);\n+\tinit_notes(t, \"notes_merge_partial\", combine_notes_overwrite, 0);\n \n \to->local_ref = local_ref_to_free =\n-\t\tresolve_refdup(\"NOTES_MERGE_REF\", 0, sha1, NULL);\n+\t\tresolve_refdup(\"notes_merge_ref\", 0, sha1, NULL);\n \tif (!o->local_ref)\n-\t\tdie(\"Failed to resolve NOTES_MERGE_REF\");\n+\t\tdie(\"Failed to resolve notes_merge_ref\");\n \n \tif (notes_merge_commit(o, t, partial, sha1))\n \t\tdie(\"Failed to finalize notes merge\");\n@@ -825,11 +825,11 @@ static int merge(int argc, const char **argv, const char *prefix)\n \t\tupdate_ref(msg.buf, default_notes_ref(), result_sha1, NULL,\n \t\t\t   0, UPDATE_REFS_DIE_ON_ERR);\n \telse { /* Merge has unresolved conflicts */\n-\t\t/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */\n-\t\tupdate_ref(msg.buf, \"NOTES_MERGE_PARTIAL\", result_sha1, NULL,\n+\t\t/* Update .git/notes_merge_partial with partial merge result */\n+\t\tupdate_ref(msg.buf, \"notes_merge_partial\", result_sha1, NULL,\n \t\t\t   0, UPDATE_REFS_DIE_ON_ERR);\n-\t\t/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */\n-\t\tif (create_symref(\"NOTES_MERGE_REF\", default_notes_ref(), NULL))\n+\t\t/* Store ref-to-be-updated into .git/notes_merge_ref */\n+\t\tif (create_symref(\"notes_merge_ref\", default_notes_ref(), NULL))\n \t\t\tdie(\"Failed to store link to current notes ref (%s)\",\n \t\t\t    default_notes_ref());\n \t\tprintf(\"Automatic notes merge failed. Fix conflicts in %s and \"\ndiff --git a/notes-merge.c b/notes-merge.c\nindex 0b2b82c..58a8637 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -274,10 +274,10 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)\n \t\t\t\t    \"'git notes merge --commit' or 'git notes \"\n \t\t\t\t    \"merge --abort' to commit/abort the \"\n \t\t\t\t    \"previous merge before you start a new \"\n-\t\t\t\t    \"notes merge.\", git_path(\"NOTES_MERGE_*\"));\n+\t\t\t\t    \"notes merge.\", git_path(\"notes_merge_*\"));\n \t\t\telse\n \t\t\t\tdie(\"You have not concluded your notes merge \"\n-\t\t\t\t    \"(%s exists).\", git_path(\"NOTES_MERGE_*\"));\n+\t\t\t\t    \"(%s exists).\", git_path(\"notes_merge_*\"));\n \t\t}\n \n \t\tif (safe_create_leading_directories_const(git_path(\n@@ -663,7 +663,7 @@ int notes_merge_commit(struct notes_merge_options *o,\n \t\t       unsigned char *result_sha1)\n {\n \t/*\n-\t * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all\n+\t * Iterate through files in .git/notes_merge_workree and add all\n \t * found notes to 'partial_tree'. Write the updated notes tree to\n \t * the DB, and commit the resulting tree object while reusing the\n \t * commit message and parents from 'partial_commit'.\n@@ -734,8 +734,8 @@ int notes_merge_commit(struct notes_merge_options *o,\n int notes_merge_abort(struct notes_merge_options *o)\n {\n \t/*\n-\t * Remove all files within .git/NOTES_MERGE_WORKTREE. We do not remove\n-\t * the .git/NOTES_MERGE_WORKTREE directory itself, since it might be\n+\t * Remove all files within .git/notes_merge_workree. We do not remove\n+\t * the .git/notes_merge_workree directory itself, since it might be\n \t * the current working directory of the user.\n \t */\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/notes-merge.h b/notes-merge.h\nindex 1d01f6a..18705d6 100644\n--- a/notes-merge.h\n+++ b/notes-merge.h\n@@ -1,7 +1,7 @@\n #ifndef NOTES_MERGE_H\n #define NOTES_MERGE_H\n \n-#define NOTES_MERGE_WORKTREE \"NOTES_MERGE_WORKTREE\"\n+#define NOTES_MERGE_WORKTREE \"notes_merge_worktree\"\n \n enum notes_merge_verbosity {\n \tNOTES_MERGE_VERBOSITY_DEFAULT = 2,\n@@ -46,7 +46,7 @@ void init_notes_merge_options(struct notes_merge_options *o);\n  *    are stored in 'local_tree', and the SHA1 or the resulting commit\n  *    (to be amended when the conflicts have been resolved) is written into\n  *    'result_sha1'. The unmerged entries are written into the\n- *    .git/NOTES_MERGE_WORKTREE directory with conflict markers.\n+ *    .git/notes_merge_worktree directory with conflict markers.\n  *    -1 is returned.\n  *\n  * Both o->local_ref and o->remote_ref must be given (non-NULL), but either ref\n@@ -65,7 +65,7 @@ int notes_merge(struct notes_merge_options *o,\n  * the given 'partial_commit', the partial result commit created by a previous\n  * call to notes_merge().\n  *\n- * This function will add the (now resolved) notes in .git/NOTES_MERGE_WORKTREE\n+ * This function will add the (now resolved) notes in .git/notes_merge_worktree\n  * to 'partial_tree', and create a final notes merge commit, the SHA1 of which\n  * will be stored in 'result_sha1'.\n  */\n@@ -77,7 +77,7 @@ int notes_merge_commit(struct notes_merge_options *o,\n /*\n  * Abort conflict resolution from an earlier notes_merge()\n  *\n- * Removes the notes merge worktree in .git/NOTES_MERGE_WORKTREE.\n+ * Removes the notes merge worktree in .git/notes_merge_worktree.\n  */\n int notes_merge_abort(struct notes_merge_options *o);\n \ndiff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh\nindex 195bb97..dc9ecd5 100755\n--- a/t/t3310-notes-merge-manual-resolve.sh\n+++ b/t/t3310-notes-merge-manual-resolve.sh\n@@ -178,12 +178,12 @@ test_expect_success 'merge z into m (== y) with default (\"manual\") resolver => C\n \tgit config core.notesRef refs/notes/m &&\n \ttest_must_fail git notes merge z >output &&\n \t# Output should point to where to resolve conflicts\n-\tgrep -q \"\\\\.git/NOTES_MERGE_WORKTREE\" output &&\n+\tgrep -q \"\\\\.git/notes_merge_worktree\" output &&\n \t# Inspect merge conflicts\n-\tls .git/NOTES_MERGE_WORKTREE >output_conflicts &&\n+\tls .git/notes_merge_worktree >output_conflicts &&\n \ttest_cmp expect_conflicts output_conflicts &&\n \t( for f in $(cat expect_conflicts); do\n-\t\ttest_cmp \"expect_conflict_$f\" \".git/NOTES_MERGE_WORKTREE/$f\" ||\n+\t\ttest_cmp \"expect_conflict_$f\" \".git/notes_merge_worktree/$f\" ||\n \t\texit 1\n \tdone ) &&\n \t# Verify that current notes tree (pre-merge) has not changed (m == y)\n@@ -222,10 +222,10 @@ test_expect_success 'change notes in z' '\n '\n \n test_expect_success 'cannot do merge w/conflicts when previous merge is unfinished' '\n-\ttest -d .git/NOTES_MERGE_WORKTREE &&\n+\ttest -d .git/notes_merge_worktree &&\n \ttest_must_fail git notes merge z >output 2>&1 &&\n \t# Output should indicate what is wrong\n-\tgrep -q \"\\\\.git/NOTES_MERGE_\\\\* exists\" output\n+\tgrep -q \"\\\\.git/notes_merge_\\\\* exists\" output\n '\n \n # Setup non-conflicting merge between x and new notes ref w\n@@ -282,7 +282,7 @@ w notes on 1st commit\n EOF\n \n test_expect_success 'can do merge without conflicts even if previous merge is unfinished (x => w)' '\n-\ttest -d .git/NOTES_MERGE_WORKTREE &&\n+\ttest -d .git/notes_merge_worktree &&\n \tgit notes merge x &&\n \tverify_notes w &&\n \t# Verify that other notes refs has not changed (x and y)\n@@ -316,15 +316,15 @@ EOF\n \n test_expect_success 'finalize conflicting merge (z => m)' '\n \t# Resolve conflicts and finalize merge\n-\tcat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&\n+\tcat >.git/notes_merge_worktree/$commit_sha1 <<EOF &&\n y and z notes on 1st commit\n EOF\n-\tcat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&\n+\tcat >.git/notes_merge_worktree/$commit_sha4 <<EOF &&\n y and z notes on 4th commit\n EOF\n \tgit notes merge --commit &&\n-\t# No .git/NOTES_MERGE_* files left\n-\ttest_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&\n+\t# No .git/notes_merge_* files left\n+\ttest_might_fail ls .git/notes_merge_* >output 2>/dev/null &&\n \ttest_cmp /dev/null output &&\n \t# Merge commit has pre-merge y and pre-merge z as parents\n \ttest \"$(git rev-parse refs/notes/m^1)\" = \"$(cat pre_merge_y)\" &&\n@@ -369,12 +369,12 @@ test_expect_success 'redo merge of z into m (== y) with default (\"manual\") resol\n \tgit config core.notesRef refs/notes/m &&\n \ttest_must_fail git notes merge z >output &&\n \t# Output should point to where to resolve conflicts\n-\tgrep -q \"\\\\.git/NOTES_MERGE_WORKTREE\" output &&\n+\tgrep -q \"\\\\.git/notes_merge_worktree\" output &&\n \t# Inspect merge conflicts\n-\tls .git/NOTES_MERGE_WORKTREE >output_conflicts &&\n+\tls .git/notes_merge_worktree >output_conflicts &&\n \ttest_cmp expect_conflicts output_conflicts &&\n \t( for f in $(cat expect_conflicts); do\n-\t\ttest_cmp \"expect_conflict_$f\" \".git/NOTES_MERGE_WORKTREE/$f\" ||\n+\t\ttest_cmp \"expect_conflict_$f\" \".git/notes_merge_worktree/$f\" ||\n \t\texit 1\n \tdone ) &&\n \t# Verify that current notes tree (pre-merge) has not changed (m == y)\n@@ -385,8 +385,8 @@ test_expect_success 'redo merge of z into m (== y) with default (\"manual\") resol\n \n test_expect_success 'abort notes merge' '\n \tgit notes merge --abort &&\n-\t# No .git/NOTES_MERGE_* files left\n-\ttest_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&\n+\t# No .git/notes_merge_* files left\n+\ttest_might_fail ls .git/notes_merge_* >output 2>/dev/null &&\n \ttest_cmp /dev/null output &&\n \t# m has not moved (still == y)\n \ttest \"$(git rev-parse refs/notes/m)\" = \"$(cat pre_merge_y)\" &&\n@@ -403,12 +403,12 @@ git rev-parse refs/notes/z > pre_merge_z\n test_expect_success 'redo merge of z into m (== y) with default (\"manual\") resolver => Conflicting 3-way merge' '\n \ttest_must_fail git notes merge z >output &&\n \t# Output should point to where to resolve conflicts\n-\tgrep -q \"\\\\.git/NOTES_MERGE_WORKTREE\" output &&\n+\tgrep -q \"\\\\.git/notes_merge_worktree\" output &&\n \t# Inspect merge conflicts\n-\tls .git/NOTES_MERGE_WORKTREE >output_conflicts &&\n+\tls .git/notes_merge_worktree >output_conflicts &&\n \ttest_cmp expect_conflicts output_conflicts &&\n \t( for f in $(cat expect_conflicts); do\n-\t\ttest_cmp \"expect_conflict_$f\" \".git/NOTES_MERGE_WORKTREE/$f\" ||\n+\t\ttest_cmp \"expect_conflict_$f\" \".git/notes_merge_worktree/$f\" ||\n \t\texit 1\n \tdone ) &&\n \t# Verify that current notes tree (pre-merge) has not changed (m == y)\n@@ -441,19 +441,19 @@ EOF\n \n test_expect_success 'add + remove notes in finalized merge (z => m)' '\n \t# Resolve one conflict\n-\tcat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&\n+\tcat >.git/notes_merge_worktree/$commit_sha1 <<EOF &&\n y and z notes on 1st commit\n EOF\n \t# Remove another conflict\n-\trm .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&\n+\trm .git/notes_merge_worktree/$commit_sha4 &&\n \t# Remove a D/F conflict\n-\trm .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&\n+\trm .git/notes_merge_worktree/$commit_sha3 &&\n \t# Add a new note\n-\techo \"new note on 5th commit\" > .git/NOTES_MERGE_WORKTREE/$commit_sha5 &&\n+\techo \"new note on 5th commit\" > .git/notes_merge_worktree/$commit_sha5 &&\n \t# Finalize merge\n \tgit notes merge --commit &&\n-\t# No .git/NOTES_MERGE_* files left\n-\ttest_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&\n+\t# No .git/notes_merge_* files left\n+\ttest_might_fail ls .git/notes_merge_* >output 2>/dev/null &&\n \ttest_cmp /dev/null output &&\n \t# Merge commit has pre-merge y and pre-merge z as parents\n \ttest \"$(git rev-parse refs/notes/m^1)\" = \"$(cat pre_merge_y)\" &&\n@@ -484,12 +484,12 @@ test_expect_success 'redo merge of z into m (== y) with default (\"manual\") resol\n \tgit update-ref refs/notes/m refs/notes/y &&\n \ttest_must_fail git notes merge z >output &&\n \t# Output should point to where to resolve conflicts\n-\tgrep -q \"\\\\.git/NOTES_MERGE_WORKTREE\" output &&\n+\tgrep -q \"\\\\.git/notes_merge_worktree\" output &&\n \t# Inspect merge conflicts\n-\tls .git/NOTES_MERGE_WORKTREE >output_conflicts &&\n+\tls .git/notes_merge_worktree >output_conflicts &&\n \ttest_cmp expect_conflicts output_conflicts &&\n \t( for f in $(cat expect_conflicts); do\n-\t\ttest_cmp \"expect_conflict_$f\" \".git/NOTES_MERGE_WORKTREE/$f\" ||\n+\t\ttest_cmp \"expect_conflict_$f\" \".git/notes_merge_worktree/$f\" ||\n \t\texit 1\n \tdone ) &&\n \t# Verify that current notes tree (pre-merge) has not changed (m == y)\n@@ -507,31 +507,31 @@ test_expect_success 'reset notes ref m to somewhere else (w)' '\n \ttest \"$(git rev-parse refs/notes/m)\" = \"$(git rev-parse refs/notes/w)\"\n '\n \n-test_expect_success 'fail to finalize conflicting merge if underlying ref has moved in the meantime (m != NOTES_MERGE_PARTIAL^1)' '\n+test_expect_success 'fail to finalize conflicting merge if underlying ref has moved in the meantime (m != notes_merge_partial^1)' '\n \t# Resolve conflicts\n-\tcat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&\n+\tcat >.git/notes_merge_worktree/$commit_sha1 <<EOF &&\n y and z notes on 1st commit\n EOF\n-\tcat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&\n+\tcat >.git/notes_merge_worktree/$commit_sha4 <<EOF &&\n y and z notes on 4th commit\n EOF\n \t# Fail to finalize merge\n \ttest_must_fail git notes merge --commit >output 2>&1 &&\n-\t# .git/NOTES_MERGE_* must remain\n-\ttest -f .git/NOTES_MERGE_PARTIAL &&\n-\ttest -f .git/NOTES_MERGE_REF &&\n-\ttest -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&\n-\ttest -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&\n-\ttest -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&\n-\ttest -f .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&\n+\t# .git/notes_merge_* must remain\n+\tgit rev-parse --verify notes_merge_partial &&\n+\tgit rev-parse --verify notes_merge_ref &&\n+\ttest -f .git/notes_merge_worktree/$commit_sha1 &&\n+\ttest -f .git/notes_merge_worktree/$commit_sha2 &&\n+\ttest -f .git/notes_merge_worktree/$commit_sha3 &&\n+\ttest -f .git/notes_merge_worktree/$commit_sha4 &&\n \t# Refs are unchanged\n \ttest \"$(git rev-parse refs/notes/m)\" = \"$(git rev-parse refs/notes/w)\" &&\n-\ttest \"$(git rev-parse refs/notes/y)\" = \"$(git rev-parse NOTES_MERGE_PARTIAL^1)\" &&\n-\ttest \"$(git rev-parse refs/notes/m)\" != \"$(git rev-parse NOTES_MERGE_PARTIAL^1)\" &&\n+\ttest \"$(git rev-parse refs/notes/y)\" = \"$(git rev-parse notes_merge_partial^1)\" &&\n+\ttest \"$(git rev-parse refs/notes/m)\" != \"$(git rev-parse notes_merge_partial^1)\" &&\n \t# Mention refs/notes/m, and its current and expected value in output\n \tgrep -q \"refs/notes/m\" output &&\n \tgrep -q \"$(git rev-parse refs/notes/m)\" output &&\n-\tgrep -q \"$(git rev-parse NOTES_MERGE_PARTIAL^1)\" output &&\n+\tgrep -q \"$(git rev-parse notes_merge_partial^1)\" output &&\n \t# Verify that other notes refs has not changed (w, x, y and z)\n \tverify_notes w &&\n \tverify_notes x &&\n@@ -541,8 +541,8 @@ EOF\n \n test_expect_success 'resolve situation by aborting the notes merge' '\n \tgit notes merge --abort &&\n-\t# No .git/NOTES_MERGE_* files left\n-\ttest_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&\n+\t# No .git/notes_merge_* files left\n+\ttest_might_fail ls .git/notes_merge_* >output 2>/dev/null &&\n \ttest_cmp /dev/null output &&\n \t# m has not moved (still == w)\n \ttest \"$(git rev-parse refs/notes/m)\" = \"$(git rev-parse refs/notes/w)\" &&\n@@ -563,7 +563,7 @@ test_expect_success 'switch cwd before committing notes merge' '\n \tgit notes --ref=other add -m bar HEAD &&\n \ttest_must_fail git notes merge refs/notes/other &&\n \t(\n-\t\tcd .git/NOTES_MERGE_WORKTREE &&\n+\t\tcd .git/notes_merge_worktree &&\n \t\techo \"foo\" > $(git rev-parse HEAD) &&\n \t\techo \"bar\" >> $(git rev-parse HEAD) &&\n \t\tgit notes merge --commit\ndiff --git a/t/t3311-notes-merge-fanout.sh b/t/t3311-notes-merge-fanout.sh\nindex 93516ef..eeaea62 100755\n--- a/t/t3311-notes-merge-fanout.sh\n+++ b/t/t3311-notes-merge-fanout.sh\n@@ -387,10 +387,10 @@ other notes for commit4\n EOF\n \n test_expect_success 'verify conflict entries (with no fanout)' '\n-\tls .git/NOTES_MERGE_WORKTREE >output_conflicts &&\n+\tls .git/notes_merge_worktree >output_conflicts &&\n \ttest_cmp expect_conflicts output_conflicts &&\n \t( for f in $(cat expect_conflicts); do\n-\t\ttest_cmp \"expect_conflict_$f\" \".git/NOTES_MERGE_WORKTREE/$f\" ||\n+\t\ttest_cmp \"expect_conflict_$f\" \".git/notes_merge_worktree/$f\" ||\n \t\texit 1\n \tdone ) &&\n \t# Verify that current notes tree (pre-merge) has not changed (m == w)\n@@ -417,7 +417,7 @@ other notes for commit1\n EOF\n \n test_expect_success 'resolve and finalize merge (z => w)' '\n-\tcat >.git/NOTES_MERGE_WORKTREE/$commit_sha3 <<EOF &&\n+\tcat >.git/notes_merge_worktree/$commit_sha3 <<EOF &&\n other notes for commit3\n \n appended notes for commit3\n-- \n2.0.4.315.gad8727a-twtrsrc\n"},{"id":"266856","messageId":"1438027720-23074-4-git-send-email-dturner@twopensource.com","threadId":"39944","inReplyTo":"1438027720-23074-1-git-send-email-dturner@twopensource.com","subject":"[PATCH 3/5] pseudorefs: create and use pseudoref update and delete functions","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-27T20:08:38Z","receivedAt":"2015-07-27T20:08:38Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Pseudorefs should not be updated through the ref transaction\nAPI, because alternate ref backends still need to store pseudorefs\nin GIT_DIR (instead of wherever they store refs).  Instead,\nchange update_ref and delete_ref to call pseudoref-specific\nfunctions.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\n---\n refs.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------\n 1 file changed, 92 insertions(+), 8 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 0d10b7b..119ac9c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2871,12 +2871,87 @@ static int is_pseudoref(const char *refname)\n \treturn 1;\n }\n \n+static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,\n+\t\t\t   const unsigned char *old_sha1, struct strbuf *err)\n+{\n+\tconst char *filename;\n+\tint fd;\n+\tstatic struct lock_file lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint ret = -1;\n+\n+\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(sha1));\n+\n+\tfilename = git_path(\"%s\", pseudoref);\n+\tfd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);\n+\tif (fd < 0) {\n+\t\tstrbuf_addf(err, \"Could not open '%s' for writing: %s\",\n+\t\t\t    filename, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\tif (old_sha1) {\n+\t\tunsigned char actual_old_sha1[20];\n+\t\tread_ref(pseudoref, actual_old_sha1);\n+\t\tif (hashcmp(actual_old_sha1, old_sha1)) {\n+\t\t\tstrbuf_addf(err, \"Unexpected sha1 when writing %s\", pseudoref);\n+\t\t\trollback_lock_file(&lock);\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tif (write_in_full(fd, buf.buf, buf.len) != buf.len) {\n+\t\tstrbuf_addf(err, \"Could not write to '%s'\", filename);\n+\t\trollback_lock_file(&lock);\n+\t\tgoto done;\n+\t}\n+\n+\tcommit_lock_file(&lock);\n+\tret = 0;\n+done:\n+\tstrbuf_release(&buf);\n+\treturn ret;\n+}\n+\n+static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)\n+{\n+\tstatic struct lock_file lock;\n+\tconst char *filename;\n+\n+\tfilename = git_path(\"%s\", pseudoref);\n+\n+\tif (old_sha1 && !is_null_sha1(old_sha1)) {\n+\t\tint fd;\n+\t\tunsigned char actual_old_sha1[20];\n+\n+\t\tfd = hold_lock_file_for_update(&lock, filename,\n+\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n+\t\tif (fd < 0)\n+\t\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n+\t\tread_ref(pseudoref, actual_old_sha1);\n+\t\tif (hashcmp(actual_old_sha1, old_sha1)) {\n+\t\t\twarning(\"Unexpected sha1 when deleting %s\", pseudoref);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tunlink(filename);\n+\t\trollback_lock_file(&lock);\n+\t} else {\n+\t\tunlink(filename);\n+\t}\n+\n+\treturn 0;\n+}\n+\n int delete_ref(const char *refname, const unsigned char *old_sha1,\n \t       unsigned int flags)\n {\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n+\tif (is_pseudoref(refname))\n+\t\treturn delete_pseudoref(refname, old_sha1);\n+\n \ttransaction = ref_transaction_begin(&err);\n \tif (!transaction ||\n \t    ref_transaction_delete(transaction, refname, old_sha1,\n@@ -3970,17 +4045,25 @@ int update_ref(const char *msg, const char *refname,\n \t       const unsigned char *new_sha1, const unsigned char *old_sha1,\n \t       unsigned int flags, enum action_on_err onerr)\n {\n-\tstruct ref_transaction *t;\n+\tstruct ref_transaction *t = NULL;\n \tstruct strbuf err = STRBUF_INIT;\n+\tint ret = 0;\n \n-\tt = ref_transaction_begin(&err);\n-\tif (!t ||\n-\t    ref_transaction_update(t, refname, new_sha1, old_sha1,\n-\t\t\t\t   flags, msg, &err) ||\n-\t    ref_transaction_commit(t, &err)) {\n+\tif (is_pseudoref(refname) && 0) {\n+\t\tret = write_pseudoref(refname, new_sha1, old_sha1, &err);\n+\t} else {\n+\t\tt = ref_transaction_begin(&err);\n+\t\tif (!t ||\n+\t\t    ref_transaction_update(t, refname, new_sha1, old_sha1,\n+\t\t\t\t\t   flags, msg, &err) ||\n+\t\t    ref_transaction_commit(t, &err)) {\n+\t\t\tret = 1;\n+\t\t\tref_transaction_free(t);\n+\t\t}\n+\t}\n+\tif (ret) {\n \t\tconst char *str = \"update_ref failed for ref '%s': %s\";\n \n-\t\tref_transaction_free(t);\n \t\tswitch (onerr) {\n \t\tcase UPDATE_REFS_MSG_ON_ERR:\n \t\t\terror(str, refname, err.buf);\n@@ -3995,7 +4078,8 @@ int update_ref(const char *msg, const char *refname,\n \t\treturn 1;\n \t}\n \tstrbuf_release(&err);\n-\tref_transaction_free(t);\n+\tif (t)\n+\t\tref_transaction_free(t);\n \treturn 0;\n }\n \n-- \n2.0.4.315.gad8727a-twtrsrc\n"},{"id":"266859","messageId":"1438027720-23074-5-git-send-email-dturner@twopensource.com","threadId":"39944","inReplyTo":"1438027720-23074-1-git-send-email-dturner@twopensource.com","subject":"[PATCH 4/5] rebase: use update_ref","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-27T20:08:39Z","receivedAt":"2015-07-27T20:08:39Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Instead of manually writing a pseudoref (in one case) and shelling out\nto git update-ref (in another), use the update_ref function.  This\nis much simpler.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\n---\n bisect.c | 37 ++++++++-----------------------------\n 1 file changed, 8 insertions(+), 29 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 857cf59..33ac88d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -19,7 +19,6 @@ static struct object_id *current_bad_oid;\n \n static const char *argv_checkout[] = {\"checkout\", \"-q\", NULL, \"--\", NULL};\n static const char *argv_show_branch[] = {\"show-branch\", NULL, NULL};\n-static const char *argv_update_ref[] = {\"update-ref\", \"--no-deref\", \"BISECT_HEAD\", NULL, NULL};\n \n static const char *term_bad;\n static const char *term_good;\n@@ -675,34 +674,16 @@ static int is_expected_rev(const struct object_id *oid)\n \treturn res;\n }\n \n-static void mark_expected_rev(char *bisect_rev_hex)\n-{\n-\tint len = strlen(bisect_rev_hex);\n-\tconst char *filename = git_path(\"BISECT_EXPECTED_REV\");\n-\tint fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);\n-\n-\tif (fd < 0)\n-\t\tdie_errno(\"could not create file '%s'\", filename);\n-\n-\tbisect_rev_hex[len] = '\\n';\n-\twrite_or_die(fd, bisect_rev_hex, len + 1);\n-\tbisect_rev_hex[len] = '\\0';\n-\n-\tif (close(fd) < 0)\n-\t\tdie(\"closing file %s: %s\", filename, strerror(errno));\n-}\n-\n-static int bisect_checkout(char *bisect_rev_hex, int no_checkout)\n+static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)\n {\n+\tchar bisect_rev_hex[GIT_SHA1_HEXSZ + 1];\n \n-\tmark_expected_rev(bisect_rev_hex);\n+\tmemcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);\n+\tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n \n \targv_checkout[2] = bisect_rev_hex;\n \tif (no_checkout) {\n-\t\targv_update_ref[3] = bisect_rev_hex;\n-\t\tif (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))\n-\t\t\tdie(\"update-ref --no-deref HEAD failed on %s\",\n-\t\t\t    bisect_rev_hex);\n+\t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n \t} else {\n \t\tint res;\n \t\tres = run_command_v_opt(argv_checkout, RUN_GIT_CMD);\n@@ -804,7 +785,7 @@ static void check_merge_bases(int no_checkout)\n \t\t\thandle_skipped_merge_base(mb);\n \t\t} else {\n \t\t\tprintf(\"Bisecting: a merge base must be tested\\n\");\n-\t\t\texit(bisect_checkout(sha1_to_hex(mb), no_checkout));\n+\t\t\texit(bisect_checkout(mb, no_checkout));\n \t\t}\n \t}\n \n@@ -948,7 +929,6 @@ int bisect_next_all(const char *prefix, int no_checkout)\n \tstruct commit_list *tried;\n \tint reaches = 0, all = 0, nr, steps;\n \tconst unsigned char *bisect_rev;\n-\tchar bisect_rev_hex[GIT_SHA1_HEXSZ + 1];\n \n \tread_bisect_terms(&term_bad, &term_good);\n \tif (read_bisect_refs())\n@@ -986,11 +966,10 @@ int bisect_next_all(const char *prefix, int no_checkout)\n \t}\n \n \tbisect_rev = revs.commits->item->object.sha1;\n-\tmemcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);\n \n \tif (!hashcmp(bisect_rev, current_bad_oid->hash)) {\n \t\texit_if_skipped_commits(tried, current_bad_oid);\n-\t\tprintf(\"%s is the first %s commit\\n\", bisect_rev_hex,\n+\t\tprintf(\"%s is the first %s commit\\n\", sha1_to_hex(bisect_rev),\n \t\t\tterm_bad);\n \t\tshow_diff_tree(prefix, revs.commits->item);\n \t\t/* This means the bisection process succeeded. */\n@@ -1003,7 +982,7 @@ int bisect_next_all(const char *prefix, int no_checkout)\n \t       \"(roughly %d step%s)\\n\", nr, (nr == 1 ? \"\" : \"s\"),\n \t       steps, (steps == 1 ? \"\" : \"s\"));\n \n-\treturn bisect_checkout(bisect_rev_hex, no_checkout);\n+\treturn bisect_checkout(bisect_rev, no_checkout);\n }\n \n static inline int log2i(int n)\n-- \n2.0.4.315.gad8727a-twtrsrc\n"},{"id":"266857","messageId":"1438027720-23074-6-git-send-email-dturner@twopensource.com","threadId":"39944","inReplyTo":"1438027720-23074-1-git-send-email-dturner@twopensource.com","subject":"[PATCH 5/5] sequencer: replace write_cherry_pick_head with update_ref","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-27T20:08:40Z","receivedAt":"2015-07-27T20:08:40Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"Now update_ref (via write_pseudoref) does almost exactly what\nwrite_cherry_pick_head did, so we can remove write_cherry_pick_head\nand just use update_ref.\n\nSigned-off-by: David Turner <dturner@twopensource.com>\n---\n sequencer.c | 23 ++++-------------------\n 1 file changed, 4 insertions(+), 19 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex c4f4b7d..554a704 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -158,23 +158,6 @@ static void free_message(struct commit *commit, struct commit_message *msg)\n \tunuse_commit_buffer(commit, msg->message);\n }\n \n-static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)\n-{\n-\tconst char *filename;\n-\tint fd;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n-\n-\tfilename = git_path(\"%s\", pseudoref);\n-\tfd = open(filename, O_WRONLY | O_CREAT, 0666);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n-\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n-\t\tdie_errno(_(\"Could not write to '%s'\"), filename);\n-\tstrbuf_release(&buf);\n-}\n-\n static void print_advice(int show_hint, struct replay_opts *opts)\n {\n \tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n@@ -607,9 +590,11 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * write it at all.\n \t */\n \tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n+\t\tupdate_ref(NULL, \"CHERRY_PICK_HEAD\", commit->object.sha1, NULL,\n+\t\t\t   REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);\n \tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n+\t\tupdate_ref(NULL, \"REVERT_HEAD\", commit->object.sha1, NULL,\n+\t\t\t   REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);\n \n \tif (res) {\n \t\terror(opts->action == REPLAY_REVERT\n-- \n2.0.4.315.gad8727a-twtrsrc\n"},{"id":"266870","messageId":"xmqq8ua1gsus.fsf@gitster.dls.corp.google.com","threadId":"39944","inReplyTo":"1438027720-23074-1-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH v2 0/5] pseudorefs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-27T22:04:27Z","receivedAt":"2015-07-27T22:04:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Turner <dturner@twopensource.com> writes:\n\n> This version of the pseudorefs patch series is much simpler.  Instead\n> of forbidding update_ref and delete_ref from updating pseudorefs,\n> these functions now just special-case pseudorefs.  So we can use\n> update_ref to write pseudorefs in a rebase and sequencer, and\n> we don't need to rewrite so much code.\n\nThe resulting update_ref/delete_ref look quite straight-forward,\ntoo.  Thanks.\n\n> In addition, I made typo fixes suggested by Eric Sunshine and Philip\n> Oakley.\n"},{"id":"266876","messageId":"1438044586.18134.9.camel@twopensource.com","threadId":"39944","inReplyTo":"1438027720-23074-4-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH 3/5] pseudorefs: create and use pseudoref update and delete functions","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-28T00:49:46Z","receivedAt":"2015-07-28T00:49:46Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"On Mon, 2015-07-27 at 16:08 -0400, David Turner wrote:\n> +\tif (is_pseudoref(refname) && 0) {\n\nWhoa, that's not right.  Will fix that in the next re-roll.\n"},{"id":"266946","messageId":"CAPig+cRgp1F3S9DXWGABmrSDZM1As-R6FB7oBQbkoDLy-v6_PA@mail.gmail.com","threadId":"39944","inReplyTo":"1438027720-23074-2-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH 1/5] refs: Introduce pseudoref and per-worktree ref concepts","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-28T17:13:14Z","receivedAt":"2015-07-28T17:13:14Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jul 27, 2015 at 4:08 PM, David Turner <dturner@twopensource.com> wrote:\n> refs: Introduce pseudoref and per-worktree ref concepts\n>\n> Add glossary entries for both concepts.\n\nBased upon the above, I thought this was going to be a\ndocumentation-only patch and was mildly surprised to find that it also\nchanged code. Perhaps:\n\n    Describe these concepts in the glossary and introduce\n    is_per_worktree_ref() to distinguish such files.\n\nor something. Of course the \"and\" in there suggests that this might be\nbetter off split into two patches...\n\nMore below.\n\n> Pseudorefs and per-worktree refs do not yet have special handling,\n> because the files refs backend already handles them correctly.  Later,\n> we will make the LMDB backend call out to the files backend to handle\n> per-worktree refs.\n>\n> Signed-off-by: David Turner <dturner@twopensource.com>\n> ---\n> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt\n> index ab18f4b..67952f3 100644\n> --- a/Documentation/glossary-content.txt\n> +++ b/Documentation/glossary-content.txt\n> @@ -411,6 +411,27 @@ exclude;;\n>         core Git. Porcelains expose more of a <<def_SCM,SCM>>\n>         interface than the <<def_plumbing,plumbing>>.\n>\n> +[[def_per_worktree_ref]]per-worktree ref::\n> +       Refs that are per-<<def_worktree,worktree>>, rather than\n> +       global.  This is presently only <<def_HEAD,HEAD>>, but might\n> +       later include other unusual refs.\n> +\n> +[[def_pseudoref]]pseudoref::\n> +       Pseudorefs are a class of files under `$GIT_DIR` which behave\n> +       like refs for the purposes of rev-parse, but which are treated\n> +       specially by git.  Psuedorefs both have names are all-caps,\n\ns/names/& that/\n\n> +       and always start with a line consisting of a\n> +       <<def_sha1,SHA-1>> followed by whitespace.  So, HEAD is not a\n> +       pseudoref, because it is sometimes a symbolic ref.  They might\n> +       optionally some additional data.  `MERGE_HEAD` and\n\ns/optionally/& contain/\n\n> +       `CHERRY_PICK_HEAD` are examples.  Unlike\n> +       <<def_per_worktree_ref,per-worktree refs>>, these files cannot\n> +       be symbolic refs, and never have reflogs.  They also cannot be\n> +       updated through the normal ref update machinery.  Instead,\n> +       they are updated by directly writing to the files.  However,\n> +       they can be read as if they were refs, so `git rev-parse\n> +       MERGE_HEAD` will work.\n> +\n>  [[def_pull]]pull::\n>         Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and\n>         <<def_merge,merge>> it.  See also linkgit:git-pull[1].\n> diff --git a/refs.c b/refs.c\n> index 0b96ece..0d10b7b 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -2848,6 +2848,29 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)\n>         return 0;\n>  }\n>\n> +int is_per_worktree_ref(const char *refname)\n> +{\n> +       return !strcmp(refname, \"HEAD\");\n> +}\n> +\n> +static int is_pseudoref(const char *refname)\n> +{\n> +       const char *c;\n> +\n> +       if (strchr(refname, '/'))\n> +               return 0;\n> +\n> +       if (is_per_worktree_ref(refname))\n> +               return 0;\n> +\n> +       for (c = refname; *c; ++c) {\n> +               if (!isupper(*c) && *c != '-' && *c != '_')\n> +                       return 0;\n> +       }\n> +\n> +       return 1;\n> +}\n\nThis static function doesn't seem to have any callers, thus seems out\nof place in this patch.\n\n> +\n>  int delete_ref(const char *refname, const unsigned char *old_sha1,\n>                unsigned int flags)\n>  {\n"},{"id":"266962","messageId":"xmqq4mkof8nq.fsf@gitster.dls.corp.google.com","threadId":"39944","inReplyTo":"1438027720-23074-5-git-send-email-dturner@twopensource.com","subject":"Re: [PATCH 4/5] rebase: use update_ref","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-28T18:18:17Z","receivedAt":"2015-07-28T18:18:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Turner <dturner@twopensource.com> writes:\n\n> Instead of manually writing a pseudoref (in one case) and shelling out\n> to git update-ref (in another), use the update_ref function.  This\n> is much simpler.\n>\n> Signed-off-by: David Turner <dturner@twopensource.com>\n> ---\n>  bisect.c | 37 ++++++++-----------------------------\n>  1 file changed, 8 insertions(+), 29 deletions(-)\n\nMistitled?  I can do s/rebase/bisect/ at my end if that is all\nneeded.\n\n>\n> diff --git a/bisect.c b/bisect.c\n> index 857cf59..33ac88d 100644\n> --- a/bisect.c\n> +++ b/bisect.c\n> @@ -19,7 +19,6 @@ static struct object_id *current_bad_oid;\n>  \n>  static const char *argv_checkout[] = {\"checkout\", \"-q\", NULL, \"--\", NULL};\n>  static const char *argv_show_branch[] = {\"show-branch\", NULL, NULL};\n> -static const char *argv_update_ref[] = {\"update-ref\", \"--no-deref\", \"BISECT_HEAD\", NULL, NULL};\n>  \n>  static const char *term_bad;\n>  static const char *term_good;\n> @@ -675,34 +674,16 @@ static int is_expected_rev(const struct object_id *oid)\n>  \treturn res;\n>  }\n>  \n> -static void mark_expected_rev(char *bisect_rev_hex)\n> -{\n> -\tint len = strlen(bisect_rev_hex);\n> -\tconst char *filename = git_path(\"BISECT_EXPECTED_REV\");\n> -\tint fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);\n> -\n> -\tif (fd < 0)\n> -\t\tdie_errno(\"could not create file '%s'\", filename);\n> -\n> -\tbisect_rev_hex[len] = '\\n';\n> -\twrite_or_die(fd, bisect_rev_hex, len + 1);\n> -\tbisect_rev_hex[len] = '\\0';\n> -\n> -\tif (close(fd) < 0)\n> -\t\tdie(\"closing file %s: %s\", filename, strerror(errno));\n> -}\n> -\n> -static int bisect_checkout(char *bisect_rev_hex, int no_checkout)\n> +static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)\n>  {\n> +\tchar bisect_rev_hex[GIT_SHA1_HEXSZ + 1];\n>  \n> -\tmark_expected_rev(bisect_rev_hex);\n> +\tmemcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);\n> +\tupdate_ref(NULL, \"BISECT_EXPECTED_REV\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n>  \n>  \targv_checkout[2] = bisect_rev_hex;\n>  \tif (no_checkout) {\n> -\t\targv_update_ref[3] = bisect_rev_hex;\n> -\t\tif (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))\n> -\t\t\tdie(\"update-ref --no-deref HEAD failed on %s\",\n> -\t\t\t    bisect_rev_hex);\n> +\t\tupdate_ref(NULL, \"BISECT_HEAD\", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);\n>  \t} else {\n>  \t\tint res;\n>  \t\tres = run_command_v_opt(argv_checkout, RUN_GIT_CMD);\n> @@ -804,7 +785,7 @@ static void check_merge_bases(int no_checkout)\n>  \t\t\thandle_skipped_merge_base(mb);\n>  \t\t} else {\n>  \t\t\tprintf(\"Bisecting: a merge base must be tested\\n\");\n> -\t\t\texit(bisect_checkout(sha1_to_hex(mb), no_checkout));\n> +\t\t\texit(bisect_checkout(mb, no_checkout));\n>  \t\t}\n>  \t}\n>  \n> @@ -948,7 +929,6 @@ int bisect_next_all(const char *prefix, int no_checkout)\n>  \tstruct commit_list *tried;\n>  \tint reaches = 0, all = 0, nr, steps;\n>  \tconst unsigned char *bisect_rev;\n> -\tchar bisect_rev_hex[GIT_SHA1_HEXSZ + 1];\n>  \n>  \tread_bisect_terms(&term_bad, &term_good);\n>  \tif (read_bisect_refs())\n> @@ -986,11 +966,10 @@ int bisect_next_all(const char *prefix, int no_checkout)\n>  \t}\n>  \n>  \tbisect_rev = revs.commits->item->object.sha1;\n> -\tmemcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);\n>  \n>  \tif (!hashcmp(bisect_rev, current_bad_oid->hash)) {\n>  \t\texit_if_skipped_commits(tried, current_bad_oid);\n> -\t\tprintf(\"%s is the first %s commit\\n\", bisect_rev_hex,\n> +\t\tprintf(\"%s is the first %s commit\\n\", sha1_to_hex(bisect_rev),\n>  \t\t\tterm_bad);\n>  \t\tshow_diff_tree(prefix, revs.commits->item);\n>  \t\t/* This means the bisection process succeeded. */\n> @@ -1003,7 +982,7 @@ int bisect_next_all(const char *prefix, int no_checkout)\n>  \t       \"(roughly %d step%s)\\n\", nr, (nr == 1 ? \"\" : \"s\"),\n>  \t       steps, (steps == 1 ? \"\" : \"s\"));\n>  \n> -\treturn bisect_checkout(bisect_rev_hex, no_checkout);\n> +\treturn bisect_checkout(bisect_rev, no_checkout);\n>  }\n>  \n>  static inline int log2i(int n)\n"},{"id":"266965","messageId":"1438109626.18134.13.camel@twopensource.com","threadId":"39944","inReplyTo":"xmqq4mkof8nq.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 4/5] rebase: use update_ref","fromName":"David Turner","fromEmail":"dturner@twopensource.com","sentAt":"2015-07-28T18:53:46Z","receivedAt":"2015-07-28T18:53:46Z","isPatch":true,"sender":{"key":"novalis@novalis.org","avatar":"https://avatars.githubusercontent.com/u/77003?v=4"},"body":"On Tue, 2015-07-28 at 11:18 -0700, Junio C Hamano wrote:\n> David Turner <dturner@twopensource.com> writes:\n> \n> > Instead of manually writing a pseudoref (in one case) and shelling out\n> > to git update-ref (in another), use the update_ref function.  This\n> > is much simpler.\n> >\n> > Signed-off-by: David Turner <dturner@twopensource.com>\n> > ---\n> >  bisect.c | 37 ++++++++-----------------------------\n> >  1 file changed, 8 insertions(+), 29 deletions(-)\n> \n> Mistitled?  I can do s/rebase/bisect/ at my end if that is all\n> needed.\n\nApparently, yes.  Thanks.\n"}]}