英文:
How to checkout a specific single file to inspect it using go-git?
问题
我想克隆一个特定的代码库,获取所有的标签,并对它们进行迭代。对于每个标签,我想在根目录下检出一个特定的文件(package.json)。如果没有该文件,则应继续迭代,否则应跳过该文件以便我可以检查它。
我从以下代码开始(我的第一个Go应用程序...)
package main
import (
"fmt"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/storage/memory"
"os"
)
func main() {
authentication := &http.BasicAuth{
Username: "me",
Password: "my-key",
}
repositoryUrl := "my-repo-url"
inMemoryStorage := memory.NewStorage()
inMemoryFilesystem := memfs.New()
repository, err := cloneRepository(repositoryUrl, authentication, inMemoryStorage, inMemoryFilesystem)
if err != nil {
handleError(err)
}
tagsIterator, err := repository.Tags()
if err != nil {
handleError(err)
}
err = tagsIterator.ForEach(func(tag *plumbing.Reference) error {
fmt.Println(tag.Name().Short()) // for debugging purposes
// checkout package.json file ( at root ) via tag
return nil
})
if err != nil {
handleError(err)
}
}
func cloneRepository(repositoryUrl string, authentication *http.BasicAuth, inMemoryStorage *memory.Storage, inMemoryFilesystem billy.Filesystem) (*git.Repository, error) {
return git.Clone(inMemoryStorage, inMemoryFilesystem, &git.CloneOptions{
URL: repositoryUrl,
Auth: authentication,
})
}
func handleError(err error) {
fmt.Println(err)
os.Exit(1)
}
有人知道如何在循环中通过给定的标签尝试检出文件吗?
英文:
I want to clone a specific repository, fetch all tags and iterate through them. For each tag I want to checkout a specific file ( package.json ) in the root directory. If no file is present, it should continue, otherwise it should pass it over so I can inspect it.
I started with the following code ( my first Go application ... )
package main
import (
"fmt"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/storage/memory"
"os"
)
func main() {
authentication := &http.BasicAuth{
Username: "me",
Password: "my-key",
}
repositoryUrl := "my-repo-url"
inMemoryStorage := memory.NewStorage()
inMemoryFilesystem := memfs.New()
repository, err := cloneRepository(repositoryUrl, authentication, inMemoryStorage, inMemoryFilesystem)
if err != nil {
handleError(err)
}
tagsIterator, err := repository.Tags()
if err != nil {
handleError(err)
}
err = tagsIterator.ForEach(func(tag *plumbing.Reference) error {
fmt.Println(tag.Name().Short()) // for debugging purposes
// checkout package.json file ( at root ) via tag
return nil
})
if err != nil {
handleError(err)
}
}
func cloneRepository(repositoryUrl string, authentication *http.BasicAuth, inMemoryStorage *memory.Storage, inMemoryFilesystem billy.Filesystem) (*git.Repository, error) {
return git.Clone(inMemoryStorage, inMemoryFilesystem, &git.CloneOptions{
URL: repositoryUrl,
Auth: authentication,
})
}
func handleError(err error) {
fmt.Println(err)
os.Exit(1)
}
Does someone know how to try checking out the file inside the loop by a given tag?
答案1
得分: 3
如果你只想获取文件内容,而不需要"check out"任何东西,你可以直接从代码库中提取文件内容。但首先,需要注意的是:我既不是一名有经验的Go程序员,也没有使用过go-git
,所以可能有更优化的方法。
从一个标签开始,你可以进行以下步骤:
- 获取标签指向的提交
- 获取提交指向的树
- 遍历树,寻找
package.json
- 如果找到了,提取相应的blob。现在你就有了文件内容!
以上步骤可能如下所示:
func getFileFromRef(repository *git.Repository, ref *plumbing.Hash, filename string) (bool, []byte, error) {
// 获取与ref对应的提交对象
commit, err := repository.CommitObject(*ref)
if err != nil {
return false, nil, err
}
// 从提交获取树对象
tree, err := repository.TreeObject(commit.TreeHash)
if err != nil {
return false, nil, err
}
// 遍历树的条目
for _, entry := range tree.Entries {
// 如果找到目标文件...
if entry.Name == filename {
// 从代码库获取blob对象
blob, err := repository.BlobObject(entry.Hash)
if err != nil {
return false, nil, err
}
// 请求一个Reader
reader, err := blob.Reader()
if err != nil {
return false, nil, err
}
// 为数据分配一个切片...
data := make([]byte, blob.Size)
// ...并读取数据
n, err := reader.Read(data)
if err != nil {
return false, nil, err
}
// 双重检查读取的字节数是否与预期相同
if int64(n) != blob.Size {
return true, nil, fmt.Errorf("wrong size")
}
return true, data, nil
}
}
return false, nil, nil
}
以上函数将在给定的提交引用中,在代码库的顶层查找filename
(当前版本不会遍历子目录)。你需要修改main
函数中的tagsIterator.ForEach
循环,以执行类似以下操作:
err = tagsIterator.ForEach(func(tag *plumbing.Reference) error {
// 获取标签所指向的提交。我们需要这个来解析注释标签。
ref, err := repository.ResolveRevision(plumbing.Revision(tag.Hash().String()))
if err != nil {
handleError(err)
}
found, content, err := getFileFromRef(repository, ref, "package.json")
if found && err == nil {
fmt.Printf("在标签 %s 中找到了\"package.json\"\n", tag.Name().Short())
fmt.Println(string(content))
}
return nil
})
我不知道这是否符合你的要求,但学习go-git
包是很有趣的。
英文:
You don't need to "check out" anything if all you want is the file content; you can extract that directly from the repository. But first, caveats: I am neither an experienced Go programmer, nor have I ever worked with go-git
before, so there may be a more optimal way of doing this.
Starting with a tag, you can:
- Get the commit to which the tag points
- Get the tree to which the commit points
- Iterate through the tree looking for
package.json
- If you find it, extract the corresponding blob. Now you have your content!
The above steps might look something like this:
func getFileFromRef(repository *git.Repository, ref *plumbing.Hash, filename string) (bool, []byte, error) {
// Get the commit object corresponding to ref
commit, err := repository.CommitObject(*ref)
if err != nil {
return false, nil, err
}
// Get the tree object from the commit
tree, err := repository.TreeObject(commit.TreeHash)
if err != nil {
return false, nil, err
}
// Iterate through tree entries
for _, entry := range tree.Entries {
// If we find the target file...
if entry.Name == filename {
// Get the blob object from the repository
blob, err := repository.BlobObject(entry.Hash)
if err != nil {
return false, nil, err
}
// Ask for a Reader
reader, err := blob.Reader()
if err != nil {
return false, nil, err
}
// Allocate a slice for the data...
data := make([]byte, blob.Size)
// ...and read it in.
n, err := reader.Read(data)
if err != nil {
return false, nil, err
}
// Double check that we read as many bytes as
// we expected
if int64(n) != blob.Size {
return true, nil, fmt.Errorf("wrong size")
}
return true, data, nil
}
}
return false, nil, nil
}
The above function will, given a commit reference, look for filename
in the top level of the repository (as written it does not traverse subdirectories). You would need to modify the tagsIterator.ForEach
loop in your main
function to do something like this:
err = tagsIterator.ForEach(func(tag *plumbing.Reference) error {
// Get the commit to which the tag refers. We need this to
// resolve annotated tags.
ref, err := repository.ResolveRevision(plumbing.Revision(tag.Hash().String()))
if err != nil {
handleError(err)
}
found, content, err := getFileFromRef(repository, ref, "package.json")
if found && err == nil {
fmt.Printf("found \"package.json\" in tag %s\n", tag.Name().Short())
fmt.Println(string(content))
}
return nil
})
I don't know if this is what you were looking for, but it was interesting learning about the go-git
package.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论