builtin-checkout-index.c 8.09 KB
Newer Older
1 2 3 4 5 6 7
/*
 * Check-out files from the "current cache directory"
 *
 * Copyright (C) 2005 Linus Torvalds
 *
 * Careful: order of argument flags does matter. For example,
 *
8
 *	git checkout-index -a -f file.c
9 10 11 12 13 14
 *
 * Will first check out all files listed in the cache (but not
 * overwrite any old ones), and then force-checkout "file.c" a
 * second time (ie that one _will_ overwrite any old contents
 * with the same filename).
 *
15 16 17
 * Also, just doing "git checkout-index" does nothing. You probably
 * meant "git checkout-index -a". And if you want to force it, you
 * want "git checkout-index -f -a".
18 19 20 21 22
 *
 * Intuitiveness is not the goal here. Repeatability is. The
 * reason for the "no arguments means no work" thing is that
 * from scripts you are supposed to be able to do things like
 *
23
 *	find . -name '*.h' -print0 | xargs -0 git checkout-index -f --
24
 *
25 26
 * or:
 *
27
 *	find . -name '*.h' -print0 | git checkout-index -f -z --stdin
28
 *
29 30 31 32 33 34 35 36 37 38
 * which will force all existing *.h files to be replaced with
 * their cached copies. If an empty command line implied "all",
 * then this would force-refresh everything in the cache, which
 * was not the point.
 *
 * Oh, and the "--" is just a good idea when you know the rest
 * will be filenames. Just so that you wouldn't have a filename
 * of "-a" causing problems (not possible in the above example,
 * but get used to it in scripting!).
 */
39
#include "builtin.h"
40
#include "cache.h"
41
#include "quote.h"
42
#include "cache-tree.h"
43
#include "parse-options.h"
44

45 46
#define CHECKOUT_ALL 4
static int line_termination = '\n';
47
static int checkout_stage; /* default to checkout stage0 */
48
static int to_tempfile;
49
static char topath[4][PATH_MAX + 1];
50

Shawn Pearce's avatar
Shawn Pearce committed
51
static struct checkout state;
52

53
static void write_tempfile_record(const char *name, int prefix_length)
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
{
	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');
70
	write_name_quoted(name + prefix_length, stdout, line_termination);
71 72 73 74 75 76

	for (i = 0; i < 4; i++) {
		topath[i][0] = 0;
	}
}

77
static int checkout_file(const char *name, int prefix_length)
78
{
79 80 81
	int namelen = strlen(name);
	int pos = cache_name_pos(name, namelen);
	int has_same_name = 0;
82 83
	int did_checkout = 0;
	int errs = 0;
84 85 86 87 88 89

	if (pos < 0)
		pos = -pos - 1;

	while (pos < active_nr) {
		struct cache_entry *ce = active_cache[pos];
90
		if (ce_namelen(ce) != namelen ||
91 92 93 94
		    memcmp(ce->name, name, namelen))
			break;
		has_same_name = 1;
		pos++;
95 96 97 98 99 100 101 102 103 104 105
		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)
106
			write_tempfile_record(name, prefix_length);
107
		return errs > 0 ? -1 : 0;
108
	}
109 110

	if (!state.quiet) {
111
		fprintf(stderr, "git checkout-index: %s ", name);
112 113 114 115 116 117 118 119 120 121
		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;
122 123
}

David Rientjes's avatar
David Rientjes committed
124
static void checkout_all(const char *prefix, int prefix_length)
125
{
126
	int i, errs = 0;
127
	struct cache_entry* last_ce = NULL;
128 129 130

	for (i = 0; i < active_nr ; i++) {
		struct cache_entry *ce = active_cache[i];
131 132
		if (ce_stage(ce) != checkout_stage
		    && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
133
			continue;
134
		if (prefix && *prefix &&
135 136
		    (ce_namelen(ce) <= prefix_length ||
		     memcmp(prefix, ce->name, prefix_length)))
137
			continue;
138 139 140
		if (last_ce && to_tempfile) {
			if (ce_namelen(last_ce) != ce_namelen(ce)
			    || memcmp(last_ce->name, ce->name, ce_namelen(ce)))
141
				write_tempfile_record(last_ce->name, prefix_length);
142 143 144
		}
		if (checkout_entry(ce, &state,
		    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
145
			errs++;
146
		last_ce = ce;
147
	}
148
	if (last_ce && to_tempfile)
149
		write_tempfile_record(last_ce->name, prefix_length);
150 151 152 153 154
	if (errs)
		/* we have already done our error reporting.
		 * exit with the same code as die().
		 */
		exit(128);
155 156
}

157 158 159 160
static const char * const builtin_checkout_index_usage[] = {
	"git checkout-index [options] [--] <file>...",
	NULL
};
161

162
static struct lock_file lock_file;
163

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
static int option_parse_u(const struct option *opt,
			      const char *arg, int unset)
{
	int *newfd = opt->value;

	state.refresh_cache = 1;
	if (*newfd < 0)
		*newfd = hold_locked_index(&lock_file, 1);
	return 0;
}

static int option_parse_z(const struct option *opt,
			  const char *arg, int unset)
{
	if (unset)
		line_termination = '\n';
	else
		line_termination = 0;
	return 0;
}

static int option_parse_prefix(const struct option *opt,
			       const char *arg, int unset)
{
	state.base_dir = arg;
	state.base_dir_len = strlen(arg);
	return 0;
}

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;
}

209
int cmd_checkout_index(int argc, const char **argv, const char *prefix)
210
{
211
	int i;
212
	int newfd = -1;
213
	int all = 0;
214
	int read_from_stdin = 0;
215
	int prefix_length;
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	int force = 0, quiet = 0, not_new = 0;
	struct option builtin_checkout_index_options[] = {
		OPT_BOOLEAN('a', "all", &all,
			"checks out all files in the index"),
		OPT_BOOLEAN('f', "force", &force,
			"forces overwrite of existing files"),
		OPT__QUIET(&quiet),
		OPT_BOOLEAN('n', "no-create", &not_new,
			"don't checkout new files"),
		{ OPTION_CALLBACK, 'u', "index", &newfd, NULL,
			"update stat information in the index file",
			PARSE_OPT_NOARG, option_parse_u },
		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
			"paths are separated with NUL character",
			PARSE_OPT_NOARG, option_parse_z },
		OPT_BOOLEAN(0, "stdin", &read_from_stdin,
			"read list of paths from the standard input"),
		OPT_BOOLEAN(0, "temp", &to_tempfile,
			"write the content to temporary files"),
		OPT_CALLBACK(0, "prefix", NULL, "string",
			"when creating files, prepend <string>",
			option_parse_prefix),
		OPT_CALLBACK(0, "stage", NULL, NULL,
			"copy out the files from named stage",
			option_parse_stage),
		OPT_END()
	};
243

244
	git_config(git_default_config, NULL);
245
	state.base_dir = "";
246 247
	prefix_length = prefix ? strlen(prefix) : 0;

248
	if (read_cache() < 0) {
249
		die("invalid cache");
250 251
	}

252 253 254 255 256
	argc = parse_options(argc, argv, builtin_checkout_index_options,
			builtin_checkout_index_usage, 0);
	state.force = force;
	state.quiet = quiet;
	state.not_new = not_new;
257

258
	if (state.base_dir_len || to_tempfile) {
259 260 261 262
		/* when --prefix is specified we do not
		 * want to update cache.
		 */
		if (state.refresh_cache) {
263
			rollback_lock_file(&lock_file);
Brandon Casey's avatar
Brandon Casey committed
264
			newfd = -1;
265 266 267 268 269
		}
		state.refresh_cache = 0;
	}

	/* Check out named files first */
270
	for (i = 0; i < argc; i++) {
271
		const char *arg = argv[i];
272
		const char *p;
273 274

		if (all)
275
			die("git checkout-index: don't mix '--all' and explicit filenames");
276
		if (read_from_stdin)
277
			die("git checkout-index: don't mix '--stdin' and explicit filenames");
278
		p = prefix_path(prefix, prefix_length, arg);
279
		checkout_file(p, prefix_length);
280
		if (p < arg || p > arg + strlen(arg))
281
			free((char*)p);
282
	}
283

284
	if (read_from_stdin) {
285
		struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
286

287
		if (all)
288
			die("git checkout-index: don't mix '--all' and '--stdin'");
289 290

		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
291
			const char *p;
292 293 294 295 296 297 298
			if (line_termination && buf.buf[0] == '"') {
				strbuf_reset(&nbuf);
				if (unquote_c_style(&nbuf, buf.buf, NULL))
					die("line is badly quoted");
				strbuf_swap(&buf, &nbuf);
			}
			p = prefix_path(prefix, prefix_length, buf.buf);
299
			checkout_file(p, prefix_length);
300
			if (p < buf.buf || p > buf.buf + buf.len)
301
				free((char *)p);
302
		}
303
		strbuf_release(&nbuf);
304
		strbuf_release(&buf);
305 306
	}

307
	if (all)
308
		checkout_all(prefix, prefix_length);
309

310 311
	if (0 <= newfd &&
	    (write_cache(newfd, active_cache, active_nr) ||
Brandon Casey's avatar
Brandon Casey committed
312
	     commit_locked_index(&lock_file)))
313
		die("Unable to write new index file");
314 315
	return 0;
}