使用 git
将一个仓库中的子目录移动到另一个仓库中,同时保留所有的上传历史记录
git filter-branch
git subtree split
只能独立一个目录
回滚仓库
git reset --hard <sha1-commit-id>
git push origin HEAD --force
使用 patch
$ git apply --check my_pcc_branch.patch
warning: src/main/java/.../AbstractedPanel.java has type 100644, expected 100755
error: patch failed: src/main/java/.../AbstractedPanel.java:13
error: src/main/java/.../AbstractedPanel.java: patch does not apply
git am --ignore-space-change --ignore-whitespace *.patch
目录结构
使用 ccat 为例
$ tree -L 2 -d
.
├── bin
├── completion
│ └── zsh
└── vendor
├── github.com
└── golang.org
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/bump_isatty
remotes/origin/bump_syntaxhighlight
remotes/origin/master
拆分一个子目录为独立仓库
将 vendor
拆分为独立仓库,保留历史记录及 Branch [master, bump_syntaxhighlight]
git subtree
- Clone原有仓库到本地
git clone https://github.com/jingweno/ccat
- 拆分指定分支 master 下项目
$ git subtree split -P vendor -b master_tmp $ git co master_tmp $ tree -L 1 -d . ├── github.com └── golang.org $ git log . # 检查 history
- 创建 New Repo
$ mkdir new-repo; cd new-repo $ git init # 本地repo $ git pull ../ccat master_tmp # ccat目录 master_tmp分支
- 清理 .git object
$ git reset --hard $ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d # 删除由git-filter-branch备份的原始参考 $ git reflog expire --expire=now --all # 使用所有reflogs $ git gc --aggressive --prune=now # 垃圾收集所有未被引用的对象
- 关联远程仓库并上传
$ git remote add origin https://github.com/breezetemple/split-test.git $ git push -u origin master
- Check
$ git clone https://github.com/breezetemple/split-test.git $ git branch -a $ git log .
保留多个分支
$ git subtree split -P vendor -b master_tmp
$ git co origin/bump_syntaxhighlight -b bump_syntaxhighlight
$ git subtree split -P vendor -b bump_syntaxhighlight_tmp
$ git branch
bump_syntaxhighlight
* bump_syntaxhighlight_tmp
master
master_tmp
$ mkdir new-repo; cd new-repo
$ git init # 本地repo
$ git pull ../ccat master_tmp # ccat目录 master_tmp分支
$ git reset --hard
$ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now
$ git remote add origin https://github.com/breezetemple/split-test.git
$ git push -u origin master
$ rm new-repo -rf; mkdir new-repo; cd new-repo
$ git init # 本地repo
$ git co -b bump_syntaxhighlight
$ git pull ../subtree bump_syntaxhighlight_tmp
$ git reset --hard
$ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now
$ git remote add origin https://github.com/breezetemple/split-test.git
$ git push -u origin bump_syntaxhighlight
git filter-branch
Clone原有仓库到本地
git clone https://github.com/jingweno/ccat filter-branch
在本地创建需要保留的分支
保留 master 和 bump_syntaxhighlight$ git co origin/bump_syntaxhighlight -b bump_syntaxhighlight $ git branch * bump_syntaxhighlight master
取消远程库的关联
$ git remote rm origin
删除所有tag
$ git tag -l v0.0.1 v0.0.2 v0.0.3 v0.1.0 v1.0.0 v1.1.0 $ git tag -l | xargs git tag -d # 需要删除所有的tag,否则会出错
保留需要的目录和历史
# 保留多个子文件夹 $ git filter-branch -f --prune-empty --index-filter 'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- vendor' -- --all # 只保留 vendor $ git filter-branch -f --prune-empty --index-filter 'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- vendor completion' -- --all # 保留 vendor completion $ git branch -a bump_syntaxhighlight * master $ tree -L 2 -d . └── vendor ├── github.com └── golang.org $ git log . # 保留一个子文件夹,并且成为 root $ git filter-branch --prune-empty --subdirectory-filter vendor $ tree -L 1 -d . ├── bin ├── completion └── vendor
清理仓库
$ git reset --hard $ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d $ git reflog expire --expire=now --all $ git gc --aggressive --prune=now
关联远程仓库并上传
$ git remote add origin https://github.com/breezetemple/split-test.git $ git push -u origin master $ git push -u origin bump_syntaxhighlight
保留了层级结构,为 vendor/*
-- --all
表明--all
为 filter-branch optionsNote the – that separates filter-branch options from revision options, and the –all to rewrite all branches and tags.
--all
all branches and tags.--subdirectory-filter <directory>
Only look at the history which touches the given subdirectory. The result will contain that directory (and only that) as its project root.
git-stitch-repo
Detach many subdirectories into a new, separate Git repository
git-filter-repo
拆分子目录并上传到一个已存在仓库
- src: ccat
$ tree -L 2 -d . ├── bin ├── completion │ └── zsh └── vendor ├── github.com └── golang.org
- dst: test
$ tree -L 1 . ├── src └── test
目标:将 ccat/vendor
移动到 split-test
git filter-branch
- 源文件处理
$ git remote rm origin # current branch: master $ git tag -l | xargs git tag -d $ git filter-branch --prune-empty --subdirectory-filter ./vendor -- --all $ tree -L 1 . ├── github.com ├── golang.org └── vendor.json $ git filter-branch -f --prune-empty --index-filter 'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- vendor' -- --all $ tree -L 2 -d . └── vendor ├── github.com └── golang.org
- 清理仓库
$ git reset --hard $ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d $ git reflog expire --expire=now --all $ git gc --aggressive --prune=now
- 目标仓库操作
$ git clone https://github.com/breezetemple/split-test.git $ cd split-test # current branch: master # 添加本地源 $ git remote add master ../filter-branch # master: ccat branch # 更新代码 $ git fetch master # 建立跟踪分支 vendor -> master/master # git remote add repo-A-branch <git repository A directory> $ git co -b vendor master/master $ git co master # 合并 $ git merge vendor --allow-unrelated-histories $ git push
拆分子仓库并且更改目录层次
目标
.
├── bin
├── completion
│ └── zsh
└── vendor
├── github.com
└── golang.org
拆分为
.
├── zsh
├── github.com
└── golang.org
git read-tree 脚本
git subtree
只能操作单个文件,当需要操作多个文件切需要更改目录层次时,需要使用更加强大的工具 git filter-branch
--index-filter
--tree-filter
编写处理脚本如下:
git read-tree --empty
git show ${GIT_COMMIT}:vendor/github.com >/dev/null 2>&1
if [ $? -eq 0 ]; then
git read-tree --prefix=github.com/ ${GIT_COMMIT}:vendor/github.com
fi
git show ${GIT_COMMIT}:vendor/golang.org >/dev/null 2>&1
if [ $? -eq 0 ]; then
git read-tree --prefix=golang.org/ ${GIT_COMMIT}:vendor/golang.org
fi
git show ${GIT_COMMIT}:completion/zsh >/dev/null 2>&1
if [ $? -eq 0 ]; then
git read-tree --prefix=zsh/ ${GIT_COMMIT}:completion/zsh
fi
脚本说明:
git read-tree <commit-id>
可读取指定提交到当前 indexgit read-tree --empty
清除当前的 index${GIT_COMMIT}
git show ${GIT_COMMIT}:vendor/github.com
判断当前提交中是否包含文件/文件夹git read-tree --prefix=zsh/ <commit-id>:completion/zsh
将指定提交的指定文件读入当前 index, 并且文件路径添加前缀
git filter-branch
- Clone原有仓库到本地
git clone https://github.com/jingweno/ccat
- 在本地创建需要保留的分支
保留 master 和 bump_syntaxhighlight$ git co origin/bump_syntaxhighlight -b bump_syntaxhighlight $ git branch * bump_syntaxhighlight master
- 取消远程库的关联
$ git remote rm origin
- 删除所有tag
$ git tag -l | xargs git tag -d # 需要删除所有的tag,否则会出错
- 保留需要的目录和历史
# 按照脚本保留多个子文件夹并调整层级 $ git filter-branch -f --index-filter PATH/TO/index-filter.sh -- --all # 绝对路径 $ git branch -a bump_syntaxhighlight * master $ tree -L 2 -d . ├── github.com ├── golang.org └── zsh $ git log .
- 清理仓库
$ git reset --hard $ git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d $ git reflog expire --expire=now --all $ git gc --aggressive --prune=now
- 关联远程仓库并上传
$ git remote add origin https://github.com/breezetemple/split-test.git $ git push -u origin master $ git push -u origin bump_syntaxhighlight
Ref
- Detach (move) subdirectory into separate Git repository
- merge_git_repo_as_subdir
- Move directory from one repository to another, preserving history
- git filter-branch
- an alternative to git-filter-branch – BFG Repo-Cleaner
- Preserve –no-ff merge commits with git filter-branch –subdirectory-filter
- git拆分工程