]> source.dussan.org Git - gitea.git/commitdiff
Make URL scheme unambiguous (#2408)
authorEthan Koenig <ethantkoenig@gmail.com>
Mon, 30 Oct 2017 02:04:25 +0000 (19:04 -0700)
committerLunny Xiao <xiaolunwen@gmail.com>
Mon, 30 Oct 2017 02:04:25 +0000 (10:04 +0800)
* Make URL scheme unambiguous

Redirect old routes to new routes

* Fix redirects to new URL scheme, and update template

* Fix branches/_new endpoints, and update integration test

20 files changed:
integrations/editor_test.go
integrations/links_test.go
integrations/repo_branch_test.go
integrations/repo_commits_test.go
models/webhook_slack.go
modules/context/repo.go
routers/api/v1/api.go
routers/repo/branch.go
routers/repo/commit.go
routers/repo/editor.go
routers/repo/repo.go
routers/repo/view.go
routers/routes/routes.go
templates/repo/branch_dropdown.tmpl
templates/repo/commits_table.tmpl
templates/repo/editor/edit.tmpl
templates/repo/home.tmpl
templates/repo/issue/list.tmpl
templates/repo/release/list.tmpl
templates/repo/view_file.tmpl

index 453b38491d74047b2c0eadaa1fa9044babe92c43..712cc91fbc1983ebae5b306c3ffd11b13d3ff54d 100644 (file)
@@ -111,7 +111,7 @@ func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePa
        resp = session.MakeRequest(t, req, http.StatusFound)
 
        // Verify the change
-       req = NewRequest(t, "GET", path.Join(user, repo, "raw", branch, filePath))
+       req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", branch, filePath))
        resp = session.MakeRequest(t, req, http.StatusOK)
        assert.EqualValues(t, newContent, string(resp.Body))
 
@@ -142,7 +142,7 @@ func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, bra
        resp = session.MakeRequest(t, req, http.StatusFound)
 
        // Verify the change
-       req = NewRequest(t, "GET", path.Join(user, repo, "raw", targetBranch, filePath))
+       req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", targetBranch, filePath))
        resp = session.MakeRequest(t, req, http.StatusOK)
        assert.EqualValues(t, newContent, string(resp.Body))
 
index 27e4054694b143a4fa28a8c409aa42d8990f791b..7eaf1c51ef84108673af9467d9800711d4f7c1e1 100644 (file)
@@ -10,6 +10,8 @@ import (
        "testing"
 
        api "code.gitea.io/sdk/gitea"
+
+       "github.com/stretchr/testify/assert"
 )
 
 func TestLinksNoLogin(t *testing.T) {
@@ -38,6 +40,20 @@ func TestLinksNoLogin(t *testing.T) {
        }
 }
 
+func TestRedirectsNoLogin(t *testing.T) {
+       prepareTestEnv(t)
+
+       var redirects = map[string]string{
+               "/user2/repo1/commits/master": "/user2/repo1/commits/branch/master",
+               "/user2/repo1/src/master":     "/user2/repo1/src/branch/master",
+       }
+       for link, redirectLink := range redirects {
+               req := NewRequest(t, "GET", link)
+               resp := MakeRequest(t, req, http.StatusFound)
+               assert.EqualValues(t, redirectLink, RedirectURL(t, resp))
+       }
+}
+
 func testLinksAsUser(userName string, t *testing.T) {
        var links = []string{
                "/explore/repos",
@@ -99,7 +115,7 @@ func testLinksAsUser(userName string, t *testing.T) {
                "",
                "/issues",
                "/pulls",
-               "/commits/master",
+               "/commits/branch/master",
                "/graph",
                "/settings",
                "/settings/collaboration",
index 32e24e83fb536f283d8a5a402e6122b2bcb39f00..df7f97bd2403b6d69a644147f64973b0c68bb42d 100644 (file)
@@ -14,14 +14,14 @@ import (
        "github.com/stretchr/testify/assert"
 )
 
-func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefName, newBranchName string, expectedStatus int) string {
+func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefSubURL, newBranchName string, expectedStatus int) string {
        var csrf string
        if expectedStatus == http.StatusNotFound {
-               csrf = GetCSRF(t, session, path.Join(user, repo, "src/master"))
+               csrf = GetCSRF(t, session, path.Join(user, repo, "src/branch/master"))
        } else {
-               csrf = GetCSRF(t, session, path.Join(user, repo, "src", oldRefName))
+               csrf = GetCSRF(t, session, path.Join(user, repo, "src", oldRefSubURL))
        }
-       req := NewRequestWithValues(t, "POST", path.Join(user, repo, "branches/_new", oldRefName), map[string]string{
+       req := NewRequestWithValues(t, "POST", path.Join(user, repo, "branches/_new", oldRefSubURL), map[string]string{
                "_csrf":           csrf,
                "new_branch_name": newBranchName,
        })
@@ -34,72 +34,72 @@ func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefName
 
 func TestCreateBranch(t *testing.T) {
        tests := []struct {
-               OldBranchOrCommit string
-               NewBranch         string
-               CreateRelease     string
-               FlashMessage      string
-               ExpectedStatus    int
+               OldRefSubURL   string
+               NewBranch      string
+               CreateRelease  string
+               FlashMessage   string
+               ExpectedStatus int
        }{
                {
-                       OldBranchOrCommit: "master",
-                       NewBranch:         "feature/test1",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "repo.branch.create_success", "feature/test1"),
+                       OldRefSubURL:   "branch/master",
+                       NewBranch:      "feature/test1",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "repo.branch.create_success", "feature/test1"),
                },
                {
-                       OldBranchOrCommit: "master",
-                       NewBranch:         "",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.require_error"),
+                       OldRefSubURL:   "branch/master",
+                       NewBranch:      "",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.require_error"),
                },
                {
-                       OldBranchOrCommit: "master",
-                       NewBranch:         "feature=test1",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.git_ref_name_error"),
+                       OldRefSubURL:   "branch/master",
+                       NewBranch:      "feature=test1",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.git_ref_name_error"),
                },
                {
-                       OldBranchOrCommit: "master",
-                       NewBranch:         strings.Repeat("b", 101),
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.max_size_error", "100"),
+                       OldRefSubURL:   "branch/master",
+                       NewBranch:      strings.Repeat("b", 101),
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.max_size_error", "100"),
                },
                {
-                       OldBranchOrCommit: "master",
-                       NewBranch:         "master",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "repo.branch.branch_already_exists", "master"),
+                       OldRefSubURL:   "branch/master",
+                       NewBranch:      "master",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "repo.branch.branch_already_exists", "master"),
                },
                {
-                       OldBranchOrCommit: "master",
-                       NewBranch:         "master/test",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "repo.branch.branch_name_conflict", "master/test", "master"),
+                       OldRefSubURL:   "branch/master",
+                       NewBranch:      "master/test",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "repo.branch.branch_name_conflict", "master/test", "master"),
                },
                {
-                       OldBranchOrCommit: "acd1d892867872cb47f3993468605b8aa59aa2e0",
-                       NewBranch:         "feature/test2",
-                       ExpectedStatus:    http.StatusNotFound,
+                       OldRefSubURL:   "commit/acd1d892867872cb47f3993468605b8aa59aa2e0",
+                       NewBranch:      "feature/test2",
+                       ExpectedStatus: http.StatusNotFound,
                },
                {
-                       OldBranchOrCommit: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
-                       NewBranch:         "feature/test3",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "repo.branch.create_success", "feature/test3"),
+                       OldRefSubURL:   "commit/65f1bf27bc3bf70f64657658635e66094edbcb4d",
+                       NewBranch:      "feature/test3",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "repo.branch.create_success", "feature/test3"),
                },
                {
-                       OldBranchOrCommit: "master",
-                       NewBranch:         "v1.0.0",
-                       CreateRelease:     "v1.0.0",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "repo.branch.tag_collision", "v1.0.0"),
+                       OldRefSubURL:   "branch/master",
+                       NewBranch:      "v1.0.0",
+                       CreateRelease:  "v1.0.0",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "repo.branch.tag_collision", "v1.0.0"),
                },
                {
-                       OldBranchOrCommit: "v1.0.0",
-                       NewBranch:         "feature/test4",
-                       CreateRelease:     "v1.0.0",
-                       ExpectedStatus:    http.StatusFound,
-                       FlashMessage:      i18n.Tr("en", "repo.branch.create_success", "feature/test4"),
+                       OldRefSubURL:   "tag/v1.0.0",
+                       NewBranch:      "feature/test4",
+                       CreateRelease:  "v1.0.0",
+                       ExpectedStatus: http.StatusFound,
+                       FlashMessage:   i18n.Tr("en", "repo.branch.create_success", "feature/test4"),
                },
        }
        for _, test := range tests {
@@ -108,7 +108,7 @@ func TestCreateBranch(t *testing.T) {
                if test.CreateRelease != "" {
                        createNewRelease(t, session, "/user2/repo1", test.CreateRelease, test.CreateRelease, false, false)
                }
-               redirectURL := testCreateBranch(t, session, "user2", "repo1", test.OldBranchOrCommit, test.NewBranch, test.ExpectedStatus)
+               redirectURL := testCreateBranch(t, session, "user2", "repo1", test.OldRefSubURL, test.NewBranch, test.ExpectedStatus)
                if test.ExpectedStatus == http.StatusFound {
                        req := NewRequest(t, "GET", redirectURL)
                        resp := session.MakeRequest(t, req, http.StatusOK)
@@ -124,7 +124,7 @@ func TestCreateBranch(t *testing.T) {
 func TestCreateBranchInvalidCSRF(t *testing.T) {
        prepareTestEnv(t)
        session := loginUser(t, "user2")
-       req := NewRequestWithValues(t, "POST", "user2/repo1/branches/_new/master", map[string]string{
+       req := NewRequestWithValues(t, "POST", "user2/repo1/branches/_new/branch/master", map[string]string{
                "_csrf":           "fake_csrf",
                "new_branch_name": "test",
        })
index bf353982458b09c01f2752885ace8ac5fbd95662..94d513370de10075bf08c2b131ddffeaf0794653 100644 (file)
@@ -20,7 +20,7 @@ func TestRepoCommits(t *testing.T) {
        session := loginUser(t, "user2")
 
        // Request repository commits page
-       req := NewRequest(t, "GET", "/user2/repo1/commits/master")
+       req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
        resp := session.MakeRequest(t, req, http.StatusOK)
 
        doc := NewHTMLParser(t, resp.Body)
@@ -35,7 +35,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
        session := loginUser(t, "user2")
 
        // Request repository commits page
-       req := NewRequest(t, "GET", "/user2/repo1/commits/master")
+       req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
        resp := session.MakeRequest(t, req, http.StatusOK)
 
        doc := NewHTMLParser(t, resp.Body)
@@ -56,7 +56,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
 
        resp = session.MakeRequest(t, req, http.StatusCreated)
 
-       req = NewRequest(t, "GET", "/user2/repo1/commits/master")
+       req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
        resp = session.MakeRequest(t, req, http.StatusOK)
 
        doc = NewHTMLParser(t, resp.Body)
index ca43cfd4274167ae77709c662c767cffb90d9420..dd25a8d7df7a89fc427dd341df431fae35ddd36a 100644 (file)
@@ -80,12 +80,22 @@ func SlackLinkFormatter(url string, text string) string {
        return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
 }
 
-func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {
-       // created tag/branch
-       refName := git.RefEndName(p.Ref)
+// SlackLinkToRef slack-formatter link to a repo ref
+func SlackLinkToRef(repoURL, ref string) string {
+       refName := git.RefEndName(ref)
+       switch {
+       case strings.HasPrefix(ref, git.BranchPrefix):
+               return SlackLinkFormatter(repoURL+"/src/branch/"+refName, refName)
+       case strings.HasPrefix(ref, git.TagPrefix):
+               return SlackLinkFormatter(repoURL+"/src/tag/"+refName, refName)
+       default:
+               return SlackLinkFormatter(repoURL+"/src/commit/"+refName, refName)
+       }
+}
 
+func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {
        repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
-       refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
+       refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
        text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
 
        return &SlackPayload{
@@ -99,7 +109,6 @@ func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayloa
 func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
        // n new commits
        var (
-               branchName   = git.RefEndName(p.Ref)
                commitDesc   string
                commitString string
        )
@@ -116,7 +125,7 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
        }
 
        repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
-       branchLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+branchName, branchName)
+       branchLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
        text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName)
 
        var attachmentText string
index 704dc59f93048530848cfca26f7767ed951f7e43..b2b58c4f26cc9b90d32b015f78fc5afe1f0afa43 100644 (file)
@@ -14,6 +14,7 @@ import (
        "code.gitea.io/git"
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/modules/cache"
+       "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
 
        "github.com/Unknwon/com"
@@ -117,6 +118,20 @@ func (r *Repository) GetCommitsCount() (int64, error) {
        })
 }
 
+// BranchNameSubURL sub-URL for the BranchName field
+func (r *Repository) BranchNameSubURL() string {
+       switch {
+       case r.IsViewBranch:
+               return "branch/" + r.BranchName
+       case r.IsViewTag:
+               return "tag/" + r.BranchName
+       case r.IsViewCommit:
+               return "commit/" + r.BranchName
+       }
+       log.Error(4, "Unknown view type for repo: %v", r)
+       return ""
+}
+
 // GetEditorconfig returns the .editorconfig definition if found in the
 // HEAD of the default repo branch.
 func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) {
@@ -444,8 +459,81 @@ func RepoAssignment() macaron.Handler {
        }
 }
 
-// RepoRef handles repository reference name including those contain `/`.
+// RepoRefType type of repo reference
+type RepoRefType int
+
+const (
+       // RepoRefLegacy unknown type, make educated guess and redirect.
+       // for backward compatibility with previous URL scheme
+       RepoRefLegacy RepoRefType = iota
+       // RepoRefBranch branch
+       RepoRefBranch
+       // RepoRefTag tag
+       RepoRefTag
+       // RepoRefCommit commit
+       RepoRefCommit
+)
+
+// RepoRef handles repository reference names when the ref name is not
+// explicitly given
 func RepoRef() macaron.Handler {
+       // since no ref name is explicitly specified, ok to just use branch
+       return RepoRefByType(RepoRefBranch)
+}
+
+func getRefNameFromPath(ctx *Context, path string, isExist func(string) bool) string {
+       refName := ""
+       parts := strings.Split(path, "/")
+       for i, part := range parts {
+               refName = strings.TrimPrefix(refName+"/"+part, "/")
+               if isExist(refName) {
+                       ctx.Repo.TreePath = strings.Join(parts[i+1:], "/")
+                       return refName
+               }
+       }
+       return ""
+}
+
+func getRefName(ctx *Context, pathType RepoRefType) string {
+       path := ctx.Params("*")
+       switch pathType {
+       case RepoRefLegacy:
+               if refName := getRefName(ctx, RepoRefBranch); len(refName) > 0 {
+                       return refName
+               }
+               if refName := getRefName(ctx, RepoRefTag); len(refName) > 0 {
+                       return refName
+               }
+               return getRefName(ctx, RepoRefCommit)
+       case RepoRefBranch:
+               return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsBranchExist)
+       case RepoRefTag:
+               return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist)
+       case RepoRefCommit:
+               parts := strings.Split(path, "/")
+               if len(parts) > 0 && len(parts[0]) == 40 {
+                       ctx.Repo.TreePath = strings.Join(parts[1:], "/")
+                       return parts[0]
+               }
+       default:
+               log.Error(4, "Unrecognized path type: %v", path)
+       }
+       return ""
+}
+
+// URL to redirect to for deprecated URL scheme
+func repoRefRedirect(ctx *Context) string {
+       urlPath := ctx.Req.URL.String()
+       idx := strings.LastIndex(urlPath, ctx.Params("*"))
+       if idx < 0 {
+               idx = len(urlPath)
+       }
+       return path.Join(urlPath[:idx], ctx.Repo.BranchNameSubURL())
+}
+
+// RepoRefByType handles repository reference name for a specific type
+// of repository reference
+func RepoRefByType(refType RepoRefType) macaron.Handler {
        return func(ctx *Context) {
                // Empty repository does not have reference information.
                if ctx.Repo.Repository.IsBare {
@@ -470,6 +558,7 @@ func RepoRef() macaron.Handler {
                // Get default branch.
                if len(ctx.Params("*")) == 0 {
                        refName = ctx.Repo.Repository.DefaultBranch
+                       ctx.Repo.BranchName = refName
                        if !ctx.Repo.GitRepo.IsBranchExist(refName) {
                                brs, err := ctx.Repo.GitRepo.GetBranches()
                                if err != nil {
@@ -492,25 +581,8 @@ func RepoRef() macaron.Handler {
                        ctx.Repo.IsViewBranch = true
 
                } else {
-                       hasMatched := false
-                       parts := strings.Split(ctx.Params("*"), "/")
-                       for i, part := range parts {
-                               refName = strings.TrimPrefix(refName+"/"+part, "/")
-
-                               if ctx.Repo.GitRepo.IsBranchExist(refName) ||
-                                       ctx.Repo.GitRepo.IsTagExist(refName) {
-                                       if i < len(parts)-1 {
-                                               ctx.Repo.TreePath = strings.Join(parts[i+1:], "/")
-                                       }
-                                       hasMatched = true
-                                       break
-                               }
-                       }
-                       if !hasMatched && len(parts[0]) == 40 {
-                               refName = parts[0]
-                               ctx.Repo.TreePath = strings.Join(parts[1:], "/")
-                       }
-
+                       refName = getRefName(ctx, refType)
+                       ctx.Repo.BranchName = refName
                        if ctx.Repo.GitRepo.IsBranchExist(refName) {
                                ctx.Repo.IsViewBranch = true
 
@@ -542,10 +614,16 @@ func RepoRef() macaron.Handler {
                                ctx.Handle(404, "RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
                                return
                        }
+
+                       if refType == RepoRefLegacy {
+                               // redirect from old URL scheme to new URL scheme
+                               ctx.Redirect(repoRefRedirect(ctx))
+                               return
+                       }
                }
 
-               ctx.Repo.BranchName = refName
                ctx.Data["BranchName"] = ctx.Repo.BranchName
+               ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
                ctx.Data["CommitID"] = ctx.Repo.CommitID
                ctx.Data["TreePath"] = ctx.Repo.TreePath
                ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
index 10391354401f06e1a2698181852b673a9670660e..79ae5799e3a4691a3dffe529c2d5b7d6cb734de4 100644 (file)
@@ -391,7 +391,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                                        Post(reqToken(), bind(api.CreateForkOption{}), repo.CreateFork)
                                m.Group("/branches", func() {
                                        m.Get("", repo.ListBranches)
-                                       m.Get("/*", context.RepoRef(), repo.GetBranch)
+                                       m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch)
                                })
                                m.Group("/keys", func() {
                                        m.Combo("").Get(repo.ListDeployKeys).
index f6eca39353b43b1c10b12844faa52a6401b51977..c56e8e86b2944723ad7ca0a8a8330912458681a3 100644 (file)
@@ -202,7 +202,7 @@ func CreateBranch(ctx *context.Context, form auth.NewBranchForm) {
 
        if ctx.HasError() {
                ctx.Flash.Error(ctx.GetErrMsg())
-               ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName)
+               ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
                return
        }
 
@@ -216,19 +216,19 @@ func CreateBranch(ctx *context.Context, form auth.NewBranchForm) {
                if models.IsErrTagAlreadyExists(err) {
                        e := err.(models.ErrTagAlreadyExists)
                        ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
-                       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName)
+                       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
                        return
                }
                if models.IsErrBranchAlreadyExists(err) {
                        e := err.(models.ErrBranchAlreadyExists)
                        ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", e.BranchName))
-                       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName)
+                       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
                        return
                }
                if models.IsErrBranchNameConflict(err) {
                        e := err.(models.ErrBranchNameConflict)
                        ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
-                       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName)
+                       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
                        return
                }
 
@@ -237,5 +237,5 @@ func CreateBranch(ctx *context.Context, form auth.NewBranchForm) {
        }
 
        ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName))
-       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + form.NewBranchName)
+       ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + form.NewBranchName)
 }
index 637a50543adb76af481dddbdd2a310b4741aebb6..afd6b113c4190b871614423746bb11fe98395428 100644 (file)
@@ -120,7 +120,7 @@ func SearchCommits(ctx *context.Context) {
 
        keyword := strings.Trim(ctx.Query("q"), " ")
        if len(keyword) == 0 {
-               ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName)
+               ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchNameSubURL())
                return
        }
        all := ctx.QueryBool("all")
index 42ede9a28fa41fa3902377e989b2825bc95cd901..a6cc9223647373468875eebc20bce8c49935219b 100644 (file)
@@ -113,7 +113,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
 
        ctx.Data["TreeNames"] = treeNames
        ctx.Data["TreePaths"] = treePaths
-       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName
+       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
        ctx.Data["commit_summary"] = ""
        ctx.Data["commit_message"] = ""
        if canCommit {
@@ -164,7 +164,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
        ctx.Data["TreePath"] = form.TreePath
        ctx.Data["TreeNames"] = treeNames
        ctx.Data["TreePaths"] = treePaths
-       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + branchName
+       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + branchName
        ctx.Data["FileContent"] = form.Content
        ctx.Data["commit_summary"] = form.CommitSummary
        ctx.Data["commit_message"] = form.CommitMessage
@@ -304,7 +304,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
                return
        }
 
-       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName + "/" + strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(form.TreePath))
+       ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(form.TreePath))
 }
 
 // EditFilePost response for editing file
@@ -348,7 +348,7 @@ func DiffPreviewPost(ctx *context.Context, form auth.EditPreviewDiffForm) {
 // DeleteFile render delete file page
 func DeleteFile(ctx *context.Context) {
        ctx.Data["PageIsDelete"] = true
-       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName
+       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
        ctx.Data["TreePath"] = ctx.Repo.TreePath
        canCommit := renderCommitRights(ctx)
 
@@ -367,7 +367,7 @@ func DeleteFile(ctx *context.Context) {
 // DeleteFilePost response for deleting file
 func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
        ctx.Data["PageIsDelete"] = true
-       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName
+       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
        ctx.Data["TreePath"] = ctx.Repo.TreePath
        canCommit := renderCommitRights(ctx)
 
@@ -422,7 +422,7 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
        }
 
        ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath))
-       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName)
+       ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName)
 }
 
 func renderUploadSettings(ctx *context.Context) {
@@ -446,7 +446,7 @@ func UploadFile(ctx *context.Context) {
 
        ctx.Data["TreeNames"] = treeNames
        ctx.Data["TreePaths"] = treePaths
-       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName
+       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
        ctx.Data["commit_summary"] = ""
        ctx.Data["commit_message"] = ""
        if canCommit {
@@ -482,7 +482,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
        ctx.Data["TreePath"] = form.TreePath
        ctx.Data["TreeNames"] = treeNames
        ctx.Data["TreePaths"] = treePaths
-       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + branchName
+       ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + branchName
        ctx.Data["commit_summary"] = form.CommitSummary
        ctx.Data["commit_message"] = form.CommitMessage
        ctx.Data["commit_choice"] = form.CommitChoice
@@ -551,7 +551,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
                return
        }
 
-       ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName + "/" + form.TreePath)
+       ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + form.TreePath)
 }
 
 // UploadFileToServer upload file to server file dir not git
index ebbce1e19cd296c00797d122ee5557cdbb98abc5..dbe78f6d1e9497c122ae0b26b805e373dc6dbaf5 100644 (file)
@@ -34,6 +34,21 @@ func MustBeNotBare(ctx *context.Context) {
        }
 }
 
+// MustBeEditable check that repo can be edited
+func MustBeEditable(ctx *context.Context) {
+       if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
+               ctx.Handle(404, "", nil)
+               return
+       }
+}
+
+// MustBeAbleToUpload check that repo can be uploaded to
+func MustBeAbleToUpload(ctx *context.Context) {
+       if !setting.Repository.Upload.Enabled {
+               ctx.Handle(404, "", nil)
+       }
+}
+
 func checkContextUser(ctx *context.Context, uid int64) *models.User {
        orgs, err := models.GetOwnedOrgsByUserIDDesc(ctx.User.ID, "updated_unix")
        if err != nil {
index 38260d2bec4945825ee51d417f6911a82c955f27..d43b4d7f78b7160e488d80e5fc8bf874a272bc71 100644 (file)
@@ -297,9 +297,9 @@ func renderCode(ctx *context.Context) {
        ctx.Data["Title"] = title
        ctx.Data["RequireHighlightJS"] = true
 
-       branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName
+       branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
        treeLink := branchLink
-       rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchName
+       rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
 
        if len(ctx.Repo.TreePath) > 0 {
                treeLink += "/" + ctx.Repo.TreePath
index 94dfe0ab3675cda61bcafadcaa3dbb447a539c8f..f1c9f184899a0245701f1218aefebfdb8070329b 100644 (file)
@@ -522,34 +522,30 @@ func RegisterRoutes(m *macaron.Macaron) {
                        Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
 
                m.Group("", func() {
-                       m.Combo("/_edit/*").Get(repo.EditFile).
-                               Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost)
-                       m.Combo("/_new/*").Get(repo.NewFile).
-                               Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost)
-                       m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost)
-                       m.Combo("/_delete/*").Get(repo.DeleteFile).
-                               Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost)
-
                        m.Group("", func() {
-                               m.Combo("/_upload/*").Get(repo.UploadFile).
+                               m.Combo("/_edit/*").Get(repo.EditFile).
+                                       Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost)
+                               m.Combo("/_new/*").Get(repo.NewFile).
+                                       Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost)
+                               m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost)
+                               m.Combo("/_delete/*").Get(repo.DeleteFile).
+                                       Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost)
+                               m.Combo("/_upload/*", repo.MustBeAbleToUpload).
+                                       Get(repo.UploadFile).
                                        Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost)
+                       }, context.RepoRefByType(context.RepoRefBranch), repo.MustBeEditable)
+                       m.Group("", func() {
                                m.Post("/upload-file", repo.UploadFileToServer)
                                m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
-                       }, func(ctx *context.Context) {
-                               if !setting.Repository.Upload.Enabled {
-                                       ctx.Handle(404, "", nil)
-                                       return
-                               }
-                       })
-               }, repo.MustBeNotBare, reqRepoWriter, context.RepoRef(), func(ctx *context.Context) {
-                       if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
-                               ctx.Handle(404, "", nil)
-                               return
-                       }
-               })
+                       }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload)
+               }, repo.MustBeNotBare, reqRepoWriter)
 
                m.Group("/branches", func() {
-                       m.Post("/_new/*", context.RepoRef(), bindIgnErr(auth.NewBranchForm{}), repo.CreateBranch)
+                       m.Group("/_new/", func() {
+                               m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch)
+                               m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch)
+                               m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch)
+                       }, bindIgnErr(auth.NewBranchForm{}))
                        m.Post("/delete", repo.DeleteBranchPost)
                        m.Post("/restore", repo.RestoreBranchPost)
                }, reqRepoWriter, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode))
@@ -629,15 +625,36 @@ func RegisterRoutes(m *macaron.Macaron) {
                        m.Post("/cleanup", context.RepoRef(), repo.CleanUpPullRequest)
                }, repo.MustAllowPulls)
 
+               m.Group("/raw", func() {
+                       m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload)
+                       m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownload)
+                       m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload)
+                       // "/*" route is deprecated, and kept for backward compatibility
+                       m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload)
+               }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode))
+
+               m.Group("/commits", func() {
+                       m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits)
+                       m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits)
+                       m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits)
+                       // "/*" route is deprecated, and kept for backward compatibility
+                       m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits)
+               }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode))
+
                m.Group("", func() {
-                       m.Get("/raw/*", repo.SingleDownload)
-                       m.Get("/commits/*", repo.RefCommits)
                        m.Get("/graph", repo.Graph)
                        m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
                }, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
 
+               m.Group("/src", func() {
+                       m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
+                       m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
+                       m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)
+                       // "/*" route is deprecated, and kept for backward compatibility
+                       m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home)
+               }, repo.SetEditorconfigIfExists)
+
                m.Group("", func() {
-                       m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home)
                        m.Get("/forks", repo.Forks)
                }, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
                m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)",
index 6b9343ac0e868d576310511662af6b82d83f4a92..881747220ea44f98741e98170589e7fa8b2bf6ea 100644 (file)
                </div>
                <div class="data" style="display: none" data-mode="{{if .IsViewTag}}tags{{else}}branches{{end}}">
                        {{range .Branches}}
-                               <div class="item branch {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div>
+                               <div class="item branch {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/branch/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div>
                        {{end}}
                        {{range .Tags}}
-                               <div class="item tag {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div>
+                               <div class="item tag {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/tag/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div>
                        {{end}}
                </div>
                <div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
index b5bd8f33d02c9238915436d10d02252993667dc9..2ca861c623ebf98879ec4b65694fe8ef862e6776 100644 (file)
@@ -2,7 +2,7 @@
        {{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}}
        {{if .PageIsCommits}}
                <div class="ui right">
-                       <form action="{{.RepoLink}}/commits/{{.BranchName}}/search">
+                       <form action="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/search">
                                <div class="ui tiny search input">
                                        <input name="q" placeholder="{{.i18n.Tr "repo.commits.search"}}" value="{{.Keyword}}" autofocus>
                                </div>
        {{if gt .TotalPages 1}}
                <div class="center page buttons">
                        <div class="ui borderless pagination menu">
-                               <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.RepoLink}}/commits/{{$.BranchName}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Previous}}"{{end}}>
+                               <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.RepoLink}}/commits/{{$.BranchNameSubURL}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Previous}}"{{end}}>
                                        <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
                                </a>
                                {{range .Pages}}
                                        {{if eq .Num -1}}
                                                <a class="disabled item">...</a>
                                        {{else}}
-                                               <a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.RepoLink}}/commits/{{$.BranchName}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Num}}"{{end}}>{{.Num}}</a>
+                                               <a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.RepoLink}}/commits/{{$.BranchNameSubURL}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Num}}"{{end}}>{{.Num}}</a>
                                        {{end}}
                                {{end}}
-                               <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.RepoLink}}/commits/{{$.BranchName}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Next}}"{{end}}>
+                               <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.RepoLink}}/commits/{{$.BranchNameSubURL}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Next}}"{{end}}>
                                        {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
                                </a>
                        </div>
index 64b09c37413cbe53d40ed913849c0dc4c826e0ed..77e40c6de3da057f72f35d198264869867724693 100644 (file)
@@ -30,7 +30,7 @@
                                <div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
                                        <a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a>
                                        {{if not .IsNewFile}}
-                                       <a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchName}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a>
+                                       <a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a>
                                        <a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName}}/{{.TreePath}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.editor.preview_changes"}}</a>
                                        {{end}}
                                </div>
index f0b4a45627e0808936b03b515b12776a8d46cbd2..c7c88aafccc2cd20b6b571277fc723bb08d485d4 100644 (file)
@@ -35,7 +35,7 @@
                        {{template "repo/branch_dropdown" .}}
                        {{ $n := len .TreeNames}}
                        {{ $l := Subtract $n 1}}
-                       <div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" href="{{.RepoLink}}/src/{{EscapePound .BranchName}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div>
+                       <div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" href="{{.RepoLink}}/src/{{EscapePound .BranchNameSubURL}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div>
                        <div class="right fitted item">
                                {{if .Repository.CanEnableEditor}}
                                        <div id="file-buttons" class="ui tiny blue buttons">
index 711d70f2a0c2a34842119436598c1997818cda8f..2b756cd88ee4c8074dea669438b9a044e9d59a31 100644 (file)
                                        <a class="title has-emoji" href="{{$.Link}}/{{.Index}}">{{.Title}}</a>
 
                                        {{if .Ref}}
-                                               <a class="ui label" href="{{$.RepoLink}}/src/{{.Ref}}">{{.Ref}}</a>
+                                               <a class="ui label" href="{{$.RepoLink}}/src/commit/{{.Ref}}">{{.Ref}}</a>
                                        {{end}}
                                        {{range .Labels}}
                                                <a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name | Sanitize}}</a>
index 9106bba9b32b9c7289ee07c3263707ee21e67723..2fed9ae58818996ddb5d10b0eb3fc7748e88e56a 100644 (file)
                                                                <span class="ui green label">{{$.i18n.Tr "repo.release.stable"}}</span>
                                                        {{end}}
                                                        <span class="tag text blue">
-                                                               <a href="{{$.RepoLink}}/src/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a>
+                                                               <a href="{{$.RepoLink}}/src/tag/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a>
                                                        </span>
                                                        <span class="commit">
-                                                               <a href="{{$.RepoLink}}/src/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a>
+                                                               <a href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a>
                                                        </span>
                                                {{end}}
                                        </div>
                                        <div class="ui twelve wide column detail">
                                                {{if .IsTag}}
                                                        <h4>
-                                                               <a href="{{$.RepoLink}}/src/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a>
+                                                               <a href="{{$.RepoLink}}/src/tag/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a>
                                                        </h4>
                                                        <div class="download">
-                                                               <a href="{{$.RepoLink}}/src/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a>
+                                                               <a href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a>
                                                                <a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> ZIP</a>
                                                                <a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> TAR.GZ</a>
                                                        </div>
                                                {{else}}
                                                        <h3>
-                                                               <a href="{{$.RepoLink}}/src/{{.TagName}}">{{.Title}}</a>
+                                                               <a href="{{$.RepoLink}}/src/tag/{{.TagName}}">{{.Title}}</a>
                                                                {{if $.IsRepositoryWriter}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}}
                                                        </h3>
                                                        <p class="text grey">
index 52bd6dfb4ef10905744d2b851758b11b5935486f..e8319881ff93df207f9b2b22c9375d87626b7555 100644 (file)
@@ -15,9 +15,9 @@
                        <div class="ui right file-actions">
                                <div class="ui buttons">
                                        {{if not .IsViewCommit}}
-                                               <a class="ui button" href="{{.RepoLink}}/src/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a>
+                                               <a class="ui button" href="{{.RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a>
                                        {{end}}
-                                       <a class="ui button" href="{{.RepoLink}}/commits/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a>
+                                       <a class="ui button" href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a>
                                        <a class="ui button" href="{{EscapePound $.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a>
                                </div>
                                {{if .Repository.CanEnableEditor}}