# [PATCH] fetch-pack: start multi-head pulling.

7 messages from 2005-08-12 to 2005-08-13. Participants: Junio C Hamano, Linus Torvalds, Johannes Schindelin, Matthias Urlichs.
Thread: https://gitlist.dev/t/1480

## Junio C Hamano, 2005-08-12 09:41

Subject: [PATCH] fetch-pack: start multi-head pulling.
Message-ID: <7vwtmr4hm5.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vwtmr4hm5.fsf%40assigned-by-dhcp.cox.net

```
This is a beginning of resurrecting the multi-head pulling support
for git-fetch-pack command.  The git-fetch-script wrapper still
only knows about fetching a single head, without renaming, so it is
not very useful unless you directly call git-fetch-pack itself yet.

It also fixes a longstanding obsolete description of how the command
discovers the list of local commits.
---

Linus, I need a bit of guidance from you about this one; an
ancient commit 4f7770c87ce3c302e1639a7737a6d2531fe4b160 removed
the multi-head support fetch-pack once had, labelling it as "a
misguided attempt", and I would like to know if I am making the
same misguided attempt again.  This update actually makes
clone-pack almost redundant.


 Documentation/git-fetch-pack.txt |   18 +++++-------
 fetch-pack.c                     |   56 +++++++++++++++++++++++++++-----------
 git-fetch-script                 |    4 +++
 3 files changed, 51 insertions(+), 27 deletions(-)

880df673e412bae9603782833d8a80a7c7c59769
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -9,19 +9,19 @@ git-fetch-pack - Receive missing objects
 
 SYNOPSIS
 --------
-git-fetch-pack [-q] [--exec=<git-upload-pack>] [<host>:]<directory> [<head>...] < <commit-list>
+git-fetch-pack [-q] [--exec=<git-upload-pack>] [<host>:]<directory> [<refs>...]
 
 DESCRIPTION
 -----------
 Invokes 'git-upload-pack' on a potentially remote repository,
 and asks it to send objects missing from this repository, to
 update the named heads.  The list of commits available locally
-is fed from the standard input, to be sent to 'git-upload-pack'
-running on the other end.
+is found out by scanning local $GIT_DIR/refs/ and sent to
+'git-upload-pack' running on the other end.
 
-This command can be used only when the local side has a common
-(ancestor) commit with the remote head that is being pulled
-from.  Use 'git-clone-pack' for that.
+This command degenerates to download everything to complete the
+asked refs from the remote side when the local side does not
+have a common ancestor commit.
 
 
 OPTIONS
@@ -50,15 +50,11 @@ OPTIONS
 <directory>::
 	The repository to sync from.
 
-<head>...::
+<refs>...::
 	The remote heads to update from. This is relative to
 	$GIT_DIR (e.g. "HEAD", "refs/heads/master").  When
 	unspecified, update from all heads the remote side has.
 
-	However the program refuses to work if more than one
-	remote head matches the specified heads.  I am not sure
-	what this means... Help!!!!!
-
 
 Author
 ------
diff --git a/fetch-pack.c b/fetch-pack.c
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -4,10 +4,13 @@
 #include <sys/wait.h>
 
 static int quiet;
-static const char fetch_pack_usage[] = "git-fetch-pack [-q] [--exec=upload-pack] [host:]directory [heads]* < mycommitlist";
+static int verbose;
+static const char fetch_pack_usage[] =
+"git-fetch-pack [-q] [-v] [--exec=upload-pack] [host:]directory <refs>...";
 static const char *exec = "git-upload-pack";
 
-static int find_common(int fd[2], unsigned char *result_sha1, unsigned char *remote)
+static int find_common(int fd[2], unsigned char *result_sha1,
+		       struct ref *refs)
 {
 	static char line[1000];
 	int count = 0, flushes = 0, retval;
@@ -16,7 +19,16 @@ static int find_common(int fd[2], unsign
 	revs = popen("git-rev-list $(git-rev-parse --all)", "r");
 	if (!revs)
 		die("unable to run 'git-rev-list'");
-	packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
+
+	while (refs) {
+		unsigned char *remote = refs->old_sha1;
+		if (verbose)
+			fprintf(stderr,
+				"want %s (%s)\n", sha1_to_hex(remote),
+				refs->name);
+		packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
+		refs = refs->next;
+	}
 	packet_flush(fd[1]);
 	flushes = 1;
 	retval = -1;
@@ -25,6 +37,8 @@ static int find_common(int fd[2], unsign
 		if (get_sha1_hex(line, sha1))
 			die("git-fetch-pack: expected object name, got crud");
 		packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
+		if (verbose)
+			fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
 		if (!(31 & ++count)) {
 			packet_flush(fd[1]);
 			flushes++;
@@ -38,6 +52,8 @@ static int find_common(int fd[2], unsign
 			if (get_ack(fd[0], result_sha1)) {
 				flushes = 0;
 				retval = 0;
+				if (verbose)
+					fprintf(stderr, "got ack\n");
 				break;
 			}
 			flushes--;
@@ -45,19 +61,19 @@ static int find_common(int fd[2], unsign
 	}
 	pclose(revs);
 	packet_write(fd[1], "done\n");
+	if (verbose)
+		fprintf(stderr, "done\n");
 	while (flushes) {
 		flushes--;
-		if (get_ack(fd[0], result_sha1))
+		if (get_ack(fd[0], result_sha1)) {
+			if (verbose)
+				fprintf(stderr, "got ack\n");
 			return 0;
+		}
 	}
 	return retval;
 }
 
-/*
- * Eventually we'll want to be able to fetch multiple heads.
- *
- * Right now we'll just require a single match.
- */
 static int fetch_pack(int fd[2], int nr_match, char **match)
 {
 	struct ref *ref;
@@ -70,12 +86,8 @@ static int fetch_pack(int fd[2], int nr_
 		packet_flush(fd[1]);
 		die("no matching remote head");
 	}
-	if (ref->next) {
-		packet_flush(fd[1]);
-		die("multiple remote heads");
-	}
-	if (find_common(fd, sha1, ref->old_sha1) < 0)
-		die("git-fetch-pack: no common commits");
+	if (find_common(fd, sha1, ref) < 0)
+		fprintf(stderr, "warning: no common commits\n");
 	pid = fork();
 	if (pid < 0)
 		die("git-fetch-pack: unable to fork off git-unpack-objects");
@@ -97,7 +109,11 @@ static int fetch_pack(int fd[2], int nr_
 		int code = WEXITSTATUS(status);
 		if (code)
 			die("git-unpack-objects died with error code %d", code);
-		puts(sha1_to_hex(ref->old_sha1));
+		while (ref) {
+			printf("%s %s\n",
+			       sha1_to_hex(ref->old_sha1), ref->name);
+			ref = ref->next;
+		}
 		return 0;
 	}
 	if (WIFSIGNALED(status)) {
@@ -124,6 +140,14 @@ int main(int argc, char **argv)
 				exec = arg + 7;
 				continue;
 			}
+			if (!strcmp("-q", arg)) {
+				quiet = 1;
+				continue;
+			}
+			if (!strcmp("-v", arg)) {
+				verbose = 1;
+				continue;
+			}
 			usage(fetch_pack_usage);
 		}
 		dest = arg;
diff --git a/git-fetch-script b/git-fetch-script
--- a/git-fetch-script
+++ b/git-fetch-script
@@ -31,6 +31,10 @@ rsync://*)
 	;;
 *)
 	head=$(git-fetch-pack "$merge_repo" "$merge_head")
+	if h=`expr "$head" : '\([^ ][^ ]*\) '`
+	then
+	    head=$h
+	fi
 	;;
 esac || exit 1
 

```

## Linus Torvalds, 2005-08-12 16:12

Subject: Re: [PATCH] fetch-pack: start multi-head pulling.
Message-ID: <Pine.LNX.4.58.0508120858420.3295@g5.osdl.org>
URL: https://gitlist.dev/e/Pine.LNX.4.58.0508120858420.3295%40g5.osdl.org
In-Reply-To: <7vwtmr4hm5.fsf@assigned-by-dhcp.cox.net>

```


On Fri, 12 Aug 2005, Junio C Hamano wrote:
>
> Linus, I need a bit of guidance from you about this one; an
> ancient commit 4f7770c87ce3c302e1639a7737a6d2531fe4b160 removed
> the multi-head support fetch-pack once had, labelling it as "a
> misguided attempt", and I would like to know if I am making the
> same misguided attempt again.  This update actually makes
> clone-pack almost redundant.

I like your version.

My misguided attempt was not the ability to pull multiple heads, but what 
to _do_ with them. I originally envisioned something more "git-push-pack" 
like, where it would pull multiple heads into real references, and _that_ 
was the misguided part. I then ended up just printing it out to stdout, 
and that made it "one ref only".

Your version where you just print out multiple heads with names is, I 
think, the right thing to do.

The shell script looks a bit unreadable to me, personally.

>  	head=$(git-fetch-pack "$merge_repo" "$merge_head")
> +	if h=`expr "$head" : '\([^ ][^ ]*\) '`
> +	then
> +	    head=$h
> +	fi

Back-ticks, and the ":" operator to "expr". You really _are_ old-fashioned 
when it comes to shell ;)

Wouldn't it be simpler/cleaner to just do

	head=($(git-fetch-pack "$merge_repo" "$merge_head"))

which gets you "$head" being the same as ${head[0]}, namely the SHA1 you 
want (and if you ever implement multi-head pulling, you'll have it all in 
${head[..]}..)

			Linus

```

## Johannes Schindelin, 2005-08-12 16:19

Subject: Re: [PATCH] fetch-pack: start multi-head pulling.
Message-ID: <Pine.LNX.4.63.0508121818230.26488@wgmdd8.biozentrum.uni-wuerzburg.de>
URL: https://gitlist.dev/e/Pine.LNX.4.63.0508121818230.26488%40wgmdd8.biozentrum.uni-wuerzburg.de
In-Reply-To: <Pine.LNX.4.58.0508120858420.3295@g5.osdl.org>

```
Hi,

On Fri, 12 Aug 2005, Linus Torvalds wrote:

> Wouldn't it be simpler/cleaner to just do
> 
> 	head=($(git-fetch-pack "$merge_repo" "$merge_head"))

I seem to remember Junio does not like bash arrays... And in a recent 
commit message, he even admits to using something different than bash!

Ciao,
Dscho

```

## Junio C Hamano, 2005-08-12 16:43

Subject: Re: [PATCH] fetch-pack: start multi-head pulling.
Message-ID: <7vk6irumv3.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vk6irumv3.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <Pine.LNX.4.63.0508121818230.26488@wgmdd8.biozentrum.uni-wuerzburg.de>

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

> I seem to remember Junio does not like bash arrays... And in a recent 
> commit message, he even admits to using something different than bash!

Correct and somewhat misleading.  My usual shell is bash but
from time to time I try to run things with (d)ash to see how far
I strayed from the common denominator.

Yes, I am old fashioned.

I have been trying, admittably perhaps not very successfully, to
stay away from bashism in the core GIT scripts.  I knew we
started using shell array when Linus did the git-diff-script,
but I have been hoping somebody coming from other platforms
(hello, Solaris and Darwin guys) would send in patches if they
cared enough.  So I try to avoid making their life harder than
already is.

Here is a semi off-topic joke, hopefully to give you a better
sense of where I am coming from.

    Anybody can start pretending to be an old fashioned shell
    programmer by adhering to the following simple rules:

     1. Never, ever say [ ... ]; we old-timers always spell that
        "test".

     2. Never use "test" when "case" would do; this comes from
        the days when test was not built-in and machines were
        small.  We tried to reduce number of forks in our
        scripts.  We also never use echo piped to grep when
        "case" would do.

     3. We tend to write && || more often than we use if
        ... then ... fi, especially for simple things.  We do
        not use parantheses () to introduce command grouping
        lightly.  We usually say braces {} and use parentheses
        only when we do want a subshell.

     4. Say ${var-default} more often than ${var:-default};
        colon form is more recent invention, and we old-timers
        tend to be more careful to consider the possibility
        that, when a user says this variable is empty, the user
        really means it.

     5. We are still allowed to use $() form in preference to
        ``, because it is clearly superior (it can nest) and
        should have been the way to spell it from the beginning.

-jc

```

## Linus Torvalds, 2005-08-12 17:12

Subject: Re: [PATCH] fetch-pack: start multi-head pulling.
Message-ID: <Pine.LNX.4.58.0508121000320.3295@g5.osdl.org>
URL: https://gitlist.dev/e/Pine.LNX.4.58.0508121000320.3295%40g5.osdl.org
In-Reply-To: <Pine.LNX.4.63.0508121818230.26488@wgmdd8.biozentrum.uni-wuerzburg.de>

```


On Fri, 12 Aug 2005, Johannes Schindelin wrote:
> 
> I seem to remember Junio does not like bash arrays... 

Well, fair enough, although they aren't really "bash" arrays, they
appeared in other shells before "bash". 

At least ksh and zsh have them, although the zsh implementation seems to
be pretty broken (indices start at 1, and $var acts like $var[*] etc).

Afaik, ksh is actually the origin of the thing.

		Linus

```

## Matthias Urlichs, 2005-08-13 03:25

Subject: Re: [PATCH] fetch-pack: start multi-head pulling.
Message-ID: <pan.2005.08.13.03.25.28.489523@smurf.noris.de>
URL: https://gitlist.dev/e/pan.2005.08.13.03.25.28.489523%40smurf.noris.de
In-Reply-To: <7vk6irumv3.fsf@assigned-by-dhcp.cox.net>

```
Hi, Junio C Hamano wrote:

> I have been trying, admittably perhaps not very successfully, to
> stay away from bashism in the core GIT scripts.

We should probably use #!/bin/bash for scripts with bashisms...

-- 
Matthias Urlichs   |   {M:U} IT Design @ m-u-it.de   |  smurf@smurf.noris.de
Disclaimer: The quote was selected randomly. Really. | http://smurf.noris.de
 - -
He has more goodness in his little finger Than you have in your whole
body.
					-- Jonathan Swift

```

## Junio C Hamano, 2005-08-13 09:09

Subject: Re: [PATCH] fetch-pack: start multi-head pulling.
Message-ID: <7vd5oip5i2.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vd5oip5i2.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <pan.2005.08.13.03.25.28.489523@smurf.noris.de>

```
Matthias Urlichs <smurf@smurf.noris.de> writes:

>> I have been trying, admittably perhaps not very successfully, to
>> stay away from bashism in the core GIT scripts.
>
> We should probably use #!/bin/bash for scripts with bashisms...

As a fall-back position, yes, but I'd rather see trivial
bashisms eradicated when the rewrite does not make things too
complicated.

```
