2006-04-24 07:52:20 +08:00
|
|
|
#include "cache.h"
|
2014-10-01 18:28:42 +08:00
|
|
|
#include "lockfile.h"
|
2006-04-24 07:52:20 +08:00
|
|
|
#include "tree.h"
|
2009-04-20 18:58:18 +08:00
|
|
|
#include "tree-walk.h"
|
2006-04-24 07:52:20 +08:00
|
|
|
#include "cache-tree.h"
|
|
|
|
|
2006-10-31 07:29:53 +08:00
|
|
|
#ifndef DEBUG
|
2006-04-24 07:52:20 +08:00
|
|
|
#define DEBUG 0
|
2006-10-31 07:29:53 +08:00
|
|
|
#endif
|
2006-04-24 07:52:20 +08:00
|
|
|
|
|
|
|
struct cache_tree *cache_tree(void)
|
|
|
|
{
|
|
|
|
struct cache_tree *it = xcalloc(1, sizeof(struct cache_tree));
|
|
|
|
it->entry_count = -1;
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
2006-04-25 12:18:58 +08:00
|
|
|
void cache_tree_free(struct cache_tree **it_p)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
|
|
|
int i;
|
2006-04-25 12:18:58 +08:00
|
|
|
struct cache_tree *it = *it_p;
|
2006-04-24 07:52:20 +08:00
|
|
|
|
|
|
|
if (!it)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < it->subtree_nr; i++)
|
2010-09-07 05:40:16 +08:00
|
|
|
if (it->down[i]) {
|
2006-04-26 08:40:02 +08:00
|
|
|
cache_tree_free(&it->down[i]->cache_tree);
|
2010-09-07 05:40:16 +08:00
|
|
|
free(it->down[i]);
|
|
|
|
}
|
2006-04-24 07:52:20 +08:00
|
|
|
free(it->down);
|
|
|
|
free(it);
|
2006-04-25 12:18:58 +08:00
|
|
|
*it_p = NULL;
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
|
2006-04-26 08:40:02 +08:00
|
|
|
static int subtree_name_cmp(const char *one, int onelen,
|
|
|
|
const char *two, int twolen)
|
|
|
|
{
|
|
|
|
if (onelen < twolen)
|
|
|
|
return -1;
|
|
|
|
if (twolen < onelen)
|
|
|
|
return 1;
|
|
|
|
return memcmp(one, two, onelen);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int subtree_pos(struct cache_tree *it, const char *path, int pathlen)
|
|
|
|
{
|
|
|
|
struct cache_tree_sub **down = it->down;
|
|
|
|
int lo, hi;
|
|
|
|
lo = 0;
|
|
|
|
hi = it->subtree_nr;
|
|
|
|
while (lo < hi) {
|
|
|
|
int mi = (lo + hi) / 2;
|
|
|
|
struct cache_tree_sub *mdl = down[mi];
|
|
|
|
int cmp = subtree_name_cmp(path, pathlen,
|
|
|
|
mdl->name, mdl->namelen);
|
|
|
|
if (!cmp)
|
|
|
|
return mi;
|
|
|
|
if (cmp < 0)
|
|
|
|
hi = mi;
|
|
|
|
else
|
|
|
|
lo = mi + 1;
|
|
|
|
}
|
|
|
|
return -lo-1;
|
|
|
|
}
|
|
|
|
|
2006-04-24 07:52:20 +08:00
|
|
|
static struct cache_tree_sub *find_subtree(struct cache_tree *it,
|
|
|
|
const char *path,
|
|
|
|
int pathlen,
|
|
|
|
int create)
|
|
|
|
{
|
|
|
|
struct cache_tree_sub *down;
|
2006-04-26 08:40:02 +08:00
|
|
|
int pos = subtree_pos(it, path, pathlen);
|
|
|
|
if (0 <= pos)
|
|
|
|
return it->down[pos];
|
2006-04-24 07:52:20 +08:00
|
|
|
if (!create)
|
|
|
|
return NULL;
|
2006-04-26 08:40:02 +08:00
|
|
|
|
|
|
|
pos = -pos-1;
|
2014-03-04 06:31:51 +08:00
|
|
|
ALLOC_GROW(it->down, it->subtree_nr + 1, it->subtree_alloc);
|
2006-04-26 08:40:02 +08:00
|
|
|
it->subtree_nr++;
|
|
|
|
|
2006-04-24 07:52:20 +08:00
|
|
|
down = xmalloc(sizeof(*down) + pathlen + 1);
|
2006-04-26 08:40:02 +08:00
|
|
|
down->cache_tree = NULL;
|
2006-04-24 07:52:20 +08:00
|
|
|
down->namelen = pathlen;
|
|
|
|
memcpy(down->name, path, pathlen);
|
2006-04-26 08:40:02 +08:00
|
|
|
down->name[pathlen] = 0;
|
|
|
|
|
|
|
|
if (pos < it->subtree_nr)
|
|
|
|
memmove(it->down + pos + 1,
|
|
|
|
it->down + pos,
|
|
|
|
sizeof(down) * (it->subtree_nr - pos - 1));
|
|
|
|
it->down[pos] = down;
|
2006-04-24 07:52:20 +08:00
|
|
|
return down;
|
|
|
|
}
|
|
|
|
|
2006-04-27 16:33:07 +08:00
|
|
|
struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
|
|
|
|
{
|
|
|
|
int pathlen = strlen(path);
|
|
|
|
return find_subtree(it, path, pathlen, 1);
|
|
|
|
}
|
|
|
|
|
2014-06-13 20:19:31 +08:00
|
|
|
static int do_invalidate_path(struct cache_tree *it, const char *path)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
|
|
|
/* a/b/c
|
|
|
|
* ==> invalidate self
|
|
|
|
* ==> find "a", have it invalidate "b/c"
|
|
|
|
* a
|
|
|
|
* ==> invalidate self
|
|
|
|
* ==> if "a" exists as a subtree, remove it.
|
|
|
|
*/
|
|
|
|
const char *slash;
|
|
|
|
int namelen;
|
|
|
|
struct cache_tree_sub *down;
|
|
|
|
|
2006-05-04 07:10:45 +08:00
|
|
|
#if DEBUG
|
|
|
|
fprintf(stderr, "cache-tree invalidate <%s>\n", path);
|
|
|
|
#endif
|
|
|
|
|
2006-04-24 07:52:20 +08:00
|
|
|
if (!it)
|
2014-06-13 20:19:31 +08:00
|
|
|
return 0;
|
2014-03-08 14:48:31 +08:00
|
|
|
slash = strchrnul(path, '/');
|
|
|
|
namelen = slash - path;
|
2006-04-24 07:52:20 +08:00
|
|
|
it->entry_count = -1;
|
2014-03-08 14:48:31 +08:00
|
|
|
if (!*slash) {
|
2006-04-26 08:40:02 +08:00
|
|
|
int pos;
|
|
|
|
pos = subtree_pos(it, path, namelen);
|
|
|
|
if (0 <= pos) {
|
|
|
|
cache_tree_free(&it->down[pos]->cache_tree);
|
|
|
|
free(it->down[pos]);
|
2006-04-24 07:52:20 +08:00
|
|
|
/* 0 1 2 3 4 5
|
|
|
|
* ^ ^subtree_nr = 6
|
2006-04-26 08:40:02 +08:00
|
|
|
* pos
|
2006-04-24 07:52:20 +08:00
|
|
|
* move 4 and 5 up one place (2 entries)
|
2006-04-26 08:40:02 +08:00
|
|
|
* 2 = 6 - 3 - 1 = subtree_nr - pos - 1
|
2006-04-24 07:52:20 +08:00
|
|
|
*/
|
2006-04-26 08:40:02 +08:00
|
|
|
memmove(it->down+pos, it->down+pos+1,
|
2006-04-24 07:52:20 +08:00
|
|
|
sizeof(struct cache_tree_sub *) *
|
2006-04-26 08:40:02 +08:00
|
|
|
(it->subtree_nr - pos - 1));
|
2006-04-24 07:52:20 +08:00
|
|
|
it->subtree_nr--;
|
|
|
|
}
|
2014-06-13 20:19:31 +08:00
|
|
|
return 1;
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
down = find_subtree(it, path, namelen, 0);
|
|
|
|
if (down)
|
2014-06-13 20:19:31 +08:00
|
|
|
do_invalidate_path(down->cache_tree, slash + 1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cache_tree_invalidate_path(struct index_state *istate, const char *path)
|
|
|
|
{
|
|
|
|
if (do_invalidate_path(istate->cache_tree, path))
|
|
|
|
istate->cache_changed |= CACHE_TREE_CHANGED;
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
|
2014-06-13 20:19:32 +08:00
|
|
|
static int verify_cache(struct cache_entry **cache,
|
2012-01-16 10:36:46 +08:00
|
|
|
int entries, int flags)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
|
|
|
int i, funny;
|
2012-01-16 10:36:46 +08:00
|
|
|
int silent = flags & WRITE_TREE_SILENT;
|
2006-04-24 07:52:20 +08:00
|
|
|
|
|
|
|
/* Verify that the tree is merged */
|
|
|
|
funny = 0;
|
|
|
|
for (i = 0; i < entries; i++) {
|
Convert "struct cache_entry *" to "const ..." wherever possible
I attempted to make index_state->cache[] a "const struct cache_entry **"
to find out how existing entries in index are modified and where. The
question I have is what do we do if we really need to keep track of on-disk
changes in the index. The result is
- diff-lib.c: setting CE_UPTODATE
- name-hash.c: setting CE_HASHED
- preload-index.c, read-cache.c, unpack-trees.c and
builtin/update-index: obvious
- entry.c: write_entry() may refresh the checked out entry via
fill_stat_cache_info(). This causes "non-const struct cache_entry
*" in builtin/apply.c, builtin/checkout-index.c and
builtin/checkout.c
- builtin/ls-files.c: --with-tree changes stagemask and may set
CE_UPDATE
Of these, write_entry() and its call sites are probably most
interesting because it modifies on-disk info. But this is stat info
and can be retrieved via refresh, at least for porcelain
commands. Other just uses ce_flags for local purposes.
So, keeping track of "dirty" entries is just a matter of setting a
flag in index modification functions exposed by read-cache.c. Except
unpack-trees, the rest of the code base does not do anything funny
behind read-cache's back.
The actual patch is less valueable than the summary above. But if
anyone wants to re-identify the above sites. Applying this patch, then
this:
diff --git a/cache.h b/cache.h
index 430d021..1692891 100644
--- a/cache.h
+++ b/cache.h
@@ -267,7 +267,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
struct index_state {
- struct cache_entry **cache;
+ const struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
will help quickly identify them without bogus warnings.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 23:29:00 +08:00
|
|
|
const struct cache_entry *ce = cache[i];
|
commit: ignore intent-to-add entries instead of refusing
Originally, "git add -N" was introduced to help users from forgetting to
add new files to the index before they ran "git commit -a". As an attempt
to help them further so that they do not forget to say "-a", "git commit"
to commit the index as-is was taught to error out, reminding the user that
they may have forgotten to add the final contents of the paths before
running the command.
This turned out to be a false "safety" that is useless. If the user made
changes to already tracked paths and paths added with "git add -N", and
then ran "git add" to register the final contents of the paths added with
"git add -N", "git commit" will happily create a commit out of the index,
without including the local changes made to the already tracked paths. It
was not a useful "safety" measure to prevent "forgetful" mistakes from
happening.
It turns out that this behaviour is not just a useless false "safety", but
actively hurts use cases of "git add -N" that were discovered later and
have become popular, namely, to tell Git to be aware of these paths added
by "git add -N", so that commands like "git status" and "git diff" would
include them in their output, even though the user is not interested in
including them in the next commit they are going to make.
Fix this ancient UI mistake, and instead make a commit from the index
ignoring the paths added by "git add -N" without adding real contents.
Based on the work by Nguyễn Thái Ngọc Duy, and helped by injection of
sanity from Jonathan Nieder and others on the Git mailing list.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-02-08 03:55:48 +08:00
|
|
|
if (ce_stage(ce)) {
|
2011-12-07 01:43:37 +08:00
|
|
|
if (silent)
|
|
|
|
return -1;
|
2006-04-24 07:52:20 +08:00
|
|
|
if (10 < ++funny) {
|
|
|
|
fprintf(stderr, "...\n");
|
|
|
|
break;
|
|
|
|
}
|
2012-12-16 12:15:25 +08:00
|
|
|
fprintf(stderr, "%s: unmerged (%s)\n",
|
|
|
|
ce->name, sha1_to_hex(ce->sha1));
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (funny)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Also verify that the cache does not have path and path/file
|
|
|
|
* at the same time. At this point we know the cache has only
|
|
|
|
* stage 0 entries.
|
|
|
|
*/
|
|
|
|
funny = 0;
|
|
|
|
for (i = 0; i < entries - 1; i++) {
|
|
|
|
/* path/file always comes after path because of the way
|
|
|
|
* the cache is sorted. Also path can appear only once,
|
|
|
|
* which means conflicting one would immediately follow.
|
|
|
|
*/
|
|
|
|
const char *this_name = cache[i]->name;
|
|
|
|
const char *next_name = cache[i+1]->name;
|
|
|
|
int this_len = strlen(this_name);
|
|
|
|
if (this_len < strlen(next_name) &&
|
|
|
|
strncmp(this_name, next_name, this_len) == 0 &&
|
|
|
|
next_name[this_len] == '/') {
|
|
|
|
if (10 < ++funny) {
|
|
|
|
fprintf(stderr, "...\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "You have both %s and %s\n",
|
|
|
|
this_name, next_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (funny)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void discard_unused_subtrees(struct cache_tree *it)
|
|
|
|
{
|
|
|
|
struct cache_tree_sub **down = it->down;
|
|
|
|
int nr = it->subtree_nr;
|
|
|
|
int dst, src;
|
|
|
|
for (dst = src = 0; src < nr; src++) {
|
|
|
|
struct cache_tree_sub *s = down[src];
|
|
|
|
if (s->used)
|
|
|
|
down[dst++] = s;
|
|
|
|
else {
|
2006-04-25 12:18:58 +08:00
|
|
|
cache_tree_free(&s->cache_tree);
|
2006-04-24 07:52:20 +08:00
|
|
|
free(s);
|
|
|
|
it->subtree_nr--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-25 12:18:58 +08:00
|
|
|
int cache_tree_fully_valid(struct cache_tree *it)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (!it)
|
|
|
|
return 0;
|
|
|
|
if (it->entry_count < 0 || !has_sha1_file(it->sha1))
|
|
|
|
return 0;
|
|
|
|
for (i = 0; i < it->subtree_nr; i++) {
|
|
|
|
if (!cache_tree_fully_valid(it->down[i]->cache_tree))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-04-24 07:52:20 +08:00
|
|
|
static int update_one(struct cache_tree *it,
|
2014-06-13 20:19:32 +08:00
|
|
|
struct cache_entry **cache,
|
2006-04-24 07:52:20 +08:00
|
|
|
int entries,
|
|
|
|
const char *base,
|
|
|
|
int baselen,
|
2012-12-16 12:15:27 +08:00
|
|
|
int *skip_count,
|
2012-01-16 10:36:46 +08:00
|
|
|
int flags)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
2007-09-06 19:20:11 +08:00
|
|
|
struct strbuf buffer;
|
2012-01-16 10:36:46 +08:00
|
|
|
int missing_ok = flags & WRITE_TREE_MISSING_OK;
|
|
|
|
int dryrun = flags & WRITE_TREE_DRY_RUN;
|
2014-07-06 12:06:56 +08:00
|
|
|
int repair = flags & WRITE_TREE_REPAIR;
|
2012-12-16 12:15:28 +08:00
|
|
|
int to_invalidate = 0;
|
2006-04-24 07:52:20 +08:00
|
|
|
int i;
|
|
|
|
|
2014-07-06 12:06:56 +08:00
|
|
|
assert(!(dryrun && repair));
|
|
|
|
|
2012-12-16 12:15:27 +08:00
|
|
|
*skip_count = 0;
|
|
|
|
|
2006-04-25 06:12:42 +08:00
|
|
|
if (0 <= it->entry_count && has_sha1_file(it->sha1))
|
2006-04-24 07:52:20 +08:00
|
|
|
return it->entry_count;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We first scan for subtrees and update them; we start by
|
|
|
|
* marking existing subtrees -- the ones that are unmarked
|
|
|
|
* should not be in the result.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < it->subtree_nr; i++)
|
|
|
|
it->down[i]->used = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the subtrees and update them.
|
|
|
|
*/
|
2012-12-16 12:15:26 +08:00
|
|
|
i = 0;
|
|
|
|
while (i < entries) {
|
Convert "struct cache_entry *" to "const ..." wherever possible
I attempted to make index_state->cache[] a "const struct cache_entry **"
to find out how existing entries in index are modified and where. The
question I have is what do we do if we really need to keep track of on-disk
changes in the index. The result is
- diff-lib.c: setting CE_UPTODATE
- name-hash.c: setting CE_HASHED
- preload-index.c, read-cache.c, unpack-trees.c and
builtin/update-index: obvious
- entry.c: write_entry() may refresh the checked out entry via
fill_stat_cache_info(). This causes "non-const struct cache_entry
*" in builtin/apply.c, builtin/checkout-index.c and
builtin/checkout.c
- builtin/ls-files.c: --with-tree changes stagemask and may set
CE_UPDATE
Of these, write_entry() and its call sites are probably most
interesting because it modifies on-disk info. But this is stat info
and can be retrieved via refresh, at least for porcelain
commands. Other just uses ce_flags for local purposes.
So, keeping track of "dirty" entries is just a matter of setting a
flag in index modification functions exposed by read-cache.c. Except
unpack-trees, the rest of the code base does not do anything funny
behind read-cache's back.
The actual patch is less valueable than the summary above. But if
anyone wants to re-identify the above sites. Applying this patch, then
this:
diff --git a/cache.h b/cache.h
index 430d021..1692891 100644
--- a/cache.h
+++ b/cache.h
@@ -267,7 +267,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
struct index_state {
- struct cache_entry **cache;
+ const struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
will help quickly identify them without bogus warnings.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 23:29:00 +08:00
|
|
|
const struct cache_entry *ce = cache[i];
|
2006-04-24 07:52:20 +08:00
|
|
|
struct cache_tree_sub *sub;
|
|
|
|
const char *path, *slash;
|
2012-12-16 12:15:27 +08:00
|
|
|
int pathlen, sublen, subcnt, subskip;
|
2006-04-24 07:52:20 +08:00
|
|
|
|
|
|
|
path = ce->name;
|
|
|
|
pathlen = ce_namelen(ce);
|
|
|
|
if (pathlen <= baselen || memcmp(base, path, baselen))
|
|
|
|
break; /* at the end of this level */
|
|
|
|
|
|
|
|
slash = strchr(path + baselen, '/');
|
2012-12-16 12:15:26 +08:00
|
|
|
if (!slash) {
|
|
|
|
i++;
|
2006-04-24 07:52:20 +08:00
|
|
|
continue;
|
2012-12-16 12:15:26 +08:00
|
|
|
}
|
2006-04-24 07:52:20 +08:00
|
|
|
/*
|
|
|
|
* a/bbb/c (base = a/, slash = /c)
|
|
|
|
* ==>
|
|
|
|
* path+baselen = bbb/c, sublen = 3
|
|
|
|
*/
|
|
|
|
sublen = slash - (path + baselen);
|
|
|
|
sub = find_subtree(it, path + baselen, sublen, 1);
|
|
|
|
if (!sub->cache_tree)
|
|
|
|
sub->cache_tree = cache_tree();
|
|
|
|
subcnt = update_one(sub->cache_tree,
|
|
|
|
cache + i, entries - i,
|
|
|
|
path,
|
|
|
|
baselen + sublen + 1,
|
2012-12-16 12:15:27 +08:00
|
|
|
&subskip,
|
2012-01-16 10:36:46 +08:00
|
|
|
flags);
|
2006-11-13 21:50:00 +08:00
|
|
|
if (subcnt < 0)
|
|
|
|
return subcnt;
|
cache-tree: avoid infinite loop on zero-entry tree
The loop in cache-tree's update_one iterates over all the
entries in the index. For each one, we find the cache-tree
subtree which represents our path (creating it if
necessary), and then recurse into update_one again. The
return value we get is the number of index entries that
belonged in that subtree. So for example, with entries:
a/one
a/two
b/one
We start by processing the first entry, "a/one". We would
find the subtree for "a" and recurse into update_one. That
would then handle "a/one" and "a/two", and return the value
2. The parent function then skips past the 2 handled
entries, and we continue by processing "b/one".
If the recursed-into update_one ever returns 0, then we make
no forward progress in our loop. We would process "a/one"
over and over, infinitely.
This should not happen normally. Any subtree we create must
have at least one path in it (the one that we are
processing!). However, we may also reuse a cache-tree entry
we found in the on-disk index. For the same reason, this
should also never have zero entries. However, certain buggy
versions of libgit2 could produce such bogus cache-tree
records. The libgit2 bug has since been fixed, but it does
not hurt to protect ourselves against bogus input coming
from the on-disk data structures.
Note that this is not a die("BUG") or assert, because it is
not an internal bug, but rather a corrupted on-disk
structure. It's possible that we could even recover from it
(by throwing out the bogus cache-tree entry), but it is not
worth the effort; the important thing is that we report an
error instead of looping infinitely.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-10-30 01:11:58 +08:00
|
|
|
if (!subcnt)
|
|
|
|
die("index cache-tree records empty sub-tree");
|
2012-12-16 12:15:26 +08:00
|
|
|
i += subcnt;
|
2012-12-16 12:15:27 +08:00
|
|
|
sub->count = subcnt; /* to be used in the next loop */
|
|
|
|
*skip_count += subskip;
|
2006-04-24 07:52:20 +08:00
|
|
|
sub->used = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
discard_unused_subtrees(it);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Then write out the tree object for this level.
|
|
|
|
*/
|
2007-09-10 18:35:04 +08:00
|
|
|
strbuf_init(&buffer, 8192);
|
2006-04-24 07:52:20 +08:00
|
|
|
|
2012-12-16 12:15:26 +08:00
|
|
|
i = 0;
|
|
|
|
while (i < entries) {
|
Convert "struct cache_entry *" to "const ..." wherever possible
I attempted to make index_state->cache[] a "const struct cache_entry **"
to find out how existing entries in index are modified and where. The
question I have is what do we do if we really need to keep track of on-disk
changes in the index. The result is
- diff-lib.c: setting CE_UPTODATE
- name-hash.c: setting CE_HASHED
- preload-index.c, read-cache.c, unpack-trees.c and
builtin/update-index: obvious
- entry.c: write_entry() may refresh the checked out entry via
fill_stat_cache_info(). This causes "non-const struct cache_entry
*" in builtin/apply.c, builtin/checkout-index.c and
builtin/checkout.c
- builtin/ls-files.c: --with-tree changes stagemask and may set
CE_UPDATE
Of these, write_entry() and its call sites are probably most
interesting because it modifies on-disk info. But this is stat info
and can be retrieved via refresh, at least for porcelain
commands. Other just uses ce_flags for local purposes.
So, keeping track of "dirty" entries is just a matter of setting a
flag in index modification functions exposed by read-cache.c. Except
unpack-trees, the rest of the code base does not do anything funny
behind read-cache's back.
The actual patch is less valueable than the summary above. But if
anyone wants to re-identify the above sites. Applying this patch, then
this:
diff --git a/cache.h b/cache.h
index 430d021..1692891 100644
--- a/cache.h
+++ b/cache.h
@@ -267,7 +267,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
struct index_state {
- struct cache_entry **cache;
+ const struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
will help quickly identify them without bogus warnings.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 23:29:00 +08:00
|
|
|
const struct cache_entry *ce = cache[i];
|
2006-04-24 07:52:20 +08:00
|
|
|
struct cache_tree_sub *sub;
|
|
|
|
const char *path, *slash;
|
|
|
|
int pathlen, entlen;
|
|
|
|
const unsigned char *sha1;
|
|
|
|
unsigned mode;
|
2014-09-03 05:16:20 +08:00
|
|
|
int expected_missing = 0;
|
2006-04-24 07:52:20 +08:00
|
|
|
|
|
|
|
path = ce->name;
|
|
|
|
pathlen = ce_namelen(ce);
|
|
|
|
if (pathlen <= baselen || memcmp(base, path, baselen))
|
|
|
|
break; /* at the end of this level */
|
|
|
|
|
|
|
|
slash = strchr(path + baselen, '/');
|
|
|
|
if (slash) {
|
|
|
|
entlen = slash - (path + baselen);
|
|
|
|
sub = find_subtree(it, path + baselen, entlen, 0);
|
|
|
|
if (!sub)
|
|
|
|
die("cache-tree.c: '%.*s' in '%s' not found",
|
|
|
|
entlen, path + baselen, path);
|
2012-12-16 12:15:27 +08:00
|
|
|
i += sub->count;
|
2006-04-24 07:52:20 +08:00
|
|
|
sha1 = sub->cache_tree->sha1;
|
|
|
|
mode = S_IFDIR;
|
2014-09-03 05:16:20 +08:00
|
|
|
if (sub->cache_tree->entry_count < 0) {
|
2012-12-16 12:15:28 +08:00
|
|
|
to_invalidate = 1;
|
2014-09-03 05:16:20 +08:00
|
|
|
expected_missing = 1;
|
|
|
|
}
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
sha1 = ce->sha1;
|
2008-01-15 08:03:17 +08:00
|
|
|
mode = ce->ce_mode;
|
2006-04-24 07:52:20 +08:00
|
|
|
entlen = pathlen - baselen;
|
2012-12-16 12:15:26 +08:00
|
|
|
i++;
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
2010-08-10 11:32:11 +08:00
|
|
|
if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
|
|
|
|
strbuf_release(&buffer);
|
2014-09-03 05:16:20 +08:00
|
|
|
if (expected_missing)
|
|
|
|
return -1;
|
2009-07-15 02:25:17 +08:00
|
|
|
return error("invalid object %06o %s for '%.*s'",
|
|
|
|
mode, sha1_to_hex(sha1), entlen+baselen, path);
|
2010-08-10 11:32:11 +08:00
|
|
|
}
|
2006-04-24 07:52:20 +08:00
|
|
|
|
2012-12-16 12:15:27 +08:00
|
|
|
/*
|
|
|
|
* CE_REMOVE entries are removed before the index is
|
|
|
|
* written to disk. Skip them to remain consistent
|
|
|
|
* with the future on-disk index.
|
|
|
|
*/
|
|
|
|
if (ce->ce_flags & CE_REMOVE) {
|
|
|
|
*skip_count = *skip_count + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-12-16 12:15:28 +08:00
|
|
|
/*
|
|
|
|
* CE_INTENT_TO_ADD entries exist on on-disk index but
|
|
|
|
* they are not part of generated trees. Invalidate up
|
|
|
|
* to root to force cache-tree users to read elsewhere.
|
|
|
|
*/
|
|
|
|
if (ce->ce_flags & CE_INTENT_TO_ADD) {
|
|
|
|
to_invalidate = 1;
|
2012-12-16 12:15:27 +08:00
|
|
|
continue;
|
2012-12-16 12:15:28 +08:00
|
|
|
}
|
2006-04-24 07:52:20 +08:00
|
|
|
|
2007-09-06 19:20:11 +08:00
|
|
|
strbuf_grow(&buffer, entlen + 100);
|
|
|
|
strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
|
|
|
|
strbuf_add(&buffer, sha1, 20);
|
2006-04-24 07:52:20 +08:00
|
|
|
|
|
|
|
#if DEBUG
|
2006-05-04 07:10:45 +08:00
|
|
|
fprintf(stderr, "cache-tree update-one %o %.*s\n",
|
2006-04-24 07:52:20 +08:00
|
|
|
mode, entlen, path + baselen);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-07-06 12:06:56 +08:00
|
|
|
if (repair) {
|
|
|
|
unsigned char sha1[20];
|
|
|
|
hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1);
|
|
|
|
if (has_sha1_file(sha1))
|
|
|
|
hashcpy(it->sha1, sha1);
|
|
|
|
else
|
|
|
|
to_invalidate = 1;
|
|
|
|
} else if (dryrun)
|
2007-09-06 19:20:11 +08:00
|
|
|
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
|
2008-04-24 00:47:17 +08:00
|
|
|
else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
|
|
|
|
strbuf_release(&buffer);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-09-06 19:20:11 +08:00
|
|
|
strbuf_release(&buffer);
|
2012-12-16 12:15:28 +08:00
|
|
|
it->entry_count = to_invalidate ? -1 : i - *skip_count;
|
2006-04-24 07:52:20 +08:00
|
|
|
#if DEBUG
|
2006-05-04 07:10:45 +08:00
|
|
|
fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
|
2006-04-24 07:52:20 +08:00
|
|
|
it->entry_count, it->subtree_nr,
|
|
|
|
sha1_to_hex(it->sha1));
|
|
|
|
#endif
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2014-06-13 20:19:32 +08:00
|
|
|
int cache_tree_update(struct index_state *istate, int flags)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
2014-06-13 20:19:32 +08:00
|
|
|
struct cache_tree *it = istate->cache_tree;
|
|
|
|
struct cache_entry **cache = istate->cache;
|
|
|
|
int entries = istate->cache_nr;
|
|
|
|
int skip, i = verify_cache(cache, entries, flags);
|
|
|
|
|
2006-04-24 07:52:20 +08:00
|
|
|
if (i)
|
|
|
|
return i;
|
2012-12-16 12:15:27 +08:00
|
|
|
i = update_one(it, cache, entries, "", 0, &skip, flags);
|
2006-04-24 07:52:20 +08:00
|
|
|
if (i < 0)
|
|
|
|
return i;
|
2014-06-13 20:19:32 +08:00
|
|
|
istate->cache_changed |= CACHE_TREE_CHANGED;
|
2006-04-24 07:52:20 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-25 16:22:44 +08:00
|
|
|
static void write_one(struct strbuf *buffer, struct cache_tree *it,
|
|
|
|
const char *path, int pathlen)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* One "cache-tree" entry consists of the following:
|
|
|
|
* path (NUL terminated)
|
|
|
|
* entry_count, subtree_nr ("%d %d\n")
|
|
|
|
* tree-sha1 (missing if invalid)
|
|
|
|
* subtree_nr "cache-tree" entries for subtrees.
|
|
|
|
*/
|
2007-09-06 19:20:11 +08:00
|
|
|
strbuf_grow(buffer, pathlen + 100);
|
|
|
|
strbuf_add(buffer, path, pathlen);
|
|
|
|
strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr);
|
2006-04-24 07:52:20 +08:00
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
if (0 <= it->entry_count)
|
|
|
|
fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
|
|
|
|
pathlen, path, it->entry_count, it->subtree_nr,
|
|
|
|
sha1_to_hex(it->sha1));
|
|
|
|
else
|
|
|
|
fprintf(stderr, "cache-tree <%.*s> (%d subtree) invalid\n",
|
|
|
|
pathlen, path, it->subtree_nr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (0 <= it->entry_count) {
|
2007-09-06 19:20:11 +08:00
|
|
|
strbuf_add(buffer, it->sha1, 20);
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
for (i = 0; i < it->subtree_nr; i++) {
|
|
|
|
struct cache_tree_sub *down = it->down[i];
|
2006-04-26 08:40:02 +08:00
|
|
|
if (i) {
|
|
|
|
struct cache_tree_sub *prev = it->down[i-1];
|
|
|
|
if (subtree_name_cmp(down->name, down->namelen,
|
|
|
|
prev->name, prev->namelen) <= 0)
|
|
|
|
die("fatal - unsorted cache subtree");
|
|
|
|
}
|
2007-09-25 16:22:44 +08:00
|
|
|
write_one(buffer, down->cache_tree, down->name, down->namelen);
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-25 16:22:44 +08:00
|
|
|
void cache_tree_write(struct strbuf *sb, struct cache_tree *root)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
2007-09-25 16:22:44 +08:00
|
|
|
write_one(sb, root, "", 0);
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
|
|
|
|
{
|
|
|
|
const char *buf = *buffer;
|
|
|
|
unsigned long size = *size_p;
|
2006-05-02 09:31:02 +08:00
|
|
|
const char *cp;
|
|
|
|
char *ep;
|
2006-04-24 07:52:20 +08:00
|
|
|
struct cache_tree *it;
|
|
|
|
int i, subtree_nr;
|
|
|
|
|
|
|
|
it = NULL;
|
|
|
|
/* skip name, but make sure name exists */
|
|
|
|
while (size && *buf) {
|
|
|
|
size--;
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
if (!size)
|
|
|
|
goto free_return;
|
|
|
|
buf++; size--;
|
|
|
|
it = cache_tree();
|
2006-05-02 09:31:02 +08:00
|
|
|
|
|
|
|
cp = buf;
|
|
|
|
it->entry_count = strtol(cp, &ep, 10);
|
|
|
|
if (cp == ep)
|
|
|
|
goto free_return;
|
|
|
|
cp = ep;
|
|
|
|
subtree_nr = strtol(cp, &ep, 10);
|
|
|
|
if (cp == ep)
|
2006-04-24 07:52:20 +08:00
|
|
|
goto free_return;
|
|
|
|
while (size && *buf && *buf != '\n') {
|
|
|
|
size--;
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
if (!size)
|
|
|
|
goto free_return;
|
|
|
|
buf++; size--;
|
|
|
|
if (0 <= it->entry_count) {
|
|
|
|
if (size < 20)
|
|
|
|
goto free_return;
|
2007-06-23 14:19:43 +08:00
|
|
|
hashcpy(it->sha1, (const unsigned char*)buf);
|
2006-04-24 07:52:20 +08:00
|
|
|
buf += 20;
|
|
|
|
size -= 20;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
if (0 <= it->entry_count)
|
|
|
|
fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
|
|
|
|
*buffer, it->entry_count, subtree_nr,
|
|
|
|
sha1_to_hex(it->sha1));
|
|
|
|
else
|
|
|
|
fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n",
|
|
|
|
*buffer, subtree_nr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Just a heuristic -- we do not add directories that often but
|
|
|
|
* we do not want to have to extend it immediately when we do,
|
|
|
|
* hence +2.
|
|
|
|
*/
|
|
|
|
it->subtree_alloc = subtree_nr + 2;
|
|
|
|
it->down = xcalloc(it->subtree_alloc, sizeof(struct cache_tree_sub *));
|
|
|
|
for (i = 0; i < subtree_nr; i++) {
|
|
|
|
/* read each subtree */
|
|
|
|
struct cache_tree *sub;
|
2006-04-26 08:40:02 +08:00
|
|
|
struct cache_tree_sub *subtree;
|
2006-04-24 07:52:20 +08:00
|
|
|
const char *name = buf;
|
2006-04-27 16:33:07 +08:00
|
|
|
|
2006-04-24 07:52:20 +08:00
|
|
|
sub = read_one(&buf, &size);
|
|
|
|
if (!sub)
|
|
|
|
goto free_return;
|
2006-04-27 16:33:07 +08:00
|
|
|
subtree = cache_tree_sub(it, name);
|
2006-04-26 08:40:02 +08:00
|
|
|
subtree->cache_tree = sub;
|
2006-04-24 07:52:20 +08:00
|
|
|
}
|
|
|
|
if (subtree_nr != it->subtree_nr)
|
|
|
|
die("cache-tree: internal error");
|
|
|
|
*buffer = buf;
|
|
|
|
*size_p = size;
|
|
|
|
return it;
|
|
|
|
|
|
|
|
free_return:
|
2006-04-25 12:18:58 +08:00
|
|
|
cache_tree_free(&it);
|
2006-04-24 07:52:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-04-25 12:18:58 +08:00
|
|
|
struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
|
2006-04-24 07:52:20 +08:00
|
|
|
{
|
2006-04-25 12:18:58 +08:00
|
|
|
if (buffer[0])
|
2006-04-24 07:52:20 +08:00
|
|
|
return NULL; /* not the whole tree */
|
|
|
|
return read_one(&buffer, &size);
|
|
|
|
}
|
2006-04-26 16:20:50 +08:00
|
|
|
|
2008-07-16 18:42:10 +08:00
|
|
|
static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
|
2006-04-26 16:20:50 +08:00
|
|
|
{
|
2009-05-21 06:53:57 +08:00
|
|
|
if (!it)
|
|
|
|
return NULL;
|
2006-04-26 16:20:50 +08:00
|
|
|
while (*path) {
|
|
|
|
const char *slash;
|
|
|
|
struct cache_tree_sub *sub;
|
|
|
|
|
2014-03-06 01:26:26 +08:00
|
|
|
slash = strchrnul(path, '/');
|
2014-03-06 01:26:27 +08:00
|
|
|
/*
|
|
|
|
* Between path and slash is the name of the subtree
|
|
|
|
* to look for.
|
2006-04-26 16:20:50 +08:00
|
|
|
*/
|
|
|
|
sub = find_subtree(it, path, slash - path, 0);
|
|
|
|
if (!sub)
|
|
|
|
return NULL;
|
|
|
|
it = sub->cache_tree;
|
2014-03-06 01:26:30 +08:00
|
|
|
|
2006-04-26 16:20:50 +08:00
|
|
|
path = slash;
|
2014-03-06 01:26:30 +08:00
|
|
|
while (*path == '/')
|
|
|
|
path++;
|
2006-04-26 16:20:50 +08:00
|
|
|
}
|
|
|
|
return it;
|
|
|
|
}
|
2008-01-11 14:49:35 +08:00
|
|
|
|
2015-08-04 21:51:40 +08:00
|
|
|
int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
|
2008-01-11 14:49:35 +08:00
|
|
|
{
|
|
|
|
int entries, was_valid, newfd;
|
2009-05-21 02:04:35 +08:00
|
|
|
struct lock_file *lock_file;
|
2008-01-11 14:49:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We can't free this memory, it becomes part of a linked list
|
|
|
|
* parsed atexit()
|
|
|
|
*/
|
2009-05-21 02:04:35 +08:00
|
|
|
lock_file = xcalloc(1, sizeof(struct lock_file));
|
2008-01-11 14:49:35 +08:00
|
|
|
|
2015-08-04 21:51:40 +08:00
|
|
|
newfd = hold_lock_file_for_update(lock_file, index_path, LOCK_DIE_ON_ERROR);
|
2008-01-11 14:49:35 +08:00
|
|
|
|
2015-08-04 21:51:40 +08:00
|
|
|
entries = read_index_from(index_state, index_path);
|
2008-01-11 14:49:35 +08:00
|
|
|
if (entries < 0)
|
|
|
|
return WRITE_TREE_UNREADABLE_INDEX;
|
2009-05-21 02:04:35 +08:00
|
|
|
if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
|
2015-08-04 21:51:40 +08:00
|
|
|
cache_tree_free(&index_state->cache_tree);
|
2008-01-11 14:49:35 +08:00
|
|
|
|
2015-08-04 21:51:40 +08:00
|
|
|
if (!index_state->cache_tree)
|
|
|
|
index_state->cache_tree = cache_tree();
|
2008-01-11 14:49:35 +08:00
|
|
|
|
2015-08-04 21:51:40 +08:00
|
|
|
was_valid = cache_tree_fully_valid(index_state->cache_tree);
|
2008-01-11 14:49:35 +08:00
|
|
|
if (!was_valid) {
|
2015-08-04 21:51:40 +08:00
|
|
|
if (cache_tree_update(index_state, flags) < 0)
|
2008-01-11 14:49:35 +08:00
|
|
|
return WRITE_TREE_UNMERGED_INDEX;
|
|
|
|
if (0 <= newfd) {
|
2015-08-04 21:51:40 +08:00
|
|
|
if (!write_locked_index(index_state, lock_file, COMMIT_LOCK))
|
2008-01-11 14:49:35 +08:00
|
|
|
newfd = -1;
|
|
|
|
}
|
|
|
|
/* Not being able to write is fine -- we are only interested
|
|
|
|
* in updating the cache-tree part, and if the next caller
|
|
|
|
* ends up using the old index with unupdated cache-tree part
|
|
|
|
* it misses the work we did here, but that is just a
|
|
|
|
* performance penalty and not a big deal.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prefix) {
|
2015-08-04 21:51:40 +08:00
|
|
|
struct cache_tree *subtree;
|
|
|
|
subtree = cache_tree_find(index_state->cache_tree, prefix);
|
2008-01-11 14:49:35 +08:00
|
|
|
if (!subtree)
|
|
|
|
return WRITE_TREE_PREFIX_ERROR;
|
|
|
|
hashcpy(sha1, subtree->sha1);
|
|
|
|
}
|
|
|
|
else
|
2015-08-04 21:51:40 +08:00
|
|
|
hashcpy(sha1, index_state->cache_tree->sha1);
|
2008-01-11 14:49:35 +08:00
|
|
|
|
|
|
|
if (0 <= newfd)
|
|
|
|
rollback_lock_file(lock_file);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-04-20 18:58:18 +08:00
|
|
|
|
2015-08-04 21:51:40 +08:00
|
|
|
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
|
|
|
|
{
|
|
|
|
return write_index_as_tree(sha1, &the_index, get_index_file(), flags, prefix);
|
|
|
|
}
|
|
|
|
|
2009-04-20 18:58:18 +08:00
|
|
|
static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
|
|
|
|
{
|
|
|
|
struct tree_desc desc;
|
|
|
|
struct name_entry entry;
|
|
|
|
int cnt;
|
|
|
|
|
2015-11-10 10:22:29 +08:00
|
|
|
hashcpy(it->sha1, tree->object.oid.hash);
|
2009-04-20 18:58:18 +08:00
|
|
|
init_tree_desc(&desc, tree->buffer, tree->size);
|
|
|
|
cnt = 0;
|
|
|
|
while (tree_entry(&desc, &entry)) {
|
|
|
|
if (!S_ISDIR(entry.mode))
|
|
|
|
cnt++;
|
|
|
|
else {
|
|
|
|
struct cache_tree_sub *sub;
|
|
|
|
struct tree *subtree = lookup_tree(entry.sha1);
|
|
|
|
if (!subtree->object.parsed)
|
|
|
|
parse_tree(subtree);
|
|
|
|
sub = cache_tree_sub(it, entry.path);
|
|
|
|
sub->cache_tree = cache_tree();
|
|
|
|
prime_cache_tree_rec(sub->cache_tree, subtree);
|
|
|
|
cnt += sub->cache_tree->entry_count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
it->entry_count = cnt;
|
|
|
|
}
|
|
|
|
|
2014-06-13 20:19:33 +08:00
|
|
|
void prime_cache_tree(struct index_state *istate, struct tree *tree)
|
2009-04-20 18:58:18 +08:00
|
|
|
{
|
2014-06-13 20:19:33 +08:00
|
|
|
cache_tree_free(&istate->cache_tree);
|
|
|
|
istate->cache_tree = cache_tree();
|
|
|
|
prime_cache_tree_rec(istate->cache_tree, tree);
|
|
|
|
istate->cache_changed |= CACHE_TREE_CHANGED;
|
2009-04-20 18:58:18 +08:00
|
|
|
}
|
Optimize "diff-index --cached" using cache-tree
When running "diff-index --cached" after making a change to only a small
portion of the index, there is no point unpacking unchanged subtrees into
the index recursively, only to find that all entries match anyway. Tweak
unpack_trees() logic that is used to read in the tree object to catch the
case where the tree entry we are looking at matches the index as a whole
by looking at the cache-tree.
As an exercise, after modifying a few paths in the kernel tree, here are
a few numbers on my Athlon 64X2 3800+:
(without patch, hot cache)
$ /usr/bin/time git diff --cached --raw
:100644 100644 b57e1f5... e69de29... M Makefile
:100644 000000 8c86b72... 0000000... D arch/x86/Makefile
:000000 100644 0000000... e69de29... A arche
0.07user 0.02system 0:00.09elapsed 102%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+9407minor)pagefaults 0swaps
(with patch, hot cache)
$ /usr/bin/time ../git.git/git-diff --cached --raw
:100644 100644 b57e1f5... e69de29... M Makefile
:100644 000000 8c86b72... 0000000... D arch/x86/Makefile
:000000 100644 0000000... e69de29... A arche
0.02user 0.00system 0:00.02elapsed 103%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+2446minor)pagefaults 0swaps
Cold cache numbers are very impressive, but it does not matter very much
in practice:
(without patch, cold cache)
$ su root sh -c 'echo 3 >/proc/sys/vm/drop_caches'
$ /usr/bin/time git diff --cached --raw
:100644 100644 b57e1f5... e69de29... M Makefile
:100644 000000 8c86b72... 0000000... D arch/x86/Makefile
:000000 100644 0000000... e69de29... A arche
0.06user 0.17system 0:10.26elapsed 2%CPU (0avgtext+0avgdata 0maxresident)k
247032inputs+0outputs (1172major+8237minor)pagefaults 0swaps
(with patch, cold cache)
$ su root sh -c 'echo 3 >/proc/sys/vm/drop_caches'
$ /usr/bin/time ../git.git/git-diff --cached --raw
:100644 100644 b57e1f5... e69de29... M Makefile
:100644 000000 8c86b72... 0000000... D arch/x86/Makefile
:000000 100644 0000000... e69de29... A arche
0.02user 0.01system 0:01.01elapsed 3%CPU (0avgtext+0avgdata 0maxresident)k
18440inputs+0outputs (79major+2369minor)pagefaults 0swaps
This of course helps "git status" as well.
(without patch, hot cache)
$ /usr/bin/time ../git.git/git-status >/dev/null
0.17user 0.18system 0:00.35elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+5336outputs (0major+10970minor)pagefaults 0swaps
(with patch, hot cache)
$ /usr/bin/time ../git.git/git-status >/dev/null
0.10user 0.16system 0:00.27elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+5336outputs (0major+3921minor)pagefaults 0swaps
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-05-21 06:57:22 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* find the cache_tree that corresponds to the current level without
|
|
|
|
* exploding the full path into textual form. The root of the
|
|
|
|
* cache tree is given as "root", and our current level is "info".
|
|
|
|
* (1) When at root level, info->prev is NULL, so it is "root" itself.
|
|
|
|
* (2) Otherwise, find the cache_tree that corresponds to one level
|
|
|
|
* above us, and find ourselves in there.
|
|
|
|
*/
|
|
|
|
static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root,
|
|
|
|
struct traverse_info *info)
|
|
|
|
{
|
|
|
|
struct cache_tree *our_parent;
|
|
|
|
|
|
|
|
if (!info->prev)
|
|
|
|
return root;
|
|
|
|
our_parent = find_cache_tree_from_traversal(root, info->prev);
|
|
|
|
return cache_tree_find(our_parent, info->name.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
int cache_tree_matches_traversal(struct cache_tree *root,
|
|
|
|
struct name_entry *ent,
|
|
|
|
struct traverse_info *info)
|
|
|
|
{
|
|
|
|
struct cache_tree *it;
|
|
|
|
|
|
|
|
it = find_cache_tree_from_traversal(root, info);
|
|
|
|
it = cache_tree_find(it, ent->path);
|
|
|
|
if (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1))
|
|
|
|
return it->entry_count;
|
|
|
|
return 0;
|
|
|
|
}
|
2011-12-07 01:43:37 +08:00
|
|
|
|
2012-01-16 10:36:46 +08:00
|
|
|
int update_main_cache_tree(int flags)
|
2011-12-07 01:43:37 +08:00
|
|
|
{
|
|
|
|
if (!the_index.cache_tree)
|
|
|
|
the_index.cache_tree = cache_tree();
|
2014-06-13 20:19:32 +08:00
|
|
|
return cache_tree_update(&the_index, flags);
|
2011-12-07 01:43:37 +08:00
|
|
|
}
|