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"
910
static int add_submodule_odb(const char *path)
11{
12struct strbuf objects_directory = STRBUF_INIT;
13struct alternate_object_database *alt_odb;
14int ret = 0;
15const char *git_dir;
1617
strbuf_addf(&objects_directory, "%s/.git", path);
18git_dir = read_gitfile_gently(objects_directory.buf);
19if (git_dir) {
20strbuf_reset(&objects_directory);
21strbuf_addstr(&objects_directory, git_dir);
22}
23strbuf_addstr(&objects_directory, "/objects/");
24if (!is_directory(objects_directory.buf)) {
25ret = -1;
26goto done;
27}
28/* avoid adding it twice */
29for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next)
30if (alt_odb->name - alt_odb->base == objects_directory.len &&
31!strncmp(alt_odb->base, objects_directory.buf,
32objects_directory.len))
33goto done;
3435
alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb));
36alt_odb->next = alt_odb_list;
37strcpy(alt_odb->base, objects_directory.buf);
38alt_odb->name = alt_odb->base + objects_directory.len;
39alt_odb->name[2] = '/';
40alt_odb->name[40] = '\0';
41alt_odb->name[41] = '\0';
42alt_odb_list = alt_odb;
43prepare_alt_odb();
44done:
45strbuf_release(&objects_directory);
46return ret;
47}
4849
void handle_ignore_submodules_arg(struct diff_options *diffopt,
50const char *arg)
51{
52if (!strcmp(arg, "all"))
53DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
54else if (!strcmp(arg, "untracked"))
55DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
56else if (!strcmp(arg, "dirty"))
57DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
58else
59die("bad --ignore-submodules argument: %s", arg);
60}
6162
void show_submodule_summary(FILE *f, const char *path,
63unsigned char one[20], unsigned char two[20],
64unsigned dirty_submodule,
65const char *del, const char *add, const char *reset)
66{
67struct rev_info rev;
68struct commit *commit, *left = left, *right = right;
69struct commit_list *merge_bases, *list;
70const char *message = NULL;
71struct strbuf sb = STRBUF_INIT;
72static const char *format = " %m %s";
73int fast_forward = 0, fast_backward = 0;
7475
if (is_null_sha1(two))
76message = "(submodule deleted)";
77else if (add_submodule_odb(path))
78message = "(not checked out)";
79else if (is_null_sha1(one))
80message = "(new submodule)";
81else if (!(left = lookup_commit_reference(one)) ||
82!(right = lookup_commit_reference(two)))
83message = "(commits not present)";
8485
if (!message) {
86init_revisions(&rev, NULL);
87setup_revisions(0, NULL, &rev, NULL);
88rev.left_right = 1;
89rev.first_parent_only = 1;
90left->object.flags |= SYMMETRIC_LEFT;
91add_pending_object(&rev, &left->object, path);
92add_pending_object(&rev, &right->object, path);
93merge_bases = get_merge_bases(left, right, 1);
94if (merge_bases) {
95if (merge_bases->item == left)
96fast_forward = 1;
97else if (merge_bases->item == right)
98fast_backward = 1;
99}
100for (list = merge_bases; list; list = list->next) {
101list->item->object.flags |= UNINTERESTING;
102add_pending_object(&rev, &list->item->object,
103sha1_to_hex(list->item->object.sha1));
104}
105if (prepare_revision_walk(&rev))
106message = "(revision walker failed)";
107}
108109
if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
110fprintf(f, "Submodule %s contains untracked content\n", path);
111if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
112fprintf(f, "Submodule %s contains modified content\n", path);
113114
if (!hashcmp(one, two)) {
115strbuf_release(&sb);
116return;
117}
118119
strbuf_addf(&sb, "Submodule %s %s..", path,
120find_unique_abbrev(one, DEFAULT_ABBREV));
121if (!fast_backward && !fast_forward)
122strbuf_addch(&sb, '.');
123strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
124if (message)
125strbuf_addf(&sb, " %s\n", message);
126else
127strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
128fwrite(sb.buf, sb.len, 1, f);
129130
if (!message) {
131while ((commit = get_revision(&rev))) {
132struct pretty_print_context ctx = {0};
133ctx.date_mode = rev.date_mode;
134strbuf_setlen(&sb, 0);
135if (commit->object.flags & SYMMETRIC_LEFT) {
136if (del)
137strbuf_addstr(&sb, del);
138}
139else if (add)
140strbuf_addstr(&sb, add);
141format_commit_message(commit, format, &sb, &ctx);
142if (reset)
143strbuf_addstr(&sb, reset);
144strbuf_addch(&sb, '\n');
145fprintf(f, "%s", sb.buf);
146}
147clear_commit_marks(left, ~0);
148clear_commit_marks(right, ~0);
149}
150strbuf_release(&sb);
151}
152153
unsigned is_submodule_modified(const char *path, int ignore_untracked)
154{
155ssize_t len;
156struct child_process cp;
157const char *argv[] = {
158"status",
159"--porcelain",
160NULL,
161NULL,
162};
163struct strbuf buf = STRBUF_INIT;
164unsigned dirty_submodule = 0;
165const char *line, *next_line;
166const char *git_dir;
167168
strbuf_addf(&buf, "%s/.git", path);
169git_dir = read_gitfile_gently(buf.buf);
170if (!git_dir)
171git_dir = buf.buf;
172if (!is_directory(git_dir)) {
173strbuf_release(&buf);
174/* The submodule is not checked out, so it is not modified */
175return 0;
176177
}
178strbuf_reset(&buf);
179180
if (ignore_untracked)
181argv[2] = "-uno";
182183
memset(&cp, 0, sizeof(cp));
184cp.argv = argv;
185cp.env = local_repo_env;
186cp.git_cmd = 1;
187cp.no_stdin = 1;
188cp.out = -1;
189cp.dir = path;
190if (start_command(&cp))
191die("Could not run git status --porcelain");
192193
len = strbuf_read(&buf, cp.out, 1024);
194line = buf.buf;
195while (len > 2) {
196if ((line[0] == '?') && (line[1] == '?')) {
197dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
198if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
199break;
200} else {
201dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
202if (ignore_untracked ||
203(dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
204break;
205}
206next_line = strchr(line, '\n');
207if (!next_line)
208break;
209next_line++;
210len -= (next_line - line);
211line = next_line;
212}
213close(cp.out);
214215
if (finish_command(&cp))
216die("git status --porcelain failed");
217218
strbuf_release(&buf);
219return dirty_submodule;
220}