* [PATCH 3/6 (v4)] support for non-commit object caching in rev-cache
From: Nick Edelen @ 2009-08-17 12:31 UTC (permalink / raw)
To: Junio C Hamano, Nicolas Pitre, Johannes Schindelin, Sam Vilain,
Michael J Gruber
Summarized, this third patch contains:
- support for non-commit object caching
- expansion of porcelain to accomodate non-commit objects
- appropriate tests
Objects are stored relative to the commit in which they were introduced --
commits are 'diffed' against their parents. This will eliminate the need for
tree recursion in cached commits (significantly reducing I/O), and potentially
be useful to external applications.
Signed-off-by: Nick Edelen <sirnot@gmail.com>
---
rev-cache.c | 204 ++++++++++++++++++++++++++++++++++++++++++++-
t/t6015-rev-cache-list.sh | 8 ++
2 files changed, 209 insertions(+), 3 deletions(-)
diff --git a/rev-cache.c b/rev-cache.c
index a01db87..ddcc596 100644
--- a/rev-cache.c
+++ b/rev-cache.c
@@ -257,6 +257,32 @@ unsigned char *get_cache_slice(struct commit *commit)
/* traversal */
+static void handle_noncommit(struct rev_info *revs, unsigned char *ptr, struct rc_object_entry *entry)
+{
+ struct object *obj = 0;
+
+ switch (entry->type) {
+ case OBJ_TREE :
+ if (revs->tree_objects)
+ obj = (struct object *)lookup_tree(entry->sha1);
+ break;
+ case OBJ_BLOB :
+ if (revs->blob_objects)
+ obj = (struct object *)lookup_blob(entry->sha1);
+ break;
+ case OBJ_TAG :
+ if (revs->tag_objects)
+ obj = (struct object *)lookup_tag(entry->sha1);
+ break;
+ }
+
+ if (!obj)
+ return;
+
+ obj->flags |= FACE_VALUE;
+ add_pending_object(revs, obj, "");
+}
+
static int setup_traversal(struct rc_slice_header *head, unsigned char *map, struct commit *commit, struct commit_list **work)
{
struct rc_index_entry *iep;
@@ -293,7 +319,7 @@ static int setup_traversal(struct rc_slice_header *head, unsigned char *map, str
if (iep->pos < retval)
retval = iep->pos;
-
+
oep = RC_OBTAIN_OBJECT_ENTRY(map + iep->pos);
/* mark this for later */
@@ -345,9 +371,12 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
i += RC_ACTUAL_OBJECT_ENTRY_SIZE(entry);
/* add extra objects if necessary */
- if (entry->type != OBJ_COMMIT)
+ if (entry->type != OBJ_COMMIT) {
+ if (consume_children)
+ handle_noncommit(revs, map + index, entry);
+
continue;
- else
+ } else
consume_children = 0;
if (path >= total_path_nr)
@@ -775,6 +804,171 @@ static void add_object_entry(const unsigned char *sha1, int type, struct rc_obje
}
+/* returns non-zero to continue parsing, 0 to skip */
+typedef int (*dump_tree_fn)(const unsigned char *, const char *, unsigned int); /* sha1, path, mode */
+
+/* we need to walk the trees by hash, so unfortunately we can't use traverse_trees in tree-walk.c */
+static int dump_tree(struct tree *tree, dump_tree_fn fn)
+{
+ struct tree_desc desc;
+ struct name_entry entry;
+ struct tree *subtree;
+ int r;
+
+ if (parse_tree(tree))
+ return -1;
+
+ init_tree_desc(&desc, tree->buffer, tree->size);
+ while (tree_entry(&desc, &entry)) {
+ switch (fn(entry.sha1, entry.path, entry.mode)) {
+ case 0 :
+ goto continue_loop;
+ default :
+ break;
+ }
+
+ if (S_ISDIR(entry.mode)) {
+ subtree = lookup_tree(entry.sha1);
+ if (!subtree)
+ return -2;
+
+ if ((r = dump_tree(subtree, fn)) < 0)
+ return r;
+ }
+
+continue_loop:
+ continue;
+ }
+
+ return 0;
+}
+
+static int dump_tree_callback(const unsigned char *sha1, const char *path, unsigned int mode)
+{
+ unsigned char data[21];
+
+ hashcpy(data, sha1);
+ data[20] = !!S_ISDIR(mode);
+
+ strbuf_add(acc_buffer, data, 21);
+
+ return 1;
+}
+
+static void tree_addremove(struct diff_options *options,
+ int whatnow, unsigned mode,
+ const unsigned char *sha1,
+ const char *concatpath)
+{
+ unsigned char data[21];
+
+ if (whatnow != '+')
+ return;
+
+ hashcpy(data, sha1);
+ data[20] = !!S_ISDIR(mode);
+
+ strbuf_add(acc_buffer, data, 21);
+}
+
+static void tree_change(struct diff_options *options,
+ unsigned old_mode, unsigned new_mode,
+ const unsigned char *old_sha1,
+ const unsigned char *new_sha1,
+ const char *concatpath)
+{
+ unsigned char data[21];
+
+ if (!hashcmp(old_sha1, new_sha1))
+ return;
+
+ hashcpy(data, new_sha1);
+ data[20] = !!S_ISDIR(new_mode);
+
+ strbuf_add(acc_buffer, data, 21);
+}
+
+static int sort_type_hash(const void *a, const void *b)
+{
+ const unsigned char *sa = (const unsigned char *)a,
+ *sb = (const unsigned char *)b;
+
+ if (sa[20] == sb[20])
+ return hashcmp(sa, sb);
+
+ return sa[20] > sb[20] ? -1 : 1;
+}
+
+static int add_unique_objects(struct commit *commit)
+{
+ struct commit_list *list;
+ struct strbuf os, ost, *orig_buf;
+ struct diff_options opts;
+ int i, j, next;
+ char is_first = 1;
+
+ strbuf_init(&os, 0);
+ strbuf_init(&ost, 0);
+ orig_buf = acc_buffer;
+
+ diff_setup(&opts);
+ DIFF_OPT_SET(&opts, RECURSIVE);
+ DIFF_OPT_SET(&opts, TREE_IN_RECURSIVE);
+ opts.change = tree_change;
+ opts.add_remove = tree_addremove;
+
+ /* this is only called for non-ends (ie. all parents interesting) */
+ for (list = commit->parents; list; list = list->next) {
+ if (is_first)
+ acc_buffer = &os;
+ else
+ acc_buffer = &ost;
+
+ strbuf_setlen(acc_buffer, 0);
+ diff_tree_sha1(list->item->tree->object.sha1, commit->tree->object.sha1, "", &opts);
+ qsort(acc_buffer->buf, acc_buffer->len / 21, 21, (int (*)(const void *, const void *))hashcmp);
+
+ /* take intersection */
+ if (!is_first) {
+ for (next = i = j = 0; i < os.len; i += 21) {
+ while (j < ost.len && hashcmp((unsigned char *)(ost.buf + j), (unsigned char *)(os.buf + i)) < 0)
+ j += 21;
+
+ if (j >= ost.len || hashcmp((unsigned char *)(ost.buf + j), (unsigned char *)(os.buf + i)))
+ continue;
+
+ if (next != i)
+ memcpy(os.buf + next, os.buf + i, 21);
+ next += 21;
+ }
+
+ if (next != i)
+ strbuf_setlen(&os, next);
+ } else
+ is_first = 0;
+ }
+
+ if (is_first) {
+ acc_buffer = &os;
+ dump_tree(commit->tree, dump_tree_callback);
+ }
+
+ if (os.len)
+ qsort(os.buf, os.len / 21, 21, sort_type_hash);
+
+ acc_buffer = orig_buf;
+ for (i = 0; i < os.len; i += 21)
+ add_object_entry((unsigned char *)(os.buf + i), os.buf[i + 20] ? OBJ_TREE : OBJ_BLOB, 0, 0, 0);
+
+ /* last but not least, the main tree */
+ add_object_entry(commit->tree->object.sha1, OBJ_TREE, 0, 0, 0);
+
+ strbuf_release(&ost);
+ strbuf_release(&os);
+
+ return i / 21 + 1;
+}
+
static void init_revcache_directory(void)
{
struct stat fi;
@@ -900,6 +1094,10 @@ int make_cache_slice(struct rev_cache_info *rci,
add_object_entry(0, 0, &object, &merge_paths, &split_paths);
object_nr++;
+ /* add all unique children for this commit */
+ if (rci->objects && !object.is_end)
+ object_nr += add_unique_objects(commit);
+
/* print every ~1MB or so */
if (buffer.len > 1000000) {
write_in_full(fd, buffer.buf, buffer.len);
diff --git a/t/t6015-rev-cache-list.sh b/t/t6015-rev-cache-list.sh
index e7474fd..afa0303 100755
--- a/t/t6015-rev-cache-list.sh
+++ b/t/t6015-rev-cache-list.sh
@@ -79,6 +79,7 @@ test_expect_success 'init repo' '
git-rev-list HEAD --not HEAD~3 >proper_commit_list_limited
git-rev-list HEAD >proper_commit_list
+git-rev-list HEAD --objects >proper_object_list
test_expect_success 'make cache slice' '
git-rev-cache add HEAD 2>output.err &&
@@ -101,4 +102,11 @@ test_expect_success 'test rev-caches walker directly (unlimited)' '
test_cmp_sorted list proper_commit_list
'
+#do the same for objects
+test_expect_success 'test rev-caches walker with objects' '
+ git-rev-cache walk --objects HEAD >list &&
+ test_cmp_sorted list proper_object_list
+'
+
test_done
+
--
tg: (212c9b6..) t/revcache/objects (depends on: t/revcache/basic)
^ permalink raw reply related
* [PATCH 2/6 (v4)] basic revision cache system, no integration or features
From: Nick Edelen @ 2009-08-17 12:31 UTC (permalink / raw)
To: Junio C Hamano, Nicolas Pitre, Johannes Schindelin, Sam Vilain,
Michael J Gruber
Second in the revision cache series, this particular patch provides:
- minimal API: caching only commit topo data
- minimal porcelain: add and walk cache slices
- appropriate tests
Signed-off-by: Nick Edelen <sirnot@gmail.com>
---
Makefile | 2 +
builtin-rev-cache.c | 206 ++++++++
builtin.h | 1 +
commit.c | 2 +
git.c | 1 +
rev-cache.c | 1169 +++++++++++++++++++++++++++++++++++++++++++++
rev-cache.h | 107 ++++
revision.c | 2 +-
revision.h | 26 +-
t/t6015-rev-cache-list.sh | 104 ++++
10 files changed, 1618 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index e6df8ec..1ceb514 100644
--- a/Makefile
+++ b/Makefile
@@ -537,6 +537,7 @@ LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
LIB_OBJS += remote.o
LIB_OBJS += rerere.o
+LIB_OBJS += rev-cache.o
LIB_OBJS += revision.o
LIB_OBJS += run-command.o
LIB_OBJS += server-info.o
@@ -627,6 +628,7 @@ BUILTIN_OBJS += builtin-reflog.o
BUILTIN_OBJS += builtin-remote.o
BUILTIN_OBJS += builtin-rerere.o
BUILTIN_OBJS += builtin-reset.o
+BUILTIN_OBJS += builtin-rev-cache.o
BUILTIN_OBJS += builtin-rev-list.o
BUILTIN_OBJS += builtin-rev-parse.o
BUILTIN_OBJS += builtin-revert.o
diff --git a/builtin-rev-cache.c b/builtin-rev-cache.c
new file mode 100644
index 0000000..c27e0f3
--- /dev/null
+++ b/builtin-rev-cache.c
@@ -0,0 +1,206 @@
+#include "cache.h"
+#include "object.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "rev-cache.h"
+
+/* porcelain for rev-cache.c */
+static int handle_add(int argc, const char *argv[]) /* args beyond this command */
+{
+ struct rev_info revs;
+ struct rev_cache_info rci;
+ char dostdin = 0;
+ unsigned int flags = 0;
+ int i, retval;
+ unsigned char cache_sha1[20];
+ struct commit_list *starts = 0, *ends = 0;
+ struct commit *commit;
+
+ init_revisions(&revs, 0);
+ init_rev_cache_info(&rci);
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--stdin"))
+ dostdin = 1;
+ else if (!strcmp(argv[i], "--fresh"))
+ starts_from_slices(&revs, UNINTERESTING);
+ else if (!strcmp(argv[i], "--not"))
+ flags ^= UNINTERESTING;
+ else if (!strcmp(argv[i], "--legs"))
+ rci.legs = 1;
+ else if (!strcmp(argv[i], "--no-objects"))
+ rci.objects = 0;
+ else if (!strcmp(argv[i], "--all")) {
+ const char *args[2];
+ int argn = 0;
+
+ args[argn++] = "rev-list";
+ args[argn++] = "--all";
+ setup_revisions(argn, args, &revs, 0);
+ } else
+ handle_revision_arg(argv[i], &revs, flags, 1);
+ }
+
+ if (dostdin) {
+ char line[1000];
+
+ flags = 0;
+ while (fgets(line, sizeof(line), stdin)) {
+ int len = strlen(line);
+ while (len && (line[len - 1] == '\n' || line[len - 1] == '\r'))
+ line[--len] = 0;
+
+ if (!len)
+ break;
+
+ if (!strcmp(line, "--not"))
+ flags ^= UNINTERESTING;
+ else
+ handle_revision_arg(line, &revs, flags, 1);
+ }
+ }
+
+ retval = make_cache_slice(&rci, &revs, &starts, &ends, cache_sha1);
+ if (retval < 0)
+ return retval;
+
+ printf("%s\n", sha1_to_hex(cache_sha1));
+
+ fprintf(stderr, "endpoints:\n");
+ while ((commit = pop_commit(&starts)))
+ fprintf(stderr, "S %s\n", sha1_to_hex(commit->object.sha1));
+ while ((commit = pop_commit(&ends)))
+ fprintf(stderr, "E %s\n", sha1_to_hex(commit->object.sha1));
+
+ return 0;
+}
+
+static int handle_walk(int argc, const char *argv[])
+{
+ struct commit *commit;
+ struct rev_info revs;
+ struct commit_list *queue, *work, **qp;
+ unsigned char *sha1p, *sha1pt;
+ unsigned long date = 0;
+ unsigned int flags = 0;
+ int retval, slop = 5, i;
+
+ init_revisions(&revs, 0);
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--not"))
+ flags ^= UNINTERESTING;
+ else if (!strcmp(argv[i], "--objects"))
+ revs.tree_objects = revs.blob_objects = 1;
+ else
+ handle_revision_arg(argv[i], &revs, flags, 1);
+ }
+
+ work = 0;
+ sha1p = 0;
+ for (i = 0; i < revs.pending.nr; i++) {
+ commit = lookup_commit(revs.pending.objects[i].item->sha1);
+
+ sha1pt = get_cache_slice(commit);
+ if (!sha1pt)
+ die("%s: not in a cache slice", sha1_to_hex(commit->object.sha1));
+
+ if (!i)
+ sha1p = sha1pt;
+ else if (sha1p != sha1pt)
+ die("walking porcelain is /per/ cache slice; commits cannot be spread out amoung several");
+
+ insert_by_date(commit, &work);
+ }
+
+ if (!sha1p)
+ die("nothing to traverse!");
+
+ queue = 0;
+ qp = &queue;
+ commit = pop_commit(&work);
+ retval = traverse_cache_slice(&revs, sha1p, commit, &date, &slop, &qp, &work);
+ if (retval < 0)
+ return retval;
+
+ fprintf(stderr, "queue:\n");
+ while ((commit = pop_commit(&queue)) != 0) {
+ printf("%s\n", sha1_to_hex(commit->object.sha1));
+ }
+
+ fprintf(stderr, "work:\n");
+ while ((commit = pop_commit(&work)) != 0) {
+ printf("%s\n", sha1_to_hex(commit->object.sha1));
+ }
+
+ fprintf(stderr, "pending:\n");
+ for (i = 0; i < revs.pending.nr; i++) {
+ struct object *obj = revs.pending.objects[i].item;
+
+ /* unfortunately, despite our careful generation, object duplication *is* a possibility...
+ * (eg. same object introduced into two different branches) */
+ if (obj->flags & SEEN)
+ continue;
+
+ printf("%s\n", sha1_to_hex(revs.pending.objects[i].item->sha1));
+ obj->flags |= SEEN;
+ }
+
+ return 0;
+}
+
+static int handle_help(void)
+{
+ char *usage = "\
+usage:\n\
+git-rev-cache COMMAND [options] [<commit-id>...]\n\
+commands:\n\
+ add - add revisions to the cache. reads commit ids from stdin, \n\
+ formatted as: START START ... --not END END ...\n\
+ options:\n\
+ --all use all branch heads as starts\n\
+ --fresh exclude everything already in a cache slice\n\
+ --stdin also read commit ids from stdin (same form as cmd)\n\
+ --legs ensure branch is entirely self-contained\n\
+ --no-objects don't add non-commit objects to slice\n\
+ walk - walk a cache slice based on set of commits; formatted as add\n\
+ options:\n\
+ --objects include non-commit objects in traversals\n\
+ fuse - coalesce cache slices into a single cache.\n\
+ options:\n\
+ --all include all objects in repository\n\
+ --no-objects don't add non-commit objects to slice\n\
+ --ignore-size[=N] ignore slices of size >= N; defaults to ~5MB\n\
+ index - regnerate the cache index.";
+
+ puts(usage);
+
+ return 0;
+}
+
+int cmd_rev_cache(int argc, const char *argv[], const char *prefix)
+{
+ const char *arg;
+ int r;
+
+ git_config(git_default_config, NULL);
+
+ if (argc > 1)
+ arg = argv[1];
+ else
+ arg = "";
+
+ argc -= 2;
+ argv += 2;
+ if (!strcmp(arg, "add"))
+ r = handle_add(argc, argv);
+ else if (!strcmp(arg, "walk"))
+ r = handle_walk(argc, argv);
+ else
+ return handle_help();
+
+ fprintf(stderr, "final return value: %d\n", r);
+
+ return 0;
+}
diff --git a/builtin.h b/builtin.h
index 20427d2..00ecc9c 100644
--- a/builtin.h
+++ b/builtin.h
@@ -87,6 +87,7 @@ extern int cmd_remote(int argc, const char **argv, const char *prefix);
extern int cmd_config(int argc, const char **argv, const char *prefix);
extern int cmd_rerere(int argc, const char **argv, const char *prefix);
extern int cmd_reset(int argc, const char **argv, const char *prefix);
+extern int cmd_rev_cache(int argc, const char **argv, const char *prefix);
extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
extern int cmd_revert(int argc, const char **argv, const char *prefix);
diff --git a/commit.c b/commit.c
index e2bcbe8..682e7a7 100644
--- a/commit.c
+++ b/commit.c
@@ -252,6 +252,8 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
item->tree = lookup_tree(parent);
bufptr += 46; /* "tree " + "hex sha1" + "\n" */
pptr = &item->parents;
+ while (pop_commit(pptr))
+ ; /* clear anything from cache */
graft = lookup_commit_graft(item->object.sha1);
while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) {
diff --git a/git.c b/git.c
index 4588a8b..7105c74 100644
--- a/git.c
+++ b/git.c
@@ -342,6 +342,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "repo-config", cmd_config },
{ "rerere", cmd_rerere, RUN_SETUP },
{ "reset", cmd_reset, RUN_SETUP },
+ { "rev-cache", cmd_rev_cache, RUN_SETUP },
{ "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse },
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
diff --git a/rev-cache.c b/rev-cache.c
new file mode 100644
index 0000000..a01db87
--- /dev/null
+++ b/rev-cache.c
@@ -0,0 +1,1169 @@
+#include "cache.h"
+#include "object.h"
+#include "commit.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "blob.h"
+#include "tag.h"
+#include "diff.h"
+#include "revision.h"
+#include "rev-cache.h"
+#include "run-command.h"
+
+/* list resembles pack index format */
+static uint32_t fanout[0xff + 2];
+
+static unsigned char *idx_map;
+static int idx_size;
+static struct rc_index_header idx_head;
+static unsigned char *idx_caches;
+static char no_idx;
+
+static struct strbuf *acc_buffer;
+
+#define SLOP 5
+
+/* initialization */
+
+struct rc_index_entry *from_disked_rc_index_entry(struct rc_index_entry_ondisk *src, struct rc_index_entry *dst)
+{
+ static struct rc_index_entry entry[4];
+ static int cur;
+
+ if (!dst)
+ dst = &entry[cur++ & 0x3];
+
+ dst->sha1 = src->sha1;
+ dst->is_start = !!(src->flags & 0x80);
+ dst->cache_index = src->flags & 0x7f;
+ dst->pos = ntohl(src->pos);
+
+ return dst;
+}
+
+struct rc_index_entry_ondisk *to_disked_rc_index_entry(struct rc_index_entry *src, struct rc_index_entry_ondisk *dst)
+{
+ static struct rc_index_entry_ondisk entry[4];
+ static int cur;
+
+ if (!dst)
+ dst = &entry[cur++ & 0x3];
+
+ hashcpy(dst->sha1, src->sha1);
+ dst->flags = (unsigned char)src->is_start << 7 | (unsigned char)src->cache_index;
+ dst->pos = htonl(src->pos);
+
+ return dst;
+}
+
+struct rc_object_entry *from_disked_rc_object_entry(struct rc_object_entry_ondisk *src, struct rc_object_entry *dst)
+{
+ static struct rc_object_entry entry[4];
+ static int cur;
+
+ if (!dst)
+ dst = &entry[cur++ & 0x3];
+
+ dst->type = src->flags >> 5;
+ dst->is_end = !!(src->flags & 0x10);
+ dst->is_start = !!(src->flags & 0x08);
+ dst->uninteresting = !!(src->flags & 0x04);
+ dst->include = !!(src->flags & 0x02);
+ dst->flag = !!(src->flags & 0x01);
+
+ dst->sha1 = src->sha1;
+ dst->merge_nr = src->merge_nr;
+ dst->split_nr = src->split_nr;
+
+ dst->size_size = src->sizes >> 5;
+ dst->padding = src->sizes & 0x1f;
+
+ dst->date = ntohl(src->date);
+ dst->path = ntohs(src->path);
+
+ return dst;
+}
+
+struct rc_object_entry_ondisk *to_disked_rc_object_entry(struct rc_object_entry *src, struct rc_object_entry_ondisk *dst)
+{
+ static struct rc_object_entry_ondisk entry[4];
+ static int cur;
+
+ if (!dst)
+ dst = &entry[cur++ & 0x3];
+
+ dst->flags = (unsigned char)src->type << 5;
+ dst->flags |= (unsigned char)src->is_end << 4;
+ dst->flags |= (unsigned char)src->is_start << 3;
+ dst->flags |= (unsigned char)src->uninteresting << 2;
+ dst->flags |= (unsigned char)src->include << 1;
+ dst->flags |= (unsigned char)src->flag;
+
+ hashcpy(dst->sha1, src->sha1);
+ dst->merge_nr = src->merge_nr;
+ dst->split_nr = src->split_nr;
+
+ dst->sizes = (unsigned char)src->size_size << 5;
+ dst->sizes |= (unsigned char)src->padding;
+
+ dst->date = htonl(src->date);
+ dst->path = htons(src->path);
+
+ return dst;
+}
+
+static int get_index_head(unsigned char *map, int len, struct rc_index_header *head, uint32_t *fanout, unsigned char **caches)
+{
+ struct rc_index_header whead;
+ int i, index = sizeof(struct rc_index_header);
+
+ memcpy(&whead, map, sizeof(struct rc_index_header));
+ if (memcmp(whead.signature, "REVINDEX", 8) || whead.version != SUPPORTED_REVINDEX_VERSION)
+ return -1;
+
+ memcpy(head->signature, "REVINDEX", 8);
+ head->version = whead.version;
+ head->ofs_objects = ntohl(whead.ofs_objects);
+ head->object_nr = ntohl(whead.object_nr);
+ head->cache_nr = whead.cache_nr;
+ head->max_date = ntohl(whead.max_date);
+
+ if (len < index + head->cache_nr * 20 + 0x100 * sizeof(uint32_t))
+ return -2;
+
+ *caches = xmalloc(head->cache_nr * 20);
+ memcpy(*caches, map + index, head->cache_nr * 20);
+ index += head->cache_nr * 20;
+
+ memcpy(fanout, map + index, 0x100 * sizeof(uint32_t));
+ for (i = 0; i <= 0xff; i++)
+ fanout[i] = ntohl(fanout[i]);
+ fanout[0x100] = len;
+
+ return 0;
+}
+
+/* added in init_index */
+static void cleanup_cache_slices(void)
+{
+ if (idx_map) {
+ free(idx_caches);
+ munmap(idx_map, idx_size);
+ idx_map = 0;
+ }
+
+}
+
+static int init_index(void)
+{
+ int fd;
+ struct stat fi;
+
+ fd = open(git_path("rev-cache/index"), O_RDONLY);
+ if (fd == -1 || fstat(fd, &fi))
+ goto end;
+ if (fi.st_size < sizeof(struct rc_index_header))
+ goto end;
+
+ idx_size = fi.st_size;
+ idx_map = xmmap(0, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (idx_map == MAP_FAILED)
+ goto end;
+ if (get_index_head(idx_map, fi.st_size, &idx_head, fanout, &idx_caches))
+ goto end;
+
+ atexit(cleanup_cache_slices);
+
+ return 0;
+
+end:
+ idx_map = 0;
+ no_idx = 1;
+ return -1;
+}
+
+/* this assumes index is already loaded */
+static struct rc_index_entry_ondisk *search_index_1(unsigned char *sha1)
+{
+ int start, end, starti, endi, i, len, r;
+ struct rc_index_entry_ondisk *ie;
+
+ if (!idx_map)
+ return 0;
+
+ /* binary search */
+ start = fanout[(int)sha1[0]];
+ end = fanout[(int)sha1[0] + 1];
+ len = (end - start) / sizeof(struct rc_index_entry_ondisk);
+ if (!len || len * sizeof(struct rc_index_entry_ondisk) != end - start)
+ return 0;
+
+ starti = 0;
+ endi = len - 1;
+ for (;;) {
+ i = (endi + starti) / 2;
+ ie = (struct rc_index_entry_ondisk *)(idx_map + start + i * sizeof(struct rc_index_entry_ondisk));
+ r = hashcmp(sha1, ie->sha1);
+
+ if (r) {
+ if (starti + 1 == endi) {
+ starti++;
+ continue;
+ } else if (starti == endi)
+ break;
+
+ if (r > 0)
+ starti = i;
+ else /* r < 0 */
+ endi = i;
+ } else
+ return ie;
+ }
+
+ return 0;
+}
+
+static struct rc_index_entry *search_index(unsigned char *sha1)
+{
+ struct rc_index_entry_ondisk *ied = search_index_1(sha1);
+
+ if (ied)
+ return from_disked_rc_index_entry(ied, 0);
+
+ return 0;
+}
+
+unsigned char *get_cache_slice(struct commit *commit)
+{
+ struct rc_index_entry *ie;
+
+ if (!idx_map) {
+ if (no_idx)
+ return 0;
+ init_index();
+ }
+
+ if (commit->date > idx_head.max_date)
+ return 0;
+
+ ie = search_index(commit->object.sha1);
+ if (ie && ie->cache_index < idx_head.cache_nr)
+ return idx_caches + ie->cache_index * 20;
+
+ return 0;
+}
+
+
+/* traversal */
+
+static int setup_traversal(struct rc_slice_header *head, unsigned char *map, struct commit *commit, struct commit_list **work)
+{
+ struct rc_index_entry *iep;
+ struct rc_object_entry *oep;
+ struct commit_list *prev, *wp, **wpp;
+ int retval;
+
+ iep = search_index(commit->object.sha1), 0;
+ oep = RC_OBTAIN_OBJECT_ENTRY(map + iep->pos);
+
+ /* the .uniniteresting bit isn't strictly necessary, as we check the object during traversal as well,
+ * but we might as well initialize it while we're at it */
+ oep->include = 1;
+ oep->uninteresting = !!(commit->object.flags & UNINTERESTING);
+ to_disked_rc_object_entry(oep, (struct rc_object_entry_ondisk *)(map + iep->pos));
+ retval = iep->pos;
+
+ /* include any others in the work array */
+ prev = 0;
+ wpp = work;
+ wp = *work;
+ while (wp) {
+ struct object *obj = &wp->item->object;
+ struct commit *co;
+
+ /* is this in our cache slice? */
+ iep = search_index(obj->sha1);
+ if (!iep || hashcmp(idx_caches + iep->cache_index * 20, head->sha1)) {
+ prev = wp;
+ wp = wp->next;
+ wpp = ℘
+ continue;
+ }
+
+ if (iep->pos < retval)
+ retval = iep->pos;
+
+ oep = RC_OBTAIN_OBJECT_ENTRY(map + iep->pos);
+
+ /* mark this for later */
+ oep->include = 1;
+ oep->uninteresting = !!(obj->flags & UNINTERESTING);
+ to_disked_rc_object_entry(oep, (struct rc_object_entry_ondisk *)(map + iep->pos));
+
+ /* remove from work list */
+ co = pop_commit(wpp);
+ wp = *wpp;
+ if (prev)
+ prev->next = wp;
+ }
+
+ return retval;
+}
+
+#define IPATH 0x40
+#define UPATH 0x80
+
+#define GET_COUNT(x) ((x) & 0x3f)
+#define SET_COUNT(x, s) ((x) = ((x) & ~0x3f) | ((s) & 0x3f))
+
+static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *map,
+ struct rev_info *revs, struct commit *commit,
+ unsigned long *date_so_far, int *slop_so_far,
+ struct commit_list ***queue, struct commit_list **work)
+{
+ struct commit_list *insert_cache = 0;
+ struct commit **last_objects, *co;
+ int i, total_path_nr = head->path_nr, retval = -1;
+ char consume_children = 0;
+ unsigned char *paths;
+
+ i = setup_traversal(head, map, commit, work);
+ if (i < 0)
+ return -1;
+
+ paths = xcalloc(total_path_nr, sizeof(uint16_t));
+ last_objects = xcalloc(total_path_nr, sizeof(struct commit *));
+
+ /* i already set */
+ while (i < head->size) {
+ struct rc_object_entry *entry = RC_OBTAIN_OBJECT_ENTRY(map + i);
+ int path = entry->path;
+ struct object *obj;
+ int index = i;
+
+ i += RC_ACTUAL_OBJECT_ENTRY_SIZE(entry);
+
+ /* add extra objects if necessary */
+ if (entry->type != OBJ_COMMIT)
+ continue;
+ else
+ consume_children = 0;
+
+ if (path >= total_path_nr)
+ goto end;
+
+ /* in one of our branches?
+ * uninteresting trumps interesting */
+ if (entry->include)
+ paths[path] |= entry->uninteresting ? UPATH : IPATH;
+ else if (!paths[path])
+ continue;
+
+ /* date stuff */
+ if (revs->max_age != -1 && entry->date < revs->max_age)
+ paths[path] |= UPATH;
+
+ /* lookup object */
+ co = lookup_commit(entry->sha1);
+ obj = &co->object;
+
+ if (obj->flags & UNINTERESTING)
+ paths[path] |= UPATH;
+
+ if ((paths[path] & IPATH) && (paths[path] & UPATH)) {
+ paths[path] = UPATH;
+
+ /* mark edge */
+ if (last_objects[path]) {
+ parse_commit(last_objects[path]);
+
+ last_objects[path]->object.flags &= ~FACE_VALUE;
+ last_objects[path] = 0;
+ }
+ }
+
+ /* now we gotta re-assess the whole interesting thing... */
+ entry->uninteresting = !!(paths[path] & UPATH);
+
+ /* first close paths */
+ if (entry->split_nr) {
+ int j, off = index + sizeof(struct rc_object_entry_ondisk) + RC_PATH_SIZE(entry->merge_nr);
+
+ for (j = 0; j < entry->split_nr; j++) {
+ unsigned short p = ntohs(*(uint16_t *)(map + off + RC_PATH_SIZE(j)));
+
+ if (p >= total_path_nr)
+ goto end;
+
+ /* boundary commit? */
+ if ((paths[p] & IPATH) && entry->uninteresting) {
+ if (last_objects[p]) {
+ parse_commit(last_objects[p]);
+
+ last_objects[p]->object.flags &= ~FACE_VALUE;
+ last_objects[p] = 0;
+ }
+ } else if (last_objects[p] && !last_objects[p]->object.parsed)
+ commit_list_insert(co, &last_objects[p]->parents);
+
+ /* can't close a merge path until all are parents have been encountered */
+ if (GET_COUNT(paths[p])) {
+ SET_COUNT(paths[p], GET_COUNT(paths[p]) - 1);
+
+ if (GET_COUNT(paths[p]))
+ continue;
+ }
+
+ paths[p] = 0;
+ last_objects[p] = 0;
+ }
+ }
+
+ /* make topo relations */
+ if (last_objects[path] && !last_objects[path]->object.parsed)
+ commit_list_insert(co, &last_objects[path]->parents);
+
+ /* initialize commit */
+ if (!entry->is_end) {
+ co->date = entry->date;
+ obj->flags |= ADDED | FACE_VALUE;
+ } else
+ parse_commit(co);
+
+ obj->flags |= SEEN;
+
+ if (entry->uninteresting)
+ obj->flags |= UNINTERESTING;
+
+ /* we need to know what the edges are */
+ last_objects[path] = co;
+
+ /* add to list */
+ if (!(obj->flags & UNINTERESTING) || revs->show_all) {
+ if (entry->is_end)
+ insert_by_date_cached(co, work, insert_cache, &insert_cache);
+ else
+ *queue = &commit_list_insert(co, *queue)->next;
+
+ /* add children to list as well */
+ if (obj->flags & UNINTERESTING)
+ consume_children = 0;
+ else
+ consume_children = 1;
+ }
+
+ /* open parents */
+ if (entry->merge_nr) {
+ int j, off = index + sizeof(struct rc_object_entry_ondisk);
+ char flag = entry->uninteresting ? UPATH : IPATH;
+
+ for (j = 0; j < entry->merge_nr; j++) {
+ unsigned short p = ntohs(*(uint16_t *)(map + off + RC_PATH_SIZE(j)));
+
+ if (p >= total_path_nr)
+ goto end;
+
+ if (paths[p] & flag)
+ continue;
+
+ paths[p] |= flag;
+ }
+
+ /* make sure we don't use this path before all our parents have had their say */
+ SET_COUNT(paths[path], entry->merge_nr);
+ }
+
+ }
+
+ retval = 0;
+
+end:
+ free(paths);
+ free(last_objects);
+
+ return retval;
+}
+
+static int get_cache_slice_header(unsigned char *cache_sha1, unsigned char *map, int len, struct rc_slice_header *head)
+{
+ int t;
+
+ memcpy(head, map, sizeof(struct rc_slice_header));
+ head->ofs_objects = ntohl(head->ofs_objects);
+ head->object_nr = ntohl(head->object_nr);
+ head->size = ntohl(head->size);
+ head->path_nr = ntohs(head->path_nr);
+
+ if (memcmp(head->signature, "REVCACHE", 8))
+ return -1;
+ if (head->version != SUPPORTED_REVCACHE_VERSION)
+ return -2;
+ if (hashcmp(head->sha1, cache_sha1))
+ return -3;
+ t = sizeof(struct rc_slice_header);
+ if (t != head->ofs_objects || t >= len)
+ return -4;
+
+ head->size = len;
+
+ return 0;
+}
+
+int traverse_cache_slice(struct rev_info *revs,
+ unsigned char *cache_sha1, struct commit *commit,
+ unsigned long *date_so_far, int *slop_so_far,
+ struct commit_list ***queue, struct commit_list **work)
+{
+ int fd = -1, retval = -3;
+ struct stat fi;
+ struct rc_slice_header head;
+ struct rev_cache_info *rci;
+ unsigned char *map = MAP_FAILED;
+
+ /* the index should've been loaded already to find cache_sha1, but it's good
+ * to be absolutely sure... */
+ if (!idx_map)
+ init_index();
+ if (!idx_map)
+ return -1;
+
+ /* load options */
+ rci = &revs->rev_cache_info;
+
+ memset(&head, 0, sizeof(struct rc_slice_header));
+
+ fd = open(git_path("rev-cache/%s", sha1_to_hex(cache_sha1)), O_RDONLY);
+ if (fd == -1)
+ goto end;
+ if (fstat(fd, &fi) || fi.st_size < sizeof(struct rc_slice_header))
+ goto end;
+
+ map = xmmap(0, fi.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED)
+ goto end;
+ if (get_cache_slice_header(cache_sha1, map, fi.st_size, &head))
+ goto end;
+
+ retval = traverse_cache_slice_1(&head, map, revs, commit, date_so_far, slop_so_far, queue, work);
+
+end:
+ if (map != MAP_FAILED)
+ munmap(map, fi.st_size);
+ if (fd != -1)
+ close(fd);
+
+ return retval;
+}
+
+
+
+/* generation */
+
+struct path_track {
+ struct commit *commit;
+ int path; /* for keeping track of children */
+
+ struct path_track *next, *prev;
+};
+
+static unsigned char *paths;
+static int path_nr = 1, path_sz;
+
+static struct path_track *path_track;
+static struct path_track *path_track_alloc;
+
+#define PATH_IN_USE 0x80 /* biggest bit we can get as a char */
+
+static int get_new_path(void)
+{
+ int i;
+
+ for (i = 1; i < path_nr; i++)
+ if (!paths[i])
+ break;
+
+ if (i == path_nr) {
+ if (path_nr >= path_sz) {
+ path_sz += 50;
+ paths = xrealloc(paths, path_sz);
+ memset(paths + path_sz - 50, 0, 50);
+ }
+ path_nr++;
+ }
+
+ paths[i] = PATH_IN_USE;
+ return i;
+}
+
+static void remove_path_track(struct path_track **ppt, char total_free)
+{
+ struct path_track *t = *ppt;
+
+ if (t->next)
+ t->next->prev = t->prev;
+ if (t->prev)
+ t->prev->next = t->next;
+
+ t = t->next;
+
+ if (total_free)
+ free(*ppt);
+ else {
+ (*ppt)->next = path_track_alloc;
+ path_track_alloc = *ppt;
+ }
+
+ *ppt = t;
+}
+
+static struct path_track *make_path_track(struct path_track **head, struct commit *commit)
+{
+ struct path_track *pt;
+
+ if (path_track_alloc) {
+ pt = path_track_alloc;
+ path_track_alloc = pt->next;
+ } else
+ pt = xmalloc(sizeof(struct path_track));
+
+ memset(pt, 0, sizeof(struct path_track));
+ pt->commit = commit;
+
+ pt->next = *head;
+ if (*head)
+ (*head)->prev = pt;
+ *head = pt;
+
+ return pt;
+}
+
+static void add_path_to_track(struct commit *commit, int path)
+{
+ make_path_track(&path_track, commit);
+ path_track->path = path;
+}
+
+static void handle_paths(struct commit *commit, struct rc_object_entry *object, struct strbuf *merge_str, struct strbuf *split_str)
+{
+ int child_nr, parent_nr, open_parent_nr, this_path;
+ struct commit_list *list;
+ struct commit *first_parent;
+ struct path_track **ppt, *pt;
+
+ /* we can only re-use a closed path once all it's children have been encountered,
+ * as we need to keep track of commit boundaries */
+ ppt = &path_track;
+ pt = *ppt;
+ child_nr = 0;
+ while (pt) {
+ if (pt->commit == commit) {
+ uint16_t write_path;
+
+ if (paths[pt->path] != PATH_IN_USE)
+ paths[pt->path]--;
+
+ /* make sure we can handle this */
+ child_nr++;
+ if (child_nr > 0x7f)
+ die("%s: too many branches! rev-cache can only handle %d parents/children per commit",
+ sha1_to_hex(object->sha1), 0x7f);
+
+ /* add to split list */
+ object->split_nr++;
+ write_path = htons((uint16_t)pt->path);
+ strbuf_add(split_str, &write_path, sizeof(uint16_t));
+
+ remove_path_track(ppt, 0);
+ pt = *ppt;
+ } else {
+ pt = pt->next;
+ ppt = &pt;
+ }
+ }
+
+ /* initialize our self! */
+ if (!commit->indegree) {
+ commit->indegree = get_new_path();
+ object->is_start = 1;
+ }
+
+ this_path = commit->indegree;
+ paths[this_path] = PATH_IN_USE;
+ object->path = this_path;
+
+ /* count interesting parents */
+ parent_nr = open_parent_nr = 0;
+ first_parent = 0;
+ for (list = commit->parents; list; list = list->next) {
+ if (list->item->object.flags & UNINTERESTING) {
+ object->is_end = 1;
+ continue;
+ }
+
+ parent_nr++;
+ if (!list->item->indegree)
+ open_parent_nr++;
+ if (!first_parent)
+ first_parent = list->item;
+ }
+
+ if (!parent_nr)
+ return;
+
+ if (parent_nr == 1 && open_parent_nr == 1) {
+ first_parent->indegree = this_path;
+ return;
+ }
+
+ /* bail out on obscene parent/child #s */
+ if (parent_nr > 0x7f)
+ die("%s: too many parents in merge! rev-cache can only handle %d parents/children per commit",
+ sha1_to_hex(object->sha1), 0x7f);
+
+ /* make merge list */
+ object->merge_nr = parent_nr;
+ paths[this_path] = parent_nr;
+
+ for (list = commit->parents; list; list = list->next) {
+ struct commit *p = list->item;
+ uint16_t write_path;
+
+ if (p->object.flags & UNINTERESTING)
+ continue;
+
+ /* unfortunately due to boundary tracking we can't re-use merge paths
+ * (unable to guarantee last parent path = this -> last won't always be able to
+ * set this as a boundary object */
+ if (!p->indegree)
+ p->indegree = get_new_path();
+
+ write_path = htons((uint16_t)p->indegree);
+ strbuf_add(merge_str, &write_path, sizeof(uint16_t));
+
+ /* make sure path is properly ended */
+ add_path_to_track(p, this_path);
+ }
+
+}
+
+
+static void add_object_entry(const unsigned char *sha1, int type, struct rc_object_entry *nothisone,
+ struct strbuf *merge_str, struct strbuf *split_str)
+{
+ struct rc_object_entry object;
+
+ if (!nothisone) {
+ memset(&object, 0, sizeof(object));
+ object.sha1 = (unsigned char *)sha1;
+ object.type = type;
+
+ if (merge_str)
+ object.merge_nr = merge_str->len / sizeof(uint16_t);
+ if (split_str)
+ object.split_nr = split_str->len / sizeof(uint16_t);
+
+ nothisone = &object;
+ }
+
+ strbuf_add(acc_buffer, to_disked_rc_object_entry(nothisone, 0), sizeof(struct rc_object_entry_ondisk));
+
+ if (merge_str && merge_str->len)
+ strbuf_add(acc_buffer, merge_str->buf, merge_str->len);
+ if (split_str && split_str->len)
+ strbuf_add(acc_buffer, split_str->buf, split_str->len);
+
+}
+
+static void init_revcache_directory(void)
+{
+ struct stat fi;
+
+ if (stat(git_path("rev-cache"), &fi) || !S_ISDIR(fi.st_mode))
+ if (mkdir(git_path("rev-cache"), 0666))
+ die("can't make rev-cache directory");
+
+}
+
+void init_rev_cache_info(struct rev_cache_info *rci)
+{
+ rci->objects = 1;
+ rci->legs = 0;
+ rci->make_index = 1;
+
+ rci->add_to_pending = 1;
+
+ rci->ignore_size = 0;
+}
+
+void maybe_fill_with_defaults(struct rev_cache_info *rci)
+{
+ static struct rev_cache_info def_rci;
+
+ if (rci)
+ return;
+
+ init_rev_cache_info(&def_rci);
+ rci = &def_rci;
+}
+
+int make_cache_slice(struct rev_cache_info *rci,
+ struct rev_info *revs, struct commit_list **starts, struct commit_list **ends,
+ unsigned char *cache_sha1)
+{
+ struct rev_info therevs;
+ struct strbuf buffer, startlist, endlist;
+ struct rc_slice_header head;
+ struct commit *commit;
+ unsigned char sha1[20];
+ struct strbuf merge_paths, split_paths;
+ int object_nr, total_sz, fd;
+ char file[PATH_MAX], *newfile;
+ struct rev_cache_info *trci;
+ git_SHA_CTX ctx;
+
+ maybe_fill_with_defaults(rci);
+
+ init_revcache_directory();
+ strcpy(file, git_path("rev-cache/XXXXXX"));
+ fd = xmkstemp(file);
+
+ strbuf_init(&buffer, 0);
+ strbuf_init(&startlist, 0);
+ strbuf_init(&endlist, 0);
+ strbuf_init(&merge_paths, 0);
+ strbuf_init(&split_paths, 0);
+ acc_buffer = &buffer;
+
+ if (!revs) {
+ revs = &therevs;
+ init_revisions(revs, 0);
+
+ /* we're gonna assume no one else has already traversed this... */
+ while ((commit = pop_commit(starts)))
+ add_pending_object(revs, &commit->object, 0);
+
+ while ((commit = pop_commit(ends))) {
+ commit->object.flags |= UNINTERESTING;
+ add_pending_object(revs, &commit->object, 0);
+ }
+ }
+
+ /* write head placeholder */
+ memset(&head, 0, sizeof(head));
+ head.ofs_objects = htonl(sizeof(head));
+ xwrite(fd, &head, sizeof(head));
+
+ /* init revisions! */
+ revs->tree_objects = 1;
+ revs->blob_objects = 1;
+ revs->topo_order = 1;
+ revs->lifo = 1;
+
+ /* re-use info from other caches if possible */
+ trci = &revs->rev_cache_info;
+ init_rev_cache_info(trci);
+ trci->add_to_pending = 0;
+
+ setup_revisions(0, 0, revs, 0);
+ if (prepare_revision_walk(revs))
+ die("died preparing revision walk");
+
+ object_nr = total_sz = 0;
+ while ((commit = get_revision(revs)) != 0) {
+ struct rc_object_entry object;
+
+ strbuf_setlen(&merge_paths, 0);
+ strbuf_setlen(&split_paths, 0);
+
+ memset(&object, 0, sizeof(object));
+ object.type = OBJ_COMMIT;
+ object.date = commit->date;
+ object.sha1 = commit->object.sha1;
+
+ handle_paths(commit, &object, &merge_paths, &split_paths);
+
+ if (object.is_end) {
+ strbuf_add(&endlist, object.sha1, 20);
+ if (ends)
+ commit_list_insert(commit, ends);
+ }
+ /* the two *aren't* mutually exclusive */
+ if (object.is_start) {
+ strbuf_add(&startlist, object.sha1, 20);
+ if (starts)
+ commit_list_insert(commit, starts);
+ }
+
+ commit->indegree = 0;
+
+ add_object_entry(0, 0, &object, &merge_paths, &split_paths);
+ object_nr++;
+
+ /* print every ~1MB or so */
+ if (buffer.len > 1000000) {
+ write_in_full(fd, buffer.buf, buffer.len);
+ total_sz += buffer.len;
+
+ strbuf_setlen(&buffer, 0);
+ }
+ }
+
+ if (buffer.len) {
+ write_in_full(fd, buffer.buf, buffer.len);
+ total_sz += buffer.len;
+ }
+
+ /* go ahead a free some stuff... */
+ strbuf_release(&buffer);
+ strbuf_release(&merge_paths);
+ strbuf_release(&split_paths);
+ if (path_sz)
+ free(paths);
+ while (path_track_alloc)
+ remove_path_track(&path_track_alloc, 1);
+
+ /* the meaning of the hash name is more or less irrelevant, it's the uniqueness that matters */
+ strbuf_add(&endlist, startlist.buf, startlist.len);
+ git_SHA1_Init(&ctx);
+ git_SHA1_Update(&ctx, endlist.buf, endlist.len);
+ git_SHA1_Final(sha1, &ctx);
+
+ /* now actually initialize header */
+ strcpy(head.signature, "REVCACHE");
+ head.version = SUPPORTED_REVCACHE_VERSION;
+
+ head.object_nr = htonl(object_nr);
+ head.size = htonl(ntohl(head.ofs_objects) + total_sz);
+ head.path_nr = htons(path_nr);
+ hashcpy(head.sha1, sha1);
+
+ /* some info! */
+ fprintf(stderr, "objects: %d\n", object_nr);
+ fprintf(stderr, "paths: %d\n", path_nr);
+
+ lseek(fd, 0, SEEK_SET);
+ xwrite(fd, &head, sizeof(head));
+
+ if (rci->make_index && make_cache_index(rci, sha1, fd, ntohl(head.size)) < 0)
+ die("can't update index");
+
+ close(fd);
+
+ newfile = git_path("rev-cache/%s", sha1_to_hex(sha1));
+ if (rename(file, newfile))
+ die("can't move temp file");
+
+ /* let our caller know what we've just made */
+ if (cache_sha1)
+ hashcpy(cache_sha1, sha1);
+
+ strbuf_release(&endlist);
+ strbuf_release(&startlist);
+
+ return 0;
+}
+
+
+static int index_sort_hash(const void *a, const void *b)
+{
+ return hashcmp(((struct rc_index_entry_ondisk *)a)->sha1, ((struct rc_index_entry_ondisk *)b)->sha1);
+}
+
+static int write_cache_index(struct strbuf *body)
+{
+ struct rc_index_header whead;
+ struct lock_file *lk;
+ int fd, i;
+
+ /* clear index map if loaded */
+ if (idx_map) {
+ munmap(idx_map, idx_size);
+ idx_map = 0;
+ }
+
+ lk = xcalloc(sizeof(struct lock_file), 1);
+ fd = hold_lock_file_for_update(lk, git_path("rev-cache/index"), 0);
+ if (fd < 0) {
+ free(lk);
+ return -1;
+ }
+
+ /* endianness yay! */
+ memset(&whead, 0, sizeof(whead));
+ memcpy(whead.signature, "REVINDEX", 8);
+ whead.version = idx_head.version;
+ whead.ofs_objects = htonl(idx_head.ofs_objects);
+ whead.object_nr = htonl(idx_head.object_nr);
+ whead.cache_nr = idx_head.cache_nr;
+ whead.max_date = htonl(idx_head.max_date);
+
+ write(fd, &whead, sizeof(struct rc_index_header));
+ write_in_full(fd, idx_caches, idx_head.cache_nr * 20);
+
+ for (i = 0; i <= 0xff; i++)
+ fanout[i] = htonl(fanout[i]);
+ write_in_full(fd, fanout, 0x100 * sizeof(uint32_t));
+
+ write_in_full(fd, body->buf, body->len);
+
+ if (commit_lock_file(lk) < 0)
+ return -2;
+
+ /* lk freed by lockfile.c */
+
+ return 0;
+}
+
+int make_cache_index(struct rev_cache_info *rci, unsigned char *cache_sha1,
+ int fd, unsigned int size)
+{
+ struct strbuf buffer;
+ int i, cache_index, cur;
+ unsigned char *map;
+ unsigned long max_date;
+
+ if (!idx_map)
+ init_index();
+
+ lseek(fd, 0, SEEK_SET);
+ map = xmmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED)
+ return -1;
+
+ strbuf_init(&buffer, 0);
+ if (idx_map) {
+ strbuf_add(&buffer, idx_map + fanout[0], fanout[0x100] - fanout[0]);
+ } else {
+ /* not an update */
+ memset(&idx_head, 0, sizeof(struct rc_index_header));
+ idx_caches = 0;
+
+ strcpy(idx_head.signature, "REVINDEX");
+ idx_head.version = SUPPORTED_REVINDEX_VERSION;
+ idx_head.ofs_objects = sizeof(struct rc_index_header) + 0x100 * sizeof(uint32_t);
+ }
+
+ /* are we remaking a slice? */
+ for (i = 0; i < idx_head.cache_nr; i++)
+ if (!hashcmp(idx_caches + i * 20, cache_sha1))
+ break;
+
+ if (i == idx_head.cache_nr) {
+ cache_index = idx_head.cache_nr++;
+ idx_head.ofs_objects += 20;
+
+ idx_caches = xrealloc(idx_caches, idx_head.cache_nr * 20);
+ hashcpy(idx_caches + cache_index * 20, cache_sha1);
+ } else
+ cache_index = i;
+
+ i = sizeof(struct rc_slice_header); /* offset */
+ max_date = idx_head.max_date;
+ while (i < size) {
+ struct rc_index_entry index_entry, *entry;
+ struct rc_index_entry_ondisk *disked_entry;
+ struct rc_object_entry *object_entry = RC_OBTAIN_OBJECT_ENTRY(map + i);
+ unsigned long date;
+ int off, pos = i;
+
+ i += RC_ACTUAL_OBJECT_ENTRY_SIZE(object_entry);
+
+ if (object_entry->type != OBJ_COMMIT)
+ continue;
+
+ /* don't include ends; otherwise we'll find ourselves in loops */
+ if (object_entry->is_end)
+ continue;
+
+ /* handle index duplication
+ * -> keep old copy unless new one is a start -- based on expected usage, older ones will be more
+ * likely to lead to greater slice traversals than new ones */
+ date = object_entry->date;
+ if (date > idx_head.max_date) {
+ disked_entry = 0;
+ if (date > max_date)
+ max_date = date;
+ } else
+ disked_entry = search_index_1(object_entry->sha1);
+
+ if (disked_entry && !object_entry->is_start)
+ continue;
+ else if (disked_entry) {
+ /* mmm, pointer arithmetic... tasty */ /* (entry - idx_map = offset, so cast is valid) */
+ off = (unsigned int)((unsigned char *)disked_entry - idx_map) - fanout[0];
+ disked_entry = (struct rc_index_entry_ondisk *)(buffer.buf + off);
+ entry = from_disked_rc_index_entry(disked_entry, 0);
+ } else
+ entry = &index_entry;
+
+ memset(entry, 0, sizeof(index_entry));
+ entry->sha1 = object_entry->sha1;
+ entry->is_start = object_entry->is_start;
+ entry->cache_index = cache_index;
+ entry->pos = pos;
+
+ if (entry == &index_entry) {
+ strbuf_add(&buffer, to_disked_rc_index_entry(entry, 0), sizeof(struct rc_index_entry_ondisk));
+ idx_head.object_nr++;
+ } else
+ to_disked_rc_index_entry(entry, disked_entry);
+
+ }
+
+ idx_head.max_date = max_date;
+ qsort(buffer.buf, buffer.len / sizeof(struct rc_index_entry_ondisk), sizeof(struct rc_index_entry_ondisk), index_sort_hash);
+
+ /* generate fanout */
+ cur = 0x00;
+ for (i = 0; i < buffer.len; i += sizeof(struct rc_index_entry_ondisk)) {
+ struct rc_index_entry_ondisk *entry = (struct rc_index_entry_ondisk *)(buffer.buf + i);
+
+ while (cur <= entry->sha1[0])
+ fanout[cur++] = i + idx_head.ofs_objects;
+ }
+
+ while (cur <= 0xff)
+ fanout[cur++] = idx_head.ofs_objects + buffer.len;
+
+ /* BOOM! */
+ if (write_cache_index(&buffer))
+ return -1;
+
+ munmap(map, size);
+ strbuf_release(&buffer);
+
+ /* idx_map is unloaded without cleanup_cache_slices(), so regardless of previous index existence
+ * we can still free this up */
+ free(idx_caches);
+
+ return 0;
+}
+
+
+/* add start-commits from each cache slice (uninterestingness will be propogated) */
+void starts_from_slices(struct rev_info *revs, unsigned int flags)
+{
+ struct commit *commit;
+ int i;
+
+ if (!idx_map)
+ init_index();
+ if (!idx_map)
+ return;
+
+ for (i = idx_head.ofs_objects; i < idx_size; i += sizeof(struct rc_index_entry_ondisk)) {
+ struct rc_index_entry *entry = RC_OBTAIN_INDEX_ENTRY(idx_map + i);
+
+ if (!entry->is_start)
+ continue;
+
+ commit = lookup_commit(entry->sha1);
+ if (!commit)
+ continue;
+
+ commit->object.flags |= flags;
+ add_pending_object(revs, &commit->object, 0);
+ }
+
+}
diff --git a/rev-cache.h b/rev-cache.h
new file mode 100644
index 0000000..a471fbf
--- /dev/null
+++ b/rev-cache.h
@@ -0,0 +1,107 @@
+#ifndef REV_CACHE_H
+#define REV_CACHE_H
+
+#define SUPPORTED_REVCACHE_VERSION 1
+#define SUPPORTED_REVINDEX_VERSION 1
+
+#define RC_PATH_SIZE(x) (sizeof(uint16_t) * (x))
+
+#define RC_OBTAIN_OBJECT_ENTRY(p) from_disked_rc_object_entry((struct rc_object_entry_ondisk *)(p), 0)
+#define RC_OBTAIN_INDEX_ENTRY(p) from_disked_rc_index_entry((struct rc_index_entry_ondisk *)(p), 0)
+
+#define RC_ACTUAL_OBJECT_ENTRY_SIZE(e) (sizeof(struct rc_object_entry_ondisk) + RC_PATH_SIZE((e)->merge_nr + (e)->split_nr) + (e)->size_size)
+
+/* single index maps objects to cache files */
+struct rc_index_header {
+ char signature[8]; /* REVINDEX */
+ unsigned char version;
+ uint32_t ofs_objects;
+
+ uint32_t object_nr;
+ unsigned char cache_nr;
+
+ uint32_t max_date;
+};
+
+struct rc_index_entry_ondisk {
+ unsigned char sha1[20];
+ unsigned char flags;
+ uint32_t pos;
+};
+
+struct rc_index_entry {
+ unsigned char *sha1;
+ unsigned is_start : 1;
+ unsigned cache_index : 7;
+ uint32_t pos;
+};
+
+
+/* structure for actual cache file */
+struct rc_slice_header {
+ char signature[8]; /* REVCACHE */
+ unsigned char version;
+ uint32_t ofs_objects;
+
+ uint32_t object_nr;
+ uint16_t path_nr;
+ uint32_t size;
+
+ unsigned char sha1[20];
+};
+
+struct rc_object_entry_ondisk {
+ unsigned char flags;
+ unsigned char sha1[20];
+
+ unsigned char merge_nr;
+ unsigned char split_nr;
+ unsigned char sizes;
+
+ uint32_t date;
+ uint16_t path;
+};
+
+struct rc_object_entry {
+ unsigned type : 3;
+ unsigned is_end : 1;
+ unsigned is_start : 1;
+ unsigned uninteresting : 1;
+ unsigned include : 1;
+ unsigned flag : 1; /* unused */
+ unsigned char *sha1; /* 20 byte */
+
+ unsigned char merge_nr; /* : 7 */
+ unsigned char split_nr; /* : 7 */
+ unsigned size_size : 3;
+ unsigned padding : 5;
+
+ uint32_t date;
+ uint16_t path;
+
+ /* merge paths */
+ /* split paths */
+ /* size */
+};
+
+struct rc_index_entry *from_disked_rc_index_entry(struct rc_index_entry_ondisk *src, struct rc_index_entry *dst);
+struct rc_index_entry_ondisk *to_disked_rc_index_entry(struct rc_index_entry *src, struct rc_index_entry_ondisk *dst);
+struct rc_object_entry *from_disked_rc_object_entry(struct rc_object_entry_ondisk *src, struct rc_object_entry *dst);
+struct rc_object_entry_ondisk *to_disked_rc_object_entry(struct rc_object_entry *src, struct rc_object_entry_ondisk *dst);
+
+extern unsigned char *get_cache_slice(struct commit *commit);
+extern int traverse_cache_slice(struct rev_info *revs,
+ unsigned char *cache_sha1, struct commit *commit,
+ unsigned long *date_so_far, int *slop_so_far,
+ struct commit_list ***queue, struct commit_list **work);
+
+extern void init_rev_cache_info(struct rev_cache_info *rci);
+extern int make_cache_slice(struct rev_cache_info *rci,
+ struct rev_info *revs, struct commit_list **starts, struct commit_list **ends,
+ unsigned char *cache_sha1);
+extern int make_cache_index(struct rev_cache_info *rci, unsigned char *cache_sha1,
+ int fd, unsigned int size);
+
+extern void starts_from_slices(struct rev_info *revs, unsigned int flags);
+
+#endif
diff --git a/revision.c b/revision.c
index 9f5dac5..485bf72 100644
--- a/revision.c
+++ b/revision.c
@@ -432,7 +432,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
commit->object.flags |= TREESAME;
}
-static void insert_by_date_cached(struct commit *p, struct commit_list **head,
+void insert_by_date_cached(struct commit *p, struct commit_list **head,
struct commit_list *cached_base, struct commit_list **cache)
{
struct commit_list *new_entry;
diff --git a/revision.h b/revision.h
index fb74492..e767a43 100644
--- a/revision.h
+++ b/revision.h
@@ -13,11 +13,25 @@
#define CHILD_SHOWN (1u<<6)
#define ADDED (1u<<7) /* Parents already parsed and added? */
#define SYMMETRIC_LEFT (1u<<8)
-#define ALL_REV_FLAGS ((1u<<9)-1)
+#define FACE_VALUE (1u<<9)
+#define ALL_REV_FLAGS ((1u<<10)-1)
struct rev_info;
struct log_info;
+struct rev_cache_info {
+ /* generation flags */
+ unsigned objects : 1,
+ legs : 1,
+ make_index : 1;
+
+ /* traversal flags */
+ unsigned add_to_pending : 1;
+
+ /* fuse options */
+ unsigned int ignore_size;
+};
+
struct rev_info {
/* Starting list */
struct commit_list *commits;
@@ -73,6 +87,10 @@ struct rev_info {
dense_combined_merges:1,
always_show_header:1;
+ /* rev-cache flags */
+ unsigned int for_pack:1,
+ dont_cache_me:1;
+
/* Format info */
unsigned int shown_one:1,
show_merge:1,
@@ -116,6 +134,9 @@ struct rev_info {
struct reflog_walk_info *reflog_info;
struct decoration children;
struct decoration merge_simplification;
+
+ /* caching info, used ONLY by traverse_cache_slice */
+ struct rev_cache_info rev_cache_info;
};
#define REV_TREE_SAME 0
@@ -167,4 +188,7 @@ enum commit_action {
extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
+extern void insert_by_date_cached(struct commit *p, struct commit_list **head,
+ struct commit_list *cached_base, struct commit_list **cache);
+
#endif
diff --git a/t/t6015-rev-cache-list.sh b/t/t6015-rev-cache-list.sh
new file mode 100755
index 0000000..e7474fd
--- /dev/null
+++ b/t/t6015-rev-cache-list.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+test_description='git rev-cache tests'
+. ./test-lib.sh
+
+test_cmp_sorted() {
+ grep -io "[a-f0-9]*" $1 | sort >.tmpfile1 &&
+ grep -io "[a-f0-9]*" $2 | sort >.tmpfile2 &&
+ test_cmp .tmpfile1 .tmpfile2
+}
+
+# we want a totally wacked out branch structure...
+# we need branching and merging of sizes up through 3, tree
+# addition/deletion, and enough branching to exercise path
+# reuse
+test_expect_success 'init repo' '
+ echo bla >file &&
+ git add . &&
+ git commit -m "bla" &&
+
+ git branch b1 &&
+ git checkout b1 &&
+ echo blu >file2 &&
+ mkdir d1 &&
+ echo bang >d1/filed1 &&
+ git add . &&
+ git commit -m "blu" &&
+
+ git checkout master &&
+ git branch b2 &&
+ git checkout b2 &&
+ echo kaplaa >>file &&
+ git commit -a -m "kaplaa" &&
+
+ git checkout master &&
+ mkdir smoke &&
+ echo omg >smoke/bong &&
+ git add . &&
+ git commit -m "omg" &&
+
+ git branch b4 &&
+ git checkout b4 &&
+ echo shazam >file8 &&
+ git add . &&
+ git commit -m "shazam" &&
+ git merge -m "merge b2" b2 &&
+
+ echo bam >smoke/pipe &&
+ git add .
+ git commit -m "bam" &&
+
+ git checkout master &&
+ echo pow >file7 &&
+ git add . &&
+ git commit -m "pow" &&
+ git merge -m "merge b4" b4 &&
+
+ git checkout b1 &&
+ echo stuff >d1/filed1 &&
+ git commit -a -m "stuff" &&
+
+ git branch b11 &&
+ git checkout b11 &&
+ echo wazzup >file3 &&
+ git add file3 &&
+ git commit -m "wazzup" &&
+
+ git checkout b1 &&
+ mkdir d1/d2 &&
+ echo lol >d1/d2/filed2 &&
+ git add . &&
+ git commit -m "lol" &&
+
+ git checkout master &&
+ git merge -m "triple merge" b1 b11 &&
+ git rm -r d1 &&
+ git commit -a -m "oh noes"
+'
+
+git-rev-list HEAD --not HEAD~3 >proper_commit_list_limited
+git-rev-list HEAD >proper_commit_list
+
+test_expect_success 'make cache slice' '
+ git-rev-cache add HEAD 2>output.err &&
+ grep "final return value: 0" output.err
+'
+
+test_expect_success 'remake cache slice' '
+ git-rev-cache add HEAD 2>output.err &&
+ grep "final return value: 0" output.err
+'
+
+#check core mechanics and rev-list hook for commits
+test_expect_success 'test rev-caches walker directly (limited)' '
+ git-rev-cache walk HEAD --not HEAD~3 >list &&
+ test_cmp_sorted list proper_commit_list_limited
+'
+
+test_expect_success 'test rev-caches walker directly (unlimited)' '
+ git-rev-cache walk HEAD >list &&
+ test_cmp_sorted list proper_commit_list
+'
+
+test_done
--
tg: (64d5fe0..) t/revcache/basic (depends on: master)
^ permalink raw reply related
* [PATCH 4/6 (v4)] administrative functions for rev-cache, start of integration into git
From: Nick Edelen @ 2009-08-17 12:31 UTC (permalink / raw)
To: Junio C Hamano, Nicolas Pitre, Johannes Schindelin, Sam Vilain,
Michael J Gruber
From: Nick Edelen <sirnot@gmail.com>
Subject: [PATCH] t/revcache/misc
This patch, fourth, contains miscellaneous (maintenance) features:
- support for cache slice fusion, index regeneration and object size caching
- non-commit object generation refactored to take advantage of 'size' field
- porcelain updated to support feature additions
The beginnings of integration into git are present in this patch, mainly
centered on caching object size; the object generation is refactored to more
elegantly exploit this. Fusion allows smaller (incremental) slices to be
coagulated into a larger slice, reducing overhead, while index regeneration
enables repair or cleaning of the cache index.
Note that tests for these features are included in the following patch, as they
take advantage of the rev-cache's integration into the revision walker.
Signed-off-by: Nick Edelen <sirnot@gmail.com>
---
builtin-gc.c | 9 +
builtin-rev-cache.c | 77 ++++++-
rev-cache.c | 717 +++++++++++++++++++++++++++++++++++++++++++++------
rev-cache.h | 9 +-
revision.h | 16 +-
5 files changed, 749 insertions(+), 79 deletions(-)
diff --git a/builtin-gc.c b/builtin-gc.c
index 7d3e9cc..c92511a 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -22,6 +22,7 @@ static const char * const builtin_gc_usage[] = {
NULL
};
+static char do_rev_cache = 0;
static int pack_refs = 1;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
@@ -34,9 +35,14 @@ static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
static const char *argv_rerere[] = {"rerere", "gc", NULL};
+static const char *argv_rev_cache[] = {"rev-cache", "fuse", "--all", "--ignore-size", NULL};
static int gc_config(const char *var, const char *value, void *cb)
{
+ if (!strcmp(var, "gc.revcache")) {
+ do_rev_cache = 1;
+ return 0;
+ }
if (!strcmp(var, "gc.packrefs")) {
if (value && !strcmp(value, "notbare"))
pack_refs = -1;
@@ -244,6 +250,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
return error(FAILED_RUN, argv_rerere[0]);
+ if (do_rev_cache && run_command_v_opt(argv_rev_cache, RUN_GIT_CMD))
+ return error(FAILED_RUN, argv_rev_cache[0]);
+
if (auto_gc && too_many_loose_objects())
warning("There are too many unreachable loose objects; "
"run 'git prune' to remove them.");
diff --git a/builtin-rev-cache.c b/builtin-rev-cache.c
index c27e0f3..7ca39c4 100644
--- a/builtin-rev-cache.c
+++ b/builtin-rev-cache.c
@@ -5,6 +5,8 @@
#include "revision.h"
#include "rev-cache.h"
+unsigned long default_ignore_size = 50 * 1024 * 1024; /* 50mb */
+
/* porcelain for rev-cache.c */
static int handle_add(int argc, const char *argv[]) /* args beyond this command */
{
@@ -24,7 +26,7 @@ static int handle_add(int argc, const char *argv[]) /* args beyond this command
if (!strcmp(argv[i], "--stdin"))
dostdin = 1;
else if (!strcmp(argv[i], "--fresh"))
- starts_from_slices(&revs, UNINTERESTING);
+ starts_from_slices(&revs, UNINTERESTING, 0, 0);
else if (!strcmp(argv[i], "--not"))
flags ^= UNINTERESTING;
else if (!strcmp(argv[i], "--legs"))
@@ -150,6 +152,57 @@ static int handle_walk(int argc, const char *argv[])
return 0;
}
+static int handle_fuse(int argc, const char *argv[])
+{
+ struct rev_info revs;
+ struct rev_cache_info rci;
+ const char *args[5];
+ int i, argn = 0;
+ char add_all = 0;
+
+ init_revisions(&revs, 0);
+ init_rev_cache_info(&rci);
+ args[argn++] = "rev-list";
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--all")) {
+ args[argn++] = "--all";
+ setup_revisions(argn, args, &revs, 0);
+ add_all = 1;
+ } else if (!strcmp(argv[i], "--no-objects"))
+ rci.objects = 0;
+ else if (!strncmp(argv[i], "--ignore-size", 13)) {
+ unsigned long sz;
+
+ if (argv[i][13] == '=')
+ git_parse_ulong(argv[i] + 14, &sz);
+ else
+ sz = default_ignore_size;
+
+ rci.ignore_size = sz;
+ } else
+ continue;
+ }
+
+ if (!add_all)
+ starts_from_slices(&revs, 0, 0, 0);
+
+ return fuse_cache_slices(&rci, &revs);
+}
+
+static int handle_index(int argc, const char *argv[])
+{
+ return regenerate_cache_index(0);
+}
+
+static int handle_alt(int argc, const char *argv[])
+{
+ if (argc < 1)
+ return -1;
+
+ return make_cache_slice_pointer(0, argv[0]);
+}
+
static int handle_help(void)
{
char *usage = "\
@@ -179,12 +232,28 @@ commands:\n\
return 0;
}
+static int rev_cache_config(const char *k, const char *v, void *cb)
+{
+ /* this could potentially be related to pack.windowmemory, but we want a max around 50mb,
+ * and .windowmemory is often >700mb, with *large* variations */
+ if (!strcmp(k, "revcache.ignoresize")) {
+ int t;
+
+ t = git_config_ulong(k, v);
+ if (t)
+ default_ignore_size = t;
+ }
+
+ return 0;
+}
+
int cmd_rev_cache(int argc, const char *argv[], const char *prefix)
{
const char *arg;
int r;
git_config(git_default_config, NULL);
+ git_config(rev_cache_config, NULL);
if (argc > 1)
arg = argv[1];
@@ -195,8 +264,14 @@ int cmd_rev_cache(int argc, const char *argv[], const char *prefix)
argv += 2;
if (!strcmp(arg, "add"))
r = handle_add(argc, argv);
+ else if (!strcmp(arg, "fuse"))
+ r = handle_fuse(argc, argv);
else if (!strcmp(arg, "walk"))
r = handle_walk(argc, argv);
+ else if (!strcmp(arg, "index"))
+ r = handle_index(argc, argv);
+ else if (!strcmp(arg, "alt"))
+ r = handle_alt(argc, argv);
else
return handle_help();
diff --git a/rev-cache.c b/rev-cache.c
index ddcc596..8eac1f0 100644
--- a/rev-cache.c
+++ b/rev-cache.c
@@ -9,6 +9,13 @@
#include "revision.h"
#include "rev-cache.h"
#include "run-command.h"
+#include "string-list.h"
+
+struct cache_slice_pointer {
+ char signature[8]; /* REVCOPTR */
+ char version;
+ char path[PATH_MAX + 1];
+};
/* list resembles pack index format */
static uint32_t fanout[0xff + 2];
@@ -257,27 +264,45 @@ unsigned char *get_cache_slice(struct commit *commit)
/* traversal */
-static void handle_noncommit(struct rev_info *revs, unsigned char *ptr, struct rc_object_entry *entry)
+static unsigned long decode_size(unsigned char *str, int len);
+
+static void handle_noncommit(struct rev_info *revs, struct commit *commit, unsigned char *ptr, struct rc_object_entry *entry)
{
- struct object *obj = 0;
+ struct blob *blob;
+ struct tree *tree;
+ struct object *obj;
+ unsigned long size;
+ size = decode_size(ptr + RC_ENTRY_SIZE_OFFSET(entry), entry->size_size);
switch (entry->type) {
case OBJ_TREE :
- if (revs->tree_objects)
- obj = (struct object *)lookup_tree(entry->sha1);
+ if (!revs->tree_objects)
+ return;
+
+ tree = lookup_tree(entry->sha1);
+ if (!tree)
+ return;
+
+ tree->size = size;
+ commit->tree = tree;
+ obj = (struct object *)tree;
break;
+
case OBJ_BLOB :
- if (revs->blob_objects)
- obj = (struct object *)lookup_blob(entry->sha1);
- break;
- case OBJ_TAG :
- if (revs->tag_objects)
- obj = (struct object *)lookup_tag(entry->sha1);
+ if (!revs->blob_objects)
+ return;
+
+ blob = lookup_blob(entry->sha1);
+ if (!blob)
+ return;
+
+ obj = (struct object *)blob;
break;
- }
- if (!obj)
+ default :
+ /* tag objects aren't really supposed to be here */
return;
+ }
obj->flags |= FACE_VALUE;
add_pending_object(revs, obj, "");
@@ -373,7 +398,7 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
/* add extra objects if necessary */
if (entry->type != OBJ_COMMIT) {
if (consume_children)
- handle_noncommit(revs, map + index, entry);
+ handle_noncommit(revs, co, map + index, entry);
continue;
} else
@@ -407,6 +432,8 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
if (last_objects[path]) {
parse_commit(last_objects[path]);
+ /* we needn't worry about the unique field; that will be valid as
+ * long as we're not a end entry */
last_objects[path]->object.flags &= ~FACE_VALUE;
last_objects[path] = 0;
}
@@ -539,6 +566,48 @@ static int get_cache_slice_header(unsigned char *cache_sha1, unsigned char *map,
return 0;
}
+int open_cache_slice(unsigned char *sha1, int flags)
+{
+ int fd;
+ char signature[8];
+
+ fd = open(git_path("rev-cache/%s", sha1_to_hex(sha1)), flags);
+ if (fd <= 0)
+ goto end;
+
+ if (read(fd, signature, 8) != 8)
+ goto end;
+
+ /* a normal revision slice */
+ if (!memcmp(signature, "REVCACHE", 8)) {
+ lseek(fd, 0, SEEK_SET);
+ return fd;
+ }
+
+ /* slice pointer */
+ if (!memcmp(signature, "REVCOPTR", 8)) {
+ struct cache_slice_pointer ptr;
+
+ lseek(fd, 0, SEEK_SET);
+ if (read_in_full(fd, &ptr, sizeof(ptr)) != sizeof(ptr))
+ goto end;
+
+ if (ptr.version != SUPPORTED_REVCOPTR_VERSION)
+ goto end;
+
+ close(fd);
+ fd = open(ptr.path, flags);
+
+ return fd;
+ }
+
+end:
+ if (fd > 0)
+ close(fd);
+
+ return -1;
+}
+
int traverse_cache_slice(struct rev_info *revs,
unsigned char *cache_sha1, struct commit *commit,
unsigned long *date_so_far, int *slop_so_far,
@@ -562,7 +631,7 @@ int traverse_cache_slice(struct rev_info *revs,
memset(&head, 0, sizeof(struct rc_slice_header));
- fd = open(git_path("rev-cache/%s", sha1_to_hex(cache_sha1)), O_RDONLY);
+ fd = open_cache_slice(cache_sha1, O_RDONLY);
if (fd == -1)
goto end;
if (fstat(fd, &fi) || fi.st_size < sizeof(struct rc_slice_header))
@@ -589,6 +658,68 @@ end:
/* generation */
+static int is_endpoint(struct commit *commit)
+{
+ struct commit_list *list = commit->parents;
+
+ while (list) {
+ if (!(list->item->object.flags & UNINTERESTING))
+ return 0;
+
+ list = list->next;
+ }
+
+ return 1;
+}
+
+/* ensures branch is self-contained: parents are either all interesting or all uninteresting */
+static void make_legs(struct rev_info *revs)
+{
+ struct commit_list *list, **plist;
+ int total = 0;
+
+ /* attach plist to end of commits list */
+ list = revs->commits;
+ while (list && list->next)
+ list = list->next;
+
+ if (list)
+ plist = &list->next;
+ else
+ return;
+
+ /* duplicates don't matter, as get_revision() ignores them */
+ for (list = revs->commits; list; list = list->next) {
+ struct commit *item = list->item;
+ struct commit_list *parents = item->parents;
+
+ if (item->object.flags & UNINTERESTING)
+ continue;
+ if (is_endpoint(item))
+ continue;
+
+ while (parents) {
+ struct commit *p = parents->item;
+ parents = parents->next;
+
+ if (!(p->object.flags & UNINTERESTING))
+ continue;
+
+ p->object.flags &= ~UNINTERESTING;
+ parse_commit(p);
+ plist = &commit_list_insert(p, plist)->next;
+
+ if (!(p->object.flags & SEEN))
+ total++;
+ }
+ }
+
+ if (total)
+ sort_in_topological_order(&revs->commits, 1);
+
+}
+
+
struct path_track {
struct commit *commit;
int path; /* for keeping track of children */
@@ -777,31 +908,76 @@ static void handle_paths(struct commit *commit, struct rc_object_entry *object,
}
-static void add_object_entry(const unsigned char *sha1, int type, struct rc_object_entry *nothisone,
+static int encode_size(unsigned long size, unsigned char *out)
+{
+ int len = 0;
+
+ while (size) {
+ *out++ = (unsigned char)(size & 0xff);
+ size >>= 8;
+ len++;
+ }
+
+ return len;
+}
+
+static unsigned long decode_size(unsigned char *str, int len)
+{
+ unsigned long size = 0;
+ int shift = 0;
+
+ while (len--) {
+ size |= (unsigned long)*str << shift;
+ shift += 8;
+ str++;
+ }
+
+ return size;
+}
+
+static void add_object_entry(const unsigned char *sha1, struct rc_object_entry *entryp,
struct strbuf *merge_str, struct strbuf *split_str)
{
- struct rc_object_entry object;
+ struct rc_object_entry entry;
+ unsigned char size_str[7];
+ unsigned long size;
+ enum object_type type;
+ void *data;
- if (!nothisone) {
- memset(&object, 0, sizeof(object));
- object.sha1 = (unsigned char *)sha1;
- object.type = type;
+ if (entryp)
+ sha1 = entryp->sha1;
+
+ /* retrieve size data */
+ data = read_sha1_file(sha1, &type, &size);
+
+ if (data)
+ free(data);
+
+ /* initialize! */
+ if (!entryp) {
+ memset(&entry, 0, sizeof(entry));
+ entry.sha1 = (unsigned char *)sha1;
+ entry.type = type;
if (merge_str)
- object.merge_nr = merge_str->len / sizeof(uint16_t);
+ entry.merge_nr = merge_str->len / sizeof(uint16_t);
if (split_str)
- object.split_nr = split_str->len / sizeof(uint16_t);
+ entry.split_nr = split_str->len / sizeof(uint16_t);
- nothisone = &object;
+ entryp = &entry;
}
- strbuf_add(acc_buffer, to_disked_rc_object_entry(nothisone, 0), sizeof(struct rc_object_entry_ondisk));
+ entryp->size_size = encode_size(size, size_str);
- if (merge_str && merge_str->len)
+ /* write the muvabitch */
+ strbuf_add(acc_buffer, to_disked_rc_object_entry(entryp, 0), sizeof(struct rc_object_entry_ondisk));
+
+ if (merge_str)
strbuf_add(acc_buffer, merge_str->buf, merge_str->len);
- if (split_str && split_str->len)
+ if (split_str)
strbuf_add(acc_buffer, split_str->buf, split_str->len);
+ strbuf_add(acc_buffer, size_str, entryp->size_size);
}
/* returns non-zero to continue parsing, 0 to skip */
@@ -845,12 +1021,7 @@ continue_loop:
static int dump_tree_callback(const unsigned char *sha1, const char *path, unsigned int mode)
{
- unsigned char data[21];
-
- hashcpy(data, sha1);
- data[20] = !!S_ISDIR(mode);
-
- strbuf_add(acc_buffer, data, 21);
+ strbuf_add(acc_buffer, sha1, 20);
return 1;
}
@@ -860,15 +1031,10 @@ static void tree_addremove(struct diff_options *options,
const unsigned char *sha1,
const char *concatpath)
{
- unsigned char data[21];
-
if (whatnow != '+')
return;
- hashcpy(data, sha1);
- data[20] = !!S_ISDIR(mode);
-
- strbuf_add(acc_buffer, data, 21);
+ strbuf_add(acc_buffer, sha1, 20);
}
static void tree_change(struct diff_options *options,
@@ -877,26 +1043,10 @@ static void tree_change(struct diff_options *options,
const unsigned char *new_sha1,
const char *concatpath)
{
- unsigned char data[21];
-
if (!hashcmp(old_sha1, new_sha1))
return;
- hashcpy(data, new_sha1);
- data[20] = !!S_ISDIR(new_mode);
-
- strbuf_add(acc_buffer, data, 21);
-}
-
-static int sort_type_hash(const void *a, const void *b)
-{
- const unsigned char *sa = (const unsigned char *)a,
- *sb = (const unsigned char *)b;
-
- if (sa[20] == sb[20])
- return hashcmp(sa, sb);
-
- return sa[20] > sb[20] ? -1 : 1;
+ strbuf_add(acc_buffer, new_sha1, 20);
}
static int add_unique_objects(struct commit *commit)
@@ -907,6 +1057,7 @@ static int add_unique_objects(struct commit *commit)
int i, j, next;
char is_first = 1;
+ /* ...no, calculate unique objects */
strbuf_init(&os, 0);
strbuf_init(&ost, 0);
orig_buf = acc_buffer;
@@ -926,20 +1077,20 @@ static int add_unique_objects(struct commit *commit)
strbuf_setlen(acc_buffer, 0);
diff_tree_sha1(list->item->tree->object.sha1, commit->tree->object.sha1, "", &opts);
- qsort(acc_buffer->buf, acc_buffer->len / 21, 21, (int (*)(const void *, const void *))hashcmp);
+ qsort(acc_buffer->buf, acc_buffer->len / 20, 20, (int (*)(const void *, const void *))hashcmp);
/* take intersection */
if (!is_first) {
- for (next = i = j = 0; i < os.len; i += 21) {
+ for (next = i = j = 0; i < os.len; i += 20) {
while (j < ost.len && hashcmp((unsigned char *)(ost.buf + j), (unsigned char *)(os.buf + i)) < 0)
- j += 21;
+ j += 20;
if (j >= ost.len || hashcmp((unsigned char *)(ost.buf + j), (unsigned char *)(os.buf + i)))
continue;
if (next != i)
- memcpy(os.buf + next, os.buf + i, 21);
- next += 21;
+ memcpy(os.buf + next, os.buf + i, 20);
+ next += 20;
}
if (next != i)
@@ -948,25 +1099,102 @@ static int add_unique_objects(struct commit *commit)
is_first = 0;
}
+ /* no parents (!) */
if (is_first) {
acc_buffer = &os;
dump_tree(commit->tree, dump_tree_callback);
}
- if (os.len)
- qsort(os.buf, os.len / 21, 21, sort_type_hash);
-
+ /* the ordering of non-commit objects dosn't really matter, so we're not gonna bother */
acc_buffer = orig_buf;
- for (i = 0; i < os.len; i += 21)
- add_object_entry((unsigned char *)(os.buf + i), os.buf[i + 20] ? OBJ_TREE : OBJ_BLOB, 0, 0, 0);
+ for (i = 0; i < os.len; i += 20)
+ add_object_entry((unsigned char *)(os.buf + i), 0, 0, 0);
/* last but not least, the main tree */
- add_object_entry(commit->tree->object.sha1, OBJ_TREE, 0, 0, 0);
+ add_object_entry(commit->tree->object.sha1, 0, 0, 0);
+
+ return i / 20 + 1;
+}
+
+static int add_objects_verbatim_1(struct rev_cache_slice_map *mapping, int *index)
+{
+ unsigned char *map = mapping->map;
+ int i = *index, object_nr = 0;
+ struct rc_object_entry *entry = RC_OBTAIN_OBJECT_ENTRY(map + i);
+
+ i += RC_ACTUAL_OBJECT_ENTRY_SIZE(entry);
+ while (i < mapping->size) {
+ int pos = i;
- strbuf_release(&ost);
- strbuf_release(&os);
+ entry = RC_OBTAIN_OBJECT_ENTRY(map + i;
+ i += RC_ACTUAL_OBJECT_ENTRY_SIZE(entry);
+
+ if (entry->type == OBJ_COMMIT) {
+ *index = pos;
+ return object_nr;
+ }
- return i / 21 + 1;
+ strbuf_add(acc_buffer, map + pos, i - pos);
+ object_nr++;
+ }
+
+ *index = 0;
+ return object_nr;
+}
+
+static int add_objects_verbatim(struct rev_cache_info *rci, struct commit *commit)
+{
+ struct rev_cache_slice_map *map;
+ char found = 0;
+ struct rc_index_entry *ie;
+ struct rc_object_entry *entry;
+ int object_nr, i;
+
+ if (!rci->maps)
+ return -1;
+
+ /* check if we can continue where we left off */
+ map = rci->last_map;
+ if (!map)
+ goto search_me;
+
+ i = map->last_index;
+ entry = RC_OBTAIN_OBJECT_ENTRY(map->map + i);
+ if (hashcmp(entry->sha1, commit->object.sha1))
+ goto search_me;
+
+ found = 1;
+
+search_me:
+ if (!found) {
+ ie = search_index(commit->object.sha1);
+ if (!ie || ie->cache_index >= idx_head.cache_nr)
+ return -2;
+
+ map = rci->maps + ie->cache_index;
+ if (!map->size)
+ return -3;
+
+ i = ie->pos;
+ entry = RC_OBTAIN_OBJECT_ENTRY(map->map + i);
+ if (entry->type != OBJ_COMMIT || hashcmp(entry->sha1, commit->object.sha1))
+ return -4;
+ }
+
+ /* can't handle end commits */
+ if (entry->is_end)
+ return -5;
+
+ object_nr = add_objects_verbatim_1(map, &i);
+
+ /* remember this */
+ if (i) {
+ rci->last_map = map;
+ map->last_index = i;
+ } else
+ rci->last_map = 0;
+
+ return object_nr;
}
static void init_revcache_directory(void)
@@ -981,9 +1209,14 @@ static void init_revcache_directory(void)
void init_rev_cache_info(struct rev_cache_info *rci)
{
+ memset(rci, 0, sizeof(struct rev_cache_info));
+
rci->objects = 1;
rci->legs = 0;
rci->make_index = 1;
+ rci->fuse_me = 0;
+
+ rci->overwrite_all = 0;
rci->add_to_pending = 1;
@@ -1063,9 +1296,13 @@ int make_cache_slice(struct rev_cache_info *rci,
if (prepare_revision_walk(revs))
die("died preparing revision walk");
+ if (rci->legs)
+ make_legs(revs);
+
object_nr = total_sz = 0;
while ((commit = get_revision(revs)) != 0) {
struct rc_object_entry object;
+ int t;
strbuf_setlen(&merge_paths, 0);
strbuf_setlen(&split_paths, 0);
@@ -1091,12 +1328,17 @@ int make_cache_slice(struct rev_cache_info *rci,
commit->indegree = 0;
- add_object_entry(0, 0, &object, &merge_paths, &split_paths);
+ add_object_entry(0, &object, &merge_paths, &split_paths);
object_nr++;
- /* add all unique children for this commit */
- if (rci->objects && !object.is_end)
- object_nr += add_unique_objects(commit);
+ if (rci->objects && !object.is_end) {
+ if (rci->fuse_me && (t = add_objects_verbatim(rci, commit)) >= 0)
+ /* yay! we did it! */
+ object_nr += t;
+ else
+ /* add all unique children for this commit */
+ object_nr += add_unique_objects(commit);
+ }
/* print every ~1MB or so */
if (buffer.len > 1000000) {
@@ -1221,6 +1463,8 @@ int make_cache_index(struct rev_cache_info *rci, unsigned char *cache_sha1,
unsigned char *map;
unsigned long max_date;
+ maybe_fill_with_defaults(rci);
+
if (!idx_map)
init_index();
@@ -1285,7 +1529,7 @@ int make_cache_index(struct rev_cache_info *rci, unsigned char *cache_sha1,
} else
disked_entry = search_index_1(object_entry->sha1);
- if (disked_entry && !object_entry->is_start)
+ if (disked_entry && !object_entry->is_start && !rci->overwrite_all)
continue;
else if (disked_entry) {
/* mmm, pointer arithmetic... tasty */ /* (entry - idx_map = offset, so cast is valid) */
@@ -1339,8 +1583,7 @@ int make_cache_index(struct rev_cache_info *rci, unsigned char *cache_sha1,
}
-/* add start-commits from each cache slice (uninterestingness will be propogated) */
-void starts_from_slices(struct rev_info *revs, unsigned int flags)
+void starts_from_slices(struct rev_info *revs, unsigned int flags, unsigned char *which, int n)
{
struct commit *commit;
int i;
@@ -1356,6 +1599,18 @@ void starts_from_slices(struct rev_info *revs, unsigned int flags)
if (!entry->is_start)
continue;
+ /* only include entries in 'which' slices */
+ if (n) {
+ int j;
+
+ for (j = 0; j < n; j++)
+ if (!hashcmp(idx_caches + entry->cache_index * 20, which + j * 20))
+ break;
+
+ if (j == n)
+ continue;
+ }
+
commit = lookup_commit(entry->sha1);
if (!commit)
continue;
@@ -1365,3 +1620,313 @@ void starts_from_slices(struct rev_info *revs, unsigned int flags)
}
}
+
+
+struct slice_fd_time {
+ unsigned char sha1[20];
+ int fd;
+ struct stat fi;
+};
+
+int slice_time_sort(const void *a, const void *b)
+{
+ unsigned long at, bt;
+
+ at = ((struct slice_fd_time *)a)->fi.st_ctime;
+ bt = ((struct slice_fd_time *)b)->fi.st_ctime;
+
+ if (at == bt)
+ return 0;
+
+ return at > bt ? 1 : -1;
+}
+
+int regenerate_cache_index(struct rev_cache_info *rci)
+{
+ DIR *dirh;
+ int i;
+ struct slice_fd_time info;
+ struct strbuf slices;
+
+ /* first remove old index if it exists */
+ unlink_or_warn(git_path("rev-cache/index"));
+
+ strbuf_init(&slices, 0);
+
+ dirh = opendir(git_path("rev-cache"));
+ if (dirh) {
+ struct dirent *de;
+ struct stat fi;
+ int fd;
+ unsigned char sha1[20];
+
+ while ((de = readdir(dirh))) {
+ if (de->d_name[0] == '.')
+ continue;
+
+ if (get_sha1_hex(de->d_name, sha1))
+ continue;
+
+ /* open with RDWR because of mmap call in make_cache_index() */
+ fd = open_cache_slice(sha1, O_RDONLY);
+ if (fd < 0 || fstat(fd, &fi)) {
+ warning("bad cache found [%s]; fuse recommended", de->d_name);
+ if (fd > 0)
+ close(fd);
+ continue;
+ }
+
+ hashcpy(info.sha1, sha1);
+ info.fd = fd;
+ memcpy(&info.fi, &fi, sizeof(struct stat));
+
+ strbuf_add(&slices, &info, sizeof(info));
+ }
+
+ closedir(dirh);
+ }
+
+ /* we want oldest first -> upon overlap, older slices are more likely to have a larger section,
+ * as of the overlapped commit */
+ qsort(slices.buf, slices.len / sizeof(info), sizeof(info), slice_time_sort);
+
+ for (i = 0; i < slices.len; i += sizeof(info)) {
+ struct slice_fd_time *infop = (struct slice_fd_time *)(slices.buf + i);
+ struct stat *fip = &infop->fi;
+ int fd = infop->fd;
+
+ if (make_cache_index(rci, infop->sha1, fd, fip->st_size) < 0)
+ die("error writing cache");
+
+ close(fd);
+ }
+
+ strbuf_release(&slices);
+
+ return 0;
+}
+
+static int add_slices_for_fuse(struct rev_cache_info *rci, struct string_list *files, struct strbuf *ignore)
+{
+ unsigned char sha1[20];
+ char base[PATH_MAX];
+ int baselen, i, slice_nr = 0;
+ struct stat fi;
+ DIR *dirh;
+ struct dirent *de;
+
+ strncpy(base, git_path("rev-cache"), sizeof(base));
+ baselen = strlen(base);
+
+ dirh = opendir(base);
+ if (!dirh)
+ return 0;
+
+ while ((de = readdir(dirh))) {
+ if (de->d_name[0] == '.')
+ continue;
+
+ base[baselen] = '/';
+ strncpy(base + baselen + 1, de->d_name, sizeof(base) - baselen - 1);
+
+ if (get_sha1_hex(de->d_name, sha1)) {
+ /* whatever it is, we don't need it... */
+ string_list_insert(base, files);
+ continue;
+ }
+
+ /* _theoretically_ it is possible a slice < ignore_size to map objects not covered by, yet reachable from,
+ * a slice >= ignore_size, meaning that we could potentially delete an 'unfused' slice; but if that
+ * ever *did* happen their cache structure'd be so fucked up they might as well refuse the entire thing.
+ * and at any rate the worst it'd do is make rev-list revert to standard walking in that (small) bit.
+ */
+ if (rci->ignore_size) {
+ if (stat(base, &fi))
+ warning("can't query file %s\n", base);
+ else if (fi.st_size >= rci->ignore_size) {
+ strbuf_add(ignore, sha1, 20);
+ continue;
+ }
+ } else {
+ /* check if a pointer */
+ struct cache_slice_pointer ptr;
+ int fd = open(base, O_RDONLY);
+
+ if (fd < 0)
+ goto dont_save;
+ if (sizeof(ptr) != read_in_full(fd, &ptr, sizeof(ptr)))
+ goto dont_save;
+
+ close(fd);
+ if (!strcmp(ptr.signature, "REVCOPTR")) {
+ strbuf_add(ignore, sha1, 20);
+ continue;
+ }
+ }
+
+dont_save:
+ for (i = idx_head.cache_nr - 1; i >= 0; i--) {
+ if (!hashcmp(idx_caches + i * 20, sha1))
+ break;
+ }
+
+ if (i >= 0)
+ rci->maps[i].size = 1;
+
+ string_list_insert(base, files);
+ slice_nr++;
+ }
+
+ closedir(dirh);
+
+ return slice_nr;
+}
+
+/* the most work-intensive attributes in the cache are the unique objects and size, both
+ * of which can be re-used. although path structures will be isomorphic, path generation is
+ * not particularly expensive, and at any rate we need to re-sort the commits */
+int fuse_cache_slices(struct rev_cache_info *rci, struct rev_info *revs)
+{
+ unsigned char cache_sha1[20];
+ struct string_list files = {0, 0, 0, 1}; /* dup */
+ struct strbuf ignore;
+ int i;
+
+ maybe_fill_with_defaults(rci);
+
+ if (!idx_map)
+ init_index();
+ if (!idx_map)
+ return -1;
+
+ strbuf_init(&ignore, 0);
+ rci->maps = xcalloc(idx_head.cache_nr, sizeof(struct rev_cache_slice_map));
+ if (add_slices_for_fuse(rci, &files, &ignore) <= 1) {
+ printf("nothing to fuse\n");
+ return 1;
+ }
+
+ if (ignore.len) {
+ starts_from_slices(revs, UNINTERESTING, (unsigned char *)ignore.buf, ignore.len / 20);
+ strbuf_release(&ignore);
+ }
+
+ /* initialize mappings */
+ for (i = idx_head.cache_nr - 1; i >= 0; i--) {
+ struct rev_cache_slice_map *map = rci->maps + i;
+ struct stat fi;
+ int fd;
+
+ if (!map->size)
+ continue;
+ map->size = 0;
+
+ /* pointers are never fused, so we can use open directly */
+ fd = open(git_path("rev-cache/%s", sha1_to_hex(idx_caches + i * 20)), O_RDONLY);
+ if (fd <= 0 || fstat(fd, &fi))
+ continue;
+ if (fi.st_size < sizeof(struct rc_slice_header))
+ continue;
+
+ map->map = xmmap(0, fi.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map->map == MAP_FAILED)
+ continue;
+
+ close(fd);
+ map->size = fi.st_size;
+ }
+
+ rci->make_index = 0;
+ rci->fuse_me = 1;
+ if (make_cache_slice(rci, revs, 0, 0, cache_sha1) < 0)
+ die("can't make cache slice");
+
+ printf("%s\n", sha1_to_hex(cache_sha1));
+
+ /* clean up time! */
+ for (i = idx_head.cache_nr - 1; i >= 0; i--) {
+ struct rev_cache_slice_map *map = rci->maps + i;
+
+ if (!map->size)
+ continue;
+
+ munmap(map->map, map->size);
+ }
+ free(rci->maps);
+ cleanup_cache_slices();
+
+ for (i = 0; i < files.nr; i++) {
+ char *name = files.items[i].string;
+
+ fprintf(stderr, "removing %s\n", name);
+ unlink_or_warn(name);
+ }
+
+ string_list_clear(&files, 0);
+
+ return regenerate_cache_index(rci);
+}
+
+static int verify_cache_slice(const char *slice_path, unsigned char *sha1)
+{
+ struct rc_slice_header head;
+ int fd, len, retval = -1;
+ unsigned char *map = MAP_FAILED;
+ struct stat fi;
+
+ len = strlen(slice_path);
+ if (len < 40)
+ return -2;
+ if (get_sha1_hex(slice_path + len - 40, sha1))
+ return -3;
+
+ fd = open(slice_path, O_RDONLY);
+ if (fd == -1)
+ goto end;
+ if (fstat(fd, &fi) || fi.st_size < sizeof(head))
+ goto end;
+
+ map = xmmap(0, sizeof(head), PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED)
+ goto end;
+ if (get_cache_slice_header(sha1, map, fi.st_size, &head))
+ goto end;
+
+ retval = 0;
+
+end:
+ if (map != MAP_FAILED)
+ munmap(map, sizeof(head));
+ if (fd > 0)
+ close(fd);
+
+ return retval;
+}
+
+int make_cache_slice_pointer(struct rev_cache_info *rci, const char *slice_path)
+{
+ struct cache_slice_pointer ptr;
+ int fd;
+ unsigned char sha1[20];
+
+ maybe_fill_with_defaults(rci);
+ rci->overwrite_all = 1;
+
+ if (verify_cache_slice(slice_path, sha1) < 0)
+ return -1;
+
+ strcpy(ptr.signature, "REVCOPTR");
+ ptr.version = SUPPORTED_REVCOPTR_VERSION;
+ strcpy(ptr.path, make_nonrelative_path(slice_path));
+
+ fd = open(git_path("rev-cache/%s", sha1_to_hex(sha1)), O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ return -2;
+
+ write_in_full(fd, &ptr, sizeof(ptr));
+ make_cache_index(rci, sha1, fd, sizeof(ptr));
+
+ close(fd);
+
+ return 0;
+}
diff --git a/rev-cache.h b/rev-cache.h
index a471fbf..14437d8 100644
--- a/rev-cache.h
+++ b/rev-cache.h
@@ -3,6 +3,7 @@
#define SUPPORTED_REVCACHE_VERSION 1
#define SUPPORTED_REVINDEX_VERSION 1
+#define SUPPORTED_REVCOPTR_VERSION 1
#define RC_PATH_SIZE(x) (sizeof(uint16_t) * (x))
@@ -10,6 +11,7 @@
#define RC_OBTAIN_INDEX_ENTRY(p) from_disked_rc_index_entry((struct rc_index_entry_ondisk *)(p), 0)
#define RC_ACTUAL_OBJECT_ENTRY_SIZE(e) (sizeof(struct rc_object_entry_ondisk) + RC_PATH_SIZE((e)->merge_nr + (e)->split_nr) + (e)->size_size)
+#define RC_ENTRY_SIZE_OFFSET(e) (RC_ACTUAL_OBJECT_ENTRY_SIZE(e) - (e)->size_size)
/* single index maps objects to cache files */
struct rc_index_header {
@@ -90,6 +92,7 @@ struct rc_object_entry *from_disked_rc_object_entry(struct rc_object_entry_ondis
struct rc_object_entry_ondisk *to_disked_rc_object_entry(struct rc_object_entry *src, struct rc_object_entry_ondisk *dst);
extern unsigned char *get_cache_slice(struct commit *commit);
+extern int open_cache_slice(unsigned char *sha1, int flags);
extern int traverse_cache_slice(struct rev_info *revs,
unsigned char *cache_sha1, struct commit *commit,
unsigned long *date_so_far, int *slop_so_far,
@@ -102,6 +105,10 @@ extern int make_cache_slice(struct rev_cache_info *rci,
extern int make_cache_index(struct rev_cache_info *rci, unsigned char *cache_sha1,
int fd, unsigned int size);
-extern void starts_from_slices(struct rev_info *revs, unsigned int flags);
+extern void starts_from_slices(struct rev_info *revs, unsigned int flags, unsigned char *which, int n);
+extern int fuse_cache_slices(struct rev_cache_info *rci, struct rev_info *revs);
+extern int regenerate_cache_index(struct rev_cache_info *rci);
+extern int make_cache_slice_pointer(struct rev_cache_info *rci, const char *slice_path);
#endif
+
diff --git a/revision.h b/revision.h
index e767a43..c3ec1b3 100644
--- a/revision.h
+++ b/revision.h
@@ -19,17 +19,31 @@
struct rev_info;
struct log_info;
+struct rev_cache_slice_map {
+ unsigned char *map;
+ int size;
+ int last_index;
+};
+
struct rev_cache_info {
/* generation flags */
unsigned objects : 1,
legs : 1,
- make_index : 1;
+ make_index : 1,
+ fuse_me : 1;
+
+ /* index inclusion */
+ unsigned overwrite_all : 1;
/* traversal flags */
unsigned add_to_pending : 1;
/* fuse options */
unsigned int ignore_size;
+
+ /* reserved */
+ struct rev_cache_slice_map *maps,
+ *last_map;
};
struct rev_info {
--
tg: (a3c9f24..) t/revcache/misc (depends on: t/revcache/objects)
^ permalink raw reply related
* [PATCH 5/6 (v4)] full integration of rev-cache into git, completed test suite
From: Nick Edelen @ 2009-08-17 12:31 UTC (permalink / raw)
To: Junio C Hamano, Nicolas Pitre, Johannes Schindelin, Sam Vilain,
Michael J Gruber
This patch provides a working integration of rev-cache into the revision
walker, along with some touch-ups:
- integration into revision walker and list-objects
- tweak of object generation to take advantage of the 'unique' field
- more fluid handling of damaged cache slices
- numerous tests for both features from the previous patch, and the
integration's integrity
'Integration' is rather broad -- a more detailed description follows for each
aspect:
- rev-cache
the traversal mechanism is updated to handle many of the non-prune options
rev-list does (date limiting, slop-handling, etc.), and is adjusted to allow
for non-fatal cache-traversal failures.
- revision walker
both limited and unlimited traversal attempt to use the cache when possible,
smoothly falling back if it's not.
- list-objects
object listing does not recurse into cached trees, and has been adjusted to
guarantee commit-tag-tree-blob ordering.
Signed-off-by: Nick Edelen <sirnot@gmail.com>
---
builtin-rev-cache.c | 40 ++++++++
list-objects.c | 49 ++++++++--
rev-cache.c | 228 +++++++++++++++++++++++++++++++++++++++-----
revision.c | 88 ++++++++++++++---
t/t6015-rev-cache-list.sh | 151 ++++++++++++++++++++++++++++-
5 files changed, 501 insertions(+), 55 deletions(-)
diff --git a/builtin-rev-cache.c b/builtin-rev-cache.c
index 7ca39c4..a3489ce 100644
--- a/builtin-rev-cache.c
+++ b/builtin-rev-cache.c
@@ -4,6 +4,7 @@
#include "diff.h"
#include "revision.h"
#include "rev-cache.h"
+#include "list-objects.h"
unsigned long default_ignore_size = 50 * 1024 * 1024; /* 50mb */
@@ -78,6 +79,43 @@ static int handle_add(int argc, const char *argv[]) /* args beyond this command
return 0;
}
+static void show_commit(struct commit *commit, void *data)
+{
+ printf("%s\n", sha1_to_hex(commit->object.sha1));
+}
+
+static void show_object(struct object *obj, const struct name_path *path, const char *last)
+{
+ printf("%s\n", sha1_to_hex(obj->sha1));
+}
+
+static int test_rev_list(int argc, const char *argv[])
+{
+ struct rev_info revs;
+ unsigned int flags = 0;
+ int i;
+
+ init_revisions(&revs, 0);
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--not"))
+ flags ^= UNINTERESTING;
+ else if (!strcmp(argv[i], "--objects"))
+ revs.tree_objects = revs.blob_objects = 1;
+ else
+ handle_revision_arg(argv[i], &revs, flags, 1);
+ }
+
+ setup_revisions(0, 0, &revs, 0);
+ revs.topo_order = 1;
+ revs.lifo = 1;
+ prepare_revision_walk(&revs);
+
+ traverse_commit_list(&revs, show_commit, show_object, 0);
+
+ return 0;
+}
+
static int handle_walk(int argc, const char *argv[])
{
struct commit *commit;
@@ -270,6 +308,8 @@ int cmd_rev_cache(int argc, const char *argv[], const char *prefix)
r = handle_walk(argc, argv);
else if (!strcmp(arg, "index"))
r = handle_index(argc, argv);
+ else if (!strcmp(arg, "test"))
+ r = test_rev_list(argc, argv);
else if (!strcmp(arg, "alt"))
r = handle_alt(argc, argv);
else
diff --git a/list-objects.c b/list-objects.c
index 8953548..821a290 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -74,22 +74,33 @@ static void process_tree(struct rev_info *revs,
die("bad tree object");
if (obj->flags & (UNINTERESTING | SEEN))
return;
- if (parse_tree(tree) < 0)
- die("bad tree object %s", sha1_to_hex(obj->sha1));
+
obj->flags |= SEEN;
show(obj, path, name);
+ if (obj->flags & FACE_VALUE)
+ return;
+
+ /* traverse_commit_list is only used for enumeration purposes,
+ * ie. nothing relies on trees being parsed in this routine */
+ if (parse_tree(tree) < 0)
+ die("bad tree object %s", sha1_to_hex(obj->sha1));
+
me.up = path;
me.elem = name;
me.elem_len = strlen(name);
-
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
- if (S_ISDIR(entry.mode))
+ if (S_ISDIR(entry.mode)) {
+ struct tree *subtree = lookup_tree(entry.sha1);
+ if (!subtree)
+ continue;
+
+ subtree->object.flags &= ~FACE_VALUE;
process_tree(revs,
- lookup_tree(entry.sha1),
+ subtree,
show, &me, entry.path);
- else if (S_ISGITLINK(entry.mode))
+ } else if (S_ISGITLINK(entry.mode))
process_gitlink(revs, entry.sha1,
show, &me, entry.path);
else
@@ -136,6 +147,7 @@ void mark_edges_uninteresting(struct commit_list *list,
static void add_pending_tree(struct rev_info *revs, struct tree *tree)
{
+ tree->object.flags &= ~FACE_VALUE;
add_pending_object(revs, &tree->object, "");
}
@@ -146,17 +158,27 @@ void traverse_commit_list(struct rev_info *revs,
{
int i;
struct commit *commit;
+ enum object_type what = OBJ_TAG;
+ char face_value = 0;
while ((commit = get_revision(revs)) != NULL) {
- add_pending_tree(revs, commit->tree);
+ if (!(commit->object.flags & FACE_VALUE))
+ add_pending_tree(revs, commit->tree);
+ else
+ face_value = 1;
show_commit(commit, data);
}
+
+loop_objects:
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *pending = revs->pending.objects + i;
struct object *obj = pending->item;
const char *name = pending->name;
if (obj->flags & (UNINTERESTING | SEEN))
continue;
+ if (obj->type != what && face_value)
+ continue;
+
if (obj->type == OBJ_TAG) {
obj->flags |= SEEN;
show_object(obj, NULL, name);
@@ -175,6 +197,19 @@ void traverse_commit_list(struct rev_info *revs,
die("unknown pending object %s (%s)",
sha1_to_hex(obj->sha1), name);
}
+ if (face_value) {
+ switch (what) {
+ case OBJ_TAG :
+ what = OBJ_TREE;
+ goto loop_objects;
+ case OBJ_TREE :
+ what = OBJ_BLOB;
+ goto loop_objects;
+ default :
+ break;
+ }
+ }
+
if (revs->pending.nr) {
free(revs->pending.objects);
revs->pending.nr = 0;
diff --git a/rev-cache.c b/rev-cache.c
index 8eac1f0..7eefd3c 100644
--- a/rev-cache.c
+++ b/rev-cache.c
@@ -11,6 +11,12 @@
#include "run-command.h"
#include "string-list.h"
+
+struct bad_slice {
+ unsigned char sha1[20];
+ struct bad_slice *next;
+};
+
struct cache_slice_pointer {
char signature[8]; /* REVCOPTR */
char version;
@@ -23,8 +29,9 @@ static uint32_t fanout[0xff + 2];
static unsigned char *idx_map;
static int idx_size;
static struct rc_index_header idx_head;
+static char no_idx, add_to_pending;
+static struct bad_slice *bad_slices;
static unsigned char *idx_caches;
-static char no_idx;
static struct strbuf *acc_buffer;
@@ -119,6 +126,30 @@ struct rc_object_entry_ondisk *to_disked_rc_object_entry(struct rc_object_entry
return dst;
}
+static void mark_bad_slice(unsigned char *sha1)
+{
+ struct bad_slice *bad;
+
+ bad = xcalloc(sizeof(struct bad_slice), 1);
+ hashcpy(bad->sha1, sha1);
+
+ bad->next = bad_slices;
+ bad_slices = bad;
+}
+
+static int is_bad_slice(unsigned char *sha1)
+{
+ struct bad_slice *bad = bad_slices;
+
+ while (bad) {
+ if (!hashcmp(bad->sha1, sha1))
+ return 1;
+ bad = bad->next;
+ }
+
+ return 0;
+}
+
static int get_index_head(unsigned char *map, int len, struct rc_index_header *head, uint32_t *fanout, unsigned char **caches)
{
struct rc_index_header whead;
@@ -244,6 +275,7 @@ static struct rc_index_entry *search_index(unsigned char *sha1)
unsigned char *get_cache_slice(struct commit *commit)
{
struct rc_index_entry *ie;
+ unsigned char *sha1;
if (!idx_map) {
if (no_idx)
@@ -255,8 +287,13 @@ unsigned char *get_cache_slice(struct commit *commit)
return 0;
ie = search_index(commit->object.sha1);
- if (ie && ie->cache_index < idx_head.cache_nr)
- return idx_caches + ie->cache_index * 20;
+ if (ie && ie->cache_index < idx_head.cache_nr) {
+ sha1 = idx_caches + ie->cache_index * 20;
+
+ if (is_bad_slice(sha1))
+ return 0;
+ return sha1;
+ }
return 0;
}
@@ -266,6 +303,20 @@ unsigned char *get_cache_slice(struct commit *commit)
static unsigned long decode_size(unsigned char *str, int len);
+/* on failure */
+static void restore_commit(struct commit *commit)
+{
+ commit->object.flags &= ~(ADDED | SEEN | FACE_VALUE);
+
+ if (!commit->object.parsed) {
+ while (pop_commit(&commit->parents))
+ ;
+
+ parse_commit(commit);
+ }
+
+}
+
static void handle_noncommit(struct rev_info *revs, struct commit *commit, unsigned char *ptr, struct rc_object_entry *entry)
{
struct blob *blob;
@@ -305,23 +356,27 @@ static void handle_noncommit(struct rev_info *revs, struct commit *commit, unsig
}
obj->flags |= FACE_VALUE;
- add_pending_object(revs, obj, "");
+ if (add_to_pending)
+ add_pending_object(revs, obj, "");
}
-static int setup_traversal(struct rc_slice_header *head, unsigned char *map, struct commit *commit, struct commit_list **work)
+static int setup_traversal(struct rc_slice_header *head, unsigned char *map, struct commit *commit, struct commit_list **work,
+ struct commit_list **unwork, int *ipath_nr, int *upath_nr, char *ioutside)
{
struct rc_index_entry *iep;
struct rc_object_entry *oep;
struct commit_list *prev, *wp, **wpp;
int retval;
- iep = search_index(commit->object.sha1), 0;
+ iep = search_index(commit->object.sha1);
oep = RC_OBTAIN_OBJECT_ENTRY(map + iep->pos);
+ if (commit->object.flags & UNINTERESTING) {
+ ++*upath_nr;
+ oep->uninteresting = 1;
+ } else
+ ++*ipath_nr;
- /* the .uniniteresting bit isn't strictly necessary, as we check the object during traversal as well,
- * but we might as well initialize it while we're at it */
oep->include = 1;
- oep->uninteresting = !!(commit->object.flags & UNINTERESTING);
to_disked_rc_object_entry(oep, (struct rc_object_entry_ondisk *)(map + iep->pos));
retval = iep->pos;
@@ -336,6 +391,10 @@ static int setup_traversal(struct rc_slice_header *head, unsigned char *map, str
/* is this in our cache slice? */
iep = search_index(obj->sha1);
if (!iep || hashcmp(idx_caches + iep->cache_index * 20, head->sha1)) {
+ /* there are interesing objects outside the slice */
+ if (!(obj->flags & UNINTERESTING))
+ *ioutside = 1;
+
prev = wp;
wp = wp->next;
wpp = ℘
@@ -352,11 +411,20 @@ static int setup_traversal(struct rc_slice_header *head, unsigned char *map, str
oep->uninteresting = !!(obj->flags & UNINTERESTING);
to_disked_rc_object_entry(oep, (struct rc_object_entry_ondisk *)(map + iep->pos));
+ /* count even if not in slice so we can stop enumerating if possible */
+ if (obj->flags & UNINTERESTING)
+ ++*upath_nr;
+ else
+ ++*ipath_nr;
+
/* remove from work list */
co = pop_commit(wpp);
wp = *wpp;
if (prev)
prev->next = wp;
+
+ /* ...and store in temp list so we can restore work on failure */
+ commit_list_insert(co, unwork);
}
return retval;
@@ -373,13 +441,18 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
unsigned long *date_so_far, int *slop_so_far,
struct commit_list ***queue, struct commit_list **work)
{
- struct commit_list *insert_cache = 0;
+ struct commit_list *insert_cache = 0, *myq = 0, **myqp = &myq, *mywork = 0, **myworkp = &mywork, *unwork = 0;
struct commit **last_objects, *co;
- int i, total_path_nr = head->path_nr, retval = -1;
- char consume_children = 0;
+ unsigned long date = date_so_far ? *date_so_far : ~0ul;
+ int i, ipath_nr = 0, upath_nr = 0, orig_obj_nr = 0,
+ total_path_nr = head->path_nr, retval = -1, slop = slop_so_far ? *slop_so_far : SLOP;
+ char consume_children = 0, ioutside = 0;
unsigned char *paths;
- i = setup_traversal(head, map, commit, work);
+ /* take note in case we need to regress */
+ orig_obj_nr = revs->pending.nr;
+
+ i = setup_traversal(head, map, commit, work, &unwork, &ipath_nr, &upath_nr, &ioutside);
if (i < 0)
return -1;
@@ -427,6 +500,7 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
if ((paths[path] & IPATH) && (paths[path] & UPATH)) {
paths[path] = UPATH;
+ ipath_nr--;
/* mark edge */
if (last_objects[path]) {
@@ -437,6 +511,7 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
last_objects[path]->object.flags &= ~FACE_VALUE;
last_objects[path] = 0;
}
+ obj->flags |= BOUNDARY;
}
/* now we gotta re-assess the whole interesting thing... */
@@ -460,8 +535,10 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
last_objects[p]->object.flags &= ~FACE_VALUE;
last_objects[p] = 0;
}
- } else if (last_objects[p] && !last_objects[p]->object.parsed)
+ obj->flags |= BOUNDARY;
+ } else if (last_objects[p] && !last_objects[p]->object.parsed) {
commit_list_insert(co, &last_objects[p]->parents);
+ }
/* can't close a merge path until all are parents have been encountered */
if (GET_COUNT(paths[p])) {
@@ -471,14 +548,33 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
continue;
}
+ if (paths[p] & IPATH)
+ ipath_nr--;
+ else
+ upath_nr--;
+
paths[p] = 0;
last_objects[p] = 0;
}
}
/* make topo relations */
- if (last_objects[path] && !last_objects[path]->object.parsed)
+ if (last_objects[path] && !last_objects[path]->object.parsed) {
commit_list_insert(co, &last_objects[path]->parents);
+ }
+
+ /* we've been here already */
+ if (obj->flags & ADDED) {
+ if (entry->uninteresting && !(obj->flags & UNINTERESTING)) {
+ obj->flags |= UNINTERESTING;
+ mark_parents_uninteresting(co);
+ upath_nr--;
+ } else if (!entry->uninteresting)
+ ipath_nr--;
+
+ paths[path] = 0;
+ continue;
+ }
/* initialize commit */
if (!entry->is_end) {
@@ -491,24 +587,51 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
if (entry->uninteresting)
obj->flags |= UNINTERESTING;
+ else if (co->date < date)
+ date = co->date;
/* we need to know what the edges are */
last_objects[path] = co;
/* add to list */
- if (!(obj->flags & UNINTERESTING) || revs->show_all) {
- if (entry->is_end)
- insert_by_date_cached(co, work, insert_cache, &insert_cache);
- else
- *queue = &commit_list_insert(co, *queue)->next;
+ if (slop && !(revs->min_age != -1 && co->date > revs->min_age)) {
+
+ if (!(obj->flags & UNINTERESTING) || revs->show_all) {
+ if (entry->is_end)
+ myworkp = &commit_list_insert(co, myworkp)->next;
+ else
+ myqp = &commit_list_insert(co, myqp)->next;
+
+ /* add children to list as well */
+ if (obj->flags & UNINTERESTING)
+ consume_children = 0;
+ else
+ consume_children = 1;
+ }
- /* add children to list as well */
- if (obj->flags & UNINTERESTING)
- consume_children = 0;
- else
- consume_children = 1;
}
+ /* should we continue? */
+ if (!slop) {
+ if (!upath_nr) {
+ break;
+ } else if (ioutside || revs->show_all) {
+ /* pass it back to rev-list
+ * we purposely ignore everything outside this cache, so we don't needlessly traverse the whole
+ * thing on uninteresting, but that does mean that we may need to bounce back
+ * and forth a few times with rev-list */
+ myworkp = &commit_list_insert(co, myworkp)->next;
+
+ paths[path] = 0;
+ upath_nr--;
+ } else {
+ break;
+ }
+ } else if (!ipath_nr && co->date <= date)
+ slop--;
+ else
+ slop = SLOP;
+
/* open parents */
if (entry->merge_nr) {
int j, off = index + sizeof(struct rc_object_entry_ondisk);
@@ -523,6 +646,11 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
if (paths[p] & flag)
continue;
+ if (flag == IPATH)
+ ipath_nr++;
+ else
+ upath_nr++;
+
paths[p] |= flag;
}
@@ -532,12 +660,55 @@ static int traverse_cache_slice_1(struct rc_slice_header *head, unsigned char *m
}
+ if (date_so_far)
+ *date_so_far = date;
+ if (slop_so_far)
+ *slop_so_far = slop;
retval = 0;
+ /* success: attach to given lists */
+ if (myqp != &myq) {
+ **queue = myq;
+ *queue = myqp;
+ }
+
+ while ((co = pop_commit(&mywork)) != 0) {
+ insert_by_date_cached(co, work, insert_cache, &insert_cache);
+ }
+
+ /* free backup */
+ while (pop_commit(&unwork))
+ ;
+
end:
free(paths);
free(last_objects);
+ /* failure: restore work to previous condition
+ * (cache corruption should *not* be fatal) */
+ if (retval) {
+ while ((co = pop_commit(&unwork)) != 0) {
+ restore_commit(co);
+ co->object.flags |= SEEN;
+ insert_by_date(co, work);
+ }
+
+ /* free lists */
+ while ((co = pop_commit(&myq)) != 0)
+ restore_commit(co);
+
+ while ((co = pop_commit(&mywork)) != 0)
+ restore_commit(co);
+
+ /* truncate object array */
+ for (i = orig_obj_nr; i < revs->pending.nr; i++) {
+ struct object *obj = revs->pending.objects[i].item;
+
+ obj->flags &= ~FACE_VALUE;
+ }
+ revs->pending.nr = orig_obj_nr;
+ }
+
return retval;
}
@@ -628,6 +799,7 @@ int traverse_cache_slice(struct rev_info *revs,
/* load options */
rci = &revs->rev_cache_info;
+ add_to_pending = rci->add_to_pending;
memset(&head, 0, sizeof(struct rc_slice_header));
@@ -651,6 +823,10 @@ end:
if (fd != -1)
close(fd);
+ /* remember this! */
+ if (retval)
+ mark_bad_slice(cache_sha1);
+
return retval;
}
diff --git a/revision.c b/revision.c
index 485bf72..4640536 100644
--- a/revision.c
+++ b/revision.c
@@ -12,6 +12,7 @@
#include "patch-ids.h"
#include "decorate.h"
#include "log-tree.h"
+#include "rev-cache.h"
volatile show_early_output_fn_t show_early_output;
@@ -638,6 +639,8 @@ static int limit_list(struct rev_info *revs)
struct commit_list *list = revs->commits;
struct commit_list *newlist = NULL;
struct commit_list **p = &newlist;
+ unsigned char *cache_sha1;
+ char used_cache;
while (list) {
struct commit_list *entry = list;
@@ -650,24 +653,39 @@ static int limit_list(struct rev_info *revs)
if (revs->max_age != -1 && (commit->date < revs->max_age))
obj->flags |= UNINTERESTING;
- if (add_parents_to_list(revs, commit, &list, NULL) < 0)
- return -1;
- if (obj->flags & UNINTERESTING) {
- mark_parents_uninteresting(commit);
- if (revs->show_all)
- p = &commit_list_insert(commit, p)->next;
- slop = still_interesting(list, date, slop);
- if (slop)
+
+ /* rev-cache to the rescue!!! */
+ used_cache = 0;
+ if (!revs->dont_cache_me && !(obj->flags & ADDED)) {
+ cache_sha1 = get_cache_slice(commit);
+ if (cache_sha1) {
+ if (traverse_cache_slice(revs, cache_sha1, commit, &date, &slop, &p, &list) < 0)
+ used_cache = 0;
+ else
+ used_cache = 1;
+ }
+ }
+
+ if (!used_cache) {
+ if (add_parents_to_list(revs, commit, &list, NULL) < 0)
+ return -1;
+ if (obj->flags & UNINTERESTING) {
+ mark_parents_uninteresting(commit); /* ME: why? */
+ if (revs->show_all)
+ p = &commit_list_insert(commit, p)->next;
+ slop = still_interesting(list, date, slop);
+ if (slop > 0)
+ continue;
+ /* If showing all, add the whole pending list to the end */
+ if (revs->show_all)
+ *p = list;
+ break;
+ }
+ if (revs->min_age != -1 && (commit->date > revs->min_age))
continue;
- /* If showing all, add the whole pending list to the end */
- if (revs->show_all)
- *p = list;
- break;
+ date = commit->date;
+ p = &commit_list_insert(commit, p)->next;
}
- if (revs->min_age != -1 && (commit->date > revs->min_age))
- continue;
- date = commit->date;
- p = &commit_list_insert(commit, p)->next;
show = show_early_output;
if (!show)
@@ -813,6 +831,8 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->diffopt.prefix = prefix;
revs->diffopt.prefix_length = strlen(prefix);
}
+
+ init_rev_cache_info(&revs->rev_cache_info);
}
static void add_pending_commit_list(struct rev_info *revs,
@@ -1372,6 +1392,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
if (revs->reflog_info && revs->graph)
die("cannot combine --walk-reflogs with --graph");
+ /* limits on caching
+ * todo: implement this functionality */
+ if (revs->prune || revs->diff)
+ revs->dont_cache_me = 1;
+
return left;
}
@@ -1654,6 +1679,8 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
{
if (!opt->grep_filter.pattern_list)
return 1;
+ if (!commit->object.parsed)
+ parse_commit(commit);
return grep_buffer(&opt->grep_filter,
NULL, /* we say nothing, not even filename */
commit->buffer, strlen(commit->buffer));
@@ -1706,6 +1733,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
do {
struct commit_list *entry = revs->commits;
struct commit *commit = entry->item;
+ struct object *obj = &commit->object;
revs->commits = entry->next;
free(entry);
@@ -1722,11 +1750,39 @@ static struct commit *get_revision_1(struct rev_info *revs)
if (revs->max_age != -1 &&
(commit->date < revs->max_age))
continue;
+
+ if (!revs->dont_cache_me) {
+ struct commit_list *queue = 0, **queuep = &queue;
+ unsigned char *cache_sha1;
+
+ if (obj->flags & ADDED)
+ goto skip_parenting;
+
+ cache_sha1 = get_cache_slice(commit);
+ if (cache_sha1) {
+ if (!traverse_cache_slice(revs, cache_sha1, commit, 0, 0, &queuep, &revs->commits)) {
+ struct commit_list *work = revs->commits;
+
+ /* attach queue to end of ->commits */
+ while (work && work->next)
+ work = work->next;
+
+ if (work)
+ work->next = queue;
+ else
+ revs->commits = queue;
+
+ goto skip_parenting;
+ }
+ }
+ }
+
if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0)
die("Failed to traverse parents of commit %s",
sha1_to_hex(commit->object.sha1));
}
+skip_parenting:
switch (simplify_commit(revs, commit)) {
case commit_ignore:
continue;
diff --git a/t/t6015-rev-cache-list.sh b/t/t6015-rev-cache-list.sh
index afa0303..fa6df21 100755
--- a/t/t6015-rev-cache-list.sh
+++ b/t/t6015-rev-cache-list.sh
@@ -38,6 +38,7 @@ test_expect_success 'init repo' '
git add . &&
git commit -m "omg" &&
+ sleep 2 &&
git branch b4 &&
git checkout b4 &&
echo shazam >file8 &&
@@ -46,7 +47,7 @@ test_expect_success 'init repo' '
git merge -m "merge b2" b2 &&
echo bam >smoke/pipe &&
- git add .
+ git add . &&
git commit -m "bam" &&
git checkout master &&
@@ -71,18 +72,26 @@ test_expect_success 'init repo' '
git add . &&
git commit -m "lol" &&
+ sleep 2 &&
git checkout master &&
git merge -m "triple merge" b1 b11 &&
git rm -r d1 &&
+ sleep 2 &&
git commit -a -m "oh noes"
'
-git-rev-list HEAD --not HEAD~3 >proper_commit_list_limited
-git-rev-list HEAD >proper_commit_list
-git-rev-list HEAD --objects >proper_object_list
+max_date=`git-rev-list --timestamp HEAD~1 --max-count=1 | grep -e "^[0-9]*" -o`
+min_date=`git-rev-list --timestamp b4 --max-count=1 | grep -e "^[0-9]*" -o`
+
+git-rev-list --topo-order HEAD --not HEAD~3 >proper_commit_list_limited
+git-rev-list --topo-order HEAD --not HEAD~2 >proper_commit_list_limited2
+git-rev-list --topo-order HEAD >proper_commit_list
+git-rev-list --objects HEAD >proper_object_list
+git-rev-list HEAD --max-age=$min_date --min-age=$max_date >proper_list_date_limited
+
+cache_sha1=`git-rev-cache add HEAD 2>output.err`
test_expect_success 'make cache slice' '
- git-rev-cache add HEAD 2>output.err &&
grep "final return value: 0" output.err
'
@@ -102,11 +111,141 @@ test_expect_success 'test rev-caches walker directly (unlimited)' '
test_cmp_sorted list proper_commit_list
'
+test_expect_success 'test rev-list traversal (limited)' '
+ git-rev-list HEAD --not HEAD~3 >list &&
+ test_cmp list proper_commit_list_limited
+'
+
+test_expect_success 'test rev-list traversal (unlimited)' '
+ git-rev-list HEAD >list &&
+ test_cmp list proper_commit_list
+'
+
#do the same for objects
test_expect_success 'test rev-caches walker with objects' '
git-rev-cache walk --objects HEAD >list &&
test_cmp_sorted list proper_object_list
'
-test_done
+test_expect_success 'test rev-list with objects (topo order)' '
+ git-rev-list --topo-order --objects HEAD >list &&
+ test_cmp_sorted list proper_object_list
+'
+
+test_expect_success 'test rev-list with objects (no order)' '
+ git-rev-list --objects HEAD >list &&
+ test_cmp_sorted list proper_object_list
+'
+
+#verify age limiting
+test_expect_success 'test rev-list date limiting (topo order)' '
+ git-rev-list --topo-order --max-age=$min_date --min-age=$max_date HEAD >list &&
+ test_cmp_sorted list proper_list_date_limited
+'
+
+test_expect_success 'test rev-list date limiting (no order)' '
+ git-rev-list --max-age=$min_date --min-age=$max_date HEAD >list &&
+ test_cmp_sorted list proper_list_date_limited
+'
+
+#check partial cache slice
+test_expect_success 'saving old cache and generating partial slice' '
+ cp ".git/rev-cache/$cache_sha1" .git/rev-cache/.old &&
+ rm ".git/rev-cache/$cache_sha1" .git/rev-cache/index &&
+
+ git-rev-cache add HEAD~2 2>output.err &&
+ grep "final return value: 0" output.err
+'
+
+test_expect_success 'rev-list with wholly interesting partial slice' '
+ git-rev-list --topo-order HEAD >list &&
+ test_cmp list proper_commit_list
+'
+
+test_expect_success 'rev-list with partly uninteresting partial slice' '
+ git-rev-list --topo-order HEAD --not HEAD~3 >list &&
+ test_cmp list proper_commit_list_limited
+'
+
+test_expect_success 'rev-list with wholly uninteresting partial slice' '
+ git-rev-list --topo-order HEAD --not HEAD~2 >list &&
+ test_cmp list proper_commit_list_limited2
+'
+
+#try out index generation and fuse (note that --all == HEAD in this case)
+#probably should make a test for that too...
+test_expect_success 'test (non-)fusion of one slice' '
+ git-rev-cache fuse >output.err &&
+ grep "nothing to fuse" output.err
+'
+test_expect_success 'make fresh slice' '
+ git-rev-cache add --all --fresh 2>output.err &&
+ grep "final return value: 0" output.err
+'
+
+test_expect_success 'check dual slices' '
+ git-rev-list --topo-order HEAD~2 HEAD >list &&
+ test_cmp list proper_commit_list
+'
+
+test_expect_success 'regenerate index' '
+ rm .git/rev-cache/index &&
+ git-rev-cache index 2>output.err &&
+ grep "final return value: 0" output.err
+'
+
+test_expect_success 'fuse slices' '
+ test -e .git/rev-cache/.old &&
+ git-rev-cache fuse 2>output.err &&
+ grep "final return value: 0" output.err &&
+ test_cmp .git/rev-cache/$cache_sha1 .git/rev-cache/.old
+'
+
+#make sure we can smoothly handle corrupted caches
+test_expect_success 'corrupt slice' '
+ echo bla >.git/rev-cache/$cache_sha1
+'
+
+test_expect_success 'test rev-list traversal (limited) (corrupt slice)' '
+ git-rev-list --topo-order HEAD --not HEAD~3 >list &&
+ test_cmp list proper_commit_list_limited
+'
+
+test_expect_success 'test rev-list traversal (unlimited) (corrupt slice)' '
+ git-rev-list HEAD >list &&
+ test_cmp_sorted list proper_commit_list
+'
+
+test_expect_success 'corrupt index' '
+ echo blu >.git/rev-cache/index
+'
+
+test_expect_success 'test rev-list traversal (limited) (corrupt index)' '
+ git-rev-list --topo-order HEAD --not HEAD~3 >list &&
+ test_cmp list proper_commit_list_limited
+'
+
+test_expect_success 'test rev-list traversal (unlimited) (corrupt index)' '
+ git-rev-list HEAD >list &&
+ test_cmp_sorted list proper_commit_list
+'
+
+#test --ignore-size in fuse
+rm .git/rev-cache/*
+cache_sha1=`git-rev-cache add HEAD~2 2>output.err`
+
+test_expect_success 'make fragmented slices' '
+ git-rev-cache add HEAD~1 --not HEAD~2 2>>output.err &&
+ git-rev-cache add HEAD --fresh 2>>output.err &&
+ test `grep "final return value: 0" output.err | wc -l` -eq 3
+'
+
+cache_size=`wc -c .git/rev-cache/$cache_sha1 | grep -o "[0-9]*"`
+test_expect_success 'test --ignore-size function in fuse' '
+ git-rev-cache fuse --ignore-size=$cache_size 2>output.err &&
+ grep "final return value: 0" output.err &&
+ test -e .git/rev-cache/$cache_sha1
+'
+
+test_done
--
tg: (2f0aff6..) t/revcache/integration (depends on: t/revcache/misc)
^ permalink raw reply related
* [PATCH 6/6 (v4)] support for path name caching in rev-cache
From: Nick Edelen @ 2009-08-17 12:31 UTC (permalink / raw)
To: Junio C Hamano, Nicolas Pitre, Johannes Schindelin, Sam Vilain,
Michael J Gruber
An update to caching mechanism, allowing path names to be cached for blob and
tree objects. A list of names appearing in each cache slice is appended to the
end of the slice, which is referenced by variable-sized indexes per entry.
This allows pack-objects to more intelligently schedule unpacked/poorly packed
object, and enables proper duplication of rev-list's behaivor.
Signed-off-by: Nick Edelen <sirnot@gmail.com>
---
builtin-rev-cache.c | 3 +-
rev-cache.c | 314 +++++++++++++++++++++++++++++++++++++--------
rev-cache.h | 16 ++-
revision.h | 6 +-
t/t6015-rev-cache-list.sh | 4 +-
5 files changed, 282 insertions(+), 61 deletions(-)
diff --git a/builtin-rev-cache.c b/builtin-rev-cache.c
index a3489ce..5ea5c6b 100644
--- a/builtin-rev-cache.c
+++ b/builtin-rev-cache.c
@@ -177,13 +177,14 @@ static int handle_walk(int argc, const char *argv[])
fprintf(stderr, "pending:\n");
for (i = 0; i < revs.pending.nr; i++) {
struct object *obj = revs.pending.objects[i].item;
+ const char *name = revs.pending.objects[i].name;
/* unfortunately, despite our careful generation, object duplication *is* a possibility...
* (eg. same object introduced into two different branches) */
if (obj->flags & SEEN)
continue;
- printf("%s\n", sha1_to_hex(revs.pending.objects[i].item->sha1));
+ printf("%s %s\n", sha1_to_hex(revs.pending.objects[i].item->sha1), name);
obj->flags |= SEEN;
}
diff --git a/rev-cache.c b/rev-cache.c
index 7eefd3c..1db5578 100644
--- a/rev-cache.c
+++ b/rev-cache.c
@@ -17,6 +17,14 @@ struct bad_slice {
struct bad_slice *next;
};
+struct name_list {
+ unsigned char sha1[20];
+ unsigned int len;
+ struct name_list *next;
+
+ char buf[FLEX_ARRAY];
+};
+
struct cache_slice_pointer {
char signature[8]; /* REVCOPTR */
char version;
@@ -29,10 +37,13 @@ static uint32_t fanout[0xff + 2];
static unsigned char *idx_map;
static int idx_size;
static struct rc_index_header idx_head;
-static char no_idx, add_to_pending;
-static struct bad_slice *bad_slices;
+static char no_idx, add_to_pending, add_names;
static unsigned char *idx_caches;
+static struct bad_slice *bad_slices;
+static struct name_list *name_lists, *cur_name_list;
+
+static struct strbuf *acc_name_buffer;
static struct strbuf *acc_buffer;
#define SLOP 5
@@ -78,7 +89,7 @@ struct rc_object_entry *from_disked_rc_object_entry(struct rc_object_entry_ondis
if (!dst)
dst = &entry[cur++ & 0x3];
- dst->type = src->flags >> 5;
+ dst->type = src->flags >> 5 & 0x03;
dst->is_end = !!(src->flags & 0x10);
dst->is_start = !!(src->flags & 0x08);
dst->uninteresting = !!(src->flags & 0x04);
@@ -89,8 +100,9 @@ struct rc_object_entry *from_disked_rc_object_entry(struct rc_object_entry_ondis
dst->merge_nr = src->merge_nr;
dst->split_nr = src->split_nr;
- dst->size_size = src->sizes >> 5;
- dst->padding = src->sizes & 0x1f;
+ dst->size_size = src->sizes >> 5 & 0x03;
+ dst->name_size = src->sizes >> 2 & 0x03;
+ dst->padding = src->sizes & 0x02;
dst->date = ntohl(src->date);
dst->path = ntohs(src->path);
@@ -118,6 +130,7 @@ struct rc_object_entry_ondisk *to_disked_rc_object_entry(struct rc_object_entry
dst->split_nr = src->split_nr;
dst->sizes = (unsigned char)src->size_size << 5;
+ dst->sizes |= (unsigned char)src->name_size << 2;
dst->sizes |= (unsigned char)src->padding;
dst->date = htonl(src->date);
@@ -190,6 +203,12 @@ static void cleanup_cache_slices(void)
idx_map = 0;
}
+ while (name_lists) {
+ struct name_list *nl = name_lists->next;
+ free(name_lists);
+ name_lists = nl;
+ }
+
}
static int init_index(void)
@@ -322,7 +341,7 @@ static void handle_noncommit(struct rev_info *revs, struct commit *commit, unsig
struct blob *blob;
struct tree *tree;
struct object *obj;
- unsigned long size;
+ unsigned long size, name_index;
size = decode_size(ptr + RC_ENTRY_SIZE_OFFSET(entry), entry->size_size);
switch (entry->type) {
@@ -355,9 +374,22 @@ static void handle_noncommit(struct rev_info *revs, struct commit *commit, unsig
return;
}
+ if (add_names && cur_name_list) {
+ name_index = decode_size(ptr + RC_ENTRY_NAME_OFFSET(entry), entry->name_size);
+
+ if (name_index >= cur_name_list->len)
+ name_index = 0;
+ } else name_index = 0;
+
obj->flags |= FACE_VALUE;
- if (add_to_pending)
- add_pending_object(revs, obj, "");
+ if (add_to_pending) {
+ char *name = "";
+
+ if (name_index)
+ name = cur_name_list->buf + name_index;
+
+ add_pending_object(revs, obj, name);
+ }
}
static int setup_traversal(struct rc_slice_header *head, unsigned char *map, struct commit *commit, struct commit_list **work,
@@ -712,15 +744,44 @@ end:
return retval;
}
-static int get_cache_slice_header(unsigned char *cache_sha1, unsigned char *map, int len, struct rc_slice_header *head)
+static struct name_list *get_cache_slice_name_list(struct rc_slice_header *head, int fd)
+{
+ struct name_list *nl = name_lists;
+
+ while (nl) {
+ if (!hashcmp(nl->sha1, head->sha1))
+ break;
+ nl = nl->next;
+ }
+
+ if (nl)
+ return nl;
+
+ nl = xcalloc(1, sizeof(struct name_list) + head->name_size);
+ nl->len = head->name_size;
+ hashcpy(nl->sha1, head->sha1);
+
+ lseek(fd, head->size, SEEK_SET);
+ read_in_full(fd, nl->buf, head->name_size);
+
+ nl->next = name_lists;
+ name_lists = nl;
+
+ return nl;
+}
+
+static int get_cache_slice_header(int fd, unsigned char *cache_sha1, int len, struct rc_slice_header *head)
{
int t;
- memcpy(head, map, sizeof(struct rc_slice_header));
+ if (xread(fd, head, sizeof(struct rc_slice_header)) != sizeof(struct rc_slice_header))
+ return -1;
+
head->ofs_objects = ntohl(head->ofs_objects);
head->object_nr = ntohl(head->object_nr);
head->size = ntohl(head->size);
head->path_nr = ntohs(head->path_nr);
+ head->name_size = ntohl(head->name_size);
if (memcmp(head->signature, "REVCACHE", 8))
return -1;
@@ -729,10 +790,10 @@ static int get_cache_slice_header(unsigned char *cache_sha1, unsigned char *map,
if (hashcmp(head->sha1, cache_sha1))
return -3;
t = sizeof(struct rc_slice_header);
- if (t != head->ofs_objects || t >= len)
+ if (t != head->ofs_objects)
return -4;
-
- head->size = len;
+ if (head->size + head->name_size != len)
+ return -5;
return 0;
}
@@ -784,7 +845,7 @@ int traverse_cache_slice(struct rev_info *revs,
unsigned long *date_so_far, int *slop_so_far,
struct commit_list ***queue, struct commit_list **work)
{
- int fd = -1, retval = -3;
+ int fd = -1, t, retval;
struct stat fi;
struct rc_slice_header head;
struct rev_cache_info *rci;
@@ -800,26 +861,31 @@ int traverse_cache_slice(struct rev_info *revs,
/* load options */
rci = &revs->rev_cache_info;
add_to_pending = rci->add_to_pending;
+ add_names = rci->add_names;
memset(&head, 0, sizeof(struct rc_slice_header));
+# define ERROR(x) do { retval = (x); goto end; } while (0);
fd = open_cache_slice(cache_sha1, O_RDONLY);
if (fd == -1)
- goto end;
+ ERROR(-1);
if (fstat(fd, &fi) || fi.st_size < sizeof(struct rc_slice_header))
- goto end;
+ ERROR(-2);
+
+ if ((t = get_cache_slice_header(fd, cache_sha1, fi.st_size, &head)) < 0)
+ ERROR(-t);
+ if (add_names)
+ cur_name_list = get_cache_slice_name_list(&head, fd);
- map = xmmap(0, fi.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ map = xmmap(0, head.size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED)
- goto end;
- if (get_cache_slice_header(cache_sha1, map, fi.st_size, &head))
- goto end;
+ ERROR(-3);
retval = traverse_cache_slice_1(&head, map, revs, commit, date_so_far, slop_so_far, queue, work);
end:
if (map != MAP_FAILED)
- munmap(map, fi.st_size);
+ munmap(map, head.size);
if (fd != -1)
close(fd);
@@ -827,6 +893,7 @@ end:
if (retval)
mark_bad_slice(cache_sha1);
+# undef ERROR
return retval;
}
@@ -1111,23 +1178,110 @@ static unsigned long decode_size(unsigned char *str, int len)
return size;
}
+
+#define NL_HASH_TABLE_SIZE (0xffff + 1)
+#define NL_HASH_NUMBER (NL_HASH_TABLE_SIZE >> 3)
+
+struct name_list_hash {
+ int ind;
+ struct name_list_hash *next;
+};
+
+static struct name_list_hash **nl_hash_table;
+static unsigned char *nl_hashes;
+
+/* FNV-1a hash */
+static unsigned int hash_name(const char *name)
+{
+ unsigned int hash = 2166136261ul;
+ const char *p = name;
+
+ while (*p) {
+ hash ^= *p++;
+ hash *= 16777619ul;
+ }
+
+ return hash & 0xffff;
+}
+
+static int name_in_list(const char *name)
+{
+ unsigned int h = hash_name(name);
+ struct name_list_hash *entry = nl_hash_table[h];
+
+ while (entry && strcmp(acc_name_buffer->buf + entry->ind, name))
+ entry = entry->next;
+
+ if (entry)
+ return entry->ind;
+
+ /* add name to buffer and create hash reference */
+ entry = xcalloc(1, sizeof(struct name_list_hash));
+ entry->ind = acc_name_buffer->len;
+ strbuf_add(acc_name_buffer, name, strlen(name) + 1);
+
+ entry->next = nl_hash_table[h];
+ nl_hash_table[h] = entry;
+
+ nl_hashes[h / 8] |= h % 8;
+
+ return entry->ind;
+}
+
+static void init_name_list_hash(void)
+{
+ nl_hash_table = xcalloc(NL_HASH_TABLE_SIZE, sizeof(struct name_list_hash));
+ nl_hashes = xcalloc(NL_HASH_NUMBER, 1);
+}
+
+static void cleanup_name_list_hash(void)
+{
+ int i;
+
+ for (i = 0; i < NL_HASH_NUMBER; i++) {
+ int j, ind = nl_hashes[i];
+
+ if (!ind)
+ continue;
+
+ for (j = 0; j < 8; j++) {
+ struct name_list_hash **entryp;
+
+ if (!(ind & 1 << j))
+ continue;
+
+ entryp = &nl_hash_table[i * 8 + j];
+ while (*entryp) {
+ struct name_list_hash *t = (*entryp)->next;
+
+ free(*entryp);
+ *entryp = t;
+ }
+ }
+ } /* code overhang! */
+
+ free(nl_hashes);
+ free(nl_hash_table);
+}
+
static void add_object_entry(const unsigned char *sha1, struct rc_object_entry *entryp,
- struct strbuf *merge_str, struct strbuf *split_str)
+ struct strbuf *merge_str, struct strbuf *split_str, char *name, unsigned long size)
{
struct rc_object_entry entry;
- unsigned char size_str[7];
- unsigned long size;
+ unsigned char size_str[7], name_str[7];
enum object_type type;
void *data;
if (entryp)
sha1 = entryp->sha1;
- /* retrieve size data */
- data = read_sha1_file(sha1, &type, &size);
+ if (!size) {
+ /* retrieve size data */
+ data = read_sha1_file(sha1, &type, &size);
- if (data)
- free(data);
+ if (data)
+ free(data);
+ }
/* initialize! */
if (!entryp) {
@@ -1145,6 +1299,9 @@ static void add_object_entry(const unsigned char *sha1, struct rc_object_entry *
entryp->size_size = encode_size(size, size_str);
+ if (name)
+ entryp->name_size = encode_size(name_in_list(name), name_str);
+
/* write the muvabitch */
strbuf_add(acc_buffer, to_disked_rc_object_entry(entryp, 0), sizeof(struct rc_object_entry_ondisk));
@@ -1154,6 +1311,9 @@ static void add_object_entry(const unsigned char *sha1, struct rc_object_entry *
strbuf_add(acc_buffer, split_str->buf, split_str->len);
strbuf_add(acc_buffer, size_str, entryp->size_size);
+
+ if (name)
+ strbuf_add(acc_buffer, name_str, entryp->name_size);
}
/* returns non-zero to continue parsing, 0 to skip */
@@ -1198,6 +1358,9 @@ continue_loop:
static int dump_tree_callback(const unsigned char *sha1, const char *path, unsigned int mode)
{
strbuf_add(acc_buffer, sha1, 20);
+ strbuf_add(acc_buffer, (char *)&acc_name_buffer->len, sizeof(size_t));
+
+ strbuf_add(acc_name_buffer, path, strlen(path) + 1);
return 1;
}
@@ -1211,6 +1374,9 @@ static void tree_addremove(struct diff_options *options,
return;
strbuf_add(acc_buffer, sha1, 20);
+ strbuf_add(acc_buffer, (char *)&acc_name_buffer->len, sizeof(size_t));
+
+ strbuf_add(acc_name_buffer, concatpath, strlen(concatpath) + 1);
}
static void tree_change(struct diff_options *options,
@@ -1223,12 +1389,15 @@ static void tree_change(struct diff_options *options,
return;
strbuf_add(acc_buffer, new_sha1, 20);
+ strbuf_add(acc_buffer, (char *)&acc_name_buffer->len, sizeof(size_t));
+
+ strbuf_add(acc_name_buffer, concatpath, strlen(concatpath) + 1);
}
static int add_unique_objects(struct commit *commit)
{
struct commit_list *list;
- struct strbuf os, ost, *orig_buf;
+ struct strbuf os, ost, names, *orig_name_buf, *orig_buf;
struct diff_options opts;
int i, j, next;
char is_first = 1;
@@ -1236,13 +1405,17 @@ static int add_unique_objects(struct commit *commit)
/* ...no, calculate unique objects */
strbuf_init(&os, 0);
strbuf_init(&ost, 0);
+ strbuf_init(&names, 0);
orig_buf = acc_buffer;
+ orig_name_buf = acc_name_buffer;
+ acc_name_buffer = &names;
diff_setup(&opts);
DIFF_OPT_SET(&opts, RECURSIVE);
DIFF_OPT_SET(&opts, TREE_IN_RECURSIVE);
opts.change = tree_change;
opts.add_remove = tree_addremove;
+# define ENTRY_SIZE (20 + sizeof(size_t))
/* this is only called for non-ends (ie. all parents interesting) */
for (list = commit->parents; list; list = list->next) {
@@ -1253,20 +1426,20 @@ static int add_unique_objects(struct commit *commit)
strbuf_setlen(acc_buffer, 0);
diff_tree_sha1(list->item->tree->object.sha1, commit->tree->object.sha1, "", &opts);
- qsort(acc_buffer->buf, acc_buffer->len / 20, 20, (int (*)(const void *, const void *))hashcmp);
+ qsort(acc_buffer->buf, acc_buffer->len / ENTRY_SIZE, ENTRY_SIZE, (int (*)(const void *, const void *))hashcmp);
/* take intersection */
if (!is_first) {
- for (next = i = j = 0; i < os.len; i += 20) {
+ for (next = i = j = 0; i < os.len; i += ENTRY_SIZE) {
while (j < ost.len && hashcmp((unsigned char *)(ost.buf + j), (unsigned char *)(os.buf + i)) < 0)
- j += 20;
+ j += ENTRY_SIZE;
if (j >= ost.len || hashcmp((unsigned char *)(ost.buf + j), (unsigned char *)(os.buf + i)))
continue;
if (next != i)
- memcpy(os.buf + next, os.buf + i, 20);
- next += 20;
+ memcpy(os.buf + next, os.buf + i, ENTRY_SIZE);
+ next += ENTRY_SIZE;
}
if (next != i)
@@ -1283,26 +1456,34 @@ static int add_unique_objects(struct commit *commit)
/* the ordering of non-commit objects dosn't really matter, so we're not gonna bother */
acc_buffer = orig_buf;
- for (i = 0; i < os.len; i += 20)
- add_object_entry((unsigned char *)(os.buf + i), 0, 0, 0);
+ acc_name_buffer = orig_name_buf;
+ for (i = 0; i < os.len; i += ENTRY_SIZE)
+ add_object_entry((unsigned char *)(os.buf + i), 0, 0, 0, names.buf + *(size_t *)(os.buf + i + 20), 0);
/* last but not least, the main tree */
- add_object_entry(commit->tree->object.sha1, 0, 0, 0);
+ add_object_entry(commit->tree->object.sha1, 0, 0, 0, 0, 0);
+
+ strbuf_release(&ost);
+ strbuf_release(&os);
+ strbuf_release(&names);
- return i / 20 + 1;
+ return i / ENTRY_SIZE + 1;
+# undef ENTRY_SIZE
}
static int add_objects_verbatim_1(struct rev_cache_slice_map *mapping, int *index)
{
- unsigned char *map = mapping->map;
int i = *index, object_nr = 0;
+ unsigned char *map = mapping->map;
struct rc_object_entry *entry = RC_OBTAIN_OBJECT_ENTRY(map + i);
+ unsigned long size;
i += RC_ACTUAL_OBJECT_ENTRY_SIZE(entry);
while (i < mapping->size) {
- int pos = i;
+ char *name;
+ int name_index, pos = i;
- entry = RC_OBTAIN_OBJECT_ENTRY(map + i;
+ entry = RC_OBTAIN_OBJECT_ENTRY(map + i);
i += RC_ACTUAL_OBJECT_ENTRY_SIZE(entry);
if (entry->type == OBJ_COMMIT) {
@@ -1310,7 +1491,15 @@ static int add_objects_verbatim_1(struct rev_cache_slice_map *mapping, int *inde
return object_nr;
}
- strbuf_add(acc_buffer, map + pos, i - pos);
+ name_index = decode_size(map + pos + RC_ENTRY_NAME_OFFSET(entry), entry->name_size);
+ if (name_index && name_index < mapping->name_size)
+ name = mapping->names + name_index;
+ else
+ name = 0;
+
+ size = decode_size(map + pos + RC_ENTRY_SIZE_OFFSET(entry), entry->size_size);
+
+ add_object_entry(0, entry, 0, 0, name, size);
object_nr++;
}
@@ -1395,6 +1584,7 @@ void init_rev_cache_info(struct rev_cache_info *rci)
rci->overwrite_all = 0;
rci->add_to_pending = 1;
+ rci->add_names = 1;
rci->ignore_size = 0;
}
@@ -1419,9 +1609,9 @@ int make_cache_slice(struct rev_cache_info *rci,
struct rc_slice_header head;
struct commit *commit;
unsigned char sha1[20];
- struct strbuf merge_paths, split_paths;
+ struct strbuf merge_paths, split_paths, namelist;
int object_nr, total_sz, fd;
- char file[PATH_MAX], *newfile;
+ char file[PATH_MAX], null, *newfile;
struct rev_cache_info *trci;
git_SHA_CTX ctx;
@@ -1436,7 +1626,13 @@ int make_cache_slice(struct rev_cache_info *rci,
strbuf_init(&endlist, 0);
strbuf_init(&merge_paths, 0);
strbuf_init(&split_paths, 0);
+ strbuf_init(&namelist, 0);
acc_buffer = &buffer;
+ acc_name_buffer = &namelist;
+
+ null = 0;
+ strbuf_add(&namelist, &null, 1);
+ init_name_list_hash();
if (!revs) {
revs = &therevs;
@@ -1467,6 +1663,7 @@ int make_cache_slice(struct rev_cache_info *rci,
trci = &revs->rev_cache_info;
init_rev_cache_info(trci);
trci->add_to_pending = 0;
+ trci->add_names = 0;
setup_revisions(0, 0, revs, 0);
if (prepare_revision_walk(revs))
@@ -1504,7 +1701,7 @@ int make_cache_slice(struct rev_cache_info *rci,
commit->indegree = 0;
- add_object_entry(0, &object, &merge_paths, &split_paths);
+ add_object_entry(0, &object, &merge_paths, &split_paths, 0, 0);
object_nr++;
if (rci->objects && !object.is_end) {
@@ -1530,10 +1727,16 @@ int make_cache_slice(struct rev_cache_info *rci,
total_sz += buffer.len;
}
+ /* write path name lookup list */
+ head.name_size = htonl(namelist.len);
+ write_in_full(fd, namelist.buf, namelist.len);
+
/* go ahead a free some stuff... */
strbuf_release(&buffer);
strbuf_release(&merge_paths);
strbuf_release(&split_paths);
+ strbuf_release(&namelist);
+ cleanup_name_list_hash();
if (path_sz)
free(paths);
while (path_track_alloc)
@@ -1991,6 +2194,7 @@ int fuse_cache_slices(struct rev_cache_info *rci, struct rev_info *revs)
for (i = idx_head.cache_nr - 1; i >= 0; i--) {
struct rev_cache_slice_map *map = rci->maps + i;
struct stat fi;
+ struct rc_slice_header head;
int fd;
if (!map->size)
@@ -2003,13 +2207,20 @@ int fuse_cache_slices(struct rev_cache_info *rci, struct rev_info *revs)
continue;
if (fi.st_size < sizeof(struct rc_slice_header))
continue;
+ if (get_cache_slice_header(fd, idx_caches + i * 20, fi.st_size, &head))
+ continue;
- map->map = xmmap(0, fi.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ map->map = xmmap(0, head.size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map->map == MAP_FAILED)
continue;
+ lseek(fd, head.size, SEEK_SET);
+ map->names = xcalloc(head.name_size, 1);
+ read_in_full(fd, map->names, head.name_size);
+
close(fd);
- map->size = fi.st_size;
+ map->size = head.size;
+ map->name_size = head.name_size;
}
rci->make_index = 0;
@@ -2026,6 +2237,7 @@ int fuse_cache_slices(struct rev_cache_info *rci, struct rev_info *revs)
if (!map->size)
continue;
+ free(map->names);
munmap(map->map, map->size);
}
free(rci->maps);
@@ -2047,7 +2259,6 @@ static int verify_cache_slice(const char *slice_path, unsigned char *sha1)
{
struct rc_slice_header head;
int fd, len, retval = -1;
- unsigned char *map = MAP_FAILED;
struct stat fi;
len = strlen(slice_path);
@@ -2062,17 +2273,12 @@ static int verify_cache_slice(const char *slice_path, unsigned char *sha1)
if (fstat(fd, &fi) || fi.st_size < sizeof(head))
goto end;
- map = xmmap(0, sizeof(head), PROT_READ, MAP_PRIVATE, fd, 0);
- if (map == MAP_FAILED)
- goto end;
- if (get_cache_slice_header(sha1, map, fi.st_size, &head))
+ if (get_cache_slice_header(fd, sha1, fi.st_size, &head))
goto end;
retval = 0;
end:
- if (map != MAP_FAILED)
- munmap(map, sizeof(head));
if (fd > 0)
close(fd);
diff --git a/rev-cache.h b/rev-cache.h
index 14437d8..c88ceae 100644
--- a/rev-cache.h
+++ b/rev-cache.h
@@ -10,8 +10,14 @@
#define RC_OBTAIN_OBJECT_ENTRY(p) from_disked_rc_object_entry((struct rc_object_entry_ondisk *)(p), 0)
#define RC_OBTAIN_INDEX_ENTRY(p) from_disked_rc_index_entry((struct rc_index_entry_ondisk *)(p), 0)
-#define RC_ACTUAL_OBJECT_ENTRY_SIZE(e) (sizeof(struct rc_object_entry_ondisk) + RC_PATH_SIZE((e)->merge_nr + (e)->split_nr) + (e)->size_size)
-#define RC_ENTRY_SIZE_OFFSET(e) (RC_ACTUAL_OBJECT_ENTRY_SIZE(e) - (e)->size_size)
+#define RC_ACTUAL_OBJECT_ENTRY_SIZE(e) (\
+ sizeof(struct rc_object_entry_ondisk) + \
+ RC_PATH_SIZE((e)->merge_nr + (e)->split_nr) + \
+ (e)->size_size + \
+ (e)->name_size\
+)
+#define RC_ENTRY_SIZE_OFFSET(e) (RC_ACTUAL_OBJECT_ENTRY_SIZE(e) - (e)->name_size - (e)->size_size)
+#define RC_ENTRY_NAME_OFFSET(e) (RC_ACTUAL_OBJECT_ENTRY_SIZE(e) - (e)->name_size)
/* single index maps objects to cache files */
struct rc_index_header {
@@ -50,6 +56,8 @@ struct rc_slice_header {
uint32_t size;
unsigned char sha1[20];
+
+ uint32_t name_size;
};
struct rc_object_entry_ondisk {
@@ -76,7 +84,8 @@ struct rc_object_entry {
unsigned char merge_nr; /* : 7 */
unsigned char split_nr; /* : 7 */
unsigned size_size : 3;
- unsigned padding : 5;
+ unsigned name_size : 3;
+ unsigned padding : 2;
uint32_t date;
uint16_t path;
@@ -84,6 +93,7 @@ struct rc_object_entry {
/* merge paths */
/* split paths */
/* size */
+ /* name id */
};
struct rc_index_entry *from_disked_rc_index_entry(struct rc_index_entry_ondisk *src, struct rc_index_entry *dst);
diff --git a/revision.h b/revision.h
index c3ec1b3..b2d5834 100644
--- a/revision.h
+++ b/revision.h
@@ -23,6 +23,9 @@ struct rev_cache_slice_map {
unsigned char *map;
int size;
int last_index;
+
+ char *names;
+ int name_size;
};
struct rev_cache_info {
@@ -36,7 +39,8 @@ struct rev_cache_info {
unsigned overwrite_all : 1;
/* traversal flags */
- unsigned add_to_pending : 1;
+ unsigned add_to_pending : 1,
+ add_names : 1;
/* fuse options */
unsigned int ignore_size;
diff --git a/t/t6015-rev-cache-list.sh b/t/t6015-rev-cache-list.sh
index fa6df21..ff36881 100755
--- a/t/t6015-rev-cache-list.sh
+++ b/t/t6015-rev-cache-list.sh
@@ -4,8 +4,8 @@ test_description='git rev-cache tests'
. ./test-lib.sh
test_cmp_sorted() {
- grep -io "[a-f0-9]*" $1 | sort >.tmpfile1 &&
- grep -io "[a-f0-9]*" $2 | sort >.tmpfile2 &&
+ sort $1 >.tmpfile1 &&
+ sort $2 >.tmpfile2 &&
test_cmp .tmpfile1 .tmpfile2
}
--
tg: (e2ef004..) t/revcache/names (depends on: t/revcache/docs)
^ permalink raw reply related
* Re: Continue git clone after interruption
From: Johannes Schindelin @ 2009-08-17 12:31 UTC (permalink / raw)
To: Tomasz Kontusz; +Cc: git
In-Reply-To: <1250509342.2885.13.camel@cf-48>
Hi,
On Mon, 17 Aug 2009, Tomasz Kontusz wrote:
> is anybody working on making it possible to continue git clone after
> interruption? It would be quite useful for people with bad internet
> connection (I was downloading a big repo lately, and it was a bit
> frustrating to start it over every time git stopped at ~90%).
Unfortunately, we did not have enough GSoC slots for the project to allow
restartable clones.
There were discussions about how to implement this on the list, though.
Ciao,
Dscho
^ permalink raw reply
* git propaganda poster
From: Christian Thaeter @ 2009-08-17 12:27 UTC (permalink / raw)
To: git
Does anyone of you know some artworkish git propaganda poster we may
show at FrOSCon next weekend? Or maybe does anyone have fun creating
such one?
Basically we (lumiera.org) want to show what tools we use and hang out
posters about these tools. The idea is that this shall be something
appealing which tells 'X is cool', not a refcard, not a slide from a
presentation but rather more artistic wakeing interest. Some git
propaganda would be a nice fit.
Christian
^ permalink raw reply
* Re: Tie a CVS-aware app into GIT?
From: david.hagood @ 2009-08-12 21:52 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: david.hagood, git
In-Reply-To: <200908122230.29363.robin.rosenberg.lists@dewire.com>
> What makes you there would be an issue here?
>
> -- robin
>
>
Mostly just paranoia, a lack of experience with it, and a general
brain-overload because of the bazillion other things I'm trying to work
through. I was hoping I might get good pointers from somebody who's
already done something like this.
^ permalink raw reply
* Re: [RFC PATCH v3 8/8] --sparse for porcelains
From: Nguyen Thai Ngoc Duy @ 2009-08-17 12:49 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Jakub Narebski, Junio C Hamano, git
In-Reply-To: <alpine.DEB.1.00.0908171101090.4991@intel-tinevez-2-302>
On Mon, Aug 17, 2009 at 4:08 PM, Johannes
Schindelin<Johannes.Schindelin@gmx.de> wrote:
> Turns out that somebody on IRC had a problem that requires to have
> sparse'd out files which _do_ have working directory copies.
>
> So just having the assume-changed bit may not be enough.
>
> The scenario is this: the repository contains a file that users are
> supposed to change, but not commit to (only the super-intelligent inventor
> of this scenario is allowed to). As this repository is originally a
> subversion one, there is no problem: people just do not switch branches.
>
> But this guy uses git-svn, so he does switch branches, and to avoid
> committing the file by mistake, he marked it assume-unchanged.
Hmm.. never thought of this use before. If he does not want to commit
by mistake, should he add to-be-committed changes to index and do "git
commit" without "-a" (even better, do "git diff --cached" first)?
> Only that a branch switch overwrites the local changes.
I don't think branch switch overwrites changes in this case. Whenever
Git is to touch worktree files, it ignores assumed-unchanged bit and
does lstat() to make sure worktree files are up to date.
> I suggested the use of the sparse feature, and mark this file (and this
> file alone) as sparse'd-out.
Sparse checkout only removes a file if its assume-unchanged bit
changes from 0 to 1. If it's already 1, it does not care whether there
is a corresponding file in worktree. So something like this should
work:
git checkout my-branch
git update-index --assume-unchanged that-special-file
echo that-special-file > .git/info/sparse
# edit that-special-file
git commit -a
# do whatever you want, git pull/checkout/read-tree... won't touch
that-special-file because it's assume-unchanged already
Too subtle?
Anyway I would not recommend this. the versions of that-special-file
in worktree and and in index will diverse. When you unmark
assume-unchanged (be it sparse checkout or plain assume-unchanged),
you may have already forgot what changes you made to this file and
"git diff" would not help.
> Is this an intended usage scenario? Then we cannot reuse the
> assume-changed bit [*1*].
It'd be great if people tell us all the scenarios they have. My use
could be too limited.
--
Duy
^ permalink raw reply
* Re: sparse support in pu
From: Peter Harris @ 2009-08-17 12:52 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Nguyen Thai Ngoc Duy, Johannes Sixt, skillzero, git
In-Reply-To: <alpine.DEB.1.00.0908171425410.4991@intel-tinevez-2-302>
On Mon, Aug 17, 2009 at 8:29 AM, Johannes Schindelin wrote:
> If I want to have a sparse checkout, I know which files I _want_.
That's funny. I have a git tree that would benefit from sparse
checkout. I know which path I _don't_ want. Specifying all the paths I
want would be a rather longer (and more error-prone) list. I suspect
it would be best to support both.
Does sparse use the same parser as .gitignore? (I guess not, if it
handles trailing slashes differently?) If so, it would be trivial to
turn "exclude path" into "exclude all but path" (or vice-versa) with:
*
!path
Peter Harris
^ permalink raw reply
* Re: sparse support in pu
From: Nguyen Thai Ngoc Duy @ 2009-08-17 12:53 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Johannes Sixt, skillzero, git
In-Reply-To: <alpine.DEB.1.00.0908171425410.4991@intel-tinevez-2-302>
On Mon, Aug 17, 2009 at 7:29 PM, Johannes
Schindelin<Johannes.Schindelin@gmx.de> wrote:
> Hi,
>
> On Mon, 17 Aug 2009, Nguyen Thai Ngoc Duy wrote:
>
>> On Mon, Aug 17, 2009 at 5:36 PM, Johannes Sixt<j.sixt@viscovery.net> wrote:
>>
>> > In order to advocate my earlier proposal: Name the file
>> > .git/info/phantoms, then it's clear: "The files mentioned here are
>> > phantoms" - they exist in the index, but not in the worktree; no
>> > phantoms means that everything is checked out.
>>
>> OK. Phantom checkout, must be unique in VCS world ;-) If no one objects
>> my next series will use this name as it's better than "sparse" and
>> "assume-unchanged" is just too vague. Would option names to
>> enable/disable this be --with[out]-phantoms?
>
> The term 'phantom' is not specified at all. At least interested people on
> the mailing list know 'sparse'. But I agree that the naming is a major
> problem, hence my earlier (unanswered) call.
>
> However, I would find specifying what you do _not_ want in that file
> rather unintuitive, in the same leage as receive.denyNonFastForwards = no.
But .gitignore is because I do _not_ want some files. Hey, how about
worktree-ignore? Turning it to positive matching instead of excluding
is not hard, but I have a feeling that people (OK, I) usually want to
ignore some directories. Applying trick:
*
!foo/
!bar/
to have foo and bar ignored too often would not be fun.
--
Duy
^ permalink raw reply
* Re: Using VC build git (split patch)
From: Frank Li @ 2009-08-17 13:13 UTC (permalink / raw)
To: git, msysGit; +Cc: Johannes Schindelin
[-- Attachment #1: Type: text/plain, Size: 246 bytes --]
Thanks Dscho for reviewing my patch patient.
I split my patch and read SumbittingPatchs.
I try to use git format-patch -M --stdout origin/master | git
imap-send to send patch directly according to guide.
But imap-send has not ported to msysgit.
[-- Attachment #2: 0011-Add-gitignore-and-ReadMe-file-for-MSVC-Build.patch --]
[-- Type: application/octet-stream, Size: 1319 bytes --]
From 2d5f52081d4d8916185ee2885cbf86604a9f54f3 Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Mon, 17 Aug 2009 15:00:45 +0800
Subject: [PATCH 11/11] Add gitignore and ReadMe file for MSVC Build
Add ReadMe to user how to build git with MSVC.
Add gitignore to filter out files created file by MSVC.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/vcbuild/.gitignore | 3 +++
compat/vcbuild/ReadMe | 13 +++++++++++++
2 files changed, 16 insertions(+), 0 deletions(-)
create mode 100644 compat/vcbuild/.gitignore
create mode 100644 compat/vcbuild/ReadMe
diff --git a/compat/vcbuild/.gitignore b/compat/vcbuild/.gitignore
new file mode 100644
index 0000000..7796990
--- /dev/null
+++ b/compat/vcbuild/.gitignore
@@ -0,0 +1,3 @@
+Debug
+Release
+*.user
\ No newline at end of file
diff --git a/compat/vcbuild/ReadMe b/compat/vcbuild/ReadMe
new file mode 100644
index 0000000..47c089e
--- /dev/null
+++ b/compat/vcbuild/ReadMe
@@ -0,0 +1,13 @@
+The Steps of Build Git with VS2008
+
+1. Create VC Build Environment.
+
+ git clone git://repo.or.cz/gitbuild.git
+
+ gitbuild include VS solution file and library such as zlib.
+
+2. Get Submodule
+
+ git submodule update
+
+3. Open gitbuild\gitbuild.sln with VS2008. Then press F7.
\ No newline at end of file
--
1.6.4.msysgit.0
[-- Attachment #3: 0001-Fix-build-failure-at-VC-because-function-declare-use.patch --]
[-- Type: application/octet-stream, Size: 1005 bytes --]
From ceb73fc1cd4c32d30c2fd534009c507cf18704fa Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Sun, 16 Aug 2009 13:15:18 +0800
Subject: [PATCH 01/11] Fix build failure at VC because function declare use old style at regex.c
regerror declare function argument type after function define.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/regex/regex.c | 6 +-----
1 files changed, 1 insertions(+), 5 deletions(-)
diff --git a/compat/regex/regex.c b/compat/regex/regex.c
index 5ea0075..5728de1 100644
--- a/compat/regex/regex.c
+++ b/compat/regex/regex.c
@@ -4852,11 +4852,7 @@ regexec (preg, string, nmatch, pmatch, eflags)
from either regcomp or regexec. We don't use PREG here. */
size_t
-regerror (errcode, preg, errbuf, errbuf_size)
- int errcode;
- const regex_t *preg;
- char *errbuf;
- size_t errbuf_size;
+regerror (int errcode, const regex_t * preg, char * errbuf,size_t errbuf_size)
{
const char *msg;
size_t msg_size;
--
1.6.4.msysgit.0
[-- Attachment #4: 0002-Fix-declare-variable-at-mid-of-function.patch --]
[-- Type: application/octet-stream, Size: 3156 bytes --]
From 753c6790df6eca740c2d708c19301d117fbbb085 Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Sun, 16 Aug 2009 13:34:03 +0800
Subject: [PATCH 02/11] Fix declare variable at mid of function
Some compiler such as MSVC can't support declear variable at mid of funtion at c file.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/mingw.c | 16 ++++++++++++----
help.c | 3 ++-
run-command.c | 2 ++
3 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/compat/mingw.c b/compat/mingw.c
index bed4178..75c74b1 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -123,13 +123,17 @@ int mingw_open (const char *filename, int oflags, ...)
{
va_list args;
unsigned mode;
+ int fd;
+
va_start(args, oflags);
mode = va_arg(args, int);
va_end(args);
if (!strcmp(filename, "/dev/null"))
filename = "nul";
- int fd = open(filename, oflags, mode);
+
+ fd = open(filename, oflags, mode);
+
if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
DWORD attrs = GetFileAttributes(filename);
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
@@ -580,10 +584,11 @@ static char **get_path_split(void)
static void free_path_split(char **path)
{
+ char **p = path;
+
if (!path)
return;
- char **p = path;
while (*p)
free(*p++);
free(path);
@@ -1108,9 +1113,11 @@ int sigaction(int sig, struct sigaction *in, struct sigaction *out)
#undef signal
sig_handler_t mingw_signal(int sig, sig_handler_t handler)
{
+ sig_handler_t old;
+
if (sig != SIGALRM)
return signal(sig, handler);
- sig_handler_t old = timer_fn;
+ old = timer_fn;
timer_fn = handler;
return old;
}
@@ -1197,8 +1204,9 @@ struct dirent *mingw_readdir(DIR *dir)
if (dir->dd_handle == (long)INVALID_HANDLE_VALUE && dir->dd_stat == 0)
{
+ DWORD lasterr;
handle = FindFirstFileA(dir->dd_name, &buf);
- DWORD lasterr = GetLastError();
+ lasterr = GetLastError();
dir->dd_handle = (long)handle;
if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
errno = err_win_to_posix(lasterr);
diff --git a/help.c b/help.c
index 6c46d8b..399b0b4 100644
--- a/help.c
+++ b/help.c
@@ -127,7 +127,7 @@ static int is_executable(const char *name)
return 0;
#ifdef __MINGW32__
- /* cannot trust the executable bit, peek into the file instead */
+{ /* cannot trust the executable bit, peek into the file instead */
char buf[3] = { 0 };
int n;
int fd = open(name, O_RDONLY);
@@ -140,6 +140,7 @@ static int is_executable(const char *name)
st.st_mode |= S_IXUSR;
close(fd);
}
+}
#endif
return st.st_mode & S_IXUSR;
}
diff --git a/run-command.c b/run-command.c
index ff3d8e2..d1df7ab 100644
--- a/run-command.c
+++ b/run-command.c
@@ -123,6 +123,7 @@ int start_command(struct child_process *cmd)
exit(127);
}
#else
+{
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
const char **sargv = cmd->argv;
char **env = environ;
@@ -186,6 +187,7 @@ int start_command(struct child_process *cmd)
dup2(s1, 1), close(s1);
if (s2 >= 0)
dup2(s2, 2), close(s2);
+}
#endif
if (cmd->pid < 0) {
--
1.6.4.msysgit.0
[-- Attachment #5: 0003-Define-SNPRINTF_SIZE_CORR-1-when-use-MSVC-build-git.patch --]
[-- Type: application/octet-stream, Size: 1157 bytes --]
From 7edc5a10d29ec735dc302d110945ad70bac3d83f Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Sun, 16 Aug 2009 13:53:30 +0800
Subject: [PATCH 03/11] Define SNPRINTF_SIZE_CORR 1 when use MSVC build git
There are not NUL at vsnprintf verstion of MSVC when rearch max len.
Define vsnprintf to _vsnprintf. vsnprintf have deprecated.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/snprintf.c | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/compat/snprintf.c b/compat/snprintf.c
index 6c0fb05..47b2b8a 100644
--- a/compat/snprintf.c
+++ b/compat/snprintf.c
@@ -6,7 +6,7 @@
* number of characters to write without the trailing NUL.
*/
#ifndef SNPRINTF_SIZE_CORR
-#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4
+#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4 || defined(_MSC_VER)
#define SNPRINTF_SIZE_CORR 1
#else
#define SNPRINTF_SIZE_CORR 0
@@ -14,6 +14,11 @@
#endif
#undef vsnprintf
+
+#if defined(_MSC_VER)
+#define vsnprintf _vsnprintf
+#endif
+
int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
{
char *s;
--
1.6.4.msysgit.0
[-- Attachment #6: 0004-Add-_MSC_VER-predefine-macro-to-make-same-behaviors-.patch --]
[-- Type: application/octet-stream, Size: 3402 bytes --]
From e82f2baea636dca70328e1e5161456bd61c21bec Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Sun, 16 Aug 2009 19:14:29 +0800
Subject: [PATCH 04/11] Add _MSC_VER predefine macro to make same behaviors with __MINGW32__
Enable MSVC build. MSVC have the save behaviors with msysgit.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
help.c | 2 +-
| 4 ++--
run-command.c | 8 ++++----
run-command.h | 2 +-
setup.c | 2 +-
5 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/help.c b/help.c
index 399b0b4..a311241 100644
--- a/help.c
+++ b/help.c
@@ -126,7 +126,7 @@ static int is_executable(const char *name)
!S_ISREG(st.st_mode))
return 0;
-#ifdef __MINGW32__
+#if defined(__MINGW32__) || defined(_MSC_VER)
{ /* cannot trust the executable bit, peek into the file instead */
char buf[3] = { 0 };
int n;
--git a/pager.c b/pager.c
index 4921843..28122c5 100644
--- a/pager.c
+++ b/pager.c
@@ -9,7 +9,7 @@
static int spawned_pager;
-#ifndef __MINGW32__
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
static void pager_preexec(void)
{
/*
@@ -70,7 +70,7 @@ void setup_pager(void)
pager_argv[2] = pager;
pager_process.argv = pager_argv;
pager_process.in = -1;
-#ifndef __MINGW32__
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
pager_process.preexec_cb = pager_preexec;
#endif
if (start_command(&pager_process))
diff --git a/run-command.c b/run-command.c
index d1df7ab..df139da 100644
--- a/run-command.c
+++ b/run-command.c
@@ -67,7 +67,7 @@ int start_command(struct child_process *cmd)
trace_argv_printf(cmd->argv, "trace: run_command:");
-#ifndef __MINGW32__
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
fflush(NULL);
cmd->pid = fork();
if (!cmd->pid) {
@@ -294,7 +294,7 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
return run_command(&cmd);
}
-#ifdef __MINGW32__
+#if defined(__MINGW32__) || defined(_MSC_VER)
static __stdcall unsigned run_thread(void *data)
{
struct async *async = data;
@@ -310,7 +310,7 @@ int start_async(struct async *async)
return error("cannot create pipe: %s", strerror(errno));
async->out = pipe_out[0];
-#ifndef __MINGW32__
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
/* Flush stdio before fork() to avoid cloning buffers */
fflush(NULL);
@@ -339,7 +339,7 @@ int start_async(struct async *async)
int finish_async(struct async *async)
{
-#ifndef __MINGW32__
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
int ret = 0;
if (wait_or_whine(async->pid))
diff --git a/run-command.h b/run-command.h
index e345502..57a707b 100644
--- a/run-command.h
+++ b/run-command.h
@@ -79,7 +79,7 @@ struct async {
int (*proc)(int fd, void *data);
void *data;
int out; /* caller reads from here and closes it */
-#ifndef __MINGW32__
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
pid_t pid;
#else
HANDLE tid;
diff --git a/setup.c b/setup.c
index e3781b6..14e3ca7 100644
--- a/setup.c
+++ b/setup.c
@@ -41,7 +41,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
{
static char path[PATH_MAX];
-#ifndef __MINGW32__
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
if (!pfx || !*pfx || is_absolute_path(arg))
return arg;
memcpy(path, pfx, pfx_len);
--
1.6.4.msysgit.0
[-- Attachment #7: 0005-Remove-va_copy-at-MSVC-because-there-are-va_copy.patch --]
[-- Type: application/octet-stream, Size: 1116 bytes --]
From 67a26d74abe9720b59c7cf56926b278f3c72a312 Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Sun, 16 Aug 2009 20:57:10 +0800
Subject: [PATCH 05/11] Remove va_copy at MSVC because there are va_copy.
MSVs have not implemented va_copy. remove va_copy at MSVC environment.
It will malloc buffer each time.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/winansi.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/compat/winansi.c b/compat/winansi.c
index 9217c24..6091138 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -3,7 +3,11 @@
*/
#include <windows.h>
+#ifdef _MSC_VER
+#include <stdio.h>
+#else
#include "../git-compat-util.h"
+#endif
/*
Functions to be wrapped:
@@ -310,9 +314,13 @@ static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
if (!console)
goto abort;
+#ifndef _MSC_VER
va_copy(cp, list);
len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
va_end(cp);
+#else
+ len= sizeof(small_buf) ;
+#endif
if (len > sizeof(small_buf) - 1) {
buf = malloc(len + 1);
--
1.6.4.msysgit.0
[-- Attachment #8: 0006-Add-miss-git-compat-util.h-at-regex.c-and-fnmatch.c.patch --]
[-- Type: application/octet-stream, Size: 1289 bytes --]
From 4f7ead893ef71510f5f0b96246692a049cd61d3b Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Sun, 16 Aug 2009 21:04:45 +0800
Subject: [PATCH 06/11] Add miss git-compat-util.h at regex.c and fnmatch.c
Add git-compat-util.h to enable build at MSVC environment
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/fnmatch/fnmatch.c | 4 ++++
compat/regex/regex.c | 4 ++++
2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c
index 14feac7..5cbd49c 100644
--- a/compat/fnmatch/fnmatch.c
+++ b/compat/fnmatch/fnmatch.c
@@ -16,6 +16,10 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
+#ifdef _MSC_VER
+#include "git-compat-util.h"
+#endif
+
#if HAVE_CONFIG_H
# include <config.h>
#endif
diff --git a/compat/regex/regex.c b/compat/regex/regex.c
index 5728de1..2298a3a 100644
--- a/compat/regex/regex.c
+++ b/compat/regex/regex.c
@@ -20,6 +20,10 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* AIX requires this to be the first thing in the file. */
+#ifdef _MSC_VER
+#include "git-compat-util.h"
+#endif
+
#if defined (_AIX) && !defined (REGEX_MALLOC)
#pragma alloca
#endif
--
1.6.4.msysgit.0
[-- Attachment #9: 0007-Add-O_BINARY-flag-to-open-flag-at-mingw.c.patch --]
[-- Type: application/octet-stream, Size: 1200 bytes --]
From ee80ee9f5f62eded25405a6158ded556216723f4 Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Mon, 17 Aug 2009 14:52:01 +0800
Subject: [PATCH 07/11] Add O_BINARY flag to open flag at mingw.c
Windows will convert CR\LF and union code at text mode.
Git doesn't like this. Add O_BINARY flag to open function
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/mingw.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/compat/mingw.c b/compat/mingw.c
index 75c74b1..d5fa0ed 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -132,7 +132,7 @@ int mingw_open (const char *filename, int oflags, ...)
if (!strcmp(filename, "/dev/null"))
filename = "nul";
- fd = open(filename, oflags, mode);
+ fd = open(filename, oflags | O_BINARY, mode);
if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
DWORD attrs = GetFileAttributes(filename);
@@ -278,7 +278,7 @@ int mkstemp(char *template)
char *filename = mktemp(template);
if (filename == NULL)
return -1;
- return open(filename, O_RDWR | O_CREAT, 0600);
+ return open(filename, O_RDWR | O_CREAT | O_BINARY, 0600);
}
int gettimeofday(struct timeval *tv, void *tz)
--
1.6.4.msysgit.0
[-- Attachment #10: 0008-Place-__stdcall-to-correct-position.patch --]
[-- Type: application/octet-stream, Size: 1864 bytes --]
From 7783d7e2d367588b4b9f8d2aa32e3135af7d12a0 Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Mon, 17 Aug 2009 14:54:31 +0800
Subject: [PATCH 08/11] Place __stdcall to correct position.
MSVC require __stdcall is between return value and function name.
ALL Win32 API definition is as TYPE WINAPI function name
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/mingw.c | 4 ++--
run-command.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/compat/mingw.c b/compat/mingw.c
index d5fa0ed..0c9c793 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1017,7 +1017,7 @@ static sig_handler_t timer_fn = SIG_DFL;
* length to call the signal handler.
*/
-static __stdcall unsigned ticktack(void *dummy)
+static unsigned __stdcall ticktack(void *dummy)
{
while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
if (timer_fn == SIG_DFL)
@@ -1146,7 +1146,7 @@ void mingw_open_html(const char *unixpath)
int link(const char *oldpath, const char *newpath)
{
- typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+ typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
static T create_hard_link = NULL;
if (!create_hard_link) {
create_hard_link = (T) GetProcAddress(
diff --git a/run-command.c b/run-command.c
index df139da..423b506 100644
--- a/run-command.c
+++ b/run-command.c
@@ -295,12 +295,12 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
}
#if defined(__MINGW32__) || defined(_MSC_VER)
-static __stdcall unsigned run_thread(void *data)
+static unsigned __stdcall run_thread(void *data)
{
struct async *async = data;
return async->proc(async->fd_for_proc, async->data);
}
-#endif
+#endif /* __MINGW32__ || _MSC_VER */
int start_async(struct async *async)
{
--
1.6.4.msysgit.0
[-- Attachment #11: 0009-Add-MSVC-porting-header-files.patch --]
[-- Type: application/octet-stream, Size: 11084 bytes --]
From 6add8b13e7b5266ba4b125e826e78bed5f72b7f7 Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Mon, 17 Aug 2009 14:56:26 +0800
Subject: [PATCH 09/11] Add MSVC porting header files.
Add unix head file, dirent.h, unistd.h and time.h
Add MSVC special porting head file msvc.h and msvc.c.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/msvc.c | 33 ++++++++++
compat/msvc.h | 95 +++++++++++++++++++++++++++++
compat/vcbuild/include/dirent.h | 127 +++++++++++++++++++++++++++++++++++++++
compat/vcbuild/include/unistd.h | 92 ++++++++++++++++++++++++++++
compat/vcbuild/sys/time.h | 21 +++++++
git-compat-util.h | 12 ++++-
6 files changed, 379 insertions(+), 1 deletions(-)
create mode 100644 compat/msvc.c
create mode 100644 compat/msvc.h
create mode 100644 compat/vcbuild/include/dirent.h
create mode 100644 compat/vcbuild/include/unistd.h
create mode 100644 compat/vcbuild/sys/time.h
diff --git a/compat/msvc.c b/compat/msvc.c
new file mode 100644
index 0000000..80afd4d
--- /dev/null
+++ b/compat/msvc.c
@@ -0,0 +1,33 @@
+#include "../git-compat-util.h"
+#include "win32.h"
+#include <conio.h>
+#include "../strbuf.h"
+
+DIR *opendir(const char *name)
+{
+ int len;
+ DIR *p;
+ p = (DIR*)malloc(sizeof(DIR));
+ memset(p, 0, sizeof(DIR));
+ strncpy(p->dd_name, name, PATH_MAX);
+ len = strlen(p->dd_name);
+ p->dd_name[len] = '/';
+ p->dd_name[len+1] = '*';
+
+ if (p == NULL)
+ return NULL;
+
+ p->dd_handle = _findfirst(p->dd_name, &p->dd_dta);
+
+ if (p->dd_handle == -1) {
+ free(p);
+ return NULL;
+ }
+ return p;
+}
+int closedir(DIR *dir)
+{
+ _findclose(dir->dd_handle);
+ free(dir);
+ return 0;
+}
diff --git a/compat/msvc.h b/compat/msvc.h
new file mode 100644
index 0000000..6071565
--- /dev/null
+++ b/compat/msvc.h
@@ -0,0 +1,95 @@
+#ifndef __MSVC__HEAD
+#define __MSVC__HEAD
+
+#define WINVER 0x0500
+#define _WIN32_WINNT 0x0500
+#define _WIN32_WINDOWS 0x0410
+#define _WIN32_IE 0x0700
+#define NTDDI_VERSION NTDDI_WIN2KSP1
+#include <winsock2.h>
+
+/*Configuration*/
+
+#define NO_PREAD
+#define NO_OPENSSL
+#define NO_LIBGEN_H
+#define NO_SYMLINK_HEAD
+#define NO_IPV6
+#define NO_SETENV
+#define NO_UNSETENV
+#define NO_STRCASESTR
+#define NO_STRLCPY
+#define NO_MEMMEM
+#define NO_C99_FORMAT
+#define NO_STRTOUMAX
+#define NO_MKDTEMP
+#define NO_MKSTEMPS
+
+#define RUNTIME_PREFIX
+#define NO_ST_BLOCKS_IN_STRUCT_STAT
+#define NO_NSEC
+#define USE_WIN32_MMAP
+#define USE_NED_ALLOCATOR
+
+#define NO_REGEX
+
+#define NO_SYS_SELECT_H
+#define NO_PTHEADS
+#define HAVE_STRING_H 1
+#define STDC_HEADERS
+#define NO_ICONV
+
+#define inline __inline
+#define __inline__ __inline
+
+#define SNPRINTF_RETURNS_BOGUS
+
+#define SHA1_HEADER "mozilla-sha1\\sha1.h"
+
+#define ETC_GITCONFIG "%HOME%"
+
+#define NO_PTHREADS
+#define NO_CURL
+
+
+#define NO_STRTOUMAX
+#define REGEX_MALLOC
+
+
+#define GIT_EXEC_PATH "bin"
+#define GIT_VERSION "1.6"
+#define BINDIR "bin"
+#define PREFIX "."
+#define GIT_MAN_PATH "man"
+#define GIT_INFO_PATH "info"
+#define GIT_HTML_PATH "html"
+#define DEFAULT_GIT_TEMPLATE_DIR "templates"
+
+#define NO_STRLCPY
+#define NO_UNSETENV
+#define NO_SETENV
+
+#define strdup _strdup
+#define read _read
+#define close _close
+#define dup _dup
+#define dup2 _dup2
+#define strncasecmp _strnicmp
+#define strtoull _strtoui64
+
+#define __attribute__(x)
+
+static __inline int strcasecmp (const char *s1, const char *s2)
+{
+ int size1=strlen(s1);
+ int sisz2=strlen(s2);
+
+ return _strnicmp(s1,s2,sisz2>size1?sisz2:size1);
+}
+
+#include "compat/mingw.h"
+#undef ERROR
+#undef stat
+#define stat(x,y) mingw_lstat
+#define stat _stat64
+#endif
\ No newline at end of file
diff --git a/compat/vcbuild/include/dirent.h b/compat/vcbuild/include/dirent.h
new file mode 100644
index 0000000..a6b6f4c
--- /dev/null
+++ b/compat/vcbuild/include/dirent.h
@@ -0,0 +1,127 @@
+/*
+ * DIRENT.H (formerly DIRLIB.H)
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is a part of the mingw-runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within the package.
+ *
+ */
+#ifndef _DIRENT_H_
+#define _DIRENT_H_
+
+/* All the headers include this file. */
+//#include <_mingw.h>
+
+#include <io.h>
+
+#define PATH_MAX 512
+
+#define __MINGW_NOTHROW
+
+#ifndef RC_INVOKED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dirent
+{
+ long d_ino; /* Always zero. */
+ unsigned short d_reclen; /* Always zero. */
+ unsigned short d_namlen; /* Length of name in d_name. */
+ char d_name[FILENAME_MAX]; /* File name. */
+};
+
+/*
+ * This is an internal data structure. Good programmers will not use it
+ * except as an argument to one of the functions below.
+ * dd_stat field is now int (was short in older versions).
+ */
+typedef struct
+{
+ /* disk transfer area for this dir */
+ struct _finddata_t dd_dta;
+
+ /* dirent struct to return from dir (NOTE: this makes this thread
+ * safe as long as only one thread uses a particular DIR struct at
+ * a time) */
+ struct dirent dd_dir;
+
+ /* _findnext handle */
+ long dd_handle;
+
+ /*
+ * Status of search:
+ * 0 = not started yet (next entry to read is first entry)
+ * -1 = off the end
+ * positive = 0 based index of next entry
+ */
+ int dd_stat;
+
+ /* given path for dir with search pattern (struct is extended) */
+ char dd_name[PATH_MAX+3];
+} DIR;
+
+DIR* __cdecl __MINGW_NOTHROW opendir (const char*);
+struct dirent* __cdecl __MINGW_NOTHROW readdir (DIR*);
+int __cdecl __MINGW_NOTHROW closedir (DIR*);
+void __cdecl __MINGW_NOTHROW rewinddir (DIR*);
+long __cdecl __MINGW_NOTHROW telldir (DIR*);
+void __cdecl __MINGW_NOTHROW seekdir (DIR*, long);
+
+
+/* wide char versions */
+
+struct _wdirent
+{
+ long d_ino; /* Always zero. */
+ unsigned short d_reclen; /* Always zero. */
+ unsigned short d_namlen; /* Length of name in d_name. */
+ wchar_t d_name[FILENAME_MAX]; /* File name. */
+};
+
+/*
+ * This is an internal data structure. Good programmers will not use it
+ * except as an argument to one of the functions below.
+ */
+typedef struct
+{
+ /* disk transfer area for this dir */
+ //struct _wfinddata_t dd_dta;
+
+ /* dirent struct to return from dir (NOTE: this makes this thread
+ * safe as long as only one thread uses a particular DIR struct at
+ * a time) */
+ struct _wdirent dd_dir;
+
+ /* _findnext handle */
+ long dd_handle;
+
+ /*
+ * Status of search:
+ * 0 = not started yet (next entry to read is first entry)
+ * -1 = off the end
+ * positive = 0 based index of next entry
+ */
+ int dd_stat;
+
+ /* given path for dir with search pattern (struct is extended) */
+ wchar_t dd_name[1];
+} _WDIR;
+
+
+
+_WDIR* __cdecl __MINGW_NOTHROW _wopendir (const wchar_t*);
+struct _wdirent* __cdecl __MINGW_NOTHROW _wreaddir (_WDIR*);
+int __cdecl __MINGW_NOTHROW _wclosedir (_WDIR*);
+void __cdecl __MINGW_NOTHROW _wrewinddir (_WDIR*);
+long __cdecl __MINGW_NOTHROW _wtelldir (_WDIR*);
+void __cdecl __MINGW_NOTHROW _wseekdir (_WDIR*, long);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* Not RC_INVOKED */
+
+#endif /* Not _DIRENT_H_ */
diff --git a/compat/vcbuild/include/unistd.h b/compat/vcbuild/include/unistd.h
new file mode 100644
index 0000000..a0f59e6
--- /dev/null
+++ b/compat/vcbuild/include/unistd.h
@@ -0,0 +1,92 @@
+#ifndef _UNISTD_
+#define _UNISTD_
+
+/* Win32 define for porting git*/
+
+#ifndef _MODE_T_
+#define _MODE_T_
+typedef unsigned short _mode_t;
+
+#ifndef _NO_OLDNAMES
+typedef _mode_t mode_t;
+#endif
+#endif /* Not _MODE_T_ */
+
+#ifndef _SSIZE_T_
+#define _SSIZE_T_
+typedef long _ssize_t;
+
+#ifndef _OFF_T_
+#define _OFF_T_
+typedef long _off_t;
+
+#ifndef _NO_OLDNAMES
+typedef _off_t off_t;
+#endif
+#endif /* Not _OFF_T_ */
+
+
+#ifndef _NO_OLDNAMES
+typedef _ssize_t ssize_t;
+#endif
+#endif /* Not _SSIZE_T_ */
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned uint32_t;
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+
+typedef long long intmax_t;
+typedef unsigned long long uintmax_t;
+
+typedef int64_t off64_t;
+
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+/* Some defines for _access nAccessMode (MS doesn't define them, but
+ * it doesn't seem to hurt to add them). */
+#define F_OK 0 /* Check for file existence */
+/* Well maybe it does hurt. On newer versions of MSVCRT, an access mode
+ of 1 causes invalid parameter error. */
+#define X_OK 0 /* MS access() doesn't check for execute permission. */
+#define W_OK 2 /* Check for write permission */
+#define R_OK 4 /* Check for read permission */
+
+#define _S_IFIFO 0x1000 /* FIFO */
+#define _S_IFCHR 0x2000 /* Character */
+#define _S_IFBLK 0x3000 /* Block: Is this ever set under w32? */
+#define _S_IFDIR 0x4000 /* Directory */
+#define _S_IFREG 0x8000 /* Regular */
+
+#define _S_IFMT 0xF000 /* File type mask */
+
+#define _S_IXUSR _S_IEXEC
+#define _S_IWUSR _S_IWRITE
+#define _S_IRUSR _S_IREAD
+#define _S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+
+#define S_IFIFO _S_IFIFO
+#define S_IFCHR _S_IFCHR
+#define S_IFBLK _S_IFBLK
+#define S_IFDIR _S_IFDIR
+#define S_IFREG _S_IFREG
+#define S_IFMT _S_IFMT
+#define S_IEXEC _S_IEXEC
+#define S_IWRITE _S_IWRITE
+#define S_IREAD _S_IREAD
+#define S_IRWXU _S_IRWXU
+#define S_IXUSR _S_IXUSR
+#define S_IWUSR _S_IWUSR
+#define S_IRUSR _S_IRUSR
+
+
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+
+#endif
\ No newline at end of file
diff --git a/compat/vcbuild/sys/time.h b/compat/vcbuild/sys/time.h
new file mode 100644
index 0000000..6ed82c0
--- /dev/null
+++ b/compat/vcbuild/sys/time.h
@@ -0,0 +1,21 @@
+#ifndef _UTIME_H_
+#define _UTIME_H_
+/*
+ * Structure used by _utime function.
+ */
+struct _utimbuf
+{
+ time_t actime; /* Access time */
+ time_t modtime; /* Modification time */
+};
+
+#ifndef _NO_OLDNAMES
+/* NOTE: Must be the same as _utimbuf above. */
+struct utimbuf
+{
+ time_t actime;
+ time_t modtime;
+};
+#endif /* Not _NO_OLDNAMES */
+
+#endif
diff --git a/git-compat-util.h b/git-compat-util.h
index 9f941e4..cb89294 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -77,8 +77,10 @@
#include <string.h>
#include <errno.h>
#include <limits.h>
+#ifndef _MSC_VER
#include <sys/param.h>
#include <sys/types.h>
+#endif
#include <dirent.h>
#include <sys/time.h>
#include <time.h>
@@ -86,7 +88,12 @@
#include <fnmatch.h>
#include <assert.h>
#include <regex.h>
+
+#ifndef _MSC_VER
#include <utime.h>
+#endif
+
+#ifndef _MSC_VER
#ifndef __MINGW32__
#include <sys/wait.h>
#include <sys/poll.h>
@@ -114,7 +121,10 @@
#else /* __MINGW32__ */
/* pull in Windows compatibility stuff */
#include "compat/mingw.h"
-#endif /* __MINGW32__ */
+#endif /* __MINGW32__ */
+#else /* _MSC_VER */
+#include "compat/msvc.h"
+#endif /* _MSC_VER */
#ifndef NO_LIBGEN_H
#include <libgen.h>
--
1.6.4.msysgit.0
[-- Attachment #12: 0010-Add-MSVC-Project-file.patch --]
[-- Type: application/octet-stream, Size: 30219 bytes --]
From 84c6da8e081b453c2ae64b1481ab385df5904c95 Mon Sep 17 00:00:00 2001
From: Frank Li <lznuaa@gmail.com>
Date: Mon, 17 Aug 2009 14:59:46 +0800
Subject: [PATCH 10/11] Add MSVC Project file
Add libgit.vcproj to build common library.
Add git.vcproj to build git program.
Signed-off-by: Frank Li <lznuaa@gmail.com>
---
compat/vcbuild/git/git.vcproj | 197 +++++
compat/vcbuild/libgit/libgit.vcproj | 1347 +++++++++++++++++++++++++++++++++++
2 files changed, 1544 insertions(+), 0 deletions(-)
create mode 100644 compat/vcbuild/git/git.vcproj
create mode 100644 compat/vcbuild/libgit/libgit.vcproj
diff --git a/compat/vcbuild/git/git.vcproj b/compat/vcbuild/git/git.vcproj
new file mode 100644
index 0000000..6f85de3
--- /dev/null
+++ b/compat/vcbuild/git/git.vcproj
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="gb2312"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="git"
+ ProjectGUID="{E3E30E51-C5AD-407B-AB43-985E4111474A}"
+ RootNamespace="git"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..;..\..\..\..\zlib;..\..;..\;..\include;..\..\..\compat;..\..\..\compat\fnmatch;..\..\..\compat\regex;.\"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wininet.lib ws2_32.lib "
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\..\..;..\..\..\..\zlib;..\..;..\;..\include;..\..\..\compat;..\..\..\compat\fnmatch;..\..\..\compat\regex;.\"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wininet.lib ws2_32.lib "
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\git.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/compat/vcbuild/libgit/libgit.vcproj b/compat/vcbuild/libgit/libgit.vcproj
new file mode 100644
index 0000000..bbc3aed
--- /dev/null
+++ b/compat/vcbuild/libgit/libgit.vcproj
@@ -0,0 +1,1347 @@
+<?xml version="1.0" encoding="gb2312"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="libgit"
+ ProjectGUID="{F6DEC8C3-B803-4A86-8848-430F08B499E3}"
+ RootNamespace="libgit"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="..\..\..;..\..\..\..\zlib;..\..;..\;..\include;..\..\..\compat;..\..\..\compat\fnmatch;..\..\..\compat\regex;.\"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\..\..;..\..\..\..\zlib;..\..;..\;..\include;..\..\..\compat;..\..\..\compat\fnmatch;..\..\..\compat\regex;.\"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\msvc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\mozilla-sha1\sha1.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\..\..\archive.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\attr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\blob.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\branch.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\bundle.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\cache-tree.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\cache.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\color.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\commit.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\csum-file.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\decorate.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\delta.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diff.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diffcore.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\dir.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\exec_cmd.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\fetch-pack.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\fsck.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\git-compat-util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\graph.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\grep.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\hash.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\help.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\http.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\levenshtein.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\list-objects.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\ll-merge.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\log-tree.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\mailmap.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\merge-recursive.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\notes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\object.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pack-refs.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pack-revindex.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pack.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\parse-options.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\patch-ids.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pkt-line.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\progress.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\quote.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\reachable.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\reflog-walk.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\refs.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\remote.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\rerere.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\revision.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\run-command.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\send-pack.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sha1-lookup.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\shortlog.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sideband.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sigchain.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\strbuf.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\string-list.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tag.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tar.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\thread-utils.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\transport.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tree-walk.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tree.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\unpack-trees.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\userdiff.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\utf8.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\walker.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\wt-status.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff-interface.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <Filter
+ Name="compat"
+ >
+ <File
+ RelativePath="..\..\..\compat\basename.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\cygwin.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\fnmatch\fnmatch.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\fnmatch\fnmatch.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\fopen.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\memmem.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\mingw.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\mingw.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\mkdtemp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\mkstemps.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\pread.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\qsort.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\regex\regex.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\regex\regex.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\setenv.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\snprintf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\strcasestr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\strlcpy.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\strtoumax.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\unsetenv.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\win32.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\win32mmap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\compat\winansi.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="git"
+ >
+ <File
+ RelativePath="..\..\..\abspath.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\alias.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\alloc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\archive-tar.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\archive-zip.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\archive.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\attr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\base85.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\bisect.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\blob.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\branch.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-add.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-annotate.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-apply.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-archive.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-bisect--helper.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-blame.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-branch.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-bundle.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-cat-file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-check-attr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-check-ref-format.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-checkout-index.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-checkout.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-clean.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-clone.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-commit-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-commit.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-count-objects.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-describe.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-diff-files.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-diff-index.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-diff-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-diff.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-fast-export.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-fetch--tool.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-fetch-pack.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-fetch.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-fmt-merge-msg.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-for-each-ref.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-fsck.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-gc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-grep.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-help.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-init-db.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-log.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-ls-files.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-ls-remote.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-ls-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-mailinfo.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-mailsplit.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-merge-base.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-merge-file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-merge-ours.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-merge-recursive.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-merge.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-mktree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-mv.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-name-rev.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-pack-objects.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-pack-refs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-prune-packed.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-prune.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-push.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-read-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-receive-pack.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-reflog.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-remote.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-rerere.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-reset.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-rev-list.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-rev-parse.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-revert.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-rm.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-send-pack.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-shortlog.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-show-branch.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-show-ref.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-stripspace.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-symbolic-ref.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-tag.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-tar-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-unpack-objects.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-update-index.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-update-ref.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-upload-archive.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-verify-pack.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-verify-tag.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\builtin-write-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\bundle.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\cache-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\color.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\combine-diff.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\commit.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\connect.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\convert.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\copy.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\csum-file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\ctype.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\date.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\decorate.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diff-delta.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diff-lib.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diff-no-index.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diff.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diffcore-break.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diffcore-delta.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diffcore-order.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diffcore-pickaxe.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\diffcore-rename.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\dir.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\editor.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\entry.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\environment.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\exec_cmd.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\fsck.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\graph.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\grep.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\hash.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\help.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\ident.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\levenshtein.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\list-objects.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\ll-merge.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\lockfile.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\log-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\mailmap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\match-trees.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\merge-file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\merge-recursive.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\merge-tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\name-hash.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\object.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pack-check.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pack-refs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pack-revindex.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pack-write.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pager.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\parse-options.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\patch-delta.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\patch-ids.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\path.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pkt-line.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\preload-index.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\pretty.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\progress.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\quote.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\reachable.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\read-cache.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\reflog-walk.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\refs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\remote.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\rerere.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\revision.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\run-command.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\server-info.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\setup.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sha1-lookup.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sha1_file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sha1_name.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\shallow.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sideband.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\sigchain.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\strbuf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\string-list.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\symlinks.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tag.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\thread-utils.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\trace.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\transport.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tree-diff.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tree-walk.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\tree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\unpack-trees.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\usage.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\userdiff.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\utf8.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\walker.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\wrapper.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\write_or_die.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\ws.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\wt-status.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff-interface.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="xdiff"
+ >
+ <File
+ RelativePath="..\..\..\xdiff\xdiff.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xdiffi.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xdiffi.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xemit.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xemit.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xinclude.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xmacros.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xmerge.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xpatience.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xprepare.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xprepare.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xtypes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xutils.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\xdiff\xutils.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
--
1.6.4.msysgit.0
^ permalink raw reply related
* Re: Using VC build git (split patch)
From: Erik Faye-Lund @ 2009-08-17 13:19 UTC (permalink / raw)
To: Frank Li; +Cc: git, msysGit, Johannes Schindelin
In-Reply-To: <1976ea660908170613ibb9a0fdr7ba630671a6b735f@mail.gmail.com>
On Mon, Aug 17, 2009 at 3:13 PM, Frank Li<lznuaa@gmail.com> wrote:
> I try to use git format-patch -M --stdout origin/master | git
> imap-send to send patch directly according to guide.
> But imap-send has not ported to msysgit.
Then use send-email, which IS supported in msysgit.
While we're on that subject; I guess we should add some documentation
(a wiki page or something?) on how to use it, as some (simple) setup
on the client side is required for msmtp.
--
Erik "kusma" Faye-Lund
kusmabite@gmail.com
(+47) 986 59 656
^ permalink raw reply
* Re: sparse support in pu
From: Johannes Schindelin @ 2009-08-17 13:23 UTC (permalink / raw)
To: Peter Harris; +Cc: Nguyen Thai Ngoc Duy, Johannes Sixt, skillzero, git
In-Reply-To: <eaa105840908170552m3eaf0f92j523ddad98dd67a3@mail.gmail.com>
Hi,
On Mon, 17 Aug 2009, Peter Harris wrote:
> On Mon, Aug 17, 2009 at 8:29 AM, Johannes Schindelin wrote:
> > If I want to have a sparse checkout, I know which files I _want_.
>
> That's funny. I have a git tree that would benefit from sparse checkout.
> I know which path I _don't_ want. Specifying all the paths I want would
> be a rather longer (and more error-prone) list. I suspect it would be
> best to support both.
Yes, I agree, but the common case is for people to know what they are
working on, right?
> Does sparse use the same parser as .gitignore? (I guess not, if it
> handles trailing slashes differently?) If so, it would be trivial to
> turn "exclude path" into "exclude all but path" (or vice-versa) with:
>
> *
> !path
That was the idea behind my suggestion to allow .gitignore syntax. And
indeed, that is what happened.
Ciao,
Dscho
^ permalink raw reply
* Re: [RFC PATCH v3 8/8] --sparse for porcelains
From: Johannes Schindelin @ 2009-08-17 13:35 UTC (permalink / raw)
To: Nguyen Thai Ngoc Duy; +Cc: Jakub Narebski, Junio C Hamano, git
In-Reply-To: <fcaeb9bf0908170549w26b008bdhe67f113a58ecb4eb@mail.gmail.com>
[-- Attachment #1: Type: TEXT/PLAIN, Size: 3893 bytes --]
Hi,
On Mon, 17 Aug 2009, Nguyen Thai Ngoc Duy wrote:
> On Mon, Aug 17, 2009 at 4:08 PM, Johannes
> Schindelin<Johannes.Schindelin@gmx.de> wrote:
> > Turns out that somebody on IRC had a problem that requires to have
> > sparse'd out files which _do_ have working directory copies.
> >
> > So just having the assume-changed bit may not be enough.
> >
> > The scenario is this: the repository contains a file that users are
> > supposed to change, but not commit to (only the super-intelligent
> > inventor of this scenario is allowed to). As this repository is
> > originally a subversion one, there is no problem: people just do not
> > switch branches.
> >
> > But this guy uses git-svn, so he does switch branches, and to avoid
> > committing the file by mistake, he marked it assume-unchanged.
>
> Hmm.. never thought of this use before. If he does not want to commit by
> mistake, should he add to-be-committed changes to index and do "git
> commit" without "-a" (even better, do "git diff --cached" first)?
You probably agree that this would be a _very_ fragile setup. Very easy
to make mistakes.
But we try to get away from that, don't we? Git had a reputation to be
easy fsck up for long enough.
> > Only that a branch switch overwrites the local changes.
>
> I don't think branch switch overwrites changes in this case. Whenever
> Git is to touch worktree files, it ignores assumed-unchanged bit and
> does lstat() to make sure worktree files are up to date.
Well, it does there, thankyouverymuch.
The problem of course is that the other branch has an ancient version of
that file (which should _not_ overwrite the current, modified version!),
i.e. "git diff HEAD..other -- file" does not come empty.
As 'file' is assume-unchanged, zinnnng, the file gets "updated".
> > I suggested the use of the sparse feature, and mark this file (and
> > this file alone) as sparse'd-out.
>
> Sparse checkout only removes a file if its assume-unchanged bit
> changes from 0 to 1.
The problem is not removing, but overwriting.
And in this respect, 'assume-unchanged' is a very different beast from
'sparse'. I am growing more and more convinced that you cannot just reuse
the assume-unchanged bit.
> If it's already 1, it does not care whether there is a corresponding
> file in worktree. So something like this should work:
>
> git checkout my-branch
> git update-index --assume-unchanged that-special-file
> echo that-special-file > .git/info/sparse
> # edit that-special-file
> git commit -a
> # do whatever you want, git pull/checkout/read-tree... won't touch
> that-special-file because it's assume-unchanged already
... except if you changed .git/info/sparse and a formerly sparse'd-out
file is overwritten by "pull". Not good.
> Anyway I would not recommend this. the versions of that-special-file in
> worktree and and in index will diverse. When you unmark assume-unchanged
> (be it sparse checkout or plain assume-unchanged), you may have already
> forgot what changes you made to this file and "git diff" would not help.
My point is that we should take the current implementation as Dictated By
The Dear Lord, but change it if the limitation is too severe.
And I do contend that 'assume-unchanged' is dissimilar enough from
'sparse' to merit a change.
> > Is this an intended usage scenario? Then we cannot reuse the
> > assume-changed bit [*1*].
>
> It'd be great if people tell us all the scenarios they have. My use
> could be too limited.
The use case I would have is where a collaborator wants to work only on
one subdirectory and the top-level directory. All other subdirectories
are of no interest to him.
Another use case: documentation. I do not have that use case yet, but I
know about people who do. Specifying what you _want_ to have checked out
is much more straight-forward here than the opposite.
Ciao,
Dscho
^ permalink raw reply
* Re: Using VC build git (split patch)
From: Johannes Schindelin @ 2009-08-17 13:43 UTC (permalink / raw)
To: Erik Faye-Lund; +Cc: Frank Li, git, msysGit
In-Reply-To: <40aa078e0908170619r3d325e0csee466446df474302@mail.gmail.com>
Hi,
On Mon, 17 Aug 2009, Erik Faye-Lund wrote:
> On Mon, Aug 17, 2009 at 3:13 PM, Frank Li<lznuaa@gmail.com> wrote:
> > I try to use git format-patch -M --stdout origin/master | git
> > imap-send to send patch directly according to guide.
> > But imap-send has not ported to msysgit.
>
> Then use send-email, which IS supported in msysgit.
Yes, I would appreciate inline patches. Otherwise it is pretty awkward
and time-consuming to quote your patch when I comment on it.
Ciao,
Dscho
^ permalink raw reply
* git clone http://git.savannah.gnu.org/cgit/xboard.git segfaults
From: Ali Polatel @ 2009-08-17 13:56 UTC (permalink / raw)
To: git
[-- Attachment #1: Type: text/plain, Size: 6516 bytes --]
$subject.
git version 1.6.4
Here's what gdb has to say about it:
2456 alip@harikalardiyari> gdb --args git clone http://git.savannah.gnu.org/cgit/xboard.git
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu"...
(gdb) run
Starting program: /usr/bin/git clone http://git.savannah.gnu.org/cgit/xboard.git
[Thread debugging using libthread_db enabled]
Initialized empty Git repository in /usr/src/alip/xboard/.git/
[New Thread 0x7f382f0566f0 (LWP 28121)]
got 6db08230fafeb8a5fd163689e0225608bac64169
walk 6db08230fafeb8a5fd163689e0225608bac64169
Getting alternates list for http://git.savannah.gnu.org/cgit/xboard.git
got 94a064d8ab2c5e91d542015dd7307924b20ce729
got a72effb29a1f88097fa34240d35380128c3c84c8
got 6cab23ba23301b578d4995f3c889e574ea4b9e99
Getting pack list for http://git.savannah.gnu.org/cgit/xboard.git
Getting index for pack 720d634dfc5e7511332c1e6851f7c5c5f88e7af2
Getting index for pack 06483273097cbac210f10a4bd43324ae660053e6
Getting index for pack a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6
Getting pack 720d634dfc5e7511332c1e6851f7c5c5f88e7af2
which contains a91f883e599185408c2e868458fc870b45f90647
walk a91f883e599185408c2e868458fc870b45f90647
walk 94a064d8ab2c5e91d542015dd7307924b20ce729
walk 6cab23ba23301b578d4995f3c889e574ea4b9e99
got ceb25797d2bdef03233071092e73d7cdf750a63d
got 00f19ab354710c1f41deba65b92af335317b25e5
walk 00f19ab354710c1f41deba65b92af335317b25e5
got 5195908348d5a926726de5d5447a27febd58b0c5
got c7646fb352bb0f7a286e991d893604190d6144bb
got b24e950f7c4105d99cb16959934231ee041e2b4e
Getting pack a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6
which contains 94a9ed024d3859793618152ea559a168bbcbb5e2
got b72e3e511a5aecf63c36ec720d819babc4a32e80
got 96872f2a88d025fb298254d7215b0a2a46ab2517
got 0a785501c2a0af15b29498dc5304d4f78e464724
got 41c83fb406c79f7482de0a63b3a41d7cdc5d9930
got 8f5adeee096e8325a77f4d0f1198c6de3e5c8c4d
got eb8df7d39eaa78cbba8ea6d43e48a9d5565f85c4
got af0c6fb67a21096bde685ebbb442089e158eaab7
got 81d056612386f3bfbb2d954b1776b385b77b0354
got 2ec8546ca81919b50458f2dfee0fb04deb909732
got fd3b1e187f4711df4b2b9be7a538e5e7a6a9d974
got 15bb2bd5db019f4b5873ff47a81876bf744fe3b7
got d0f226bee1b67eecf307cc625925a0aa6b7543ec
got 266e77e80239db82c6c9c74190b172bb4eb9aa2d
got a546658f7c58b1f72e13d038efc77347060ddcb6
got 87f71b2afc316dc18ed39ec5e0eba041b9bc850e
got c89673fe13e92003dd53fc590b682f4bae229a0f
got 71d706e2cd500e15b0b3fcccaba5530a85173759
got a0978a0f361032a670dc644752dae210a2b3d1e8
got 17463be1dd790b3c8e64eaae95135add449e82db
got 8b401ae12b0e72fe9b03ea1b08b2e4cb89378a8d
got 6cdb7dfebf13e58ce8bbca0a2890057694eee073
got f997ac9797b7427a2c3e3abf8dd95bc2df2bf028
got 7d80cb4c3eb366e28f542b095f958289b4a719e3
got 40fb9ce5458e0df6f33160d7392a8b56aa0e1501
got f945dbf2bcef821a53120501b0a06fc0388a2d5c
got c7b87e2a937e0e83a6f6347649e38c1e4636c14a
got b5b9c2fdb4f3964777c3a3f2f92d2784430feddd
got 0d4b26f7ca8c3108d839e9e339adecb8d1395aad
got b2fcec6a8916695ecb5f1007db2e7fc504d23d05
got 82957eb41b19cd497b761c42cebb4580bddd6baf
got 394a9fdac5b251114074c8bea117c5676fd7caa5
got 9c345f61dd8eaf6c5fb45cc90bb133227609608a
got f02507f1df2b2fc9e999d89b2003ef80b92fae8b
got 5806d53acedf3633d17151c1dd3d0f7316dc3264
got 460073895fc709ae6844800bbe61300bf51a0154
got d4c3dbec93edfd27c52775178ba11242ded59193
got e777067113c5726d1706f266de057aa7361dbec5
got 46e2b200ccb840dfab62258434570c840ebebfa1
got f020f22a9ab9e88c6585d8b204aa8ce016a291e5
got 02c9ec3643c6eb94212be431c72a11f03b4e5b1a
got 0657847b19f925d8af5f450c78dda46f8ae5f7d0
got 05af22bd8201f75fb9718382a3307eda68d5e976
got 427cd7d5dfc211ccd962a18bc9666c72a7966b79
got 77d5bf31108026eb97c6b8e288c9d6d092b767dd
got 22737c1f642d614a14045c72a648bfb0fbfbf9da
got b95f2d1475ce464e507d4dbaf2e3af8f6b6f9a48
got f718239d651c2476ff8b7abff7940d2054f44e4f
got ba30dcb13a69fd3b82a10da5456e47b448d4bcc9
got 83d0f555a7dad1d410761ec89273d795f038860c
got 562609284c1f98090bd14dfddbbe2a4991d1e5a8
got d9a98ef891407dd11863ff5e2553ff79aa24d32e
got 0ae04cca10cea96e05c7d4c261aa5d3b208a0b47
got db9e377a69a3d6bd4a8256d00dca3bee680a6a2a
got 1e1e01c9481a519faffbaf6c645dfa8017cabdb5
got 6cd5f80a6aa0390d2412db7d49427ce845d7c946
got b5381c9bfd883c7a84167b022a64c5ca0385930d
got cd0415a4adb0af252cfdf588323a5814a249e90c
got 2a574b7641b9c0f6414b9943695391b6b8252a54
got 5d662edd417ae9f858cb374e03f09a7639b2c03d
got 2bae494f2cf9486b771c706a1eec6217e46ce778
got 8f84481839d7eeb3e0b0da0088b3093742b546cd
got a6d954d86827b3d3777c186d836f4b533602e567
walk 83d0f555a7dad1d410761ec89273d795f038860c
walk 2bae494f2cf9486b771c706a1eec6217e46ce778
Getting pack 06483273097cbac210f10a4bd43324ae660053e6
which contains 74e24bdc2ec3f275da63ca1396a773e7043cb9e9
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7f382f0566f0 (LWP 28121)]
0x00000000004ada21 in process_object_response (callback_data=<value optimized out>) at http-walker.c:93
93 http-walker.c: No such file or directory.
in http-walker.c
(gdb) thread apply all bt
Thread 1 (Thread 0x7f382f0566f0 (LWP 28121)):
#0 0x00000000004ada21 in process_object_response (callback_data=<value optimized out>) at http-walker.c:93
#1 0x00000000004ab3e3 in process_curl_messages () at http.c:657
#2 0x00000000004ab475 in step_active_slots () at http.c:571
#3 0x00000000004add95 in fetch (walker=0x13d3b50, sha1=<value optimized out>) at http-walker.c:476
#4 0x00000000004a74e3 in walker_fetch (walker=0x13d3b50, targets=4, target=<value optimized out>, write_ref=0x0,
write_ref_log_details=<value optimized out>) at walker.c:176
#5 0x00000000004a0e46 in fetch_objs_via_curl (transport=0x20, nr_objs=4, to_fetch=0x13d5780) at transport.c:375
#6 0x000000000049feab in transport_fetch_refs (transport=0x13d4300, refs=<value optimized out>) at transport.c:1064
#7 0x0000000000416a05 in cmd_clone (argc=<value optimized out>, argv=<value optimized out>, prefix=<value optimized out>)
at builtin-clone.c:513
#8 0x0000000000404391 in handle_internal_command (argc=2, argv=0x7fff89c74b10) at git.c:246
#9 0x000000000040453d in main (argc=2, argv=0x7fff89c74b10) at git.c:438
(gdb)
--
Regards,
Ali Polatel
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: git clone http://git.savannah.gnu.org/cgit/xboard.git segfaults
From: Johannes Schindelin @ 2009-08-17 14:17 UTC (permalink / raw)
To: Ali Polatel; +Cc: git
In-Reply-To: <20090817135651.GA4570@harikalardiyari>
Hi,
On Mon, 17 Aug 2009, Ali Polatel wrote:
> Here's what gdb has to say about it:
>
> [...]
Here's what valgrind has to say about it (with a current 'next' +
patches):
==25434== Invalid read of size 8
==25434== at 0x407433: process_object_response (http-walker.c:91)
==25434== by 0x405713: finish_active_slot (http.c:657)
==25434== by 0x40448F: process_curl_messages (http.c:119)
==25434== by 0x40546A: step_active_slots (http.c:571)
==25434== by 0x4080E8: fetch_object (http-walker.c:476)
==25434== by 0x408316: fetch (http-walker.c:526)
==25434== by 0x42876C: loop (walker.c:176)
==25434== by 0x428C65: walker_fetch (walker.c:287)
==25434== by 0x40401F: main (remote-curl.c:111)
Ciao,
Dscho
^ permalink raw reply
* Re: Linus' sha1 is much faster!
From: Nicolas Pitre @ 2009-08-17 14:20 UTC (permalink / raw)
To: George Spelvin; +Cc: bdonlan, johnflux, P, art.08.09, git, Linus Torvalds
In-Reply-To: <20090817072315.4314.qmail@science.horizon.com>
On Mon, 17 Aug 2009, George Spelvin wrote:
> If it helps anyone resolve license issues, here's a from-FIPS-180-2
> implementation that's placed in the public domain. That should be
> compatible with any license.
>
> It uses Linus's and Artur's performance ideas, and some of Linus' macro
> ideas (in the rotate implementation), but tries to be textually different.
> Is there anything recognizable that anyone cares to clam copyright to?
>
> It's not quite 100% finished, as I haven't benchmarked it against Linus's
> code yet, but it's functionally correct.
>
> It's also clean with -W -Wall -Wextra.
>
> TODO: Check if an initial copy to w[] is faster on i386 (less register
> pressure).
>
> /*
> * Secure Hash Algorith SHA-1, as published in FIPS PUB 180-2.
> *
> * This implementation is in the public domain. Copyright abandoned.
> * You may do anything you like with it, including evil things.
> *
> * This is a rewrite from scratch, based on Linus Torvalds' "block-sha1"
> * from the git mailing list (August, 2009). Additional optimization
> * ideas cribbed from
> * - Artur Skawina (x86, particularly P4, and much benchmarking)
> * - Nicilas Pitre (ARM)
Please be careful to spell my name correctly.
Nicolas
^ permalink raw reply
* Re: git clone http://git.savannah.gnu.org/cgit/xboard.git segfaults
From: Johannes Schindelin @ 2009-08-17 14:22 UTC (permalink / raw)
To: Ali Polatel, Tay Ray Chuan; +Cc: git
In-Reply-To: <alpine.DEB.1.00.0908171616340.4991@intel-tinevez-2-302>
Hi,
On Mon, 17 Aug 2009, Johannes Schindelin wrote:
> On Mon, 17 Aug 2009, Ali Polatel wrote:
>
> > Here's what gdb has to say about it:
> >
> > [...]
>
> Here's what valgrind has to say about it (with a current 'next' +
> patches):
>
> ==25434== Invalid read of size 8
> ==25434== at 0x407433: process_object_response (http-walker.c:91)
> ==25434== by 0x405713: finish_active_slot (http.c:657)
> ==25434== by 0x40448F: process_curl_messages (http.c:119)
> ==25434== by 0x40546A: step_active_slots (http.c:571)
> ==25434== by 0x4080E8: fetch_object (http-walker.c:476)
> ==25434== by 0x408316: fetch (http-walker.c:526)
> ==25434== by 0x42876C: loop (walker.c:176)
> ==25434== by 0x428C65: walker_fetch (walker.c:287)
> ==25434== by 0x40401F: main (remote-curl.c:111)
Oops, forgot the more important part:
==25434== Address 0x87a7bd0 is 0 bytes inside a block of size 64 free'd
==25434== at 0x4C265AF: free (vg_replace_malloc.c:323)
==25434== by 0x4075F5: release_object_request (http-walker.c:128)
==25434== by 0x408022: abort_object_request (http-walker.c:452)
==25434== by 0x4080D7: fetch_object (http-walker.c:470)
==25434== by 0x408316: fetch (http-walker.c:526)
==25434== by 0x42876C: loop (walker.c:176)
==25434== by 0x428C65: walker_fetch (walker.c:287)
==25434== by 0x40401F: main (remote-curl.c:111)
Seems that an object request is aborted, but the slot, and therefore the
callback, is called nevertheless. Tay, does that ring a bell?
Ciao,
Dscho
^ permalink raw reply
* Re: "make quick-install-man" broke recently
From: Randal L. Schwartz @ 2009-08-17 14:26 UTC (permalink / raw)
To: Junio C Hamano
Cc: Jacob Helwig, Linus Torvalds, Kjetil Barvik, git@vger.kernel.org
In-Reply-To: <7vhbw72ap3.fsf@alter.siamese.dyndns.org>
>>>>> "Junio" == Junio C Hamano <gitster@pobox.com> writes:
Junio> Merlyn noticed that Documentation/install-doc-quick.sh no longer correctly
Junio> removes old installed documents when the target directory has a leading
Junio> path that is a symlink. It turns out that "checkout-index --prefix" was
Junio> broken by recent b6986d8 (git-checkout: be careful about untracked
Junio> symlinks, 2009-07-29).
It's good to know I wasn't hallucinating, in any case. :)
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion
^ permalink raw reply
* Re: sparse support in pu
From: Peter Harris @ 2009-08-17 14:30 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Nguyen Thai Ngoc Duy, Johannes Sixt, skillzero, git
In-Reply-To: <alpine.DEB.1.00.0908171522510.4991@intel-tinevez-2-302>
On Mon, Aug 17, 2009 at 9:23 AM, Johannes Schindelin wrote:
> On Mon, 17 Aug 2009, Peter Harris wrote:
>> On Mon, Aug 17, 2009 at 8:29 AM, Johannes Schindelin wrote:
>> > If I want to have a sparse checkout, I know which files I _want_.
>>
>> That's funny. I have a git tree that would benefit from sparse checkout.
>> I know which path I _don't_ want. Specifying all the paths I want would
>> be a rather longer (and more error-prone) list. I suspect it would be
>> best to support both.
>
> Yes, I agree, but the common case is for people to know what they are
> working on, right?
I would presume so, but anecdotal "evidence" points the other way. I
don't have a statistically significant number of samples. Maybe it
should have been asked in the Git User's Survey? ;-)
>> Does sparse use the same parser as .gitignore? (I guess not, if it
>> handles trailing slashes differently?) If so, it would be trivial to
>> turn "exclude path" into "exclude all but path" (or vice-versa) with:
>>
>> *
>> !path
>
> That was the idea behind my suggestion to allow .gitignore syntax. And
> indeed, that is what happened.
Excellent. In that case, I don't care which way around the default is.
Thanks,
Peter Harris
^ permalink raw reply
* Re: git clone http://git.savannah.gnu.org/cgit/xboard.git segfaults
From: Lars Hjemli @ 2009-08-17 14:40 UTC (permalink / raw)
To: Ali Polatel; +Cc: git
In-Reply-To: <20090817135651.GA4570@harikalardiyari>
On Mon, Aug 17, 2009 at 15:56, Ali Polatel<polatel@gmail.com> wrote:
> $subject.
> git version 1.6.4
>
> Here's what gdb has to say about it:
>
> 2456 alip@harikalardiyari> gdb --args git clone http://git.savannah.gnu.org/cgit/xboard.git
>...
> Getting pack 06483273097cbac210f10a4bd43324ae660053e6
> which contains 74e24bdc2ec3f275da63ca1396a773e7043cb9e9
>
> Program received signal SIGSEGV, Segmentation fault.
This is the http clone interface generated by cgit, and a quick test
gives me an identical segfault, while `git clone
http://git.sv.gnu.org/r/xboard.git` succeeds. I can only guess, but
maybe this has something to do with caching in cgit:
$ curl http://git.savannah.gnu.org/cgit/xboard.git/objects/info/packs
P pack-720d634dfc5e7511332c1e6851f7c5c5f88e7af2.pack
P pack-06483273097cbac210f10a4bd43324ae660053e6.pack
P pack-a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6.pack
$ curl http://git.savannah.gnu.org/cgit/xboard.git/objects/pack/pack-720d634dfc5e7511332c1e6851f7c5c5f88e7af2.pack
>pack-720d634dfc5e7511332c1e6851f7c5c5f88e7af2.pack
$ curl http://git.savannah.gnu.org/cgit/xboard.git/objects/pack/pack-720d634dfc5e7511332c1e6851f7c5c5f88e7af2.idx
>pack-720d634dfc5e7511332c1e6851f7c5c5f88e7af2.idx
$ git verify-pack -v pack-720d634dfc5e7511332c1e6851f7c5c5f88e7af2.pack
[snip]
non delta: 115 objects
chain length = 1: 64 objects
chain length = 2: 57 objects
chain length = 3: 16 objects
chain length = 4: 10 objects
chain length = 5: 7 objects
chain length = 6: 1 object
pack-720d634dfc5e7511332c1e6851f7c5c5f88e7af2.pack: ok
$ curl http://git.savannah.gnu.org/cgit/xboard.git/objects/pack/pack-06483273097cbac210f10a4bd43324ae660053e6.pack
>pack-06483273097cbac210f10a4bd43324ae660053e6.pack
$ curl http://git.savannah.gnu.org/cgit/xboard.git/objects/pack/pack-06483273097cbac210f10a4bd43324ae660053e6.idx
>pack-06483273097cbac210f10a4bd43324ae660053e6.idx
$ git verify-pack -v pack-06483273097cbac210f10a4bd43324ae660053e6.pack
[snip]
74e24bdc2ec3f275da63ca1396a773e7043cb9e9 blob 921 286 12694
[snip]
non delta: 233 objects
chain length = 1: 86 objects
chain length = 2: 74 objects
chain length = 3: 47 objects
chain length = 4: 8 objects
chain length = 5: 5 objects
chain length = 6: 1 object
pack-06483273097cbac210f10a4bd43324ae660053e6.pack: ok
$ curl http://git.savannah.gnu.org/cgit/xboard.git/objects/pack/pack-a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6.pack
>pack-a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6.pack
$ curl http://git.savannah.gnu.org/cgit/xboard.git/objects/pack/pack-a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6.idx
>pack-a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6.idx
$ git verify-pack -v pack-a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6.pack
[snip]
non delta: 1139 objects
chain length = 1: 454 objects
chain length = 2: 349 objects
chain length = 3: 301 objects
chain length = 4: 235 objects
chain length = 5: 179 objects
chain length = 6: 96 objects
chain length = 7: 52 objects
chain length = 8: 37 objects
chain length = 9: 21 objects
chain length = 10: 6 objects
chain length = 11: 5 objects
chain length = 12: 1 object
chain length = 13: 2 objects
chain length = 14: 1 object
chain length = 15: 2 objects
chain length = 16: 1 object
chain length = 17: 1 object
chain length = 18: 2 objects
chain length = 19: 1 object
chain length = 20: 1 object
chain length = 21: 1 object
chain length = 22: 1 object
chain length = 23: 2 objects
chain length = 24: 2 objects
chain length = 25: 1 object
chain length = 26: 1 object
chain length = 27: 1 object
chain length = 28: 1 object
chain length = 29: 1 object
chain length = 30: 1 object
chain length = 31: 1 object
chain length = 32: 1 object
chain length = 33: 2 objects
chain length = 34: 3 objects
chain length = 35: 2 objects
chain length = 36: 1 object
chain length = 37: 1 object
chain length = 38: 1 object
chain length = 39: 2 objects
chain length = 40: 3 objects
chain length = 41: 1 object
chain length = 42: 2 objects
chain length = 43: 1 object
chain length = 44: 1 object
chain length = 45: 2 objects
chain length = 46: 1 object
pack-a0a25c7cc57128b5317e0b5e7b8de7a59afe9af6.pack: ok
Hmm, the packs looks ok so maybe the problem is related to our usage
of libcurl after all...
--
larsh
^ permalink raw reply
* Re: [RFC PATCH v3 8/8] --sparse for porcelains
From: Nguyen Thai Ngoc Duy @ 2009-08-17 14:41 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Jakub Narebski, Junio C Hamano, git
In-Reply-To: <alpine.DEB.1.00.0908171524150.4991@intel-tinevez-2-302>
On Mon, Aug 17, 2009 at 8:35 PM, Johannes
Schindelin<Johannes.Schindelin@gmx.de> wrote:
> Hi,
>
> On Mon, 17 Aug 2009, Nguyen Thai Ngoc Duy wrote:
>
>> On Mon, Aug 17, 2009 at 4:08 PM, Johannes
>> Schindelin<Johannes.Schindelin@gmx.de> wrote:
>> > Turns out that somebody on IRC had a problem that requires to have
>> > sparse'd out files which _do_ have working directory copies.
>> >
>> > So just having the assume-changed bit may not be enough.
>> >
>> > The scenario is this: the repository contains a file that users are
>> > supposed to change, but not commit to (only the super-intelligent
>> > inventor of this scenario is allowed to). As this repository is
>> > originally a subversion one, there is no problem: people just do not
>> > switch branches.
>> >
>> > But this guy uses git-svn, so he does switch branches, and to avoid
>> > committing the file by mistake, he marked it assume-unchanged.
>>
>> Hmm.. never thought of this use before. If he does not want to commit by
>> mistake, should he add to-be-committed changes to index and do "git
>> commit" without "-a" (even better, do "git diff --cached" first)?
>
> You probably agree that this would be a _very_ fragile setup. Very easy
> to make mistakes.
>
> But we try to get away from that, don't we? Git had a reputation to be
> easy fsck up for long enough.
Well.. of course I don't want Git to keep that reputation :-)
>> > Only that a branch switch overwrites the local changes.
>>
>> I don't think branch switch overwrites changes in this case. Whenever
>> Git is to touch worktree files, it ignores assumed-unchanged bit and
>> does lstat() to make sure worktree files are up to date.
>
> Well, it does there, thankyouverymuch.
>
> The problem of course is that the other branch has an ancient version of
> that file (which should _not_ overwrite the current, modified version!),
> i.e. "git diff HEAD..other -- file" does not come empty.
>
> As 'file' is assume-unchanged, zinnnng, the file gets "updated".
Then it is a bug. Assume-unchanged as in reading is good.
Assume-unchanged in writing sounds scary. Something like this should
fix it (not well tested though). It's on top of my series, but you can
adapt it to 'next' or 'master' easily.
diff --git a/unpack-trees.c b/unpack-trees.c
index eb47676..7b9ddf6 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -538,7 +538,9 @@ static int verify_uptodate_1(struct cache_entry *ce,
{
struct stat st;
- if (o->index_only || o->reset || ce_uptodate(ce))
+ if (o->index_only || o->reset ||
+ /* we are going to update worktree, don't trust ce_uptodate if
it is CE_VALID'd */
+ (!(ce->ce_flags & CE_VALID) && ce_uptodate(ce)))
return 0;
if (!lstat(ce->name, &st)) {
>> > I suggested the use of the sparse feature, and mark this file (and
>> > this file alone) as sparse'd-out.
>>
>> Sparse checkout only removes a file if its assume-unchanged bit
>> changes from 0 to 1.
>
> The problem is not removing, but overwriting.
>
> And in this respect, 'assume-unchanged' is a very different beast from
> 'sparse'. I am growing more and more convinced that you cannot just reuse
> the assume-unchanged bit.
And assume-unchanged bit could get lost during index merging, which
may cause unexpected effect if sparse checkout bases off
assume-unchanged. Let me think more of it tonight.
>> If it's already 1, it does not care whether there is a corresponding
>> file in worktree. So something like this should work:
>>
>> git checkout my-branch
>> git update-index --assume-unchanged that-special-file
>> echo that-special-file > .git/info/sparse
>> # edit that-special-file
>> git commit -a
>> # do whatever you want, git pull/checkout/read-tree... won't touch
>> that-special-file because it's assume-unchanged already
>
> ... except if you changed .git/info/sparse and a formerly sparse'd-out
> file is overwritten by "pull". Not good.
Again, I think it's a bug.
>> > Is this an intended usage scenario? Then we cannot reuse the
>> > assume-changed bit [*1*].
>>
>> It'd be great if people tell us all the scenarios they have. My use
>> could be too limited.
>
> The use case I would have is where a collaborator wants to work only on
> one subdirectory and the top-level directory. All other subdirectories
> are of no interest to him.
>
> Another use case: documentation. I do not have that use case yet, but I
> know about people who do.
Translators usually checkout one or two files (I am Vietnamese
Translation Coordinator of GNOME, but well... I check them all out. I
suppose "normal" translators would not want to do like I do.)
> Specifying what you _want_ to have checked out
> is much more straight-forward here than the opposite.
I think it depends on type of projects. For documentation projects,
you may want a few files. For software projects, usually you need
everything _except_ a few big directories. For WebKit, it's a bunch of
test data that I don't care about. Firmware in hardware-related
projects or media files in game projects fall in the same category. I
don't have strong opinion on this. Either include or exclude is fine
to me.
--
Duy
^ permalink raw reply related
* Re: bbchop & Wikipedia's Bayesian search theory page
From: Ealdwulf Wuffinga @ 2009-08-17 14:46 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <alpine.DEB.1.00.0908161907580.8306@pacific.mpi-cbg.de>
On Sun, Aug 16, 2009 at 6:13 PM, Johannes
Schindelin<Johannes.Schindelin@gmx.de> wrote:
> I tried to find some documentation for Bayesian search theory, but it
> seems those ridiculous Wikipedia admins struck once again, in their
> mission to reduce the world's intellect to their own.
It looks like it is still there to me:
http://en.wikipedia.org/wiki/Bayesian_search_theory
It looks like github has included a ')' on the end when html-ifying
the link inthe README, making it into a dead link. I'll fix that.
The wikipedia article is still not amazing,though. Unfortunately most
of the online descriptions
of Bayesian Search Theory, such as:
http://www.sarinz.com/index.cfm/3,112,261/landsearchmethodsreview.pdf
seem to go heavily into the minutia of search-and-rescue, which while
interesting, is not
relevant to git.
However, although I got the idea of bbchop from search theory, it is
not necessary to know much
of search theory in order to understand bbchop. The basic algorithm is
very simple:
At each step, test the commit for which the expected gain of information (about
the location of the bug) is greatest.
That is basically all I got from search theory so far - the
calculation of the probability of the
bug existing in each location is standard bayesian probability theory,
which maybe you already
know. If not, a very readable reference is:
http://www.inference.phy.cam.ac.uk/mackay/itila/book.html (free on-line book).
So all the code does is compute N entropies and pick the best. Most of the
complexity is introduced by:
- calculating the N entropies without calculating N^2 probabilities
- calculations over a DAG.
Ealdwulf
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox