简体   繁体   English

将派生的存储库转回分支

[英]Turning a forked repository back into a branch

I'm working with a codebase A which was at some point forked to codebase B . 我正在使用代码库A ,该代码库在某个时候分叉到代码库B。 These codebases live in separate Git repositories, and we can assume that all development has happened on a single branch in each one. 这些代码库位于单独的Git存储库中,我们可以假定所有开发都发生在每个分支的单个分支上。

Unfortunately the history of B is not complete, and starts with an "Initial import" commit that starts somewhere in the middle of the development A . 不幸的是, B的历史还不完整,并且以“初始导入”提交开始,该提交从开发A的中间开始。

Subsequently, both A and B were developed separately and diverged. 随后, AB分别开发并分开。

I'd like to try to wrangle this mess so that B is a branch of A , with a common history up to the point that they diverged. 我想尝试解决这个混乱的问题,以便BA的分支,具有共同的历史,直到它们分歧为止。

There's a relatively easy way to do this that might be "good enough". 有一个相对简单的方法可以做到这一点,它可能“足够好”。 It requires that you pick one commit in repository A that you will declare to be the "base commit" of all commits in repository B . 它要求您在存储库A中选择一个提交, 然后将其声明为存储库B中所有提交的“基本提交”。 It encourages, but does not require, that you then replace all the commits from B with new-and-improved commits, which you can do with git filter-branch . 它鼓励(但不要求)然后您来自B的所有提交替换为新的和改进的提交,您可以使用git filter-branch进行替换。

Background 背景

The process is fairly simple and easy to visualize as long as you remember that each Git commit is a snapshot of all source files, plus some metadata. 只要您记得每个Git提交都是所有源文件的快照以及一些元数据,该过程就非常简单且易于可视化。 The metadata in each commit gives: 每次提交中的元数据提供:

  • who made the commit (author and committer), when (time-stamps for each of those), and why (log message); 谁进行提交(作者和提交者),何时(每个时间戳)以及为什么(日志消息);
  • the tree that represents its snapshot; 代表其快照的树; and
  • the parent commit(s), so that Git can follow history backwards, one commit at a time, from each commit to its predecessor(s). 父提交,这样Git可以从每个提交到其前身,向后追溯历史记录,一次提交一次。

This information forms all the commits in a repository into a graph G = (V, E) , where V and E are sets of nodes and edges. 该信息将存储库中的所有提交形成为图G =(V,E) ,其中VE是节点和边的集合。 Each vertex V is denoted by one commit hash ID—each hash ID is unique to that one commit—and each edge E is a one-way arrow or arc , with the edge-set built from all the stored parent hash IDs in each commit. 每个顶点V都由一个提交哈希ID表示-每个哈希ID对该提交都是唯一的-每个边缘E是单向箭头或弧线 ,并且边缘集是由每个提交中所有已存储的父哈希ID构建的。 Intuitively, this means we can draw a simple linear graph like this: 直观地讲,这意味着我们可以绘制一个简单的线性图,如下所示:

[start] o <-o <-o ... <-o  [end]

A branch-y graph simply forks somewhere: 一个y分支图只是在某个地方分叉:

        o--o   <-- end1
       /
o--o--o
       \
        o--o--o   <-- end2

and a graph that forks, then re-merges, might look like: 分叉然后重新合并的图形可能类似于:

        o--o
       /    \
o--o--o      o--o   <-- master
       \    /
        o--o

The end nodes are found via branch names , such as end1 and end2 , or master . 可以通过分支名称 (例如end1end2master找到末端节点。 Since Git's internal arrows are all one-way, pointing backwards, we need these starting-points (ending-points?) to be able to find the rest of the commits. 由于Git的内部箭头都是向后指向的单向箭头,因此我们需要这些起点(终点?)才能找到其余的提交。

There's no requirement that the graph be connected: 不需要连接图形

A--B--C   <-- br1

F--G--H   <-- br2

could represent a repository with six commits and two branches. 可以代表具有六个提交和两个分支的存储库。 Commit C is the last commit on branch br1 and commit H is the last one on br2 ; 提交C是分支br1上的最后一个提交,提交Hbr2上的最后一个; working backwards from the two tips, we can enumerate all six commits. 从这两个技巧倒退,我们可以列举所有六个提交。

Your case 你的情况

In your case, you have a repository A with some set of branches—perhaps just one—which identify some set of tip commits, and a separate repository B with some set of branches, again maybe just one, identifying some set of tip commits. 在您的情况下,您有一个存储库A ,其中包含一组分支(也许只有一个),它标识一组提示提交,而另一个存储库B中包含一组分支,也可能只有一个,标识了一组提示提交。 Using git remote add <url> within A (or any identical clone of A ), you can have the Git for that repository call up the Git for a repository-B, obtain all of its commits and branches, and put them into repository A . 使用git remote add <url>A(A的任一相同克隆),你可以有Git的用于该储存库调出的Git对储存-B,获得其所有的提交和分支机构,并把它们放到存储库

Let's draw an example, assuming for simplicity that repository A has exactly three commits ending in master : 让我们画一个例子,为简单起见,假设存储库A恰好有三个以master结尾的提交:

A--B--C   <-- master

and that repository B also has three commits, ending in its master : 而库B也有三个提交,在其结束master

D--E--F   <-- master

We'll use remote name remote-B to have Git A call up Git B and get its commits. 我们将使用远程名称remote-B让Git A调用Git B并获取其提交。 Git A will rename B 's master to remote-B/master in the process, so now we have: Git A将在此过程中将Bmaster重命名为remote-B/master ,所以现在我们有了:

A--B--C   <-- master

D--E--F   <-- remote-B/master

in A . A中 So now we have one repository with two disconnected graphs. 因此,现在我们有了一个包含两个断开连接的图的存储库。 You can, if you wish, now attach a regular branch name—rather than a remote-tracking name—to commit F : 如果愿意,现在可以附加一个常规分支名称(而不是远程跟踪名称)来提交F

git branch develop remote-B/master

so that F is named by both develop and remote-B/master , and you can now, if you wish, git remote remove remote-B to remove the name remote-B/master . 这样F就可以同时由develop remote-B/master来命名,如果愿意,您现在可以使用git remote remove remote-B来删除名称remote-B/master Commits DEF remain in your repository, find-able via your name develop . 承诺将DEF保留在您的存储库中,可通过您的名字develop找到。

Connecting the graphs 连接图

Now let's say that we decide that commit D , which we got from B but is now ours as well, closely resembles commit B , and we'd like to pretend that D has B as a parent: 现在,我们决定由B获得但现在也属于我们的commit D ,它非常类似于commit B ,并且我们要假装 DB为父对象:

A--B--C   <-- master
    \

      D--E--F   <-- develop

We can't actually change commit D . 我们实际上不能更改 commit D Its unique hash ID is uniquely D -ish. 其唯一的哈希ID是唯一的D -ish。 We could extract commit D and make a copy of it that's slightly different—a D' , if you will—and make D' re-use everything from original commit but have B as its parent: 我们可以提取提交D复制稍有不同的副本 (如果需要,可以复制D' ,并使D'重用原始提交中的所有内容,但将B作为其父级:

A--B--C   <-- master
    \
     D'

      D--E--F   <-- develop

We'd then need to copy E and F to E' and F' , where E' is just like E except that it has D' as its parent, instead of D as its parent; 然后,我们需要将EF复制到E'F' ,其中E'E相似, 只是它的父对象为D' ,而不是D为父对象。 and F' is like F but has E' as its parent: F'F但以E'作为其父级:

A--B--C   <-- master
    \
     D'-E'-F'

      D--E--F   <-- develop

Then all we have to do is peel the name develop off commit F and paste it onto F' and we have what we want: 那么我们需要做的是剥离名称develop关闭提交F并粘贴至F'和我们我们想要的:

A--B--C   <-- master
    \
     D'-E'-F'   <-- develop

      D--E--F   [abandoned]

But how do we arrange to copy D to D' , E to E' , and F to F' ? 但是,我们如何安排复制DD' EE' ,和FF' The answer is to use git filter-branch , which can copy commits and make changes as it does its copying. 答案是使用git filter-branch ,它可以复制提交在复制时进行更改。 There's a hard way—not really that hard, but it's harder than the easier way—and an easier way, and we should actually start with the hard way. 有一种困难的方法-并不是真的那么难,但是比简单的方法难-而且更容易的方法,我们实际上应该从困难的方法开始。

The hard way is to use --commit-filter . 困难的方法是使用--commit-filter Here, we have a say in how each new commit is to be made. 在这里,我们对如何进行每个新提交都有发言权。 The default action is to use git commit-tree "$@" ; 默认操作是使用git commit-tree "$@" ; if we did that, we'd make all the copies 100% identical to the originals, so that instead of making a D' we'd just actually re-use D , then re-use E and F and we'd have no change. 如果这样做,我们将使所有副本与原始副本100%相同,因此,我们实际上只是重复使用D而不是D' ,而是重新使用EF而没有更改。 So we have to use something more complicated: 所以我们必须使用更复杂的东西:

'if [ $GIT_COMMIT = ___ ]; then git commit-tree -p ___ "$@"; else git commit-tree "$@"; fi'

except that we have to fill in both blanks. 除了我们必须填写两个空白。 The first one, we'd fill in with the actual hash ID of commit D . 第一个,我们将使用提交D的实际哈希ID进行填充。 The second, we'd fill in with the actual hash ID of commit B . 第二,我们将使用提交B的实际哈希ID进行填充。 This says: when we're copying original commit D , add B as a parent. 这就是说:当我们复制原始提交D ,将B添加为父对象。 Since D itself has no parents, this makes Git create D' with one parent, namely B . 由于D本身没有父母,这使得Git与一个父母B一起创建D' After that, when filter-branch copies E , it will use D' as its parent, creating E' ; 之后,当分支过滤器复制E ,它将使用D'作为其父对象,从而创建E' and then it will use E' as the parent for the copy of F . 然后它将E'作为F的副本的父对象。 Then, having copied all the commits that we need to copy (which is really just D-through-F), filter-branch will yank the old branch name(s) off the old commits and make them point to the new copies. 然后,在复制了我们需要复制的所有提交(实际上只是D-F)之后,filter-branch将把旧的分支名称从旧的提交中删除,并使它们指向新的副本。

Getting to this state more easily 更轻松地达到此状态

The problem with the above is that we have to find the hash IDs of B and D , and then type them in very carefully . 上面的问题是我们必须找到BD的哈希ID,然后非常小心地键入它们。 One slip-up, one single-character typo, and this whole filter-branch operation—which can be pretty slow, depending on how many commits you copy—is ruined and we have to start over. 一次滑动,一次单字符错字和整个筛选分支操作(可能会很慢,具体取决于您复制的提交数量)会毁掉,我们必须重新开始。 (If you're really good at Git, this starting-over is not that bad, but this is a pretty rare exercise and few people are good at it.) (如果您真的很擅长Git,那么这个开始并不是那么糟糕,但这是一种非常罕见的练习,很少有人擅长。)

So, instead of doing it this way, we can use git replace . 因此,我们可以使用git replace这种方式。 What git replace does is—well, it has several modes of operation, but the one we'll use here is: it copies one commit, and lets us make changes before making the copy. git replace作用是-它有多种操作模式,但是我们这里使用的是:它复制一个提交,并让我们在进行复制之前进行更改。 We'll copy commit D to an altered D' . 我们将提交D复制到更改的D' Having made the copy, it then arranges for most Git commands to switch to the copy automatically. 制作完副本后,它会安排大多数 Git命令自动切换到副本。

So, what we'll do here is find the hash ID for D —that's pretty easy: 因此,我们在这里要做的是找到D的哈希ID-这很简单:

git rev-list --topo-order develop | tail

for instance. 例如。 The rev-list walks the list of commits. 修订列表遍历提交列表。 The --topo-order ensures that even if there's weird internal branching and such, the first commit comes out last. --topo-order可确保即使内部分支怪异,第一个提交也将最后出现。 This only fails if there are multiple first commits, ie, we have a situation like: 仅当有多个首次提交时,此操作才会失败,即,我们遇到以下情况:

D
 \
  F   <-- develop
 /
E

in which case we have to replace both D and E . 在这种情况下,我们必须同时替换D E Or we can use: 或者我们可以使用:

git rev-list --max-parents=0 develop

which lists all root commits reachable from develop , which finds us both D and E directly if we have something like the above. 它列出了develop可以访问的所有根提交,如果我们有上述类似的东西,它会直接找到DE

Anyway, having found D —and assuming there's just the one commit—we now want to replace it with its D' copy. 无论如何,找到D并假设只有一次提交,我们现在要用D'副本替换它。 Now we need to pick some commit such as B , using git log on the original set of commits and picking a usable one. 现在我们需要选择一些提交,例如B ,使用git log原始提交集并选择一个可用的提交。 Which one we pick isn't all that important, but you can run: 我们选择哪一个并不那么重要,但是您可以运行:

git diff <hash-of-B> <hash-of-D>

to see how close the two snapshots are. 看看两个快照有多近。 A commit with a "really close" or "exact match" snapshot is a good candidate for the new parent. 具有“完全关闭”或“完全匹配”快照的提交是新父级的不错选择。

Now we run: 现在我们运行:

git replace --graft hash-of-D hash-of-B

This makes our D' , along with a special name, refs/replace/ hash , that Git uses so that every time it comes across D , Git quickly looks away to D' instead. 这使得Git使用我们的D'以及特殊名称refs/replace/ hash ,以便每次遇到D ,Git都会迅速将目光移向D' Since D' has parent B , most of Git now believes that from commit E , the next-back commit is D' , and then there's another commit one step back from that: B . 由于D'拥有父B ,因此大多数Git认为从提交E ,下一个后退提交是D' ,然后又有一个提交向后退了一步: B

That is, we now have: 也就是说,我们现在有:

A--B--C   <-- master
    \
     D'   <-- refs/replace/<big-ugly-hash-of-D>

      D--E--F   <-- develop

You can stop at this state , but note that if you do, any clone of this repository won't clone the replacement D' commit by default. 您可以在此状态下停止 ,但是请注意,如果这样做,则默认情况下,此存储库的任何克隆 都不会克隆替换D'提交。 So the clone won't look aside from D to D' and won't think that the history goes F to E to D' to B to A . 因此克隆不会从DD'转移到其他地方,也不会认为历史从FED'BA The clone will see the true history, F to E to D and stop. 克隆将看到真实的历史记录,即从FED并停止。

You can make clones pick up the replacement commit, after which they'll pretend that the histories are joined. 可以让克隆选择替换提交,然后它们会假装历史记录已合并。 But it's simpler now to just actually-join the histories, using git filter-branch . 但是,现在使用git filter-branch实际加入历史记录更为简单。 By default, filter-branch obeys replacements—so it will copy commit A (with no changes so that the result is A ), then B , then (in some order) C and D' . 默认情况下,filter-branch服从替换,因此它将复制提交A (不作任何更改,因此结果 A ),然后是B ,然后(以某种顺序)是CD' After it has copied D' —with no changes so that the result is D' —filter-branch will copy E using D' as E 's parent, then F using E' as F 's parent. 它已经复制完成后D' -with没有变化,这样的结果 D'型滤波器分支将复制E使用D'E的母公司,然后F使用E'作为F的母公司。 So now you have the same result you would have if you had run git filter-branch with the right --commit-filter and no typos. 因此,如果您使用正确的--commit-filter且没有错字来运行git filter-branch ,那么您将获得与原来相同的结果。

The other nice thing about using git replace here is that you can: 在这里使用git replace的另一个git replace是您可以:

  • delete the replacement, if you don't like it, or 如果您不喜欢它,请删除它,或者
  • replace the replacement with a better replacement (equivalent to delete and then re-replace), using the -f / --force flag. 使用-f / --force标志将替换项替换为更好的替换项(等同于删除然后重新替换)。

So you can experiment with different joinings-up of the histories, and decide which one you like the most, before cementing it into place with git filter-branch . 因此,您可以尝试各种不同的历史记录,并确定最喜欢的历史记录,然后再使用git filter-branch将其固定到位。 Before the cementing, you can still obtain new commits from repository B . 在合并之前,您仍然可以从存储库B获得新的提交。 After the cementing, you've, well, committed, if you'll pardon the phrasing, to the new replacement commit hash IDs and you can no longer easily incorporate new commits from B . 合并之后,如果可以免除措辞,则可以将其提交到新的替换提交哈希ID中,而您再也无法轻松地合并来自B的新提交了。

There is a way to merge these to repositories into one, but it is a bit complicated and I would strongly recommend to save a backup of your complete repository somewhere safe beforehand, eg git clone --mirror https://git.example.org/repo.git and keep this backup for a while in case you discover problems at some point in the future. 有一种方法可以将这些合并到存储库中,但是有点复杂,我强烈建议您事先将完整存储库的备份保存在安全的地方,例如git clone --mirror https://git.example.org/repo.git并保留此备份一段时间,以防将来您发现问题。

The method I describe uses git replace to first tell git to replace a certain commit with a different commit, namely replace the initial import commit of B with the corresponding commit of A and afterwards rewriting the whole history to make these replacements permanent as the replace mechanics are not too stable across all git commands. 我所描述的方法使用git replace先通知Git更换不同的一定犯犯,即更换相应的事后重写整个历史,使这些替代品永久的替代机制B的初始导入提交在所有git命令中都不太稳定。 It is better to not have those in your repo forever. 最好不要将这些永久保存在您的存储库中。

To better illustrate the procedure and let you try it first, I prepare some test repo first. 为了更好地说明过程并让您先尝试一下,我首先准备一些测试库。

mkdir orig-repo
cd orig-repo/
git init
touch foo
git add foo
git commit -m 1
echo bar > foo
git commit -m 2 foo
echo foo > bar
git add bar
git commit -m 3
cd ..
mkdir fork-repo
cd fork-repo/
git init
cp ../orig-repo/foo ../orig-repo/bar .
git add foo bar
git commit -m a
echo baz >> foo
git commit -m b foo
cd ../orig-repo/
echo bla >> bar
git commit -m 4 bar

This just creates two distinct repos with a couple of commits and a shared set of files. 这只会创建两个不同的回购协议,其中包含几个提交和一组共享的文件。

Working of this base, let's merge these two repos together: 在此基础上,我们将这两个存储库合并在一起:

user@host:/tmp/git-replace-test/orig-repo (master)$ git remote add fork ../fork-repo/
user@host:/tmp/git-replace-test/orig-repo (master)$ git fetch fork
warning: no common commits
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 7 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (7/7), done.
From ../fork-repo
 * [new branch]      master     -> fork/master

Basic. 基本。 Just fetch the fork repo so its commits are available in our original repo. 只需获取fork仓库,即可在我们的原始仓库中使用其提交。

user@host:/tmp/git-replace-test/orig-repo (master)$ git replace --graft $(git rev-parse fork/master) $(git rev-parse master~)

This is the import one, it tells git that the first commit of our fork repo is to be replaced by a certain commit of our original repo. 这是导入项,它告诉git我们的fork存储库的第一个提交将由我们原始存储库的某个提交替换。 --graft option practically changes the parent of a commit to something else, here we replace the initial commit with the whole history of that commit from the original repo. --graft选项实际上将提交的父项更改为其他内容,这里我们将原始提交替换为原始回购中该提交的全部历史记录。

Please do not use the command as is on your real repo, the revisions I use here work only for my example. 请不要按原样使用该命令,我在这里使用的修订仅适用于我的示例。 The first revision is the first commit of the fork after the initial import commit (b in the example). 第一个修订版是初始导入提交(示例中的b)之后的fork的第一次提交。 As our example only has two commits it is the head commit. 因为我们的示例只有两个提交,所以它是头提交。 If there were a third commit it would not be the head commit obviously. 如果有第三次提交,那显然不是头提交。 The second revision is the commit from which the repo was forked originally. 第二个修订是最初由仓库分叉的提交。 In our case this is commit 3, or one commit behind master. 在我们的例子中,这是提交3,或在master之后提交1。 Please insert real commit hashes instead of rev-parse to be sure of what exactly you are doing. 请插入实际的提交哈希值而不是rev-parse ,以确保您确实在做什么。

After this you can check with git log , your original repo is still unchanged. 之后,您可以使用git log进行检查,您的原始存储库仍保持不变。 But the log of the fork is more interesting now: 但是,分叉的日志现在更加有趣:

user@host:/tmp/git-replace-test/orig-repo (master)$ git log fork/master
commit 61ca43d062128c9fcddb9352698363e1bcf12a86 (replaced, fork/master)
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    b

commit 2ff1b501ecadf5af0fcb1462e6aece1f70aa2ab6
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    3

commit 646f4082ee2cb77cd11179fe33be1890f04a4c7d
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    2

commit c6501b32d69cdc9d79bcd6dc6b8220456c4ceb02
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    1

Noticed the word "replaced" at the head commit? 注意到开头提交中的“已替换”一词? Our replace command replaced this commit with a commit with identical content but a different parent. 我们的replace命令用内容相同但父级不同的提交替换了该提交。 Looks good, right? 看起来不错吧? You can check, the commit hashes from commits 1 to 3 are the same as on our original master branch. 您可以检查一下,提交1到3的提交哈希与我们原来的master分支上的相同。 Looks good for merging, so let's do that now. 看起来很适合合并,所以现在就开始做吧。

user@host:/tmp/git-replace-test/orig-repo (master)$ git merge -m 'Merge in fork' fork/master
Merge made by the 'recursive' strategy.
 foo | 1 +
 1 file changed, 1 insertion(+)
user@host:/tmp/git-replace-test/orig-repo (master)$ git log
commit 9d8a33dd4ec3a8bdf746e717ccc3d9df74af66f5 (HEAD -> master)
Merge: a406f35 61ca43d
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:37 2019 +0200

    Merge in fork

commit a406f35bae904389b739c2a06cebd15e87146f21
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    4

commit 61ca43d062128c9fcddb9352698363e1bcf12a86 (replaced, fork/master)
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    b

commit 2ff1b501ecadf5af0fcb1462e6aece1f70aa2ab6
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    3
...

Nearly perfect. 几乎完美。 But there is still this replaced commit lingering in there. 但是仍然有替换的提交在其中徘徊。 We should get rid of that now as it can cause problems. 我们现在应该摆脱它,因为它可能会引起问题。

user@host:/tmp/git-replace-test/orig-repo (master)$ git filter-branch -- --all
Rewrite c6501b32d69cdc9d79bcd6dc6b8220456c4ceb02 (1/7) (0 seconds passed, remaining 0 predicteRewrite 646f4082ee2cb77cd11179fe33be1890f04a4c7d (2/7) (0 seconds passed, remaining 0 predicteRewrite 2ff1b501ecadf5af0fcb1462e6aece1f70aa2ab6 (3/7) (0 seconds passed, remaining 0 predicteRewrite 7cfc7a8647fd74696852e05635a6eb3c823d3766 (4/7) (0 seconds passed, remaining 0 predicteRewrite a406f35bae904389b739c2a06cebd15e87146f21 (5/7) (0 seconds passed, remaining 0 predicteRewrite 61ca43d062128c9fcddb9352698363e1bcf12a86 (6/7) (0 seconds passed, remaining 0 predicteRewrite 9d8a33dd4ec3a8bdf746e717ccc3d9df74af66f5 (7/7) (0 seconds passed, remaining 0 predicted)    
Ref 'refs/heads/master' was rewritten
Ref 'refs/remotes/fork/master' was rewritten
WARNING: Ref 'refs/replace/61ca43d062128c9fcddb9352698363e1bcf12a86' is unchanged
user@host:/tmp/git-replace-test/orig-repo (master)$ git log
commit d84bcb9117f93655b72843cb051c923d1ea2ddb1 (HEAD -> master)
Merge: a406f35 7cfc7a8
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:37 2019 +0200

    Merge in fork

commit a406f35bae904389b739c2a06cebd15e87146f21
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    4

commit 7cfc7a8647fd74696852e05635a6eb3c823d3766 (fork/master)
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    b

commit 2ff1b501ecadf5af0fcb1462e6aece1f70aa2ab6
Author: User Name <user@example.org>
Date:   Wed Sep 4 23:20:28 2019 +0200

    3
...

Commit b is now a real commit in that branch, but it also got a new hash. 现在,提交b是该分支中的实际提交,但它也获得了新的哈希。 The commits from the original repo kept their hashes, so no need to force push or anything. 来自原始存储库的提交保留了其哈希值,因此无需强行推动或执行任何操作。

I hope this gives you the result you were hoping for. 我希望这能给您您所期望的结果。

Please bear in mind that this is a simple example. 请记住,这是一个简单的示例。 Your mileage with a big repo may vary. 您的大回购里程可能会有所不同。 I also made the assumption that only one commit in the fork has the inital commit as parent, otherwise you probably have to use replace on each of them. 我还假设分叉中只有一个提交将初始提交作为父级,否则您可能必须在每个提交上使用replace。

Good luck. 祝好运。

This is a little tricky since there isn't a shared history between the two repos. 这有点棘手,因为两个存储库之间没有共享的历史记录。 With that said, here are two commands that I would use to get started if I were in the same situation: 话虽如此,如果我处在相同的情况下,这是两个我可以用来入门的命令:

  1. git remote - You can create multiple remotes in a single repository with git remote . git remote您可以使用git remote在单个存储库中创建多个git remote By default, git clone creates a remote named origin . 默认情况下, git clone创建一个远程命名origin You can add other remotes with git remote add <uri> where <uri> can be a URL or a file path. 您可以使用git remote add <uri>添加其他远程,其中<uri>可以是URL或文件路径。

  2. git rebase - Use this to copy commits from one history to another. git rebase使用它可以将提交从一个历史复制到另一个历史。 I'm actually not sure how this works when dealing with two unrelated histories. 我实际上不确定在处理两个不相关的历史记录时这是如何工作的。 I suggest looking at git help rebase for more information. 我建议查看git help rebase以获取更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM