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>
		
	
		
			
				
	
	
		
			102 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "cache.h"
 | 
						|
#include "dir.h"
 | 
						|
#include "pathspec.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * Finds which of the given pathspecs match items in the index.
 | 
						|
 *
 | 
						|
 * For each pathspec, sets the corresponding entry in the seen[] array
 | 
						|
 * (which should be specs items long, i.e. the same size as pathspec)
 | 
						|
 * to the nature of the "closest" (i.e. most specific) match found for
 | 
						|
 * that pathspec in the index, if it was a closer type of match than
 | 
						|
 * the existing entry.  As an optimization, matching is skipped
 | 
						|
 * altogether if seen[] already only contains non-zero entries.
 | 
						|
 *
 | 
						|
 * If seen[] has not already been written to, it may make sense
 | 
						|
 * to use find_pathspecs_matching_against_index() instead.
 | 
						|
 */
 | 
						|
void add_pathspec_matches_against_index(const char **pathspec,
 | 
						|
					char *seen, int specs)
 | 
						|
{
 | 
						|
	int num_unmatched = 0, i;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Since we are walking the index as if we were walking the directory,
 | 
						|
	 * we have to mark the matched pathspec as seen; otherwise we will
 | 
						|
	 * mistakenly think that the user gave a pathspec that did not match
 | 
						|
	 * anything.
 | 
						|
	 */
 | 
						|
	for (i = 0; i < specs; i++)
 | 
						|
		if (!seen[i])
 | 
						|
			num_unmatched++;
 | 
						|
	if (!num_unmatched)
 | 
						|
		return;
 | 
						|
	for (i = 0; i < active_nr; i++) {
 | 
						|
		const struct cache_entry *ce = active_cache[i];
 | 
						|
		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Finds which of the given pathspecs match items in the index.
 | 
						|
 *
 | 
						|
 * This is a one-shot wrapper around add_pathspec_matches_against_index()
 | 
						|
 * which allocates, populates, and returns a seen[] array indicating the
 | 
						|
 * nature of the "closest" (i.e. most specific) matches which each of the
 | 
						|
 * given pathspecs achieves against all items in the index.
 | 
						|
 */
 | 
						|
char *find_pathspecs_matching_against_index(const char **pathspec)
 | 
						|
{
 | 
						|
	char *seen;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; pathspec[i];  i++)
 | 
						|
		; /* just counting */
 | 
						|
	seen = xcalloc(i, 1);
 | 
						|
	add_pathspec_matches_against_index(pathspec, seen, i);
 | 
						|
	return seen;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Check the index to see whether path refers to a submodule, or
 | 
						|
 * something inside a submodule.  If the former, returns the path with
 | 
						|
 * any trailing slash stripped.  If the latter, dies with an error
 | 
						|
 * message.
 | 
						|
 */
 | 
						|
const char *check_path_for_gitlink(const char *path)
 | 
						|
{
 | 
						|
	int i, path_len = strlen(path);
 | 
						|
	for (i = 0; i < active_nr; i++) {
 | 
						|
		const struct cache_entry *ce = active_cache[i];
 | 
						|
		if (S_ISGITLINK(ce->ce_mode)) {
 | 
						|
			int ce_len = ce_namelen(ce);
 | 
						|
			if (path_len <= ce_len || path[ce_len] != '/' ||
 | 
						|
			    memcmp(ce->name, path, ce_len))
 | 
						|
				/* path does not refer to this
 | 
						|
				 * submodule or anything inside it */
 | 
						|
				continue;
 | 
						|
			if (path_len == ce_len + 1) {
 | 
						|
				/* path refers to submodule;
 | 
						|
				 * strip trailing slash */
 | 
						|
				return xstrndup(ce->name, ce_len);
 | 
						|
			} else {
 | 
						|
				die (_("Path '%s' is in submodule '%.*s'"),
 | 
						|
				     path, ce_len, ce->name);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return path;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Dies if the given path refers to a file inside a symlinked
 | 
						|
 * directory in the index.
 | 
						|
 */
 | 
						|
void die_if_path_beyond_symlink(const char *path, const char *prefix)
 | 
						|
{
 | 
						|
	if (has_symlink_leading_path(path, strlen(path))) {
 | 
						|
		int len = prefix ? strlen(prefix) : 0;
 | 
						|
		die(_("'%s' is beyond a symbolic link"), path + len);
 | 
						|
	}
 | 
						|
}
 |