【问题标题】:Reapply Git commits from copied fork repository to original repository将复制的 fork 存储库中的 Git 提交重新应用到原始存储库
【发布时间】:2016-09-12 20:50:20
【问题描述】:

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

【问题讨论】:

  • 使用移植和过滤分支。
  • 有更智能的方法可以做到这一点,但一个后备方案是对 N、O 和 P 中的每一个使用 git log -n 1 -p,然后将它们一个一个地应用到原始仓库克隆中的一个分支上patch -p1 并根据需要手动添加和删除文件。如果它少于 50 次左右的提交,那么它可能比以官方方式进行战斗要快。
  • 我只想指出,如果你没有.git 文件夹,你就没有“新初始化的存储库”。我假设您的意思是他将文件复制到了一个空目录。
  • 我的意思是,他将没有.git文件夹的原始存储库中的文件复制到一个空目录中,以便在目标目录中初始化一个新的存储库。

标签: git git-rebase git-rewrite-history git-patch


【解决方案1】:

TL;DR;

在您的原始 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 不同,后者将产生一个合并提交,同时也会在您合并的分支中留下一个冗余/无父级的M 提交。我不喜欢那种悬空提交的场景。

原帖

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

这些是我遵循的步骤:

  1. 克隆原始存储库
  2. 将遥控器添加到坏仓库
  3. 获取错误的 repo master 分支
  4. 分支到获取的坏回购中
  5. 将错误的 master 分支重新设置为您的 master(将声称已经应用了一些更改)
  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

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

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

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

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

我假设您了解重写历史的含义,并且您需要与您的用户沟通,以免他们被它所困扰。


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

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

【讨论】:

    【解决方案2】:

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

    在原始仓库目录中:

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

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

    【讨论】:

    • 非常感谢。尽管这似乎是最简单的答案,但它确实是一个可行的答案。正如 torek 所提到的,git fetch fork 在两者之间仍然是必要的。
    • 这很简短,但并不完整。我已经为你添加了缺少的命令并给了你我的 +1,即使我的答案低于 ? 不过,我不喜欢由此产生的浮动 M 提交。我的回答产生了线性历史。
    【解决方案3】:

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

    作为jthill suggested in a comment,您可以使用嫁接,或更现代的git replace,然后使用git filter-branch 使嫁接永久化。这假设与提交关联的 是正确的,即,您不希望对与每个提交关联的源进行任何更改(这可能是正确的)。有关使用移植物的更多信息,请参阅 How do git grafts and replace differ? (Are grafts now deprecated?)How to prepend the past to a git repository?git replace

    鉴于这两个存储库是从一个共同的基础开始的,您还可以使用David Siro's answer 中概述的方法。缺少一个步骤:在git remote add 之后,您必须运行git fetch 以将两个存储库混合到一个新的“联合存储库”中,就像它一样。我觉得这个方法其实更简单更容易,会先试试。将两个存储库合并为一个后,您可以根据需要进行 rebase、merge、filter-branch 等操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-23
      • 2013-11-06
      • 2011-05-28
      • 1970-01-01
      • 1970-01-01
      • 2015-09-25
      • 2021-03-27
      相关资源
      最近更新 更多