Skip to content

Commit

Permalink
Merge branch 'jl/submodule-diff'
Browse files Browse the repository at this point in the history
* jl/submodule-diff:
  Performance optimization for detection of modified submodules
  git status: Show uncommitted submodule changes too when enabled
  Teach diff that modified submodule directory is dirty
  Show submodules as modified when they contain a dirty work tree
  • Loading branch information
Junio C Hamano committed Jan 23, 2010
2 parents 16735ae + e3d42c4 commit c6ec7ef
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 52 deletions.
44 changes: 32 additions & 12 deletions diff-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "cache-tree.h"
#include "unpack-trees.h"
#include "refs.h"
#include "submodule.h"

/*
* diff-files
Expand Down Expand Up @@ -72,6 +73,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
unsigned int oldmode, newmode;
struct cache_entry *ce = active_cache[i];
int changed;
unsigned dirty_submodule = 0;

if (DIFF_OPT_TST(&revs->diffopt, QUICK) &&
DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
Expand Down Expand Up @@ -159,7 +161,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
continue;
}

if (ce_uptodate(ce) || ce_skip_worktree(ce))
if ((ce_uptodate(ce) && !S_ISGITLINK(ce->ce_mode)) || ce_skip_worktree(ce))
continue;

/* If CE_VALID is set, don't look at workdir for file removal */
Expand All @@ -172,10 +174,16 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (silent_on_removed)
continue;
diff_addremove(&revs->diffopt, '-', ce->ce_mode,
ce->sha1, ce->name);
ce->sha1, ce->name, 0);
continue;
}
changed = ce_match_stat(ce, &st, ce_option);
if (S_ISGITLINK(ce->ce_mode)
&& (!changed || (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
&& is_submodule_modified(ce->name)) {
changed = 1;
dirty_submodule = 1;
}
if (!changed) {
ce_mark_uptodate(ce);
if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
Expand All @@ -185,7 +193,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
newmode = ce_mode_from_stat(ce, st.st_mode);
diff_change(&revs->diffopt, oldmode, newmode,
ce->sha1, (changed ? null_sha1 : ce->sha1),
ce->name);
ce->name, 0, dirty_submodule);

}
diffcore_std(&revs->diffopt);
Expand All @@ -201,16 +209,18 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
static void diff_index_show_file(struct rev_info *revs,
const char *prefix,
struct cache_entry *ce,
const unsigned char *sha1, unsigned int mode)
const unsigned char *sha1, unsigned int mode,
unsigned dirty_submodule)
{
diff_addremove(&revs->diffopt, prefix[0], mode,
sha1, ce->name);
sha1, ce->name, dirty_submodule);
}

static int get_stat_data(struct cache_entry *ce,
const unsigned char **sha1p,
unsigned int *modep,
int cached, int match_missing)
int cached, int match_missing,
unsigned *dirty_submodule, int output_format)
{
const unsigned char *sha1 = ce->sha1;
unsigned int mode = ce->ce_mode;
Expand All @@ -230,6 +240,12 @@ static int get_stat_data(struct cache_entry *ce,
return -1;
}
changed = ce_match_stat(ce, &st, 0);
if (S_ISGITLINK(ce->ce_mode)
&& (!changed || (output_format & DIFF_FORMAT_PATCH))
&& is_submodule_modified(ce->name)) {
changed = 1;
*dirty_submodule = 1;
}
if (changed) {
mode = ce_mode_from_stat(ce, st.st_mode);
sha1 = null_sha1;
Expand All @@ -247,15 +263,17 @@ static void show_new_file(struct rev_info *revs,
{
const unsigned char *sha1;
unsigned int mode;
unsigned dirty_submodule = 0;

/*
* New file in the index: it might actually be different in
* the working copy.
*/
if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
if (get_stat_data(new, &sha1, &mode, cached, match_missing,
&dirty_submodule, revs->diffopt.output_format) < 0)
return;

diff_index_show_file(revs, "+", new, sha1, mode);
diff_index_show_file(revs, "+", new, sha1, mode, dirty_submodule);
}

static int show_modified(struct rev_info *revs,
Expand All @@ -266,11 +284,13 @@ static int show_modified(struct rev_info *revs,
{
unsigned int mode, oldmode;
const unsigned char *sha1;
unsigned dirty_submodule = 0;

if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
if (get_stat_data(new, &sha1, &mode, cached, match_missing,
&dirty_submodule, revs->diffopt.output_format) < 0) {
if (report_missing)
diff_index_show_file(revs, "-", old,
old->sha1, old->ce_mode);
old->sha1, old->ce_mode, 0);
return -1;
}

Expand Down Expand Up @@ -305,7 +325,7 @@ static int show_modified(struct rev_info *revs,
return 0;

diff_change(&revs->diffopt, oldmode, mode,
old->sha1, sha1, old->name);
old->sha1, sha1, old->name, 0, dirty_submodule);
return 0;
}

Expand Down Expand Up @@ -352,7 +372,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
* Something removed from the tree?
*/
if (!idx) {
diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode);
diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode, 0);
return;
}

Expand Down
22 changes: 17 additions & 5 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -2029,9 +2029,14 @@ static int populate_from_stdin(struct diff_filespec *s)
static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
{
int len;
char *data = xmalloc(100);
char *data = xmalloc(100), *dirty = "";

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

len = snprintf(data, 100,
"Subproject commit %s\n", sha1_to_hex(s->sha1));
"Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
s->data = data;
s->size = len;
s->should_free = 1;
Expand Down Expand Up @@ -3714,7 +3719,7 @@ int diff_result_code(struct diff_options *opt, int status)
void diff_addremove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
const char *concatpath)
const char *concatpath, unsigned dirty_submodule)
{
struct diff_filespec *one, *two;

Expand Down Expand Up @@ -3746,8 +3751,10 @@ void diff_addremove(struct diff_options *options,

if (addremove != '+')
fill_filespec(one, sha1, mode);
if (addremove != '-')
if (addremove != '-') {
fill_filespec(two, sha1, mode);
two->dirty_submodule = dirty_submodule;
}

diff_queue(&diff_queued_diff, one, two);
if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
Expand All @@ -3758,7 +3765,8 @@ void diff_change(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1,
const unsigned char *new_sha1,
const char *concatpath)
const char *concatpath,
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
{
struct diff_filespec *one, *two;

Expand All @@ -3771,6 +3779,8 @@ void diff_change(struct diff_options *options,
const unsigned char *tmp_c;
tmp = old_mode; old_mode = new_mode; new_mode = tmp;
tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule;
new_dirty_submodule = tmp;
}

if (options->prefix &&
Expand All @@ -3781,6 +3791,8 @@ void diff_change(struct diff_options *options,
two = alloc_filespec(concatpath);
fill_filespec(one, old_sha1, old_mode);
fill_filespec(two, new_sha1, new_mode);
one->dirty_submodule = old_dirty_submodule;
two->dirty_submodule = new_dirty_submodule;

diff_queue(&diff_queued_diff, one, two);
if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
Expand Down
10 changes: 6 additions & 4 deletions diff.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1,
const unsigned char *new_sha1,
const char *fullpath);
const char *fullpath,
unsigned old_dirty_submodule, unsigned new_dirty_submodule);

typedef void (*add_remove_fn_t)(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
const char *fullpath);
const char *fullpath, unsigned dirty_submodule);

typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
struct diff_options *options, void *data);
Expand Down Expand Up @@ -177,13 +178,14 @@ extern void diff_addremove(struct diff_options *,
int addremove,
unsigned mode,
const unsigned char *sha1,
const char *fullpath);
const char *fullpath, unsigned dirty_submodule);

extern void diff_change(struct diff_options *,
unsigned mode1, unsigned mode2,
const unsigned char *sha1,
const unsigned char *sha2,
const char *fullpath);
const char *fullpath,
unsigned dirty_submodule1, unsigned dirty_submodule2);

extern void diff_unmerge(struct diff_options *,
const char *path,
Expand Down
1 change: 1 addition & 0 deletions diffcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ 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 */

struct userdiff_driver *driver;
/* data should be considered "binary"; -1 means "don't know yet" */
Expand Down
6 changes: 5 additions & 1 deletion git-submodule.sh
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,11 @@ cmd_summary() {
echo
done |
if test -n "$for_status"; then
echo "# Modified submodules:"
if [ -n "$files" ]; then
echo "# Submodules changed but not updated:"
else
echo "# Submodule changes to be committed:"
fi
echo "#"
sed -e 's|^|# |' -e 's|^# $|#|'
else
Expand Down
5 changes: 3 additions & 2 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ static int tree_difference = REV_TREE_SAME;
static void file_add_remove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
const char *fullpath)
const char *fullpath, unsigned dirty_submodule)
{
int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;

Expand All @@ -281,7 +281,8 @@ static void file_change(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1,
const unsigned char *new_sha1,
const char *fullpath)
const char *fullpath,
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
{
tree_difference = REV_TREE_DIFFERENT;
DIFF_OPT_SET(options, HAS_CHANGES);
Expand Down
49 changes: 49 additions & 0 deletions submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "diff.h"
#include "commit.h"
#include "revision.h"
#include "run-command.h"

static int add_submodule_odb(const char *path)
{
Expand Down Expand Up @@ -112,3 +113,51 @@ void show_submodule_summary(FILE *f, const char *path,
}
strbuf_release(&sb);
}

int is_submodule_modified(const char *path)
{
int len;
struct child_process cp;
const char *argv[] = {
"status",
"--porcelain",
NULL,
};
char *env[3];
struct strbuf buf = STRBUF_INIT;

strbuf_addf(&buf, "%s/.git/", path);
if (!is_directory(buf.buf)) {
strbuf_release(&buf);
/* The submodule is not checked out, so it is not modified */
return 0;

}
strbuf_reset(&buf);

strbuf_addf(&buf, "GIT_WORK_TREE=%s", path);
env[0] = strbuf_detach(&buf, NULL);
strbuf_addf(&buf, "GIT_DIR=%s/.git", path);
env[1] = strbuf_detach(&buf, NULL);
env[2] = NULL;

memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = (const char *const *)env;
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.out = -1;
if (start_command(&cp))
die("Could not run git status --porcelain");

len = strbuf_read(&buf, cp.out, 1024);
close(cp.out);

if (finish_command(&cp))
die("git status --porcelain failed");

free(env[0]);
free(env[1]);
strbuf_release(&buf);
return len != 0;
}
1 change: 1 addition & 0 deletions submodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
void show_submodule_summary(FILE *f, const char *path,
unsigned char one[20], unsigned char two[20],
const char *del, const char *add, const char *reset);
int is_submodule_modified(const char *path);

#endif
Loading

0 comments on commit c6ec7ef

Please sign in to comment.