git:可靠地从脚本切换到分离的HEAD,然后稍后恢复HEAD

4
所以这是场景。我有一个运行一些测试的脚本。我需要编写另一个脚本,它接受一个git提交名称作为参数,然后执行以下操作:
  1. 保存当前提交状态-分支名称或未命名提交。
  2. 切换到指定提交的分离HEAD
  3. 对该提交运行测试脚本
  4. 切换回来,使HEAD与此之前的状态相同
我需要确保这个脚本是健壮的,无论存储库的状态如何,它都不会破坏性。当它从分离头或常规分支运行时,它应该工作,并且最好即使存在未提交或未暂存的更改也应该工作。
我觉得这应该是一个容易回答的问题,因为运行测试脚本以前的提交似乎是一个很常见的自动化任务。但是我似乎找不到任何简单的命令序列来执行它。
(类似于 pushd / cd / popd为当前工作目录执行的操作)。
2个回答

3

如果这只是一个脚本,你不需要做任何高级操作,只需在使用前存储HEAD的位置,在完成后再次检出即可:

# If HEAD is a sym-ref, the first assignment will work
# otherwise, it's detached, so get the SHA1 with rev-parse
if ! head=$(git symbolic-ref HEAD 2>&1); then
    head=$(git rev-parse HEAD)
fi
# trim a refs/heads/ prefix; no-op otherwise
head=${head#refs/heads/}


# now go on and do your stuff, test, whatever you like

# then return to where you were
# This will ERASE ANY LOCAL CHANGES.
git checkout -f $head

这种方法的优点是无论在中间做什么操作都可以工作 - 特别是你可以在那里进行很多git操作 - 也许是测试合并,或者挑选一个提交以进行测试(也许是测试该提交,也许它只包含用于测试的构建配置设置)。由于这些操作会创建提交,它们会导致 HEAD@{1} 方法失败(您需要使用 HEAD@{2})。更好的是,如果你的测试实际上涉及创建临时分支,这种方法仍然有效,而 @{-1} 的方法则不行。
(另外,据我所知,HEAD@{1} 总是检出 HEAD 在那个时间点引用的提交,而不是当时指向该提交的分支。这有点说得通,因为分支在那之后可能已经改变。)

我已更新我的答案以说明其缺点(在结尾处分离模式)。对于你的脚本加一。 - VonC
这种方法也没有恢复正确的分支。 "git symbolic-ref HEAD" 给出了 "refs/heads/master",如果我执行 "git checkout refs/heads/master",它实际上会导致 HEAD 变为分离状态。 "@{-1}" 符号确实起作用,我可能最终会采用它(尽管如你所提到的,如果测试涉及某些 git 操作,它将无法工作...但我认为我可以保证它不会)。 - Eddy
@Eddy:哦,我忘记它会这样了。我将其编辑为修剪refs/heads/前缀。没什么大不了的。 - Cascabel

0
尝试(未测试)
git checkout HEAD@{1}

将代码库“切换回之前的HEAD”,就像你执行checkout xxx命令之前一样。

另请参阅 "Git中的HEADORIG_HEAD"


所有其他的修订规范都在这里:rev_parse,“SPECIFYING REVISIONS”部分
例如,要返回到以前的分支,可以尝试@{-1}

刚刚测试了一下(“以前的HEAD”选项):

简单的git仓库,有三个文件,分别在三个提交中添加(a,然后b,然后c):

C:\git\tests\p3>git log --oneline
6e5b961 c
66c68e3 b
77e9a40 a

我检出了第一个提交(DETACHED HEAD

C:\git\tests\p3>git checkout 77e9a40
Note: moving to '77e9a40' which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 77e9a40... a

C:git\tests\p3>dir
08/12/2010  12:27 PM                 4 a.txt

我试图在进行“DETACHED HEAD”之前返回到先前的HEAD

C:\git\tests\p3>git checkout HEAD@{1}
Previous HEAD position was 77e9a40... a
HEAD is now at 6e5b961... c

它有效!

你得到了正确的提交,但不是正确的分支(即你仍处于分离模式)

C:\git\tests\p3>git branch
* (no branch)
  master

在这种配置下,试图返回到以前的分支是行不通的

C:\git\tests\p3>git checkout HEAD@{-1}
error: pathspec 'HEAD@{-1}' did not match any file(s) known to git.

所以唯一真正的解决方案是先记住 HEAD(而不是在分离模式下提交),然后返回。

git symbolic-ref HEA

请参考Jefromi的回答


在执行了 "git checkout HEAD@{1}" 命令后,仓库进入了分离 HEAD 状态,HEAD 指向该分支上的最后一次提交,而不是指向该分支本身。因此命令未能生效。 - Eddy
@Eddy:我刚测试过了,它可以工作,看看我的更新答案。(但要确保在进行git checkout commit之前没有进入分离头模式,不要再次进入分离头模式) - VonC
嗯,这真的有效吗?在我看来,就像 Eddy 所说的那样,它正在检查之前由 HEAD 引用的提交,即使该 HEAD 实际上指向了指向该提交的主分支,而且自那时以来主分支仍未移动,它仍然不会将 HEAD 指向主分支,只是提交。 - Cascabel
也许你正在寻找 @{-1},就像 Stefan Näwe 在链接的答案中提到的那样。 - Cascabel
@Eddy:我已经更新了我的答案,解释了为什么它不起作用。请参考Jeformi的答案获取更完整的解决方案。 - VonC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接