Skip to content

Commit

Permalink
Merge branch 'jl/submodule-deinit'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Mar 25, 2013
2 parents 4744b33 + cf41982 commit b03b41e
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 3 deletions.
4 changes: 4 additions & 0 deletions Documentation/git-rm.txt
Expand Up @@ -149,6 +149,10 @@ files that aren't ignored are present in the submodules work tree.
Ignored files are deemed expendable and won't stop a submodule's work Ignored files are deemed expendable and won't stop a submodule's work
tree from being removed. tree from being removed.


If you only want to remove the local checkout of a submodule from your
work tree without committing the removal,
use linkgit:git-submodule[1] `deinit` instead.

EXAMPLES EXAMPLES
-------- --------
`git rm Documentation/\*.txt`:: `git rm Documentation/\*.txt`::
Expand Down
18 changes: 17 additions & 1 deletion Documentation/git-submodule.txt
Expand Up @@ -13,6 +13,7 @@ SYNOPSIS
[--reference <repository>] [--] <repository> [<path>] [--reference <repository>] [--] <repository> [<path>]
'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...] 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...] 'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch] 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
[-f|--force] [--rebase] [--reference <repository>] [-f|--force] [--rebase] [--reference <repository>]
[--merge] [--recursive] [--] [<path>...] [--merge] [--recursive] [--] [<path>...]
Expand Down Expand Up @@ -135,6 +136,19 @@ init::
the explicit 'init' step if you do not intend to customize the explicit 'init' step if you do not intend to customize
any submodule locations. any submodule locations.


deinit::
Unregister the given submodules, i.e. remove the whole
`submodule.$name` section from .git/config together with their work
tree. Further calls to `git submodule update`, `git submodule foreach`
and `git submodule sync` will skip any unregistered submodules until
they are initialized again, so use this command if you don't want to
have a local checkout of the submodule in your work tree anymore. If
you really want to remove a submodule from the repository and commit
that use linkgit:git-rm[1] instead.
+
If `--force` is specified, the submodule's work tree will be removed even if
it contains local modifications.

update:: update::
Update the registered submodules, i.e. clone missing submodules and Update the registered submodules, i.e. clone missing submodules and
checkout the commit specified in the index of the containing repository. checkout the commit specified in the index of the containing repository.
Expand Down Expand Up @@ -214,8 +228,10 @@ OPTIONS


-f:: -f::
--force:: --force::
This option is only valid for add and update commands. This option is only valid for add, deinit and update commands.
When running add, allow adding an otherwise ignored submodule path. When running add, allow adding an otherwise ignored submodule path.
When running deinit the submodule work trees will be removed even if
they contain local changes.
When running update, throw away local changes in submodules when When running update, throw away local changes in submodules when
switching to a different commit; and always run a checkout operation switching to a different commit; and always run a checkout operation
in the submodule, even if the commit listed in the index of the in the submodule, even if the commit listed in the index of the
Expand Down
2 changes: 1 addition & 1 deletion contrib/completion/git-completion.bash
Expand Up @@ -2419,7 +2419,7 @@ _git_submodule ()
{ {
__git_has_doubledash && return __git_has_doubledash && return


local subcommands="add status init update summary foreach sync" local subcommands="add status init deinit update summary foreach sync"
if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
case "$cur" in case "$cur" in
--*) --*)
Expand Down
77 changes: 76 additions & 1 deletion git-submodule.sh
Expand Up @@ -8,6 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /')
USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>] USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...] or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...] or: $dashless [--quiet] init [--] [<path>...]
or: $dashless [--quiet] deinit [-f|--force] [--] <path>...
or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: $dashless [--quiet] foreach [--recursive] <command> or: $dashless [--quiet] foreach [--recursive] <command>
Expand Down Expand Up @@ -546,6 +547,80 @@ cmd_init()
done done
} }


#
# Unregister submodules from .git/config and remove their work tree
#
# $@ = requested paths (use '.' to deinit all submodules)
#
cmd_deinit()
{
# parse $args after "submodule ... deinit".
while test $# -ne 0
do
case "$1" in
-f|--force)
force=$1
;;
-q|--quiet)
GIT_QUIET=1
;;
--)
shift
break
;;
-*)
usage
;;
*)
break
;;
esac
shift
done

if test $# = 0
then
die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
fi

module_list "$@" |
while read mode sha1 stage sm_path
do
die_if_unmatched "$mode"
name=$(module_name "$sm_path") || exit

# Remove the submodule work tree (unless the user already did it)
if test -d "$sm_path"
then
# Protect submodules containing a .git directory
if test -d "$sm_path/.git"
then
echo >&2 "$(eval_gettext "Submodule work tree '\$sm_path' contains a .git directory")"
die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
fi

if test -z "$force"
then
git rm -n "$sm_path" ||
die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
fi
rm -rf "$sm_path" || say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
fi

mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"

# Remove the .git/config entries (unless the user already did it)
if test -n "$(git config --get-regexp submodule."$name\.")"
then
# Remove the whole section so we have a clean state when
# the user later decides to init this submodule again
url=$(git config submodule."$name".url)
git config --remove-section submodule."$name" 2>/dev/null &&
say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$sm_path'")"
fi
done
}

# #
# Update each submodule path to correct revision, using clone and checkout as needed # Update each submodule path to correct revision, using clone and checkout as needed
# #
Expand Down Expand Up @@ -1162,7 +1237,7 @@ cmd_sync()
while test $# != 0 && test -z "$command" while test $# != 0 && test -z "$command"
do do
case "$1" in case "$1" in
add | foreach | init | update | status | summary | sync) add | foreach | init | deinit | update | status | summary | sync)
command=$1 command=$1
;; ;;
-q|--quiet) -q|--quiet)
Expand Down
100 changes: 100 additions & 0 deletions t/t7400-submodule-basic.sh
Expand Up @@ -757,4 +757,104 @@ test_expect_success 'submodule add with an existing name fails unless forced' '
) )
' '


test_expect_success 'set up a second submodule' '
git submodule add ./init2 example2 &&
git commit -m "submodule example2 added"
'

test_expect_success 'submodule deinit should remove the whole submodule section from .git/config' '
git config submodule.example.foo bar &&
git config submodule.example2.frotz nitfol &&
git submodule deinit init &&
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 &&
git config submodule.example2.frotz nitfol &&
test_must_fail git submodule deinit &&
git submodule deinit . &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
rmdir init example2
'

test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' '
git submodule update --init &&
rm -rf init example2/* example2/.git &&
git submodule deinit init example2 &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
test -z "$(git config --get-regexp "submodule\.example2\.")" &&
rmdir init
'

test_expect_success 'submodule deinit fails when the submodule contains modifications unless forced' '
git submodule update --init &&
echo X >>init/s &&
test_must_fail git submodule deinit init &&
test -n "$(git config --get-regexp "submodule\.example\.")" &&
test -f example2/.git &&
git submodule deinit -f init &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
rmdir init
'

test_expect_success 'submodule deinit fails when the submodule contains untracked files unless forced' '
git submodule update --init &&
echo X >>init/untracked &&
test_must_fail git submodule deinit init &&
test -n "$(git config --get-regexp "submodule\.example\.")" &&
test -f example2/.git &&
git submodule deinit -f init &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
rmdir init
'

test_expect_success 'submodule deinit fails when the submodule HEAD does not match unless forced' '
git submodule update --init &&
(
cd init &&
git checkout HEAD^
) &&
test_must_fail git submodule deinit init &&
test -n "$(git config --get-regexp "submodule\.example\.")" &&
test -f example2/.git &&
git submodule deinit -f init &&
test -z "$(git config --get-regexp "submodule\.example\.")" &&
rmdir init
'

test_expect_success 'submodule deinit is silent when used on an uninitialized submodule' '
git submodule update --init &&
git submodule deinit init >actual &&
test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
git submodule deinit init >actual &&
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
git submodule deinit . >actual &&
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
git submodule deinit . >actual &&
test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
rmdir init example2
'

test_expect_success 'submodule deinit fails when submodule has a .git directory even when forced' '
git submodule update --init &&
(
cd init &&
rm .git &&
cp -R ../.git/modules/example .git &&
GIT_WORK_TREE=. git config --unset core.worktree
) &&
test_must_fail git submodule deinit init &&
test_must_fail git submodule deinit -f init &&
test -d init/.git &&
test -n "$(git config --get-regexp "submodule\.example\.")"
'

test_done test_done

0 comments on commit b03b41e

Please sign in to comment.