Submodules: Use "ignore" settings from .gitmodules too for diff and status
[git/git.git] / submodule.c
CommitLineData
752c0c24
JS
1#include "cache.h"
2#include "submodule.h"
3#include "dir.h"
4#include "diff.h"
5#include "commit.h"
6#include "revision.h"
ee6fc514 7#include "run-command.h"
c7e1a736 8#include "diffcore.h"
aee9c7d6
JL
9#include "string-list.h"
10
11struct string_list config_name_for_path;
12struct string_list config_ignore_for_name;
752c0c24 13
cb58c932 14static int add_submodule_odb(const char *path)
752c0c24
JS
15{
16 struct strbuf objects_directory = STRBUF_INIT;
17 struct alternate_object_database *alt_odb;
de7a7960 18 int ret = 0;
eee49b6c 19 const char *git_dir;
752c0c24 20
eee49b6c
JL
21 strbuf_addf(&objects_directory, "%s/.git", path);
22 git_dir = read_gitfile_gently(objects_directory.buf);
23 if (git_dir) {
24 strbuf_reset(&objects_directory);
25 strbuf_addstr(&objects_directory, git_dir);
26 }
27 strbuf_addstr(&objects_directory, "/objects/");
de7a7960
JL
28 if (!is_directory(objects_directory.buf)) {
29 ret = -1;
30 goto done;
31 }
752c0c24
JS
32 /* avoid adding it twice */
33 for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next)
34 if (alt_odb->name - alt_odb->base == objects_directory.len &&
35 !strncmp(alt_odb->base, objects_directory.buf,
36 objects_directory.len))
de7a7960 37 goto done;
752c0c24
JS
38
39 alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb));
40 alt_odb->next = alt_odb_list;
41 strcpy(alt_odb->base, objects_directory.buf);
42 alt_odb->name = alt_odb->base + objects_directory.len;
43 alt_odb->name[2] = '/';
44 alt_odb->name[40] = '\0';
45 alt_odb->name[41] = '\0';
46 alt_odb_list = alt_odb;
47 prepare_alt_odb();
de7a7960
JL
48done:
49 strbuf_release(&objects_directory);
50 return ret;
752c0c24
JS
51}
52
aee9c7d6
JL
53void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
54 const char *path)
55{
56 struct string_list_item *path_option, *ignore_option;
57 path_option = unsorted_string_list_lookup(&config_name_for_path, path);
58 if (path_option) {
59 ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
60 if (ignore_option)
61 handle_ignore_submodules_arg(diffopt, ignore_option->util);
62 }
63}
64
302ad7a9
JL
65static int submodule_config(const char *var, const char *value, void *cb)
66{
67 if (!prefixcmp(var, "submodule."))
68 return parse_submodule_config_option(var, value);
69 return 0;
70}
71
72void gitmodules_config(void)
73{
74 const char *work_tree = get_git_work_tree();
75 if (work_tree) {
76 struct strbuf gitmodules_path = STRBUF_INIT;
77 strbuf_addstr(&gitmodules_path, work_tree);
78 strbuf_addstr(&gitmodules_path, "/.gitmodules");
79 git_config_from_file(submodule_config, gitmodules_path.buf, NULL);
80 strbuf_release(&gitmodules_path);
81 }
82}
83
aee9c7d6
JL
84int parse_submodule_config_option(const char *var, const char *value)
85{
86 int len;
87 struct string_list_item *config;
88 struct strbuf submodname = STRBUF_INIT;
89
90 var += 10; /* Skip "submodule." */
91
92 len = strlen(var);
93 if ((len > 5) && !strcmp(var + len - 5, ".path")) {
94 strbuf_add(&submodname, var, len - 5);
95 config = unsorted_string_list_lookup(&config_name_for_path, value);
96 if (config)
97 free(config->util);
98 else
99 config = string_list_append(&config_name_for_path, xstrdup(value));
100 config->util = strbuf_detach(&submodname, NULL);
101 strbuf_release(&submodname);
102 } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
103 if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
104 strcmp(value, "all") && strcmp(value, "none")) {
105 warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
106 return 0;
107 }
108
109 strbuf_add(&submodname, var, len - 7);
110 config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
111 if (config)
112 free(config->util);
113 else
114 config = string_list_append(&config_ignore_for_name,
115 strbuf_detach(&submodname, NULL));
116 strbuf_release(&submodname);
117 config->util = xstrdup(value);
118 return 0;
119 }
120 return 0;
121}
122
46a958b3
JL
123void handle_ignore_submodules_arg(struct diff_options *diffopt,
124 const char *arg)
125{
126 if (!strcmp(arg, "all"))
127 DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
128 else if (!strcmp(arg, "untracked"))
129 DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
130 else if (!strcmp(arg, "dirty"))
131 DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
aee9c7d6 132 else if (strcmp(arg, "none"))
46a958b3
JL
133 die("bad --ignore-submodules argument: %s", arg);
134}
135
752c0c24
JS
136void show_submodule_summary(FILE *f, const char *path,
137 unsigned char one[20], unsigned char two[20],
721ceec1 138 unsigned dirty_submodule,
752c0c24
JS
139 const char *del, const char *add, const char *reset)
140{
141 struct rev_info rev;
75b9a8a6 142 struct commit *commit, *left = left, *right = right;
752c0c24
JS
143 struct commit_list *merge_bases, *list;
144 const char *message = NULL;
145 struct strbuf sb = STRBUF_INIT;
146 static const char *format = " %m %s";
147 int fast_forward = 0, fast_backward = 0;
148
149 if (is_null_sha1(two))
150 message = "(submodule deleted)";
151 else if (add_submodule_odb(path))
152 message = "(not checked out)";
153 else if (is_null_sha1(one))
154 message = "(new submodule)";
155 else if (!(left = lookup_commit_reference(one)) ||
156 !(right = lookup_commit_reference(two)))
157 message = "(commits not present)";
158
159 if (!message) {
160 init_revisions(&rev, NULL);
161 setup_revisions(0, NULL, &rev, NULL);
162 rev.left_right = 1;
163 rev.first_parent_only = 1;
164 left->object.flags |= SYMMETRIC_LEFT;
165 add_pending_object(&rev, &left->object, path);
166 add_pending_object(&rev, &right->object, path);
167 merge_bases = get_merge_bases(left, right, 1);
168 if (merge_bases) {
169 if (merge_bases->item == left)
170 fast_forward = 1;
171 else if (merge_bases->item == right)
172 fast_backward = 1;
173 }
174 for (list = merge_bases; list; list = list->next) {
175 list->item->object.flags |= UNINTERESTING;
176 add_pending_object(&rev, &list->item->object,
177 sha1_to_hex(list->item->object.sha1));
178 }
179 if (prepare_revision_walk(&rev))
180 message = "(revision walker failed)";
181 }
182
c7e1a736
JL
183 if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
184 fprintf(f, "Submodule %s contains untracked content\n", path);
185 if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
186 fprintf(f, "Submodule %s contains modified content\n", path);
187
188 if (!hashcmp(one, two)) {
189 strbuf_release(&sb);
190 return;
191 }
192
752c0c24
JS
193 strbuf_addf(&sb, "Submodule %s %s..", path,
194 find_unique_abbrev(one, DEFAULT_ABBREV));
195 if (!fast_backward && !fast_forward)
196 strbuf_addch(&sb, '.');
197 strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
198 if (message)
199 strbuf_addf(&sb, " %s\n", message);
200 else
201 strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
202 fwrite(sb.buf, sb.len, 1, f);
203
204 if (!message) {
205 while ((commit = get_revision(&rev))) {
5f809ff5
JH
206 struct pretty_print_context ctx = {0};
207 ctx.date_mode = rev.date_mode;
752c0c24
JS
208 strbuf_setlen(&sb, 0);
209 if (commit->object.flags & SYMMETRIC_LEFT) {
210 if (del)
211 strbuf_addstr(&sb, del);
212 }
213 else if (add)
214 strbuf_addstr(&sb, add);
5f809ff5 215 format_commit_message(commit, format, &sb, &ctx);
752c0c24
JS
216 if (reset)
217 strbuf_addstr(&sb, reset);
218 strbuf_addch(&sb, '\n');
219 fprintf(f, "%s", sb.buf);
220 }
221 clear_commit_marks(left, ~0);
222 clear_commit_marks(right, ~0);
223 }
224 strbuf_release(&sb);
225}
ee6fc514 226
3bfc4504 227unsigned is_submodule_modified(const char *path, int ignore_untracked)
ee6fc514 228{
c7e1a736 229 ssize_t len;
ee6fc514
JL
230 struct child_process cp;
231 const char *argv[] = {
232 "status",
233 "--porcelain",
234 NULL,
3bfc4504 235 NULL,
ee6fc514 236 };
ee6fc514 237 struct strbuf buf = STRBUF_INIT;
c7e1a736
JL
238 unsigned dirty_submodule = 0;
239 const char *line, *next_line;
eee49b6c 240 const char *git_dir;
ee6fc514 241
eee49b6c
JL
242 strbuf_addf(&buf, "%s/.git", path);
243 git_dir = read_gitfile_gently(buf.buf);
244 if (!git_dir)
245 git_dir = buf.buf;
246 if (!is_directory(git_dir)) {
ee6fc514
JL
247 strbuf_release(&buf);
248 /* The submodule is not checked out, so it is not modified */
249 return 0;
250
251 }
252 strbuf_reset(&buf);
253
3bfc4504
JL
254 if (ignore_untracked)
255 argv[2] = "-uno";
256
ee6fc514
JL
257 memset(&cp, 0, sizeof(cp));
258 cp.argv = argv;
eee49b6c 259 cp.env = local_repo_env;
ee6fc514
JL
260 cp.git_cmd = 1;
261 cp.no_stdin = 1;
262 cp.out = -1;
eee49b6c 263 cp.dir = path;
ee6fc514
JL
264 if (start_command(&cp))
265 die("Could not run git status --porcelain");
266
267 len = strbuf_read(&buf, cp.out, 1024);
c7e1a736
JL
268 line = buf.buf;
269 while (len > 2) {
270 if ((line[0] == '?') && (line[1] == '?')) {
271 dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
272 if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
273 break;
274 } else {
275 dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
3bfc4504
JL
276 if (ignore_untracked ||
277 (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
c7e1a736
JL
278 break;
279 }
280 next_line = strchr(line, '\n');
281 if (!next_line)
282 break;
283 next_line++;
284 len -= (next_line - line);
285 line = next_line;
286 }
ee6fc514
JL
287 close(cp.out);
288
289 if (finish_command(&cp))
290 die("git status --porcelain failed");
291
ee6fc514 292 strbuf_release(&buf);
c7e1a736 293 return dirty_submodule;
ee6fc514 294}