正如标题所示,我试图弄清楚如何使用创建本地分支go-git
与 Git CLI 命令给出相同结果的方式git branch <branchname>
.
据我所知,git branch <branchname>
(没有明确的<start-point>
论证)做了两件事:
- Creates
.git/refs/heads/<branchname>
指向当前的HEAD
commit
- Creates
.git/logs/refs/heads/<branchname>
用一行记录分支的创建。
它可能会做更多的事情,但我知道它肯定会做这两件事。 (如果你知道更多它的作用,请分享!)
接下来的大部分内容记录了我在研究我的选择时的发现之旅,我想我现在可能已经掌握了上面的#1。不过,对于#2,我开始认为我可能是 SOL,至少使用go-git
.
第一个想法:Repository.CreateBranch
我最初天真的想法就是打电话Repository.CreateBranch
,并且有一个answer https://stackoverflow.com/a/64131712/1337498类似的问题(“How to checkout a new local Branch using go-git?”)似乎证实了这个想法。但一旦我开始研究细节,事情就变得very令人困惑。
First, Repository.CreateBranch
需要一个config.Config
作为输入(为什么?),并且似乎还修改了存储库的.git/config
文件(再次,为什么?)。我已经验证过git branch <branchname>
命令不会触及存储库的配置,当我调用该命令时,我当然不需要提及任何有关配置的内容。
其次,我上面链接的SO答案引用了代码go-git
's repository_test.go
执行以下操作:
r, _ := Init(memory.NewStorage(), nil) // init repo
testBranch := &config.Branch{
Name: "foo",
Remote: "origin",
Merge: "refs/heads/foo",
}
err := r.CreateBranch(testBranch)
但定义config.Branch
is:
type Branch struct {
// Name of branch
Name string
// Remote name of remote to track
Remote string
// Merge is the local refspec for the branch <=== ???
Merge plumbing.ReferenceName
...
}
and "refs/heads/foo"
不是参考规范(因为 refspec 有一个:
分离其src
and dst
成分)。
经过大量的绞尽脑汁和代码阅读之后,我得出了(非常)初步的结论:注释中的“refspec”一词一定是错误的,而应该只是“ref”。但我对此完全不确定:如果我是对的,那么为什么这个字段被命名为Merge
而不仅仅是Ref
?
另一个初步的结论是Repository.CreateBranch
并不是真正用于创建纯粹的本地分支,而是用于创建与远程分支存在某种关系的本地分支 - 例如,如果我从远程拉取其他人的分支。
其实,重读一遍存储库.CreateBranch https://github.com/go-git/go-git/blob/a50d22d149e9bb5f0b64f5d4c4a8e2cd0a2a51a4/repository.go#L638-L654方法,我一点也不相信它真的创建一个分支(也就是说,它创造了.git/refs/heads/<branchname>
)。除非我错过了一些东西(完全有可能),否则它所做的似乎就是创建一个[branch "<name>"]
section in .git/config
。但如果这是真的的话为什么它是一种方法Repository
at all?为什么它不是一种方法config.Config
?
同样,还有一个相关的函数:
func (r *Repository) Branch(name string) (*config.Branch, error)
只会从配置返回分支信息。然而,文档中的下一个函数Repository
is:
func (r *Repository) Branches() (storer.ReferenceIter, error)
它确实返回了所有条目的迭代器.git/refs/heads/
.
这是非常令人困惑的,而且文档(就像它本身一样)没有任何帮助。无论如何,除非有人能说服我,否则我很确定CreateBranch
实际上不会有太大帮助创建分支.
工作树.结帐???
一些额外的网络搜索发现了旧版本中的这两个问题d-src/go-git
repo:
- 创建分支的示例#551 https://github.com/src-d/go-git/issues/551
- 创建分支?第713章 https://github.com/src-d/go-git/issues/713
这两篇文章都提出了创建本地分支的基本方法:
wt, err := repo.Worktree()
if err != nil {
// deal with it
}
err = w.Checkout(&git.CheckoutOptions{
Create: true,
Force: false,
Branch: plumbing.ReferenceName("refs/heads/<branchname>"),
})
除了这个事实之外退房新的分支机构,其中git branch <branchname>
不这样做,它也无法创建.git/logs/refs/heads/<branchname>
.
另外——作为一个潜在的very令人讨厌的惊喜——它清除工作树中所有未跟踪的文件。默认情况下,git checkout
keeps对工作树中的文件进行本地修改,但在go-git
你需要明确指定Keep: true
,即使您已指定Force: false
.
绝对违反了“最小惊讶原则”。值得庆幸的是,在我测试过的本地存储库中,它们都是旧的编辑器备份文件或我很久以前就放弃的旧项目的片段。
存储器.ReferenceStorer
事情发生时,其中一位go-git
作者/维护者回应了第二个问题,并建议:
为了独立于工作树创建和删除引用,您应该使用storer.ReferenceStorer
.
请看一下分支示例:https://github.com/src-d/go-git/blob/master/_examples/branch/main.go https://github.com/src-d/go-git/blob/master/_examples/branch/main.go
这很好而且很简单,但它只解决了分支引用的创建问题。
我在以下位置找到的所有出现的“log”一词go-git
源代码似乎参考commit日志,不ref日志。鉴于引用日志条目看起来与.git
树,我想需要一种不同类型的存储器来创建/更新它们——并且现有的存储器看起来(对我来说)都不是这样做的。
So...
关于如何获得适当的参考日志以配合参考有什么建议吗?
(或者,也许我误解得很严重,而且is一些创建分支的方法go-git
,除了我上面列出的那些之外,这将满足我的要求。)