0%

Git引用规范

背景

从git push origin HEAD:refs/for/xxxxx说起,这条命令是在使用mk ak 提交review代码发现的,对HEAD和refs/for/xxxxx 所代表的含义充满了好奇,可能有些人能理解HEAD是.git/HEAD,那refs/for/xxxxx是什么东西呢?

常规

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# fetch
> git fetch

# 源端分支映射
> git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/develop
remotes/origin/master

# 切一个本地分支mydevelop,源端分支为 develop
> git checkout -b mydevelop refs/remotes/origin/develop

# 查看config文件
> cat .git/config
...
[remote "origin"] 本地源 名称
url = git@github.com:JuneLeo/CardControl.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[branch "mydevelop"]
remote = origin
merge = refs/heads/develop

通过config文件和 refs目录,我们发现

  • config 为git配置文件, [remote “origin”] :对应.git/refs/remotes/origin

  • refs/heads 为本地分支,.git/refs/remote/origin为源端分支

  • fetch = +refs/heads/:refs/remotes/origin/ ,+ 号告诉 Git 即使在不能快进的情况下也要(强制)更新引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 查看 refs源端映射的本地引用目录
    > ls .git/refs
    heads remotes tags

    > ls .git/refs/heads // heads 为本地分支
    master mydevelop

    > ls .git/refs/remotes //源端 heads目录映射(refs/heads/*:refs/remotes/origin/* )
    origin

    > ls .git/refs/remotes/origin // 参考后面pack-refs
    HEAD

    上述我们查看了.git/refs 目录,

  • heads 为本地分支目录

  • remotes 为源端映射目录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 查看 HEAD 本地的当前分支
    > cat .git/HEAD
    ref: refs/heads/mydevelop //当前分支为mydevelop

    # 查看 packed-refs
    > cat .git/packed-refs
    # pack-refs with: peeled fully-peeled sorted
    5aca94c89c5d873acd0169a2c98910d00986fa45 refs/remotes/origin/develop
    720c57484f53f66ce310a3f303cffe6d0af954c1 refs/remotes/origin/master

    其他一些目录信息

  • HEAD 保存着当前分支

  • packed-refs (后面讲解)

我们继续看 git push origin HEAD:refs/for/xxxxx

  • origin: 本地源端目录映射.git/refs/remote/origin
  • HEAD: 本地当前分支 .git/HEAD
  • refs/for/xxxxx 源端 for/xxxxx 目录(或者标签)

这条命令会将HEAD提交到源端的for/xxxxx目录(或者标签)下

变形

为了排除干扰 ,我们把项目删除,重新克隆,并且重新添加remote

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
>  git remote add other git@github.com:JuneLeo/CardControl.git

> cat .git/config
...
[remote "origin"]
url = git@github.com:JuneLeo/CardControl.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "other"]
url = git@github.com:JuneLeo/CardControl.git
fetch = +refs/*:refs/remotes/other/*
...

# 修改 other

[remote "other"]
url = git@github.com:JuneLeo/CardControl.git
fetch = +refs/*:refs/remotes/other/*

> git fetch other
remote: Enumerating objects: 216, done.
remote: Counting objects: 100% (184/184), done.
remote: Compressing objects: 100% (56/56), done.
remote: Total 158 (delta 57), reused 150 (delta 50), pack-reused 0
Receiving objects: 100% (158/158), 13.56 KiB | 4.52 MiB/s, done.
Resolving deltas: 100% (57/57), completed with 9 local objects.
From github.com:JuneLeo/CardControl
* [new ref] refs/for/develop -> other/for/develop
* [new branch] develop -> other/heads/develop
* [new branch] master -> other/heads/master
* [new ref] refs/pull/1/head -> other/pull/1/head
* [new ref] refs/pull/10/head -> other/pull/10/head
* [new ref] refs/pull/11/head -> other/pull/11/head
* [new ref] refs/pull/11/merge -> other/pull/11/merge
* [new ref] refs/pull/2/head -> other/pull/2/head
* [new ref] refs/pull/3/head -> other/pull/3/head
* [new ref] refs/pull/4/head -> other/pull/4/head
* [new ref] refs/pull/5/head -> other/pull/5/head
* [new ref] refs/pull/6/head -> other/pull/6/head
* [new ref] refs/pull/7/head -> other/pull/7/head
* [new ref] refs/pull/8/head -> other/pull/8/head
* [new ref] refs/pull/9/head -> other/pull/9/head
* [new ref] refs/song/develop -> other/song/develop


> git branch -a
* master
remotes/origin/HEAD -> origin/master //本地存在
remotes/origin/develop // 本地仓库不存在
remotes/origin/master // 本地仓库不存在
remotes/other/for/develop // review标签
remotes/other/heads/develop
remotes/other/heads/master
remotes/other/pull/1/head
remotes/other/pull/10/head
remotes/other/pull/11/head
remotes/other/pull/11/merge
remotes/other/pull/2/head
remotes/other/pull/3/head
remotes/other/pull/4/head
remotes/other/pull/5/head
remotes/other/pull/6/head
remotes/other/pull/7/head
remotes/other/pull/8/head
remotes/other/pull/9/head
remotes/other/song/develop

通过上面的命令,我们发现把+refs/heads/:refs/remotes/other/ 改为+refs/*:refs/remotes/other/*,会将源端refs目录下的所有文件映射到本地refs/remote/other 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>  ls .git/refs/remotes
origin other

> ls .git/refs/remotes/other //此处为源other 映射目录
for heads pull song

> ls .git/refs/remotes/other/heads //other/heads目录
develop master

> ls .git/refs/remotes/origin // origin 等同 other/heads
HEAD

> cat .git/refs/remotes/origin/HEAD
ref: refs/remotes/origin/master

通过比较发现 origin目录下只有HEAD文件,且HEAD中存储了源端映射信息;我们再次分析

  • +refs/:refs/remotes/other/ ,将 源端refs 映射到refs/remotes/other目录
  • +refs/heads/:refs/remotes/origin/ 将源端refs/heads 映射到refs/remotes/origin目录

为什么.git/refs/remotes/origin只有一个HEAD目录?而不是develop和master文件???

1
2
3
4
5
6
7
8
9
10
>  git remote add juneleo git@github.com:JuneLeo/CardControl.git

> cat .git/config
...
[remote "juneleo"]
url = git@github.com:JuneLeo/CardControl.git
fetch = +refs/heads/*:refs/remotes/juneleo/*

> ls .git/refs/remotes/juneleo
develop master

通过添加新的源juneleo,我们发现refs/remotes/juneleo目录下有develop和master文件,那这又是为什么呢?

打包标头和标签以便高效的存储库访问

1
2
3
4
5
6
7
8
9
>  cat .git/packed-refs
# pack-refs with: peeled fully-peeled sorted
5aca94c89c5d873acd0169a2c98910d00986fa45 refs/remotes/origin/develop
720c57484f53f66ce310a3f303cffe6d0af954c1 refs/remotes/origin/master

> git pack-refs --all

> ls .git/refs/remotes
origin

执行 git pack-refs –all,我们发现 refs/remotes目录中的other和juneleo目录消失了,这又是为什么呢??

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>  cat .git/packed-refs
# pack-refs with: peeled fully-peeled sorted
5aca94c89c5d873acd0169a2c98910d00986fa45 refs/heads/develop_origin
720c57484f53f66ce310a3f303cffe6d0af954c1 refs/heads/master
720c57484f53f66ce310a3f303cffe6d0af954c1 refs/heads/master_other
5aca94c89c5d873acd0169a2c98910d00986fa45 refs/remotes/juneleo/develop
720c57484f53f66ce310a3f303cffe6d0af954c1 refs/remotes/juneleo/master
5aca94c89c5d873acd0169a2c98910d00986fa45 refs/remotes/origin/develop
720c57484f53f66ce310a3f303cffe6d0af954c1 refs/remotes/origin/master
073b759c5f2b2054086010a5259be14c9c85a6bf refs/remotes/other/for/develop
5aca94c89c5d873acd0169a2c98910d00986fa45 refs/remotes/other/heads/develop
720c57484f53f66ce310a3f303cffe6d0af954c1 refs/remotes/other/heads/master
effecabb3c199b271deb2f00a7d0dc9846a9317c refs/remotes/other/pull/1/head
7bc68c59ba42beeffce0521c32eb017bf22c9e1b refs/remotes/other/pull/10/head
5aca94c89c5d873acd0169a2c98910d00986fa45 refs/remotes/other/pull/11/head
f73286b0a6beca997c40ac7472ae7ce28067b3f1 refs/remotes/other/pull/11/merge
68faf293665a0431cd88dfd00df1ed013281595b refs/remotes/other/pull/2/head
d34af6f4d72385d06aa32a8d8ed23f2ae8847ad6 refs/remotes/other/pull/3/head
f70a3be25c18a267ec404c2325fdb4bf803e42dc refs/remotes/other/pull/4/head
d34af6f4d72385d06aa32a8d8ed23f2ae8847ad6 refs/remotes/other/pull/5/head
6be27fa9215b3576c20f32a25ee740b91cc338bf refs/remotes/other/pull/6/head
fa82c7d436b599b359c756be00e23181c32c7a7a refs/remotes/other/pull/7/head
b394a520bd70fd042378fc2bc2ec81c75cb84622 refs/remotes/other/pull/8/head
c1d0d352abb347eea4af9bfec999eba5a72305ba refs/remotes/other/pull/9/head
073b759c5f2b2054086010a5259be14c9c85a6bf refs/remotes/other/song/develop

再次打开.git/packed-refs目录,我们发现这个文件里面多了很多映射关系

传统上,分支存储在refs 目录中,尽管许多分支提示往往会经常更新,但是大多数标记和某些分支提示从未更新过。当存储库中有成百上千个标签时,这种“每个引用一个文件”的格式既浪费存储空间,又会损害性能。

git pack-refs 用于通过将引用存储在单个文件中来解决存储和性能问题 .git/packed-refs。当传统.git/refs目录层次结构中缺少ref时,将在此文件中查找该引用,并在找到该引用时使用。

分支更新总是.git/refs目录层次结构下创建新文件 。

1
2
3
4
5
6
git pack-refs

--all -- pack all refs
--no-all -- do not pack all refs
--no-prune -- do not remove loose refs after packing them
--prune -- remove loose refs after packing them