| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 | 
							- // 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 models
 
- import (
 
- 	"bufio"
 
- 	"container/list"
 
- 	"fmt"
 
- 	"io"
 
- 	"os"
 
- 	"os/exec"
 
- 	"path"
 
- 	"strings"
 
- 	"github.com/gogits/git"
 
- )
 
- // RepoFile represents a file object in git repository.
 
- type RepoFile struct {
 
- 	*git.TreeEntry
 
- 	Path   string
 
- 	Size   int64
 
- 	Repo   *git.Repository
 
- 	Commit *git.Commit
 
- }
 
- // LookupBlob returns the content of an object.
 
- func (file *RepoFile) LookupBlob() (*git.Blob, error) {
 
- 	if file.Repo == nil {
 
- 		return nil, ErrRepoFileNotLoaded
 
- 	}
 
- 	return file.Repo.LookupBlob(file.Id)
 
- }
 
- // GetBranches returns all branches of given repository.
 
- func GetBranches(userName, reposName string) ([]string, error) {
 
- 	repo, err := git.OpenRepository(RepoPath(userName, reposName))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	refs, err := repo.AllReferences()
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	brs := make([]string, len(refs))
 
- 	for i, ref := range refs {
 
- 		brs[i] = ref.Name
 
- 	}
 
- 	return brs, nil
 
- }
 
- func GetTargetFile(userName, reposName, branchName, commitId, rpath string) (*RepoFile, error) {
 
- 	repo, err := git.OpenRepository(RepoPath(userName, reposName))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	commit, err := repo.GetCommit(branchName, commitId)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	parts := strings.Split(path.Clean(rpath), "/")
 
- 	var entry *git.TreeEntry
 
- 	tree := commit.Tree
 
- 	for i, part := range parts {
 
- 		if i == len(parts)-1 {
 
- 			entry = tree.EntryByName(part)
 
- 			if entry == nil {
 
- 				return nil, ErrRepoFileNotExist
 
- 			}
 
- 		} else {
 
- 			tree, err = repo.SubTree(tree, part)
 
- 			if err != nil {
 
- 				return nil, err
 
- 			}
 
- 		}
 
- 	}
 
- 	size, err := repo.ObjectSize(entry.Id)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	repoFile := &RepoFile{
 
- 		entry,
 
- 		rpath,
 
- 		size,
 
- 		repo,
 
- 		commit,
 
- 	}
 
- 	return repoFile, nil
 
- }
 
- // GetReposFiles returns a list of file object in given directory of repository.
 
- func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]*RepoFile, error) {
 
- 	repo, err := git.OpenRepository(RepoPath(userName, reposName))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	commit, err := repo.GetCommit(branchName, commitId)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	var repodirs []*RepoFile
 
- 	var repofiles []*RepoFile
 
- 	commit.Tree.Walk(func(dirname string, entry *git.TreeEntry) int {
 
- 		if dirname == rpath {
 
- 			// TODO: size get method shoule be improved
 
- 			size, err := repo.ObjectSize(entry.Id)
 
- 			if err != nil {
 
- 				return 0
 
- 			}
 
- 			var cm = commit
 
- 			var i int
 
- 			for {
 
- 				i = i + 1
 
- 				//fmt.Println(".....", i, cm.Id(), cm.ParentCount())
 
- 				if cm.ParentCount() == 0 {
 
- 					break
 
- 				} else if cm.ParentCount() == 1 {
 
- 					pt, _ := repo.SubTree(cm.Parent(0).Tree, dirname)
 
- 					if pt == nil {
 
- 						break
 
- 					}
 
- 					pEntry := pt.EntryByName(entry.Name)
 
- 					if pEntry == nil || !pEntry.Id.Equal(entry.Id) {
 
- 						break
 
- 					} else {
 
- 						cm = cm.Parent(0)
 
- 					}
 
- 				} else {
 
- 					var emptyCnt = 0
 
- 					var sameIdcnt = 0
 
- 					var lastSameCm *git.Commit
 
- 					//fmt.Println(".....", cm.ParentCount())
 
- 					for i := 0; i < cm.ParentCount(); i++ {
 
- 						//fmt.Println("parent", i, cm.Parent(i).Id())
 
- 						p := cm.Parent(i)
 
- 						pt, _ := repo.SubTree(p.Tree, dirname)
 
- 						var pEntry *git.TreeEntry
 
- 						if pt != nil {
 
- 							pEntry = pt.EntryByName(entry.Name)
 
- 						}
 
- 						//fmt.Println("pEntry", pEntry)
 
- 						if pEntry == nil {
 
- 							emptyCnt = emptyCnt + 1
 
- 							if emptyCnt+sameIdcnt == cm.ParentCount() {
 
- 								if lastSameCm == nil {
 
- 									goto loop
 
- 								} else {
 
- 									cm = lastSameCm
 
- 									break
 
- 								}
 
- 							}
 
- 						} else {
 
- 							//fmt.Println(i, "pEntry", pEntry.Id, "entry", entry.Id)
 
- 							if !pEntry.Id.Equal(entry.Id) {
 
- 								goto loop
 
- 							} else {
 
- 								lastSameCm = cm.Parent(i)
 
- 								sameIdcnt = sameIdcnt + 1
 
- 								if emptyCnt+sameIdcnt == cm.ParentCount() {
 
- 									// TODO: now follow the first parent commit?
 
- 									cm = lastSameCm
 
- 									//fmt.Println("sameId...")
 
- 									break
 
- 								}
 
- 							}
 
- 						}
 
- 					}
 
- 				}
 
- 			}
 
- 		loop:
 
- 			rp := &RepoFile{
 
- 				entry,
 
- 				path.Join(dirname, entry.Name),
 
- 				size,
 
- 				repo,
 
- 				cm,
 
- 			}
 
- 			if entry.IsFile() {
 
- 				repofiles = append(repofiles, rp)
 
- 			} else if entry.IsDir() {
 
- 				repodirs = append(repodirs, rp)
 
- 			}
 
- 		}
 
- 		return 0
 
- 	})
 
- 	return append(repodirs, repofiles...), nil
 
- }
 
- func GetCommit(userName, repoName, branchname, commitid string) (*git.Commit, error) {
 
- 	repo, err := git.OpenRepository(RepoPath(userName, repoName))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	return repo.GetCommit(branchname, commitid)
 
- }
 
- // GetCommits returns all commits of given branch of repository.
 
- func GetCommits(userName, reposName, branchname string) (*list.List, error) {
 
- 	repo, err := git.OpenRepository(RepoPath(userName, reposName))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	r, err := repo.LookupReference(fmt.Sprintf("refs/heads/%s", branchname))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	return r.AllCommits()
 
- }
 
- // Diff line types.
 
- const (
 
- 	DIFF_LINE_PLAIN = iota + 1
 
- 	DIFF_LINE_ADD
 
- 	DIFF_LINE_DEL
 
- 	DIFF_LINE_SECTION
 
- )
 
- const (
 
- 	DIFF_FILE_ADD = iota + 1
 
- 	DIFF_FILE_CHANGE
 
- 	DIFF_FILE_DEL
 
- )
 
- type DiffLine struct {
 
- 	LeftIdx  int
 
- 	RightIdx int
 
- 	Type     int
 
- 	Content  string
 
- }
 
- type DiffSection struct {
 
- 	Name  string
 
- 	Lines []*DiffLine
 
- }
 
- type DiffFile struct {
 
- 	Name               string
 
- 	Addition, Deletion int
 
- 	Type               int
 
- 	Sections           []*DiffSection
 
- }
 
- type Diff struct {
 
- 	TotalAddition, TotalDeletion int
 
- 	Files                        []*DiffFile
 
- }
 
- func (diff *Diff) NumFiles() int {
 
- 	return len(diff.Files)
 
- }
 
- const DIFF_HEAD = "diff --git "
 
- func ParsePatch(reader io.Reader) (*Diff, error) {
 
- 	scanner := bufio.NewScanner(reader)
 
- 	var totalAdd, totalDel int
 
- 	var curFile *DiffFile
 
- 	curSection := &DiffSection{
 
- 		Lines: make([]*DiffLine, 0, 10),
 
- 	}
 
- 	//var leftLine, rightLine int
 
- 	diff := &Diff{Files: make([]*DiffFile, 0)}
 
- 	var i int
 
- 	for scanner.Scan() {
 
- 		line := scanner.Text()
 
- 		fmt.Println(i, line)
 
- 		i = i + 1
 
- 		if line == "" {
 
- 			continue
 
- 		}
 
- 		if line[0] == ' ' {
 
- 			diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line}
 
- 			curSection.Lines = append(curSection.Lines, diffLine)
 
- 			continue
 
- 		} else if line[0] == '@' {
 
- 			curSection = &DiffSection{}
 
- 			curFile.Sections = append(curFile.Sections, curSection)
 
- 			ss := strings.Split(line, "@@")
 
- 			diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: "@@" + ss[len(ss)-2] + "@@"}
 
- 			curSection.Lines = append(curSection.Lines, diffLine)
 
- 			diffLine = &DiffLine{Type: DIFF_LINE_PLAIN, Content: ss[len(ss)-1]}
 
- 			curSection.Lines = append(curSection.Lines, diffLine)
 
- 			continue
 
- 		} else if line[0] == '+' {
 
- 			diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line}
 
- 			curSection.Lines = append(curSection.Lines, diffLine)
 
- 			continue
 
- 		} else if line[0] == '-' {
 
- 			diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line}
 
- 			curSection.Lines = append(curSection.Lines, diffLine)
 
- 			continue
 
- 		}
 
- 		if strings.HasPrefix(line, DIFF_HEAD) {
 
- 			if curFile != nil {
 
- 				curFile.Addition, totalAdd = totalAdd, 0
 
- 				curFile.Deletion, totalDel = totalDel, 0
 
- 				curFile = nil
 
- 			}
 
- 			fs := strings.Split(line[len(DIFF_HEAD):], " ")
 
- 			a := fs[0]
 
- 			curFile = &DiffFile{
 
- 				Name:     a[strings.Index(a, "/")+1:],
 
- 				Type:     DIFF_FILE_CHANGE,
 
- 				Sections: make([]*DiffSection, 0),
 
- 			}
 
- 			diff.Files = append(diff.Files, curFile)
 
- 			scanner.Scan()
 
- 			scanner.Scan()
 
- 			if scanner.Text() == "--- /dev/null" {
 
- 				curFile.Type = DIFF_FILE_ADD
 
- 			}
 
- 			scanner.Scan()
 
- 		}
 
- 	}
 
- 	return diff, nil
 
- }
 
- func GetDiff(repoPath, commitid string) (*Diff, error) {
 
- 	repo, err := git.OpenRepository(repoPath)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	commit, err := repo.GetCommit("", commitid)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	// ????
 
- 	if commit.ParentCount() == 0 {
 
- 		return &Diff{}, err
 
- 	}
 
- 	rd, wr := io.Pipe()
 
- 	go func() {
 
- 		cmd := exec.Command("git", "diff", commitid, commit.Parent(0).Oid.String())
 
- 		cmd.Dir = repoPath
 
- 		cmd.Stdout = wr
 
- 		cmd.Stdin = os.Stdin
 
- 		cmd.Stderr = os.Stderr
 
- 		cmd.Run()
 
- 		//if err != nil {
 
- 		//	return nil, err
 
- 		//}
 
- 		wr.Close()
 
- 	}()
 
- 	defer rd.Close()
 
- 	return ParsePatch(rd)
 
- }
 
- /*func GetDiff(repoPath, commitid string) (*Diff, error) {
 
- 	stdout, _, err := com.ExecCmdDir(repoPath, "git", "show", commitid)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	// Sperate parts by file.
 
- 	startIndex := strings.Index(stdout, "diff --git ") + 12
 
- 	// First part is commit information.
 
- 	// Check if it's a merge.
 
- 	mergeIndex := strings.Index(stdout[:startIndex], "merge")
 
- 	if mergeIndex > -1 {
 
- 		mergeCommit := strings.SplitN(strings.Split(stdout[:startIndex], "\n")[1], "", 3)[2]
 
- 		return GetDiff(repoPath, mergeCommit)
 
- 	}
 
- 	parts := strings.Split(stdout[startIndex:], "diff --git ")
 
- 	diff := &Diff{NumFiles: len(parts)}
 
- 	diff.Files = make([]*DiffFile, 0, diff.NumFiles)
 
- 	for _, part := range parts {
 
- 		infos := strings.SplitN(part, "\n", 6)
 
- 		maxIndex := len(infos) - 1
 
- 		infos[maxIndex] = strings.TrimSuffix(strings.TrimSuffix(infos[maxIndex], "\n"), "\n\\ No newline at end of file")
 
- 		file := &DiffFile{
 
- 			Name:    strings.TrimPrefix(strings.Split(infos[0], " ")[0], "a/"),
 
- 			Content: strings.Split(infos[maxIndex], "\n"),
 
- 		}
 
- 		diff.Files = append(diff.Files, file)
 
- 	}
 
- 	return diff, nil
 
- }*/
 
 
  |