merge-recursive.c: use string_list_sort instead of qsort
[gitweb.git] / builtin / pack-objects.c
index b6664ce4d8d72cde05e1f1be5edcd967f003f0ef..0954375be9ea5fe2b416d4142b6987ff2b5ec94f 100644 (file)
@@ -44,7 +44,9 @@ static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
 static unsigned long unpack_unreachable_expiration;
+static int pack_loose_unreachable;
 static int local;
+static int have_non_local_packs;
 static int incremental;
 static int ignore_packed_keep;
 static int allow_ofs_delta;
@@ -341,15 +343,15 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
 }
 
 /* Return 0 if we will bust the pack-size limit */
-static unsigned long write_reuse_object(struct sha1file *f, struct object_entry *entry,
-                                       unsigned long limit, int usable_delta)
+static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
+                               unsigned long limit, int usable_delta)
 {
        struct packed_git *p = entry->in_pack;
        struct pack_window *w_curs = NULL;
        struct revindex_entry *revidx;
        off_t offset;
        enum object_type type = entry->type;
-       unsigned long datalen;
+       off_t datalen;
        unsigned char header[10], dheader[10];
        unsigned hdrlen;
 
@@ -415,11 +417,12 @@ static unsigned long write_reuse_object(struct sha1file *f, struct object_entry
 }
 
 /* Return 0 if we will bust the pack-size limit */
-static unsigned long write_object(struct sha1file *f,
-                                 struct object_entry *entry,
-                                 off_t write_offset)
+static off_t write_object(struct sha1file *f,
+                         struct object_entry *entry,
+                         off_t write_offset)
 {
-       unsigned long limit, len;
+       unsigned long limit;
+       off_t len;
        int usable_delta, to_reuse;
 
        if (!pack_to_stdout)
@@ -491,7 +494,7 @@ static enum write_one_status write_one(struct sha1file *f,
                                       struct object_entry *e,
                                       off_t *offset)
 {
-       unsigned long size;
+       off_t size;
        int recursing;
 
        /*
@@ -835,8 +838,7 @@ static void write_pack_file(void)
                         * to preserve this property.
                         */
                        if (stat(pack_tmp_name, &st) < 0) {
-                               warning("failed to stat %s: %s",
-                                       pack_tmp_name, strerror(errno));
+                               warning_errno("failed to stat %s", pack_tmp_name);
                        } else if (!last_mtime) {
                                last_mtime = st.st_mtime;
                        } else {
@@ -844,8 +846,7 @@ static void write_pack_file(void)
                                utb.actime = st.st_atime;
                                utb.modtime = --last_mtime;
                                if (utime(pack_tmp_name, &utb) < 0)
-                                       warning("failed utime() on %s: %s",
-                                               pack_tmp_name, strerror(errno));
+                                       warning_errno("failed utime() on %s", pack_tmp_name);
                        }
 
                        strbuf_addf(&tmpname, "%s-", base_name);
@@ -978,6 +979,23 @@ static int want_object_in_pack(const unsigned char *sha1,
                                return 1;
                        if (incremental)
                                return 0;
+
+                       /*
+                        * When asked to do --local (do not include an
+                        * object that appears in a pack we borrow
+                        * from elsewhere) or --honor-pack-keep (do not
+                        * include an object that appears in a pack marked
+                        * with .keep), we need to make sure no copy of this
+                        * object come from in _any_ pack that causes us to
+                        * omit it, and need to complete this loop.  When
+                        * neither option is in effect, we know the object
+                        * we just found is going to be packed, so break
+                        * out of the loop to return 1 now.
+                        */
+                       if (!ignore_packed_keep &&
+                           (!local || !have_non_local_packs))
+                               break;
+
                        if (local && !p->pack_local)
                                return 0;
                        if (ignore_packed_keep && p->pack_local && p->pack_keep)
@@ -1193,7 +1211,7 @@ static void add_pbase_object(struct tree_desc *tree,
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
-                       add_object_entry(entry.sha1,
+                       add_object_entry(entry.oid->hash,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
@@ -1204,7 +1222,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
 
-                       tree = pbase_tree_get(entry.sha1);
+                       tree = pbase_tree_get(entry.oid->hash);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
@@ -2105,6 +2123,35 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
 #define ll_find_deltas(l, s, w, d, p)  find_deltas(l, &s, w, d, p)
 #endif
 
+static void add_tag_chain(const struct object_id *oid)
+{
+       struct tag *tag;
+
+       /*
+        * We catch duplicates already in add_object_entry(), but we'd
+        * prefer to do this extra check to avoid having to parse the
+        * tag at all if we already know that it's being packed (e.g., if
+        * it was included via bitmaps, we would not have parsed it
+        * previously).
+        */
+       if (packlist_find(&to_pack, oid->hash, NULL))
+               return;
+
+       tag = lookup_tag(oid->hash);
+       while (1) {
+               if (!tag || parse_tag(tag) || !tag->tagged)
+                       die("unable to pack objects reachable from tag %s",
+                           oid_to_hex(oid));
+
+               add_object_entry(tag->object.oid.hash, OBJ_TAG, NULL, 0);
+
+               if (tag->tagged->type != OBJ_TAG)
+                       return;
+
+               tag = (struct tag *)tag->tagged;
+       }
+}
+
 static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data)
 {
        struct object_id peeled;
@@ -2112,7 +2159,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag,
        if (starts_with(path, "refs/tags/") && /* is a tag? */
            !peel_ref(path, peeled.hash)    && /* peelable? */
            packlist_find(&to_pack, peeled.hash, NULL))      /* object packed? */
-               add_object_entry(oid->hash, OBJ_TAG, NULL, 0);
+               add_tag_chain(oid);
        return 0;
 }
 
@@ -2380,6 +2427,32 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
        free(in_pack.array);
 }
 
+static int add_loose_object(const unsigned char *sha1, const char *path,
+                           void *data)
+{
+       enum object_type type = sha1_object_info(sha1, NULL);
+
+       if (type < 0) {
+               warning("loose object at %s could not be examined", path);
+               return 0;
+       }
+
+       add_object_entry(sha1, type, "", 0);
+       return 0;
+}
+
+/*
+ * We actually don't even have to worry about reachability here.
+ * add_object_entry will weed out duplicates, so we just add every
+ * loose object we find.
+ */
+static void add_unreachable_loose_objects(void)
+{
+       for_each_loose_file_in_objdir(get_object_directory(),
+                                     add_loose_object,
+                                     NULL, NULL, NULL);
+}
+
 static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 {
        static struct packed_git *last_found = (void *)1;
@@ -2549,6 +2622,8 @@ static void get_object_list(int ac, const char **av)
 
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
+       if (pack_loose_unreachable)
+               add_unreachable_loose_objects();
        if (unpack_unreachable)
                loosen_unused_packed_objects(&revs);
 
@@ -2649,6 +2724,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                         N_("include tag objects that refer to objects to be packed")),
                OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
                         N_("keep unreachable objects")),
+               OPT_BOOL(0, "pack-loose-unreachable", &pack_loose_unreachable,
+                        N_("pack loose unreachable objects")),
                { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"),
                  N_("unpack unreachable objects newer than <time>"),
                  PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
@@ -2754,6 +2831,28 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                progress = 2;
 
        prepare_packed_git();
+       if (ignore_packed_keep) {
+               struct packed_git *p;
+               for (p = packed_git; p; p = p->next)
+                       if (p->pack_local && p->pack_keep)
+                               break;
+               if (!p) /* no keep-able packs found */
+                       ignore_packed_keep = 0;
+       }
+       if (local) {
+               /*
+                * unlike ignore_packed_keep above, we do not want to
+                * unset "local" based on looking at packs, as it
+                * also covers non-local objects
+                */
+               struct packed_git *p;
+               for (p = packed_git; p; p = p->next) {
+                       if (!p->pack_local) {
+                               have_non_local_packs = 1;
+                               break;
+                       }
+               }
+       }
 
        if (progress)
                progress_state = start_progress(_("Counting objects"), 0);