Skip to content

Commit

Permalink
feat: add server-side git commands (#96)
Browse files Browse the repository at this point in the history
Co-authored-by: Joe Chen <[email protected]>
  • Loading branch information
aymanbagabas and unknwon authored Aug 11, 2023
1 parent bd58efd commit 323efc1
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 1 deletion.
2 changes: 1 addition & 1 deletion repo_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ func (r *Repository) Branches() ([]string, error) {

// DeleteBranchOptions contains optional arguments for deleting a branch.
//
// // Docs: https://git-scm.com/docs/git-branch
// Docs: https://git-scm.com/docs/git-branch
type DeleteBranchOptions struct {
// Indicates whether to force delete the branch.
Force bool
Expand Down
113 changes: 113 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2023 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
"time"
)

// UpdateServerInfoOptions contains optional arguments for updating auxiliary
// info file on the server side.
//
// Docs: https://git-scm.com/docs/git-update-server-info
type UpdateServerInfoOptions struct {
// Indicates whether to overwrite the existing server info.
Force bool
// The timeout duration before giving up for each shell command execution. The
// default timeout duration will be used when not supplied.
Timeout time.Duration
// The additional options to be passed to the underlying git.
CommandOptions
}

// UpdateServerInfo updates the auxiliary info file on the server side for the
// repository in given path.
func UpdateServerInfo(path string, opts ...UpdateServerInfoOptions) error {
var opt UpdateServerInfoOptions
if len(opts) > 0 {
opt = opts[0]
}
cmd := NewCommand("update-server-info").AddOptions(opt.CommandOptions)
if opt.Force {
cmd.AddArgs("--force")
}
_, err := cmd.RunInDirWithTimeout(opt.Timeout, path)
return err
}

// ReceivePackOptions contains optional arguments for receiving the info pushed
// to the repository.
//
// Docs: https://git-scm.com/docs/git-receive-pack
type ReceivePackOptions struct {
// Indicates whether to suppress the log output.
Quiet bool
// Indicates whether to generate the "info/refs" used by the "git http-backend".
HTTPBackendInfoRefs bool
// The timeout duration before giving up for each shell command execution. The
// default timeout duration will be used when not supplied.
Timeout time.Duration
// The additional options to be passed to the underlying git.
CommandOptions
}

// ReceivePack receives what is pushed into the repository in given path.
func ReceivePack(path string, opts ...ReceivePackOptions) ([]byte, error) {
var opt ReceivePackOptions
if len(opts) > 0 {
opt = opts[0]
}
cmd := NewCommand("receive-pack").AddOptions(opt.CommandOptions)
if opt.Quiet {
cmd.AddArgs("--quiet")
}
if opt.HTTPBackendInfoRefs {
cmd.AddArgs("--http-backend-info-refs")
}
cmd.AddArgs(".")
return cmd.RunInDirWithTimeout(opt.Timeout, path)
}

// UploadPackOptions contains optional arguments for sending the packfile to the
// client.
//
// Docs: https://git-scm.com/docs/git-upload-pack
type UploadPackOptions struct {
// Indicates whether to quit after a single request/response exchange.
StatelessRPC bool
// Indicates whether to not try "<directory>/.git/" if "<directory>" is not a
// Git directory.
Strict bool
// Indicates whether to generate the "info/refs" used by the "git http-backend".
HTTPBackendInfoRefs bool
// The timeout duration before giving up for each shell command execution. The
// default timeout duration will be used when not supplied.
Timeout time.Duration
// The additional options to be passed to the underlying git.
CommandOptions
}

// UploadPack sends the packfile to the client for the repository in given path.
func UploadPack(path string, opts ...UploadPackOptions) ([]byte, error) {
var opt UploadPackOptions
if len(opts) > 0 {
opt = opts[0]
}
cmd := NewCommand("upload-pack").AddOptions(opt.CommandOptions)
if opt.StatelessRPC {
cmd.AddArgs("--stateless-rpc")
}
if opt.Strict {
cmd.AddArgs("--strict")
}
if opt.Timeout > 0 {
cmd.AddArgs("--timeout", opt.Timeout.String())
}
if opt.HTTPBackendInfoRefs {
cmd.AddArgs("--http-backend-info-refs")
}
cmd.AddArgs(".")
return cmd.RunInDirWithTimeout(opt.Timeout, path)
}
42 changes: 42 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2023 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestUpdateServerInfo(t *testing.T) {
err := os.RemoveAll(filepath.Join(repoPath, "info"))
require.NoError(t, err)
err = UpdateServerInfo(repoPath, UpdateServerInfoOptions{Force: true})
require.NoError(t, err)
assert.True(t, isFile(filepath.Join(repoPath, "info", "refs")))
}

func TestReceivePack(t *testing.T) {
got, err := ReceivePack(repoPath, ReceivePackOptions{HTTPBackendInfoRefs: true})
require.NoError(t, err)
const contains = "report-status report-status-v2 delete-refs side-band-64k quiet atomic ofs-delta object-format=sha1 agent=git/"
assert.Contains(t, string(got), contains)
}

func TestUploadPack(t *testing.T) {
got, err := UploadPack(repoPath,
UploadPackOptions{
StatelessRPC: true,
Strict: true,
HTTPBackendInfoRefs: true,
},
)
require.NoError(t, err)
const contains = "multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed no-done symref=HEAD:refs/heads/master object-format=sha1 agent=git/"
assert.Contains(t, string(got), contains)
}

0 comments on commit 323efc1

Please sign in to comment.