Add multi_ack_detailed capability to fetch-pack/upload-pack

When multi_ack_detailed is enabled the ACK continue messages returned
by the remote upload-pack are broken out to describe the different
states within the peer.  This permits the client to better understand
the server's in-memory state.

The fetch-pack/upload-pack protocol now looks like:

NAK
---------------------------------
  Always sent in response to "done" if there was no common base
  selected from the "have" lines (or no have lines were sent).

  * no multi_ack or multi_ack_detailed:

    Sent when the client has sent a pkt-line flush ("0000") and
    the server has not yet found a common base object.

  * either multi_ack or multi_ack_detailed:

    Always sent in response to a pkt-line flush.

ACK %s
-----------------------------------
  * no multi_ack or multi_ack_detailed:

    Sent in response to "have" when the object exists on the remote
    side and is therefore an object in common between the peers.
    The argument is the SHA-1 of the common object.

  * either multi_ack or multi_ack_detailed:

    Sent in response to "done" if there are common objects.
    The argument is the last SHA-1 determined to be common.

ACK %s continue
-----------------------------------
  * multi_ack only:

    Sent in response to "have".

    The remote side wants the client to consider this object as
    common, and immediately stop transmitting additional "have"
    lines for objects that are reachable from it.  The reason
    the client should stop is not given, but is one of the two
    cases below available under multi_ack_detailed.

ACK %s common
-----------------------------------
  * multi_ack_detailed only:

    Sent in response to "have".  Both sides have this object.
    Like with "ACK %s continue" above the client should stop
    sending have lines reachable for objects from the argument.

ACK %s ready
-----------------------------------
  * multi_ack_detailed only:

    Sent in response to "have".

    The client should stop transmitting objects which are reachable
    from the argument, and send "done" soon to get the objects.

    If the remote side has the specified object, it should
    first send an "ACK %s common" message prior to sending
    "ACK %s ready".

    Clients may still submit additional "have" lines if there are
    more side branches for the client to explore that might be added
    to the common set and reduce the number of objects to transfer.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Shawn O. Pearce 2009-10-30 17:47:25 -07:00 committed by Junio C Hamano
parent 28754ab5f0
commit 78affc49de
2 changed files with 50 additions and 22 deletions

View File

@ -157,7 +157,15 @@ static const unsigned char *get_rev(void)
return commit->object.sha1; return commit->object.sha1;
} }
static int get_ack(int fd, unsigned char *result_sha1) enum ack_type {
NAK = 0,
ACK,
ACK_continue,
ACK_common,
ACK_ready
};
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
{ {
static char line[1000]; static char line[1000];
int len = packet_read_line(fd, line, sizeof(line)); int len = packet_read_line(fd, line, sizeof(line));
@ -167,12 +175,16 @@ static int get_ack(int fd, unsigned char *result_sha1)
if (line[len-1] == '\n') if (line[len-1] == '\n')
line[--len] = 0; line[--len] = 0;
if (!strcmp(line, "NAK")) if (!strcmp(line, "NAK"))
return 0; return NAK;
if (!prefixcmp(line, "ACK ")) { if (!prefixcmp(line, "ACK ")) {
if (!get_sha1_hex(line+4, result_sha1)) { if (!get_sha1_hex(line+4, result_sha1)) {
if (strstr(line+45, "continue")) if (strstr(line+45, "continue"))
return 2; return ACK_continue;
return 1; if (strstr(line+45, "common"))
return ACK_common;
if (strstr(line+45, "ready"))
return ACK_ready;
return ACK;
} }
} }
die("git fetch_pack: expected ACK/NAK, got '%s'", line); die("git fetch_pack: expected ACK/NAK, got '%s'", line);
@ -218,7 +230,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
remote_hex = sha1_to_hex(remote); remote_hex = sha1_to_hex(remote);
if (!fetching) { if (!fetching) {
struct strbuf c = STRBUF_INIT; struct strbuf c = STRBUF_INIT;
if (multi_ack) strbuf_addstr(&c, " multi_ack"); if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed");
if (multi_ack == 1) strbuf_addstr(&c, " multi_ack");
if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k"); if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k");
if (use_sideband == 1) strbuf_addstr(&c, " side-band"); if (use_sideband == 1) strbuf_addstr(&c, " side-band");
if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack"); if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack");
@ -298,18 +311,23 @@ static int find_common(int fd[2], unsigned char *result_sha1,
if (args.verbose && ack) if (args.verbose && ack)
fprintf(stderr, "got ack %d %s\n", ack, fprintf(stderr, "got ack %d %s\n", ack,
sha1_to_hex(result_sha1)); sha1_to_hex(result_sha1));
if (ack == 1) { switch (ack) {
case ACK:
flushes = 0; flushes = 0;
multi_ack = 0; multi_ack = 0;
retval = 0; retval = 0;
goto done; goto done;
} else if (ack == 2) { case ACK_common:
case ACK_ready:
case ACK_continue: {
struct commit *commit = struct commit *commit =
lookup_commit(result_sha1); lookup_commit(result_sha1);
mark_common(commit, 0, 1); mark_common(commit, 0, 1);
retval = 0; retval = 0;
in_vain = 0; in_vain = 0;
got_continue = 1; got_continue = 1;
break;
}
} }
} while (ack); } while (ack);
flushes--; flushes--;
@ -336,7 +354,7 @@ done:
if (args.verbose) if (args.verbose)
fprintf(stderr, "got ack (%d) %s\n", ack, fprintf(stderr, "got ack (%d) %s\n", ack,
sha1_to_hex(result_sha1)); sha1_to_hex(result_sha1));
if (ack == 1) if (ack == ACK)
return 0; return 0;
multi_ack = 1; multi_ack = 1;
continue; continue;
@ -618,7 +636,12 @@ static struct ref *do_fetch_pack(int fd[2],
if (is_repository_shallow() && !server_supports("shallow")) if (is_repository_shallow() && !server_supports("shallow"))
die("Server does not support shallow clients"); die("Server does not support shallow clients");
if (server_supports("multi_ack")) { if (server_supports("multi_ack_detailed")) {
if (args.verbose)
fprintf(stderr, "Server supports multi_ack_detailed\n");
multi_ack = 2;
}
else if (server_supports("multi_ack")) {
if (args.verbose) if (args.verbose)
fprintf(stderr, "Server supports multi_ack\n"); fprintf(stderr, "Server supports multi_ack\n");
multi_ack = 1; multi_ack = 1;

View File

@ -498,7 +498,7 @@ static int get_common_commits(void)
{ {
static char line[1000]; static char line[1000];
unsigned char sha1[20]; unsigned char sha1[20];
char hex[41], last_hex[41]; char last_hex[41];
save_commit_buffer = 0; save_commit_buffer = 0;
@ -515,19 +515,22 @@ static int get_common_commits(void)
if (!prefixcmp(line, "have ")) { if (!prefixcmp(line, "have ")) {
switch (got_sha1(line+5, sha1)) { switch (got_sha1(line+5, sha1)) {
case -1: /* they have what we do not */ case -1: /* they have what we do not */
if (multi_ack && ok_to_give_up()) if (multi_ack && ok_to_give_up()) {
packet_write(1, "ACK %s continue\n", const char *hex = sha1_to_hex(sha1);
sha1_to_hex(sha1)); if (multi_ack == 2)
packet_write(1, "ACK %s ready\n", hex);
else
packet_write(1, "ACK %s continue\n", hex);
}
break; break;
default: default:
memcpy(hex, sha1_to_hex(sha1), 41); memcpy(last_hex, sha1_to_hex(sha1), 41);
if (multi_ack) { if (multi_ack == 2)
const char *msg = "ACK %s continue\n"; packet_write(1, "ACK %s common\n", last_hex);
packet_write(1, msg, hex); else if (multi_ack)
memcpy(last_hex, hex, 41); packet_write(1, "ACK %s continue\n", last_hex);
}
else if (have_obj.nr == 1) else if (have_obj.nr == 1)
packet_write(1, "ACK %s\n", hex); packet_write(1, "ACK %s\n", last_hex);
break; break;
} }
continue; continue;
@ -587,7 +590,9 @@ static void receive_needs(void)
get_sha1_hex(line+5, sha1_buf)) get_sha1_hex(line+5, sha1_buf))
die("git upload-pack: protocol error, " die("git upload-pack: protocol error, "
"expected to get sha, not '%s'", line); "expected to get sha, not '%s'", line);
if (strstr(line+45, "multi_ack")) if (strstr(line+45, "multi_ack_detailed"))
multi_ack = 2;
else if (strstr(line+45, "multi_ack"))
multi_ack = 1; multi_ack = 1;
if (strstr(line+45, "thin-pack")) if (strstr(line+45, "thin-pack"))
use_thin_pack = 1; use_thin_pack = 1;
@ -681,7 +686,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
{ {
static const char *capabilities = "multi_ack thin-pack side-band" static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow no-progress" " side-band-64k ofs-delta shallow no-progress"
" include-tag"; " include-tag multi_ack_detailed";
struct object *o = parse_object(sha1); struct object *o = parse_object(sha1);
if (!o) if (!o)