Code

support fetching into a shallow repository
authorJohannes Schindelin <Johannes.Schindelin@gmx.de>
Mon, 30 Oct 2006 19:09:06 +0000 (20:09 +0100)
committerJunio C Hamano <junkio@cox.net>
Fri, 24 Nov 2006 23:42:49 +0000 (15:42 -0800)
A shallow commit is a commit which has parents, which in turn are
"grafted away", i.e. the commit appears as if it were a root.

Since these shallow commits should not be edited by the user, but
only by core git, they are recorded in the file $GIT_DIR/shallow.

A repository containing shallow commits is called shallow.

The advantage of a shallow repository is that even if the upstream
contains lots of history, your local (shallow) repository needs not
occupy much disk space.

The disadvantage is that you might miss a merge base when pulling
some remote branch.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Makefile
commit.c
commit.h
fetch-pack.c
shallow.c [new file with mode: 0644]
upload-pack.c

index 36ce8cd606395217850dd824c47d887783a09471..ea5d2cf7239645295c81ae078f8a4a3ce13debcb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -260,7 +260,7 @@ LIB_OBJS = \
        revision.o pager.o tree-walk.o xdiff-interface.o \
        write_or_die.o trace.o list-objects.o grep.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
-       color.o wt-status.o archive-zip.o archive-tar.o
+       color.o wt-status.o archive-zip.o archive-tar.o shallow.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
index a6d543eee7831cd6a479d4cf8b2ba81a1438b298..bffa278868f13b8ba3ba72d45c3dee9eb813fca8 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "tag.h"
 #include "commit.h"
+#include "pkt-line.h"
 
 int save_commit_buffer = 1;
 
@@ -221,6 +222,8 @@ static void prepare_commit_graft(void)
                return;
        graft_file = get_graft_file();
        read_graft_file(graft_file);
+       /* make sure shallows are read */
+       is_repository_shallow();
        commit_graft_prepared = 1;
 }
 
@@ -234,6 +237,24 @@ static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
        return commit_graft[pos];
 }
 
+int write_shallow_commits(int fd, int use_pack_protocol)
+{
+       int i, count = 0;
+       for (i = 0; i < commit_graft_nr; i++)
+               if (commit_graft[i]->nr_parent < 0) {
+                       const char *hex =
+                               sha1_to_hex(commit_graft[i]->sha1);
+                       count++;
+                       if (use_pack_protocol)
+                               packet_write(fd, "shallow %s", hex);
+                       else {
+                               write(fd, hex,  40);
+                               write(fd, "\n", 1);
+                       }
+               }
+       return count;
+}
+
 int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
 {
        char *tail = buffer;
index fc13de9780f98c3bd9f330ef6177fd47a4da3d80..c559510b46d7aa84187b616be1ed7654a0447ec8 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -97,7 +97,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
 
 struct commit_graft {
        unsigned char sha1[20];
-       int nr_parent;
+       int nr_parent; /* < 0 if shallow commit */
        unsigned char parent[FLEX_ARRAY][20]; /* more */
 };
 
@@ -107,4 +107,10 @@ int read_graft_file(const char *graft_file);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
 
+extern int register_shallow(const unsigned char *sha1);
+extern int write_shallow_commits(int fd, int use_pack_protocol);
+extern int is_repository_shallow();
+extern struct commit_list *get_shallow_commits(struct object_array *heads,
+               int depth);
+
 #endif /* COMMIT_H */
index 0a169dce8574102a03c978bb24f8d0664f1fc9b6..bc5e725055198e28a9174f249902e5be095851de 100644 (file)
@@ -180,6 +180,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
                fetching++;
        }
+       if (is_repository_shallow())
+               write_shallow_commits(fd[1], 1);
        packet_flush(fd[1]);
        if (!fetching)
                return 1;
@@ -523,6 +525,8 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
        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");
diff --git a/shallow.c b/shallow.c
new file mode 100644 (file)
index 0000000..3cf2127
--- /dev/null
+++ b/shallow.c
@@ -0,0 +1,97 @@
+#include "cache.h"
+#include "commit.h"
+
+static int is_shallow = -1;
+
+int register_shallow(const unsigned char *sha1)
+{
+       struct commit_graft *graft =
+               xmalloc(sizeof(struct commit_graft));
+       struct commit *commit = lookup_commit(sha1);
+
+       hashcpy(graft->sha1, sha1);
+       graft->nr_parent = -1;
+       if (commit && commit->object.parsed)
+               commit->parents = NULL;
+       return register_commit_graft(graft, 0);
+}
+
+int is_repository_shallow()
+{
+       FILE *fp;
+       char buf[1024];
+
+       if (is_shallow >= 0)
+               return is_shallow;
+
+       fp = fopen(git_path("shallow"), "r");
+       if (!fp) {
+               is_shallow = 0;
+               return is_shallow;
+       }
+       is_shallow = 1;
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               unsigned char sha1[20];
+               if (get_sha1_hex(buf, sha1))
+                       die("bad shallow line: %s", buf);
+               register_shallow(sha1);
+       }
+       fclose(fp);
+       return is_shallow;
+}
+
+struct commit_list *get_shallow_commits(struct object_array *heads, int depth)
+{
+       int i = 0, cur_depth = 0;
+       struct commit_list *result = NULL;
+       struct object_array stack = {0, 0, NULL};
+       struct commit *commit = NULL;
+
+       while (commit || i < heads->nr || stack.nr) {
+               struct commit_list *p;
+               if (!commit) {
+                       if (i < heads->nr) {
+                               commit = (struct commit *)
+                                       heads->objects[i++].item;
+                               if (commit->object.type != OBJ_COMMIT) {
+                                       commit = NULL;
+                                       continue;
+                               }
+                               commit->util = xcalloc(1, sizeof(int));
+                               cur_depth = 0;
+                       } else {
+                               commit = (struct commit *)
+                                       stack.objects[--stack.nr].item;
+                               cur_depth = *(int *)commit->util;
+                       }
+               }
+               parse_commit(commit);
+               cur_depth++;
+               for (p = commit->parents, commit = NULL; p; p = p->next) {
+                       if (!p->item->util) {
+                               int *pointer = xmalloc(sizeof(int));
+                               p->item->util = pointer;
+                               *pointer =  cur_depth;
+                       } else {
+                               int *pointer = p->item->util;
+                               if (cur_depth >= *pointer)
+                                       continue;
+                               *pointer = cur_depth;
+                       }
+                       if (cur_depth < depth) {
+                               if (p->next)
+                                       add_object_array(&p->item->object,
+                                                       NULL, &stack);
+                               else {
+                                       commit = p->item;
+                                       cur_depth = *(int *)commit->util;
+                               }
+                       } else
+                               commit_list_insert(p->item, &result);
+               }
+       }
+
+       return result;
+}
+
index 7f7df2afd3c4640630f54bad96bfe3c952200c48..8dd6121dab89963aa771e6e67b8dee71fdbc009f 100644 (file)
@@ -486,6 +486,7 @@ static int get_common_commits(void)
 
 static void receive_needs(void)
 {
+       struct object_array shallows = {0, 0, NULL};
        static char line[1000];
        int len;
 
@@ -495,8 +496,19 @@ static void receive_needs(void)
                len = packet_read_line(0, line, sizeof(line));
                reset_timeout();
                if (!len)
-                       return;
+                       break;
 
+               if (!strncmp("shallow ", line, 8)) {
+                       unsigned char sha1[20];
+                       struct object *object;
+                       if (get_sha1(line + 8, sha1))
+                               die("invalid shallow line: %s", line);
+                       object = parse_object(sha1);
+                       if (!object)
+                               die("did not find object for %s", line);
+                       add_object_array(object, NULL, &shallows);
+                       continue;
+               }
                if (strncmp("want ", line, 5) ||
                    get_sha1_hex(line+5, sha1_buf))
                        die("git-upload-pack: protocol error, "
@@ -528,11 +540,17 @@ static void receive_needs(void)
                        add_object_array(o, NULL, &want_obj);
                }
        }
+       if (shallows.nr > 0) {
+               int i;
+               for (i = 0; i < shallows.nr; i++)
+                       register_shallow(shallows.objects[i].item->sha1);
+       }
 }
 
 static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
-       static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta";
+       static const char *capabilities = "multi_ack thin-pack side-band"
+               " side-band-64k ofs-delta shallow";
        struct object *o = parse_object(sha1);
 
        if (!o)