GIT Split Repository目录保留*move/renames*history

sim*_*olo 11 git

假设您拥有存储库:

myCode/megaProject/moduleA
myCode/megaProject/moduleB
Run Code Online (Sandbox Code Playgroud)

随着时间的推移(月),您重新组织项目.重构代码以使模块独立.megaProject目录中的文件将移动到它们自己的目录中.强调移动 - 保留这些文件的历史.

myCode/megaProject
myCode/moduleA
myCode/moduleB
Run Code Online (Sandbox Code Playgroud)

现在您希望将这些模块移动到他们自己的GIT仓库中.只留下megaProject的原件.

myCode/megaProject
newRepoA/moduleA
newRepoB/moduleB
Run Code Online (Sandbox Code Playgroud)

记录该filter-branch命令是为了执行此操作,但是当文件移出目标目录之外时,它不会跟踪历史记录.因此,历史记录从文件移动到新目录开始,而不是文件的历史,然后它们存在于旧的megaProject目录中.

如何基于目标目录拆分GIT历史记录,并遵循此路径之外的历史记录 - 只保留与这些文件相关的提交历史记录而不是其他内容?

关于SO的众多其他答案主要集中在拆分回购 - 但没有提及拆分和跟随移动历史.

Rob*_*rto 6

这是一个基于@rksawyer 脚本的版本,但它使用git-filter-repo代替。我发现它比 git-filter-branch 更容易使用且速度更快(现在 git 推荐它作为替代品)。

# This script should run in the same folder as the project folder is.
# This script uses git-filter-repo (https://github.com/newren/git-filter-repo).
# The list of files and folders that you want to keep should be named <your_repo_folder_name>_KEEP.txt. I should contain a line end in the last line, otherwise the last file/folder will be skipped.
# The result will be the folder called <your_repo_folder_name>_REWRITE_CLONE. Your original repo won't be changed.
# Tags are not preserved, see line below to preserve tags.
# Running subsequent times will backup the last run in <your_repo_folder_name>_REWRITE_CLONE_BKP.

# Define here the name of the folder containing the repo: 
GIT_REPO="git-test-orig"

clone="$GIT_REPO"_REWRITE_CLONE
temp=/tmp/git_rewrite_temp
rm -Rf "$clone"_BKP
mv "$clone" "$clone"_BKP
rm -Rf "$temp"
mkdir "$temp"
git clone "$GIT_REPO" "$clone"
cd "$clone"
git remote remove origin
open .
open "$temp"

# Comment line below to preserve tags
git tag | xargs git tag -d

echo 'Start logging file history...'
echo "# git log results:\n" > "$temp"/log.txt

while read p
do
    shopt -s dotglob
    find "$p" -type f > "$temp"/temp
    while read f
    do
        echo "## " "$f" >> "$temp"/log.txt
        # print every file and follow to get any previous renames
        # Then remove blank lines.  Then remove every other line to end up with the list of filenames       
        git log --pretty=format:'%H' --name-only --follow -- "$f" | awk 'NF > 0' | awk 'NR%2==0' | tee -a "$temp"/log.txt
        
        echo "\n\n" >> "$temp"/log.txt
    done < "$temp"/temp
done < ../"$GIT_REPO"_KEEP.txt > "$temp"/PRESERVE

mv "$temp"/PRESERVE "$temp"/PRESERVE_full
awk '!a[$0]++' "$temp"/PRESERVE_full > "$temp"/PRESERVE

sort -o "$temp"/PRESERVE "$temp"/PRESERVE

echo 'Starting filter-branch --------------------------'
git filter-repo --paths-from-file "$temp"/PRESERVE --force --replace-refs delete-no-add
echo 'Finished filter-branch --------------------------'
Run Code Online (Sandbox Code Playgroud)

它将结果记录git log到 中的文件中/tmp/git_rewrite_temp/log.txt,因此如果您不需要 log.txt 并希望它运行得更快,则可以删除这些行。

  • 我对 git-filter-repo 有点陌生,但是通读文档,“git filter-repo --analyze”不应该能够为您提供有关重命名的信息吗? (2认同)

Mat*_*dge 4

在克隆的存储库中运行git filter-branch --subdirectory-filter将删除不影响该子目录中内容的所有提交,其中包括在移动文件之前影响文件的提交。

相反,您需要--index-filter在脚本中使用该标志来删除您不感兴趣的所有文件,并使用该--prune-empty标志来忽略影响其他内容的任何提交。

Kevin Deldycke 的一篇博客文章提供了一个很好的例子:

git filter-branch --prune-empty --tree-filter 'find ./ -maxdepth 1 -not -path "./e107*" -and -not -path "./wordpress-e107*" -and -not -path "./.git" -and -not -path "./" -print -exec rm -rf "{}" \;' -- --all
Run Code Online (Sandbox Code Playgroud)

该命令有效地依次检查每个提交,从工作目录中删除所有不感兴趣的文件,如果自上次提交以来有任何更改,则它将签入(重写历史记录)。您需要调整该命令以删除除 中的文件以及您想要保留的特定文件之外的/moduleA所有/megaProject/moduleA文件/megaProject