Git里引用一个commit
git所有的操作, 其实都是在跟commit
打交道:
你stage
你的commit
, 创建一个commit,
查看之前的commit
, 把commit
在不同的branch之间挪动, push
你的commit
等等.
那么, 怎么指定commit
就很重要.
1. 通过commit hash
来指定.
一个commit
跟一个commit hash
是一一对应的, 这是最直接的指定commit
的方式.
2. 通过refs
refs
是一种间接指定commit
的方式, 相当于commit hash
的别名, 它更加对人类友好, 不过, 不是所有的commit hash
都有别名(也就是refs
)的. 通常你见到的branch name, 比如master
, 其实就是一种ref
.
1. 一般的ref
要知道你的repo有多少refs
, 可以在.git/refs
这个目录里看到类似下面的目录树.
如果你的git repo是一个大型repo, 很可能你的refs被压缩成一个叫.git/packed-refs的文件, 而不是在.git/refs目录下.
heads/ # 这个目录里存放了你所有的local branch
master
some-feature
remotes/ # 这个目录下存放的是你所有fetch到本地的remote branch
origin/
master
tags/ # 这个目录下是你所有的tag
v0.9
使用一个ref来指定一个commit很简单, 比如
git show master
# 这里的master, 只是refs/heads/master的简写, 正常情况下, 我们简写refs就可以, 除非tags目录下也有一个master的tag存在, 这时候就不能简写master,那样就无法区分是refs/heads/master, 还是refs/tags/master.
2. 特殊的ref
除了.git/refs
(或者.git/packed-refs
)下的refs, 还有一些特殊的refs, 定义在.git
这个目录下, 说几个常见的:
HEAD
: 当然checkout的commit/branchFETCH_HEAD
: 最近一次从远端 fetch的branchMERGE_HEAD
: 你正在merge到当前brach的那个commit.
3. 相对refs
你可以指定通过一个commit
来指定另一个commit
:
-
~
这个字符, 可以帮助你指定你当前commit的parent commit.git show HEAD~1
: HEAD的上一个commit. -
如果发生过merge, 那么一个commit, 就有可能有两个parent, 这个时候, 如何指定另一个parent呢?
^
字符, 可以帮你:git show HEAD^2
~
跟^
的区别, 下面这张图你可以看到.
4. Reflog
你在local repo上做的所有关于commit hash的历史操作, git其实都为你保留在reflog
里. 可以通过git reflog
来查看:
5a9f9aa25 (HEAD -> xtao, origin/xtao, origin/HEAD) HEAD@{0}: reset: moving to origin/xtao
53ca49f77 HEAD@{1}: cherry-pick: change dht log level
c83c124b2 HEAD@{2}: cherry-pick: delete unecessary code to fix make error.
如果你要恢复某一个历史commit hash, 你可以通过HEAD@{<n>}
这样的语法, 来指定在reflog里的一个commit hash, 比如:
git checkout HEAD@{1}
总结
通过了解git如何引用一个commit, 我们其实了解到了很多git内部的机制, 比如它是如何存储branch信息和tag信息的, 这会帮助我们更好的理解平时使用的git 命令.
有的时候, 我们还需要指定一定范围内的commit, 比如当你想把一个branch上最近提交的几个commit, 提交到另一个branch上, 该怎么做呢? 敬请期待下一篇啦.
参考: https://www.atlassian.com/git/tutorials/refs-and-the-reflog
- 原文作者:比克王国
- 原文链接:https://wgqimut.github.io/posts/git-refs/
- 版权声明:本作品采用4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。