Skip to content

Commit b03b41e

Browse files
committed
Merge branch 'jl/submodule-deinit'
There was no Porcelain way to say "I no longer am interested in this submodule", once you express your interest in a submodule with "submodule init". "submodule deinit" is the way to do so. * jl/submodule-deinit: submodule: add 'deinit' command
2 parents 4744b33 + cf41982 commit b03b41e

File tree

5 files changed

+198
-3
lines changed

5 files changed

+198
-3
lines changed

Documentation/git-rm.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ files that aren't ignored are present in the submodules work tree.
149149
Ignored files are deemed expendable and won't stop a submodule's work
150150
tree from being removed.
151151

152+
If you only want to remove the local checkout of a submodule from your
153+
work tree without committing the removal,
154+
use linkgit:git-submodule[1] `deinit` instead.
155+
152156
EXAMPLES
153157
--------
154158
`git rm Documentation/\*.txt`::

Documentation/git-submodule.txt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ SYNOPSIS
1313
[--reference <repository>] [--] <repository> [<path>]
1414
'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
1515
'git submodule' [--quiet] init [--] [<path>...]
16+
'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
1617
'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
1718
[-f|--force] [--rebase] [--reference <repository>]
1819
[--merge] [--recursive] [--] [<path>...]
@@ -135,6 +136,19 @@ init::
135136
the explicit 'init' step if you do not intend to customize
136137
any submodule locations.
137138

139+
deinit::
140+
Unregister the given submodules, i.e. remove the whole
141+
`submodule.$name` section from .git/config together with their work
142+
tree. Further calls to `git submodule update`, `git submodule foreach`
143+
and `git submodule sync` will skip any unregistered submodules until
144+
they are initialized again, so use this command if you don't want to
145+
have a local checkout of the submodule in your work tree anymore. If
146+
you really want to remove a submodule from the repository and commit
147+
that use linkgit:git-rm[1] instead.
148+
+
149+
If `--force` is specified, the submodule's work tree will be removed even if
150+
it contains local modifications.
151+
138152
update::
139153
Update the registered submodules, i.e. clone missing submodules and
140154
checkout the commit specified in the index of the containing repository.
@@ -214,8 +228,10 @@ OPTIONS
214228

215229
-f::
216230
--force::
217-
This option is only valid for add and update commands.
231+
This option is only valid for add, deinit and update commands.
218232
When running add, allow adding an otherwise ignored submodule path.
233+
When running deinit the submodule work trees will be removed even if
234+
they contain local changes.
219235
When running update, throw away local changes in submodules when
220236
switching to a different commit; and always run a checkout operation
221237
in the submodule, even if the commit listed in the index of the

contrib/completion/git-completion.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2419,7 +2419,7 @@ _git_submodule ()
24192419
{
24202420
__git_has_doubledash && return
24212421

2422-
local subcommands="add status init update summary foreach sync"
2422+
local subcommands="add status init deinit update summary foreach sync"
24232423
if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
24242424
case "$cur" in
24252425
--*)

git-submodule.sh

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /')
88
USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
99
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
1010
or: $dashless [--quiet] init [--] [<path>...]
11+
or: $dashless [--quiet] deinit [-f|--force] [--] <path>...
1112
or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
1213
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
1314
or: $dashless [--quiet] foreach [--recursive] <command>
@@ -546,6 +547,80 @@ cmd_init()
546547
done
547548
}
548549

550+
#
551+
# Unregister submodules from .git/config and remove their work tree
552+
#
553+
# $@ = requested paths (use '.' to deinit all submodules)
554+
#
555+
cmd_deinit()
556+
{
557+
# parse $args after "submodule ... deinit".
558+
while test $# -ne 0
559+
do
560+
case "$1" in
561+
-f|--force)
562+
force=$1
563+
;;
564+
-q|--quiet)
565+
GIT_QUIET=1
566+
;;
567+
--)
568+
shift
569+
break
570+
;;
571+
-*)
572+
usage
573+
;;
574+
*)
575+
break
576+
;;
577+
esac
578+
shift
579+
done
580+
581+
if test $# = 0
582+
then
583+
die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
584+
fi
585+
586+
module_list "$@" |
587+
while read mode sha1 stage sm_path
588+
do
589+
die_if_unmatched "$mode"
590+
name=$(module_name "$sm_path") || exit
591+
592+
# Remove the submodule work tree (unless the user already did it)
593+
if test -d "$sm_path"
594+
then
595+
# Protect submodules containing a .git directory
596+
if test -d "$sm_path/.git"
597+
then
598+
echo >&2 "$(eval_gettext "Submodule work tree '\$sm_path' contains a .git directory")"
599+
die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
600+
fi
601+
602+
if test -z "$force"
603+
then
604+
git rm -n "$sm_path" ||
605+
die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
606+
fi
607+
rm -rf "$sm_path" || say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
608+
fi
609+
610+
mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
611+
612+
# Remove the .git/config entries (unless the user already did it)
613+
if test -n "$(git config --get-regexp submodule."$name\.")"
614+
then
615+
# Remove the whole section so we have a clean state when
616+
# the user later decides to init this submodule again
617+
url=$(git config submodule."$name".url)
618+
git config --remove-section submodule."$name" 2>/dev/null &&
619+
say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$sm_path'")"
620+
fi
621+
done
622+
}
623+
549624
#
550625
# Update each submodule path to correct revision, using clone and checkout as needed
551626
#
@@ -1162,7 +1237,7 @@ cmd_sync()
11621237
while test $# != 0 && test -z "$command"
11631238
do
11641239
case "$1" in
1165-
add | foreach | init | update | status | summary | sync)
1240+
add | foreach | init | deinit | update | status | summary | sync)
11661241
command=$1
11671242
;;
11681243
-q|--quiet)

t/t7400-submodule-basic.sh

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,4 +757,104 @@ test_expect_success 'submodule add with an existing name fails unless forced' '
757757
)
758758
'
759759

760+
test_expect_success 'set up a second submodule' '
761+
git submodule add ./init2 example2 &&
762+
git commit -m "submodule example2 added"
763+
'
764+
765+
test_expect_success 'submodule deinit should remove the whole submodule section from .git/config' '
766+
git config submodule.example.foo bar &&
767+
git config submodule.example2.frotz nitfol &&
768+
git submodule deinit init &&
769+
test -z "$(git config --get-regexp "submodule\.example\.")" &&
770+
test -n "$(git config --get-regexp "submodule\.example2\.")" &&
771+
test -f example2/.git &&
772+
rmdir init
773+
'
774+
775+
test_expect_success 'submodule deinit . deinits all initialized submodules' '
776+
git submodule update --init &&
777+
git config submodule.example.foo bar &&
778+
git config submodule.example2.frotz nitfol &&
779+
test_must_fail git submodule deinit &&
780+
git submodule deinit . &&
781+
test -z "$(git config --get-regexp "submodule\.example\.")" &&
782+
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
783+
rmdir init example2
784+
'
785+
786+
test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' '
787+
git submodule update --init &&
788+
rm -rf init example2/* example2/.git &&
789+
git submodule deinit init example2 &&
790+
test -z "$(git config --get-regexp "submodule\.example\.")" &&
791+
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
792+
rmdir init
793+
'
794+
795+
test_expect_success 'submodule deinit fails when the submodule contains modifications unless forced' '
796+
git submodule update --init &&
797+
echo X >>init/s &&
798+
test_must_fail git submodule deinit init &&
799+
test -n "$(git config --get-regexp "submodule\.example\.")" &&
800+
test -f example2/.git &&
801+
git submodule deinit -f init &&
802+
test -z "$(git config --get-regexp "submodule\.example\.")" &&
803+
rmdir init
804+
'
805+
806+
test_expect_success 'submodule deinit fails when the submodule contains untracked files unless forced' '
807+
git submodule update --init &&
808+
echo X >>init/untracked &&
809+
test_must_fail git submodule deinit init &&
810+
test -n "$(git config --get-regexp "submodule\.example\.")" &&
811+
test -f example2/.git &&
812+
git submodule deinit -f init &&
813+
test -z "$(git config --get-regexp "submodule\.example\.")" &&
814+
rmdir init
815+
'
816+
817+
test_expect_success 'submodule deinit fails when the submodule HEAD does not match unless forced' '
818+
git submodule update --init &&
819+
(
820+
cd init &&
821+
git checkout HEAD^
822+
) &&
823+
test_must_fail git submodule deinit init &&
824+
test -n "$(git config --get-regexp "submodule\.example\.")" &&
825+
test -f example2/.git &&
826+
git submodule deinit -f init &&
827+
test -z "$(git config --get-regexp "submodule\.example\.")" &&
828+
rmdir init
829+
'
830+
831+
test_expect_success 'submodule deinit is silent when used on an uninitialized submodule' '
832+
git submodule update --init &&
833+
git submodule deinit init >actual &&
834+
test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
835+
git submodule deinit init >actual &&
836+
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
837+
git submodule deinit . >actual &&
838+
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
839+
test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
840+
git submodule deinit . >actual &&
841+
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
842+
test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
843+
rmdir init example2
844+
'
845+
846+
test_expect_success 'submodule deinit fails when submodule has a .git directory even when forced' '
847+
git submodule update --init &&
848+
(
849+
cd init &&
850+
rm .git &&
851+
cp -R ../.git/modules/example .git &&
852+
GIT_WORK_TREE=. git config --unset core.worktree
853+
) &&
854+
test_must_fail git submodule deinit init &&
855+
test_must_fail git submodule deinit -f init &&
856+
test -d init/.git &&
857+
test -n "$(git config --get-regexp "submodule\.example\.")"
858+
'
859+
760860
test_done

0 commit comments

Comments
 (0)