2006-04-24 01:52:20 +02:00
|
|
|
#include "cache.h"
|
2014-10-01 12:28:42 +02:00
|
|
|
#include "lockfile.h"
|
2006-04-24 01:52:20 +02:00
|
|
|
#include "tree.h"
|
2009-04-20 12:58:18 +02:00
|
|
|
#include "tree-walk.h"
|
2006-04-24 01:52:20 +02:00
|
|
|
#include "cache-tree.h"
|
|
|
|
|
2006-10-31 00:29:53 +01:00
|
|
|
#ifndef DEBUG
|
2006-04-24 01:52:20 +02:00
|
|
|
#define DEBUG 0
|
2006-10-31 00:29:53 +01:00
|
|
|
#endif
|
2006-04-24 01:52:20 +02: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 06:18:58 +02:00
|
|
|
void cache_tree_free(struct cache_tree **it_p)
|
2006-04-24 01:52:20 +02:00
|
|
|
{
|
|
|
|
int i;
|
2006-04-25 06:18:58 +02:00
|
|
|
struct cache_tree *it = *it_p;
|
2006-04-24 01:52:20 +02:00
|
|
|
|
|
|
|
if (!it)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < it->subtree_nr; i++)
|
2010-09-06 23:40:16 +02:00
|
|
|
if (it->down[i]) {
|
2006-04-26 02:40:02 +02:00
|
|
|
cache_tree_free(&it->down[i]->cache_tree);
|
2010-09-06 23:40:16 +02:00
|
|
|
free(it->down[i]);
|
|
|
|
}
|
2006-04-24 01:52:20 +02:00
|
|
|
free(it->down);
|
|
|
|
free(it);
|
2006-04-25 06:18:58 +02:00
|
|
|
*it_p = NULL;
|
2006-04-24 01:52:20 +02:00
|
|
|
}
|
|
|
|
|
2006-04-26 02:40:02 +02: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 01:52:20 +02: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 02:40:02 +02:00
|
|
|
int pos = subtree_pos(it, path, pathlen);
|
|
|
|
if (0 <= pos)
|
|
|
|
return it->down[pos];
|
2006-04-24 01:52:20 +02:00
|
|
|
if (!create)
|
|
|
|
return NULL;
|
2006-04-26 02:40:02 +02:00
|
|
|
|
|
|
|
pos = -pos-1;
|
2014-03-03 23:31:51 +01:00
|
|
|
ALLOC_GROW(it->down, it->subtree_nr + 1, it->subtree_alloc);
|
2006-04-26 02:40:02 +02:00
|
|
|
it->subtree_nr++;
|
|
|
|
|
2006-04-24 01:52:20 +02:00
|
|
|
down = xmalloc(sizeof(*down) + pathlen + 1);
|
2006-04-26 02:40:02 +02:00
|
|
|
down->cache_tree = NULL;
|
2006-04-24 01:52:20 +02:00
|
|
|
down->namelen = pathlen;
|
|
|
|
memcpy(down->name, path, pathlen);
|
2006-04-26 02:40:02 +02: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 01:52:20 +02:00
|
|
|
return down;
|
|
|
|
}
|
|
|
|
|
2006-04-27 10:33:07 +02: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 14:19:31 +02:00
|
|
|
static int do_invalidate_path(struct cache_tree *it, const char *path)
|
2006-04-24 01:52:20 +02: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 01:10:45 +02:00
|
|
|
#if DEBUG
|
|
|
|
fprintf(stderr, "cache-tree invalidate <%s>\n", path);
|
|
|
|
#endif
|
|
|
|
|
2006-04-24 01:52:20 +02:00
|
|
|
if (!it)
|
2014-06-13 14:19:31 +02:00
|
|
|
return 0;
|
2014-03-08 07:48:31 +01:00
|
|
|
slash = strchrnul(path, '/');
|
|
|
|
namelen = slash - path;
|
2006-04-24 01:52:20 +02:00
|
|
|
it->entry_count = -1;
|
2014-03-08 07:48:31 +01:00
|
|
|
if (!*slash) {
|
2006-04-26 02:40:02 +02: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 01:52:20 +02:00
|
|
|
/* 0 1 2 3 4 5
|
|
|
|
* ^ ^subtree_nr = 6
|
2006-04-26 02:40:02 +02:00
|
|
|
* pos
|
2006-04-24 01:52:20 +02:00
|
|
|
* move 4 and 5 up one place (2 entries)
|
2006-04-26 02:40:02 +02:00
|
|
|
* 2 = 6 - 3 - 1 = subtree_nr - pos - 1
|
2006-04-24 01:52:20 +02:00
|
|
|
*/
|
2006-04-26 02:40:02 +02:00
|
|
|
memmove(it->down+pos, it->down+pos+1,
|
2006-04-24 01:52:20 +02:00
|
|
|
sizeof(struct cache_tree_sub *) *
|
2006-04-26 02:40:02 +02:00
|
|
|
(it->subtree_nr - pos - 1));
|
2006-04-24 01:52:20 +02:00
|
|
|
it->subtree_nr--;
|
|
|
|
}
|
2014-06-13 14:19:31 +02:00
|
|
|
return 1;
|
2006-04-24 01:52:20 +02:00
|
|
|
}
|
|
|
|
down = find_subtree(it, path, namelen, 0);
|
|
|
|
if (down)
|
2014-06-13 14:19:31 +02: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 01:52:20 +02:00
|
|
|
}
|
|
|
|
|
2014-06-13 14:19:32 +02:00
|
|
|
static int verify_cache(struct cache_entry **cache,
|
2012-01-16 03:36:46 +01:00
|
|
|
int entries, int flags)
|
2006-04-24 01:52:20 +02:00
|
|
|
{
|
|
|
|
int i, funny;
|
2012-01-16 03:36:46 +01:00
|
|
|
int silent = flags & WRITE_TREE_SILENT;
|
2006-04-24 01:52:20 +02: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 17:29:00 +02: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-07 20:55:48 +01:00
|
|
|
if (ce_stage(ce)) {
|
2011-12-06 18:43:37 +01:00
|
|
|
if (silent)
|
|
|
|
return -1;
|
2006-04-24 01:52:20 +02:00
|
|
|
if (10 < ++funny) {
|
|
|
|
fprintf(stderr, "...\n");
|
|
|
|
break;
|
|
|
|
}
|
2012-12-16 05:15:25 +01:00
|
|
|
fprintf(stderr, "%s: unmerged (%s)\n",
|
|
|
|
ce->name, sha1_to_hex(ce->sha1));
|
2006-04-24 01:52:20 +02: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 06:18:58 +02:00
|
|
|
cache_tree_free(&s->cache_tree);
|
2006-04-24 01:52:20 +02:00
|
|
|
free(s);
|
|
|
|
it->subtree_nr--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-25 06:18:58 +02: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 01:52:20 +02:00
|
|
|
static int update_one(struct cache_tree *it,
|
2014-06-13 14:19:32 +02:00
|
|
|
struct cache_entry **cache,
|
2006-04-24 01:52:20 +02:00
|
|
|
int entries,
|
|
|
|
const char *base,
|
|
|
|
int baselen,
|
2012-12-16 05:15:27 +01:00
|
|
|
int *skip_count,
|
2012-01-16 03:36:46 +01:00
|
|
|
int flags)
|
2006-04-24 01:52:20 +02:00
|
|
|
{
|
2007-09-06 13:20:11 +02:00
|
|
|
struct strbuf buffer;
|
2012-01-16 03:36:46 +01:00
|
|
|
int missing_ok = flags & WRITE_TREE_MISSING_OK;
|
|
|
|
int dryrun = flags & WRITE_TREE_DRY_RUN;
|
2014-07-06 06:06:56 +02:00
|
|
|
int repair = flags & WRITE_TREE_REPAIR;
|
2012-12-16 05:15:28 +01:00
|
|
|
int to_invalidate = 0;
|
2006-04-24 01:52:20 +02:00
|
|
|
int i;
|
|
|
|
|
2014-07-06 06:06:56 +02:00
|
|
|
assert(!(dryrun && repair));
|
|
|
|
|
2012-12-16 05:15:27 +01:00
|
|
|
*skip_count = 0;
|
|
|
|
|
2006-04-25 00:12:42 +02:00
|
|
|
if (0 <= it->entry_count && has_sha1_file(it->sha1))
|
2006-04-24 01:52:20 +02: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 05:15:26 +01: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 17:29:00 +02:00
|
|
|
const struct cache_entry *ce = cache[i];
|
2006-04-24 01:52:20 +02:00
|
|
|
struct cache_tree_sub *sub;
|
|
|
|
const char *path, *slash;
|
2012-12-16 05:15:27 +01:00
|
|
|
int pathlen, sublen, subcnt, subskip;
|
2006-04-24 01:52:20 +02: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 05:15:26 +01:00
|
|
|
if (!slash) {
|
|
|
|
i++;
|
2006-04-24 01:52:20 +02:00
|
|
|
continue;
|
2012-12-16 05:15:26 +01:00
|
|
|
}
|
2006-04-24 01:52:20 +02: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 05:15:27 +01:00
|
|
|
&subskip,
|
2012-01-16 03:36:46 +01:00
|
|
|
flags);
|
2006-11-13 14:50:00 +01: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-29 18:11:58 +01:00
|
|
|
if (!subcnt)
|
|
|
|
die("index cache-tree records empty sub-tree");
|
2012-12-16 05:15:26 +01:00
|
|
|
i += subcnt;
|
2012-12-16 05:15:27 +01:00
|
|
|
sub->count = subcnt; /* to be used in the next loop */
|
|
|
|
*skip_count += subskip;
|
2006-04-24 01:52:20 +02:00
|
|
|
sub->used = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
discard_unused_subtrees(it);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Then write out the tree object for this level.
|
|
|
|
*/
|
2007-09-10 12:35:04 +02:00
|
|
|
strbuf_init(&buffer, 8192);
|
2006-04-24 01:52:20 +02:00
|
|
|
|
2012-12-16 05:15:26 +01: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 17:29:00 +02:00
|
|
|
const struct cache_entry *ce = cache[i];
|
2006-04-24 01:52:20 +02:00
|
|
|
struct cache_tree_sub *sub;
|
|
|
|
const char *path, *slash;
|
|
|
|
int pathlen, entlen;
|
|
|
|
const unsigned char *sha1;
|
|
|
|
unsigned mode;
|
2014-09-02 23:16:20 +02:00
|
|
|
int expected_missing = 0;
|
2006-04-24 01:52:20 +02: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 05:15:27 +01:00
|
|
|
i += sub->count;
|
2006-04-24 01:52:20 +02:00
|
|
|
sha1 = sub->cache_tree->sha1;
|
|
|
|
mode = S_IFDIR;
|
2014-09-02 23:16:20 +02:00
|
|
|
if (sub->cache_tree->entry_count < 0) {
|
2012-12-16 05:15:28 +01:00
|
|
|
to_invalidate = 1;
|
2014-09-02 23:16:20 +02:00
|
|
|
expected_missing = 1;
|
|
|
|
}
|
2006-04-24 01:52:20 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
sha1 = ce->sha1;
|
2008-01-15 01:03:17 +01:00
|
|
|
mode = ce->ce_mode;
|
2006-04-24 01:52:20 +02:00
|
|
|
entlen = pathlen - baselen;
|
2012-12-16 05:15:26 +01:00
|
|
|
i++;
|
2006-04-24 01:52:20 +02:00
|
|
|
}
|
2010-08-10 05:32:11 +02:00
|
|
|
if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
|
|
|
|
strbuf_release(&buffer);
|
2014-09-02 23:16:20 +02:00
|
|
|
if (expected_missing)
|
|
|
|
return -1;
|
2009-07-14 20:25:17 +02:00
|
|
|
return error("invalid object %06o %s for '%.*s'",
|
|
|
|
mode, sha1_to_hex(sha1), entlen+baselen, path);
|
2010-08-10 05:32:11 +02:00
|
|
|
}
|
2006-04-24 01:52:20 +02:00
|
|
|
|
2012-12-16 05:15:27 +01: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 05:15:28 +01: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 05:15:27 +01:00
|
|
|
continue;
|
2012-12-16 05:15:28 +01:00
|
|
|
}
|
2006-04-24 01:52:20 +02:00
|
|
|
|
2007-09-06 13:20:11 +02: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 01:52:20 +02:00
|
|
|
|
|
|
|
#if DEBUG
|
2006-05-04 01:10:45 +02:00
|
|
|
fprintf(stderr, "cache-tree update-one %o %.*s\n",
|
2006-04-24 01:52:20 +02:00
|
|
|
mode, entlen, path + baselen);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-07-06 06:06:56 +02: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 13:20:11 +02:00
|
|
|
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
|
2008-04-23 18:47:17 +02:00
|
|
|
else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
|
|
|
|
strbuf_release(&buffer);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-09-06 13:20:11 +02:00
|
|
|
strbuf_release(&buffer);
|
2012-12-16 05:15:28 +01:00
|
|
|
it->entry_count = to_invalidate ? -1 : i - *skip_count;
|
2006-04-24 01:52:20 +02:00
|
|
|
#if DEBUG
|
2006-05-04 01:10:45 +02:00
|
|
|
fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
|
2006-04-24 01:52:20 +02:00
|
|
|
it->entry_count, it->subtree_nr,
|
|
|
|
sha1_to_hex(it->sha1));
|
|
|
|
#endif
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2014-06-13 14:19:32 +02:00
|
|
|
int cache_tree_update(struct index_state *istate, int flags)
|
2006-04-24 01:52:20 +02:00
|
|
|
{
|
2014-06-13 14:19:32 +02: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 01:52:20 +02:00
|
|
|
if (i)
|
|
|
|
return i;
|
2012-12-16 05:15:27 +01:00
|
|
|
i = update_one(it, cache, entries, "", 0, &skip, flags);
|
2006-04-24 01:52:20 +02:00
|
|
|
if (i < 0)
|
|
|
|
return i;
|
2014-06-13 14:19:32 +02:00
|
|
|
istate->cache_changed |= CACHE_TREE_CHANGED;
|
2006-04-24 01:52:20 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-25 10:22:44 +02:00
|
|
|
static void write_one(struct strbuf *buffer, struct cache_tree *it,
|
|
|
|
const char *path, int pathlen)
|
2006-04-24 01:52:20 +02: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 13:20:11 +02: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 01:52:20 +02: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 13:20:11 +02:00
|
|
|
strbuf_add(buffer, it->sha1, 20);
|
2006-04-24 01:52:20 +02:00
|
|
|
}
|
|
|
|
for (i = 0; i < it->subtree_nr; i++) {
|
|
|
|
struct cache_tree_sub *down = it->down[i];
|
2006-04-26 02:40:02 +02: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 10:22:44 +02:00
|
|
|
write_one(buffer, down->cache_tree, down->name, down->namelen);
|
2006-04-24 01:52:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-25 10:22:44 +02:00
|
|
|
void cache_tree_write(struct strbuf *sb, struct cache_tree *root)
|
2006-04-24 01:52:20 +02:00
|
|
|
{
|
2007-09-25 10:22:44 +02:00
|
|
|
write_one(sb, root, "", 0);
|
2006-04-24 01:52:20 +02: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 03:31:02 +02:00
|
|
|
const char *cp;
|
|
|
|
char *ep;
|
2006-04-24 01:52:20 +02: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 03:31:02 +02: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 01:52:20 +02: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 08:19:43 +02:00
|
|
|
hashcpy(it->sha1, (const unsigned char*)buf);
|
2006-04-24 01:52:20 +02: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 02:40:02 +02:00
|
|
|
struct cache_tree_sub *subtree;
|
2006-04-24 01:52:20 +02:00
|
|
|
const char *name = buf;
|
2006-04-27 10:33:07 +02:00
|
|
|
|
2006-04-24 01:52:20 +02:00
|
|
|
sub = read_one(&buf, &size);
|
|
|
|
if (!sub)
|
|
|
|
goto free_return;
|
2006-04-27 10:33:07 +02:00
|
|
|
subtree = cache_tree_sub(it, name);
|
2006-04-26 02:40:02 +02:00
|
|
|
subtree->cache_tree = sub;
|
2006-04-24 01:52:20 +02:00
|
|
|
}
|
|
|
|
if (subtree_nr != it->subtree_nr)
|
|
|
|
die("cache-tree: internal error");
|
|
|
|
*buffer = buf;
|
|
|
|
*size_p = size;
|
|
|
|
return it;
|
|
|
|
|
|
|
|
free_return:
|
2006-04-25 06:18:58 +02:00
|
|
|
cache_tree_free(&it);
|
2006-04-24 01:52:20 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-04-25 06:18:58 +02:00
|
|
|
struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
|
2006-04-24 01:52:20 +02:00
|
|
|
{
|
2006-04-25 06:18:58 +02:00
|
|
|
if (buffer[0])
|
2006-04-24 01:52:20 +02:00
|
|
|
return NULL; /* not the whole tree */
|
|
|
|
return read_one(&buffer, &size);
|
|
|
|
}
|
2006-04-26 10:20:50 +02:00
|
|
|
|
2008-07-16 12:42:10 +02:00
|
|
|
static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
|
2006-04-26 10:20:50 +02:00
|
|
|
{
|
2009-05-21 00:53:57 +02:00
|
|
|
if (!it)
|
|
|
|
return NULL;
|
2006-04-26 10:20:50 +02:00
|
|
|
while (*path) {
|
|
|
|
const char *slash;
|
|
|
|
struct cache_tree_sub *sub;
|
|
|
|
|
2014-03-05 18:26:26 +01:00
|
|
|
slash = strchrnul(path, '/');
|
2014-03-05 18:26:27 +01:00
|
|
|
/*
|
|
|
|
* Between path and slash is the name of the subtree
|
|
|
|
* to look for.
|
2006-04-26 10:20:50 +02:00
|
|
|
*/
|
|
|
|
sub = find_subtree(it, path, slash - path, 0);
|
|
|
|
if (!sub)
|
|
|
|
return NULL;
|
|
|
|
it = sub->cache_tree;
|
2014-03-05 18:26:30 +01:00
|
|
|
|
2006-04-26 10:20:50 +02:00
|
|
|
path = slash;
|
2014-03-05 18:26:30 +01:00
|
|
|
while (*path == '/')
|
|
|
|
path++;
|
2006-04-26 10:20:50 +02:00
|
|
|
}
|
|
|
|
return it;
|
|
|
|
}
|
2008-01-11 07:49:35 +01:00
|
|
|
|
2015-08-04 15:51:40 +02: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 07:49:35 +01:00
|
|
|
{
|
|
|
|
int entries, was_valid, newfd;
|
2009-05-20 20:04:35 +02:00
|
|
|
struct lock_file *lock_file;
|
2008-01-11 07:49:35 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We can't free this memory, it becomes part of a linked list
|
|
|
|
* parsed atexit()
|
|
|
|
*/
|
2009-05-20 20:04:35 +02:00
|
|
|
lock_file = xcalloc(1, sizeof(struct lock_file));
|
2008-01-11 07:49:35 +01:00
|
|
|
|
2015-08-04 15:51:40 +02:00
|
|
|
newfd = hold_lock_file_for_update(lock_file, index_path, LOCK_DIE_ON_ERROR);
|
2008-01-11 07:49:35 +01:00
|
|
|
|
2015-08-04 15:51:40 +02:00
|
|
|
entries = read_index_from(index_state, index_path);
|
2008-01-11 07:49:35 +01:00
|
|
|
if (entries < 0)
|
|
|
|
return WRITE_TREE_UNREADABLE_INDEX;
|
2009-05-20 20:04:35 +02:00
|
|
|
if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
|
2015-08-04 15:51:40 +02:00
|
|
|
cache_tree_free(&index_state->cache_tree);
|
2008-01-11 07:49:35 +01:00
|
|
|
|
2015-08-04 15:51:40 +02:00
|
|
|
if (!index_state->cache_tree)
|
|
|
|
index_state->cache_tree = cache_tree();
|
2008-01-11 07:49:35 +01:00
|
|
|
|
2015-08-04 15:51:40 +02:00
|
|
|
was_valid = cache_tree_fully_valid(index_state->cache_tree);
|
2008-01-11 07:49:35 +01:00
|
|
|
if (!was_valid) {
|
2015-08-04 15:51:40 +02:00
|
|
|
if (cache_tree_update(index_state, flags) < 0)
|
2008-01-11 07:49:35 +01:00
|
|
|
return WRITE_TREE_UNMERGED_INDEX;
|
|
|
|
if (0 <= newfd) {
|
2015-08-04 15:51:40 +02:00
|
|
|
if (!write_locked_index(index_state, lock_file, COMMIT_LOCK))
|
2008-01-11 07:49:35 +01:00
|
|
|
newfd = -1;
|
|
|
|
}
|
|
|
|
/* Not being able to write is fine -- we are only interested
|
|
|
|
* in updating the cache-tree part, and if |