mirror of
https://github.com/git/git.git
synced 2024-11-01 06:47:52 +01:00
b3e83cc752
Callers of the hold_locked_index() function pass 0 when they want to prepare to write a new version of the index file without wishing to die or emit an error message when the request fails (e.g. somebody else already held the lock), and pass 1 when they want the call to die upon failure. This option is called LOCK_DIE_ON_ERROR by the underlying lockfile API, and the hold_locked_index() function translates the paramter to LOCK_DIE_ON_ERROR when calling the hold_lock_file_for_update(). Replace these hardcoded '1' with LOCK_DIE_ON_ERROR and stop translating. Callers other than the ones that are replaced with this change pass '0' to the function; no behaviour change is intended with this patch. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Among the callers of hold_locked_index() that passes 0: - diff.c::refresh_index_quietly() at the end of "git diff" is an opportunistic update; it leaks the lockfile structure but it is just before the program exits and nobody should care. - builtin/describe.c::cmd_describe(), builtin/commit.c::cmd_status(), sequencer.c::read_and_refresh_cache() are all opportunistic updates and they are OK. - builtin/update-index.c::cmd_update_index() takes a lock upfront but we may end up not needing to update the index (i.e. the entries may be fully up-to-date), in which case we do not need to issue an error upon failure to acquire the lock. We do diagnose and die if we indeed need to update, so it is OK. - wt-status.c::require_clean_work_tree() IS BUGGY. It asks silence, does not check the returned value. Compare with callsites like cmd_describe() and cmd_status() to notice that it is wrong to call update_index_if_able() unconditionally.
257 lines
6.5 KiB
C
257 lines
6.5 KiB
C
/*
|
|
* Check-out files from the "current cache directory"
|
|
*
|
|
* Copyright (C) 2005 Linus Torvalds
|
|
*
|
|
*/
|
|
#include "builtin.h"
|
|
#include "lockfile.h"
|
|
#include "quote.h"
|
|
#include "cache-tree.h"
|
|
#include "parse-options.h"
|
|
|
|
#define CHECKOUT_ALL 4
|
|
static int nul_term_line;
|
|
static int checkout_stage; /* default to checkout stage0 */
|
|
static int to_tempfile;
|
|
static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
|
|
|
|
static struct checkout state = CHECKOUT_INIT;
|
|
|
|
static void write_tempfile_record(const char *name, const char *prefix)
|
|
{
|
|
int i;
|
|
|
|
if (CHECKOUT_ALL == checkout_stage) {
|
|
for (i = 1; i < 4; i++) {
|
|
if (i > 1)
|
|
putchar(' ');
|
|
if (topath[i][0])
|
|
fputs(topath[i], stdout);
|
|
else
|
|
putchar('.');
|
|
}
|
|
} else
|
|
fputs(topath[checkout_stage], stdout);
|
|
|
|
putchar('\t');
|
|
write_name_quoted_relative(name, prefix, stdout,
|
|
nul_term_line ? '\0' : '\n');
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
topath[i][0] = 0;
|
|
}
|
|
}
|
|
|
|
static int checkout_file(const char *name, const char *prefix)
|
|
{
|
|
int namelen = strlen(name);
|
|
int pos = cache_name_pos(name, namelen);
|
|
int has_same_name = 0;
|
|
int did_checkout = 0;
|
|
int errs = 0;
|
|
|
|
if (pos < 0)
|
|
pos = -pos - 1;
|
|
|
|
while (pos < active_nr) {
|
|
struct cache_entry *ce = active_cache[pos];
|
|
if (ce_namelen(ce) != namelen ||
|
|
memcmp(ce->name, name, namelen))
|
|
break;
|
|
has_same_name = 1;
|
|
pos++;
|
|
if (ce_stage(ce) != checkout_stage
|
|
&& (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
|
|
continue;
|
|
did_checkout = 1;
|
|
if (checkout_entry(ce, &state,
|
|
to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
|
|
errs++;
|
|
}
|
|
|
|
if (did_checkout) {
|
|
if (to_tempfile)
|
|
write_tempfile_record(name, prefix);
|
|
return errs > 0 ? -1 : 0;
|
|
}
|
|
|
|
if (!state.quiet) {
|
|
fprintf(stderr, "git checkout-index: %s ", name);
|
|
if (!has_same_name)
|
|
fprintf(stderr, "is not in the cache");
|
|
else if (checkout_stage)
|
|
fprintf(stderr, "does not exist at stage %d",
|
|
checkout_stage);
|
|
else
|
|
fprintf(stderr, "is unmerged");
|
|
fputc('\n', stderr);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void checkout_all(const char *prefix, int prefix_length)
|
|
{
|
|
int i, errs = 0;
|
|
struct cache_entry *last_ce = NULL;
|
|
|
|
for (i = 0; i < active_nr ; i++) {
|
|
struct cache_entry *ce = active_cache[i];
|
|
if (ce_stage(ce) != checkout_stage
|
|
&& (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
|
|
continue;
|
|
if (prefix && *prefix &&
|
|
(ce_namelen(ce) <= prefix_length ||
|
|
memcmp(prefix, ce->name, prefix_length)))
|
|
continue;
|
|
if (last_ce && to_tempfile) {
|
|
if (ce_namelen(last_ce) != ce_namelen(ce)
|
|
|| memcmp(last_ce->name, ce->name, ce_namelen(ce)))
|
|
write_tempfile_record(last_ce->name, prefix);
|
|
}
|
|
if (checkout_entry(ce, &state,
|
|
to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
|
|
errs++;
|
|
last_ce = ce;
|
|
}
|
|
if (last_ce && to_tempfile)
|
|
write_tempfile_record(last_ce->name, prefix);
|
|
if (errs)
|
|
/* we have already done our error reporting.
|
|
* exit with the same code as die().
|
|
*/
|
|
exit(128);
|
|
}
|
|
|
|
static const char * const builtin_checkout_index_usage[] = {
|
|
N_("git checkout-index [<options>] [--] [<file>...]"),
|
|
NULL
|
|
};
|
|
|
|
static struct lock_file lock_file;
|
|
|
|
static int option_parse_stage(const struct option *opt,
|
|
const char *arg, int unset)
|
|
{
|
|
if (!strcmp(arg, "all")) {
|
|
to_tempfile = 1;
|
|
checkout_stage = CHECKOUT_ALL;
|
|
} else {
|
|
int ch = arg[0];
|
|
if ('1' <= ch && ch <= '3')
|
|
checkout_stage = arg[0] - '0';
|
|
else
|
|
die(_("stage should be between 1 and 3 or all"));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cmd_checkout_index(int argc, const char **argv, const char *prefix)
|
|
{
|
|
int i;
|
|
int newfd = -1;
|
|
int all = 0;
|
|
int read_from_stdin = 0;
|
|
int prefix_length;
|
|
int force = 0, quiet = 0, not_new = 0;
|
|
int index_opt = 0;
|
|
struct option builtin_checkout_index_options[] = {
|
|
OPT_BOOL('a', "all", &all,
|
|
N_("check out all files in the index")),
|
|
OPT__FORCE(&force, N_("force overwrite of existing files")),
|
|
OPT__QUIET(&quiet,
|
|
N_("no warning for existing files and files not in index")),
|
|
OPT_BOOL('n', "no-create", ¬_new,
|
|
N_("don't checkout new files")),
|
|
OPT_BOOL('u', "index", &index_opt,
|
|
N_("update stat information in the index file")),
|
|
OPT_BOOL('z', NULL, &nul_term_line,
|
|
N_("paths are separated with NUL character")),
|
|
OPT_BOOL(0, "stdin", &read_from_stdin,
|
|
N_("read list of paths from the standard input")),
|
|
OPT_BOOL(0, "temp", &to_tempfile,
|
|
N_("write the content to temporary files")),
|
|
OPT_STRING(0, "prefix", &state.base_dir, N_("string"),
|
|
N_("when creating files, prepend <string>")),
|
|
{ OPTION_CALLBACK, 0, "stage", NULL, "1-3|all",
|
|
N_("copy out the files from named stage"),
|
|
PARSE_OPT_NONEG, option_parse_stage },
|
|
OPT_END()
|
|
};
|
|
|
|
if (argc == 2 && !strcmp(argv[1], "-h"))
|
|
usage_with_options(builtin_checkout_index_usage,
|
|
builtin_checkout_index_options);
|
|
git_config(git_default_config, NULL);
|
|
prefix_length = prefix ? strlen(prefix) : 0;
|
|
|
|
if (read_cache() < 0) {
|
|
die("invalid cache");
|
|
}
|
|
|
|
argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
|
|
builtin_checkout_index_usage, 0);
|
|
state.force = force;
|
|
state.quiet = quiet;
|
|
state.not_new = not_new;
|
|
|
|
if (!state.base_dir)
|
|
state.base_dir = "";
|
|
state.base_dir_len = strlen(state.base_dir);
|
|
|
|
/*
|
|
* when --prefix is specified we do not want to update cache.
|
|
*/
|
|
if (index_opt && !state.base_dir_len && !to_tempfile) {
|
|
state.refresh_cache = 1;
|
|
state.istate = &the_index;
|
|
newfd = hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
|
|
}
|
|
|
|
/* Check out named files first */
|
|
for (i = 0; i < argc; i++) {
|
|
const char *arg = argv[i];
|
|
char *p;
|
|
|
|
if (all)
|
|
die("git checkout-index: don't mix '--all' and explicit filenames");
|
|
if (read_from_stdin)
|
|
die("git checkout-index: don't mix '--stdin' and explicit filenames");
|
|
p = prefix_path(prefix, prefix_length, arg);
|
|
checkout_file(p, prefix);
|
|
free(p);
|
|
}
|
|
|
|
if (read_from_stdin) {
|
|
struct strbuf buf = STRBUF_INIT;
|
|
struct strbuf unquoted = STRBUF_INIT;
|
|
strbuf_getline_fn getline_fn;
|
|
|
|
if (all)
|
|
die("git checkout-index: don't mix '--all' and '--stdin'");
|
|
|
|
getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
|
|
while (getline_fn(&buf, stdin) != EOF) {
|
|
char *p;
|
|
if (!nul_term_line && buf.buf[0] == '"') {
|
|
strbuf_reset(&unquoted);
|
|
if (unquote_c_style(&unquoted, buf.buf, NULL))
|
|
die("line is badly quoted");
|
|
strbuf_swap(&buf, &unquoted);
|
|
}
|
|
p = prefix_path(prefix, prefix_length, buf.buf);
|
|
checkout_file(p, prefix);
|
|
free(p);
|
|
}
|
|
strbuf_release(&unquoted);
|
|
strbuf_release(&buf);
|
|
}
|
|
|
|
if (all)
|
|
checkout_all(prefix, prefix_length);
|
|
|
|
if (0 <= newfd &&
|
|
write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
|
die("Unable to write new index file");
|
|
return 0;
|
|
}
|