Git - 子模块 HEAD 在 运行 更新后总是分离的?

Git - submodules HEAD is always detached after running update?

我在 .gitmodules 文件中有这个子模块配置:

[submodule "sub"]
    shallow = true
    branch = master
    path = sub
    url = https://path/to/repo.git

现在我想要当有人克隆我的 repo 然后 运行s 这些命令时:

git submodule init
git submodule update

是获取子模块的shallow master分支。但是发生的事情是它没有结帐到 master 分支。它总是会分离出来,所以我需要手动 运行 git checkout master。因此,除了这两个命令之外,用户还需要 运行 一个额外的命令。

我调查了这个:Why is my GIT Submodule HEAD detached from master?

但是关于已接受答案的任何建议似乎都没有帮助:我在 .gitmodules 文件中添加了我想要的分支,我添加了远程上游为主(这只适用于已经 cloned/updated 存储库之后我不得不结帐以掌握自己)。

如果有人克隆了我的存储库并想要设置子模块,这是否意味着总是分离 HEAD?

我仍在调查这个问题,但这是我想出并正在使用的脚本:

#! /bin/bash                                                                                                                                                                                 
# Written by Carlo Wood 2016                                                                                                                                                                 

echo "In \"$(pwd)\", entering [=10=] $*"                                                                                                                                                         

# This script should be run from the root of the parent project.                                                                                                                             
if ! test -e .git; then                                                                                                                                                                      
  echo "[=10=]: $(pwd) is not a git repository."                                                                                                                                                 
  exit 1                                                                                                                                                                                     
fi                                                                                                                                                                                           

# Parse command line parameters.                                                                                                                                                             
opt_init=                                                                                                                                                                                    
opt_recursive=                                                                                                                                                                               
do_foreach=0                                                                                                                                                                                 
initial_call=1                                                                                                                                                                               
while [[ $# -gt 0 ]]                                                                                                                                                                         
do                                                                                                                                                                                           
  case  in                                                                                                                                                                                 
    --init)                                                                                                                                                                                  
      opt_init=                                                                                                                                                                            
      ;;                                                                                                                                                                                     
    --recursive)                                                                                                                                                                             
      opt_recursive=                                                                                                                                                                       
      do_foreach=1                                                                                                                                                                           
      ;;                                                                                                                                                                                     
    --reentry)
      initial_call=0
      ;;
    --)
      break;
      ;;
    -*)
      echo "Unknown option "
      exit 1
      ;;
    *)
      break
      ;;
  esac
  shift
done

# Determine the full path to this script.
if [[ ${0:0:1} = / ]]; then
  FULL_PATH="[=10=]"
else
  FULL_PATH="$(realpath [=10=])"
fi

if test "$initial_call" -eq 1; then
  do_foreach=1
else
  # Script is called from git submodule foreach ...'
  name=""
  path=""
  sha1=""
  toplevel=""
  # Make sure we are in the right directory.
  cd "$toplevel/$path" || exit 1
  # Does the parent project want us to checkout a branch for this module?
  SUBMODULE_BRANCH=$(git config -f "$toplevel/.gitmodules" submodule.$name.branch)
  if test -n "$SUBMODULE_BRANCH"; then
    echo "Calling 'git checkout $SUBMODULE_BRANCH' in $(pwd)"
    git checkout $SUBMODULE_BRANCH || exit 1
    echo "Calling 'git pull' in $(pwd)"
    git pull || exit 1
    if test $(git rev-parse HEAD) != "$sha1"; then
      # Update the parent project to point to the head of this branch.
      pushd "$toplevel" >/dev/null
      SN1=$(git stash list | grep '^stash' | wc --lines)
      git stash save --quiet Automatic stash of parent project by update_submodules.sh
      SN2=$(git stash list | grep '^stash' | wc --lines)
      git add $name
      git commit -m "Update of submodule $name to current $SUBMODULE_BRANCH"
      if test $SN1 -ne $SN2; then
        git stash pop --quiet
      fi
      popd >/dev/null
    fi
  elif test $(git rev-parse HEAD) != "$sha1"; then
    # No submodule.$name.branch for this submodule. Just checkout the detached HEAD.
    git checkout $sha1
  fi
fi

echo "do_foreach=$do_foreach; opt_init=$opt_init; opt_recursive=$opt_recursive; name=$name; path=$path; sha1=$sha1; toplevel=$toplevel; pwd=$(pwd)"

if test $do_foreach -eq 1; then
  if test -n "$opt_init"; then
    echo "Calling 'git submodule init'"
    git submodule init
  fi
  # Make sure the submodules even exist.
  echo "Calling 'git submodule update'"
  git submodule update
  # Call this script recursively for all submodules.
  echo 'Calling '"'"'git submodule foreach '"$FULL_PATH --reentery $opt_init $opt_recursive"' $name $path $sha1 $toplevel'"'"
  git submodule foreach "$FULL_PATH --reentry $opt_init $opt_recursive"' $name $path $sha1 $toplevel'
fi

调用此脚本将执行与 'git submodule update' 相同的操作,并且 甚至支持 --init 和 --recursive。但是,您可以通过设置 'branch' 值将子模块配置为检出和拉出分支;例如:git config -f .gitmodules submodule.NAME.branch master 将导致子模块 NAME 检出并拉取分支 master 而不是当前的 SHA1 是什么。然后它还会更新父项目以指向该分支的 HEAD。

是的,你是对的。来自 Why is my GIT Submodule HEAD detached from master? 的用户 mkungla 做出的最佳回答是胡说八道。

.gitmodule 中添加 branch 选项 与子模块的分离行为完全 无关 .

git submodule --help 开始,HEAD 分离是 git submodule update --remote 的默认行为

首先,不需要指定要跟踪的分支origin/master 是要跟踪的默认分支。

--remote

Instead of using the superproject's recorded SHA-1 to update the submodule, use the status of the submodule's remote-tracking branch. The remote used is branch's remote (branch.<name>.remote), defaulting to origin. The remote branch used defaults to master.

为什么

那么为什么 HEAD 在 update 之后分离?因为 submodule.$name.update 默认行为是 checkout

--checkout

Checkout the commit recorded in the superproject on a detached HEAD in the submodule. This is the default behavior, the main use of this option is to override submodule.$name.update when set to a value other than checkout.

如何

如果你想子模块自动合并到远程分支,使用--merge--rebase

--merge

This option is only valid for the update command. Merge the commit recorded in the superproject into the current branch of the submodule. If this option is given, the submodule's HEAD will not be detached.

--rebase

Rebase the current branch onto the commit recorded in the superproject. If this option is given, the submodule's HEAD will not be detached.

你需要做的就是,

git submodule update --remote --merge
# or
git submodule update --remote --rebase

还有一个选项可以将 --merge--rebase 作为 git submodule update 的默认行为,方法是将 submodule.$name.update 设置为 merge 或 [=32] =].

这里有一个关于如何在 .gitmodule.

中配置子模块更新的默认更新行为的例子
[submodule "bash/plugins/dircolors-solarized"]
    path = bash/plugins/dircolors-solarized
    url = https://github.com/seebi/dircolors-solarized.git
    update = merge # <-- this is what you need to add

我的整个答案都是基于手册。 git submodule --help.