git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [RFC PATCH 00/16] Checkout aware of Submodules!
@ 2016-11-15 23:06 Stefan Beller
  2016-11-15 23:06 ` [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80 Stefan Beller
                   ` (16 more replies)
  0 siblings, 17 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

When working with submodules, nearly anytime after checking out 
a different state of the projects, that has submodules changed
you'd run "git submodule update" with a current version of Git.

There are two problems with this approach:

* The "submodule update" command is dangerous as it
  doesn't check for work that may be lost in the submodule
  (e.g. a dangling commit).
* you may forget to run the command as checkout is supposed
  to do all the work for you.

Integrate updating the submodules into git checkout, with the same
safety promises that git-checkout has, i.e. not throw away data unless
asked to. This is done by first checking if the submodule is at the same
sha1 as it is recorded in the superproject. If there are changes we stop
proceeding the checkout just like it is when checking out a file that
has local changes.

The integration happens in the code that is also used in other commands
such that it will be easier in the future to make other commands aware
of submodule.

This also solves d/f conflicts in case you replace a file/directory
with a submodule or vice versa.

The patches are still a bit rough, but the overall series seems
promising enough to me that I want to put it out here.

Any review, specifically on the design level welcome!

Thanks,
Stefan

Stefan Beller (16):
  submodule.h: add extern keyword to functions, break line before 80
  submodule: modernize ok_to_remove_submodule to use argv_array
  submodule: use absolute path for computing relative path connecting
  update submodules: add is_submodule_populated
  update submodules: add submodule config parsing
  update submodules: add a config option to determine if submodules are
    updated
  update submodules: introduce submodule_is_interesting
  update submodules: add depopulate_submodule
  update submodules: add scheduling to update submodules
  update submodules: is_submodule_checkout_safe
  teach unpack_trees() to remove submodule contents
  entry: write_entry to write populate submodules
  submodule: teach unpack_trees() to update submodules
  checkout: recurse into submodules if asked to
  completion: add '--recurse-submodules' to checkout
  checkout: add config option to recurse into submodules by default

 Documentation/config.txt               |   6 +
 Documentation/git-checkout.txt         |   8 +
 builtin/checkout.c                     |  31 ++-
 cache.h                                |   2 +
 contrib/completion/git-completion.bash |   2 +-
 entry.c                                |  17 +-
 submodule-config.c                     |  22 +++
 submodule-config.h                     |  17 +-
 submodule.c                            | 246 +++++++++++++++++++++--
 submodule.h                            |  77 +++++---
 t/lib-submodule-update.sh              |  10 +-
 t/t2013-checkout-submodule.sh          | 344 ++++++++++++++++++++++++++++++++-
 t/t9902-completion.sh                  |   1 +
 unpack-trees.c                         | 103 ++++++++--
 unpack-trees.h                         |   1 +
 wrapper.c                              |   4 +
 16 files changed, 806 insertions(+), 85 deletions(-)

-- 
2.10.1.469.g00a8914


^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-16 19:08   ` Junio C Hamano
  2016-11-15 23:06 ` [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array Stefan Beller
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

As the upcoming series will add a lot of functions to the submodule
header, it's good to start of a sane base. So format the header to
not exceed 80 characters a line and mark all functions to be extern.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.h | 60 ++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 34 insertions(+), 26 deletions(-)

diff --git a/submodule.h b/submodule.h
index d9e197a..afc58d0 100644
--- a/submodule.h
+++ b/submodule.h
@@ -29,50 +29,58 @@ struct submodule_update_strategy {
 };
 #define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
 
-int is_staging_gitmodules_ok(void);
-int update_path_in_gitmodules(const char *oldpath, const char *newpath);
-int remove_path_from_gitmodules(const char *path);
-void stage_updated_gitmodules(void);
-void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+extern int is_staging_gitmodules_ok(void);
+extern int update_path_in_gitmodules(const char *oldpath, const char *newpath);
+extern int remove_path_from_gitmodules(const char *path);
+extern void stage_updated_gitmodules(void);
+extern void set_diffopt_flags_from_submodule_config(
+		struct diff_options *diffopt,
 		const char *path);
-int submodule_config(const char *var, const char *value, void *cb);
-void gitmodules_config(void);
-int parse_submodule_update_strategy(const char *value,
+extern int submodule_config(const char *var, const char *value, void *cb);
+extern void gitmodules_config(void);
+extern int parse_submodule_update_strategy(const char *value,
 		struct submodule_update_strategy *dst);
-const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
-void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
-void show_submodule_summary(FILE *f, const char *path,
+extern const char *submodule_strategy_to_string(
+		const struct submodule_update_strategy *s);
+extern void handle_ignore_submodules_arg(struct diff_options *diffopt,
+					 const char *);
+extern void show_submodule_summary(FILE *f, const char *path,
 		const char *line_prefix,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule, const char *meta,
 		const char *del, const char *add, const char *reset);
-void show_submodule_inline_diff(FILE *f, const char *path,
+extern void show_submodule_inline_diff(FILE *f, const char *path,
 		const char *line_prefix,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule, const char *meta,
 		const char *del, const char *add, const char *reset,
 		const struct diff_options *opt);
-void set_config_fetch_recurse_submodules(int value);
-void check_for_new_submodule_commits(unsigned char new_sha1[20]);
-int fetch_populated_submodules(const struct argv_array *options,
+extern void set_config_fetch_recurse_submodules(int value);
+extern void check_for_new_submodule_commits(unsigned char new_sha1[20]);
+extern int fetch_populated_submodules(const struct argv_array *options,
 			       const char *prefix, int command_line_option,
 			       int quiet, int max_parallel_jobs);
-unsigned is_submodule_modified(const char *path, int ignore_untracked);
-int submodule_uses_gitfile(const char *path);
-int ok_to_remove_submodule(const char *path);
-int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
-		    const unsigned char a[20], const unsigned char b[20], int search);
-int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
-		struct string_list *needs_pushing);
-int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
-void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
-int parallel_submodules(void);
+extern unsigned is_submodule_modified(const char *path, int ignore_untracked);
+extern int submodule_uses_gitfile(const char *path);
+extern int ok_to_remove_submodule(const char *path);
+extern int merge_submodule(unsigned char result[20], const char *path,
+			   const unsigned char base[20],
+			   const unsigned char a[20],
+			   const unsigned char b[20], int search);
+extern int find_unpushed_submodules(unsigned char new_sha1[20],
+				    const char *remotes_name,
+				    struct string_list *needs_pushing);
+extern int push_unpushed_submodules(unsigned char new_sha1[20],
+				    const char *remotes_name);
+extern void connect_work_tree_and_git_dir(const char *work_tree,
+					  const char *git_dir);
+extern int parallel_submodules(void);
 
 /*
  * Prepare the "env_array" parameter of a "struct child_process" for executing
  * a submodule by clearing any repo-specific envirionment variables, but
  * retaining any config in the environment.
  */
-void prepare_submodule_repo_env(struct argv_array *out);
+extern void prepare_submodule_repo_env(struct argv_array *out);
 
 #endif
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
  2016-11-15 23:06 ` [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80 Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:11   ` David Turner
  2016-11-15 23:06 ` [PATCH 03/16] submodule: use absolute path for computing relative path connecting Stefan Beller
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Instead of constructing the NULL terminated array ourselves, we
should make use of the argv_array infrastructure.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/submodule.c b/submodule.c
index 6f7d883..53a6dbb 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1022,13 +1022,6 @@ int ok_to_remove_submodule(const char *path)
 {
 	ssize_t len;
 	struct child_process cp = CHILD_PROCESS_INIT;
-	const char *argv[] = {
-		"status",
-		"--porcelain",
-		"-u",
-		"--ignore-submodules=none",
-		NULL,
-	};
 	struct strbuf buf = STRBUF_INIT;
 	int ok_to_remove = 1;
 
@@ -1038,7 +1031,8 @@ int ok_to_remove_submodule(const char *path)
 	if (!submodule_uses_gitfile(path))
 		return 0;
 
-	cp.argv = argv;
+	argv_array_pushl(&cp.args, "status", "--porcelain", "-uall",
+				   "--ignore-submodules=none", NULL);
 	prepare_submodule_repo_env(&cp.env_array);
 	cp.git_cmd = 1;
 	cp.no_stdin = 1;
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 03/16] submodule: use absolute path for computing relative path connecting
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
  2016-11-15 23:06 ` [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80 Stefan Beller
  2016-11-15 23:06 ` [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:06 ` [PATCH 04/16] update submodules: add is_submodule_populated Stefan Beller
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

This addresses a similar concern as in f8eaa0ba98b (submodule--helper,
module_clone: always operate on absolute paths, 2016-03-31)

When computing the relative path from one to another location, we
need to provide both locations as absolute paths to make sure the
computation of the relative path is correct.

While at it, change `real_work_tree` to be non const as we own
the memory.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/submodule.c b/submodule.c
index 53a6dbb..c9d22e5 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1221,23 +1221,25 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
 {
 	struct strbuf file_name = STRBUF_INIT;
 	struct strbuf rel_path = STRBUF_INIT;
-	const char *real_work_tree = xstrdup(real_path(work_tree));
+	char *real_git_dir = xstrdup(real_path(git_dir));
+	char *real_work_tree = xstrdup(real_path(work_tree));
 
 	/* Update gitfile */
 	strbuf_addf(&file_name, "%s/.git", work_tree);
 	write_file(file_name.buf, "gitdir: %s",
-		   relative_path(git_dir, real_work_tree, &rel_path));
+		   relative_path(real_git_dir, real_work_tree, &rel_path));
 
 	/* Update core.worktree setting */
 	strbuf_reset(&file_name);
-	strbuf_addf(&file_name, "%s/config", git_dir);
+	strbuf_addf(&file_name, "%s/config", real_git_dir);
 	git_config_set_in_file(file_name.buf, "core.worktree",
-			       relative_path(real_work_tree, git_dir,
+			       relative_path(real_work_tree, real_git_dir,
 					     &rel_path));
 
 	strbuf_release(&file_name);
 	strbuf_release(&rel_path);
-	free((void *)real_work_tree);
+	free(real_work_tree);
+	free(real_git_dir);
 }
 
 int parallel_submodules(void)
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 04/16] update submodules: add is_submodule_populated
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (2 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 03/16] submodule: use absolute path for computing relative path connecting Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:20   ` Brandon Williams
  2016-11-15 23:06 ` [PATCH 05/16] update submodules: add submodule config parsing Stefan Beller
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

This is nearly same as Brandon sent out.
(First patch of origin/bw/grep-recurse-submodules,
will drop this patch once Brandons series is stable
enough to build on).

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.c | 11 +++++++++++
 submodule.h |  1 +
 2 files changed, 12 insertions(+)

diff --git a/submodule.c b/submodule.c
index c9d22e5..97eaf7c 100644
--- a/submodule.c
+++ b/submodule.c
@@ -914,6 +914,17 @@ int fetch_populated_submodules(const struct argv_array *options,
 	return spf.result;
 }
 
+int is_submodule_populated(const char *path)
+{
+	int retval = 0;
+	struct strbuf gitdir = STRBUF_INIT;
+	strbuf_addf(&gitdir, "%s/.git", path);
+	if (resolve_gitdir(gitdir.buf))
+		retval = 1;
+	strbuf_release(&gitdir);
+	return retval;
+}
+
 unsigned is_submodule_modified(const char *path, int ignore_untracked)
 {
 	ssize_t len;
diff --git a/submodule.h b/submodule.h
index afc58d0..d44b4f1 100644
--- a/submodule.h
+++ b/submodule.h
@@ -61,6 +61,7 @@ extern int fetch_populated_submodules(const struct argv_array *options,
 			       const char *prefix, int command_line_option,
 			       int quiet, int max_parallel_jobs);
 extern unsigned is_submodule_modified(const char *path, int ignore_untracked);
+extern int is_submodule_populated(const char *path);
 extern int submodule_uses_gitfile(const char *path);
 extern int ok_to_remove_submodule(const char *path);
 extern int merge_submodule(unsigned char result[20], const char *path,
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 05/16] update submodules: add submodule config parsing
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (3 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 04/16] update submodules: add is_submodule_populated Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:06 ` [PATCH 06/16] update submodules: add a config option to determine if submodules are updated Stefan Beller
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Similar as in b33a15b08 (push: add recurseSubmodules config option,
2015-11-17) and 027771fcb1 (submodule: allow erroneous values for the
fetchRecurseSubmodules option, 2015-08-17), we add submodule-config code
that is later used to parse whether we are interested in updating
submodules.

We need the `die_on_error` parameter to be able to call this parsing
function for the config file as well, which if incorrect let's Git die.

As we're just touching the header file, also mark all functions extern.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule-config.c | 22 ++++++++++++++++++++++
 submodule-config.h | 17 +++++++++--------
 2 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/submodule-config.c b/submodule-config.c
index 098085b..4b5297e 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -234,6 +234,28 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
 	return parse_fetch_recurse(opt, arg, 1);
 }
 
+static int parse_update_recurse(const char *opt, const char *arg,
+				int die_on_error)
+{
+	switch (git_config_maybe_bool(opt, arg)) {
+	case 1:
+		return RECURSE_SUBMODULES_ON;
+	case 0:
+		return RECURSE_SUBMODULES_OFF;
+	default:
+		if (!strcmp(arg, "checkout"))
+			return RECURSE_SUBMODULES_ON;
+		if (die_on_error)
+			die("bad %s argument: %s", opt, arg);
+		return RECURSE_SUBMODULES_ERROR;
+	}
+}
+
+int parse_update_recurse_submodules_arg(const char *opt, const char *arg)
+{
+	return parse_update_recurse(opt, arg, 1);
+}
+
 static int parse_push_recurse(const char *opt, const char *arg,
 			       int die_on_error)
 {
diff --git a/submodule-config.h b/submodule-config.h
index d05c542..992bfbe 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -22,13 +22,14 @@ struct submodule {
 	int recommend_shallow;
 };
 
-int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
-int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
-int parse_submodule_config_option(const char *var, const char *value);
-const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
-		const char *name);
-const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
-		const char *path);
-void submodule_free(void);
+extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
+extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
+extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
+extern int parse_submodule_config_option(const char *var, const char *value);
+extern const struct submodule *submodule_from_name(
+		const unsigned char *commit_sha1, const char *name);
+extern const struct submodule *submodule_from_path(
+		const unsigned char *commit_sha1, const char *path);
+extern void submodule_free(void);
 
 #endif /* SUBMODULE_CONFIG_H */
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 06/16] update submodules: add a config option to determine if submodules are updated
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (4 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 05/16] update submodules: add submodule config parsing Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:06 ` [PATCH 07/16] update submodules: introduce submodule_is_interesting Stefan Beller
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

In later patches we introduce the options and flag for commands
that modify the working directory, e.g. git-checkout.

Have a central place to store such settings whether we want to update
a submodule.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.c | 6 ++++++
 submodule.h | 1 +
 2 files changed, 7 insertions(+)

diff --git a/submodule.c b/submodule.c
index 97eaf7c..38b0573 100644
--- a/submodule.c
+++ b/submodule.c
@@ -16,6 +16,7 @@
 #include "quote.h"
 
 static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int parallel_jobs = 1;
 static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP;
 static int initialized_fetch_ref_tips;
@@ -494,6 +495,11 @@ void set_config_fetch_recurse_submodules(int value)
 	config_fetch_recurse_submodules = value;
 }
 
+void set_config_update_recurse_submodules(int value)
+{
+	config_update_recurse_submodules = value;
+}
+
 static int has_remote(const char *refname, const struct object_id *oid,
 		      int flags, void *cb_data)
 {
diff --git a/submodule.h b/submodule.h
index d44b4f1..185ad18 100644
--- a/submodule.h
+++ b/submodule.h
@@ -56,6 +56,7 @@ extern void show_submodule_inline_diff(FILE *f, const char *path,
 		const char *del, const char *add, const char *reset,
 		const struct diff_options *opt);
 extern void set_config_fetch_recurse_submodules(int value);
+extern void set_config_update_recurse_submodules(int value);
 extern void check_for_new_submodule_commits(unsigned char new_sha1[20]);
 extern int fetch_populated_submodules(const struct argv_array *options,
 			       const char *prefix, int command_line_option,
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 07/16] update submodules: introduce submodule_is_interesting
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (5 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 06/16] update submodules: add a config option to determine if submodules are updated Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:34   ` Brandon Williams
                     ` (2 more replies)
  2016-11-15 23:06 ` [PATCH 08/16] update submodules: add depopulate_submodule Stefan Beller
                   ` (9 subsequent siblings)
  16 siblings, 3 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

In later patches we introduce the --recurse-submodule flag for commands
that modify the working directory, e.g. git-checkout.

It is potentially expensive to check if a submodule needs an update,
because a common theme to interact with submodules is to spawn a child
process for each interaction.

So let's introduce a function that pre checks if a submodule needs
to be checked for an update.

I am not particular happy with the name `submodule_is_interesting`,
in internal iterations I had `submodule_requires_check_for_update`
and `submodule_needs_update`, but I was even less happy with those
names. Maybe `submodule_interesting_for_update`?

Generally this is to answer "Am I allowed to touch the submodule
at all?" or: "Does the user expect me to touch it?"
which includes all of creation/deletion/update.

This patch is based off a prior attempt by Jens Lehmann to add
submodules to checkout.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.c | 37 +++++++++++++++++++++++++++++++++++++
 submodule.h |  8 ++++++++
 2 files changed, 45 insertions(+)

diff --git a/submodule.c b/submodule.c
index 38b0573..d34b721 100644
--- a/submodule.c
+++ b/submodule.c
@@ -500,6 +500,43 @@ void set_config_update_recurse_submodules(int value)
 	config_update_recurse_submodules = value;
 }
 
+int submodules_interesting_for_update(void)
+{
+	/*
+	 * Update can't be "none", "merge" or "rebase",
+	 * treat any value as OFF, except an explicit ON.
+	 */
+	return config_update_recurse_submodules == RECURSE_SUBMODULES_ON;
+}
+
+int submodule_is_interesting(const char *path, const unsigned char *sha1)
+{
+	/*
+	 * If we cannot load a submodule config, we cannot get the name
+	 * of the submodule, so we'd need to follow the gitlink file
+	 */
+	const struct submodule *sub;
+
+	if (!submodules_interesting_for_update())
+		return 0;
+
+	sub = submodule_from_path(sha1, path);
+	if (!sub)
+		return 0;
+
+	switch (sub->update_strategy.type) {
+	case SM_UPDATE_UNSPECIFIED:
+	case SM_UPDATE_CHECKOUT:
+		return 1;
+	case SM_UPDATE_REBASE:
+	case SM_UPDATE_MERGE:
+	case SM_UPDATE_NONE:
+	case SM_UPDATE_COMMAND:
+		return 0;
+	}
+	return 0;
+}
+
 static int has_remote(const char *refname, const struct object_id *oid,
 		      int flags, void *cb_data)
 {
diff --git a/submodule.h b/submodule.h
index 185ad18..3df6881 100644
--- a/submodule.h
+++ b/submodule.h
@@ -57,6 +57,14 @@ extern void show_submodule_inline_diff(FILE *f, const char *path,
 		const struct diff_options *opt);
 extern void set_config_fetch_recurse_submodules(int value);
 extern void set_config_update_recurse_submodules(int value);
+/**
+ * When updating the working tree, do we need to check if the submodule needs
+ * updating. We do not require a check if we are already sure that the
+ * submodule doesn't need updating, e.g. when we are not interested in submodules
+ * or the submodule is marked uninteresting by being not initialized.
+ */
+extern int submodule_is_interesting(const char *path, const unsigned char *sha1);
+extern int submodules_interesting_for_update(void);
 extern void check_for_new_submodule_commits(unsigned char new_sha1[20]);
 extern int fetch_populated_submodules(const struct argv_array *options,
 			       const char *prefix, int command_line_option,
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (6 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 07/16] update submodules: introduce submodule_is_interesting Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:44   ` Brandon Williams
       [not found]   ` <20161117111337.GD39230@book.hvoigt.net>
  2016-11-15 23:06 ` [PATCH 09/16] update submodules: add scheduling to update submodules Stefan Beller
                   ` (8 subsequent siblings)
  16 siblings, 2 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Implement the functionality needed to enable work tree manipulating
commands to that a deleted submodule should not only affect the index
(leaving all the files of the submodule in the work tree) but also to
remove the work tree of the superproject (including any untracked
files).

To do so, we need an equivalent of "rm -rf", which is already found in
entry.c, so expose that and for clarity add a suffix "_or_dir" to it.

That will only work properly when the submodule uses a gitfile instead of
a .git directory and no untracked files are present. Otherwise the removal
will fail with a warning (which is just what happened until now).

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 cache.h     |  2 ++
 entry.c     |  8 ++++++++
 submodule.c | 25 +++++++++++++++++++++++++
 submodule.h |  1 +
 4 files changed, 36 insertions(+)

diff --git a/cache.h b/cache.h
index a50a61a..65c47e4 100644
--- a/cache.h
+++ b/cache.h
@@ -2018,4 +2018,6 @@ void sleep_millisec(int millisec);
  */
 void safe_create_dir(const char *dir, int share);
 
+void remove_subtree_or_die(const char *path);
+
 #endif /* CACHE_H */
diff --git a/entry.c b/entry.c
index c6eea24..019826b 100644
--- a/entry.c
+++ b/entry.c
@@ -73,6 +73,14 @@ static void remove_subtree(struct strbuf *path)
 		die_errno("cannot rmdir '%s'", path->buf);
 }
 
+void remove_subtree_or_die(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addstr(&sb, path);
+	remove_subtree(&sb);
+	strbuf_release(&sb);
+}
+
 static int create_file(const char *path, unsigned int mode)
 {
 	mode = (mode & 0100) ? 0777 : 0666;
diff --git a/submodule.c b/submodule.c
index d34b721..0fa4613 100644
--- a/submodule.c
+++ b/submodule.c
@@ -308,6 +308,31 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
 	strbuf_release(&sb);
 }
 
+int depopulate_submodule(const char *path)
+{
+	int ret = 0;
+	char *dot_git = xstrfmt("%s/.git", path);
+
+	/* Is it populated? */
+	if (!resolve_gitdir(dot_git))
+		goto out;
+
+	/* Does it have a .git directory? */
+	if (!submodule_uses_gitfile(path)) {
+		warning(_("cannot remove submodule '%s' because it (or one of "
+			  "its nested submodules) uses a .git directory"),
+			  path);
+		ret = -1;
+		goto out;
+	}
+
+	remove_subtree_or_die(path);
+
+out:
+	free(dot_git);
+	return ret;
+}
+
 /* Helper function to display the submodule header line prior to the full
  * summary output. If it can locate the submodule objects directory it will
  * attempt to lookup both the left and right commits and put them into the
diff --git a/submodule.h b/submodule.h
index 3df6881..8518cf3 100644
--- a/submodule.h
+++ b/submodule.h
@@ -65,6 +65,7 @@ extern void set_config_update_recurse_submodules(int value);
  */
 extern int submodule_is_interesting(const char *path, const unsigned char *sha1);
 extern int submodules_interesting_for_update(void);
+extern int depopulate_submodule(const char *path);
 extern void check_for_new_submodule_commits(unsigned char new_sha1[20]);
 extern int fetch_populated_submodules(const struct argv_array *options,
 			       const char *prefix, int command_line_option,
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 09/16] update submodules: add scheduling to update submodules
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (7 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 08/16] update submodules: add depopulate_submodule Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-16  0:02   ` Brandon Williams
  2016-11-15 23:06 ` [PATCH 10/16] update submodules: is_submodule_checkout_safe Stefan Beller
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

The walker of a tree is only expected to call `schedule_submodule_for_update`
and once done, to run `update_submodules`. This avoids directory/file
conflicts and later we can parallelize all submodule actions if needed.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 submodule.h |   4 +++
 2 files changed, 121 insertions(+)

diff --git a/submodule.c b/submodule.c
index 0fa4613..2773151 100644
--- a/submodule.c
+++ b/submodule.c
@@ -308,6 +308,75 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
 	strbuf_release(&sb);
 }
 
+static int update_submodule(const char *path, const struct object_id *oid,
+			    int force, int is_new)
+{
+	const char *git_dir;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	const struct submodule *sub = submodule_from_path(null_sha1, path);
+
+	if (!sub || !sub->name)
+		return -1;
+
+	git_dir = resolve_gitdir(git_common_path("modules/%s", sub->name));
+
+	if (!git_dir)
+		return -1;
+
+	if (is_new)
+		connect_work_tree_and_git_dir(path, git_dir);
+
+	/* update index via `read-tree --reset sha1` */
+	argv_array_pushl(&cp.args, "read-tree",
+				   force ? "--reset" : "-m",
+				   "-u", sha1_to_hex(oid->hash), NULL);
+	prepare_submodule_repo_env(&cp.env_array);
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	cp.dir = path;
+	if (run_command(&cp)) {
+		warning(_("reading the index in submodule '%s' failed"), path);
+		child_process_clear(&cp);
+		return -1;
+	}
+
+	/* write index to working dir */
+	child_process_clear(&cp);
+	child_process_init(&cp);
+	argv_array_pushl(&cp.args, "checkout-index", "-a", NULL);
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	cp.dir = path;
+	if (force)
+		argv_array_push(&cp.args, "-f");
+
+	if (run_command(&cp)) {
+		warning(_("populating the working directory in submodule '%s' failed"), path);
+		child_process_clear(&cp);
+		return -1;
+	}
+
+	/* get the HEAD right */
+	child_process_clear(&cp);
+	child_process_init(&cp);
+	argv_array_pushl(&cp.args, "checkout", "--recurse-submodules", NULL);
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	cp.dir = path;
+	if (force)
+		argv_array_push(&cp.args, "-f");
+	argv_array_push(&cp.args, sha1_to_hex(oid->hash));
+
+	if (run_command(&cp)) {
+		warning(_("setting the HEAD in submodule '%s' failed"), path);
+		child_process_clear(&cp);
+		return -1;
+	}
+
+	child_process_clear(&cp);
+	return 0;
+}
+
 int depopulate_submodule(const char *path)
 {
 	int ret = 0;
@@ -1336,3 +1405,51 @@ void prepare_submodule_repo_env(struct argv_array *out)
 	}
 	argv_array_push(out, "GIT_DIR=.git");
 }
+
+struct scheduled_submodules_update_type {
+	const char *path;
+	const struct object_id *oid;
+	/*
+	 * Do we need to perform a complete checkout or just incremental
+	 * update?
+	 */
+	unsigned is_new:1;
+} *scheduled_submodules;
+#define SCHEDULED_SUBMODULES_INIT {NULL, NULL}
+
+int scheduled_submodules_nr, scheduled_submodules_alloc;
+
+void schedule_submodule_for_update(const struct cache_entry *ce, int is_new)
+{
+	struct scheduled_submodules_update_type *ssu;
+	ALLOC_GROW(scheduled_submodules,
+		   scheduled_submodules_nr + 1,
+		   scheduled_submodules_alloc);
+	ssu = &scheduled_submodules[scheduled_submodules_nr++];
+	ssu->path = ce->name;
+	ssu->oid = &ce->oid;
+	ssu->is_new = !!is_new;
+}
+
+int update_submodules(int force)
+{
+	int i;
+	gitmodules_config();
+
+	/*
+	 * NEEDSWORK: As submodule updates can potentially take some
+	 * time each and they do not overlap (i.e. no d/f conflicts;
+	 * this can be parallelized using the run_commands.h API.
+	 */
+	for (i = 0; i < scheduled_submodules_nr; i++) {
+		struct scheduled_submodules_update_type *ssu =
+			&scheduled_submodules[i];
+
+		if (submodule_is_interesting(ssu->path, null_sha1))
+			update_submodule(ssu->path, ssu->oid,
+					 force, ssu->is_new);
+	}
+
+	scheduled_submodules_nr = 0;
+	return 0;
+}
diff --git a/submodule.h b/submodule.h
index 8518cf3..f01f87c 100644
--- a/submodule.h
+++ b/submodule.h
@@ -94,4 +94,8 @@ extern int parallel_submodules(void);
  */
 extern void prepare_submodule_repo_env(struct argv_array *out);
 
+extern void schedule_submodule_for_update(const struct cache_entry *ce,
+					  int new);
+extern int update_submodules(int force);
+
 #endif
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 10/16] update submodules: is_submodule_checkout_safe
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (8 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 09/16] update submodules: add scheduling to update submodules Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-16  0:06   ` Brandon Williams
  2016-11-15 23:06 ` [PATCH 11/16] teach unpack_trees() to remove submodule contents Stefan Beller
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

In later patches we introduce the options and flag for commands
that modify the working directory, e.g. git-checkout.

This piece of code will answer the question:
"Is it safe to change the submodule to this new state?"
e.g. is it overwriting untracked files or are there local
changes that would be overwritten?

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 submodule.c | 22 ++++++++++++++++++++++
 submodule.h |  2 ++
 2 files changed, 24 insertions(+)

diff --git a/submodule.c b/submodule.c
index 2773151..2149ef7 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1201,6 +1201,28 @@ int ok_to_remove_submodule(const char *path)
 	return ok_to_remove;
 }
 
+int is_submodule_checkout_safe(const char *path, const struct object_id *oid)
+{
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	if (!is_submodule_populated(path))
+		/* The submodule is not populated, it's safe to check it out */
+		/*
+		 * TODO: When git learns to re-populate submodules, a check must be
+		 * added here to assert that no local files will be overwritten.
+		 */
+		return 1;
+
+	argv_array_pushl(&cp.args, "read-tree", "-n", "-m", "HEAD",
+			 sha1_to_hex(oid->hash), NULL);
+
+	prepare_submodule_repo_env(&cp.env_array);
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	cp.dir = path;
+	return run_command(&cp) == 0;
+}
+
 static int find_first_merges(struct object_array *result, const char *path,
 		struct commit *a, struct commit *b)
 {
diff --git a/submodule.h b/submodule.h
index f01f87c..a7fa634 100644
--- a/submodule.h
+++ b/submodule.h
@@ -74,6 +74,8 @@ extern unsigned is_submodule_modified(const char *path, int ignore_untracked);
 extern int is_submodule_populated(const char *path);
 extern int submodule_uses_gitfile(const char *path);
 extern int ok_to_remove_submodule(const char *path);
+extern int is_submodule_checkout_safe(const char *path,
+				      const struct object_id *oid);
 extern int merge_submodule(unsigned char result[20], const char *path,
 			   const unsigned char base[20],
 			   const unsigned char a[20],
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 11/16] teach unpack_trees() to remove submodule contents
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (9 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 10/16] update submodules: is_submodule_checkout_safe Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-16  0:14   ` Brandon Williams
       [not found]   ` <20161117133538.GF39230@book.hvoigt.net>
  2016-11-15 23:06 ` [PATCH 12/16] entry: write_entry to write populate submodules Stefan Beller
                   ` (5 subsequent siblings)
  16 siblings, 2 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Extend rmdir_or_warn() to remove the directories of those submodules which
are scheduled for removal. Also teach verify_clean_submodule() to check
that a submodule configured to be removed is not modified before scheduling
it for removal.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 unpack-trees.c | 6 ++----
 wrapper.c      | 4 ++++
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index ea6bdd2..576e1d5 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -9,6 +9,7 @@
 #include "refs.h"
 #include "attr.h"
 #include "split-index.h"
+#include "submodule.h"
 #include "dir.h"
 
 /*
@@ -1361,15 +1362,12 @@ static void invalidate_ce_path(const struct cache_entry *ce,
 /*
  * Check that checking out ce->sha1 in subdir ce->name is not
  * going to overwrite any working files.
- *
- * Currently, git does not checkout subprojects during a superproject
- * checkout, so it is not going to overwrite anything.
  */
 static int verify_clean_submodule(const struct cache_entry *ce,
 				  enum unpack_trees_error_types error_type,
 				  struct unpack_trees_options *o)
 {
-	return 0;
+	return submodule_is_interesting(ce->name, null_sha1) && is_submodule_modified(ce->name, 0);
 }
 
 static int verify_clean_subdirectory(const struct cache_entry *ce,
diff --git a/wrapper.c b/wrapper.c
index e7f1979..17c08de 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -2,6 +2,7 @@
  * Various trivial helper wrappers around standard functions
  */
 #include "cache.h"
+#include "submodule.h"
 
 static void do_nothing(size_t size)
 {
@@ -592,6 +593,9 @@ int unlink_or_warn(const char *file)
 
 int rmdir_or_warn(const char *file)
 {
+	if (submodule_is_interesting(file, null_sha1)
+	    && depopulate_submodule(file))
+		return -1;
 	return warn_if_unremovable("rmdir", file, rmdir(file));
 }
 
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 12/16] entry: write_entry to write populate submodules
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (10 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 11/16] teach unpack_trees() to remove submodule contents Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:06 ` [PATCH 13/16] submodule: teach unpack_trees() to update submodules Stefan Beller
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 entry.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/entry.c b/entry.c
index 019826b..2330b6e 100644
--- a/entry.c
+++ b/entry.c
@@ -2,6 +2,7 @@
 #include "blob.h"
 #include "dir.h"
 #include "streaming.h"
+#include "submodule.h"
 
 static void create_directories(const char *path, int path_len,
 			       const struct checkout *state)
@@ -211,6 +212,7 @@ static int write_entry(struct cache_entry *ce,
 			return error("cannot create temporary submodule %s", path);
 		if (mkdir(path, 0777) < 0)
 			return error("cannot create submodule directory %s", path);
+		schedule_submodule_for_update(ce, 1);
 		break;
 	default:
 		return error("unknown file mode for %s in index", path);
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 13/16] submodule: teach unpack_trees() to update submodules
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (11 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 12/16] entry: write_entry to write populate submodules Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-16  0:22   ` David Turner
  2016-11-16  0:25   ` Brandon Williams
  2016-11-15 23:06 ` [PATCH 14/16] checkout: recurse into submodules if asked to Stefan Beller
                   ` (3 subsequent siblings)
  16 siblings, 2 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 entry.c        |  7 +++--
 unpack-trees.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++----------
 unpack-trees.h |  1 +
 3 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/entry.c b/entry.c
index 2330b6e..dd829ec 100644
--- a/entry.c
+++ b/entry.c
@@ -270,7 +270,7 @@ int checkout_entry(struct cache_entry *ce,
 
 	if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
 		unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
-		if (!changed)
+		if (!changed && (!S_ISDIR(st.st_mode) || !S_ISGITLINK(ce->ce_mode)))
 			return 0;
 		if (!state->force) {
 			if (!state->quiet)
@@ -287,9 +287,10 @@ int checkout_entry(struct cache_entry *ce,
 		 * just do the right thing)
 		 */
 		if (S_ISDIR(st.st_mode)) {
-			/* If it is a gitlink, leave it alone! */
-			if (S_ISGITLINK(ce->ce_mode))
+			if (S_ISGITLINK(ce->ce_mode)) {
+				schedule_submodule_for_update(ce, 1);
 				return 0;
+			}
 			if (!state->force)
 				return error("%s is a directory", path.buf);
 			remove_subtree(&path);
diff --git a/unpack-trees.c b/unpack-trees.c
index 576e1d5..c5c22ed 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -29,6 +29,9 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
 	/* ERROR_NOT_UPTODATE_DIR */
 	"Updating '%s' would lose untracked files in it",
 
+	/* ERROR_NOT_UPTODATE_SUBMODULE */
+	"Updating submodule '%s' would lose modifications in it",
+
 	/* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
 	"Untracked working tree file '%s' would be overwritten by merge.",
 
@@ -80,6 +83,8 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
 
 	msgs[ERROR_NOT_UPTODATE_DIR] =
 		_("Updating the following directories would lose untracked files in it:\n%s");
+	msgs[ERROR_NOT_UPTODATE_SUBMODULE] =
+		_("Updating the following submodules would lose modifications in it:\n%s");
 
 	if (!strcmp(cmd, "checkout"))
 		msg = advice_commit_before_merge
@@ -1315,19 +1320,18 @@ static int verify_uptodate_1(const struct cache_entry *ce,
 		return 0;
 
 	if (!lstat(ce->name, &st)) {
-		int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
-		unsigned changed = ie_match_stat(o->src_index, ce, &st, flags);
-		if (!changed)
-			return 0;
-		/*
-		 * NEEDSWORK: the current default policy is to allow
-		 * submodule to be out of sync wrt the superproject
-		 * index.  This needs to be tightened later for
-		 * submodules that are marked to be automatically
-		 * checked out.
-		 */
-		if (S_ISGITLINK(ce->ce_mode))
-			return 0;
+		if (!S_ISGITLINK(ce->ce_mode)) {
+			int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
+			unsigned changed = ie_match_stat(o->src_index, ce, &st, flags);
+			if (!changed)
+				return 0;
+		} else {
+			if (!submodule_is_interesting(ce->name, null_sha1))
+				return 0;
+			if (ce_stage(ce) ? is_submodule_checkout_safe(ce->name, &ce->oid)
+			    : !is_submodule_modified(ce->name, 1))
+				return 0;
+		}
 		errno = 0;
 	}
 	if (errno == ENOENT)
@@ -1350,6 +1354,59 @@ static int verify_uptodate_sparse(const struct cache_entry *ce,
 	return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
+/*
+ * When a submodule gets turned into an unmerged entry, we want it to be
+ * up-to-date regarding the merge changes.
+ */
+static int verify_uptodate_submodule(const struct cache_entry *old,
+				     const struct cache_entry *new,
+				     struct unpack_trees_options *o)
+{
+	struct stat st;
+
+	if (o->index_only
+	    || (!((old->ce_flags & CE_VALID) || ce_skip_worktree(old))
+		&& (o->reset || ce_uptodate(old))))
+		return 0;
+	if (!submodule_is_interesting(new->name, null_sha1))
+		return 0;
+	if (!lstat(old->name, &st)) {
+		unsigned changed = ie_match_stat(o->src_index, old, &st,
+						 CE_MATCH_IGNORE_VALID|
+						 CE_MATCH_IGNORE_SKIP_WORKTREE);
+		if (!changed) {
+			/* old is always a submodule */
+			if (S_ISGITLINK(new->ce_mode)) {
+				/*
+				 * new is also a submodule, so check if we care
+				 * and then if can checkout the new sha1 safely
+				 */
+				if (submodule_is_interesting(old->name, null_sha1)
+				    && is_submodule_checkout_safe(new->name, &new->oid))
+					return 0;
+			} else {
+				/*
+				 * new is not a submodule any more, so only
+				 * care if we care:
+				 */
+				if (submodule_is_interesting(old->name, null_sha1)
+				    && ok_to_remove_submodule(old->name))
+					return 0;
+			}
+		} else {
+			if (S_ISGITLINK(new->ce_mode))
+				return !is_submodule_checkout_safe(new->name, &new->oid);
+			else
+				return !ok_to_remove_submodule(old->name);
+		}
+		errno = 0;
+	}
+	if (errno == ENOENT)
+		return 0;
+	return o->gently ? -1 :
+		add_rejected_path(o, ERROR_NOT_UPTODATE_SUBMODULE, old->name);
+}
+
 static void invalidate_ce_path(const struct cache_entry *ce,
 			       struct unpack_trees_options *o)
 {
@@ -1608,9 +1665,17 @@ static int merged_entry(const struct cache_entry *ce,
 			copy_cache_entry(merge, old);
 			update = 0;
 		} else {
-			if (verify_uptodate(old, o)) {
-				free(merge);
-				return -1;
+			if (S_ISGITLINK(old->ce_mode) ||
+			    S_ISGITLINK(merge->ce_mode)) {
+				if (verify_uptodate_submodule(old, merge, o)) {
+					free(merge);
+					return -1;
+				}
+			} else {
+				if (verify_uptodate(old, o)) {
+					free(merge);
+					return -1;
+				}
 			}
 			/* Migrate old flags over */
 			update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
diff --git a/unpack-trees.h b/unpack-trees.h
index 36a73a6..bee8740 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -15,6 +15,7 @@ enum unpack_trees_error_types {
 	ERROR_WOULD_OVERWRITE = 0,
 	ERROR_NOT_UPTODATE_FILE,
 	ERROR_NOT_UPTODATE_DIR,
+	ERROR_NOT_UPTODATE_SUBMODULE,
 	ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
 	ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
 	ERROR_BIND_OVERLAP,
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 14/16] checkout: recurse into submodules if asked to
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (12 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 13/16] submodule: teach unpack_trees() to update submodules Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-16  0:33   ` Brandon Williams
                     ` (2 more replies)
  2016-11-15 23:06 ` [PATCH 15/16] completion: add '--recurse-submodules' to checkout Stefan Beller
                   ` (2 subsequent siblings)
  16 siblings, 3 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Allow checkout to recurse into submodules via
the command line option --[no-]recurse-submodules.

The flag for recurse-submodules in its current form
could be an OPT_BOOL, but eventually we may want to have
it as:

    git checkout --recurse-submodules=rebase|merge| \
			cherry-pick|checkout|none

which resembles the submodule.<name>.update options,
so naturally a value such as
"as-configured-or-X-as-fallback" would also come in handy.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 Documentation/git-checkout.txt |   7 +
 builtin/checkout.c             |  31 +++-
 t/lib-submodule-update.sh      |  10 +-
 t/t2013-checkout-submodule.sh  | 325 ++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 362 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 8e2c066..a0ea2c5 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -256,6 +256,13 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
+--[no-]recurse-submodules::
+	Using --recurse-submodules will update the content of all initialized
+	submodules according to the commit recorded in the superproject. If
+	local modifications in a submodule would be overwritten the checkout
+	will fail until `-f` is used. If nothing (or --no-recurse-submodules)
+	is used, the work trees of submodules will not be updated.
+
 <branch>::
 	Branch to checkout; if it refers to a branch (i.e., a name that,
 	when prepended with "refs/heads/", is a valid ref), then that
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9b2a5b3..2a626a3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -21,12 +21,31 @@
 #include "submodule-config.h"
 #include "submodule.h"
 
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
 	N_("git checkout [<options>] [<branch>] -- <file>..."),
 	NULL,
 };
 
+int option_parse_recurse_submodules(const struct option *opt,
+				    const char *arg, int unset)
+{
+	if (unset) {
+		recurse_submodules = RECURSE_SUBMODULES_OFF;
+		return 0;
+	}
+	if (arg)
+		recurse_submodules =
+			parse_update_recurse_submodules_arg(opt->long_name,
+							    arg);
+	else
+		recurse_submodules = RECURSE_SUBMODULES_ON;
+
+	return 0;
+}
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
@@ -826,7 +845,8 @@ static int switch_branches(const struct checkout_opts *opts,
 		parse_commit_or_die(new->commit);
 	}
 
-	ret = merge_working_tree(opts, &old, new, &writeout_error);
+	ret = merge_working_tree(opts, &old, new, &writeout_error) ||
+	      update_submodules(opts->force);
 	if (ret) {
 		free(path_to_free);
 		return ret;
@@ -1160,6 +1180,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 				N_("second guess 'git checkout <no-such-branch>'")),
 		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
 			 N_("do not check if another worktree is holding the given ref")),
+		{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+			    "checkout", "control recursive updating of submodules",
+			    PARSE_OPT_OPTARG, option_parse_recurse_submodules },
 		OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
 		OPT_END(),
 	};
@@ -1190,6 +1213,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
+	if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+		git_config(submodule_config, NULL);
+		if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
+			set_config_update_recurse_submodules(recurse_submodules);
+	}
+
 	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
 
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 79cdd34..e0773c6 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -634,7 +634,13 @@ test_submodule_forced_switch () {
 
 	########################## Modified submodule #########################
 	# Updating a submodule sha1 doesn't update the submodule's work tree
-	test_expect_success "$command: modified submodule does not update submodule work tree" '
+	if test "$KNOWN_FAILURE_RECURSE_SUBMODULE_SERIES_BREAKS_REPLACE_SUBMODULE_TEST" = 1
+	then
+		RESULT="failure"
+	else
+		RESULT="success"
+	fi
+	test_expect_$RESULT "$command: modified submodule does not update submodule work tree" '
 		prolog &&
 		reset_work_tree_to add_sub1 &&
 		(
@@ -649,7 +655,7 @@ test_submodule_forced_switch () {
 	'
 	# Updating a submodule to an invalid sha1 doesn't update the
 	# submodule's work tree, subsequent update will fail
-	test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+	test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" '
 		prolog &&
 		reset_work_tree_to add_sub1 &&
 		(
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index 6847f75..60f6987 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -7,15 +7,21 @@ test_description='checkout can handle submodules'
 
 test_expect_success 'setup' '
 	mkdir submodule &&
-	(cd submodule &&
-	 git init &&
-	 test_commit first) &&
-	git add submodule &&
+	(
+		cd submodule &&
+		git init &&
+		test_commit first
+	) &&
+	echo first >file &&
+	git add file submodule &&
 	test_tick &&
 	git commit -m superproject &&
-	(cd submodule &&
-	 test_commit second) &&
-	git add submodule &&
+	(
+		cd submodule &&
+		test_commit second
+	) &&
+	echo second > file &&
+	git add file submodule &&
 	test_tick &&
 	git commit -m updated.superproject
 '
@@ -37,7 +43,8 @@ test_expect_success '"checkout <submodule>" updates the index only' '
 	git checkout HEAD^ submodule &&
 	test_must_fail git diff-files --quiet &&
 	git checkout HEAD submodule &&
-	git diff-files --quiet
+	git diff-files --quiet &&
+	git diff-index --quiet --cached HEAD
 '
 
 test_expect_success '"checkout <submodule>" honors diff.ignoreSubmodules' '
@@ -63,8 +70,310 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/
 	! test -s actual
 '
 
+# TODO: hardcode $2
+# use flag instead of checking for directory.
+submodule_creation_must_succeed() {
+	# checkout base ($1)
+	git checkout -f --recurse-submodules $1 &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached $1 &&
+
+	# checkout target ($2)
+	if test -d submodule; then
+		echo change >>submodule/first.t &&
+		test_must_fail git checkout --recurse-submodules $2 &&
+		git checkout -f --recurse-submodules $2
+	else
+		git checkout --recurse-submodules $2
+	fi &&
+	test -e submodule/.git &&
+	test -f submodule/first.t &&
+	test -f submodule/second.t &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached $2
+}
+
+# TODO: inline this:
+submodule_removal_must_succeed() {
+
+	# checkout base ($1)
+	git checkout -f --recurse-submodules $1 &&
+	git submodule update -f . &&
+	test -e submodule/.git &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached $1 &&
+
+	# checkout target ($2)
+	echo change >>submodule/first.t &&
+	test_must_fail git checkout --recurse-submodules $2 &&
+	git checkout -f --recurse-submodules $2 &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached $2 &&
+	! test -d submodule
+}
+
+test_expect_success '"check --recurse-submodules" removes deleted submodule' '
+	# first some setup:
+	git config -f .gitmodules submodule.submodule.path submodule &&
+	git config -f .gitmodules submodule.submodule.url ./submodule.bare &&
+	git -C submodule clone --bare . ../submodule.bare &&
+	echo submodule.bare >>.gitignore &&
+	git add .gitignore .gitmodules submodule &&
+	git commit -m "submodule registered with a gitmodules file" &&
+
+	# for testing this case, do it in a fresh clone with the submodules
+	# git dir inside the superprojects .git/modules dir.
+	git clone --recurse-submodules . super &&
+	(
+		cd super &&
+		git checkout -b base &&
+		git checkout -b delete_submodule &&
+		rm -rf submodule &&
+		git rm submodule &&
+		git commit -m "submodule deleted" &&
+		submodule_removal_must_succeed base delete_submodule
+	)
+'
+
+test_expect_success '"checkout --recurse-submodules" does not delete submodule with .git dir inside' '
+	git fetch super delete_submodule &&
+	git checkout --recurse-submodules FETCH_HEAD 2>output.err &&
+	test_i18ngrep "cannot remove submodule" output.err &&
+	test -d submodule/.git
+'
+
+test_expect_success '"checkout --recurse-submodules" repopulates submodule' '
+	(
+		cd super &&
+		submodule_creation_must_succeed delete_submodule base
+	)
+'
+
+test_expect_success '"checkout --recurse-submodules" repopulates submodule in existing directory' '
+	(
+		cd super &&
+		git checkout --recurse-submodules delete_submodule &&
+		mkdir submodule &&
+		submodule_creation_must_succeed delete_submodule base
+	)
+'
+
+test_expect_success '"checkout --recurse-submodules" replaces submodule with files' '
+	(
+		cd super
+		git checkout -f base &&
+		git checkout -b replace_submodule_with_file &&
+		git rm -f submodule &&
+		echo "file instead" >submodule &&
+		git add submodule &&
+		git commit -m "submodule replaced" &&
+		git checkout -f base &&
+		git submodule update -f . &&
+		git checkout --recurse-submodules replace_submodule_with_file &&
+		test -f submodule
+	)
+'
+
+test_expect_success '"checkout --recurse-submodules" removes files and repopulates submodule' '
+	(
+		cd super &&
+		submodule_creation_must_succeed replace_submodule_with_file base
+	)
+'
+
+test_expect_success '"checkout --recurse-submodules" replaces submodule with a directory' '
+	(
+		cd super
+		git checkout -f base &&
+		git checkout -b replace_submodule_with_dir &&
+		git rm -f submodule &&
+		mkdir -p submodule/dir &&
+		echo content >submodule/dir/file &&
+		git add submodule &&
+		git commit -m "submodule replaced with a directory (file inside)" &&
+		git checkout -f base &&
+		git submodule update -f . &&
+		git checkout --recurse-submodules replace_submodule_with_dir &&
+		test -d submodule &&
+		! test -e submodule/.git &&
+		! test -f submodule/first.t &&
+		! test -f submodule/second.t &&
+		test -d submodule/dir
+	)
+'
+
+test_expect_success '"checkout --recurse-submodules" removes the directory and repopulates submodule' '
+	(
+		cd super
+		submodule_creation_must_succeed replace_submodule_with_dir base
+	)
+'
+
+test_expect_success SYMLINKS '"checkout --recurse-submodules" replaces submodule with a link' '
+	(
+		cd super
+		git checkout -f base &&
+		git checkout -b replace_submodule_with_link &&
+		git rm -f submodule &&
+		ln -s submodule &&
+		git add submodule &&
+		git commit -m "submodule replaced with a link" &&
+		git checkout -f base &&
+		git submodule update -f . &&
+		git checkout --recurse-submodules replace_submodule_with_link &&
+		test -L submodule
+	)
+'
+
+test_expect_success SYMLINKS '"checkout --recurse-submodules" removes the link and repopulates submodule' '
+	(
+		cd super
+		submodule_creation_must_succeed replace_submodule_with_link base
+	)
+'
+
+test_expect_success '"checkout --recurse-submodules" updates the submodule' '
+	(
+		cd super
+		git checkout --recurse-submodules base &&
+		git diff-files --quiet &&
+		git diff-index --quiet --cached HEAD &&
+		git checkout -b updated_submodule &&
+		(
+			cd submodule &&
+			echo x >>first.t &&
+			git add first.t &&
+			test_commit third
+		) &&
+		git add submodule &&
+		test_tick &&
+		git commit -m updated.superproject &&
+		git checkout --recurse-submodules base &&
+		git diff-files --quiet &&
+		git diff-index --quiet --cached HEAD
+	)
+'
+
+test_expect_failure 'untracked file is not deleted' '
+	(
+		cd super &&
+		git checkout --recurse-submodules base &&
+		echo important >submodule/untracked &&
+		test_must_fail git checkout --recurse-submodules delete_submodule &&
+		git checkout -f --recurse-submodules delete_submodule
+	)
+'
+
+test_expect_success 'ignored file works just fine' '
+	(
+		cd super &&
+		git checkout --recurse-submodules base &&
+		echo important >submodule/ignored &&
+		echo ignored >.git/modules/submodule/info/exclude &&
+		git checkout --recurse-submodules delete_submodule
+	)
+'
+
+test_expect_success 'dirty file file is not deleted' '
+	(
+		cd super &&
+		git checkout --recurse-submodules base &&
+		echo important >submodule/first.t &&
+		test_must_fail git checkout --recurse-submodules delete_submodule &&
+		git checkout -f --recurse-submodules delete_submodule
+	)
+'
+
+test_expect_success 'added to index is not deleted' '
+	(
+		cd super &&
+		git checkout --recurse-submodules base &&
+		echo important >submodule/to_index &&
+		git -C submodule add to_index &&
+		test_must_fail git checkout --recurse-submodules delete_submodule &&
+		git checkout -f --recurse-submodules delete_submodule
+	)
+'
+
+# This is ok in theory, we just need to make sure
+# the garbage collection doesn't eat the commit.
+test_expect_success 'different commit prevents from deleting' '
+	(
+		cd super &&
+		git checkout --recurse-submodules base &&
+		echo important >submodule/to_index &&
+		git -C submodule add to_index &&
+		test_must_fail git checkout --recurse-submodules delete_submodule &&
+		git checkout -f --recurse-submodules delete_submodule
+	)
+'
+
+test_expect_failure '"checkout --recurse-submodules" needs -f to update a modifed submodule commit' '
+	(
+		cd submodule &&
+		git checkout --recurse-submodules HEAD^
+	) &&
+	test_must_fail git checkout --recurse-submodules master &&
+	test_must_fail git diff-files --quiet submodule &&
+	git diff-files --quiet file &&
+	git checkout --recurse-submodules -f master &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached HEAD
+'
+
+test_expect_failure '"checkout --recurse-submodules" needs -f to update modifed submodule content' '
+	echo modified >submodule/second.t &&
+	test_must_fail git checkout --recurse-submodules HEAD^ &&
+	test_must_fail git diff-files --quiet submodule &&
+	git diff-files --quiet file &&
+	git checkout --recurse-submodules -f HEAD^ &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached HEAD &&
+	git checkout --recurse-submodules -f master &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached HEAD
+'
+
+test_expect_failure '"checkout --recurse-submodules" ignores modified submodule content that would not be changed' '
+	echo modified >expected &&
+	cp expected submodule/first.t &&
+	git checkout --recurse-submodules HEAD^ &&
+	test_cmp expected submodule/first.t &&
+	test_must_fail git diff-files --quiet submodule &&
+	git diff-index --quiet --cached HEAD &&
+	git checkout --recurse-submodules -f master &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached HEAD
+'
+
+test_expect_failure '"checkout --recurse-submodules" does not care about untracked submodule content' '
+	echo untracked >submodule/untracked &&
+	git checkout --recurse-submodules master &&
+	git diff-files --quiet --ignore-submodules=untracked &&
+	git diff-index --quiet --cached HEAD &&
+	rm submodule/untracked
+'
+
+test_expect_failure '"checkout --recurse-submodules" needs -f when submodule commit is not present (but does fail anyway)' '
+	git checkout --recurse-submodules -b bogus_commit master &&
+	git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submodule &&
+	BOGUS_TREE=$(git write-tree) &&
+	BOGUS_COMMIT=$(echo "bogus submodule commit" | git commit-tree $BOGUS_TREE) &&
+	git commit -m "bogus submodule commit" &&
+	git checkout --recurse-submodules -f master &&
+	test_must_fail git checkout --recurse-submodules bogus_commit &&
+	git diff-files --quiet &&
+	test_must_fail git checkout --recurse-submodules -f bogus_commit &&
+	test_must_fail git diff-files --quiet submodule &&
+	git diff-files --quiet file &&
+	git diff-index --quiet --cached HEAD &&
+	git checkout --recurse-submodules -f master
+'
+
+KNOWN_FAILURE_RECURSE_SUBMODULE_SERIES_BREAKS_REPLACE_SUBMODULE_TEST=1
 test_submodule_switch "git checkout"
 
+KNOWN_FAILURE_RECURSE_SUBMODULE_SERIES_BREAKS_REPLACE_SUBMODULE_TEST=
 test_submodule_forced_switch "git checkout -f"
 
 test_done
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 15/16] completion: add '--recurse-submodules' to checkout
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (13 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 14/16] checkout: recurse into submodules if asked to Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-11-15 23:06 ` [PATCH 16/16] checkout: add config option to recurse into submodules by default Stefan Beller
  2016-12-03  6:13 ` [RFC PATCH 00/16] Checkout aware of Submodules! Xiaodong Qi
  16 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 contrib/completion/git-completion.bash | 2 +-
 t/t9902-completion.sh                  | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 21016bf..28acfdb 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1068,7 +1068,7 @@ _git_checkout ()
 	--*)
 		__gitcomp "
 			--quiet --ours --theirs --track --no-track --merge
-			--conflict= --orphan --patch
+			--conflict= --orphan --patch --recurse-submodules
 			"
 		;;
 	*)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2ba62fb..d2d1102 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -447,6 +447,7 @@ test_expect_success 'double dash "git checkout"' '
 	--conflict=
 	--orphan Z
 	--patch Z
+	--recurse-submodules Z
 	EOF
 '
 
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 16/16] checkout: add config option to recurse into submodules by default
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (14 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 15/16] completion: add '--recurse-submodules' to checkout Stefan Beller
@ 2016-11-15 23:06 ` Stefan Beller
  2016-12-03  6:13 ` [RFC PATCH 00/16] Checkout aware of Submodules! Xiaodong Qi
  16 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-15 23:06 UTC (permalink / raw)
  Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner,
	Stefan Beller

To make it easier for the user, who doesn't want to give the
`--recurse-submodules` option whenever they run checkout, have an
option for to set the default behavior for checkout to recurse into
submodules.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 Documentation/config.txt       |  6 ++++++
 Documentation/git-checkout.txt |  5 +++--
 submodule.c                    |  6 +++---
 t/t2013-checkout-submodule.sh  | 19 +++++++++++++++++++
 4 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index a0ab66a..67e0714 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -949,6 +949,12 @@ clean.requireForce::
 	A boolean to make git-clean do nothing unless given -f,
 	-i or -n.   Defaults to true.
 
+checkout.recurseSubmodules::
+	This option can be set to a boolean value.
+	When set to true checkout will recurse into submodules and
+	update them. When set to false, which is the default, checkout
+	will leave submodules untouched.
+
 color.branch::
 	A boolean to enable/disable color in the output of
 	linkgit:git-branch[1]. May be set to `always`,
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index a0ea2c5..819c430 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -260,8 +260,9 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 	Using --recurse-submodules will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
-	will fail until `-f` is used. If nothing (or --no-recurse-submodules)
-	is used, the work trees of submodules will not be updated.
+	will fail until `-f` is used. If `--no-recurse-submodules` is used,
+	the work trees of submodules will not be updated. If no command line
+	argument is given, `checkout.recurseSubmodules` is used as a default.
 
 <branch>::
 	Branch to checkout; if it refers to a branch (i.e., a name that,
diff --git a/submodule.c b/submodule.c
index 2149ef7..0c807d9 100644
--- a/submodule.c
+++ b/submodule.c
@@ -160,10 +160,10 @@ int submodule_config(const char *var, const char *value, void *cb)
 		return 0;
 	} else if (starts_with(var, "submodule."))
 		return parse_submodule_config_option(var, value);
-	else if (!strcmp(var, "fetch.recursesubmodules")) {
+	else if (!strcmp(var, "fetch.recursesubmodules"))
 		config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
-		return 0;
-	}
+	else if (!strcmp(var, "checkout.recursesubmodules"))
+		config_update_recurse_submodules = parse_update_recurse_submodules_arg(var, value);
 	return 0;
 }
 
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index 60f6987..788c59d 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -149,6 +149,25 @@ test_expect_success '"checkout --recurse-submodules" repopulates submodule' '
 	)
 '
 
+test_expect_success 'option checkout.recurseSubmodules updates submodule' '
+	test_config -C super checkout.recurseSubmodules 1 &&
+	(
+		cd super &&
+		git checkout base &&
+		git checkout -b advanced-base &&
+		git -C submodule commit --allow-empty -m "empty commit" &&
+		git add submodule &&
+		git commit -m "advance submodule" &&
+		git checkout base &&
+		git diff-files --quiet &&
+		git diff-index --quiet --cached base &&
+		git checkout advanced-base &&
+		git diff-files --quiet &&
+		git diff-index --quiet --cached advanced-base &&
+		git checkout --recurse-submodules base
+	)
+'
+
 test_expect_success '"checkout --recurse-submodules" repopulates submodule in existing directory' '
 	(
 		cd super &&
-- 
2.10.1.469.g00a8914


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* RE: [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array
  2016-11-15 23:06 ` [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array Stefan Beller
@ 2016-11-15 23:11   ` David Turner
  2016-11-16 19:03     ` Junio C Hamano
  0 siblings, 1 reply; 50+ messages in thread
From: David Turner @ 2016-11-15 23:11 UTC (permalink / raw)
  To: 'Stefan Beller'
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

> -		"-u",
...
> +	argv_array_pushl(&cp.args, "status", "--porcelain", "-uall",

This also changes -u to -uall, which is not mentioned in the commit message.  That should probably be called out.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 04/16] update submodules: add is_submodule_populated
  2016-11-15 23:06 ` [PATCH 04/16] update submodules: add is_submodule_populated Stefan Beller
@ 2016-11-15 23:20   ` Brandon Williams
  0 siblings, 0 replies; 50+ messages in thread
From: Brandon Williams @ 2016-11-15 23:20 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> This is nearly same as Brandon sent out.
> (First patch of origin/bw/grep-recurse-submodules,
> will drop this patch once Brandons series is stable
> enough to build on).

I would be thrilled to see more reviews on that series :D

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 07/16] update submodules: introduce submodule_is_interesting
  2016-11-15 23:06 ` [PATCH 07/16] update submodules: introduce submodule_is_interesting Stefan Beller
@ 2016-11-15 23:34   ` Brandon Williams
  2016-11-16  0:14   ` David Turner
       [not found]   ` <20161117105715.GC39230@book.hvoigt.net>
  2 siblings, 0 replies; 50+ messages in thread
From: Brandon Williams @ 2016-11-15 23:34 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> +/**
> + * When updating the working tree, do we need to check if the submodule needs
> + * updating. We do not require a check if we are already sure that the
> + * submodule doesn't need updating, e.g. when we are not interested in submodules
> + * or the submodule is marked uninteresting by being not initialized.
> + */

The first sentence seems a bit awkward.  It seems like its worded as a
question, maybe drop the 'do'?

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-15 23:06 ` [PATCH 08/16] update submodules: add depopulate_submodule Stefan Beller
@ 2016-11-15 23:44   ` Brandon Williams
  2016-11-17 22:23     ` Stefan Beller
       [not found]   ` <20161117111337.GD39230@book.hvoigt.net>
  1 sibling, 1 reply; 50+ messages in thread
From: Brandon Williams @ 2016-11-15 23:44 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> Implement the functionality needed to enable work tree manipulating
> commands to that a deleted submodule should not only affect the index

"to that a deleted"  did you mean "so that a deleted"

> (leaving all the files of the submodule in the work tree) but also to
> remove the work tree of the superproject (including any untracked
> files).
> 
> To do so, we need an equivalent of "rm -rf", which is already found in
> entry.c, so expose that and for clarity add a suffix "_or_dir" to it.
> 
> That will only work properly when the submodule uses a gitfile instead of
> a .git directory and no untracked files are present. Otherwise the removal
> will fail with a warning (which is just what happened until now).

So if a submodule uses a .git directory then it will be ignored during
the checkout?  All other submodules will actually be removed? Couldn't
you end up in an undesirable state with a checkout effecting one
submodule but not another?

> diff --git a/cache.h b/cache.h
> index a50a61a..65c47e4 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -2018,4 +2018,6 @@ void sleep_millisec(int millisec);
>   */
>  void safe_create_dir(const char *dir, int share);
>  
> +void remove_subtree_or_die(const char *path);
> +
>  #endif /* CACHE_H */

Should probably place an explicit 'extern' in the function prototype.

> +int depopulate_submodule(const char *path)
> +{
> +	int ret = 0;
> +	char *dot_git = xstrfmt("%s/.git", path);
> +
> +	/* Is it populated? */
> +	if (!resolve_gitdir(dot_git))
> +		goto out;
> +
> +	/* Does it have a .git directory? */
> +	if (!submodule_uses_gitfile(path)) {
> +		warning(_("cannot remove submodule '%s' because it (or one of "
> +			  "its nested submodules) uses a .git directory"),
> +			  path);
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	remove_subtree_or_die(path);
> +
> +out:
> +	free(dot_git);
> +	return ret;
> +}

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 09/16] update submodules: add scheduling to update submodules
  2016-11-15 23:06 ` [PATCH 09/16] update submodules: add scheduling to update submodules Stefan Beller
@ 2016-11-16  0:02   ` Brandon Williams
  2016-11-16  0:07     ` David Turner
  2016-11-18  0:28     ` Stefan Beller
  0 siblings, 2 replies; 50+ messages in thread
From: Brandon Williams @ 2016-11-16  0:02 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> +static int update_submodule(const char *path, const struct object_id *oid,
> +			    int force, int is_new)
> +{
> +	const char *git_dir;
> +	struct child_process cp = CHILD_PROCESS_INIT;
> +	const struct submodule *sub = submodule_from_path(null_sha1, path);
> +
> +	if (!sub || !sub->name)
> +		return -1;
> +
> +	git_dir = resolve_gitdir(git_common_path("modules/%s", sub->name));
> +
> +	if (!git_dir)
> +		return -1;
> +
> +	if (is_new)
> +		connect_work_tree_and_git_dir(path, git_dir);

> +
> +	/* update index via `read-tree --reset sha1` */
> +	argv_array_pushl(&cp.args, "read-tree",
> +				   force ? "--reset" : "-m",
> +				   "-u", sha1_to_hex(oid->hash), NULL);
> +	prepare_submodule_repo_env(&cp.env_array);
> +	cp.git_cmd = 1;
> +	cp.no_stdin = 1;
> +	cp.dir = path;
> +	if (run_command(&cp)) {
> +		warning(_("reading the index in submodule '%s' failed"), path);
> +		child_process_clear(&cp);
> +		return -1;
> +	}
> +
> +	/* write index to working dir */
> +	child_process_clear(&cp);
> +	child_process_init(&cp);
> +	argv_array_pushl(&cp.args, "checkout-index", "-a", NULL);
> +	cp.git_cmd = 1;
> +	cp.no_stdin = 1;
> +	cp.dir = path;
> +	if (force)
> +		argv_array_push(&cp.args, "-f");
> +
> +	if (run_command(&cp)) {
> +		warning(_("populating the working directory in submodule '%s' failed"), path);
> +		child_process_clear(&cp);
> +		return -1;
> +	}
> +
> +	/* get the HEAD right */
> +	child_process_clear(&cp);
> +	child_process_init(&cp);
> +	argv_array_pushl(&cp.args, "checkout", "--recurse-submodules", NULL);
> +	cp.git_cmd = 1;
> +	cp.no_stdin = 1;
> +	cp.dir = path;
> +	if (force)
> +		argv_array_push(&cp.args, "-f");
> +	argv_array_push(&cp.args, sha1_to_hex(oid->hash));
> +
> +	if (run_command(&cp)) {
> +		warning(_("setting the HEAD in submodule '%s' failed"), path);
> +		child_process_clear(&cp);
> +		return -1;
> +	}
> +
> +	child_process_clear(&cp);
> +	return 0;
> +}

If run command is successful then it handles the clearing of the child
process struct, correct?  Is there a negative to having all the explicit
clears when the child was successful?

> +
>  int depopulate_submodule(const char *path)
>  {
>  	int ret = 0;
> @@ -1336,3 +1405,51 @@ void prepare_submodule_repo_env(struct argv_array *out)
>  	}
>  	argv_array_push(out, "GIT_DIR=.git");
>  }
> +
> +struct scheduled_submodules_update_type {
> +	const char *path;
> +	const struct object_id *oid;
> +	/*
> +	 * Do we need to perform a complete checkout or just incremental
> +	 * update?
> +	 */
> +	unsigned is_new:1;
> +} *scheduled_submodules;
> +#define SCHEDULED_SUBMODULES_INIT {NULL, NULL}

I may not know enough about these types of initializors but that Init
macro only has 2 entries while there are three entries in the struct
itself.

> +
> +int scheduled_submodules_nr, scheduled_submodules_alloc;

Should these globals be static since they should be scoped to only this
file?

> +
> +void schedule_submodule_for_update(const struct cache_entry *ce, int is_new)
> +{
> +	struct scheduled_submodules_update_type *ssu;
> +	ALLOC_GROW(scheduled_submodules,
> +		   scheduled_submodules_nr + 1,
> +		   scheduled_submodules_alloc);
> +	ssu = &scheduled_submodules[scheduled_submodules_nr++];
> +	ssu->path = ce->name;
> +	ssu->oid = &ce->oid;
> +	ssu->is_new = !!is_new;
> +}
> +
> +int update_submodules(int force)
> +{
> +	int i;
> +	gitmodules_config();
> +
> +	/*
> +	 * NEEDSWORK: As submodule updates can potentially take some
> +	 * time each and they do not overlap (i.e. no d/f conflicts;
> +	 * this can be parallelized using the run_commands.h API.
> +	 */
> +	for (i = 0; i < scheduled_submodules_nr; i++) {
> +		struct scheduled_submodules_update_type *ssu =
> +			&scheduled_submodules[i];
> +
> +		if (submodule_is_interesting(ssu->path, null_sha1))
> +			update_submodule(ssu->path, ssu->oid,
> +					 force, ssu->is_new);
> +	}
> +
> +	scheduled_submodules_nr = 0;
> +	return 0;
> +}

nit: organization wise it makes more sense to me to have the
'update_submodule' helper function be located more closely to the
'update_submodules' function.

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 10/16] update submodules: is_submodule_checkout_safe
  2016-11-15 23:06 ` [PATCH 10/16] update submodules: is_submodule_checkout_safe Stefan Beller
@ 2016-11-16  0:06   ` Brandon Williams
  0 siblings, 0 replies; 50+ messages in thread
From: Brandon Williams @ 2016-11-16  0:06 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> In later patches we introduce the options and flag for commands
> that modify the working directory, e.g. git-checkout.
> 
> This piece of code will answer the question:
> "Is it safe to change the submodule to this new state?"
> e.g. is it overwriting untracked files or are there local
> changes that would be overwritten?
> 
> Signed-off-by: Stefan Beller <sbeller@google.com>
> ---
>  submodule.c | 22 ++++++++++++++++++++++
>  submodule.h |  2 ++
>  2 files changed, 24 insertions(+)
> 
> diff --git a/submodule.c b/submodule.c
> index 2773151..2149ef7 100644
> --- a/submodule.c
> +++ b/submodule.c
> @@ -1201,6 +1201,28 @@ int ok_to_remove_submodule(const char *path)
>  	return ok_to_remove;
>  }
>  
> +int is_submodule_checkout_safe(const char *path, const struct object_id *oid)
> +{
> +	struct child_process cp = CHILD_PROCESS_INIT;
> +
> +	if (!is_submodule_populated(path))
> +		/* The submodule is not populated, it's safe to check it out */
> +		/*
> +		 * TODO: When git learns to re-populate submodules, a check must be
> +		 * added here to assert that no local files will be overwritten.
> +		 */

When you mean local files do you mean in the situation where we want to
checkout a submodule at path 'foo' but there already exists a file at
path 'foo'?

> +		return 1;
> +
> +	argv_array_pushl(&cp.args, "read-tree", "-n", "-m", "HEAD",
> +			 sha1_to_hex(oid->hash), NULL);
> +
> +	prepare_submodule_repo_env(&cp.env_array);
> +	cp.git_cmd = 1;
> +	cp.no_stdin = 1;
> +	cp.dir = path;
> +	return run_command(&cp) == 0;
> +}
> +
>  static int find_first_merges(struct object_array *result, const char *path,
>  		struct commit *a, struct commit *b)
>  {
> diff --git a/submodule.h b/submodule.h
> index f01f87c..a7fa634 100644
> --- a/submodule.h
> +++ b/submodule.h
> @@ -74,6 +74,8 @@ extern unsigned is_submodule_modified(const char *path, int ignore_untracked);
>  extern int is_submodule_populated(const char *path);
>  extern int submodule_uses_gitfile(const char *path);
>  extern int ok_to_remove_submodule(const char *path);
> +extern int is_submodule_checkout_safe(const char *path,
> +				      const struct object_id *oid);
>  extern int merge_submodule(unsigned char result[20], const char *path,
>  			   const unsigned char base[20],
>  			   const unsigned char a[20],
> -- 
> 2.10.1.469.g00a8914
> 

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* RE: [PATCH 09/16] update submodules: add scheduling to update submodules
  2016-11-16  0:02   ` Brandon Williams
@ 2016-11-16  0:07     ` David Turner
  2016-11-18  0:28     ` Stefan Beller
  1 sibling, 0 replies; 50+ messages in thread
From: David Turner @ 2016-11-16  0:07 UTC (permalink / raw)
  To: 'Brandon Williams', Stefan Beller
  Cc: git@vger.kernel.org, gitster@pobox.com, jrnieder@gmail.com,
	mogulguy10@gmail.com

> -----Original Message-----
> From: Brandon Williams [mailto:bmwill@google.com]
> > +struct scheduled_submodules_update_type {
> > +	const char *path;
> > +	const struct object_id *oid;
> > +	/*
> > +	 * Do we need to perform a complete checkout or just incremental
> > +	 * update?
> > +	 */
> > +	unsigned is_new:1;
> > +} *scheduled_submodules;
> > +#define SCHEDULED_SUBMODULES_INIT {NULL, NULL}
> 
> I may not know enough about these types of initializors but that Init
> macro only has 2 entries while there are three entries in the struct
> itself.

In fact, only the first NULL is necessary; unspecified initializer entries in C default to zero.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 11/16] teach unpack_trees() to remove submodule contents
  2016-11-15 23:06 ` [PATCH 11/16] teach unpack_trees() to remove submodule contents Stefan Beller
@ 2016-11-16  0:14   ` Brandon Williams
       [not found]   ` <20161117133538.GF39230@book.hvoigt.net>
  1 sibling, 0 replies; 50+ messages in thread
From: Brandon Williams @ 2016-11-16  0:14 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> Extend rmdir_or_warn() to remove the directories of those submodules which
> are scheduled for removal. Also teach verify_clean_submodule() to check
> that a submodule configured to be removed is not modified before scheduling
> it for removal.
> 
> Signed-off-by: Stefan Beller <sbeller@google.com>
> ---
>  unpack-trees.c | 6 ++----
>  wrapper.c      | 4 ++++
>  2 files changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/unpack-trees.c b/unpack-trees.c
> index ea6bdd2..576e1d5 100644
> --- a/unpack-trees.c
> +++ b/unpack-trees.c
> @@ -9,6 +9,7 @@
>  #include "refs.h"
>  #include "attr.h"
>  #include "split-index.h"
> +#include "submodule.h"
>  #include "dir.h"
>  
>  /*
> @@ -1361,15 +1362,12 @@ static void invalidate_ce_path(const struct cache_entry *ce,
>  /*
>   * Check that checking out ce->sha1 in subdir ce->name is not
>   * going to overwrite any working files.
> - *
> - * Currently, git does not checkout subprojects during a superproject
> - * checkout, so it is not going to overwrite anything.
>   */
>  static int verify_clean_submodule(const struct cache_entry *ce,
>  				  enum unpack_trees_error_types error_type,
>  				  struct unpack_trees_options *o)
>  {
> -	return 0;
> +	return submodule_is_interesting(ce->name, null_sha1) && is_submodule_modified(ce->name, 0);
>  }

So what does the return value from this function meant to mean? Is '1'
mean the submodule is clean while '0' indicates it is dirty or is it the
reverse of that?  Reading this it seems to me a value of '1' means "yes
the submodule is clean!" but the way the return value is calculated
tells a different story.  Either I'm understanding it incorrectly or I
think the return should be something like this:

  return submodule_is_interesting(ce->name, null_sha1) && !is_submodule_modified(ce->name, 0);

Where we return '1' if the submodule is interesting and it hasn't been
modified.

>  
>  static int verify_clean_subdirectory(const struct cache_entry *ce,
> diff --git a/wrapper.c b/wrapper.c
> index e7f1979..17c08de 100644
> --- a/wrapper.c
> +++ b/wrapper.c
> @@ -2,6 +2,7 @@
>   * Various trivial helper wrappers around standard functions
>   */
>  #include "cache.h"
> +#include "submodule.h"
>  
>  static void do_nothing(size_t size)
>  {
> @@ -592,6 +593,9 @@ int unlink_or_warn(const char *file)
>  
>  int rmdir_or_warn(const char *file)
>  {
> +	if (submodule_is_interesting(file, null_sha1)
> +	    && depopulate_submodule(file))
> +		return -1;
>  	return warn_if_unremovable("rmdir", file, rmdir(file));
>  }

It seems weird to me that rmdir is doing checks to see if the file being
removed is a submodule.  Shouldn't those checks have occurred before
calling rmdir?

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* RE: [PATCH 07/16] update submodules: introduce submodule_is_interesting
  2016-11-15 23:06 ` [PATCH 07/16] update submodules: introduce submodule_is_interesting Stefan Beller
  2016-11-15 23:34   ` Brandon Williams
@ 2016-11-16  0:14   ` David Turner
  2016-11-17 20:03     ` Stefan Beller
       [not found]   ` <20161117105715.GC39230@book.hvoigt.net>
  2 siblings, 1 reply; 50+ messages in thread
From: David Turner @ 2016-11-16  0:14 UTC (permalink / raw)
  To: 'Stefan Beller'
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

> -----Original Message-----
> From: Stefan Beller [mailto:sbeller@google.com]
> Sent: Tuesday, November 15, 2016 6:07 PM
> Cc: git@vger.kernel.org; bmwill@google.com; gitster@pobox.com;
> jrnieder@gmail.com; mogulguy10@gmail.com; David Turner; Stefan Beller
> Subject: [PATCH 07/16] update submodules: introduce
> submodule_is_interesting
> +int submodule_is_interesting(const char *path, const unsigned char
> +*sha1) {

This is apparently only ever (in this series) called with null_sha1.  So either this arg is unnecessary, or there are bugs elsewhere in the code.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* RE: [PATCH 13/16] submodule: teach unpack_trees() to update submodules
  2016-11-15 23:06 ` [PATCH 13/16] submodule: teach unpack_trees() to update submodules Stefan Beller
@ 2016-11-16  0:22   ` David Turner
  2016-11-18 23:33     ` Stefan Beller
  2016-11-16  0:25   ` Brandon Williams
  1 sibling, 1 reply; 50+ messages in thread
From: David Turner @ 2016-11-16  0:22 UTC (permalink / raw)
  To: 'Stefan Beller'
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

[I've reviewed up-to and including 13; I'll look at 14-16 tomorrow-ish]

> -----Original Message-----
> From: Stefan Beller [mailto:sbeller@google.com]
> Sent: Tuesday, November 15, 2016 6:07 PM
> Cc: git@vger.kernel.org; bmwill@google.com; gitster@pobox.com;
> jrnieder@gmail.com; mogulguy10@gmail.com; David Turner; Stefan Beller
> Subject: [PATCH 13/16] submodule: teach unpack_trees() to update
> submodules
...
>  	msgs[ERROR_NOT_UPTODATE_DIR] =
>  		_("Updating the following directories would lose untracked
> files in it:\n%s");
> +	msgs[ERROR_NOT_UPTODATE_SUBMODULE] =
> +		_("Updating the following submodules would lose modifications
> in
> +it:\n%s");

s/it/them/

>  	if (!strcmp(cmd, "checkout"))
>  		msg = advice_commit_before_merge
> @@ -1315,19 +1320,18 @@ static int verify_uptodate_1(const struct
> cache_entry *ce,
>  		return 0;
> 
>  	if (!lstat(ce->name, &st)) {
> -		int flags =
> CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
> -		unsigned changed = ie_match_stat(o->src_index, ce, &st,
> flags);
> -		if (!changed)
> -			return 0;
> -		/*
> -		 * NEEDSWORK: the current default policy is to allow
> -		 * submodule to be out of sync wrt the superproject
> -		 * index.  This needs to be tightened later for
> -		 * submodules that are marked to be automatically
> -		 * checked out.
> -		 */
> -		if (S_ISGITLINK(ce->ce_mode))
> -			return 0;
> +		if (!S_ISGITLINK(ce->ce_mode)) {

I generally prefer to avoid if (!x) { A } else { B } -- I would rather just see if (x) { B } else { A }.

> +		if (!changed) {
> +			/* old is always a submodule */
> +			if (S_ISGITLINK(new->ce_mode)) {
> +				/*
> +				 * new is also a submodule, so check if we care
> +				 * and then if can checkout the new sha1 safely
> +				 */
> +				if (submodule_is_interesting(old->name, null_sha1)
> +				    && is_submodule_checkout_safe(new->name, &new-
> >oid))
> +					return 0;
> +			} else {
> +				/*
> +				 * new is not a submodule any more, so only
> +				 * care if we care:
> +				 */
> +				if (submodule_is_interesting(old->name, null_sha1)
> +				    && ok_to_remove_submodule(old->name))
> +					return 0;
> +			}

Do we need a return 1 in here somewhere?  Because otherwise, we fall through and return 0 later.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/16] submodule: teach unpack_trees() to update submodules
  2016-11-15 23:06 ` [PATCH 13/16] submodule: teach unpack_trees() to update submodules Stefan Beller
  2016-11-16  0:22   ` David Turner
@ 2016-11-16  0:25   ` Brandon Williams
  2016-11-18 23:39     ` Stefan Beller
  1 sibling, 1 reply; 50+ messages in thread
From: Brandon Williams @ 2016-11-16  0:25 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> +		if (!S_ISGITLINK(ce->ce_mode)) {
> +			int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;

For readability you may want to have spaces between the two flags

> +	if (o->index_only
> +	    || (!((old->ce_flags & CE_VALID) || ce_skip_worktree(old))
> +		&& (o->reset || ce_uptodate(old))))
> +		return 0;

The coding guidelines say that git prefers to have the logical operators
on the right side like this:

if (o->index_only ||
    (!((old->ce_flags & CE_VALID) || ce_skip_worktree(old))	&&
     (o->reset || ce_uptodate(old))))
	return 0;

It also says the other way is ok too, just a thought :)

> +				if (submodule_is_interesting(old->name, null_sha1)
> +				    && is_submodule_checkout_safe(new->name, &new->oid))
> +					return 0;

here too.

> +			} else {
> +				/*
> +				 * new is not a submodule any more, so only
> +				 * care if we care:
> +				 */
> +				if (submodule_is_interesting(old->name, null_sha1)
> +				    && ok_to_remove_submodule(old->name))
> +					return 0;

and here

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 14/16] checkout: recurse into submodules if asked to
  2016-11-15 23:06 ` [PATCH 14/16] checkout: recurse into submodules if asked to Stefan Beller
@ 2016-11-16  0:33   ` Brandon Williams
  2016-11-16 17:03   ` David Turner
  2016-11-16 17:05   ` David Turner
  2 siblings, 0 replies; 50+ messages in thread
From: Brandon Williams @ 2016-11-16  0:33 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jrnieder, mogulguy10, David.Turner

On 11/15, Stefan Beller wrote:
> +int option_parse_recurse_submodules(const struct option *opt,
> +				    const char *arg, int unset)
> +{
> +	if (unset) {
> +		recurse_submodules = RECURSE_SUBMODULES_OFF;
> +		return 0;
> +	}
> +	if (arg)
> +		recurse_submodules =
> +			parse_update_recurse_submodules_arg(opt->long_name,
> +							    arg);
> +	else
> +		recurse_submodules = RECURSE_SUBMODULES_ON;
> +
> +	return 0;
> +}

I assume it is ok to always return 0 from this function?  Also, I know
we don't like having braces on single statement if's but it is a bit
hard to read that multi-line statement without braces.  But that's just
my opinion :)


-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* RE: [PATCH 14/16] checkout: recurse into submodules if asked to
  2016-11-15 23:06 ` [PATCH 14/16] checkout: recurse into submodules if asked to Stefan Beller
  2016-11-16  0:33   ` Brandon Williams
@ 2016-11-16 17:03   ` David Turner
  2016-11-16 17:05   ` David Turner
  2 siblings, 0 replies; 50+ messages in thread
From: David Turner @ 2016-11-16 17:03 UTC (permalink / raw)
  To: 'Stefan Beller'
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com



> -----Original Message-----
> From: Stefan Beller [mailto:sbeller@google.com]
> 
> diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index
> 79cdd34..e0773c6 100755
> --- a/t/lib-submodule-update.sh
> +++ b/t/lib-submodule-update.sh
> @@ -634,7 +634,13 @@ test_submodule_forced_switch () {
> 
>  	########################## Modified submodule
> #########################
>  	# Updating a submodule sha1 doesn't update the submodule's work tree
> -	test_expect_success "$command: modified submodule does not update
> submodule work tree" '
> +	if test
> "$KNOWN_FAILURE_RECURSE_SUBMODULE_SERIES_BREAKS_REPLACE_SUBMODULE_TEST" =
> 1
> +	then
> +		RESULT="failure"
> +	else
> +		RESULT="success"
> +	fi
> +	test_expect_$RESULT "$command: modified submodule does not update
> submodule work tree" '

Why does this break?  I thought it was only if checkout is run with --recurse-submodules that anything should change?

> +test_expect_success 'dirty file file is not deleted' '

Duplicate 'file' in this test name.
> +# This is ok in theory, we just need to make sure # the garbage
> +collection doesn't eat the commit.
> +test_expect_success 'different commit prevents from deleting' '

This isn't a different commit -- it's a dirty index, right? 

> +test_expect_failure '"checkout --recurse-submodules" does not care about
> untracked submodule content' '
> +	echo untracked >submodule/untracked &&
> +	git checkout --recurse-submodules master &&
> +	git diff-files --quiet --ignore-submodules=untracked &&
> +	git diff-index --quiet --cached HEAD &&
> +	rm submodule/untracked
> +'

Use test_when_finished for cleanup.

> +test_expect_failure '"checkout --recurse-submodules" needs -f when
> submodule commit is not present (but does fail anyway)' '
> +	git checkout --recurse-submodules -b bogus_commit master &&
> +	git update-index --cacheinfo 160000
> 0123456789012345678901234567890123456789 submodule &&
> +	BOGUS_TREE=$(git write-tree) &&
> +	BOGUS_COMMIT=$(echo "bogus submodule commit" | git commit-tree
> $BOGUS_TREE) &&
> +	git commit -m "bogus submodule commit" &&
> +	git checkout --recurse-submodules -f master &&
> +	test_must_fail git checkout --recurse-submodules bogus_commit &&
> +	git diff-files --quiet &&
> +	test_must_fail git checkout --recurse-submodules -f bogus_commit &&
> +	test_must_fail git diff-files --quiet submodule &&
> +	git diff-files --quiet file &&
> +	git diff-index --quiet --cached HEAD &&
> +	git checkout --recurse-submodules -f master '
> +KNOWN_FAILURE_RECURSE_SUBMODULE_SERIES_BREAKS_REPLACE_SUBMODULE_TEST=1
>  test_submodule_switch "git checkout"
> 
> +KNOWN_FAILURE_RECURSE_SUBMODULE_SERIES_BREAKS_REPLACE_SUBMODULE_TEST=
>  test_submodule_forced_switch "git checkout -f"
> 
>  test_done
> --
> 2.10.1.469.g00a8914


^ permalink raw reply	[flat|nested] 50+ messages in thread

* RE: [PATCH 14/16] checkout: recurse into submodules if asked to
  2016-11-15 23:06 ` [PATCH 14/16] checkout: recurse into submodules if asked to Stefan Beller
  2016-11-16  0:33   ` Brandon Williams
  2016-11-16 17:03   ` David Turner
@ 2016-11-16 17:05   ` David Turner
  2 siblings, 0 replies; 50+ messages in thread
From: David Turner @ 2016-11-16 17:05 UTC (permalink / raw)
  To: 'Stefan Beller'
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

Sorry, my previous message accidentally sent before I was done.  One more comment:

> -----Original Message-----
> From: Stefan Beller [mailto:sbeller@google.com]

> +test_expect_failure '"checkout --recurse-submodules" needs -f to update
> modifed submodule content' '
> +	echo modified >submodule/second.t &&
> +	test_must_fail git checkout --recurse-submodules HEAD^ &&
> +	test_must_fail git diff-files --quiet submodule &&
> +	git diff-files --quiet file &&
> +	git checkout --recurse-submodules -f HEAD^ &&
> +	git diff-files --quiet &&
> +	git diff-index --quiet --cached HEAD &&
> +	git checkout --recurse-submodules -f master &&
> +	git diff-files --quiet &&
> +	git diff-index --quiet --cached HEAD
> +'

It might be worth adding some comments explaining why you expect these to fail.  


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array
  2016-11-15 23:11   ` David Turner
@ 2016-11-16 19:03     ` Junio C Hamano
  2016-11-17 18:36       ` Stefan Beller
  0 siblings, 1 reply; 50+ messages in thread
From: Junio C Hamano @ 2016-11-16 19:03 UTC (permalink / raw)
  To: David Turner
  Cc: 'Stefan Beller', git@vger.kernel.org, bmwill@google.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

David Turner <David.Turner@twosigma.com> writes:

>> -		"-u",
> ...
>> +	argv_array_pushl(&cp.args, "status", "--porcelain", "-uall",
>
> This also changes -u to -uall, which is not mentioned in the
> commit message.  That should probably be called out.

Or not making that change at all.  Isn't "-u" the same as "-uall"?

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80
  2016-11-15 23:06 ` [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80 Stefan Beller
@ 2016-11-16 19:08   ` Junio C Hamano
  2016-11-17 18:29     ` Stefan Beller
  0 siblings, 1 reply; 50+ messages in thread
From: Junio C Hamano @ 2016-11-16 19:08 UTC (permalink / raw)
  To: Stefan Beller

Stefan Beller <sbeller@google.com> writes:

> submodule.h: add extern keyword to functions, break line before 80

The former is probably a good change for consistency.  As the latter
change breaks a workflow around quickly checking the output from
"git grep funcname \*.h", I am not sure if it is a good idea.

Especially things like this look like a usability regression:

-void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
+extern void handle_ignore_submodules_arg(struct diff_options *diffopt,
+					 const char *);

Perhaps the name "diffopt" can be dropped from there if we want it
to be shorter.  Names in prototypes are valuable for parameters of
more generic types like "char *", especially when there are more
than one parameters of the same type, but in this case the typename
is specific enough for readers to tell what it is.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80
  2016-11-16 19:08   ` Junio C Hamano
@ 2016-11-17 18:29     ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-17 18:29 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git@vger.kernel.org, Brandon Williams, Jonathan Nieder,
	Martin Fick, David Turner

On Wed, Nov 16, 2016 at 11:08 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Stefan Beller <sbeller@google.com> writes:
>
>> submodule.h: add extern keyword to functions, break line before 80
>
> The former is probably a good change for consistency.  As the latter
> change breaks a workflow around quickly checking the output from
> "git grep funcname \*.h", I am not sure if it is a good idea.
>
> Especially things like this look like a usability regression:
>
> -void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
> +extern void handle_ignore_submodules_arg(struct diff_options *diffopt,
> +                                        const char *);
>
> Perhaps the name "diffopt" can be dropped from there if we want it
> to be shorter.

Does the Git community have an opinion on dropping the name?
Usually the meaning can be inferred from the type such as in this example.
"diffopt" doesn't add information to my understanding when looking at
the header file.

One (cherry-picked) counter example is:

    /**
    * Like `strbuf_getwholeline`, but operates on a file descriptor.
    * It reads one character at a time, so it is very slow.  Do not
    * use it unless you need the correct position in the file
    * descriptor.
    */
    extern int strbuf_getwholeline_fd(struct strbuf *, int, int);

where I'd need a bit of time to figure out which of the 2 ints is the
fd and which is the line termination character.

So I'd rather have a broken line than dropping names,
as when grepping for names I'd look them up most of the time
anyway (to e.g. read additional documentation or just reading the
source)


> Names in prototypes are valuable for parameters of
> more generic types like "char *", especially when there are more
> than one parameters of the same type, but in this case the typename
> is specific enough for readers to tell what it is.

Ok, that seems a reasonable to me.

I'll squash the changes below, with an updated commit message:

submodule.h: add extern keyword to functions

As the upcoming series will add a lot of functions to the submodule
header, let's first make the header consistent to the rest of the project
by adding the extern keyword to functions.

As per the CodingGuidelines we try to stay below 80 characters per line,
so adapt all those functions to stay below 80 characters that are already
using more than one line.  Those function using just one line are better
kept in one line than breaking them up into multiple lines just for the
goal of staying below the character limit as it makes grepping
for functions easier if they are one liners.

(diff on top of the patch under discussion)
diff --git a/submodule.h b/submodule.h
index afc58d0..2082847 100644
--- a/submodule.h
+++ b/submodule.h
@@ -33,17 +33,14 @@ extern int is_staging_gitmodules_ok(void);
 extern int update_path_in_gitmodules(const char *oldpath, const char *newpath);
 extern int remove_path_from_gitmodules(const char *path);
 extern void stage_updated_gitmodules(void);
-extern void set_diffopt_flags_from_submodule_config(
-               struct diff_options *diffopt,
+extern void set_diffopt_flags_from_submodule_config(struct diff_options *,
                const char *path);
 extern int submodule_config(const char *var, const char *value, void *cb);
 extern void gitmodules_config(void);
 extern int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst);
-extern const char *submodule_strategy_to_string(
-               const struct submodule_update_strategy *s);
-extern void handle_ignore_submodules_arg(struct diff_options *diffopt,
-                                        const char *);
+extern const char *submodule_strategy_to_string(const struct
submodule_update_strategy *s);
+extern void handle_ignore_submodules_arg(struct diff_options *, const char *);
 extern void show_submodule_summary(FILE *f, const char *path,
                const char *line_prefix,
                struct object_id *one, struct object_id *two,
@@ -72,8 +69,7 @@ extern int find_unpushed_submodules(unsigned char
new_sha1[20],
                                    struct string_list *needs_pushing);
 extern int push_unpushed_submodules(unsigned char new_sha1[20],
                                    const char *remotes_name);
-extern void connect_work_tree_and_git_dir(const char *work_tree,
-                                         const char *git_dir);
+extern void connect_work_tree_and_git_dir(const char *work_tree,
const char *git_dir);
 extern int parallel_submodules(void);

 /*

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array
  2016-11-16 19:03     ` Junio C Hamano
@ 2016-11-17 18:36       ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-17 18:36 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: David Turner, git@vger.kernel.org, bmwill@google.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

On Wed, Nov 16, 2016 at 11:03 AM, Junio C Hamano <gitster@pobox.com> wrote:
> David Turner <David.Turner@twosigma.com> writes:
>
>>> -            "-u",
>> ...
>>> +    argv_array_pushl(&cp.args, "status", "--porcelain", "-uall",
>>
>> This also changes -u to -uall, which is not mentioned in the
>> commit message.  That should probably be called out.
>
> Or not making that change at all.  Isn't "-u" the same as "-uall"?

Yes it is.  My original line of thinking was to have it spelled out
clearly in case
it changes in the future, but then we could argue that the --porcelain parameter
ought to keep the default of -u to "all".

I'll undo that change then.

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 07/16] update submodules: introduce submodule_is_interesting
  2016-11-16  0:14   ` David Turner
@ 2016-11-17 20:03     ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-17 20:03 UTC (permalink / raw)
  To: David Turner
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

On Tue, Nov 15, 2016 at 4:14 PM, David Turner <David.Turner@twosigma.com> wrote:

>> +int submodule_is_interesting(const char *path, const unsigned char
>> +*sha1) {
>
> This is apparently only ever (in this series) called with null_sha1.  So either this arg is unnecessary, or there are bugs elsewhere in the code.

I was torn when writing the series, as I initially had submodule_is_interesting
with no sha1 argument and it turned out to be buggy in my first
initial implementation,
which lead me to thinking the sha1 actually matters.

The line of thinking was similar to loading the submodules from the
submodule-config cache as that also has different values for different sha1s,
e.g. a submodule is only interesting if submodule.<name>.update != none,
which can have changed with different sha1s.

I refactored the series since then to call the _is_initeresting method
at different times
(before and after the actual checkout), such that we implicitly have
the correct sha1
while calling it.

So I would argue the sha1 argument is not needed. I'll remove it.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 07/16] update submodules: introduce submodule_is_interesting
       [not found]   ` <20161117105715.GC39230@book.hvoigt.net>
@ 2016-11-17 20:08     ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-17 20:08 UTC (permalink / raw)
  To: Heiko Voigt
  Cc: git@vger.kernel.org, Brandon Williams, Junio C Hamano,
	Jonathan Nieder, Martin Fick, David Turner

On Thu, Nov 17, 2016 at 2:57 AM, Heiko Voigt <hvoigt@hvoigt.net> wrote:

> It seems that you are only looking at the submodule config from a
> commit. Should a user be able to override this with local configuration?
> Haven't looked further in the patchseries so maybe that is somewhere
> else?

It turns out that in later patches we pass in null_sha1 only, which is
looking at the config and possible overrides.

I'll refactor to take no sha1 argument and use null_sha1 here directly.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-15 23:44   ` Brandon Williams
@ 2016-11-17 22:23     ` Stefan Beller
  2016-11-17 22:29       ` Brandon Williams
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-17 22:23 UTC (permalink / raw)
  To: Brandon Williams
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On Tue, Nov 15, 2016 at 3:44 PM, Brandon Williams <bmwill@google.com> wrote:
> "to that a deleted"  did you mean "so that a deleted"

done

>> That will only work properly when the submodule uses a gitfile instead of
>> a .git directory and no untracked files are present. Otherwise the removal
>> will fail with a warning (which is just what happened until now).
>
> So if a submodule uses a .git directory then it will be ignored during
> the checkout?

Well first you get the warning:

    "cannot remove submodule '%s' because it (or one of "
    "its nested submodules) uses a .git directory"),

and in case a d/f/ conflict arises in a later stage (e.g. when the submodule
is replaced by a file or symlink), you get another related error with
less helpful description how to debug it.

>  All other submodules will actually be removed? Couldn't
> you end up in an undesirable state with a checkout effecting one
> submodule but not another?

Yes you could. Maybe it's time to add
"git submodule intern-git-dir", which can be given as a helpful hint
or even run here first.

>
> Should probably place an explicit 'extern' in the function prototype.

done

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
       [not found]   ` <20161117111337.GD39230@book.hvoigt.net>
@ 2016-11-17 22:28     ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-17 22:28 UTC (permalink / raw)
  To: Heiko Voigt
  Cc: git@vger.kernel.org, Brandon Williams, Junio C Hamano,
	Jonathan Nieder, Martin Fick, David Turner

On Thu, Nov 17, 2016 at 3:13 AM, Heiko Voigt <hvoigt@hvoigt.net> wrote:
> On Tue, Nov 15, 2016 at 03:06:43PM -0800, Stefan Beller wrote:
>> diff --git a/cache.h b/cache.h
>> index a50a61a..65c47e4 100644
>> --- a/cache.h
>> +++ b/cache.h
>> @@ -2018,4 +2018,6 @@ void sleep_millisec(int millisec);
>>   */
>>  void safe_create_dir(const char *dir, int share);
>>
>> +void remove_subtree_or_die(const char *path);
>
> It seems that it is called remove_subtree already internally but can we
> maybe change that name? The term 'subtree' refers to something else[1] for
> me.

Doh, right.

> Maybe just: remove_directory() would make it also clear that there
> is no special internal git datatype meant by that but just a directory
> in the filesystem.

I'll go with `remove_directory_or_die` for now.

>
>> +
>>  #endif /* CACHE_H */
>> diff --git a/entry.c b/entry.c
>> index c6eea24..019826b 100644
>> --- a/entry.c
>> +++ b/entry.c
>> @@ -73,6 +73,14 @@ static void remove_subtree(struct strbuf *path)
>>               die_errno("cannot rmdir '%s'", path->buf);
>>  }
>>
>> +void remove_subtree_or_die(const char *path)
>> +{
>> +     struct strbuf sb = STRBUF_INIT;
>> +     strbuf_addstr(&sb, path);
>> +     remove_subtree(&sb);
>> +     strbuf_release(&sb);
>> +}
>
> Why are you exposing it with const char * instead of strbuf? We get
> unnecessary conversions in case a caller already has a strbuf ready.
> Just in case later code also wants to use it.

For the cleanliness of API design. (I thought `void remove(char *dir)` is
the closest approximation of `rm -rf $1`)

I'll use a strbuf instead.

Thanks!
Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-17 22:23     ` Stefan Beller
@ 2016-11-17 22:29       ` Brandon Williams
  2016-11-17 22:42         ` Stefan Beller
  0 siblings, 1 reply; 50+ messages in thread
From: Brandon Williams @ 2016-11-17 22:29 UTC (permalink / raw)
  To: Stefan Beller
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On 11/17, Stefan Beller wrote:
> Well first you get the warning:
> 
>     "cannot remove submodule '%s' because it (or one of "
>     "its nested submodules) uses a .git directory"),
> 
> and in case a d/f/ conflict arises in a later stage (e.g. when the submodule
> is replaced by a file or symlink), you get another related error with
> less helpful description how to debug it.

Maybe a warning isn't the right thing?  Shouldn't the checkout fail if
there are any issues?  This would force the user to stash/commit their
changes and then retry.

> >  All other submodules will actually be removed? Couldn't
> > you end up in an undesirable state with a checkout effecting one
> > submodule but not another?
> 
> Yes you could. Maybe it's time to add
> "git submodule intern-git-dir", which can be given as a helpful hint
> or even run here first.

That would be a good idea, does that functionality already exist in one
form or another?  I'm assuming it must since git update does just that
when cloning a submodule.

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-17 22:29       ` Brandon Williams
@ 2016-11-17 22:42         ` Stefan Beller
  2016-11-18  0:16           ` Stefan Beller
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-17 22:42 UTC (permalink / raw)
  To: Brandon Williams
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On Thu, Nov 17, 2016 at 2:29 PM, Brandon Williams <bmwill@google.com> wrote:
> On 11/17, Stefan Beller wrote:
>> Well first you get the warning:
>>
>>     "cannot remove submodule '%s' because it (or one of "
>>     "its nested submodules) uses a .git directory"),
>>
>> and in case a d/f/ conflict arises in a later stage (e.g. when the submodule
>> is replaced by a file or symlink), you get another related error with
>> less helpful description how to debug it.
>
> Maybe a warning isn't the right thing?  Shouldn't the checkout fail if
> there are any issues?  This would force the user to stash/commit their
> changes and then retry.

Well if the path is not reused, e.g. you just delete a submodule in a commit
without anything else, you could proceed and have the submodule laying
around dirty?

>
>> >  All other submodules will actually be removed? Couldn't
>> > you end up in an undesirable state with a checkout effecting one
>> > submodule but not another?
>>
>> Yes you could. Maybe it's time to add
>> "git submodule intern-git-dir", which can be given as a helpful hint
>> or even run here first.
>
> That would be a good idea, does that functionality already exist in one
> form or another?  I'm assuming it must since git update does just that
> when cloning a submodule.

No it doesn't (it is roughly these three steps):

    mv ${SUBMODULE_PATH}/.git ${GIT_DIR}/modules/${SUBMODULE_NAME}
    git config -f ${GIT_DIR}/modules/${SUBMODULE_NAME}/config
core.worktree ${SUBMODULE_PATH}
    echo "gitdir: ${GIT_DIR}/modules/${SUBMODULE_NAME}" >
${SUBMODULE_PATH}/.git

The last 2 steps are done via

    void connect_work_tree_and_git_dir(const char *work_tree, const
char *git_dir);

in submodule.{c,h}

However we'd need to make sure the first step is performed correctly. (and make
damn sure we don't loose that git dir), so I think rename(2) does the
correct thing
for directories, except when these two locations are on a different mount point.

I think I'll just write this functionality in C and optionally expose
it via the submodule--helper,
such that the user facing git-submodule.sh only has to call that helper.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-17 22:42         ` Stefan Beller
@ 2016-11-18  0:16           ` Stefan Beller
  2016-11-18 17:46             ` Brandon Williams
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-18  0:16 UTC (permalink / raw)
  To: Brandon Williams
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On Thu, Nov 17, 2016 at 2:42 PM, Stefan Beller <sbeller@google.com> wrote:
>
> I think I'll just write this functionality in C and optionally expose
> it via the submodule--helper,
> such that the user facing git-submodule.sh only has to call that helper.

I think it will roughly look like this:
(white space mangled)


commit e72ef244c667920c874247aa32aa55845500aac8
Author: Stefan Beller <sbeller@google.com>
Date:   Thu Nov 17 16:14:46 2016 -0800

    submodule--helper: add intern-git-dir function

    When a submodule has its git dir inside the working dir, the submodule
    support for checkout that we plan to add in a later patch will fail.

    Add functionality to migrate the git directory to be embedded
    into the superprojects git directory.

    Signed-off-by: Stefan Beller <sbeller@google.com>

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4beeda5..4f31100 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1076,6 +1076,21 @@ static int resolve_remote_submodule_branch(int
argc, const char **argv,
        return 0;
 }

+static int intern_git_dir(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+
+       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+               return 1;
+
+       for (i = 0; i < list.nr; i++)
+               migrate_submodule_gitdir(list.entries[i]->name);
+
+       return 0;
+}
+
 struct cmd_struct {
        const char *cmd;
        int (*fn)(int, const char **, const char *);
@@ -1090,7 +1105,8 @@ static struct cmd_struct commands[] = {
        {"resolve-relative-url", resolve_relative_url},
        {"resolve-relative-url-test", resolve_relative_url_test},
        {"init", module_init},
-       {"remote-branch", resolve_remote_submodule_branch}
+       {"remote-branch", resolve_remote_submodule_branch},
+       {"intern-git-dir", intern_git_dir}
 };

 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/submodule.c b/submodule.c
index 45b9060..e513bba 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1335,3 +1335,42 @@ void prepare_submodule_repo_env(struct argv_array *out)
        }
        argv_array_push(out, "GIT_DIR=.git");
 }
+
+/*
+ * Migrate the given submodule (and all its submodules recursively) from
+ * having its git directory within the working tree to the git dir nested
+ * in its superprojects git dir under modules/.
+ */
+void migrate_submodule_gitdir(const char *path)
+{
+       char *old_git_dir;
+       const char *new_git_dir;
+       const struct submodule *sub;
+
+       struct child_process cp = CHILD_PROCESS_INIT;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.dir = path;
+       argv_array_pushl(&cp.args, "submodule", "foreach", "--recursive",
+                       "git", "submodule--helper" "intern-git-dir", NULL);
+
+       if (run_command(&cp))
+               die(_("Could not migrate git directory in submodule '%s'"),
+                   path);
+
+       old_git_dir = xstrfmt("%s/.git", path);
+       if (read_gitfile(old_git_dir))
+               /* If it is an actual gitfile, it doesn't need migration. */
+               goto out;
+
+       sub = submodule_from_path(null_sha1, path);
+       new_git_dir = git_common_path("modules/%s", sub->name);
+
+       if (rename(old_git_dir, new_git_dir) < 0)
+               die_errno(_("Could not migrate git directory from %s to %s"),
+                       old_git_dir, new_git_dir);
+
+       connect_work_tree_and_git_dir(path, new_git_dir);
+out:
+       free(old_git_dir);
+}
diff --git a/submodule.h b/submodule.h
index aac202c..143ec18 100644
--- a/submodule.h
+++ b/submodule.h
@@ -90,5 +90,6 @@ extern int parallel_submodules(void);
  * retaining any config in the environment.
  */
 extern void prepare_submodule_repo_env(struct argv_array *out);
+extern void migrate_submodule_gitdir(const char *path);

 #endif

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 09/16] update submodules: add scheduling to update submodules
  2016-11-16  0:02   ` Brandon Williams
  2016-11-16  0:07     ` David Turner
@ 2016-11-18  0:28     ` Stefan Beller
  1 sibling, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-18  0:28 UTC (permalink / raw)
  To: Brandon Williams
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On Tue, Nov 15, 2016 at 4:02 PM, Brandon Williams <bmwill@google.com> wrote:
> On 11/15, Stefan Beller wrote:
>> +
>> +     child_process_clear(&cp);
>> +     return 0;
>> +}
>
> If run command is successful then it handles the clearing of the child
> process struct, correct?  Is there a negative to having all the explicit
> clears when the child was successful?

void child_process_clear(struct child_process *child)
{
    argv_array_clear(&child->args);
    argv_array_clear(&child->env_array);
}

I don't think so, as clearing empty arg arrays is a no op.

>> +#define SCHEDULED_SUBMODULES_INIT {NULL, NULL}
>
> I may not know enough about these types of initializors but that Init
> macro only has 2 entries while there are three entries in the struct
> itself.

Filled up to 3 to be explicit.

>
>> +
>> +int scheduled_submodules_nr, scheduled_submodules_alloc;
>
> Should these globals be static since they should be scoped to only this
> file?

Of course, done.

>
> nit: organization wise it makes more sense to me to have the
> 'update_submodule' helper function be located more closely to the
> 'update_submodules' function.
>

done


David wrote:
> In fact, only the first NULL is necessary; unspecified initializer entries in C default to zero.

as said above, I explicitly init all of them now.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-18  0:16           ` Stefan Beller
@ 2016-11-18 17:46             ` Brandon Williams
  2016-11-18 18:25               ` Stefan Beller
  0 siblings, 1 reply; 50+ messages in thread
From: Brandon Williams @ 2016-11-18 17:46 UTC (permalink / raw)
  To: Stefan Beller
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On 11/17, Stefan Beller wrote:
> On Thu, Nov 17, 2016 at 2:42 PM, Stefan Beller <sbeller@google.com> wrote:
> >
> > I think I'll just write this functionality in C and optionally expose
> > it via the submodule--helper,
> > such that the user facing git-submodule.sh only has to call that helper.
> 
> I think it will roughly look like this:
> (white space mangled)
> 
> 
> commit e72ef244c667920c874247aa32aa55845500aac8
> Author: Stefan Beller <sbeller@google.com>
> Date:   Thu Nov 17 16:14:46 2016 -0800
> 
>     submodule--helper: add intern-git-dir function
> 
>     When a submodule has its git dir inside the working dir, the submodule
>     support for checkout that we plan to add in a later patch will fail.
> 
>     Add functionality to migrate the git directory to be embedded
>     into the superprojects git directory.
> 
>     Signed-off-by: Stefan Beller <sbeller@google.com>
> 
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 4beeda5..4f31100 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1076,6 +1076,21 @@ static int resolve_remote_submodule_branch(int
> argc, const char **argv,
>         return 0;
>  }
> 
> +static int intern_git_dir(int argc, const char **argv, const char *prefix)
> +{
> +       int i;
> +       struct pathspec pathspec;
> +       struct module_list list = MODULE_LIST_INIT;
> +
> +       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
> +               return 1;
> +
> +       for (i = 0; i < list.nr; i++)
> +               migrate_submodule_gitdir(list.entries[i]->name);
> +
> +       return 0;
> +}
> +
>  struct cmd_struct {
>         const char *cmd;
>         int (*fn)(int, const char **, const char *);
> @@ -1090,7 +1105,8 @@ static struct cmd_struct commands[] = {
>         {"resolve-relative-url", resolve_relative_url},
>         {"resolve-relative-url-test", resolve_relative_url_test},
>         {"init", module_init},
> -       {"remote-branch", resolve_remote_submodule_branch}
> +       {"remote-branch", resolve_remote_submodule_branch},
> +       {"intern-git-dir", intern_git_dir}
>  };
> 
>  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
> diff --git a/submodule.c b/submodule.c
> index 45b9060..e513bba 100644
> --- a/submodule.c
> +++ b/submodule.c
> @@ -1335,3 +1335,42 @@ void prepare_submodule_repo_env(struct argv_array *out)
>         }
>         argv_array_push(out, "GIT_DIR=.git");
>  }
> +
> +/*
> + * Migrate the given submodule (and all its submodules recursively) from
> + * having its git directory within the working tree to the git dir nested
> + * in its superprojects git dir under modules/.
> + */
> +void migrate_submodule_gitdir(const char *path)
> +{
> +       char *old_git_dir;
> +       const char *new_git_dir;
> +       const struct submodule *sub;
> +
> +       struct child_process cp = CHILD_PROCESS_INIT;
> +       cp.git_cmd = 1;
> +       cp.no_stdin = 1;
> +       cp.dir = path;
> +       argv_array_pushl(&cp.args, "submodule", "foreach", "--recursive",
> +                       "git", "submodule--helper" "intern-git-dir", NULL);
> +
> +       if (run_command(&cp))
> +               die(_("Could not migrate git directory in submodule '%s'"),
> +                   path);
> +
> +       old_git_dir = xstrfmt("%s/.git", path);
> +       if (read_gitfile(old_git_dir))
> +               /* If it is an actual gitfile, it doesn't need migration. */
> +               goto out;
> +
> +       sub = submodule_from_path(null_sha1, path);

This should probably be checked to see if sub not NULL before
dereferencing it right?

> +       new_git_dir = git_common_path("modules/%s", sub->name);
> +
> +       if (rename(old_git_dir, new_git_dir) < 0)
> +               die_errno(_("Could not migrate git directory from %s to %s"),
> +                       old_git_dir, new_git_dir);
> +
> +       connect_work_tree_and_git_dir(path, new_git_dir);
> +out:
> +       free(old_git_dir);
> +}
> diff --git a/submodule.h b/submodule.h
> index aac202c..143ec18 100644
> --- a/submodule.h
> +++ b/submodule.h
> @@ -90,5 +90,6 @@ extern int parallel_submodules(void);
>   * retaining any config in the environment.
>   */
>  extern void prepare_submodule_repo_env(struct argv_array *out);
> +extern void migrate_submodule_gitdir(const char *path);
> 
>  #endif

-- 
Brandon Williams

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 08/16] update submodules: add depopulate_submodule
  2016-11-18 17:46             ` Brandon Williams
@ 2016-11-18 18:25               ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-18 18:25 UTC (permalink / raw)
  To: Brandon Williams
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On Fri, Nov 18, 2016 at 9:46 AM, Brandon Williams <bmwill@google.com> wrote:
>> +
>> +       sub = submodule_from_path(null_sha1, path);
>
> This should probably be checked to see if sub not NULL before
> dereferencing it right?

yeah, will fix.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 11/16] teach unpack_trees() to remove submodule contents
       [not found]   ` <20161117133538.GF39230@book.hvoigt.net>
@ 2016-11-18 19:25     ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-18 19:25 UTC (permalink / raw)
  To: Heiko Voigt
  Cc: git@vger.kernel.org, Brandon Williams, Junio C Hamano,
	Jonathan Nieder, Martin Fick, David Turner

On Thu, Nov 17, 2016 at 5:35 AM, Heiko Voigt <hvoigt@hvoigt.net> wrote:
> On Tue, Nov 15, 2016 at 03:06:46PM -0800, Stefan Beller wrote:
>> Extend rmdir_or_warn() to remove the directories of those submodules which
>> are scheduled for removal. Also teach verify_clean_submodule() to check
>> that a submodule configured to be removed is not modified before scheduling
>> it for removal.
>>
>> Signed-off-by: Stefan Beller <sbeller@google.com>
>> ---
> [...]
>> --- a/wrapper.c
>> +++ b/wrapper.c
>> @@ -2,6 +2,7 @@
>>   * Various trivial helper wrappers around standard functions
>>   */
>>  #include "cache.h"
>> +#include "submodule.h"
>>
>>  static void do_nothing(size_t size)
>>  {
>> @@ -592,6 +593,9 @@ int unlink_or_warn(const char *file)
>>
>>  int rmdir_or_warn(const char *file)
>>  {
>> +     if (submodule_is_interesting(file, null_sha1)
>> +         && depopulate_submodule(file))
>> +             return -1;
>
> How about untracked files inside submodules? Should we not care about
> them? With this code the entire directory will be removed. In general
> git would not remove a directory with untracked files in it even though
> it is removed in the database. I wonder if we should imitate that here
> and just remove files that are tracked by git so that users do not loose
> files that might be precious to them.

Well from the superprojects perspective a submodule looks like a file,
so if the checkout involved removing a file it had to either be clean or the
checkout failed. So I though we'd go with a similar way here?

We need to stop when untracked files are present, I am not sure if we need
to care if the files are ignored, though.

In this version of the series, the test added in patch 14, testing for
treating untracked files properly is a failing test. So I agree that we should
not just delete them; I'll fix that in the next version of the series.

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/16] submodule: teach unpack_trees() to update submodules
  2016-11-16  0:22   ` David Turner
@ 2016-11-18 23:33     ` Stefan Beller
  2016-11-21 18:12       ` David Turner
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Beller @ 2016-11-18 23:33 UTC (permalink / raw)
  To: David Turner
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com

On Tue, Nov 15, 2016 at 4:22 PM, David Turner <David.Turner@twosigma.com> wrote:
>>       msgs[ERROR_NOT_UPTODATE_DIR] =
>>               _("Updating the following directories would lose untracked
>> files in it:\n%s");
>> +     msgs[ERROR_NOT_UPTODATE_SUBMODULE] =
>> +             _("Updating the following submodules would lose modifications
>> in
>> +it:\n%s");
>
> s/it/them/

done, also fixed the existing ERROR_NOT_UPTODATE_DIR.

>> +             if (!S_ISGITLINK(ce->ce_mode)) {
>
> I generally prefer to avoid if (!x) { A } else { B } -- I would rather just see if (x) { B } else { A }.

done.

>> +                             if (submodule_is_interesting(old->name, null_sha1)
>> +                                 && ok_to_remove_submodule(old->name))
>> +                                     return 0;
>> +                     }
>
> Do we need a return 1 in here somewhere?  Because otherwise, we fall through and return 0 later.

Otherwise we would fall through and run

    if (errno == ENOENT)
        return 0;
    return o->gently ? -1 :
        add_rejected_path(o, error_type, ce->name);

which produces different results than 0?

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/16] submodule: teach unpack_trees() to update submodules
  2016-11-16  0:25   ` Brandon Williams
@ 2016-11-18 23:39     ` Stefan Beller
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Beller @ 2016-11-18 23:39 UTC (permalink / raw)
  To: Brandon Williams
  Cc: git@vger.kernel.org, Junio C Hamano, Jonathan Nieder, Martin Fick,
	David Turner

On Tue, Nov 15, 2016 at 4:25 PM, Brandon Williams <bmwill@google.com> wrote:
> On 11/15, Stefan Beller wrote:
>> +                     int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
>
> For readability you may want to have spaces between the two flags

done

>
>> +     if (o->index_only
>> +         || (!((old->ce_flags & CE_VALID) || ce_skip_worktree(old))
>> +             && (o->reset || ce_uptodate(old))))
>> +             return 0;
>
> The coding guidelines say that git prefers to have the logical operators
> on the right side like this:

fixed all the coding style issues.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* RE: [PATCH 13/16] submodule: teach unpack_trees() to update submodules
  2016-11-18 23:33     ` Stefan Beller
@ 2016-11-21 18:12       ` David Turner
  0 siblings, 0 replies; 50+ messages in thread
From: David Turner @ 2016-11-21 18:12 UTC (permalink / raw)
  To: 'Stefan Beller'
  Cc: git@vger.kernel.org, bmwill@google.com, gitster@pobox.com,
	jrnieder@gmail.com, mogulguy10@gmail.com



> -----Original Message-----
> From: Stefan Beller [mailto:sbeller@google.com]

> >> +                             if (submodule_is_interesting(old->name,
> null_sha1)
> >> +                                 && ok_to_remove_submodule(old->name))
> >> +                                     return 0;
> >> +                     }
> >
> > Do we need a return 1 in here somewhere?  Because otherwise, we fall
> through and return 0 later.
> 
> Otherwise we would fall through and run
> 
>     if (errno == ENOENT)
>         return 0;
>     return o->gently ? -1 :
>         add_rejected_path(o, error_type, ce->name);
> 
> which produces different results than 0?

Oh, I see.  I was misreading that errno check.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC PATCH 00/16] Checkout aware of Submodules!
  2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
                   ` (15 preceding siblings ...)
  2016-11-15 23:06 ` [PATCH 16/16] checkout: add config option to recurse into submodules by default Stefan Beller
@ 2016-12-03  6:13 ` Xiaodong Qi
  16 siblings, 0 replies; 50+ messages in thread
From: Xiaodong Qi @ 2016-12-03  6:13 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, bmwill, gitster, jrnieder, mogulguy10, David.Turner

I found this patch on Reddit and personally support this idea to
simplify the submodule update and checkout process. I don't know how
other users handle the submodule update process, I do sometimes forget
to checkout in superprojects with submodules and get a lot of trouble in
using the submodule function. The patch seems aims to making the process
easier than before, although I am not qualified to review the code in
detail. I suggest experts in this area to review the code promptly and
work out the nuts and bolts toward the goal of this patch. Thank you for
listening.

Regards,
Xiaodong Qi

On 11/15/2016 04:06 PM, Stefan Beller wrote:
> When working with submodules, nearly anytime after checking out 
> a different state of the projects, that has submodules changed
> you'd run "git submodule update" with a current version of Git.
> 
> There are two problems with this approach:
> 
> * The "submodule update" command is dangerous as it
>   doesn't check for work that may be lost in the submodule
>   (e.g. a dangling commit).
> * you may forget to run the command as checkout is supposed
>   to do all the work for you.
> 
> Integrate updating the submodules into git checkout, with the same
> safety promises that git-checkout has, i.e. not throw away data unless
> asked to. This is done by first checking if the submodule is at the same
> sha1 as it is recorded in the superproject. If there are changes we stop
> proceeding the checkout just like it is when checking out a file that
> has local changes.
> 
> The integration happens in the code that is also used in other commands
> such that it will be easier in the future to make other commands aware
> of submodule.
> 
> This also solves d/f conflicts in case you replace a file/directory
> with a submodule or vice versa.
> 
> The patches are still a bit rough, but the overall series seems
> promising enough to me that I want to put it out here.
> 
> Any review, specifically on the design level welcome!
> 
> Thanks,
> Stefan
> 
> Stefan Beller (16):
>   submodule.h: add extern keyword to functions, break line before 80
>   submodule: modernize ok_to_remove_submodule to use argv_array
>   submodule: use absolute path for computing relative path connecting
>   update submodules: add is_submodule_populated
>   update submodules: add submodule config parsing
>   update submodules: add a config option to determine if submodules are
>     updated
>   update submodules: introduce submodule_is_interesting
>   update submodules: add depopulate_submodule
>   update submodules: add scheduling to update submodules
>   update submodules: is_submodule_checkout_safe
>   teach unpack_trees() to remove submodule contents
>   entry: write_entry to write populate submodules
>   submodule: teach unpack_trees() to update submodules
>   checkout: recurse into submodules if asked to
>   completion: add '--recurse-submodules' to checkout
>   checkout: add config option to recurse into submodules by default
> 
>  Documentation/config.txt               |   6 +
>  Documentation/git-checkout.txt         |   8 +
>  builtin/checkout.c                     |  31 ++-
>  cache.h                                |   2 +
>  contrib/completion/git-completion.bash |   2 +-
>  entry.c                                |  17 +-
>  submodule-config.c                     |  22 +++
>  submodule-config.h                     |  17 +-
>  submodule.c                            | 246 +++++++++++++++++++++--
>  submodule.h                            |  77 +++++---
>  t/lib-submodule-update.sh              |  10 +-
>  t/t2013-checkout-submodule.sh          | 344 ++++++++++++++++++++++++++++++++-
>  t/t9902-completion.sh                  |   1 +
>  unpack-trees.c                         | 103 ++++++++--
>  unpack-trees.h                         |   1 +
>  wrapper.c                              |   4 +
>  16 files changed, 806 insertions(+), 85 deletions(-)
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2016-12-03  6:13 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-15 23:06 [RFC PATCH 00/16] Checkout aware of Submodules! Stefan Beller
2016-11-15 23:06 ` [PATCH 01/16] submodule.h: add extern keyword to functions, break line before 80 Stefan Beller
2016-11-16 19:08   ` Junio C Hamano
2016-11-17 18:29     ` Stefan Beller
2016-11-15 23:06 ` [PATCH 02/16] submodule: modernize ok_to_remove_submodule to use argv_array Stefan Beller
2016-11-15 23:11   ` David Turner
2016-11-16 19:03     ` Junio C Hamano
2016-11-17 18:36       ` Stefan Beller
2016-11-15 23:06 ` [PATCH 03/16] submodule: use absolute path for computing relative path connecting Stefan Beller
2016-11-15 23:06 ` [PATCH 04/16] update submodules: add is_submodule_populated Stefan Beller
2016-11-15 23:20   ` Brandon Williams
2016-11-15 23:06 ` [PATCH 05/16] update submodules: add submodule config parsing Stefan Beller
2016-11-15 23:06 ` [PATCH 06/16] update submodules: add a config option to determine if submodules are updated Stefan Beller
2016-11-15 23:06 ` [PATCH 07/16] update submodules: introduce submodule_is_interesting Stefan Beller
2016-11-15 23:34   ` Brandon Williams
2016-11-16  0:14   ` David Turner
2016-11-17 20:03     ` Stefan Beller
     [not found]   ` <20161117105715.GC39230@book.hvoigt.net>
2016-11-17 20:08     ` Stefan Beller
2016-11-15 23:06 ` [PATCH 08/16] update submodules: add depopulate_submodule Stefan Beller
2016-11-15 23:44   ` Brandon Williams
2016-11-17 22:23     ` Stefan Beller
2016-11-17 22:29       ` Brandon Williams
2016-11-17 22:42         ` Stefan Beller
2016-11-18  0:16           ` Stefan Beller
2016-11-18 17:46             ` Brandon Williams
2016-11-18 18:25               ` Stefan Beller
     [not found]   ` <20161117111337.GD39230@book.hvoigt.net>
2016-11-17 22:28     ` Stefan Beller
2016-11-15 23:06 ` [PATCH 09/16] update submodules: add scheduling to update submodules Stefan Beller
2016-11-16  0:02   ` Brandon Williams
2016-11-16  0:07     ` David Turner
2016-11-18  0:28     ` Stefan Beller
2016-11-15 23:06 ` [PATCH 10/16] update submodules: is_submodule_checkout_safe Stefan Beller
2016-11-16  0:06   ` Brandon Williams
2016-11-15 23:06 ` [PATCH 11/16] teach unpack_trees() to remove submodule contents Stefan Beller
2016-11-16  0:14   ` Brandon Williams
     [not found]   ` <20161117133538.GF39230@book.hvoigt.net>
2016-11-18 19:25     ` Stefan Beller
2016-11-15 23:06 ` [PATCH 12/16] entry: write_entry to write populate submodules Stefan Beller
2016-11-15 23:06 ` [PATCH 13/16] submodule: teach unpack_trees() to update submodules Stefan Beller
2016-11-16  0:22   ` David Turner
2016-11-18 23:33     ` Stefan Beller
2016-11-21 18:12       ` David Turner
2016-11-16  0:25   ` Brandon Williams
2016-11-18 23:39     ` Stefan Beller
2016-11-15 23:06 ` [PATCH 14/16] checkout: recurse into submodules if asked to Stefan Beller
2016-11-16  0:33   ` Brandon Williams
2016-11-16 17:03   ` David Turner
2016-11-16 17:05   ` David Turner
2016-11-15 23:06 ` [PATCH 15/16] completion: add '--recurse-submodules' to checkout Stefan Beller
2016-11-15 23:06 ` [PATCH 16/16] checkout: add config option to recurse into submodules by default Stefan Beller
2016-12-03  6:13 ` [RFC PATCH 00/16] Checkout aware of Submodules! Xiaodong Qi

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).