浏览代码

introduce git-shell

Unknwon 8 年之前
父节点
当前提交
c50a3503e6

+ 1 - 0
.gopmfile

@@ -17,6 +17,7 @@ github.com/go-sql-driver/mysql = commit:d512f20
 github.com/go-xorm/core = commit:3e10003353
 github.com/go-xorm/xorm = commit:c643188
 github.com/gogits/chardet = commit:2404f77725
+github.com/gogits/git-shell = 
 github.com/gogits/go-gogs-client = commit:7c02c95
 github.com/issue9/identicon = commit:5a61672
 github.com/klauspost/compress = commit:bbfa9dc

+ 2 - 1
cmd/web.go

@@ -536,7 +536,8 @@ func runWeb(ctx *cli.Context) {
 			m.Get("/?:page", repo.Wiki)
 
 			m.Group("", func() {
-				m.Get("/_new", repo.NewWiki)
+				m.Combo("/_new").Get(repo.NewWiki).
+					Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost)
 				m.Get("/:page/_edit", repo.EditWiki)
 			}, reqSignIn)
 		}, middleware.RepoRef())

+ 1 - 6
models/pull.go

@@ -415,12 +415,7 @@ func (pr *PullRequest) UpdatePatch() (err error) {
 		return fmt.Errorf("GetOwner: %v", err)
 	}
 
-	headRepoPath, err := pr.HeadRepo.RepoPath()
-	if err != nil {
-		return fmt.Errorf("HeadRepo.RepoPath: %v", err)
-	}
-
-	headGitRepo, err := git.OpenRepository(headRepoPath)
+	headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
 	if err != nil {
 		return fmt.Errorf("OpenRepository: %v", err)
 	}

+ 2 - 6
models/release.go

@@ -175,12 +175,8 @@ func DeleteReleaseByID(id int64) error {
 		return fmt.Errorf("GetRepositoryByID: %v", err)
 	}
 
-	repoPath, err := repo.RepoPath()
-	if err != nil {
-		return fmt.Errorf("RepoPath: %v", err)
-	}
-
-	_, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
+	_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
+		fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
 		"git", "tag", "-d", rel.TagName)
 	if err != nil && !strings.Contains(stderr, "not found") {
 		return fmt.Errorf("git tag -d: %v - %s", err, stderr)

+ 43 - 86
models/repo.go

@@ -24,11 +24,14 @@ import (
 	"github.com/Unknwon/cae/zip"
 	"github.com/Unknwon/com"
 	"github.com/go-xorm/xorm"
+	"github.com/mcuadros/go-version"
 	"gopkg.in/ini.v1"
 
+	"github.com/gogits/git-shell"
+
 	"github.com/gogits/gogs/modules/base"
 	"github.com/gogits/gogs/modules/bindata"
-	"github.com/gogits/gogs/modules/git"
+	oldgit "github.com/gogits/gogs/modules/git"
 	"github.com/gogits/gogs/modules/log"
 	"github.com/gogits/gogs/modules/process"
 	"github.com/gogits/gogs/modules/setting"
@@ -95,19 +98,15 @@ func NewRepoContext() {
 	}
 
 	// Check Git version.
-	ver, err := git.GetVersion()
+	gitVer, err := git.Version()
 	if err != nil {
 		log.Fatal(4, "Fail to get Git version: %v", err)
 	}
 
-	reqVer, err := git.ParseVersion("1.7.1")
-	if err != nil {
-		log.Fatal(4, "Fail to parse required Git version: %v", err)
-	}
-	if ver.LessThan(reqVer) {
+	log.Info("Git Version: %s", gitVer)
+	if version.Compare("1.7.1", gitVer, ">") {
 		log.Fatal(4, "Gogs requires Git version greater or equal to 1.7.1")
 	}
-	log.Info("Git Version: %s", ver.String())
 
 	// Git requires setting user.name and user.email in order to commit changes.
 	for configKey, defaultValue := range map[string]string{"user.name": "Gogs", "user.email": "gogs@fake.local"} {
@@ -197,6 +196,25 @@ func (repo *Repository) GetOwner() error {
 	return repo.getOwner(x)
 }
 
+func (repo *Repository) mustOwner(e Engine) *User {
+	if err := repo.getOwner(e); err != nil {
+		return &User{
+			Name:     "error",
+			FullName: err.Error(),
+		}
+	}
+
+	return repo.Owner
+}
+
+// MustOwner always returns a valid *User object to avoid
+// conceptually impossible error handling.
+// It creates a fake object that contains error deftail
+// when error occurs.
+func (repo *Repository) MustOwner() *User {
+	return repo.mustOwner(x)
+}
+
 // GetAssignees returns all users that have write access of repository.
 func (repo *Repository) GetAssignees() (_ []*User, err error) {
 	if err = repo.GetOwner(); err != nil {
@@ -253,30 +271,16 @@ func (repo *Repository) GetBaseRepo() (err error) {
 	return err
 }
 
-func (repo *Repository) repoPath(e Engine) (string, error) {
-	if err := repo.getOwner(e); err != nil {
-		return "", err
-	}
-	return RepoPath(repo.Owner.Name, repo.Name), nil
+func (repo *Repository) repoPath(e Engine) string {
+	return RepoPath(repo.mustOwner(e).Name, repo.Name)
 }
 
-func (repo *Repository) RepoPath() (string, error) {
+func (repo *Repository) RepoPath() string {
 	return repo.repoPath(x)
 }
 
-func (repo *Repository) WikiPath() (string, error) {
-	if err := repo.GetOwner(); err != nil {
-		return "", err
-	}
-
-	return WikiPath(repo.Owner.Name, repo.Name), nil
-}
-
-func (repo *Repository) RepoLink() (string, error) {
-	if err := repo.GetOwner(); err != nil {
-		return "", err
-	}
-	return setting.AppSubUrl + "/" + repo.Owner.Name + "/" + repo.Name, nil
+func (repo *Repository) RepoLink() string {
+	return setting.AppSubUrl + "/" + repo.MustOwner().Name + "/" + repo.Name
 }
 
 func (repo *Repository) HasAccess(u *User) bool {
@@ -315,11 +319,7 @@ func (repo *Repository) LocalCopyPath() string {
 
 // UpdateLocalCopy makes sure the local copy of repository is up-to-date.
 func (repo *Repository) UpdateLocalCopy() error {
-	repoPath, err := repo.RepoPath()
-	if err != nil {
-		return err
-	}
-
+	repoPath := repo.RepoPath()
 	localPath := repo.LocalCopyPath()
 	if !com.IsExist(localPath) {
 		_, stderr, err := process.Exec(
@@ -399,7 +399,7 @@ func (repo *Repository) CloneLink() (cl CloneLink, err error) {
 
 var (
 	reservedNames    = []string{"debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new"}
-	reservedPatterns = []string{"*.git", "*.keys"}
+	reservedPatterns = []string{"*.git", "*.keys", "*.wiki"}
 )
 
 // IsUsableName checks if name is reserved or pattern of name is not allowed.
@@ -569,7 +569,7 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
 	}
 
 	// Check if repository has master branch, if so set it to default branch.
-	gitRepo, err := git.OpenRepository(repoPath)
+	gitRepo, err := oldgit.OpenRepository(repoPath)
 	if err != nil {
 		return repo, fmt.Errorf("open git repository: %v", err)
 	}
@@ -581,7 +581,7 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
 }
 
 // initRepoCommit temporarily changes with work directory.
-func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
+func initRepoCommit(tmpPath string, sig *oldgit.Signature) (err error) {
 	var stderr string
 	if _, stderr, err = process.ExecDir(-1,
 		tmpPath, fmt.Sprintf("initRepoCommit (git add): %s", tmpPath),
@@ -885,11 +885,6 @@ func RepoPath(userName, repoName string) string {
 	return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".git")
 }
 
-// WikiPath returns wiki data path by given user and repository name.
-func WikiPath(userName, repoName string) string {
-	return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".wiki.git")
-}
-
 // TransferOwnership transfers all corresponding setting from old user to new one.
 func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
 	newOwner, err := GetUserByName(newOwnerName)
@@ -1177,10 +1172,7 @@ func DeleteRepository(uid, repoID int64) error {
 	}
 
 	// Remove repository files.
-	repoPath, err := repo.repoPath(sess)
-	if err != nil {
-		return fmt.Errorf("RepoPath: %v", err)
-	}
+	repoPath := repo.repoPath(sess)
 	if err = os.RemoveAll(repoPath); err != nil {
 		desc := fmt.Sprintf("delete repository files[%s]: %v", repoPath, err)
 		log.Warn(desc)
@@ -1328,14 +1320,7 @@ func DeleteRepositoryArchives() error {
 	return x.Where("id > 0").Iterate(new(Repository),
 		func(idx int, bean interface{}) error {
 			repo := bean.(*Repository)
-			repoPath, err := repo.RepoPath()
-			if err != nil {
-				if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepositoryArchives.RepoPath [%d]: %v", repo.ID, err)); err2 != nil {
-					log.Error(4, "CreateRepositoryNotice: %v", err2)
-				}
-				return nil
-			}
-			return os.RemoveAll(filepath.Join(repoPath, "archives"))
+			return os.RemoveAll(filepath.Join(repo.RepoPath(), "archives"))
 		})
 }
 
@@ -1345,15 +1330,7 @@ func DeleteMissingRepositories() error {
 	if err := x.Where("id > 0").Iterate(new(Repository),
 		func(idx int, bean interface{}) error {
 			repo := bean.(*Repository)
-			repoPath, err := repo.RepoPath()
-			if err != nil {
-				if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepositoryArchives.RepoPath [%d]: %v", repo.ID, err)); err2 != nil {
-					log.Error(4, "CreateRepositoryNotice: %v", err2)
-				}
-				return nil
-			}
-
-			if !com.IsDir(repoPath) {
+			if !com.IsDir(repo.RepoPath()) {
 				repos = append(repos, repo)
 			}
 			return nil
@@ -1384,14 +1361,7 @@ func RewriteRepositoryUpdateHook() error {
 	return x.Where("id > 0").Iterate(new(Repository),
 		func(idx int, bean interface{}) error {
 			repo := bean.(*Repository)
-			repoPath, err := repo.RepoPath()
-			if err != nil {
-				if err2 := CreateRepositoryNotice(fmt.Sprintf("RewriteRepositoryUpdateHook[%d]: %v", repo.ID, err)); err2 != nil {
-					log.Error(4, "CreateRepositoryNotice: %v", err2)
-				}
-				return nil
-			}
-			return createUpdateHook(repoPath)
+			return createUpdateHook(repo.RepoPath())
 		})
 }
 
@@ -1458,11 +1428,7 @@ func MirrorUpdate() {
 			return nil
 		}
 
-		repoPath, err := m.Repo.RepoPath()
-		if err != nil {
-			return fmt.Errorf("Repo.RepoPath: %v", err)
-		}
-
+		repoPath := m.Repo.RepoPath()
 		if _, stderr, err := process.ExecDir(10*time.Minute,
 			repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
 			"git", "remote", "update", "--prune"); err != nil {
@@ -1502,12 +1468,8 @@ func GitFsck() {
 	if err := x.Where("id>0").Iterate(new(Repository),
 		func(idx int, bean interface{}) error {
 			repo := bean.(*Repository)
-			repoPath, err := repo.RepoPath()
-			if err != nil {
-				return fmt.Errorf("RepoPath: %v", err)
-			}
-
-			_, _, err = process.ExecDir(-1, repoPath, "Repository health check", "git", args...)
+			repoPath := repo.RepoPath()
+			_, _, err := process.ExecDir(-1, repoPath, "Repository health check", "git", args...)
 			if err != nil {
 				desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath)
 				log.Warn(desc)
@@ -1925,15 +1887,10 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
 		return nil, err
 	}
 
-	oldRepoPath, err := oldRepo.RepoPath()
-	if err != nil {
-		return nil, fmt.Errorf("get old repository path: %v", err)
-	}
-
 	repoPath := RepoPath(u.Name, repo.Name)
 	_, stderr, err := process.ExecTimeout(10*time.Minute,
 		fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name),
-		"git", "clone", "--bare", oldRepoPath, repoPath)
+		"git", "clone", "--bare", oldRepo.RepoPath(), repoPath)
 	if err != nil {
 		return nil, fmt.Errorf("git clone: %v", stderr)
 	}

+ 56 - 0
models/wiki.go

@@ -0,0 +1,56 @@
+// 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 models
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/Unknwon/com"
+
+	"github.com/gogits/git-shell"
+)
+
+// ToWikiPageName formats a string to corresponding wiki URL name.
+func ToWikiPageName(name string) string {
+	return strings.Replace(name, " ", "-", -1)
+}
+
+// WikiPath returns wiki data path by given user and repository name.
+func WikiPath(userName, repoName string) string {
+	return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".wiki.git")
+}
+
+func (repo *Repository) WikiPath() string {
+	return WikiPath(repo.MustOwner().Name, repo.Name)
+}
+
+// HasWiki returns true if repository has wiki.
+func (repo *Repository) HasWiki() bool {
+	return com.IsDir(repo.WikiPath())
+}
+
+// InitWiki initializes a wiki for repository,
+// it does nothing when repository already has wiki.
+func (repo *Repository) InitWiki() error {
+	if repo.HasWiki() {
+		return nil
+	}
+
+	if err := git.InitRepository(repo.WikiPath(), true); err != nil {
+		return fmt.Errorf("InitRepository: %v", err)
+	}
+	return nil
+}
+
+// AddWikiPage adds new page to repository wiki.
+func (repo *Repository) AddWikiPage(title, content, message string) (err error) {
+	if err = repo.InitWiki(); err != nil {
+		return fmt.Errorf("InitWiki: %v", err)
+	}
+
+	return nil
+}

+ 18 - 0
modules/auth/repo_form.go

@@ -237,3 +237,21 @@ type EditReleaseForm struct {
 func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 	return validate(errs, ctx.Data, f, ctx.Locale)
 }
+
+//  __      __.__ __   .__
+// /  \    /  \__|  | _|__|
+// \   \/\/   /  |  |/ /  |
+//  \        /|  |    <|  |
+//   \__/\  / |__|__|_ \__|
+//        \/          \/
+
+type NewWikiForm struct {
+	Title   string `binding:"Required"`
+	Content string
+	Message string
+}
+
+// FIXME: use code generation to generate this method.
+func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
+	return validate(errs, ctx.Data, f, ctx.Locale)
+}

+ 1 - 5
modules/middleware/repo.go

@@ -311,11 +311,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
 			return
 		}
 		ctx.Repo.GitRepo = gitRepo
-		ctx.Repo.RepoLink, err = repo.RepoLink()
-		if err != nil {
-			ctx.Handle(500, "RepoLink", err)
-			return
-		}
+		ctx.Repo.RepoLink = repo.RepoLink()
 		ctx.Data["RepoLink"] = ctx.Repo.RepoLink
 		ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
 

+ 2 - 1
modules/process/manager.go

@@ -113,7 +113,8 @@ func Remove(pid int64) {
 func Kill(pid int64) error {
 	for i, proc := range Processes {
 		if proc.Pid == pid {
-			if proc.Cmd.Process != nil && proc.Cmd.ProcessState != nil && !proc.Cmd.ProcessState.Exited() {
+			if proc.Cmd != nil && proc.Cmd.Process != nil &&
+				proc.Cmd.ProcessState != nil && !proc.Cmd.ProcessState.Exited() {
 				if err := proc.Cmd.Process.Kill(); err != nil {
 					return fmt.Errorf("fail to kill process(%d/%s): %v", proc.Pid, proc.Description, err)
 				}

+ 3 - 0
routers/install.go

@@ -17,6 +17,8 @@ import (
 	"gopkg.in/ini.v1"
 	"gopkg.in/macaron.v1"
 
+	"github.com/gogits/git-shell"
+
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/models/cron"
 	"github.com/gogits/gogs/modules/auth"
@@ -39,6 +41,7 @@ func checkRunMode() {
 		macaron.Env = macaron.PROD
 		macaron.ColorLog = false
 		setting.ProdMode = true
+		git.Debug = false
 	}
 	log.Info("Run Mode: %s", strings.Title(macaron.Env))
 }

+ 1 - 1
routers/repo/http.go

@@ -31,7 +31,7 @@ import (
 func authRequired(ctx *middleware.Context) {
 	ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=\".\"")
 	ctx.Data["ErrorMsg"] = "no basic auth and digit auth"
-	ctx.HTML(401, base.TplName("status/401"))
+	ctx.Error(401)
 }
 
 func HTTP(ctx *middleware.Context) {

+ 2 - 12
routers/repo/pull.go

@@ -210,13 +210,7 @@ func PrepareViewPullInfo(ctx *middleware.Context, pull *models.Issue) *git.PullR
 	}
 
 	if pull.HeadRepo != nil {
-		headRepoPath, err := pull.HeadRepo.RepoPath()
-		if err != nil {
-			ctx.Handle(500, "HeadRepo.RepoPath", err)
-			return nil
-		}
-
-		headGitRepo, err = git.OpenRepository(headRepoPath)
+		headGitRepo, err = git.OpenRepository(pull.HeadRepo.RepoPath())
 		if err != nil {
 			ctx.Handle(500, "OpenRepository", err)
 			return nil
@@ -496,11 +490,7 @@ func PrepareCompareDiff(
 	)
 
 	// Get diff information.
-	ctx.Data["CommitRepoLink"], err = headRepo.RepoLink()
-	if err != nil {
-		ctx.Handle(500, "RepoLink", err)
-		return false
-	}
+	ctx.Data["CommitRepoLink"] = headRepo.RepoLink()
 
 	headCommitID, err := headGitRepo.GetCommitIdOfBranch(headBranch)
 	if err != nil {

+ 21 - 6
routers/repo/wiki.go

@@ -5,9 +5,8 @@
 package repo
 
 import (
-	"github.com/Unknwon/com"
-
 	"github.com/gogits/gogs/models"
+	"github.com/gogits/gogs/modules/auth"
 	"github.com/gogits/gogs/modules/base"
 	"github.com/gogits/gogs/modules/middleware"
 )
@@ -22,8 +21,7 @@ func Wiki(ctx *middleware.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.wiki")
 	ctx.Data["PageIsWiki"] = true
 
-	wikiPath := models.WikiPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
-	if !com.IsDir(wikiPath) {
+	if !ctx.Repo.Repository.HasWiki() {
 		ctx.HTML(200, WIKI_START)
 		return
 	}
@@ -36,14 +34,31 @@ func NewWiki(ctx *middleware.Context) {
 	ctx.Data["PageIsWiki"] = true
 	ctx.Data["RequireSimpleMDE"] = true
 
-	wikiPath := models.WikiPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
-	if !com.IsDir(wikiPath) {
+	if !ctx.Repo.Repository.HasWiki() {
 		ctx.Data["title"] = "Home"
 	}
 
 	ctx.HTML(200, WIKI_NEW)
 }
 
+func NewWikiPost(ctx *middleware.Context, form auth.NewWikiForm) {
+	ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
+	ctx.Data["PageIsWiki"] = true
+	ctx.Data["RequireSimpleMDE"] = true
+
+	if ctx.HasError() {
+		ctx.HTML(200, WIKI_NEW)
+		return
+	}
+
+	if err := ctx.Repo.Repository.AddWikiPage(form.Title, form.Content, form.Message); err != nil {
+		ctx.Handle(500, "AddWikiPage", err)
+		return
+	}
+
+	ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + models.ToWikiPageName(form.Title))
+}
+
 func EditWiki(ctx *middleware.Context) {
 	ctx.PlainText(200, []byte(ctx.Params(":page")))
 }

+ 2 - 1
templates/repo/wiki/new.tmpl

@@ -7,8 +7,9 @@
       {{.i18n.Tr "repo.wiki.new_page"}}
     </div>
     <form class="ui form" action="{{.Link}}" method="post">
+      {{.CsrfTokenHtml}}
       <div class="field">
-        <input name="title" value="{{.title}}" autofocus>
+        <input name="title" value="{{.title}}" autofocus required>
       </div>
       <div class="field">
         <textarea id="edit-area" name="content" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "repo.wiki.welcome"}}</textarea>

+ 0 - 5
templates/status/401.tmpl

@@ -1,5 +0,0 @@
-{{template "base/head" .}}
-<div class="ui container center">
-	401 Unauthorized
-</div>
-{{template "base/footer" .}}

+ 0 - 5
templates/status/403.tmpl

@@ -1,5 +0,0 @@
-{{template "base/head" .}}
-<div class="ui container center">
-	403 Forbidden
-</div>
-{{template "base/footer" .}}