builtin-add.c 6.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 * "git add" builtin command
 *
 * Copyright (C) 2006 Linus Torvalds
 */
#include "cache.h"
#include "builtin.h"
#include "dir.h"
J
Junio C Hamano 已提交
9
#include "exec_cmd.h"
10
#include "cache-tree.h"
11 12 13 14
#include "diff.h"
#include "diffcore.h"
#include "commit.h"
#include "revision.h"
15
#include "run-command.h"
16
#include "parse-options.h"
L
Linus Torvalds 已提交
17

18 19 20 21
static const char * const builtin_add_usage[] = {
	"git-add [options] [--] <filepattern>...",
	NULL
};
22
static int patch_interactive = 0, add_interactive = 0;
J
Jeff King 已提交
23
static int take_worktree_changes;
24

L
Linus Torvalds 已提交
25 26
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
{
27 28
	char *seen;
	int i, specs;
L
Linus Torvalds 已提交
29 30
	struct dir_entry **src, **dst;

31 32
	for (specs = 0; pathspec[specs];  specs++)
		/* nothing */;
33
	seen = xcalloc(specs, 1);
34

L
Linus Torvalds 已提交
35 36 37 38
	src = dst = dir->entries;
	i = dir->nr;
	while (--i >= 0) {
		struct dir_entry *entry = *src++;
J
Junio C Hamano 已提交
39 40 41
		if (match_pathspec(pathspec, entry->name, entry->len,
				   prefix, seen))
			*dst++ = entry;
L
Linus Torvalds 已提交
42 43
	}
	dir->nr = dst - dir->entries;
44 45

	for (i = 0; i < specs; i++) {
46 47 48
		if (!seen[i] && !file_exists(pathspec[i]))
			die("pathspec '%s' did not match any files",
					pathspec[i]);
49
	}
50
        free(seen);
L
Linus Torvalds 已提交
51 52
}

53 54
static void fill_directory(struct dir_struct *dir, const char **pathspec,
		int ignored_too)
L
Linus Torvalds 已提交
55 56 57 58 59 60
{
	const char *path, *base;
	int baselen;

	/* Set up the default git porcelain excludes */
	memset(dir, 0, sizeof(*dir));
61 62
	if (!ignored_too) {
		dir->collect_ignored = 1;
J
Junio C Hamano 已提交
63
		setup_standard_excludes(dir);
64
	}
L
Linus Torvalds 已提交
65 66 67 68 69 70 71 72

	/*
	 * Calculate common prefix for the pathspec, and
	 * use that to optimize the directory walk
	 */
	baselen = common_prefix(pathspec);
	path = ".";
	base = "";
P
Pierre Habouzit 已提交
73 74
	if (baselen)
		path = base = xmemdupz(*pathspec, baselen);
L
Linus Torvalds 已提交
75 76

	/* Read the directory and prune it */
77
	read_directory(dir, path, base, baselen, pathspec);
L
Linus Torvalds 已提交
78 79 80 81
	if (pathspec)
		prune_directory(dir, pathspec, baselen);
}

82 83 84 85 86 87 88 89 90 91 92
static void update_callback(struct diff_queue_struct *q,
			    struct diff_options *opt, void *cbdata)
{
	int i, verbose;

	verbose = *((int *)cbdata);
	for (i = 0; i < q->nr; i++) {
		struct diff_filepair *p = q->queue[i];
		const char *path = p->one->path;
		switch (p->status) {
		default:
93
			die("unexpected diff status %c", p->status);
94 95
		case DIFF_STATUS_UNMERGED:
		case DIFF_STATUS_MODIFIED:
96
		case DIFF_STATUS_TYPE_CHANGED:
97 98 99 100 101 102 103 104 105 106 107
			add_file_to_cache(path, verbose);
			break;
		case DIFF_STATUS_DELETED:
			remove_file_from_cache(path);
			if (verbose)
				printf("remove '%s'\n", path);
			break;
		}
	}
}

108
void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)
109 110
{
	struct rev_info rev;
111
	init_revisions(&rev, prefix);
112
	setup_revisions(0, NULL, &rev, NULL);
113
	rev.prune_data = pathspec;
114 115 116
	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
	rev.diffopt.format_callback = update_callback;
	rev.diffopt.format_callback_data = &verbose;
117
	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
118 119
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
static void refresh(int verbose, const char **pathspec)
{
	char *seen;
	int i, specs;

	for (specs = 0; pathspec[specs];  specs++)
		/* nothing */;
	seen = xcalloc(specs, 1);
	if (read_cache() < 0)
		die("index file corrupt");
	refresh_index(&the_index, verbose ? 0 : REFRESH_QUIET, pathspec, seen);
	for (i = 0; i < specs; i++) {
		if (!seen[i])
			die("pathspec '%s' did not match any files", pathspec[i]);
	}
135
        free(seen);
136 137
}

138 139 140 141 142 143 144 145
static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
{
	const char **pathspec = get_pathspec(prefix, argv);

	return pathspec;
}

int interactive_add(int argc, const char **argv, const char *prefix)
146
{
147
	int status, ac;
148 149 150 151 152 153 154 155
	const char **args;
	const char **pathspec = NULL;

	if (argc) {
		pathspec = validate_pathspec(argc, argv, prefix);
		if (!pathspec)
			return -1;
	}
156

157 158 159 160 161 162 163 164 165 166 167
	args = xcalloc(sizeof(const char *), (argc + 4));
	ac = 0;
	args[ac++] = "add--interactive";
	if (patch_interactive)
		args[ac++] = "--patch";
	args[ac++] = "--";
	if (argc) {
		memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
		ac += argc;
	}
	args[ac] = NULL;
168 169 170 171

	status = run_command_v_opt(args, RUN_GIT_CMD);
	free(args);
	return status;
172 173
}

174
static struct lock_file lock_file;
L
Linus Torvalds 已提交
175

176
static const char ignore_error[] =
177 178
"The following paths are ignored by one of your .gitignore files:\n";

179 180 181 182 183 184 185
static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;

static struct option builtin_add_options[] = {
	OPT__DRY_RUN(&show_only),
	OPT__VERBOSE(&verbose),
	OPT_GROUP(""),
	OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
186
	OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
187 188 189 190 191 192
	OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
	OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
	OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
	OPT_END(),
};

193
int cmd_add(int argc, const char **argv, const char *prefix)
L
Linus Torvalds 已提交
194
{
195
	int i, newfd;
L
Linus Torvalds 已提交
196 197
	const char **pathspec;
	struct dir_struct dir;
J
Junio C Hamano 已提交
198

199 200
	argc = parse_options(argc, argv, builtin_add_options,
			  builtin_add_usage, 0);
201 202
	if (patch_interactive)
		add_interactive = 1;
203
	if (add_interactive)
204
		exit(interactive_add(argc, argv, prefix));
L
Linus Torvalds 已提交
205

J
Junio C Hamano 已提交
206
	git_config(git_default_config);
L
Linus Torvalds 已提交
207

208
	newfd = hold_locked_index(&lock_file, 1);
L
Linus Torvalds 已提交
209

J
Jeff King 已提交
210
	if (take_worktree_changes) {
211
		const char **pathspec;
212 213
		if (read_cache() < 0)
			die("index file corrupt");
214 215
		pathspec = get_pathspec(prefix, argv);
		add_files_to_cache(verbose, prefix, pathspec);
216 217 218
		goto finish;
	}

219
	if (argc == 0) {
220 221 222 223
		fprintf(stderr, "Nothing specified, nothing added.\n");
		fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
		return 0;
	}
224
	pathspec = get_pathspec(prefix, argv);
L
Linus Torvalds 已提交
225

226 227 228 229 230
	if (refresh_only) {
		refresh(verbose, pathspec);
		goto finish;
	}

231
	fill_directory(&dir, pathspec, ignored_too);
L
Linus Torvalds 已提交
232 233 234 235 236 237 238 239 240 241 242 243

	if (show_only) {
		const char *sep = "", *eof = "";
		for (i = 0; i < dir.nr; i++) {
			printf("%s%s", sep, dir.entries[i]->name);
			sep = " ";
			eof = "\n";
		}
		fputs(eof, stdout);
		return 0;
	}

244 245 246
	if (read_cache() < 0)
		die("index file corrupt");

247
	if (dir.ignored_nr) {
248
		fprintf(stderr, ignore_error);
249 250
		for (i = 0; i < dir.ignored_nr; i++) {
			fprintf(stderr, "%s\n", dir.ignored[i]->name);
251
		}
252
		fprintf(stderr, "Use -f if you really want to add them.\n");
253
		die("no files added");
254 255
	}

L
Linus Torvalds 已提交
256
	for (i = 0; i < dir.nr; i++)
257
		add_file_to_cache(dir.entries[i]->name, verbose);
L
Linus Torvalds 已提交
258

259
 finish:
L
Linus Torvalds 已提交
260 261
	if (active_cache_changed) {
		if (write_cache(newfd, active_cache, active_nr) ||
B
Brandon Casey 已提交
262
		    commit_locked_index(&lock_file))
L
Linus Torvalds 已提交
263 264 265 266 267
			die("Unable to write new index file");
	}

	return 0;
}