summaryrefslogtreecommitdiff
path: root/builtin-fetch-pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin-fetch-pack.c')
-rw-r--r--builtin-fetch-pack.c252
1 files changed, 200 insertions, 52 deletions
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 273239af3b..8ed4a6feaa 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -13,6 +13,7 @@
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
static int unpack_limit = 100;
+static int prefer_ofs_delta = 1;
static struct fetch_pack_args args = {
/* .uploadpack = */ "git-upload-pack",
};
@@ -111,7 +112,7 @@ static void mark_common(struct commit *commit,
Get the next rev to send, ignoring the common.
*/
-static const unsigned char* get_rev(void)
+static const unsigned char *get_rev(void)
{
struct commit *commit = NULL;
@@ -156,6 +157,66 @@ static const unsigned char* get_rev(void)
return commit->object.sha1;
}
+enum ack_type {
+ NAK = 0,
+ ACK,
+ ACK_continue,
+ ACK_common,
+ ACK_ready
+};
+
+static void consume_shallow_list(int fd)
+{
+ if (args.stateless_rpc && args.depth > 0) {
+ /* If we sent a depth we will get back "duplicate"
+ * shallow and unshallow commands every time there
+ * is a block of have lines exchanged.
+ */
+ char line[1000];
+ while (packet_read_line(fd, line, sizeof(line))) {
+ if (!prefixcmp(line, "shallow "))
+ continue;
+ if (!prefixcmp(line, "unshallow "))
+ continue;
+ die("git fetch-pack: expected shallow list");
+ }
+ }
+}
+
+static enum ack_type get_ack(int fd, unsigned char *result_sha1)
+{
+ static char line[1000];
+ int len = packet_read_line(fd, line, sizeof(line));
+
+ if (!len)
+ die("git fetch-pack: expected ACK/NAK, got EOF");
+ if (line[len-1] == '\n')
+ line[--len] = 0;
+ if (!strcmp(line, "NAK"))
+ return NAK;
+ if (!prefixcmp(line, "ACK ")) {
+ if (!get_sha1_hex(line+4, result_sha1)) {
+ if (strstr(line+45, "continue"))
+ return ACK_continue;
+ 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);
+}
+
+static void send_request(int fd, struct strbuf *buf)
+{
+ if (args.stateless_rpc) {
+ send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
+ packet_flush(fd);
+ } else
+ safe_write(fd, buf->buf, buf->len);
+}
+
static int find_common(int fd[2], unsigned char *result_sha1,
struct ref *refs)
{
@@ -164,7 +225,11 @@ static int find_common(int fd[2], unsigned char *result_sha1,
const unsigned char *sha1;
unsigned in_vain = 0;
int got_continue = 0;
+ struct strbuf req_buf = STRBUF_INIT;
+ size_t state_len = 0;
+ if (args.stateless_rpc && multi_ack == 1)
+ die("--stateless-rpc requires multi_ack_detailed");
if (marked)
for_each_ref(clear_marks, NULL);
marked = 1;
@@ -174,6 +239,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
fetching = 0;
for ( ; refs ; refs = refs->next) {
unsigned char *remote = refs->old_sha1;
+ const char *remote_hex;
struct object *o;
/*
@@ -191,34 +257,43 @@ static int find_common(int fd[2], unsigned char *result_sha1,
continue;
}
- if (!fetching)
- packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
- sha1_to_hex(remote),
- (multi_ack ? " multi_ack" : ""),
- (use_sideband == 2 ? " side-band-64k" : ""),
- (use_sideband == 1 ? " side-band" : ""),
- (args.use_thin_pack ? " thin-pack" : ""),
- (args.no_progress ? " no-progress" : ""),
- (args.include_tag ? " include-tag" : ""),
- " ofs-delta");
- else
- packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
+ remote_hex = sha1_to_hex(remote);
+ if (!fetching) {
+ struct strbuf c = STRBUF_INIT;
+ 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 == 1) strbuf_addstr(&c, " side-band");
+ if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack");
+ if (args.no_progress) strbuf_addstr(&c, " no-progress");
+ if (args.include_tag) strbuf_addstr(&c, " include-tag");
+ if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta");
+ packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
+ strbuf_release(&c);
+ } else
+ packet_buf_write(&req_buf, "want %s\n", remote_hex);
fetching++;
}
+
+ if (!fetching) {
+ strbuf_release(&req_buf);
+ packet_flush(fd[1]);
+ return 1;
+ }
+
if (is_repository_shallow())
- write_shallow_commits(fd[1], 1);
+ write_shallow_commits(&req_buf, 1);
if (args.depth > 0)
- packet_write(fd[1], "deepen %d", args.depth);
- packet_flush(fd[1]);
- if (!fetching)
- return 1;
+ packet_buf_write(&req_buf, "deepen %d", args.depth);
+ packet_buf_flush(&req_buf);
+ state_len = req_buf.len;
if (args.depth > 0) {
char line[1024];
unsigned char sha1[20];
- int len;
- while ((len = packet_read_line(fd[0], line, sizeof(line)))) {
+ send_request(fd[1], &req_buf);
+ while (packet_read_line(fd[0], line, sizeof(line))) {
if (!prefixcmp(line, "shallow ")) {
if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line);
@@ -239,45 +314,73 @@ static int find_common(int fd[2], unsigned char *result_sha1,
}
die("expected shallow/unshallow, got %s", line);
}
+ } else if (!args.stateless_rpc)
+ send_request(fd[1], &req_buf);
+
+ if (!args.stateless_rpc) {
+ /* If we aren't using the stateless-rpc interface
+ * we don't need to retain the headers.
+ */
+ strbuf_setlen(&req_buf, 0);
+ state_len = 0;
}
flushes = 0;
retval = -1;
while ((sha1 = get_rev())) {
- packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
+ packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
if (args.verbose)
fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
in_vain++;
if (!(31 & ++count)) {
int ack;
- packet_flush(fd[1]);
+ packet_buf_flush(&req_buf);
+ send_request(fd[1], &req_buf);
+ strbuf_setlen(&req_buf, state_len);
flushes++;
/*
* We keep one window "ahead" of the other side, and
* will wait for an ACK only on the next one
*/
- if (count == 32)
+ if (!args.stateless_rpc && count == 32)
continue;
+ consume_shallow_list(fd[0]);
do {
ack = get_ack(fd[0], result_sha1);
if (args.verbose && ack)
fprintf(stderr, "got ack %d %s\n", ack,
sha1_to_hex(result_sha1));
- if (ack == 1) {
+ switch (ack) {
+ case ACK:
flushes = 0;
multi_ack = 0;
retval = 0;
goto done;
- } else if (ack == 2) {
+ case ACK_common:
+ case ACK_ready:
+ case ACK_continue: {
struct commit *commit =
lookup_commit(result_sha1);
+ if (args.stateless_rpc
+ && ack == ACK_common
+ && !(commit->object.flags & COMMON)) {
+ /* We need to replay the have for this object
+ * on the next RPC request so the peer knows
+ * it is in common with us.
+ */
+ const char *hex = sha1_to_hex(result_sha1);
+ packet_buf_write(&req_buf, "have %s\n", hex);
+ state_len = req_buf.len;
+ }
mark_common(commit, 0, 1);
retval = 0;
in_vain = 0;
got_continue = 1;
+ break;
+ }
}
} while (ack);
flushes--;
@@ -289,20 +392,24 @@ static int find_common(int fd[2], unsigned char *result_sha1,
}
}
done:
- packet_write(fd[1], "done\n");
+ packet_buf_write(&req_buf, "done\n");
+ send_request(fd[1], &req_buf);
if (args.verbose)
fprintf(stderr, "done\n");
if (retval != 0) {
multi_ack = 0;
flushes++;
}
+ strbuf_release(&req_buf);
+
+ consume_shallow_list(fd[0]);
while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_sha1);
if (ack) {
if (args.verbose)
fprintf(stderr, "got ack (%d) %s\n", ack,
sha1_to_hex(result_sha1));
- if (ack == 1)
+ if (ack == ACK)
return 0;
multi_ack = 1;
continue;
@@ -483,7 +590,9 @@ static int sideband_demux(int fd, void *data)
{
int *xd = data;
- return recv_sideband("fetch-pack", xd[0], fd, 2);
+ int ret = recv_sideband("fetch-pack", xd[0], fd);
+ close(fd);
+ return ret;
}
static int get_pack(int xd[2], char **pack_lockfile)
@@ -540,7 +649,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
*av++ = "--fix-thin";
if (args.lock_pack || unpack_limit) {
int s = sprintf(keep_arg,
- "--keep=fetch-pack %d on ", getpid());
+ "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
*av++ = keep_arg;
@@ -582,7 +691,12 @@ static struct ref *do_fetch_pack(int fd[2],
if (is_repository_shallow() && !server_supports("shallow"))
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)
fprintf(stderr, "Server supports multi_ack\n");
multi_ack = 1;
@@ -597,6 +711,11 @@ static struct ref *do_fetch_pack(int fd[2],
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
}
+ if (server_supports("ofs-delta")) {
+ if (args.verbose)
+ fprintf(stderr, "Server supports ofs-delta\n");
+ } else
+ prefer_ofs_delta = 0;
if (everything_local(&ref, nr_match, match)) {
packet_flush(fd[1]);
goto all_done;
@@ -606,10 +725,12 @@ static struct ref *do_fetch_pack(int fd[2],
/* When cloning, it is not unusual to have
* no common commit.
*/
- fprintf(stderr, "warning: no common commits\n");
+ warning("no common commits");
+ if (args.stateless_rpc)
+ packet_flush(fd[1]);
if (get_pack(fd, pack_lockfile))
- die("git-fetch-pack: fetch failed.");
+ die("git fetch-pack: fetch failed.");
all_done:
return ref;
@@ -649,6 +770,11 @@ static int fetch_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
+ prefer_ofs_delta = git_config_bool(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -673,6 +799,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
struct ref *ref = NULL;
char *dest = NULL, **heads;
int fd[2];
+ char *pack_lockfile = NULL;
+ char **pack_lockfile_ptr = NULL;
struct child_process *conn;
nr_heads = 0;
@@ -722,6 +850,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.no_progress = 1;
continue;
}
+ if (!strcmp("--stateless-rpc", arg)) {
+ args.stateless_rpc = 1;
+ continue;
+ }
+ if (!strcmp("--lock-pack", arg)) {
+ args.lock_pack = 1;
+ pack_lockfile_ptr = &pack_lockfile;
+ continue;
+ }
usage(fetch_pack_usage);
}
dest = (char *)arg;
@@ -732,25 +869,33 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
if (!dest)
usage(fetch_pack_usage);
- conn = git_connect(fd, (char *)dest, args.uploadpack,
- args.verbose ? CONNECT_VERBOSE : 0);
- if (conn) {
- get_remote_heads(fd[0], &ref, 0, NULL, 0);
-
- ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
- close(fd[0]);
- close(fd[1]);
- if (finish_connect(conn))
- ref = NULL;
+ if (args.stateless_rpc) {
+ conn = NULL;
+ fd[0] = 0;
+ fd[1] = 1;
} else {
- ref = NULL;
+ conn = git_connect(fd, (char *)dest, args.uploadpack,
+ args.verbose ? CONNECT_VERBOSE : 0);
}
+
+ get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
+
+ ref = fetch_pack(&args, fd, conn, ref, dest,
+ nr_heads, heads, pack_lockfile_ptr);
+ if (pack_lockfile) {
+ printf("lock %s\n", pack_lockfile);
+ fflush(stdout);
+ }
+ close(fd[0]);
+ close(fd[1]);
+ if (finish_connect(conn))
+ ref = NULL;
ret = !ref;
if (!ret && nr_heads) {
/* If the heads to pull were given, we should have
* consumed all of them by matching the remote.
- * Otherwise, 'git-fetch remote no-such-ref' would
+ * Otherwise, 'git fetch remote no-such-ref' would
* silently succeed without issuing an error.
*/
for (i = 0; i < nr_heads; i++)
@@ -780,7 +925,8 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
struct ref *ref_cpy;
fetch_pack_setup();
- memcpy(&args, my_args, sizeof(args));
+ if (&args != my_args)
+ memcpy(&args, my_args, sizeof(args));
if (args.depth > 0) {
if (stat(git_path("shallow"), &st))
st.st_mtime = 0;
@@ -796,30 +942,32 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
if (args.depth > 0) {
struct cache_time mtime;
+ struct strbuf sb = STRBUF_INIT;
char *shallow = git_path("shallow");
int fd;
mtime.sec = st.st_mtime;
-#ifdef USE_NSEC
- mtime.usec = st.st_mtim.usec;
-#endif
+ mtime.nsec = ST_MTIME_NSEC(st);
if (stat(shallow, &st)) {
if (mtime.sec)
die("shallow file was removed during fetch");
} else if (st.st_mtime != mtime.sec
#ifdef USE_NSEC
- || st.st_mtim.usec != mtime.usec
+ || ST_MTIME_NSEC(st) != mtime.nsec
#endif
)
die("shallow file was changed during fetch");
- fd = hold_lock_file_for_update(&lock, shallow, 1);
- if (!write_shallow_commits(fd, 0)) {
- unlink(shallow);
+ fd = hold_lock_file_for_update(&lock, shallow,
+ LOCK_DIE_ON_ERROR);
+ if (!write_shallow_commits(&sb, 0)
+ || write_in_full(fd, sb.buf, sb.len) != sb.len) {
+ unlink_or_warn(shallow);
rollback_lock_file(&lock);
} else {
commit_lock_file(&lock);
}
+ strbuf_release(&sb);
}
reprepare_packed_git();