中级GIT 概览

@vrqq  August 13, 2023
branch更像是个tag 打在点上 打在边上

本地repo 和 远程repo

首先抛开branch,tag,refs这些概念,一个repo(源码库)是由一系列commit组成的,每个commit的commit-id唯一。
这些commit组成了一个树,所谓同步代码,就是同步这棵树。
本地repo远程repo两个repo,只是他们的node恰好commit-id相同,且可以通过某些指令同步。

注:在 libgit2中,struct git_commit inherit from struct git_object

树的附属品

tag
好理解的概念,我们可以选择给树上某些commit挂上一个tag,例如"v1.0.2"。
显然,一个tag只能对应一个commit,但一个commit可以挂多个tag。

branch
想象commit作为点(node)组成一棵树,那么 一个branch就是一个collection覆盖了树上的部分边(edge)。
branch不是commit的上级,也不是edge的上级,可以把一个branch看作view-set,他是一个“虚拟集合”,指向了一些edge。
显然 在某个repo 中,某个edge只能在一个branch中出现,某个commit却可以被多个branch关联。(远程repo和本地repo不能看做同一个repo 只是他们恰好可以同步)

commit不属于branch,branch与他关联仅仅是因为有连接这个commit node的edge在branch里。
每个branch内部,有一个HEAD指针指向某一个commit,表示“下一个提交”要在这个HEAD后面生长,并把新边纳入当前branch中。

起初一个空的repo建立时,可以没有branch,或者有一个默认的branch(例如名为master),此时并没有commit。
这时的branch只是个虚拟概念,这个branch内部的HEAD可以认为也空着。
当提交了首个commit后,HEAD有了,下次“在这个branch上的提交”产生的边,就属于这个branch了。

某个branch的HEAD可以用git reset命令挪位置,分为--soft, --mixed, --hard三级,暂且不表,详见下一章 'working dir and staged area'。

如果在某个branch上,HEAD往期挪了,然后又提交了个新的commit?
新的commit成为HEAD,"到达新commit的边" 进入当前branch,"到达旧的commit的边" 自动从当前branch中移出,然后不属于任何一个branch。那些旧的commit仍然存在,但只能通过commit-id找到他们,因为他们不与 "任何一个branch覆盖的边" 相连。
(警告:使用 prune 指令可能会将这些commit删掉)

同步
tag同步比较简单,拉取远程tag对应的commit-id的提交到本地。

在思考branch同步前,先考虑个问题:如果一个本地repo从远程repo同步而来,是否需要同步全部的commit?

  • 显然不是,例如--depth=1同步一个。本地repo可以只含有零零散散几个commit,他们甚至不用相连,但这种情况只能查看commit,并不能修改。
  • GIT还有一个特性:如果想基于某个commit修改并提交,需要获得当前commit前面的所有历史。

那么,远程branch和本地branch是否要一致?

  • 根据上述资料,我们可以拼凑出这么一个想法:站在某个commit上,选择一条branch(这个branch可以已经有edge了,也可以有0个edge 即从当前node分叉)。我们选的"这条branch"在本地的名称是 lmaster,在远程repo里的名称叫 rmain。
    我们无需关心其他branch,也无需关心 "和当前commit无关的 其他分叉上的commit",在这里 只是借用branch的概念,表示了commit的生长关系
  • 所以,"同步branch" 的含义是:同步 "在某个杈上 commit的生长关系"

现在来回答这个问题,本地branch和远程branch可以名称不一致,甚至本地多一些branch,或者远程多一些branch。我们无需关心branch是否覆盖相同的edge,仅仅需要关心commit的先后关系即可。

P.S:

  • 使用git remote 命令,或在 <project>/.git/config 中配置以下参数表示其对应关系,fetch下行,push上行。
    fetch = +refs/heads/lmaster:refs/remotes/origin/rmain
  • branch可以改名,名称是个代号而已

特殊的例子,远程branch:当说到remote时,语义上认为一经创建不可更改,因为默认有很多用户共享。所以如果在某个branch上git reset挪动HEAD后再--force提交,对他人并不友好。

ref
最后才说到ref,当前文看完后,ref的概念显而易见:
http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions

常用git指令

从远程repo拉指定commit-id到本地

git_adddress=https://github.com/vrqq/cnl-fixing.git
revspec=3ef9b0e224f135dbfed9d210fa8bdf53367b18ff
git init
git remote add origin $git_adddress
git fetch --depth=1 origin $revspec
git reset --hard $revspec

clone某个tag/branch到本地
clone是个快捷指令,其背后自动执行上述操作

git clone -b $tag --depth=1 https://github.com/vrqq/cnl-fixing.git

添加新评论