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"910static 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;1617strbuf_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;3435alt_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}4849void 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);58else59die("bad --ignore-submodules argument: %s", arg);60}6162void 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;7475if (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)";8485if (!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}108109if (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);113114if (!hashcmp(one, two)) {115strbuf_release(&sb);116return;117}118119strbuf_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);126else127strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");128fwrite(sb.buf, sb.len, 1, f);129130if (!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}152153unsigned 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;167168strbuf_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);179180if (ignore_untracked)181argv[2] = "-uno";182183memset(&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");192193len = 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);214215if (finish_command(&cp))216die("git status --porcelain failed");217218strbuf_release(&buf);219return dirty_submodule;220}