重写历史git筛选器分支创建/拆分为子模块/子项目
我目前正在将cvs项目导入git。重写历史git筛选器分支创建/拆分为子模块/子项目,git,rewrite,git-submodules,git-filter-branch,subproject,Git,Rewrite,Git Submodules,Git Filter Branch,Subproject,我目前正在将cvs项目导入git。 导入后,我想重写历史记录,将现有目录移动到单独的子模块中 假设我有一个这样的结构: 文件1 文件2 文件3 dir1 dir2 图书馆 现在我想重写历史记录,以便目录库始终是git子模块。例如,将指定的目录拆分为它们自己的子模块/子项目 这是我当前的代码: 文件重写子模块(称为) 文件重写子模块树过滤器 注意:子模块条目仅在创建时从父回购a创建 git submodule init git submodule update 您不需要在重写子模块树过滤器脚
导入后,我想重写历史记录,将现有目录移动到单独的子模块中 假设我有一个这样的结构:
文件1
文件2
文件3
dir1
dir2
图书馆
现在我想重写历史记录,以便目录库
始终是git子模块。例如,将指定的目录拆分为它们自己的子模块/子项目
这是我当前的代码:
文件重写子模块(称为)
文件重写子模块树过滤器
注意:子模块条目仅在创建时从父回购a创建
git submodule init
git submodule update
您不需要在重写子模块树过滤器
脚本中使用这些命令,因为它只是关于正确设置.gitmodules
文件内容
只有在第一次使用父repo时,才能执行那些“
git submodule
”命令:请参见”。我解决了自己的问题,下面是解决方案:
git子模块拆分库另一个\u库
脚本git子模块拆分
:
#!/bin/bash
set -eu
if [ $# -eq 0 ]
then
echo "Usage: $0 submodules-to-split"
fi
export _tmp=$(mktemp -d)
export _libs="$@"
for i in $_libs
do
mkdir -p $_tmp/$i
done
git filter-branch --commit-filter '
function gitCommit()
{
git add -A
if [ -n "$(git diff --cached --name-only)" ]
then
git commit -F $_msg
fi
} >/dev/null
# from git-filter-branch
git checkout-index -f -u -a || die "Could not checkout the index"
# files that $commit removed are now still in the working tree;
# remove them, else they would be added again
git clean -d -q -f -x
_git_dir=$GIT_DIR
_git_work_tree=$GIT_WORK_TREE
_git_index_file=$GIT_INDEX_FILE
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
_msg=$(tempfile)
cat /dev/stdin > $_msg
for i in $_libs
do
if [ -d "$i" ]
then
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
cd $i
if [ -d ".git" ]
then
gitCommit
else
git init >/dev/null
gitCommit
fi
cd ..
rsync -a -rtu $i/.git/ $_tmp/$i/.git/
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
git rm -q -r --cached $i
git submodule add ./$i >/dev/null
git add $i
fi
done
rm $_msg
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
if [ -f ".gitmodules" ]
then
git add .gitmodules
fi
_new_rev=$(git write-tree)
shift
git commit-tree "$_new_rev" "$@";
' --tag-name-filter cat -- --all
for i in $_libs
do
if [ -d "$_tmp/$i/.git" ]
then
rsync -a -i -rtu $_tmp/$i/.git/ $i/.git/
cd $i
git reset --hard
cd ..
fi
done
rm -r $_tmp
git for-each-ref refs/original --format="%(refname)" | while read i; do git update-ref -d $i; done
git reflog expire --expire=now --all
git gc --aggressive --prune=now
#!/bin/bash
set-eu
如果[$#-eq 0]
然后
echo“用法:$0要拆分的子模块”
fi
导出_tmp=$(mktemp-d)
导出_libs=“$@”
以美元为单位的i
做
mkdir-p$\u tmp/$i
完成
git筛选器分支--提交筛选器'
函数gitCommit()
{
git添加-A
if[-n“$(git diff--cached--name only)”]
然后
git提交-F$\u msg
fi
}>/dev/null
#来自git过滤器分支
git签出索引-f-u-a | | die“无法签出索引”
#$commit删除的文件现在仍在工作树中;
#删除它们,否则会再次添加它们
git clean-d-q-f-x
_git_dir=$git_dir
_git_work_tree=$git_work_tree
_git_index_file=$git_index_file
未设置GIT_目录
取消设置GIT\u工作树
取消设置GIT\u索引\u文件
_msg=$(临时文件)
cat/dev/stdin>$\u msg
以美元为单位的i
做
如果[-d“$i”]
然后
未设置GIT_目录
取消设置GIT\u工作树
取消设置GIT\u索引\u文件
cd$i
如果[-d“.git”]
然后
gitCommit
其他的
git init>/dev/null
gitCommit
fi
光盘
rsync-a-rtu$i/.git/$\u tmp/$i/.git/
导出GIT\u DIR=$\u GIT\u DIR
导出GIT\u工作树=$\u GIT\u工作树
导出GIT\u索引\u文件=$\u GIT\u索引\u文件
gitrm-q-r——缓存的$i
git子模块add./$i>/dev/null
加币$i
fi
完成
马来西亚元整
导出GIT\u DIR=$\u GIT\u DIR
导出GIT\u工作树=$\u GIT\u工作树
导出GIT\u索引\u文件=$\u GIT\u索引\u文件
如果[-f“.gitmodules”]
然后
git add.git模块
fi
_新版本=$(git写入树)
转移
git提交树“$\u new\u rev”“$@”;
'--标记名筛选器类别--all
以美元为单位的i
做
如果[-d“$\u tmp/$i/.git”]
然后
rsync-a-i-rtu$\u tmp/$i/.git/$i/.git/
cd$i
git重置——硬
光盘
fi
完成
rm-r$\u tmp
每个ref/original的git——读取i时格式为=“%(refname)”;做git更新ref-d$i;完成
git reflog expire--expire=now--all
git-gc--aggressive--prune=now
下面是一个在MacOSX上对我有用的更新答案。主要的变化是使用pushd/popd来更改目录,这样子模块就可以像module/glop一样,而不仅仅是glop
#!/bin/bash
set -eu
if [ $# -eq 0 ]
then
echo "Usage: $0 submodules-to-split"
fi
export _tmp=$(mktemp -d /tmp/git-submodule-split.XXXXXX)
export _libs="$@"
for i in $_libs
do
mkdir -p $_tmp/$i
done
git filter-branch --commit-filter '
function gitCommit()
{
git add -A
if [ -n "$(git diff --cached --name-only)" ]
then
git commit -F $_msg
fi
} >/dev/null
# from git-filter-branch
git checkout-index -f -u -a || die "Could not checkout the index"
# files that $commit removed are now still in the working tree;
# remove them, else they would be added again
git clean -d -q -f -x >&2
_git_dir=$GIT_DIR
_git_work_tree=$GIT_WORK_TREE
_git_index_file=$GIT_INDEX_FILE
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
_msg=$(mktemp /tmp/git-submodule-split-msg.XXXXXX)
cat /dev/stdin > $_msg
for i in $_libs
do
if [ -d "$i" ]
then
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
pushd $i > /dev/null
if [ -d ".git" ]
then
gitCommit
else
git init >/dev/null
gitCommit
fi
popd > /dev/null
mkdir -p $_tmp/$i
rsync -a -rtu $i/.git/ $_tmp/$i/.git/
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
git rm -q -r --cached $i >&2
git submodule add ./$i $i >&2
git add $i >&2
fi
done
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
if [ -f ".gitmodules" ]
then
git add .gitmodules >&2
fi
_new_rev=$(git write-tree)
shift
git commit-tree -F $_msg "$_new_rev" $@;
rm -f $_msg
' --tag-name-filter cat -- --all
for i in $_libs
do
if [ -d "$_tmp/$i/.git" ]
then
rsync -a -i -rtu $_tmp/$i/.git/ $i/.git/
pushd $i
git reset --hard
popd
fi
done
rm -rf $_tmp
git for-each-ref refs/original --format="%(refname)" | while read i; do git update-ref -d $i; done
git reflog expire --expire=now --all
git gc --aggressive --prune=now
我有一个带有
utils
库的项目,该库在其他项目中开始有用,并希望将其历史分割为子模块。没有想到要先查看,所以我编写了自己的,它在本地构建历史记录,因此速度要快一点,之后如果需要,您可以设置helper命令的.gitmodules
文件等,并将子模块历史记录推到您想要的任何位置
剥离命令本身在这里,文档在注释中,在后面的非剥离命令中。将其作为自己的命令运行,并设置subdir
set,如果要拆分utils
目录,则类似于subdir=utils git split submodule
。它之所以有黑客行为是因为它是一次性的,但我在Git历史记录中的文档子目录中对它进行了测试
#!/bin/bash
# put this or the commented version below in e.g. ~/bin/git-split-submodule
${GIT_COMMIT-exec git filter-branch --index-filter "subdir=$subdir; ${debug+debug=$debug;} $(sed 1,/SNIP/d "$0")" "$@"}
${debug+set -x}
fam=(`git rev-list --no-walk --parents $GIT_COMMIT`)
pathcheck=(`printf "%s:$subdir\\n" ${fam[@]} \
| git cat-file --batch-check='%(objectname)' | uniq`)
[[ $pathcheck = *:* ]] || {
subfam=($( set -- ${fam[@]}; shift;
for par; do tpar=`map $par`; [[ $tpar != $par ]] &&
git rev-parse -q --verify $tpar:"$subdir"
done
))
git rm -rq --cached --ignore-unmatch "$subdir"
if (( ${#pathcheck[@]} == 1 && ${#fam[@]} > 1 && ${#subfam[@]} > 0)); then
git update-index --add --cacheinfo 160000,$subfam,"$subdir"
else
subnew=`git cat-file -p $GIT_COMMIT | sed 1,/^$/d \
| git commit-tree $GIT_COMMIT:"$subdir" $(
${subfam:+printf ' -p %s' ${subfam[@]}}) 2>&-
` &&
git update-index --add --cacheinfo 160000,$subnew,"$subdir"
fi
}
${debug+set +x}
我有一个模糊的概念,但你的问题到底是什么?我想重写历史记录,以便目录库始终是CVS的
git子模块
,导入时我经常使用另一种策略。以下不是现有的git
repos的解决方案:创建一个虚拟CVSROOT
,其中CVS
文件已拆分为单独的CVS
“模块”(以下简称子目录CVSROOT
)。然后使用git cvsimport
将它们分别导入不同的git
repos。如何设置这样一个“虚拟CVSROOT”见(是的,这是一个很晚的评论)我决定将其作为一个副本关闭,在重新打开以更正目标后,我不能投票再次关闭它。唉。您好,VonC,我的意思是,在子项目条目中,subproject commit
output bygit diff
。目录库中的文件仍在主存储库中进行版本控制。@MartinF确定。在您的筛选器分支之后,.gitmodules
文件是什么样子的?我添加了.gitmodules文件的内容。因此,我想如果您尝试git子模块init
和git子模块更新
,那将不起作用,那么我将在今天下午尝试。您的脚本在我的repo上运行时没有问题。然而,对于如何使用结果,我有点困惑。我使用您建议的命令“git clone-sb…”将子目录作为一个新的repo拉出。是东北
#!/bin/bash
set -eu
if [ $# -eq 0 ]
then
echo "Usage: $0 submodules-to-split"
fi
export _tmp=$(mktemp -d)
export _libs="$@"
for i in $_libs
do
mkdir -p $_tmp/$i
done
git filter-branch --commit-filter '
function gitCommit()
{
git add -A
if [ -n "$(git diff --cached --name-only)" ]
then
git commit -F $_msg
fi
} >/dev/null
# from git-filter-branch
git checkout-index -f -u -a || die "Could not checkout the index"
# files that $commit removed are now still in the working tree;
# remove them, else they would be added again
git clean -d -q -f -x
_git_dir=$GIT_DIR
_git_work_tree=$GIT_WORK_TREE
_git_index_file=$GIT_INDEX_FILE
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
_msg=$(tempfile)
cat /dev/stdin > $_msg
for i in $_libs
do
if [ -d "$i" ]
then
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
cd $i
if [ -d ".git" ]
then
gitCommit
else
git init >/dev/null
gitCommit
fi
cd ..
rsync -a -rtu $i/.git/ $_tmp/$i/.git/
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
git rm -q -r --cached $i
git submodule add ./$i >/dev/null
git add $i
fi
done
rm $_msg
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
if [ -f ".gitmodules" ]
then
git add .gitmodules
fi
_new_rev=$(git write-tree)
shift
git commit-tree "$_new_rev" "$@";
' --tag-name-filter cat -- --all
for i in $_libs
do
if [ -d "$_tmp/$i/.git" ]
then
rsync -a -i -rtu $_tmp/$i/.git/ $i/.git/
cd $i
git reset --hard
cd ..
fi
done
rm -r $_tmp
git for-each-ref refs/original --format="%(refname)" | while read i; do git update-ref -d $i; done
git reflog expire --expire=now --all
git gc --aggressive --prune=now
#!/bin/bash
set -eu
if [ $# -eq 0 ]
then
echo "Usage: $0 submodules-to-split"
fi
export _tmp=$(mktemp -d /tmp/git-submodule-split.XXXXXX)
export _libs="$@"
for i in $_libs
do
mkdir -p $_tmp/$i
done
git filter-branch --commit-filter '
function gitCommit()
{
git add -A
if [ -n "$(git diff --cached --name-only)" ]
then
git commit -F $_msg
fi
} >/dev/null
# from git-filter-branch
git checkout-index -f -u -a || die "Could not checkout the index"
# files that $commit removed are now still in the working tree;
# remove them, else they would be added again
git clean -d -q -f -x >&2
_git_dir=$GIT_DIR
_git_work_tree=$GIT_WORK_TREE
_git_index_file=$GIT_INDEX_FILE
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
_msg=$(mktemp /tmp/git-submodule-split-msg.XXXXXX)
cat /dev/stdin > $_msg
for i in $_libs
do
if [ -d "$i" ]
then
unset GIT_DIR
unset GIT_WORK_TREE
unset GIT_INDEX_FILE
pushd $i > /dev/null
if [ -d ".git" ]
then
gitCommit
else
git init >/dev/null
gitCommit
fi
popd > /dev/null
mkdir -p $_tmp/$i
rsync -a -rtu $i/.git/ $_tmp/$i/.git/
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
git rm -q -r --cached $i >&2
git submodule add ./$i $i >&2
git add $i >&2
fi
done
export GIT_DIR=$_git_dir
export GIT_WORK_TREE=$_git_work_tree
export GIT_INDEX_FILE=$_git_index_file
if [ -f ".gitmodules" ]
then
git add .gitmodules >&2
fi
_new_rev=$(git write-tree)
shift
git commit-tree -F $_msg "$_new_rev" $@;
rm -f $_msg
' --tag-name-filter cat -- --all
for i in $_libs
do
if [ -d "$_tmp/$i/.git" ]
then
rsync -a -i -rtu $_tmp/$i/.git/ $i/.git/
pushd $i
git reset --hard
popd
fi
done
rm -rf $_tmp
git for-each-ref refs/original --format="%(refname)" | while read i; do git update-ref -d $i; done
git reflog expire --expire=now --all
git gc --aggressive --prune=now
#!/bin/bash
# put this or the commented version below in e.g. ~/bin/git-split-submodule
${GIT_COMMIT-exec git filter-branch --index-filter "subdir=$subdir; ${debug+debug=$debug;} $(sed 1,/SNIP/d "$0")" "$@"}
${debug+set -x}
fam=(`git rev-list --no-walk --parents $GIT_COMMIT`)
pathcheck=(`printf "%s:$subdir\\n" ${fam[@]} \
| git cat-file --batch-check='%(objectname)' | uniq`)
[[ $pathcheck = *:* ]] || {
subfam=($( set -- ${fam[@]}; shift;
for par; do tpar=`map $par`; [[ $tpar != $par ]] &&
git rev-parse -q --verify $tpar:"$subdir"
done
))
git rm -rq --cached --ignore-unmatch "$subdir"
if (( ${#pathcheck[@]} == 1 && ${#fam[@]} > 1 && ${#subfam[@]} > 0)); then
git update-index --add --cacheinfo 160000,$subfam,"$subdir"
else
subnew=`git cat-file -p $GIT_COMMIT | sed 1,/^$/d \
| git commit-tree $GIT_COMMIT:"$subdir" $(
${subfam:+printf ' -p %s' ${subfam[@]}}) 2>&-
` &&
git update-index --add --cacheinfo 160000,$subnew,"$subdir"
fi
}
${debug+set +x}
#!/bin/bash
# Git filter-branch to split a subdirectory into a submodule history.
# In each commit, the subdirectory tree is replaced in the index with an
# appropriate submodule commit.
# * If the subdirectory tree has changed from any parent, or there are
# no parents, a new submodule commit is made for the subdirectory (with
# the current commit's message, which should presumably say something
# about the change). The new submodule commit's parents are the
# submodule commits in any rewrites of the current commit's parents.
# * Otherwise, the submodule commit is copied from a parent.
# Since the new history includes references to the new submodule
# history, the new submodule history isn't dangling, it's incorporated.
# Branches for any part of it can be made casually and pushed into any
# other repo as desired, so hooking up the `git submodule` helper
# command's conveniences is easy, e.g.
# subdir=utils git split-submodule master
# git branch utils $(git rev-parse master:utils)
# git clone -sb utils . ../utilsrepo
# and you can then submodule add from there in other repos, but really,
# for small utility libraries and such, just fetching the submodule
# histories into your own repo is easiest. Setup on cloning a
# project using "incorporated" submodules like this is:
# setup: utils/.git
#
# utils/.git:
# @if _=`git rev-parse -q --verify utils`; then \
# git config submodule.utils.active true \
# && git config submodule.utils.url "`pwd -P`" \
# && git clone -s . utils -nb utils \
# && git submodule absorbgitdirs utils \
# && git -C utils checkout $$(git rev-parse :utils); \
# fi
# with `git config -f .gitmodules submodule.utils.path utils` and
# `git config -f .gitmodules submodule.utils.url ./`; cloners don't
# have to do anything but `make setup`, and `setup` should be a prereq
# on most things anyway.
# You can test that a commit and its rewrite put the same tree in the
# same place with this function:
# testit ()
# {
# tree=($(git rev-parse `git rev-parse $1`: refs/original/refs/heads/$1));
# echo $tree `test $tree != ${tree[1]} && echo ${tree[1]}`
# }
# so e.g. `testit make~95^2:t` will print the `t` tree there and if
# the `t` tree at ~95^2 from the original differs it'll print that too.
# To run it, say `subdir=path/to/it git split-submodule` with whatever
# filter-branch args you want.
# $GIT_COMMIT is set if we're already in filter-branch, if not, get there:
${GIT_COMMIT-exec git filter-branch --index-filter "subdir=$subdir; ${debug+debug=$debug;} $(sed 1,/SNIP/d "$0")" "$@"}
${debug+set -x}
fam=(`git rev-list --no-walk --parents $GIT_COMMIT`)
pathcheck=(`printf "%s:$subdir\\n" ${fam[@]} \
| git cat-file --batch-check='%(objectname)' | uniq`)
[[ $pathcheck = *:* ]] || {
subfam=($( set -- ${fam[@]}; shift;
for par; do tpar=`map $par`; [[ $tpar != $par ]] &&
git rev-parse -q --verify $tpar:"$subdir"
done
))
git rm -rq --cached --ignore-unmatch "$subdir"
if (( ${#pathcheck[@]} == 1 && ${#fam[@]} > 1 && ${#subfam[@]} > 0)); then
# one id same for all entries, copy mapped mom's submod commit
git update-index --add --cacheinfo 160000,$subfam,"$subdir"
else
# no mapped parents or something changed somewhere, make new
# submod commit for current subdir content. The new submod
# commit has all mapped parents' submodule commits as parents:
subnew=`git cat-file -p $GIT_COMMIT | sed 1,/^$/d \
| git commit-tree $GIT_COMMIT:"$subdir" $(
${subfam:+printf ' -p %s' ${subfam[@]}}) 2>&-
` &&
git update-index --add --cacheinfo 160000,$subnew,"$subdir"
fi
}
${debug+set +x}