2016-11-03 23:16:01 +01:00
// 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"
2017-04-08 10:23:39 +08:00
"strings"
2016-11-03 23:16:01 +01:00
"time"
2017-04-08 10:23:39 +08:00
"github.com/Unknwon/com"
2016-11-03 23:16:01 +01:00
)
// Repository represents a Git repository.
type Repository struct {
Path string
2016-12-22 17:30:52 +08:00
commitCache * ObjectCache
tagCache * ObjectCache
2016-11-03 23:16:01 +01:00
}
2016-12-22 17:30:52 +08:00
const prettyLogFormat = ` --pretty=format:%H `
2016-11-03 23:16:01 +01:00
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' } )
2016-12-22 17:30:52 +08:00
for _ , commitID := range parts {
commit , err := repo . GetCommit ( string ( commitID ) )
2016-11-03 23:16:01 +01:00
if err != nil {
return nil , err
}
l . PushBack ( commit )
}
return l , nil
}
// IsRepoURLAccessible checks if given repository URL is accessible.
func IsRepoURLAccessible ( url string ) bool {
_ , err := NewCommand ( "ls-remote" , "-q" , "-h" , url , "HEAD" ) . Run ( )
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
}
2016-12-22 17:30:52 +08:00
// CloneRepoOptions options when clone a repository
2016-11-03 23:16:01 +01:00
type CloneRepoOptions struct {
Timeout time . Duration
Mirror bool
Bare bool
Quiet bool
Branch string
}
// 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
}
2016-12-22 17:30:52 +08:00
// PullRemoteOptions options when pull from remote
2016-11-03 23:16:01 +01:00
type PullRemoteOptions struct {
Timeout time . Duration
All bool
2016-11-12 12:09:25 +01:00
Rebase bool
2016-11-03 23:16:01 +01:00
Remote string
Branch string
}
// Pull pulls changes from remotes.
func Pull ( repoPath string , opts PullRemoteOptions ) error {
cmd := NewCommand ( "pull" )
2016-11-12 12:09:25 +01:00
if opts . Rebase {
cmd . AddArguments ( "--rebase" )
}
2016-11-03 23:16:01 +01:00
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
}
2016-12-22 17:30:52 +08:00
// CheckoutOptions options when heck out some branch
2016-11-03 23:16:01 +01:00
type CheckoutOptions struct {
Timeout time . Duration
Branch string
OldBranch string
}
// Checkout checkouts a branch
func Checkout ( repoPath string , opts CheckoutOptions ) error {
cmd := NewCommand ( "checkout" )
if len ( opts . OldBranch ) > 0 {
cmd . AddArguments ( "-b" )
}
if opts . Timeout <= 0 {
opts . Timeout = - 1
}
cmd . AddArguments ( opts . Branch )
if len ( opts . OldBranch ) > 0 {
cmd . AddArguments ( opts . OldBranch )
}
_ , 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
}
2017-04-08 10:23:39 +08:00
// CountObject represents repository count objects report
type CountObject struct {
Count int64
Size int64
InPack int64
Packs int64
SizePack int64
PrunePack int64
Garbage int64
SizeGarbage int64
}
const (
statCount = "count: "
statSize = "size: "
statInpack = "in-pack: "
statPacks = "packs: "
statSizePack = "size-pack: "
statPrunePackage = "prune-package: "
statGarbage = "garbage: "
statSizeGarbage = "size-garbage: "
)
// GetRepoSize returns disk consumption for repo in path
func GetRepoSize ( repoPath string ) ( * CountObject , error ) {
cmd := NewCommand ( "count-objects" , "-v" )
stdout , err := cmd . RunInDir ( repoPath )
if err != nil {
return nil , err
}
return parseSize ( stdout ) , nil
}
// parseSize parses the output from count-objects and return a CountObject
func parseSize ( objects string ) * CountObject {
repoSize := new ( CountObject )
for _ , line := range strings . Split ( objects , "\n" ) {
switch {
case strings . HasPrefix ( line , statCount ) :
repoSize . Count = com . StrTo ( line [ 7 : ] ) . MustInt64 ( )
case strings . HasPrefix ( line , statSize ) :
repoSize . Size = com . StrTo ( line [ 6 : ] ) . MustInt64 ( ) * 1024
case strings . HasPrefix ( line , statInpack ) :
repoSize . InPack = com . StrTo ( line [ 9 : ] ) . MustInt64 ( )
case strings . HasPrefix ( line , statPacks ) :
repoSize . Packs = com . StrTo ( line [ 7 : ] ) . MustInt64 ( )
case strings . HasPrefix ( line , statSizePack ) :
repoSize . SizePack = com . StrTo ( line [ 11 : ] ) . MustInt64 ( ) * 1024
case strings . HasPrefix ( line , statPrunePackage ) :
repoSize . PrunePack = com . StrTo ( line [ 16 : ] ) . MustInt64 ( )
case strings . HasPrefix ( line , statGarbage ) :
repoSize . Garbage = com . StrTo ( line [ 9 : ] ) . MustInt64 ( )
case strings . HasPrefix ( line , statSizeGarbage ) :
repoSize . SizeGarbage = com . StrTo ( line [ 14 : ] ) . MustInt64 ( ) * 1024
}
}
return repoSize
}