2

几天前,我从 master 分支,并随后对newbranch.

后来,我注意到我第一次提交的文件不应该被更改。我还没有将任何东西推送到主分支,我所有的更改都在newbranch. 当我最初从 master 分支时,如何将这个文件回滚newbranch到它所在的位置?

4

3 回答 3

2

git checkout master -- filepath

于 2013-10-29T21:25:47.610 回答
1

根据您想要的结果,有几种不同的方法可以做到这一点。

euphoria83的回答是这样的……

开始时,您的提交历史记录如下所示(每个字母代表一个提交):

A - B - C          <-- master
          \
            D
              \
                E  <-- HEAD=newbranch

提交C是您开始时在 master 中的内容。您进行了一些更改并提交(创建D),然后进行了更多更改并提交(创建E)。

现在你运行git checkout HEAD~1. 这将更新您的工作目录以匹配提交 D,并为您提供一个“分离的 HEAD”(法国大革命的阴影!)直接指向 commit D,而不是包含分支名称:

A - B - C          <-- master
          \
            D      <-- HEAD
              \
                E  <-- newbranch

(这种形式的 checkoutgit checkout <branch-name-or-rev>告诉 git 更改HEAD名称。<rev>在这种情况下,HEAD~1的意思是“查找什么提交 HEAD 名称并移回一个”,所以这表示从 commit 移回一个E。因为这是一个特定的<rev>和不是分支名称,它也具有“分离 HEAD”效果。)

现在你运行git checkout HEAD~1 filename. 为了更明确地了解 git,您可以说git checkout HEAD~1 -- filename. 说“剩余的--参数是文件名,而不是分支或修订名称”——如果你省略--,git 会尝试猜测它是分支名还是文件名。

这种形式的checkout, 带有-- filename参数,做了一些相当不同的事情:它说像往常一样查找修订版HEAD~1,但是这次,不要更改 HEAD,只需提取给定的文件。由于HEAD现在命名为 revision D,git 再备份一个,以 revision C,并从该修订中提取文件的filename版本并将其放在工作目录中。

(请注意,分支名称master 命名为 revision C,因此您可以git checkout master -- filename在此时编写。)

接下来,git commit -a --amend告诉 git 添加任何更改的文件——在这种情况下,你实际上并不需要它,但通常这会添加你已经修复的其他文件——然后执行“修改提交”。“修改提交”意味着“创建一个新提交,但使其父提交与我们的父提交相同”。这会创建一个新的提交——我们称其D'为父提交C。与往常一样,新的提交变为HEAD. 由于HEAD已分离,因此尚未移动分支名称。生成的提交树如下所示:

A - B - C               <-- master
        | \
        \   D
          \   \
            \   E       <-- newbranch
              \
                D'      <-- HEAD

中的文件与D'中的相同D,除了文件filename,现在与修订中的相同C

接下来,git cherry-pick newbranch说要获得由newbranch-that's revision 命名的修订版E- 并对 HEAD 修订版进行相同的更改,作为一个新的提交(我们称之为E'):

A - B - C               <-- master
        | \
        \   D
          \   \
            \   E       <-- newbranch
              \
                D' - E' <-- HEAD

现在你只需要给HEAD. 然后可以安全地删除newbranch,或将其重命名,然后重命名newnewnewbranch

git checkout -b newnew          # now HEAD=newnew which points to E'
git branch -m newbranch oldnew  # rename out of the way
git branch -m newnew newbranch  # and rename newnew to newbranch

(你可以缩短一点,但让我们继续......)


有一种更简单的方法可以做同样的事情。跑吧git rebase -i master。这表示将当前分支 ( newbranch)master用作其“上游”。也就是说,在master(这意味着,DE)之后找到所有提交并将它们重新定位到master(这是它们之前的基础)。没有-i这将是愚蠢的——变基DE完全按照以前的方式,这只是一种什么都不做的昂贵方式——但是使用-igit 打开一系列命令的编辑器。这些命令将pick提交DE. pick将for commit更改Dedit,并写出文件。

然后,rebase 将为您挑选D并停止并让您修改提交。在 shell 中,输入:

git checkout master -- filename
git commit -a --amend

和以前一样,保存修改后的提交,它变成D'. 然后运行:

git rebase --continue

Git 现在将挑选 commit E,给出 commit E'。现在一切都完成了,所以 rebase 完成,并且分支newbranch有你想要的提交。


ДМИТРИЙ МАЛИКОВ的回答做了一些不同的事情。它包含相同的git checkout master -- filename(拼写为filepath)但没有类似 rebase 的序列。

所以,和以前一样,你从这个开始:

A - B - C             <-- master
          \
            D
              \
                E     <-- HEAD=newbranch

filename然后您将正在修订的版本提取C到工作目录中。如果你现在git commit(或者git commit -a,在这种特殊情况下也是一样的——git,这是因为上面首先checkout将提取的 rev-C 文件写入暂存区),你将获得一个新的提交F,将文件更改filename回原来的样子C.

换句话说,提交D并且E仍然会对文件进行更改;新的提交F将撤消更改。

于 2013-10-29T22:20:09.167 回答
1

在 new_branch 上:

git checkout HEAD~1
git checkout HEAD~1 file_not_needed
git commit -a --amend
git cherry-pick new_branch
git branch -f new_branch HEAD
于 2013-10-29T21:26:42.500 回答