如何将HEAD移回之前的位置?(分离头)&撤销提交

在Git中,我试图通过合并另一个分支来做squash commit,然后通过以下方式将HEAD重置到之前的位置:

git reset origin/master

但我需要跳出来。如何将HEAD移回之前的位置?

我有需要移动到的提交的SHA-1片段(23b6772)。我怎样才能回到这个承诺?

466977 次浏览

在回答之前,让我们添加一些背景,解释HEAD是什么。

< >强First of all what is HEAD? < / >强

HEAD只是当前分支上当前提交(最新)的引用。
在任何给定时间只能有一个HEAD(不包括git worktree)

HEAD的内容存储在.git/HEAD中,它包含当前提交的40字节SHA-1。


< >强detached HEAD < / >强

如果你不在最近一次提交上——这意味着HEAD指向历史上的前一次提交,它被称为< >强detached HEAD < / >强

Enter image description here

在命令行中,它看起来像这样- SHA-1而不是分支名称,因为HEAD并不指向当前分支的顶端:

Enter image description here

Enter image description here


关于如何从分离的HEAD中恢复的一些选项:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits to go back

这将检出指向所需提交的新分支。
该命令将检出到给定的提交。
在这一点上,你可以创建一个分支,并从这里开始工作

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>


# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

你也可以总是使用reflog
git reflog 将显示更新了HEAD的任何更改,检出所需的reflog条目将把HEAD设置回此提交

每次HEAD被修改时,reflog中都会有一个新条目

git reflog
git checkout HEAD@{...}

这将使您回到期望的提交

Enter image description here


git reset --hard <commit_id>

“Move"你的头回到你想要提交的地方。

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32


# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.

enter image description here


git revert <sha-1>

< p >“Undo"给定的提交或提交范围。
恢复命令将“撤销”;在给定提交中所做的任何更改。
带有撤销补丁的新提交将被提交,而原始提交也将保留在历史记录中
# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>

这个模式说明了哪个命令做什么。
正如你在这里看到的,reset && checkout修改了HEAD.

. 0

Enter image description here

最快的解决方案(只需一步)

使用git checkout -

你会看到Switched to branch <branch_name>。确认这是您想要的分支。


简单解释:这个命令将把HEAD移回它的最后一个位置。参见答案后面结果的注释。


记住:这种方法很像使用cd -返回到你以前访问过的目录。语法和适用的情况是一个相当好的匹配(例如,当你实际上想要HEAD返回到它原来的位置时,它是有用的)。


更有条理的解决方案(2步,但令人难忘)

快速的方法解决了OP的问题。但是如果您的情况略有不同:假设您重新启动了Bash,然后发现自己与HEAD分离了。在这种情况下,这里有两个简单,容易记住的步骤。

1. 选择你需要的分支

使用git branch -v

您将看到现有本地分支的列表。获取适合您需要的分支名称。

2. 向目标前进

使用git checkout <branch_name>

你会看到Switched to branch <branch_name>。成功!


结果

使用任何一种方法,你现在都可以像以前一样继续添加和提交你的工作:你的下一个更改将被跟踪在<branch_name>上。

注意,如果你在分离HEAD时提交了更改,git checkout -git checkout <branch_name>都会给出额外的指示。

这个问题可以理解为:

我是在分离状态与HEAD23b6772和键入git reset origin/master(因为我想挤压)。现在我改变了我的想法,我如何回到HEAD23b6772?< / em >

简单的答案是:git reset 23b6772

但我打了这个问题,因为我厌倦了打字(复制&每次我想引用之前的HEAD时,提交哈希值或其缩写,并在谷歌上搜索是否有任何形式的速记。

事实证明是有的!

git reset -(在我的例子中是git cherry-pick -)

顺便提一下,它与cd -相同,返回到*nix中的先前当前目录 !哇,我一石二鸟。

第一个reset本地:

git reset 23b6772

看看你的姿势是否正确,验证一下:

git status

你会看到如下内容:

你的分支落后于'origin/master' 17次提交,

然后重写历史在您的远程跟踪分支上来反映变化:

git push --force-with-lease // a useful command @oktober mentions in comments

如果其他人同时已提交到远程分支,则使用--force-with-lease而不是--force将引发错误,在这种情况下,您应该先获取。更多信息请参见本文

当你运行命令git checkout commit_id时,从13ca5593d(say commit-id)和branch分离的HEAD将更长时间可用。

移动到之前的位置运行命令-

  1. git pull origin branch_name (say master)
  2. git checkout branch_name
  3. git pull origin branch_name

您将返回到以前的位置,并从远程存储库提交更新后的文件。

今天,我错误地签出了一个提交,并开始处理它,在分离HEAD状态下进行了一些提交。然后我用下面的命令推到远程分支:

git push origin HEAD: <My-remote-branch>

然后

git checkout <My-remote-branch>

然后

git pull

我终于在我的分支中得到了我在分离HEAD中所做的所有更改。

这可能不是一个技术上的解决方案,但它是有效的。(如果你的队友在本地有相同的分支)

让我们假设您的分支名称为branch-xxx

解决步骤:

  • 不要做更新或拉-什么都没有
  • 只需从他的机器上的branch-xxx创建一个新分支(branch-yyy)
  • 这就是全部,所有现有的更改将在这个新分支(branch-yyy)中。您可以继续使用这个分支进行工作。

注意:同样,这不是一个技术解决方案,但它肯定会有帮助。

移动最后一个未被推送的分支提交到一个新的分支

如果您的问题是您开始在WRONG_BRANCH上提交,并且希望将最后那些未推送的提交移动到RIGHT_BRANCH,最简单的方法是

  1. git checkout WRONG_BRANCH
  2. git branch RIGHT_BRANCH
  3. git reset —-hard LAST_PUSHED_COMMIT
  4. git checkout RIGHT_BRANCH

此时,如果你运行git log HEAD,你会看到你所有的提交都在那里,在RIGHT_BRACH中。

数据

  • WRONG_BRANCH是你提交的更改(尚未推送)现在所在的位置
  • RIGHT_BRANCH是你提交的更改(尚未推送)的位置
  • LAST_PUSHED_COMMIT是你想要恢复WRONG_BRANCH到的地方

首先,讨论这个主题的一些基本知识。

Git中的HEAD是什么

当你运行git分支时,git如何知道最后一次提交的SHA-1 ?答案是HEAD文件。

通常,HEAD文件是你当前所在分支的符号引用。通过符号引用,我们的意思是,与普通引用不同,它包含指向另一个引用的指针。

$ cat .git/HEAD
ref: refs/heads/master

如果你运行git签出测试,git会将文件更新为如下所示:

$ cat .git/HEAD
ref: refs/heads/test

当你运行git commit时,它会创建提交对象,并将该提交对象的父对象指定为HEAD中的引用所指向的SHA-1值。

分离HEAD是什么意思?

HEAD通常指一个命名的分支(例如master)。同时,每个分支引用一个特定的提交。例如,让我们假设我们在master分支,提交了c1c2c3。现在,当一个提交被创建时,master分支被更新以引用新的提交。具体来说,git提交创建了一个新的提交c4,它的父文件是之前的提交c3,然后更新分支master以引用新的提交c4HEAD仍然指向分支主,因此现在间接指向提交c4

现在如果我们签出到之前的一个提交,例如:c1HEAD现在直接指向提交c1这被称为处于分离HEAD状态。这仅仅意味着HEAD引用一个特定的提交,而不是引用一个命名的分支

撤销提交可以通过多种方式完成,目前最著名的是:

解决方案1

使用git revert,该命令将撤销在给定提交中所做的所有更改。

git-revert所做的基本工作是创建一个提交,撤销在给定提交中所做的更改,创建一个与给定提交相反(嗯,是互惠)的提交:

git revert <SHA-1>

同样,如果你想还原HEADsha-id之间的所有提交,那么你可以传递一个提交范围给git revert:

# revert all commits between 7db02faec and HEAD
# (excluding the start point of the range, 7db02faec)
git revert 7db02faec..HEAD

解决方案2

如果你想回退到指定的提交,使用git-reset,你可以这样做,因为这部分历史还没有发布。

git reset --hard <SHA-1>
此外,添加git reset --soft "HEAD{@1}"只是在调用git reset --hard <SHA-1>之前将指针移回HEAD。 例如:< / p >
# Reset the index and working tree to the desired tree
# Ensure you have no uncommitted changes that you want to keep
git reset --hard 7db02faec


# Move the branch pointer back to the previous HEAD
git reset --soft "HEAD@{1}"
git commit -m "Revert to 7db02faec"