Commit Diff


commit - 2a06fe5f59f1b7b42f63bd9afe5187935c341321
commit + 63c5ca5de411be54e75480b0efec04014ffab46e
blob - 45c8c5cc48c35aa3cd1e7e967be8cc112febeff7
blob + ffc4d0796f9263c15cc1a6ce02ca1ffbd707328f
--- got/got.1
+++ got/got.1
@@ -405,6 +405,7 @@ annotations:
 .It @ Ta entry is a symbolic link
 .It / Ta entry is a directory
 .It * Ta entry is an executable file
+.It $ Ta entry is a Git submodule
 .El
 .Pp
 If no
blob - 34e071967697fcabd8e050f2be3856f67a3cacee
blob + 77942a84ae2f6687f4c1b016ab97b7fcc7578feb
--- got/got.c
+++ got/got.c
@@ -2511,7 +2511,9 @@ print_entry(struct got_tree_entry *te, const char *id,
 	while (path[0] == '/')
 		path++;
 
-	if (S_ISLNK(te->mode))
+	if (got_object_tree_entry_is_submodule(te))
+		modestr = "$";
+	else if (S_ISLNK(te->mode))
 		modestr = "@";
 	else if (S_ISDIR(te->mode))
 		modestr = "/";
blob - 93db9af0036c3dc58da8d9f1c76664ddf7fde238
blob + 8063afe6c8c64dee5df429c78330e6af4c4deb3b
--- include/got_object.h
+++ include/got_object.h
@@ -186,6 +186,9 @@ const struct got_tree_entries *got_object_tree_get_ent
 const struct got_tree_entry *got_object_tree_find_entry(
     struct got_tree_object *, const char *);
 
+/* Return non-zero if the specified tree entry is a Git submodule. */
+int got_object_tree_entry_is_submodule(const struct got_tree_entry *);
+
 /*
  * Compare two trees and indicate whether the entry at the specified path
  * differs between them. The path must not be the root path "/"; the function
blob - 3c3c3397cc0c1896e190ac74c4ac314c5dc6e550
blob + e269983fad8691f00210e955a6383796011dcfc7
--- lib/diff.c
+++ lib/diff.c
@@ -497,6 +497,9 @@ diff_entry_old_new(const struct got_tree_entry *te1,
 {
 	const struct got_error *err = NULL;
 	int id_match;
+
+	if (got_object_tree_entry_is_submodule(te1))
+		return NULL;
 
 	if (te2 == NULL) {
 		if (S_ISDIR(te1->mode))
@@ -511,7 +514,8 @@ diff_entry_old_new(const struct got_tree_entry *te1,
 				    label1, NULL, repo);
 		}
 		return err;
-	}
+	} else if (got_object_tree_entry_is_submodule(te2))
+		return NULL;
 
 	id_match = (got_object_id_cmp(te1->id, te2->id) == 0);
 	if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) {
@@ -543,6 +547,9 @@ diff_entry_new_old(const struct got_tree_entry *te2,
     int diff_content)
 {
 	if (te1 != NULL) /* handled by diff_entry_old_new() */
+		return NULL;
+
+	if (got_object_tree_entry_is_submodule(te2))
 		return NULL;
 
 	if (S_ISDIR(te2->mode))
blob - 1f0d5de40d9e53338b3595c9b4cbdf20e30ad8a5
blob + 696ab23bd6d6225884a016c70864988c476f669f
--- lib/fileindex.c
+++ lib/fileindex.c
@@ -704,7 +704,7 @@ walk_tree(struct got_tree_entry **next, struct got_fil
 {
 	const struct got_error *err = NULL;
 
-	if (S_ISDIR(te->mode)) {
+	if (!got_object_tree_entry_is_submodule(te) && S_ISDIR(te->mode)) {
 		char *subpath;
 		struct got_tree_object *subtree;
 
@@ -756,7 +756,9 @@ diff_fileindex_tree(struct got_fileindex *fileindex,
 			free(te_path);
 			if (cmp == 0) {
 				if (got_path_is_child((*ie)->path, path,
-				    path_len) && (entry_name == NULL ||
+				    path_len) &&
+				    !got_object_tree_entry_is_submodule(te) &&
+				    (entry_name == NULL ||
 				    strcmp(te->name, entry_name) == 0)) {
 					err = cb->diff_old_new(cb_arg, *ie, te,
 					    path);
@@ -799,8 +801,9 @@ diff_fileindex_tree(struct got_fileindex *fileindex,
 			}
 			*ie = next;
 		} else if (te) {
-			if (entry_name == NULL ||
-			    strcmp(te->name, entry_name) == 0) {
+			if (!got_object_tree_entry_is_submodule(te) &&
+			    (entry_name == NULL ||
+			    strcmp(te->name, entry_name) == 0)) {
 				err = cb->diff_new(cb_arg, te, path);
 				if (err || entry_name)
 					break;
blob - 72be8b628623db608bfe5c38242bcd135e8425cd
blob + e5c043ce567c8eb883622b2a78b71bb9ba64227d
--- lib/object.c
+++ lib/object.c
@@ -1676,4 +1676,10 @@ done:
 		*new_te = NULL;
 	}
 	return err;
+}
+
+int
+got_object_tree_entry_is_submodule(const struct got_tree_entry *te)
+{
+	return (te->mode & S_IFMT) == (S_IFDIR | S_IFLNK);
 }
blob - 9a7c693b6fe913d70e282679c979cb7a7af850c3
blob + 8921d44e97b711564ee7ae00213598871b7242e3
--- lib/worktree.c
+++ lib/worktree.c
@@ -1426,6 +1426,9 @@ diff_new(void *arg, struct got_tree_entry *te, const c
 	if (a->cancel_cb && a->cancel_cb(a->cancel_arg))
 		return got_error(GOT_ERR_CANCELLED);
 
+	if (got_object_tree_entry_is_submodule(te))
+		return NULL;
+
 	if (asprintf(&path, "%s%s%s", parent_path,
 	    parent_path[0] ? "/" : "", te->name)
 	    == -1)
@@ -3834,6 +3837,17 @@ write_tree(struct got_object_id **new_tree_id,
 		base_entries = got_object_tree_get_entries(base_tree);
 		SIMPLEQ_FOREACH(te, &base_entries->head, entry) {
 			struct got_commitable *ct = NULL;
+
+			if (got_object_tree_entry_is_submodule(te)) {
+				/* Entry is a submodule; just copy it. */
+				err = got_object_tree_entry_dup(&new_te, te);
+				if (err)
+					goto done;
+				err = insert_tree_entry(new_te, &paths);
+				if (err)
+					goto done;
+				continue;
+			}
 
 			if (S_ISDIR(te->mode)) {
 				int modified;
blob - b967040ffd7b21b1c0cda99ba37b84c58b0a8cb3
blob + 76ad000185ee0b02fa1a80d4ee24110a2fab7191
--- regress/cmdline/checkout.sh
+++ regress/cmdline/checkout.sh
@@ -251,10 +251,55 @@ function test_checkout_tag {
 	fi
 	test_done "$testroot" "$ret"
 }
+
+function test_checkout_ignores_submodules {
+	local testroot=`test_init checkout_ignores_submodules`
+
+	(cd $testroot && git clone -q repo repo2 >/dev/null)
+	(cd $testroot/repo && git submodule -q add ../repo2)
+	(cd $testroot/repo && git commit -q -m 'adding submodule')
+
+	echo "A  $testroot/wt/.gitmodules" > $testroot/stdout.expected
+	echo "A  $testroot/wt/alpha" >> $testroot/stdout.expected
+	echo "A  $testroot/wt/beta" >> $testroot/stdout.expected
+	echo "A  $testroot/wt/epsilon/zeta" >> $testroot/stdout.expected
+	echo "A  $testroot/wt/gamma/delta" >> $testroot/stdout.expected
+	echo "Now shut up and hack" >> $testroot/stdout.expected
 
+	got checkout $testroot/repo $testroot/wt > $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "alpha" > $testroot/content.expected
+	echo "beta" >> $testroot/content.expected
+	echo "zeta" >> $testroot/content.expected
+	echo "delta" >> $testroot/content.expected
+	cat $testroot/wt/alpha $testroot/wt/beta $testroot/wt/epsilon/zeta \
+	    $testroot/wt/gamma/delta > $testroot/content
+
+	cmp -s $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_checkout_basic
 run_test test_checkout_dir_exists
 run_test test_checkout_dir_not_empty
 run_test test_checkout_sets_xbit
 run_test test_checkout_commit_from_wrong_branch
 run_test test_checkout_tag
+run_test test_checkout_ignores_submodules
blob - 46ebc3a92caf8f4523fa205630faacccc3fa263e
blob + 4af5ca7c1f56bf6f604dc53635ac15ce4a13edb1
--- tog/tog.1
+++ tog/tog.1
@@ -247,6 +247,7 @@ Displayed tree entries may carry one of the following 
 .It @ Ta entry is a symbolic link
 .It / Ta entry is a directory
 .It * Ta entry is an executable file
+.It $ Ta entry is a Git submodule
 .El
 .Pp
 The key bindings for
blob - d5a10ac995e03463cd40984b797f33f7f214f61d
blob + fbc6df447875424b0d5974612adb03ccbb793fdf
--- tog/tog.c
+++ tog/tog.c
@@ -3935,7 +3935,9 @@ draw_tree_entries(struct tog_view *view,
 				return got_error_from_errno(
 				    "got_object_id_str");
 		}
-		if (S_ISLNK(te->mode))
+		if (got_object_tree_entry_is_submodule(te))
+			modestr = "$";
+		else if (S_ISLNK(te->mode))
 			modestr = "@";
 		else if (S_ISDIR(te->mode))
 			modestr = "/";