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