mirror of
https://github.com/git/git
synced 2024-08-27 11:39:22 +00:00
![H.Merijn Brand](/assets/img/avatar_default.png)
POSIX says that exit status "0" means that "unset" successfully unset the variable. However, it is kind of ambiguous if an environment variable which was not set could be successfully unset. At least the default shell on HP-UX insists on reporting an error in such a case, so just ignore the exit status of "unset". [Dscho: extended the patch to git-submodule.sh, as Junio realized that this is the only other place where we check the exit status of "unset".] Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
365 lines
7 KiB
Bash
Executable file
365 lines
7 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# git-submodules.sh: add, init, update or list git submodules
|
|
#
|
|
# Copyright (c) 2007 Lars Hjemli
|
|
|
|
USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
|
|
OPTIONS_SPEC=
|
|
. git-sh-setup
|
|
require_work_tree
|
|
|
|
add=
|
|
branch=
|
|
init=
|
|
update=
|
|
status=
|
|
quiet=
|
|
cached=
|
|
|
|
#
|
|
# print stuff on stdout unless -q was specified
|
|
#
|
|
say()
|
|
{
|
|
if test -z "$quiet"
|
|
then
|
|
echo "$@"
|
|
fi
|
|
}
|
|
|
|
# NEEDSWORK: identical function exists in get_repo_base in clone.sh
|
|
get_repo_base() {
|
|
(
|
|
cd "`/bin/pwd`" &&
|
|
cd "$1" || cd "$1.git" &&
|
|
{
|
|
cd .git
|
|
pwd
|
|
}
|
|
) 2>/dev/null
|
|
}
|
|
|
|
# Resolve relative url by appending to parent's url
|
|
resolve_relative_url ()
|
|
{
|
|
branch="$(git symbolic-ref HEAD 2>/dev/null)"
|
|
remote="$(git config branch.${branch#refs/heads/}.remote)"
|
|
remote="${remote:-origin}"
|
|
remoteurl="$(git config remote.$remote.url)" ||
|
|
die "remote ($remote) does not have a url in .git/config"
|
|
url="$1"
|
|
while test -n "$url"
|
|
do
|
|
case "$url" in
|
|
../*)
|
|
url="${url#../}"
|
|
remoteurl="${remoteurl%/*}"
|
|
;;
|
|
./*)
|
|
url="${url#./}"
|
|
;;
|
|
*)
|
|
break;;
|
|
esac
|
|
done
|
|
echo "$remoteurl/$url"
|
|
}
|
|
|
|
#
|
|
# Map submodule path to submodule name
|
|
#
|
|
# $1 = path
|
|
#
|
|
module_name()
|
|
{
|
|
# Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
|
|
re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
|
|
name=$( GIT_CONFIG=.gitmodules \
|
|
git config --get-regexp '^submodule\..*\.path$' |
|
|
sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
|
|
test -z "$name" &&
|
|
die "No submodule mapping found in .gitmodules for path '$path'"
|
|
echo "$name"
|
|
}
|
|
|
|
#
|
|
# Clone a submodule
|
|
#
|
|
# Prior to calling, modules_update checks that a possibly existing
|
|
# path is not a git repository.
|
|
# Likewise, module_add checks that path does not exist at all,
|
|
# since it is the location of a new submodule.
|
|
#
|
|
module_clone()
|
|
{
|
|
path=$1
|
|
url=$2
|
|
|
|
# If there already is a directory at the submodule path,
|
|
# expect it to be empty (since that is the default checkout
|
|
# action) and try to remove it.
|
|
# Note: if $path is a symlink to a directory the test will
|
|
# succeed but the rmdir will fail. We might want to fix this.
|
|
if test -d "$path"
|
|
then
|
|
rmdir "$path" 2>/dev/null ||
|
|
die "Directory '$path' exist, but is neither empty nor a git repository"
|
|
fi
|
|
|
|
test -e "$path" &&
|
|
die "A file already exist at path '$path'"
|
|
|
|
git-clone -n "$url" "$path" ||
|
|
die "Clone of '$url' into submodule path '$path' failed"
|
|
}
|
|
|
|
#
|
|
# Add a new submodule to the working tree, .gitmodules and the index
|
|
#
|
|
# $@ = repo [path]
|
|
#
|
|
# optional branch is stored in global branch variable
|
|
#
|
|
module_add()
|
|
{
|
|
repo=$1
|
|
path=$2
|
|
|
|
if test -z "$repo"; then
|
|
usage
|
|
fi
|
|
|
|
case "$repo" in
|
|
./*|../*)
|
|
# dereference source url relative to parent's url
|
|
realrepo="$(resolve_relative_url $repo)" ;;
|
|
*)
|
|
# Turn the source into an absolute path if
|
|
# it is local
|
|
if base=$(get_repo_base "$repo"); then
|
|
repo="$base"
|
|
fi
|
|
realrepo=$repo
|
|
;;
|
|
esac
|
|
|
|
# Guess path from repo if not specified or strip trailing slashes
|
|
if test -z "$path"; then
|
|
path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
|
|
else
|
|
path=$(echo "$path" | sed -e 's|/*$||')
|
|
fi
|
|
|
|
test -e "$path" &&
|
|
die "'$path' already exists"
|
|
|
|
git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
|
|
die "'$path' already exists in the index"
|
|
|
|
module_clone "$path" "$realrepo" || exit
|
|
(unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
|
|
die "Unable to checkout submodule '$path'"
|
|
git add "$path" ||
|
|
die "Failed to add submodule '$path'"
|
|
|
|
GIT_CONFIG=.gitmodules git config submodule."$path".path "$path" &&
|
|
GIT_CONFIG=.gitmodules git config submodule."$path".url "$repo" &&
|
|
git add .gitmodules ||
|
|
die "Failed to register submodule '$path'"
|
|
}
|
|
|
|
#
|
|
# Register submodules in .git/config
|
|
#
|
|
# $@ = requested paths (default to all)
|
|
#
|
|
modules_init()
|
|
{
|
|
git ls-files --stage -- "$@" | grep -e '^160000 ' |
|
|
while read mode sha1 stage path
|
|
do
|
|
# Skip already registered paths
|
|
name=$(module_name "$path") || exit
|
|
url=$(git config submodule."$name".url)
|
|
test -z "$url" || continue
|
|
|
|
url=$(GIT_CONFIG=.gitmodules git config submodule."$name".url)
|
|
test -z "$url" &&
|
|
die "No url found for submodule path '$path' in .gitmodules"
|
|
|
|
# Possibly a url relative to parent
|
|
case "$url" in
|
|
./*|../*)
|
|
url="$(resolve_relative_url "$url")"
|
|
;;
|
|
esac
|
|
|
|
git config submodule."$name".url "$url" ||
|
|
die "Failed to register url for submodule path '$path'"
|
|
|
|
say "Submodule '$name' ($url) registered for path '$path'"
|
|
done
|
|
}
|
|
|
|
#
|
|
# Update each submodule path to correct revision, using clone and checkout as needed
|
|
#
|
|
# $@ = requested paths (default to all)
|
|
#
|
|
modules_update()
|
|
{
|
|
git ls-files --stage -- "$@" | grep -e '^160000 ' |
|
|
while read mode sha1 stage path
|
|
do
|
|
name=$(module_name "$path") || exit
|
|
url=$(git config submodule."$name".url)
|
|
if test -z "$url"
|
|
then
|
|
# Only mention uninitialized submodules when its
|
|
# path have been specified
|
|
test "$#" != "0" &&
|
|
say "Submodule path '$path' not initialized"
|
|
continue
|
|
fi
|
|
|
|
if ! test -d "$path"/.git
|
|
then
|
|
module_clone "$path" "$url" || exit
|
|
subsha1=
|
|
else
|
|
subsha1=$(unset GIT_DIR; cd "$path" &&
|
|
git rev-parse --verify HEAD) ||
|
|
die "Unable to find current revision in submodule path '$path'"
|
|
fi
|
|
|
|
if test "$subsha1" != "$sha1"
|
|
then
|
|
(unset GIT_DIR; cd "$path" && git-fetch &&
|
|
git-checkout -q "$sha1") ||
|
|
die "Unable to checkout '$sha1' in submodule path '$path'"
|
|
|
|
say "Submodule path '$path': checked out '$sha1'"
|
|
fi
|
|
done
|
|
}
|
|
|
|
set_name_rev () {
|
|
revname=$( (
|
|
unset GIT_DIR
|
|
cd "$1" && {
|
|
git describe "$2" 2>/dev/null ||
|
|
git describe --tags "$2" 2>/dev/null ||
|
|
git describe --contains --tags "$2"
|
|
}
|
|
) )
|
|
test -z "$revname" || revname=" ($revname)"
|
|
}
|
|
|
|
#
|
|
# List all submodules, prefixed with:
|
|
# - submodule not initialized
|
|
# + different revision checked out
|
|
#
|
|
# If --cached was specified the revision in the index will be printed
|
|
# instead of the currently checked out revision.
|
|
#
|
|
# $@ = requested paths (default to all)
|
|
#
|
|
modules_list()
|
|
{
|
|
git ls-files --stage -- "$@" | grep -e '^160000 ' |
|
|
while read mode sha1 stage path
|
|
do
|
|
name=$(module_name "$path") || exit
|
|
url=$(git config submodule."$name".url)
|
|
if test -z "url" || ! test -d "$path"/.git
|
|
then
|
|
say "-$sha1 $path"
|
|
continue;
|
|
fi
|
|
set_name_rev "$path" "$sha1"
|
|
if git diff-files --quiet -- "$path"
|
|
then
|
|
say " $sha1 $path$revname"
|
|
else
|
|
if test -z "$cached"
|
|
then
|
|
sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
|
|
set_name_rev "$path" "$sha1"
|
|
fi
|
|
say "+$sha1 $path$revname"
|
|
fi
|
|
done
|
|
}
|
|
|
|
while test $# != 0
|
|
do
|
|
case "$1" in
|
|
add)
|
|
add=1
|
|
;;
|
|
init)
|
|
init=1
|
|
;;
|
|
update)
|
|
update=1
|
|
;;
|
|
status)
|
|
status=1
|
|
;;
|
|
-q|--quiet)
|
|
quiet=1
|
|
;;
|
|
-b|--branch)
|
|
case "$2" in
|
|
'')
|
|
usage
|
|
;;
|
|
esac
|
|
branch="$2"; shift
|
|
;;
|
|
--cached)
|
|
cached=1
|
|
;;
|
|
--)
|
|
break
|
|
;;
|
|
-*)
|
|
usage
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
case "$add,$branch" in
|
|
1,*)
|
|
;;
|
|
,)
|
|
;;
|
|
,*)
|
|
usage
|
|
;;
|
|
esac
|
|
|
|
case "$add,$init,$update,$status,$cached" in
|
|
1,,,,)
|
|
module_add "$@"
|
|
;;
|
|
,1,,,)
|
|
modules_init "$@"
|
|
;;
|
|
,,1,,)
|
|
modules_update "$@"
|
|
;;
|
|
,,,*,*)
|
|
modules_list "$@"
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|