Skip to content

Commit

Permalink
Avoid running lstat(2) on the same cache entry.
Browse files Browse the repository at this point in the history
Aside from the lstat(2) done for work tree files, there are
quite many lstat(2) calls in refname dwimming codepath.  This
patch is not about reducing them.

 * It adds a new ce_flag, CE_UPTODATE, that is meant to mark the
   cache entries that record a regular file blob that is up to
   date in the work tree.  If somebody later walks the index and
   wants to see if the work tree has changes, they do not have
   to be checked with lstat(2) again.

 * fill_stat_cache_info() marks the cache entry it just added
   with CE_UPTODATE.  This has the effect of marking the paths
   we write out of the index and lstat(2) immediately as "no
   need to lstat -- we know it is up-to-date", from quite a lot
   fo callers:

    - git-apply --index
    - git-update-index
    - git-checkout-index
    - git-add (uses add_file_to_index())
    - git-commit (ditto)
    - git-mv (ditto)

 * refresh_cache_ent() also marks the cache entry that are clean
   with CE_UPTODATE.

 * write_index is changed not to write CE_UPTODATE out to the
   index file, because CE_UPTODATE is meant to be transient only
   in core.  For the same reason, CE_UPDATE is not written to
   prevent an accident from happening.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Junio C Hamano authored and Linus Torvalds committed Jan 21, 2008
1 parent 7fec10b commit eadb583
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 10 deletions.
3 changes: 3 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct cache_entry {
/* In-memory only */
#define CE_UPDATE (0x10000)
#define CE_REMOVE (0x20000)
#define CE_UPTODATE (0x40000)

static inline unsigned create_ce_flags(size_t len, unsigned stage)
{
Expand All @@ -149,6 +150,8 @@ static inline size_t ce_namelen(const struct cache_entry *ce)
#define ce_size(ce) cache_entry_size(ce_namelen(ce))
#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)

#define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
static inline unsigned int create_ce_mode(unsigned int mode)
Expand Down
23 changes: 14 additions & 9 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -1510,17 +1510,22 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
if (pos < 0)
return 0;
ce = active_cache[pos];
if ((lstat(name, &st) < 0) ||
!S_ISREG(st.st_mode) || /* careful! */
ce_match_stat(ce, &st, 0) ||
hashcmp(sha1, ce->sha1))

/*
* This is not the sha1 we are looking for, or
* unreusable because it is not a regular file.
*/
if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
return 0;
/* we return 1 only when we can stat, it is a regular file,
* stat information matches, and sha1 recorded in the cache
* matches. I.e. we know the file in the work tree really is
* the same as the <name, sha1> pair.

/*
* If ce matches the file in the work tree, we can reuse it.
*/
return 1;
if (ce_uptodate(ce) ||
(!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
return 1;

return 0;
}

static int populate_from_stdin(struct diff_filespec *s)
Expand Down
16 changes: 15 additions & 1 deletion read-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)

if (assume_unchanged)
ce->ce_flags |= CE_VALID;

if (S_ISREG(st->st_mode))
ce_mark_uptodate(ce);
}

static int ce_compare_data(struct cache_entry *ce, struct stat *st)
Expand Down Expand Up @@ -412,6 +415,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
!ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
/* Nothing changed, really */
free(ce);
ce_mark_uptodate(istate->cache[pos]);
return 0;
}

Expand Down Expand Up @@ -779,6 +783,9 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
int changed, size;
int ignore_valid = options & CE_MATCH_IGNORE_VALID;

if (ce_uptodate(ce))
return ce;

if (lstat(ce->name, &st) < 0) {
if (err)
*err = errno;
Expand All @@ -797,8 +804,15 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
if (ignore_valid && assume_unchanged &&
!(ce->ce_flags & CE_VALID))
; /* mark this one VALID again */
else
else {
/*
* We do not mark the index itself "modified"
* because CE_UPTODATE flag is in-core only;
* we are not going to write this change out.
*/
ce_mark_uptodate(ce);
return ce;
}
}

if (ie_modified(istate, ce, &st, options)) {
Expand Down

0 comments on commit eadb583

Please sign in to comment.