mirror of
https://github.com/gogs/gogs.git
synced 2025-12-20 23:30:00 +01:00
After sync mirror, get latest commit date and compare to current repository updated time, only update it if the commit date is newer.
296 lines
6.9 KiB
Go
296 lines
6.9 KiB
Go
// Copyright 2015 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 (
|
|
"bytes"
|
|
"container/list"
|
|
"errors"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Unknwon/com"
|
|
)
|
|
|
|
// Repository represents a Git repository.
|
|
type Repository struct {
|
|
Path string
|
|
|
|
commitCache *objectCache
|
|
tagCache *objectCache
|
|
}
|
|
|
|
const _PRETTY_LOG_FORMAT = `--pretty=format:%H`
|
|
|
|
func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, error) {
|
|
l := list.New()
|
|
if len(logs) == 0 {
|
|
return l, nil
|
|
}
|
|
|
|
parts := bytes.Split(logs, []byte{'\n'})
|
|
|
|
for _, commitId := range parts {
|
|
commit, err := repo.GetCommit(string(commitId))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l.PushBack(commit)
|
|
}
|
|
|
|
return l, nil
|
|
}
|
|
|
|
type NetworkOptions struct {
|
|
URL string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// IsRepoURLAccessible checks if given repository URL is accessible.
|
|
func IsRepoURLAccessible(opts NetworkOptions) bool {
|
|
cmd := NewCommand("ls-remote", "-q", "-h", opts.URL, "HEAD")
|
|
if opts.Timeout <= 0 {
|
|
opts.Timeout = -1
|
|
}
|
|
_, err := cmd.RunTimeout(opts.Timeout)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// InitRepository initializes a new Git repository.
|
|
func InitRepository(repoPath string, bare bool) error {
|
|
os.MkdirAll(repoPath, os.ModePerm)
|
|
|
|
cmd := NewCommand("init")
|
|
if bare {
|
|
cmd.AddArguments("--bare")
|
|
}
|
|
_, err := cmd.RunInDir(repoPath)
|
|
return err
|
|
}
|
|
|
|
// OpenRepository opens the repository at the given path.
|
|
func OpenRepository(repoPath string) (*Repository, error) {
|
|
repoPath, err := filepath.Abs(repoPath)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !isDir(repoPath) {
|
|
return nil, errors.New("no such file or directory")
|
|
}
|
|
|
|
return &Repository{
|
|
Path: repoPath,
|
|
commitCache: newObjectCache(),
|
|
tagCache: newObjectCache(),
|
|
}, nil
|
|
}
|
|
|
|
type CloneRepoOptions struct {
|
|
Mirror bool
|
|
Bare bool
|
|
Quiet bool
|
|
Branch string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// Clone clones original repository to target path.
|
|
func Clone(from, to string, opts CloneRepoOptions) (err error) {
|
|
toDir := path.Dir(to)
|
|
if err = os.MkdirAll(toDir, os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd := NewCommand("clone")
|
|
if opts.Mirror {
|
|
cmd.AddArguments("--mirror")
|
|
}
|
|
if opts.Bare {
|
|
cmd.AddArguments("--bare")
|
|
}
|
|
if opts.Quiet {
|
|
cmd.AddArguments("--quiet")
|
|
}
|
|
if len(opts.Branch) > 0 {
|
|
cmd.AddArguments("-b", opts.Branch)
|
|
}
|
|
cmd.AddArguments(from, to)
|
|
|
|
if opts.Timeout <= 0 {
|
|
opts.Timeout = -1
|
|
}
|
|
_, err = cmd.RunTimeout(opts.Timeout)
|
|
return err
|
|
}
|
|
|
|
type FetchRemoteOptions struct {
|
|
Prune bool
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// Fetch fetches changes from remotes without merging.
|
|
func Fetch(repoPath string, opts FetchRemoteOptions) error {
|
|
cmd := NewCommand("fetch")
|
|
if opts.Prune {
|
|
cmd.AddArguments("--prune")
|
|
}
|
|
|
|
if opts.Timeout <= 0 {
|
|
opts.Timeout = -1
|
|
}
|
|
_, err := cmd.RunInDirTimeout(opts.Timeout, repoPath)
|
|
return err
|
|
}
|
|
|
|
type PullRemoteOptions struct {
|
|
All bool
|
|
Rebase bool
|
|
Remote string
|
|
Branch string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// Pull pulls changes from remotes.
|
|
func Pull(repoPath string, opts PullRemoteOptions) error {
|
|
cmd := NewCommand("pull")
|
|
if opts.Rebase {
|
|
cmd.AddArguments("--rebase")
|
|
}
|
|
if opts.All {
|
|
cmd.AddArguments("--all")
|
|
} else {
|
|
cmd.AddArguments(opts.Remote)
|
|
cmd.AddArguments(opts.Branch)
|
|
}
|
|
|
|
if opts.Timeout <= 0 {
|
|
opts.Timeout = -1
|
|
}
|
|
_, err := cmd.RunInDirTimeout(opts.Timeout, repoPath)
|
|
return err
|
|
}
|
|
|
|
// Push pushs local commits to given remote branch.
|
|
func Push(repoPath, remote, branch string) error {
|
|
_, err := NewCommand("push", remote, branch).RunInDir(repoPath)
|
|
return err
|
|
}
|
|
|
|
type CheckoutOptions struct {
|
|
Branch string
|
|
OldBranch string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// Checkout checkouts a branch
|
|
func Checkout(repoPath string, opts CheckoutOptions) error {
|
|
cmd := NewCommand("checkout")
|
|
if len(opts.OldBranch) > 0 {
|
|
cmd.AddArguments("-b")
|
|
}
|
|
|
|
cmd.AddArguments(opts.Branch)
|
|
|
|
if len(opts.OldBranch) > 0 {
|
|
cmd.AddArguments(opts.OldBranch)
|
|
}
|
|
if opts.Timeout <= 0 {
|
|
opts.Timeout = -1
|
|
}
|
|
_, err := cmd.RunInDirTimeout(opts.Timeout, repoPath)
|
|
return err
|
|
}
|
|
|
|
// ResetHEAD resets HEAD to given revision or head of branch.
|
|
func ResetHEAD(repoPath string, hard bool, revision string) error {
|
|
cmd := NewCommand("reset")
|
|
if hard {
|
|
cmd.AddArguments("--hard")
|
|
}
|
|
_, err := cmd.AddArguments(revision).RunInDir(repoPath)
|
|
return err
|
|
}
|
|
|
|
// MoveFile moves a file to another file or directory.
|
|
func MoveFile(repoPath, oldTreeName, newTreeName string) error {
|
|
_, err := NewCommand("mv").AddArguments(oldTreeName, newTreeName).RunInDir(repoPath)
|
|
return err
|
|
}
|
|
|
|
// CountObject represents disk usage report of Git repository.
|
|
type CountObject struct {
|
|
Count int64
|
|
Size int64
|
|
InPack int64
|
|
Packs int64
|
|
SizePack int64
|
|
PrunePackable int64
|
|
Garbage int64
|
|
SizeGarbage int64
|
|
}
|
|
|
|
const (
|
|
_STAT_COUNT = "count: "
|
|
_STAT_SIZE = "size: "
|
|
_STAT_IN_PACK = "in-pack: "
|
|
_STAT_PACKS = "packs: "
|
|
_STAT_SIZE_PACK = "size-pack: "
|
|
_STAT_PRUNE_PACKABLE = "prune-packable: "
|
|
_STAT_GARBAGE = "garbage: "
|
|
_STAT_SIZE_GARBAGE = "size-garbage: "
|
|
)
|
|
|
|
// GetRepoSize returns disk usage report of repository in given path.
|
|
func GetRepoSize(repoPath string) (*CountObject, error) {
|
|
cmd := NewCommand("count-objects", "-v")
|
|
stdout, err := cmd.RunInDir(repoPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
countObject := new(CountObject)
|
|
for _, line := range strings.Split(stdout, "\n") {
|
|
switch {
|
|
case strings.HasPrefix(line, _STAT_COUNT):
|
|
countObject.Count = com.StrTo(line[7:]).MustInt64()
|
|
case strings.HasPrefix(line, _STAT_SIZE):
|
|
countObject.Size = com.StrTo(line[6:]).MustInt64() * 1024
|
|
case strings.HasPrefix(line, _STAT_IN_PACK):
|
|
countObject.InPack = com.StrTo(line[9:]).MustInt64()
|
|
case strings.HasPrefix(line, _STAT_PACKS):
|
|
countObject.Packs = com.StrTo(line[7:]).MustInt64()
|
|
case strings.HasPrefix(line, _STAT_SIZE_PACK):
|
|
countObject.SizePack = com.StrTo(line[11:]).MustInt64() * 1024
|
|
case strings.HasPrefix(line, _STAT_PRUNE_PACKABLE):
|
|
countObject.PrunePackable = com.StrTo(line[16:]).MustInt64()
|
|
case strings.HasPrefix(line, _STAT_GARBAGE):
|
|
countObject.Garbage = com.StrTo(line[9:]).MustInt64()
|
|
case strings.HasPrefix(line, _STAT_SIZE_GARBAGE):
|
|
countObject.SizeGarbage = com.StrTo(line[14:]).MustInt64() * 1024
|
|
}
|
|
}
|
|
|
|
return countObject, nil
|
|
}
|
|
|
|
// GetLatestCommitDate returns the date of latest commit of repository.
|
|
// If branch is empty, it returns the latest commit across all branches.
|
|
func GetLatestCommitDate(repoPath, branch string) (time.Time, error) {
|
|
cmd := NewCommand("for-each-ref", "--count=1", "--sort=-committerdate", "--format=%(committerdate:iso8601)")
|
|
if len(branch) > 0 {
|
|
cmd.AddArguments("refs/heads/" + branch)
|
|
}
|
|
stdout, err := cmd.RunInDir(repoPath)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
return time.Parse("2006-01-02 15:04:05 -0700", strings.TrimSpace(stdout))
|
|
}
|