@@ -294,6 +294,25 @@ Pack file entry: <+
All 4-byte numbers are in network order.
+== pack-*.mtimes files have the format:
+
+ - A 4-byte magic number '0x4d544d45' ('MTME').
+
+ - A 4-byte version identifier (= 1).
+
+ - A 4-byte hash function identifier (= 1 for SHA-1, 2 for SHA-256).
+
+ - A table of 4-byte unsigned integers in network order. The ith
+ value is the modification time (mtime) of the ith object in the
+ corresponding pack by lexicographic (index) order. The mtimes
+ count standard epoch seconds.
+
+ - A trailer, containing a checksum of the corresponding packfile,
+ and a checksum of all of the above (each having length according
+ to the specified hash function).
+
+All 4-byte numbers are in network order.
+
== multi-pack-index (MIDX) files have the following format:
The multi-pack-index files refer to multiple pack-files and loose objects.
@@ -959,6 +959,7 @@ LIB_OBJS += oidtree.o
LIB_OBJS += pack-bitmap-write.o
LIB_OBJS += pack-bitmap.o
LIB_OBJS += pack-check.o
+LIB_OBJS += pack-mtimes.o
LIB_OBJS += pack-objects.o
LIB_OBJS += pack-revindex.o
LIB_OBJS += pack-write.o
@@ -212,6 +212,7 @@ static struct {
} exts[] = {
{".pack"},
{".rev", 1},
+ {".mtimes", 1},
{".bitmap", 1},
{".promisor", 1},
{".idx"},
@@ -115,12 +115,15 @@ struct packed_git {
freshened:1,
do_not_close:1,
pack_promisor:1,
- multi_pack_index:1;
+ multi_pack_index:1,
+ is_cruft:1;
unsigned char hash[GIT_MAX_RAWSZ];
struct revindex_entry *revindex;
const uint32_t *revindex_data;
const uint32_t *revindex_map;
size_t revindex_size;
+ const uint32_t *mtimes_map;
+ size_t mtimes_size;
/* something like ".git/objects/pack/xxxxx.pack" */
char pack_name[FLEX_ARRAY]; /* more */
};
new file mode 100644
@@ -0,0 +1,126 @@
+#include "pack-mtimes.h"
+#include "object-store.h"
+#include "packfile.h"
+
+static char *pack_mtimes_filename(struct packed_git *p)
+{
+ size_t len;
+ if (!strip_suffix(p->pack_name, ".pack", &len))
+ BUG("pack_name does not end in .pack");
+ /* NEEDSWORK: this could reuse code from pack-revindex.c. */
+ return xstrfmt("%.*s.mtimes", (int)len, p->pack_name);
+}
+
+#define MTIMES_HEADER_SIZE (12)
+#define MTIMES_MIN_SIZE (MTIMES_HEADER_SIZE + (2 * the_hash_algo->rawsz))
+
+struct mtimes_header {
+ uint32_t signature;
+ uint32_t version;
+ uint32_t hash_id;
+};
+
+static int load_pack_mtimes_file(char *mtimes_file,
+ uint32_t num_objects,
+ const uint32_t **data_p, size_t *len_p)
+{
+ int fd, ret = 0;
+ struct stat st;
+ void *data = NULL;
+ size_t mtimes_size;
+ struct mtimes_header header;
+ uint32_t *hdr;
+
+ fd = git_open(mtimes_file);
+
+ if (fd < 0) {
+ ret = -1;
+ goto cleanup;
+ }
+ if (fstat(fd, &st)) {
+ ret = error_errno(_("failed to read %s"), mtimes_file);
+ goto cleanup;
+ }
+
+ mtimes_size = xsize_t(st.st_size);
+
+ if (mtimes_size < MTIMES_MIN_SIZE) {
+ ret = error(_("mtimes file %s is too small"), mtimes_file);
+ goto cleanup;
+ }
+
+ if (mtimes_size - MTIMES_MIN_SIZE != st_mult(sizeof(uint32_t), num_objects)) {
+ ret = error(_("mtimes file %s is corrupt"), mtimes_file);
+ goto cleanup;
+ }
+
+ data = hdr = xmmap(NULL, mtimes_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ header.signature = ntohl(hdr[0]);
+ header.version = ntohl(hdr[1]);
+ header.hash_id = ntohl(hdr[2]);
+
+ if (header.signature != MTIMES_SIGNATURE) {
+ ret = error(_("mtimes file %s has unknown signature"), mtimes_file);
+ goto cleanup;
+ }
+
+ if (header.version != 1) {
+ ret = error(_("mtimes file %s has unsupported version %"PRIu32),
+ mtimes_file, header.version);
+ goto cleanup;
+ }
+
+ if (!(header.hash_id == 1 || header.hash_id == 2)) {
+ ret = error(_("mtimes file %s has unsupported hash id %"PRIu32),
+ mtimes_file, header.hash_id);
+ goto cleanup;
+ }
+
+cleanup:
+ if (ret) {
+ if (data)
+ munmap(data, mtimes_size);
+ } else {
+ *len_p = mtimes_size;
+ *data_p = (const uint32_t *)data;
+ }
+
+ close(fd);
+ return ret;
+}
+
+int load_pack_mtimes(struct packed_git *p)
+{
+ char *mtimes_name = NULL;
+ int ret = 0;
+
+ if (!p->is_cruft)
+ return ret; /* not a cruft pack */
+ if (p->mtimes_map)
+ return ret; /* already loaded */
+
+ ret = open_pack_index(p);
+ if (ret < 0)
+ goto cleanup;
+
+ mtimes_name = pack_mtimes_filename(p);
+ ret = load_pack_mtimes_file(mtimes_name,
+ p->num_objects,
+ &p->mtimes_map,
+ &p->mtimes_size);
+cleanup:
+ free(mtimes_name);
+ return ret;
+}
+
+uint32_t nth_packed_mtime(struct packed_git *p, uint32_t pos)
+{
+ if (!p->mtimes_map)
+ BUG("pack .mtimes file not loaded for %s", p->pack_name);
+ if (p->num_objects <= pos)
+ BUG("pack .mtimes out-of-bounds (%"PRIu32" vs %"PRIu32")",
+ pos, p->num_objects);
+
+ return get_be32(p->mtimes_map + pos + 3);
+}
new file mode 100644
@@ -0,0 +1,15 @@
+#ifndef PACK_MTIMES_H
+#define PACK_MTIMES_H
+
+#include "git-compat-util.h"
+
+#define MTIMES_SIGNATURE 0x4d544d45 /* "MTME" */
+#define MTIMES_VERSION 1
+
+struct packed_git;
+
+int load_pack_mtimes(struct packed_git *p);
+
+uint32_t nth_packed_mtime(struct packed_git *p, uint32_t pos);
+
+#endif
@@ -334,12 +334,22 @@ static void close_pack_revindex(struct packed_git *p)
p->revindex_data = NULL;
}
+static void close_pack_mtimes(struct packed_git *p)
+{
+ if (!p->mtimes_map)
+ return;
+
+ munmap((void *)p->mtimes_map, p->mtimes_size);
+ p->mtimes_map = NULL;
+}
+
void close_pack(struct packed_git *p)
{
close_pack_windows(p);
close_pack_fd(p);
close_pack_index(p);
close_pack_revindex(p);
+ close_pack_mtimes(p);
oidset_clear(&p->bad_objects);
}
@@ -363,7 +373,7 @@ void close_object_store(struct raw_object_store *o)
void unlink_pack_path(const char *pack_name, int force_delete)
{
- static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor"};
+ static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor", ".mtimes"};
int i;
struct strbuf buf = STRBUF_INIT;
size_t plen;
@@ -718,6 +728,10 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
if (!access(p->pack_name, F_OK))
p->pack_promisor = 1;
+ xsnprintf(p->pack_name + path_len, alloc - path_len, ".mtimes");
+ if (!access(p->pack_name, F_OK))
+ p->is_cruft = 1;
+
xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
free(p);
@@ -869,7 +883,8 @@ static void prepare_pack(const char *full_name, size_t full_name_len,
ends_with(file_name, ".pack") ||
ends_with(file_name, ".bitmap") ||
ends_with(file_name, ".keep") ||
- ends_with(file_name, ".promisor"))
+ ends_with(file_name, ".promisor") ||
+ ends_with(file_name, ".mtimes"))
string_list_append(data->garbage, full_name);
else
report_garbage(PACKDIR_FILE_GARBAGE, full_name);
To store the individual mtimes of objects in a cruft pack, introduce a new `.mtimes` format that can optionally accompany a single pack in the repository. The format is defined in Documentation/technical/pack-format.txt, and stores a 4-byte network order timestamp for each object in name (index) order. This patch prepares for cruft packs by defining the `.mtimes` format, and introducing a basic API that callers can use to read out individual mtimes. Signed-off-by: Taylor Blau <me@ttaylorr.com> --- Documentation/technical/pack-format.txt | 19 ++++ Makefile | 1 + builtin/repack.c | 1 + object-store.h | 5 +- pack-mtimes.c | 126 ++++++++++++++++++++++++ pack-mtimes.h | 15 +++ packfile.c | 19 +++- 7 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 pack-mtimes.c create mode 100644 pack-mtimes.h