将 Git 次提交从复制的分支存储库重新应用到原始存储库

Reapply Git commits from copied fork repository to original repository

我的一位大学同事认为通过克隆存储库并将其内容复制到一个新的、刚初始化的存储库中但不包含原始存储库中的 .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 来自 fork.
    • git apply 在原始存储库中。
    • 有效,但不保留提交。
  1. 其他各种不行的东西
    • 克隆原始存储库。
    • 创建并切换到新分支。
    • 使用 git reset --hard COMMIT_HASH_A.
    • 将其重置为提交 A
    • 使用分支上的 git format-patch COMMIT_HASH_M --stdout > /path/to/patchN <- O <- P 创建补丁。
    • 使用 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 有帮助吗?

如果您不坚持线性历史,您可以将您的复刻合并到原始存储库中。

在原始 repo 目录中:

git remote add fork /path/to/fork
git fetch fork
git merge fork/master

如果合并可以快进,这将保留提交并可能导致线性历史记录(无合并提交)。

首先,请注意:与所有存储库范围内的 "rewrite everything" 操作一样,在克隆 上执行此操作。如果顺利的话,太好了!如果它严重失败,删除克隆,你的情况不会比以前更糟。 :-)

作为 , you can use grafts, or the more modern git replace, and then use git filter-branch to make the grafting permanent. This assumes that the trees associated with the commits are correct, i.e., that you do not want any changes made to the source associated with each commit (which is probably true). See How do git grafts and replace differ? (Are grafts now deprecated?) and How to prepend the past to a git repository? 了解更多关于使用移植物和 git replace

鉴于这两个存储库是从一个共同的基础开始的,您也可以使用 中概述的方法。缺少一个步骤:在 git remote add 之后,您必须 运行 git fetch 才能将两个存储库混合成一个新的 "union repository"。我觉得这个方法其实更简单易行,先试试看。一旦两个存储库合并为一个,您可以根据需要进行 rebase、merge、filter-branch 等操作。

TL;DR;

在您的原始回购克隆中,您应该:

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提交。

这与 不同,后者将生成一个合并提交,该提交还会在您合并的分支中留下一个 redundant/parent-less M 提交。我不喜欢那种悬而未决的提交场景。

原版Post

我复制了你好的和坏的存储库历史,并且能够通过基本上重新设置远程基础来解决问题。

这些是我遵循的步骤:

  1. 克隆原始存储库
  2. 向错误的存储库添加一个遥控器
  3. 获取错误的回购 master 分支
  4. 分支到获取的错误 repo
  5. 将错误的主分支重新设置为您的主分支(将声称已经应用了一些更改)
  6. 将此分支合并到您的主分支中
  7. 推回原始存储库
  8. 安排你同事的死亡

通过该设置,我使用的命令和关键输出如下。

#
# 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

您现在可以用火烧毁您同事的乱七八糟的存储库,并让其他人继续使用原来的、现在已修复的存储库。

注意: 在你的 post 中,你说你想要提交:

A <- B <- C <- N <- O <- P

但我的解决方案包括提交 DE 之间:A <- B <- C <- D <- E <- N <- O <- P。如果您真的想要丢弃这些提交,即假设它不是您post中的拼写错误,那么您可以简单地git rebase -i HEAD~5,删除那些提交的 pick 行,然后 git push --force 到你好的回购的来源。

我假设您了解重写历史的含义,并且您需要与您的用户进行沟通,以免他们受其影响。


为了完整起见,我将您的设置复制如下:

  1. 创建原始良好回购历史:A <- B <- C
  2. 手动复制原始内容到乱七八糟的repo
  3. 生成混乱的提交历史记录:M <- N <- O <- P,其中 M 与原始内容相同 A <- B <- C
  4. 将作品添加到原始仓库:... C <- D <- E