我有两个分支--A和B. B是从A。
创建的两个分支机构的工作同时进行。分支A上的工作很糟糕(导致非工作版本),而分支B上的工作很好。在此期间,分支B有时会合并到分支A(但不是相反)。
现在我想让分支A与分支B相同。我不能使用git revert,因为我需要恢复过多的提交 - 我只想恢复在分支A上完成的提交但不是合并分支B的结果。
我找到的解决方案是将分支B克隆到另一个文件夹,从分支A的工作文件夹中删除所有文件,从temp分支B文件夹中复制文件并添加所有未跟踪的文件。
是否有一个git命令执行相同的操作?我错过了一些git revert开关?
答案 0 :(得分:34)
有很多方法可以做到这一点,你应该使用哪种方法取决于你想要的结果,特别是你和任何与你合作的人(如果这是一个共享的存储库)希望将来看到的。 / p>
执行此操作的三种主要方式:
A
,制作一个新的A2
,然后使用它。使用git reset
或等效内容在其他地方重新点A
。
从长远来看,方法1和2实际上是相同的。例如,假设您首先放弃A
。每个人都会在A2
上发展一段时间。然后,每个人都使用A2
后,您将完全删除名称A
。现在A
已经消失,您甚至可以将A2
重命名为A
(其他人使用它时必须进行同样的重命名)。此时,如果有的话,在使用方法1的情况和使用方法2的情况之间看起来有什么不同? (有一个地方你可能仍然能够看到差异,这取决于你的长期运行时间和旧的reflogs到期时间。)
-s theirs
,但它是doesn't exist,你必须假装它。旁注:分支是"从"基本上是不相关的:git没有跟踪这些事情,一般来说以后都不可能发现它,所以对你来说也许并不重要。 (如果它真的对你很重要,你可以对你如何操纵分支施加一些限制,或者用标记标记它的起始点"以便你可以在以后机械地恢复这些信息。但是这个通常表明你在第一时间做错了什么,或者至少,期待git没有提供的git。参见下面的脚注1。)
分支 - 或者更准确地说,分支名称 -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
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 merge
和recursive
,但它们并没有达到我们想要的效果。这些只是说在冲突的情况下,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的处理方式) ...
答案 6 :(得分:0)
这就是TortoiseGit
对我有用的
branch
作为您想与您的首选位置保持一致的ZIP文件: .git
文件夹从原始代码库复制到此unzipped-folder
( ENSURE 在原始代码中选择target branch
-base)git add .
git commit -m "Achieving identicality with abc-branch"
git push
将更改推送到存储库。 (--up-stream
(如果需要)祝你好运...
答案 7 :(得分:-1)
git merge A
如果头在分支b中,是使两个分支相同的最佳方法