submodule.c 7.5 KB
Newer Older
1 2 3 4 5 6
#include "cache.h"
#include "submodule.h"
#include "dir.h"
#include "diff.h"
#include "commit.h"
#include "revision.h"
7
#include "run-command.h"
8
#include "diffcore.h"
9 10 11 12
#include "string-list.h"

struct string_list config_name_for_path;
struct string_list config_ignore_for_name;
13

14
static int add_submodule_odb(const char *path)
15 16 17
{
	struct strbuf objects_directory = STRBUF_INIT;
	struct alternate_object_database *alt_odb;
J
Jens Lehmann 已提交
18
	int ret = 0;
19
	const char *git_dir;
20

21 22 23 24 25 26 27
	strbuf_addf(&objects_directory, "%s/.git", path);
	git_dir = read_gitfile_gently(objects_directory.buf);
	if (git_dir) {
		strbuf_reset(&objects_directory);
		strbuf_addstr(&objects_directory, git_dir);
	}
	strbuf_addstr(&objects_directory, "/objects/");
J
Jens Lehmann 已提交
28 29 30 31
	if (!is_directory(objects_directory.buf)) {
		ret = -1;
		goto done;
	}
32 33 34 35 36
	/* avoid adding it twice */
	for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next)
		if (alt_odb->name - alt_odb->base == objects_directory.len &&
				!strncmp(alt_odb->base, objects_directory.buf,
					objects_directory.len))
J
Jens Lehmann 已提交
37
			goto done;
38 39 40 41 42 43 44 45 46 47

	alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb));
	alt_odb->next = alt_odb_list;
	strcpy(alt_odb->base, objects_directory.buf);
	alt_odb->name = alt_odb->base + objects_directory.len;
	alt_odb->name[2] = '/';
	alt_odb->name[40] = '\0';
	alt_odb->name[41] = '\0';
	alt_odb_list = alt_odb;
	prepare_alt_odb();
J
Jens Lehmann 已提交
48 49 50
done:
	strbuf_release(&objects_directory);
	return ret;
51 52
}

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
					     const char *path)
{
	struct string_list_item *path_option, *ignore_option;
	path_option = unsorted_string_list_lookup(&config_name_for_path, path);
	if (path_option) {
		ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
		if (ignore_option)
			handle_ignore_submodules_arg(diffopt, ignore_option->util);
	}
}

int parse_submodule_config_option(const char *var, const char *value)
{
	int len;
	struct string_list_item *config;
	struct strbuf submodname = STRBUF_INIT;

	var += 10;		/* Skip "submodule." */

	len = strlen(var);
	if ((len > 5) && !strcmp(var + len - 5, ".path")) {
		strbuf_add(&submodname, var, len - 5);
		config = unsorted_string_list_lookup(&config_name_for_path, value);
		if (config)
			free(config->util);
		else
			config = string_list_append(&config_name_for_path, xstrdup(value));
		config->util = strbuf_detach(&submodname, NULL);
		strbuf_release(&submodname);
	} else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
		if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
		    strcmp(value, "all") && strcmp(value, "none")) {
			warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
			return 0;
		}

		strbuf_add(&submodname, var, len - 7);
		config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
		if (config)
			free(config->util);
		else
			config = string_list_append(&config_ignore_for_name,
						    strbuf_detach(&submodname, NULL));
		strbuf_release(&submodname);
		config->util = xstrdup(value);
		return 0;
	}
	return 0;
}

104 105 106 107 108 109 110 111 112
void handle_ignore_submodules_arg(struct diff_options *diffopt,
				  const char *arg)
{
	if (!strcmp(arg, "all"))
		DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
	else if (!strcmp(arg, "untracked"))
		DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
	else if (!strcmp(arg, "dirty"))
		DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
113
	else if (strcmp(arg, "none"))
114 115 116
		die("bad --ignore-submodules argument: %s", arg);
}

117 118
void show_submodule_summary(FILE *f, const char *path,
		unsigned char one[20], unsigned char two[20],
119
		unsigned dirty_submodule,
120 121 122
		const char *del, const char *add, const char *reset)
{
	struct rev_info rev;
123
	struct commit *commit, *left = left, *right = right;
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	struct commit_list *merge_bases, *list;
	const char *message = NULL;
	struct strbuf sb = STRBUF_INIT;
	static const char *format = "  %m %s";
	int fast_forward = 0, fast_backward = 0;

	if (is_null_sha1(two))
		message = "(submodule deleted)";
	else if (add_submodule_odb(path))
		message = "(not checked out)";
	else if (is_null_sha1(one))
		message = "(new submodule)";
	else if (!(left = lookup_commit_reference(one)) ||
		 !(right = lookup_commit_reference(two)))
		message = "(commits not present)";

	if (!message) {
		init_revisions(&rev, NULL);
		setup_revisions(0, NULL, &rev, NULL);
		rev.left_right = 1;
		rev.first_parent_only = 1;
		left->object.flags |= SYMMETRIC_LEFT;
		add_pending_object(&rev, &left->object, path);
		add_pending_object(&rev, &right->object, path);
		merge_bases = get_merge_bases(left, right, 1);
		if (merge_bases) {
			if (merge_bases->item == left)
				fast_forward = 1;
			else if (merge_bases->item == right)
				fast_backward = 1;
		}
		for (list = merge_bases; list; list = list->next) {
			list->item->object.flags |= UNINTERESTING;
			add_pending_object(&rev, &list->item->object,
				sha1_to_hex(list->item->object.sha1));
		}
		if (prepare_revision_walk(&rev))
			message = "(revision walker failed)";
	}

164 165 166 167 168 169 170 171 172 173
	if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
		fprintf(f, "Submodule %s contains untracked content\n", path);
	if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
		fprintf(f, "Submodule %s contains modified content\n", path);

	if (!hashcmp(one, two)) {
		strbuf_release(&sb);
		return;
	}

174 175 176 177 178 179 180 181 182 183 184 185 186
	strbuf_addf(&sb, "Submodule %s %s..", path,
			find_unique_abbrev(one, DEFAULT_ABBREV));
	if (!fast_backward && !fast_forward)
		strbuf_addch(&sb, '.');
	strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
	if (message)
		strbuf_addf(&sb, " %s\n", message);
	else
		strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
	fwrite(sb.buf, sb.len, 1, f);

	if (!message) {
		while ((commit = get_revision(&rev))) {
J
Junio C Hamano 已提交
187 188
			struct pretty_print_context ctx = {0};
			ctx.date_mode = rev.date_mode;
189 190 191 192 193 194 195
			strbuf_setlen(&sb, 0);
			if (commit->object.flags & SYMMETRIC_LEFT) {
				if (del)
					strbuf_addstr(&sb, del);
			}
			else if (add)
				strbuf_addstr(&sb, add);
J
Junio C Hamano 已提交
196
			format_commit_message(commit, format, &sb, &ctx);
197 198 199 200 201 202 203 204 205 206
			if (reset)
				strbuf_addstr(&sb, reset);
			strbuf_addch(&sb, '\n');
			fprintf(f, "%s", sb.buf);
		}
		clear_commit_marks(left, ~0);
		clear_commit_marks(right, ~0);
	}
	strbuf_release(&sb);
}
207

208
unsigned is_submodule_modified(const char *path, int ignore_untracked)
209
{
210
	ssize_t len;
211 212 213 214 215
	struct child_process cp;
	const char *argv[] = {
		"status",
		"--porcelain",
		NULL,
216
		NULL,
217 218
	};
	struct strbuf buf = STRBUF_INIT;
219 220
	unsigned dirty_submodule = 0;
	const char *line, *next_line;
221
	const char *git_dir;
222

223 224 225 226 227
	strbuf_addf(&buf, "%s/.git", path);
	git_dir = read_gitfile_gently(buf.buf);
	if (!git_dir)
		git_dir = buf.buf;
	if (!is_directory(git_dir)) {
228 229 230 231 232 233 234
		strbuf_release(&buf);
		/* The submodule is not checked out, so it is not modified */
		return 0;

	}
	strbuf_reset(&buf);

235 236 237
	if (ignore_untracked)
		argv[2] = "-uno";

238 239
	memset(&cp, 0, sizeof(cp));
	cp.argv = argv;
240
	cp.env = local_repo_env;
241 242 243
	cp.git_cmd = 1;
	cp.no_stdin = 1;
	cp.out = -1;
244
	cp.dir = path;
245 246 247 248
	if (start_command(&cp))
		die("Could not run git status --porcelain");

	len = strbuf_read(&buf, cp.out, 1024);
249 250 251 252 253 254 255 256
	line = buf.buf;
	while (len > 2) {
		if ((line[0] == '?') && (line[1] == '?')) {
			dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
			if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
				break;
		} else {
			dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
257 258
			if (ignore_untracked ||
			    (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
259 260 261 262 263 264 265 266 267
				break;
		}
		next_line = strchr(line, '\n');
		if (!next_line)
			break;
		next_line++;
		len -= (next_line - line);
		line = next_line;
	}
268 269 270 271 272 273
	close(cp.out);

	if (finish_command(&cp))
		die("git status --porcelain failed");

	strbuf_release(&buf);
274
	return dirty_submodule;
275
}