I'm just starting to use the go-git library and so far it looks very promising.
However, I'm trying to do a basic checkout of an existing branch and it is failing with "reference not found".
I have a simple repository, with several branches but one is "main" and another is "testoffmain".
Cloning, pulling and fetching the github repo work without issues.
Getting a hash for the branch seems to work fine as well:
repo, err := git.PlainOpen(localGitRepo)
w, err := repo.Worktree()
// Clone, Fetch, Pull all work, when I'm on the main branch
headRef, err := repo.Head()
newHashRef := plumbing.NewHashReference("refs/heads/testoffmain", headRef.Hash())
hashRef := plumbing.NewHash(newHashRef.String())
fmt.Printf("HashRef: %s", hashRef.String()) // Successfully displays the hash
// Returns a valid hash
revision := "origin/testoffmain"
revHash, err := repo.ResolveRevision(plumbing.Revision(revision))
fmt.Printf("HashRef: %s\n", revHash.String())
// Checkout fails with "reference not found"
referenceName := plumbing.ReferenceName("refs/heads/testoffmain")
err = w.Checkout(&git.CheckoutOptions{
Branch: referenceName,
Force: true})
if err != nil {
fmt.Printf("%s: Checkout: Cound not open local repository, Error: %s\n", project.LocalRepo, err)
return err
}
I've search around, and oddly can't find this simple usecase, so I'm assuming I'm doing something basic wrong.
I'm using github.com/go-git/go-git/v5 v5.5.2
I've tried a variety of options in
referenceName := plumbing.ReferenceName("refs/heads/testoffmain")
referenceName := plumbing.ReferenceName("testoffmain")
referenceName := plumbing.ReferenceName("origin/testoffmain")
referenceName := plumbing.ReferenceName("refs/origin/testoffmain")
referenceName := plumbing.ReferenceName("refs/remotes/origin/testoffmain")
referenceName := plumbing.ReferenceName("refs/remotes/testoffmain")
As I test, I tried copying .git/refs/remote/origin/testoffmain to .git/refs/head/testoffmain and I can the checkout to work, but then other issues occur.
You can list the referenceNames as below:
refs, _ := r.References()
refs.ForEach(func(ref *plumbing.Reference) error {
if ref.Type() == plumbing.HashReference {
fmt.Println(ref)
}
return nil
})
See official link.
And then we can use it for checkout:
w, err := r.Worktree()
if err != nil {
fmt.Println(err)
}
err = w.Checkout(&git.CheckoutOptions{
Branch: plumbing.ReferenceName("refs/remotes/origin/" + branchName),
})
if err != nil {
fmt.Println(err, branchName)
}
where branchName is the name of the git branch you want to switch
Related
I'm trying to execute .ics file that my program just created. Basically, my program is simple CLI calendar app, which generates .ics file. It would be nice if my program would execute this file and add it straight to OS calendar app, without unnecessary searching and executing through OS GUI.
I paste main function to better understanding.
func main() {
serialized, name := cal()
f, err := os.Create(name + ".ics")
if err != nil {
log.Fatal(err)
}
defer f.Close()
_, err2 := f.WriteString(serialized)
if err2 != nil {
log.Fatal(err2)
}
cmd := exec.Command(name + ".ics")
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
}
As it's shown I tried with exec.Command, but it doesnt' work. I was even trying with additional prefixes like ./ or ~/, but it as well didn't work.
Error messages:
fork/exec ./Meeting.ics: permission denied
exec: "Meeting.ics": executable file not found in $PATH
So to sum it up - I want to skip the process where the user has to find a file and open it. I want to make it automatically as a part of my application.
Here's my repository if it would help https://github.com/lenoopaleno/golang-calendar
I'm working on WSL 2, Ubuntu 22.04
Beside the comments above, you might have a problem in your code with the defer f.Close()
The defer runs when the function ends. Until that time your file might or might not be closed and accessible by a different process.
Second you will most likely have to set an execute flag on the a program to run under unix style operating systems.
Program adjustment:
func main() {
serialized, name := cal()
f, err := os.Create(name + ".ics")
if err != nil {
log.Fatal(err)
}
_, err2 := f.WriteString(serialized)
if err2 != nil {
log.Fatal(err2)
}
f.Sync()
f.Close()
exec.Command(`chmod +x `+name+".ics").Run() // This can be done prettier
cmd := exec.Command(name + ".ics")
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
}
I want a list of all the remote branches for a git repo.
Now, this git repo can be private / public. I have the access to the token to access the repo.
I am using this particular SDK : https://pkg.go.dev/github.com/go-git/go-git/v5
One way to do this is ..
r, cloneErr := git.PlainClone(projectRoot, false, cloneOptions)
remote, err := r.Remote("origin")
if err != nil {
panic(err)
}
refList, err := remote.List(&git.ListOptions{})
if err != nil {
panic(err)
}
refPrefix := "refs/heads/"
for _, ref := range refList {
refName := ref.Name().String()
if !strings.HasPrefix(refName, refPrefix) {
continue
}
branchName := refName[len(refPrefix):]
fmt.Println(branchName)
}
But, this involves cloning the repo first.
How can I get the list without cloning the repo ?
Thanks in advence !
would like to use go GitHub client to get result like
git branch -r origin/<branch> --contains <sha>
is there a way to verify if a git sha belongs to a git branch with go-github client?
I don't seem to be able to find an API endpoint that would specifically answer whether the SHA belongs to the branch or not, so you would have to iterate over commit within the branch. Something like:
func main() {
ctx := context.Background()
sts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: "<token>"},
)
tc := oauth2.NewClient(ctx, sts)
client := github.NewClient(tc)
repoOwner := "<owner>"
repoName := "<repo>"
branchToSearch := "<branch>"
shaToFind := "<sha>"
resultsPerPage := 25
listOptions := github.ListOptions{PerPage: resultsPerPage}
for {
rc, resp, err := client.Repositories.ListCommits(ctx,
repoOwner,
repoName,
&github.CommitsListOptions{
SHA: branchToSearch,
ListOptions: listOptions,
},
)
if err != nil {
log.Panic(err)
}
for _, c := range rc {
if *c.SHA == shaToFind {
log.Printf("FOUND commit \"%s\" in the branch \"%s\"\n", shaToFind, branchToSearch)
return
}
}
if resp.NextPage == 0 {
break
}
listOptions.Page = resp.NextPage
}
log.Printf("NOT FOUND commit \"%s\" in the branch \"%s\"\n", shaToFind, branchToSearch)
}
I'm trying to generate keypair with openpgp lib and when I want to test it by encrypting a test string, it returns the following error openpgp: invalid argument: cannot encrypt because no candidate hash functions are compiled in. (Wanted RIPEMD160 in this case.). However it works when I pass a public key exported from gpg.
Also I'm wondering how to encrypt the private key like gpg --generate-key does?
func main() {
var e *openpgp.Entity
var pubKey *bytes.Buffer
e, _ = openpgp.NewEntity("testUser", "test", "test#test.test", nil)
for _, id := range e.Identities {
err := id.SelfSignature.SignUserId(id.UserId.Id, e.PrimaryKey, e.PrivateKey, nil)
if err != nil {
fmt.Println(err)
return
}
}
buf := new(bytes.Buffer)
w, err := armor.Encode(buf, openpgp.PublicKeyType, nil)
if err != nil {
fmt.Println(err)
return
}
e.Serialize(w)
w.Close()
pubKey = buf
// Encrypting test with public key
entity, err := openpgp.ReadArmoredKeyRing(pubKey)
if err != nil {
fmt.Println(err)
return
}
buf = new(bytes.Buffer)
encoderWriter, err := armor.Encode(buf, "PGP MESSAGE", make(map[string]string))
if err != nil {
fmt.Println(err)
return
}
encryptorWriter, err := openpgp.Encrypt(encoderWriter, entity, nil, nil, nil)
if err != nil {
fmt.Println(err)
return
}
encryptorWriter.Write([]byte("hello world"))
encryptorWriter.Close()
encoderWriter.Close()
fmt.Println(buf.String())
}
I had the exact same error.
TL; DR
It seems that it's an abandoned bug of the golang.org/x/crypto/openpgp package.
golang.org/x/crypto/openpgp package is frozen and deprecated. (= wontfix)
Use a patched fork package instead.
github.com/ProtonMail/go-crypto package # GitHub
TS; DR
Since the official "golang.org/x/crypto/openpgp" package was frozen and deprecated, as long as we use the "golang.org/x/crypto/openpgp" package, it seems that the only current workaround is to either;
Downgrade the Go version and the package, then blank import "_ golang.org/x/crypto/ripemd160" as #mh-cbon mentioned.
Patch the rejected PR on your own. (Rejected due to the freezing of x/crypto/openpgp package)
Patch: https://github.com/golang/crypto/pull/128/files
PR: Use correct default hashes and default ciphers when no preferences given
But I had to implement an OpenPGP key pair generator on Go 1.16.6. Don't ask why...
So, my current alternative was to use the forked package. Which was one of the abounding forks that the Go team mentioned as a sample.
github.com/ProtonMail/go-crypto package # GitHub
go get github.com/ProtonMail/go-crypto
Remove golang.org/x/crypto/openpgp from go.mod
Replace all the "golang.org/x/crypto/openpgp" to "github.com/ProtonMail/go-crypto/openpgp" in the source code.
go mod tidy
References
"x/crypto/openpgp: mark as frozen and deprecated" | Issue #44226 | go | golang # GitHub
"x/crypto/openpgp: new entities cannot be encrypted to by default" | Issue #37646 | go | golang # GitHub
"x/crypto/openpgp: new entities cannot be encrypted to by default" | Issue #12153 | go | golang # GitHub
I'm trying to delete a local branch using go-git, but getting an error branch not found when I run
branch := "refs/heads/template/test"
err = repo.DeleteBranch(branch)
or
err = repo.DeleteBranch(plumbing.ReferenceName(branch))
Using just the name of the branch (template/test) doesn't work either. The branch is included in the list of branches. This
refs, err := repo.Branches()
err = refs.ForEach(func(ref *plumbing.Reference) error {
fmt.Println(ref)
return nil
})
provides the following output:
f2d93bb67ced13936dbbbbfb44502abd42e7df13 refs/heads/global
df46ab083f17051afd6ca20e3ea4bfe01aedbb37 refs/heads/template/test
141f45305380aa0dc9f6802512ea76c5d48a87a1 refs/heads/template/test2
How can I delete it?
Update:
I checked the function DeleteBranch, it looks like this:
// DeleteBranch delete a Branch from the repository and delete the config
func (r *Repository) DeleteBranch(name string) error {
cfg, err := r.Storer.Config()
if err != nil {
return err
}
if _, ok := cfg.Branches[name]; !ok {
return ErrBranchNotFound
}
delete(cfg.Branches, name)
return r.Storer.SetConfig(cfg)
}
then I created cfg := repo.Storer.Config() and checked what cfg.Branches contains. Surprisingly, this map has only the following element: &{global origin refs/heads/global 0xc0007fbf80}. So other branches can't be deleted because they are not found in this config.
I know that this question is quite old but the naming here tripped me up as well so I thought I'd provide an answer.
Thanks to #Hephaestus and the post linked to, I was able to sort out the correct approach. If you're trying to delete a local branch (e.g. git branch -d foo) it looks like you're meant to use repo.Storer.RemoveReference.
In my case I already have a slice of (short) branch names:
for _, branchName := range branches {
ref := plumbing.NewBranchReferenceName(branchName)
err = repo.Storer.RemoveReference(ref)
if err != nil {
return "", err
}
}