我的一位大学同事认为,通过克隆存储库并将其内容复制到新的初始化存储库但没有原始存储库中的.git
文件夹来分叉存储库是个好主意。之后,他只使用一次提交就提交了这个副本,整个团队就开始根据这个提交来处理项目:
A <- B <- C <- D <- E (original repository)
\ clone / |_____|
\ / |
\ / Ofc. work on the original repository was continued after cloning...
\ /
M <- N <- O <-P (our "fork", commits from my team)
现在,我的第一个目标是获得以下存储库结构:
A <- B <- C <- N <- O <- P
我在过去几个小时内一直试图做的事情如下:
git diff > /path/to/patch
。git apply
。A
将其重置为提交git reset --hard COMMIT_HASH_A
。N <- O <- P
从git format-patch COMMIT_HASH_M --stdout > /path/to/patch
创建补丁。git am -3 /path/to/patch
在原始存储库中应用此修补程序。在解决了诸如重复创建空文件之类的多个冲突之后,这将导致以下错误:
fatal: sha1 information is lacking or useless (some_file_name).
Repository lacks necessary blobs to fall back on 3-way merge.
这是我无法接受的地方。那么如何创建一个存储库,包括原始存储库和我们团队提交的所有提交,如上所述,最终拉取请求可以发送到原始存储库?可以git-rebase
帮助吗?
答案 0 :(得分:5)
如果您不坚持使用线性历史记录,则可以将您的fork合并到原始存储库中。
在最初的回购中:
git remote add fork /path/to/fork
git fetch fork
git merge fork/master
如果可以快速转发合并,这将保留提交并可能导致班轮历史记录(无合并提交)。
答案 1 :(得分:4)
在原始的repo克隆中,你应该:
git remote add colleague /path/to/colleague
git fetch colleague
git checkout -b colleague colleague/master
git rebase master
git checkout master
git merge colleague
这将为您提供线性历史记录,并且不会留下冗余且无父级的M
提交。
这与David Siro's answer不同,http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/将产生合并提交,该合并提交也会在您合并的分支中留下冗余/父级M
提交。我不喜欢那种晃来晃去的场景。
我复制了您的好的和坏的存储库历史,并且能够通过基本上重新定位远程来解决问题。
这是我遵循的步骤:
master
分支使用该设置,我使用的命令和键输出如下。
#
# Step 1
#
$ git clone <path-to-original-repo>
$ cd original-repo
#
# Step 2
#
$ git remote add messed-up-repo <path-to-messed-up-repo>
#
# Step 3
#
$ git fetch messed-up-repo
#
# Step 4
#
$ git checkout -b bad-master bad-orig/master
#
# Step 5
#
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: commit M
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
Applying: commit N
Applying: commit O
Applying: commit P
#
# Step 5.1: look at your new history
#
$ git log --oneline --graph --decorate
* cc3121d (HEAD -> bad-master) commit P
* 1144414 commit O
* 7b3851c commit N
* b1dc670 (origin/master, origin/HEAD, master) commit E
* ec9eb4e commit D
* 9c2988f commit C
* 9d35ed6 commit B
* ae9fc2f commit A
#
# Step 6
#
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge bad-master
Updating b1dc670..cc3121d
Fast-forward
n.txt | 1 +
o.txt | 1 +
p.txt | 1 +
3 files changed, 3 insertions(+)
create mode 100644 n.txt
create mode 100644 o.txt
create mode 100644 p.txt
#
# Step 7
#
$ git push
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (9/9), 714 bytes | 0 bytes/s, done.
Total 9 (delta 3), reused 0 (delta 0)
To /tmp/repotest/good-orig.git
b1dc670..cc3121d master -> master
#
# Step 7.1: look at your history again
#
$ git log --oneline --graph --decorate
* cc3121d (HEAD -> master, origin/master, origin/HEAD, bad-master) commit P
* 1144414 commit O
* 7b3851c commit N
* b1dc670 commit E
* ec9eb4e commit D
* 9c2988f commit C
* 9d35ed6 commit B
* ae9fc2f commit A
现在,您可以用火摧毁同事的混乱存储库,并让其他人继续使用原始的,现在已修复的存储库。
注意:在您的帖子中,您说您想要提交:
A <- B <- C <- N <- O <- P
但我的解决方案包括中间的D
和E
提交:A <- B <- C <- D <- E <- N <- O <- P
。如果确实想要抛弃这些提交,即假设它不是您帖子中的拼写错误,那么您可以简单git rebase -i HEAD~5
,删除这些提交的pick
行,然后是git push --force
来自您的好回购来源。
我假设您了解重写历史记录的含义,并且您需要与您的用户进行沟通,以便他们不会对此感到满意。
为了完整起见,我按如下方式复制了您的设置:
A <- B <- C
M <- N <- O <- P
,其中M
与原始A <- B <- C
具有相同的内容... C <- D <- E
答案 2 :(得分:2)
首先,注意:与所有存储库范围内的“重写所有”操作一样,在克隆上执行此操作。如果进展顺利,太棒了!如果它失败了,删除克隆,你不会比以前更糟糕。 : - )
作为jthill suggested in a comment,您可以使用移植物或更现代的git replace
,然后使用git filter-branch
使嫁接永久化。这假设与提交相关联的树是正确的,即您不希望对与每个提交相关联的源进行任何更改(这可能是真的)。有关使用移植物和git replace
的更多信息,请参阅How do git grafts and replace differ? (Are grafts now deprecated?)和How to prepend the past to a git repository?。
鉴于这两个存储库是从一个公共基础开始的,您还可以使用David Siro's answer中概述的方法。缺少一个步骤:在git remote add
之后,您必须运行git fetch
以将两个存储库混合到一个新的“联合存储库”中。我认为这种方法实际上更简单,更容易,并且会先尝试一下。将两个存储库合并为一个后,您可以根据需要进行rebase,merge,filter-branch等。