从复制的fork存储库重新应用Git提交到原始存储库

时间:2016-09-12 20:50:20

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

我的一位大学同事认为,通过克隆存储库并将其内容复制到新的初始化存储库但没有原始存储库中的.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

我在过去几个小时内一直试图做的事情如下:

    • 克隆原始存储库。
    • 来自fork内的
    • git diff > /path/to/patch
    • 原始存储库中的
    • git apply
    • 正常工作,但不保留提交。
  1. 其他各种不起作用的东西。
    • 克隆原始存储库。
    • 创建并切换到新分支。
    • 使用A将其重置为提交git reset --hard COMMIT_HASH_A
    • 使用叉上的N <- O <- Pgit 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. 这是我无法接受的地方。
  2. 那么如何创建一个存储库,包括原始存储库和我们团队提交的所有提交,如上所述,最终拉取请求可以发送到原始存储库?可以git-rebase帮助吗?

3 个答案:

答案 0 :(得分:5)

如果您不坚持使用线性历史记录,则可以将您的fork合并到原始存储库中。

在最初的回购中:

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

如果可以快速转发合并,这将保留提交并可能导致班轮历史记录(无合并提交)。

答案 1 :(得分:4)

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不同,http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/将产生合并提交,该合并提交也会在您合并的分支中留下冗余/父级M提交。我不喜欢那种晃来晃去的场景。

原帖

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

这是我遵循的步骤:

  1. 克隆原始存储库
  2. 向坏帐户添加遥控器
  3. 获取错误的回购master分支
  4. 分支到获取的坏回购
  5. 将坏主分支重新定位到主分支(将声明已应用某些更改)
  6. 将此分支合并到您的主人
  7. 推回原始存储库
  8. 安排好同事的死亡
  9. 使用该设置,我使用的命令和键输出如下。

    #
    # 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 <- B <- C <- D <- E <- N <- O <- P。如果确实想要抛弃这些提交,即假设它不是您帖子中的拼写错误,那么您可以简单git rebase -i HEAD~5,删除这些提交的pick行,然后是git push --force来自您的好回购来源。

    我假设您了解重写历史记录的含义,并且您需要与您的用户进行沟通,以便他们不会对此感到满意。

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

    1. 创建原始的良好回购历史记录:A <- B <- C
    2. 手动将原始内容复制到搞砸了回购
    3. 生成混乱的提交历史记录:M <- N <- O <- P,其中M与原始A <- B <- C具有相同的内容
    4. 将作品添加到原始仓库:... 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等。