Code

ff2863074beeb76924ceb20e7f2c18b71c44f88e
[git.git] / submodule.c
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"
11 struct string_list config_name_for_path;
12 struct string_list config_ignore_for_name;
14 static 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;
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;
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();
48 done:
49         strbuf_release(&objects_directory);
50         return ret;
51 }
53 void 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 }
65 int 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;
71         var += 10;              /* Skip "submodule." */
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                 }
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;
104 void handle_ignore_submodules_arg(struct diff_options *diffopt,
105                                   const char *arg)
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);
117 void 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)
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;
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)";
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         }
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);
169         if (!hashcmp(one, two)) {
170                 strbuf_release(&sb);
171                 return;
172         }
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);
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);
208 unsigned is_submodule_modified(const char *path, int ignore_untracked)
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;
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;
232         }
233         strbuf_reset(&buf);
235         if (ignore_untracked)
236                 argv[2] = "-uno";
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");
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);
270         if (finish_command(&cp))
271                 die("git status --porcelain failed");
273         strbuf_release(&buf);
274         return dirty_submodule;