git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
@ 2015-11-11  2:11 Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream Michael Rappazzo
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Michael Rappazzo @ 2015-11-11  2:11 UTC (permalink / raw)
  To: gitster; +Cc: git, mhagger, peff, dturner, pclouds, sunshine, Michael Rappazzo

This patch series is built on (based on) 'next' because it relies on
worktree.c

`ff-refs` will update local branches which can be fast-forwarded to their
upstream tracking branch.  Any branch which has diverged from the upstream
will be left untouched by this command.  Additionally, there are options
for '--dry-run' and to '--skip-worktrees'.

There are two primary update mechanisms for fast-forwarding a branch.
  - For a checked out branch, emulate `git-merge --ff-only`
  - For a non-checked out branch, emulate `git update-ref`

When run on a repo with multiple worktrees (created with git-worktree add),
git-ff-refs will take that into account when fast-forwarding.  That is, it
will run in 'merge --ff-only' emulation mode when a branch is checked out
in a worktree, rather than in 'update-ref' mode.

The primary benefit of ff-refs will come for those who maintain several
local branches which track upstream remote branches that update often.  The
intended usage pattern is to run `git-fetch` followed by `git-ff-refs`.

Michael Rappazzo (5):
  ff-refs: builtin cmd to check and fast forward local refs to their
    upstream
  ff-refs: update each updatable ref
  ff-refs: add --dry-run and --skip-worktree options
  ff-refs: Add documentation
  ff-refs: Add tests

 .gitignore                    |   1 +
 Documentation/git-ff-refs.txt |  55 +++++++++
 Makefile                      |   1 +
 builtin.h                     |   1 +
 builtin/ff-refs.c             | 272 ++++++++++++++++++++++++++++++++++++++++++
 command-list.txt              |   1 +
 git.c                         |   1 +
 t/t7900-ff-refs.sh            | 164 +++++++++++++++++++++++++
 8 files changed, 496 insertions(+)
 create mode 100644 Documentation/git-ff-refs.txt
 create mode 100644 builtin/ff-refs.c
 create mode 100755 t/t7900-ff-refs.sh

-- 
2.6.2

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

* [PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream
  2015-11-11  2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
@ 2015-11-11  2:11 ` Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 2/5] ff-refs: update each updatable ref Michael Rappazzo
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Michael Rappazzo @ 2015-11-11  2:11 UTC (permalink / raw)
  To: gitster; +Cc: git, mhagger, peff, dturner, pclouds, sunshine, Michael Rappazzo

Each local branch with an upstream remote is checked to see if it can be
fast-forwarded to its upstream.  If fast-forward applies to the branch,
then this is reported to the user.

The statuses are
	UP-TO-DATE - The local branch is the same or equal to the upstream
	WOULD-UPDATE - The branch would be fast forwarded
	REMOTE-MISSING - The branch is tracking an upstream that is not present
	NON-FAST-FORWARD - The branch has diverged from the upstream

Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>
---
 .gitignore        |   1 +
 Makefile          |   1 +
 builtin.h         |   1 +
 builtin/ff-refs.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 command-list.txt  |   1 +
 git.c             |   1 +
 6 files changed, 226 insertions(+)
 create mode 100644 builtin/ff-refs.c

diff --git a/.gitignore b/.gitignore
index 1c2f832..e86a490 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,6 +53,7 @@
 /git-difftool--helper
 /git-describe
 /git-fast-export
+/git-ff-refs
 /git-fast-import
 /git-fetch
 /git-fetch-pack
diff --git a/Makefile b/Makefile
index 43ceeb9..8e312ad 100644
--- a/Makefile
+++ b/Makefile
@@ -853,6 +853,7 @@ BUILTIN_OBJS += builtin/diff.o
 BUILTIN_OBJS += builtin/fast-export.o
 BUILTIN_OBJS += builtin/fetch-pack.o
 BUILTIN_OBJS += builtin/fetch.o
+BUILTIN_OBJS += builtin/ff-refs.o
 BUILTIN_OBJS += builtin/fmt-merge-msg.o
 BUILTIN_OBJS += builtin/for-each-ref.o
 BUILTIN_OBJS += builtin/fsck.o
diff --git a/builtin.h b/builtin.h
index 6b95006..5680e33 100644
--- a/builtin.h
+++ b/builtin.h
@@ -63,6 +63,7 @@ extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
 extern int cmd_fetch(int argc, const char **argv, const char *prefix);
 extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
+extern int cmd_ff_refs(int argc, const char **argv, const char *prefix);
 extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
 extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
diff --git a/builtin/ff-refs.c b/builtin/ff-refs.c
new file mode 100644
index 0000000..94a4649
--- /dev/null
+++ b/builtin/ff-refs.c
@@ -0,0 +1,221 @@
+#include "cache.h"
+#include "refs.h"
+#include "builtin.h"
+#include "remote.h"
+#include "run-command.h"
+#include "worktree.h"
+
+struct worktree **worktrees;
+const char *padding = ".....................................................";
+
+static const char * const builtin_ff_refs_usage[] = {
+	N_("git ff-refs [<options>]"),
+	NULL
+};
+
+enum ff_result_type {
+	UP_TO_DATE,
+	UPDATABLE,
+	REMOTE_MISSING,
+	NON_FAST_FORWARD,
+	UNABLE_TO_UPDATE
+};
+
+struct ff_ref_details {
+	struct branch *branch;
+	const char *upstream;
+	const char *shortened_upstream;
+	int names_length;
+	enum ff_result_type result_type;
+
+	struct commit *branch_commit;
+	struct commit *upstream_commit;
+	struct commit *merge_base;
+	struct worktree *wt;
+};
+
+struct ff_ref_data {
+	int max_names_length;
+
+	int detail_counter;
+	int detail_alloc;
+	struct ff_ref_details **detail_list;
+};
+
+static const char *result_type_str(enum ff_result_type result_type)
+{
+	switch (result_type) {
+	case UP_TO_DATE:
+		return _("UP-TO-DATE");
+	case UPDATABLE:
+		return _("WOULD-UPDATE");
+	case REMOTE_MISSING:
+		return _("REMOTE-MISSING");
+	case NON_FAST_FORWARD:
+		return _("NON-FAST-FORWARD");
+	default:
+		return _("UNABLE-TO-UPDATE");
+	}
+}
+
+/**
+ * return the worktree with the given refname checked out, or NULL if that
+ * ref is not checked out in any branch.
+ *
+ * This implementation assumes a small number of worktrees (since it loops
+ * through each worktree for every ref).  If a repository has a large number
+ * of worktrees, then it might be beneficial to implement this as a hashmap
+ * lookup instead.
+ */
+static struct worktree *find_worktree(const char *refname)
+{
+	int i = 0;
+
+	for (i = 0; worktrees[i]; i++) {
+		if (!worktrees[i]->is_detached && !strcmp(worktrees[i]->head_ref, refname)) {
+			return worktrees[i];
+		}
+	}
+	return NULL;
+}
+
+/**
+ * After all of the relevant refs have been collected, process the
+ * interesting ones
+ */
+static void process_refs(struct ff_ref_data *data)
+{
+	int i = 0;
+
+	for (i = 0; data->detail_list[i]; i++) {
+		struct ff_ref_details *details;
+		int padLen;
+
+		details = data->detail_list[i];
+		padLen = 3 + data->max_names_length - details->names_length;
+		if (padLen < 0)
+			padLen = 0;
+
+		printf("     %s -> %s%*.*s",
+			details->branch->name, details->shortened_upstream, padLen, padLen, padding);
+		printf("[%s]\n", result_type_str(details->result_type));
+	}
+}
+
+static void add_to_detail_list(struct ff_ref_data *data,
+		struct ff_ref_details *details)
+{
+	if (!data->detail_alloc) {
+		data->detail_list = xmalloc(sizeof(struct ff_ref_details *));
+		data->detail_alloc = 1;
+	} else
+		ALLOC_GROW(data->detail_list, data->detail_counter + 1, data->detail_alloc);
+
+	if (details && details->names_length > data->max_names_length)
+		data->max_names_length = details->names_length;
+
+	data->detail_list[data->detail_counter++] = details;
+}
+
+/**
+ * Look for refs which have an upstream configured.  Each ref with an upstream
+ * is added to a list to later possibly make changes on.  All of the necessary
+ * read-only data is gleaned here.
+ */
+static int analize_refs(const char *refname,
+			const struct object_id *oid, int flags, void *cb_data) {
+
+	struct branch *branch;
+	const char *upstream;
+	struct ff_ref_data *data = cb_data;
+
+	branch = branch_get(shorten_unambiguous_ref(refname, 0));
+	upstream = branch_get_upstream(branch, NULL);
+	if (upstream) {
+		struct ff_ref_details *details = xmalloc(sizeof(struct ff_ref_details));
+		unsigned char upstream_hash[GIT_SHA1_RAWSZ];
+
+		details->branch = branch;
+		details->upstream = upstream;
+
+		details->shortened_upstream = shorten_unambiguous_ref(upstream, 0);
+		details->branch_commit = NULL;
+		details->upstream_commit = NULL;
+		details->merge_base = NULL;
+		details->result_type = UNABLE_TO_UPDATE;
+		details->names_length = strlen(branch->name) +
+				strlen(details->shortened_upstream);
+		details->wt = find_worktree(details->branch->refname);
+
+		if (!resolve_ref_unsafe(details->upstream, RESOLVE_REF_READING,
+				upstream_hash, NULL))
+			details->result_type = REMOTE_MISSING;
+
+		else if (!hashcmp(oid->hash, upstream_hash))
+			details->result_type = UP_TO_DATE;
+		else {
+			struct commit_list *bases;
+
+			details->branch_commit = lookup_commit_reference(oid->hash);
+			details->upstream_commit = lookup_commit_reference(upstream_hash);
+			bases = get_merge_bases(details->branch_commit,
+					details->upstream_commit);
+			details->merge_base = bases->item;
+
+			if (!hashcmp(upstream_hash, details->merge_base->object.sha1))
+				details->result_type = UP_TO_DATE;
+
+			else if (!in_merge_bases(details->branch_commit, details->upstream_commit))
+				details->result_type = NON_FAST_FORWARD;
+
+			else
+				details->result_type = UPDATABLE;
+		}
+		add_to_detail_list(data, details);
+	}
+	return 0;
+}
+
+/**
+ * Free the memory allocated for all of the data
+ */
+static void free_data(struct ff_ref_data *data)
+{
+	int i = 0;
+
+	for (i = 0; data->detail_list[i]; i++)
+		free(data->detail_list[i]);
+	free(data);
+}
+
+int cmd_ff_refs(int argc, const char **argv, const char *prefix)
+{
+	int ret = 0;
+
+	struct option options[] = {
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options, builtin_ff_refs_usage, 0);
+	if (argc)
+		usage_with_options(builtin_ff_refs_usage, options);
+	else {
+		struct ff_ref_data *data = NULL;
+
+		worktrees = get_worktrees();
+		data = xmalloc(sizeof(struct ff_ref_data));
+		data->detail_alloc = 0;
+		data->detail_counter = 0;
+		data->max_names_length = 0;
+
+		ret = for_each_ref(&analize_refs, data);
+		add_to_detail_list(data, NULL);
+
+		//for each detail
+		process_refs(data);
+
+		free_worktrees(worktrees);
+		free_data(data);
+	}
+	return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 2a94137..b766ea8 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -54,6 +54,7 @@ git-fast-export                         ancillarymanipulators
 git-fast-import                         ancillarymanipulators
 git-fetch                               mainporcelain           remote
 git-fetch-pack                          synchingrepositories
+git-ff-refs                             mainporcelain           history
 git-filter-branch                       ancillarymanipulators
 git-fmt-merge-msg                       purehelpers
 git-for-each-ref                        plumbinginterrogators
diff --git a/git.c b/git.c
index 6ed824c..1c75156 100644
--- a/git.c
+++ b/git.c
@@ -404,6 +404,7 @@ static struct cmd_struct commands[] = {
 	{ "fast-export", cmd_fast_export, RUN_SETUP },
 	{ "fetch", cmd_fetch, RUN_SETUP },
 	{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
+	{ "ff-refs", cmd_ff_refs, RUN_SETUP },
 	{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
 	{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
 	{ "format-patch", cmd_format_patch, RUN_SETUP },
-- 
2.6.2

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

* [PATCH 2/5] ff-refs: update each updatable ref
  2015-11-11  2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream Michael Rappazzo
@ 2015-11-11  2:11 ` Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 3/5] ff-refs: add --dry-run and --skip-worktree options Michael Rappazzo
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Michael Rappazzo @ 2015-11-11  2:11 UTC (permalink / raw)
  To: gitster; +Cc: git, mhagger, peff, dturner, pclouds, sunshine, Michael Rappazzo

If an updatable ref is checked out in any worktree, emulate merge
--ff-only to also update the local work tree (including executing the
post-merge hook).

If an updatable ref is not checked out in any worktree, the ref is
simply updated.

If a ref update is successful, that ref is reported as 'UPDATED'.  If
it is not successful, the ref is reported as 'UNABLE-TO-UPDATE'.

Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>
---
 builtin/ff-refs.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/builtin/ff-refs.c b/builtin/ff-refs.c
index 94a4649..f14afc5 100644
--- a/builtin/ff-refs.c
+++ b/builtin/ff-refs.c
@@ -59,6 +59,43 @@ static const char *result_type_str(enum ff_result_type result_type)
 }
 
 /**
+ * Do the ref update.
+ *  - If the ref is checked out in any worktree, emulate merge --ff-only to
+ *    also update the local work tree (including executing the post-merge hook).
+ *
+ *  - If the ref is not checked out in any worktree, update it
+ *
+ *  - If any of the ref updates fails, the result_type is set to UNABLE-TO-UPDATE
+ */
+static void do_ref_update(struct ff_ref_data *data, struct ff_ref_details *details)
+{
+	const char *refname = details->branch->refname;
+
+	if (details->wt) {
+		struct strbuf path = STRBUF_INIT;
+
+		strbuf_getcwd(&path);
+		chdir(details->wt->path);
+		set_git_dir(details->wt->git_dir);
+		read_index(&the_index);
+
+		if (checkout_fast_forward(details->branch_commit->object.sha1,
+				details->upstream_commit->object.sha1, 1))
+			details->result_type = NON_FAST_FORWARD;
+		else if (update_ref("ff-refs", refname, details->upstream_commit->object.sha1,
+				details->branch_commit->object.sha1, 0, UPDATE_REFS_QUIET_ON_ERR)) {
+			details->result_type = UNABLE_TO_UPDATE;
+			run_hook_le(NULL, "post-merge", "0", NULL);
+		}
+		discard_index(&the_index);
+		chdir(path.buf);
+		strbuf_release(&path);
+	} else if (update_ref("ff-refs", refname, details->upstream_commit->object.sha1,
+			details->branch_commit->object.sha1, 0, UPDATE_REFS_QUIET_ON_ERR))
+		details->result_type = UNABLE_TO_UPDATE;
+}
+
+/**
  * return the worktree with the given refname checked out, or NULL if that
  * ref is not checked out in any branch.
  *
@@ -98,6 +135,9 @@ static void process_refs(struct ff_ref_data *data)
 
 		printf("     %s -> %s%*.*s",
 			details->branch->name, details->shortened_upstream, padLen, padLen, padding);
+		if (details->result_type == UPDATABLE)
+			do_ref_update(data, details);
+
 		printf("[%s]\n", result_type_str(details->result_type));
 	}
 }
-- 
2.6.2

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

* [PATCH 3/5] ff-refs: add --dry-run and --skip-worktree options
  2015-11-11  2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 2/5] ff-refs: update each updatable ref Michael Rappazzo
@ 2015-11-11  2:11 ` Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 4/5] ff-refs: Add documentation Michael Rappazzo
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Michael Rappazzo @ 2015-11-11  2:11 UTC (permalink / raw)
  To: gitster; +Cc: git, mhagger, peff, dturner, pclouds, sunshine, Michael Rappazzo

'--dry-run' reports the updatable state of each ref without acutally
updating any refs.  Refs which are fast-forwardable are reported as
'WOULD-UPDATE'.

'--skip-worktrees' does not update refs which are checked out in any
worktree.  Branches which are fast-forwardable but checked out in a
worktree are reported as 'SKIPPED'.  With `--dry-run` these are reported
'WOULD-SKIP'.

Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>
---
 builtin/ff-refs.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/builtin/ff-refs.c b/builtin/ff-refs.c
index f14afc5..ae68cfb 100644
--- a/builtin/ff-refs.c
+++ b/builtin/ff-refs.c
@@ -5,6 +5,8 @@
 #include "run-command.h"
 #include "worktree.h"
 
+int dry_run = 0;
+int no_wt = 0;
 struct worktree **worktrees;
 const char *padding = ".....................................................";
 
@@ -18,6 +20,7 @@ enum ff_result_type {
 	UPDATABLE,
 	REMOTE_MISSING,
 	NON_FAST_FORWARD,
+	SKIPPED,
 	UNABLE_TO_UPDATE
 };
 
@@ -48,11 +51,13 @@ static const char *result_type_str(enum ff_result_type result_type)
 	case UP_TO_DATE:
 		return _("UP-TO-DATE");
 	case UPDATABLE:
-		return _("WOULD-UPDATE");
+		return dry_run ? _("WOULD-UPDATE") : _("UPDATED");
 	case REMOTE_MISSING:
 		return _("REMOTE-MISSING");
 	case NON_FAST_FORWARD:
 		return _("NON-FAST-FORWARD");
+	case SKIPPED:
+		return dry_run ? _("WOULD-SKIP") : _("SKIPPED");
 	default:
 		return _("UNABLE-TO-UPDATE");
 	}
@@ -135,7 +140,7 @@ static void process_refs(struct ff_ref_data *data)
 
 		printf("     %s -> %s%*.*s",
 			details->branch->name, details->shortened_upstream, padLen, padLen, padding);
-		if (details->result_type == UPDATABLE)
+		if (!dry_run && details->result_type == UPDATABLE)
 			do_ref_update(data, details);
 
 		printf("[%s]\n", result_type_str(details->result_type));
@@ -208,6 +213,9 @@ static int analize_refs(const char *refname,
 			else if (!in_merge_bases(details->branch_commit, details->upstream_commit))
 				details->result_type = NON_FAST_FORWARD;
 
+			else if (no_wt && details->wt)
+				details->result_type = SKIPPED;
+
 			else
 				details->result_type = UPDATABLE;
 		}
@@ -233,6 +241,9 @@ int cmd_ff_refs(int argc, const char **argv, const char *prefix)
 	int ret = 0;
 
 	struct option options[] = {
+		OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be fast-forwarded")),
+		OPT_BOOL(0, "skip-worktrees", &no_wt,
+				N_("skip refs checked out in any worktree")),
 		OPT_END()
 	};
 
-- 
2.6.2

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

* [PATCH 4/5] ff-refs: Add documentation
  2015-11-11  2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
                   ` (2 preceding siblings ...)
  2015-11-11  2:11 ` [PATCH 3/5] ff-refs: add --dry-run and --skip-worktree options Michael Rappazzo
@ 2015-11-11  2:11 ` Michael Rappazzo
  2015-11-11  2:11 ` [PATCH 5/5] ff-refs: Add tests Michael Rappazzo
  2015-11-11 10:41 ` [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael J Gruber
  5 siblings, 0 replies; 13+ messages in thread
From: Michael Rappazzo @ 2015-11-11  2:11 UTC (permalink / raw)
  To: gitster; +Cc: git, mhagger, peff, dturner, pclouds, sunshine, Michael Rappazzo

Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>
---
 Documentation/git-ff-refs.txt | 55 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 Documentation/git-ff-refs.txt

diff --git a/Documentation/git-ff-refs.txt b/Documentation/git-ff-refs.txt
new file mode 100644
index 0000000..569d1d4
--- /dev/null
+++ b/Documentation/git-ff-refs.txt
@@ -0,0 +1,55 @@
+git-ff-refs(1)
+============
+
+NAME
+----
+git-ff-refs - Fast-forward each local branch to its upstream (if possible)
+
+
+SYNOPSIS
+--------
+[verse]
+'git ff-refs' [<options>]
+
+
+DESCRIPTION
+-----------
+Update local branches to their tracked upstream if and only if the local branch
+can be fast-forwarded to the upstream ref.  Local branches which have diverged
+from the upstream will not be updated.
+
+Each local branch tracking an upstream will be reported with a status based on
+whether or not it it was updated.  The statuses are:
+
+	UP-TO-DATE - The local branch is the same or equal to the upstream
+	UPDATED - The branch was fast forwarded
+	WOULD-UPDATE - With `--dry-run`, the branch is fast forwardable
+	REMOTE-MISSING - The branch is tracking an upstream that is not present
+	NON-FAST-FORWARD - The branch has diverged from the upstream
+	SKIPPED - With `--skip-worktrees` the branch is fast forwardable but checked
+		out in a local worktree
+	WOULD-SKIP - With `--skip-worktrees` and `--dry-run`, the branch is fast
+		forwardable but checked out in a local worktree
+	UNABLE-TO-UPDATE - An error occurred while trying to update the branch
+
+OPTIONS
+-------
+--dry-run::
+	Report the updatable state of each branch without acutally updating anything.
+	In a dry run, fast-forwardable branches are marked 'WOULD-UPDATE' instead of
+	'UPDATED'.
+
+--skip-worktrees::
+	Does not update branches which are checked out in any worktree.  Branches
+	which are fast-forwardable that are checked out in any worktree are marked
+	'SKIPPED'.  If `--dry-run` is also set, those branches are marked 'WOULD-SKIP'.
+
+
+SEE ALSO
+--------
+linkgit:git-update-ref[1]
+linkgit:git-merge[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
-- 
2.6.2

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

* [PATCH 5/5] ff-refs: Add tests
  2015-11-11  2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
                   ` (3 preceding siblings ...)
  2015-11-11  2:11 ` [PATCH 4/5] ff-refs: Add documentation Michael Rappazzo
@ 2015-11-11  2:11 ` Michael Rappazzo
  2015-11-11 10:41 ` [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael J Gruber
  5 siblings, 0 replies; 13+ messages in thread
From: Michael Rappazzo @ 2015-11-11  2:11 UTC (permalink / raw)
  To: gitster; +Cc: git, mhagger, peff, dturner, pclouds, sunshine, Michael Rappazzo

Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>
---
 t/t7900-ff-refs.sh | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 164 insertions(+)
 create mode 100755 t/t7900-ff-refs.sh

diff --git a/t/t7900-ff-refs.sh b/t/t7900-ff-refs.sh
new file mode 100755
index 0000000..3cfbcb8
--- /dev/null
+++ b/t/t7900-ff-refs.sh
@@ -0,0 +1,164 @@
+#!/bin/sh
+
+# SKIPPED
+
+test_description='test ff-refs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init &&
+	for i in $(test_seq 1 9)
+	do
+		echo "data" >file_$i &&
+		git add file_$i &&
+		git commit -m"Commit $i" &&
+		git branch br_$i
+	done
+'
+
+test_expect_success 'UP-TO-DATE for equal branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git ff-refs >actual &&
+		grep "UP-TO-DATE" actual
+	)
+'
+
+test_expect_success 'UP-TO-DATE for ahead local branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git ff-refs >actual &&
+		echo "data" >file_new &&
+		git add file_new &&
+		git commit -m"Commit new" &&
+		grep "UP-TO-DATE" actual
+	)
+'
+
+test_expect_success 'REMOTE-MISSING by local config change' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git config --replace branch.master.merge refs/heads/nothing &&
+		git ff-refs >actual &&
+		grep "REMOTE-MISSING" actual
+	)
+'
+
+test_expect_success 'NON-FAST-FORWARD for diverged branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		echo "data" >file_new &&
+		git add file_new &&
+		git commit -m"Commit new" &&
+		git ff-refs >actual &&
+		grep "NON-FAST-FORWARD" actual
+	)
+'
+
+test_expect_success 'UPDATED for fast-forwardable branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git ff-refs >actual &&
+		grep "UPDATED" actual
+	)
+'
+
+test_expect_success 'WOULD-UPDATE for dry-run on fast-forwardable branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git ff-refs --dry-run >actual &&
+		grep "WOULD-UPDATE" actual
+	)
+'
+
+test_expect_success 'SKIPPED for skip-worktrees on fast-forwardable branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git ff-refs --skip-worktrees >actual &&
+		grep "SKIPPED" actual
+	)
+'
+
+test_expect_success 'WOULD-SKIP for dry-run, skip-worktrees on fast-forwardable branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git ff-refs --dry-run --skip-worktrees >actual &&
+		grep "WOULD-SKIP" actual
+	)
+'
+
+test_expect_success 'UPDATE for fast-forwardable, not checked-out branch' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git checkout -b other origin/br_3 &&
+		git ff-refs >actual &&
+		grep "master" actual | grep "UPDATED"
+	)
+'
+
+test_expect_success 'UPDATE for fast-forwardable, not checked-out branch using --skip-worktrees' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git checkout -b other origin/br_3 &&
+		git ff-refs --skip-worktrees >actual &&
+		grep "master" actual | grep "UPDATED"
+	)
+'
+
+test_expect_success 'UPDATE multiple' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git checkout -b other origin/br_5 &&
+		git reset --hard origin/br_3 &&
+		git ff-refs >actual &&
+		grep "master" actual | grep "UPDATED" &&
+		grep "other" actual | grep "UPDATED"
+	)
+'
+
+test_expect_success 'UPDATE one, skip worktree on another' '
+	test_when_finished "rm -rf local" &&
+	git clone . local &&
+	(
+		cd local &&
+		git reset --hard origin/br_3 &&
+		git checkout -b other origin/br_5 &&
+		git reset --hard origin/br_3 &&
+		git ff-refs --skip-worktrees >actual &&
+		grep "master" actual | grep "UPDATED" &&
+		grep "other" actual | grep "SKIPPED"
+	)
+'
+
+test_done
-- 
2.6.2

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

* Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
  2015-11-11  2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
                   ` (4 preceding siblings ...)
  2015-11-11  2:11 ` [PATCH 5/5] ff-refs: Add tests Michael Rappazzo
@ 2015-11-11 10:41 ` Michael J Gruber
  2015-11-11 12:32   ` Mike Rappazzo
  5 siblings, 1 reply; 13+ messages in thread
From: Michael J Gruber @ 2015-11-11 10:41 UTC (permalink / raw)
  To: Michael Rappazzo, gitster; +Cc: git, mhagger, peff, dturner, pclouds, sunshine

Michael Rappazzo venit, vidit, dixit 11.11.2015 03:11:
> This patch series is built on (based on) 'next' because it relies on
> worktree.c
> 
> `ff-refs` will update local branches which can be fast-forwarded to their
> upstream tracking branch.  Any branch which has diverged from the upstream
> will be left untouched by this command.  Additionally, there are options
> for '--dry-run' and to '--skip-worktrees'.
> 
> There are two primary update mechanisms for fast-forwarding a branch.
>   - For a checked out branch, emulate `git-merge --ff-only`
>   - For a non-checked out branch, emulate `git update-ref`
> 
> When run on a repo with multiple worktrees (created with git-worktree add),
> git-ff-refs will take that into account when fast-forwarding.  That is, it
> will run in 'merge --ff-only' emulation mode when a branch is checked out
> in a worktree, rather than in 'update-ref' mode.
> 
> The primary benefit of ff-refs will come for those who maintain several
> local branches which track upstream remote branches that update often.  The
> intended usage pattern is to run `git-fetch` followed by `git-ff-refs`.

I'm sorry, but I don't see why this deserves a new command. If refspec
with and without "+" are not enough then maybe "git fetch --all" or "git
remote update" should learn a new "--ff-only" option (ignoring all "+")
like merge has.

As for updating worktrees: This shouldn't be taken too lightly anyways.
But the worktree interface still has some rough edges, and I would hope
that it learns a "foreach" subcommand very much like the submodule
version. That would allow you to

git worktree foreach git merge --ff-only

with a systematic aproach that opens many other opportunities.

Michael

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

* Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
  2015-11-11 10:41 ` [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael J Gruber
@ 2015-11-11 12:32   ` Mike Rappazzo
       [not found]     ` <CANoM8SWxMeDjwy-GwVc+En8D7N8LyzzsBKtX_MbiS4Z49DjD7g@mail.gmail.com>
  0 siblings, 1 reply; 13+ messages in thread
From: Mike Rappazzo @ 2015-11-11 12:32 UTC (permalink / raw)
  To: Michael J Gruber
  Cc: Junio C Hamano, Git List, Michael Haggerty, Jeff King,
	David Turner, Nguyễn Thái Ngọc, Eric Sunshine

On Wed, Nov 11, 2015 at 5:41 AM, Michael J Gruber
<git@drmicha.warpmail.net> wrote:
> Michael Rappazzo venit, vidit, dixit 11.11.2015 03:11:
>> This patch series is built on (based on) 'next' because it relies on
>> worktree.c
>>
>> `ff-refs` will update local branches which can be fast-forwarded to their
>> upstream tracking branch.  Any branch which has diverged from the upstream
>> will be left untouched by this command.  Additionally, there are options
>> for '--dry-run' and to '--skip-worktrees'.
>>
>> There are two primary update mechanisms for fast-forwarding a branch.
>>   - For a checked out branch, emulate `git-merge --ff-only`
>>   - For a non-checked out branch, emulate `git update-ref`
>>
>> When run on a repo with multiple worktrees (created with git-worktree add),
>> git-ff-refs will take that into account when fast-forwarding.  That is, it
>> will run in 'merge --ff-only' emulation mode when a branch is checked out
>> in a worktree, rather than in 'update-ref' mode.
>>
>> The primary benefit of ff-refs will come for those who maintain several
>> local branches which track upstream remote branches that update often.  The
>> intended usage pattern is to run `git-fetch` followed by `git-ff-refs`.
>
> I'm sorry, but I don't see why this deserves a new command. If refspec
> with and without "+" are not enough then maybe "git fetch --all" or "git
> remote update" should learn a new "--ff-only" option (ignoring all "+")
> like merge has.

Maybe I wasn't clear in my description, or maybe I misunderstand
something.  This command is about updating local refs (branches,
really), not the local copy of a remote ref.  If, for example I have
local branches:

    master -> origin/master
    next -> origin/next
    pu -> origin/pu
    feature1 -> features/feature1
    feature2 -> features/feature2
    feature3 -> features/feature3
    bug1 -> features/bugs/bug1
    bug2 -> features/bugs/bug2

If I don't use multiple worktrees, I probably only have one of those
checked out at any one time.  If any of the upstream branches are
updated, then when I fetch those branches will be behind.  If I wanted
to make sure that the branches I am not touching are updated, I would
have to do it individually (AFAIK).  And why not update my local
worktree if it is a fast-forward?.  This command aims to put that
local branch update into a single command.

    > git fetch --all
    fetching origin...
        abc1234..abc1235  next -> origin/next
        abd1234..abd1235  pu -> origin/pu

    fetching features...
        123abcd..123abce  feature1 -> features/feature1
      + 124abcd...124abce feature2 -> features/feature2
        125abcd..125abce  feature3 -> features/feature3
    > git ff-refs
        master -> origin/master.........[UP-TO-DATE]
        next -> origin/next.............[UPDATED]
        pu -> origin/pu.................[UPDATED]
        feature1 -> features/feature1...[UPDATED]
        feature2 -> features/feature2...[NON-FAST-FORWARD]
        feature3 -> features/feature3...[UPDATED]
        bug1 -> features/bugs/bug1......[UP-TO-DATE]
        bug2 -> features/bugs/bug2......[UP-TO-DATE]

For reference, I have been using a scripted version of this command
[1].  Assuming that I change your mind on this command, I will add
this example to the help doc.

>
> As for updating worktrees: This shouldn't be taken too lightly anyways.
> But the worktree interface still has some rough edges, and I would hope
> that it learns a "foreach" subcommand very much like the submodule
> version. That would allow you to
>
> git worktree foreach git merge --ff-only
>
> with a systematic aproach that opens many other opportunities.
>
> Michael

I am aware of the current status of the worktrees command (I worked on
the 'list' command).  If a user only wants to update unchecked out
branches, there is a command line option provided, '--skip-worktrees'.

The foreach command sounds like a good idea, but I don't know that it
would help here, as ff-refs is looping through all of the refs already
(ala for-each-ref).  If you are proposing foreach-worktree as an
alternative, that is good for half of the command, but I would still
want to update the unchecked out refs.

_Mike


[1] https://github.com/rappazzo/dotfiles/blob/ff-refs/bin/git-ff-refs

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

* Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
       [not found]     ` <CANoM8SWxMeDjwy-GwVc+En8D7N8LyzzsBKtX_MbiS4Z49DjD7g@mail.gmail.com>
@ 2015-11-17 15:28       ` Michael J Gruber
  2015-11-17 15:36         ` Mike Rappazzo
  0 siblings, 1 reply; 13+ messages in thread
From: Michael J Gruber @ 2015-11-17 15:28 UTC (permalink / raw)
  To: Mike Rappazzo; +Cc: Git Mailing List

Mike Rappazzo venit, vidit, dixit 17.11.2015 14:58:
> (This message is off list)
[cut the message part, though no big secrets there]

Hi Mike,

I don't think there's a point in discussing this off-list.

Your intentions are clearer now, yes, or else I would have asked more.

I still don't like the idea of having a new command just for the purpose
of fast-forwarding local branches from specified upstreams.

What's wrong with "git merge --ff-only" once you check them out? We have
all the gory messages when you checkout a branch or use the git prompt
or "branch -vv". And if you don't - how is forgetting to "ff-refs"
better than forgetting to "merge --ff-only"?

In short, I don't see a problem that this is solving, but maybe it's
because we use local branches differently, I dunno.

If other people were interested they should or would have come up with
comments, I think (as a general rule).

Cheers,
Michael

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

* Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
  2015-11-17 15:28       ` Michael J Gruber
@ 2015-11-17 15:36         ` Mike Rappazzo
  2015-11-18  9:56           ` Johannes Schindelin
  0 siblings, 1 reply; 13+ messages in thread
From: Mike Rappazzo @ 2015-11-17 15:36 UTC (permalink / raw)
  To: Michael J Gruber; +Cc: Git Mailing List

On Tue, Nov 17, 2015 at 10:28 AM, Michael J Gruber
<git@drmicha.warpmail.net> wrote:
> Mike Rappazzo venit, vidit, dixit 17.11.2015 14:58:
>
> I still don't like the idea of having a new command just for the purpose
> of fast-forwarding local branches from specified upstreams.
>
> What's wrong with "git merge --ff-only" once you check them out? We have
> all the gory messages when you checkout a branch or use the git prompt
> or "branch -vv". And if you don't - how is forgetting to "ff-refs"
> better than forgetting to "merge --ff-only"?
>
> In short, I don't see a problem that this is solving, but maybe it's
> because we use local branches differently, I dunno.

For me I use this command more as a post-fetch:

git fetch --all --prune && git-ff-refs

I imagine that the big difference is in the number of branches that I
maintain, and perhaps in the way that I use gitk to visualize them.  I
would be happy to add another option to git-fetch for --ff-refs as an
alternative if that would feel better than a full-on builtin.

>
> If other people were interested they should or would have come up with
> comments, I think (as a general rule).
>
> Cheers,
> Michael

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

* Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
  2015-11-17 15:36         ` Mike Rappazzo
@ 2015-11-18  9:56           ` Johannes Schindelin
  2015-11-24 22:39             ` Jeff King
  0 siblings, 1 reply; 13+ messages in thread
From: Johannes Schindelin @ 2015-11-18  9:56 UTC (permalink / raw)
  To: Mike Rappazzo; +Cc: Michael J Gruber, Git Mailing List

Hi Mike,

On Tue, 17 Nov 2015, Mike Rappazzo wrote:

> On Tue, Nov 17, 2015 at 10:28 AM, Michael J Gruber
> <git@drmicha.warpmail.net> wrote:
> > Mike Rappazzo venit, vidit, dixit 17.11.2015 14:58:
> >
> > I still don't like the idea of having a new command just for the purpose
> > of fast-forwarding local branches from specified upstreams.
> >
> > What's wrong with "git merge --ff-only" once you check them out? We have
> > all the gory messages when you checkout a branch or use the git prompt
> > or "branch -vv". And if you don't - how is forgetting to "ff-refs"
> > better than forgetting to "merge --ff-only"?
> >
> > In short, I don't see a problem that this is solving, but maybe it's
> > because we use local branches differently, I dunno.
> 
> For me I use this command more as a post-fetch:
> 
> git fetch --all --prune && git-ff-refs
> 
> I imagine that the big difference is in the number of branches that I
> maintain, and perhaps in the way that I use gitk to visualize them.  I
> would be happy to add another option to git-fetch for --ff-refs as an
> alternative if that would feel better than a full-on builtin.

I would much prefer, say, `git fetch --all --prune
--fast-forward-tracking-branches` (with maybe `-T` as short option for
`--fast-forward-tracking-branches` and/or a shorter `--ff-tracking`) to a
new builtin.

But yeah, I can see how it is convenient when you have to work with N
tracking branches where N > 2.

Thanks,
Dscho

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

* Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
  2015-11-18  9:56           ` Johannes Schindelin
@ 2015-11-24 22:39             ` Jeff King
  2015-12-01  0:24               ` Junio C Hamano
  0 siblings, 1 reply; 13+ messages in thread
From: Jeff King @ 2015-11-24 22:39 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Mike Rappazzo, Michael J Gruber, Git Mailing List

On Wed, Nov 18, 2015 at 10:56:02AM +0100, Johannes Schindelin wrote:

> > For me I use this command more as a post-fetch:
> > 
> > git fetch --all --prune && git-ff-refs
> > 
> > I imagine that the big difference is in the number of branches that I
> > maintain, and perhaps in the way that I use gitk to visualize them.  I
> > would be happy to add another option to git-fetch for --ff-refs as an
> > alternative if that would feel better than a full-on builtin.
> 
> I would much prefer, say, `git fetch --all --prune
> --fast-forward-tracking-branches` (with maybe `-T` as short option for
> `--fast-forward-tracking-branches` and/or a shorter `--ff-tracking`) to a
> new builtin.

FWIW, that makes a lot more sense to me, as it would presumably touch
only branches which track whatever we just updated, and not other random
refs.

I have to admit that I'm a little wary of something like ff-refs meeting
all needs, though. I have custom scripts that match my workflow and tell
me when a branch could be updated. I could replace part of them with
"ff-refs --dry-run", but that is really not much code. Basically:

  git for-each-ref --format='%(refname) %(upstream)' refs/heads |
  while read ref upstream; do
    git merge-base --is-ancestor $ref $upstream &&
      echo "$ref can fast-forward"
  done

-Peff

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

* Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs
  2015-11-24 22:39             ` Jeff King
@ 2015-12-01  0:24               ` Junio C Hamano
  0 siblings, 0 replies; 13+ messages in thread
From: Junio C Hamano @ 2015-12-01  0:24 UTC (permalink / raw)
  To: Jeff King
  Cc: Johannes Schindelin, Mike Rappazzo, Michael J Gruber,
	Git Mailing List

Jeff King <peff@peff.net> writes:

> On Wed, Nov 18, 2015 at 10:56:02AM +0100, Johannes Schindelin wrote:
>
>> > For me I use this command more as a post-fetch:
>> > 
>> > git fetch --all --prune && git-ff-refs
>> > 
>> > I imagine that the big difference is in the number of branches that I
>> > maintain, and perhaps in the way that I use gitk to visualize them.  I
>> > would be happy to add another option to git-fetch for --ff-refs as an
>> > alternative if that would feel better than a full-on builtin.
>> 
>> I would much prefer, say, `git fetch --all --prune
>> --fast-forward-tracking-branches` (with maybe `-T` as short option for
>> `--fast-forward-tracking-branches` and/or a shorter `--ff-tracking`) to a
>> new builtin.

Hmph, I am not sure it is a good idea to allow "git fetch" affect
refs that it was not told to "fetch", but that is why you give a new
option from the command line to update refs that are not involved in
the fetch based on what was fetched, so it might be OK.

But the above is *NOT* fast-forwarding "tracking" branch.  It is
doing something else: fast-forwarding the local branch that is based
on a remote-tracking branch.

    They have refs/heads/master, and they call it their 'master'
    branch.

    You have refs/remotes/origin/master, and that is the
    remote-tracking branch for their 'master'.

    You may have prepared your 'master' to build on their 'master'
    branch.  That is not a 'tracking branch' for anything.

So --ff-tracking and the other name above need to be rethought.

> FWIW, that makes a lot more sense to me, as it would presumably touch
> only branches which track whatever we just updated, and not other random
> refs.
>

This ff-refs series breaks build for me by introducing calls to chdir()
whose return values are not checked -Werror=unused-result, by the way.

> I have to admit that I'm a little wary of something like ff-refs meeting
> all needs, though. I have custom scripts that match my workflow and tell
> me when a branch could be updated. I could replace part of them with
> "ff-refs --dry-run", but that is really not much code. Basically:
>
>   git for-each-ref --format='%(refname) %(upstream)' refs/heads |
>   while read ref upstream; do
>     git merge-base --is-ancestor $ref $upstream &&
>       echo "$ref can fast-forward"
>   done

Yup.  I like that one.

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

end of thread, other threads:[~2015-12-01  0:24 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-11  2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
2015-11-11  2:11 ` [PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream Michael Rappazzo
2015-11-11  2:11 ` [PATCH 2/5] ff-refs: update each updatable ref Michael Rappazzo
2015-11-11  2:11 ` [PATCH 3/5] ff-refs: add --dry-run and --skip-worktree options Michael Rappazzo
2015-11-11  2:11 ` [PATCH 4/5] ff-refs: Add documentation Michael Rappazzo
2015-11-11  2:11 ` [PATCH 5/5] ff-refs: Add tests Michael Rappazzo
2015-11-11 10:41 ` [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael J Gruber
2015-11-11 12:32   ` Mike Rappazzo
     [not found]     ` <CANoM8SWxMeDjwy-GwVc+En8D7N8LyzzsBKtX_MbiS4Z49DjD7g@mail.gmail.com>
2015-11-17 15:28       ` Michael J Gruber
2015-11-17 15:36         ` Mike Rappazzo
2015-11-18  9:56           ` Johannes Schindelin
2015-11-24 22:39             ` Jeff King
2015-12-01  0:24               ` Junio C Hamano

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 NNTP newsgroup(s).