4

A university colleague of mine thought it was a good idea to fork a repository by cloning it and copy its contents into a new, freshly initialized repository but without the .git folder from the original repository. Afterwards, he simply committed this copy using a single commit and the whole team began working on the project based on this commit:

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)

Now, my first goal is to get the following repository structure:

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

What I have been trying to do now during the past few hours is the following:

    • Clone the original repository.
    • git diff > /path/to/patch from within the fork.
    • git apply within the original repository.
    • Works, but does not preserve the commits.
  1. Various other things that will not work.
    • Clone the original repository.
    • Create and switch to a new branch.
    • Reset it to the commit A using git reset --hard COMMIT_HASH_A.
    • Create a patch from N <- O <- P using git format-patch COMMIT_HASH_M --stdout > /path/to/patch on the fork.
    • Apply this patch on the original repository using git am -3 /path/to/patch. After resolving several conflicts such as the duplicate creation of empty files, this will result in the following error: fatal: sha1 information is lacking or useless (some_file_name). Repository lacks necessary blobs to fall back on 3-way merge. This is where I cannot get on.

So how do I create a repository including all commits from the original repository and from our team as described, so that eventually, a pull request could be sent to the original repository? Might a git-rebase help?

4

3 回答 3

6

TL;博士;

在您的原始仓库克隆中,您应该:

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

原帖

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

这些是我遵循的步骤:

  1. 克隆原始存储库
  2. 将遥控器添加到坏仓库
  3. 获取坏的 repomaster分支
  4. 分支到获取的坏回购
  5. 将坏的主分支重新设置为您的主分支(将声称已经应用了一些更改)
  6. 将此分支合并到您的 master 中
  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 <- 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 <- PM与原始内容相同A <- B <- C
  4. 将工作添加到原始仓库:... C <- D <- E
于 2016-09-12T22:01:54.947 回答
6

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

在原始存储库目录中:

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

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

于 2016-09-12T21:26:28.850 回答
2

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

正如jthill 在评论中建议的那样,您可以使用嫁接,或更现代git replace的,然后使用git filter-branch使嫁接永久化。这假设与提交关联的是正确的,即您不希望对与每个提交关联的源进行任何更改(这可能是正确的)。请参阅git 移植和替换有何不同?(现在是否已弃用移植?)以及如何将过去添加到 git 存储库?有关使用移植物的更多信息和git replace.

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

于 2016-09-12T21:49:12.767 回答