Skip to content

Commit

Permalink
Merge branch 'jl/submodule-diff-dirtiness'
Browse files Browse the repository at this point in the history
* jl/submodule-diff-dirtiness:
  git status: ignoring untracked files must apply to submodules too
  git status: Fix false positive "new commits" output for dirty submodules
  Refactor dirty submodule detection in diff-lib.c
  git status: Show detailed dirty status of submodules in long format
  git diff --submodule: Show detailed dirty status of submodules
  • Loading branch information
Junio C Hamano committed Mar 24, 2010
2 parents 797d443 + 3bfc450 commit b6a7a06
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 43 deletions.
45 changes: 27 additions & 18 deletions diff-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,27 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
return 0;
}

/*
* Has a file changed or has a submodule new commits or a dirty work tree?
*
* Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES
* option is set, the caller does not only want to know if a submodule is
* modified at all but wants to know all the conditions that are met (new
* commits, untracked content and/or modified content).
*/
static int match_stat_with_submodule(struct diff_options *diffopt,
struct cache_entry *ce, struct stat *st,
unsigned ce_option, unsigned *dirty_submodule)
{
int changed = ce_match_stat(ce, st, ce_option);
if (S_ISGITLINK(ce->ce_mode)
&& !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
&& (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
*dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
}
return changed;
}

int run_diff_files(struct rev_info *revs, unsigned int option)
{
int entries, i;
Expand Down Expand Up @@ -177,15 +198,9 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
ce->sha1, ce->name, 0);
continue;
}
changed = ce_match_stat(ce, &st, ce_option);
if (S_ISGITLINK(ce->ce_mode)
&& !DIFF_OPT_TST(&revs->diffopt, IGNORE_SUBMODULES)
&& (!changed || (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
&& is_submodule_modified(ce->name)) {
changed = 1;
dirty_submodule = 1;
}
if (!changed) {
changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
ce_option, &dirty_submodule);
if (!changed && !dirty_submodule) {
ce_mark_uptodate(ce);
if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
continue;
Expand Down Expand Up @@ -240,14 +255,8 @@ static int get_stat_data(struct cache_entry *ce,
}
return -1;
}
changed = ce_match_stat(ce, &st, 0);
if (S_ISGITLINK(ce->ce_mode)
&& !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
&& (!changed || (diffopt->output_format & DIFF_FORMAT_PATCH))
&& is_submodule_modified(ce->name)) {
changed = 1;
*dirty_submodule = 1;
}
changed = match_stat_with_submodule(diffopt, ce, &st,
0, dirty_submodule);
if (changed) {
mode = ce_mode_from_stat(ce, st.st_mode);
sha1 = null_sha1;
Expand Down Expand Up @@ -322,7 +331,7 @@ static int show_modified(struct rev_info *revs,
}

oldmode = old->ce_mode;
if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
if (mode == oldmode && !hashcmp(sha1, old->sha1) && !dirty_submodule &&
!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
return 0;

Expand Down
13 changes: 11 additions & 2 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -2032,7 +2032,7 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
char *data = xmalloc(100), *dirty = "";

/* Are we looking at the work tree? */
if (!s->sha1_valid && s->dirty_submodule)
if (s->dirty_submodule)
dirty = "-dirty";

len = snprintf(data, 100,
Expand Down Expand Up @@ -2628,6 +2628,12 @@ int diff_setup_done(struct diff_options *options)
*/
if (options->pickaxe)
DIFF_OPT_SET(options, RECURSIVE);
/*
* When patches are generated, submodules diffed against the work tree
* must be checked for dirtiness too so it can be shown in the output
*/
if (options->output_format & DIFF_FORMAT_PATCH)
DIFF_OPT_SET(options, DIRTY_SUBMODULES);

if (options->detect_rename && options->rename_limit < 0)
options->rename_limit = diff_rename_limit_default;
Expand Down Expand Up @@ -3086,7 +3092,8 @@ int diff_unmodified_pair(struct diff_filepair *p)
* dealing with a change.
*/
if (one->sha1_valid && two->sha1_valid &&
!hashcmp(one->sha1, two->sha1))
!hashcmp(one->sha1, two->sha1) &&
!one->dirty_submodule && !two->dirty_submodule)
return 1; /* no change */
if (!one->sha1_valid && !two->sha1_valid)
return 1; /* both look at the same file on the filesystem. */
Expand Down Expand Up @@ -3221,6 +3228,8 @@ static void diff_resolve_rename_copy(void)
}
else if (hashcmp(p->one->sha1, p->two->sha1) ||
p->one->mode != p->two->mode ||
p->one->dirty_submodule ||
p->two->dirty_submodule ||
is_null_sha1(p->one->sha1))
p->status = DIFF_STATUS_MODIFIED;
else {
Expand Down
2 changes: 2 additions & 0 deletions diff.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
#define DIFF_OPT_ALLOW_TEXTCONV (1 << 21)
#define DIFF_OPT_DIFF_FROM_CONTENTS (1 << 22)
#define DIFF_OPT_SUBMODULE_LOG (1 << 23)
#define DIFF_OPT_DIRTY_SUBMODULES (1 << 24)
#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)

#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
Expand Down
4 changes: 3 additions & 1 deletion diffcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ struct diff_filespec {
#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
unsigned should_free : 1; /* data should be free()'ed */
unsigned should_munmap : 1; /* data should be munmap()'ed */
unsigned dirty_submodule : 1; /* For submodules: its work tree is dirty */
unsigned dirty_submodule : 2; /* For submodules: its work tree is dirty */
#define DIRTY_SUBMODULE_UNTRACKED 1
#define DIRTY_SUBMODULE_MODIFIED 2

struct userdiff_driver *driver;
/* data should be considered "binary"; -1 means "don't know yet" */
Expand Down
45 changes: 40 additions & 5 deletions submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "commit.h"
#include "revision.h"
#include "run-command.h"
#include "diffcore.h"

static int add_submodule_odb(const char *path)
{
Expand Down Expand Up @@ -85,13 +86,21 @@ void show_submodule_summary(FILE *f, const char *path,
message = "(revision walker failed)";
}

if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
fprintf(f, "Submodule %s contains untracked content\n", path);
if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
fprintf(f, "Submodule %s contains modified content\n", path);

if (!hashcmp(one, two)) {
strbuf_release(&sb);
return;
}

strbuf_addf(&sb, "Submodule %s %s..", path,
find_unique_abbrev(one, DEFAULT_ABBREV));
if (!fast_backward && !fast_forward)
strbuf_addch(&sb, '.');
strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
if (dirty_submodule)
strbuf_add(&sb, "-dirty", 6);
if (message)
strbuf_addf(&sb, " %s\n", message);
else
Expand Down Expand Up @@ -121,17 +130,21 @@ void show_submodule_summary(FILE *f, const char *path,
strbuf_release(&sb);
}

int is_submodule_modified(const char *path)
unsigned is_submodule_modified(const char *path, int ignore_untracked)
{
int len, i;
int i;
ssize_t len;
struct child_process cp;
const char *argv[] = {
"status",
"--porcelain",
NULL,
NULL,
};
const char *env[LOCAL_REPO_ENV_SIZE + 3];
struct strbuf buf = STRBUF_INIT;
unsigned dirty_submodule = 0;
const char *line, *next_line;

for (i = 0; i < LOCAL_REPO_ENV_SIZE; i++)
env[i] = local_repo_env[i];
Expand All @@ -151,6 +164,9 @@ int is_submodule_modified(const char *path)
env[i++] = strbuf_detach(&buf, NULL);
env[i] = NULL;

if (ignore_untracked)
argv[2] = "-uno";

memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = env;
Expand All @@ -161,6 +177,25 @@ int is_submodule_modified(const char *path)
die("Could not run git status --porcelain");

len = strbuf_read(&buf, cp.out, 1024);
line = buf.buf;
while (len > 2) {
if ((line[0] == '?') && (line[1] == '?')) {
dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
break;
} else {
dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
if (ignore_untracked ||
(dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
break;
}
next_line = strchr(line, '\n');
if (!next_line)
break;
next_line++;
len -= (next_line - line);
line = next_line;
}
close(cp.out);

if (finish_command(&cp))
Expand All @@ -169,5 +204,5 @@ int is_submodule_modified(const char *path)
for (i = LOCAL_REPO_ENV_SIZE; env[i]; i++)
free((char *)env[i]);
strbuf_release(&buf);
return len != 0;
return dirty_submodule;
}
2 changes: 1 addition & 1 deletion submodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ void show_submodule_summary(FILE *f, const char *path,
unsigned char one[20], unsigned char two[20],
unsigned dirty_submodule,
const char *del, const char *add, const char *reset);
int is_submodule_modified(const char *path);
unsigned is_submodule_modified(const char *path, int ignore_untracked);

#endif
17 changes: 11 additions & 6 deletions t/t4041-diff-submodule.sh
Original file line number Diff line number Diff line change
Expand Up @@ -201,23 +201,24 @@ test_expect_success 'submodule contains untracked content' "
echo new > sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
diff actual - <<-EOF
Submodule sm1 $head6..$head6-dirty:
Submodule sm1 contains untracked content
EOF
"

test_expect_success 'submodule contains untracked and modifed content' "
echo new > sm1/foo6 &&
git diff-index -p --submodule=log HEAD >actual &&
diff actual - <<-EOF
Submodule sm1 $head6..$head6-dirty:
Submodule sm1 contains untracked content
Submodule sm1 contains modified content
EOF
"

test_expect_success 'submodule contains modifed content' "
rm -f sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
diff actual - <<-EOF
Submodule sm1 $head6..$head6-dirty:
Submodule sm1 contains modified content
EOF
"

Expand All @@ -235,7 +236,8 @@ test_expect_success 'modified submodule contains untracked content' "
echo new > sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
diff actual - <<-EOF
Submodule sm1 $head6..$head8-dirty:
Submodule sm1 contains untracked content
Submodule sm1 $head6..$head8:
> change
EOF
"
Expand All @@ -244,7 +246,9 @@ test_expect_success 'modified submodule contains untracked and modifed content'
echo modification >> sm1/foo6 &&
git diff-index -p --submodule=log HEAD >actual &&
diff actual - <<-EOF
Submodule sm1 $head6..$head8-dirty:
Submodule sm1 contains untracked content
Submodule sm1 contains modified content
Submodule sm1 $head6..$head8:
> change
EOF
"
Expand All @@ -253,7 +257,8 @@ test_expect_success 'modified submodule contains modifed content' "
rm -f sm1/new-file &&
git diff-index -p --submodule=log HEAD >actual &&
diff actual - <<-EOF
Submodule sm1 $head6..$head8-dirty:
Submodule sm1 contains modified content
Submodule sm1 $head6..$head8:
> change
EOF
"
Expand Down
Loading

0 comments on commit b6a7a06

Please sign in to comment.