complete migration from gogs module

This commit is contained in:
Unknwon 2015-12-09 20:45:25 -05:00
Родитель d14b1e8b81
Коммит 4cc1cbbfea
17 изменённых файлов: 978 добавлений и 46 удалений

151
commit.go
Просмотреть файл

@ -5,7 +5,10 @@
package git
import (
"bufio"
"container/list"
"fmt"
"net/http"
"strconv"
"strings"
@ -20,8 +23,18 @@ type Commit struct {
Committer *Signature
CommitMessage string
parents []sha1 // SHA1 strings
// submodules map[string]*SubModule
parents []sha1 // SHA1 strings
submodules map[string]*SubModule
}
// 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).
@ -47,14 +60,41 @@ func (c *Commit) Parent(n int) (*Commit, error) {
}
// ParentCount returns number of parents of the commit.
// 0 if this is the root commit, otherwise 1,2, etc.
// 0 if this is the root commit, otherwise 1,2, etc.
func (c *Commit) ParentCount() int {
return len(c.parents)
}
// GetCommitOfRelPath return the commit of relative path object.
func (c *Commit) GetCommitOfRelPath(relpath string) (*Commit, error) {
return c.repo.getCommitOfRelPath(c.ID, relpath)
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
}
// 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.
@ -82,19 +122,102 @@ func CommitChanges(repoPath, message string, author *Signature) error {
return err
}
// CommitsCount returns number of total commits of until given revision.
func CommitsCount(repoPath, revision string) (int64, error) {
func commitsCount(repoPath, revision, relpath string) (int64, error) {
var cmd *Command
isFallback := false
if version.Compare(gitVersion, "1.8.0", "<") {
stdout, err := NewCommand("log", "--pretty=format:''", revision).RunInDir(repoPath)
if err != nil {
return 0, err
}
return int64(len(strings.Split(stdout, "\n"))), nil
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 := NewCommand("rev-list", "--count", revision).RunInDir(repoPath)
stdout, err := cmd.RunInDir(repoPath)
if err != nil {
return 0, err
}
if isFallback {
return int64(len(strings.Split(stdout, "\n"))), 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) CommitsByRange(page int) (*list.List, error) {
return c.repo.commitsByRange(c.ID, page)
}
func (c *Commit) CommitsBefore() (*list.List, error) {
return c.repo.getCommitsBefore(c.ID)
}
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) 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
}

29
commit_archive.go Normal file
Просмотреть файл

@ -0,0 +1,29 @@
// 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 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)
}
_, err := NewCommand("archive", "--format="+format, "-o", path, c.ID.String()).RunInDir(c.repo.Path)
return err
}

Просмотреть файл

@ -35,3 +35,16 @@ func IsErrNotExist(err error) bool {
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)
}

10
git.go
Просмотреть файл

@ -10,6 +10,12 @@ import (
"time"
)
const _VERSION = "0.1.0"
func Version() string {
return _VERSION
}
var (
// Debug enables verbose logging on everything.
// This should be false in case Gogs starts in SSH mode.
@ -33,7 +39,7 @@ func log(format string, args ...interface{}) {
var gitVersion string
// Version returns current Git version from shell.
func Version() (string, error) {
func BinVersion() (string, error) {
if len(gitVersion) > 0 {
return gitVersion, nil
}
@ -53,7 +59,7 @@ func Version() (string, error) {
}
func init() {
Version()
BinVersion()
}
// Fsck verifies the connectivity and validity of the objects in the database

127
hook.go Normal file
Просмотреть файл

@ -0,0 +1,127 @@
// 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"
)
// 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 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
}
const (
HOOK_PATH_UPDATE = "hooks/update"
)
// SetUpdateHook writes given content to update hook of the reposiotry.
func SetUpdateHook(repoPath, content string) error {
log("Setting update hook: %s", repoPath)
hookPath := path.Join(repoPath, HOOK_PATH_UPDATE)
os.MkdirAll(path.Dir(hookPath), os.ModePerm)
return ioutil.WriteFile(hookPath, []byte(content), 0777)
}

Просмотреть файл

@ -19,6 +19,7 @@ type Repository struct {
Path string
commitCache map[sha1]*Commit
tagCache map[sha1]*Tag
}
const _PRETTY_LOG_FORMAT = `--pretty=format:%H`

Просмотреть файл

@ -7,10 +7,27 @@ 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
@ -35,8 +52,49 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
}, nil
}
// IsBranchExist returns true if given branch exists in repository.
func IsBranchExist(repoPath, branch string) bool {
_, err := NewCommand("show-ref", "--verify", BRANCH_PREFIX+branch).RunInDir(repoPath)
return err == 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", "refs/heads/"+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], "refs/heads/")
}
return branches, nil
}
// 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
}

Просмотреть файл

@ -6,21 +6,29 @@ package git
import (
"bytes"
"container/list"
"fmt"
"strconv"
"strings"
)
// getCommitIDOfRef returns the last commit ID string of given reference (branch or tag).
func (repo *Repository) getCommitIDOfRef(refName string) (string, error) {
stdout, err := NewCommand("show-ref", "--verify", refName).RunInDir(repo.Path)
// 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 {
return "", err
}
return strings.Split(stdout, " ")[0], nil
}
// GetCommitIDOfBranch returns last commit ID string of given branch.
func (repo *Repository) GetCommitIDOfBranch(branch string) (string, error) {
return repo.getCommitIDOfRef(BRANCH_PREFIX + branch)
// 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)
}
// parseCommitData parses commit information from the (uncompressed) raw
@ -113,16 +121,24 @@ func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
return repo.getCommit(id)
}
// GetCommitOfBranch returns the last commit of given branch.
func (repo *Repository) GetCommitOfBranch(branch string) (*Commit, error) {
commitID, err := repo.GetCommitIDOfBranch(branch)
// 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)
}
func (repo *Repository) getCommitOfRelPath(id sha1, relpath string) (*Commit, error) {
func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
commitID, err := repo.GetTagCommitID(name)
if err != nil {
return nil, err
}
return repo.GetCommit(commitID)
}
func (repo *Repository) getCommitByPathWithID(id sha1, relpath string) (*Commit, error) {
stdout, err := NewCommand("log", "-1", _PRETTY_LOG_FORMAT, id.String(), "--", relpath).RunInDir(repo.Path)
if err != nil {
return nil, err
@ -149,3 +165,142 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
}
return commits.Front().Value.(*Commit), nil
}
var CommitsRangeSize = 50
func (repo *Repository) commitsByRange(id sha1, page int) (*list.List, error) {
stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*CommitsRangeSize),
"--max-count="+strconv.Itoa(CommitsRangeSize), _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
return repo.parsePrettyFormatLogToList(stdout)
}
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) FileCommitsCount(revision, file string) (int64, error) {
return commitsCount(repo.Path, revision, file)
}
func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (*list.List, error) {
stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*50),
"--max-count="+strconv.Itoa(CommitsRangeSize), _PRETTY_LOG_FORMAT, "--", file).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
return repo.parsePrettyFormatLogToList(stdout)
}
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
}
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) 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, "")
}
func (repo *Repository) commitsBefore(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(l, pr, id, 0)
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, 0)
}

Просмотреть файл

@ -4,20 +4,10 @@
package git
import (
"io/ioutil"
"os"
"path"
)
const (
HOOK_PATH_UPDATE = "hooks/update"
)
// SetUpdateHook writes given content to update hook of the reposiotry.
func SetUpdateHook(repoPath, content string) error {
log("Setting update hook: %s", repoPath)
hookPath := path.Join(repoPath, HOOK_PATH_UPDATE)
os.MkdirAll(path.Dir(hookPath), os.ModePerm)
return ioutil.WriteFile(hookPath, []byte(content), 0777)
func (repo *Repository) GetHook(name string) (*Hook, error) {
return GetHook(repo.Path, name)
}
func (repo *Repository) Hooks() ([]*Hook, error) {
return ListHooks(repo.Path)
}

69
repo_pull.go Normal file
Просмотреть файл

@ -0,0 +1,69 @@
// 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)
return strings.TrimSpace(stdout), err
}
// 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 := strconv.FormatInt(time.Now().UnixNano(), 10)
if err = repo.AddRemote(tmpRemote, basePath, true); 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)
}
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)
}

108
repo_tag.go Normal file
Просмотреть файл

@ -0,0 +1,108 @@
// 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"
"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) {
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, 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[id] = 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[id] = 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")
return tags[:len(tags)-1], nil
}

Просмотреть файл

@ -15,3 +15,12 @@ func (repo *Repository) getTree(id sha1) (*Tree, error) {
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)
}

30
sha1.go
Просмотреть файл

@ -12,6 +12,36 @@ import (
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)

71
submodule.go Normal file
Просмотреть файл

@ -0,0 +1,71 @@
// 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"
"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,
}
}
// FIXME: remove import of setting
// 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
}

65
tag.go Normal 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
}

Просмотреть файл

@ -4,6 +4,12 @@
package git
import (
"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
@ -35,6 +41,27 @@ 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
}
@ -47,3 +74,33 @@ func (te *TreeEntry) Blob() *Blob {
}
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)
}

Просмотреть файл

@ -8,6 +8,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
)
// isDir returns true if given path is a directory,
@ -30,6 +31,13 @@ func isFile(filePath string) bool {
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
@ -43,3 +51,16 @@ func concatenateError(err error, stderr string) error {
func filepathFromSHA1(rootdir, sha1 string) string {
return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
}
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
}