使两个分支相同

时间:2016-03-30 22:00:46

标签: git version-control merge

我有两个分支--A和B. B是从A。

创建的

两个分支机构的工作同时进行。分支A上的工作很糟糕(导致非工作版本),而分支B上的工作很好。在此期间,分支B有时会合并到分支A(但不是相反)。

现在我想让分支A与分支B相同。我不能使用git revert,因为我需要恢复过多的提交 - 我只想恢复在分支A上完成的提交但不是合并分支B的结果。

我找到的解决方案是将分支B克隆到另一个文件夹,从分支A的工作文件夹中删除所有文件,从temp分支B文件夹中复制文件并添加所有未跟踪的文件。

是否有一个git命令执行相同的操作?我错过了一些git revert开关?

8 个答案:

答案 0 :(得分:34)

有很多方法可以做到这一点,你应该使用哪种方法取决于你想要的结果,特别是你和任何与你合作的人(如果这是一个共享的存储库)希望将来看到的。 / p>

执行此操作的三种主要方式:

  1. 不要打扰。放弃分支A,制作一个新的A2,然后使用它。
  2. 使用git reset或等效内容在其他地方重新点A

    从长远来看,方法1和2实际上是相同的。例如,假设您首先放弃A。每个人都会在A2上发展一段时间。然后,每个人都使用A2后,您将完全删除名称A。现在A已经消失,您甚至可以将A2重命名为A(其他人使用它时必须进行同样的重命名)。此时,如果有的话,在使用方法1的情况和使用方法2的情况之间看起来有什么不同? (有一个地方你可能仍然能够看到差异,这取决于你的长期运行时间和旧的reflogs到期时间。)

    < / LI>
  3. 使用特殊策略进行合并(请参阅&#34;替代合并策略&#34;下面)。你想要的是-s theirs,但它是doesn't exist,你必须假装它。
  4. 旁注:分支是&#34;从&#34;基本上是不相关的:git没有跟踪这些事情,一般来说以后都不可能发现它,所以对你来说也许并不重要。 (如果它真的对你很重要,你可以对你如何操纵分支施加一些限制,或者用标记标记它的起始点&#34;以便你可以在以后机械地恢复这些信息。但是这个通常表明你在第一时间做错了什么,或者至少,期待git没有提供的git。参见下面的脚注1。)

    分支的定义(另见What exactly do we mean by branch?

    分支 - 或者更准确地说,分支名称 -in git只是指向提交图中某些特定提交的指针。其他引用(如标记名称)也是如此。与标签相比,分支的特殊之处在于,当您 on 分支并进行 new 提交时,git会自动更新分支名称,以便它现在指向新的提交。

    例如,假设我们有一个类似于此的提交图,其中o个节点(以及标记为*的节点)表示提交,A和{{1是分支名称:

    B

    o <- * <- o <- o <- o <- o <-- A \ o <- o <- o <-- B A每个都指向分支的最尖端提交,或者更确切地说,分支数据结构由一些提交和工作开始形成通过所有可达提交,每次提交都指向一些父提交。

    如果您使用B以便在分支git checkout B上进行新的提交,则会使用上一个B提示设置新提交(单个)parent,并且git更改存储在分支名称B下的ID,以便B指向新的提示最多提交:

    B

    标记为o <- * <- o <- o <- o <- o <-- A \ o <- o <- o <- o <-- B 的提交是两个分支提示的合并基础。这个提交和所有早期的提交都在两个分支上。 1 合并基础很重要,合并(duh :-)),但也适用于{ {1}}和其他发布管理类型的操作。

    您提到分支*偶尔会合并回git cherry

    B

    这会产生一个新的合并提交,这是一个包含两个 2 父项的提交。 第一个父级是当前分支的前一个提示,即A的上一个提示,第二个父级是命名提交,即分支的最尖端提交{ {1}}。稍微重新绘制上面的内容(但添加更多git checkout A; git merge B 以使A的排列更好),我们从:

    开始
    B

    并以:

    结束
    -

    我们将o移动到o--*--o--o--o--o <-- A \ o---o--o---o <-- B o--*--o--o--o--o--o <-- A \ / o---o--o---* <-- B 的新合并基础(实际上是*的提示)。据推测,我们会向A添加更多提交,并且可能会合并几次:

    B

    git默认使用B

    执行什么操作

    要进行合并,请检查一些分支(在这些情况下为B),然后运行...---o--...--o--o <-- A / / ...-o--...--*--o--o <-- B 并为其提供至少一个参数,通常是另一个分支名称,如git merge <thing>。 merge命令首先将名称转换为提交ID。分支名称将变为分支上最末端提交的ID。

    接下来,A找到合并基础。这些是我们一直用git merge标记的提交。合并基础的技术定义是提交图中的最低公共祖先(在某些情况下可能不止一个),但我们只是使用#34;提交标记为B& #34;这里为了简单起见。

    最后,对于普通合并,git运行两个git merge命令。 3 第一个*将提交*git diff提交进行比较,即,当前分支的尖端。第二个差异将提交git diff与参数提交进行比较,即另一个分支的提示(您可以命名一个特定的提交,它不必是分支的提示,但在我们的例子中,我们是&#39;将*合并到HEAD中,以便我们获得这两个分支提示。)

    当git发现某些文件被修改时,与基于合并的版本相比,在两个分支中,git尝试以半智能(但不是非常智能)的方式组合这些更改:两个更改都将相同的文本添加到同一区域,git保留一个添加的副本。如果两个更改都删除了同一区域中的相同文本,git只删除该文本一次。如果两个更改都修改了同一区域中的文本,则会产生冲突,除非修改完全匹配(然后您将获得一个修改副本)。如果一方进行一次更改而另一方做出不同的更改,但更改似乎不重叠,则git会进行两项更改。这是三向合并的本质。

    最后,假设一切顺利,git会做出一个新的提交,其中有两个(或者我们在脚注2中已经注明过的)父母。与这个新提交相关联的 work-tree 是一个git在进行三向合并时提出的。

    替代合并策略

    虽然*的默认B策略包含A个选项git mergerecursive,但它们并没有达到我们想要的效果。这些只是说在冲突的情况下,git应该通过选择&#34;我们的变化&#34;来自动解决冲突。 (-X)或&#34;他们的变化&#34; (ours)。

    theirs命令完全有另一个策略,-X ours:这个说明不是将合并基础与两个提交区分开来,而是使用我们的源代码树。换句话说,如果我们在分支-X theirs上运行merge,git将进行新的合并提交,第二个父代是分支-s ours的提示,但是源树与分支A的上一个提示中的版本匹配。也就是说,新提交的代码将与其父代码完全匹配。

    正如this other answer中所述,有很多方法可以强制git有效地实现git merge -s ours B。我认为最简单的解释就是这个序列:

    B

    第一步是像往常一样确保我们在分支A上。第二个是启动合并,但是避免提交结果(-s theirs)。为了使git更容易合并 - 这不是必需的,它只是让事情更快更安静 - 我们使用git checkout A git merge --no-commit -s ours B git rm -rf . # make sure you are at the top level! git checkout B -- . git commit 以便git可以完全跳过差异步骤,我们避免所有合并冲突投诉。

    在这一点上,我们得到了诀窍。首先,我们删除整个合并结果,因为它实际上毫无价值:我们不希望工件树来自A的提示,而是来自--no-commit的提示。然后我们从-s ours的提示中查看每个文件,使其准备好提交。

    最后,我们提交了新的合并,它的第一个父项是A的旧提示,第二个父项是B的提示,但是有来自提交B

    如果提交之前的图表是:

    A

    然后新图表现在是:

    B

    新的合并库像往常一样是B的提示,从提交的角度来看,这种合并看起来与任何其他合并完全一样。不同寻常之处在于...---o--...--o--o <-- A / / ...-o--...--*--o--o <-- B 顶端新合并的源代码树...---o--...--o--o--o <-- A / / / ...-o--...--o--o--* <-- B 提示的源树完全匹配。

    1 在这种特殊情况下,由于两个分支保持独立(从未被合并),它也可能两个分支中的一个分支是创建的(或者甚至可能在两者都被创建的情况下),虽然你现在无法证明这一点(因为有人可能已经使用过B或其他各种技巧来移动分支标签) 。但是,一旦我们开始合并,合并基础显然不再是起点,而且合理的起点更难找到。

    2 从技术上讲,合并提交是指具有两个或更多父项的任何提交。 Git调用与两个以上父母的合并&#34;章鱼合并&#34;。根据我的经验,除了git本身的git存储库之外,它们并不常见,最后,它们实现了与多个普通双父合并相同的功能。

    3 差异通常在内部完成,而不是运行实际的命令。这允许进行大量的快捷优化。这也意味着如果您编写自定义合并驱动程序,则不会运行该自定义合并驱动程序,除非git发现在两个差异中都修改​​了该文件。如果它只在两个差异中的一个中被修改,则默认合并只需要修改一个。

答案 1 :(得分:7)

检查目标分支:

git checkout A;

删除分支A中的所有内容并使其与B:

相同
git reset B --hard;

如果您需要放弃所有本地更改(恢复类型,但不会像git revert这样混乱):

git reset HEAD --hard;

完成后,不要忘记更新远程分支(如果需要覆盖历史记录,请使用--force-f标记。)

git push origin A -f;

答案 2 :(得分:2)

这篇文章中的答案方法太复杂了,无法满足我的口味。

这就是我所做的:

假设您在要更改的分支上

第1步-使分支与master相同

git reset origin/master --hard

第2步-拉出您想要与之相同的分支

git pull origin branch-i-want-to-be-identical-to

第3步(可选)-强制将其推向远程以覆盖远程历史记录

git push --force

答案 3 :(得分:0)

您需要git reset分支A到分支B. 请参阅docs

答案 4 :(得分:0)

这是我用来帮我做这件事的好方法。 这个想法是获取A和B的差异,提交差异,将其还原以创建相反的提交,而不是将最后一个还原提交到A的樱桃选择

这是命令

git checkout B
git checkout -b B_tmp 
git merge --squash A
git commit -a -m 'diff'
git revert ⬆_commit_sha(paste here 'diff' commit sha, get from 'git log -1')
git checkout A                    
git cherry-pick revert_commit_sha (B_tmp branch last commit)
git branch -d B_tmp

在这里,A与B相同,并且在A历史记录中只有一次提交会重置为与B分支相同。

答案 5 :(得分:0)

这是不得已的主意(这很脏而且不是git的处理方式) ...

  1. 转到分支B(git结帐B)。
  2. 复制该分支的所有内容(ctrl + c)
  3. 转到分支A(git结帐A)
  4. 删除分支A中的所有内容(使用鼠标全选并删除)
  5. 将分支B的所有内容复制到所有分支A所在的文件夹中。 (Ctrl + V)
  6. 暂存所有新更改(git add)
  7. 提交分阶段的更改(git commit -m“分支A现在与B相同”)

答案 6 :(得分:0)

这就是TortoiseGit对我有用的

STEPS:

  1. 导出 branch作为您想与您的首选位置保持一致的ZIP文件:enter image description here

  2. UNZIP 存档/压缩文件。

  3. .git文件夹从原始代码库复制到此unzipped-folder ENSURE 在原始代码中选择target branch -base)

  4. git add .

  5. git commit -m "Achieving identicality with abc-branch"

  6. git push将更改推送到存储库。 (--up-stream(如果需要)

TortoiseGit:https://tortoisegit.org

祝你好运...

答案 7 :(得分:-1)

 git merge A 
如果头在分支b中,

是使两个分支相同的最佳方法