繁体   English   中英

我如何变基 git 超级项目更改子模块的哈希值?

[英]How do I rebase a git superproject changing the hashes of the submodules?

背景

假设我们有两个 git 存储库,一个是另一个的子模块( A将是超级项目, B将是子模块)。 项目A本身不是源代码,而是一个收集和跟踪有关其子模块的信息的项目。 A repo 很少(如果有的话)存在于本地机器上,而是一堆脚本让它保持更新。

有一天,有人意识到 repo B应该更好地使用 LFS,并使用git lfs migrate import清理了 repo。 我有一个B的旧散列和新散列的列表。

我做了什么

由于 repo A恰好是线性的(无分支),我能够执行git rebase --root -i ,将所有提交更改为edit ,并运行一个简单的 bash 脚本,将子模块重置为新的哈希值。 这是脚本的示例:

#!/bin/bash
#set the submodule path and input files
submodulePath=foo
newHashesFile=NewHashes.txt
originalHashesFile=OriginalHashes.txt

while [ (test -d "$(git rev-parse --git-path rebase-merge)" || test -d "$(git rev-parse --git-path rebase-apply)" ) ]; do
    numLines=`git ls-files --stage | grep $submodulePath | wc -l`
    if [ $numLines = 1 ];
    then
        oldHash=`git ls-files --stage | grep $submodulePath | sed -e 's/^160000 \([^ ]*\) 0.*$/\1/g'`
        echo oldHash: $oldHash
    else
        echo merge conflict
        oldHash=`git ls-files --stage | grep $submodulePath | grep '^160000 \([^ ]*\) 3.*' | sed -e 's/^160000 \([^ ]*\) 3.*$/\1/g'`
        echo oldHash: $oldHash    
    fi

    lineNumber=`grep -n $oldHash $originalHashesFile | sed -e 's/^\([^:]*\):.*/\1/g'`
    newHash=`head -n $lineNumber $newHashesFile | tail -n 1`

    if [ ! $lineNumber ];
    then
        echo Hash not changed
    else
        cd $submodulePath
        git reset --hard $newHash
        cd ../
    fi

    git add $submodulePath/
    git commit --amend
    git rebase --continue
done

问题

所有这一切都奏效了,但我想知道是否有更简单的方法来做到这一点,因为我想我会被要求再次这样做。 这个问题有两个部分。

  1. 有没有一种简单的方法可以告诉 git 您希望默认为edit而不是pick ,而不依赖于编辑器?
  2. 有没有更简单的方法告诉 git 执行脚本的操作? 如果我从超级项目中执行git lfs migrate import ,会有帮助吗?

有没有一种简单的方法可以告诉 git 您希望默认为编辑而不是选择,而不依赖于编辑器?

不,但是,有一种方法可以将命令序列编辑器设置为与其他编辑器不同的编辑器:设置环境变量GIT_SEQUENCE_EDITOR 因此,例如,您可以执行以下操作:

GIT_SEQUENCE_EDITOR="sed -i '' s/^pick/edit/" git rebase -i ...

(假设您的sed有一个-i以这种方式工作,等等)。

有没有更简单的方法告诉 git 执行脚本的操作?

Given that you want to update each gitlink hash, I'd use git filter-branch (rather than git rebase ) to do it, with an --index-filter that does the gitlink hash updates. 我不确定这是否更简单,但更直接。 索引过滤器本身将包括使用git ls-files --stage类似于您执行此操作的方式,但它本身可能使用生成的sed脚本或awk脚本。 Generated-sed 可能会更快,而 awk 会更简单,特别是如果您有一个现代 awk ,您可以在其中读取 hash 映射。

多年来不得不这样做几次之后,我听取了torek 的建议,并将过于冗长的 bash 脚本编写为单个git filter-branch 我将其张贴在这里,供其他用户和未来的我使用。

首先,只是为了阐明我是如何执行lfs migrate import的(而且我确定我为其中一些行走了很长的路):

# Make sure we have the up-to-date remote branches
git submodule update --init SubmodulePath/
cd SubmodulePath/
git fetch --all

# Create local branches that mirror the remote ones
git branch -lr | grep -v "origin/HEAD" | sed 's/^.*origin\///' | 
   xargs -I @ git branch @ origin/@ --force

#Find all files that git identifies as binary and create the lfs migrate command, then run it
git log --all --numstat | grep '^-' | cut -f3 | sed 's|^.*/\(.*\)|\1|' | sed 's|^.*\.\([^.]*\)|\1|' |
   sort -u --ignore-case | sed 's|\([^0-9]\)|[\L\1\U\1]|g' | awk '{print}' ORS=',*.' |
   sed 's|^\(.*\),\*\.$|git lfs migrate import --everything --object-map=LFSImport.txt --include="*.\1"|' | . /dev/stdin

然后我将 LFSImport 移动到另一个目录(我也将它提交给子模块 repo)并使用index-filter运行filter-branch

git filter-branch -f --index-filter '
   numLines=`git ls-files --stage | grep SubmodulePath | wc -l`
   if [ $numLines = 1 ];
   then
     echo 
     oldHash="$(git rev-parse --quiet --verify :SubmodulePath)"
     echo oldHash: $oldHash
     newHash="$(grep  $oldHash /path/to/LFSImport.txt | cut -d , -f2)"
     echo newHash: $newHash
     git update-index --add --cacheinfo 160000 $newHash SubmodulePath
   fi
   ' HEAD

我可能应该在$newHash上添加一个检查以查看它是否不为空(它在我的一次提交中,但我手动只是将它设置为其他不存在的东西)。 正如 torek 提到的,这更干净、更快,而且工作得很好,如果不是更好的话。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM