Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test PR #8

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions wt-status-deserialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,69 @@ static int my_validate_index(const struct cache_time *mtime_reported)
return DESERIALIZE_OK;
}

/*
* Use the given key and exclude pathname to compute a serialization header
* reflecting the current contents on disk. See if that matches the value
* computed for this key when the cache was written. Reject the cache if
* anything has changed.
*/
static int my_validate_excludes(const char *path, const char *key, const char *line)
{
struct strbuf sb = STRBUF_INIT;
int r;

wt_serialize_compute_exclude_header(&sb, key, path);

r = (strcmp(line, sb.buf) ? DESERIALIZE_ERR : DESERIALIZE_OK);

if (r == DESERIALIZE_ERR)
trace_printf_key(&trace_deserialize,
"%s changed [cached '%s'][observed '%s']",
key, line, sb.buf);

strbuf_release(&sb);
return r;
}

static int my_parse_core_excludes(const char *line)
{
/*
* In dir.c:setup_standard_excludes() they use either the value of
* the "core.excludefile" variable (stored in the global "excludes_file"
* variable) -or- the default value "$XDG_HOME/git/ignore". This is done
* during wt_status_collect_untracked() which we are hoping to not call.
*
* Fake the setup here.
*/

if (excludes_file) {
return my_validate_excludes(excludes_file, "core_excludes", line);
} else {
char *path = xdg_config_home("ignore");
int r = my_validate_excludes(path, "core_excludes", line);
free(path);
return r;
}
}

static int my_parse_repo_excludes(const char *line)
{
char *path = git_pathdup("info/exclude");
int r = my_validate_excludes(path, "repo_excludes", line);
free(path);

return r;
}

static int wt_deserialize_v1_header(struct wt_status *s, int fd)
{
struct cache_time index_mtime;
int line_len, nr_fields;
const char *line;
const char *arg;
int have_required_index_mtime = 0;
int have_required_core_excludes = 0;
int have_required_repo_excludes = 0;

/*
* parse header lines up to the first flush packet.
Expand All @@ -86,6 +143,20 @@ static int wt_deserialize_v1_header(struct wt_status *s, int fd)
nr_fields, line);
return DESERIALIZE_ERR;
}
have_required_index_mtime = 1;
continue;
}

if (skip_prefix(line, "core_excludes ", &arg)) {
if (my_parse_core_excludes(line) != DESERIALIZE_OK)
return DESERIALIZE_ERR;
have_required_core_excludes = 1;
continue;
}
if (skip_prefix(line, "repo_excludes ", &arg)) {
if (my_parse_repo_excludes(line) != DESERIALIZE_OK)
return DESERIALIZE_ERR;
have_required_repo_excludes = 1;
continue;
}

Expand Down Expand Up @@ -170,6 +241,19 @@ static int wt_deserialize_v1_header(struct wt_status *s, int fd)
return DESERIALIZE_ERR;
}

if (!have_required_index_mtime) {
trace_printf_key(&trace_deserialize, "missing '%s'", "index_mtime");
return DESERIALIZE_ERR;
}
if (!have_required_core_excludes) {
trace_printf_key(&trace_deserialize, "missing '%s'", "core_excludes");
return DESERIALIZE_ERR;
}
if (!have_required_repo_excludes) {
trace_printf_key(&trace_deserialize, "missing '%s'", "repo_excludes");
return DESERIALIZE_ERR;
}

return my_validate_index(&index_mtime);
}

Expand Down
88 changes: 88 additions & 0 deletions wt-status-serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,92 @@

static struct trace_key trace_serialize = TRACE_KEY_INIT(SERIALIZE);

/*
* Compute header record for exclude file using format:
* <key> SP <status_char> SP <variant> LF
*/
void wt_serialize_compute_exclude_header(struct strbuf *sb,
const char *key,
const char *path)
{
struct stat st;
struct stat_data sd;

memset(&sd, 0, sizeof(sd));

strbuf_setlen(sb, 0);

if (!path || !*path) {
strbuf_addf(sb, "%s U (unset)", key);
}
else if (lstat(path, &st) == -1) {
if (is_missing_file_error(errno))
strbuf_addf(sb, "%s E (not-found) %s", key, path);
else
strbuf_addf(sb, "%s E (other) %s", key, path);
}
else {
fill_stat_data(&sd, &st);
strbuf_addf(sb, "%s F %d %d %s",
key, sd.sd_mtime.sec, sd.sd_mtime.nsec, path);
}
}

static void append_exclude_info(int fd, const char *path, const char *key)
{
struct strbuf sb = STRBUF_INIT;

wt_serialize_compute_exclude_header(&sb, key, path);

packet_write_fmt(fd, "%s\n", sb.buf);

strbuf_release(&sb);
}

static void append_core_excludes_file_info(int fd)
{
/*
* Write pathname and mtime of the core/global excludes file to
* the header since a change in the excludes will change the
* results reported by status.
*
* The "core.excludefile" setting defaults to $XDG_HOME/git/ignore
* and uses a global variable which should have been set during
* wt_status_collect_untracked().
*
* See dir.c:setup_standard_excludes()
*/
append_exclude_info(fd, excludes_file, "core_excludes");
}

static void append_repo_excludes_file_info(int fd)
{
/*
* Likewise, there is a per-repo excludes file in .git/info/excludes
* that will change the results reported by status.
*
* See dir.c:setup_standard_excludes() and git_path_info_excludes().
* We replicate the pathname construction here because of the static
* variables/functions used in dir.c.
*/
char *path = git_pathdup("info/exclude");

append_exclude_info(fd, path, "repo_excludes");

free(path);
}

/*
* WARNING: there are also per-directory .gitignore files that can change
* the results reported by status. If you run a serialize with untracked
* .gitignore files and then change a per-directory .gitignore file or
* change a tracked but unstaged .gitignore file, and then run deserialize,
* you MAY get incorrect results.
*
* TODO We may want to also include the mtime of the .gitignore file in
* the root of the workdir to guard against the common cases.
*/

/*
* Write V1 header fields.
*/
Expand All @@ -16,6 +102,8 @@ static void wt_serialize_v1_header(struct wt_status *s, int fd)
packet_write_fmt(fd, "index_mtime %d %d\n",
the_index.timestamp.sec,
the_index.timestamp.nsec);
append_core_excludes_file_info(fd);
append_repo_excludes_file_info(fd);

/*
* Write data from wt_status to qualify this status report.
Expand Down
8 changes: 8 additions & 0 deletions wt-status.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,12 @@ void wt_status_serialize_v1(int fd, struct wt_status *s);
int wt_status_deserialize(const struct wt_status *cmd_s,
const char *path);

/*
* A helper routine for serialize and deserialize to compute
* metadata for the user-global and repo-local excludes files.
*/
void wt_serialize_compute_exclude_header(struct strbuf *sb,
const char *key,
const char *path);

#endif /* STATUS_H */