submodule: drop the top-level requirement

Use the new rev-parse --prefix option to process all paths given to the
submodule command, dropping the requirement that it be run from the
top-level of the repository.

Since the interpretation of a relative submodule URL depends on whether
or not "remote.origin.url" is configured, explicitly block relative URLs
in "git submodule add" when not at the top level of the working tree.

Signed-off-by: John Keeping <john@keeping.me.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index ff26535..a38fd92 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -212,6 +212,32 @@
 	test_cmp empty untracked
 '
 
+test_expect_success 'submodule add in subdirectory' '
+	echo "refs/heads/master" >expect &&
+	>empty &&
+
+	mkdir addtest/sub &&
+	(
+		cd addtest/sub &&
+		git submodule add "$submodurl" ../realsubmod3 &&
+		git submodule init
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/realsubmod3 ../.. &&
+	test_cmp expect heads &&
+	test_cmp expect head &&
+	test_cmp empty untracked
+'
+
+test_expect_success 'submodule add in subdirectory with relative path should fail' '
+	(
+		cd addtest/sub &&
+		test_must_fail git submodule add ../../ submod3 2>../../output.err
+	) &&
+	test_i18ngrep toplevel output.err
+'
+
 test_expect_success 'setup - add an example entry to .gitmodules' '
 	GIT_CONFIG=.gitmodules \
 	git config submodule.example.url git://example.com/init.git
@@ -319,6 +345,26 @@
 	grep "^ $rev1" list
 '
 
+test_expect_success 'status "up-to-date" from subdirectory' '
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule status >../list
+	) &&
+	grep "^ $rev1" list &&
+	grep "\\.\\./init" list
+'
+
+test_expect_success 'status "up-to-date" from subdirectory with path' '
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule status ../init >../list
+	) &&
+	grep "^ $rev1" list &&
+	grep "\\.\\./init" list
+'
+
 test_expect_success 'status should be "modified" after submodule commit' '
 	(
 		cd init &&
@@ -399,6 +445,25 @@
 	git rev-parse --resolve-git-dir init/.git
 '
 
+test_expect_success 'update --init from subdirectory' '
+	mv init init2 &&
+	git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+	git config --remove-section submodule.example &&
+	test_must_fail git config submodule.example.url &&
+
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule update ../init >update.out &&
+		cat update.out &&
+		test_i18ngrep "not initialized" update.out &&
+		test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
+
+		git submodule update --init ../init
+	) &&
+	git rev-parse --resolve-git-dir init/.git
+'
+
 test_expect_success 'do not add files from a submodule' '
 
 	git reset --hard &&
@@ -772,6 +837,21 @@
 	rmdir init
 '
 
+test_expect_success 'submodule deinit from subdirectory' '
+	git submodule update --init &&
+	git config submodule.example.foo bar &&
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule deinit ../init >../output
+	) &&
+	grep "\\.\\./init" output &&
+	test -z "$(git config --get-regexp "submodule\.example\.")" &&
+	test -n "$(git config --get-regexp "submodule\.example2\.")" &&
+	test -f example2/.git &&
+	rmdir init
+'
+
 test_expect_success 'submodule deinit . deinits all initialized submodules' '
 	git submodule update --init &&
 	git config submodule.example.foo bar &&
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index c328726..ac2434c 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -45,6 +45,42 @@
 	test_cmp expected actual
 "
 
+test_expect_success 'added submodule (subdirectory)' "
+	mkdir sub &&
+	(
+		cd sub &&
+		git submodule summary >../actual
+	) &&
+	cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory only)' "
+	(
+		cd sub &&
+		git submodule summary . >../actual
+	) &&
+	>expected &&
+	test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory with explicit path)' "
+	(
+		cd sub &&
+		git submodule summary ../sm1 >../actual
+	) &&
+	cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+	test_cmp expected actual
+"
+
 commit_file sm1 &&
 head2=$(add_file sm1 foo3)
 
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index bf90098..79bc135 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -61,6 +61,19 @@
 	)
 '
 
+reset_submodule_urls () {
+	local root
+	root=$(pwd) &&
+	(
+		cd super-clone/submodule &&
+		git config remote.origin.url "$root/submodule"
+	) &&
+	(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url "$root/submodule"
+	)
+}
+
 test_expect_success 'change submodule url' '
 	(
 		cd super &&
@@ -132,6 +145,65 @@
 	)
 '
 
+test_expect_success 'reset submodule URLs' '
+	reset_submodule_urls super-clone
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs - subdirectory' '
+	(
+		cd super-clone &&
+		git pull --no-recurse-submodules &&
+		mkdir -p sub &&
+		cd sub &&
+		git submodule sync >../../output
+	) &&
+	grep "\\.\\./submodule" output &&
+	test -d "$(
+		cd super-clone/submodule &&
+		git config remote.origin.url
+	)" &&
+	test ! -d "$(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url
+	)" &&
+	(
+		cd super-clone/submodule &&
+		git checkout master &&
+		git pull
+	) &&
+	(
+		cd super-clone &&
+		test -d "$(git config submodule.submodule.url)"
+	)
+'
+
+test_expect_success '"git submodule sync --recursive" should update all submodule URLs - subdirectory' '
+	(
+		cd super-clone &&
+		(
+			cd submodule &&
+			git pull --no-recurse-submodules
+		) &&
+		mkdir -p sub &&
+		cd sub &&
+		git submodule sync --recursive >../../output
+	) &&
+	grep "\\.\\./submodule/sub-submodule" output &&
+	test -d "$(
+		cd super-clone/submodule &&
+		git config remote.origin.url
+	)" &&
+	test -d "$(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url
+	)" &&
+	(
+		cd super-clone/submodule/sub-submodule &&
+		git checkout master &&
+		git pull
+	)
+'
+
 test_expect_success '"git submodule sync" should update known submodule URLs' '
 	(
 		cd empty-clone &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index a4ffea0..cdb0538 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -80,6 +80,21 @@
 	)
 '
 
+test_expect_success 'submodule update from subdirectory' '
+	(cd super/submodule &&
+	 git reset --hard HEAD~1
+	) &&
+	mkdir super/sub &&
+	(cd super/sub &&
+	 (cd ../submodule &&
+	  compare_head
+	 ) &&
+	 git submodule update ../submodule &&
+	 cd ../submodule &&
+	 ! compare_head
+	)
+'
+
 apos="'";
 test_expect_success 'submodule update does not fetch already present commits' '
 	(cd submodule &&
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index 107b4b7..91d4fd1 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -80,6 +80,22 @@
 	test_i18ncmp expect actual
 '
 
+cat >expect <<EOF
+Entering '../sub1'
+$pwd/clone-foo1-../sub1-$sub1sha1
+Entering '../sub3'
+$pwd/clone-foo3-../sub3-$sub3sha1
+EOF
+
+test_expect_success 'test "submodule foreach" from subdirectory' '
+	mkdir clone/sub &&
+	(
+		cd clone/sub &&
+		git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual
+	) &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'setup nested submodules' '
 	git clone submodule nested1 &&
 	git clone submodule nested2 &&