5

我无法弄清楚如何将一个分支更新为与另一个分支相同。这是一个例子:

git init test; cd test
echo apple >a; echo banana >b; git add a b; git commit -m 'A1/a:apple;b:banana'
echo carrot >c; git add c; git commit -m 'A2/c:carrot'
git checkout -b second HEAD^1
echo beets >b; echo dandelion >d; git add b d; git commit -m 'B1/b:beets;d:dandelion'

在这一点上,我的历史看起来像这样:

A1-----A2     (master, contains a:apple, b:banana, c:carrot)
 \
  \----B1     (second, contains a:apple, b:beets, d:dandelion)

现在我想向“second”分支添加一个提交,使其内容与“master”分支匹配,即:

A1-----A2
 \
  \----B1--?--B2     (desired contents a:apple, b:banana, c:carrot)

我的问题是,我要运行哪些 git 命令来执行此操作?我想出的最接近的是:

git checkout master .
git commit -m B2

但是,虽然这会将 b 重置为香蕉并恢复 c,但它不会删除文件 d。我也无法想出任何git reset我想要的变体。我不认为我想做一个版本git revert,因为在我的实际存储库中,分支上的历史比这个例子更复杂,并且分支可能包含合并。

任何帮助将不胜感激!

4

5 回答 5

2

The simple way

You almost had the right answer. The solution is to first remove all of the files before checking out from master.

git checkout second
git rm -rf .
git checkout master .
git commit -m B2

This will create a new commit with a new message, author, and timestamp. If you all that to be the same, your best bet is...

Using git-reparent (shameless plug)

Download git-reparent, install it in your $PATH, and then

git checkout second
git branch parent
git reset --hard master
git reparent -p parent
git branch -d parent

Explanation:

  1. Switch to the branch we want to modify (second).
  2. Save a pointer (parent) to the current commit.
  3. Point the commit that we want to end up at (master).
  4. Modify the current branch (second) to have the contents of the current commit but have the given parents (parent).
  5. Delete our saved pointer (parent).
于 2013-04-04T03:31:04.137 回答
2

我假设当前分支是second. 如果您不满意git reset --HARD master(因为如果在任何地方发布它将需要强制推送),您可以通过(或者只是如果您不想列出所有提交,例如 B1)second撤消 B1 ,然后.git revert B1git revert master..secondgit merge master

使用@StevenPenny 的想法和ours策略我想出了这个解决方案:

git checkout master
git checkout -b transfer_second_to_master
git merge -s ours second
git checkout second
git merge transfer_second_to_master
git branch -d transfer_second_to_master

这是一种theirs策略模拟,作为策略的倒置ours。现在git diff master second什么都不给,这意味着分支是相同的。

于 2013-04-03T23:55:47.610 回答
1

这听起来像是一份工作 git merge

git checkout master
git merge -s ours second

更多信息

这解决了任意数量的头,但合并的结果树总是
当前分支头的变化,有效地忽略所有其他分支的所有更改
分支机构。它旨在用于取代side的旧开发历史
分支机构。
于 2013-04-03T23:56:26.427 回答
1

A1-----A2     (master, contains a:apple, b:banana, c:carrot)
 \
  \----B1     (second, contains a:apple, b:beets, d:dandelion)
  • 当前分支:第二
  • git status: 干净的

变体 1:没有引用 master(非常丑陋)

git read-tree -m -u master
git commit -m 'B2: copy of A2'
git diff master

后:

A1-----A2      (master, contains a:apple, b:banana, c:carrot)
 \
  \----B1---B2 (second, contains a:apple, b:banana, c:carrot)
  • 当前分支:第二个(ff)
  • git status: 干净的
  • diff状态为master:干净

确认回退工作:

git reset --hard HEAD^
git clean -f
git branch -avv

再次显示之前的状态。

这个变体有什么作用?

  • git read-tree将给定的其他提交(此处master)读入 INDEX
  • 作为选项-u,它会相应地更新工作树
  • 然后像往常一样将其提交到 B1
  • 但是,它不会添加对“合并”树的引用。

变体 2:显示对 master 的合并引用(首选!)

对不起,我没有找到正确的方法。这是一种解决方法:

第一:做一个虚拟合并

git merge -s ours -m 'B2 dummy' master

第二:修复合并到正确的数据

git read-tree -m -u master
git commit --amend -m 'B2: copy of A2'

第三:检查它是否真的匹配master

git diff master

后:

A1-------A2    (master, contains a:apple, b:banana, c:carrot)
 \         \
  \----B1---B2 (second, contains a:apple, b:banana, c:carrot)
  • 当前分支:第二个(ff,合并,第一个父级:B1)
  • git status: 干净的
  • diff状态为master:干净

确认回退工作:

git reset --hard HEAD^
git clean -f
git branch -avv

再次显示之前的状态,因为它应该。

这是如何运作的:

  • git read-tree -m -u master重置合并信息,所以我们不能开始合并,读取树然后提交这个合并,很遗憾。
  • 但是我们想使用 option -u,因为这对于更新工作树至关重要。但是-u需要-m或类似的东西会让我们退出合并模式。
  • 所以我们之前做了一个虚拟合并(并使用策略ours来防止合并冲突,这正是我们想要发生的错误事情)。
  • git read-tree然后我们根据需要从 master 读取状态
  • 现在我们amend与现在终于正确的索引内容合并。
  • 这将保留原始合并信息。
  • 请注意,这是ff根据上游。

这有什么问题?

  • git commit意味着是原子的。要么你有一个正确的状态,要么没有。
  • 然而,这引入了一种非原子情况,在这种情况下,竞争条件(停电)可能会使您的本地 git repo 处于某种不需要的状态
  • 如果在自动设置中使用此策略,这是不好的,例如,在发布 (B1) 之后,当前上游(主)将用于重新启动以创建下一个版本(B2 和更高版本)。
  • 因此,变体 2 不能被视为对此的最终定论。是的,它以某种方式有效,但它既不漂亮、干净也不完美。

其他解决方案似乎太复杂了。但也许我监督了一点?

  • 要么使用完全难以理解的 git-plumbing 命令来自己创建正确的提交对象——不适合易于理解和使用的解决方案。

  • 或者做一些非常复杂的子分支,然后快进到子分支,然后删除它 - 双重禁止,因为这甚至会引入更复杂的事情,比如如果出现问题需要进行复杂的清理。

  • 或者找出需要哪些命令来将工作树更新为 a 之后的索引(就像-u已经可以做到的那样重新发明轮子)git read-tree master,因为这应该发生在(!)提交之前(像往常一样)。

去做

这尚未通过子模块进行彻底测试。如果它们被添加、删除、替换等,.gitmodules并且.git/modules/已经更新或阻止了一些东西。Loooooong 的故事。

于 2016-03-11T17:53:29.677 回答
0

听起来您只想second从 A2 开始分支。这很简单:

git branch -f second <commit-for-A2>

-f标志的意思是“重置分支以提交”。没有理由添加提交来展开/重置second分支。

[编辑] 如果您不能简单地重置,则执行 B1 的还原(在 上second),然后将 A2 从合并mastersecond. 像这样:

ebg@ebg(168)$ git log --graph --oneline --all
* ad6fb9d B1/b:beets;d:dandelion
| * 64c9f45 A2/c:carrot
|/  
* 07a4ceb A1/a:apple;b:banana
ebg@ebg(169)$ ls
a       b       d
ebg@ebg(170)$ git revert HEAD
[second 2b1de0c] Revert "B1/b:beets;d:dandelion"
 2 files changed, 1 insertion(+), 2 deletions(-)
 delete mode 100644 d
ebg@ebg(171)$ ls
a       b
ebg@ebg(172)$ git merge master
Merge made by the 'recursive' strategy.
 c |    1 +
 1 file changed, 1 insertion(+)
 create mode 100644 c
ebg@ebg(173)$ ls
a       b       c
ebg@ebg(174)$ git log --graph --oneline --all
*   0ffd24e Merge branch 'master' into second
|\  
| * 64c9f45 A2/c:carrot
* | 2b1de0c Revert "B1/b:beets;d:dandelion"
* | ad6fb9d B1/b:beets;d:dandelion
|/  
* 07a4ceb A1/a:apple;b:banana
于 2013-04-04T02:22:20.533 回答