Commit 24d17de255caa57b01c0c758e6fc81aad493806e

Russell Belfer 2014-04-02T12:07:27

Make stash and checkout ignore contained repos To emulate git, stash should not remove untracked git repositories inside the parent repo, and checkout's REMOVE_UNTRACKED should also skip over these items. `git stash` actually prints a warning message for these items. That should be possible with a checkout notify callback if you wanted to, although it would require a bit of extra logic as things are at the moment.

diff --git a/src/checkout.c b/src/checkout.c
index 0b38522..04b2a66 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -259,6 +259,17 @@ static int checkout_action_no_wd(
 	return checkout_action_common(action, data, delta, NULL);
 }
 
+static bool wd_item_is_removable(git_iterator *iter, const git_index_entry *wd)
+{
+	git_buf *full = NULL;
+
+	if (wd->mode != GIT_FILEMODE_TREE)
+		return true;
+	if (git_iterator_current_workdir_path(&full, iter) < 0)
+		return true;
+	return !full || !git_path_contains(full, DOT_GIT);
+}
+
 static int checkout_action_wd_only(
 	checkout_data *data,
 	git_iterator *workdir,
@@ -307,11 +318,13 @@ static int checkout_action_wd_only(
 		/* found in index */;
 	else if (git_iterator_current_is_ignored(workdir)) {
 		notify = GIT_CHECKOUT_NOTIFY_IGNORED;
-		remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0);
+		remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0) &&
+			wd_item_is_removable(workdir, wd);
 	}
 	else {
 		notify = GIT_CHECKOUT_NOTIFY_UNTRACKED;
-		remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0);
+		remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0) &&
+			wd_item_is_removable(workdir, wd);
 	}
 
 	error = checkout_notify(data, notify, NULL, wd);
diff --git a/src/stash.c b/src/stash.c
index d20e29b..86e0a62 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -178,7 +178,8 @@ static int stash_update_index_from_diff(
 			break;
 
 		case GIT_DELTA_UNTRACKED:
-			if (data->include_untracked)
+			if (data->include_untracked &&
+				delta->new_file.mode != GIT_FILEMODE_TREE)
 				add_path = delta->new_file.path;
 			break;
 
diff --git a/tests/stash/save.c b/tests/stash/save.c
index f06c1fb..5165eea 100644
--- a/tests/stash/save.c
+++ b/tests/stash/save.c
@@ -342,7 +342,7 @@ void test_stash_save__can_stage_normal_then_stage_untracked(void)
 
 void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void)
 {
-	cl_git_pass(p_unlink("stash/when"));
+	cl_must_pass(p_unlink("stash/when"));
 
 	assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
 	assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
@@ -354,3 +354,18 @@ void test_stash_save__including_untracked_without_any_untracked_file_creates_an_
 
 	assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJ_TREE);
 }
+
+void test_stash_save__skip_submodules(void)
+{
+	git_repository *untracked_repo;
+	cl_git_pass(git_repository_init(&untracked_repo, "stash/untracked_repo", false));
+	cl_git_mkfile("stash/untracked_repo/content", "stuff");
+	git_repository_free(untracked_repo);
+
+	assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW);
+
+	cl_git_pass(git_stash_save(
+		&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+	assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW);
+}
diff --git a/tests/stash/stash_helpers.c b/tests/stash/stash_helpers.c
index 8b7d685..29cb25c 100644
--- a/tests/stash/stash_helpers.c
+++ b/tests/stash/stash_helpers.c
@@ -44,13 +44,10 @@ void assert_status(
 	unsigned int status;
 	int error;
 
-	error = git_status_file(&status, repo, path);
-
-	if (status_flags < 0) {
-		cl_assert_equal_i(status_flags, error);
-		return;
+	if (status_flags < 0)
+		cl_assert_equal_i(status_flags, git_status_file(&status, repo, path));
+	else {
+		cl_git_pass(git_status_file(&status, repo, path));
+		cl_assert_equal_i((unsigned int)status_flags, status);
 	}
-
-	cl_assert_equal_i(0, error);
-	cl_assert_equal_i((unsigned int)status_flags, status);
 }