背景
大きなGitリポジトリをCloneする際に --depth <depth>
オプションを使用すると、指定した数だけの履歴を持つShallow Copyを取得するようになり、Cloneにかかる時間を短縮することができる。
しかし、 HEAD
ではないある特定のコミット時点のリポジトリを取得したい場合、 git clone
コマンドにはCommit Hashを指定してリポジトリを取得するオプションがないため、結局 git clone
の --depth
オプションは使用できず、全ての履歴を取得した上で git checkout <sha1>
などでリポジトリの履歴を戻す必要がある。(例えば、リポジトリマイニング的な事をするためにGitHub APIでリポジトリの各Release時点のコミットIDは分かっている、という状況を想定)
解決方法
手順は増えるが、 git fetch
ならば --depth
を使用でき、かつCommit Hashを指定して特定のコミット時点のリポジトリの情報を取得できるため、やりたい事を実現することができる。
$ git init
$ git remote add origin https://github.com/$OWNER/$NAME
$ git fetch --depth 1 origin $SHA1
$ git checkout FETCH_HEAD
参考: 20211018: git - commit hash 値で shallow clone - PIB
go-gitを利用したコード
go-git
を使用してこれを実現しようとする場合、 git fetch
に直接Commit Hashだけを指定することはできない(?)ため、Refspecを指定する形に変更する必要があった。
package main
import (
"log"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
)
const (
url = "https://github.com/matterpoll/matterpoll"
sha string = "a62dbe0410aa0836cd4b26e75f9319a952ff153d"
)
func main() {
// git init
repo, _ := git.PlainInit("work", false)
// git remote add origin https://github.com/owner/name
remote, _ := repo.CreateRemote(&config.RemoteConfig{
Name: "origin",
URLs: []string{url},
})
// git fetch --depth 1 origin $SHA1:refs/heads/target
var target = plumbing.NewBranchReferenceName("target")
_ = remote.Fetch(&git.FetchOptions{
RemoteName: "origin",
RefSpecs: []config.RefSpec{
config.RefSpec(plumbing.ReferenceName(sha) + ":" + target),
},
Depth: 1,
})
// git checkout target
tree, _ := repo.Worktree()
_ = tree.Checkout(&git.CheckoutOptions{Branch: target})
}
上記のGoコードを実行すると、1コミットのみを持つGitリポジトリが取得できている。(go.mod
等の記述は割愛)
$ go run main.go
$ cd work
$ git log
commit a62dbe0410aa0836cd4b26e75f9319a952ff153d (grafted, HEAD -> target)
Author: Yusuke Nemoto <kaakaa@users.noreply.github.com>
Date: Mon Apr 29 05:31:07 2019 +0900
Release 1.1.0 (#150)
$