【问题标题】:Transfer most recent commit to another branch将最近的提交转移到另一个分支
【发布时间】:2014-03-06 13:13:04
【问题描述】:

我为某些测试目的创建了一个额外的分支。

在开始工作之前,我切换回了master分支,喝了茶之后,我开始在master分支中添加文件和修改一些其他文件。

只有在我提交之后,我才想起我在 master 分支,而在开始更改之前我必须切换到我的第二个分支。

能否告诉我是否可以将此提交发送到第二个分支并将其从主分支中删除?

谢谢

【问题讨论】:

  • 请在尝试之前备份(不是 100% 确定):git reset --soft HEAD^git checkout second-branchgit commit

标签: git


【解决方案1】:

如果我理解正确,你可以做这样的事情:

git checkout master
git log
now check hash commit you want to move - for example: 0123456
git checkout branch
git cherry-pick 0123456
git checkout master
git revert 0123456

就是这样。

【讨论】:

  • 请注意,这将让您挑选出不在“分支尖端”的提交,然后将其还原(因此它比我的答案更笼统)。我的回答是回滚git reset 步骤,专门针对“您在错误分支的尖端进行了提交”。
  • 我有一个需要丢弃的提交。提交仅在本地机器上可用,在远程机器上不可用。我试过了,现在在 master 上有 2 次提交。而我希望主人没有这些提交。有什么想法,我该怎么做?
【解决方案2】:

这个问题的答案是“有点,但也许你不必”。

复制提交,请使用git cherry-pick <em>commit</em>。这实质上是告诉 git:“去看看我要求你挑选的提交。无论它做了什么更改,在当前分支上再次进行相同的更改。然后进行新的提交1具有相同的提交消息。”

因此,如果您已经在 master 上进行了提交,而您希望在 newbranch 上进行提交:

$ git checkout newbranch
$ git cherry-pick master

此时,您将在newbranch 上,并且提交将被复制。您现在需要对 master 上的旧提交做一些事情。

如果您尚未在任何地方发布(推送或让其他人拉取)此提交,则只需“回滚”:

$ git checkout master
$ git reset --hard HEAD^  # be careful here! I always run "git status" first

请注意,git reset --hard 将清除所有正在进行的工作。

如果您已经发布了提交,您通常应该将其还原(参见user2699113's answer)。 “还原”很像挑选樱桃,复制提交,不同之处在于它反转更改:当时添加的任何内容现在都将被删除,而删除的任何内容都会被添加回来。 (而且,与cherry-pick 一样,revert 会自动提交,除非你告诉它不要这样做。)

(请注意,您可以通过 SHA-1 ID 指定提交,但如果有名称(分支或标签名称,或其他参考名称),您可以只写名称。)


1除非您使用-n--no-commit 来抑制它,否则git 无法进行相同的更改并告诉您存在冲突并让您自己修复.


“也许你不必”部分是怎么回事?

这是关于 git 及其提交和分支的事情。 Git 与大多数其他版本控制系统不同,当您在“分支上”进行提交时,分支 name 实际上并不是提交的一部分。2分配分支名称(或标签,无论您想如何称呼它们),但实际的分支结构——“结构分支”——是由每个提交的历史形成。

提交时,该提交记录其父提交。分支名称只是标签,它为您(和 git)提供了一个起点,然后 git 遵循提交父 ID。这意味着我们可以手动绘制提交图(有向无环图或提交的 DAG):

A <-- B <-- C     <-- master
            ^
             \
              D   <-- branch

这里commit D是分支branch的尖端,它指向commit C作为它的父级。提交C 指向B,它指向A,这是初始(根,无父)提交。

当你“在分支主”上并创建一个新分支时,只会创建一个新的 标签,它指向你现在所在的同一个地方。所以假设你之前有AC(这次我不会用那么多箭头来画这些;假设箭头通常指向左):

A - B - C      <-- HEAD=master

这次我添加了HEAD= 来展示 git 如何跟踪你“在”哪个分支。 (.git 目录中的 HEAD 文件只包含分支的名称:cat .git/HEAD,您通常会看到 ref: refs/heads/master 或其他任何内容。)如果您现在执行 git checkout -b branch,您会得到:

A - B - C      <-- master, HEAD=branch

(现在.git/HEAD 包含branch 而不是master)。

当您进行新提交(任何新提交,包括来自cherry-pick 和revert 的提交)时,git 会查看HEAD 以查看您所在的分支,以及该分支的SHA-1 ID。这就是“树枝的尖端”。然后 Git 进行一个新的提交,其父是旧的分支提示,并将新的提交 ID 写入分支文件,以便分支名称指向新的提交。 (当然HEAD 仍然只有分支名称。)

所以,如果你“在分支 branch”上并进行新的提交 D,这就是你得到的:

A - B - C      <-- master
          \
            D  <-- HEAD=branch

如果你搞砸了一点,并且“在分支master”上,你会得到这个:

A - B - C      <-- branch
          \
            D  <-- HEAD=master

但是等等,这正是你想要的,除了分支标签。您需要做的就是将名称 master 指向 branch 所在的位置,并将 branch 指向 master 所在的位置,并切换 HEAD 使其显示为 branch 而不是 master

事实证明,这很容易……嗯,相当容易。 :-) 你需要一个临时名称,因为 git 不会让你有两个同名的分支。首先,将分支重命名为branch,让您更改master。然后将master重命名为branch,并将branch的新名称重命名为master

$ git branch -m branch temp
$ git branch -m master branch
$ git branch -m temp master

第一步将标签branch 更改为temp。第二个将master 更改为branch(你还在上面,所以也重写HEAD)。最后一个将temp 更改为master,为您提供最初想要的提交 DAG 和标签设置。

与其他部分一样,您应该只在未发布(未推送)提交上尝试这种“标签交换”。 (这样做的原因很简单:当您发布提交时,您通过 name-to-commit-ID 映射来完成。如果您将标签打乱并“重新发布”,则提交 ID 会没有改变,但名称到 ID 的映射会改变,所以任何拿起你之前广告的人,例如 master 意味着 bb71dde...,现在得到一个与旧映射不“快进相关”的新映射. 人们和 git 期望分支映射会改变,但通常只是“快进风格”。)


2例如,在 Mercurial 中,分支名称实际上记录在 commit-data 中。

【讨论】:

  • 非常好的答案!但是,如果修改已经被推送,分支标签交换技巧是否有效?恐怕它会弄乱提交历史。
  • 如果推送/发布,那么不,你不想那样做!好吧,除非每个与您一起处理已发布提交的人都可以接受。 :-)
  • @GabrielePetronella:“不[礼貌]回报”是“发布”而不是“推动”;当然,在许多情况下,这两个事件是同时发生的,但并非总是如此;例如,如果您对只有您有权访问的存储库执行 push,则可以使用后续的 push -f... 覆盖它。
  • @kjo 同意了。从“不要让你的同事讨厌你”的角度来看,这是有道理的。
  • 我认为“但也许你不必”的答案将是:如果你只是提交了一个提交,但提交给了错误的分支,那么你刚刚提交的内容(并且你现在希望提交到不同的分支)仍在您的工作目录中。因此,另一个可能更直接的解决方法是执行git reset HEAD^没有 --hard,然后是您要进行的提交。 (我想。我还在学习这些东西。我可能会将其作为单独的答案发布。)
猜你喜欢
  • 2018-08-12
  • 2011-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-20
  • 1970-01-01
  • 2022-10-19
相关资源
最近更新 更多