Skip to content

Commit 5f73076

Browse files
author
Junio C Hamano
committed
"Assume unchanged" git
This adds "assume unchanged" logic, started by this message in the list discussion recently: <Pine.LNX.4.64.0601311807470.7301@g5.osdl.org> This is a workaround for filesystems that do not have lstat() that is quick enough for the index mechanism to take advantage of. On the paths marked as "assumed to be unchanged", the user needs to explicitly use update-index to register the object name to be in the next commit. You can use two new options to update-index to set and reset the CE_VALID bit: git-update-index --assume-unchanged path... git-update-index --no-assume-unchanged path... These forms manipulate only the CE_VALID bit; it does not change the object name recorded in the index file. Nor they add a new entry to the index. When the configuration variable "core.ignorestat = true" is set, the index entries are marked with CE_VALID bit automatically after: - update-index to explicitly register the current object name to the index file. - when update-index --refresh finds the path to be up-to-date. - when tools like read-tree -u and apply --index update the working tree file and register the current object name to the index file. The flag is dropped upon read-tree that does not check out the index entry. This happens regardless of the core.ignorestat settings. Index entries marked with CE_VALID bit are assumed to be unchanged most of the time. However, there are cases that CE_VALID bit is ignored for the sake of safety and usability: - while "git-read-tree -m" or git-apply need to make sure that the paths involved in the merge do not have local modifications. This sacrifices performance for safety. - when git-checkout-index -f -q -u -a tries to see if it needs to checkout the paths. Otherwise you can never check anything out ;-). - when git-update-index --really-refresh (a new flag) tries to see if the index entry is up to date. You can start with everything marked as CE_VALID and run this once to drop CE_VALID bit for paths that are modified. Most notably, "update-index --refresh" honours CE_VALID and does not actively stat, so after you modified a file in the working tree, update-index --refresh would not notice until you tell the index about it with "git-update-index path" or "git-update-index --no-assume-unchanged path". This version is not expected to be perfect. I think diff between index and/or tree and working files may need some adjustment, and there probably needs other cases we should automatically unmark paths that are marked to be CE_VALID. But the basics seem to work, and ready to be tested by people who asked for this feature. Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent d19e06f commit 5f73076

File tree

13 files changed

+99
-21
lines changed

13 files changed

+99
-21
lines changed

apply.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,7 @@ static int check_patch(struct patch *patch)
13091309
return -1;
13101310
}
13111311

1312-
changed = ce_match_stat(active_cache[pos], &st);
1312+
changed = ce_match_stat(active_cache[pos], &st, 1);
13131313
if (changed)
13141314
return error("%s: does not match index",
13151315
old_name);

cache.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ struct cache_entry {
9191
#define CE_NAMEMASK (0x0fff)
9292
#define CE_STAGEMASK (0x3000)
9393
#define CE_UPDATE (0x4000)
94+
#define CE_VALID (0x8000)
9495
#define CE_STAGESHIFT 12
9596

9697
#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
@@ -144,8 +145,8 @@ extern int add_cache_entry(struct cache_entry *ce, int option);
144145
extern int remove_cache_entry_at(int pos);
145146
extern int remove_file_from_cache(const char *path);
146147
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
147-
extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
148-
extern int ce_modified(struct cache_entry *ce, struct stat *st);
148+
extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
149+
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
149150
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
150151
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
151152
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
@@ -161,6 +162,7 @@ extern int commit_index_file(struct cache_file *);
161162
extern void rollback_index_file(struct cache_file *);
162163

163164
extern int trust_executable_bit;
165+
extern int assume_unchanged;
164166
extern int only_use_symrefs;
165167
extern int diff_rename_limit_default;
166168
extern int shared_repository;

checkout-index.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ int main(int argc, char **argv)
116116
int all = 0;
117117

118118
prefix = setup_git_directory();
119+
git_config(git_default_config);
119120
prefix_length = prefix ? strlen(prefix) : 0;
120121

121122
if (read_cache() < 0) {

config.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ int git_default_config(const char *var, const char *value)
222222
return 0;
223223
}
224224

225+
if (!strcmp(var, "core.ignorestat")) {
226+
assume_unchanged = git_config_bool(var, value);
227+
return 0;
228+
}
229+
225230
if (!strcmp(var, "core.symrefsonly")) {
226231
only_use_symrefs = git_config_bool(var, value);
227232
return 0;

diff-files.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ int main(int argc, const char **argv)
191191
show_file('-', ce);
192192
continue;
193193
}
194-
changed = ce_match_stat(ce, &st);
194+
changed = ce_match_stat(ce, &st, 0);
195195
if (!changed && !diff_options.find_copies_harder)
196196
continue;
197197
oldmode = ntohl(ce->ce_mode);

diff-index.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ static int get_stat_data(struct cache_entry *ce,
3333
}
3434
return -1;
3535
}
36-
changed = ce_match_stat(ce, &st);
36+
changed = ce_match_stat(ce, &st, 0);
3737
if (changed) {
3838
mode = create_ce_mode(st.st_mode);
3939
if (!trust_executable_bit &&

diff.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1)
311311
ce = active_cache[pos];
312312
if ((lstat(name, &st) < 0) ||
313313
!S_ISREG(st.st_mode) || /* careful! */
314-
ce_match_stat(ce, &st) ||
314+
ce_match_stat(ce, &st, 0) ||
315315
memcmp(sha1, ce->sha1, 20))
316316
return 0;
317317
/* we return 1 only when we can stat, it is a regular file,

entry.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state)
123123
strcpy(path + len, ce->name);
124124

125125
if (!lstat(path, &st)) {
126-
unsigned changed = ce_match_stat(ce, &st);
126+
unsigned changed = ce_match_stat(ce, &st, 1);
127127
if (!changed)
128128
return 0;
129129
if (!state->force) {

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
char git_default_email[MAX_GITNAME];
1313
char git_default_name[MAX_GITNAME];
1414
int trust_executable_bit = 1;
15+
int assume_unchanged = 0;
1516
int only_use_symrefs = 0;
1617
int repository_format_version = 0;
1718
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";

read-cache.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
2727
ce->ce_uid = htonl(st->st_uid);
2828
ce->ce_gid = htonl(st->st_gid);
2929
ce->ce_size = htonl(st->st_size);
30+
31+
if (assume_unchanged)
32+
ce->ce_flags |= htons(CE_VALID);
3033
}
3134

3235
static int ce_compare_data(struct cache_entry *ce, struct stat *st)
@@ -146,9 +149,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
146149
return changed;
147150
}
148151

149-
int ce_match_stat(struct cache_entry *ce, struct stat *st)
152+
int ce_match_stat(struct cache_entry *ce, struct stat *st, int ignore_valid)
150153
{
151-
unsigned int changed = ce_match_stat_basic(ce, st);
154+
unsigned int changed;
155+
156+
/*
157+
* If it's marked as always valid in the index, it's
158+
* valid whatever the checked-out copy says.
159+
*/
160+
if (!ignore_valid && (ce->ce_flags & htons(CE_VALID)))
161+
return 0;
162+
163+
changed = ce_match_stat_basic(ce, st);
152164

153165
/*
154166
* Within 1 second of this sequence:
@@ -164,7 +176,7 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
164176
* effectively mean we can make at most one commit per second,
165177
* which is not acceptable. Instead, we check cache entries
166178
* whose mtime are the same as the index file timestamp more
167-
* careful than others.
179+
* carefully than others.
168180
*/
169181
if (!changed &&
170182
index_file_timestamp &&
@@ -174,10 +186,10 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
174186
return changed;
175187
}
176188

177-
int ce_modified(struct cache_entry *ce, struct stat *st)
189+
int ce_modified(struct cache_entry *ce, struct stat *st, int really)
178190
{
179191
int changed, changed_fs;
180-
changed = ce_match_stat(ce, st);
192+
changed = ce_match_stat(ce, st, really);
181193
if (!changed)
182194
return 0;
183195
/*
@@ -233,6 +245,11 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
233245
return -1;
234246
if (len1 > len2)
235247
return 1;
248+
249+
/* Differences between "assume up-to-date" should not matter. */
250+
flags1 &= ~CE_VALID;
251+
flags2 &= ~CE_VALID;
252+
236253
if (flags1 < flags2)
237254
return -1;
238255
if (flags1 > flags2)
@@ -430,6 +447,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
430447
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
431448
int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
432449
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
450+
433451
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
434452

435453
/* existing match? Just replace it. */

0 commit comments

Comments
 (0)