- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
如何在特定提交 SHA 处将我的 git 存储库拆分为两个存储库(最近和历史),同时保留每个存储库中的分支,并正确链接到它们在 master 上的提交?
问题描述
虽然许多 SO 问题都会询问并回答如何拆分子目录(例如 The Easy Way ),但这不是我需要做的。相反,我需要将存储库提交拆分为某个提交之前的所有提交,以及之后的所有提交。虽然我的存储库很大,在十年的历史中包含数千次提交和数百个分支,但问题可以归结为一个简单的存储库,其中包含 8 个提交 (1-8) 和三个分支(master、A、B):
1 - 2 - 3 - 4 - 5 - 6 - master
\ \
7 8
\ \
A B
project-history project-recent
1 - 2 - 3 - 4 -master 4 - 5 - 6 - master
\ \
7 8
\ \
A B
history
在那一点上的分支aaf5c36
git commit-tree 8e3dbc5^{tree}
aaf5c36
从拆分提交的父级开始git rebase --preserve-merges --onto aaf5c36 8e3dbc5
project-history project-recent
1 - 2 - 3 - 4 -master 4 - 5 - 6 - master
\
7 1 - 2 - 3 - 4 - 5 - 8- B
\
A
repo-split-example
),然后使用此技术将其拆分为
repo-split-history
和
repo-split-recent
存储库,但分支 B 在后者中未附加。此外,通过将分支 B 推送到最近的仓库,历史提交也被推送到仓库(提交 1、2、3),并且提交 4 和 5 有重复(原始的,加上从 rebase )。这是项目最近 repo 的最终状态:
$ git log --graph --all --oneline --decorate
* c29649c (HEAD -> master) sixth
* e8545fd fifth
* 8e3dbc5 fourth
* aaf5c36 Get history from historical repository at file:///Users/jones/development/git-svn-migrate/repo-split-history
* 7a98d11 (B) branchB
* 1f620ac fifth
* 1853778 fourth
* 14ab901 third
* 8dd0189 second
* bb1fc8d first
$ git log --graph --all --oneline --decorate
* c29649c (HEAD -> master) sixth
| * 7a98d11 (B) branchB
|/
* e8545fd fifth
* 8e3dbc5 fourth
* aaf5c36 Get history from historical repository at file:///Users/jones/development/git-svn-migrate/repo-split-history
fifth
)?
# Rebase branch B onto the newly rewritten fifth commit
git branch temp e8545fd # the SHA of the rewritten fifth commit
git checkout B
git rebase temp # This works, but will (always?) have conflicts because it starts
# from the beginning because there is no common merge base for the commit
git branch -d temp
git rebase temp
产生大量 merge 冲突(自历史开始以来的每次提交都有一个),因为重写的第五次提交不与原始分支 B 共享任何历史记录。所以这里有很多手动冲突解决,它会我的真实存储库需要太长时间。所以仍然在寻找一个可行的解决方案,其中 rebase 可以在没有 merge 冲突的情况下工作。
最佳答案
我终于想通了,所以在这里记录程序,希望它有用。可以使用嫁接来拆分存储库,然后使用 filter-branch 重写树以使嫁接永久化,而不是 rebase 。所以,鉴于 TRUNCPOINT
是拆分存储库的提交的 SHA,TRUNCPARENT
是其父级的 SHA,并且都是 project-history
和 project-recent
是新初始化的存储库,准备接收历史提交和最近提交,将存储库分成两半的最终过程如下:
首先为历史提交创建一个分支
只需在 $TRUNCPOINT 处创建一个分支,并将该分支和源自它的所有分支推送到 project-history
即可完成。 .
git branch history $TRUNCPOINT
git push project-history history:master
git push project-history A
hisotry
分支到
master
project-history
的分支repo,然后将分支 A 推送到
project-history
存储库也是如此。结果如下:
git log --graph --oneline --decorate --all
* fdc8f84 (A) branchA a1
| * 7237a3e (HEAD -> master) fourth
| * 55be55d third
|/
* 26555d8 second
* 5a68ca2 first
MESSAGE="Get history from historical repository"
BASECOMMIT=`echo $MESSAGE | git commit-tree ${TRUNCPARENT}^{tree}`
filter-branch
重写历史以使移植永久化,然后将 master 及其关联的分支 B 推送到
project-recent
存储库。
echo "${TRUNCPOINT} ${BASECOMMIT}" > .git/info/grafts
git filter-branch -- --all
git push project-recent master
git push project-recent B
project-recent
的结果拆分内容 repo 。
git log --graph --oneline --decorate --all
* 2335aeb (B) branchB b2
* 2bb7ea3 branchB b1
| * 83c3ae9 (HEAD -> master) sixth
|/
* 25931c5 fifth
* 1e1e201 fourth
* a7f3373 Get history from historical repository
a7f3373
是我们人为创建的 BASECOMMIT,它的提交日志可以包含一条消息,将用户指向带有项目历史的存储库的位置,允许 future 用户使用
git replace
重新加入两个存储库如果需要的话。
#!/bin/bash
WORKDIR=${PWD}
create_repos () {
rm -rf repo-split-example repo-split-recent repo-split-history
# Create the repo to be split
example_repo
# Create the repo to contain the historical commits
HISTREPO="file://${WORKDIR}/repo-split-history"
mkdir ../repo-split-history
cd ../repo-split-history/
git init --bare
cd ../repo-split-example
git remote add project-history $HISTREPO
# Create the repo to contain the recent commits
RECEREPO="file://${WORKDIR}/repo-split-recent"
mkdir ../repo-split-recent
cd ../repo-split-recent/
git init --bare
cd ../repo-split-example
git remote add project-recent $RECEREPO
}
example_repo () {
# Part I: set up a test repo with our example commits
mkdir repo-split-example
cd repo-split-example/
git init
echo "We want to split the repository into project-recent and project-history portions, following the instructions at https://git-scm.com/book/en/v2/Git-Tools-Replace., but also including branches." > README.md
echo " "
echo "First commit." >> README.md
git add README.md
git commit -m "first"
echo "Second commit." >> README.md
git add README.md
git commit -m "second"
git checkout -b A HEAD
echo "Add Branch A change." >> README.md
git add README.md
git commit -m "branchA a1"
git checkout master
echo "Third commit." >> README.md
git add README.md
git commit -m "third"
TRUNCPARENT=`git rev-parse HEAD`
echo "Fourth commit." >> README.md
git add README.md
git commit -m "fourth"
TRUNCPOINT=`git rev-parse HEAD`
echo "Fifth commit." >> README.md
git add README.md
git commit -m "fifth"
FIFTH=`git rev-parse HEAD`
git checkout -b B HEAD
echo "Add Branch B change. b1" >> README.md
git add README.md
git commit -m "branchB b1"
B1=`git rev-parse HEAD`
echo "Add Branch B change. b2" >> README.md
git add README.md
git commit -m "branchB b2"
B2=`git rev-parse HEAD`
git checkout master
echo "Sixth commit." >> README.md
git add README.md
git commit -m "sixth"
# Now we have a repo with the requisite structure, ready to be split
git log --graph --all --oneline --decorate
}
split_repo () {
# Part II: Split the git repo into historical and current halves at $TRUNCPOINT
# Following guidelines at https://git-scm.com/book/en/v2/Git-Tools-Replace
# First create a branch for the historical commits
echo "Branching history at $TRUNCPOINT"
git branch history $TRUNCPOINT
git log --graph --oneline --decorate history A
# Now copy the history repo to the remote HISTREPO repository
git push project-history history:master
git push project-history A
# Now to split the repo to get the recent history from TRUNCPOINT to HEAD of master
# Create a base commit for the new new recent history
MESSAGE="Get history from historical repository at $HISTREPO"
BASECOMMIT=`echo $MESSAGE | git commit-tree ${TRUNCPARENT}^{tree}`
# Split the repository by grafting the TRUNCPARENT onto BASECOMMIT
echo "${TRUNCPOINT} ${BASECOMMIT}" > .git/info/grafts
git filter-branch -- --all
# Finally, push the current rewritten master and associated branches to a new repository
git push project-recent master
git push project-recent B
}
create_repos
split_repo
关于git - 如何在特定提交点将 git 存储库拆分为最近和较旧的提交,保留分支?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48179834/
我时不时地输入“git”,然后想到别的东西,然后输入例如“git checkout master”。当然,这给我留下了 $ git git checkout master git: 'git' is
我做到了 git 克隆 git://foo.git 光盘富 ...编辑文件.. 现在我想重新开始。我不在乎我已经做出的任何改变,但我不想再次克隆整个巨型 foo.git,只是丢失我所有的更改。我怎
我在我的电脑上开发代码,我的计算节点很少。 为了让我的程序保持同步,我决定使用 git。以前,我以一种单向模式使用它来“下推”从 PC 到计算节点的更改。但是时不时遇到计算节点特有的小bug,现场修复
虽然它似乎什么也没做,但它没有给出任何警告或错误消息。有什么想法吗? 最佳答案 来自 Git 源的注释: /* * Read a directory tree. We currently ignor
我知道如何为这样的 HTTPS 请求提供用户名和密码: git clone https://username:password@remote 但我想知道如何像这样向 Remote 提供用户名和密码:
Git GUI、Git Bash 和 Git CMD 之间有什么区别?我是初学者,为了进行安装,我发现自己通常同时使用 git bash 和 git CMD 最佳答案 Git CMD 就像使用 git
有人能告诉我git中文件索引被删除是什么意思吗?这些文件在我的 VS Code 中标记为红色,但我仍然可以修改文件并将更改推送到将反射(reflect)这些更改的远程存储库。我认为这一切都是在我使用命
我通过 git 子树将 GLFV 库添加到项目中,但出现此警告“看起来您的 git 安装或您的 git-subtree 安装已损坏”。还描述了几个原因,为什么这可能是: 如 git --exec-pa
我有需要外部 git 项目的 repo,但我不想使用子模块,因为我想在 github 上存档所有文件,所以我认为我只是将具有 git repo 的整个目录添加到 git 但它不t 添加里面的 .git
我有需要外部 git 项目的 repo,但我不想使用子模块,因为我想在 github 上存档所有文件,所以我认为我只是将具有 git repo 的整个目录添加到 git 但它不t 添加里面的 .git
我一直在阅读一篇文章,作者在其中指示:在现有存储库中创建一个新存储库,并想知道这是否是他忽略的错误。稍后我会与他核实。 这些是我要检查的条件: 将现有目录制作成仓库的条件,并且已经 checkin 主
我确实在不同的计算机上处理相同的项目,我想知道是否有一种方法可以跟踪该 .git 文件夹,这样我就不必在所有本地文件中重新配置配置文件。 我将所有工作推送到 bitbucket。 最佳答案 不,没
这个问题在这里已经有了答案: How does git store files? (3 个答案) 关闭 9 年前。 我为我的许多项目创建了一个远程存储库,所以它是我的push 的目的地。与 git
应该如何在 git 中查看文件内容的完整历史记录? 一个文件在 git 中的历史很短,存储库通过 git-svn 同步,但在 svn 中的历史很长。 git 中的历史记录到达文件移动的位置。要查看历史
我是confused here ... 如何对修改后的文件进行git commit,以及如何对新文件进行git commit? 还有,你如何在git中单独提交文件? 最佳答案 git 提交过程分为两个
正在搜索 throw SO 来寻找答案。遇到这个似乎没有给出任何答案的旧线程。重新触发此线程,希望有人知道! 有人能告诉我 git subtree 和 git filter-branch 的区别吗?为
我想知道是否有一种方法可以避免在每个 Git 命令的开头键入单词 git。 如果有一种方法可以在打开命令提示符进入 “Git 模式” 后只使用一次 git 命令就好了。 例如: git> 之后,我们键
当您修改工作目录中的文件时,git 会告诉您使用“git add”暂存。 当您向工作目录添加新文件时,git 会告诉您使用“git add”开始跟踪。 我对这两个概念有点困惑,因为我假设跟踪文件的更改
为什么 git://有效 $ git clone git://github.com/schacon/grit.git Cloning into 'grit'... ... Checking conne
我在以下沙箱中练习 git:https://learngitbranching.js.org/?NODEMO 我在两个单独的 session 中运行了两组命令。第一组命令顺序如下: git clone
我是一名优秀的程序员,十分优秀!