shallow.c: the 8 steps to select new commits for .git/shallow
[gitweb.git] / shallow.c
index cdf37d694de175adcce10fa2e2a79c6579603b0b..ecd950fd04101f2d0597f798ccc4024e901960e0 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -2,6 +2,12 @@
 #include "commit.h"
 #include "tag.h"
 #include "pkt-line.h"
+#include "remote.h"
+#include "refs.h"
+#include "sha1-array.h"
+#include "diff.h"
+#include "revision.h"
+#include "commit-slab.h"
 
 static int is_shallow = -1;
 static struct stat shallow_stat;
@@ -165,22 +171,31 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
        return 0;
 }
 
-int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
+                         const struct sha1_array *extra)
 {
        struct write_shallow_data data;
+       int i;
        data.out = out;
        data.use_pack_protocol = use_pack_protocol;
        data.count = 0;
        for_each_commit_graft(write_one_shallow, &data);
+       if (!extra)
+               return data.count;
+       for (i = 0; i < extra->nr; i++) {
+               strbuf_addstr(out, sha1_to_hex(extra->sha1[i]));
+               strbuf_addch(out, '\n');
+               data.count++;
+       }
        return data.count;
 }
 
-char *setup_temporary_shallow(void)
+char *setup_temporary_shallow(const struct sha1_array *extra)
 {
        struct strbuf sb = STRBUF_INIT;
        int fd;
 
-       if (write_shallow_commits(&sb, 0)) {
+       if (write_shallow_commits(&sb, 0, extra)) {
                struct strbuf path = STRBUF_INIT;
                strbuf_addstr(&path, git_path("shallow_XXXXXX"));
                fd = xmkstemp(path.buf);
@@ -199,7 +214,8 @@ char *setup_temporary_shallow(void)
 }
 
 void setup_alternate_shallow(struct lock_file *shallow_lock,
-                            const char **alternate_shallow_file)
+                            const char **alternate_shallow_file,
+                            const struct sha1_array *extra)
 {
        struct strbuf sb = STRBUF_INIT;
        int fd;
@@ -207,7 +223,7 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
        check_shallow_file_for_update();
        fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
                                       LOCK_DIE_ON_ERROR);
-       if (write_shallow_commits(&sb, 0)) {
+       if (write_shallow_commits(&sb, 0, extra)) {
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
                                  shallow_lock->filename);
@@ -220,3 +236,84 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
                *alternate_shallow_file = "";
        strbuf_release(&sb);
 }
+
+static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *cb)
+{
+       int fd = *(int *)cb;
+       if (graft->nr_parent == -1)
+               packet_write(fd, "shallow %s\n", sha1_to_hex(graft->sha1));
+       return 0;
+}
+
+void advertise_shallow_grafts(int fd)
+{
+       if (!is_repository_shallow())
+               return;
+       for_each_commit_graft(advertise_shallow_grafts_cb, &fd);
+}
+
+#define TRACE_KEY "GIT_TRACE_SHALLOW"
+
+/*
+ * Step 1, split sender shallow commits into "ours" and "theirs"
+ * Step 2, clean "ours" based on .git/shallow
+ */
+void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
+{
+       int i;
+       trace_printf_key(TRACE_KEY, "shallow: prepare_shallow_info\n");
+       memset(info, 0, sizeof(*info));
+       info->shallow = sa;
+       if (!sa)
+               return;
+       info->ours = xmalloc(sizeof(*info->ours) * sa->nr);
+       info->theirs = xmalloc(sizeof(*info->theirs) * sa->nr);
+       for (i = 0; i < sa->nr; i++) {
+               if (has_sha1_file(sa->sha1[i])) {
+                       struct commit_graft *graft;
+                       graft = lookup_commit_graft(sa->sha1[i]);
+                       if (graft && graft->nr_parent < 0)
+                               continue;
+                       info->ours[info->nr_ours++] = i;
+               } else
+                       info->theirs[info->nr_theirs++] = i;
+       }
+}
+
+void clear_shallow_info(struct shallow_info *info)
+{
+       free(info->ours);
+       free(info->theirs);
+}
+
+/* Step 4, remove non-existent ones in "theirs" after getting the pack */
+
+void remove_nonexistent_theirs_shallow(struct shallow_info *info)
+{
+       unsigned char (*sha1)[20] = info->shallow->sha1;
+       int i, dst;
+       trace_printf_key(TRACE_KEY, "shallow: remove_nonexistent_theirs_shallow\n");
+       for (i = dst = 0; i < info->nr_theirs; i++) {
+               if (i != dst)
+                       info->theirs[dst] = info->theirs[i];
+               if (has_sha1_file(sha1[info->theirs[i]]))
+                       dst++;
+       }
+       info->nr_theirs = dst;
+}
+
+/* Step 5, remove non-existent ones in "ours" in the pack */
+void remove_nonexistent_ours_in_pack(struct shallow_info *info,
+                                    struct packed_git *p)
+{
+       unsigned char (*sha1)[20] = info->shallow->sha1;
+       int i, dst;
+       trace_printf_key(TRACE_KEY, "shallow: remove_nonexistent_ours_in_pack\n");
+       for (i = dst = 0; i < info->nr_ours; i++) {
+               if (i != dst)
+                       info->ours[dst] = info->ours[i];
+               if (find_pack_entry_one(sha1[info->ours[i]], p))
+                       dst++;
+       }
+       info->nr_ours = dst;
+}