git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] push: add 'prune' option
@ 2012-02-22 22:43 Felipe Contreras
  2012-02-22 22:43 ` [PATCH 1/4] remote: use a local variable in match_push_refs() Felipe Contreras
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Felipe Contreras @ 2012-02-22 22:43 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Felipe Contreras

Hi,

As mentioned in a previous thread[1], git is lacking some functionality to
synchronize completely with remote repositories.

As an example I put my use-case; I want to backup *all* my local branches to a
personal repository, and I want to remove branches that I have removed from my
local repository. git push personal 'refs/heads/*' mostly does the job, but it
doesn't remove anything, and that's where 'prune' comes from.

Do not confuse the remote branches with the upstream ones; you could have two
repositories where you want to synchronize branches to:
% git push --prune --force backup1 'refs/heads/*'
% git push --prune --force backup2 'refs/heads/*'

I still think a 'git remote sync' would be tremendously useful, but it can
actually use this --prune option.

Cheers.

[1] http://article.gmane.org/gmane.comp.version-control.git/184990

Since RFC:

 - Most of comments addressed
 - Documentation and tests added

Felipe Contreras (4):
  remote: use a local variable in match_push_refs()
  remote: reorganize check_pattern_match()
  remote: refactor code into alloc_delete_ref()
  push: add 'prune' option

 Documentation/git-push.txt |    9 +++-
 builtin/push.c             |    2 +
 remote.c                   |  107 ++++++++++++++++++++++++++++----------------
 remote.h                   |    3 +-
 t/t5516-fetch-push.sh      |   16 +++++++
 transport.c                |    2 +
 transport.h                |    1 +
 7 files changed, 100 insertions(+), 40 deletions(-)

-- 
1.7.9.1

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

* [PATCH 1/4] remote: use a local variable in match_push_refs()
  2012-02-22 22:43 [PATCH 0/4] push: add 'prune' option Felipe Contreras
@ 2012-02-22 22:43 ` Felipe Contreras
  2012-02-22 22:43 ` [PATCH 2/4] remote: reorganize check_pattern_match() Felipe Contreras
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: Felipe Contreras @ 2012-02-22 22:43 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Felipe Contreras

So that we can reuse src later on. No functional changes.

Will be useful in next patches.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 remote.c |   19 ++++++++++---------
 1 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/remote.c b/remote.c
index 73a3809..55d68d1 100644
--- a/remote.c
+++ b/remote.c
@@ -1157,7 +1157,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
 	int send_mirror = flags & MATCH_REFS_MIRROR;
 	int errs;
 	static const char *default_refspec[] = { ":", NULL };
-	struct ref **dst_tail = tail_ref(dst);
+	struct ref *ref, **dst_tail = tail_ref(dst);
 
 	if (!nr_refspec) {
 		nr_refspec = 1;
@@ -1167,14 +1167,14 @@ int match_push_refs(struct ref *src, struct ref **dst,
 	errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
 
 	/* pick the remainder */
-	for ( ; src; src = src->next) {
+	for (ref = src; ref; ref = ref->next) {
 		struct ref *dst_peer;
 		const struct refspec *pat = NULL;
 		char *dst_name;
-		if (src->peer_ref)
+		if (ref->peer_ref)
 			continue;
 
-		pat = check_pattern_match(rs, nr_refspec, src);
+		pat = check_pattern_match(rs, nr_refspec, ref);
 		if (!pat)
 			continue;
 
@@ -1184,13 +1184,14 @@ int match_push_refs(struct ref *src, struct ref **dst,
 			 * including refs outside refs/heads/ hierarchy, but
 			 * that does not make much sense these days.
 			 */
-			if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
+			if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
 				continue;
-			dst_name = xstrdup(src->name);
+			dst_name = xstrdup(ref->name);
+
 
 		} else {
 			const char *dst_side = pat->dst ? pat->dst : pat->src;
-			if (!match_name_with_pattern(pat->src, src->name,
+			if (!match_name_with_pattern(pat->src, ref->name,
 						     dst_side, &dst_name))
 				die("Didn't think it matches any more");
 		}
@@ -1211,9 +1212,9 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
 			/* Create a new one and link it */
 			dst_peer = make_linked_ref(dst_name, &dst_tail);
-			hashcpy(dst_peer->new_sha1, src->new_sha1);
+			hashcpy(dst_peer->new_sha1, ref->new_sha1);
 		}
-		dst_peer->peer_ref = copy_ref(src);
+		dst_peer->peer_ref = copy_ref(ref);
 		dst_peer->force = pat->force;
 	free_name:
 		free(dst_name);
-- 
1.7.9.1

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

* [PATCH 2/4] remote: reorganize check_pattern_match()
  2012-02-22 22:43 [PATCH 0/4] push: add 'prune' option Felipe Contreras
  2012-02-22 22:43 ` [PATCH 1/4] remote: use a local variable in match_push_refs() Felipe Contreras
@ 2012-02-22 22:43 ` Felipe Contreras
  2012-02-22 22:43 ` [PATCH 3/4] remote: refactor code into alloc_delete_ref() Felipe Contreras
  2012-02-22 22:43 ` [PATCH 4/4] push: add 'prune' option Felipe Contreras
  3 siblings, 0 replies; 9+ messages in thread
From: Felipe Contreras @ 2012-02-22 22:43 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Felipe Contreras

There's a lot of code that can be consolidated there, and will be useful
for next patches.

Right now match_name_with_pattern() is called twice, the first one
without value and result, and the second one with them. Since
check_pattern_match() is only used in one place, we can just reorganize
it to make a single call and fetch the values at the same time.

Since this is a semantic change, also rename the function to
get_ref_match() which actually describes more closely what it's actually
doing now.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 remote.c |   59 ++++++++++++++++++++++++++++++-----------------------------
 1 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/remote.c b/remote.c
index 55d68d1..9d29acc 100644
--- a/remote.c
+++ b/remote.c
@@ -1110,10 +1110,11 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
 	return errs;
 }
 
-static const struct refspec *check_pattern_match(const struct refspec *rs,
-						 int rs_nr,
-						 const struct ref *src)
+static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
+		int send_mirror, const struct refspec **ret_pat)
 {
+	const struct refspec *pat;
+	char *name;
 	int i;
 	int matching_refs = -1;
 	for (i = 0; i < rs_nr; i++) {
@@ -1123,14 +1124,31 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
 			continue;
 		}
 
-		if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name,
-							     NULL, NULL))
-			return rs + i;
+		if (rs[i].pattern) {
+			const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
+			if (match_name_with_pattern(rs[i].src, ref->name, dst_side, &name)) {
+				matching_refs = i;
+				break;
+			}
+		}
 	}
-	if (matching_refs != -1)
-		return rs + matching_refs;
-	else
+	if (matching_refs == -1)
 		return NULL;
+
+	pat = rs + matching_refs;
+	if (pat->matching) {
+		/*
+		 * "matching refs"; traditionally we pushed everything
+		 * including refs outside refs/heads/ hierarchy, but
+		 * that does not make much sense these days.
+		 */
+		if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
+			return NULL;
+		name = xstrdup(ref->name);
+	}
+	if (ret_pat)
+		*ret_pat = pat;
+	return name;
 }
 
 static struct ref **tail_ref(struct ref **head)
@@ -1171,36 +1189,19 @@ int match_push_refs(struct ref *src, struct ref **dst,
 		struct ref *dst_peer;
 		const struct refspec *pat = NULL;
 		char *dst_name;
+
 		if (ref->peer_ref)
 			continue;
 
-		pat = check_pattern_match(rs, nr_refspec, ref);
-		if (!pat)
+		dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, &pat);
+		if (!dst_name)
 			continue;
 
-		if (pat->matching) {
-			/*
-			 * "matching refs"; traditionally we pushed everything
-			 * including refs outside refs/heads/ hierarchy, but
-			 * that does not make much sense these days.
-			 */
-			if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
-				continue;
-			dst_name = xstrdup(ref->name);
-
-
-		} else {
-			const char *dst_side = pat->dst ? pat->dst : pat->src;
-			if (!match_name_with_pattern(pat->src, ref->name,
-						     dst_side, &dst_name))
-				die("Didn't think it matches any more");
-		}
 		dst_peer = find_ref_by_name(*dst, dst_name);
 		if (dst_peer) {
 			if (dst_peer->peer_ref)
 				/* We're already sending something to this ref. */
 				goto free_name;
-
 		} else {
 			if (pat->matching && !(send_all || send_mirror))
 				/*
-- 
1.7.9.1

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

* [PATCH 3/4] remote: refactor code into alloc_delete_ref()
  2012-02-22 22:43 [PATCH 0/4] push: add 'prune' option Felipe Contreras
  2012-02-22 22:43 ` [PATCH 1/4] remote: use a local variable in match_push_refs() Felipe Contreras
  2012-02-22 22:43 ` [PATCH 2/4] remote: reorganize check_pattern_match() Felipe Contreras
@ 2012-02-22 22:43 ` Felipe Contreras
  2012-02-22 22:43 ` [PATCH 4/4] push: add 'prune' option Felipe Contreras
  3 siblings, 0 replies; 9+ messages in thread
From: Felipe Contreras @ 2012-02-22 22:43 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Felipe Contreras

Will be useful in next patches. No functional changes.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 remote.c |   14 +++++++++-----
 1 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/remote.c b/remote.c
index 9d29acc..4709e20 100644
--- a/remote.c
+++ b/remote.c
@@ -978,16 +978,20 @@ static void tail_link_ref(struct ref *ref, struct ref ***tail)
 	*tail = &ref->next;
 }
 
+static struct ref *alloc_delete_ref(void)
+{
+	struct ref *ref = alloc_ref("(delete)");
+	hashclr(ref->new_sha1);
+	return ref;
+}
+
 static struct ref *try_explicit_object_name(const char *name)
 {
 	unsigned char sha1[20];
 	struct ref *ref;
 
-	if (!*name) {
-		ref = alloc_ref("(delete)");
-		hashclr(ref->new_sha1);
-		return ref;
-	}
+	if (!*name)
+		return alloc_delete_ref();
 	if (get_sha1(name, sha1))
 		return NULL;
 	ref = alloc_ref(name);
-- 
1.7.9.1

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

* [PATCH 4/4] push: add 'prune' option
  2012-02-22 22:43 [PATCH 0/4] push: add 'prune' option Felipe Contreras
                   ` (2 preceding siblings ...)
  2012-02-22 22:43 ` [PATCH 3/4] remote: refactor code into alloc_delete_ref() Felipe Contreras
@ 2012-02-22 22:43 ` Felipe Contreras
  2012-02-23  0:42   ` Junio C Hamano
  3 siblings, 1 reply; 9+ messages in thread
From: Felipe Contreras @ 2012-02-22 22:43 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Felipe Contreras

This will allow us to remove refs from the remote that have been removed
locally.

It's useful to conveniently synchronize all the local branches to
certain remote.

Unfortunately, if we want to handle src:dst refspecs properly, some
extra complexity is needed, so check_pattern_match() needs to be
reorganized (previous patch).

This ensures that the following works:

 $ git push --prune remote refs/heads/*:refs/tmp/*

So that local branches are not only pushed as custom refs in the remote,
but also refs/tmp/foo would be removed if there's no corresponding local
refs/heads/foo.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 Documentation/git-push.txt |    9 ++++++++-
 builtin/push.c             |    2 ++
 remote.c                   |   31 ++++++++++++++++++++++++++++---
 remote.h                   |    3 ++-
 t/t5516-fetch-push.sh      |   16 ++++++++++++++++
 transport.c                |    2 ++
 transport.h                |    1 +
 7 files changed, 59 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index aede488..204f36d 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git push' [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
-	   [--repo=<repository>] [-f | --force] [-v | --verbose] [-u | --set-upstream]
+	   [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
 	   [<repository> [<refspec>...]]
 
 DESCRIPTION
@@ -71,6 +71,13 @@ nor in any Push line of the corresponding remotes file---see below).
 	Instead of naming each ref to push, specifies that all
 	refs under `refs/heads/` be pushed.
 
+--prune::
+	Remove remote branches that don't have a local counterpart. For example
+	a remote branch `tmp` will be removed if a local branch with the same
+	name doesn't exist any more. This also respects refspecs, e.g.
+	`refs/heads/*:refs/tmp/*` would make sure that remote `refs/tmp/foo`
+	will be removed if `refs/heads/foo` doesn't exist.
+
 --mirror::
 	Instead of naming each ref to push, specifies that all
 	refs under `refs/` (which includes but is not
diff --git a/builtin/push.c b/builtin/push.c
index 35cce53..fdfb2c4 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -261,6 +261,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 		OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
 			TRANSPORT_PUSH_SET_UPSTREAM),
 		OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
+		OPT_BIT(0, "prune", &flags, "prune locally removed refs",
+			TRANSPORT_PUSH_PRUNE),
 		OPT_END()
 	};
 
diff --git a/remote.c b/remote.c
index 4709e20..9c590ae 100644
--- a/remote.c
+++ b/remote.c
@@ -8,6 +8,8 @@
 #include "tag.h"
 #include "string-list.h"
 
+enum map_direction { FROM_SRC, FROM_DST };
+
 static struct refspec s_tag_refspec = {
 	0,
 	1,
@@ -1115,7 +1117,7 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
 }
 
 static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
-		int send_mirror, const struct refspec **ret_pat)
+		int send_mirror, int direction, const struct refspec **ret_pat)
 {
 	const struct refspec *pat;
 	char *name;
@@ -1130,7 +1132,12 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref
 
 		if (rs[i].pattern) {
 			const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
-			if (match_name_with_pattern(rs[i].src, ref->name, dst_side, &name)) {
+			int match;
+			if (direction == FROM_SRC)
+				match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);
+			else
+				match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);
+			if (match) {
 				matching_refs = i;
 				break;
 			}
@@ -1177,6 +1184,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
 	struct refspec *rs;
 	int send_all = flags & MATCH_REFS_ALL;
 	int send_mirror = flags & MATCH_REFS_MIRROR;
+	int send_prune = flags & MATCH_REFS_PRUNE;
 	int errs;
 	static const char *default_refspec[] = { ":", NULL };
 	struct ref *ref, **dst_tail = tail_ref(dst);
@@ -1197,7 +1205,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
 		if (ref->peer_ref)
 			continue;
 
-		dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, &pat);
+		dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
 		if (!dst_name)
 			continue;
 
@@ -1224,6 +1232,23 @@ int match_push_refs(struct ref *src, struct ref **dst,
 	free_name:
 		free(dst_name);
 	}
+	if (send_prune) {
+		/* check for missing refs on the remote */
+		for (ref = *dst; ref; ref = ref->next) {
+			char *src_name;
+
+			if (ref->peer_ref)
+				/* We're already sending something to this ref. */
+				continue;
+
+			src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
+			if (src_name) {
+				if (!find_ref_by_name(src, src_name))
+					ref->peer_ref = alloc_delete_ref();
+				free(src_name);
+			}
+		}
+	}
 	if (errs)
 		return -1;
 	return 0;
diff --git a/remote.h b/remote.h
index b395598..341142c 100644
--- a/remote.h
+++ b/remote.h
@@ -145,7 +145,8 @@ int branch_merge_matches(struct branch *, int n, const char *);
 enum match_refs_flags {
 	MATCH_REFS_NONE		= 0,
 	MATCH_REFS_ALL 		= (1 << 0),
-	MATCH_REFS_MIRROR	= (1 << 1)
+	MATCH_REFS_MIRROR	= (1 << 1),
+	MATCH_REFS_PRUNE	= (1 << 2),
 };
 
 /* Reporting of tracking info */
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index b69cf57..b5417cc 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -979,4 +979,20 @@ test_expect_success 'push --porcelain --dry-run rejected' '
 	test_cmp .git/foo .git/bar
 '
 
+test_expect_success 'push --prune' '
+	mk_test heads/master heads/second heads/foo heads/bar &&
+	git push --prune testrepo &&
+	check_push_result $the_commit heads/master &&
+	check_push_result $the_first_commit heads/second &&
+	! check_push_result $the_first_commit heads/foo heads/bar
+'
+
+test_expect_success 'push --prune refspec' '
+	mk_test tmp/master tmp/second tmp/foo tmp/bar &&
+	git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
+	check_push_result $the_commit tmp/master &&
+	check_push_result $the_first_commit tmp/second &&
+	! check_push_result $the_first_commit tmp/foo tmp/bar
+'
+
 test_done
diff --git a/transport.c b/transport.c
index cac0c06..c20267c 100644
--- a/transport.c
+++ b/transport.c
@@ -1028,6 +1028,8 @@ int transport_push(struct transport *transport,
 			match_flags |= MATCH_REFS_ALL;
 		if (flags & TRANSPORT_PUSH_MIRROR)
 			match_flags |= MATCH_REFS_MIRROR;
+		if (flags & TRANSPORT_PUSH_PRUNE)
+			match_flags |= MATCH_REFS_PRUNE;
 
 		if (match_push_refs(local_refs, &remote_refs,
 				    refspec_nr, refspec, match_flags)) {
diff --git a/transport.h b/transport.h
index 059b330..ce99ef8 100644
--- a/transport.h
+++ b/transport.h
@@ -102,6 +102,7 @@ struct transport {
 #define TRANSPORT_PUSH_PORCELAIN 16
 #define TRANSPORT_PUSH_SET_UPSTREAM 32
 #define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
+#define TRANSPORT_PUSH_PRUNE 128
 
 #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
-- 
1.7.9.1

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

* Re: [PATCH 4/4] push: add 'prune' option
  2012-02-22 22:43 ` [PATCH 4/4] push: add 'prune' option Felipe Contreras
@ 2012-02-23  0:42   ` Junio C Hamano
  2012-02-23  1:09     ` Felipe Contreras
  0 siblings, 1 reply; 9+ messages in thread
From: Junio C Hamano @ 2012-02-23  0:42 UTC (permalink / raw)
  To: Felipe Contreras; +Cc: git, Jeff King

Felipe Contreras <felipe.contreras@gmail.com> writes:

> +--prune::
> +	Remove remote branches that don't have a local counterpart. For example
> +	a remote branch `tmp` will be removed if a local branch with the same
> +	name doesn't exist any more. This also respects refspecs, e.g.
> +	`refs/heads/*:refs/tmp/*` would make sure that remote `refs/tmp/foo`
> +	will be removed if `refs/heads/foo` doesn't exist.

I do not think it adds much useful information to mention `tmp` only once
over what is already said by the first sentence.  Also, the first sentence
of the example does not make it clear that it is assuming a same-for-same
mapping.

Coming up with a precise technical description is easy, but it is hard to
explain to the end user in easy terms, and I commend you for attempting to
add an example in a short single sentence, though.

Perhaps spelling out the underlying assumption the example makes is the
best we could do here without going too technical.

        ... For example, if you are pushing all your local branches to
        update the local branches of the remote, `tmp` branch will be
        removed from the remote if you removed your `tmp` branch locally.

        If you are pushing all your local branches on your laptop to a
        repository on your desktop machine under `refs/remotes/laptop/`
        hierarchy to back them up, `refs/remotes/laptop/tmp` is removed
        from the remote if you no longer have the branch `tmp` on your
        laptop.


Will queue with a slight fix-ups, including this bit:

> diff --git a/remote.h b/remote.h
> index b395598..341142c 100644
> --- a/remote.h
> +++ b/remote.h
> @@ -145,7 +145,8 @@ int branch_merge_matches(struct branch *, int n, const char *);
>  enum match_refs_flags {
>  	MATCH_REFS_NONE		= 0,
>  	MATCH_REFS_ALL 		= (1 << 0),
> -	MATCH_REFS_MIRROR	= (1 << 1)
> +	MATCH_REFS_MIRROR	= (1 << 1),
> +	MATCH_REFS_PRUNE	= (1 << 2),
>  };

Lose the ',' at the end, for the same reason why deleted line did not have
one.

Thanks.

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

* Re: [PATCH 4/4] push: add 'prune' option
  2012-02-23  0:42   ` Junio C Hamano
@ 2012-02-23  1:09     ` Felipe Contreras
  2012-02-23  1:31       ` Junio C Hamano
  0 siblings, 1 reply; 9+ messages in thread
From: Felipe Contreras @ 2012-02-23  1:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

On Thu, Feb 23, 2012 at 2:42 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
>> +--prune::
>> +     Remove remote branches that don't have a local counterpart. For example
>> +     a remote branch `tmp` will be removed if a local branch with the same
>> +     name doesn't exist any more. This also respects refspecs, e.g.
>> +     `refs/heads/*:refs/tmp/*` would make sure that remote `refs/tmp/foo`
>> +     will be removed if `refs/heads/foo` doesn't exist.
>
> I do not think it adds much useful information to mention `tmp` only once
> over what is already said by the first sentence.  Also, the first sentence
> of the example does not make it clear that it is assuming a same-for-same
> mapping.

Sure, the first sentence doesn't make it clear, but it would be a
valid and obvious assumption. The second sentence makes it clear, and
the name `tmp` immediately evokes a branch that will probably be
removed.

> Coming up with a precise technical description is easy, but it is hard to
> explain to the end user in easy terms, and I commend you for attempting to
> add an example in a short single sentence, though.
>
> Perhaps spelling out the underlying assumption the example makes is the
> best we could do here without going too technical.
>
>        ... For example, if you are pushing all your local branches to
>        update the local branches of the remote,

Yeah, but that's 'git push --all', and that's not the common
operation--'git push' is. So that's what I presumed the reader would
assume, and it really doesn't make a difference as to what will
follow:

>        `tmp` branch will be
>        removed from the remote if you removed your `tmp` branch locally.

This reuses the name `tmp`, which seems to be your objective, but it
doesn't explain _why_ it would remove `tmp`; is it because `tmp` is
the upstream branch, or is it because it has the same name?

>        If you are pushing all your local branches on your laptop to a
>        repository on your desktop machine under `refs/remotes/laptop/`
>        hierarchy to back them up, `refs/remotes/laptop/tmp` is removed
>        from the remote if you no longer have the branch `tmp` on your
>        laptop.

Unfortunately, I as a reader have trouble understanding this. More
specifically I have trouble understanding where `refs/remotes/laptop/`
is coming from, and what it is meaning. I have always pictured
`refs/remotes` as something that 'git fetch' updates, and always from
the relevant repository. While eventually I could understand what this
thing is doing, it took me more than one read, and I had to read
slowly, and even then it seems completely non-standard to me.

I think a synthetic example, like `refs/heads/*:refs/tmp/*`, is much
easier to understand because it doesn't mess up with any established
refs, and also has the advantage that it shows the relevant refspec,
which is useful for people that are not familiar with refspecs, and in
fact, people could try it out without messing with their current refs.

>> diff --git a/remote.h b/remote.h
>> index b395598..341142c 100644
>> --- a/remote.h
>> +++ b/remote.h
>> @@ -145,7 +145,8 @@ int branch_merge_matches(struct branch *, int n, const char *);
>>  enum match_refs_flags {
>>       MATCH_REFS_NONE         = 0,
>>       MATCH_REFS_ALL          = (1 << 0),
>> -     MATCH_REFS_MIRROR       = (1 << 1)
>> +     MATCH_REFS_MIRROR       = (1 << 1),
>> +     MATCH_REFS_PRUNE        = (1 << 2),
>>  };
>
> Lose the ',' at the end, for the same reason why deleted line did not have
> one.

And why is that? Isn't git c99? That comma would only ensure that the
next patch that touches this would be two lines instead of one.

Cheers.

-- 
Felipe Contreras

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

* Re: [PATCH 4/4] push: add 'prune' option
  2012-02-23  1:09     ` Felipe Contreras
@ 2012-02-23  1:31       ` Junio C Hamano
  2012-02-23  2:30         ` Felipe Contreras
  0 siblings, 1 reply; 9+ messages in thread
From: Junio C Hamano @ 2012-02-23  1:31 UTC (permalink / raw)
  To: Felipe Contreras; +Cc: git, Jeff King

Felipe Contreras <felipe.contreras@gmail.com> writes:

> On Thu, Feb 23, 2012 at 2:42 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Yeah, but that's 'git push --all', and that's not the common
> operation--'git push' is.

"git push" is common but that does not give you a solid base to guess what
the reader's assumption would be.  Are you assuming "matching" semantics?

> So that's what I presumed the reader would
> assume,...

I do not want let us guess what the reader assumes, as many people seem to
suggest setting push.default to different values and that would change
what the reader would assume.  That was the whole reason that I suggested
to spell the assumption out, so that the reader's assumption does not have
to get into the picture.

> This reuses the name `tmp`, which seems to be your objective, but it
> doesn't explain _why_ it would remove `tmp`; is it because `tmp` is
> the upstream branch, or is it because it has the same name?

The example is to clarify "local counterpart" in the main text.  I
actually would prefer to get rid of `tmp` but I left it as-is as you
wrote.  The exact name used in the example does not matter, whether it is
`tmp` or `xyzzy`.

> Unfortunately, I as a reader have trouble understanding this. More
> specifically I have trouble understanding where `refs/remotes/laptop/`
> is coming from, and what it is meaning. I have always pictured
> `refs/remotes` as something that 'git fetch' updates, and always from
> the relevant repository.

The layout is the recommended set-up to emulate a fetch with a push in the
reverse direction, which I thought anybody should notice.  It is a failure
in our documentation that even an expert didn't.

>>> diff --git a/remote.h b/remote.h
>>> index b395598..341142c 100644
>>> --- a/remote.h
>>> +++ b/remote.h
>>> @@ -145,7 +145,8 @@ int branch_merge_matches(struct branch *, int n, const char *);
>>>  enum match_refs_flags {
>>>       MATCH_REFS_NONE         = 0,
>>>       MATCH_REFS_ALL          = (1 << 0),
>>> -     MATCH_REFS_MIRROR       = (1 << 1)
>>> +     MATCH_REFS_MIRROR       = (1 << 1),
>>> +     MATCH_REFS_PRUNE        = (1 << 2),
>>>  };
>>
>> Lose the ',' at the end, for the same reason why deleted line did not have
>> one.
>
> And why is that?

Because I told you so ;-).

More seriously, we have had patches to accomodate other people's compilers
by dropping the last comma in enum {}.  See c9b6782 (enums: omit trailing
comma for portability, 2011-03-16), 4b05548 (enums: omit trailing comma
for portability, 2010-05-14) for examples.

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

* Re: [PATCH 4/4] push: add 'prune' option
  2012-02-23  1:31       ` Junio C Hamano
@ 2012-02-23  2:30         ` Felipe Contreras
  0 siblings, 0 replies; 9+ messages in thread
From: Felipe Contreras @ 2012-02-23  2:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

On Thu, Feb 23, 2012 at 3:31 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
>> On Thu, Feb 23, 2012 at 2:42 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Yeah, but that's 'git push --all', and that's not the common
>> operation--'git push' is.
>
> "git push" is common but that does not give you a solid base to guess what
> the reader's assumption would be.  Are you assuming "matching" semantics?

But if you want to spell the assumption, why not spell the most common one?

>> So that's what I presumed the reader would
>> assume,...
>
> I do not want let us guess what the reader assumes, as many people seem to
> suggest setting push.default to different values and that would change
> what the reader would assume.

No, it wouldn't. Almost all readers are familiar about what 'git push'
does by default--I would even adventure to say all--the ones that set
a custom push.default would do it because they _know_ they don't want
the default.

And in fact, as I said, it doesn't matter what the reader has set in
push.default, or if the reader is assuming 'git push --all', or 'git
push :', or 'git push --tags', or 'git push refs/heads/*', the end
result is the same.

> That was the whole reason that I suggested
> to spell the assumption out, so that the reader's assumption does not have
> to get into the picture.

I don't think it matters what the reader assumes, because the end
result is the same, so there's no reason to spell any assumption.

>> This reuses the name `tmp`, which seems to be your objective, but it
>> doesn't explain _why_ it would remove `tmp`; is it because `tmp` is
>> the upstream branch, or is it because it has the same name?
>
> The example is to clarify "local counterpart" in the main text.  I
> actually would prefer to get rid of `tmp` but I left it as-is as you
> wrote.  The exact name used in the example does not matter, whether it is
> `tmp` or `xyzzy`.

I believe `tmp` is more useful than `xyzzy`, or even more than
`master`. I already explained why.

>> Unfortunately, I as a reader have trouble understanding this. More
>> specifically I have trouble understanding where `refs/remotes/laptop/`
>> is coming from, and what it is meaning. I have always pictured
>> `refs/remotes` as something that 'git fetch' updates, and always from
>> the relevant repository.
>
> The layout is the recommended set-up to emulate a fetch with a push in the
> reverse direction, which I thought anybody should notice.  It is a failure
> in our documentation that even an expert didn't.

The problem is not figuring out the `refs/remotes/foo`, but picturing
the fact that there are two repositories, the user ran on one of them
'git remote add laptop', and then 'git remote add pc', and then 'git
push --prune refs/heads*:refs/remotes/laptop/*'. Lots of things to
keep in mind for this paragraph. And, as you say, it's the reverse
direction of a fetch, so it's immediately weird.

Moreover, I find it strange that you want to rely on the assumption
that the reader is familiar with the pattern `refs/remotes/foo`, and
the refspec syntax, which is pretty deep plumbing, and not on the
default action of 'git push', which is high-level porcelain, and as I
said, not even needed to understand my example.

>>>> diff --git a/remote.h b/remote.h
>>>> index b395598..341142c 100644
>>>> --- a/remote.h
>>>> +++ b/remote.h
>>>> @@ -145,7 +145,8 @@ int branch_merge_matches(struct branch *, int n, const char *);
>>>>  enum match_refs_flags {
>>>>       MATCH_REFS_NONE         = 0,
>>>>       MATCH_REFS_ALL          = (1 << 0),
>>>> -     MATCH_REFS_MIRROR       = (1 << 1)
>>>> +     MATCH_REFS_MIRROR       = (1 << 1),
>>>> +     MATCH_REFS_PRUNE        = (1 << 2),
>>>>  };
>>>
>>> Lose the ',' at the end, for the same reason why deleted line did not have
>>> one.
>>
>> And why is that?
>
> Because I told you so ;-).

You didn't tell me anything about the previous line :)

> More seriously, we have had patches to accomodate other people's compilers
> by dropping the last comma in enum {}.  See c9b6782 (enums: omit trailing
> comma for portability, 2011-03-16), 4b05548 (enums: omit trailing comma
> for portability, 2010-05-14) for examples.

Yeah, I remembered those patches after sending the mail. Shame. Maybe
next decade =/

Cheers.
-- 
Felipe Contreras

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

end of thread, other threads:[~2012-02-23  2:30 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-02-22 22:43 [PATCH 0/4] push: add 'prune' option Felipe Contreras
2012-02-22 22:43 ` [PATCH 1/4] remote: use a local variable in match_push_refs() Felipe Contreras
2012-02-22 22:43 ` [PATCH 2/4] remote: reorganize check_pattern_match() Felipe Contreras
2012-02-22 22:43 ` [PATCH 3/4] remote: refactor code into alloc_delete_ref() Felipe Contreras
2012-02-22 22:43 ` [PATCH 4/4] push: add 'prune' option Felipe Contreras
2012-02-23  0:42   ` Junio C Hamano
2012-02-23  1:09     ` Felipe Contreras
2012-02-23  1:31       ` Junio C Hamano
2012-02-23  2:30         ` Felipe Contreras

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).