git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCHv3 0/4] git-rm absorbs submodule git directory before deletion
@ 2016-12-14 22:40 Stefan Beller
  2016-12-14 22:40 ` [PATCHv3 1/4] submodule.h: add extern keyword to functions Stefan Beller
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Stefan Beller @ 2016-12-14 22:40 UTC (permalink / raw)
  To: gitster; +Cc: git, David.Turner, bmwill, sandals, Stefan Beller

v3:
* removed the patch to enhance ok_to_remove_submodule to absorb the submodule
  if needed
* Removed all the error reporting from git-rm that was related to submodule
  git directories not absorbed.
* instead just absorb the git repositories or let the absorb function die
  with an appropriate error message.

v2:
* new base where to apply the patch:
  sb/submodule-embed-gitdir merged with sb/t3600-cleanup.
  I got merge conflicts and resolved them this way:
#@@@ -709,9 -687,10 +687,9 @@@ test_expect_success 'checking out a com
#          git commit -m "submodule removal" submod &&
#          git checkout HEAD^ &&
#          git submodule update &&
#-         git checkout -q HEAD^ 2>actual &&
#+         git checkout -q HEAD^ &&
#          git checkout -q master 2>actual &&
# -        echo "warning: unable to rmdir submod: Directory not empty" >expected &&
# -        test_i18ncmp expected actual &&
# +        test_i18ngrep "^warning: unable to rmdir submod:" actual &&
#          git status -s submod >actual &&
#          echo "?? submod/" >expected &&
#          test_cmp expected actual &&
#

* improved commit message in "ok_to_remove_submodule: absorb the submodule git dir"
  (David Turner offered me some advice on how to write better English off list)
* simplified code in last patch:
  -> dropped wrong comment for fallthrough
  -> moved redundant code out of both bodies of an if-clause.
* Fixed last patchs commit message to have "or_die" instead of or_dir.

v1:
The "checkout --recurse-submodules" series got too large to comfortably send
it out for review, so I had to break it up into smaller series'; this is the
first subseries, but it makes sense on its own.

This series teaches git-rm to absorb the git directory of a submodule instead
of failing and complaining about the git directory preventing deletion.

It applies on origin/sb/submodule-embed-gitdir.

Any feedback welcome!

Thanks,
Stefan


Stefan Beller (4):
  submodule.h: add extern keyword to functions
  submodule: modernize ok_to_remove_submodule to use argv_array
  submodule: add flags to ok_to_remove_submodule
  rm: add absorb a submodules git dir before deletion

 builtin/rm.c  | 78 +++++++++++++++--------------------------------------------
 cache.h       |  2 ++
 entry.c       |  8 ++++++
 submodule.c   | 31 +++++++++++++++---------
 submodule.h   | 58 +++++++++++++++++++++++++-------------------
 t/t3600-rm.sh | 39 ++++++++++++------------------
 6 files changed, 97 insertions(+), 119 deletions(-)

-- 
2.11.0.rc2.35.g26e18c9


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

* [PATCHv3 1/4] submodule.h: add extern keyword to functions
  2016-12-14 22:40 [PATCHv3 0/4] git-rm absorbs submodule git directory before deletion Stefan Beller
@ 2016-12-14 22:40 ` Stefan Beller
  2016-12-14 22:40 ` [PATCHv3 2/4] submodule: modernize ok_to_remove_submodule to use argv_array Stefan Beller
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Stefan Beller @ 2016-12-14 22:40 UTC (permalink / raw)
  To: gitster; +Cc: git, David.Turner, bmwill, sandals, Stefan Beller

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.

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

diff --git a/submodule.h b/submodule.h
index 6229054b99..61fb610749 100644
--- a/submodule.h
+++ b/submodule.h
@@ -29,50 +29,55 @@ 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 *,
 		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 *, 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);
-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);
 
 #define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
 extern void absorb_git_dir_into_superproject(const char *prefix,
-- 
2.11.0.rc2.35.g26e18c9


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

* [PATCHv3 2/4] submodule: modernize ok_to_remove_submodule to use argv_array
  2016-12-14 22:40 [PATCHv3 0/4] git-rm absorbs submodule git directory before deletion Stefan Beller
  2016-12-14 22:40 ` [PATCHv3 1/4] submodule.h: add extern keyword to functions Stefan Beller
@ 2016-12-14 22:40 ` Stefan Beller
  2016-12-14 22:41 ` [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule Stefan Beller
  2016-12-14 22:41 ` [PATCHv3 4/4] rm: add absorb a submodules git dir before deletion Stefan Beller
  3 siblings, 0 replies; 8+ messages in thread
From: Stefan Beller @ 2016-12-14 22:40 UTC (permalink / raw)
  To: gitster; +Cc: git, David.Turner, bmwill, sandals, Stefan Beller

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

While at it, adapt the error messages to reflect the actual invocation.

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

diff --git a/submodule.c b/submodule.c
index 45ccfb7ab4..9f0b544ebe 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1023,13 +1023,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;
 
@@ -1039,14 +1032,15 @@ 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", "-u",
+				   "--ignore-submodules=none", NULL);
 	prepare_submodule_repo_env(&cp.env_array);
 	cp.git_cmd = 1;
 	cp.no_stdin = 1;
 	cp.out = -1;
 	cp.dir = path;
 	if (start_command(&cp))
-		die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path);
+		die(_("could not run 'git status --porcelain -u --ignore-submodules=none' in submodule %s"), path);
 
 	len = strbuf_read(&buf, cp.out, 1024);
 	if (len > 2)
@@ -1054,7 +1048,7 @@ int ok_to_remove_submodule(const char *path)
 	close(cp.out);
 
 	if (finish_command(&cp))
-		die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path);
+		die(_("'git status --porcelain -u --ignore-submodules=none' failed in submodule %s"), path);
 
 	strbuf_release(&buf);
 	return ok_to_remove;
-- 
2.11.0.rc2.35.g26e18c9


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

* [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule
  2016-12-14 22:40 [PATCHv3 0/4] git-rm absorbs submodule git directory before deletion Stefan Beller
  2016-12-14 22:40 ` [PATCHv3 1/4] submodule.h: add extern keyword to functions Stefan Beller
  2016-12-14 22:40 ` [PATCHv3 2/4] submodule: modernize ok_to_remove_submodule to use argv_array Stefan Beller
@ 2016-12-14 22:41 ` Stefan Beller
  2016-12-14 23:10   ` Brandon Williams
  2016-12-14 23:48   ` Junio C Hamano
  2016-12-14 22:41 ` [PATCHv3 4/4] rm: add absorb a submodules git dir before deletion Stefan Beller
  3 siblings, 2 replies; 8+ messages in thread
From: Stefan Beller @ 2016-12-14 22:41 UTC (permalink / raw)
  To: gitster; +Cc: git, David.Turner, bmwill, sandals, Stefan Beller

In different contexts the question whether deleting a submodule is ok
to remove may be answered differently.

In 293ab15eea (submodule: teach rm to remove submodules unless they
contain a git directory, 2012-09-26) a case was made that we can safely
ignore ignored untracked files for removal as we explicitely ask for the
removal of the submodule.

In a later patch we want to remove submodules even when the user doesn't
explicitly ask for it (e.g. checking out a tree-ish in which the submodule
doesn't exist).  In that case we want to be more careful when it comes
to deletion of untracked files. As of this patch it is unclear how this
will be implemented exactly, so we'll offer flags in which the caller
can specify how the different untracked files ought to be handled.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 builtin/rm.c |  3 ++-
 submodule.c  | 23 +++++++++++++++++++----
 submodule.h  |  5 ++++-
 3 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/builtin/rm.c b/builtin/rm.c
index 3f3e24eb36..fdd7183f61 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -187,7 +187,8 @@ static int check_local_mod(struct object_id *head, int index_only)
 		 */
 		if (ce_match_stat(ce, &st, 0) ||
 		    (S_ISGITLINK(ce->ce_mode) &&
-		     !ok_to_remove_submodule(ce->name)))
+		     !ok_to_remove_submodule(ce->name,
+				SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)))
 			local_changes = 1;
 
 		/*
diff --git a/submodule.c b/submodule.c
index 9f0b544ebe..2d13744b06 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1019,7 +1019,7 @@ int submodule_uses_gitfile(const char *path)
 	return 1;
 }
 
-int ok_to_remove_submodule(const char *path)
+int ok_to_remove_submodule(const char *path, unsigned flags)
 {
 	ssize_t len;
 	struct child_process cp = CHILD_PROCESS_INIT;
@@ -1032,15 +1032,27 @@ int ok_to_remove_submodule(const char *path)
 	if (!submodule_uses_gitfile(path))
 		return 0;
 
-	argv_array_pushl(&cp.args, "status", "--porcelain", "-u",
+	argv_array_pushl(&cp.args, "status", "--porcelain",
 				   "--ignore-submodules=none", NULL);
+
+	if (flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED)
+		argv_array_push(&cp.args, "-uno");
+	else
+		argv_array_push(&cp.args, "-uall");
+
+	if (!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))
+		argv_array_push(&cp.args, "--ignored");
+
 	prepare_submodule_repo_env(&cp.env_array);
 	cp.git_cmd = 1;
 	cp.no_stdin = 1;
 	cp.out = -1;
 	cp.dir = path;
 	if (start_command(&cp))
-		die(_("could not run 'git status --porcelain -u --ignore-submodules=none' in submodule %s"), path);
+		die(_("could not run 'git status --porcelain --ignore-submodules=none %s %s' in submodule '%s'"),
+			(flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED) ? "-uno" : "-uall",
+			(!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)) ? "--ignored" : "",
+			path);
 
 	len = strbuf_read(&buf, cp.out, 1024);
 	if (len > 2)
@@ -1048,7 +1060,10 @@ int ok_to_remove_submodule(const char *path)
 	close(cp.out);
 
 	if (finish_command(&cp))
-		die(_("'git status --porcelain -u --ignore-submodules=none' failed in submodule %s"), path);
+		die(_("'git status --porcelain --ignore-submodules=none %s %s' failed in submodule '%s'"),
+			(flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED) ? "-uno" : "-uall",
+			(!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)) ? "--ignored" : "",
+			path);
 
 	strbuf_release(&buf);
 	return ok_to_remove;
diff --git a/submodule.h b/submodule.h
index 61fb610749..3ed3aa479a 100644
--- a/submodule.h
+++ b/submodule.h
@@ -59,7 +59,10 @@ extern int fetch_populated_submodules(const struct argv_array *options,
 			       int quiet, int max_parallel_jobs);
 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);
+
+#define SUBMODULE_REMOVAL_IGNORE_UNTRACKED (1<<0)
+#define SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED (1<<1)
+extern int ok_to_remove_submodule(const char *path, unsigned flags);
 extern int merge_submodule(unsigned char result[20], const char *path,
 			   const unsigned char base[20],
 			   const unsigned char a[20],
-- 
2.11.0.rc2.35.g26e18c9


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

* [PATCHv3 4/4] rm: add absorb a submodules git dir before deletion
  2016-12-14 22:40 [PATCHv3 0/4] git-rm absorbs submodule git directory before deletion Stefan Beller
                   ` (2 preceding siblings ...)
  2016-12-14 22:41 ` [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule Stefan Beller
@ 2016-12-14 22:41 ` Stefan Beller
  2016-12-14 23:55   ` Junio C Hamano
  3 siblings, 1 reply; 8+ messages in thread
From: Stefan Beller @ 2016-12-14 22:41 UTC (permalink / raw)
  To: gitster; +Cc: git, David.Turner, bmwill, sandals, Stefan Beller

When deleting a submodule, we need to keep the actual git directory around,
such that we do not lose local changes in there and at a later checkout
of the submodule we don't need to clone it again.

Now that the functionality is available to absorb the git directory of a
submodule, rewrite the checking in git-rm to not complain, but rather
relocate the git directories inside the superproject.

An alternative solution was discussed to have a function
`depopulate_submodule`, that couples the check for its git directory and
possible relocation before the the removal, such that it is less likely to
miss the check in the future, but the indirection with such a function
added seemed also complex. The reason for that was that this possible
move of the git directory was also implemented in `ok_to_remove_submodule`,
such that this function could truthfully answer whether it is ok to remove
the submodule.

The solution proposed here defers all these checks to the caller.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 builtin/rm.c  | 75 ++++++++++++++---------------------------------------------
 cache.h       |  2 ++
 entry.c       |  8 +++++++
 t/t3600-rm.sh | 39 ++++++++++++-------------------
 4 files changed, 42 insertions(+), 82 deletions(-)

diff --git a/builtin/rm.c b/builtin/rm.c
index fdd7183f61..025ef4c735 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -59,27 +59,9 @@ static void print_error_files(struct string_list *files_list,
 	}
 }
 
-static void error_removing_concrete_submodules(struct string_list *files, int *errs)
-{
-	print_error_files(files,
-			  Q_("the following submodule (or one of its nested "
-			     "submodules)\n"
-			     "uses a .git directory:",
-			     "the following submodules (or one of their nested "
-			     "submodules)\n"
-			     "use a .git directory:", files->nr),
-			  _("\n(use 'rm -rf' if you really want to remove "
-			    "it including all of its history)"),
-			  errs);
-	string_list_clear(files, 0);
-}
-
-static int check_submodules_use_gitfiles(void)
+static void submodules_absorb_gitdir_if_needed(const char *prefix)
 {
 	int i;
-	int errs = 0;
-	struct string_list files = STRING_LIST_INIT_NODUP;
-
 	for (i = 0; i < list.nr; i++) {
 		const char *name = list.entry[i].name;
 		int pos;
@@ -99,12 +81,9 @@ static int check_submodules_use_gitfiles(void)
 			continue;
 
 		if (!submodule_uses_gitfile(name))
-			string_list_append(&files, name);
+			absorb_git_dir_into_superproject(prefix, name,
+				ABSORB_GITDIR_RECURSE_SUBMODULES);
 	}
-
-	error_removing_concrete_submodules(&files, &errs);
-
-	return errs;
 }
 
 static int check_local_mod(struct object_id *head, int index_only)
@@ -120,7 +99,6 @@ static int check_local_mod(struct object_id *head, int index_only)
 	int errs = 0;
 	struct string_list files_staged = STRING_LIST_INIT_NODUP;
 	struct string_list files_cached = STRING_LIST_INIT_NODUP;
-	struct string_list files_submodule = STRING_LIST_INIT_NODUP;
 	struct string_list files_local = STRING_LIST_INIT_NODUP;
 
 	no_head = is_null_oid(head);
@@ -218,13 +196,8 @@ static int check_local_mod(struct object_id *head, int index_only)
 		else if (!index_only) {
 			if (staged_changes)
 				string_list_append(&files_cached, name);
-			if (local_changes) {
-				if (S_ISGITLINK(ce->ce_mode) &&
-				    !submodule_uses_gitfile(name))
-					string_list_append(&files_submodule, name);
-				else
-					string_list_append(&files_local, name);
-			}
+			if (local_changes)
+				string_list_append(&files_local, name);
 		}
 	}
 	print_error_files(&files_staged,
@@ -246,8 +219,6 @@ static int check_local_mod(struct object_id *head, int index_only)
 			  &errs);
 	string_list_clear(&files_cached, 0);
 
-	error_removing_concrete_submodules(&files_submodule, &errs);
-
 	print_error_files(&files_local,
 			  Q_("the following file has local modifications:",
 			     "the following files have local modifications:",
@@ -341,6 +312,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 			exit(0);
 	}
 
+	submodules_absorb_gitdir_if_needed(prefix);
+
 	/*
 	 * If not forced, the file, the index and the HEAD (if exists)
 	 * must match; but the file can already been removed, since
@@ -357,9 +330,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 			oidclr(&oid);
 		if (check_local_mod(&oid, index_only))
 			exit(1);
-	} else if (!index_only) {
-		if (check_submodules_use_gitfiles())
-			exit(1);
 	}
 
 	/*
@@ -393,27 +363,16 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 			const char *path = list.entry[i].name;
 			if (list.entry[i].is_submodule) {
 				if (is_empty_dir(path)) {
-					if (!rmdir(path)) {
-						removed = 1;
-						if (!remove_path_from_gitmodules(path))
-							gitmodules_modified = 1;
-						continue;
-					}
-				} else {
-					strbuf_reset(&buf);
-					strbuf_addstr(&buf, path);
-					if (!remove_dir_recursively(&buf, 0)) {
-						removed = 1;
-						if (!remove_path_from_gitmodules(path))
-							gitmodules_modified = 1;
-						strbuf_release(&buf);
-						continue;
-					} else if (!file_exists(path))
-						/* Submodule was removed by user */
-						if (!remove_path_from_gitmodules(path))
-							gitmodules_modified = 1;
-					/* Fallthrough and let remove_path() fail. */
-				}
+					if (rmdir(path))
+						die_errno("git rm: '%s'", path);
+				} else if (file_exists(path))
+					/* non empty directory: */
+					remove_directory_or_die(path);
+
+				removed = 1;
+				if (!remove_path_from_gitmodules(path))
+					gitmodules_modified = 1;
+				continue;
 			}
 			if (!remove_path(path)) {
 				removed = 1;
diff --git a/cache.h b/cache.h
index a50a61a197..3a423af59b 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);
 
+extern void remove_directory_or_die(const char *path);
+
 #endif /* CACHE_H */
diff --git a/entry.c b/entry.c
index c6eea240b6..ddd4cfb2bf 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_directory_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/t/t3600-rm.sh b/t/t3600-rm.sh
index bcbb680651..5aa6db584c 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -569,26 +569,22 @@ test_expect_success 'rm of a conflicted unpopulated submodule succeeds' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rm of a populated submodule with a .git directory fails even when forced' '
+test_expect_success 'rm of a populated submodule with a .git directory migrates git dir' '
 	git checkout -f master &&
 	git reset --hard &&
 	git submodule update &&
 	(cd submod &&
 		rm .git &&
 		cp -R ../.git/modules/sub .git &&
-		GIT_WORK_TREE=. git config --unset core.worktree
+		GIT_WORK_TREE=. git config --unset core.worktree &&
+		rm -r ../.git/modules/sub
 	) &&
-	test_must_fail git rm submod &&
-	test -d submod &&
-	test -d submod/.git &&
-	git status -s -uno --ignore-submodules=none >actual &&
-	! test -s actual &&
-	test_must_fail git rm -f submod &&
-	test -d submod &&
-	test -d submod/.git &&
+	git rm submod 2>output.err &&
+	! test -d submod &&
+	! test -d submod/.git &&
 	git status -s -uno --ignore-submodules=none >actual &&
-	! test -s actual &&
-	rm -rf submod
+	test -s actual &&
+	test_i18ngrep Migrating output.err
 '
 
 cat >expect.deepmodified <<EOF
@@ -667,24 +663,19 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc
 	git submodule update --recursive &&
 	(cd submod/subsubmod &&
 		rm .git &&
-		cp -R ../../.git/modules/sub/modules/sub .git &&
+		mv ../../.git/modules/sub/modules/sub .git &&
 		GIT_WORK_TREE=. git config --unset core.worktree
 	) &&
-	test_must_fail git rm submod &&
-	test -d submod &&
-	test -d submod/subsubmod/.git &&
-	git status -s -uno --ignore-submodules=none >actual &&
-	! test -s actual &&
-	test_must_fail git rm -f submod &&
-	test -d submod &&
-	test -d submod/subsubmod/.git &&
+	git rm submod 2>output.err &&
+	! test -d submod &&
+	! test -d submod/subsubmod/.git &&
 	git status -s -uno --ignore-submodules=none >actual &&
-	! test -s actual &&
-	rm -rf submod
+	test -s actual &&
+	test_i18ngrep Migrating output.err
 '
 
 test_expect_success 'checking out a commit after submodule removal needs manual updates' '
-	git commit -m "submodule removal" submod &&
+	git commit -m "submodule removal" submod .gitmodules &&
 	git checkout HEAD^ &&
 	git submodule update &&
 	git checkout -q HEAD^ &&
-- 
2.11.0.rc2.35.g26e18c9


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

* Re: [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule
  2016-12-14 22:41 ` [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule Stefan Beller
@ 2016-12-14 23:10   ` Brandon Williams
  2016-12-14 23:48   ` Junio C Hamano
  1 sibling, 0 replies; 8+ messages in thread
From: Brandon Williams @ 2016-12-14 23:10 UTC (permalink / raw)
  To: Stefan Beller; +Cc: gitster, git, David.Turner, sandals

On 12/14, Stefan Beller wrote:
> In different contexts the question whether deleting a submodule is ok
> to remove may be answered differently.

This sentence is oddly worded.  Maybe this:

  In different context the question "Is it ok to delete a submodule?"
  may be answered differently.

-- 
Brandon Williams

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

* Re: [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule
  2016-12-14 22:41 ` [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule Stefan Beller
  2016-12-14 23:10   ` Brandon Williams
@ 2016-12-14 23:48   ` Junio C Hamano
  1 sibling, 0 replies; 8+ messages in thread
From: Junio C Hamano @ 2016-12-14 23:48 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, David.Turner, bmwill, sandals

Stefan Beller <sbeller@google.com> writes:

> diff --git a/submodule.c b/submodule.c
> index 9f0b544ebe..2d13744b06 100644
> --- a/submodule.c
> +++ b/submodule.c
> @@ -1019,7 +1019,7 @@ int submodule_uses_gitfile(const char *path)
>  	return 1;
>  }
>  
> -int ok_to_remove_submodule(const char *path)
> +int ok_to_remove_submodule(const char *path, unsigned flags)
>  {
>  	ssize_t len;
>  	struct child_process cp = CHILD_PROCESS_INIT;
> @@ -1032,15 +1032,27 @@ int ok_to_remove_submodule(const char *path)
>  	if (!submodule_uses_gitfile(path))
>  		return 0;
>  
> -	argv_array_pushl(&cp.args, "status", "--porcelain", "-u",
> +	argv_array_pushl(&cp.args, "status", "--porcelain",
>  				   "--ignore-submodules=none", NULL);
> +
> +	if (flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED)
> +		argv_array_push(&cp.args, "-uno");
> +	else
> +		argv_array_push(&cp.args, "-uall");
> +
> +	if (!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))
> +		argv_array_push(&cp.args, "--ignored");
> +

These "internal values to assemble command line" operations we
cannot avoid.  But things like this ...

>  	if (start_command(&cp))
> -		die(_("could not run 'git status --porcelain -u --ignore-submodules=none' in submodule %s"), path);
> +		die(_("could not run 'git status --porcelain --ignore-submodules=none %s %s' in submodule '%s'"),
> +			(flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED) ? "-uno" : "-uall",
> +			(!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)) ? "--ignored" : "",
> +			path);

and this ...

>  	if (finish_command(&cp))
> -		die(_("'git status --porcelain -u --ignore-submodules=none' failed in submodule %s"), path);
> +		die(_("'git status --porcelain --ignore-submodules=none %s %s' failed in submodule '%s'"),
> +			(flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED) ? "-uno" : "-uall",
> +			(!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)) ? "--ignored" : "",
> +			path);

makes me wonder if we want a helper that builds the string out of an
already assembled cp.args[] array, so that we won't have to do the
same thing twice/thrice and more importantly we won't have to worry
about these three going out of sync.


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

* Re: [PATCHv3 4/4] rm: add absorb a submodules git dir before deletion
  2016-12-14 22:41 ` [PATCHv3 4/4] rm: add absorb a submodules git dir before deletion Stefan Beller
@ 2016-12-14 23:55   ` Junio C Hamano
  0 siblings, 0 replies; 8+ messages in thread
From: Junio C Hamano @ 2016-12-14 23:55 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, David.Turner, bmwill, sandals

Stefan Beller <sbeller@google.com> writes:

>  			if (list.entry[i].is_submodule) {
>  				if (is_empty_dir(path)) {
> +					if (rmdir(path))
> +						die_errno("git rm: '%s'", path);
> +				} else if (file_exists(path))
> +					/* non empty directory: */

Lose colon?

> +					remove_directory_or_die(path);

... otherwise?  I.e.

				else
					???

If we are running "git rm -f <path>", then the path could be a
submodule in the index and on the filesystem, it could be (1)
already missing, as the user removed an empty submodule directory
she is not interested in, (2) a non-directory, e.g. a file or a
symbolic link, perhaps because she was trying to reorganize the
superproject working tree but decided against it, or (3) something
else?

(1) is perfectly OK; we end up with a result without the path, which
is what "git rm -f" wanted to do anyway.  I am not sure what should
happen in (2), and what other corner cases there are for (3), though.

And use of file_exists(path) in the above patch may trigger a
strange error message in case (2), as remove_directory_or_die()
would say "path is not a directory", to which the user will say "Yes
I know, I wanted you to remove it with 'git rm -f'".




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

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

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-14 22:40 [PATCHv3 0/4] git-rm absorbs submodule git directory before deletion Stefan Beller
2016-12-14 22:40 ` [PATCHv3 1/4] submodule.h: add extern keyword to functions Stefan Beller
2016-12-14 22:40 ` [PATCHv3 2/4] submodule: modernize ok_to_remove_submodule to use argv_array Stefan Beller
2016-12-14 22:41 ` [PATCHv3 3/4] submodule: add flags to ok_to_remove_submodule Stefan Beller
2016-12-14 23:10   ` Brandon Williams
2016-12-14 23:48   ` Junio C Hamano
2016-12-14 22:41 ` [PATCHv3 4/4] rm: add absorb a submodules git dir before deletion Stefan Beller
2016-12-14 23:55   ` Junio C Hamano

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).