git/wt-status-deserialize.c

818 строки
22 KiB
C

#include "git-compat-util.h"
#include "environment.h"
#include "hex.h"
#include "hash.h"
#include "cache.h"
#include "wt-status.h"
#include "pkt-line.h"
#include "trace.h"
static struct trace_key trace_deserialize = TRACE_KEY_INIT(DESERIALIZE);
enum deserialize_parse_strategy {
DESERIALIZE_STRATEGY_AS_IS,
DESERIALIZE_STRATEGY_SKIP,
DESERIALIZE_STRATEGY_NORMAL,
DESERIALIZE_STRATEGY_ALL
};
static int check_path_contains(const char *out, int out_len, const char *in, int in_len)
{
return (out_len > 0 &&
out_len < in_len &&
(out[out_len - 1] == '/') &&
!memcmp(out, in, out_len));
}
static const char *my_packet_read_line(int fd, int *line_len)
{
static char buf[LARGE_PACKET_MAX];
*line_len = packet_read(fd, buf, sizeof(buf),
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_GENTLE_ON_EOF);
return (*line_len > 0) ? buf : NULL;
}
/*
* mtime_reported contains the mtime of the index when the
* serialization snapshot was computed.
*
* mtime_observed_on_disk contains the mtime of the index now.
*
* If these 2 times are different, then the .git/index has
* changed since the serialization cache was created and we
* must reject the cache because anything could have changed.
*
* If they are the same, we continue trying to use the cache.
*/
static int my_validate_index(const struct cache_time *mtime_reported)
{
const char *path = get_index_file();
struct stat st;
struct cache_time mtime_observed_on_disk;
if (lstat(path, &st)) {
trace_printf_key(&trace_deserialize, "could not stat index");
return DESERIALIZE_ERR;
}
mtime_observed_on_disk.sec = st.st_mtime;
mtime_observed_on_disk.nsec = ST_MTIME_NSEC(st);
if ((mtime_observed_on_disk.sec != mtime_reported->sec) ||
(mtime_observed_on_disk.nsec != mtime_reported->nsec)) {
trace_printf_key(&trace_deserialize,
"index mtime changed [des %d %d][obs %d %d]",
mtime_reported->sec, mtime_reported->nsec,
mtime_observed_on_disk.sec, mtime_observed_on_disk.nsec);
return DESERIALIZE_ERR;
}
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.
*/
while ((line = my_packet_read_line(fd, &line_len))) {
if (skip_prefix(line, "index_mtime ", &arg)) {
nr_fields = sscanf(arg, "%u %u",
&index_mtime.sec,
&index_mtime.nsec);
if (nr_fields != 2) {
trace_printf_key(&trace_deserialize, "invalid index_mtime (%d) '%s'",
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;
}
if (skip_prefix(line, "is_initial ", &arg)) {
s->is_initial = (int)strtol(arg, NULL, 10);
continue;
}
if (skip_prefix(line, "branch ", &arg)) {
s->branch = xstrdup(arg);
continue;
}
if (skip_prefix(line, "reference ", &arg)) {
s->reference = xstrdup(arg);
continue;
}
/* pathspec */
/* verbose */
/* amend */
if (skip_prefix(line, "whence ", &arg)) {
s->whence = (int)strtol(arg, NULL, 10);
continue;
}
/* nowarn */
/* use_color */
/* no_gettext */
/* display_comment_prefix */
/* relative_paths */
/* submodule_summary */
if (skip_prefix(line, "show_ignored_mode ", &arg)) {
s->show_ignored_mode = (int)strtol(arg, NULL, 10);
continue;
}
if (skip_prefix(line, "show_untracked_files ", &arg)) {
s->show_untracked_files = (int)strtol(arg, NULL, 10);
continue;
}
if (skip_prefix(line, "ignore_submodule_arg ", &arg)) {
s->ignore_submodule_arg = xstrdup(arg);
continue;
}
/* color_palette */
/* colopts */
/* null_termination */
/* commit_template */
/* show_branch */
/* show_stash */
if (skip_prefix(line, "hints ", &arg)) {
s->hints = (int)strtol(arg, NULL, 10);
continue;
}
if (skip_prefix(line, "detect_rename ", &arg)) {
s->detect_rename = (int)strtol(arg, NULL, 10);
continue;
}
if (skip_prefix(line, "rename_score ", &arg)) {
s->rename_score = (int)strtol(arg, NULL, 10);
continue;
}
if (skip_prefix(line, "rename_limit ", &arg)) {
s->rename_limit = (int)strtol(arg, NULL, 10);
continue;
}
/* status_format */
if (skip_prefix(line, "sha1_commit ", &arg)) {
if (get_oid_hex(arg, &s->oid_commit)) {
trace_printf_key(&trace_deserialize, "invalid sha1_commit");
return DESERIALIZE_ERR;
}
continue;
}
if (skip_prefix(line, "committable ", &arg)) {
s->committable = (int)strtol(arg, NULL, 10);
continue;
}
if (skip_prefix(line, "workdir_dirty ", &arg)) {
s->workdir_dirty = (int)strtol(arg, NULL, 10);
continue;
}
/* prefix */
trace_printf_key(&trace_deserialize, "unexpected line '%s'", line);
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);
}
/*
* Build a string-list of (count) <changed-item> lines from the input.
*/
static int wt_deserialize_v1_changed_items(const struct wt_status *cmd_s,
struct wt_status *s, int fd, int count)
{
struct wt_status_serialize_data *sd;
char *p;
int line_len;
const char *line;
struct string_list_item *item;
memset(&s->change, 0, sizeof(s->change));
s->change.strdup_strings = 1;
/*
* <wt_status_change_data_fields>+
* <flush>
*
* <fixed_part><path> NUL [<head_path>] NUL
*/
while ((line = my_packet_read_line(fd, &line_len))) {
struct wt_status_change_data *d = xcalloc(1, sizeof(*d));
sd = (struct wt_status_serialize_data *)line;
d->worktree_status = ntohl(sd->fixed.worktree_status);
d->index_status = ntohl(sd->fixed.index_status);
d->stagemask = ntohl(sd->fixed.stagemask);
d->rename_status = ntohl(sd->fixed.rename_status);
d->rename_score = ntohl(sd->fixed.rename_score);
d->mode_head = ntohl(sd->fixed.mode_head);
d->mode_index = ntohl(sd->fixed.mode_index);
d->mode_worktree = ntohl(sd->fixed.mode_worktree);
d->dirty_submodule = ntohl(sd->fixed.dirty_submodule);
d->new_submodule_commits = ntohl(sd->fixed.new_submodule_commits);
oidcpy(&d->oid_head, &sd->fixed.oid_head);
oidcpy(&d->oid_index, &sd->fixed.oid_index);
p = sd->variant;
item = string_list_append(&s->change, p);
p += strlen(p) + 1;
if (*p)
d->rename_source = xstrdup(p);
item->util = d;
trace_printf_key(
&trace_deserialize,
"change: %d %d %d %d %d %o %o %o %d %d %s %s '%s' '%s'",
d->worktree_status,
d->index_status,
d->stagemask,
d->rename_status,
d->rename_score,
d->mode_head,
d->mode_index,
d->mode_worktree,
d->dirty_submodule,
d->new_submodule_commits,
oid_to_hex(&d->oid_head),
oid_to_hex(&d->oid_index),
item->string,
(d->rename_source ? d->rename_source : ""));
if (d->stagemask &&
cmd_s->status_format == STATUS_FORMAT_PORCELAIN_V2) {
/*
* We have an unresolved conflict and the user wants
* to see porcelain V2 output. The cached status data
* does not contain enough information for V2 (because
* the main status computation does not capture it).
* We only get a single change record for the file with
* a single SHA -- we don't get the stage [123] mode
* and SHA data. The V2 detail-line print code looks
* up this information directly from the index. The
* whole point of this serialization cache is to avoid
* reading the index, so the V2 print code gets zeros.
* So we reject the status cache and let the fallback
* code run.
*/
trace_printf_key(
&trace_deserialize,
"reject: V2 format and umerged file: %s",
item->string);
return DESERIALIZE_ERR;
}
}
return DESERIALIZE_OK;
}
static int wt_deserialize_v1_untracked_items(struct wt_status *s,
int fd,
int count,
enum deserialize_parse_strategy strategy)
{
int line_len;
const char *line;
char *out = NULL;
int out_len = 0;
memset(&s->untracked, 0, sizeof(s->untracked));
s->untracked.strdup_strings = 1;
/*
* <pathname>+
* <flush>
*/
while ((line = my_packet_read_line(fd, &line_len))) {
if (strategy == DESERIALIZE_STRATEGY_AS_IS)
string_list_append(&s->untracked, line);
if (strategy == DESERIALIZE_STRATEGY_SKIP)
continue;
if (strategy == DESERIALIZE_STRATEGY_NORMAL) {
/* Only add "normal" entries to list */
if (out &&
check_path_contains(out, out_len, line, line_len)) {
continue;
}
else {
out = string_list_append(&s->untracked, line)->string;
out_len = line_len;
}
}
if (strategy == DESERIALIZE_STRATEGY_ALL) {
/* Only add "all" entries to list */
if (line[line_len - 1] != '/')
string_list_append(&s->untracked, line);
}
}
return DESERIALIZE_OK;
}
static int wt_deserialize_v1_ignored_items(struct wt_status *s,
int fd,
int count,
enum deserialize_parse_strategy strategy)
{
int line_len;
const char *line;
memset(&s->ignored, 0, sizeof(s->ignored));
s->ignored.strdup_strings = 1;
/*
* <pathname>+
* <flush>
*/
while ((line = my_packet_read_line(fd, &line_len))) {
if (strategy == DESERIALIZE_STRATEGY_AS_IS)
string_list_append(&s->ignored, line);
else
continue;
}
return DESERIALIZE_OK;
}
static int validate_untracked_files_arg(enum untracked_status_type cmd,
enum untracked_status_type *des,
enum deserialize_parse_strategy *strategy)
{
*strategy = DESERIALIZE_STRATEGY_AS_IS;
if (cmd == *des) {
*strategy = DESERIALIZE_STRATEGY_AS_IS;
} else if (cmd == SHOW_NO_UNTRACKED_FILES) {
*strategy = DESERIALIZE_STRATEGY_SKIP;
*des = cmd;
} else if (*des == SHOW_COMPLETE_UNTRACKED_FILES) {
if (cmd == SHOW_ALL_UNTRACKED_FILES) {
*strategy = DESERIALIZE_STRATEGY_ALL;
*des = cmd;
} else if (cmd == SHOW_NORMAL_UNTRACKED_FILES) {
*strategy = DESERIALIZE_STRATEGY_NORMAL;
*des = cmd;
}
} else {
return DESERIALIZE_ERR;
}
return DESERIALIZE_OK;
}
static int validate_ignored_files_arg(enum show_ignored_type cmd,
enum show_ignored_type des,
enum deserialize_parse_strategy *strategy)
{
*strategy = DESERIALIZE_STRATEGY_AS_IS;
if (cmd == SHOW_NO_IGNORED) {
*strategy = DESERIALIZE_STRATEGY_SKIP;
}
else if (cmd != des) {
return DESERIALIZE_ERR;
}
return DESERIALIZE_OK;
}
static int wt_deserialize_v1(const struct wt_status *cmd_s, struct wt_status *s, int fd)
{
int line_len;
const char *line;
const char *arg;
int nr_changed = 0;
int nr_untracked = 0;
int nr_ignored = 0;
enum deserialize_parse_strategy ignored_strategy = DESERIALIZE_STRATEGY_AS_IS, untracked_strategy = DESERIALIZE_STRATEGY_AS_IS;
if (wt_deserialize_v1_header(s, fd) == DESERIALIZE_ERR)
return DESERIALIZE_ERR;
/*
* We now have the header parsed. Look at the command args (as passed in), and see how to parse
* the serialized data
*/
if (validate_untracked_files_arg(cmd_s->show_untracked_files, &s->show_untracked_files, &untracked_strategy)) {
trace_printf_key(&trace_deserialize, "reject: show_untracked_file: command: %d, serialized : %d",
cmd_s->show_untracked_files,
s->show_untracked_files);
return DESERIALIZE_ERR;
}
if (validate_ignored_files_arg(cmd_s->show_ignored_mode, s->show_ignored_mode, &ignored_strategy)) {
trace_printf_key(&trace_deserialize, "reject: show_ignored_mode: command: %d, serialized: %d",
cmd_s->show_ignored_mode,
s->show_ignored_mode);
return DESERIALIZE_ERR;
}
/*
* [<changed-header> [<changed-item>+] <flush>]
* [<untracked-header> [<untracked-item>+] <flush>]
* [<ignored-header> [<ignored-item>+] <flush>]
*/
while ((line = my_packet_read_line(fd, &line_len))) {
if (skip_prefix(line, "changed ", &arg)) {
nr_changed = (int)strtol(arg, NULL, 10);
if (wt_deserialize_v1_changed_items(cmd_s, s, fd, nr_changed)
== DESERIALIZE_ERR)
return DESERIALIZE_ERR;
continue;
}
if (skip_prefix(line, "untracked ", &arg)) {
nr_untracked = (int)strtol(arg, NULL, 10);
if (wt_deserialize_v1_untracked_items(s, fd, nr_untracked, untracked_strategy)
== DESERIALIZE_ERR)
return DESERIALIZE_ERR;
continue;
}
if (skip_prefix(line, "ignored ", &arg)) {
nr_ignored = (int)strtol(arg, NULL, 10);
if (wt_deserialize_v1_ignored_items(s, fd, nr_ignored, ignored_strategy)
== DESERIALIZE_ERR)
return DESERIALIZE_ERR;
continue;
}
trace_printf_key(&trace_deserialize, "unexpected line '%s'", line);
return DESERIALIZE_ERR;
}
return DESERIALIZE_OK;
}
static int wt_deserialize_parse(const struct wt_status *cmd_s, struct wt_status *s, int fd)
{
int line_len;
const char *line;
const char *arg;
memset(s, 0, sizeof(*s));
if ((line = my_packet_read_line(fd, &line_len)) &&
(skip_prefix(line, "version ", &arg))) {
int version = (int)strtol(arg, NULL, 10);
if (version == 1)
return wt_deserialize_v1(cmd_s, s, fd);
}
trace_printf_key(&trace_deserialize, "missing/unsupported version");
return DESERIALIZE_ERR;
}
static inline int my_strcmp_null(const char *a, const char *b)
{
const char *alt_a = (a) ? a : "";
const char *alt_b = (b) ? b : "";
return strcmp(alt_a, alt_b);
}
static int wt_deserialize_fd(const struct wt_status *cmd_s, struct wt_status *des_s, int fd)
{
memset(des_s, 0, sizeof(*des_s));
/*
* Check the path spec on the current command
*/
if (cmd_s->pathspec.nr > 1) {
trace_printf_key(&trace_deserialize, "reject: multiple pathspecs");
return DESERIALIZE_ERR;
}
/*
* If we have a pathspec, but it maches the root (e.g. no filtering)
* then this is OK.
*/
if (cmd_s->pathspec.nr == 1 &&
my_strcmp_null(cmd_s->pathspec.items[0].match, "")) {
trace_printf_key(&trace_deserialize, "reject: pathspec");
return DESERIALIZE_ERR;
}
/*
* Deserialize cached status
*/
if (wt_deserialize_parse(cmd_s, des_s, fd) == DESERIALIZE_ERR)
return DESERIALIZE_ERR;
/*
* Compare fields in cmd_s with those observed in des_s and
* complain if they are incompatible (such as different "-u"
* or "--ignored" settings).
*/
if (cmd_s->is_initial != des_s->is_initial) {
trace_printf_key(&trace_deserialize, "reject: is_initial");
return DESERIALIZE_ERR;
}
if (my_strcmp_null(cmd_s->branch, des_s->branch)) {
trace_printf_key(&trace_deserialize, "reject: branch");
return DESERIALIZE_ERR;
}
if (my_strcmp_null(cmd_s->reference, des_s->reference)) {
trace_printf_key(&trace_deserialize, "reject: reference");
return DESERIALIZE_ERR;
}
/* verbose */
/* amend */
if (cmd_s->whence != des_s->whence) {
trace_printf_key(&trace_deserialize, "reject: whence");
return DESERIALIZE_ERR;
}
/* nowarn */
/* use_color */
/* no_gettext */
/* display_comment_prefix */
/* relative_paths */
/* submodule_summary */
/* show_ignored_files - already validated */
/* show_untrackes_files - already validated */
/*
* Submodules are not supported by status serialization.
* The status will not be serialized if it contains submodules,
* and so this check is not needed.
*
* if (my_strcmp_null(cmd_s->ignore_submodule_arg, des_s->ignore_submodule_arg)) {
* trace_printf_key(&trace_deserialize, "reject: ignore_submodule_arg");
* return DESERIALIZE_ERR;
* }
*/
/* color_palette */
/* colopts */
/* null_termination */
/* commit_template */
/* show_branch */
/* show_stash */
/* hints */
/* ahead_behind_flags */
if (cmd_s->detect_rename != des_s->detect_rename) {
trace_printf_key(&trace_deserialize, "reject: detect_rename");
return DESERIALIZE_ERR;
}
if (cmd_s->rename_score != des_s->rename_score) {
trace_printf_key(&trace_deserialize, "reject: rename_score");
return DESERIALIZE_ERR;
}
if (cmd_s->rename_limit != des_s->rename_limit) {
trace_printf_key(&trace_deserialize, "reject: rename_limit");
return DESERIALIZE_ERR;
}
/* status_format */
if (!oideq(&cmd_s->oid_commit, &des_s->oid_commit)) {
trace_printf_key(&trace_deserialize, "reject: sha1_commit");
return DESERIALIZE_ERR;
}
/*
* Copy over display-related fields from the current command.
*/
des_s->repo = cmd_s->repo;
des_s->verbose = cmd_s->verbose;
/* amend */
/* whence */
des_s->nowarn = cmd_s->nowarn;
des_s->use_color = cmd_s->use_color;
des_s->no_gettext = cmd_s->no_gettext;
des_s->display_comment_prefix = cmd_s->display_comment_prefix;
des_s->relative_paths = cmd_s->relative_paths;
des_s->submodule_summary = cmd_s->submodule_summary;
memcpy(des_s->color_palette, cmd_s->color_palette,
sizeof(char)*WT_STATUS_MAXSLOT*COLOR_MAXLEN);
des_s->colopts = cmd_s->colopts;
des_s->null_termination = cmd_s->null_termination;
/* commit_template */
des_s->show_branch = cmd_s->show_branch;
des_s->show_stash = cmd_s->show_stash;
/* hints */
des_s->ahead_behind_flags = cmd_s->ahead_behind_flags;
des_s->status_format = cmd_s->status_format;
des_s->fp = cmd_s->fp;
if (cmd_s->prefix && *cmd_s->prefix)
des_s->prefix = xstrdup(cmd_s->prefix);
return DESERIALIZE_OK;
}
static struct cache_time deserialize_prev_mtime = { 0, 0 };
static int try_deserialize_read_from_file_1(const struct wt_status *cmd_s,
const char *path,
struct wt_status *des_s)
{
struct stat st;
int result;
int fd;
/*
* If we are spinning waiting for the status cache to become
* valid, skip re-reading it if the mtime has not changed
* since the last time we read it.
*/
if (lstat(path, &st)) {
trace_printf_key(&trace_deserialize,
"could not lstat '%s'", path);
return DESERIALIZE_ERR;
}
if (st.st_mtime == deserialize_prev_mtime.sec &&
ST_MTIME_NSEC(st) == deserialize_prev_mtime.nsec) {
trace_printf_key(&trace_deserialize,
"mtime has not changed '%s'", path);
return DESERIALIZE_ERR;
}
fd = xopen(path, O_RDONLY);
if (fd == -1) {
trace_printf_key(&trace_deserialize,
"could not read '%s'", path);
return DESERIALIZE_ERR;
}
deserialize_prev_mtime.sec = st.st_mtime;
deserialize_prev_mtime.nsec = ST_MTIME_NSEC(st);
trace_printf_key(&trace_deserialize,
"reading serialization file (%d %d) '%s'",
deserialize_prev_mtime.sec,
deserialize_prev_mtime.nsec,
path);
result = wt_deserialize_fd(cmd_s, des_s, fd);
close(fd);
return result;
}
static int try_deserialize_read_from_file(const struct wt_status *cmd_s,
const char *path,
enum wt_status_deserialize_wait dw,
struct wt_status *des_s)
{
int k, limit;
int result = DESERIALIZE_ERR;
/*
* For "fail" or "no", try exactly once to read the status cache.
* Return an error if the file is stale.
*/
if (dw == DESERIALIZE_WAIT__FAIL || dw == DESERIALIZE_WAIT__NO)
return try_deserialize_read_from_file_1(cmd_s, path, des_s);
/*
* Wait for the status cache file to refresh. Wait duration can
* be in tenths of a second or unlimited. Poll every 100ms.
*/
if (dw == DESERIALIZE_WAIT__BLOCK) {
/*
* Convert "unlimited" to 1 day.
*/
limit = 10 * 60 * 60 * 24;
} else {
/* spin for dw tenths of a second */
limit = dw;
}
for (k = 0; k < limit; k++) {
result = try_deserialize_read_from_file_1(
cmd_s, path, des_s);
if (result == DESERIALIZE_OK)
break;
sleep_millisec(100);
}
trace_printf_key(&trace_deserialize,
"wait polled=%d result=%d '%s'",
k, result, path);
return result;
}
/*
* Read raw serialized status data from the given file (or STDIN).
*
* Verify that the args specified in the current command
* are compatible with the deserialized data (such as "-uno").
*
* Copy display-related fields from the current command
* into the deserialized data (so that the user can request
* long or short as they please).
*
* Print status report using cached data.
*/
int wt_status_deserialize(const struct wt_status *cmd_s,
const char *path,
enum wt_status_deserialize_wait dw)
{
struct wt_status des_s;
int result;
if (path && *path && strcmp(path, "0")) {
result = try_deserialize_read_from_file(cmd_s, path, dw, &des_s);
} else {
trace_printf_key(&trace_deserialize, "reading stdin");
/*
* Read status cache data from stdin. Ignore the deserialize-wait
* term, since we cannot read stdin multiple times.
*/
result = wt_deserialize_fd(cmd_s, &des_s, 0);
}
if (result == DESERIALIZE_OK) {
wt_status_get_state(cmd_s->repo, &des_s.state, des_s.branch &&
!strcmp(des_s.branch, "HEAD"));
wt_status_print(&des_s);
}
return result;
}