Merge branch 'nd/versioncmp-prereleases'
[gitweb.git] / fetch-pack.c
index fab5e801755a9152aa19baf72b609052e1b32035..48526aa54bbb8458a10fc178f4fa5a429b77bc87 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "commit.h"
@@ -13,6 +14,7 @@
 #include "transport.h"
 #include "version.h"
 #include "prio-queue.h"
+#include "sha1-array.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -25,6 +27,7 @@ static int agent_supported;
 static struct lock_file shallow_lock;
 static const char *alternate_shallow_file;
 
+/* Remember to update object flag allocation in object.h */
 #define COMPLETE       (1U << 0)
 #define COMMON         (1U << 1)
 #define COMMON_REF     (1U << 2)
@@ -47,9 +50,8 @@ static void rev_list_push(struct commit *commit, int mark)
        if (!(commit->object.flags & mark)) {
                commit->object.flags |= mark;
 
-               if (!(commit->object.parsed))
-                       if (parse_commit(commit))
-                               return;
+               if (parse_commit(commit))
+                       return;
 
                prio_queue_put(&rev_list, commit);
 
@@ -128,8 +130,7 @@ static const unsigned char *get_rev(void)
                        return NULL;
 
                commit = prio_queue_get(&rev_list);
-               if (!commit->object.parsed)
-                       parse_commit(commit);
+               parse_commit(commit);
                parents = commit->parents;
 
                commit->object.flags |= POPPED;
@@ -176,9 +177,9 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
                 */
                char *line;
                while ((line = packet_read_line(fd, NULL))) {
-                       if (!prefixcmp(line, "shallow "))
+                       if (starts_with(line, "shallow "))
                                continue;
-                       if (!prefixcmp(line, "unshallow "))
+                       if (starts_with(line, "unshallow "))
                                continue;
                        die("git fetch-pack: expected shallow list");
                }
@@ -189,20 +190,23 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 {
        int len;
        char *line = packet_read_line(fd, &len);
+       const char *arg;
 
        if (!len)
                die("git fetch-pack: expected ACK/NAK, got EOF");
        if (!strcmp(line, "NAK"))
                return NAK;
-       if (!prefixcmp(line, "ACK ")) {
-               if (!get_sha1_hex(line+4, result_sha1)) {
-                       if (len < 45)
+       if (skip_prefix(line, "ACK ", &arg)) {
+               if (!get_sha1_hex(arg, result_sha1)) {
+                       arg += 40;
+                       len -= arg - line;
+                       if (len < 1)
                                return ACK;
-                       if (strstr(line+45, "continue"))
+                       if (strstr(arg, "continue"))
                                return ACK_continue;
-                       if (strstr(line+45, "common"))
+                       if (strstr(arg, "common"))
                                return ACK_common;
-                       if (strstr(line+45, "ready"))
+                       if (strstr(arg, "ready"))
                                return ACK_ready;
                        return ACK;
                }
@@ -311,7 +315,7 @@ static int find_common(struct fetch_pack_args *args,
        }
 
        if (is_repository_shallow())
-               write_shallow_commits(&req_buf, 1);
+               write_shallow_commits(&req_buf, 1, NULL);
        if (args->depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args->depth);
        packet_buf_flush(&req_buf);
@@ -319,18 +323,19 @@ static int find_common(struct fetch_pack_args *args,
 
        if (args->depth > 0) {
                char *line;
+               const char *arg;
                unsigned char sha1[20];
 
                send_request(args, fd[1], &req_buf);
                while ((line = packet_read_line(fd[0], NULL))) {
-                       if (!prefixcmp(line, "shallow ")) {
-                               if (get_sha1_hex(line + 8, sha1))
+                       if (skip_prefix(line, "shallow ", &arg)) {
+                               if (get_sha1_hex(arg, sha1))
                                        die("invalid shallow line: %s", line);
                                register_shallow(sha1);
                                continue;
                        }
-                       if (!prefixcmp(line, "unshallow ")) {
-                               if (get_sha1_hex(line + 10, sha1))
+                       if (skip_prefix(line, "unshallow ", &arg)) {
+                               if (get_sha1_hex(arg, sha1))
                                        die("invalid unshallow line: %s", line);
                                if (!lookup_object(sha1))
                                        die("object not found: %s", line);
@@ -440,7 +445,8 @@ static int find_common(struct fetch_pack_args *args,
        }
        strbuf_release(&req_buf);
 
-       consume_shallow_list(args, fd[0]);
+       if (!got_ready || !no_done)
+               consume_shallow_list(args, fd[0]);
        while (flushes || multi_ack) {
                int ack = get_ack(fd[0], result_sha1);
                if (ack) {
@@ -506,7 +512,7 @@ static void filter_refs(struct fetch_pack_args *args,
                int keep = 0;
                next = ref->next;
 
-               if (!memcmp(ref->name, "refs/", 5) &&
+               if (starts_with(ref->name, "refs/") &&
                    check_refname_format(ref->name, 0))
                        ; /* trash */
                else {
@@ -523,7 +529,7 @@ static void filter_refs(struct fetch_pack_args *args,
                }
 
                if (!keep && args->fetch_all &&
-                   (!args->depth || prefixcmp(ref->name, "refs/tags/")))
+                   (!args->depth || !starts_with(ref->name, "refs/tags/")))
                        keep = 1;
 
                if (keep) {
@@ -538,16 +544,19 @@ static void filter_refs(struct fetch_pack_args *args,
        /* Append unmatched requests to the list */
        if (allow_tip_sha1_in_want) {
                for (i = 0; i < nr_sought; i++) {
+                       unsigned char sha1[20];
+
                        ref = sought[i];
                        if (ref->matched)
                                continue;
-                       if (get_sha1_hex(ref->name, ref->old_sha1))
+                       if (get_sha1_hex(ref->name, sha1) ||
+                           ref->name[40] != '\0' ||
+                           hashcmp(sha1, ref->old_sha1))
                                continue;
 
                        ref->matched = 1;
-                       *newtail = ref;
-                       ref->next = NULL;
-                       newtail = &ref->next;
+                       *newtail = copy_ref(ref);
+                       newtail = &(*newtail)->next;
                }
        }
        *refs = newlist;
@@ -619,7 +628,6 @@ static int everything_local(struct fetch_pack_args *args,
 
        for (retval = 1, ref = *refs; ref ; ref = ref->next) {
                const unsigned char *remote = ref->old_sha1;
-               unsigned char local[20];
                struct object *o;
 
                o = lookup_object(remote);
@@ -632,8 +640,6 @@ static int everything_local(struct fetch_pack_args *args,
                                ref->name);
                        continue;
                }
-
-               hashcpy(ref->new_sha1, local);
                if (!args->verbose)
                        continue;
                fprintf(stderr,
@@ -661,7 +667,7 @@ static int get_pack(struct fetch_pack_args *args,
        char hdr_arg[256];
        const char **av, *cmd_name;
        int do_keep = args->keep_pack;
-       struct child_process cmd;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int ret;
 
        memset(&demux, 0, sizeof(demux));
@@ -680,7 +686,6 @@ static int get_pack(struct fetch_pack_args *args,
        else
                demux.out = xd[0];
 
-       memset(&cmd, 0, sizeof(cmd));
        cmd.argv = argv;
        av = argv;
        *hdr_arg = 0;
@@ -774,6 +779,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                                 int fd[2],
                                 const struct ref *orig_ref,
                                 struct ref **sought, int nr_sought,
+                                struct shallow_info *si,
                                 char **pack_lockfile)
 {
        struct ref *ref = copy_ref_list(orig_ref);
@@ -850,7 +856,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        if (args->stateless_rpc)
                packet_flush(fd[1]);
        if (args->depth > 0)
-               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file);
+               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
+                                       NULL);
+       else if (si->nr_ours || si->nr_theirs)
+               alternate_shallow_file = setup_temporary_shallow(si->shallow);
        else
                alternate_shallow_file = NULL;
        if (get_pack(args, fd, pack_lockfile))
@@ -860,34 +869,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        return ref;
 }
 
-static int fetch_pack_config(const char *var, const char *value, void *cb)
+static void fetch_pack_config(void)
 {
-       if (strcmp(var, "fetch.unpacklimit") == 0) {
-               fetch_unpack_limit = git_config_int(var, value);
-               return 0;
-       }
-
-       if (strcmp(var, "transfer.unpacklimit") == 0) {
-               transfer_unpack_limit = git_config_int(var, value);
-               return 0;
-       }
+       git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit);
+       git_config_get_int("transfer.unpacklimit", &transfer_unpack_limit);
+       git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
+       git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
+       git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
 
-       if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
-               prefer_ofs_delta = git_config_bool(var, value);
-               return 0;
-       }
-
-       if (!strcmp(var, "fetch.fsckobjects")) {
-               fetch_fsck_objects = git_config_bool(var, value);
-               return 0;
-       }
-
-       if (!strcmp(var, "transfer.fsckobjects")) {
-               transfer_fsck_objects = git_config_bool(var, value);
-               return 0;
-       }
-
-       return git_default_config(var, value, cb);
+       git_config(git_default_config, NULL);
 }
 
 static void fetch_pack_setup(void)
@@ -895,7 +885,7 @@ static void fetch_pack_setup(void)
        static int did_setup;
        if (did_setup)
                return;
-       git_config(fetch_pack_config, NULL);
+       fetch_pack_config();
        if (0 <= transfer_unpack_limit)
                unpack_limit = transfer_unpack_limit;
        else if (0 <= fetch_unpack_limit)
@@ -924,14 +914,110 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
        return dst;
 }
 
+static void update_shallow(struct fetch_pack_args *args,
+                          struct ref **sought, int nr_sought,
+                          struct shallow_info *si)
+{
+       struct sha1_array ref = SHA1_ARRAY_INIT;
+       int *status;
+       int i;
+
+       if (args->depth > 0 && alternate_shallow_file) {
+               if (*alternate_shallow_file == '\0') { /* --unshallow */
+                       unlink_or_warn(git_path("shallow"));
+                       rollback_lock_file(&shallow_lock);
+               } else
+                       commit_lock_file(&shallow_lock);
+               return;
+       }
+
+       if (!si->shallow || !si->shallow->nr)
+               return;
+
+       if (args->cloning) {
+               /*
+                * remote is shallow, but this is a clone, there are
+                * no objects in repo to worry about. Accept any
+                * shallow points that exist in the pack (iow in repo
+                * after get_pack() and reprepare_packed_git())
+                */
+               struct sha1_array extra = SHA1_ARRAY_INIT;
+               unsigned char (*sha1)[20] = si->shallow->sha1;
+               for (i = 0; i < si->shallow->nr; i++)
+                       if (has_sha1_file(sha1[i]))
+                               sha1_array_append(&extra, sha1[i]);
+               if (extra.nr) {
+                       setup_alternate_shallow(&shallow_lock,
+                                               &alternate_shallow_file,
+                                               &extra);
+                       commit_lock_file(&shallow_lock);
+               }
+               sha1_array_clear(&extra);
+               return;
+       }
+
+       if (!si->nr_ours && !si->nr_theirs)
+               return;
+
+       remove_nonexistent_theirs_shallow(si);
+       if (!si->nr_ours && !si->nr_theirs)
+               return;
+       for (i = 0; i < nr_sought; i++)
+               sha1_array_append(&ref, sought[i]->old_sha1);
+       si->ref = &ref;
+
+       if (args->update_shallow) {
+               /*
+                * remote is also shallow, .git/shallow may be updated
+                * so all refs can be accepted. Make sure we only add
+                * shallow roots that are actually reachable from new
+                * refs.
+                */
+               struct sha1_array extra = SHA1_ARRAY_INIT;
+               unsigned char (*sha1)[20] = si->shallow->sha1;
+               assign_shallow_commits_to_refs(si, NULL, NULL);
+               if (!si->nr_ours && !si->nr_theirs) {
+                       sha1_array_clear(&ref);
+                       return;
+               }
+               for (i = 0; i < si->nr_ours; i++)
+                       sha1_array_append(&extra, sha1[si->ours[i]]);
+               for (i = 0; i < si->nr_theirs; i++)
+                       sha1_array_append(&extra, sha1[si->theirs[i]]);
+               setup_alternate_shallow(&shallow_lock,
+                                       &alternate_shallow_file,
+                                       &extra);
+               commit_lock_file(&shallow_lock);
+               sha1_array_clear(&extra);
+               sha1_array_clear(&ref);
+               return;
+       }
+
+       /*
+        * remote is also shallow, check what ref is safe to update
+        * without updating .git/shallow
+        */
+       status = xcalloc(nr_sought, sizeof(*status));
+       assign_shallow_commits_to_refs(si, NULL, status);
+       if (si->nr_ours || si->nr_theirs) {
+               for (i = 0; i < nr_sought; i++)
+                       if (status[i])
+                               sought[i]->status = REF_STATUS_REJECT_SHALLOW;
+       }
+       free(status);
+       sha1_array_clear(&ref);
+}
+
 struct ref *fetch_pack(struct fetch_pack_args *args,
                       int fd[], struct child_process *conn,
                       const struct ref *ref,
                       const char *dest,
                       struct ref **sought, int nr_sought,
+                      struct sha1_array *shallow,
                       char **pack_lockfile)
 {
        struct ref *ref_cpy;
+       struct shallow_info si;
 
        fetch_pack_setup();
        if (nr_sought)
@@ -941,16 +1027,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                packet_flush(fd[1]);
                die("no matching remote head");
        }
-       ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
-
-       if (args->depth > 0 && alternate_shallow_file) {
-               if (*alternate_shallow_file == '\0') { /* --unshallow */
-                       unlink_or_warn(git_path("shallow"));
-                       rollback_lock_file(&shallow_lock);
-               } else
-                       commit_lock_file(&shallow_lock);
-       }
-
+       prepare_shallow_info(&si, shallow);
+       ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
+                               &si, pack_lockfile);
        reprepare_packed_git();
+       update_shallow(args, sought, nr_sought, &si);
+       clear_shallow_info(&si);
        return ref_cpy;
 }