Skip to content

Commit

Permalink
Merge branch 'jc/nostat'
Browse files Browse the repository at this point in the history
* jc/nostat:
  cache_name_compare() compares name and stage, nothing else.
  "assume unchanged" git: documentation.
  ls-files: split "show-valid-bit" into a different option.
  "Assume unchanged" git: --really-refresh fix.
  ls-files: debugging aid for CE_VALID changes.
  "Assume unchanged" git: do not set CE_VALID with --refresh
  "Assume unchanged" git
  • Loading branch information
Junio C Hamano committed Feb 22, 2006
2 parents 712b1dd + 7b80be1 commit ee07226
Show file tree
Hide file tree
Showing 16 changed files with 222 additions and 27 deletions.
7 changes: 6 additions & 1 deletion Documentation/git-ls-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ git-ls-files - Information about files in the index/working directory

SYNOPSIS
--------
'git-ls-files' [-z] [-t]
[verse]
'git-ls-files' [-z] [-t] [-v]
(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])\*
(-[c|d|o|i|s|u|k|m])\*
[-x <pattern>|--exclude=<pattern>]
Expand Down Expand Up @@ -87,6 +88,10 @@ OPTIONS
K:: to be killed
?:: other

-v::
Similar to `-t`, but use lowercase letters for files
that are marked as 'always matching index'.

--full-name::
When run from a subdirectory, the command usually
outputs paths relative to the current directory. This
Expand Down
78 changes: 78 additions & 0 deletions Documentation/git-update-index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ git-update-index - Modifies the index or directory cache

SYNOPSIS
--------
[verse]
'git-update-index'
[--add] [--remove | --force-remove] [--replace]
[--refresh [-q] [--unmerged] [--ignore-missing]]
[--cacheinfo <mode> <object> <file>]\*
[--chmod=(+|-)x]
[--assume-unchanged | --no-assume-unchanged]
[--really-refresh]
[--info-only] [--index-info]
[-z] [--stdin]
[--verbose]
Expand Down Expand Up @@ -65,6 +68,18 @@ OPTIONS
--chmod=(+|-)x::
Set the execute permissions on the updated files.

--assume-unchanged, --no-assume-unchanged::
When these flags are specified, the object name recorded
for the paths are not updated. Instead, these options
sets and unsets the "assume unchanged" bit for the
paths. When the "assume unchanged" bit is on, git stops
checking the working tree files for possible
modifications, so you need to manually unset the bit to
tell git when you change the working tree file. This is
sometimes helpful when working with a big project on a
filesystem that has very slow lstat(2) system call
(e.g. cifs).

--info-only::
Do not create objects in the object database for all
<file> arguments that follow this flag; just insert
Expand Down Expand Up @@ -193,6 +208,37 @@ $ git ls-files -s
------------


Using "assume unchanged" bit
----------------------------

Many operations in git depend on your filesystem to have an
efficient `lstat(2)` implementation, so that `st_mtime`
information for working tree files can be cheaply checked to see
if the file contents have changed from the version recorded in
the index file. Unfortunately, some filesystems have
inefficient `lstat(2)`. If your filesystem is one of them, you
can set "assume unchanged" bit to paths you have not changed to
cause git not to do this check. Note that setting this bit on a
path does not mean git will check the contents of the file to
see if it has changed -- it makes git to omit any checking and
assume it has *not* changed. When you make changes to working
tree files, you have to explicitly tell git about it by dropping
"assume unchanged" bit, either before or after you modify them.

In order to set "assume unchanged" bit, use `--assume-unchanged`
option. To unset, use `--no-assume-unchanged`.

The command looks at `core.ignorestat` configuration variable. When
this is true, paths updated with `git-update-index paths...` and
paths updated with other git commands that update both index and
working tree (e.g. `git-apply --index`, `git-checkout-index -u`,
and `git-read-tree -u`) are automatically marked as "assume
unchanged". Note that "assume unchanged" bit is *not* set if
`git-update-index --refresh` finds the working tree file matches
the index (use `git-update-index --really-refresh` if you want
to mark them as "assume unchanged").


Examples
--------
To update and refresh only the files already checked out:
Expand All @@ -201,6 +247,35 @@ To update and refresh only the files already checked out:
$ git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh
----------------

On an inefficient filesystem with `core.ignorestat` set:

------------
$ git update-index --really-refresh <1>
$ git update-index --no-assume-unchanged foo.c <2>
$ git diff --name-only <3>
$ edit foo.c
$ git diff --name-only <4>
M foo.c
$ git update-index foo.c <5>
$ git diff --name-only <6>
$ edit foo.c
$ git diff --name-only <7>
$ git update-index --no-assume-unchanged foo.c <8>
$ git diff --name-only <9>
M foo.c

<1> forces lstat(2) to set "assume unchanged" bits for paths
that match index.
<2> mark the path to be edited.
<3> this does lstat(2) and finds index matches the path.
<4> this does lstat(2) and finds index does not match the path.
<5> registering the new version to index sets "assume unchanged" bit.
<6> and it is assumed unchanged.
<7> even after you edit it.
<8> you can tell about the change after the fact.
<9> now it checks with lstat(2) and finds it has been changed.
------------


Configuration
-------------
Expand All @@ -213,6 +288,9 @@ in the index and the file mode on the filesystem if they differ only on
executable bit. On such an unfortunate filesystem, you may
need to use `git-update-index --chmod=`.

The command looks at `core.ignorestat` configuration variable. See
'Using "assume unchanged" bit' section above.


See Also
--------
Expand Down
2 changes: 1 addition & 1 deletion apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,7 @@ static int check_patch(struct patch *patch)
return -1;
}

changed = ce_match_stat(active_cache[pos], &st);
changed = ce_match_stat(active_cache[pos], &st, 1);
if (changed)
return error("%s: does not match index",
old_name);
Expand Down
6 changes: 4 additions & 2 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ struct cache_entry {
#define CE_NAMEMASK (0x0fff)
#define CE_STAGEMASK (0x3000)
#define CE_UPDATE (0x4000)
#define CE_VALID (0x8000)
#define CE_STAGESHIFT 12

#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
Expand Down Expand Up @@ -144,8 +145,8 @@ extern int add_cache_entry(struct cache_entry *ce, int option);
extern int remove_cache_entry_at(int pos);
extern int remove_file_from_cache(const char *path);
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
extern int ce_modified(struct cache_entry *ce, struct stat *st);
extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
Expand All @@ -161,6 +162,7 @@ extern int commit_index_file(struct cache_file *);
extern void rollback_index_file(struct cache_file *);

extern int trust_executable_bit;
extern int assume_unchanged;
extern int only_use_symrefs;
extern int diff_rename_limit_default;
extern int shared_repository;
Expand Down
1 change: 1 addition & 0 deletions checkout-index.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ int main(int argc, char **argv)
int all = 0;

prefix = setup_git_directory();
git_config(git_default_config);
prefix_length = prefix ? strlen(prefix) : 0;

if (read_cache() < 0) {
Expand Down
5 changes: 5 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}

if (!strcmp(var, "core.ignorestat")) {
assume_unchanged = git_config_bool(var, value);
return 0;
}

if (!strcmp(var, "core.symrefsonly")) {
only_use_symrefs = git_config_bool(var, value);
return 0;
Expand Down
2 changes: 1 addition & 1 deletion diff-files.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ int main(int argc, const char **argv)
show_file('-', ce);
continue;
}
changed = ce_match_stat(ce, &st);
changed = ce_match_stat(ce, &st, 0);
if (!changed && !diff_options.find_copies_harder)
continue;
oldmode = ntohl(ce->ce_mode);
Expand Down
2 changes: 1 addition & 1 deletion diff-index.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static int get_stat_data(struct cache_entry *ce,
}
return -1;
}
changed = ce_match_stat(ce, &st);
changed = ce_match_stat(ce, &st, 0);
if (changed) {
mode = create_ce_mode(st.st_mode);
if (!trust_executable_bit &&
Expand Down
2 changes: 1 addition & 1 deletion diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1)
ce = active_cache[pos];
if ((lstat(name, &st) < 0) ||
!S_ISREG(st.st_mode) || /* careful! */
ce_match_stat(ce, &st) ||
ce_match_stat(ce, &st, 0) ||
memcmp(sha1, ce->sha1, 20))
return 0;
/* we return 1 only when we can stat, it is a regular file,
Expand Down
2 changes: 1 addition & 1 deletion entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state)
strcpy(path + len, ce->name);

if (!lstat(path, &st)) {
unsigned changed = ce_match_stat(ce, &st);
unsigned changed = ce_match_stat(ce, &st, 1);
if (!changed)
return 0;
if (!state->force) {
Expand Down
1 change: 1 addition & 0 deletions environment.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
int trust_executable_bit = 1;
int assume_unchanged = 0;
int only_use_symrefs = 0;
int repository_format_version = 0;
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
Expand Down
26 changes: 23 additions & 3 deletions ls-files.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ static int show_unmerged = 0;
static int show_modified = 0;
static int show_killed = 0;
static int show_other_directories = 0;
static int show_valid_bit = 0;
static int line_terminator = '\n';

static int prefix_len = 0, prefix_offset = 0;
Expand Down Expand Up @@ -457,6 +458,23 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
if (pathspec && !match(pathspec, ps_matched, ce->name, len))
return;

if (tag && *tag && show_valid_bit &&
(ce->ce_flags & htons(CE_VALID))) {
static char alttag[4];
memcpy(alttag, tag, 3);
if (isalpha(tag[0]))
alttag[0] = tolower(tag[0]);
else if (tag[0] == '?')
alttag[0] = '!';
else {
alttag[0] = 'v';
alttag[1] = tag[0];
alttag[2] = ' ';
alttag[3] = 0;
}
tag = alttag;
}

if (!show_stage) {
fputs(tag, stdout);
write_name_quoted("", 0, ce->name + offset,
Expand Down Expand Up @@ -533,7 +551,7 @@ static void show_files(void)
err = lstat(ce->name, &st);
if (show_deleted && err)
show_ce_entry(tag_removed, ce);
if (show_modified && ce_modified(ce, &st))
if (show_modified && ce_modified(ce, &st, 0))
show_ce_entry(tag_modified, ce);
}
}
Expand Down Expand Up @@ -606,7 +624,7 @@ static void verify_pathspec(void)
}

static const char ls_files_usage[] =
"git-ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
"git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
"[ --exclude-per-directory=<filename> ] [--full-name] [--] [<file>]*";

Expand All @@ -631,13 +649,15 @@ int main(int argc, const char **argv)
line_terminator = 0;
continue;
}
if (!strcmp(arg, "-t")) {
if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
tag_cached = "H ";
tag_unmerged = "M ";
tag_removed = "R ";
tag_modified = "C ";
tag_other = "? ";
tag_killed = "K ";
if (arg[1] == 'v')
show_valid_bit = 1;
continue;
}
if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
Expand Down
28 changes: 23 additions & 5 deletions read-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
ce->ce_uid = htonl(st->st_uid);
ce->ce_gid = htonl(st->st_gid);
ce->ce_size = htonl(st->st_size);

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

static int ce_compare_data(struct cache_entry *ce, struct stat *st)
Expand Down Expand Up @@ -146,9 +149,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
return changed;
}

int ce_match_stat(struct cache_entry *ce, struct stat *st)
int ce_match_stat(struct cache_entry *ce, struct stat *st, int ignore_valid)
{
unsigned int changed = ce_match_stat_basic(ce, st);
unsigned int changed;

/*
* If it's marked as always valid in the index, it's
* valid whatever the checked-out copy says.
*/
if (!ignore_valid && (ce->ce_flags & htons(CE_VALID)))
return 0;

changed = ce_match_stat_basic(ce, st);

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

int ce_modified(struct cache_entry *ce, struct stat *st)
int ce_modified(struct cache_entry *ce, struct stat *st, int really)
{
int changed, changed_fs;
changed = ce_match_stat(ce, st);
changed = ce_match_stat(ce, st, really);
if (!changed)
return 0;
/*
Expand Down Expand Up @@ -233,6 +245,11 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
return -1;
if (len1 > len2)
return 1;

/* Compare stages */
flags1 &= CE_STAGEMASK;
flags2 &= CE_STAGEMASK;

if (flags1 < flags2)
return -1;
if (flags1 > flags2)
Expand Down Expand Up @@ -430,6 +447,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;

pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));

/* existing match? Just replace it. */
Expand Down
2 changes: 1 addition & 1 deletion read-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ static void verify_uptodate(struct cache_entry *ce)
return;

if (!lstat(ce->name, &st)) {
unsigned changed = ce_match_stat(ce, &st);
unsigned changed = ce_match_stat(ce, &st, 1);
if (!changed)
return;
errno = 0;
Expand Down
Loading

0 comments on commit ee07226

Please sign in to comment.