Skip to contentSkip to conversation

git-fetch, git-branch: Support local --track via a special remote `.'

PATCH In review Paolo Bonzini 3 participants 6 messages Jun 15, 2016 Documentation

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 git-fetch, git-branch: Support local --track via a special remote `.'

This patch adds support for a dummy remote `.' to avoid having to declare a fake remote like

	[remote "local"]
		url = .
		fetch = refs/heads/*:refs/heads/*

Such a builtin remote simplifies the operation of "git-fetch", which will populate FETCH_HEAD but will not pretend that two repositories are in use, will not create a thin pack, and will not perform any useless remapping of names. The speed improvement is around 20%, and it should improve more if "git-fetch" is converted to a builtin.

To this end, git-parse-remote is grown with a new kind of remote, `builtin'. In git-fetch.sh, we treat the builtin remote specially in that it needs no pack/store operations. In fact, doing git-fetch on a builtin remote will simply populate FETCH_HEAD appropriately.

The patch also improves of the --track/--no-track support, extending it so that branch.<name>.remote items referring `.' can be created.

---
 Documentation/config.txt |    4 ++++
 builtin-branch.c         |   39 +++++++++++++++++++++++++--------------
 git-fetch.sh             |    9 +++++++--
 git-parse-remote.sh      |   22 +++++++++++++++++++++-
 t/t3200-branch.sh        |    6 ++++++
 t/t5520-pull.sh          |   13 +++++++++++++
 6 files changed, 76 insertions(+), 17 deletions(-)

I hope this is what you meant in your reply to my message.

I removed completely the configuration variable since it made no sense to me, either.

Documentation/config.txt +4 -0
index 5408dd6..28899d9 100644
@@ -272,6 +272,10 @@ branch.<name>.merge::
 	`git fetch`) to lookup the default branch for merging. Without
 	this option, `git pull` defaults to merge the first refspec fetched.
 	Specify multiple values to get an octopus merge.
+	If you wish to setup `git pull` so that it merges into <name> from
+	another branch in the local repository, you can point
+	branch.<name>.merge to the desired branch, and use the special setting
+	`.` (a period) for branch.<name>.remote.
 
 color.branch::
 	A boolean to enable/disable color in the output of
builtin-branch.c +25 -14
index 42b1ff1..14c4219 100644
@@ -372,9 +372,26 @@ static int get_remote_config(const char *key, const char *value)
 	return 0;
 }
 
-static void set_branch_defaults(const char *name, const char *real_ref)
+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)
+{
 	const char *slash = strrchr(real_ref, '/');
 
 	if (!slash)
@@ -384,21 +401,15 @@ static void set_branch_defaults(const char *name, const char *real_ref)
 	start_len = strlen(real_ref);
 	base_len = slash - 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) {
-		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);
-
+		set_branch_merge(name, config_remote, config_repo);
 		printf("Branch %s set up to track remote branch %s.\n",
 		       name, real_ref);
 	}
git-fetch.sh +7 -2
index b9d7a75..e2a6878 100755
@@ -164,6 +164,12 @@ then
 fi
 
 fetch_native () {
+  if test "$(get_data_source $remote_nick)" = builtin; then
+    for name in $(get_remote_fetch_branches "$remote_nick"); do
+      append_fetch_head $(git-rev-parse "$name") . "$name" "$name" || exit
+    done
+    return
+  fi
 
   eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
   eval "$eval"
@@ -353,8 +359,7 @@ case ",$#,$remote_nick," in
 	curr_branch=$(git-symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
 	merge_branches=$(git-config \
 		--get-all "branch.${curr_branch}.merge" | sort -u)
-	fetch_branches=$(get_remote_default_refs_for_fetch -n $remote_nick |
-		sed -e 's/:.*$//g' -e 's/^+//' | sort -u)
+	fetch_branches=$(get_remote_fetch_branches $remote_nick)
 	test -n "$merge_branches" && test -n "$fetch_branches" &&
 	merge_branches=$(printf '%s\n%s' "$merge_branches" "$fetch_branches" |
 		sort | uniq -d)
git-parse-remote.sh +21 -1
index 99e7184..bc7533a 100755
@@ -9,6 +9,9 @@ get_data_source () {
 	*/*)
 		echo ''
 		;;
+	.)
+		echo builtin
+		;;
 	*)
 		if test "$(git-config --get "remote.$1.url")"
 		then
@@ -31,6 +34,9 @@ get_remote_url () {
 	'')
 		echo "$1"
 		;;
+	builtin)
+		echo "$1"
+		;;
 	config)
 		git-config --get "remote.$1.url"
 		;;
@@ -57,7 +63,7 @@ get_default_remote () {
 get_remote_default_refs_for_push () {
 	data_source=$(get_data_source "$1")
 	case "$data_source" in
-	'' | branches)
+	'' | branches | builtin)
 		;; # no default push mapping, just send matching refs.
 	config)
 		git-config --get-all "remote.$1.push" ;;
@@ -128,6 +134,8 @@ get_remote_default_refs_for_fetch () {
 	case "$data_source" in
 	'')
 		set explicit "HEAD:" ;;
+	builtin)
+		set glob ;;
 	config)
 		set $(expand_refs_wildcard "$1" \
 			$(git-config --get-all "remote.$1.fetch")) ;;
@@ -154,6 +162,18 @@ get_remote_default_refs_for_fetch () {
 	fi
 }
 
+# Returns list of sources
+get_remote_fetch_branches () {
+	data_source=$(get_data_source "$1")
+	case "$data_source" in
+	builtin)
+	        git-show-ref --heads | sed -n 's,.*[      ]refs/,refs/,p' ;;
+	*)
+		get_remote_default_refs_for_fetch -n "$1" | \
+			sed -e 's/:.*$//g' -e 's/^+//' | sort -u ;;
+	esac
+}
+
 get_remote_refs_for_push () {
 	case "$#" in
 	0) die "internal error: get-remote-refs-for-push." ;;
t/t3200-branch.sh +6 -0
index 75c000a..9558bdb 100755
@@ -145,9 +145,15 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
      git-branch --no-track my2 local/master &&
+     git-config branch.autosetupmerge false &&
      ! test $(git-config branch.my2.remote) = local &&
      ! test $(git-config branch.my2.merge) = refs/heads/master'
 
+test_expect_success 'test local tracking setup' \
+    'git branch --track my6 s &&
+     test $(git-config branch.my6.remote) = . &&
+     test $(git-config branch.my6.merge) = refs/heads/s'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
t/t5520-pull.sh +13 -0
index 7eb3783..c424e5b 100755
@@ -29,5 +29,18 @@ test_expect_success 'checking the results' '
 	diff file cloned/file
 '
 
+test_expect_success 'test . as a remote' '
+
+	git branch copy master &&
+	git config branch.copy.remote . &&
+	git config branch.copy.merge refs/heads/master &&
+	echo updated >file &&
+	git commit -a -m updated &&
+	git checkout copy &&
+	test `cat file` = file &&
+	git pull &&
+	test `cat file` = updated
+'
+
 test_done
 
#1 lore raw
Quoted 1 line · Paolo Bonzini

I hope this is what you meant in your reply to my message.

Almost, except I wouldn't have made this part of fetch_native() like this:

Quoted 7 lines
git-fetch.sh +0 -0
index b9d7a75..e2a6878 100755
@@ -164,6 +164,12 @@ then
 fi
 

git-fetch.sh then · line 175

Quoted 10 lines
git-fetch.sh +6 -0
 fetch_native () {
+  if test "$(get_data_source $remote_nick)" = builtin; then
+    for name in $(get_remote_fetch_branches "$remote_nick"); do
+      append_fetch_head $(git-rev-parse "$name") . "$name" "$name" || exit
+    done
+    return
+  fi
 
   eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
   eval "$eval"

but probably would have created another sub, fetch_fake.

Quoted 2 lines

I removed completely the configuration variable since it made no sense to me, either.

I think that is sensible.

Currently the patch is parked at the tip of 'pu' after merging all the other topics, as the test t9101 seems to expose breakage in "git pull . remotes/git-svn".

#2 lore raw

This patch on top of next adds support for a dummy remote `.' to avoid having to declare a fake remote like

	[remote "local"]
		url = .
		fetch = refs/heads/*:refs/heads/*

Such a builtin remote simplifies the operation of "git-fetch", which will populate FETCH_HEAD but will not pretend that two repositories are in use, will not create a thin pack, and will not perform any useless remapping of names. The speed improvement is around 20%, and it should improve more if "git-fetch" is converted to a builtin.

To this end, git-parse-remote is grown with a new kind of remote, `builtin'. In git-fetch.sh, we treat the builtin remote specially in that it needs no pack/store operations. In fact, doing git-fetch on a builtin remote will simply populate FETCH_HEAD appropriately.

The patch also improves of the --track/--no-track support, extending it so that branch.<name>.remote items referring `.' can be created. Finally, it fixes a typo in git-checkout.sh.

The patch was tested including the git-svn tests and has no regression.

---
 Documentation/config.txt |    4 ++++
 builtin-branch.c         |   39 +++++++++++++++++++++++++--------------
 git-checkout.sh          |    2 +-
 git-fetch.sh             |   30 +++++++++++++++++++-----------
 git-parse-remote.sh      |   22 +++++++++++++++++++++-
 t/t3200-branch.sh        |    6 ++++++
 t/t5520-pull.sh          |   13 +++++++++++++
 t/t9109-git-svn-pull.sh  |   31 +++++++++++++++++++++++++++++++
 8 files changed, 120 insertions(+), 27 deletions(-)

The failure, which I didn't see because I didn't have svn-perl bindings installed, was caused by my usage of "git-show-ref --heads" where I should have used plain "git-show-ref".

I include a smaller testcase for the same problem, t9109.

Documentation/config.txt +4 -0
index 5408dd6..28899d9 100644
@@ -272,6 +272,10 @@ branch.<name>.merge::
 	`git fetch`) to lookup the default branch for merging. Without
 	this option, `git pull` defaults to merge the first refspec fetched.
 	Specify multiple values to get an octopus merge.
+	If you wish to setup `git pull` so that it merges into <name> from
+	another branch in the local repository, you can point
+	branch.<name>.merge to the desired branch, and use the special setting
+	`.` (a period) for branch.<name>.remote.
 
 color.branch::
 	A boolean to enable/disable color in the output of
builtin-branch.c +25 -14
index 42b1ff1..14c4219 100644
@@ -372,9 +372,26 @@ static int get_remote_config(const char *key, const char *value)
 	return 0;
 }
 
-static void set_branch_defaults(const char *name, const char *real_ref)
+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)
+{
 	const char *slash = strrchr(real_ref, '/');
 
 	if (!slash)
@@ -384,21 +401,15 @@ static void set_branch_defaults(const char *name, const char *real_ref)
 	start_len = strlen(real_ref);
 	base_len = slash - 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) {
-		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);
-
+		set_branch_merge (name, config_remote, config_repo);
 		printf("Branch %s set up to track remote branch %s.\n",
 		       name, real_ref);
 	}
git-checkout.sh +1 -1
index 6caa9fd..b292ff0 100755
@@ -89,7 +89,7 @@ while [ "$#" != "0" ]; do
     esac
 done
 
-case "$new_branch,$track" in
+case "$newbranch,$track" in
 ,--*)
 	die "git checkout: --track and --no-track require -b"
 esac
git-fetch.sh +19 -11
index b9d7a75..f06fa59 100755
@@ -163,8 +163,13 @@ then
 	fi
 fi
 
-fetch_native () {
+fetch_builtin () {
+    for name in $(get_remote_fetch_branches "$remote_nick"); do
+      append_fetch_head $(git-rev-parse "$name") . "$name" "$name" || exit
+    done
+}
 
+fetch_native () {
   eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
   eval "$eval"
 
@@ -291,14 +296,18 @@ fetch_dumb () {
 }
 
 fetch_main () {
-	case "$remote" in
-	http://* | https://* | ftp://* | rsync://* )
-		fetch_dumb "$@"
-		;;
-	*)
-		fetch_native "$@"
-		;;
-	esac
+	if test "$(get_data_source $remote_nick)" = builtin; then
+		fetch_builtin "$@"
+	else
+		case "$remote" in
+		http://* | https://* | ftp://* | rsync://* )
+			fetch_dumb "$@"
+			;;
+		*)
+			fetch_native "$@"
+			;;
+		esac
+	fi
 }
 
 fetch_main "$reflist" || exit
@@ -353,8 +362,7 @@ case ",$#,$remote_nick," in
 	curr_branch=$(git-symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
 	merge_branches=$(git-config \
 		--get-all "branch.${curr_branch}.merge" | sort -u)
-	fetch_branches=$(get_remote_default_refs_for_fetch -n $remote_nick |
-		sed -e 's/:.*$//g' -e 's/^+//' | sort -u)
+	fetch_branches=$(get_remote_fetch_branches $remote_nick)
 	test -n "$merge_branches" && test -n "$fetch_branches" &&
 	merge_branches=$(printf '%s\n%s' "$merge_branches" "$fetch_branches" |
 		sort | uniq -d)
git-parse-remote.sh +21 -1
index 99e7184..4964810 100755
@@ -9,6 +9,9 @@ get_data_source () {
 	*/*)
 		echo ''
 		;;
+	.)
+		echo builtin
+		;;
 	*)
 		if test "$(git-config --get "remote.$1.url")"
 		then
@@ -31,6 +34,9 @@ get_remote_url () {
 	'')
 		echo "$1"
 		;;
+	builtin)
+		echo "$1"
+		;;
 	config)
 		git-config --get "remote.$1.url"
 		;;
@@ -57,7 +63,7 @@ get_default_remote () {
 get_remote_default_refs_for_push () {
 	data_source=$(get_data_source "$1")
 	case "$data_source" in
-	'' | branches)
+	'' | branches | builtin)
 		;; # no default push mapping, just send matching refs.
 	config)
 		git-config --get-all "remote.$1.push" ;;
@@ -128,6 +134,8 @@ get_remote_default_refs_for_fetch () {
 	case "$data_source" in
 	'')
 		set explicit "HEAD:" ;;
+	builtin)
+		set explicit ;;
 	config)
 		set $(expand_refs_wildcard "$1" \
 			$(git-config --get-all "remote.$1.fetch")) ;;
@@ -154,6 +162,18 @@ get_remote_default_refs_for_fetch () {
 	fi
 }
 
+# Returns list of sources
+get_remote_fetch_branches () {
+	data_source=$(get_data_source "$1")
+	case "$data_source" in
+	builtin)
+	        git-show-ref | sed -n 's,.*[      ]refs/,refs/,p' ;;
+	*)
+		get_remote_default_refs_for_fetch -n "$1" | \
+			sed -e 's/:.*$//g' -e 's/^+//' | sort -u ;;
+	esac
+}
+
 get_remote_refs_for_push () {
 	case "$#" in
 	0) die "internal error: get-remote-refs-for-push." ;;
t/t3200-branch.sh +6 -0
index 75c000a..9558bdb 100755
@@ -145,9 +145,15 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
      git-branch --no-track my2 local/master &&
+     git-config branch.autosetupmerge false &&
      ! test $(git-config branch.my2.remote) = local &&
      ! test $(git-config branch.my2.merge) = refs/heads/master'
 
+test_expect_success 'test local tracking setup' \
+    'git branch --track my6 s &&
+     test $(git-config branch.my6.remote) = . &&
+     test $(git-config branch.my6.merge) = refs/heads/s'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
t/t5520-pull.sh +13 -0
index 7eb3783..c424e5b 100755
@@ -29,5 +29,18 @@ test_expect_success 'checking the results' '
 	diff file cloned/file
 '
 
+test_expect_success 'test . as a remote' '
+
+	git branch copy master &&
+	git config branch.copy.remote . &&
+	git config branch.copy.merge refs/heads/master &&
+	echo updated >file &&
+	git commit -a -m updated &&
+	git checkout copy &&
+	test `cat file` = file &&
+	git pull &&
+	test `cat file` = updated
+'
+
 test_done
 
t/t9109-git-svn-pull.sh new file +31 -0
new file mode 100755
index 0000000..4d59f60
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Paolo Bonzini
+#
+
+test_description='git-svn pull test'
+. ./lib-git-svn.sh
+
+mkdir import
+
+cd import
+	echo Hello World > motd
+	svn import -m 'import for git-svn' . "$svnrepo" >/dev/null
+cd ..
+
+test_expect_success 'initialize git-svn and fetch' "
+	git-svn init $svnrepo
+	git-svn fetch"
+
+svn co $svnrepo test_wc > /dev/null
+cd test_wc
+	echo Goodbye World > motd
+	svn commit -m "another svn commit" > /dev/null
+cd ..
+
+test_expect_success 'fetch and pull latest from svn' \
+	'git-svn fetch &&
+	 git pull . remotes/git-svn &&
+	 grep Goodbye motd'
+
+test_done
#3 lore raw
Quoted 8 lines · Paolo Bonzini
 t/t9109-git-svn-pull.sh  |   31 +++++++++++++++++++++++++++++++
 8 files changed, 120 insertions(+), 27 deletions(-)

The failure, which I didn't see because I didn't have svn-perl bindings installed, was caused by my usage of "git-show-ref --heads" where I should have used plain "git-show-ref".

I include a smaller testcase for the same problem, t9109.

I'd prefer dropping the t9109 test, as it seems to be redundant. Existing t9101 seems to cover pulling from remote tracking branch maintained by git-svn already.

Also I'll be reverting a couple of patches from 'next', so this no longer applies. Would appreciate a re-spin.

Sorry about that.

#4 lore raw

PATCH 1/1 git-fetch, git-branch: Support local --track via a special remote `.'

This patch adds support for a dummy remote `.' to avoid having to declare a fake remote like

[remote "local"]
url = .
fetch = refs/heads/*:refs/heads/*

Such a builtin remote simplifies the operation of "git-fetch", which will populate FETCH_HEAD but will not pretend that two repositories are in use, will not create a thin pack, and will not perform any useless remapping of names. The speed improvement is around 20%, and it should improve more if "git-fetch" is converted to a builtin.

To this end, git-parse-remote is grown with a new kind of remote, `builtin'. In git-fetch.sh, we treat the builtin remote specially in that it needs no pack/store operations. In fact, doing git-fetch on a builtin remote will simply populate FETCH_HEAD appropriately.

The patch also improves of the --track/--no-track support, extending it so that branch.<name>.remote items referring `.' can be created. Finally, it fixes a typo in git-checkout.sh.

---
 Documentation/config.txt |    4 ++++
 builtin-branch.c         |   39 +++++++++++++++++++++++++--------------
 git-checkout.sh          |    2 +-
 git-fetch.sh             |   28 ++++++++++++++++++++--------
 git-parse-remote.sh      |   12 ++++++++++--
 t/t3200-branch.sh        |    6 ++++++
 t/t5520-pull.sh          |   13 +++++++++++++
 7 files changed, 79 insertions(+), 25 deletions(-)

> I'd prefer dropping the t9109 test, as it seems to be redundant.

As you wish.

> Also I'll be reverting a couple of patches from 'next', so this > no longer applies. Would appreciate a re-spin.
>
> Sorry about that.

No problem.

I must say that from a clarity point of view I preferred the older code. It did encapsulate fetching in a pretty easy to understand way. OTOH, my patch is smaller now.

Just my 0.02 cents, or something to remember once the git-fetch--tool conversion proceeds and possibly the merge logic is written in C.

Documentation/config.txt +4 -0
index aaae9ac..953acae 100644
@@ -272,6 +272,10 @@ branch.<name>.merge::
 	`git fetch`) to lookup the default branch for merging. Without
 	this option, `git pull` defaults to merge the first refspec fetched.
 	Specify multiple values to get an octopus merge.
+	If you wish to setup `git pull` so that it merges into <name> from
+	another branch in the local repository, you can point
+	branch.<name>.merge to the desired branch, and use the special setting
+	`.` (a period) for branch.<name>.remote.
 
 color.branch::
 	A boolean to enable/disable color in the output of
builtin-branch.c +25 -14
index 42b1ff1..14c4219 100644
@@ -372,9 +372,26 @@ static int get_remote_config(const char *key, const char *value)
 	return 0;
 }
 
-static void set_branch_defaults(const char *name, const char *real_ref)
+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)
+{
 	const char *slash = strrchr(real_ref, '/');
 
 	if (!slash)
@@ -384,21 +401,15 @@ static void set_branch_defaults(const char *name, const char *real_ref)
 	start_len = strlen(real_ref);
 	base_len = slash - 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) {
-		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);
-
+		set_branch_merge(name, config_remote, config_repo);
 		printf("Branch %s set up to track remote branch %s.\n",
 		       name, real_ref);
 	}
git-checkout.sh +1 -1
index 6caa9fd..b292ff0 100755
@@ -89,7 +89,7 @@ while [ "$#" != "0" ]; do
     esac
 done
 
-case "$new_branch,$track" in
+case "$newbranch,$track" in
 ,--*)
 	die "git checkout: --track and --no-track require -b"
 esac
git-fetch.sh +20 -8
index ebe6c33..88bf64d 100755
@@ -161,6 +161,14 @@ then
 	fi
 fi
 
+fetch_builtin () {
+    for ref in $(get_remote_default_refs_for_fetch "$remote_nick"); do
+      name=$(expr "z$ref" : 'z\([^:]*\):')
+      append_fetch_head $(git-rev-parse "$name") "$remote" \
+	  "$name" "$remote_nick" "$name" "" || exit
+    done
+}
+
 fetch_native () {
   eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
   eval "$eval"
@@ -296,14 +303,18 @@ fetch_dumb () {
 }
 
 fetch_main () {
-	case "$remote" in
-	http://* | https://* | ftp://* | rsync://* )
-		fetch_dumb "$@"
-		;;
-	*)
-		fetch_native "$@"
-		;;
-	esac
+	if test "$(get_data_source $remote_nick)" = builtin; then
+		fetch_builtin
+	else
+		case "$remote" in
+		http://* | https://* | ftp://* | rsync://* )
+			fetch_dumb "$@"
+			;;
+		*)
+			fetch_native "$@"
+			;;
+		esac
+	fi
 }
 
 fetch_main "$reflist" || exit
git-parse-remote.sh +10 -2
index c46131f..1b96ec6 100755
@@ -9,6 +9,9 @@ get_data_source () {
 	*/*)
 		echo ''
 		;;
+	.)
+		echo builtin
+		;;
 	*)
 		if test "$(git-config --get "remote.$1.url")"
 		then
@@ -31,6 +34,9 @@ get_remote_url () {
 	'')
 		echo "$1"
 		;;
+	builtin)
+		echo "$1"
+		;;
 	config)
 		git-config --get "remote.$1.url"
 		;;
@@ -57,7 +63,7 @@ get_default_remote () {
 get_remote_default_refs_for_push () {
 	data_source=$(get_data_source "$1")
 	case "$data_source" in
-	'' | branches)
+	'' | branches | builtin)
 		;; # no default push mapping, just send matching refs.
 	config)
 		git-config --get-all "remote.$1.push" ;;
@@ -163,6 +169,8 @@ get_remote_default_refs_for_fetch () {
 	case "$data_source" in
 	'')
 		echo "HEAD:" ;;
+	builtin)
+	        git-show-ref | sed -n 's,.*[      ]\(refs/.*\),\1:,p' ;;
 	config)
 		canon_refs_list_for_fetch -d "$1" \
 			$(git-config --get-all "remote.$1.fetch") ;;
@@ -177,7 +185,7 @@ get_remote_default_refs_for_fetch () {
 					}' "$GIT_DIR/remotes/$1")
 		;;
 	*)
-		die "internal error: get-remote-default-ref-for-push $1" ;;
+		die "internal error: get-remote-default-ref-for-fetch $1" ;;
 	esac
 }
 
t/t3200-branch.sh +6 -0
index 75c000a..9558bdb 100755
@@ -145,9 +145,15 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
      git-branch --no-track my2 local/master &&
+     git-config branch.autosetupmerge false &&
      ! test $(git-config branch.my2.remote) = local &&
      ! test $(git-config branch.my2.merge) = refs/heads/master'
 
+test_expect_success 'test local tracking setup' \
+    'git branch --track my6 s &&
+     test $(git-config branch.my6.remote) = . &&
+     test $(git-config branch.my6.merge) = refs/heads/s'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
t/t5520-pull.sh +12 -0
index 7eb3783..c424e5b 100755
@@ -29,5 +29,17 @@ test_expect_success 'checking the results' '
 	diff file cloned/file
 '
 
+test_expect_success 'test . as a remote' '
+	git branch copy master &&
+	git config branch.copy.remote . &&
+	git config branch.copy.merge refs/heads/master &&
+	echo updated >file &&
+	git commit -a -m updated &&
+	git checkout copy &&
+	test `cat file` = file &&
+	git pull &&
+	test `cat file` = updated
+'
+
 test_done
 
#5 lore raw
In reply to Paolo Bonzini

Paolo Bonzini <[email protected]> writes:

I've done it different, please see the mail:

Subject: [PATCH 2/3] git-fetch: Support the local remote "."

I've done it only modifying git-parse-remote.

[...]

Quoted 4 lines

The failure, which I didn't see because I didn't have svn-perl bindings installed, was caused by my usage of "git-show-ref --heads" where I should have used plain "git-show-ref".

But it should be test in t5515. I've added tests for this.

Thanks,

Santi