git:如何使用另一个分支的内容重置分支?

时间:2013-04-03 23:40:45

标签: git

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

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重置为banana并恢复c,但它不会删除文件d。我无法想出git reset的任何变体,它可以做我想做的事情。我认为我不想做git revert的版本,因为在我的实际存储库中,分支上的历史比这个例子更复杂,并且分支可能包含合并。

任何帮助将不胜感激!

5 个答案:

答案 0 :(得分:2)

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

ours策略中使用@StevenPenny构思我提出了这个解决方案:

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没有给出任何东西,它意味着分支是相同的。

答案 1 :(得分:1)

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

git checkout master
git merge -s ours second

更多信息

This resolves any number of heads, but the resulting tree of the merge is always
that of the current branch head, effectively ignoring all changes from all other
branches. It is meant to be used to supersede old development history of side
branches.

答案 2 :(得分:1)

简单方法

几乎有正确的答案。解决方案是在从master签出之前先删除所有文件。

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

这将创建一个带有新消息,作者和时间戳的新提交。如果你们都一样,最好的选择是......

使用git-reparent(无耻插件)

下载git-reparent,将其安装在$PATH,然后

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

说明:

  1. 切换到我们要修改的分支(second)。
  2. 将指针(parent)保存到当前提交。
  3. 将我们想要的提交指向(master)。
  4. 修改当前分支(second)以获取当前提交的内容但具有给定的父级(parent)。
  5. 删除我们保存的指针(parent)。

答案 3 :(得分:0)

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

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

-f标志表示“重置分支以提交”。没有理由添加提交来解除/重置second分支。

[edit]如果您无法简单地重置,请执行B1的恢复(在second上时),然后将master中的A2合并到second。像这样:

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

答案 4 :(得分:0)

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

变体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:clean
  • diff状态为master:clean

确认后退工作:

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

再次显示之前的状态。

这个变种做了什么?

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

变体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:clean
  • diff状态为master:clean

确认后退工作:

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

再次显示之前的状态。

这是如何运作的:

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

这有什么问题?

  • git commit意味着是原子的。你是否有正确的状态。
  • 然而,这引入了一种非原子状态,其中竞争条件(停电)可能会使您的本地git仓库处于某种不需要的状态
  • 如果此策略应在自动设置中使用,这是不好的,例如,在发布(B1)之后,当前上游(主)应用于重新启动以创建下一个版本(B2及更高版本)
  • 因此,变体2不能被视为对此的明确最后一句话。是的,不管怎样,它都有效,但它更整洁,干净,也不完美。

其他解决方案似乎过于复杂。但也许我会稍微监督一下?

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

  • 或做一些非常复杂的子分支然后快进到子分支,然后删除它 - 双重禁止,因为这甚至引入了更复杂的事情,如复杂的清理需要有些事情会破裂。

  • 或者找出在-u之后将工作树更新为索引(在[{1}}已经可以执行此操作时重新发明轮子)所需的命令,因为这应该发生在(!)提交之前(像往常一样)。

TODO

尚未对子模块进行彻底测试。如果他们被添加,删除,替换等,git read-tree master.gitmodules已更新或阻止某些事情。 Loooooong故事。