Merge branch 'dev' of github.com:gogits/gogs into dev

release/v1.15
skyblue 2014-04-12 09:42:15 +08:00
commit f8e97b75fb
16 changed files with 222 additions and 40 deletions

View File

@ -2,7 +2,7 @@
> Thanks [drone](https://github.com/drone/drone) because this guidelines sheet is forked from its [CONTRIBUTING.md](https://github.com/drone/drone/blob/master/CONTRIBUTING.md). > Thanks [drone](https://github.com/drone/drone) because this guidelines sheet is forked from its [CONTRIBUTING.md](https://github.com/drone/drone/blob/master/CONTRIBUTING.md).
**This document is pre^3 release, we're not ready for receiving contribution until v0.5.0 release.** **This document is pre^2 release, we're not ready for receiving contribution until v0.5.0 release.**
Want to hack on Gogs? Awesome! Here are instructions to get you started. They are probably not perfect, please let us know if anything feels wrong or incomplete. Want to hack on Gogs? Awesome! Here are instructions to get you started. They are probably not perfect, please let us know if anything feels wrong or incomplete.

View File

@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language
![Demo](http://gowalker.org/public/gogs_demo.gif) ![Demo](http://gowalker.org/public/gogs_demo.gif)
##### Current version: 0.2.5 Alpha ##### Current version: 0.2.6 Alpha
#### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in April 6, 2014 and will reset multiple times after. Please do NOT put your important data on the site. #### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in April 6, 2014 and will reset multiple times after. Please do NOT put your important data on the site.
@ -43,7 +43,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o
Make sure you install [Prerequirements](https://github.com/gogits/gogs/wiki/Prerequirements) first. Make sure you install [Prerequirements](https://github.com/gogits/gogs/wiki/Prerequirements) first.
There are two ways to install Gogs: There are 3 ways to install Gogs:
- [Install from binary](https://github.com/gogits/gogs/wiki/Install-from-binary): **STRONGLY RECOMMENDED** - [Install from binary](https://github.com/gogits/gogs/wiki/Install-from-binary): **STRONGLY RECOMMENDED**
- [Install from source](https://github.com/gogits/gogs/wiki/Install-from-source) - [Install from source](https://github.com/gogits/gogs/wiki/Install-from-source)

View File

@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。
![Demo](http://gowalker.org/public/gogs_demo.gif) ![Demo](http://gowalker.org/public/gogs_demo.gif)
##### 当前版本0.2.5 Alpha ##### 当前版本0.2.6 Alpha
## 开发目的 ## 开发目的
@ -37,7 +37,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依
在安装 Gogs 之前,您需要先安装 [基本环境](https://github.com/gogits/gogs/wiki/Prerequirements)。 在安装 Gogs 之前,您需要先安装 [基本环境](https://github.com/gogits/gogs/wiki/Prerequirements)。
然后,您可以通过以下种方式来安装 Gogs 然后,您可以通过以下 3 种方式来安装 Gogs
- [二进制安装](https://github.com/gogits/gogs/wiki/Install-from-binary): **强烈推荐** - [二进制安装](https://github.com/gogits/gogs/wiki/Install-from-binary): **强烈推荐**
- [源码安装](https://github.com/gogits/gogs/wiki/Install-from-source) - [源码安装](https://github.com/gogits/gogs/wiki/Install-from-source)

View File

@ -19,7 +19,7 @@ import (
// Test that go1.2 tag above is included in builds. main.go refers to this definition. // Test that go1.2 tag above is included in builds. main.go refers to this definition.
const go12tag = true const go12tag = true
const APP_VER = "0.2.5.0410 Alpha" const APP_VER = "0.2.6.0411 Alpha"
func init() { func init() {
base.AppVer = APP_VER base.AppVer = APP_VER

View File

@ -6,7 +6,9 @@ package models
import ( import (
"bufio" "bufio"
"bytes"
"container/list" "container/list"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -409,3 +411,85 @@ func GetDiff(repoPath, commitid string) (*Diff, error) {
defer rd.Close() defer rd.Close()
return ParsePatch(rd) return ParsePatch(rd)
} }
const prettyLogFormat = `--pretty=format:%H%n%an <%ae> %at%n%s`
func parsePrettyFormatLog(logByts []byte) (*list.List, error) {
l := list.New()
buf := bytes.NewBuffer(logByts)
if buf.Len() == 0 {
return l, nil
}
idx := 0
var commit *git.Commit
for {
line, err := buf.ReadString('\n')
if err != nil && err != io.EOF {
return nil, err
}
line = strings.TrimSpace(line)
// fmt.Println(line)
var parseErr error
switch idx {
case 0: // SHA1.
commit = &git.Commit{}
commit.Oid, parseErr = git.NewOidFromString(line)
case 1: // Signature.
commit.Author, parseErr = git.NewSignatureFromCommitline([]byte(line + " "))
case 2: // Commit message.
commit.CommitMessage = line
l.PushBack(commit)
idx = -1
}
if parseErr != nil {
return nil, parseErr
}
idx++
if err == io.EOF {
break
}
}
return l, nil
}
// SearchCommits searches commits in given branch and keyword of repository.
func SearchCommits(repoPath, branch, keyword string) (*list.List, error) {
stdout, stderr, err := com.ExecCmdDirBytes(repoPath, "git", "log", branch, "-100",
"-i", "--grep="+keyword, prettyLogFormat)
if err != nil {
return nil, err
} else if len(stderr) > 0 {
return nil, errors.New(string(stderr))
}
return parsePrettyFormatLog(stdout)
}
// GetCommitsByRange returns certain number of commits with given page of repository.
func GetCommitsByRange(repoPath, branch string, page int) (*list.List, error) {
stdout, stderr, err := com.ExecCmdDirBytes(repoPath, "git", "log", branch,
"--skip="+base.ToStr((page-1)*50), "--max-count=50", prettyLogFormat)
if err != nil {
return nil, err
} else if len(stderr) > 0 {
return nil, errors.New(string(stderr))
}
return parsePrettyFormatLog(stdout)
}
// GetCommitsCount returns the commits count of given branch of repository.
func GetCommitsCount(repoPath, branch string) (int, error) {
stdout, stderr, err := com.ExecCmdDir(repoPath, "git", "rev-list", "--count", branch)
if err != nil {
return 0, err
} else if len(stderr) > 0 {
return 0, errors.New(stderr)
}
return base.StrTo(strings.TrimSpace(stdout)).Int()
}

View File

@ -32,7 +32,7 @@ var (
func init() { func init() {
tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch), tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch),
new(Action), new(Access), new(Issue), new(Comment), new(Oauth2)) new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow))
} }
func LoadModelsConfig() { func LoadModelsConfig() {

View File

@ -192,12 +192,6 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv
return nil, err return nil, err
} }
c := exec.Command("git", "update-server-info")
c.Dir = repoPath
if err = c.Run(); err != nil {
log.Error("repo.CreateRepository(exec update-server-info): %v", err)
}
if err = NewRepoAction(user, repo); err != nil { if err = NewRepoAction(user, repo); err != nil {
log.Error("repo.CreateRepository(NewRepoAction): %v", err) log.Error("repo.CreateRepository(NewRepoAction): %v", err)
} }
@ -210,6 +204,12 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv
return nil, err return nil, err
} }
c := exec.Command("git", "update-server-info")
c.Dir = repoPath
if err = c.Run(); err != nil {
log.Error("repo.CreateRepository(exec update-server-info): %v", err)
}
return repo, nil return repo, nil
} }

View File

@ -294,6 +294,8 @@ func DeleteUser(user *User) error {
return err return err
} }
// Delete oauth2.
// Delete all feeds. // Delete all feeds.
if _, err = orm.Delete(&Action{UserId: user.Id}); err != nil { if _, err = orm.Delete(&Action{UserId: user.Id}); err != nil {
return err return err

View File

@ -0,0 +1,32 @@
// 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 v1
import (
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/middleware"
)
func SearchCommits(ctx *middleware.Context) {
userName := ctx.Query("username")
repoName := ctx.Query("reponame")
branch := ctx.Query("branch")
keyword := ctx.Query("q")
if len(keyword) == 0 {
ctx.Render.JSON(404, nil)
return
}
commits, err := models.SearchCommits(models.RepoPath(userName, repoName), branch, keyword)
if err != nil {
ctx.Render.JSON(200, map[string]interface{}{"ok": false})
return
}
ctx.Render.JSON(200, map[string]interface{}{
"ok": true,
"commits": commits,
})
}

View File

@ -22,29 +22,53 @@ func Commits(ctx *middleware.Context, params martini.Params) {
brs, err := models.GetBranches(userName, repoName) brs, err := models.GetBranches(userName, repoName)
if err != nil { if err != nil {
ctx.Handle(200, "repo.Commits", err) ctx.Handle(500, "repo.Commits", err)
return return
} else if len(brs) == 0 { } else if len(brs) == 0 {
ctx.Handle(404, "repo.Commits", nil) ctx.Handle(404, "repo.Commits", nil)
return return
} }
repoPath := models.RepoPath(userName, repoName)
commitsCount, err := models.GetCommitsCount(repoPath, branchName)
if err != nil {
ctx.Handle(500, "repo.Commits(GetCommitsCount)", err)
return
}
// Calculate and validate page number.
page, _ := base.StrTo(ctx.Query("p")).Int()
if page < 1 {
page = 1
}
lastPage := page - 1
if lastPage < 0 {
lastPage = 0
}
nextPage := page + 1
if nextPage*50 > commitsCount {
nextPage = 0
}
var commits *list.List var commits *list.List
if models.IsBranchExist(userName, repoName, branchName) { if models.IsBranchExist(userName, repoName, branchName) {
commits, err = models.GetCommitsByBranch(userName, repoName, branchName) // commits, err = models.GetCommitsByBranch(userName, repoName, branchName)
commits, err = models.GetCommitsByRange(repoPath, branchName, page)
} else { } else {
commits, err = models.GetCommitsByCommitId(userName, repoName, branchName) commits, err = models.GetCommitsByCommitId(userName, repoName, branchName)
} }
if err != nil { if err != nil {
ctx.Handle(404, "repo.Commits", err) ctx.Handle(404, "repo.Commits(get commits)", err)
return return
} }
ctx.Data["Username"] = userName ctx.Data["Username"] = userName
ctx.Data["Reponame"] = repoName ctx.Data["Reponame"] = repoName
ctx.Data["CommitCount"] = commits.Len() ctx.Data["CommitCount"] = commitsCount
ctx.Data["Commits"] = commits ctx.Data["Commits"] = commits
ctx.Data["LastPageNum"] = lastPage
ctx.Data["NextPageNum"] = nextPage
ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["IsRepoToolbarCommits"] = true
ctx.HTML(200, "repo/commits") ctx.HTML(200, "repo/commits")
} }
@ -90,3 +114,42 @@ func Diff(ctx *middleware.Context, params martini.Params) {
ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId) ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId)
ctx.HTML(200, "repo/diff") ctx.HTML(200, "repo/diff")
} }
func SearchCommits(ctx *middleware.Context, params martini.Params) {
keyword := ctx.Query("q")
if len(keyword) == 0 {
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName)
return
}
userName := params["username"]
repoName := params["reponame"]
branchName := params["branchname"]
brs, err := models.GetBranches(userName, repoName)
if err != nil {
ctx.Handle(500, "repo.SearchCommits(GetBranches)", err)
return
} else if len(brs) == 0 {
ctx.Handle(404, "repo.SearchCommits(GetBranches)", nil)
return
}
var commits *list.List
if !models.IsBranchExist(userName, repoName, branchName) {
ctx.Handle(404, "repo.SearchCommits(IsBranchExist)", err)
return
} else if commits, err = models.SearchCommits(models.RepoPath(userName, repoName), branchName, keyword); err != nil {
ctx.Handle(500, "repo.SearchCommits(SearchCommits)", err)
return
}
ctx.Data["Keyword"] = keyword
ctx.Data["Username"] = userName
ctx.Data["Reponame"] = repoName
ctx.Data["CommitCount"] = commits.Len()
ctx.Data["Commits"] = commits
ctx.Data["IsSearchPage"] = true
ctx.Data["IsRepoToolbarCommits"] = true
ctx.HTML(200, "repo/commits")
}

View File

@ -425,3 +425,7 @@ func Action(ctx *middleware.Context, params martini.Params) {
"ok": true, "ok": true,
}) })
} }
func Import(ctx *middleware.Context, params martini.Params) {
ctx.ResponseWriter.Write([]byte("not done yet"))
}

View File

@ -4,19 +4,19 @@
<a id="nav-logo" class="nav-item pull-left{{if .PageIsHome}} active{{end}}" href="/"><img src="/img/favicon.png" alt="Gogs Logo" id="logo"></a> <a id="nav-logo" class="nav-item pull-left{{if .PageIsHome}} active{{end}}" href="/"><img src="/img/favicon.png" alt="Gogs Logo" id="logo"></a>
<a class="nav-item pull-left{{if .PageIsUserDashboard}} active{{end}}" href="/">Dashboard</a> <a class="nav-item pull-left{{if .PageIsUserDashboard}} active{{end}}" href="/">Dashboard</a>
<a class="nav-item pull-left{{if .PageIsHelp}} active{{end}}" href="https://github.com/gogits/gogs/wiki">Help</a>{{if .IsSigned}} <a class="nav-item pull-left{{if .PageIsHelp}} active{{end}}" href="https://github.com/gogits/gogs/wiki">Help</a>{{if .IsSigned}}
{{if .Repository}}<form class="nav-item pull-left{{if .PageIsNewRepo}} active{{end}}" id="nav-search-form"> <form class="nav-item pull-left{{if .PageIsNewRepo}} active{{end}}" id="nav-search-form">
<div class="input-group"> <div class="input-group">
<div class="input-group-btn"> <div class="input-group-btn">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown">All Repositories <span class="caret"></span></button> <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown">{{if .Repository}}This Repository{{else}}All Repositories{{end}} <span class="caret"></span></button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{{if .Repository}}<li><a href="#">This Repository</a></li>
<li class="divider"></li>{{end}}
<li><a href="#">All Repositories</a></li> <li><a href="#">All Repositories</a></li>
<li class="divider"></li>
<li><a href="#">This Repository</a></li>
</ul> </ul>
</div> </div>
<input type="search" class="form-control input-sm" name="q" placeholder="search code, commits and issues"/> <input type="search" class="form-control input-sm" name="q" placeholder="search code, commits and issues"/>
</div> </div>
</form>{{end}} </form>
<a id="nav-out" class="nav-item navbar-right navbar-btn btn btn-danger" href="/user/logout/"><i class="fa fa-power-off fa-lg"></i></a> <a id="nav-out" class="nav-item navbar-right navbar-btn btn btn-danger" href="/user/logout/"><i class="fa fa-power-off fa-lg"></i></a>
<a id="nav-avatar" class="nav-item navbar-right{{if .PageIsUserProfile}} active{{end}}" href="{{.SignedUser.HomeLink}}" data-toggle="tooltip" data-placement="bottom" title="{{.SignedUserName}}"> <a id="nav-avatar" class="nav-item navbar-right{{if .PageIsUserProfile}} active{{end}}" href="{{.SignedUser.HomeLink}}" data-toggle="tooltip" data-placement="bottom" title="{{.SignedUserName}}">
<img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username"/> <img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username"/>
@ -29,7 +29,7 @@
<ul class="list-unstyled"> <ul class="list-unstyled">
<li><a href="/repo/create"><i class="fa fa-book"></i>Repository</a></li> <li><a href="/repo/create"><i class="fa fa-book"></i>Repository</a></li>
<li><a href="/repo/mirror"><i class="fa fa-clipboard"></i>Mirror</a></li> <li><a href="/repo/mirror"><i class="fa fa-clipboard"></i>Mirror</a></li>
<li><a href="#"><i class="fa fa-users"></i>Organization</a></li> <!-- <li><a href="#"><i class="fa fa-users"></i>Organization</a></li> -->
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -6,11 +6,11 @@
<div id="commits"> <div id="commits">
<div class="panel panel-default commit-box info-box"> <div class="panel panel-default commit-box info-box">
<div class="panel-heading info-head"> <div class="panel-heading info-head">
<form class="search pull-right col-md-3" action="" method="post" id="commits-search-form"> <form class="search pull-right col-md-3" action="{{.RepoLink}}/commits/{{.BranchName}}/search" method="get" id="commits-search-form">
<div class="input-group"> <div class="input-group">
<input class="form-control search" type="search" placeholder="search commit" name="q"/> <input class="form-control search" type="search" placeholder="search commit" name="q" value="{{.Keyword}}" />
<div class="input-group-btn"> <div class="input-group-btn">
<button type="button" class="btn btn-default">Find</button> <button type="submit" class="btn btn-default">Find</button>
</div> </div>
</div> </div>
</form> </form>
@ -20,7 +20,7 @@
<thead> <thead>
<tr> <tr>
<th class="author">Author</th> <th class="author">Author</th>
<th class="sha">Commit</th> <th class="sha">SHA1</th>
<th class="message">Message</th> <th class="message">Message</th>
<th class="date">Date</th> <th class="date">Date</th>
</tr> </tr>
@ -31,24 +31,19 @@
{{$r := List .Commits}} {{$r := List .Commits}}
{{range $r}} {{range $r}}
<tr> <tr>
<td class="author"><img class="avatar" src="{{AvatarLink .Committer.Email}}" alt=""/><a href="/user/{{.Committer.Name}}">{{.Committer.Name}}</a></td> <td class="author"><img class="avatar" src="{{AvatarLink .Author.Email}}" alt=""/><a href="/user/{{.Author.Name}}">{{.Author.Name}}</a></td>
<td class="sha"><a class="label label-success" href="/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td> <td class="sha"><a class="label label-success" href="/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td>
<td class="message">{{.Message}} </td> <td class="message">{{.Message}} </td>
<td class="date">{{TimeSince .Committer.When}}</td> <td class="date">{{TimeSince .Author.When}}</td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
</div> </div>
<ul class="pagination" id="commits-pager"> {{if not .IsSearchPage}}<ul class="pagination" id="commits-pager">
<li><a href="#">&laquo;</a></li> {{if .LastPageNum}}<li><a href="{{.RepoLink}}/commits/{{.BranchName}}?p={{.LastPageNum}}">&laquo; Newer</a></li>{{end}}
<li><a href="#">1</a></li> {{if .NextPageNum}}<li><a href="{{.RepoLink}}/commits/{{.BranchName}}?p={{.NextPageNum}}">&raquo; Older</a></li>{{end}}
<li><a href="#">2</a></li> </ul>{{end}}
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">&raquo;</a></li>
</ul>
</div> </div>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}

View File

@ -16,7 +16,7 @@
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default" type="button">URL</button> <button class="btn btn-default" type="button">URL</button>
</span> </span>
<input name="passwd" type="password" class="form-control" placeholder="Type existing repository address" required="required"> <input name="import_url" class="form-control" placeholder="Type existing repository address" required="required">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="submit" class="btn btn-default" type="button">Clone</button> <button type="submit" class="btn btn-default" type="button">Clone</button>
</span> </span>

View File

@ -35,7 +35,7 @@
<ul class="list-unstyled"> <ul class="list-unstyled">
<li><a href="/repo/create"><i class="fa fa-book"></i>Repository</a></li> <li><a href="/repo/create"><i class="fa fa-book"></i>Repository</a></li>
<li><a href="/repo/mirror"><i class="fa fa-clipboard"></i>Mirror</a></li> <li><a href="/repo/mirror"><i class="fa fa-clipboard"></i>Mirror</a></li>
<li><a href="#"><i class="fa fa-users"></i>Organization</a></li> <!-- <li><a href="#"><i class="fa fa-users"></i>Organization</a></li> -->
</ul> </ul>
</div> </div>
</div> </div>

2
web.go
View File

@ -152,6 +152,7 @@ func runWeb(*cli.Context) {
r.Post("/issues/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost) r.Post("/issues/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
r.Post("/issues/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue) r.Post("/issues/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
r.Post("/comment/:action", repo.Comment) r.Post("/comment/:action", repo.Comment)
r.Post("/import", repo.Import)
}, reqSignIn, middleware.RepoAssignment(true)) }, reqSignIn, middleware.RepoAssignment(true))
m.Group("/:username/:reponame", func(r martini.Router) { m.Group("/:username/:reponame", func(r martini.Router) {
@ -168,6 +169,7 @@ func runWeb(*cli.Context) {
r.Get("/src/:branchname/**", repo.Single) r.Get("/src/:branchname/**", repo.Single)
r.Get("/raw/:branchname/**", repo.SingleDownload) r.Get("/raw/:branchname/**", repo.SingleDownload)
r.Get("/commits/:branchname", repo.Commits) r.Get("/commits/:branchname", repo.Commits)
r.Get("/commits/:branchname/search", repo.SearchCommits)
r.Get("/commit/:branchname", repo.Diff) r.Get("/commit/:branchname", repo.Diff)
r.Get("/commit/:branchname/**", repo.Diff) r.Get("/commit/:branchname/**", repo.Diff)
}, ignSignIn, middleware.RepoAssignment(true, true)) }, ignSignIn, middleware.RepoAssignment(true, true))