从 Catchall(脏)创建适当的 Git 存储库
Create proper Git repositories from a Catchall (dirty) one
让我们调用 my-dirty-repository
现有的 Git 存储库,其中包含许多不相关的脚本。这是一个包罗万象的存储库,需要 正确清理 。
作为 Minimal, Complete, and Verifiable example,假设此存储库仅包含:
script1.sh
script2.sh
在多个分支中独立更新的各种提交。
目标是创建 2 个 100% 独立的 Git 存储库,仅包含保存文件的历史记录(参考)。
我们称它们为 my-clean-repository1
和 my-clean-repository2
,第一个只有关于 script1 的历史,第二个只有关于 script2 的历史。
我尝试了 3 种方法来满足我的需求,但都没有成功:
- 简单克隆 +
git rm
删除不需要的引用
- Sparse Checkout完全没有适配
- Shallow Clone
我很确定有一种方法可以正确执行它。
编辑:我创建了专用工具cloneToCleanGitRepositories来满足这个需求。
是下一个旧版本的完整版本。
@mkasberg 感谢您提供有关交互式变基的建议,这在一些简单的历史情况下非常有趣。
我试过了,它解决了我想要一个干净、专用、独立的 git 存储库的一些脚本的问题。
最终,这对他们中的大多数人来说还不够,我再次尝试了另一种 Git filtering system 的解决方案。
最后,我写了这个小脚本:
#!/bin/bash
##
## Author: Bertrand Benoit <mailto:contact@bertrand-benoit.net>
## Description: Create clean git repositories for each file in root of specified source Git repository, updating history consequently.
## Version: 1.0
[ $# -lt 2 ] && echo -e "Usage: [=10=] <source repository> <dest root directory>" >&2 && exit 1
SOURCE_REPO=""
[ ! -d "$SOURCE_REPO" ] && echo -e "Specified source Git repository '$SOURCE_REPO' does not exist." >&2 && exit 1
DEST_ROOT_DIR=""
[ ! -d "$DEST_ROOT_DIR" ] && echo -e "Specified destination root directory '$DEST_ROOT_DIR' does not exist." >&2 && exit 1
sourceRepoName=$( basename "$SOURCE_REPO" )
# For each file in root of the source git repository.
for refToManage in $( find "$SOURCE_REPO" -maxdepth 1 -type f ); do
echo -ne "Managing $refToManage ... "
refFileName=$( basename "$refToManage" )
newDestRepo="$DEST_ROOT_DIR/$refFileName"
# Creates the repository if not existing.
logFile="$newDestRepo/logFile.txt"
echo -ne "creating new repository: $newDestRepo, Log file: $logFile ... "
if [ ! -d "$newDestRepo" ]; then
mkdir -p "$newDestRepo"
cd "$newDestRepo"
! git clone -q "$SOURCE_REPO" && echo -e "Error while cloning source repository to $newDestRepo." >&2 && exit 2
fi
cd "$newDestRepo/$sourceRepoName"
# Removes all other resources.
FILTER='git ls-tree -r --name-only --full-tree "$GIT_COMMIT" | grep -v "'$refFileName'" | tr "\n" "[=10=]" | xargs -0 git rm -f --cached -r --ignore-unmatch'
! git filter-branch -f --prune-empty --index-filter "$FILTER" -- --all >"$logFile" 2>&1 && echo -e "Error while cleaning new git repository." >&2 && exit 3
# Cleans remote information to ensure there is no push to the source repository.
! git remote remove origin >>"$logFile" 2>&1 && echo -e "Error while removing remote." >&2 && exit 2
echo "done"
done
用法:
mkdir /tmp/cleanRepoDest
createCleanGitRepo.sh ~/_gitRepo/Scripts /tmp/cleanRepoDest
在目标目录中,它将为指定源 Git 存储库的根目录中的每个文件创建一个新的干净 git 存储库。
在每一个中,历史都是干净的,只与保留的脚本有关。
此外,它 disconnects/removes 远程确保避免将更改推回源存储库的问题。
这样,很容易 'migrate' 从一个大的脏包 Git 存储库到各种干净的存储库:-)
让我们调用 my-dirty-repository
现有的 Git 存储库,其中包含许多不相关的脚本。这是一个包罗万象的存储库,需要 正确清理 。
作为 Minimal, Complete, and Verifiable example,假设此存储库仅包含:
script1.sh
script2.sh
在多个分支中独立更新的各种提交。
目标是创建 2 个 100% 独立的 Git 存储库,仅包含保存文件的历史记录(参考)。
我们称它们为 my-clean-repository1
和 my-clean-repository2
,第一个只有关于 script1 的历史,第二个只有关于 script2 的历史。
我尝试了 3 种方法来满足我的需求,但都没有成功:
- 简单克隆 +
git rm
删除不需要的引用 - Sparse Checkout完全没有适配
- Shallow Clone
我很确定有一种方法可以正确执行它。
编辑:我创建了专用工具cloneToCleanGitRepositories来满足这个需求。
是下一个旧版本的完整版本。
@mkasberg 感谢您提供有关交互式变基的建议,这在一些简单的历史情况下非常有趣。
我试过了,它解决了我想要一个干净、专用、独立的 git 存储库的一些脚本的问题。
最终,这对他们中的大多数人来说还不够,我再次尝试了另一种 Git filtering system 的解决方案。
最后,我写了这个小脚本:
#!/bin/bash
##
## Author: Bertrand Benoit <mailto:contact@bertrand-benoit.net>
## Description: Create clean git repositories for each file in root of specified source Git repository, updating history consequently.
## Version: 1.0
[ $# -lt 2 ] && echo -e "Usage: [=10=] <source repository> <dest root directory>" >&2 && exit 1
SOURCE_REPO=""
[ ! -d "$SOURCE_REPO" ] && echo -e "Specified source Git repository '$SOURCE_REPO' does not exist." >&2 && exit 1
DEST_ROOT_DIR=""
[ ! -d "$DEST_ROOT_DIR" ] && echo -e "Specified destination root directory '$DEST_ROOT_DIR' does not exist." >&2 && exit 1
sourceRepoName=$( basename "$SOURCE_REPO" )
# For each file in root of the source git repository.
for refToManage in $( find "$SOURCE_REPO" -maxdepth 1 -type f ); do
echo -ne "Managing $refToManage ... "
refFileName=$( basename "$refToManage" )
newDestRepo="$DEST_ROOT_DIR/$refFileName"
# Creates the repository if not existing.
logFile="$newDestRepo/logFile.txt"
echo -ne "creating new repository: $newDestRepo, Log file: $logFile ... "
if [ ! -d "$newDestRepo" ]; then
mkdir -p "$newDestRepo"
cd "$newDestRepo"
! git clone -q "$SOURCE_REPO" && echo -e "Error while cloning source repository to $newDestRepo." >&2 && exit 2
fi
cd "$newDestRepo/$sourceRepoName"
# Removes all other resources.
FILTER='git ls-tree -r --name-only --full-tree "$GIT_COMMIT" | grep -v "'$refFileName'" | tr "\n" "[=10=]" | xargs -0 git rm -f --cached -r --ignore-unmatch'
! git filter-branch -f --prune-empty --index-filter "$FILTER" -- --all >"$logFile" 2>&1 && echo -e "Error while cleaning new git repository." >&2 && exit 3
# Cleans remote information to ensure there is no push to the source repository.
! git remote remove origin >>"$logFile" 2>&1 && echo -e "Error while removing remote." >&2 && exit 2
echo "done"
done
用法:
mkdir /tmp/cleanRepoDest
createCleanGitRepo.sh ~/_gitRepo/Scripts /tmp/cleanRepoDest
在目标目录中,它将为指定源 Git 存储库的根目录中的每个文件创建一个新的干净 git 存储库。 在每一个中,历史都是干净的,只与保留的脚本有关。
此外,它 disconnects/removes 远程确保避免将更改推回源存储库的问题。
这样,很容易 'migrate' 从一个大的脏包 Git 存储库到各种干净的存储库:-)