summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUnknwon <joe2010xtmf@163.com>2014-11-05 23:30:04 -0500
committerUnknwon <joe2010xtmf@163.com>2014-11-05 23:30:04 -0500
commit4e7eb5be9d3e9c9ba7238769d2b5f2f471d51b67 (patch)
treee563d92084e488d97839e7e227e3e7dc6662fad9
parentb37519235242b17e47c71dec53ad61a6ca4bedc1 (diff)
downloadgitea-4e7eb5be9d3e9c9ba7238769d2b5f2f471d51b67.tar.gz
gitea-4e7eb5be9d3e9c9ba7238769d2b5f2f471d51b67.zip
Work on #5 fork and fix #608
-rw-r--r--LICENSE40
-rw-r--r--README.md4
-rw-r--r--README_ZH.md4
-rw-r--r--cmd/web.go6
-rw-r--r--conf/locale/locale_en-US.ini4
-rw-r--r--gogs.go2
-rw-r--r--models/repo.go10
-rw-r--r--routers/repo/repo.go179
-rw-r--r--templates/.VERSION2
-rw-r--r--templates/repo/fork.tmpl65
-rw-r--r--templates/repo/header.tmpl2
11 files changed, 241 insertions, 77 deletions
diff --git a/LICENSE b/LICENSE
index 3af16b0724..546e65ae3f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,27 +1,19 @@
-Copyright (c) 2014
-All rights reserved.
+Copyright (c) 2014 All Gogs Contributors
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of the {organization} nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/README.md b/README.md
index 36e8ca6e66..8a5a71e3d0 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
![Demo](https://gowalker.org/public/gogs_demo.gif)
-##### Current version: 0.5.6 Beta
+##### Current version: 0.5.7 Beta
### NOTICES
@@ -35,7 +35,7 @@ The goal of this project is to make the easiest, fastest and most painless way t
- Reverse proxy suburl support
- Register/delete/rename account
- Create/manage/delete organization with team management
-- Create/migrate/mirror/delete/watch/rename/transfer public/private repository
+- Create/fork/migrate/mirror/delete/watch/rename/transfer public/private repository
- Repository viewer/release/issue tracker
- Repository and Organization level webhooks
- Repository Git hooks
diff --git a/README_ZH.md b/README_ZH.md
index 83addc0f4c..cb5e5a1a69 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
![Demo](https://gowalker.org/public/gogs_demo.gif)
-##### 当前版本:0.5.6 Beta
+##### 当前版本:0.5.7 Beta
## 开发目的
@@ -26,7 +26,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- 支持反向代理子路径
- 支持 注册/删除/重命名 用户
- 支持 创建/管理/删除 组织以及团队管理功能
-- 支持 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
+- 支持 创建/派生/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
- 支持仓库 浏览/发布/工单管理
- 支持仓库和组织级别 Web 钩子
- 支持仓库 Git 钩子
diff --git a/cmd/web.go b/cmd/web.go
index dc89fed78d..73cec968d0 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -265,7 +265,7 @@ func runWeb(*cli.Context) {
reqTrueOwner := middleware.RequireTrueOwner()
- // Organization routers.
+ // Organization.
m.Group("/org", func() {
m.Get("/create", org.Create)
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
@@ -309,12 +309,14 @@ func runWeb(*cli.Context) {
m.Get("/:org", org.Home)
}, middleware.OrgAssignment(true))
- // Repository routers.
+ // Repository.
m.Group("/repo", func() {
m.Get("/create", repo.Create)
m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost)
m.Get("/migrate", repo.Migrate)
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost)
+ m.Get("/fork", repo.Fork)
+ m.Post("/fork", bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost)
}, reqSignIn)
m.Group("/:username/:reponame", func() {
diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini
index 9b796278fd..5380ce8245 100644
--- a/conf/locale/locale_en-US.ini
+++ b/conf/locale/locale_en-US.ini
@@ -26,6 +26,7 @@ organization = Organization
mirror = Mirror
new_repo = New Repository
new_migrate = New Migration
+new_fork = New Fork Repository
new_org = New Organization
manage_org = Manage Organizations
admin_panel = Admin Panel
@@ -233,6 +234,9 @@ repo_name = Repository Name
repo_name_helper = Great repository names are short, memorable and <strong>unique</strong>.
visibility = Visibility
visiblity_helper = This repository is <span class="label label-red label-radius">Private</span>
+fork_repo = Fork Repository
+fork_from = Fork From
+fork_visiblity_helper = Forked repository cannot change its visiblity
repo_desc = Description
repo_lang = Language
repo_lang_helper = Select a .gitignore file
diff --git a/gogs.go b/gogs.go
index 5e817cc25f..97dd40bdac 100644
--- a/gogs.go
+++ b/gogs.go
@@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
-const APP_VER = "0.5.6.1104 Beta"
+const APP_VER = "0.5.7.1105 Beta"
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
diff --git a/models/repo.go b/models/repo.go
index 37cc7eabf5..1dbda2a006 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1298,8 +1298,8 @@ func IsStaring(uid, repoId int64) bool {
// \___ / \____/|__| |__|_ \
// \/ \/
-func ForkRepository(u *User, oldRepo *Repository) (*Repository, error) {
- isExist, err := IsRepositoryExist(u, oldRepo.Name)
+func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repository, error) {
+ isExist, err := IsRepositoryExist(u, name)
if err != nil {
return nil, err
} else if isExist {
@@ -1323,9 +1323,9 @@ func ForkRepository(u *User, oldRepo *Repository) (*Repository, error) {
repo := &Repository{
OwnerId: u.Id,
Owner: u,
- Name: oldRepo.Name,
- LowerName: oldRepo.LowerName,
- Description: oldRepo.Description,
+ Name: name,
+ LowerName: strings.ToLower(name),
+ Description: desc,
IsPrivate: oldRepo.IsPrivate,
IsFork: true,
ForkId: oldRepo.Id,
diff --git a/routers/repo/repo.go b/routers/repo/repo.go
index c6e8b504a9..8e4ace9940 100644
--- a/routers/repo/repo.go
+++ b/routers/repo/repo.go
@@ -24,8 +24,23 @@ import (
const (
CREATE base.TplName = "repo/create"
MIGRATE base.TplName = "repo/migrate"
+ FORK base.TplName = "repo/fork"
)
+func checkContextUser(ctx *middleware.Context, uid int64) (*models.User, error) {
+ ctxUser := ctx.User
+ if uid > 0 {
+ org, err := models.GetUserById(uid)
+ if err != models.ErrUserNotExist {
+ if err != nil {
+ return nil, fmt.Errorf("GetUserById: %v", err)
+ }
+ ctxUser = org
+ }
+ }
+ return ctxUser, nil
+}
+
func Create(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("new_repo")
@@ -35,14 +50,10 @@ func Create(ctx *middleware.Context) {
ctx.Data["Gitignores"] = models.Gitignores
ctx.Data["Licenses"] = models.Licenses
- ctxUser := ctx.User
- if orgId := com.StrTo(ctx.Query("org")).MustInt64(); orgId > 0 {
- org, err := models.GetUserById(orgId)
- if err != nil && err != models.ErrUserNotExist {
- ctx.Handle(500, "GetUserById", err)
- return
- }
- ctxUser = org
+ ctxUser, err := checkContextUser(ctx, ctx.QueryInt64("org"))
+ if err != nil {
+ ctx.Handle(500, "checkContextUser", err)
+ return
}
ctx.Data["ContextUser"] = ctxUser
@@ -64,12 +75,12 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
ctxUser := ctx.User
// Not equal means current user is an organization.
if form.Uid != ctx.User.Id {
- org, err := models.GetUserById(form.Uid)
- if err != nil && err != models.ErrUserNotExist {
- ctx.Handle(500, "GetUserById", err)
+ var err error
+ ctxUser, err = checkContextUser(ctx, form.Uid)
+ if err != nil {
+ ctx.Handle(500, "checkContextUser", err)
return
}
- ctxUser = org
}
ctx.Data["ContextUser"] = ctxUser
@@ -95,8 +106,8 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
repo, err := models.CreateRepository(ctxUser, form.RepoName, form.Description,
form.Gitignore, form.License, form.Private, false, form.InitReadme)
if err == nil {
- log.Trace("Repository created: %s/%s", ctxUser.Name, form.RepoName)
- ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + form.RepoName)
+ log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name)
+ ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
return
} else if err == models.ErrRepoAlreadyExist {
ctx.Data["Err_RepoName"] = true
@@ -119,14 +130,10 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
func Migrate(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("new_migrate")
- ctxUser := ctx.User
- if orgId := com.StrTo(ctx.Query("org")).MustInt64(); orgId > 0 {
- org, err := models.GetUserById(orgId)
- if err != nil && err != models.ErrUserNotExist {
- ctx.Handle(500, "GetUserById", err)
- return
- }
- ctxUser = org
+ ctxUser, err := checkContextUser(ctx, ctx.QueryInt64("org"))
+ if err != nil {
+ ctx.Handle(500, "checkContextUser", err)
+ return
}
ctx.Data["ContextUser"] = ctxUser
@@ -145,12 +152,12 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
ctxUser := ctx.User
// Not equal means current user is an organization.
if form.Uid != ctx.User.Id {
- org, err := models.GetUserById(form.Uid)
+ var err error
+ ctxUser, err = checkContextUser(ctx, form.Uid)
if err != nil {
- ctx.Handle(500, "GetUserById", err)
+ ctx.Handle(500, "checkContextUser", err)
return
}
- ctxUser = org
}
ctx.Data["ContextUser"] = ctxUser
@@ -206,6 +213,114 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
ctx.Handle(500, "MigratePost", err)
}
+func getForkRepository(ctx *middleware.Context) (*models.Repository, error) {
+ forkId := ctx.QueryInt64("fork_id")
+ ctx.Data["ForkId"] = forkId
+
+ forkRepo, err := models.GetRepositoryById(forkId)
+ if err != nil {
+ return nil, fmt.Errorf("GetRepositoryById: %v", err)
+ }
+ ctx.Data["repo_name"] = forkRepo.Name
+ ctx.Data["desc"] = forkRepo.Description
+
+ if err = forkRepo.GetOwner(); err != nil {
+ return nil, fmt.Errorf("GetOwner: %v", err)
+ }
+ ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name
+ return forkRepo, nil
+}
+
+func Fork(ctx *middleware.Context) {
+ ctx.Data["Title"] = ctx.Tr("new_fork")
+
+ if _, err := getForkRepository(ctx); err != nil {
+ if err == models.ErrRepoNotExist {
+ ctx.Redirect(setting.AppSubUrl + "/")
+ } else {
+ ctx.Handle(500, "getForkRepository", err)
+ }
+ return
+ }
+
+ // FIXME: maybe sometime can directly fork to organization?
+ ctx.Data["ContextUser"] = ctx.User
+ if err := ctx.User.GetOrganizations(); err != nil {
+ ctx.Handle(500, "GetOrganizations", err)
+ return
+ }
+ ctx.Data["Orgs"] = ctx.User.Orgs
+
+ ctx.HTML(200, FORK)
+}
+
+func ForkPost(ctx *middleware.Context, form auth.CreateRepoForm) {
+ ctx.Data["Title"] = ctx.Tr("new_fork")
+
+ forkRepo, err := getForkRepository(ctx)
+ if err != nil {
+ if err == models.ErrRepoNotExist {
+ ctx.Redirect(setting.AppSubUrl + "/")
+ } else {
+ ctx.Handle(500, "getForkRepository", err)
+ }
+ return
+ }
+
+ ctxUser := ctx.User
+ // Not equal means current user is an organization.
+ if form.Uid != ctx.User.Id {
+ var err error
+ ctxUser, err = checkContextUser(ctx, form.Uid)
+ if err != nil {
+ ctx.Handle(500, "checkContextUser", err)
+ return
+ }
+ }
+ ctx.Data["ContextUser"] = ctxUser
+
+ if err := ctx.User.GetOrganizations(); err != nil {
+ ctx.Handle(500, "GetOrganizations", err)
+ return
+ }
+ ctx.Data["Orgs"] = ctx.User.Orgs
+
+ if ctx.HasError() {
+ ctx.HTML(200, CREATE)
+ return
+ }
+
+ if ctxUser.IsOrganization() {
+ // Check ownership of organization.
+ if !ctxUser.IsOrgOwner(ctx.User.Id) {
+ ctx.Error(403)
+ return
+ }
+ }
+
+ repo, err := models.ForkRepository(ctxUser, forkRepo, form.RepoName, form.Description)
+ if err == nil {
+ log.Trace("Repository forked: %s/%s", ctxUser.Name, repo.Name)
+ ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
+ return
+ } else if err == models.ErrRepoAlreadyExist {
+ ctx.Data["Err_RepoName"] = true
+ ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), FORK, &form)
+ return
+ } else if err == models.ErrRepoNameIllegal {
+ ctx.Data["Err_RepoName"] = true
+ ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), CREATE, &form)
+ return
+ }
+
+ if repo != nil {
+ if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
+ log.Error(4, "DeleteRepository: %v", errDelete)
+ }
+ }
+ ctx.Handle(500, "ForkPost", err)
+}
+
func Action(ctx *middleware.Context) {
var err error
switch ctx.Params(":action") {
@@ -217,20 +332,6 @@ func Action(ctx *middleware.Context) {
err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
case "unstar":
err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
- case "fork":
- repo, err := models.ForkRepository(ctx.User, ctx.Repo.Repository)
- if err != nil {
- if err != models.ErrRepoAlreadyExist {
- log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
- ctx.JSON(200, map[string]interface{}{
- "ok": false,
- "err": err.Error(),
- })
- return
- }
- }
- ctx.Redirect(setting.AppSubUrl + "/" + repo.Owner.Name + "/" + repo.Name)
- return
case "desc":
if !ctx.Repo.IsOwner {
ctx.Error(404)
diff --git a/templates/.VERSION b/templates/.VERSION
index 188756b2f0..f2ebc3fb97 100644
--- a/templates/.VERSION
+++ b/templates/.VERSION
@@ -1 +1 @@
-0.5.6.1104 Beta \ No newline at end of file
+0.5.7.1105 Beta \ No newline at end of file
diff --git a/templates/repo/fork.tmpl b/templates/repo/fork.tmpl
new file mode 100644
index 0000000000..1d096a75d8
--- /dev/null
+++ b/templates/repo/fork.tmpl
@@ -0,0 +1,65 @@
+{{template "ng/base/head" .}}
+{{template "ng/base/header" .}}
+<div id="repo-wrapper">
+ <form id="repo-create-form" class="form form-align panel panel-radius" action="{{AppSubUrl}}/repo/fork?fork_id={{.ForkId}}" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="panel-header">
+ <h2>{{.i18n.Tr "new_fork"}}</h2>
+ </div>
+ <div class="panel-content">
+ {{template "ng/base/alert" .}}
+ <div class="field">
+ <label for="owner" class="req">{{.i18n.Tr "repo.owner"}}</label>
+ <input id="repo-owner-id" type="hidden" name="uid" value="{{.ContextUser.Id}}" />
+ <div class="inline-block drop">
+ <a class="drop-bottom">
+ <img class="avatar" src="{{.ContextUser.AvatarLink}}" id="repo-owner-avatar" alt="user-avatar">
+ <strong id="repo-owner-name">{{.ContextUser.Name}}</strong>
+ </a>
+ <ul class="drop-down menu menu-vertical menu-radius switching-list" id="repo-create-owner-list">
+ <li {{if eq $.ContextUser.Id .SignedUser.Id}}class="checked"{{end}} data-uid="{{.SignedUser.Id}}">
+ <a>
+ <i class="octicon octicon-check"></i>
+ <img class="avatar" src="{{.SignedUser.AvatarLink}}" alt="user-avatar">
+ <strong>{{.SignedUser.Name}}</strong>
+ </a>
+ </li>
+ {{range .Orgs}}
+ <li {{if eq $.ContextUser.Id .Id}}class="checked"{{end}} data-uid="{{.Id}}">
+ <a>
+ <i class="octicon octicon-check"></i>
+ <img class="avatar" src="{{.AvatarLink}}" alt="user-avatar">
+ <strong>{{.Name}}</strong>
+ </a>
+ </li>
+ {{end}}
+ </ul>
+ </div>
+ </div>
+ <div class="field">
+ <label>{{.i18n.Tr "repo.fork_from"}}</label>
+ <span><a target="_blank" href="{{AppSubUrl}}/{{.ForkFrom}}">{{.ForkFrom}}</a></span>
+ </div>
+ <div class="field">
+ <label class="req" for="repo-name">{{.i18n.Tr "repo.repo_name"}}</label>
+ <input class="ipt ipt-large ipt-radius {{if .Err_RepoName}}ipt-error{{end}}" id="repo-name" name="repo_name" type="text" value="{{.repo_name}}" required />
+ <span class="form-label"></span>
+ <span class="help">{{.i18n.Tr "repo.repo_name_helper" | Str2html}}</span>
+ </div>
+ <div class="field">
+ <label for="visibility">{{.i18n.Tr "repo.visibility"}}</label>
+ <span>{{.i18n.Tr "repo.fork_visiblity_helper"}}</span>
+ </div>
+ <div class="field clear">
+ <label class="left" for="desc">{{.i18n.Tr "repo.repo_desc"}}</label>
+ <textarea class="ipt ipt-large ipt-radius {{if .Err_Description}}ipt-error{{end}}" id="desc" name="desc">{{.desc}}</textarea>
+ </div>
+ <div class="field">
+ <label></label>
+ <button class="btn btn-large btn-blue btn-radius">{{.i18n.Tr "repo.fork_repo"}}</button>
+ <a class="btn btn-small btn-gray btn-radius" id="repo-create-cancel" href="{{AppSubUrl}}/"><strong>{{.i18n.Tr "cancel"}}</strong></a>
+ </div>
+ </div>
+ </form>
+</div>
+{{template "ng/base/footer" .}} \ No newline at end of file
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index c2caf4b015..976c8e0c00 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -47,7 +47,7 @@
</a>
</li>
<li id="repo-header-fork">
- <a id="repo-header-fork-btn" {{if not $.IsRepositoryTrueOwner}}href="{{.RepoLink}}/action/fork"{{end}}>
+ <a id="repo-header-fork-btn" {{if or (not $.IsRepositoryTrueOwner) $.Owner.IsOrganization}}href="/repo/fork?fork_id={{.Id}}"{{end}}>
<button class="btn btn-gray text-bold btn-radius">
<i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
<span class="num">{{.NumForks}}</span>