3

What is the canonical way to push specific single local branch to specific remote with go-git?

I have a local repository checked out and opened with go-git

repo, err := git.PlainOpen("my-repo")

The repo has default origin remote.

I'm trying to sync contents of this repository to another remote mirror, so I'm adding the remote

repo.CreateRemote(&config.RemoteConfig{
                Name: "mirror",
                URLs: []string{"git@github.com:foo/mirror.git"},
            })

First I fetch repo contents from origin

err = remote.Fetch(&git.FetchOptions{
                RemoteName: "origin",
                Tags:       git.AllTags,
            })

... and do a discovery of all branches and tags of interest with remote.List()

Final step is pushing the branches to mirror, while rewriting branch names based on a map. E.g. refs/remotes/origin/master checked out as refs/heads/master should be pushed to mirror remote as main. Therefore I'm iterating over the branches and trying to push them one by one:

refSpec := config.RefSpec(fmt.Sprintf(
                "+%s:refs/remotes/mirror/%s",
                localBranch.Name().String(),
                // map branch names, e.g. master -> main
                mapBranch(remoteBranch.Name().Short()),
            ))
err = repo.Push(&git.PushOptions{
                RemoteName: "mirror",
                Force:      true,
                RefSpecs:   []config.RefSpec{refSpec},
                Atomic:     true,
            })

But this results in git.NoErrAlreadyUpToDate and there's nothing happening on the mirror remote.

David Lukac
  • 728
  • 7
  • 20
  • from my deep dive - https://github.com/go-git/go-git/blob/master/remote.go#L741 - `cmd.Old == cmd.New` - the hashes are equal and therefore push command is not added to the queue – David Lukac Mar 11 '23 at 02:27
  • what does `localBranch` refer to ? don't you want to push `refs/remotes/origin/*` to your mirror ? – LeGEC Mar 11 '23 at 18:10
  • That being said: I find it surprising that `git-go` does the check *locally* and chooses to not send what he seems "up to date". This may actually be an issue. – LeGEC Mar 11 '23 at 18:11

1 Answers1

2

The refSpec, when pushing single branch to a remote, should NOT be in format +refs/heads/localBranchName:refs/remotes/remoteName/remoteBranchName as indicate e.g. here:

// RefSpec is a mapping from local branches to remote references.
...
// eg.: "+refs/heads/*:refs/remotes/origin/*"
//
// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
type RefSpec string

but as

"+refs/heads/localBranchName:refs/heads/remoteBranchName"

instead. See example:

    refSpecStr := fmt.Sprintf(
        "+%s:refs/heads/%s",
        localBranch.Name().String(),
        mapBranch(remoteBranch.Name().Short()),
    )
    refSpec := config.RefSpec(refSpecStr)
    log.Infof("Pushing %s", refSpec)
    err = repo.Push(&git.PushOptions{
        RemoteName: "mirror",
        Force:      true,
        RefSpecs:   []config.RefSpec{refSpec},
        Atomic:     true,
    })
David Lukac
  • 728
  • 7
  • 20