git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH/RFC 0/9] Pseudorefs
@ 2015-07-24  4:45 David Turner
  2015-07-24  4:45 ` [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts David Turner
                   ` (9 more replies)
  0 siblings, 10 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger

This series is another chunk of the pluggable refs backend work.  The
major reason it is listed as "PATCH/RFC" is beacuse it breaks
t9300-fast-import.sh, because fast-import wants to create a ref called
TEMP_TAG, which would now be a pseudoref.  The commit that introduces
this test says that cvs2svn creates a tag called TAG_FIXUP "as a branch
name for temporary work needed to cleanup the tree prior to creating
an annotated tag object."

It appears that cvs2svn still does this.  So I'm not quite sure what to
do about this particular case.

As we discussed earlier, the motivation for this series is that refs
backends other than the files-based backend need to treat per-worktree
refs (HEAD) and pseudorefs (FETCH_HEAD) differently from other refs;
other refs are per-repo rather than per-worktree.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/9] refs:  Introduce pseudoref and per-worktree ref concepts
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24  7:34   ` Eric Sunshine
                     ` (2 more replies)
  2015-07-24  4:45 ` [PATCH 2/9] notes: replace pseudorefs with real refs David Turner
                   ` (8 subsequent siblings)
  9 siblings, 3 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

Add glossary entries for both concepts.

Pseudorefs and per-worktree refs do not yet have special handling,
because the files refs backend already handles them correctly.  Later,
we will make the LMDB backend call out to the files backend to handle
per-worktree refs.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 Documentation/glossary-content.txt | 17 +++++++++++++++++
 refs.c                             | 23 +++++++++++++++++++++++
 refs.h                             |  2 ++
 3 files changed, 42 insertions(+)

diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index ab18f4b..d04819e 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -411,6 +411,23 @@ exclude;;
 	core Git. Porcelains expose more of a <<def_SCM,SCM>>
 	interface than the <<def_plumbing,plumbing>>.
 
+[[def_per_worktree_ref]]per-worktree ref::
+	Refs that are per-<<def_worktree,worktree>>, rather than
+	global.  This is presently only <<def_HEAD,HEAD>>, but might
+	later include other unsuual refs.
+
+[[def_pseudoref]]pseudoref::
+	Files under `$GIT_DIR` whose names are all-caps, and that
+	contain a line consisting of a <<def_sha1,SHA-1>> followed by
+	a newline, and optionally some additional data.  `MERGE_HEAD`
+	and `CHERRY_PICK_HEAD` are examples.  Unlike
+	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
+	be symbolic refs, and never not have reflogs.  They also
+	cannot be updated through the normal ref update machinery.
+	Instead, they are updated by directly writing to the files.
+	However, they can be read as if they were refs, so `git
+	rev-parse MERGE_HEAD` will work.
+
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
 	<<def_merge,merge>> it.  See also linkgit:git-pull[1].
diff --git a/refs.c b/refs.c
index 0b96ece..d31ca42 100644
--- a/refs.c
+++ b/refs.c
@@ -3857,6 +3857,29 @@ void ref_transaction_free(struct ref_transaction *transaction)
 	free(transaction);
 }
 
+int is_per_worktree_ref(const char *refname)
+{
+	return !strcmp(refname, "HEAD");
+}
+
+static int is_pseudoref(const char *refname)
+{
+	const char *c;
+
+	if (strchr(refname, '/'))
+		return 0;
+
+	if (is_per_worktree_ref(refname))
+		return 0;
+
+	for (c = refname; *c; ++c) {
+		if (!isupper(*c) && *c != '-' && *c != '_')
+			return 0;
+	}
+
+	return 1;
+}
+
 static struct ref_update *add_update(struct ref_transaction *transaction,
 				     const char *refname)
 {
diff --git a/refs.h b/refs.h
index e4e46c3..bd5526e 100644
--- a/refs.h
+++ b/refs.h
@@ -445,6 +445,8 @@ extern int parse_hide_refs_config(const char *var, const char *value, const char
 
 extern int ref_is_hidden(const char *);
 
+int is_per_worktree_ref(const char *refname);
+
 enum expire_reflog_flags {
 	EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
 	EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/9] notes: replace pseudorefs with real refs
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
  2015-07-24  4:45 ` [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24  4:45 ` [PATCH 3/9] bisect: do not use update-ref for BISECT_HEAD David Turner
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

All-caps files like NOTES_MERGE_REF are pseudorefs, and thus are
per-worktree.  We don't want multiple notes merges happening at once
(in different worktrees), so we want to make these refs true refs.

So, we lowercase NOTES_MERGE_REF and friends.  That way, backends
that distinguish between pseudorefs and real refs can correctly
handle notes merges.

This will also enable us to prevent pseudorefs from being updated by
the ref update machinery e.g. git update-ref.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 Documentation/git-notes.txt           | 12 ++---
 builtin/notes.c                       | 38 ++++++++--------
 notes-merge.c                         | 10 ++--
 notes-merge.h                         |  8 ++--
 t/t3310-notes-merge-manual-resolve.sh | 86 +++++++++++++++++------------------
 t/t3311-notes-merge-fanout.sh         |  6 +--
 6 files changed, 80 insertions(+), 80 deletions(-)

diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 851518d..82fa3fd 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -103,7 +103,7 @@ merge::
 If conflicts arise and a strategy for automatically resolving
 conflicting notes (see the -s/--strategy option) is not given,
 the "manual" resolver is used. This resolver checks out the
-conflicting notes in a special worktree (`.git/NOTES_MERGE_WORKTREE`),
+conflicting notes in a special worktree (`.git/notes_merge_worktree`),
 and instructs the user to manually resolve the conflicts there.
 When done, the user can either finalize the merge with
 'git notes merge --commit', or abort the merge with
@@ -189,11 +189,11 @@ OPTIONS
 --commit::
 	Finalize an in-progress 'git notes merge'. Use this option
 	when you have resolved the conflicts that 'git notes merge'
-	stored in .git/NOTES_MERGE_WORKTREE. This amends the partial
+	stored in .git/notes_merge_worktree. This amends the partial
 	merge commit created by 'git notes merge' (stored in
-	.git/NOTES_MERGE_PARTIAL) by adding the notes in
-	.git/NOTES_MERGE_WORKTREE. The notes ref stored in the
-	.git/NOTES_MERGE_REF symref is updated to the resulting commit.
+	.git/notes_merge_partial) by adding the notes in
+	.git/notes_merge_worktree. The notes ref stored in the
+	.git/notes_merge_ref symref is updated to the resulting commit.
 
 --abort::
 	Abort/reset a in-progress 'git notes merge', i.e. a notes merge
@@ -241,7 +241,7 @@ NOTES MERGE STRATEGIES
 
 The default notes merge strategy is "manual", which checks out
 conflicting notes in a special work tree for resolving notes conflicts
-(`.git/NOTES_MERGE_WORKTREE`), and instructs the user to resolve the
+(`.git/notes_merge_worktree`), and instructs the user to resolve the
 conflicts in that work tree.
 When done, the user can either finalize the merge with
 'git notes merge --commit', or abort the merge with
diff --git a/builtin/notes.c b/builtin/notes.c
index 63f95fc..e0b8a02 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -670,14 +670,14 @@ static int merge_abort(struct notes_merge_options *o)
 	int ret = 0;
 
 	/*
-	 * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call
-	 * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
+	 * Remove .git/notes_merge_partial and .git/notes_merge_ref, and call
+	 * notes_merge_abort() to remove .git/notes_merge_worktree.
 	 */
 
-	if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0))
-		ret += error("Failed to delete ref NOTES_MERGE_PARTIAL");
-	if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF))
-		ret += error("Failed to delete ref NOTES_MERGE_REF");
+	if (delete_ref("notes_merge_partial", NULL, 0))
+		ret += error("Failed to delete ref notes_merge_partial");
+	if (delete_ref("notes_merge_ref", NULL, REF_NODEREF))
+		ret += error("Failed to delete ref notes_merge_ref");
 	if (notes_merge_abort(o))
 		ret += error("Failed to remove 'git notes merge' worktree");
 	return ret;
@@ -694,16 +694,16 @@ static int merge_commit(struct notes_merge_options *o)
 	int ret;
 
 	/*
-	 * Read partial merge result from .git/NOTES_MERGE_PARTIAL,
-	 * and target notes ref from .git/NOTES_MERGE_REF.
+	 * Read partial merge result from .git/notes_merge_partial,
+	 * and target notes ref from .git/notes_merge_ref.
 	 */
 
-	if (get_sha1("NOTES_MERGE_PARTIAL", sha1))
-		die("Failed to read ref NOTES_MERGE_PARTIAL");
+	if (get_sha1("notes_merge_partial", sha1))
+		die("Failed to read ref notes_merge_partial");
 	else if (!(partial = lookup_commit_reference(sha1)))
-		die("Could not find commit from NOTES_MERGE_PARTIAL.");
+		die("Could not find commit from notes_merge_partial.");
 	else if (parse_commit(partial))
-		die("Could not parse commit from NOTES_MERGE_PARTIAL.");
+		die("Could not parse commit from notes_merge_partial.");
 
 	if (partial->parents)
 		hashcpy(parent_sha1, partial->parents->item->object.sha1);
@@ -711,12 +711,12 @@ static int merge_commit(struct notes_merge_options *o)
 		hashclr(parent_sha1);
 
 	t = xcalloc(1, sizeof(struct notes_tree));
-	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
+	init_notes(t, "notes_merge_partial", combine_notes_overwrite, 0);
 
 	o->local_ref = local_ref_to_free =
-		resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL);
+		resolve_refdup("notes_merge_ref", 0, sha1, NULL);
 	if (!o->local_ref)
-		die("Failed to resolve NOTES_MERGE_REF");
+		die("Failed to resolve notes_merge_ref");
 
 	if (notes_merge_commit(o, t, partial, sha1))
 		die("Failed to finalize notes merge");
@@ -825,11 +825,11 @@ static int merge(int argc, const char **argv, const char *prefix)
 		update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
 	else { /* Merge has unresolved conflicts */
-		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
-		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
+		/* Update .git/notes_merge_partial with partial merge result */
+		update_ref(msg.buf, "notes_merge_partial", result_sha1, NULL,
 			   0, UPDATE_REFS_DIE_ON_ERR);
-		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+		/* Store ref-to-be-updated into .git/notes_merge_ref */
+		if (create_symref("notes_merge_ref", default_notes_ref(), NULL))
 			die("Failed to store link to current notes ref (%s)",
 			    default_notes_ref());
 		printf("Automatic notes merge failed. Fix conflicts in %s and "
diff --git a/notes-merge.c b/notes-merge.c
index 0b2b82c..58a8637 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -274,10 +274,10 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
 				    "'git notes merge --commit' or 'git notes "
 				    "merge --abort' to commit/abort the "
 				    "previous merge before you start a new "
-				    "notes merge.", git_path("NOTES_MERGE_*"));
+				    "notes merge.", git_path("notes_merge_*"));
 			else
 				die("You have not concluded your notes merge "
-				    "(%s exists).", git_path("NOTES_MERGE_*"));
+				    "(%s exists).", git_path("notes_merge_*"));
 		}
 
 		if (safe_create_leading_directories_const(git_path(
@@ -663,7 +663,7 @@ int notes_merge_commit(struct notes_merge_options *o,
 		       unsigned char *result_sha1)
 {
 	/*
-	 * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
+	 * Iterate through files in .git/notes_merge_workree and add all
 	 * found notes to 'partial_tree'. Write the updated notes tree to
 	 * the DB, and commit the resulting tree object while reusing the
 	 * commit message and parents from 'partial_commit'.
@@ -734,8 +734,8 @@ int notes_merge_commit(struct notes_merge_options *o,
 int notes_merge_abort(struct notes_merge_options *o)
 {
 	/*
-	 * Remove all files within .git/NOTES_MERGE_WORKTREE. We do not remove
-	 * the .git/NOTES_MERGE_WORKTREE directory itself, since it might be
+	 * Remove all files within .git/notes_merge_workree. We do not remove
+	 * the .git/notes_merge_workree directory itself, since it might be
 	 * the current working directory of the user.
 	 */
 	struct strbuf buf = STRBUF_INIT;
diff --git a/notes-merge.h b/notes-merge.h
index 1d01f6a..18705d6 100644
--- a/notes-merge.h
+++ b/notes-merge.h
@@ -1,7 +1,7 @@
 #ifndef NOTES_MERGE_H
 #define NOTES_MERGE_H
 
-#define NOTES_MERGE_WORKTREE "NOTES_MERGE_WORKTREE"
+#define NOTES_MERGE_WORKTREE "notes_merge_worktree"
 
 enum notes_merge_verbosity {
 	NOTES_MERGE_VERBOSITY_DEFAULT = 2,
@@ -46,7 +46,7 @@ void init_notes_merge_options(struct notes_merge_options *o);
  *    are stored in 'local_tree', and the SHA1 or the resulting commit
  *    (to be amended when the conflicts have been resolved) is written into
  *    'result_sha1'. The unmerged entries are written into the
- *    .git/NOTES_MERGE_WORKTREE directory with conflict markers.
+ *    .git/notes_merge_worktree directory with conflict markers.
  *    -1 is returned.
  *
  * Both o->local_ref and o->remote_ref must be given (non-NULL), but either ref
@@ -65,7 +65,7 @@ int notes_merge(struct notes_merge_options *o,
  * the given 'partial_commit', the partial result commit created by a previous
  * call to notes_merge().
  *
- * This function will add the (now resolved) notes in .git/NOTES_MERGE_WORKTREE
+ * This function will add the (now resolved) notes in .git/notes_merge_worktree
  * to 'partial_tree', and create a final notes merge commit, the SHA1 of which
  * will be stored in 'result_sha1'.
  */
@@ -77,7 +77,7 @@ int notes_merge_commit(struct notes_merge_options *o,
 /*
  * Abort conflict resolution from an earlier notes_merge()
  *
- * Removes the notes merge worktree in .git/NOTES_MERGE_WORKTREE.
+ * Removes the notes merge worktree in .git/notes_merge_worktree.
  */
 int notes_merge_abort(struct notes_merge_options *o);
 
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index 195bb97..dc9ecd5 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -178,12 +178,12 @@ test_expect_success 'merge z into m (== y) with default ("manual") resolver => C
 	git config core.notesRef refs/notes/m &&
 	test_must_fail git notes merge z >output &&
 	# Output should point to where to resolve conflicts
-	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	grep -q "\\.git/notes_merge_worktree" output &&
 	# Inspect merge conflicts
-	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	ls .git/notes_merge_worktree >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
 	( for f in $(cat expect_conflicts); do
-		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		test_cmp "expect_conflict_$f" ".git/notes_merge_worktree/$f" ||
 		exit 1
 	done ) &&
 	# Verify that current notes tree (pre-merge) has not changed (m == y)
@@ -222,10 +222,10 @@ test_expect_success 'change notes in z' '
 '
 
 test_expect_success 'cannot do merge w/conflicts when previous merge is unfinished' '
-	test -d .git/NOTES_MERGE_WORKTREE &&
+	test -d .git/notes_merge_worktree &&
 	test_must_fail git notes merge z >output 2>&1 &&
 	# Output should indicate what is wrong
-	grep -q "\\.git/NOTES_MERGE_\\* exists" output
+	grep -q "\\.git/notes_merge_\\* exists" output
 '
 
 # Setup non-conflicting merge between x and new notes ref w
@@ -282,7 +282,7 @@ w notes on 1st commit
 EOF
 
 test_expect_success 'can do merge without conflicts even if previous merge is unfinished (x => w)' '
-	test -d .git/NOTES_MERGE_WORKTREE &&
+	test -d .git/notes_merge_worktree &&
 	git notes merge x &&
 	verify_notes w &&
 	# Verify that other notes refs has not changed (x and y)
@@ -316,15 +316,15 @@ EOF
 
 test_expect_success 'finalize conflicting merge (z => m)' '
 	# Resolve conflicts and finalize merge
-	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+	cat >.git/notes_merge_worktree/$commit_sha1 <<EOF &&
 y and z notes on 1st commit
 EOF
-	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+	cat >.git/notes_merge_worktree/$commit_sha4 <<EOF &&
 y and z notes on 4th commit
 EOF
 	git notes merge --commit &&
-	# No .git/NOTES_MERGE_* files left
-	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	# No .git/notes_merge_* files left
+	test_might_fail ls .git/notes_merge_* >output 2>/dev/null &&
 	test_cmp /dev/null output &&
 	# Merge commit has pre-merge y and pre-merge z as parents
 	test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
@@ -369,12 +369,12 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
 	git config core.notesRef refs/notes/m &&
 	test_must_fail git notes merge z >output &&
 	# Output should point to where to resolve conflicts
-	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	grep -q "\\.git/notes_merge_worktree" output &&
 	# Inspect merge conflicts
-	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	ls .git/notes_merge_worktree >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
 	( for f in $(cat expect_conflicts); do
-		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		test_cmp "expect_conflict_$f" ".git/notes_merge_worktree/$f" ||
 		exit 1
 	done ) &&
 	# Verify that current notes tree (pre-merge) has not changed (m == y)
@@ -385,8 +385,8 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
 
 test_expect_success 'abort notes merge' '
 	git notes merge --abort &&
-	# No .git/NOTES_MERGE_* files left
-	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	# No .git/notes_merge_* files left
+	test_might_fail ls .git/notes_merge_* >output 2>/dev/null &&
 	test_cmp /dev/null output &&
 	# m has not moved (still == y)
 	test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)" &&
@@ -403,12 +403,12 @@ git rev-parse refs/notes/z > pre_merge_z
 test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
 	test_must_fail git notes merge z >output &&
 	# Output should point to where to resolve conflicts
-	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	grep -q "\\.git/notes_merge_worktree" output &&
 	# Inspect merge conflicts
-	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	ls .git/notes_merge_worktree >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
 	( for f in $(cat expect_conflicts); do
-		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		test_cmp "expect_conflict_$f" ".git/notes_merge_worktree/$f" ||
 		exit 1
 	done ) &&
 	# Verify that current notes tree (pre-merge) has not changed (m == y)
@@ -441,19 +441,19 @@ EOF
 
 test_expect_success 'add + remove notes in finalized merge (z => m)' '
 	# Resolve one conflict
-	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+	cat >.git/notes_merge_worktree/$commit_sha1 <<EOF &&
 y and z notes on 1st commit
 EOF
 	# Remove another conflict
-	rm .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+	rm .git/notes_merge_worktree/$commit_sha4 &&
 	# Remove a D/F conflict
-	rm .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
+	rm .git/notes_merge_worktree/$commit_sha3 &&
 	# Add a new note
-	echo "new note on 5th commit" > .git/NOTES_MERGE_WORKTREE/$commit_sha5 &&
+	echo "new note on 5th commit" > .git/notes_merge_worktree/$commit_sha5 &&
 	# Finalize merge
 	git notes merge --commit &&
-	# No .git/NOTES_MERGE_* files left
-	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	# No .git/notes_merge_* files left
+	test_might_fail ls .git/notes_merge_* >output 2>/dev/null &&
 	test_cmp /dev/null output &&
 	# Merge commit has pre-merge y and pre-merge z as parents
 	test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
@@ -484,12 +484,12 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
 	git update-ref refs/notes/m refs/notes/y &&
 	test_must_fail git notes merge z >output &&
 	# Output should point to where to resolve conflicts
-	grep -q "\\.git/NOTES_MERGE_WORKTREE" output &&
+	grep -q "\\.git/notes_merge_worktree" output &&
 	# Inspect merge conflicts
-	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	ls .git/notes_merge_worktree >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
 	( for f in $(cat expect_conflicts); do
-		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		test_cmp "expect_conflict_$f" ".git/notes_merge_worktree/$f" ||
 		exit 1
 	done ) &&
 	# Verify that current notes tree (pre-merge) has not changed (m == y)
@@ -507,31 +507,31 @@ test_expect_success 'reset notes ref m to somewhere else (w)' '
 	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
 '
 
-test_expect_success 'fail to finalize conflicting merge if underlying ref has moved in the meantime (m != NOTES_MERGE_PARTIAL^1)' '
+test_expect_success 'fail to finalize conflicting merge if underlying ref has moved in the meantime (m != notes_merge_partial^1)' '
 	# Resolve conflicts
-	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha1 <<EOF &&
+	cat >.git/notes_merge_worktree/$commit_sha1 <<EOF &&
 y and z notes on 1st commit
 EOF
-	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha4 <<EOF &&
+	cat >.git/notes_merge_worktree/$commit_sha4 <<EOF &&
 y and z notes on 4th commit
 EOF
 	# Fail to finalize merge
 	test_must_fail git notes merge --commit >output 2>&1 &&
-	# .git/NOTES_MERGE_* must remain
-	test -f .git/NOTES_MERGE_PARTIAL &&
-	test -f .git/NOTES_MERGE_REF &&
-	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
-	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
-	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
-	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
+	# .git/notes_merge_* must remain
+	git rev-parse --verify notes_merge_partial &&
+	git rev-parse --verify notes_merge_ref &&
+	test -f .git/notes_merge_worktree/$commit_sha1 &&
+	test -f .git/notes_merge_worktree/$commit_sha2 &&
+	test -f .git/notes_merge_worktree/$commit_sha3 &&
+	test -f .git/notes_merge_worktree/$commit_sha4 &&
 	# Refs are unchanged
 	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
-	test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
-	test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
+	test "$(git rev-parse refs/notes/y)" = "$(git rev-parse notes_merge_partial^1)" &&
+	test "$(git rev-parse refs/notes/m)" != "$(git rev-parse notes_merge_partial^1)" &&
 	# Mention refs/notes/m, and its current and expected value in output
 	grep -q "refs/notes/m" output &&
 	grep -q "$(git rev-parse refs/notes/m)" output &&
-	grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
+	grep -q "$(git rev-parse notes_merge_partial^1)" output &&
 	# Verify that other notes refs has not changed (w, x, y and z)
 	verify_notes w &&
 	verify_notes x &&
@@ -541,8 +541,8 @@ EOF
 
 test_expect_success 'resolve situation by aborting the notes merge' '
 	git notes merge --abort &&
-	# No .git/NOTES_MERGE_* files left
-	test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+	# No .git/notes_merge_* files left
+	test_might_fail ls .git/notes_merge_* >output 2>/dev/null &&
 	test_cmp /dev/null output &&
 	# m has not moved (still == w)
 	test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
@@ -563,7 +563,7 @@ test_expect_success 'switch cwd before committing notes merge' '
 	git notes --ref=other add -m bar HEAD &&
 	test_must_fail git notes merge refs/notes/other &&
 	(
-		cd .git/NOTES_MERGE_WORKTREE &&
+		cd .git/notes_merge_worktree &&
 		echo "foo" > $(git rev-parse HEAD) &&
 		echo "bar" >> $(git rev-parse HEAD) &&
 		git notes merge --commit
diff --git a/t/t3311-notes-merge-fanout.sh b/t/t3311-notes-merge-fanout.sh
index 93516ef..eeaea62 100755
--- a/t/t3311-notes-merge-fanout.sh
+++ b/t/t3311-notes-merge-fanout.sh
@@ -387,10 +387,10 @@ other notes for commit4
 EOF
 
 test_expect_success 'verify conflict entries (with no fanout)' '
-	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
+	ls .git/notes_merge_worktree >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
 	( for f in $(cat expect_conflicts); do
-		test_cmp "expect_conflict_$f" ".git/NOTES_MERGE_WORKTREE/$f" ||
+		test_cmp "expect_conflict_$f" ".git/notes_merge_worktree/$f" ||
 		exit 1
 	done ) &&
 	# Verify that current notes tree (pre-merge) has not changed (m == w)
@@ -417,7 +417,7 @@ other notes for commit1
 EOF
 
 test_expect_success 'resolve and finalize merge (z => w)' '
-	cat >.git/NOTES_MERGE_WORKTREE/$commit_sha3 <<EOF &&
+	cat >.git/notes_merge_worktree/$commit_sha3 <<EOF &&
 other notes for commit3
 
 appended notes for commit3
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/9] bisect: do not use update-ref for BISECT_HEAD
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
  2015-07-24  4:45 ` [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts David Turner
  2015-07-24  4:45 ` [PATCH 2/9] notes: replace pseudorefs with real refs David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24  4:45 ` [PATCH 4/9] tests: treat FETCH_HEAD as a pseudoref David Turner
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

Because BISECT_HEAD is a pseudoref, we shouldn't use update-ref to
update it.  Instead, we simply write to and delete the file.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 git-bisect.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/git-bisect.sh b/git-bisect.sh
index ea63223..8263555 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -181,7 +181,7 @@ bisect_start() {
 	#
 	echo "$start_head" >"$GIT_DIR/BISECT_START" && {
 		test "z$mode" != "z--no-checkout" ||
-		git update-ref --no-deref BISECT_HEAD "$start_head"
+		git rev-parse "$start_head" > "$GIT_DIR/BISECT_HEAD"
 	} &&
 	git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
 	eval "$eval true" &&
@@ -425,7 +425,7 @@ bisect_clean_state() {
 	rm -f "$GIT_DIR/BISECT_TERMS" &&
 	# Cleanup head-name if it got left by an old version of git-bisect
 	rm -f "$GIT_DIR/head-name" &&
-	git update-ref -d --no-deref BISECT_HEAD &&
+	rm -f "$GIT_DIR/BISECT_HEAD" &&
 	# clean up BISECT_START last
 	rm -f "$GIT_DIR/BISECT_START"
 }
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 4/9] tests: treat FETCH_HEAD as a pseudoref
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
                   ` (2 preceding siblings ...)
  2015-07-24  4:45 ` [PATCH 3/9] bisect: do not use update-ref for BISECT_HEAD David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24  4:45 ` [PATCH 5/9] tests: use --create-reflog option to git update-ref David Turner
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

Signed-off-by: David Turner <dturner@twopensource.com>
---
 t/t5510-fetch.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 0ba9db0..467cade 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -362,7 +362,7 @@ test_expect_success 'fetch with a non-applying branch.<name>.merge' '
 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
 	one_head=$(cd one && git rev-parse HEAD) &&
 	this_head=$(git rev-parse HEAD) &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
@@ -374,7 +374,7 @@ test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge
 	one_ref=$(cd one && git symbolic-ref HEAD) &&
 	git config branch.master.remote blub &&
 	git config branch.master.merge "$one_ref" &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
@@ -384,7 +384,7 @@ test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge
 # the merge spec does not match the branch the remote HEAD points to
 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
 	git config branch.master.merge "${one_ref}_not" &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 5/9] tests: use --create-reflog option to git update-ref
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
                   ` (3 preceding siblings ...)
  2015-07-24  4:45 ` [PATCH 4/9] tests: treat FETCH_HEAD as a pseudoref David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24  4:45 ` [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions David Turner
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

Instead of manually precreating reflogs by writing files, use
update-ref --create-reflog to create the reflogs at the same time
we first create the ref.

In one case, we need an entirely empty reflog, so we do a no-op
update-ref with --create-reflog and then expire the reflog.

This makes it slighly easier to test alternate ref backends.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 t/t1400-update-ref.sh       | 5 ++---
 t/t1411-reflog-show.sh      | 3 ++-
 t/t1503-rev-parse-verify.sh | 9 +++------
 3 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 9d21c19..0038f28 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -174,12 +174,11 @@ test_expect_success "(not) changed .git/$m" "
 '
 rm -f .git/$m
 
-: a repository with working tree always has reflog these days...
-: >.git/logs/refs/heads/master
+rm .git/logs/refs/heads/master
 test_expect_success \
 	"create $m (logged by touch)" \
 	'GIT_COMMITTER_DATE="2005-05-26 23:30" \
-	 git update-ref HEAD '"$A"' -m "Initial Creation" &&
+	 git update-ref --create-reflog HEAD '"$A"' -m "Initial Creation" &&
 	 test '"$A"' = $(cat .git/'"$m"')'
 test_expect_success \
 	"update $m (logged by touch)" \
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 3eb4f10..ee069ac 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -138,7 +138,8 @@ test_expect_success '--date magic does not override explicit @{0} syntax' '
 : >expect
 test_expect_success 'empty reflog file' '
 	git branch empty &&
-	: >.git/logs/refs/heads/empty &&
+	git update-ref --create-reflog refs/heads/empty empty &&
+	git reflog expire --expire=all refs/heads/empty &&
 
 	git log -g empty >actual &&
 	test_cmp expect actual
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index 823fe1d..ab27d0d 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -85,8 +85,7 @@ test_expect_success 'fails silently when using -q' '
 
 test_expect_success 'fails silently when using -q with deleted reflogs' '
 	ref=$(git rev-parse HEAD) &&
-	: >.git/logs/refs/test &&
-	git update-ref -m "message for refs/test" refs/test "$ref" &&
+	git update-ref --create-reflog -m "message for refs/test" refs/test "$ref" &&
 	git reflog delete --updateref --rewrite refs/test@{0} &&
 	test_must_fail git rev-parse -q --verify refs/test@{0} >error 2>&1 &&
 	test_must_be_empty error
@@ -94,16 +93,14 @@ test_expect_success 'fails silently when using -q with deleted reflogs' '
 
 test_expect_success 'fails silently when using -q with not enough reflogs' '
 	ref=$(git rev-parse HEAD) &&
-	: >.git/logs/refs/test2 &&
-	git update-ref -m "message for refs/test2" refs/test2 "$ref" &&
+	git update-ref --create-reflog -m "message for refs/test2" refs/test2 "$ref" &&
 	test_must_fail git rev-parse -q --verify refs/test2@{999} >error 2>&1 &&
 	test_must_be_empty error
 '
 
 test_expect_success 'succeeds silently with -q and reflogs that do not go far back enough in time' '
 	ref=$(git rev-parse HEAD) &&
-	: >.git/logs/refs/test3 &&
-	git update-ref -m "message for refs/test3" refs/test3 "$ref" &&
+	git update-ref --create-reflog -m "message for refs/test3" refs/test3 "$ref" &&
 	git rev-parse -q --verify refs/test3@{1.year.ago} >actual 2>error &&
 	test_must_be_empty error &&
 	echo "$ref" >expect &&
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
                   ` (4 preceding siblings ...)
  2015-07-24  4:45 ` [PATCH 5/9] tests: use --create-reflog option to git update-ref David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24 19:25   ` Junio C Hamano
  2015-07-24  4:45 ` [PATCH 7/9] am/rebase: update pseudorefs by writing files David Turner
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

Pseudorefs should not be updated through the ref API, because the ref
API is for real refs.  Instead, use a dedicated pseudoref API.

This patch changes writes to CHERRY_PICK_HEAD, REVERT_HEAD, and ORIG_HEAD.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 builtin/merge.c |  3 +--
 builtin/reset.c |  6 ++----
 refs.c          | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 refs.h          |  5 +++++
 sequencer.c     | 21 ++-------------------
 5 files changed, 64 insertions(+), 26 deletions(-)

diff --git a/builtin/merge.c b/builtin/merge.c
index 7e2541a..cea57a0 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1354,8 +1354,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		free(list);
 	}
 
-	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
-		   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+	write_pseudoref("ORIG_HEAD", head_commit->object.sha1, NULL);
 
 	if (remoteheads && !common)
 		; /* No common ancestors found. We need a real merge. */
diff --git a/builtin/reset.c b/builtin/reset.c
index 4c08ddc..0282e78 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -252,11 +252,9 @@ static int reset_refs(const char *rev, const unsigned char *sha1)
 		old_orig = sha1_old_orig;
 	if (!get_sha1("HEAD", sha1_orig)) {
 		orig = sha1_orig;
-		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
-		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
-			   UPDATE_REFS_MSG_ON_ERR);
+		write_pseudoref("ORIG_HEAD", orig, old_orig);
 	} else if (old_orig)
-		delete_ref("ORIG_HEAD", old_orig, 0);
+		delete_pseudoref("ORIG_HEAD", old_orig);
 	set_reflog_message(&msg, "updating HEAD", rev);
 	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0,
 				       UPDATE_REFS_MSG_ON_ERR);
diff --git a/refs.c b/refs.c
index d31ca42..efa84e3 100644
--- a/refs.c
+++ b/refs.c
@@ -3862,7 +3862,7 @@ int is_per_worktree_ref(const char *refname)
 	return !strcmp(refname, "HEAD");
 }
 
-static int is_pseudoref(const char *refname)
+int is_pseudoref(const char *refname)
 {
 	const char *c;
 
@@ -4544,3 +4544,56 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 	unlock_ref(lock);
 	return -1;
 }
+
+void write_pseudoref(const char *pseudoref, const unsigned char *sha1,
+		     const unsigned char *old_sha1)
+{
+	const char *filename;
+	int fd;
+	static struct lock_file lock;
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+
+	filename = git_path("%s", pseudoref);
+	fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+	if (fd < 0)
+		die_errno(_("Could not open '%s' for writing"), filename);
+
+	if (old_sha1) {
+		unsigned char actual_old_sha1[20];
+		read_ref(pseudoref, actual_old_sha1);
+		if (hashcmp(actual_old_sha1, old_sha1))
+			die("Unexpected sha1 when writing %s", pseudoref);
+	}
+	if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+		die_errno(_("Could not write to '%s'"), filename);
+	strbuf_release(&buf);
+	commit_lock_file(&lock);
+}
+
+void delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)
+{
+	static struct lock_file lock;
+	const char *filename;
+
+	filename = git_path("%s", pseudoref);
+
+	if (old_sha1) {
+		int fd;
+		unsigned char actual_old_sha1[20];
+
+		fd = hold_lock_file_for_update(&lock, filename,
+					       LOCK_DIE_ON_ERROR);
+		if (fd < 0)
+			die_errno(_("Could not open '%s' for writing"), filename);
+		read_ref(pseudoref, actual_old_sha1);
+		if (hashcmp(actual_old_sha1, old_sha1))
+			die("Unexpected sha1 when writing %s", pseudoref);
+
+		unlink(pseudoref);
+		rollback_lock_file(&lock);
+	} else {
+		unlink(pseudoref);
+	}
+}
diff --git a/refs.h b/refs.h
index bd5526e..450860b 100644
--- a/refs.h
+++ b/refs.h
@@ -441,6 +441,11 @@ int update_ref(const char *msg, const char *refname,
 	       const unsigned char *new_sha1, const unsigned char *old_sha1,
 	       unsigned int flags, enum action_on_err onerr);
 
+void write_pseudoref(const char *pseudoref, const unsigned char *sha1,
+		     const unsigned char *old_sha1);
+
+void delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1);
+
 extern int parse_hide_refs_config(const char *var, const char *value, const char *);
 
 extern int ref_is_hidden(const char *);
diff --git a/sequencer.c b/sequencer.c
index c4f4b7d..c9fc9a7 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -158,23 +158,6 @@ static void free_message(struct commit *commit, struct commit_message *msg)
 	unuse_commit_buffer(commit, msg->message);
 }
 
-static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
-{
-	const char *filename;
-	int fd;
-	struct strbuf buf = STRBUF_INIT;
-
-	strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
-
-	filename = git_path("%s", pseudoref);
-	fd = open(filename, O_WRONLY | O_CREAT, 0666);
-	if (fd < 0)
-		die_errno(_("Could not open '%s' for writing"), filename);
-	if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
-		die_errno(_("Could not write to '%s'"), filename);
-	strbuf_release(&buf);
-}
-
 static void print_advice(int show_hint, struct replay_opts *opts)
 {
 	char *msg = getenv("GIT_CHERRY_PICK_HELP");
@@ -607,9 +590,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 	 * write it at all.
 	 */
 	if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
-		write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+		write_pseudoref("CHERRY_PICK_HEAD", commit->object.sha1, NULL);
 	if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
-		write_cherry_pick_head(commit, "REVERT_HEAD");
+		write_pseudoref("REVERT_HEAD", commit->object.sha1, NULL);
 
 	if (res) {
 		error(opts->action == REPLAY_REVERT
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 7/9] am/rebase: update pseudorefs by writing files
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
                   ` (5 preceding siblings ...)
  2015-07-24  4:45 ` [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24  4:45 ` [PATCH 8/9] rebase: use write_pseudoref David Turner
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

git update-ref can no longer be used to update pseudorefs, so in
git-am and git-rebase, we just directly edit the files.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 builtin/am.c               | 6 +++---
 contrib/examples/git-am.sh | 4 ++--
 git-rebase--interactive.sh | 2 +-
 git-rebase.sh              | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 3f0fc75..ff3eeee 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1049,12 +1049,12 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
 	if (!get_sha1("HEAD", curr_head)) {
 		write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(curr_head));
 		if (!state->rebasing)
-			update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
-					UPDATE_REFS_DIE_ON_ERR);
+			write_pseudoref("ORIG_HEAD", curr_head, NULL);
 	} else {
 		write_file(am_path(state, "abort-safety"), 1, "%s", "");
 		if (!state->rebasing)
-			delete_ref("ORIG_HEAD", NULL, 0);
+			if (unlink(git_path("ORIG_HEAD")) && errno != ENOENT)
+				die("failed to unlink ORIG_HEAD");
 	}
 
 	/*
diff --git a/contrib/examples/git-am.sh b/contrib/examples/git-am.sh
index a3d0c84..a01a06e 100755
--- a/contrib/examples/git-am.sh
+++ b/contrib/examples/git-am.sh
@@ -614,9 +614,9 @@ Use \"git am --abort\" to remove it.")"
 		: >"$dotest/applying"
 		if test -n "$HAS_HEAD"
 		then
-			git update-ref ORIG_HEAD HEAD
+			git rev-parse HEAD >"$GIT_DIR/ORIG_HEAD"
 		else
-			git update-ref -d ORIG_HEAD >/dev/null 2>&1
+			rm -f "$GIT_DIR/ORIG_HEAD"
 		fi
 	fi
 fi
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index f01637b..ae116a6 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -948,7 +948,7 @@ warn_lines () {
 checkout_onto () {
 	GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
 	output git checkout $onto || die_abort "could not detach HEAD"
-	git update-ref ORIG_HEAD $orig_head
+	echo $orig_head >"$GIT_DIR/ORIG_HEAD"
 }
 
 get_missing_commit_check_level () {
diff --git a/git-rebase.sh b/git-rebase.sh
index 1757404..23e2e64 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -611,7 +611,7 @@ say "$(gettext "First, rewinding head to replay your work on top of it...")"
 
 GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
 	git checkout -q "$onto^0" || die "could not detach HEAD"
-git update-ref ORIG_HEAD $orig_head
+echo $orig_head >"$GIT_DIR/ORIG_HEAD"
 
 # If the $onto is a proper descendant of the tip of the branch, then
 # we just fast-forwarded.
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 8/9] rebase: use write_pseudoref
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
                   ` (6 preceding siblings ...)
  2015-07-24  4:45 ` [PATCH 7/9] am/rebase: update pseudorefs by writing files David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-07-24  4:45 ` [PATCH 9/9] refs: forbid pseudoref updates through ref update API David Turner
  2015-08-02 22:48 ` [PATCH/RFC 0/9] Pseudorefs Michael Haggerty
  9 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

Instead of manually writing a pseudoref (in one case) and using git
update-ref (in another), use the new write_pseudoref function.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 bisect.c | 35 +++++++----------------------------
 1 file changed, 7 insertions(+), 28 deletions(-)

diff --git a/bisect.c b/bisect.c
index 857cf59..3a9fd63 100644
--- a/bisect.c
+++ b/bisect.c
@@ -19,7 +19,6 @@ static struct object_id *current_bad_oid;
 
 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
-static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
 
 static const char *term_bad;
 static const char *term_good;
@@ -675,34 +674,16 @@ static int is_expected_rev(const struct object_id *oid)
 	return res;
 }
 
-static void mark_expected_rev(char *bisect_rev_hex)
-{
-	int len = strlen(bisect_rev_hex);
-	const char *filename = git_path("BISECT_EXPECTED_REV");
-	int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
-
-	if (fd < 0)
-		die_errno("could not create file '%s'", filename);
-
-	bisect_rev_hex[len] = '\n';
-	write_or_die(fd, bisect_rev_hex, len + 1);
-	bisect_rev_hex[len] = '\0';
-
-	if (close(fd) < 0)
-		die("closing file %s: %s", filename, strerror(errno));
-}
-
-static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
+static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
 {
+	char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
 
-	mark_expected_rev(bisect_rev_hex);
+	memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
+	write_pseudoref("BISECT_EXPECTED_REV", bisect_rev, NULL);
 
 	argv_checkout[2] = bisect_rev_hex;
 	if (no_checkout) {
-		argv_update_ref[3] = bisect_rev_hex;
-		if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
-			die("update-ref --no-deref HEAD failed on %s",
-			    bisect_rev_hex);
+		write_pseudoref("BISECT_HEAD", bisect_rev, NULL);
 	} else {
 		int res;
 		res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
@@ -804,7 +785,7 @@ static void check_merge_bases(int no_checkout)
 			handle_skipped_merge_base(mb);
 		} else {
 			printf("Bisecting: a merge base must be tested\n");
-			exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
+			exit(bisect_checkout(mb, no_checkout));
 		}
 	}
 
@@ -948,7 +929,6 @@ int bisect_next_all(const char *prefix, int no_checkout)
 	struct commit_list *tried;
 	int reaches = 0, all = 0, nr, steps;
 	const unsigned char *bisect_rev;
-	char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
 
 	read_bisect_terms(&term_bad, &term_good);
 	if (read_bisect_refs())
@@ -986,7 +966,6 @@ int bisect_next_all(const char *prefix, int no_checkout)
 	}
 
 	bisect_rev = revs.commits->item->object.sha1;
-	memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
 
 	if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
 		exit_if_skipped_commits(tried, current_bad_oid);
@@ -1003,7 +982,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
 	       "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
 	       steps, (steps == 1 ? "" : "s"));
 
-	return bisect_checkout(bisect_rev_hex, no_checkout);
+	return bisect_checkout(bisect_rev, no_checkout);
 }
 
 static inline int log2i(int n)
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 9/9] refs: forbid pseudoref updates through ref update API
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
                   ` (7 preceding siblings ...)
  2015-07-24  4:45 ` [PATCH 8/9] rebase: use write_pseudoref David Turner
@ 2015-07-24  4:45 ` David Turner
  2015-08-02 22:48 ` [PATCH/RFC 0/9] Pseudorefs Michael Haggerty
  9 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24  4:45 UTC (permalink / raw)
  To: git, mhagger; +Cc: David Turner

It is now forbidden to update pseudorefs through the ref update
framework.

Because of the pseudoref update rules change, test cases that used git
update-ref on a pseudoref now operate on plain refs.

Signed-off-by: David Turner <dturner@twopensource.com>
---
 bisect.c              |  2 +-
 refs.c                |  2 ++
 t/t1400-update-ref.sh | 24 ++++++++++++------------
 3 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/bisect.c b/bisect.c
index 3a9fd63..47cc778 100644
--- a/bisect.c
+++ b/bisect.c
@@ -969,7 +969,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
 
 	if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
 		exit_if_skipped_commits(tried, current_bad_oid);
-		printf("%s is the first %s commit\n", bisect_rev_hex,
+		printf("%s is the first %s commit\n", sha1_to_hex(bisect_rev),
 			term_bad);
 		show_diff_tree(prefix, revs.commits->item);
 		/* This means the bisection process succeeded. */
diff --git a/refs.c b/refs.c
index efa84e3..f8dc877 100644
--- a/refs.c
+++ b/refs.c
@@ -3902,6 +3902,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	struct ref_update *update;
 
 	assert(err);
+	if (is_pseudoref(refname))
+		return error(_("BUG: Cannot update pseudorefs (all-caps files in .git) through the refs API"));
 
 	if (transaction->state != REF_TRANSACTION_OPEN)
 		die("BUG: update called for transaction that is not open");
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 0038f28..b05657c 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -608,13 +608,13 @@ test_expect_success 'stdin delete ref fails with zero old value' '
 '
 
 test_expect_success 'stdin update symref works option no-deref' '
-	git symbolic-ref TESTSYMREF $b &&
+	git symbolic-ref refs/TESTSYMREF $b &&
 	cat >stdin <<-EOF &&
 	option no-deref
-	update TESTSYMREF $a $b
+	update refs/TESTSYMREF $a $b
 	EOF
 	git update-ref --stdin <stdin &&
-	git rev-parse TESTSYMREF >expect &&
+	git rev-parse refs/TESTSYMREF >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual &&
 	git rev-parse $m~1 >expect &&
@@ -623,13 +623,13 @@ test_expect_success 'stdin update symref works option no-deref' '
 '
 
 test_expect_success 'stdin delete symref works option no-deref' '
-	git symbolic-ref TESTSYMREF $b &&
+	git symbolic-ref refs/TESTSYMREF $b &&
 	cat >stdin <<-EOF &&
 	option no-deref
-	delete TESTSYMREF $b
+	delete refs/TESTSYMREF $b
 	EOF
 	git update-ref --stdin <stdin &&
-	test_must_fail git rev-parse --verify -q TESTSYMREF &&
+	test_must_fail git rev-parse --verify -q refs/TESTSYMREF &&
 	git rev-parse $m~1 >expect &&
 	git rev-parse $b >actual &&
 	test_cmp expect actual
@@ -983,10 +983,10 @@ test_expect_success 'stdin -z delete ref fails with zero old value' '
 '
 
 test_expect_success 'stdin -z update symref works option no-deref' '
-	git symbolic-ref TESTSYMREF $b &&
-	printf $F "option no-deref" "update TESTSYMREF" "$a" "$b" >stdin &&
+	git symbolic-ref refs/TESTSYMREF $b &&
+	printf $F "option no-deref" "update refs/TESTSYMREF" "$a" "$b" >stdin &&
 	git update-ref -z --stdin <stdin &&
-	git rev-parse TESTSYMREF >expect &&
+	git rev-parse refs/TESTSYMREF >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual &&
 	git rev-parse $m~1 >expect &&
@@ -995,10 +995,10 @@ test_expect_success 'stdin -z update symref works option no-deref' '
 '
 
 test_expect_success 'stdin -z delete symref works option no-deref' '
-	git symbolic-ref TESTSYMREF $b &&
-	printf $F "option no-deref" "delete TESTSYMREF" "$b" >stdin &&
+	git symbolic-ref refs/TESTSYMREF $b &&
+	printf $F "option no-deref" "delete refs/TESTSYMREF" "$b" >stdin &&
 	git update-ref -z --stdin <stdin &&
-	test_must_fail git rev-parse --verify -q TESTSYMREF &&
+	test_must_fail git rev-parse --verify -q refs/TESTSYMREF &&
 	git rev-parse $m~1 >expect &&
 	git rev-parse $b >actual &&
 	test_cmp expect actual
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts
  2015-07-24  4:45 ` [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts David Turner
@ 2015-07-24  7:34   ` Eric Sunshine
  2015-07-24 19:20   ` Junio C Hamano
  2015-07-24 19:52   ` Philip Oakley
  2 siblings, 0 replies; 20+ messages in thread
From: Eric Sunshine @ 2015-07-24  7:34 UTC (permalink / raw)
  To: David Turner; +Cc: Git List, Michael Haggerty

On Fri, Jul 24, 2015 at 12:45 AM, David Turner <dturner@twopensource.com> wrote:
> Add glossary entries for both concepts.
>
> Pseudorefs and per-worktree refs do not yet have special handling,
> because the files refs backend already handles them correctly.  Later,
> we will make the LMDB backend call out to the files backend to handle
> per-worktree refs.
>
> Signed-off-by: David Turner <dturner@twopensource.com>
> ---
> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
> index ab18f4b..d04819e 100644
> --- a/Documentation/glossary-content.txt
> +++ b/Documentation/glossary-content.txt
> @@ -411,6 +411,23 @@ exclude;;
>         core Git. Porcelains expose more of a <<def_SCM,SCM>>
>         interface than the <<def_plumbing,plumbing>>.
>
> +[[def_per_worktree_ref]]per-worktree ref::
> +       Refs that are per-<<def_worktree,worktree>>, rather than
> +       global.  This is presently only <<def_HEAD,HEAD>>, but might
> +       later include other unsuual refs.
> +
> +[[def_pseudoref]]pseudoref::
> +       Files under `$GIT_DIR` whose names are all-caps, and that
> +       contain a line consisting of a <<def_sha1,SHA-1>> followed by
> +       a newline, and optionally some additional data.  `MERGE_HEAD`
> +       and `CHERRY_PICK_HEAD` are examples.  Unlike
> +       <<def_per_worktree_ref,per-worktree refs>>, these files cannot
> +       be symbolic refs, and never not have reflogs.  They also

s/never not/never/

> +       cannot be updated through the normal ref update machinery.
> +       Instead, they are updated by directly writing to the files.
> +       However, they can be read as if they were refs, so `git
> +       rev-parse MERGE_HEAD` will work.
> +
>  [[def_pull]]pull::
>         Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
>         <<def_merge,merge>> it.  See also linkgit:git-pull[1].

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/9] refs:  Introduce pseudoref and per-worktree ref concepts
  2015-07-24  4:45 ` [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts David Turner
  2015-07-24  7:34   ` Eric Sunshine
@ 2015-07-24 19:20   ` Junio C Hamano
  2015-07-24 20:13     ` David Turner
  2015-07-24 19:52   ` Philip Oakley
  2 siblings, 1 reply; 20+ messages in thread
From: Junio C Hamano @ 2015-07-24 19:20 UTC (permalink / raw)
  To: David Turner; +Cc: git, mhagger

David Turner <dturner@twopensource.com> writes:

> +[[def_pseudoref]]pseudoref::
> +	Files under `$GIT_DIR` whose names are all-caps, and that
> +	contain a line consisting of a <<def_sha1,SHA-1>> followed by
> +	a newline, and optionally some additional data.  `MERGE_HEAD`
> +	and `CHERRY_PICK_HEAD` are examples.  Unlike...

I wonder if you meant to include FETCH_HEAD in this category (I am
not complaining about not having it listed as an example).  If you
did mean to include FETCH_HEAD, then "followed-by a newline" must
be rethought.

Documentation pedant might say that the above definition would throw
HEAD into this category.  s/all-caps,/all-caps (except "HEAD"),/ or
something like that may be needed to prevent them from making useless
noise.

> diff --git a/refs.c b/refs.c
> index 0b96ece..d31ca42 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -3857,6 +3857,29 @@ void ref_transaction_free(struct ref_transaction *transaction)
>  	free(transaction);
>  }
>  
> +int is_per_worktree_ref(const char *refname)
> +{
> +	return !strcmp(refname, "HEAD");
> +}
> +
> +static int is_pseudoref(const char *refname)
> +{
> +	const char *c;
> +
> +	if (strchr(refname, '/'))
> +		return 0;
> +
> +	if (is_per_worktree_ref(refname))
> +		return 0;
> +
> +	for (c = refname; *c; ++c) {
> +		if (!isupper(*c) && *c != '-' && *c != '_')
> +			return 0;
> +	}
> +
> +	return 1;
> +}
> +
>  static struct ref_update *add_update(struct ref_transaction *transaction,
>  				     const char *refname)
>  {
> diff --git a/refs.h b/refs.h
> index e4e46c3..bd5526e 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -445,6 +445,8 @@ extern int parse_hide_refs_config(const char *var, const char *value, const char
>  
>  extern int ref_is_hidden(const char *);
>  
> +int is_per_worktree_ref(const char *refname);
> +
>  enum expire_reflog_flags {
>  	EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
>  	EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions
  2015-07-24  4:45 ` [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions David Turner
@ 2015-07-24 19:25   ` Junio C Hamano
  2015-07-24 20:17     ` David Turner
  0 siblings, 1 reply; 20+ messages in thread
From: Junio C Hamano @ 2015-07-24 19:25 UTC (permalink / raw)
  To: David Turner; +Cc: git, mhagger

David Turner <dturner@twopensource.com> writes:

> Pseudorefs should not be updated through the ref API, because the ref
> API is for real refs.  Instead, use a dedicated pseudoref API.
>
> This patch changes writes to CHERRY_PICK_HEAD, REVERT_HEAD, and ORIG_HEAD.

This feels somewhat backwards, and it makes me wonder if it is a
better approach to teach update_ref() about the naming rules, so
that the callers do not have to say the same thing twice "This is
not a ref and I am giving all-caps name, by the way I am also not
calling update_ref() because that is only for real refs".

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/9] refs:  Introduce pseudoref and per-worktree ref concepts
  2015-07-24  4:45 ` [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts David Turner
  2015-07-24  7:34   ` Eric Sunshine
  2015-07-24 19:20   ` Junio C Hamano
@ 2015-07-24 19:52   ` Philip Oakley
  2015-07-24 20:14     ` David Turner
  2 siblings, 1 reply; 20+ messages in thread
From: Philip Oakley @ 2015-07-24 19:52 UTC (permalink / raw)
  To: David Turner, git, mhagger; +Cc: David Turner

From: "David Turner" <dturner@twopensource.com>
> Add glossary entries for both concepts.
>
> Pseudorefs and per-worktree refs do not yet have special handling,
> because the files refs backend already handles them correctly.  Later,
> we will make the LMDB backend call out to the files backend to handle
> per-worktree refs.
>
> Signed-off-by: David Turner <dturner@twopensource.com>
> ---
> Documentation/glossary-content.txt | 17 +++++++++++++++++
> refs.c                             | 23 +++++++++++++++++++++++
> refs.h                             |  2 ++
> 3 files changed, 42 insertions(+)
>
> diff --git a/Documentation/glossary-content.txt 
> b/Documentation/glossary-content.txt
> index ab18f4b..d04819e 100644
> --- a/Documentation/glossary-content.txt
> +++ b/Documentation/glossary-content.txt
> @@ -411,6 +411,23 @@ exclude;;
>  core Git. Porcelains expose more of a <<def_SCM,SCM>>
>  interface than the <<def_plumbing,plumbing>>.
>
> +[[def_per_worktree_ref]]per-worktree ref::
> + Refs that are per-<<def_worktree,worktree>>, rather than
> + global.  This is presently only <<def_HEAD,HEAD>>, but might
> + later include other unsuual refs.
s/unsuual/unusual/

> +
> +[[def_pseudoref]]pseudoref::
> + Files under `$GIT_DIR` whose names are all-caps, and that

s/and that/excluding 'HEAD', which is special./ ?

> + contain a line consisting of a <<def_sha1,SHA-1>> followed by

s/contain/Pseudorefs typically contain/ perhaps?

> + a newline, and optionally some additional data.  `MERGE_HEAD`
> + and `CHERRY_PICK_HEAD` are examples.  Unlike
> + <<def_per_worktree_ref,per-worktree refs>>, these files cannot
> + be symbolic refs, and never not have reflogs.  They also
> + cannot be updated through the normal ref update machinery.
> + Instead, they are updated by directly writing to the files.
> + However, they can be read as if they were refs, so `git
> + rev-parse MERGE_HEAD` will work.
> +
> [[def_pull]]pull::
>  Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
>  <<def_merge,merge>> it.  See also linkgit:git-pull[1].
[...]

The key points I'd picked out were:

*    ALL_CAPS names

*    acts like a ref <<>>, but separate from refs

*    HEAD is special and not a pseudoref (i.e. see <<HEAD>>).

*    CHERRY_PICK_HEAD and REVERT_HEAD are examples.

*    three way split: refs/; pseudorefs; HEAD.

*    one-level ALL_CAPS names are per worktree

*    is a file in $GIT_DIR/ that always contains at least a SHA1.

--
Philip 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/9] refs:  Introduce pseudoref and per-worktree ref concepts
  2015-07-24 19:20   ` Junio C Hamano
@ 2015-07-24 20:13     ` David Turner
  2015-07-24 20:44       ` Junio C Hamano
  0 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-07-24 20:13 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, mhagger

On Fri, 2015-07-24 at 12:20 -0700, Junio C Hamano wrote:
> David Turner <dturner@twopensource.com> writes:
> 
> > +[[def_pseudoref]]pseudoref::
> > +	Files under `$GIT_DIR` whose names are all-caps, and that
> > +	contain a line consisting of a <<def_sha1,SHA-1>> followed by
> > +	a newline, and optionally some additional data.  `MERGE_HEAD`
> > +	and `CHERRY_PICK_HEAD` are examples.  Unlike...
> 
> I wonder if you meant to include FETCH_HEAD in this category (I am
> not complaining about not having it listed as an example).  If you
> did mean to include FETCH_HEAD, then "followed-by a newline" must
> be rethought.

I guess "followed by whitespace"?

> Documentation pedant might say that the above definition would throw
> HEAD into this category.  s/all-caps,/all-caps (except "HEAD"),/ or
> something like that may be needed to prevent them from making useless
> noise.

I meant "and that contain a line consisting of a SHA" to exclude HEAD,
but I need to add a "always" in there, since detached HEAD would
otherwise apply.  That is, it is an additional condition on what a
pseudoref is.

Something like the following?

	Pseudorefs are a class of files under `$GIT_DIR` which behave
	like refs for the purposes of rev-parse, but which are treated
	specially by git.  Psuedorefs both have names are all-caps,
	and always start with a line consisting of a
	<<def_sha1,SHA-1>> followed by whitespace.  So, HEAD is not a
	pseudoref, because it is sometimes a symbolic ref.  They might
	optionally some additional data.  `MERGE_HEAD` and
	`CHERRY_PICK_HEAD` are examples.  Unlike
	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
	be symbolic refs, and never have reflogs.  They also cannot be
	updated through the normal ref update machinery.  Instead,
	they are updated by directly writing to the files.  However,
	they can be read as if they were refs, so `git rev-parse
	MERGE_HEAD` will work.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/9] refs:  Introduce pseudoref and per-worktree ref concepts
  2015-07-24 19:52   ` Philip Oakley
@ 2015-07-24 20:14     ` David Turner
  0 siblings, 0 replies; 20+ messages in thread
From: David Turner @ 2015-07-24 20:14 UTC (permalink / raw)
  To: Philip Oakley; +Cc: git, mhagger

On Fri, 2015-07-24 at 20:52 +0100, Philip Oakley wrote:
> From: "David Turner" <dturner@twopensource.com>
> > Add glossary entries for both concepts.
> >
> > Pseudorefs and per-worktree refs do not yet have special handling,
> > because the files refs backend already handles them correctly.  Later,
> > we will make the LMDB backend call out to the files backend to handle
> > per-worktree refs.
> >
> > Signed-off-by: David Turner <dturner@twopensource.com>
> > ---
> > Documentation/glossary-content.txt | 17 +++++++++++++++++
> > refs.c                             | 23 +++++++++++++++++++++++
> > refs.h                             |  2 ++
> > 3 files changed, 42 insertions(+)
> >
> > diff --git a/Documentation/glossary-content.txt 
> > b/Documentation/glossary-content.txt
> > index ab18f4b..d04819e 100644
> > --- a/Documentation/glossary-content.txt
> > +++ b/Documentation/glossary-content.txt
> > @@ -411,6 +411,23 @@ exclude;;
> >  core Git. Porcelains expose more of a <<def_SCM,SCM>>
> >  interface than the <<def_plumbing,plumbing>>.
> >
> > +[[def_per_worktree_ref]]per-worktree ref::
> > + Refs that are per-<<def_worktree,worktree>>, rather than
> > + global.  This is presently only <<def_HEAD,HEAD>>, but might
> > + later include other unsuual refs.
> s/unsuual/unusual/

Thanks.

> > +
> > +[[def_pseudoref]]pseudoref::
> > + Files under `$GIT_DIR` whose names are all-caps, and that
> 
> s/and that/excluding 'HEAD', which is special./ ?
> 
> > + contain a line consisting of a <<def_sha1,SHA-1>> followed by
> 
> s/contain/Pseudorefs typically contain/ perhaps?

See my reply to Junio on these; that "contain a line" thing was intended
to be an additional condition on what constitutes a pseudoref.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions
  2015-07-24 19:25   ` Junio C Hamano
@ 2015-07-24 20:17     ` David Turner
  2015-07-24 20:53       ` Junio C Hamano
  0 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-07-24 20:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, mhagger

On Fri, 2015-07-24 at 12:25 -0700, Junio C Hamano wrote:
> David Turner <dturner@twopensource.com> writes:
> 
> > Pseudorefs should not be updated through the ref API, because the ref
> > API is for real refs.  Instead, use a dedicated pseudoref API.
> >
> > This patch changes writes to CHERRY_PICK_HEAD, REVERT_HEAD, and ORIG_HEAD.
> 
> This feels somewhat backwards, and it makes me wonder if it is a
> better approach to teach update_ref() about the naming rules, so
> that the callers do not have to say the same thing twice "This is
> not a ref and I am giving all-caps name, by the way I am also not
> calling update_ref() because that is only for real refs".

Do you mean teach update_ref to call write_pseudoref for pseudorefs and
call the normal codepath for regular refs?  I considered this, but I was
worried about ref transactions.  It would be odd if
ref_transaction_update worked differently than update_ref.  And I didn't
want to deal with making pseudoref updates transactional, because then
alternate backends would have to maintain multiple transactions, which
turned out to be a giant hassle.

But maybe it's OK if update_ref operates differently than
ref_transaction_update?  (and same for
delete_ref/ref_transaction_delete).  After all, people who call
update_ref don't really care about the transactional semantics.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/9] refs:  Introduce pseudoref and per-worktree ref concepts
  2015-07-24 20:13     ` David Turner
@ 2015-07-24 20:44       ` Junio C Hamano
  0 siblings, 0 replies; 20+ messages in thread
From: Junio C Hamano @ 2015-07-24 20:44 UTC (permalink / raw)
  To: David Turner; +Cc: git, mhagger

David Turner <dturner@twopensource.com> writes:

> I meant "and that contain a line consisting of a SHA" to exclude HEAD,
> but I need to add a "always" in there, since detached HEAD would
> otherwise apply.

I do not think "always" is as a good escape hatch as an explicit
exclusion of "HEAD".  The latter is more direct and to the point.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions
  2015-07-24 20:17     ` David Turner
@ 2015-07-24 20:53       ` Junio C Hamano
  0 siblings, 0 replies; 20+ messages in thread
From: Junio C Hamano @ 2015-07-24 20:53 UTC (permalink / raw)
  To: David Turner; +Cc: git, mhagger

David Turner <dturner@twopensource.com> writes:

> On Fri, 2015-07-24 at 12:25 -0700, Junio C Hamano wrote:
>> David Turner <dturner@twopensource.com> writes:
>> 
>> > Pseudorefs should not be updated through the ref API, because the ref
>> > API is for real refs.  Instead, use a dedicated pseudoref API.
>> >
>> > This patch changes writes to CHERRY_PICK_HEAD, REVERT_HEAD, and ORIG_HEAD.
>> 
>> This feels somewhat backwards, and it makes me wonder if it is a
>> better approach to teach update_ref() about the naming rules, so
>> that the callers do not have to say the same thing twice "This is
>> not a ref and I am giving all-caps name, by the way I am also not
>> calling update_ref() because that is only for real refs".
>
> Do you mean teach update_ref to call write_pseudoref for pseudorefs and
> call the normal codepath for regular refs?

While reviewing this series, I mostly am viewing them from the point
of view of a user of the ref API.

You may name that "always delegate to filesystem backend" helper
function "write_pseudoref()" or whatever name we (i.e. the ref API
implementors) find easy to remember and understand, and as long as
it is static within refs.c (or refs-be-file.c), a user of the ref
API does not have to know.  He does not have to care (ideally he
shouldn't have to be able to call it directly, but that is merely a
safety against bugs).

> I considered this, but I was worried about ref transactions.

Considering the nature of these FOO_HEADs, I think it is perfectly
fine to declare that handling of them _must_ happen within a single
transaction that does not involve regular refs, or that handling of
them _must_ happen outside transactions.  I do not think any of the
existing creation, update, or deletion of these FOO_HEADs happen
inside any transaction anyway.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH/RFC 0/9] Pseudorefs
  2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
                   ` (8 preceding siblings ...)
  2015-07-24  4:45 ` [PATCH 9/9] refs: forbid pseudoref updates through ref update API David Turner
@ 2015-08-02 22:48 ` Michael Haggerty
  9 siblings, 0 replies; 20+ messages in thread
From: Michael Haggerty @ 2015-08-02 22:48 UTC (permalink / raw)
  To: David Turner, git

On 07/24/2015 06:45 AM, David Turner wrote:
> This series is another chunk of the pluggable refs backend work.  The
> major reason it is listed as "PATCH/RFC" is beacuse it breaks
> t9300-fast-import.sh, because fast-import wants to create a ref called
> TEMP_TAG, which would now be a pseudoref.  The commit that introduces
> this test says that cvs2svn creates a tag called TAG_FIXUP "as a branch
> name for temporary work needed to cleanup the tree prior to creating
> an annotated tag object."
> 
> It appears that cvs2svn still does this.  So I'm not quite sure what to
> do about this particular case.
> 
> As we discussed earlier, the motivation for this series is that refs
> backends other than the files-based backend need to treat per-worktree
> refs (HEAD) and pseudorefs (FETCH_HEAD) differently from other refs;
> other refs are per-repo rather than per-worktree.

Sorry, I missed this email while on vacation.

cvs2git needs to create a temporary reference for cobbling together
tagged commits. Originally I was going to use the name `TAG_FIXUP`, as
suggested by git-fast-import(1) [1]. But that functionality turned out
to be buggy in `git fast-import` at the time. So I reported the bug but
changed cvs2git to use `refs/heads/TAG.FIXUP` [2]. The bug in `git
fast-import` was fixed, including the test case that you found, but I
never changed cvs2git back to using the originally-planned name. The
fact that the test case uses a name different than `TAG_FIXUP` probably
means that Shawn thought that any references named similarly should be
allowed.

Summary: support for a reference named `TAG_FIXUP` is not needed by
cvs2git. However, its use is a documented recommendation of `git
fast-import`, so it is quite possible that other importers are using
this name.

I expect that such importers would slightly prefer that this reference
be worktree-specific, but I doubt that anybody really cares that much.
It seems unlikely that people will run `git fast-import` in multiple
worktrees simultaneously.

Michael

[1] http://git-scm.com/docs/git-fast-import#_use_tag_fixup_branches
[2]
https://github.com/mhagger/cvs2svn/blob/master/cvs2svn_lib/git_output_option.py#L502-L509

-- 
Michael Haggerty
mhagger@alum.mit.edu

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2015-08-02 22:48 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-24  4:45 [PATCH/RFC 0/9] Pseudorefs David Turner
2015-07-24  4:45 ` [PATCH 1/9] refs: Introduce pseudoref and per-worktree ref concepts David Turner
2015-07-24  7:34   ` Eric Sunshine
2015-07-24 19:20   ` Junio C Hamano
2015-07-24 20:13     ` David Turner
2015-07-24 20:44       ` Junio C Hamano
2015-07-24 19:52   ` Philip Oakley
2015-07-24 20:14     ` David Turner
2015-07-24  4:45 ` [PATCH 2/9] notes: replace pseudorefs with real refs David Turner
2015-07-24  4:45 ` [PATCH 3/9] bisect: do not use update-ref for BISECT_HEAD David Turner
2015-07-24  4:45 ` [PATCH 4/9] tests: treat FETCH_HEAD as a pseudoref David Turner
2015-07-24  4:45 ` [PATCH 5/9] tests: use --create-reflog option to git update-ref David Turner
2015-07-24  4:45 ` [PATCH 6/9] pseudorefs: create and use pseudoref update and delete functions David Turner
2015-07-24 19:25   ` Junio C Hamano
2015-07-24 20:17     ` David Turner
2015-07-24 20:53       ` Junio C Hamano
2015-07-24  4:45 ` [PATCH 7/9] am/rebase: update pseudorefs by writing files David Turner
2015-07-24  4:45 ` [PATCH 8/9] rebase: use write_pseudoref David Turner
2015-07-24  4:45 ` [PATCH 9/9] refs: forbid pseudoref updates through ref update API David Turner
2015-08-02 22:48 ` [PATCH/RFC 0/9] Pseudorefs Michael Haggerty

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).