vendor: rename "gogits" to "gogs"

This commit is contained in:
Unknwon
2018-05-27 09:07:15 +08:00
parent aff4208244
commit e33d9e77f4
84 changed files with 25 additions and 12 deletions

19
vendor/github.com/gogs/git-module/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 All Gogs Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

13
vendor/github.com/gogs/git-module/README.md generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# Git Module [![Build Status](https://travis-ci.org/gogits/git-module.svg?branch=master)](https://travis-ci.org/gogits/git-module)
Package git-module is a Go module for Git access through shell commands.
## Limitations
- Go version must be at least **1.4**.
- Git version must be no less than **1.7.1**, and greater than or equal to **1.8.3** is recommended.
- For Windows users, try use as new a version as possible.
## License
This project is under the MIT License. See the [LICENSE](LICENSE) file for the full license text.

35
vendor/github.com/gogs/git-module/blob.go generated vendored Normal file
View File

@@ -0,0 +1,35 @@
// 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"
"io"
)
// Blob represents a Git object.
type Blob struct {
repo *Repository
*TreeEntry
}
// Data gets content of blob all at once and wrap it as io.Reader.
// This can be very slow and memory consuming for huge content.
func (b *Blob) Data() (io.Reader, error) {
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
// Preallocate memory to save ~50% memory usage on big files.
stdout.Grow(int(b.Size() + 2048))
if err := b.DataPipeline(stdout, stderr); err != nil {
return nil, concatenateError(err, stderr.String())
}
return stdout, nil
}
func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
}

150
vendor/github.com/gogs/git-module/command.go generated vendored Normal file
View File

@@ -0,0 +1,150 @@
// 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"
"fmt"
"io"
"os"
"os/exec"
"strings"
"time"
)
// Command represents a command with its subcommands or arguments.
type Command struct {
name string
args []string
envs []string
}
func (c *Command) String() string {
if len(c.args) == 0 {
return c.name
}
return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " "))
}
// NewCommand creates and returns a new Git Command based on given command and arguments.
func NewCommand(args ...string) *Command {
return &Command{
name: "git",
args: args,
}
}
// AddArguments adds new argument(s) to the command.
func (c *Command) AddArguments(args ...string) *Command {
c.args = append(c.args, args...)
return c
}
// AddEnvs adds new environment variables to the command.
func (c *Command) AddEnvs(envs ...string) *Command {
c.envs = append(c.envs, envs...)
return c
}
const DEFAULT_TIMEOUT = 60 * time.Second
// RunInDirTimeoutPipeline executes the command in given directory with given timeout,
// it pipes stdout and stderr to given io.Writer.
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
if timeout == -1 {
timeout = DEFAULT_TIMEOUT
}
if len(dir) == 0 {
log(c.String())
} else {
log("%s: %v", dir, c)
}
cmd := exec.Command(c.name, c.args...)
if c.envs != nil {
cmd.Env = append(os.Environ(), c.envs...)
}
cmd.Dir = dir
cmd.Stdout = stdout
cmd.Stderr = stderr
if err := cmd.Start(); err != nil {
return err
}
done := make(chan error)
go func() {
done <- cmd.Wait()
}()
var err error
select {
case <-time.After(timeout):
if cmd.Process != nil && cmd.ProcessState != nil && !cmd.ProcessState.Exited() {
if err := cmd.Process.Kill(); err != nil {
return fmt.Errorf("fail to kill process: %v", err)
}
}
<-done
return ErrExecTimeout{timeout}
case err = <-done:
}
return err
}
// RunInDirTimeout executes the command in given directory with given timeout,
// and returns stdout in []byte and error (combined with stderr).
func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil {
return nil, concatenateError(err, stderr.String())
}
if stdout.Len() > 0 {
log("stdout:\n%s", stdout.Bytes()[:1024])
}
return stdout.Bytes(), nil
}
// RunInDirPipeline executes the command in given directory,
// it pipes stdout and stderr to given io.Writer.
func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr)
}
// RunInDir executes the command in given directory
// and returns stdout in []byte and error (combined with stderr).
func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
return c.RunInDirTimeout(-1, dir)
}
// RunInDir executes the command in given directory
// and returns stdout in string and error (combined with stderr).
func (c *Command) RunInDir(dir string) (string, error) {
stdout, err := c.RunInDirTimeout(-1, dir)
if err != nil {
return "", err
}
return string(stdout), nil
}
// RunTimeout executes the command in defualt working directory with given timeout,
// and returns stdout in string and error (combined with stderr).
func (c *Command) RunTimeout(timeout time.Duration) (string, error) {
stdout, err := c.RunInDirTimeout(timeout, "")
if err != nil {
return "", err
}
return string(stdout), nil
}
// Run executes the command in defualt working directory
// and returns stdout in string and error (combined with stderr).
func (c *Command) Run() (string, error) {
return c.RunTimeout(-1)
}

310
vendor/github.com/gogs/git-module/commit.go generated vendored Normal file
View File

@@ -0,0 +1,310 @@
// 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 (
"bufio"
"bytes"
"container/list"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"github.com/mcuadros/go-version"
)
// Commit represents a git commit.
type Commit struct {
Tree
ID sha1 // The ID of this commit object
Author *Signature
Committer *Signature
CommitMessage string
parents []sha1 // SHA1 strings
submoduleCache *objectCache
}
// Message returns the commit message. Same as retrieving CommitMessage directly.
func (c *Commit) Message() string {
return c.CommitMessage
}
// Summary returns first line of commit message.
func (c *Commit) Summary() string {
return strings.Split(c.CommitMessage, "\n")[0]
}
// ParentID returns oid of n-th parent (0-based index).
// It returns nil if no such parent exists.
func (c *Commit) ParentID(n int) (sha1, error) {
if n >= len(c.parents) {
return sha1{}, ErrNotExist{"", ""}
}
return c.parents[n], nil
}
// Parent returns n-th parent (0-based index) of the commit.
func (c *Commit) Parent(n int) (*Commit, error) {
id, err := c.ParentID(n)
if err != nil {
return nil, err
}
parent, err := c.repo.getCommit(id)
if err != nil {
return nil, err
}
return parent, nil
}
// ParentCount returns number of parents of the commit.
// 0 if this is the root commit, otherwise 1,2, etc.
func (c *Commit) ParentCount() int {
return len(c.parents)
}
func isImageFile(data []byte) (string, bool) {
contentType := http.DetectContentType(data)
if strings.Index(contentType, "image/") != -1 {
return contentType, true
}
return contentType, false
}
func (c *Commit) IsImageFile(name string) bool {
blob, err := c.GetBlobByPath(name)
if err != nil {
return false
}
dataRc, err := blob.Data()
if err != nil {
return false
}
buf := make([]byte, 1024)
n, _ := dataRc.Read(buf)
buf = buf[:n]
_, isImage := isImageFile(buf)
return isImage
}
// GetCommitByPath return the commit of relative path object.
func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) {
return c.repo.getCommitByPathWithID(c.ID, relpath)
}
// AddAllChanges marks local changes to be ready for commit.
func AddChanges(repoPath string, all bool, files ...string) error {
cmd := NewCommand("add")
if all {
cmd.AddArguments("--all")
}
_, err := cmd.AddArguments(files...).RunInDir(repoPath)
return err
}
type CommitChangesOptions struct {
Committer *Signature
Author *Signature
Message string
}
// CommitChanges commits local changes with given committer, author and message.
// If author is nil, it will be the same as committer.
func CommitChanges(repoPath string, opts CommitChangesOptions) error {
cmd := NewCommand()
if opts.Committer != nil {
cmd.AddEnvs("GIT_COMMITTER_NAME="+opts.Committer.Name, "GIT_COMMITTER_EMAIL="+opts.Committer.Email)
}
cmd.AddArguments("commit")
if opts.Author == nil {
opts.Author = opts.Committer
}
if opts.Author != nil {
cmd.AddArguments(fmt.Sprintf("--author='%s <%s>'", opts.Author.Name, opts.Author.Email))
}
cmd.AddArguments("-m", opts.Message)
_, err := cmd.RunInDir(repoPath)
// No stderr but exit status 1 means nothing to commit.
if err != nil && err.Error() == "exit status 1" {
return nil
}
return err
}
func commitsCount(repoPath, revision, relpath string) (int64, error) {
var cmd *Command
isFallback := false
if version.Compare(gitVersion, "1.8.0", "<") {
isFallback = true
cmd = NewCommand("log", "--pretty=format:''")
} else {
cmd = NewCommand("rev-list", "--count")
}
cmd.AddArguments(revision)
if len(relpath) > 0 {
cmd.AddArguments("--", relpath)
}
stdout, err := cmd.RunInDir(repoPath)
if err != nil {
return 0, err
}
if isFallback {
return int64(strings.Count(stdout, "\n")) + 1, nil
}
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
}
// CommitsCount returns number of total commits of until given revision.
func CommitsCount(repoPath, revision string) (int64, error) {
return commitsCount(repoPath, revision, "")
}
func (c *Commit) CommitsCount() (int64, error) {
return CommitsCount(c.repo.Path, c.ID.String())
}
func (c *Commit) CommitsByRangeSize(page, size int) (*list.List, error) {
return c.repo.CommitsByRangeSize(c.ID.String(), page, size)
}
func (c *Commit) CommitsByRange(page int) (*list.List, error) {
return c.repo.CommitsByRange(c.ID.String(), page)
}
func (c *Commit) CommitsBefore() (*list.List, error) {
return c.repo.getCommitsBefore(c.ID)
}
func (c *Commit) CommitsBeforeLimit(num int) (*list.List, error) {
return c.repo.getCommitsBeforeLimit(c.ID, num)
}
func (c *Commit) CommitsBeforeUntil(commitID string) (*list.List, error) {
endCommit, err := c.repo.GetCommit(commitID)
if err != nil {
return nil, err
}
return c.repo.CommitsBetween(c, endCommit)
}
func (c *Commit) SearchCommits(keyword string) (*list.List, error) {
return c.repo.searchCommits(c.ID, keyword)
}
func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error) {
return c.repo.getFilesChanged(pastCommit, c.ID.String())
}
func (c *Commit) GetSubModules() (*objectCache, error) {
if c.submoduleCache != nil {
return c.submoduleCache, nil
}
entry, err := c.GetTreeEntryByPath(".gitmodules")
if err != nil {
return nil, err
}
rd, err := entry.Blob().Data()
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(rd)
c.submoduleCache = newObjectCache()
var ismodule bool
var path string
for scanner.Scan() {
if strings.HasPrefix(scanner.Text(), "[submodule") {
ismodule = true
continue
}
if ismodule {
fields := strings.Split(scanner.Text(), "=")
k := strings.TrimSpace(fields[0])
if k == "path" {
path = strings.TrimSpace(fields[1])
} else if k == "url" {
c.submoduleCache.Set(path, &SubModule{path, strings.TrimSpace(fields[1])})
ismodule = false
}
}
}
return c.submoduleCache, nil
}
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
modules, err := c.GetSubModules()
if err != nil {
return nil, err
}
module, has := modules.Get(entryname)
if has {
return module.(*SubModule), nil
}
return nil, nil
}
// CommitFileStatus represents status of files in a commit.
type CommitFileStatus struct {
Added []string
Removed []string
Modified []string
}
func NewCommitFileStatus() *CommitFileStatus {
return &CommitFileStatus{
[]string{}, []string{}, []string{},
}
}
// GetCommitFileStatus returns file status of commit in given repository.
func GetCommitFileStatus(repoPath, commitID string) (*CommitFileStatus, error) {
stdout, w := io.Pipe()
done := make(chan struct{})
fileStatus := NewCommitFileStatus()
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) < 2 {
continue
}
switch fields[0][0] {
case 'A':
fileStatus.Added = append(fileStatus.Added, fields[1])
case 'D':
fileStatus.Removed = append(fileStatus.Removed, fields[1])
case 'M':
fileStatus.Modified = append(fileStatus.Modified, fields[1])
}
}
done <- struct{}{}
}()
stderr := new(bytes.Buffer)
err := NewCommand("log", "-1", "--name-status", "--pretty=format:''", commitID).RunInDirPipeline(repoPath, w, stderr)
w.Close() // Close writer to exit parsing goroutine
if err != nil {
return nil, concatenateError(err, stderr.String())
}
<-done
return fileStatus, nil
}
// FileStatus returns file status of commit.
func (c *Commit) FileStatus() (*CommitFileStatus, error) {
return GetCommitFileStatus(c.repo.Path, c.ID.String())
}

33
vendor/github.com/gogs/git-module/commit_archive.go generated vendored Normal file
View File

@@ -0,0 +1,33 @@
// 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 (
"fmt"
"path/filepath"
"strings"
)
type ArchiveType int
const (
ZIP ArchiveType = iota + 1
TARGZ
)
func (c *Commit) CreateArchive(target string, archiveType ArchiveType) error {
var format string
switch archiveType {
case ZIP:
format = "zip"
case TARGZ:
format = "tar.gz"
default:
return fmt.Errorf("unknown format: %v", archiveType)
}
_, err := NewCommand("archive", "--prefix="+filepath.Base(strings.TrimSuffix(c.repo.Path, ".git"))+"/", "--format="+format, "-o", target, c.ID.String()).RunInDir(c.repo.Path)
return err
}

61
vendor/github.com/gogs/git-module/error.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
// 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 (
"fmt"
"time"
)
type ErrExecTimeout struct {
Duration time.Duration
}
func IsErrExecTimeout(err error) bool {
_, ok := err.(ErrExecTimeout)
return ok
}
func (err ErrExecTimeout) Error() string {
return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
}
type ErrNotExist struct {
ID string
RelPath string
}
func IsErrNotExist(err error) bool {
_, ok := err.(ErrNotExist)
return ok
}
func (err ErrNotExist) Error() string {
return fmt.Sprintf("object does not exist [id: %s, rel_path: %s]", err.ID, err.RelPath)
}
type ErrUnsupportedVersion struct {
Required string
}
func IsErrUnsupportedVersion(err error) bool {
_, ok := err.(ErrUnsupportedVersion)
return ok
}
func (err ErrUnsupportedVersion) Error() string {
return fmt.Sprintf("Operation requires higher version [required: %s]", err.Required)
}
type ErrNoMergeBase struct{}
func IsErrNoMergeBase(err error) bool {
_, ok := err.(ErrNoMergeBase)
return ok
}
func (err ErrNoMergeBase) Error() string {
return "no merge based found"
}

80
vendor/github.com/gogs/git-module/git.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// 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 (
"fmt"
"strings"
"time"
)
const _VERSION = "0.6.4"
func Version() string {
return _VERSION
}
var (
// Debug enables verbose logging on everything.
// This should be false in case Gogs starts in SSH mode.
Debug = false
Prefix = "[git-module] "
)
func log(format string, args ...interface{}) {
if !Debug {
return
}
fmt.Print(Prefix)
if len(args) == 0 {
fmt.Println(format)
} else {
fmt.Printf(format+"\n", args...)
}
}
var gitVersion string
// Version returns current Git version from shell.
func BinVersion() (string, error) {
if len(gitVersion) > 0 {
return gitVersion, nil
}
stdout, err := NewCommand("version").Run()
if err != nil {
return "", err
}
fields := strings.Fields(stdout)
if len(fields) < 3 {
return "", fmt.Errorf("not enough output: %s", stdout)
}
// Handle special case on Windows.
i := strings.Index(fields[2], "windows")
if i >= 1 {
gitVersion = fields[2][:i-1]
return gitVersion, nil
}
gitVersion = fields[2]
return gitVersion, nil
}
func init() {
BinVersion()
}
// Fsck verifies the connectivity and validity of the objects in the database
func Fsck(repoPath string, timeout time.Duration, args ...string) error {
// Make sure timeout makes sense.
if timeout <= 0 {
timeout = -1
}
_, err := NewCommand("fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath)
return err
}

111
vendor/github.com/gogs/git-module/hook.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
// 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 (
"errors"
"io/ioutil"
"os"
"path"
"strings"
)
var (
// Direcotry of hook and sample files. Can be changed to "custom_hooks" for very purpose.
HookDir = "hooks"
HookSampleDir = HookDir
// HookNames is a list of Git server hooks' name that are supported.
HookNames = []string{
"pre-receive",
"update",
"post-receive",
}
)
var (
ErrNotValidHook = errors.New("not a valid Git hook")
)
// IsValidHookName returns true if given name is a valid Git hook.
func IsValidHookName(name string) bool {
for _, hn := range HookNames {
if hn == name {
return true
}
}
return false
}
// Hook represents a Git hook.
type Hook struct {
name string
IsActive bool // Indicates whether repository has this hook.
Content string // Content of hook if it's active.
Sample string // Sample content from Git.
path string // Hook file path.
}
// GetHook returns a Git hook by given name and repository.
func GetHook(repoPath, name string) (*Hook, error) {
if !IsValidHookName(name) {
return nil, ErrNotValidHook
}
h := &Hook{
name: name,
path: path.Join(repoPath, HookDir, name),
}
if isFile(h.path) {
data, err := ioutil.ReadFile(h.path)
if err != nil {
return nil, err
}
h.IsActive = true
h.Content = string(data)
return h, nil
}
// Check sample file
samplePath := path.Join(repoPath, HookSampleDir, h.name) + ".sample"
if isFile(samplePath) {
data, err := ioutil.ReadFile(samplePath)
if err != nil {
return nil, err
}
h.Sample = string(data)
}
return h, nil
}
func (h *Hook) Name() string {
return h.name
}
// Update updates content hook file.
func (h *Hook) Update() error {
if len(strings.TrimSpace(h.Content)) == 0 {
if isExist(h.path) {
return os.Remove(h.path)
}
return nil
}
os.MkdirAll(path.Dir(h.path), os.ModePerm)
return ioutil.WriteFile(h.path, []byte(strings.Replace(h.Content, "\r", "", -1)), os.ModePerm)
}
// ListHooks returns a list of Git hooks of given repository.
func ListHooks(repoPath string) (_ []*Hook, err error) {
if !isDir(path.Join(repoPath, "hooks")) {
return nil, errors.New("hooks path does not exist")
}
hooks := make([]*Hook, len(HookNames))
for i, name := range HookNames {
hooks[i], err = GetHook(repoPath, name)
if err != nil {
return nil, err
}
}
return hooks, nil
}

295
vendor/github.com/gogs/git-module/repo.go generated vendored Normal file
View File

@@ -0,0 +1,295 @@
// 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))
}

126
vendor/github.com/gogs/git-module/repo_branch.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
// 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 (
"fmt"
"strings"
"github.com/mcuadros/go-version"
)
const BRANCH_PREFIX = "refs/heads/"
// IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(repoPath, name string) bool {
_, err := NewCommand("show-ref", "--verify", name).RunInDir(repoPath)
return err == nil
}
// IsBranchExist returns true if given branch exists in the repository.
func IsBranchExist(repoPath, name string) bool {
return IsReferenceExist(repoPath, BRANCH_PREFIX+name)
}
func (repo *Repository) IsBranchExist(name string) bool {
return IsBranchExist(repo.Path, name)
}
// Branch represents a Git branch.
type Branch struct {
Name string
Path string
}
// GetHEADBranch returns corresponding branch of HEAD.
func (repo *Repository) GetHEADBranch() (*Branch, error) {
stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path)
if err != nil {
return nil, err
}
stdout = strings.TrimSpace(stdout)
if !strings.HasPrefix(stdout, BRANCH_PREFIX) {
return nil, fmt.Errorf("invalid HEAD branch: %v", stdout)
}
return &Branch{
Name: stdout[len(BRANCH_PREFIX):],
Path: stdout,
}, nil
}
// SetDefaultBranch sets default branch of repository.
func (repo *Repository) SetDefaultBranch(name string) error {
if version.Compare(gitVersion, "1.7.10", "<") {
return ErrUnsupportedVersion{"1.7.10"}
}
_, err := NewCommand("symbolic-ref", "HEAD", BRANCH_PREFIX+name).RunInDir(repo.Path)
return err
}
// GetBranches returns all branches of the repository.
func (repo *Repository) GetBranches() ([]string, error) {
stdout, err := NewCommand("show-ref", "--heads").RunInDir(repo.Path)
if err != nil {
return nil, err
}
infos := strings.Split(stdout, "\n")
branches := make([]string, len(infos)-1)
for i, info := range infos[:len(infos)-1] {
fields := strings.Fields(info)
if len(fields) != 2 {
continue // NOTE: I should believe git will not give me wrong string.
}
branches[i] = strings.TrimPrefix(fields[1], BRANCH_PREFIX)
}
return branches, nil
}
// Option(s) for delete branch
type DeleteBranchOptions struct {
Force bool
}
// DeleteBranch deletes a branch from given repository path.
func DeleteBranch(repoPath, name string, opts DeleteBranchOptions) error {
cmd := NewCommand("branch")
if opts.Force {
cmd.AddArguments("-D")
} else {
cmd.AddArguments("-d")
}
cmd.AddArguments(name)
_, err := cmd.RunInDir(repoPath)
return err
}
// DeleteBranch deletes a branch from repository.
func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error {
return DeleteBranch(repo.Path, name, opts)
}
// AddRemote adds a new remote to repository.
func (repo *Repository) AddRemote(name, url string, fetch bool) error {
cmd := NewCommand("remote", "add")
if fetch {
cmd.AddArguments("-f")
}
cmd.AddArguments(name, url)
_, err := cmd.RunInDir(repo.Path)
return err
}
// RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error {
_, err := NewCommand("remote", "remove", name).RunInDir(repo.Path)
return err
}

381
vendor/github.com/gogs/git-module/repo_commit.go generated vendored Normal file
View File

@@ -0,0 +1,381 @@
// 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"
"fmt"
"strconv"
"strings"
"github.com/mcuadros/go-version"
)
const REMOTE_PREFIX = "refs/remotes/"
// getRefCommitID returns the last commit ID string of given reference (branch or tag).
func (repo *Repository) getRefCommitID(name string) (string, error) {
stdout, err := NewCommand("show-ref", "--verify", name).RunInDir(repo.Path)
if err != nil {
if strings.Contains(err.Error(), "not a valid ref") {
return "", ErrNotExist{name, ""}
}
return "", err
}
return strings.Split(stdout, " ")[0], nil
}
// GetBranchCommitID returns last commit ID string of given branch.
func (repo *Repository) GetBranchCommitID(name string) (string, error) {
return repo.getRefCommitID(BRANCH_PREFIX + name)
}
// GetTagCommitID returns last commit ID string of given tag.
func (repo *Repository) GetTagCommitID(name string) (string, error) {
return repo.getRefCommitID(TAG_PREFIX + name)
}
// GetRemoteBranchCommitID returns last commit ID string of given remote branch.
func (repo *Repository) GetRemoteBranchCommitID(name string) (string, error) {
return repo.getRefCommitID(REMOTE_PREFIX + name)
}
// parseCommitData parses commit information from the (uncompressed) raw
// data from the commit object.
// \n\n separate headers from message
func parseCommitData(data []byte) (*Commit, error) {
commit := new(Commit)
commit.parents = make([]sha1, 0, 1)
// we now have the contents of the commit object. Let's investigate...
nextline := 0
l:
for {
eol := bytes.IndexByte(data[nextline:], '\n')
switch {
case eol > 0:
line := data[nextline : nextline+eol]
spacepos := bytes.IndexByte(line, ' ')
reftype := line[:spacepos]
switch string(reftype) {
case "tree", "object":
id, err := NewIDFromString(string(line[spacepos+1:]))
if err != nil {
return nil, err
}
commit.Tree.ID = id
case "parent":
// A commit can have one or more parents
oid, err := NewIDFromString(string(line[spacepos+1:]))
if err != nil {
return nil, err
}
commit.parents = append(commit.parents, oid)
case "author", "tagger":
sig, err := newSignatureFromCommitline(line[spacepos+1:])
if err != nil {
return nil, err
}
commit.Author = sig
case "committer":
sig, err := newSignatureFromCommitline(line[spacepos+1:])
if err != nil {
return nil, err
}
commit.Committer = sig
}
nextline += eol + 1
case eol == 0:
commit.CommitMessage = string(data[nextline+1:])
break l
default:
break l
}
}
return commit, nil
}
func (repo *Repository) getCommit(id sha1) (*Commit, error) {
c, ok := repo.commitCache.Get(id.String())
if ok {
log("Hit cache: %s", id)
return c.(*Commit), nil
}
data, err := NewCommand("cat-file", "commit", id.String()).RunInDirBytes(repo.Path)
if err != nil {
if strings.Contains(err.Error(), "exit status 128") {
return nil, ErrNotExist{id.String(), ""}
}
return nil, err
}
commit, err := parseCommitData(data)
if err != nil {
return nil, err
}
commit.repo = repo
commit.ID = id
repo.commitCache.Set(id.String(), commit)
return commit, nil
}
// GetCommit returns commit object of by ID string.
func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
if len(commitID) != 40 {
var err error
commitID, err = NewCommand("rev-parse", commitID).RunInDir(repo.Path)
if err != nil {
if strings.Contains(err.Error(), "exit status 128") {
return nil, ErrNotExist{commitID, ""}
}
return nil, err
}
}
id, err := NewIDFromString(commitID)
if err != nil {
return nil, err
}
return repo.getCommit(id)
}
// GetBranchCommit returns the last commit of given branch.
func (repo *Repository) GetBranchCommit(name string) (*Commit, error) {
commitID, err := repo.GetBranchCommitID(name)
if err != nil {
return nil, err
}
return repo.GetCommit(commitID)
}
// GetTagCommit returns the commit of given tag.
func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
commitID, err := repo.GetTagCommitID(name)
if err != nil {
return nil, err
}
return repo.GetCommit(commitID)
}
// GetRemoteBranchCommit returns the last commit of given remote branch.
func (repo *Repository) GetRemoteBranchCommit(name string) (*Commit, error) {
commitID, err := repo.GetRemoteBranchCommitID(name)
if err != nil {
return nil, err
}
return repo.GetCommit(commitID)
}
func (repo *Repository) getCommitByPathWithID(id sha1, relpath string) (*Commit, error) {
// File name starts with ':' must be escaped.
if relpath[0] == ':' {
relpath = `\` + relpath
}
stdout, err := NewCommand("log", "-1", _PRETTY_LOG_FORMAT, id.String(), "--", relpath).RunInDir(repo.Path)
if err != nil {
return nil, err
}
id, err = NewIDFromString(stdout)
if err != nil {
return nil, err
}
return repo.getCommit(id)
}
// GetCommitByPath returns the last commit of relative path.
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
stdout, err := NewCommand("log", "-1", _PRETTY_LOG_FORMAT, "--", relpath).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
commits, err := repo.parsePrettyFormatLogToList(stdout)
if err != nil {
return nil, err
}
return commits.Front().Value.(*Commit), nil
}
func (repo *Repository) CommitsByRangeSize(revision string, page, size int) (*list.List, error) {
stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*size),
"--max-count="+strconv.Itoa(size), _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
return repo.parsePrettyFormatLogToList(stdout)
}
var DefaultCommitsPageSize = 30
func (repo *Repository) CommitsByRange(revision string, page int) (*list.List, error) {
return repo.CommitsByRangeSize(revision, page, DefaultCommitsPageSize)
}
func (repo *Repository) searchCommits(id sha1, keyword string) (*list.List, error) {
stdout, err := NewCommand("log", id.String(), "-100", "-i", "--grep="+keyword, _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
return repo.parsePrettyFormatLogToList(stdout)
}
func (repo *Repository) getFilesChanged(id1 string, id2 string) ([]string, error) {
stdout, err := NewCommand("diff", "--name-only", id1, id2).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
return strings.Split(string(stdout), "\n"), nil
}
func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
return commitsCount(repo.Path, revision, file)
}
func (repo *Repository) CommitsByFileAndRangeSize(revision, file string, page, size int) (*list.List, error) {
stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*size),
"--max-count="+strconv.Itoa(size), _PRETTY_LOG_FORMAT, "--", file).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
return repo.parsePrettyFormatLogToList(stdout)
}
func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (*list.List, error) {
return repo.CommitsByFileAndRangeSize(revision, file, page, DefaultCommitsPageSize)
}
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
stdout, err := NewCommand("diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
if err != nil {
return 0, err
}
return len(strings.Split(stdout, "\n")) - 1, nil
}
// CommitsBetween returns a list that contains commits between [last, before).
func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) {
if version.Compare(gitVersion, "1.8.0", ">=") {
stdout, err := NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
return repo.parsePrettyFormatLogToList(bytes.TrimSpace(stdout))
}
// Fallback to stupid solution, which iterates all commits of the repository
// if before is not an ancestor of last.
l := list.New()
if last == nil || last.ParentCount() == 0 {
return l, nil
}
var err error
cur := last
for {
if cur.ID.Equal(before.ID) {
break
}
l.PushBack(cur)
if cur.ParentCount() == 0 {
break
}
cur, err = cur.Parent(0)
if err != nil {
return nil, err
}
}
return l, nil
}
func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, error) {
lastCommit, err := repo.GetCommit(last)
if err != nil {
return nil, err
}
beforeCommit, err := repo.GetCommit(before)
if err != nil {
return nil, err
}
return repo.CommitsBetween(lastCommit, beforeCommit)
}
func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
return commitsCount(repo.Path, start+"..."+end, "")
}
// The limit is depth, not total number of returned commits.
func (repo *Repository) commitsBefore(l *list.List, parent *list.Element, id sha1, current, limit int) error {
// Reach the limit
if limit > 0 && current > limit {
return nil
}
commit, err := repo.getCommit(id)
if err != nil {
return fmt.Errorf("getCommit: %v", err)
}
var e *list.Element
if parent == nil {
e = l.PushBack(commit)
} else {
var in = parent
for {
if in == nil {
break
} else if in.Value.(*Commit).ID.Equal(commit.ID) {
return nil
} else if in.Next() == nil {
break
}
if in.Value.(*Commit).Committer.When.Equal(commit.Committer.When) {
break
}
if in.Value.(*Commit).Committer.When.After(commit.Committer.When) &&
in.Next().Value.(*Commit).Committer.When.Before(commit.Committer.When) {
break
}
in = in.Next()
}
e = l.InsertAfter(commit, in)
}
pr := parent
if commit.ParentCount() > 1 {
pr = e
}
for i := 0; i < commit.ParentCount(); i++ {
id, err := commit.ParentID(i)
if err != nil {
return err
}
err = repo.commitsBefore(l, pr, id, current+1, limit)
if err != nil {
return err
}
}
return nil
}
func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) {
l := list.New()
return l, repo.commitsBefore(l, nil, id, 1, 0)
}
func (repo *Repository) getCommitsBeforeLimit(id sha1, num int) (*list.List, error) {
l := list.New()
return l, repo.commitsBefore(l, nil, id, 1, num)
}

400
vendor/github.com/gogs/git-module/repo_diff.go generated vendored Normal file
View File

@@ -0,0 +1,400 @@
// Copyright 2017 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 (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"strconv"
"strings"
"time"
)
// DiffLineType represents the type of a line in diff.
type DiffLineType uint8
const (
DIFF_LINE_PLAIN DiffLineType = iota + 1
DIFF_LINE_ADD
DIFF_LINE_DEL
DIFF_LINE_SECTION
)
// DiffFileType represents the file status in diff.
type DiffFileType uint8
const (
DIFF_FILE_ADD DiffFileType = iota + 1
DIFF_FILE_CHANGE
DIFF_FILE_DEL
DIFF_FILE_RENAME
)
// DiffLine represents a line in diff.
type DiffLine struct {
LeftIdx int
RightIdx int
Type DiffLineType
Content string
}
func (d *DiffLine) GetType() int {
return int(d.Type)
}
// DiffSection represents a section in diff.
type DiffSection struct {
Name string
Lines []*DiffLine
}
// Line returns a specific line by type (add or del) and file line number from a section.
func (diffSection *DiffSection) Line(lineType DiffLineType, idx int) *DiffLine {
var (
difference = 0
addCount = 0
delCount = 0
matchDiffLine *DiffLine
)
LOOP:
for _, diffLine := range diffSection.Lines {
switch diffLine.Type {
case DIFF_LINE_ADD:
addCount++
case DIFF_LINE_DEL:
delCount++
default:
if matchDiffLine != nil {
break LOOP
}
difference = diffLine.RightIdx - diffLine.LeftIdx
addCount = 0
delCount = 0
}
switch lineType {
case DIFF_LINE_DEL:
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
matchDiffLine = diffLine
}
case DIFF_LINE_ADD:
if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
matchDiffLine = diffLine
}
}
}
if addCount == delCount {
return matchDiffLine
}
return nil
}
// DiffFile represents a file in diff.
type DiffFile struct {
Name string
OldName string
Index string // 40-byte SHA, Changed/New: new SHA; Deleted: old SHA
Addition, Deletion int
Type DiffFileType
IsCreated bool
IsDeleted bool
IsBin bool
IsRenamed bool
IsSubmodule bool
Sections []*DiffSection
IsIncomplete bool
}
func (diffFile *DiffFile) GetType() int {
return int(diffFile.Type)
}
func (diffFile *DiffFile) NumSections() int {
return len(diffFile.Sections)
}
// Diff contains all information of a specific diff output.
type Diff struct {
TotalAddition, TotalDeletion int
Files []*DiffFile
IsIncomplete bool
}
func (diff *Diff) NumFiles() int {
return len(diff.Files)
}
const _DIFF_HEAD = "diff --git "
// ParsePatch takes a reader and parses everything it receives in diff format.
func ParsePatch(done chan<- error, maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) *Diff {
var (
diff = &Diff{Files: make([]*DiffFile, 0)}
curFile *DiffFile
curSection = &DiffSection{
Lines: make([]*DiffLine, 0, 10),
}
leftLine, rightLine int
lineCount int
curFileLinesCount int
)
input := bufio.NewReader(reader)
isEOF := false
for !isEOF {
// TODO: would input.ReadBytes be more memory-efficient?
line, err := input.ReadString('\n')
if err != nil {
if err == io.EOF {
isEOF = true
} else {
done <- fmt.Errorf("ReadString: %v", err)
return nil
}
}
if len(line) > 0 && line[len(line)-1] == '\n' {
// Remove line break.
line = line[:len(line)-1]
}
if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") || len(line) == 0 {
continue
}
curFileLinesCount++
lineCount++
// Diff data too large, we only show the first about maxlines lines
if curFileLinesCount >= maxLines || len(line) >= maxLineCharacteres {
curFile.IsIncomplete = true
}
switch {
case line[0] == ' ':
diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
leftLine++
rightLine++
curSection.Lines = append(curSection.Lines, diffLine)
continue
case line[0] == '@':
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
ss := strings.Split(line, "@@")
diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line}
curSection.Lines = append(curSection.Lines, diffLine)
// Parse line number.
ranges := strings.Split(ss[1][1:], " ")
leftLine, _ = strconv.Atoi(strings.Split(ranges[0], ",")[0][1:])
if len(ranges) > 1 {
rightLine, _ = strconv.Atoi(strings.Split(ranges[1], ",")[0])
} else {
rightLine = leftLine
}
continue
case line[0] == '+':
curFile.Addition++
diff.TotalAddition++
diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
rightLine++
curSection.Lines = append(curSection.Lines, diffLine)
continue
case line[0] == '-':
curFile.Deletion++
diff.TotalDeletion++
diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine}
if leftLine > 0 {
leftLine++
}
curSection.Lines = append(curSection.Lines, diffLine)
case strings.HasPrefix(line, "Binary"):
curFile.IsBin = true
continue
}
// Get new file.
if strings.HasPrefix(line, _DIFF_HEAD) {
middle := -1
// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
// e.g. diff --git "a/xxx" "b/xxx"
hasQuote := line[len(_DIFF_HEAD)] == '"'
if hasQuote {
middle = strings.Index(line, ` "b/`)
} else {
middle = strings.Index(line, " b/")
}
beg := len(_DIFF_HEAD)
a := line[beg+2 : middle]
b := line[middle+3:]
if hasQuote {
a = string(UnescapeChars([]byte(a[1 : len(a)-1])))
b = string(UnescapeChars([]byte(b[1 : len(b)-1])))
}
curFile = &DiffFile{
Name: a,
Type: DIFF_FILE_CHANGE,
Sections: make([]*DiffSection, 0, 10),
}
diff.Files = append(diff.Files, curFile)
if len(diff.Files) >= maxFiles {
diff.IsIncomplete = true
io.Copy(ioutil.Discard, reader)
break
}
curFileLinesCount = 0
// Check file diff type and submodule.
CHECK_TYPE:
for {
line, err := input.ReadString('\n')
if err != nil {
if err == io.EOF {
isEOF = true
} else {
done <- fmt.Errorf("ReadString: %v", err)
return nil
}
}
switch {
case strings.HasPrefix(line, "new file"):
curFile.Type = DIFF_FILE_ADD
curFile.IsCreated = true
curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
case strings.HasPrefix(line, "deleted"):
curFile.Type = DIFF_FILE_DEL
curFile.IsDeleted = true
curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
case strings.HasPrefix(line, "index"):
if curFile.IsDeleted {
curFile.Index = line[6:46]
} else if len(line) >= 88 {
curFile.Index = line[49:88]
} else {
curFile.Index = curFile.Name
}
break CHECK_TYPE
case strings.HasPrefix(line, "similarity index 100%"):
curFile.Type = DIFF_FILE_RENAME
curFile.IsRenamed = true
curFile.OldName = curFile.Name
curFile.Name = b
curFile.Index = b
break CHECK_TYPE
case strings.HasPrefix(line, "old mode"):
break CHECK_TYPE
}
}
}
}
done <- nil
return diff
}
// GetDiffRange returns a parsed diff object between given commits.
func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
repo, err := OpenRepository(repoPath)
if err != nil {
return nil, err
}
commit, err := repo.GetCommit(afterCommitID)
if err != nil {
return nil, err
}
cmd := NewCommand()
if len(beforeCommitID) == 0 {
// First commit of repository
if commit.ParentCount() == 0 {
cmd.AddArguments("show", "--full-index", afterCommitID)
} else {
c, _ := commit.Parent(0)
cmd.AddArguments("diff", "--full-index", "-M", c.ID.String(), afterCommitID)
}
} else {
cmd.AddArguments("diff", "--full-index", "-M", beforeCommitID, afterCommitID)
}
stdout, w := io.Pipe()
done := make(chan error)
var diff *Diff
go func() {
diff = ParsePatch(done, maxLines, maxLineCharacteres, maxFiles, stdout)
}()
stderr := new(bytes.Buffer)
err = cmd.RunInDirTimeoutPipeline(2*time.Minute, repoPath, w, stderr)
w.Close() // Close writer to exit parsing goroutine
if err != nil {
return nil, concatenateError(err, stderr.String())
}
return diff, <-done
}
// RawDiffType represents the type of raw diff format.
type RawDiffType string
const (
RAW_DIFF_NORMAL RawDiffType = "diff"
RAW_DIFF_PATCH RawDiffType = "patch"
)
// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Writer) error {
repo, err := OpenRepository(repoPath)
if err != nil {
return fmt.Errorf("OpenRepository: %v", err)
}
commit, err := repo.GetCommit(commitID)
if err != nil {
return err
}
cmd := NewCommand()
switch diffType {
case RAW_DIFF_NORMAL:
if commit.ParentCount() == 0 {
cmd.AddArguments("show", commitID)
} else {
c, _ := commit.Parent(0)
cmd.AddArguments("diff", "-M", c.ID.String(), commitID)
}
case RAW_DIFF_PATCH:
if commit.ParentCount() == 0 {
cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root", commitID)
} else {
c, _ := commit.Parent(0)
query := fmt.Sprintf("%s...%s", commitID, c.ID.String())
cmd.AddArguments("format-patch", "--no-signature", "--stdout", query)
}
default:
return fmt.Errorf("invalid diffType: %s", diffType)
}
stderr := new(bytes.Buffer)
if err = cmd.RunInDirPipeline(repoPath, writer, stderr); err != nil {
return concatenateError(err, stderr.String())
}
return nil
}
// GetDiffCommit returns a parsed diff object of given commit.
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
}

13
vendor/github.com/gogs/git-module/repo_hook.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// 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
func (repo *Repository) GetHook(name string) (*Hook, error) {
return GetHook(repo.Path, name)
}
func (repo *Repository) Hooks() ([]*Hook, error) {
return ListHooks(repo.Path)
}

14
vendor/github.com/gogs/git-module/repo_object.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// Copyright 2014 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
type ObjectType string
const (
OBJECT_COMMIT ObjectType = "commit"
OBJECT_TREE ObjectType = "tree"
OBJECT_BLOB ObjectType = "blob"
OBJECT_TAG ObjectType = "tag"
)

81
vendor/github.com/gogs/git-module/repo_pull.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
// 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 (
"container/list"
"fmt"
"strconv"
"strings"
"time"
)
// PullRequestInfo represents needed information for a pull request.
type PullRequestInfo struct {
MergeBase string
Commits *list.List
NumFiles int
}
// GetMergeBase checks and returns merge base of two branches.
func (repo *Repository) GetMergeBase(base, head string) (string, error) {
stdout, err := NewCommand("merge-base", base, head).RunInDir(repo.Path)
if err != nil {
if strings.Contains(err.Error(), "exit status 1") {
return "", ErrNoMergeBase{}
}
return "", err
}
return strings.TrimSpace(stdout), nil
}
// GetPullRequestInfo generates and returns pull request information
// between base and head branches of repositories.
func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch string) (_ *PullRequestInfo, err error) {
var remoteBranch string
// We don't need a temporary remote for same repository.
if repo.Path != basePath {
// Add a temporary remote
tmpRemote := strconv.FormatInt(time.Now().UnixNano(), 10)
if err = repo.AddRemote(tmpRemote, basePath, true); err != nil {
return nil, fmt.Errorf("AddRemote: %v", err)
}
defer repo.RemoveRemote(tmpRemote)
remoteBranch = "remotes/" + tmpRemote + "/" + baseBranch
} else {
remoteBranch = baseBranch
}
prInfo := new(PullRequestInfo)
prInfo.MergeBase, err = repo.GetMergeBase(remoteBranch, headBranch)
if err != nil {
return nil, err
}
logs, err := NewCommand("log", prInfo.MergeBase+"..."+headBranch, _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
prInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
if err != nil {
return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
}
// Count number of changed files.
stdout, err := NewCommand("diff", "--name-only", remoteBranch+"..."+headBranch).RunInDir(repo.Path)
if err != nil {
return nil, err
}
prInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1
return prInfo, nil
}
// GetPatch generates and returns patch data between given revisions.
func (repo *Repository) GetPatch(base, head string) ([]byte, error) {
return NewCommand("diff", "-p", "--binary", base, head).RunInDirBytes(repo.Path)
}

209
vendor/github.com/gogs/git-module/repo_tag.go generated vendored Normal file
View File

@@ -0,0 +1,209 @@
// 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 (
"fmt"
"strings"
"github.com/mcuadros/go-version"
)
const TAG_PREFIX = "refs/tags/"
// IsTagExist returns true if given tag exists in the repository.
func IsTagExist(repoPath, name string) bool {
return IsReferenceExist(repoPath, TAG_PREFIX+name)
}
func (repo *Repository) IsTagExist(name string) bool {
return IsTagExist(repo.Path, name)
}
func (repo *Repository) CreateTag(name, revision string) error {
_, err := NewCommand("tag", name, revision).RunInDir(repo.Path)
return err
}
func (repo *Repository) getTag(id sha1) (*Tag, error) {
t, ok := repo.tagCache.Get(id.String())
if ok {
log("Hit cache: %s", id)
return t.(*Tag), nil
}
// Get tag type
tp, err := NewCommand("cat-file", "-t", id.String()).RunInDir(repo.Path)
if err != nil {
return nil, err
}
tp = strings.TrimSpace(tp)
// Tag is a commit.
if ObjectType(tp) == OBJECT_COMMIT {
tag := &Tag{
ID: id,
Object: id,
Type: string(OBJECT_COMMIT),
repo: repo,
}
repo.tagCache.Set(id.String(), tag)
return tag, nil
}
// Tag with message.
data, err := NewCommand("cat-file", "-p", id.String()).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
tag, err := parseTagData(data)
if err != nil {
return nil, err
}
tag.ID = id
tag.repo = repo
repo.tagCache.Set(id.String(), tag)
return tag, nil
}
// GetTag returns a Git tag by given name.
func (repo *Repository) GetTag(name string) (*Tag, error) {
stdout, err := NewCommand("show-ref", "--tags", name).RunInDir(repo.Path)
if err != nil {
return nil, err
}
id, err := NewIDFromString(strings.Split(stdout, " ")[0])
if err != nil {
return nil, err
}
tag, err := repo.getTag(id)
if err != nil {
return nil, err
}
tag.Name = name
return tag, nil
}
// GetTags returns all tags of the repository.
func (repo *Repository) GetTags() ([]string, error) {
cmd := NewCommand("tag", "-l")
if version.Compare(gitVersion, "2.0.0", ">=") {
cmd.AddArguments("--sort=-v:refname")
}
stdout, err := cmd.RunInDir(repo.Path)
if err != nil {
return nil, err
}
tags := strings.Split(stdout, "\n")
tags = tags[:len(tags)-1]
if version.Compare(gitVersion, "2.0.0", "<") {
version.Sort(tags)
// Reverse order
for i := 0; i < len(tags)/2; i++ {
j := len(tags) - i - 1
tags[i], tags[j] = tags[j], tags[i]
}
}
return tags, nil
}
type TagsResult struct {
// Indicates whether results include the latest tag.
HasLatest bool
// If results do not include the latest tag, a indicator 'after' to go back.
PreviousAfter string
// Indicates whether results include the oldest tag.
ReachEnd bool
// List of returned tags.
Tags []string
}
// GetTagsAfter returns list of tags 'after' (exlusive) given tag.
func (repo *Repository) GetTagsAfter(after string, limit int) (*TagsResult, error) {
allTags, err := repo.GetTags()
if err != nil {
return nil, fmt.Errorf("GetTags: %v", err)
}
if limit < 0 {
limit = 0
}
numAllTags := len(allTags)
if len(after) == 0 && limit == 0 {
return &TagsResult{
HasLatest: true,
ReachEnd: true,
Tags: allTags,
}, nil
} else if len(after) == 0 && limit > 0 {
endIdx := limit
if limit >= numAllTags {
endIdx = numAllTags
}
return &TagsResult{
HasLatest: true,
ReachEnd: limit >= numAllTags,
Tags: allTags[:endIdx],
}, nil
}
previousAfter := ""
hasMatch := false
tags := make([]string, 0, len(allTags))
for i := range allTags {
if hasMatch {
tags = allTags[i:]
break
}
if allTags[i] == after {
hasMatch = true
if limit > 0 && i-limit >= 0 {
previousAfter = allTags[i-limit]
}
continue
}
}
if !hasMatch {
tags = allTags
}
// If all tags after match is equal to the limit, it reaches the oldest tag as well.
if limit == 0 || len(tags) <= limit {
return &TagsResult{
HasLatest: !hasMatch,
PreviousAfter: previousAfter,
ReachEnd: true,
Tags: tags,
}, nil
}
return &TagsResult{
HasLatest: !hasMatch,
PreviousAfter: previousAfter,
Tags: tags[:limit],
}, nil
}
// DeleteTag deletes a tag from the repository
func (repo *Repository) DeleteTag(name string) error {
cmd := NewCommand("tag", "-d")
cmd.AddArguments(name)
_, err := cmd.RunInDir(repo.Path)
return err
}

26
vendor/github.com/gogs/git-module/repo_tree.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
// 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
func (repo *Repository) getTree(id sha1) (*Tree, error) {
treePath := filepathFromSHA1(repo.Path, id.String())
if isFile(treePath) {
_, err := NewCommand("ls-tree", id.String()).RunInDir(repo.Path)
if err != nil {
return nil, ErrNotExist{id.String(), ""}
}
}
return NewTree(repo, id), nil
}
// Find the tree object in the repository.
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
id, err := NewIDFromString(idStr)
if err != nil {
return nil, err
}
return repo.getTree(id)
}

93
vendor/github.com/gogs/git-module/sha1.go generated vendored Normal file
View File

@@ -0,0 +1,93 @@
// 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 (
"encoding/hex"
"fmt"
"strings"
)
const EMPTY_SHA = "0000000000000000000000000000000000000000"
type sha1 [20]byte
// Equal returns true if s has the same sha1 as caller.
// Support 40-length-string, []byte, sha1.
func (id sha1) Equal(s2 interface{}) bool {
switch v := s2.(type) {
case string:
if len(v) != 40 {
return false
}
return v == id.String()
case []byte:
if len(v) != 20 {
return false
}
for i, v := range v {
if id[i] != v {
return false
}
}
case sha1:
for i, v := range v {
if id[i] != v {
return false
}
}
default:
return false
}
return true
}
// String returns string (hex) representation of the Oid.
func (s sha1) String() string {
result := make([]byte, 0, 40)
hexvalues := []byte("0123456789abcdef")
for i := 0; i < 20; i++ {
result = append(result, hexvalues[s[i]>>4])
result = append(result, hexvalues[s[i]&0xf])
}
return string(result)
}
// MustID always creates a new sha1 from a [20]byte array with no validation of input.
func MustID(b []byte) sha1 {
var id sha1
for i := 0; i < 20; i++ {
id[i] = b[i]
}
return id
}
// NewID creates a new sha1 from a [20]byte array.
func NewID(b []byte) (sha1, error) {
if len(b) != 20 {
return sha1{}, fmt.Errorf("Length must be 20: %v", b)
}
return MustID(b), nil
}
// MustIDFromString always creates a new sha from a ID with no validation of input.
func MustIDFromString(s string) sha1 {
b, _ := hex.DecodeString(s)
return MustID(b)
}
// NewIDFromString creates a new sha1 from a ID string of length 40.
func NewIDFromString(s string) (sha1, error) {
var id sha1
s = strings.TrimSpace(s)
if len(s) != 40 {
return id, fmt.Errorf("Length must be 40: %s", s)
}
b, err := hex.DecodeString(s)
if err != nil {
return id, err
}
return NewID(b)
}

48
vendor/github.com/gogs/git-module/signature.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// 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"
"strconv"
"time"
)
// Signature represents the Author or Committer information.
type Signature struct {
Email string
Name string
When time.Time
}
// Helper to get a signature from the commit line, which looks like these:
// author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200
// author Patrick Gundlach <gundlach@speedata.de> Thu, 07 Apr 2005 22:13:13 +0200
// but without the "author " at the beginning (this method should)
// be used for author and committer.
//
// FIXME: include timezone for timestamp!
func newSignatureFromCommitline(line []byte) (_ *Signature, err error) {
sig := new(Signature)
emailStart := bytes.IndexByte(line, '<')
sig.Name = string(line[:emailStart-1])
emailEnd := bytes.IndexByte(line, '>')
sig.Email = string(line[emailStart+1 : emailEnd])
// Check date format.
firstChar := line[emailEnd+2]
if firstChar >= 48 && firstChar <= 57 {
timestop := bytes.IndexByte(line[emailEnd+2:], ' ')
timestring := string(line[emailEnd+2 : emailEnd+2+timestop])
seconds, _ := strconv.ParseInt(timestring, 10, 64)
sig.When = time.Unix(seconds, 0)
} else {
sig.When, err = time.Parse("Mon Jan _2 15:04:05 2006 -0700", string(line[emailEnd+2:]))
if err != nil {
return nil, err
}
}
return sig, nil
}

78
vendor/github.com/gogs/git-module/submodule.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
// 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 "strings"
type SubModule struct {
Name string
URL string
}
// SubModuleFile represents a file with submodule type.
type SubModuleFile struct {
*Commit
refURL string
refID string
}
func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile {
return &SubModuleFile{
Commit: c,
refURL: refURL,
refID: refID,
}
}
// RefURL guesses and returns reference URL.
func (sf *SubModuleFile) RefURL(urlPrefix string, parentPath string) string {
if sf.refURL == "" {
return ""
}
url := strings.TrimSuffix(sf.refURL, ".git")
// git://xxx/user/repo
if strings.HasPrefix(url, "git://") {
return "http://" + strings.TrimPrefix(url, "git://")
}
// http[s]://xxx/user/repo
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
return url
}
// Relative url prefix check (according to git submodule documentation)
if strings.HasPrefix(url, "./") || strings.HasPrefix(url, "../") {
// ...construct and return correct submodule url here...
idx := strings.Index(parentPath, "/src/")
if idx == -1 {
return url
}
return strings.TrimSuffix(urlPrefix, "/") + parentPath[:idx] + "/" + url
}
// sysuser@xxx:user/repo
i := strings.Index(url, "@")
j := strings.LastIndex(url, ":")
// Only process when i < j because git+ssh://git@git.forwardbias.in/npploader.git
if i > -1 && j > -1 && i < j {
// fix problem with reverse proxy works only with local server
if strings.Contains(urlPrefix, url[i+1:j]) {
return urlPrefix + url[j+1:]
} else {
return "http://" + url[i+1:j] + "/" + url[j+1:]
}
}
return url
}
// RefID returns reference ID.
func (sf *SubModuleFile) RefID() string {
return sf.refID
}

65
vendor/github.com/gogs/git-module/tag.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
// 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"
// Tag represents a Git tag.
type Tag struct {
Name string
ID sha1
repo *Repository
Object sha1 // The id of this commit object
Type string
Tagger *Signature
Message string
}
func (tag *Tag) Commit() (*Commit, error) {
return tag.repo.getCommit(tag.Object)
}
// Parse commit information from the (uncompressed) raw
// data from the commit object.
// \n\n separate headers from message
func parseTagData(data []byte) (*Tag, error) {
tag := new(Tag)
// we now have the contents of the commit object. Let's investigate...
nextline := 0
l:
for {
eol := bytes.IndexByte(data[nextline:], '\n')
switch {
case eol > 0:
line := data[nextline : nextline+eol]
spacepos := bytes.IndexByte(line, ' ')
reftype := line[:spacepos]
switch string(reftype) {
case "object":
id, err := NewIDFromString(string(line[spacepos+1:]))
if err != nil {
return nil, err
}
tag.Object = id
case "type":
// A commit can have one or more parents
tag.Type = string(line[spacepos+1:])
case "tagger":
sig, err := newSignatureFromCommitline(line[spacepos+1:])
if err != nil {
return nil, err
}
tag.Tagger = sig
}
nextline += eol + 1
case eol == 0:
tag.Message = string(data[nextline+1:])
break l
default:
break l
}
}
return tag, nil
}

149
vendor/github.com/gogs/git-module/tree.go generated vendored Normal file
View File

@@ -0,0 +1,149 @@
// 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"
"fmt"
"strings"
)
// Tree represents a flat directory listing.
type Tree struct {
ID sha1
repo *Repository
// parent tree
ptree *Tree
entries Entries
entriesParsed bool
}
func NewTree(repo *Repository, id sha1) *Tree {
return &Tree{
ID: id,
repo: repo,
}
}
// Predefine []byte variables to avoid runtime allocations.
var (
escapedSlash = []byte(`\\`)
regularSlash = []byte(`\`)
escapedTab = []byte(`\t`)
regularTab = []byte("\t")
)
// UnescapeChars reverses escaped characters.
func UnescapeChars(in []byte) []byte {
// LEGACY [Go 1.7]: use more expressive bytes.ContainsAny
if bytes.IndexAny(in, "\\\t") == -1 {
return in
}
out := bytes.Replace(in, escapedSlash, regularSlash, -1)
out = bytes.Replace(out, escapedTab, regularTab, -1)
return out
}
// parseTreeData parses tree information from the (uncompressed) raw
// data from the tree object.
func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
entries := make([]*TreeEntry, 0, 10)
l := len(data)
pos := 0
for pos < l {
entry := new(TreeEntry)
entry.ptree = tree
step := 6
switch string(data[pos : pos+step]) {
case "100644", "100664":
entry.mode = ENTRY_MODE_BLOB
entry.Type = OBJECT_BLOB
case "100755":
entry.mode = ENTRY_MODE_EXEC
entry.Type = OBJECT_BLOB
case "120000":
entry.mode = ENTRY_MODE_SYMLINK
entry.Type = OBJECT_BLOB
case "160000":
entry.mode = ENTRY_MODE_COMMIT
entry.Type = OBJECT_COMMIT
step = 8
case "040000":
entry.mode = ENTRY_MODE_TREE
entry.Type = OBJECT_TREE
default:
return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+step]))
}
pos += step + 6 // Skip string type of entry type.
step = 40
id, err := NewIDFromString(string(data[pos : pos+step]))
if err != nil {
return nil, err
}
entry.ID = id
pos += step + 1 // Skip half of sha1.
step = bytes.IndexByte(data[pos:], '\n')
// In case entry name is surrounded by double quotes(it happens only in git-shell).
if data[pos] == '"' {
entry.name = string(UnescapeChars(data[pos+1 : pos+step-1]))
} else {
entry.name = string(data[pos : pos+step])
}
pos += step + 1
entries = append(entries, entry)
}
return entries, nil
}
func (t *Tree) SubTree(rpath string) (*Tree, error) {
if len(rpath) == 0 {
return t, nil
}
paths := strings.Split(rpath, "/")
var (
err error
g = t
p = t
te *TreeEntry
)
for _, name := range paths {
te, err = p.GetTreeEntryByPath(name)
if err != nil {
return nil, err
}
g, err = t.repo.getTree(te.ID)
if err != nil {
return nil, err
}
g.ptree = p
p = g
}
return g, nil
}
// ListEntries returns all entries of current tree.
func (t *Tree) ListEntries() (Entries, error) {
if t.entriesParsed {
return t.entries, nil
}
t.entriesParsed = true
stdout, err := NewCommand("ls-tree", t.ID.String()).RunInDirBytes(t.repo.Path)
if err != nil {
return nil, err
}
t.entries, err = parseTreeData(t, stdout)
return t.entries, err
}

57
vendor/github.com/gogs/git-module/tree_blob.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
// 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 (
"path"
"strings"
)
func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
if len(relpath) == 0 {
return &TreeEntry{
ID: t.ID,
Type: OBJECT_TREE,
mode: ENTRY_MODE_TREE,
}, nil
}
relpath = path.Clean(relpath)
parts := strings.Split(relpath, "/")
var err error
tree := t
for i, name := range parts {
if i == len(parts)-1 {
entries, err := tree.ListEntries()
if err != nil {
return nil, err
}
for _, v := range entries {
if v.name == name {
return v, nil
}
}
} else {
tree, err = tree.SubTree(name)
if err != nil {
return nil, err
}
}
}
return nil, ErrNotExist{"", relpath}
}
func (t *Tree) GetBlobByPath(relpath string) (*Blob, error) {
entry, err := t.GetTreeEntryByPath(relpath)
if err != nil {
return nil, err
}
if !entry.IsDir() {
return entry.Blob(), nil
}
return nil, ErrNotExist{"", relpath}
}

226
vendor/github.com/gogs/git-module/tree_entry.go generated vendored Normal file
View File

@@ -0,0 +1,226 @@
// 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 (
"fmt"
"path"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
)
type EntryMode int
// There are only a few file modes in Git. They look like unix file modes, but they can only be
// one of these.
const (
ENTRY_MODE_BLOB EntryMode = 0100644
ENTRY_MODE_EXEC EntryMode = 0100755
ENTRY_MODE_SYMLINK EntryMode = 0120000
ENTRY_MODE_COMMIT EntryMode = 0160000
ENTRY_MODE_TREE EntryMode = 0040000
)
type TreeEntry struct {
ID sha1
Type ObjectType
mode EntryMode
name string
ptree *Tree
commited bool
size int64
sized bool
}
func (te *TreeEntry) Name() string {
return te.name
}
func (te *TreeEntry) Size() int64 {
if te.IsDir() {
return 0
} else if te.sized {
return te.size
}
stdout, err := NewCommand("cat-file", "-s", te.ID.String()).RunInDir(te.ptree.repo.Path)
if err != nil {
return 0
}
te.sized = true
te.size, _ = strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
return te.size
}
func (te *TreeEntry) IsSubModule() bool {
return te.mode == ENTRY_MODE_COMMIT
}
func (te *TreeEntry) IsDir() bool {
return te.mode == ENTRY_MODE_TREE
}
func (te *TreeEntry) IsLink() bool {
return te.mode == ENTRY_MODE_SYMLINK
}
func (te *TreeEntry) Blob() *Blob {
return &Blob{
repo: te.ptree.repo,
TreeEntry: te,
}
}
type Entries []*TreeEntry
var sorter = []func(t1, t2 *TreeEntry) bool{
func(t1, t2 *TreeEntry) bool {
return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
},
func(t1, t2 *TreeEntry) bool {
return t1.name < t2.name
},
}
func (tes Entries) Len() int { return len(tes) }
func (tes Entries) Swap(i, j int) { tes[i], tes[j] = tes[j], tes[i] }
func (tes Entries) Less(i, j int) bool {
t1, t2 := tes[i], tes[j]
var k int
for k = 0; k < len(sorter)-1; k++ {
sort := sorter[k]
switch {
case sort(t1, t2):
return true
case sort(t2, t1):
return false
}
}
return sorter[k](t1, t2)
}
func (tes Entries) Sort() {
sort.Sort(tes)
}
var defaultConcurrency = runtime.NumCPU()
type commitInfo struct {
entryName string
infos []interface{}
err error
}
// GetCommitsInfo takes advantages of concurrency to speed up getting information
// of all commits that are corresponding to these entries. This method will automatically
// choose the right number of goroutine (concurrency) to use related of the host CPU.
func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interface{}, error) {
return tes.GetCommitsInfoWithCustomConcurrency(commit, treePath, 0)
}
// GetCommitsInfoWithCustomConcurrency takes advantages of concurrency to speed up getting information
// of all commits that are corresponding to these entries. If the given maxConcurrency is negative or
// equal to zero: the right number of goroutine (concurrency) to use will be choosen related of the
// host CPU.
func (tes Entries) GetCommitsInfoWithCustomConcurrency(commit *Commit, treePath string, maxConcurrency int) ([][]interface{}, error) {
if len(tes) == 0 {
return nil, nil
}
if maxConcurrency <= 0 {
maxConcurrency = defaultConcurrency
}
// Length of taskChan determines how many goroutines (subprocesses) can run at the same time.
// The length of revChan should be same as taskChan so goroutines whoever finished job can
// exit as early as possible, only store data inside channel.
taskChan := make(chan bool, maxConcurrency)
revChan := make(chan commitInfo, maxConcurrency)
doneChan := make(chan error)
// Receive loop will exit when it collects same number of data pieces as tree entries.
// It notifies doneChan before exits or notify early with possible error.
infoMap := make(map[string][]interface{}, len(tes))
go func() {
i := 0
for info := range revChan {
if info.err != nil {
doneChan <- info.err
return
}
infoMap[info.entryName] = info.infos
i++
if i == len(tes) {
break
}
}
doneChan <- nil
}()
for i := range tes {
// When taskChan is idle (or has empty slots), put operation will not block.
// However when taskChan is full, code will block and wait any running goroutines to finish.
taskChan <- true
if tes[i].Type != OBJECT_COMMIT {
go func(i int) {
cinfo := commitInfo{entryName: tes[i].Name()}
c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
if err != nil {
cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
} else {
cinfo.infos = []interface{}{tes[i], c}
}
revChan <- cinfo
<-taskChan // Clear one slot from taskChan to allow new goroutines to start.
}(i)
continue
}
// Handle submodule
go func(i int) {
cinfo := commitInfo{entryName: tes[i].Name()}
sm, err := commit.GetSubModule(path.Join(treePath, tes[i].Name()))
if err != nil && !IsErrNotExist(err) {
cinfo.err = fmt.Errorf("GetSubModule (%s/%s): %v", treePath, tes[i].Name(), err)
revChan <- cinfo
return
}
smURL := ""
if sm != nil {
smURL = sm.URL
}
c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
if err != nil {
cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
} else {
cinfo.infos = []interface{}{tes[i], NewSubModuleFile(c, smURL, tes[i].ID.String())}
}
revChan <- cinfo
<-taskChan
}(i)
}
if err := <-doneChan; err != nil {
return nil, err
}
commitsInfo := make([][]interface{}, len(tes))
for i := 0; i < len(tes); i++ {
commitsInfo[i] = infoMap[tes[i].Name()]
}
return commitsInfo, nil
}

93
vendor/github.com/gogs/git-module/utils.go generated vendored Normal file
View File

@@ -0,0 +1,93 @@
// 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 (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
// objectCache provides thread-safe cache opeations.
type objectCache struct {
lock sync.RWMutex
cache map[string]interface{}
}
func newObjectCache() *objectCache {
return &objectCache{
cache: make(map[string]interface{}, 10),
}
}
func (oc *objectCache) Set(id string, obj interface{}) {
oc.lock.Lock()
defer oc.lock.Unlock()
oc.cache[id] = obj
}
func (oc *objectCache) Get(id string) (interface{}, bool) {
oc.lock.RLock()
defer oc.lock.RUnlock()
obj, has := oc.cache[id]
return obj, has
}
// isDir returns true if given path is a directory,
// or returns false when it's a file or does not exist.
func isDir(dir string) bool {
f, e := os.Stat(dir)
if e != nil {
return false
}
return f.IsDir()
}
// isFile returns true if given path is a file,
// or returns false when it's a directory or does not exist.
func isFile(filePath string) bool {
f, e := os.Stat(filePath)
if e != nil {
return false
}
return !f.IsDir()
}
// isExist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func isExist(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
func concatenateError(err error, stderr string) error {
if len(stderr) == 0 {
return err
}
return fmt.Errorf("%v - %s", err, stderr)
}
// If the object is stored in its own file (i.e not in a pack file),
// this function returns the full path to the object file.
// It does not test if the file exists.
func filepathFromSHA1(rootdir, sha1 string) string {
return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
}
func RefEndName(refStr string) string {
if strings.HasPrefix(refStr, BRANCH_PREFIX) {
return refStr[len(BRANCH_PREFIX):]
}
if strings.HasPrefix(refStr, TAG_PREFIX) {
return refStr[len(TAG_PREFIX):]
}
return refStr
}