mirror of https://github.com/gogits/gogs.git
Unknwon
9 years ago
45 changed files with 185 additions and 2002 deletions
@ -1,26 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"errors" |
|
||||||
"io" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
type Blob struct { |
|
||||||
repo *Repository |
|
||||||
*TreeEntry |
|
||||||
} |
|
||||||
|
|
||||||
func (b *Blob) Data() (io.Reader, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(b.repo.Path, "git", "show", b.ID.String()) |
|
||||||
if err != nil { |
|
||||||
return nil, errors.New(string(stderr)) |
|
||||||
} |
|
||||||
return bytes.NewBuffer(stdout), nil |
|
||||||
} |
|
@ -1,162 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bufio" |
|
||||||
"container/list" |
|
||||||
"net/http" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
// 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
|
|
||||||
submodules map[string]*SubModule |
|
||||||
} |
|
||||||
|
|
||||||
// Return the commit message. Same as retrieving CommitMessage directly.
|
|
||||||
func (c *Commit) Message() string { |
|
||||||
return c.CommitMessage |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) Summary() string { |
|
||||||
return strings.Split(c.CommitMessage, "\n")[0] |
|
||||||
} |
|
||||||
|
|
||||||
// Return oid of the parent number n (0-based index). Return nil if no such parent exists.
|
|
||||||
func (c *Commit) ParentId(n int) (id sha1, err error) { |
|
||||||
if n >= len(c.parents) { |
|
||||||
err = IDNotExist |
|
||||||
return |
|
||||||
} |
|
||||||
return c.parents[n], nil |
|
||||||
} |
|
||||||
|
|
||||||
// Return parent number n (0-based index)
|
|
||||||
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 |
|
||||||
} |
|
||||||
|
|
||||||
// Return the number of parents of the commit. 0 if this is the
|
|
||||||
// root commit, otherwise 1,2,...
|
|
||||||
func (c *Commit) ParentCount() int { |
|
||||||
return len(c.parents) |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) CommitsBefore() (*list.List, error) { |
|
||||||
return c.repo.getCommitsBefore(c.ID) |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) CommitsBeforeUntil(commitId string) (*list.List, error) { |
|
||||||
ec, err := c.repo.GetCommit(commitId) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return c.repo.CommitsBetween(c, ec) |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) CommitsCount() (int, error) { |
|
||||||
return c.repo.commitsCount(c.ID) |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) SearchCommits(keyword string) (*list.List, error) { |
|
||||||
return c.repo.searchCommits(c.ID, keyword) |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) CommitsByRange(page int) (*list.List, error) { |
|
||||||
return c.repo.commitsByRange(c.ID, page) |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) GetCommitOfRelPath(relPath string) (*Commit, error) { |
|
||||||
return c.repo.getCommitOfRelPath(c.ID, relPath) |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) { |
|
||||||
modules, err := c.GetSubModules() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return modules[entryname], nil |
|
||||||
} |
|
||||||
|
|
||||||
func (c *Commit) GetSubModules() (map[string]*SubModule, error) { |
|
||||||
if c.submodules != nil { |
|
||||||
return c.submodules, 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.submodules = make(map[string]*SubModule) |
|
||||||
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.submodules[path] = &SubModule{path, strings.TrimSpace(fields[1])} |
|
||||||
ismodule = false |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return c.submodules, nil |
|
||||||
} |
|
||||||
|
|
||||||
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) |
|
||||||
if n > 0 { |
|
||||||
buf = buf[:n] |
|
||||||
} |
|
||||||
_, isImage := isImageFile(buf) |
|
||||||
return isImage |
|
||||||
} |
|
@ -1,36 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
type ArchiveType int |
|
||||||
|
|
||||||
const ( |
|
||||||
ZIP ArchiveType = iota + 1 |
|
||||||
TARGZ |
|
||||||
) |
|
||||||
|
|
||||||
func (c *Commit) CreateArchive(path 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) |
|
||||||
} |
|
||||||
|
|
||||||
_, stderr, err := com.ExecCmdDir(c.repo.Path, "git", "archive", "--format="+format, "-o", path, c.ID.String()) |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("%s", stderr) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
// 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" |
|
||||||
) |
|
||||||
|
|
||||||
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) |
|
||||||
} |
|
@ -1,125 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"io/ioutil" |
|
||||||
"os" |
|
||||||
"path" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
// hookNames is a list of Git hooks' name that are supported.
|
|
||||||
var hookNames = []string{ |
|
||||||
"applypatch-msg", |
|
||||||
"pre-applypatch", |
|
||||||
"post-applypatch", |
|
||||||
"pre-commit", |
|
||||||
"prepare-commit-msg", |
|
||||||
"commit-msg", |
|
||||||
"post-commit", |
|
||||||
"pre-rebase", |
|
||||||
"post-checkout", |
|
||||||
"post-merge", |
|
||||||
"pre-push", |
|
||||||
"pre-receive", |
|
||||||
// "update",
|
|
||||||
"post-receive", |
|
||||||
"post-update", |
|
||||||
"push-to-checkout", |
|
||||||
"pre-auto-gc", |
|
||||||
"post-rewrite", |
|
||||||
} |
|
||||||
|
|
||||||
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, "hooks", name), |
|
||||||
} |
|
||||||
if isFile(h.path) { |
|
||||||
data, err := ioutil.ReadFile(h.path) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
h.IsActive = true |
|
||||||
h.Content = string(data) |
|
||||||
} else if isFile(h.path + ".sample") { |
|
||||||
data, err := ioutil.ReadFile(h.path + ".sample") |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
h.Sample = string(data) |
|
||||||
} |
|
||||||
return h, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (h *Hook) Name() string { |
|
||||||
return h.name |
|
||||||
} |
|
||||||
|
|
||||||
// Update updates hook settings.
|
|
||||||
func (h *Hook) Update() error { |
|
||||||
if len(strings.TrimSpace(h.Content)) == 0 { |
|
||||||
if com.IsExist(h.path) { |
|
||||||
return os.Remove(h.path) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
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 |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) GetHook(name string) (*Hook, error) { |
|
||||||
return GetHook(repo.Path, name) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) Hooks() ([]*Hook, error) { |
|
||||||
return ListHooks(repo.Path) |
|
||||||
} |
|
@ -1,30 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"path/filepath" |
|
||||||
) |
|
||||||
|
|
||||||
// Repository represents a Git repository.
|
|
||||||
type Repository struct { |
|
||||||
Path string |
|
||||||
|
|
||||||
commitCache map[sha1]*Commit |
|
||||||
tagCache map[sha1]*Tag |
|
||||||
} |
|
||||||
|
|
||||||
// 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}, nil |
|
||||||
} |
|
@ -1,50 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
func IsBranchExist(repoPath, branchName string) bool { |
|
||||||
_, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/heads/"+branchName) |
|
||||||
return err == nil |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) IsBranchExist(branchName string) bool { |
|
||||||
return IsBranchExist(repo.Path, branchName) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) GetBranches() ([]string, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--heads") |
|
||||||
if err != nil { |
|
||||||
return nil, concatenateError(err, stderr) |
|
||||||
} |
|
||||||
infos := strings.Split(stdout, "\n") |
|
||||||
branches := make([]string, len(infos)-1) |
|
||||||
for i, info := range infos[:len(infos)-1] { |
|
||||||
parts := strings.Split(info, " ") |
|
||||||
if len(parts) != 2 { |
|
||||||
continue // NOTE: I should believe git will not give me wrong string.
|
|
||||||
} |
|
||||||
branches[i] = strings.TrimPrefix(parts[1], "refs/heads/") |
|
||||||
} |
|
||||||
return branches, nil |
|
||||||
} |
|
||||||
|
|
||||||
// SetDefaultBranch sets default branch of repository.
|
|
||||||
func (repo *Repository) SetDefaultBranch(branchName string) error { |
|
||||||
if gitVer.LessThan(MustParseVersion("1.7.10")) { |
|
||||||
return ErrUnsupportedVersion{"1.7.10"} |
|
||||||
} |
|
||||||
|
|
||||||
_, stderr, err := com.ExecCmdDir(repo.Path, "git", "symbolic-ref", "HEAD", "refs/heads/"+branchName) |
|
||||||
if err != nil { |
|
||||||
return concatenateError(err, stderr) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -1,341 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"container/list" |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
"sync" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
func (repo *Repository) getCommitIdOfRef(refpath string) (string, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--verify", refpath) |
|
||||||
if err != nil { |
|
||||||
return "", errors.New(stderr) |
|
||||||
} |
|
||||||
return strings.Split(stdout, " ")[0], nil |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) GetCommitIdOfBranch(branchName string) (string, error) { |
|
||||||
return repo.getCommitIdOfRef("refs/heads/" + branchName) |
|
||||||
} |
|
||||||
|
|
||||||
// get branch's last commit or a special commit by id string
|
|
||||||
func (repo *Repository) GetCommitOfBranch(branchName string) (*Commit, error) { |
|
||||||
commitId, err := repo.GetCommitIdOfBranch(branchName) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return repo.GetCommit(commitId) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) GetCommitIdOfTag(tagName string) (string, error) { |
|
||||||
return repo.getCommitIdOfRef("refs/tags/" + tagName) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) GetCommitOfTag(tagName string) (*Commit, error) { |
|
||||||
tag, err := repo.GetTag(tagName) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return tag.Commit() |
|
||||||
} |
|
||||||
|
|
||||||
// Parse 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": |
|
||||||
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": |
|
||||||
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) { |
|
||||||
if repo.commitCache != nil { |
|
||||||
if c, ok := repo.commitCache[id]; ok { |
|
||||||
return c, nil |
|
||||||
} |
|
||||||
} else { |
|
||||||
repo.commitCache = make(map[sha1]*Commit, 10) |
|
||||||
} |
|
||||||
|
|
||||||
data, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String()) |
|
||||||
if err != nil { |
|
||||||
return nil, concatenateError(err, string(stderr)) |
|
||||||
} |
|
||||||
|
|
||||||
commit, err := parseCommitData(data) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
commit.repo = repo |
|
||||||
commit.ID = id |
|
||||||
|
|
||||||
repo.commitCache[id] = commit |
|
||||||
return commit, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Find the commit object in the repository.
|
|
||||||
func (repo *Repository) GetCommit(commitId string) (*Commit, error) { |
|
||||||
id, err := NewIdFromString(commitId) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return repo.getCommit(id) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) commitsCount(id sha1) (int, error) { |
|
||||||
if gitVer.LessThan(MustParseVersion("1.8.0")) { |
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", |
|
||||||
"--pretty=format:''", id.String()) |
|
||||||
if err != nil { |
|
||||||
return 0, errors.New(string(stderr)) |
|
||||||
} |
|
||||||
return len(bytes.Split(stdout, []byte("\n"))), nil |
|
||||||
} |
|
||||||
|
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", id.String()) |
|
||||||
if err != nil { |
|
||||||
return 0, errors.New(stderr) |
|
||||||
} |
|
||||||
return com.StrTo(strings.TrimSpace(stdout)).Int() |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) CommitsCount(commitId string) (int, error) { |
|
||||||
id, err := NewIdFromString(commitId) |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
return repo.commitsCount(id) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) commitsCountBetween(start, end sha1) (int, error) { |
|
||||||
if gitVer.LessThan(MustParseVersion("1.8.0")) { |
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", |
|
||||||
"--pretty=format:''", start.String()+"..."+end.String()) |
|
||||||
if err != nil { |
|
||||||
return 0, errors.New(string(stderr)) |
|
||||||
} |
|
||||||
return len(bytes.Split(stdout, []byte("\n"))), nil |
|
||||||
} |
|
||||||
|
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", |
|
||||||
start.String()+"..."+end.String()) |
|
||||||
if err != nil { |
|
||||||
return 0, errors.New(stderr) |
|
||||||
} |
|
||||||
return com.StrTo(strings.TrimSpace(stdout)).Int() |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) CommitsCountBetween(startCommitID, endCommitID string) (int, error) { |
|
||||||
start, err := NewIdFromString(startCommitID) |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
end, err := NewIdFromString(endCommitID) |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
return repo.commitsCountBetween(start, end) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "diff", "--name-only", |
|
||||||
startCommitID+"..."+endCommitID) |
|
||||||
if err != nil { |
|
||||||
return 0, fmt.Errorf("list changed files: %v", concatenateError(err, stderr)) |
|
||||||
} |
|
||||||
return len(strings.Split(stdout, "\n")) - 1, nil |
|
||||||
} |
|
||||||
|
|
||||||
// used only for single tree, (]
|
|
||||||
func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) { |
|
||||||
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) commitsBefore(lock *sync.Mutex, l *list.List, parent *list.Element, id sha1, limit int) error { |
|
||||||
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) |
|
||||||
} |
|
||||||
|
|
||||||
var 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(lock, l, pr, id, 0) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) FileCommitsCount(branch, file string) (int, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", |
|
||||||
branch, "--", file) |
|
||||||
if err != nil { |
|
||||||
return 0, errors.New(stderr) |
|
||||||
} |
|
||||||
return com.StrTo(strings.TrimSpace(stdout)).Int() |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) CommitsByFileAndRange(branch, file string, page int) (*list.List, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", branch, |
|
||||||
"--skip="+com.ToStr((page-1)*50), "--max-count=50", prettyLogFormat, "--", file) |
|
||||||
if err != nil { |
|
||||||
return nil, errors.New(string(stderr)) |
|
||||||
} |
|
||||||
return parsePrettyFormatLog(repo, stdout) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) { |
|
||||||
l := list.New() |
|
||||||
lock := new(sync.Mutex) |
|
||||||
return l, repo.commitsBefore(lock, l, nil, id, 0) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) searchCommits(id sha1, keyword string) (*list.List, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), "-100", |
|
||||||
"-i", "--grep="+keyword, prettyLogFormat) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} else if len(stderr) > 0 { |
|
||||||
return nil, errors.New(string(stderr)) |
|
||||||
} |
|
||||||
return parsePrettyFormatLog(repo, stdout) |
|
||||||
} |
|
||||||
|
|
||||||
var CommitsRangeSize = 50 |
|
||||||
|
|
||||||
func (repo *Repository) commitsByRange(id sha1, page int) (*list.List, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), |
|
||||||
"--skip="+com.ToStr((page-1)*CommitsRangeSize), "--max-count="+com.ToStr(CommitsRangeSize), prettyLogFormat) |
|
||||||
if err != nil { |
|
||||||
return nil, errors.New(string(stderr)) |
|
||||||
} |
|
||||||
return parsePrettyFormatLog(repo, stdout) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) getCommitOfRelPath(id sha1, relPath string) (*Commit, error) { |
|
||||||
stdout, _, err := com.ExecCmdDir(repo.Path, "git", "log", "-1", prettyLogFormat, id.String(), "--", relPath) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
id, err = NewIdFromString(string(stdout)) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return repo.getCommit(id) |
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
// 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 ( |
|
||||||
COMMIT ObjectType = "commit" |
|
||||||
TREE ObjectType = "tree" |
|
||||||
BLOB ObjectType = "blob" |
|
||||||
TAG ObjectType = "tag" |
|
||||||
) |
|
@ -1,104 +0,0 @@ |
|||||||
// 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" |
|
||||||
"strings" |
|
||||||
"time" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
type PullRequestInfo struct { |
|
||||||
MergeBase string |
|
||||||
Commits *list.List |
|
||||||
// Diff *Diff
|
|
||||||
NumFiles int |
|
||||||
} |
|
||||||
|
|
||||||
// GetMergeBase checks and returns merge base of two branches.
|
|
||||||
func (repo *Repository) GetMergeBase(remoteBranch, headBranch string) (string, error) { |
|
||||||
// Get merge base commit.
|
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "merge-base", remoteBranch, headBranch) |
|
||||||
if err != nil { |
|
||||||
return "", fmt.Errorf("get merge base: %v", concatenateError(err, stderr)) |
|
||||||
} |
|
||||||
return strings.TrimSpace(stdout), nil |
|
||||||
} |
|
||||||
|
|
||||||
// AddRemote adds a remote to repository.
|
|
||||||
func (repo *Repository) AddRemote(name, path string) error { |
|
||||||
_, stderr, err := com.ExecCmdDir(repo.Path, "git", "remote", "add", "-f", name, path) |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("add remote(%s - %s): %v", name, path, concatenateError(err, stderr)) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// RemoveRemote removes a remote from repository.
|
|
||||||
func (repo *Repository) RemoveRemote(name string) error { |
|
||||||
_, stderr, err := com.ExecCmdDir(repo.Path, "git", "remote", "remove", name) |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("remove remote(%s): %v", name, concatenateError(err, stderr)) |
|
||||||
} |
|
||||||
return 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) { |
|
||||||
// Add a temporary remote.
|
|
||||||
tmpRemote := com.ToStr(time.Now().UnixNano()) |
|
||||||
if err = repo.AddRemote(tmpRemote, basePath); err != nil { |
|
||||||
return nil, fmt.Errorf("AddRemote: %v", err) |
|
||||||
} |
|
||||||
defer func() { |
|
||||||
repo.RemoveRemote(tmpRemote) |
|
||||||
}() |
|
||||||
|
|
||||||
remoteBranch := "remotes/" + tmpRemote + "/" + baseBranch |
|
||||||
|
|
||||||
prInfo := new(PullRequestInfo) |
|
||||||
prInfo.MergeBase, err = repo.GetMergeBase(remoteBranch, headBranch) |
|
||||||
if err != nil { |
|
||||||
return nil, fmt.Errorf("GetMergeBase: %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "log", prInfo.MergeBase+"..."+headBranch, prettyLogFormat) |
|
||||||
if err != nil { |
|
||||||
return nil, fmt.Errorf("list diff logs: %v", concatenateError(err, stderr)) |
|
||||||
} |
|
||||||
prInfo.Commits, err = parsePrettyFormatLog(repo, []byte(stdout)) |
|
||||||
if err != nil { |
|
||||||
return nil, fmt.Errorf("parsePrettyFormatLog: %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
// Count number of changed files.
|
|
||||||
stdout, stderr, err = com.ExecCmdDir(repo.Path, "git", "diff", "--name-only", remoteBranch+"..."+headBranch) |
|
||||||
if err != nil { |
|
||||||
return nil, fmt.Errorf("list changed files: %v", concatenateError(err, stderr)) |
|
||||||
} |
|
||||||
prInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1 |
|
||||||
|
|
||||||
return prInfo, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetPatch generates and returns patch data between given branches.
|
|
||||||
func (repo *Repository) GetPatch(mergeBase, headBranch string) ([]byte, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "diff", "-p", "--binary", mergeBase, headBranch) |
|
||||||
if err != nil { |
|
||||||
return nil, concatenateError(err, string(stderr)) |
|
||||||
} |
|
||||||
|
|
||||||
return stdout, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Merge merges pull request from head repository and branch.
|
|
||||||
func (repo *Repository) Merge(headRepoPath string, baseBranch, headBranch string) error { |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
@ -1,117 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
func IsTagExist(repoPath, tagName string) bool { |
|
||||||
_, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/tags/"+tagName) |
|
||||||
return err == nil |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) IsTagExist(tagName string) bool { |
|
||||||
return IsTagExist(repo.Path, tagName) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) getTagsReversed() ([]string, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l", "--sort=-v:refname") |
|
||||||
if err != nil { |
|
||||||
return nil, concatenateError(err, stderr) |
|
||||||
} |
|
||||||
tags := strings.Split(stdout, "\n") |
|
||||||
return tags[:len(tags)-1], nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetTags returns all tags of given repository.
|
|
||||||
func (repo *Repository) GetTags() ([]string, error) { |
|
||||||
if gitVer.AtLeast(MustParseVersion("2.0.0")) { |
|
||||||
return repo.getTagsReversed() |
|
||||||
} |
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l") |
|
||||||
if err != nil { |
|
||||||
return nil, concatenateError(err, stderr) |
|
||||||
} |
|
||||||
tags := strings.Split(stdout, "\n") |
|
||||||
return tags[:len(tags)-1], nil |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) CreateTag(tagName, idStr string) error { |
|
||||||
_, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", tagName, idStr) |
|
||||||
if err != nil { |
|
||||||
return errors.New(stderr) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) getTag(id sha1) (*Tag, error) { |
|
||||||
if repo.tagCache != nil { |
|
||||||
if t, ok := repo.tagCache[id]; ok { |
|
||||||
return t, nil |
|
||||||
} |
|
||||||
} else { |
|
||||||
repo.tagCache = make(map[sha1]*Tag, 10) |
|
||||||
} |
|
||||||
|
|
||||||
// Get tag type.
|
|
||||||
tp, stderr, err := com.ExecCmdDir(repo.Path, "git", "cat-file", "-t", id.String()) |
|
||||||
if err != nil { |
|
||||||
return nil, errors.New(stderr) |
|
||||||
} |
|
||||||
tp = strings.TrimSpace(tp) |
|
||||||
|
|
||||||
// Tag is a commit.
|
|
||||||
if ObjectType(tp) == COMMIT { |
|
||||||
tag := &Tag{ |
|
||||||
ID: id, |
|
||||||
Object: id, |
|
||||||
Type: string(COMMIT), |
|
||||||
repo: repo, |
|
||||||
} |
|
||||||
repo.tagCache[id] = tag |
|
||||||
return tag, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Tag with message.
|
|
||||||
data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String()) |
|
||||||
if err != nil { |
|
||||||
return nil, errors.New(string(bytErr)) |
|
||||||
} |
|
||||||
|
|
||||||
tag, err := parseTagData(data) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
tag.ID = id |
|
||||||
tag.repo = repo |
|
||||||
|
|
||||||
repo.tagCache[id] = tag |
|
||||||
return tag, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetTag returns a Git tag by given name.
|
|
||||||
func (repo *Repository) GetTag(tagName string) (*Tag, error) { |
|
||||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--tags", tagName) |
|
||||||
if err != nil { |
|
||||||
return nil, errors.New(stderr) |
|
||||||
} |
|
||||||
|
|
||||||
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 = tagName |
|
||||||
return tag, nil |
|
||||||
} |
|
@ -1,32 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
// 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) |
|
||||||
} |
|
||||||
|
|
||||||
func (repo *Repository) getTree(id sha1) (*Tree, error) { |
|
||||||
treePath := filepathFromSHA1(repo.Path, id.String()) |
|
||||||
if !com.IsFile(treePath) { |
|
||||||
_, _, err := com.ExecCmdDir(repo.Path, "git", "ls-tree", id.String()) |
|
||||||
if err != nil { |
|
||||||
return nil, fmt.Errorf("repo.getTree: %v", ErrNotExist) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return NewTree(repo, id), nil |
|
||||||
} |
|
@ -1,87 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/hex" |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
IDNotExist = errors.New("sha1 ID does not exist") |
|
||||||
) |
|
||||||
|
|
||||||
type sha1 [20]byte |
|
||||||
|
|
||||||
// Return 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 |
|
||||||
} |
|
||||||
|
|
||||||
// Return 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) |
|
||||||
} |
|
||||||
|
|
||||||
// Create a new sha1 from a 20 byte slice.
|
|
||||||
func NewId(b []byte) (sha1, error) { |
|
||||||
var id sha1 |
|
||||||
if len(b) != 20 { |
|
||||||
return id, errors.New("Length must be 20") |
|
||||||
} |
|
||||||
|
|
||||||
for i := 0; i < 20; i++ { |
|
||||||
id[i] = b[i] |
|
||||||
} |
|
||||||
return id, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Create a new sha1 from a Sha1 string of length 40.
|
|
||||||
func NewIdFromString(s string) (sha1, error) { |
|
||||||
s = strings.TrimSpace(s) |
|
||||||
var id sha1 |
|
||||||
if len(s) != 40 { |
|
||||||
return id, fmt.Errorf("Length must be 40") |
|
||||||
} |
|
||||||
b, err := hex.DecodeString(s) |
|
||||||
if err != nil { |
|
||||||
return id, err |
|
||||||
} |
|
||||||
|
|
||||||
return NewId(b) |
|
||||||
} |
|
@ -1,51 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"strconv" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
// Author and 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, err := strconv.ParseInt(timestring, 10, 64) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
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 |
|
||||||
} |
|
@ -1,20 +0,0 @@ |
|||||||
// 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 ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey" |
|
||||||
) |
|
||||||
|
|
||||||
func Test_newSignatureFromCommitline(t *testing.T) { |
|
||||||
Convey("Parse signature from commit line", t, func() { |
|
||||||
line := "Intern <intern@macbook-intern.(none)> 1445412825 +0200" |
|
||||||
sig, err := newSignatureFromCommitline([]byte(line)) |
|
||||||
So(err, ShouldBeNil) |
|
||||||
So(sig, ShouldNotBeNil) |
|
||||||
}) |
|
||||||
} |
|
@ -1,70 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/setting" |
|
||||||
) |
|
||||||
|
|
||||||
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() 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 |
|
||||||
} |
|
||||||
|
|
||||||
// sysuser@xxx:user/repo
|
|
||||||
i := strings.Index(url, "@") |
|
||||||
j := strings.LastIndex(url, ":") |
|
||||||
if i > -1 && j > -1 { |
|
||||||
// fix problem with reverse proxy works only with local server
|
|
||||||
if strings.Contains(setting.AppUrl, url[i+1:j]) { |
|
||||||
return setting.AppUrl + 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 |
|
||||||
} |
|
@ -1,67 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
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 |
|
||||||
TagMessage 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.TagMessage = string(data[nextline+1:]) |
|
||||||
break l |
|
||||||
default: |
|
||||||
break l |
|
||||||
} |
|
||||||
} |
|
||||||
return tag, nil |
|
||||||
} |
|
@ -1,157 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"errors" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
ErrNotExist = errors.New("error not exist") |
|
||||||
) |
|
||||||
|
|
||||||
// A tree is a flat directory listing.
|
|
||||||
type Tree struct { |
|
||||||
ID sha1 |
|
||||||
repo *Repository |
|
||||||
|
|
||||||
// parent tree
|
|
||||||
ptree *Tree |
|
||||||
|
|
||||||
entries Entries |
|
||||||
entriesParsed bool |
|
||||||
} |
|
||||||
|
|
||||||
var escapeChar = []byte("\\") |
|
||||||
|
|
||||||
func UnescapeChars(in []byte) []byte { |
|
||||||
if bytes.Index(in, escapeChar) == -1 { |
|
||||||
return in |
|
||||||
} |
|
||||||
|
|
||||||
endIdx := len(in) - 1 |
|
||||||
isEscape := false |
|
||||||
out := make([]byte, 0, endIdx+1) |
|
||||||
for i := range in { |
|
||||||
if in[i] == '\\' && !isEscape { |
|
||||||
isEscape = true |
|
||||||
continue |
|
||||||
} |
|
||||||
isEscape = false |
|
||||||
out = append(out, in[i]) |
|
||||||
} |
|
||||||
return out |
|
||||||
} |
|
||||||
|
|
||||||
// Parse 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": |
|
||||||
entry.mode = ModeBlob |
|
||||||
entry.Type = BLOB |
|
||||||
case "100755": |
|
||||||
entry.mode = ModeExec |
|
||||||
entry.Type = BLOB |
|
||||||
case "120000": |
|
||||||
entry.mode = ModeSymlink |
|
||||||
entry.Type = BLOB |
|
||||||
case "160000": |
|
||||||
entry.mode = ModeCommit |
|
||||||
entry.Type = COMMIT |
|
||||||
|
|
||||||
step = 8 |
|
||||||
case "040000": |
|
||||||
entry.mode = ModeTree |
|
||||||
entry.Type = TREE |
|
||||||
default: |
|
||||||
return nil, errors.New("unknown type: " + 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 |
|
||||||
var g = t |
|
||||||
var p = t |
|
||||||
var 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 |
|
||||||
} |
|
||||||
|
|
||||||
func (t *Tree) ListEntries(relpath string) (Entries, error) { |
|
||||||
if t.entriesParsed { |
|
||||||
return t.entries, nil |
|
||||||
} |
|
||||||
t.entriesParsed = true |
|
||||||
|
|
||||||
stdout, stderr, err := com.ExecCmdDirBytes(t.repo.Path, |
|
||||||
"git", "ls-tree", t.ID.String()) |
|
||||||
if err != nil { |
|
||||||
if strings.Contains(err.Error(), "exit status 128") { |
|
||||||
return nil, errors.New(strings.TrimSpace(string(stderr))) |
|
||||||
} |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
t.entries, err = parseTreeData(t, stdout) |
|
||||||
return t.entries, err |
|
||||||
} |
|
||||||
|
|
||||||
func NewTree(repo *Repository, id sha1) *Tree { |
|
||||||
tree := new(Tree) |
|
||||||
tree.ID = id |
|
||||||
tree.repo = repo |
|
||||||
return tree |
|
||||||
} |
|
@ -1,59 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"path" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { |
|
||||||
if len(relpath) == 0 { |
|
||||||
return &TreeEntry{ |
|
||||||
ID: t.ID, |
|
||||||
Type: TREE, |
|
||||||
mode: ModeTree, |
|
||||||
}, nil |
|
||||||
// return nil, fmt.Errorf("GetTreeEntryByPath(empty relpath): %v", ErrNotExist)
|
|
||||||
} |
|
||||||
|
|
||||||
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(path.Dir(relpath)) |
|
||||||
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, fmt.Errorf("GetTreeEntryByPath: %v", ErrNotExist) |
|
||||||
} |
|
||||||
|
|
||||||
func (t *Tree) GetBlobByPath(rpath string) (*Blob, error) { |
|
||||||
entry, err := t.GetTreeEntryByPath(rpath) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if !entry.IsDir() { |
|
||||||
return entry.Blob(), nil |
|
||||||
} |
|
||||||
|
|
||||||
return nil, ErrNotExist |
|
||||||
} |
|
@ -1,113 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"sort" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
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 ( |
|
||||||
ModeBlob EntryMode = 0100644 |
|
||||||
ModeExec EntryMode = 0100755 |
|
||||||
ModeSymlink EntryMode = 0120000 |
|
||||||
ModeCommit EntryMode = 0160000 |
|
||||||
ModeTree 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 |
|
||||||
} |
|
||||||
|
|
||||||
if te.sized { |
|
||||||
return te.size |
|
||||||
} |
|
||||||
|
|
||||||
stdout, _, err := com.ExecCmdDir(te.ptree.repo.Path, "git", "cat-file", "-s", te.ID.String()) |
|
||||||
if err != nil { |
|
||||||
return 0 |
|
||||||
} |
|
||||||
|
|
||||||
te.sized = true |
|
||||||
te.size = com.StrTo(strings.TrimSpace(stdout)).MustInt64() |
|
||||||
return te.size |
|
||||||
} |
|
||||||
|
|
||||||
func (te *TreeEntry) IsSubModule() bool { |
|
||||||
return te.mode == ModeCommit |
|
||||||
} |
|
||||||
|
|
||||||
func (te *TreeEntry) IsDir() bool { |
|
||||||
return te.mode == ModeTree |
|
||||||
} |
|
||||||
|
|
||||||
func (te *TreeEntry) EntryMode() EntryMode { |
|
||||||
return te.mode |
|
||||||
} |
|
||||||
|
|
||||||
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 (bs Entries) Len() int { return len(bs) } |
|
||||||
func (bs Entries) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] } |
|
||||||
func (bs Entries) Less(i, j int) bool { |
|
||||||
t1, t2 := bs[i], bs[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 (bs Entries) Sort() { |
|
||||||
sort.Sort(bs) |
|
||||||
} |
|
@ -1,82 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"container/list" |
|
||||||
"fmt" |
|
||||||
"os" |
|
||||||
"path/filepath" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
const prettyLogFormat = `--pretty=format:%H` |
|
||||||
|
|
||||||
func parsePrettyFormatLog(repo *Repository, logByts []byte) (*list.List, error) { |
|
||||||
l := list.New() |
|
||||||
if len(logByts) == 0 { |
|
||||||
return l, nil |
|
||||||
} |
|
||||||
|
|
||||||
parts := bytes.Split(logByts, []byte{'\n'}) |
|
||||||
|
|
||||||
for _, commitId := range parts { |
|
||||||
commit, err := repo.GetCommit(string(commitId)) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
l.PushBack(commit) |
|
||||||
} |
|
||||||
|
|
||||||
return l, nil |
|
||||||
} |
|
||||||
|
|
||||||
func RefEndName(refStr string) string { |
|
||||||
if strings.HasPrefix(refStr, "refs/heads/") { |
|
||||||
// trim the "refs/heads/"
|
|
||||||
return refStr[len("refs/heads/"):] |
|
||||||
} |
|
||||||
|
|
||||||
index := strings.LastIndex(refStr, "/") |
|
||||||
if index != -1 { |
|
||||||
return refStr[index+1:] |
|
||||||
} |
|
||||||
return refStr |
|
||||||
} |
|
||||||
|
|
||||||
// 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:]) |
|
||||||
} |
|
||||||
|
|
||||||
// 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() |
|
||||||
} |
|
||||||
|
|
||||||
func concatenateError(err error, stderr string) error { |
|
||||||
if len(stderr) == 0 { |
|
||||||
return err |
|
||||||
} |
|
||||||
return fmt.Errorf("%v: %s", err, stderr) |
|
||||||
} |
|
@ -1,104 +0,0 @@ |
|||||||
// 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 |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unknwon/com" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// Cached Git version.
|
|
||||||
gitVer *Version |
|
||||||
) |
|
||||||
|
|
||||||
// Version represents version of Git.
|
|
||||||
type Version struct { |
|
||||||
Major, Minor, Patch int |
|
||||||
} |
|
||||||
|
|
||||||
func ParseVersion(verStr string) (*Version, error) { |
|
||||||
infos := strings.Split(verStr, ".") |
|
||||||
if len(infos) < 3 { |
|
||||||
return nil, errors.New("incorrect version input") |
|
||||||
} |
|
||||||
|
|
||||||
v := &Version{} |
|
||||||
for i, s := range infos { |
|
||||||
switch i { |
|
||||||
case 0: |
|
||||||
v.Major, _ = com.StrTo(s).Int() |
|
||||||
case 1: |
|
||||||
v.Minor, _ = com.StrTo(s).Int() |
|
||||||
case 2: |
|
||||||
v.Patch, _ = com.StrTo(strings.TrimSpace(s)).Int() |
|
||||||
} |
|
||||||
} |
|
||||||
return v, nil |
|
||||||
} |
|
||||||
|
|
||||||
func MustParseVersion(verStr string) *Version { |
|
||||||
v, _ := ParseVersion(verStr) |
|
||||||
return v |
|
||||||
} |
|
||||||
|
|
||||||
// Compare compares two versions,
|
|
||||||
// it returns 1 if original is greater, -1 if original is smaller, 0 if equal.
|
|
||||||
func (v *Version) Compare(that *Version) int { |
|
||||||
if v.Major > that.Major { |
|
||||||
return 1 |
|
||||||
} else if v.Major < that.Major { |
|
||||||
return -1 |
|
||||||
} |
|
||||||
|
|
||||||
if v.Minor > that.Minor { |
|
||||||
return 1 |
|
||||||
} else if v.Minor < that.Minor { |
|
||||||
return -1 |
|
||||||
} |
|
||||||
|
|
||||||
if v.Patch > that.Patch { |
|
||||||
return 1 |
|
||||||
} else if v.Patch < that.Patch { |
|
||||||
return -1 |
|
||||||
} |
|
||||||
|
|
||||||
return 0 |
|
||||||
} |
|
||||||
|
|
||||||
func (v *Version) LessThan(that *Version) bool { |
|
||||||
return v.Compare(that) < 0 |
|
||||||
} |
|
||||||
|
|
||||||
func (v *Version) AtLeast(that *Version) bool { |
|
||||||
return v.Compare(that) >= 0 |
|
||||||
} |
|
||||||
|
|
||||||
func (v *Version) String() string { |
|
||||||
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) |
|
||||||
} |
|
||||||
|
|
||||||
// GetVersion returns current Git version installed.
|
|
||||||
func GetVersion() (*Version, error) { |
|
||||||
if gitVer != nil { |
|
||||||
return gitVer, nil |
|
||||||
} |
|
||||||
|
|
||||||
stdout, stderr, err := com.ExecCmd("git", "version") |
|
||||||
if err != nil { |
|
||||||
return nil, errors.New(stderr) |
|
||||||
} |
|
||||||
|
|
||||||
infos := strings.Split(stdout, " ") |
|
||||||
if len(infos) < 3 { |
|
||||||
return nil, errors.New("not enough output") |
|
||||||
} |
|
||||||
|
|
||||||
gitVer, err = ParseVersion(infos[2]) |
|
||||||
return gitVer, err |
|
||||||
} |
|
Loading…
Reference in new issue