git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [RFC/PATCH] git-branch: default to --track
@ 2007-07-06 21:54 Johannes Schindelin
  2007-07-08  8:59 ` Junio C Hamano
  0 siblings, 1 reply; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-06 21:54 UTC (permalink / raw
  To: git, gitster


"git branch --track" will setup config variables when branching from 
a remote branch, so that if you say "git pull" while being on that
branch, it automatically fetches the correct remote, and merges the
correct branch.

Often people complain that this is not the default for "git branch". 
Make it so.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>

---

	With 1.5.3 knocking at the door, maybe it is too late to include
	this. However, I am in favour of changing the default behaviour
	here.

 builtin-branch.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index ae450b0..507b47c 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -22,7 +22,7 @@ static const char builtin_branch_usage[] =
 static const char *head;
 static unsigned char head_sha1[20];
 
-static int branch_track_remotes;
+static int branch_track_remotes = 1;
 
 static int branch_use_color;
 static char branch_colors[][COLOR_MAXLEN] = {

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

* Re: [RFC/PATCH] git-branch: default to --track
  2007-07-06 21:54 [RFC/PATCH] git-branch: default to --track Johannes Schindelin
@ 2007-07-08  8:59 ` Junio C Hamano
  2007-07-08 12:41   ` [PATCH] branch.autosetupmerge: allow boolean values, or "all" Johannes Schindelin
  0 siblings, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2007-07-08  8:59 UTC (permalink / raw
  To: Johannes Schindelin; +Cc: git, Paolo Bonzini

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> "git branch --track" will setup config variables when branching from 
> a remote branch, so that if you say "git pull" while being on that
> branch, it automatically fetches the correct remote, and merges the
> correct branch.

While I think it would have been the right thing to do if the
code did this only for a remote branch, I think there is a bug
somewhere.  I just saw this:

	... some random changes ...
        master$ git commit -a -s -m 'Some work meant for topic.'
        master$ git branch jc/new-topic
	Branch jc/new-topic set up to track local branch refs/heads/master

Eh?  I did not want this to get applied for my local branches.

The intention of the above command sequence was to do a branch
and then "reset --hard HEAD^" to rewind the 'master', as if I
did not commit but instead did "checkout -b jc/new-topic &&
commit && checkout master".

But "checkout -b jc/newtopic" has the same problem, as it
eventually uses the same "git-branch" that defaults to --track
even for a case where I branch off of a local branch.

I do not necessarily think the command line --track is broken.
If the user explicitly says a branch tracks a local branch, so
be it.  If --track comes from autosetupmerge or built-in default
like your patch, however, I do not think it makes much sense to
pollute the config file with useless "tracking" information.

I am very tempted to revert this, but won't do so tonight, yet.

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

* [PATCH] branch.autosetupmerge: allow boolean values, or "all"
  2007-07-08  8:59 ` Junio C Hamano
@ 2007-07-08 12:41   ` Johannes Schindelin
  2007-07-08 18:41     ` Junio C Hamano
  0 siblings, 1 reply; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-08 12:41 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Paolo Bonzini


Junio noticed that switching on autosetupmerge unilaterally started
cluttering the config for local branches.  That is not the original
intention of branch.autosetupmerge, which was meant purely for
convenience when branching off of remote branches, but that semantics
got lost somewhere.

If you still want that "new" behavior, you can switch
branch.autosetupmerge to the value "all".  Otherwise, it is interpreted
as a boolean, which triggers setting up defaults _only_ when branching
off of a remote branch, i.e. the originally intended behavior.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	On Sun, 8 Jul 2007, Junio C Hamano wrote:

	> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
	> 
	> > "git branch --track" will setup config variables when 
	> > branching from a remote branch, so that if you say "git pull" 
	> > while being on that branch, it automatically fetches the 
	> > correct remote, and merges the correct branch.
	> 
	> While I think it would have been the right thing to do if the
	> code did this only for a remote branch, I think there is a bug
	> somewhere.  I just saw this:
	> 
	> 	... some random changes ...
	>         master$ git commit -a -s -m 'Some work meant for topic.'
	>         master$ git branch jc/new-topic
	> 	Branch jc/new-topic set up to track local branch refs/heads/master
	> 
	> Eh?  I did not want this to get applied for my local branches.

	That is certainly unexpected and unwelcomed.  Alas, I think it is 
	one of the consequences of rarely executed (and thus, tested) 
	code.

	I rarely branch, but use one long running branch to commit and 
	revert, so that a "git log -S<keyword> -p" brings me back my huge 
	debug output changes, and therefore I did not catch it.

	> I do not necessarily think the command line --track is broken.

	Me, neither.  Therefore, this patch does not change the semantics 
	of that one.  But it was really unexpected for me to see that this 
	works with anything but remote branches.

	> I am very tempted to revert this, but won't do so tonight, yet.

	Well, I marked it as RFC, and was surprised to wake up to it being 
	applied.  But thanks for not reverting right away; I think this 
	patch should fix the issue.

 builtin-branch.c  |   18 ++++++++++++------
 t/t3200-branch.sh |    9 +++++++++
 2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 507b47c..49195a1 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -22,7 +22,7 @@ static const char builtin_branch_usage[] =
 static const char *head;
 static unsigned char head_sha1[20];
 
-static int branch_track_remotes = 1;
+static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */
 
 static int branch_use_color;
 static char branch_colors[][COLOR_MAXLEN] = {
@@ -66,8 +66,12 @@ static int git_branch_config(const char *var, const char *value)
 		color_parse(value, var, branch_colors[slot]);
 		return 0;
 	}
-	if (!strcmp(var, "branch.autosetupmerge"))
-		branch_track_remotes = git_config_bool(var, value);
+	if (!strcmp(var, "branch.autosetupmerge")) {
+		if (!strcmp(value, "all"))
+			branch_track = 2;
+		else
+			branch_track = git_config_bool(var, value);
+	}
 
 	return git_default_config(var, value);
 }
@@ -525,7 +529,9 @@ static void create_branch(const char *name, const char *start_name,
 	/* When branching off a remote branch, set up so that git-pull
 	   automatically merges from there.  So far, this is only done for
 	   remotes registered via .git/config.  */
-	if (real_ref && track)
+	if (real_ref && (track == 2 ||
+				(track == 1 &&
+				 !prefixcmp(real_ref, "refs/remotes/"))))
 		set_branch_defaults(name, real_ref);
 
 	if (write_ref_sha1(lock, sha1, msg) < 0)
@@ -586,7 +592,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	int i;
 
 	git_config(git_branch_config);
-	track = branch_track_remotes;
+	track = branch_track;
 
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
@@ -598,7 +604,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			break;
 		}
 		if (!strcmp(arg, "--track")) {
-			track = 1;
+			track = 2;
 			continue;
 		}
 		if (!strcmp(arg, "--no-track")) {
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index c6f472a..a19e961 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -148,6 +148,15 @@ test_expect_success 'test tracking setup via config' \
      test $(git config branch.my3.remote) = local &&
      test $(git config branch.my3.merge) = refs/heads/master'
 
+test_expect_success 'autosetupmerge = all' '
+	git config branch.autosetupmerge true &&
+	git branch all1 master &&
+	test -z "$(git config branch.all1.merge)" &&
+	git config branch.autosetupmerge all &&
+	git branch all2 master &&
+	test $(git config branch.all2.merge) = refs/heads/master
+'
+
 test_expect_success 'test overriding tracking setup via --no-track' \
     'git config branch.autosetupmerge true &&
      git config remote.local.url . &&
-- 
1.5.3.rc0.2742.g2050

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

* Re: [PATCH] branch.autosetupmerge: allow boolean values, or "all"
  2007-07-08 12:41   ` [PATCH] branch.autosetupmerge: allow boolean values, or "all" Johannes Schindelin
@ 2007-07-08 18:41     ` Junio C Hamano
  2007-07-08 19:15       ` Johannes Schindelin
  2007-07-09  1:59       ` Paolo Bonzini
  0 siblings, 2 replies; 23+ messages in thread
From: Junio C Hamano @ 2007-07-08 18:41 UTC (permalink / raw
  To: Johannes Schindelin; +Cc: git, Paolo Bonzini

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> 	> Eh?  I did not want this to get applied for my local branches.
>
> 	That is certainly unexpected and unwelcomed.  Alas, I think it is 
> 	one of the consequences of rarely executed (and thus, tested) 
> 	code.
> ...
> +test_expect_success 'autosetupmerge = all' '
> +	git config branch.autosetupmerge true &&
> +	git branch all1 master &&
> +	test -z "$(git config branch.all1.merge)" &&
> +	git config branch.autosetupmerge all &&
> +	git branch all2 master &&
> +	test $(git config branch.all2.merge) = refs/heads/master
> +'

Thanks.

Having prepared the patch below, I do not think if the original
patch even wanted to have 'all' semantics.  The surrounding text
only talks about "off a remote branch" and I strongly suspect
that nobody wanted to do this for a local branch case at all.



diff --git a/Documentation/config.txt b/Documentation/config.txt
index 4b67f0a..aeece84 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -309,7 +309,10 @@ branch.autosetupmerge::
 	so that gitlink:git-pull[1] will appropriately merge from that
 	remote branch.  Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
-	and `--no-track` options.  This option defaults to false.
+	and `--no-track` options.  This option can have values
+	'false' (never touch the configuration), 'all' (do this
+	for all branches), or 'true' (do this only when
+	branching from a remote tracking branch), and defaults to 'true'.
 
 branch.<name>.remote::
 	When in branch <name>, it tells `git fetch` which remote to fetch.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 818b720..8292952 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -52,8 +52,9 @@ OPTIONS
 	set up configuration so that git-pull will automatically
 	retrieve data from the remote branch.  Set the
 	branch.autosetupmerge configuration variable to true if you
-	want git-checkout and git-branch to always behave as if
-	'--track' were given.
+	want git-checkout and git-branch to behave as if
+	'--track' were given when you branch from a remote
+	tracking branch.
 
 --no-track::
 	When -b is given and a branch is created off a remote branch,

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

* Re: [PATCH] branch.autosetupmerge: allow boolean values, or "all"
  2007-07-08 18:41     ` Junio C Hamano
@ 2007-07-08 19:15       ` Johannes Schindelin
  2007-07-09  1:59       ` Paolo Bonzini
  1 sibling, 0 replies; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-08 19:15 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Paolo Bonzini

Hi,

On Sun, 8 Jul 2007, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > 	> Eh?  I did not want this to get applied for my local branches.
> >
> > 	That is certainly unexpected and unwelcomed.  Alas, I think it is 
> > 	one of the consequences of rarely executed (and thus, tested) 
> > 	code.
> > ...
> > +test_expect_success 'autosetupmerge = all' '
> > +	git config branch.autosetupmerge true &&
> > +	git branch all1 master &&
> > +	test -z "$(git config branch.all1.merge)" &&
> > +	git config branch.autosetupmerge all &&
> > +	git branch all2 master &&
> > +	test $(git config branch.all2.merge) = refs/heads/master
> > +'
> 
> Thanks.
> 
> Having prepared the patch below, I do not think if the original
> patch even wanted to have 'all' semantics.  The surrounding text
> only talks about "off a remote branch" and I strongly suspect
> that nobody wanted to do this for a local branch case at all.

I remember that the comment was correct for the first few versions.  
Somehow I missed that change in semantics.  Paolo, what was the rationale?

Ciao,
Dscho

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

* Re: [PATCH] branch.autosetupmerge: allow boolean values, or "all"
  2007-07-08 18:41     ` Junio C Hamano
  2007-07-08 19:15       ` Johannes Schindelin
@ 2007-07-09  1:59       ` Paolo Bonzini
  2007-07-09  2:27         ` Junio C Hamano
  2007-07-09 11:28         ` [PATCH] branch.autosetupmerge: allow boolean values, or "all" Johannes Schindelin
  1 sibling, 2 replies; 23+ messages in thread
From: Paolo Bonzini @ 2007-07-09  1:59 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Johannes Schindelin, git

	> Having prepared the patch below, I do not think if the original
> patch even wanted to have 'all' semantics.  The surrounding text
> only talks about "off a remote branch" and I strongly suspect
> that nobody wanted to do this for a local branch case at all.

If I remember correctly, the problem was that you are not sure that
remote branches are in refs/remotes.

Paolo

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

* Re: [PATCH] branch.autosetupmerge: allow boolean values, or "all"
  2007-07-09  1:59       ` Paolo Bonzini
@ 2007-07-09  2:27         ` Junio C Hamano
  2007-07-09 11:35           ` [PATCH] branch --track: code cleanup and saner handling of local branches Johannes Schindelin
  2007-07-09 11:28         ` [PATCH] branch.autosetupmerge: allow boolean values, or "all" Johannes Schindelin
  1 sibling, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2007-07-09  2:27 UTC (permalink / raw
  To: Paolo Bonzini; +Cc: Johannes Schindelin, git

Paolo Bonzini <bonzini@gnu.org> writes:

> 	> Having prepared the patch below, I do not think if the original
>> patch even wanted to have 'all' semantics.  The surrounding text
>> only talks about "off a remote branch" and I strongly suspect
>> that nobody wanted to do this for a local branch case at all.
>
> If I remember correctly, the problem was that you are not sure that
> remote branches are in refs/remotes.

Yes, the user can use traditional layout (e.g. refs/heads/origin
is used as a remote tracking branch).

So the check with refs/remotes/ is not technically correct, but
it should probably look-up the configuration to check the
tracking, if we really want to be strict about it.

I personally do not care too much about it, though.

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

* Re: [PATCH] branch.autosetupmerge: allow boolean values, or "all"
  2007-07-09  1:59       ` Paolo Bonzini
  2007-07-09  2:27         ` Junio C Hamano
@ 2007-07-09 11:28         ` Johannes Schindelin
  1 sibling, 0 replies; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-09 11:28 UTC (permalink / raw
  To: Paolo Bonzini; +Cc: Junio C Hamano, git

Hi,

On Sun, 8 Jul 2007, Paolo Bonzini wrote:

> [Paolo tried to hide the fact that it was Junio who wrote this:]
>
> > Having prepared the patch below, I do not think if the original patch 
> > even wanted to have 'all' semantics.  The surrounding text only talks 
> > about "off a remote branch" and I strongly suspect that nobody wanted 
> > to do this for a local branch case at all.
> 
> If I remember correctly, the problem was that you are not sure that 
> remote branches are in refs/remotes.

Then you code is incorrect.

Basically, you use a confusing set of four functions to do the following:

- read the config, and
- write the branch.<name>.{remote,merge} variables

Two functions would have been sufficient, and easier to read.  And as I 
fully expect with non-simple code, a bug was lurking.  This time in 
set_branch_defaults():

you check if neither config_repo nor config_remote (which is a misnomer, 
as it does not contain a "remote", but a "remote branch") is set.  But 
that happens when there was no information in the config, too!

Also you miss the case that there is ambiguous information:

[remote "hello"]
	url = git://blub/x.git
	fetch = refs/heads/master:refs/heads/origin

[remote "bello"]
	url = git://yaddayadda/x.git
	fetch = refs/heads/master:refs/heads/origin

See? Your code just uses "bello".

Will send out a fix shortly.

Ciao,
Dscho

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

* [PATCH] branch --track: code cleanup and saner handling of local branches
  2007-07-09  2:27         ` Junio C Hamano
@ 2007-07-09 11:35           ` Johannes Schindelin
  2007-07-09 21:05             ` Junio C Hamano
  0 siblings, 1 reply; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-09 11:35 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Paolo Bonzini, git


This patch cleans up some complicated code, and replaces it with a
cleaner version.  This also enables us to fix two cases:

The earlier "fix" to setup tracking only when the original ref started
with "refs/remotes" is wrong.  You are absolutely allowed to use a
separate layout for your tracking branches.  The correct fix, of course,
is to set up tracking information only when there is a matching
remote.<nick>.fetch line containing a colon.

Another corner case was not handled properly.  If two remotes write to
the original ref, just warn the user and do not set up tracking.

Also, the "branch name too long" condition had an off-by-one.  Not that it 
matters in real life.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	On Sun, 8 Jul 2007, Junio C Hamano wrote:

	> Paolo Bonzini <bonzini@gnu.org> writes:
	> 
	> >> Having prepared the patch below, I do not think if the 
	> >> original patch even wanted to have 'all' semantics.  The 
	> >> surrounding text only talks about "off a remote branch" and I 
	> >> strongly suspect that nobody wanted to do this for a local 
	> >> branch case at all.
	> >
	> > If I remember correctly, the problem was that you are not sure 
	> > that remote branches are in refs/remotes.
	> 
	> Yes, the user can use traditional layout (e.g. refs/heads/origin
	> is used as a remote tracking branch).
	> 
	> So the check with refs/remotes/ is not technically correct, but
	> it should probably look-up the configuration to check the
	> tracking, if we really want to be strict about it.

	Okay, so here is the correct fix.  In the process, I rewrote large 
	parts of it.  I really did not like the asnprintf() parts of the 
	original part, and I am not comfortable with it anyway, so that is 
	replaced, too.

	BTW if someone wonders why the wildcards look so strange in the 
	comment: after the second compilation I got sick of the "warning: 
	/* contained within comment", and wanted to spare everybody.

	Ah yes, I tried to prepare this patch with "format-patch -B", but 
	it seems to be too much in love with the few lines that match. 

 builtin-branch.c  |  196 ++++++++++++++++++++++++-----------------------------
 t/t3200-branch.sh |   21 +++---
 2 files changed, 99 insertions(+), 118 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 49195a1..d290a7a 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -22,7 +22,7 @@ static const char builtin_branch_usage[] =
 static const char *head;
 static unsigned char head_sha1[20];
 
-static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */
+static int branch_track = 1;
 
 static int branch_use_color;
 static char branch_colors[][COLOR_MAXLEN] = {
@@ -66,12 +66,8 @@ static int git_branch_config(const char *var, const char *value)
 		color_parse(value, var, branch_colors[slot]);
 		return 0;
 	}
-	if (!strcmp(var, "branch.autosetupmerge")) {
-		if (!strcmp(value, "all"))
-			branch_track = 2;
-		else
+	if (!strcmp(var, "branch.autosetupmerge"))
 			branch_track = git_config_bool(var, value);
-	}
 
 	return git_default_config(var, value);
 }
@@ -349,125 +345,111 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,
 	free_ref_list(&ref_list);
 }
 
-static char *config_repo;
-static char *config_remote;
-static const char *start_ref;
+static struct {
+	const char *ref;
+	int ref_len;
+	char *remote;
+	char *merge;
+	int matches;
+} tracking;
 
-static int get_remote_branch_name(const char *value)
+static int tracking_config(const char *key, const char *value)
 {
 	const char *colon;
-	const char *end;
-
-	if (*value == '+')
-		value++;
+	int key_len, value_len, match_len;
+	char *merge = NULL;
 
-	colon = strchr(value, ':');
-	if (!colon)
+	/* we want: remote.<nick>.fetch = <merge>:<ref> */
+	if (prefixcmp(key, "remote.") || (key_len = strlen(key)) < 6 ||
+			strcmp(key + key_len - 6, ".fetch") ||
+			!(colon = strchr(value, ':')))
 		return 0;
 
-	end = value + strlen(value);
+	if (*value == '+')
+		value++;
 
 	/*
-	 * Try an exact match first.  I.e. handle the case where the
-	 * value is "$anything:refs/foo/bar/baz" and start_ref is exactly
-	 * "refs/foo/bar/baz". Then the name at the remote is $anything.
+	 * A remote.<name>.fetch value can have two forms:
+	 *
+	 * - exact:
+	 *
+	 *	refs/heads/gnu:refs/heads/my-upstream
+	 *
+	 * - wildcard:
+	 *
+	 *	refs/heads/ *:refs/remotes/gnu/ *
+	 *
+	 * try exact match first:
 	 */
-	if (!strcmp(colon + 1, start_ref)) {
+	if (!strcmp(colon + 1, tracking.ref))
 		/* Truncate the value before the colon. */
-		nfasprintf(&config_repo, "%.*s", colon - value, value);
-		return 1;
+		merge = xstrndup(value, colon - value);
+
+	/* wildcard; match_len is the length of the matching prefix */
+	else if ((value_len = strlen(value)) > tracking.ref_len &&
+			!strcmp(value + value_len - 2, "/*") &&
+			(match_len = value_len - (colon - value) - 2) > 0 &&
+			match_len < tracking.ref_len &&
+			!memcmp(colon + 1, tracking.ref, match_len)) {
+		int postfix_len = tracking.ref_len - match_len;
+		int replace_len = colon - 1 - value;
+		merge = xmalloc(replace_len + postfix_len);
+		memcpy(merge, value, replace_len);
+		memcpy(merge + replace_len,
+				tracking.ref + match_len, postfix_len);
 	}
 
-	/*
-	 * Is this a wildcard match?
-	 */
-	if ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||
-	    (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')
-		return 0;
-
-	/*
-	 * Value is "refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>"
-	 * and start_ref begins with "refs/baz/boa/"; the name at the
-	 * remote is refs/foo/bar/ with the remaining part of the
-	 * start_ref.  The length of the prefix on the RHS is (end -
-	 * colon - 2), including the slash immediately before the
-	 * asterisk.
-	 */
-	if ((strlen(start_ref) < end - colon - 2) ||
-	    memcmp(start_ref, colon + 1, end - colon - 2))
-		return 0; /* does not match prefix */
-
-	/* Replace the asterisk with the remote branch name.  */
-	nfasprintf(&config_repo, "%.*s%s",
-		   (colon - 1) - value, value,
-		   start_ref + (end - colon - 2));
-	return 1;
-}
-
-static int get_remote_config(const char *key, const char *value)
-{
-	const char *var;
-	if (prefixcmp(key, "remote."))
+	if (!merge)
 		return 0;
 
-	var = strrchr(key, '.');
-	if (var == key + 6 || strcmp(var, ".fetch"))
-		return 0;
-	/*
-	 * Ok, we are looking at key == "remote.$foo.fetch";
-	 */
-	if (get_remote_branch_name(value))
-		nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
+	tracking.matches++;
+	if (tracking.merge)
+		free(merge);
+	else {
+		tracking.merge = merge;
+		tracking.remote = xstrndup(key + 7, key_len - 7 - 6);
+	}
 
 	return 0;
 }
 
-static void set_branch_merge(const char *name, const char *config_remote,
-			     const char *config_repo)
-{
-	char key[1024];
-	if (sizeof(key) <=
-	    snprintf(key, sizeof(key), "branch.%s.remote", name))
-		die("what a long branch name you have!");
-	git_config_set(key, config_remote);
 
-	/*
-	 * We do not have to check if we have enough space for
-	 * the 'merge' key, since it's shorter than the
-	 * previous 'remote' key, which we already checked.
-	 */
-	snprintf(key, sizeof(key), "branch.%s.merge", name);
-	git_config_set(key, config_repo);
-}
-
-static void set_branch_defaults(const char *name, const char *real_ref)
+/*
+ * This is called when new_ref is branched off of orig_ref, and tries
+ * to infer the settings for branch.<new_ref>.{remote,merge} from the
+ * config.
+ */
+static void setup_tracking(const char *new_ref, const char *orig_ref)
 {
-	/*
-	 * name is the name of new branch under refs/heads;
-	 * real_ref is typically refs/remotes/$foo/$bar, where
-	 * $foo is the remote name (there typically are no slashes)
-	 * and $bar is the branch name we map from the remote
-	 * (it could have slashes).
-	 */
-	start_ref = real_ref;
-	git_config(get_remote_config);
-	if (!config_repo && !config_remote &&
-	    !prefixcmp(real_ref, "refs/heads/")) {
-		set_branch_merge(name, ".", real_ref);
-		printf("Branch %s set up to track local branch %s.\n",
-		       name, real_ref);
-	}
-
-	if (config_repo && config_remote) {
-		set_branch_merge(name, config_remote, config_repo);
-		printf("Branch %s set up to track remote branch %s.\n",
-		       name, real_ref);
+	tracking.ref = orig_ref;
+	tracking.ref_len = strlen(tracking.ref);
+	git_config(tracking_config);
+
+	if (tracking.matches > 1)
+		error("Not tracking: ambiguous information for ref %s",
+				orig_ref);
+	else if (tracking.matches == 1) {
+		char key[1024];
+		int n = snprintf(key, sizeof(key), "branch.%s.remote",
+				new_ref);
+		if (n < sizeof(key) - 1) {
+			git_config_set(key, tracking.remote ?
+					tracking.remote : ".");
+
+			snprintf(key, sizeof(key), "branch.%s.merge", new_ref);
+			git_config_set(key, tracking.merge);
+
+			printf("Branch %s set up to track remote branch %s.\n",
+			       new_ref, orig_ref);
+		} else
+			error("Tracking not set up: name too long: %s",
+				new_ref);
 	}
 
-	if (config_repo)
-		free(config_repo);
-	if (config_remote)
-		free(config_remote);
+	if (tracking.remote)
+		free(tracking.remote);
+	if (tracking.merge)
+		free(tracking.merge);
 }
 
 static void create_branch(const char *name, const char *start_name,
@@ -529,10 +511,8 @@ static void create_branch(const char *name, const char *start_name,
 	/* When branching off a remote branch, set up so that git-pull
 	   automatically merges from there.  So far, this is only done for
 	   remotes registered via .git/config.  */
-	if (real_ref && (track == 2 ||
-				(track == 1 &&
-				 !prefixcmp(real_ref, "refs/remotes/"))))
-		set_branch_defaults(name, real_ref);
+	if (real_ref && track)
+		setup_tracking(name, real_ref);
 
 	if (write_ref_sha1(lock, sha1, msg) < 0)
 		die("Failed to write ref: %s.", strerror(errno));
@@ -604,7 +584,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			break;
 		}
 		if (!strcmp(arg, "--track")) {
-			track = 2;
+			track = 1;
 			continue;
 		}
 		if (!strcmp(arg, "--no-track")) {
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index a19e961..ef1eeb7 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -148,13 +148,14 @@ test_expect_success 'test tracking setup via config' \
      test $(git config branch.my3.remote) = local &&
      test $(git config branch.my3.merge) = refs/heads/master'
 
-test_expect_success 'autosetupmerge = all' '
+test_expect_success 'avoid ambiguous track' '
 	git config branch.autosetupmerge true &&
+	git config remote.ambi1.url = lalala &&
+	git config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&
+	git config remote.ambi2.url = lilili &&
+	git config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&
 	git branch all1 master &&
-	test -z "$(git config branch.all1.merge)" &&
-	git config branch.autosetupmerge all &&
-	git branch all2 master &&
-	test $(git config branch.all2.merge) = refs/heads/master
+	test -z "$(git config branch.all1.merge)"
 '
 
 test_expect_success 'test overriding tracking setup via --no-track' \
@@ -167,10 +168,10 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      ! test "$(git config branch.my2.remote)" = local &&
      ! test "$(git config branch.my2.merge)" = refs/heads/master'
 
-test_expect_success 'test local tracking setup' \
+test_expect_success 'no tracking without .fetch entries' \
     'git branch --track my6 s &&
-     test $(git config branch.my6.remote) = . &&
-     test $(git config branch.my6.merge) = refs/heads/s'
+     test -z "$(git config branch.my6.remote)" &&
+     test -z "$(git config branch.my6.merge)"'
 
 test_expect_success 'test tracking setup via --track but deeper' \
     'git config remote.local.url . &&
@@ -182,8 +183,8 @@ test_expect_success 'test tracking setup via --track but deeper' \
 
 test_expect_success 'test deleting branch deletes branch config' \
     'git branch -d my7 &&
-     test "$(git config branch.my7.remote)" = "" &&
-     test "$(git config branch.my7.merge)" = ""'
+     test -z "$(git config branch.my7.remote)" &&
+     test -z "$(git config branch.my7.merge)"'
 
 test_expect_success 'test deleting branch without config' \
     'git branch my7 s &&
-- 
1.5.3.rc0.2769.gd9be2

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

* Re: [PATCH] branch --track: code cleanup and saner handling of local branches
  2007-07-09 21:05             ` Junio C Hamano
@ 2007-07-09 21:05               ` Johannes Schindelin
  2007-07-09 22:01                 ` Junio C Hamano
  0 siblings, 1 reply; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-09 21:05 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Paolo Bonzini, git, Daniel Barkalow

Hi,

On Mon, 9 Jul 2007, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > -static int get_remote_branch_name(const char *value)
> > +static int tracking_config(const char *key, const char *value)
> > [...]
> >  	/*
> > -	 * Try an exact match first.  I.e. handle the case where the
> > -	 * value is "$anything:refs/foo/bar/baz" and start_ref is exactly
> > -	 * "refs/foo/bar/baz". Then the name at the remote is $anything.
> > +	 * A remote.<name>.fetch value can have two forms:
> > +	 *
> > +	 * - exact:
> > +	 *
> > +	 *	refs/heads/gnu:refs/heads/my-upstream
> > +	 *
> > +	 * - wildcard:
> > +	 *
> > +	 *	refs/heads/ *:refs/remotes/gnu/ *
> > +	 *
> > +	 * try exact match first:
> >  	 */
> 
> It strikes me a bit odd if Daniel's remote.[ch] infrastructure
> does not give you easy access to this kind of information...

Yes, probably.  However, at the time he was sending that patch, I was 
already preparing the patch.  Besides, we can always go back and change 
the code, if you really want to pull in the remotes stuff into 
builtin-branch (as of yet, they are almost independent).

My vote is to have it indpendent for now, if only to fix existing issues.  
But in the end it is your choice.

Ciao,
Dscho

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

* Re: [PATCH] branch --track: code cleanup and saner handling of local branches
  2007-07-09 11:35           ` [PATCH] branch --track: code cleanup and saner handling of local branches Johannes Schindelin
@ 2007-07-09 21:05             ` Junio C Hamano
  2007-07-09 21:05               ` Johannes Schindelin
  0 siblings, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2007-07-09 21:05 UTC (permalink / raw
  To: Johannes Schindelin; +Cc: Paolo Bonzini, git, Daniel Barkalow

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> @@ -349,125 +345,111 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,
>  	free_ref_list(&ref_list);
>  }
>  
> -static char *config_repo;
> -static char *config_remote;
> -static const char *start_ref;
> +static struct {
> +	const char *ref;
> +	int ref_len;
> +	char *remote;
> +	char *merge;
> +	int matches;
> +} tracking;
>  
> -static int get_remote_branch_name(const char *value)
> +static int tracking_config(const char *key, const char *value)
>  {
>  	const char *colon;
> +	int key_len, value_len, match_len;
> +	char *merge = NULL;
>  
> -	colon = strchr(value, ':');
> -	if (!colon)
> +	/* we want: remote.<nick>.fetch = <merge>:<ref> */
> +	if (prefixcmp(key, "remote.") || (key_len = strlen(key)) < 6 ||
> +			strcmp(key + key_len - 6, ".fetch") ||
> +			!(colon = strchr(value, ':')))
>  		return 0;
>  
> -	end = value + strlen(value);
> +	if (*value == '+')
> +		value++;
>  
>  	/*
> -	 * Try an exact match first.  I.e. handle the case where the
> -	 * value is "$anything:refs/foo/bar/baz" and start_ref is exactly
> -	 * "refs/foo/bar/baz". Then the name at the remote is $anything.
> +	 * A remote.<name>.fetch value can have two forms:
> +	 *
> +	 * - exact:
> +	 *
> +	 *	refs/heads/gnu:refs/heads/my-upstream
> +	 *
> +	 * - wildcard:
> +	 *
> +	 *	refs/heads/ *:refs/remotes/gnu/ *
> +	 *
> +	 * try exact match first:
>  	 */

It strikes me a bit odd if Daniel's remote.[ch] infrastructure
does not give you easy access to this kind of information...

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

* Re: [PATCH] branch --track: code cleanup and saner handling of local branches
  2007-07-09 21:05               ` Johannes Schindelin
@ 2007-07-09 22:01                 ` Junio C Hamano
  2007-07-10  3:02                   ` [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking() Johannes Schindelin
  2007-07-10  3:05                   ` [PATCH " Johannes Schindelin
  0 siblings, 2 replies; 23+ messages in thread
From: Junio C Hamano @ 2007-07-09 22:01 UTC (permalink / raw
  To: Johannes Schindelin; +Cc: Paolo Bonzini, git, Daniel Barkalow

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> It strikes me a bit odd if Daniel's remote.[ch] infrastructure
>> does not give you easy access to this kind of information...
>
> Yes, probably.  However, at the time he was sending that patch, I was 
> already preparing the patch.

I was talking about existing remote.[ch] patches, which has been
in tree since late May this year, not his recent round of
"approach to builtin git-fetch" series.

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

* [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-09 22:01                 ` Junio C Hamano
@ 2007-07-10  3:02                   ` Johannes Schindelin
  2007-07-10  3:55                     ` Daniel Barkalow
  2007-07-10  5:07                     ` Junio C Hamano
  2007-07-10  3:05                   ` [PATCH " Johannes Schindelin
  1 sibling, 2 replies; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-10  3:02 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Paolo Bonzini, git, Daniel Barkalow


The function for_each_remote() does exactly what the name suggests.

The function remote_find_tracking() was extended to be able to search
remote refs for a given local ref.  You have to set the parameter
"reverse" to true for that behavior.

Both changes are required for the next step: simplification of
git-branch's --track functionality.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	You're right. I completely missed that functionality. Well, a
	few tweaks were needed. If this clashes too seriously with
	Daniel's work, I will gladly redo it after his changes are
	in "next".

 remote.c    |   42 ++++++++++++++++++++++++++++++++----------
 remote.h    |    7 ++++++-
 send-pack.c |    3 +--
 3 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/remote.c b/remote.c
index cf98a44..21adb0d 100644
--- a/remote.c
+++ b/remote.c
@@ -279,6 +279,26 @@ struct remote *remote_get(const char *name)
 	return ret;
 }
 
+int for_each_remote(each_remote_fn fn, void *priv)
+{
+	int i, result = 0;
+	read_config();
+	for (i = 0; i < allocated_remotes; i++) {
+		struct remote *r = remotes[i];
+		if (!r)
+			continue;
+		if (!r->fetch)
+			r->fetch = parse_ref_spec(r->fetch_refspec_nr,
+					r->fetch_refspec);
+		if (!r->push)
+			r->push = parse_ref_spec(r->push_refspec_nr,
+					r->push_refspec);
+		if ((result = fn(r, priv)))
+			break;
+	}
+	return result;
+}
+
 int remote_has_uri(struct remote *remote, const char *uri)
 {
 	int i;
@@ -289,34 +309,36 @@ int remote_has_uri(struct remote *remote, const char *uri)
 	return 0;
 }
 
-int remote_find_tracking(struct remote *remote, struct refspec *refspec)
+int remote_find_tracking(struct remote *remote, struct refspec *refspec,
+		int reverse)
 {
 	int i;
 	for (i = 0; i < remote->fetch_refspec_nr; i++) {
 		struct refspec *fetch = &remote->fetch[i];
+		const char *src = reverse ? fetch->dst : fetch->src;
+		const char *dst = reverse ? fetch->src : fetch->dst;
 		if (!fetch->dst)
 			continue;
 		if (fetch->pattern) {
-			if (!prefixcmp(refspec->src, fetch->src)) {
+			if (!prefixcmp(refspec->src, src)) {
 				refspec->dst =
-					xmalloc(strlen(fetch->dst) +
+					xmalloc(strlen(dst) +
 						strlen(refspec->src) -
-						strlen(fetch->src) + 1);
-				strcpy(refspec->dst, fetch->dst);
-				strcpy(refspec->dst + strlen(fetch->dst),
-				       refspec->src + strlen(fetch->src));
+						strlen(src) + 1);
+				strcpy(refspec->dst, dst);
+				strcpy(refspec->dst + strlen(dst),
+				       refspec->src + strlen(src));
 				refspec->force = fetch->force;
 				return 0;
 			}
 		} else {
-			if (!strcmp(refspec->src, fetch->src)) {
-				refspec->dst = xstrdup(fetch->dst);
+			if (!strcmp(refspec->src, src)) {
+				refspec->dst = xstrdup(dst);
 				refspec->force = fetch->force;
 				return 0;
 			}
 		}
 	}
-	refspec->dst = NULL;
 	return -1;
 }
 
diff --git a/remote.h b/remote.h
index 01dbcef..9ab7eb6 100644
--- a/remote.h
+++ b/remote.h
@@ -20,6 +20,9 @@ struct remote {
 
 struct remote *remote_get(const char *name);
 
+typedef int each_remote_fn(struct remote *remote, void *priv);
+int for_each_remote(each_remote_fn fn, void *priv);
+
 int remote_has_uri(struct remote *remote, const char *uri);
 
 struct refspec {
@@ -35,7 +38,9 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 
 /*
  * For the given remote, reads the refspec's src and sets the other fields.
+ * If reverse is 1, the given src is the local ref, and we want the remote.
  */
-int remote_find_tracking(struct remote *remote, struct refspec *refspec);
+int remote_find_tracking(struct remote *remote, struct refspec *refspec,
+	int reverse);
 
 #endif
diff --git a/send-pack.c b/send-pack.c
index fecbda9..9fdd7b4 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -305,8 +305,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
 		if (remote) {
 			struct refspec rs;
 			rs.src = ref->name;
-			remote_find_tracking(remote, &rs);
-			if (rs.dst) {
+			if (!remote_find_tracking(remote, &rs, 0)) {
 				struct ref_lock *lock;
 				fprintf(stderr, " Also local %s\n", rs.dst);
 				if (will_delete_ref) {
-- 
1.5.3.rc0.2769.gd9be2

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

* [PATCH 2/2] branch --track: code cleanup and saner handling of local branches
  2007-07-09 22:01                 ` Junio C Hamano
  2007-07-10  3:02                   ` [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking() Johannes Schindelin
@ 2007-07-10  3:05                   ` Johannes Schindelin
  1 sibling, 0 replies; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-10  3:05 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Paolo Bonzini, git, Daniel Barkalow


This patch cleans up some complicated code, and replaces it with a
cleaner version, using code from remote.[ch], which got extended a
little in the process.  This also enables us to fix two cases:

The earlier "fix" to setup tracking only when the original ref started
with "refs/remotes" is wrong.  You are absolutely allowed to use a
separate layout for your tracking branches.  The correct fix, of course,
is to set up tracking information only when there is a matching
remote.<nick>.fetch line containing a colon.

Another corner case was not handled properly.  If two remotes write to
the original ref, just warn the user and do not set up tracking.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	... and again I was bitten by the minimality of the script. The
	original diff was _unreadable_, because it matched some empty 
	lines here and there, or some lonely curly brackets.

	Therefore I manually tweaked it to show first the cruft that was 
	removed, and then the rewrite using remote.[ch]. As a consequence, 
	the diffstat is not the one of this patch, but the one "git show 
	--stat" showed.

 builtin-branch.c  |  167 ++++++++++++++++------------------------------------
 t/t3200-branch.sh |   21 ++++---
 2 files changed, 63 insertions(+), 125 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 49195a1..0dbd6d7 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -10,6 +10,7 @@
 #include "refs.h"
 #include "commit.h"
 #include "builtin.h"
+#include "remote.h"
 
 static const char builtin_branch_usage[] =
   "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]] [--sort-by-date]";
@@ -22,7 +23,7 @@ static const char builtin_branch_usage[] =
 static const char *head;
 static unsigned char head_sha1[20];
 
-static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */
+static int branch_track = 1;
 
 static int branch_use_color;
 static char branch_colors[][COLOR_MAXLEN] = {
@@ -66,12 +67,8 @@ static int git_branch_config(const char *var, const char *value)
 		color_parse(value, var, branch_colors[slot]);
 		return 0;
 	}
-	if (!strcmp(var, "branch.autosetupmerge")) {
-		if (!strcmp(value, "all"))
-			branch_track = 2;
-		else
+	if (!strcmp(var, "branch.autosetupmerge"))
 			branch_track = git_config_bool(var, value);
-	}
 
 	return git_default_config(var, value);
 }
@@ -349,125 +346,67 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,
 	free_ref_list(&ref_list);
 }
 
-static char *config_repo;
-static char *config_remote;
-static const char *start_ref;
-
-static int get_remote_branch_name(const char *value)
-{
-	const char *colon;
-	const char *end;
-
-	if (*value == '+')
-		value++;
-
-	colon = strchr(value, ':');
-	if (!colon)
-		return 0;
-
-	end = value + strlen(value);
-
-	/*
-	 * Try an exact match first.  I.e. handle the case where the
-	 * value is "$anything:refs/foo/bar/baz" and start_ref is exactly
-	 * "refs/foo/bar/baz". Then the name at the remote is $anything.
-	 */
-	if (!strcmp(colon + 1, start_ref)) {
-		/* Truncate the value before the colon. */
-		nfasprintf(&config_repo, "%.*s", colon - value, value);
-		return 1;
-	}
-
-	/*
-	 * Is this a wildcard match?
-	 */
-	if ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||
-	    (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')
-		return 0;
-
-	/*
-	 * Value is "refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>"
-	 * and start_ref begins with "refs/baz/boa/"; the name at the
-	 * remote is refs/foo/bar/ with the remaining part of the
-	 * start_ref.  The length of the prefix on the RHS is (end -
-	 * colon - 2), including the slash immediately before the
-	 * asterisk.
-	 */
-	if ((strlen(start_ref) < end - colon - 2) ||
-	    memcmp(start_ref, colon + 1, end - colon - 2))
-		return 0; /* does not match prefix */
-
-	/* Replace the asterisk with the remote branch name.  */
-	nfasprintf(&config_repo, "%.*s%s",
-		   (colon - 1) - value, value,
-		   start_ref + (end - colon - 2));
-	return 1;
-}
-
-static int get_remote_config(const char *key, const char *value)
-{
-	const char *var;
-	if (prefixcmp(key, "remote."))
-		return 0;
-
-	var = strrchr(key, '.');
-	if (var == key + 6 || strcmp(var, ".fetch"))
-		return 0;
-	/*
-	 * Ok, we are looking at key == "remote.$foo.fetch";
-	 */
-	if (get_remote_branch_name(value))
-		nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
-
-	return 0;
-}
-
-static void set_branch_merge(const char *name, const char *config_remote,
-			     const char *config_repo)
-{
-	char key[1024];
-	if (sizeof(key) <=
-	    snprintf(key, sizeof(key), "branch.%s.remote", name))
-		die("what a long branch name you have!");
-	git_config_set(key, config_remote);
-
-	/*
-	 * We do not have to check if we have enough space for
-	 * the 'merge' key, since it's shorter than the
-	 * previous 'remote' key, which we already checked.
-	 */
-	snprintf(key, sizeof(key), "branch.%s.merge", name);
-	git_config_set(key, config_repo);
-}
-
-static void set_branch_defaults(const char *name, const char *real_ref)
-{
-	/*
-	 * name is the name of new branch under refs/heads;
-	 * real_ref is typically refs/remotes/$foo/$bar, where
-	 * $foo is the remote name (there typically are no slashes)
-	 * and $bar is the branch name we map from the remote
-	 * (it could have slashes).
-	 */
-	start_ref = real_ref;
-	git_config(get_remote_config);
-	if (!config_repo && !config_remote &&
-	    !prefixcmp(real_ref, "refs/heads/")) {
-		set_branch_merge(name, ".", real_ref);
-		printf("Branch %s set up to track local branch %s.\n",
-		       name, real_ref);
-	}
-
-	if (config_repo && config_remote) {
-		set_branch_merge(name, config_remote, config_repo);
-		printf("Branch %s set up to track remote branch %s.\n",
-		       name, real_ref);
-	}
-
-	if (config_repo)
-		free(config_repo);
-	if (config_remote)
-		free(config_remote);
+struct tracking {
+	struct refspec spec;
+	char *dst;
+	const char *remote;
+	int matches;
+};
+
+static int find_tracked_branch(struct remote *remote, void *priv)
+{
+	struct tracking *tracking = priv;
+
+	if (!remote_find_tracking(remote, &tracking->spec, 1)) {
+		if (++tracking->matches == 1) {
+			tracking->dst = tracking->spec.dst;
+			tracking->remote = remote->name;
+		} else {
+			free(tracking->spec.dst);
+			free(tracking->dst);
+			tracking->dst = NULL;
+		}
+	}
+
+	return 0;
+}
+
+
+/*
+ * This is called when new_ref is branched off of orig_ref, and tries
+ * to infer the settings for branch.<new_ref>.{remote,merge} from the
+ * config.
+ */
+static int setup_tracking(const char *new_ref, const char *orig_ref)
+{
+	char key[1024];
+	struct tracking tracking;
+
+	if (strlen(new_ref) > 1024 - 7 - 7 - 1)
+		return error("Tracking not set up: name too long: %s",
+				new_ref);
+
+	memset(&tracking, 0, sizeof(tracking));
+	tracking.spec.src = orig_ref;
+	if (for_each_remote(find_tracked_branch, &tracking) ||
+			!tracking.matches)
+		return 1;
+
+	if (tracking.matches > 1)
+		return error("Not tracking: ambiguous information for ref %s",
+				orig_ref);
+
+	if (tracking.matches == 1) {
+		sprintf(key, "branch.%s.remote", new_ref);
+		git_config_set(key, tracking.remote ?  tracking.remote : ".");
+		sprintf(key, "branch.%s.merge", new_ref);
+		git_config_set(key, tracking.dst);
+		free(tracking.dst);
+		printf("Branch %s set up to track remote branch %s.\n",
+			       new_ref, orig_ref);
+	}
+
+	return 0;
 }
 
 static void create_branch(const char *name, const char *start_name,
@@ -529,10 +468,8 @@ static void create_branch(const char *name, const char *start_name,
 	/* When branching off a remote branch, set up so that git-pull
 	   automatically merges from there.  So far, this is only done for
 	   remotes registered via .git/config.  */
-	if (real_ref && (track == 2 ||
-				(track == 1 &&
-				 !prefixcmp(real_ref, "refs/remotes/"))))
-		set_branch_defaults(name, real_ref);
+	if (real_ref && track)
+		setup_tracking(name, real_ref);
 
 	if (write_ref_sha1(lock, sha1, msg) < 0)
 		die("Failed to write ref: %s.", strerror(errno));
@@ -604,7 +541,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			break;
 		}
 		if (!strcmp(arg, "--track")) {
-			track = 2;
+			track = 1;
 			continue;
 		}
 		if (!strcmp(arg, "--no-track")) {
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index a19e961..ef1eeb7 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -148,13 +148,14 @@ test_expect_success 'test tracking setup via config' \
      test $(git config branch.my3.remote) = local &&
      test $(git config branch.my3.merge) = refs/heads/master'
 
-test_expect_success 'autosetupmerge = all' '
+test_expect_success 'avoid ambiguous track' '
 	git config branch.autosetupmerge true &&
+	git config remote.ambi1.url = lalala &&
+	git config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&
+	git config remote.ambi2.url = lilili &&
+	git config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&
 	git branch all1 master &&
-	test -z "$(git config branch.all1.merge)" &&
-	git config branch.autosetupmerge all &&
-	git branch all2 master &&
-	test $(git config branch.all2.merge) = refs/heads/master
+	test -z "$(git config branch.all1.merge)"
 '
 
 test_expect_success 'test overriding tracking setup via --no-track' \
@@ -167,10 +168,10 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      ! test "$(git config branch.my2.remote)" = local &&
      ! test "$(git config branch.my2.merge)" = refs/heads/master'
 
-test_expect_success 'test local tracking setup' \
+test_expect_success 'no tracking without .fetch entries' \
     'git branch --track my6 s &&
-     test $(git config branch.my6.remote) = . &&
-     test $(git config branch.my6.merge) = refs/heads/s'
+     test -z "$(git config branch.my6.remote)" &&
+     test -z "$(git config branch.my6.merge)"'
 
 test_expect_success 'test tracking setup via --track but deeper' \
     'git config remote.local.url . &&
@@ -182,8 +183,8 @@ test_expect_success 'test tracking setup via --track but deeper' \
 
 test_expect_success 'test deleting branch deletes branch config' \
     'git branch -d my7 &&
-     test "$(git config branch.my7.remote)" = "" &&
-     test "$(git config branch.my7.merge)" = ""'
+     test -z "$(git config branch.my7.remote)" &&
+     test -z "$(git config branch.my7.merge)"'
 
 test_expect_success 'test deleting branch without config' \
     'git branch my7 s &&
-- 
1.5.3.rc0.2769.gd9be2

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

* Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10  3:02                   ` [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking() Johannes Schindelin
@ 2007-07-10  3:55                     ` Daniel Barkalow
  2007-07-10 14:11                       ` Johannes Schindelin
  2007-07-10  5:07                     ` Junio C Hamano
  1 sibling, 1 reply; 23+ messages in thread
From: Daniel Barkalow @ 2007-07-10  3:55 UTC (permalink / raw
  To: Johannes Schindelin; +Cc: Junio C Hamano, Paolo Bonzini, git

On Tue, 10 Jul 2007, Johannes Schindelin wrote:

> The function for_each_remote() does exactly what the name suggests.
> 
> The function remote_find_tracking() was extended to be able to search
> remote refs for a given local ref.  You have to set the parameter
> "reverse" to true for that behavior.

I think I'd like this better if reverse meant that it looked at 
refspec->dst and set refspec->src, rather than returning the refspec 
reversed; the current version sets the refspec so that it's effectively 
something from the list, which makes it easier to understand.

Maybe make it so the user calls it with at most one of src and dst NULL, 
and it returns with neither NULL or returns -1 if it can't find anything?

	-Daniel
*This .sig left intentionally blank*

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

* Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10  3:02                   ` [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking() Johannes Schindelin
  2007-07-10  3:55                     ` Daniel Barkalow
@ 2007-07-10  5:07                     ` Junio C Hamano
  2007-07-10  5:23                       ` Daniel Barkalow
                                         ` (2 more replies)
  1 sibling, 3 replies; 23+ messages in thread
From: Junio C Hamano @ 2007-07-10  5:07 UTC (permalink / raw
  To: Johannes Schindelin; +Cc: Paolo Bonzini, git, Daniel Barkalow

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> The function for_each_remote() does exactly what the name suggests.
>
> The function remote_find_tracking() was extended to be able to search
> remote refs for a given local ref.  You have to set the parameter
> "reverse" to true for that behavior.
>
> Both changes are required for the next step: simplification of
> git-branch's --track functionality.
>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
>
> 	You're right. I completely missed that functionality. Well, a
> 	few tweaks were needed. If this clashes too seriously with
> 	Daniel's work, I will gladly redo it after his changes are
> 	in "next".

No offence meant to Daniel, but I am inclined to postpone the
current round of changes from him to move the stuff further
to get us closer to built-in git-fetch until 1.5.3 final is
done.  The amount of C code changes otherwise would be a bit too
much for me to be comfortable between -rc0 and -rc1.

> diff --git a/remote.c b/remote.c
> index cf98a44..21adb0d 100644
> --- a/remote.c
> +++ b/remote.c
> @@ -279,6 +279,26 @@ struct remote *remote_get(const char *name)
>  	return ret;
>  }
>  
> +int for_each_remote(each_remote_fn fn, void *priv)
> +{
> ...
> +		if ((result = fn(r, priv)))
> +			break;

Just a minor style, but (you know what comes)...

> @@ -289,34 +309,36 @@ int remote_has_uri(struct remote *remote, const char *uri)
>  	return 0;
>  }
>  
> +int remote_find_tracking(struct remote *remote, struct refspec *refspec,
> +		int reverse)
>  {
>  	int i;
>  	for (i = 0; i < remote->fetch_refspec_nr; i++) {
>  		struct refspec *fetch = &remote->fetch[i];
> +		const char *src = reverse ? fetch->dst : fetch->src;
> +		const char *dst = reverse ? fetch->src : fetch->dst;

I have to agree with Daniel here --- variable names src and dst
are quite confusing.  It seems to mean that "we search with
'src' to fill 'dst', but if reverse incoming refspec is given
reversed so matching refspec->src with what in fact is 'dst' in
the configuration file is fine".  Utterly confusing.

Even though this is a good opportunity for you to improve your
comment ratio in ohloh stats, I doubt any amount of explanation
can unconfuse readers of this code.

>  		if (!fetch->dst)
>  			continue;
>
>  		if (fetch->pattern) {
> +			if (!prefixcmp(refspec->src, src)) {
>  				refspec->dst =
> +					xmalloc(strlen(dst) +
>  						strlen(refspec->src) -
> +						strlen(src) + 1);
> +				strcpy(refspec->dst, dst);
> +				strcpy(refspec->dst + strlen(dst),
> +				       refspec->src + strlen(src));
>  				refspec->force = fetch->force;
>  				return 0;
>  			}
>  		} else {
> +			if (!strcmp(refspec->src, src)) {
> +				refspec->dst = xstrdup(dst);
>  				refspec->force = fetch->force;
>  				return 0;
>  			}
>  		}
>  	}
> -	refspec->dst = NULL;
>  	return -1;
>  }

The original remote_find_tracking() took a single remote and a
refspec with src filled, and returned the given refspec after
filling its dst, if an appropriate tracking was configured for
the remote.  What you want to do is from the same remote
information and a refspec with dst filled to find a src branch
that would be stored to that dst.

In either case, incoming refspec would not have both src and dst
filled.  The caller has one side of the information and asking
for the other.  As Daniel suggests, the 'reverse' parameter is
not needed.  If you must have it, please do not call it
'reverse' -- it is more like "find_by_dst".

Following Daniel's suggestion might make the code a bit
lengthier, but I think that would not be as confusing.

> diff --git a/send-pack.c b/send-pack.c
> index fecbda9..9fdd7b4 100644
> --- a/send-pack.c
> +++ b/send-pack.c
> @@ -305,8 +305,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
>  		if (remote) {
>  			struct refspec rs;
>  			rs.src = ref->name;
> -			remote_find_tracking(remote, &rs);
> -			if (rs.dst) {
> +			if (!remote_find_tracking(remote, &rs, 0)) {
>  				struct ref_lock *lock;
>  				fprintf(stderr, " Also local %s\n", rs.dst);
>  				if (will_delete_ref) {

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

* Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10  5:07                     ` Junio C Hamano
@ 2007-07-10  5:23                       ` Daniel Barkalow
  2007-07-10 17:48                       ` [PATCH v2 " Johannes Schindelin
  2007-07-10 17:50                       ` [PATCH v2 2/2] branch --track: code cleanup and saner handling of local branches Johannes Schindelin
  2 siblings, 0 replies; 23+ messages in thread
From: Daniel Barkalow @ 2007-07-10  5:23 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Johannes Schindelin, git

On Mon, 9 Jul 2007, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > 	You're right. I completely missed that functionality. Well, a
> > 	few tweaks were needed. If this clashes too seriously with
> > 	Daniel's work, I will gladly redo it after his changes are
> > 	in "next".
> 
> No offence meant to Daniel, but I am inclined to postpone the
> current round of changes from him to move the stuff further
> to get us closer to built-in git-fetch until 1.5.3 final is
> done.  The amount of C code changes otherwise would be a bit too
> much for me to be comfortable between -rc0 and -rc1.

That's what I'd expect; I'm posting stuff now so that I'm not proposing it 
unreviewed after 1.5.3. Certainly anything that's needed to fix current 
issues should go ahead of these changes, and it doesn't look like there 
would be any conflicts anyway, aside from maybe adding two functions in 
the same place in the file, which is trivial to fix by hand.

	-Daniel
*This .sig left intentionally blank*

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

* Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10  3:55                     ` Daniel Barkalow
@ 2007-07-10 14:11                       ` Johannes Schindelin
  0 siblings, 0 replies; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-10 14:11 UTC (permalink / raw
  To: Daniel Barkalow; +Cc: Junio C Hamano, Paolo Bonzini, git

Hi,

On Mon, 9 Jul 2007, Daniel Barkalow wrote:

> On Tue, 10 Jul 2007, Johannes Schindelin wrote:
> 
> > The function for_each_remote() does exactly what the name suggests.
> > 
> > The function remote_find_tracking() was extended to be able to search
> > remote refs for a given local ref.  You have to set the parameter
> > "reverse" to true for that behavior.
> 
> I think I'd like this better if reverse meant that it looked at 
> refspec->dst and set refspec->src, rather than returning the refspec 
> reversed; the current version sets the refspec so that it's effectively 
> something from the list, which makes it easier to understand.
> 
> Maybe make it so the user calls it with at most one of src and dst NULL, 
> and it returns with neither NULL or returns -1 if it can't find anything?

Will do.

Ciao,
Dscho

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

* [PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10  5:07                     ` Junio C Hamano
  2007-07-10  5:23                       ` Daniel Barkalow
@ 2007-07-10 17:48                       ` Johannes Schindelin
  2007-07-10 18:38                         ` Junio C Hamano
  2007-07-10 17:50                       ` [PATCH v2 2/2] branch --track: code cleanup and saner handling of local branches Johannes Schindelin
  2 siblings, 1 reply; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-10 17:48 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Paolo Bonzini, git, Daniel Barkalow


The function for_each_remote() does exactly what the name suggests.

The function remote_find_tracking() was extended to be able to search
remote refs for a given local ref.  You have to set the parameter
"reverse" to true for that behavior.

Both changes are required for the next step: simplification of
git-branch's --track functionality.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	Thanks, Daniel and Junio, for your suggestions.

 remote.c    |   60 ++++++++++++++++++++++++++++++++++++++++++++--------------
 remote.h    |    5 +++-
 send-pack.c |    4 +-
 3 files changed, 51 insertions(+), 18 deletions(-)

diff --git a/remote.c b/remote.c
index 09c4279..bb774d0 100644
--- a/remote.c
+++ b/remote.c
@@ -279,6 +279,25 @@ struct remote *remote_get(const char *name)
 	return ret;
 }
 
+int for_each_remote(each_remote_fn fn, void *priv)
+{
+	int i, result = 0;
+	read_config();
+	for (i = 0; i < allocated_remotes && !result; i++) {
+		struct remote *r = remotes[i];
+		if (!r)
+			continue;
+		if (!r->fetch)
+			r->fetch = parse_ref_spec(r->fetch_refspec_nr,
+					r->fetch_refspec);
+		if (!r->push)
+			r->push = parse_ref_spec(r->push_refspec_nr,
+					r->push_refspec);
+		result = fn(r, priv);
+	}
+	return result;
+}
+
 int remote_has_uri(struct remote *remote, const char *uri)
 {
 	int i;
@@ -291,32 +310,43 @@ int remote_has_uri(struct remote *remote, const char *uri)
 
 int remote_find_tracking(struct remote *remote, struct refspec *refspec)
 {
+	int find_src = refspec->src == NULL;
+	char *needle, **result;
 	int i;
+
+	if (find_src) {
+		if (refspec->dst == NULL)
+			return error("find_tracking: need either src or dst");
+		needle = refspec->dst;
+		result = &refspec->src;
+	} else {
+		needle = refspec->src;
+		result = &refspec->dst;
+	}
+
 	for (i = 0; i < remote->fetch_refspec_nr; i++) {
 		struct refspec *fetch = &remote->fetch[i];
+		const char *key = find_src ? fetch->dst : fetch->src;
+		const char *value = find_src ? fetch->src : fetch->dst;
 		if (!fetch->dst)
 			continue;
 		if (fetch->pattern) {
-			if (!prefixcmp(refspec->src, fetch->src)) {
-				refspec->dst =
-					xmalloc(strlen(fetch->dst) +
-						strlen(refspec->src) -
-						strlen(fetch->src) + 1);
-				strcpy(refspec->dst, fetch->dst);
-				strcpy(refspec->dst + strlen(fetch->dst),
-				       refspec->src + strlen(fetch->src));
-				refspec->force = fetch->force;
-				return 0;
-			}
-		} else {
-			if (!strcmp(refspec->src, fetch->src)) {
-				refspec->dst = xstrdup(fetch->dst);
+			if (!prefixcmp(needle, key)) {
+				*result = xmalloc(strlen(value) +
+						  strlen(needle) -
+						  strlen(key) + 1);
+				strcpy(*result, value);
+				strcpy(*result + strlen(value),
+				       needle + strlen(key));
 				refspec->force = fetch->force;
 				return 0;
 			}
+		} else if (!strcmp(needle, key)) {
+			*result = xstrdup(value);
+			refspec->force = fetch->force;
+			return 0;
 		}
 	}
-	refspec->dst = NULL;
 	return -1;
 }
 
diff --git a/remote.h b/remote.h
index 080b7da..17b8b5b 100644
--- a/remote.h
+++ b/remote.h
@@ -20,13 +20,16 @@ struct remote {
 
 struct remote *remote_get(const char *name);
 
+typedef int each_remote_fn(struct remote *remote, void *priv);
+int for_each_remote(each_remote_fn fn, void *priv);
+
 int remote_has_uri(struct remote *remote, const char *uri);
 
 struct refspec {
 	unsigned force : 1;
 	unsigned pattern : 1;
 
-	const char *src;
+	char *src;
 	char *dst;
 };
 
diff --git a/send-pack.c b/send-pack.c
index fecbda9..9fc8a81 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -305,8 +305,8 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
 		if (remote) {
 			struct refspec rs;
 			rs.src = ref->name;
-			remote_find_tracking(remote, &rs);
-			if (rs.dst) {
+			rs.dst = NULL;
+			if (!remote_find_tracking(remote, &rs)) {
 				struct ref_lock *lock;
 				fprintf(stderr, " Also local %s\n", rs.dst);
 				if (will_delete_ref) {
-- 
1.5.3.rc0.2783.gf3f7

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

* [PATCH v2 2/2] branch --track: code cleanup and saner handling of local branches
  2007-07-10  5:07                     ` Junio C Hamano
  2007-07-10  5:23                       ` Daniel Barkalow
  2007-07-10 17:48                       ` [PATCH v2 " Johannes Schindelin
@ 2007-07-10 17:50                       ` Johannes Schindelin
  2 siblings, 0 replies; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-10 17:50 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Paolo Bonzini, git, Daniel Barkalow


This patch cleans up some complicated code, and replaces it with a
cleaner version, using code from remote.[ch], which got extended a
little in the process.  This also enables us to fix two cases:

The earlier "fix" to setup tracking only when the original ref started 
with "refs/remotes" is wrong.  You are absolutely allowed to use a 
separate layout for your tracking branches.  The correct fix, of course, 
is to set up tracking information only when there is a matching 
remote.<nick>.fetch line containing a colon.

Another corner case was not handled properly.  If two remotes write to
the original ref, just warn the user and do not set up tracking.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	Sorry, but I really do not feel like manually making the patch
	nicer to read today.  It is really annoying to me to see the
	empty and the not-really-interesting lines match up.  Oh, well.

 builtin-branch.c  |  170 +++++++++++++++++------------------------------------
 t/t3200-branch.sh |   21 ++++---
 2 files changed, 66 insertions(+), 125 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 49195a1..d16d3f2 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -10,6 +10,7 @@
 #include "refs.h"
 #include "commit.h"
 #include "builtin.h"
+#include "remote.h"
 
 static const char builtin_branch_usage[] =
   "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]] [--sort-by-date]";
@@ -22,7 +23,7 @@ static const char builtin_branch_usage[] =
 static const char *head;
 static unsigned char head_sha1[20];
 
-static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */
+static int branch_track = 1;
 
 static int branch_use_color;
 static char branch_colors[][COLOR_MAXLEN] = {
@@ -66,12 +67,8 @@ static int git_branch_config(const char *var, const char *value)
 		color_parse(value, var, branch_colors[slot]);
 		return 0;
 	}
-	if (!strcmp(var, "branch.autosetupmerge")) {
-		if (!strcmp(value, "all"))
-			branch_track = 2;
-		else
+	if (!strcmp(var, "branch.autosetupmerge"))
 			branch_track = git_config_bool(var, value);
-	}
 
 	return git_default_config(var, value);
 }
@@ -349,125 +346,70 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,
 	free_ref_list(&ref_list);
 }
 
-static char *config_repo;
-static char *config_remote;
-static const char *start_ref;
+struct tracking {
+	struct refspec spec;
+	char *src;
+	const char *remote;
+	int matches;
+};
 
-static int get_remote_branch_name(const char *value)
+static int find_tracked_branch(struct remote *remote, void *priv)
 {
-	const char *colon;
-	const char *end;
+	struct tracking *tracking = priv;
 
-	if (*value == '+')
-		value++;
-
-	colon = strchr(value, ':');
-	if (!colon)
-		return 0;
-
-	end = value + strlen(value);
-
-	/*
-	 * Try an exact match first.  I.e. handle the case where the
-	 * value is "$anything:refs/foo/bar/baz" and start_ref is exactly
-	 * "refs/foo/bar/baz". Then the name at the remote is $anything.
-	 */
-	if (!strcmp(colon + 1, start_ref)) {
-		/* Truncate the value before the colon. */
-		nfasprintf(&config_repo, "%.*s", colon - value, value);
-		return 1;
+	if (!remote_find_tracking(remote, &tracking->spec)) {
+		if (++tracking->matches == 1) {
+			tracking->src = tracking->spec.src;
+			tracking->remote = remote->name;
+		} else {
+			free(tracking->spec.src);
+			if (tracking->src) {
+				free(tracking->src);
+				tracking->src = NULL;
+			}
+		}
+		tracking->spec.src = NULL;
 	}
 
-	/*
-	 * Is this a wildcard match?
-	 */
-	if ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||
-	    (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')
-		return 0;
-
-	/*
-	 * Value is "refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>"
-	 * and start_ref begins with "refs/baz/boa/"; the name at the
-	 * remote is refs/foo/bar/ with the remaining part of the
-	 * start_ref.  The length of the prefix on the RHS is (end -
-	 * colon - 2), including the slash immediately before the
-	 * asterisk.
-	 */
-	if ((strlen(start_ref) < end - colon - 2) ||
-	    memcmp(start_ref, colon + 1, end - colon - 2))
-		return 0; /* does not match prefix */
-
-	/* Replace the asterisk with the remote branch name.  */
-	nfasprintf(&config_repo, "%.*s%s",
-		   (colon - 1) - value, value,
-		   start_ref + (end - colon - 2));
-	return 1;
-}
-
-static int get_remote_config(const char *key, const char *value)
-{
-	const char *var;
-	if (prefixcmp(key, "remote."))
-		return 0;
-
-	var = strrchr(key, '.');
-	if (var == key + 6 || strcmp(var, ".fetch"))
-		return 0;
-	/*
-	 * Ok, we are looking at key == "remote.$foo.fetch";
-	 */
-	if (get_remote_branch_name(value))
-		nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
-
 	return 0;
 }
 
-static void set_branch_merge(const char *name, const char *config_remote,
-			     const char *config_repo)
+
+/*
+ * This is called when new_ref is branched off of orig_ref, and tries
+ * to infer the settings for branch.<new_ref>.{remote,merge} from the
+ * config.
+ */
+static int setup_tracking(const char *new_ref, const char *orig_ref)
 {
 	char key[1024];
-	if (sizeof(key) <=
-	    snprintf(key, sizeof(key), "branch.%s.remote", name))
-		die("what a long branch name you have!");
-	git_config_set(key, config_remote);
-
-	/*
-	 * We do not have to check if we have enough space for
-	 * the 'merge' key, since it's shorter than the
-	 * previous 'remote' key, which we already checked.
-	 */
-	snprintf(key, sizeof(key), "branch.%s.merge", name);
-	git_config_set(key, config_repo);
-}
+	struct tracking tracking;
 
-static void set_branch_defaults(const char *name, const char *real_ref)
-{
-	/*
-	 * name is the name of new branch under refs/heads;
-	 * real_ref is typically refs/remotes/$foo/$bar, where
-	 * $foo is the remote name (there typically are no slashes)
-	 * and $bar is the branch name we map from the remote
-	 * (it could have slashes).
-	 */
-	start_ref = real_ref;
-	git_config(get_remote_config);
-	if (!config_repo && !config_remote &&
-	    !prefixcmp(real_ref, "refs/heads/")) {
-		set_branch_merge(name, ".", real_ref);
-		printf("Branch %s set up to track local branch %s.\n",
-		       name, real_ref);
-	}
+	if (strlen(new_ref) > 1024 - 7 - 7 - 1)
+		return error("Tracking not set up: name too long: %s",
+				new_ref);
 
-	if (config_repo && config_remote) {
-		set_branch_merge(name, config_remote, config_repo);
+	memset(&tracking, 0, sizeof(tracking));
+	tracking.spec.dst = (char *)orig_ref;
+	if (for_each_remote(find_tracked_branch, &tracking) ||
+			!tracking.matches)
+		return 1;
+
+	if (tracking.matches > 1)
+		return error("Not tracking: ambiguous information for ref %s",
+				orig_ref);
+
+	if (tracking.matches == 1) {
+		sprintf(key, "branch.%s.remote", new_ref);
+		git_config_set(key, tracking.remote ?  tracking.remote : ".");
+		sprintf(key, "branch.%s.merge", new_ref);
+		git_config_set(key, tracking.src);
+		free(tracking.src);
 		printf("Branch %s set up to track remote branch %s.\n",
-		       name, real_ref);
+			       new_ref, orig_ref);
 	}
 
-	if (config_repo)
-		free(config_repo);
-	if (config_remote)
-		free(config_remote);
+	return 0;
 }
 
 static void create_branch(const char *name, const char *start_name,
@@ -529,10 +471,8 @@ static void create_branch(const char *name, const char *start_name,
 	/* When branching off a remote branch, set up so that git-pull
 	   automatically merges from there.  So far, this is only done for
 	   remotes registered via .git/config.  */
-	if (real_ref && (track == 2 ||
-				(track == 1 &&
-				 !prefixcmp(real_ref, "refs/remotes/"))))
-		set_branch_defaults(name, real_ref);
+	if (real_ref && track)
+		setup_tracking(name, real_ref);
 
 	if (write_ref_sha1(lock, sha1, msg) < 0)
 		die("Failed to write ref: %s.", strerror(errno));
@@ -604,7 +544,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			break;
 		}
 		if (!strcmp(arg, "--track")) {
-			track = 2;
+			track = 1;
 			continue;
 		}
 		if (!strcmp(arg, "--no-track")) {
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index a19e961..ef1eeb7 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -148,13 +148,14 @@ test_expect_success 'test tracking setup via config' \
      test $(git config branch.my3.remote) = local &&
      test $(git config branch.my3.merge) = refs/heads/master'
 
-test_expect_success 'autosetupmerge = all' '
+test_expect_success 'avoid ambiguous track' '
 	git config branch.autosetupmerge true &&
+	git config remote.ambi1.url = lalala &&
+	git config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&
+	git config remote.ambi2.url = lilili &&
+	git config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&
 	git branch all1 master &&
-	test -z "$(git config branch.all1.merge)" &&
-	git config branch.autosetupmerge all &&
-	git branch all2 master &&
-	test $(git config branch.all2.merge) = refs/heads/master
+	test -z "$(git config branch.all1.merge)"
 '
 
 test_expect_success 'test overriding tracking setup via --no-track' \
@@ -167,10 +168,10 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      ! test "$(git config branch.my2.remote)" = local &&
      ! test "$(git config branch.my2.merge)" = refs/heads/master'
 
-test_expect_success 'test local tracking setup' \
+test_expect_success 'no tracking without .fetch entries' \
     'git branch --track my6 s &&
-     test $(git config branch.my6.remote) = . &&
-     test $(git config branch.my6.merge) = refs/heads/s'
+     test -z "$(git config branch.my6.remote)" &&
+     test -z "$(git config branch.my6.merge)"'
 
 test_expect_success 'test tracking setup via --track but deeper' \
     'git config remote.local.url . &&
@@ -182,8 +183,8 @@ test_expect_success 'test tracking setup via --track but deeper' \
 
 test_expect_success 'test deleting branch deletes branch config' \
     'git branch -d my7 &&
-     test "$(git config branch.my7.remote)" = "" &&
-     test "$(git config branch.my7.merge)" = ""'
+     test -z "$(git config branch.my7.remote)" &&
+     test -z "$(git config branch.my7.merge)"'
 
 test_expect_success 'test deleting branch without config' \
     'git branch my7 s &&
-- 
1.5.3.rc0.2783.gf3f7

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

* Re: [PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10 17:48                       ` [PATCH v2 " Johannes Schindelin
@ 2007-07-10 18:38                         ` Junio C Hamano
  2007-07-10 19:28                           ` Johannes Schindelin
  2007-07-10 21:09                           ` Daniel Barkalow
  0 siblings, 2 replies; 23+ messages in thread
From: Junio C Hamano @ 2007-07-10 18:38 UTC (permalink / raw
  To: Johannes Schindelin; +Cc: Paolo Bonzini, git, Daniel Barkalow

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> The function for_each_remote() does exactly what the name suggests.
>
> The function remote_find_tracking() was extended to be able to search
> remote refs for a given local ref.  You have to set the parameter
> "reverse" to true for that behavior.

The updated patch does not use "reverse" but the old description
is still there.

Daniel, one thing I fear about your "I want to store the message
in the object store so that I can reuse even after I re-polish
the series" desire on the cover letter topic is this kind of
gotcha, and that is why I suggested "*** BLURB GOES HERE ***".
Both the summary (diffstat and shortlog) part and the
description part should be kept fresh in the updated 0/N; while
we can automate the summary part whenever we re-generate 0/N,
you cannot automate the description part.

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

* Re: [PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10 18:38                         ` Junio C Hamano
@ 2007-07-10 19:28                           ` Johannes Schindelin
  2007-07-10 21:09                           ` Daniel Barkalow
  1 sibling, 0 replies; 23+ messages in thread
From: Johannes Schindelin @ 2007-07-10 19:28 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Paolo Bonzini, git, Daniel Barkalow

Hi,

On Tue, 10 Jul 2007, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > The function for_each_remote() does exactly what the name suggests.
> >
> > The function remote_find_tracking() was extended to be able to search
> > remote refs for a given local ref.  You have to set the parameter
> > "reverse" to true for that behavior.
> 
> The updated patch does not use "reverse" but the old description
> is still there.

Urgh. Right. May I ask you to paste this instead?

The function remote_find_tracking() was extended to be able to search 
remote refs for a given local ref.  You have to set either src or dst in 
the refspec, and remote_find_tracking() will fill in the other and return 
0.

> Daniel, one thing I fear about your "I want to store the message
> in the object store so that I can reuse even after I re-polish
> the series" desire on the cover letter topic is this kind of
> gotcha, and that is why I suggested "*** BLURB GOES HERE ***".

I am happy that my fsckup served a purpose, then.  (And maybe this would 
be a good hint in rebase -i's man page, too, since that is how the error 
was introduced here.)

Ciao,
Dscho

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

* Re: [PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()
  2007-07-10 18:38                         ` Junio C Hamano
  2007-07-10 19:28                           ` Johannes Schindelin
@ 2007-07-10 21:09                           ` Daniel Barkalow
  1 sibling, 0 replies; 23+ messages in thread
From: Daniel Barkalow @ 2007-07-10 21:09 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Johannes Schindelin, Paolo Bonzini, git

On Tue, 10 Jul 2007, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > The function for_each_remote() does exactly what the name suggests.
> >
> > The function remote_find_tracking() was extended to be able to search
> > remote refs for a given local ref.  You have to set the parameter
> > "reverse" to true for that behavior.
> 
> The updated patch does not use "reverse" but the old description
> is still there.
> 
> Daniel, one thing I fear about your "I want to store the message
> in the object store so that I can reuse even after I re-polish
> the series" desire on the cover letter topic is this kind of
> gotcha, and that is why I suggested "*** BLURB GOES HERE ***".
> Both the summary (diffstat and shortlog) part and the
> description part should be kept fresh in the updated 0/N; while
> we can automate the summary part whenever we re-generate 0/N,
> you cannot automate the description part.

It seems to me that commit messages are much more likely to mention the 
sorts of details that are affected by review than cover letters are. 
Furthermore, if the message is coming out of a tag on the head of the 
series, whatever is used to put the tag onto the new head of the series 
would present the buffer for editting again, just like commit --amend 
does. So the user would be just as likely to think to update a series 
header as a commit message, and less likely to need to.

	-Daniel
*This .sig left intentionally blank*

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

end of thread, other threads:[~2007-07-10 21:09 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-06 21:54 [RFC/PATCH] git-branch: default to --track Johannes Schindelin
2007-07-08  8:59 ` Junio C Hamano
2007-07-08 12:41   ` [PATCH] branch.autosetupmerge: allow boolean values, or "all" Johannes Schindelin
2007-07-08 18:41     ` Junio C Hamano
2007-07-08 19:15       ` Johannes Schindelin
2007-07-09  1:59       ` Paolo Bonzini
2007-07-09  2:27         ` Junio C Hamano
2007-07-09 11:35           ` [PATCH] branch --track: code cleanup and saner handling of local branches Johannes Schindelin
2007-07-09 21:05             ` Junio C Hamano
2007-07-09 21:05               ` Johannes Schindelin
2007-07-09 22:01                 ` Junio C Hamano
2007-07-10  3:02                   ` [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking() Johannes Schindelin
2007-07-10  3:55                     ` Daniel Barkalow
2007-07-10 14:11                       ` Johannes Schindelin
2007-07-10  5:07                     ` Junio C Hamano
2007-07-10  5:23                       ` Daniel Barkalow
2007-07-10 17:48                       ` [PATCH v2 " Johannes Schindelin
2007-07-10 18:38                         ` Junio C Hamano
2007-07-10 19:28                           ` Johannes Schindelin
2007-07-10 21:09                           ` Daniel Barkalow
2007-07-10 17:50                       ` [PATCH v2 2/2] branch --track: code cleanup and saner handling of local branches Johannes Schindelin
2007-07-10  3:05                   ` [PATCH " Johannes Schindelin
2007-07-09 11:28         ` [PATCH] branch.autosetupmerge: allow boolean values, or "all" Johannes Schindelin

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