perl: install private Error.pm if the site version is older than our own
[gitweb.git] / fetch-pack.c
index 92322cf4da39434d44e4aa52fa0c3f735b22d752..06f4aeced439312c43f2736a1684f9e22218899b 100644 (file)
@@ -4,15 +4,21 @@
 #include "commit.h"
 #include "tag.h"
 #include "exec_cmd.h"
+#include "pack.h"
 #include "sideband.h"
 
 static int keep_pack;
+static int transfer_unpack_limit = -1;
+static int fetch_unpack_limit = -1;
+static int unpack_limit = 100;
 static int quiet;
 static int verbose;
 static int fetch_all;
+static int depth;
+static int no_progress;
 static const char fetch_pack_usage[] =
-"git-fetch-pack [--all] [-q] [-v] [-k] [--thin] [--exec=upload-pack] [host:]directory <refs>...";
-static const char *exec = "git-upload-pack";
+"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+static const char *uploadpack = "git-upload-pack";
 
 #define COMPLETE       (1U << 0)
 #define COMMON         (1U << 1)
@@ -168,21 +174,53 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                }
 
                if (!fetching)
-                       packet_write(fd[1], "want %s%s%s%s%s%s\n",
+                       packet_write(fd[1], "want %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" : ""),
                                     (use_thin_pack ? " thin-pack" : ""),
+                                    (no_progress ? " no-progress" : ""),
                                     " ofs-delta");
                else
                        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
                fetching++;
        }
+       if (is_repository_shallow())
+               write_shallow_commits(fd[1], 1);
+       if (depth > 0)
+               packet_write(fd[1], "deepen %d", depth);
        packet_flush(fd[1]);
        if (!fetching)
                return 1;
 
+       if (depth > 0) {
+               char line[1024];
+               unsigned char sha1[20];
+               int len;
+
+               while ((len = 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);
+                               register_shallow(sha1);
+                               continue;
+                       }
+                       if (!prefixcmp(line, "unshallow ")) {
+                               if (get_sha1_hex(line + 10, sha1))
+                                       die("invalid unshallow line: %s", line);
+                               if (!lookup_object(sha1))
+                                       die("object not found: %s", line);
+                               /* make sure that it is parsed as shallow */
+                               parse_object(sha1);
+                               if (unregister_shallow(sha1))
+                                       die("no shallow found: %s", line);
+                               continue;
+                       }
+                       die("expected shallow/unshallow, got %s", line);
+               }
+       }
+
        flushes = 0;
        retval = -1;
        while ((sha1 = get_rev())) {
@@ -309,7 +347,8 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
                if (!memcmp(ref->name, "refs/", 5) &&
                    check_ref_format(ref->name + 5))
                        ; /* trash */
-               else if (fetch_all) {
+               else if (fetch_all &&
+                        (!depth || prefixcmp(ref->name, "refs/tags/") )) {
                        *newtail = ref;
                        ref->next = NULL;
                        newtail = &ref->next;
@@ -368,9 +407,11 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                }
        }
 
-       for_each_ref(mark_complete, NULL);
-       if (cutoff)
-               mark_recent_complete_commits(cutoff);
+       if (!depth) {
+               for_each_ref(mark_complete, NULL);
+               if (cutoff)
+                       mark_recent_complete_commits(cutoff);
+       }
 
        /*
         * Mark all complete remote refs as common refs.
@@ -451,13 +492,58 @@ static pid_t setup_sideband(int fd[2], int xd[2])
        return side_pid;
 }
 
-static int get_pack(int xd[2], const char **argv)
+static int get_pack(int xd[2])
 {
        int status;
        pid_t pid, side_pid;
        int fd[2];
+       const char *argv[20];
+       char keep_arg[256];
+       char hdr_arg[256];
+       const char **av;
+       int do_keep = keep_pack;
 
        side_pid = setup_sideband(fd, xd);
+
+       av = argv;
+       *hdr_arg = 0;
+       if (unpack_limit) {
+               struct pack_header header;
+
+               if (read_pack_header(fd[0], &header))
+                       die("protocol error: bad pack header");
+               snprintf(hdr_arg, sizeof(hdr_arg), "--pack_header=%u,%u",
+                        ntohl(header.hdr_version), ntohl(header.hdr_entries));
+               if (ntohl(header.hdr_entries) < unpack_limit)
+                       do_keep = 0;
+               else
+                       do_keep = 1;
+       }
+
+       if (do_keep) {
+               *av++ = "index-pack";
+               *av++ = "--stdin";
+               if (!quiet && !no_progress)
+                       *av++ = "-v";
+               if (use_thin_pack)
+                       *av++ = "--fix-thin";
+               if (keep_pack > 1 || unpack_limit) {
+                       int s = sprintf(keep_arg,
+                                       "--keep=fetch-pack %d on ", getpid());
+                       if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
+                               strcpy(keep_arg + s, "localhost");
+                       *av++ = keep_arg;
+               }
+       }
+       else {
+               *av++ = "unpack-objects";
+               if (quiet)
+                       *av++ = "-q";
+       }
+       if (*hdr_arg)
+               *av++ = hdr_arg;
+       *av++ = NULL;
+
        pid = fork();
        if (pid < 0)
                die("fetch-pack: unable to fork off %s", argv[0]);
@@ -487,41 +573,14 @@ static int get_pack(int xd[2], const char **argv)
        die("%s died of unnatural causes %d", argv[0], status);
 }
 
-static int explode_rx_pack(int xd[2])
-{
-       const char *argv[3] = { "unpack-objects", quiet ? "-q" : NULL, NULL };
-       return get_pack(xd, argv);
-}
-
-static int keep_rx_pack(int xd[2])
-{
-       const char *argv[6];
-       char keep_arg[256];
-       int n = 0;
-
-       argv[n++] = "index-pack";
-       argv[n++] = "--stdin";
-       if (!quiet)
-               argv[n++] = "-v";
-       if (use_thin_pack)
-               argv[n++] = "--fix-thin";
-       if (keep_pack > 1) {
-               int s = sprintf(keep_arg, "--keep=fetch-pack %i on ", getpid());
-               if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
-                       strcpy(keep_arg + s, "localhost");
-               argv[n++] = keep_arg;
-       }
-       argv[n] = NULL;
-       return get_pack(xd, argv);
-}
-
 static int fetch_pack(int fd[2], int nr_match, char **match)
 {
        struct ref *ref;
        unsigned char sha1[20];
-       int status;
 
        get_remote_heads(fd[0], &ref, 0, NULL, 0);
+       if (is_repository_shallow() && !server_supports("shallow"))
+               die("Server does not support shallow clients");
        if (server_supports("multi_ack")) {
                if (verbose)
                        fprintf(stderr, "Server supports multi_ack\n");
@@ -552,8 +611,7 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
                         */
                        fprintf(stderr, "warning: no common commits\n");
 
-       status = (keep_pack) ? keep_rx_pack(fd) : explode_rx_pack(fd);
-       if (status)
+       if (get_pack(fd))
                die("git-fetch-pack: fetch failed.");
 
  all_done:
@@ -588,14 +646,38 @@ static int remove_duplicates(int nr_heads, char **heads)
        return dst;
 }
 
+static int fetch_pack_config(const char *var, const char *value)
+{
+       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;
+       }
+
+       return git_default_config(var, value);
+}
+
+static struct lock_file lock;
+
 int main(int argc, char **argv)
 {
        int i, ret, nr_heads;
        char *dest = NULL, **heads;
        int fd[2];
        pid_t pid;
+       struct stat st;
 
        setup_git_directory();
+       git_config(fetch_pack_config);
+
+       if (0 <= transfer_unpack_limit)
+               unpack_limit = transfer_unpack_limit;
+       else if (0 <= fetch_unpack_limit)
+               unpack_limit = fetch_unpack_limit;
 
        nr_heads = 0;
        heads = NULL;
@@ -603,8 +685,12 @@ int main(int argc, char **argv)
                char *arg = argv[i];
 
                if (*arg == '-') {
-                       if (!strncmp("--exec=", arg, 7)) {
-                               exec = arg + 7;
+                       if (!prefixcmp(arg, "--upload-pack=")) {
+                               uploadpack = arg + 14;
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--exec=")) {
+                               uploadpack = arg + 7;
                                continue;
                        }
                        if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
@@ -613,6 +699,7 @@ int main(int argc, char **argv)
                        }
                        if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
                                keep_pack++;
+                               unpack_limit = 0;
                                continue;
                        }
                        if (!strcmp("--thin", arg)) {
@@ -627,6 +714,16 @@ int main(int argc, char **argv)
                                verbose = 1;
                                continue;
                        }
+                       if (!prefixcmp(arg, "--depth=")) {
+                               depth = strtol(arg + 8, NULL, 0);
+                               if (stat(git_path("shallow"), &st))
+                                       st.st_mtime = 0;
+                               continue;
+                       }
+                       if (!strcmp("--no-progress", arg)) {
+                               no_progress = 1;
+                               continue;
+                       }
                        usage(fetch_pack_usage);
                }
                dest = arg;
@@ -636,7 +733,7 @@ int main(int argc, char **argv)
        }
        if (!dest)
                usage(fetch_pack_usage);
-       pid = git_connect(fd, dest, exec);
+       pid = git_connect(fd, dest, uploadpack);
        if (pid < 0)
                return 1;
        if (heads && nr_heads)
@@ -659,5 +756,34 @@ int main(int argc, char **argv)
                        }
        }
 
+       if (!ret && depth > 0) {
+               struct cache_time mtime;
+               char *shallow = git_path("shallow");
+               int fd;
+
+               mtime.sec = st.st_mtime;
+#ifdef USE_NSEC
+               mtime.usec = st.st_mtim.usec;
+#endif
+               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
+#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);
+                       rollback_lock_file(&lock);
+               } else {
+                       close(fd);
+                       commit_lock_file(&lock);
+               }
+       }
+
        return !!ret;
 }