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 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。