Skip to content

Commit 091a6eb

Browse files
johnkeepinggitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 12b9d32 commit 091a6eb

6 files changed

+319
-35
lines changed

git-submodule.sh

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re
1414
or: $dashless [--quiet] foreach [--recursive] <command>
1515
or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
1616
OPTIONS_SPEC=
17+
SUBDIRECTORY_OK=Yes
1718
. git-sh-setup
1819
. git-sh-i18n
1920
. git-parse-remote
2021
require_work_tree
22+
wt_prefix=$(git rev-parse --show-prefix)
23+
cd_to_toplevel
2124

2225
command=
2326
branch=
@@ -106,12 +109,48 @@ resolve_relative_url ()
106109
echo "${is_relative:+${up_path}}${remoteurl#./}"
107110
}
108111

112+
# Resolve a path to be relative to another path. This is intended for
113+
# converting submodule paths when git-submodule is run in a subdirectory
114+
# and only handles paths where the directory separator is '/'.
115+
#
116+
# The output is the first argument as a path relative to the second argument,
117+
# which defaults to $wt_prefix if it is omitted.
118+
relative_path ()
119+
{
120+
local target curdir result
121+
target=$1
122+
curdir=${2-$wt_prefix}
123+
curdir=${curdir%/}
124+
result=
125+
126+
while test -n "$curdir"
127+
do
128+
case "$target" in
129+
"$curdir/"*)
130+
target=${target#"$curdir"/}
131+
break
132+
;;
133+
esac
134+
135+
result="${result}../"
136+
if test "$curdir" = "${curdir%/*}"
137+
then
138+
curdir=
139+
else
140+
curdir="${curdir%/*}"
141+
fi
142+
done
143+
144+
echo "$result$target"
145+
}
146+
109147
#
110148
# Get submodule info for registered submodules
111149
# $@ = path to limit submodule list
112150
#
113151
module_list()
114152
{
153+
eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
115154
(
116155
git ls-files --error-unmatch --stage -- "$@" ||
117156
echo "unmatched pathspec exists"
@@ -282,6 +321,7 @@ isnumber()
282321
cmd_add()
283322
{
284323
# parse $args after "submodule ... add".
324+
reference_path=
285325
while test $# -ne 0
286326
do
287327
case "$1" in
@@ -298,11 +338,11 @@ cmd_add()
298338
;;
299339
--reference)
300340
case "$2" in '') usage ;; esac
301-
reference="--reference=$2"
341+
reference_path=$2
302342
shift
303343
;;
304344
--reference=*)
305-
reference="$1"
345+
reference_path="${1#--reference=}"
306346
;;
307347
--name)
308348
case "$2" in '') usage ;; esac
@@ -323,6 +363,14 @@ cmd_add()
323363
shift
324364
done
325365

366+
if test -n "$reference_path"
367+
then
368+
is_absolute_path "$reference_path" ||
369+
reference_path="$wt_prefix$reference_path"
370+
371+
reference="--reference=$reference_path"
372+
fi
373+
326374
repo=$1
327375
sm_path=$2
328376

@@ -335,9 +383,14 @@ cmd_add()
335383
usage
336384
fi
337385

386+
is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
387+
338388
# assure repo is absolute or relative to parent
339389
case "$repo" in
340390
./*|../*)
391+
test -z "$wt_prefix" ||
392+
die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
393+
341394
# dereference source url relative to parent's url
342395
realrepo=$(resolve_relative_url "$repo") || exit
343396
;;
@@ -471,21 +524,23 @@ cmd_foreach()
471524
die_if_unmatched "$mode"
472525
if test -e "$sm_path"/.git
473526
then
474-
say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
527+
displaypath=$(relative_path "$sm_path")
528+
say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
475529
name=$(module_name "$sm_path")
476530
(
477531
prefix="$prefix$sm_path/"
478532
clear_local_git_env
479-
# we make $path available to scripts ...
480-
path=$sm_path
481533
cd "$sm_path" &&
534+
sm_path=$(relative_path "$sm_path") &&
535+
# we make $path available to scripts ...
536+
path=$sm_path &&
482537
eval "$@" &&
483538
if test -n "$recursive"
484539
then
485540
cmd_foreach "--recursive" "$@"
486541
fi
487542
) <&3 3<&- ||
488-
die "$(eval_gettext "Stopping at '\$prefix\$sm_path'; script returned non-zero status.")"
543+
die "$(eval_gettext "Stopping at '\$prefix\$displaypath'; script returned non-zero status.")"
489544
fi
490545
done
491546
}
@@ -524,12 +579,14 @@ cmd_init()
524579
die_if_unmatched "$mode"
525580
name=$(module_name "$sm_path") || exit
526581

582+
displaypath=$(relative_path "$sm_path")
583+
527584
# Copy url setting when it is not set yet
528585
if test -z "$(git config "submodule.$name.url")"
529586
then
530587
url=$(git config -f .gitmodules submodule."$name".url)
531588
test -z "$url" &&
532-
die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
589+
die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
533590

534591
# Possibly a url relative to parent
535592
case "$url" in
@@ -538,17 +595,17 @@ cmd_init()
538595
;;
539596
esac
540597
git config submodule."$name".url "$url" ||
541-
die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
598+
die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
542599

543-
say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
600+
say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
544601
fi
545602

546603
# Copy "update" setting when it is not set yet
547604
upd="$(git config -f .gitmodules submodule."$name".update)"
548605
test -z "$upd" ||
549606
test -n "$(git config submodule."$name".update)" ||
550607
git config submodule."$name".update "$upd" ||
551-
die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
608+
die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
552609
done
553610
}
554611

@@ -594,27 +651,29 @@ cmd_deinit()
594651
die_if_unmatched "$mode"
595652
name=$(module_name "$sm_path") || exit
596653

654+
displaypath=$(relative_path "$sm_path")
655+
597656
# Remove the submodule work tree (unless the user already did it)
598657
if test -d "$sm_path"
599658
then
600659
# Protect submodules containing a .git directory
601660
if test -d "$sm_path/.git"
602661
then
603-
echo >&2 "$(eval_gettext "Submodule work tree '\$sm_path' contains a .git directory")"
662+
echo >&2 "$(eval_gettext "Submodule work tree '\$displaypath' contains a .git directory")"
604663
die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
605664
fi
606665

607666
if test -z "$force"
608667
then
609668
git rm -qn "$sm_path" ||
610-
die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
669+
die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
611670
fi
612671
rm -rf "$sm_path" &&
613-
say "$(eval_gettext "Cleared directory '\$sm_path'")" ||
614-
say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
672+
say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
673+
say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
615674
fi
616675

617-
mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
676+
mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
618677

619678
# Remove the .git/config entries (unless the user already did it)
620679
if test -n "$(git config --get-regexp submodule."$name\.")"
@@ -623,7 +682,7 @@ cmd_deinit()
623682
# the user later decides to init this submodule again
624683
url=$(git config submodule."$name".url)
625684
git config --remove-section submodule."$name" 2>/dev/null &&
626-
say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$sm_path'")"
685+
say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
627686
fi
628687
done
629688
}
@@ -717,9 +776,11 @@ cmd_update()
717776
update_module=$(git config submodule."$name".update)
718777
fi
719778

779+
displaypath=$(relative_path "$prefix$sm_path")
780+
720781
if test "$update_module" = "none"
721782
then
722-
echo "Skipping submodule '$prefix$sm_path'"
783+
echo "Skipping submodule '$displaypath'"
723784
continue
724785
fi
725786

@@ -728,7 +789,7 @@ cmd_update()
728789
# Only mention uninitialized submodules when its
729790
# path have been specified
730791
test "$#" != "0" &&
731-
say "$(eval_gettext "Submodule path '\$prefix\$sm_path' not initialized
792+
say "$(eval_gettext "Submodule path '\$displaypath' not initialized
732793
Maybe you want to use 'update --init'?")"
733794
continue
734795
fi
@@ -741,7 +802,7 @@ Maybe you want to use 'update --init'?")"
741802
else
742803
subsha1=$(clear_local_git_env; cd "$sm_path" &&
743804
git rev-parse --verify HEAD) ||
744-
die "$(eval_gettext "Unable to find current revision in submodule path '\$prefix\$sm_path'")"
805+
die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
745806
fi
746807

747808
if test -n "$remote"
@@ -774,7 +835,7 @@ Maybe you want to use 'update --init'?")"
774835
(clear_local_git_env; cd "$sm_path" &&
775836
( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
776837
test -z "$rev") || git-fetch)) ||
777-
die "$(eval_gettext "Unable to fetch in submodule path '\$prefix\$sm_path'")"
838+
die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
778839
fi
779840

780841
# Is this something we just cloned?
@@ -788,20 +849,20 @@ Maybe you want to use 'update --init'?")"
788849
case "$update_module" in
789850
rebase)
790851
command="git rebase"
791-
die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$prefix\$sm_path'")"
792-
say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': rebased into '\$sha1'")"
852+
die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$displaypath'")"
853+
say_msg="$(eval_gettext "Submodule path '\$displaypath': rebased into '\$sha1'")"
793854
must_die_on_failure=yes
794855
;;
795856
merge)
796857
command="git merge"
797-
die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$prefix\$sm_path'")"
798-
say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': merged in '\$sha1'")"
858+
die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$displaypath'")"
859+
say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")"
799860
must_die_on_failure=yes
800861
;;
801862
*)
802863
command="git checkout $subforce -q"
803-
die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$prefix\$sm_path'")"
804-
say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': checked out '\$sha1'")"
864+
die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
865+
say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
805866
;;
806867
esac
807868

@@ -828,7 +889,7 @@ Maybe you want to use 'update --init'?")"
828889
res=$?
829890
if test $res -gt 0
830891
then
831-
die_msg="$(eval_gettext "Failed to recurse into submodule path '\$prefix\$sm_path'")"
892+
die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
832893
if test $res -eq 1
833894
then
834895
err="${err};$die_msg"
@@ -942,6 +1003,7 @@ cmd_summary() {
9421003
fi
9431004

9441005
cd_to_toplevel
1006+
eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
9451007
# Get modified modules cared by user
9461008
modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
9471009
sane_egrep '^:([0-7]* )?160000' |
@@ -991,16 +1053,18 @@ cmd_summary() {
9911053
! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
9921054
missing_dst=t
9931055

1056+
display_name=$(relative_path "$name")
1057+
9941058
total_commits=
9951059
case "$missing_src,$missing_dst" in
9961060
t,)
997-
errmsg="$(eval_gettext " Warn: \$name doesn't contain commit \$sha1_src")"
1061+
errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_src")"
9981062
;;
9991063
,t)
1000-
errmsg="$(eval_gettext " Warn: \$name doesn't contain commit \$sha1_dst")"
1064+
errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_dst")"
10011065
;;
10021066
t,t)
1003-
errmsg="$(eval_gettext " Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
1067+
errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
10041068
;;
10051069
*)
10061070
errmsg=
@@ -1029,12 +1093,12 @@ cmd_summary() {
10291093
submodule="$(gettext "submodule")"
10301094
if test $mod_dst = 160000
10311095
then
1032-
echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
1096+
echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
10331097
else
1034-
echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
1098+
echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
10351099
fi
10361100
else
1037-
echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
1101+
echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
10381102
fi
10391103
if test -n "$errmsg"
10401104
then
@@ -1118,7 +1182,7 @@ cmd_status()
11181182
die_if_unmatched "$mode"
11191183
name=$(module_name "$sm_path") || exit
11201184
url=$(git config submodule."$name".url)
1121-
displaypath="$prefix$sm_path"
1185+
displaypath=$(relative_path "$prefix$sm_path")
11221186
if test "$stage" = U
11231187
then
11241188
say "U$sha1 $displaypath"
@@ -1213,7 +1277,8 @@ cmd_sync()
12131277

12141278
if git config "submodule.$name.url" >/dev/null 2>/dev/null
12151279
then
1216-
say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")"
1280+
displaypath=$(relative_path "$prefix$sm_path")
1281+
say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
12171282
git config submodule."$name".url "$super_config_url"
12181283

12191284
if test -e "$sm_path"/.git

0 commit comments

Comments
 (0)