{"thread":{"id":"23401","subject":"[WIP PATCH 0/4] Recursively checkout submodules","startedAt":"2010-04-09T21:34:39Z","lastAt":"2010-04-10T20:57:52Z","messageCount":13,"participants":["Jens Lehmann","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"139078","messageId":"4BBF9D6F.2000006@web.de","threadId":"23401","inReplyTo":null,"subject":"[WIP PATCH 0/4] Recursively checkout submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-09T21:34:39Z","receivedAt":"2010-04-09T21:34:39Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Having found some Git time recently i started to tackle the next\nmajor issue on my agenda to improve submodule experience: Check\nout the matching versions of populated submodules when doing a\ncheckout in the superproject (without having to do an explicit\n\"git submodule update\").\n\nSo here is what i came up with so far: The first two patches lay\nthe groundwork by introducing a new function checkout_submodule()\nin submodule.c and calling it from checkout_entry(). The new\n\"ignore_submodules\" flags added to \"struct checkout\" and \"struct\nunpack_trees_options\" can be used to prevent that (and default\nto true, which is the old behavior then changed by the following\npatches). The next two patches teach \"git checkout\" and \"git\ncheckout-index\" to default to checking out submodules too unless\nthe new option \"--ignore-subodules\" is used.\n\nWhat's working:\n* Changing branches and specific revisions update the submodules\n  accordingly\n* \"Revert changes\" in \"git gui\" resets submodules now\n\nWhat's missing:\n* Test cases\n* I think \"git fetch\" should recurse into populated submodules\n  too, otherwise the commits to check out there might be missing\n\n\nOpinions?\n\n\nJens Lehmann (4):\n  Prepare checkout_entry() for recursive checkout of submodules\n  Add \"ignore_submodules\" member to \"struct unpack_trees_options\"\n  Teach checkout to recursively checkout submodules\n  Teach checkout-index to recursively checkout submodules\n\n Documentation/git-checkout-index.txt |    9 ++++++++-\n Documentation/git-checkout.txt       |    7 +++++++\n archive.c                            |    1 +\n builtin/apply.c                      |    1 +\n builtin/checkout-index.c             |    5 ++++-\n builtin/checkout.c                   |    6 ++++++\n builtin/clone.c                      |    1 +\n builtin/commit.c                     |    1 +\n builtin/merge.c                      |    2 ++\n builtin/read-tree.c                  |    1 +\n builtin/reset.c                      |    1 +\n cache.h                              |    3 ++-\n diff-lib.c                           |    2 ++\n entry.c                              |    9 ++++++---\n merge-recursive.c                    |    1 +\n submodule.c                          |   32 ++++++++++++++++++++++++++++++++\n submodule.h                          |    1 +\n t/t2013-checkout-submodule.sh        |   14 ++++++++++++--\n unpack-trees.c                       |    1 +\n unpack-trees.h                       |    3 ++-\n 20 files changed, 92 insertions(+), 9 deletions(-)\n"},{"id":"139079","messageId":"4BBF9DC9.8030905@web.de","threadId":"23401","inReplyTo":"4BBF9D6F.2000006@web.de","subject":"[WIP PATCH 1/4] Prepare checkout_entry() for recursive checkout of submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-09T21:36:09Z","receivedAt":"2010-04-09T21:36:09Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"To achieve this, the new member \"ignore_submodules\" has been added to the\nbitfield in \"struct checkout\". All users of \"struct checkout\" are setting\nthis flag to 1 for backward compatibility for now. Also the new function\ncheckout_submodule() has been added and is called by checkout_entry() when\n\"ignore_submodules\" is false (which will be the default behavior later).\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n builtin/apply.c          |    1 +\n builtin/checkout-index.c |    1 +\n builtin/checkout.c       |    1 +\n cache.h                  |    3 ++-\n entry.c                  |    9 ++++++---\n submodule.c              |   32 ++++++++++++++++++++++++++++++++\n submodule.h              |    1 +\n unpack-trees.c           |    1 +\n 8 files changed, 45 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex ec755ac..a7788a9 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -2827,6 +2827,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s\n \t\t\tcostate.quiet = 0;\n \t\t\tcostate.not_new = 0;\n \t\t\tcostate.refresh_cache = 1;\n+\t\t\tcostate.ignore_submodules = 1;\n \t\t\tif (checkout_entry(*ce, &costate, NULL) ||\n \t\t\t    lstat(old_name, st))\n \t\t\t\treturn -1;\ndiff --git a/builtin/checkout-index.c b/builtin/checkout-index.c\nindex a7a5ee1..c35bad3 100644\n--- a/builtin/checkout-index.c\n+++ b/builtin/checkout-index.c\n@@ -254,6 +254,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)\n \tstate.force = force;\n \tstate.quiet = quiet;\n \tstate.not_new = not_new;\n+\tstate.ignore_submodules = 1;\n\n \tif (state.base_dir_len || to_tempfile) {\n \t\t/* when --prefix is specified we do not\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c382521..ee198ae 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -248,6 +248,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \tmemset(&state, 0, sizeof(state));\n \tstate.force = 1;\n \tstate.refresh_cache = 1;\n+\tstate.ignore_submodules = 1;\n \tfor (pos = 0; pos < active_nr; pos++) {\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {\ndiff --git a/cache.h b/cache.h\nindex 303c107..996fa69 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -802,7 +802,8 @@ struct checkout {\n \tunsigned force:1,\n \t\t quiet:1,\n \t\t not_new:1,\n-\t\t refresh_cache:1;\n+\t\t refresh_cache:1,\n+\t\t ignore_submodules:1;\n };\n\n extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);\ndiff --git a/entry.c b/entry.c\nindex 004182c..15dec09 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"blob.h\"\n #include \"dir.h\"\n+#include \"submodule.h\"\n\n static void create_directories(const char *path, int path_len,\n \t\t\t       const struct checkout *state)\n@@ -222,9 +223,11 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t\n \t\t * just do the right thing)\n \t\t */\n \t\tif (S_ISDIR(st.st_mode)) {\n-\t\t\t/* If it is a gitlink, leave it alone! */\n-\t\t\tif (S_ISGITLINK(ce->ce_mode))\n-\t\t\t\treturn 0;\n+\t\t\tif (S_ISGITLINK(ce->ce_mode)) {\n+\t\t\t\tif (state->ignore_submodules)\n+\t\t\t\t\treturn 0;\n+\t\t\t\treturn checkout_submodule(ce->name, ce->sha1, state->force);\n+\t       \t\t}\n \t\t\tif (!state->force)\n \t\t\t\treturn error(\"%s is a directory\", path);\n \t\t\tremove_subtree(path);\ndiff --git a/submodule.c b/submodule.c\nindex b3b8bc1..59029b2 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -206,3 +206,35 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)\n \tstrbuf_release(&buf);\n \treturn dirty_submodule;\n }\n+\n+int checkout_submodule(const char *path, const unsigned char sha1[20], int force)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct child_process cp;\n+\tconst char *hex_sha1 = sha1_to_hex(sha1);\n+\tconst char *argv[] = {\n+\t\t\"checkout\",\n+                force ? \"-qf\" : \"-q\",\n+\t\thex_sha1,\n+\t\tNULL,\n+\t};\n+\t\n+\tstrbuf_addf(&buf, \"%s/.git/\", path);\n+\tif (!is_directory(buf.buf)) {\n+\t\tstrbuf_release(&buf);\n+\t\t/* The submodule is not populated, so we can't check it out */\n+\t\treturn 0;\n+\t}\n+\n+\tmemset(&cp, 0, sizeof(cp));\n+\tcp.argv = argv;\n+\tcp.env = local_repo_env;\n+\tcp.git_cmd = 1;\n+\tcp.no_stdin = 1;\n+\tcp.out = -1;\n+\tcp.dir = path;   /* GIT_WORK_TREE doesn't work for git checkout */\n+\tif (run_command(&cp))\n+\t\treturn error(\"Could not checkout submodule %s\", path);\n+\n+\treturn 0;\n+}\ndiff --git a/submodule.h b/submodule.h\nindex dbda270..fc6909e 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -6,5 +6,6 @@ void show_submodule_summary(FILE *f, const char *path,\n \t\tunsigned dirty_submodule,\n \t\tconst char *del, const char *add, const char *reset);\n unsigned is_submodule_modified(const char *path, int ignore_untracked);\n+int checkout_submodule(const char *path, const unsigned char sha1[20], int force);\n\n #endif\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex c29a9e0..151b422 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -711,6 +711,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tstate.force = 1;\n \tstate.quiet = 1;\n \tstate.refresh_cache = 1;\n+\tstate.ignore_submodules = 1;\n\n \tmemset(&el, 0, sizeof(el));\n \tif (!core_apply_sparse_checkout || !o->update)\n-- \n1.7.1.rc0.248.g09203\n"},{"id":"139080","messageId":"4BBF9DFD.5080909@web.de","threadId":"23401","inReplyTo":"4BBF9D6F.2000006@web.de","subject":"[WIP PATCH 2/4] Add \"ignore_submodules\" member to \"struct unpack_trees_options\"","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-09T21:37:01Z","receivedAt":"2010-04-09T21:37:01Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"To be able to checkout submodule content the new member \"ignore_submodules\"\nis added to \"struct unpack_trees_options\". In \"unpack_callback()\" it is\nused to initialize the recently introduced member with the same name in\n\"struct checkout\".\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n archive.c           |    1 +\n builtin/clone.c     |    1 +\n builtin/commit.c    |    1 +\n builtin/merge.c     |    2 ++\n builtin/read-tree.c |    1 +\n builtin/reset.c     |    1 +\n diff-lib.c          |    2 ++\n merge-recursive.c   |    1 +\n unpack-trees.c      |    2 +-\n unpack-trees.h      |    3 ++-\n 10 files changed, 13 insertions(+), 2 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex d700af3..d730bab 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -184,6 +184,7 @@ int write_archive_entries(struct archiver_args *args,\n \t\topts.src_index = &the_index;\n \t\topts.dst_index = &the_index;\n \t\topts.fn = oneway_merge;\n+\t\topts.ignore_submodules = 1;\n \t\tinit_tree_desc(&t, args->tree->buffer, args->tree->size);\n \t\tif (unpack_trees(1, &t, &opts))\n \t\t\treturn -1;\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 05be999..f826fe9 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -637,6 +637,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\topts.verbose_update = (option_verbosity > 0);\n \t\topts.src_index = &the_index;\n \t\topts.dst_index = &the_index;\n+\t\topts.ignore_submodules = 1;\n\n \t\ttree = parse_tree_indirect(our_head_points_at->old_sha1);\n \t\tparse_tree(tree);\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 017b8a5..419a4cf 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -257,6 +257,7 @@ static void create_base_index(void)\n \topts.dst_index = &the_index;\n\n \topts.fn = oneway_merge;\n+\topts.ignore_submodules = 1;\n \ttree = parse_tree_indirect(head_sha1);\n \tif (!tree)\n \t\tdie(\"failed to unpack HEAD tree object\");\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 37d414b..e589404 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -532,6 +532,7 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,\n \tif (!trees[nr_trees++])\n \t\treturn -1;\n \topts.fn = threeway_merge;\n+\topts.ignore_submodules = 1;\n \tcache_tree_free(&active_cache_tree);\n \tfor (i = 0; i < nr_trees; i++) {\n \t\tparse_tree(trees[i]);\n@@ -705,6 +706,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote\n \topts.merge = 1;\n \topts.fn = twoway_merge;\n \topts.msgs = get_porcelain_error_msgs();\n+\topts.ignore_submodules = 1;\n\n \ttrees[nr_trees] = parse_tree_indirect(head);\n \tif (!trees[nr_trees++])\ndiff --git a/builtin/read-tree.c b/builtin/read-tree.c\nindex 8bdcab1..cd3edfa 100644\n--- a/builtin/read-tree.c\n+++ b/builtin/read-tree.c\n@@ -201,6 +201,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)\n\n \tif (opts.debug_unpack)\n \t\topts.fn = debug_merge;\n+\topts.ignore_submodules = 1;\n\n \tcache_tree_free(&active_cache_tree);\n \tfor (i = 0; i < nr_trees; i++) {\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex 1283068..0ac7e6f 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -98,6 +98,7 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet\n \t\tnr++;\n \t\topts.fn = twoway_merge;\n \t}\n+\topts.ignore_submodules = 1;\n\n \tif (!fill_tree_descriptor(desc + nr - 1, sha1))\n \t\treturn error(\"Failed to find tree of %s.\", sha1_to_hex(sha1));\ndiff --git a/diff-lib.c b/diff-lib.c\nindex c9f6e05..64264a9 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -451,6 +451,7 @@ int run_diff_index(struct rev_info *revs, int cached)\n \topts.unpack_data = revs;\n \topts.src_index = &the_index;\n \topts.dst_index = NULL;\n+\topts.ignore_submodules = 1;\n\n \tinit_tree_desc(&t, tree->buffer, tree->size);\n \tif (unpack_trees(1, &t, &opts))\n@@ -509,6 +510,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)\n \topts.unpack_data = &revs;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n+\topts.ignore_submodules = 1;\n\n \tinit_tree_desc(&t, tree->buffer, tree->size);\n \tif (unpack_trees(1, &t, &opts))\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 206c103..d294e62 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -186,6 +186,7 @@ static int git_merge_trees(int index_only,\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n \topts.msgs = get_porcelain_error_msgs();\n+\topts.ignore_submodules = 1;\n\n \tinit_tree_desc_from_tree(t+0, common);\n \tinit_tree_desc_from_tree(t+1, head);\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 151b422..6821d7d 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -711,7 +711,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tstate.force = 1;\n \tstate.quiet = 1;\n \tstate.refresh_cache = 1;\n-\tstate.ignore_submodules = 1;\n+\tstate.ignore_submodules = o->ignore_submodules;\n\n \tmemset(&el, 0, sizeof(el));\n \tif (!core_apply_sparse_checkout || !o->update)\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex ef70eab..bbb49fd 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -33,7 +33,8 @@ struct unpack_trees_options {\n \t\t     diff_index_cached,\n \t\t     debug_unpack,\n \t\t     skip_sparse_checkout,\n-\t\t     gently;\n+\t\t     gently,\n+\t\t     ignore_submodules;\n \tconst char *prefix;\n \tint cache_bottom;\n \tstruct dir_struct *dir;\n-- \n1.7.1.rc0.248.g09203\n"},{"id":"139081","messageId":"4BBF9E89.3010806@web.de","threadId":"23401","inReplyTo":"4BBF9D6F.2000006@web.de","subject":"[WIP PATCH 3/4] Teach checkout to recursively checkout submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-09T21:39:21Z","receivedAt":"2010-04-09T21:39:21Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Change the default behavior of \"git checkout\" to check out submodules\ntoo. This can be prevented by using the new \"--ignore-submodules\" option.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n Documentation/git-checkout.txt |    7 +++++++\n builtin/checkout.c             |    7 ++++++-\n t/t2013-checkout-submodule.sh  |   14 ++++++++++++--\n 3 files changed, 25 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 4505eb6..63ac3bc 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -144,6 +144,13 @@ the conflicted merge in the specified paths.\n This means that you can use `git checkout -p` to selectively discard\n edits from your current working tree.\n\n+--ignore-submodules::\n+\tSince version 1.8.0 the default behavior is to checkout populated\n+\tsubmodules recursively. When this option is used, the work trees of\n+\tsubmodules will not be updated, only the hash recorded in the\n+\tsuperproject will be changed (this was the default behavior until\n+\t1.8.0).\n+\n <branch>::\n \tBranch to checkout; if it refers to a branch (i.e., a name that,\n \twhen prepended with \"refs/heads/\", is a valid ref), then that\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex ee198ae..0008ec5 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -31,6 +31,7 @@ struct checkout_opts {\n \tint force;\n \tint writeout_stage;\n \tint writeout_error;\n+\tint ignore_submodules;\n\n \tconst char *new_branch;\n \tconst char *new_orphan_branch;\n@@ -248,7 +249,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \tmemset(&state, 0, sizeof(state));\n \tstate.force = 1;\n \tstate.refresh_cache = 1;\n-\tstate.ignore_submodules = 1;\n+\tstate.ignore_submodules = opts->ignore_submodules;\n \tfor (pos = 0; pos < active_nr; pos++) {\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {\n@@ -314,6 +315,7 @@ static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)\n \topts.verbose_update = !o->quiet;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n+\topts.ignore_submodules = o->ignore_submodules;\n \tparse_tree(tree);\n \tinit_tree_desc(&tree_desc, tree->buffer, tree->size);\n \tswitch (unpack_trees(1, &tree_desc, &opts)) {\n@@ -393,6 +395,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.dir = xcalloc(1, sizeof(*topts.dir));\n \t\ttopts.dir->flags |= DIR_SHOW_IGNORED;\n \t\ttopts.dir->exclude_per_dir = \".gitignore\";\n+\t\ttopts.ignore_submodules = opts->ignore_submodules;\n \t\ttree = parse_tree_indirect(old->commit ?\n \t\t\t\t\t   old->commit->object.sha1 :\n \t\t\t\t\t   (unsigned char *)EMPTY_TREE_SHA1_BIN);\n@@ -649,6 +652,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_BOOLEAN, 0, \"guess\", &dwim_new_local_branch, NULL,\n \t\t  \"second guess 'git checkout no-such-branch'\",\n \t\t  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },\n+\t\tOPT_BOOLEAN(0, \"ignore-submodules\", &opts.ignore_submodules,\n+\t\t\t    \"don't update submodule work trees\"),\n \t\tOPT_END(),\n \t};\n \tint has_dash_dash;\ndiff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh\nindex fda3f0a..e5f2e6e 100755\n--- a/t/t2013-checkout-submodule.sh\n+++ b/t/t2013-checkout-submodule.sh\n@@ -29,14 +29,24 @@ test_expect_success '\"reset <submodule>\" updates the index' '\n \tgit diff-files --quiet\n '\n\n-test_expect_success '\"checkout <submodule>\" updates the index only' '\n+test_expect_success '\"checkout --ignore-submodules <submodule>\" updates the index only' '\n \tgit update-index --refresh &&\n \tgit diff-files --quiet &&\n \tgit diff-index --quiet --cached HEAD &&\n-\tgit checkout HEAD^ submodule &&\n+\tgit checkout --ignore-submodules HEAD^ submodule &&\n \ttest_must_fail git diff-files --quiet &&\n \tgit checkout HEAD submodule &&\n \tgit diff-files --quiet\n '\n\n+test_expect_success '\"checkout <submodule>\" updates recursively' '\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet &&\n+\tgit diff-index --quiet --cached HEAD &&\n+\tgit checkout HEAD^ submodule &&\n+\tgit diff-files --quiet &&\n+\tgit checkout HEAD submodule &&\n+\tgit diff-files --quiet\n+'\n+\n test_done\n-- \n1.7.1.rc0.248.g09203\n"},{"id":"139082","messageId":"4BBF9ED3.9010502@web.de","threadId":"23401","inReplyTo":"4BBF9D6F.2000006@web.de","subject":"[WIP PATCH 4/4] Teach checkout-index to recursively checkout submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-09T21:40:35Z","receivedAt":"2010-04-09T21:40:35Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Change the default behavior of \"git checkout-index\" to check out submodules\ntoo. This can be prevented by using the new \"--ignore-submodules\" option.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n Documentation/git-checkout-index.txt |    9 ++++++++-\n builtin/checkout-index.c             |    6 ++++--\n 2 files changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt\nindex d6aa6e1..dbd6625 100644\n--- a/Documentation/git-checkout-index.txt\n+++ b/Documentation/git-checkout-index.txt\n@@ -11,7 +11,7 @@ SYNOPSIS\n [verse]\n 'git checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>]\n \t\t   [--stage=<number>|all]\n-\t\t   [--temp]\n+\t\t   [--temp] [--ignore-submodules]\n \t\t   [-z] [--stdin]\n \t\t   [--] [<file>]\\*\n\n@@ -68,6 +68,13 @@ OPTIONS\n \tOnly meaningful with `--stdin`; paths are separated with\n \tNUL character instead of LF.\n\n+--ignore-submodules::\n+\tSince version 1.8.0 the default behavior is to checkout populated\n+\tsubmodules recursively. When this option is used, the work trees of\n+\tsubmodules will not be updated, only the hash recorded in the\n+\tsuperproject will be changed (this was the default behavior until\n+\t1.8.0).\n+\n \\--::\n \tDo not interpret any more arguments as options.\n\ndiff --git a/builtin/checkout-index.c b/builtin/checkout-index.c\nindex c35bad3..1e16d0d 100644\n--- a/builtin/checkout-index.c\n+++ b/builtin/checkout-index.c\n@@ -213,7 +213,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)\n \tint all = 0;\n \tint read_from_stdin = 0;\n \tint prefix_length;\n-\tint force = 0, quiet = 0, not_new = 0;\n+\tint force = 0, quiet = 0, not_new = 0, ignore_submodules = 0;\n \tstruct option builtin_checkout_index_options[] = {\n \t\tOPT_BOOLEAN('a', \"all\", &all,\n \t\t\t\"checks out all files in the index\"),\n@@ -238,6 +238,8 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)\n \t\tOPT_CALLBACK(0, \"stage\", NULL, NULL,\n \t\t\t\"copy out the files from named stage\",\n \t\t\toption_parse_stage),\n+\t\tOPT_BOOLEAN(0, \"ignore-submodules\", &ignore_submodules,\n+\t\t\t\"don't update submodule work trees\"),\n \t\tOPT_END()\n \t};\n\n@@ -254,7 +256,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)\n \tstate.force = force;\n \tstate.quiet = quiet;\n \tstate.not_new = not_new;\n-\tstate.ignore_submodules = 1;\n+\tstate.ignore_submodules = ignore_submodules;\n\n \tif (state.base_dir_len || to_tempfile) {\n \t\t/* when --prefix is specified we do not\n-- \n1.7.1.rc0.248.g09203\n"},{"id":"139083","messageId":"7vd3y81e3s.fsf@alter.siamese.dyndns.org","threadId":"23401","inReplyTo":"4BBF9DC9.8030905@web.de","subject":"Re: [WIP PATCH 1/4] Prepare checkout_entry() for recursive checkout of submodules","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-09T21:59:51Z","receivedAt":"2010-04-09T21:59:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jens Lehmann <Jens.Lehmann@web.de> writes:\n\n> +int checkout_submodule(const char *path, const unsigned char sha1[20], int force)\n> +{\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tstruct child_process cp;\n> +\tconst char *hex_sha1 = sha1_to_hex(sha1);\n> +\tconst char *argv[] = {\n> +\t\t\"checkout\",\n> +                force ? \"-qf\" : \"-q\",\n> +\t\thex_sha1,\n> +\t\tNULL,\n> +\t};\n\nWhy force -q?\n\n> +\tstrbuf_addf(&buf, \"%s/.git/\", path);\n> +\tif (!is_directory(buf.buf)) {\n> +\t\tstrbuf_release(&buf);\n> +\t\t/* The submodule is not populated, so we can't check it out */\n> +\t\treturn 0;\n> +\t}\n\nThis would give you an incorrect result if .git is a file that records\n\"gitdir: overthere\" (see read_gitfile_gently() in setup.c); I would expect\nit would become a fairly important ingredient if we ever enhance the\nsubmodule support to add submodule that disappears/reappears in the\nhistory.\n"},{"id":"139084","messageId":"7v8w8w1dwl.fsf@alter.siamese.dyndns.org","threadId":"23401","inReplyTo":"4BBF9ED3.9010502@web.de","subject":"Re: [WIP PATCH 4/4] Teach checkout-index to recursively checkout submodules","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-09T22:04:10Z","receivedAt":"2010-04-09T22:04:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"As a plumbing I would prefer to leave checkout-index as is; script writers\ncan choose to recurse if they choose to.\n\nThis is not limited to checkout-index, but what is the stance of this new\nfeature on failures from sub-checkout when there are local modifications\nin the work tree?  Some parts of the work tree is checked out while the\nones after the failure that sorts later in the alphabetical order will not\nbe checked out, resulting in an inconsistent state (I am not saying that\nit is good or bad---I am trying to see what the users should expect from\nthis new feature)?\n"},{"id":"139086","messageId":"4BBFB40C.1060905@web.de","threadId":"23401","inReplyTo":"7vd3y81e3s.fsf@alter.siamese.dyndns.org","subject":"Re: [WIP PATCH 1/4] Prepare checkout_entry() for recursive checkout of submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-09T23:11:08Z","receivedAt":"2010-04-09T23:11:08Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 09.04.2010 23:59, schrieb Junio C Hamano:\n> Jens Lehmann <Jens.Lehmann@web.de> writes:\n> \n>> +int checkout_submodule(const char *path, const unsigned char sha1[20], int force)\n>> +{\n>> +\tstruct strbuf buf = STRBUF_INIT;\n>> +\tstruct child_process cp;\n>> +\tconst char *hex_sha1 = sha1_to_hex(sha1);\n>> +\tconst char *argv[] = {\n>> +\t\t\"checkout\",\n>> +                force ? \"-qf\" : \"-q\",\n>> +\t\thex_sha1,\n>> +\t\tNULL,\n>> +\t};\n> \n> Why force -q?\n\nGood question. I seem to have tried to silence the output of checkout\nfrom run_command(), which AFIACS doesn't reach the console anyway\nunless i want it to ... (while at the same time managing to mess up\nthe tabs in that line ... :-/ )\n\n\n>> +\tstrbuf_addf(&buf, \"%s/.git/\", path);\n>> +\tif (!is_directory(buf.buf)) {\n>> +\t\tstrbuf_release(&buf);\n>> +\t\t/* The submodule is not populated, so we can't check it out */\n>> +\t\treturn 0;\n>> +\t}\n> \n> This would give you an incorrect result if .git is a file that records\n> \"gitdir: overthere\" (see read_gitfile_gently() in setup.c); I would expect\n> it would become a fairly important ingredient if we ever enhance the\n> submodule support to add submodule that disappears/reappears in the\n> history.\n\nRight. This assumption is also present in add_submodule_odb() (used by\nshow_submodule_summary()) and is_submodule_modified(), so i just reused\nit. This should be addressed in another patch.\n"},{"id":"139088","messageId":"4BBFB6C2.1090506@web.de","threadId":"23401","inReplyTo":"7v8w8w1dwl.fsf@alter.siamese.dyndns.org","subject":"Re: [WIP PATCH 4/4] Teach checkout-index to recursively checkout submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-09T23:22:42Z","receivedAt":"2010-04-09T23:22:42Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 10.04.2010 00:04, schrieb Junio C Hamano:\n> As a plumbing I would prefer to leave checkout-index as is; script writers\n> can choose to recurse if they choose to.\n\nO.k.; what about adding an option \"--recurse-submodules\" to activate that\nbehavior - without having to code it every time - when wanted?\n\n\n> This is not limited to checkout-index, but what is the stance of this new\n> feature on failures from sub-checkout when there are local modifications\n> in the work tree?  Some parts of the work tree is checked out while the\n> ones after the failure that sorts later in the alphabetical order will not\n> be checked out, resulting in an inconsistent state (I am not saying that\n> it is good or bad---I am trying to see what the users should expect from\n> this new feature)?\n\nI would like to see the same attitude that checkout has on local files:\nDon't overwrite anything changed (unless forced to). And an inconsistent\nstate should never happen, the checks on the submodules should be done\nbefore the first file or submodule is checked out (But i still have to\ntest and verify that behavior in yet to be added tests).\n"},{"id":"139111","messageId":"7v39z3zyhn.fsf@alter.siamese.dyndns.org","threadId":"23401","inReplyTo":"4BBF9D6F.2000006@web.de","subject":"Re: [WIP PATCH 0/4] Recursively checkout submodules","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-10T05:07:48Z","receivedAt":"2010-04-10T05:07:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jens Lehmann <Jens.Lehmann@web.de> writes:\n\n> Having found some Git time recently i started to tackle the next\n> major issue on my agenda to improve submodule experience: Check\n> out the matching versions of populated submodules when doing a\n> checkout in the superproject (without having to do an explicit\n> \"git submodule update\").\n> ...\n> Opinions?\n\nForgot to say the most important thing.  I like the general direction.\n"},{"id":"139163","messageId":"4BC0AED8.1080006@web.de","threadId":"23401","inReplyTo":"4BBFB40C.1060905@web.de","subject":"Re: [WIP PATCH 1/4] Prepare checkout_entry() for recursive checkout of submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-10T17:01:12Z","receivedAt":"2010-04-10T17:01:12Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 10.04.2010 01:11, schrieb Jens Lehmann:\n> Am 09.04.2010 23:59, schrieb Junio C Hamano:\n>> Jens Lehmann <Jens.Lehmann@web.de> writes:\n>>> +\tstrbuf_addf(&buf, \"%s/.git/\", path);\n>>> +\tif (!is_directory(buf.buf)) {\n>>> +\t\tstrbuf_release(&buf);\n>>> +\t\t/* The submodule is not populated, so we can't check it out */\n>>> +\t\treturn 0;\n>>> +\t}\n>>\n>> This would give you an incorrect result if .git is a file that records\n>> \"gitdir: overthere\" (see read_gitfile_gently() in setup.c); I would expect\n>> it would become a fairly important ingredient if we ever enhance the\n>> submodule support to add submodule that disappears/reappears in the\n>> history.\n> \n> Right. This assumption is also present in add_submodule_odb() (used by\n> show_submodule_summary()) and is_submodule_modified(), so i just reused\n> it. This should be addressed in another patch.\n\nWhat about this one:\n\n-----------------8<---------------\nSubject: [PATCH] Teach diff --submodule and status to handle .git files in submodules\n\nThe simple test for an existing .git directory gives an incorrect result\nif .git is a file that records \"gitdir: overthere\". So for submodules that\nuse a .git file, \"git status\" and the diff family - when the \"--submodule\"\noption is given - did assume the submodule was not populated at all when\na .git file was used, thus generating wrong output or no output at all.\n\nThis is fixed by using read_gitfile_gently() to get the correct location\nof the .git directory. While at it, is_submodule_modified() was cleaned up\nto use the \"dir\" member of \"struct child_process\" instead of setting the\nGIT_WORK_TREE and GIT_DIR environment variables.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n submodule.c                 |   33 ++++++++++++++++-----------------\n t/t4041-diff-submodule.sh   |   15 +++++++++++++++\n t/t7506-status-submodule.sh |   16 ++++++++++++++++\n 3 files changed, 47 insertions(+), 17 deletions(-)\n\ndiff --git a/submodule.c b/submodule.c\nindex b3b8bc1..676d48f 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -12,8 +12,15 @@ static int add_submodule_odb(const char *path)\n \tstruct strbuf objects_directory = STRBUF_INIT;\n \tstruct alternate_object_database *alt_odb;\n \tint ret = 0;\n+\tconst char *git_dir;\n\n-\tstrbuf_addf(&objects_directory, \"%s/.git/objects/\", path);\n+\tstrbuf_addf(&objects_directory, \"%s/.git\", path);\n+\tgit_dir = read_gitfile_gently(objects_directory.buf);\n+\tif (git_dir) {\n+\t\tstrbuf_reset(&objects_directory);\n+\t\tstrbuf_addstr(&objects_directory, git_dir);\n+\t}\n+\tstrbuf_addstr(&objects_directory, \"/objects/\");\n \tif (!is_directory(objects_directory.buf)) {\n \t\tret = -1;\n \t\tgoto done;\n@@ -132,7 +139,6 @@ void show_submodule_summary(FILE *f, const char *path,\n\n unsigned is_submodule_modified(const char *path, int ignore_untracked)\n {\n-\tint i;\n \tssize_t len;\n \tstruct child_process cp;\n \tconst char *argv[] = {\n@@ -141,16 +147,16 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)\n \t\tNULL,\n \t\tNULL,\n \t};\n-\tconst char *env[LOCAL_REPO_ENV_SIZE + 3];\n \tstruct strbuf buf = STRBUF_INIT;\n \tunsigned dirty_submodule = 0;\n \tconst char *line, *next_line;\n+\tconst char *git_dir;\n\n-\tfor (i = 0; i < LOCAL_REPO_ENV_SIZE; i++)\n-\t\tenv[i] = local_repo_env[i];\n-\n-\tstrbuf_addf(&buf, \"%s/.git/\", path);\n-\tif (!is_directory(buf.buf)) {\n+\tstrbuf_addf(&buf, \"%s/.git\", path);\n+\tgit_dir = read_gitfile_gently(buf.buf);\n+\tif (!git_dir)\n+\t\tgit_dir = buf.buf;\n+\tif (!is_directory(git_dir)) {\n \t\tstrbuf_release(&buf);\n \t\t/* The submodule is not checked out, so it is not modified */\n \t\treturn 0;\n@@ -158,21 +164,16 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)\n \t}\n \tstrbuf_reset(&buf);\n\n-\tstrbuf_addf(&buf, \"GIT_WORK_TREE=%s\", path);\n-\tenv[i++] = strbuf_detach(&buf, NULL);\n-\tstrbuf_addf(&buf, \"GIT_DIR=%s/.git\", path);\n-\tenv[i++] = strbuf_detach(&buf, NULL);\n-\tenv[i] = NULL;\n-\n \tif (ignore_untracked)\n \t\targv[2] = \"-uno\";\n\n \tmemset(&cp, 0, sizeof(cp));\n \tcp.argv = argv;\n-\tcp.env = env;\n+\tcp.env = local_repo_env;\n \tcp.git_cmd = 1;\n \tcp.no_stdin = 1;\n \tcp.out = -1;\n+\tcp.dir = path;\n \tif (start_command(&cp))\n \t\tdie(\"Could not run git status --porcelain\");\n\n@@ -201,8 +202,6 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)\n \tif (finish_command(&cp))\n \t\tdie(\"git status --porcelain failed\");\n\n-\tfor (i = LOCAL_REPO_ENV_SIZE; env[i]; i++)\n-\t\tfree((char *)env[i]);\n \tstrbuf_release(&buf);\n \treturn dirty_submodule;\n }\ndiff --git a/t/t4041-diff-submodule.sh b/t/t4041-diff-submodule.sh\nindex 11b1997..019acb9 100755\n--- a/t/t4041-diff-submodule.sh\n+++ b/t/t4041-diff-submodule.sh\n@@ -329,4 +329,19 @@ index 0000000..$head7\n EOF\n \"\n\n+test_expect_success 'setup .git file for sm2' '\n+\t(cd sm2 &&\n+\t REAL=\"$(pwd)/../.real\" &&\n+\t mv .git \"$REAL\"\n+\t echo \"gitdir: $REAL\" >.git)\n+'\n+\n+test_expect_success 'diff --submodule with .git file' '\n+\tgit diff --submodule HEAD^ >actual &&\n+\tdiff actual - <<-EOF\n+Submodule sm1 $head6...0000000 (submodule deleted)\n+Submodule sm2 0000000...$head7 (new submodule)\n+EOF\n+'\n+\n test_done\ndiff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh\nindex aeec1f6..3d4f85d 100755\n--- a/t/t7506-status-submodule.sh\n+++ b/t/t7506-status-submodule.sh\n@@ -157,6 +157,22 @@ test_expect_success 'status with added and untracked file in modified submodule\n \tEOF\n '\n\n+test_expect_success 'setup .git file for sub' '\n+\t(cd sub &&\n+\t rm -f new-file\n+\t REAL=\"$(pwd)/../.real\" &&\n+\t mv .git \"$REAL\"\n+\t echo \"gitdir: $REAL\" >.git) &&\n+\t echo .real >>.gitignore &&\n+\t git commit -m \"added .real to .gitignore\" .gitignore\n+'\n+\n+test_expect_success 'status with added file in modified submodule with .git file' '\n+\t(cd sub && git reset --hard && echo >foo && git add foo) &&\n+\tgit status >output &&\n+\tgrep \"modified:   sub (new commits, modified content)\" output\n+'\n+\n test_expect_success 'rm submodule contents' '\n \trm -rf sub/* sub/.git\n '\n-- \n1.7.1.rc0.265.g16922.dirty\n"},{"id":"139179","messageId":"7vwrwfw3jn.fsf@alter.siamese.dyndns.org","threadId":"23401","inReplyTo":"4BC0AED8.1080006@web.de","subject":"Re: [WIP PATCH 1/4] Prepare checkout_entry() for recursive checkout of submodules","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-10T18:44:28Z","receivedAt":"2010-04-10T18:44:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jens Lehmann <Jens.Lehmann@web.de> writes:\n\n>>> This would give you an incorrect result if .git is a file that records\n>>> \"gitdir: overthere\" (see read_gitfile_gently() in setup.c); I would expect\n>>> it would become a fairly important ingredient if we ever enhance the\n>>> submodule support to add submodule that disappears/reappears in the\n>>> history.\n>> \n>> Right. This assumption is also present in add_submodule_odb() (used by\n>> show_submodule_summary()) and is_submodule_modified(), so i just reused\n>> it. This should be addressed in another patch.\n>\n> What about this one:\n\nLooks sensible to me.  Thanks, and sorry for not catching this earlier.\n"},{"id":"139196","messageId":"4BC0E650.2030409@web.de","threadId":"23401","inReplyTo":"7vwrwfw3jn.fsf@alter.siamese.dyndns.org","subject":"Re: [WIP PATCH 1/4] Prepare checkout_entry() for recursive checkout of submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-04-10T20:57:52Z","receivedAt":"2010-04-10T20:57:52Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 10.04.2010 20:44, schrieb Junio C Hamano:\n> Jens Lehmann <Jens.Lehmann@web.de> writes:\n> \n>>>> This would give you an incorrect result if .git is a file that records\n>>>> \"gitdir: overthere\" (see read_gitfile_gently() in setup.c); I would expect\n>>>> it would become a fairly important ingredient if we ever enhance the\n>>>> submodule support to add submodule that disappears/reappears in the\n>>>> history.\n>>>\n>>> Right. This assumption is also present in add_submodule_odb() (used by\n>>> show_submodule_summary()) and is_submodule_modified(), so i just reused\n>>> it. This should be addressed in another patch.\n>>\n>> What about this one:\n> \n> Looks sensible to me.  Thanks, and sorry for not catching this earlier.\n\nWhile glancing over add_submodule_odb(): It protects against adding\na /new/ odb twice, but assuming the submodules objects later live in\nthe odb of the superproject too it will happily add that to the\nalt_odb_list the first time it sees it, no?\nNot knowing the odb code, would it make sense to protect against that?\nOr does adding the superprojects .git/objects to the alt_odb_list have\nno negative consequences?\n"}]}