跳转至

git的指针ref和reflog

Git指针ref概念

ref是一种指向一次提交的非直接方式,可以看做是提交哈希(commit hash)的别名,用于表示Git的分支和标签。ref作为普通文件存储于.git/refs目录下:

$ ls .git/refs
heads remotes tags

其中,

  • heads目录定义了仓库中所有的本地分支,heads目录下的每个文件的文件名对应分支名,文件内容为该分支最后一次提交对应的哈希值。在main分支上进行一次提交,Git实际就是修改refs/heads/main的内容,新建一个分支,实际就是将提交哈希写入一个新文件中。
  • tags目录与heads作用类似,不同之处在于保存git tags而非分支信息。
  • remote目录存放着所有远程仓库,每个远程仓库对应一个子目录,该子目录下又存放着获取(fetch)到存储库中的所有远程分支:

    1
    2
    3
    4
    5
    6
    7
    $ ls .git/refs/remotes 
    origin
    $ ls .git/refs/remotes/origin 
    HEAD                        fix_db7f3d3                 master
    Tacotron2-iter-260K-824c091 fix_server                  refactor
    dependabot                  ljspeech-tacotron-iter-185K
    dev                         main
    

因此,在一些需要指定具体提交哈希的场景中,除了指定哈希值之外,还可以直接指定ref,比如git show .git/refs/heads/HEAD

对于大型仓库,Git会周期性执行垃圾回收,以减少不必要的对象并压缩ref,以提高性能。当然,也可以使用git gc手动执行垃圾回收,该命令会将上述分支和tag对应的ref文件压缩到.git目录下的packed-refs文件中。

特殊ref的指向

  • HEAD:最近检出(check out)的提交(commit)或分支。
  • FETCH_HEAD:最近从远程仓库中获取(fetch)的分支。
  • ORIG_HEAD:在较大变更前HEAD的备份。
  • MERGE_HEAD:使用git merge合并到当前分支的提交(commit)。
  • CHERRY_PICK_HEAD:使用git cherry-pick摘取部分内容到当前分支的提交(commit)。

远程仓库的默认名origin

origin是远程仓库链接的别名,origin对应的链接太长,使用起来较为麻烦。因此origin的本质是指向远程仓库的一个指针名,而master/main则属于仓库中的一个部分。

可以通过git remote -v或者仓库配置文件.git/config查看远程仓库的具体链接,可以通过git remote add <remote-repo-name> <url>添加新的远程仓库别名<remote-repo-name>和对应的链接<url>

git push默认创建originmain,因此推送时可以省略,完整形式应是git push origin main。如果希望Git记录推送到远程分支的默认值,可以加上-u参数,也就是git push -u <remote> <local>,这样当下次还想要继续推送到该远程分支时,推送命令就可以简写为git push

远程分支和本地分支的映射refspecs

refspecs将本地仓库的分支映射到远程仓库的分支,以便使用本地Git命令管理远程分支,并配置一些特殊的git pushgit fetch行为。

refspecs定义如下:

1
2
3
4
# definition
[+]<src>:<dst>
# example
+refs/heads/*:refs/remotes/origin/*

其中,

  • +表示强制远程仓库执行非快进更新(non-fast-forward update)。
  • <src>表示本地仓库的源分支。
  • <dst>表示远程仓库的目标分支。

refspecs可以与git push一起使用,为远程分支指定不同的名称。比如以下命令将main本地分支推送到origin远程仓库,但使用qa-main作为远程仓库origin中对应分支的名称:

git push origin main:refs/heads/qa-main

同样可以利用refspecs删除远程分支,类似地,此时将本地分支的名称置为空即可:

1
2
3
git push origin :qa-main
# 该删除分支的操作等同于
git push origin --delete qa:main

通过修改Git的配置文件.git/config,可以使用refspecs修改git fetch的默认行为。默认情况下,git fetch获取远程仓库的所有分支,因此.git/config文件相关内容如下:

1
2
3
[remote "origin"]
        url = https://github.com/wenet-e2e/wenet.git
        fetch = +refs/heads/*:refs/remotes/origin/*

上述配置要求Git下载远程仓库origin的所有分支,如果只需要main分支,可以更新相关内容如下:

1
2
3
[remote "origin"]
        url = https://github.com/wenet-e2e/wenet.git
        fetch = +refs/heads/main:refs/remotes/origin/main

类似地,可以修改git push的默认行为。比如,如果想一直将本地分支main推送到远程仓库originqa-main,可以更新相关内容如下:

1
2
3
4
[remote "origin"]
        url = https://github.com/wenet-e2e/wenet.git
        fetch = +refs/heads/main:refs/remotes/origin/main
        push = refs/heads/main:refs/heads/qa-main

相对ref

  • HEAD~1:等同于HEAD~HEAD^1HEAD^,后退至HEAD之前的提交。
  • HEAD^2:后退至当前分支的第二个父提交。
  • HEAD~1^2:后退至HEAD之前的提交,再后退到当前分支的第二个父提交上,如果HEAD没有合并分支,则非法。
  • HEAD@{2}:指向git reflog记录的整体操作的第三条操作(git reflog记录的整体操作从0开始)。

reflog

reflog记录了在当前仓库上进行的所有Git操作,可以通过git reflog查看该Git日志,比如:

1
2
3
4
5
6
7
8
$ git reflog
02d7064 (HEAD -> main, origin/main, origin/HEAD) HEAD@{0}: pull: Fast-forward
25897e0 HEAD@{1}: checkout: moving from 8a1cb96795b38ba9aeb23c4005242acd0d9829e8 to main
8a1cb96 (origin/kaitang-ssl-train) HEAD@{2}: pull https://github.com/cnlinxi/wenet.git kaitang-ssl-train: Fast-forward
422c89e HEAD@{3}: checkout: moving from main to remotes/origin/kaitang-ssl-train
88a652a HEAD@{4}: commit: delete set_audio_backend
a9bae0c HEAD@{5}: commit: update
f0fe211 HEAD@{6}: commit: update(examples/librispeech):为kaggle修改数据路径

其中,HEAD{<num>}用于标识在reflog中的提交(commit),可以配合其他命令对指定提交(commit)进行操作,比如删除最后一次提交时,可以执行git reset --soft HEAD@{1}

Refs and the Reflog Git 里面的 origin 到底代表啥意思? git push 的 -u 参数含义 这才是真正的GIT——GIT实用技巧 这才是真正的GIT——分支合并


最后更新: 2022-06-08

评论