diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 13234fa280..859d67990a 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -228,6 +228,9 @@ endif::git-format-patch[] --no-ext-diff:: Disallow external diff drivers. +--ignore-submodules:: + Ignore changes to submodules in the diff generation. + --src-prefix=:: Show the given source prefix instead of "a/". diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 66be18ef36..06640603c4 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -15,6 +15,7 @@ SYNOPSIS [--cacheinfo ]\* [--chmod=(+|-)x] [--assume-unchanged | --no-assume-unchanged] + [--ignore-submodules] [--really-refresh] [--unresolve] [--again | -g] [--info-only] [--index-info] [-z] [--stdin] @@ -54,6 +55,10 @@ OPTIONS default behavior is to error out. This option makes git-update-index continue anyway. +--ignore-submodules: + Do not try to update submodules. This option is only respected + when passed before --refresh. + --unmerged:: If --refresh finds unmerged changes in the index, the default behavior is to error out. This option makes git-update-index diff --git a/builtin-update-index.c b/builtin-update-index.c index a8795d3d5f..d4c85c0cbc 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -593,6 +593,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) refresh_flags |= REFRESH_QUIET; continue; } + if (!strcmp(path, "--ignore-submodules")) { + refresh_flags |= REFRESH_IGNORE_SUBMODULES; + continue; + } if (!strcmp(path, "--add")) { allow_add = 1; continue; diff --git a/cache.h b/cache.h index 8db19cc253..ef330b4c6b 100644 --- a/cache.h +++ b/cache.h @@ -388,6 +388,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ +#define REFRESH_IGNORE_SUBMODULES 0x0008 /* ignore submodules */ extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen); struct lock_file { diff --git a/diff.c b/diff.c index 439d4746ca..d57bc299ee 100644 --- a/diff.c +++ b/diff.c @@ -2496,6 +2496,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) DIFF_OPT_CLR(options, ALLOW_EXTERNAL); + else if (!strcmp(arg, "--ignore-submodules")) + DIFF_OPT_SET(options, IGNORE_SUBMODULES); /* misc options */ else if (!strcmp(arg, "-z")) @@ -3355,6 +3357,9 @@ void diff_addremove(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode)) + return; + /* This may look odd, but it is a preparation for * feeding "there are unchanged files which should * not produce diffs, but when you are doing copy @@ -3399,6 +3404,10 @@ void diff_change(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode) + && S_ISGITLINK(new_mode)) + return; + if (DIFF_OPT_TST(options, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_c; diff --git a/diff.h b/diff.h index 3a02d38d12..1dfe1f98b1 100644 --- a/diff.h +++ b/diff.h @@ -63,6 +63,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_OPT_REVERSE_DIFF (1 << 15) #define DIFF_OPT_CHECK_FAILED (1 << 16) #define DIFF_OPT_RELATIVE_NAME (1 << 17) +#define DIFF_OPT_IGNORE_SUBMODULES (1 << 18) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 8aa73712ca..8ee08ff2fd 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -56,9 +56,9 @@ output () { require_clean_work_tree () { # test if working tree is dirty git rev-parse --verify HEAD > /dev/null && - git update-index --refresh && - git diff-files --quiet && - git diff-index --cached --quiet HEAD -- || + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules && + git diff-index --cached --quiet HEAD --ignore-submodules -- || die "Working tree is dirty" } @@ -377,11 +377,12 @@ do # Sanity check git rev-parse --verify HEAD >/dev/null || die "Cannot read HEAD" - git update-index --refresh && git diff-files --quiet || + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules || die "Working tree is dirty" # do we have anything to commit? - if git diff-index --cached --quiet HEAD -- + if git diff-index --cached --quiet --ignore-submodules HEAD -- then : Nothing to commit -- skip this else diff --git a/git-rebase.sh b/git-rebase.sh index 68855c18ae..dd7dfe123c 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -60,7 +60,7 @@ continue_merge () { fi cmt=`cat "$dotest/current"` - if ! git diff-index --quiet HEAD -- + if ! git diff-index --quiet --ignore-submodules HEAD -- then if ! git commit --no-verify -C "$cmt" then @@ -150,7 +150,7 @@ while test $# != 0 do case "$1" in --continue) - git diff-files --quiet || { + git diff-files --quiet --ignore-submodules || { echo "You must edit all merge conflicts and then" echo "mark them as resolved using git add" exit 1 @@ -282,8 +282,8 @@ else fi # The tree must be really really clean. -git update-index --refresh || exit -diff=$(git diff-index --cached --name-status -r HEAD --) +git update-index --ignore-submodules --refresh || exit +diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --) case "$diff" in ?*) echo "cannot rebase: your index is not up-to-date" echo "$diff" diff --git a/git-stash.sh b/git-stash.sh index c2b68205a2..4938ade589 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -15,8 +15,8 @@ trap 'rm -f "$TMP-*"' 0 ref_stash=refs/stash no_changes () { - git diff-index --quiet --cached HEAD -- && - git diff-files --quiet + git diff-index --quiet --cached HEAD --ignore-submodules -- && + git diff-files --quiet --ignore-submodules } clear_stash () { @@ -130,7 +130,7 @@ show_stash () { } apply_stash () { - git diff-files --quiet || + git diff-files --quiet --ignore-submodules || die 'Cannot restore on top of a dirty state' unstash_index= diff --git a/read-cache.c b/read-cache.c index 8b467f8f41..bc039819ee 100644 --- a/read-cache.c +++ b/read-cache.c @@ -942,6 +942,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int allow_unmerged = (flags & REFRESH_UNMERGED) != 0; int quiet = (flags & REFRESH_QUIET) != 0; int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; + int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0; unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; for (i = 0; i < istate->cache_nr; i++) { @@ -949,6 +950,9 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int cache_errno = 0; ce = istate->cache[i]; + if (ignore_submodules && S_ISGITLINK(ce->ce_mode)) + continue; + if (ce_stage(ce)) { while ((i < istate->cache_nr) && ! strcmp(istate->cache[i]->name, ce->name)) diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh new file mode 100755 index 0000000000..5becb3ec54 --- /dev/null +++ b/t/t7402-submodule-rebase.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# +# Copyright (c) 2008 Johannes Schindelin +# + +test_description='Test rebasing and stashing with dirty submodules' + +. ./test-lib.sh + +test_expect_success setup ' + + echo file > file && + git add file && + test_tick && + git commit -m initial && + git clone . submodule && + git add submodule && + test_tick && + git commit -m submodule && + echo second line >> file && + (cd submodule && git pull) && + test_tick && + git commit -m file-and-submodule -a + +' + +test_expect_success 'rebase with a dirty submodule' ' + + (cd submodule && + echo 3rd line >> file && + test_tick && + git commit -m fork -a) && + echo unrelated >> file2 && + git add file2 && + test_tick && + git commit -m unrelated file2 && + echo other line >> file && + test_tick && + git commit -m update file && + CURRENT=$(cd submodule && git rev-parse HEAD) && + EXPECTED=$(git rev-parse HEAD~2:submodule) && + GIT_TRACE=1 git rebase --onto HEAD~2 HEAD^ && + STORED=$(git rev-parse HEAD:submodule) && + test $EXPECTED = $STORED && + test $CURRENT = $(cd submodule && git rev-parse HEAD) + +' + +cat > fake-editor.sh << \EOF +#!/bin/sh +echo $EDITOR_TEXT +EOF +chmod a+x fake-editor.sh + +test_expect_success 'interactive rebase with a dirty submodule' ' + + test submodule = $(git diff --name-only) && + HEAD=$(git rev-parse HEAD) && + GIT_EDITOR="\"$(pwd)/fake-editor.sh\"" EDITOR_TEXT="pick $HEAD" \ + git rebase -i HEAD^ && + test submodule = $(git diff --name-only) + +' + +test_expect_success 'rebase with dirty file and submodule fails' ' + + echo yet another line >> file && + test_tick && + git commit -m next file && + echo rewrite > file && + test_tick && + git commit -m rewrite file && + echo dirty > file && + ! git rebase --onto HEAD~2 HEAD^ + +' + +test_expect_success 'stash with a dirty submodule' ' + + echo new > file && + CURRENT=$(cd submodule && git rev-parse HEAD) && + git stash && + test new != $(cat file) && + test submodule = $(git diff --name-only) && + test $CURRENT = $(cd submodule && git rev-parse HEAD) && + git stash apply && + test new = $(cat file) && + test $CURRENT = $(cd submodule && git rev-parse HEAD) + +' + +test_done