diff options
-rw-r--r-- | modules/git/commit_info_gogit.go | 61 | ||||
-rw-r--r-- | modules/git/commit_info_nogogit.go | 47 | ||||
-rw-r--r-- | modules/git/last_commit_cache.go | 3 | ||||
-rw-r--r-- | modules/git/last_commit_cache_gogit.go | 8 | ||||
-rw-r--r-- | modules/git/last_commit_cache_nogogit.go | 16 | ||||
-rw-r--r-- | modules/git/log_name_status.go | 13 | ||||
-rw-r--r-- | modules/git/notes_gogit.go | 3 | ||||
-rw-r--r-- | modules/git/notes_nogogit.go | 3 | ||||
-rw-r--r-- | routers/web/repo/view.go | 205 | ||||
-rw-r--r-- | routers/web/web.go | 3 | ||||
-rw-r--r-- | templates/repo/view_list.tmpl | 68 | ||||
-rw-r--r-- | web_src/js/features/lastcommitloader.js | 40 | ||||
-rw-r--r-- | web_src/js/index.js | 2 |
13 files changed, 321 insertions, 151 deletions
diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go index 8b82f3f66c..ccf90fc8c7 100644 --- a/modules/git/commit_info_gogit.go +++ b/modules/git/commit_info_gogit.go @@ -44,20 +44,17 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath return nil, nil, err } if len(unHitPaths) > 0 { - revs2, err := GetLastCommitForPaths(ctx, c, treePath, unHitPaths) + revs2, err := GetLastCommitForPaths(ctx, cache, c, treePath, unHitPaths) if err != nil { return nil, nil, err } for k, v := range revs2 { - if err := cache.Put(commit.ID.String(), path.Join(treePath, k), v.ID().String()); err != nil { - return nil, nil, err - } revs[k] = v } } } else { - revs, err = GetLastCommitForPaths(ctx, c, treePath, entryPaths) + revs, err = GetLastCommitForPaths(ctx, nil, c, treePath, entryPaths) } if err != nil { return nil, nil, err @@ -70,25 +67,29 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath commitsInfo[i] = CommitInfo{ Entry: entry, } + + // Check if we have found a commit for this entry in time if rev, ok := revs[entry.Name()]; ok { entryCommit := convertCommit(rev) commitsInfo[i].Commit = entryCommit - if entry.IsSubModule() { - subModuleURL := "" - var fullPath string - if len(treePath) > 0 { - fullPath = treePath + "/" + entry.Name() - } else { - fullPath = entry.Name() - } - if subModule, err := commit.GetSubModule(fullPath); err != nil { - return nil, nil, err - } else if subModule != nil { - subModuleURL = subModule.URL - } - subModuleFile := NewSubModuleFile(entryCommit, subModuleURL, entry.ID.String()) - commitsInfo[i].SubModuleFile = subModuleFile + } + + // If the entry if a submodule add a submodule file for this + if entry.IsSubModule() { + subModuleURL := "" + var fullPath string + if len(treePath) > 0 { + fullPath = treePath + "/" + entry.Name() + } else { + fullPath = entry.Name() } + if subModule, err := commit.GetSubModule(fullPath); err != nil { + return nil, nil, err + } else if subModule != nil { + subModuleURL = subModule.URL + } + subModuleFile := NewSubModuleFile(commitsInfo[i].Commit, subModuleURL, entry.ID.String()) + commitsInfo[i].SubModuleFile = subModuleFile } } @@ -175,7 +176,9 @@ func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cac } // GetLastCommitForPaths returns last commit information -func GetLastCommitForPaths(ctx context.Context, c cgobject.CommitNode, treePath string, paths []string) (map[string]*object.Commit, error) { +func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, c cgobject.CommitNode, treePath string, paths []string) (map[string]*object.Commit, error) { + refSha := c.ID().String() + // We do a tree traversal with nodes sorted by commit time heap := binaryheap.NewWith(func(a, b interface{}) int { if a.(*commitAndPaths).commit.CommitTime().Before(b.(*commitAndPaths).commit.CommitTime()) { @@ -192,10 +195,13 @@ func GetLastCommitForPaths(ctx context.Context, c cgobject.CommitNode, treePath // Start search from the root commit and with full set of paths heap.Push(&commitAndPaths{c, paths, initialHashes}) - +heaploop: for { select { case <-ctx.Done(): + if ctx.Err() == context.DeadlineExceeded { + break heaploop + } return nil, ctx.Err() default: } @@ -233,14 +239,14 @@ func GetLastCommitForPaths(ctx context.Context, c cgobject.CommitNode, treePath } var remainingPaths []string - for i, path := range current.paths { + for i, pth := range current.paths { // The results could already contain some newer change for the same path, // so don't override that and bail out on the file early. - if resultNodes[path] == nil { + if resultNodes[pth] == nil { if pathUnchanged[i] { // The path existed with the same hash in at least one parent so it could // not have been changed in this commit directly. - remainingPaths = append(remainingPaths, path) + remainingPaths = append(remainingPaths, pth) } else { // There are few possible cases how can we get here: // - The path didn't exist in any parent, so it must have been created by @@ -250,7 +256,10 @@ func GetLastCommitForPaths(ctx context.Context, c cgobject.CommitNode, treePath // - We are looking at a merge commit and the hash of the file doesn't // match any of the hashes being merged. This is more common for directories, // but it can also happen if a file is changed through conflict resolution. - resultNodes[path] = current.commit + resultNodes[pth] = current.commit + if err := cache.Put(refSha, path.Join(treePath, pth), current.commit.ID().String()); err != nil { + return nil, err + } } } } diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go index f57355d16e..261252dab1 100644 --- a/modules/git/commit_info_nogogit.go +++ b/modules/git/commit_info_nogogit.go @@ -37,21 +37,18 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath } if len(unHitPaths) > 0 { sort.Strings(unHitPaths) - commits, err := GetLastCommitForPaths(ctx, commit, treePath, unHitPaths) + commits, err := GetLastCommitForPaths(ctx, cache, commit, treePath, unHitPaths) if err != nil { return nil, nil, err } for pth, found := range commits { - if err := cache.Put(commit.ID.String(), path.Join(treePath, pth), found.ID.String()); err != nil { - return nil, nil, err - } revs[pth] = found } } } else { sort.Strings(entryPaths) - revs, err = GetLastCommitForPaths(ctx, commit, treePath, entryPaths) + revs, err = GetLastCommitForPaths(ctx, nil, commit, treePath, entryPaths) } if err != nil { return nil, nil, err @@ -62,27 +59,31 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath commitsInfo[i] = CommitInfo{ Entry: entry, } + + // Check if we have found a commit for this entry in time if entryCommit, ok := revs[entry.Name()]; ok { commitsInfo[i].Commit = entryCommit - if entry.IsSubModule() { - subModuleURL := "" - var fullPath string - if len(treePath) > 0 { - fullPath = treePath + "/" + entry.Name() - } else { - fullPath = entry.Name() - } - if subModule, err := commit.GetSubModule(fullPath); err != nil { - return nil, nil, err - } else if subModule != nil { - subModuleURL = subModule.URL - } - subModuleFile := NewSubModuleFile(entryCommit, subModuleURL, entry.ID.String()) - commitsInfo[i].SubModuleFile = subModuleFile - } } else { log.Debug("missing commit for %s", entry.Name()) } + + // If the entry if a submodule add a submodule file for this + if entry.IsSubModule() { + subModuleURL := "" + var fullPath string + if len(treePath) > 0 { + fullPath = treePath + "/" + entry.Name() + } else { + fullPath = entry.Name() + } + if subModule, err := commit.GetSubModule(fullPath); err != nil { + return nil, nil, err + } else if subModule != nil { + subModuleURL = subModule.URL + } + subModuleFile := NewSubModuleFile(commitsInfo[i].Commit, subModuleURL, entry.ID.String()) + commitsInfo[i].SubModuleFile = subModuleFile + } } // Retrieve the commit for the treePath itself (see above). We basically @@ -121,9 +122,9 @@ func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string } // GetLastCommitForPaths returns last commit information -func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) { +func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) { // We read backwards from the commit to obtain all of the commits - revs, err := WalkGitLog(ctx, commit.repo, commit, treePath, paths...) + revs, err := WalkGitLog(ctx, cache, commit.repo, commit, treePath, paths...) if err != nil { return nil, err } diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go index e2d296641f..d4ec517b51 100644 --- a/modules/git/last_commit_cache.go +++ b/modules/git/last_commit_cache.go @@ -26,6 +26,9 @@ func (c *LastCommitCache) getCacheKey(repoPath, ref, entryPath string) string { // Put put the last commit id with commit and entry path func (c *LastCommitCache) Put(ref, entryPath, commitID string) error { + if c == nil || c.cache == nil { + return nil + } log.Debug("LastCommitCache save: [%s:%s:%s]", ref, entryPath, commitID) return c.cache.Put(c.getCacheKey(c.repoPath, ref, entryPath), commitID, c.ttl()) } diff --git a/modules/git/last_commit_cache_gogit.go b/modules/git/last_commit_cache_gogit.go index fb09af6f2a..b57e9ad11f 100644 --- a/modules/git/last_commit_cache_gogit.go +++ b/modules/git/last_commit_cache_gogit.go @@ -9,7 +9,6 @@ package git import ( "context" - "path" "code.gitea.io/gitea/modules/log" @@ -93,15 +92,12 @@ func (c *LastCommitCache) recursiveCache(ctx context.Context, index cgobject.Com entryMap[entry.Name()] = entry } - commits, err := GetLastCommitForPaths(ctx, index, treePath, entryPaths) + commits, err := GetLastCommitForPaths(ctx, c, index, treePath, entryPaths) if err != nil { return err } - for entry, cm := range commits { - if err := c.Put(index.ID().String(), path.Join(treePath, entry), cm.ID().String()); err != nil { - return err - } + for entry := range commits { if entryMap[entry].IsDir() { subTree, err := tree.SubTree(entry) if err != nil { diff --git a/modules/git/last_commit_cache_nogogit.go b/modules/git/last_commit_cache_nogogit.go index f71e7350a1..5315c0a152 100644 --- a/modules/git/last_commit_cache_nogogit.go +++ b/modules/git/last_commit_cache_nogogit.go @@ -10,7 +10,6 @@ package git import ( "bufio" "context" - "path" "code.gitea.io/gitea/modules/log" ) @@ -80,28 +79,23 @@ func (c *LastCommitCache) recursiveCache(ctx context.Context, commit *Commit, tr } entryPaths := make([]string, len(entries)) - entryMap := make(map[string]*TreeEntry) for i, entry := range entries { entryPaths[i] = entry.Name() - entryMap[entry.Name()] = entry } - commits, err := GetLastCommitForPaths(ctx, commit, treePath, entryPaths) + _, err = WalkGitLog(ctx, c, commit.repo, commit, treePath, entryPaths...) if err != nil { return err } - for entry, entryCommit := range commits { - if err := c.Put(commit.ID.String(), path.Join(treePath, entry), entryCommit.ID.String()); err != nil { - return err - } + for _, treeEntry := range entries { // entryMap won't contain "" therefore skip this. - if treeEntry := entryMap[entry]; treeEntry != nil && treeEntry.IsDir() { - subTree, err := tree.SubTree(entry) + if treeEntry.IsDir() { + subTree, err := tree.SubTree(treeEntry.Name()) if err != nil { return err } - if err := c.recursiveCache(ctx, commit, subTree, entry, level-1); err != nil { + if err := c.recursiveCache(ctx, commit, subTree, treeEntry.Name(), level-1); err != nil { return err } } diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index 7d83ac7270..e792b02a5d 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -275,7 +275,9 @@ func (g *LogNameStatusRepoParser) Close() { } // WalkGitLog walks the git log --name-status for the head commit in the provided treepath and files -func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath string, paths ...string) (map[string]string, error) { +func WalkGitLog(ctx context.Context, cache *LastCommitCache, repo *Repository, head *Commit, treepath string, paths ...string) (map[string]string, error) { + headRef := head.ID.String() + tree, err := head.SubTree(treepath) if err != nil { return nil, err @@ -339,6 +341,9 @@ heaploop: for { select { case <-ctx.Done(): + if ctx.Err() == context.DeadlineExceeded { + break heaploop + } g.Close() return nil, ctx.Err() default: @@ -360,10 +365,16 @@ heaploop: changed[i] = false if results[i] == "" { results[i] = current.CommitID + if err := cache.Put(headRef, path.Join(treepath, paths[i]), current.CommitID); err != nil { + return nil, err + } delete(path2idx, paths[i]) remaining-- if results[0] == "" { results[0] = current.CommitID + if err := cache.Put(headRef, treepath, current.CommitID); err != nil { + return nil, err + } delete(path2idx, "") remaining-- } diff --git a/modules/git/notes_gogit.go b/modules/git/notes_gogit.go index 28dbbc0ee5..6cb719ce92 100644 --- a/modules/git/notes_gogit.go +++ b/modules/git/notes_gogit.go @@ -17,6 +17,7 @@ import ( ) // GetNote retrieves the git-notes data for a given commit. +// FIXME: Add LastCommitCache support func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) error { log.Trace("Searching for git note corresponding to the commit %q in the repository %q", commitID, repo.Path) notes, err := repo.GetCommit(NotesRef) @@ -75,7 +76,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) return err } - lastCommits, err := GetLastCommitForPaths(ctx, commitNode, "", []string{path}) + lastCommits, err := GetLastCommitForPaths(ctx, nil, commitNode, "", []string{path}) if err != nil { log.Error("Unable to get the commit for the path %q. Error: %v", path, err) return err diff --git a/modules/git/notes_nogogit.go b/modules/git/notes_nogogit.go index 5afe8d4614..13b4b7b36a 100644 --- a/modules/git/notes_nogogit.go +++ b/modules/git/notes_nogogit.go @@ -16,6 +16,7 @@ import ( ) // GetNote retrieves the git-notes data for a given commit. +// FIXME: Add LastCommitCache support func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) error { log.Trace("Searching for git note corresponding to the commit %q in the repository %q", commitID, repo.Path) notes, err := repo.GetCommit(NotesRef) @@ -75,7 +76,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) path = path[idx+1:] } - lastCommits, err := GetLastCommitForPaths(ctx, notes, treePath, []string{path}) + lastCommits, err := GetLastCommitForPaths(ctx, nil, notes, treePath, []string{path}) if err != nil { log.Error("Unable to get the commit for the path %q. Error: %v", treePath, err) return err diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index c0a35bcb4f..0777a10e7b 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -7,6 +7,7 @@ package repo import ( "bytes" + gocontext "context" "encoding/base64" "fmt" gotemplate "html/template" @@ -16,6 +17,7 @@ import ( "path" "strconv" "strings" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" @@ -34,11 +36,12 @@ import ( ) const ( - tplRepoEMPTY base.TplName = "repo/empty" - tplRepoHome base.TplName = "repo/home" - tplWatchers base.TplName = "repo/watchers" - tplForks base.TplName = "repo/forks" - tplMigrating base.TplName = "repo/migrate/migrating" + tplRepoEMPTY base.TplName = "repo/empty" + tplRepoHome base.TplName = "repo/home" + tplRepoViewList base.TplName = "repo/view_list" + tplWatchers base.TplName = "repo/watchers" + tplForks base.TplName = "repo/forks" + tplMigrating base.TplName = "repo/migrate/migrating" ) type namedBlob struct { @@ -128,28 +131,8 @@ func getReadmeFileFromPath(commit *git.Commit, treePath string) (*namedBlob, err } func renderDirectory(ctx *context.Context, treeLink string) { - tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath) - if err != nil { - ctx.NotFoundOrServerError("Repo.Commit.SubTree", git.IsErrNotExist, err) - return - } - - entries, err := tree.ListEntries() - if err != nil { - ctx.ServerError("ListEntries", err) - return - } - entries.CustomSort(base.NaturalSortLess) - - var c *git.LastCommitCache - if setting.CacheService.LastCommit.Enabled && ctx.Repo.CommitsCount >= setting.CacheService.LastCommit.CommitsCount { - c = git.NewLastCommitCache(ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache()) - } - - var latestCommit *git.Commit - ctx.Data["Files"], latestCommit, err = entries.GetCommitsInfo(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, c) - if err != nil { - ctx.ServerError("GetCommitsInfo", err) + entries := renderDirectoryFiles(ctx, 1*time.Second) + if ctx.Written() { return } @@ -186,6 +169,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { isSymlink := entry.IsLink() target := entry if isSymlink { + var err error target, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { ctx.ServerError("FollowLinks", err) @@ -207,6 +191,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { name := entry.Name() isSymlink := entry.IsLink() if isSymlink { + var err error entry, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { ctx.ServerError("FollowLinks", err) @@ -237,6 +222,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { if entry == nil { continue } + var err error readmeFile, err = getReadmeFileFromPath(ctx.Repo.Commit, entry.GetSubJumpablePathName()) if err != nil { ctx.ServerError("getReadmeFileFromPath", err) @@ -365,34 +351,12 @@ func renderDirectory(ctx *context.Context, treeLink string) { } } - // Show latest commit info of repository in table header, - // or of directory if not in root directory. - ctx.Data["LatestCommit"] = latestCommit - verification := models.ParseCommitWithSignature(latestCommit) - - if err := models.CalculateTrustStatus(verification, ctx.Repo.Repository, nil); err != nil { - ctx.ServerError("CalculateTrustStatus", err) - return - } - ctx.Data["LatestCommitVerification"] = verification - - ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit) - - statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, ctx.Repo.Commit.ID.String(), db.ListOptions{}) - if err != nil { - log.Error("GetLatestCommitStatus: %v", err) - } - - ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(statuses) - ctx.Data["LatestCommitStatuses"] = statuses - // Check permission to add or upload new file. if ctx.Repo.CanWrite(models.UnitTypeCode) && ctx.Repo.IsViewBranch { ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchived ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived } - ctx.Data["SSHDomain"] = setting.SSH.Domain } func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) { @@ -614,8 +578,7 @@ func safeURL(address string) string { return u.String() } -// Home render repository home page -func Home(ctx *context.Context) { +func checkHomeCodeViewable(ctx *context.Context) { if len(ctx.Repo.Units) > 0 { if ctx.Repo.Repository.IsBeingCreated() { task, err := models.GetMigratingTask(ctx.Repo.Repository.ID) @@ -648,7 +611,6 @@ func Home(ctx *context.Context) { var firstUnit *models.Unit for _, repoUnit := range ctx.Repo.Units { if repoUnit.Type == models.UnitTypeCode { - renderCode(ctx) return } @@ -667,6 +629,145 @@ func Home(ctx *context.Context) { ctx.NotFound("Home", fmt.Errorf(ctx.Tr("units.error.no_unit_allowed_repo"))) } +// Home render repository home page +func Home(ctx *context.Context) { + checkHomeCodeViewable(ctx) + if ctx.Written() { + return + } + + renderCode(ctx) +} + +// LastCommit returns lastCommit data for the provided branch/tag/commit and directory (in url) and filenames in body +func LastCommit(ctx *context.Context) { + checkHomeCodeViewable(ctx) + if ctx.Written() { + return + } + + renderDirectoryFiles(ctx, 0) + if ctx.Written() { + return + } + + var treeNames []string + paths := make([]string, 0, 5) + if len(ctx.Repo.TreePath) > 0 { + treeNames = strings.Split(ctx.Repo.TreePath, "/") + for i := range treeNames { + paths = append(paths, strings.Join(treeNames[:i+1], "/")) + } + + ctx.Data["HasParentPath"] = true + if len(paths)-2 >= 0 { + ctx.Data["ParentPath"] = "/" + paths[len(paths)-2] + } + } + branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() + ctx.Data["BranchLink"] = branchLink + + ctx.HTML(http.StatusOK, tplRepoViewList) +} + +func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entries { + tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath) + if err != nil { + ctx.NotFoundOrServerError("Repo.Commit.SubTree", git.IsErrNotExist, err) + return nil + } + + ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + ctx.Repo.CommitID + "/" + ctx.Repo.TreePath + + // Get current entry user currently looking at. + entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + if err != nil { + ctx.NotFoundOrServerError("Repo.Commit.GetTreeEntryByPath", git.IsErrNotExist, err) + return nil + } + + if !entry.IsDir() { + ctx.NotFoundOrServerError("Repo.Commit.GetTreeEntryByPath", git.IsErrNotExist, err) + return nil + } + + allEntries, err := tree.ListEntries() + if err != nil { + ctx.ServerError("ListEntries", err) + return nil + } + allEntries.CustomSort(base.NaturalSortLess) + + commitInfoCtx := gocontext.Context(ctx) + if timeout > 0 { + var cancel gocontext.CancelFunc + commitInfoCtx, cancel = gocontext.WithTimeout(ctx, timeout) + defer cancel() + } + + var c *git.LastCommitCache + if setting.CacheService.LastCommit.Enabled && ctx.Repo.CommitsCount >= setting.CacheService.LastCommit.CommitsCount { + c = git.NewLastCommitCache(ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache()) + } + + selected := map[string]bool{} + for _, pth := range ctx.FormStrings("f[]") { + selected[pth] = true + } + + entries := allEntries + if len(selected) > 0 { + entries = make(git.Entries, 0, len(selected)) + for _, entry := range allEntries { + if selected[entry.Name()] { + entries = append(entries, entry) + } + } + } + + var latestCommit *git.Commit + ctx.Data["Files"], latestCommit, err = entries.GetCommitsInfo(commitInfoCtx, ctx.Repo.Commit, ctx.Repo.TreePath, c) + if err != nil { + ctx.ServerError("GetCommitsInfo", err) + return nil + } + + // Show latest commit info of repository in table header, + // or of directory if not in root directory. + ctx.Data["LatestCommit"] = latestCommit + if latestCommit != nil { + + verification := models.ParseCommitWithSignature(latestCommit) + + if err := models.CalculateTrustStatus(verification, ctx.Repo.Repository, nil); err != nil { + ctx.ServerError("CalculateTrustStatus", err) + return nil + } + ctx.Data["LatestCommitVerification"] = verification + ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit) + } + + statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, ctx.Repo.Commit.ID.String(), db.ListOptions{}) + if err != nil { + log.Error("GetLatestCommitStatus: %v", err) + } + + ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(statuses) + ctx.Data["LatestCommitStatuses"] = statuses + + branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() + treeLink := branchLink + + if len(ctx.Repo.TreePath) > 0 { + treeLink += "/" + ctx.Repo.TreePath + } + + ctx.Data["TreeLink"] = treeLink + ctx.Data["SSHDomain"] = setting.SSH.Domain + + return allEntries +} + func renderLanguageStats(ctx *context.Context) { langs, err := ctx.Repo.Repository.GetTopLanguageStats(5) if err != nil { diff --git a/routers/web/web.go b/routers/web/web.go index e12552f4d4..01d90d206f 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -995,6 +995,9 @@ func RegisterRoutes(m *web.Route) { m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) }, ignSignIn, context.RepoAssignment, context.UnitTypes()) + + m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit) + m.Group("/{username}/{reponame}", func() { m.Get("/stars", repo.Stars) m.Get("/watchers", repo.Watchers) diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index fc66fb6b2d..3e7bcbe505 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -1,36 +1,40 @@ -<table id="repo-files-table" class="ui single line table"> +<table id="repo-files-table" class="ui single line table" data-last-commit-loader-url="{{.LastCommitLoaderURL}}"> <thead> <tr class="commit-list"> - <th colspan="2"> - {{if .LatestCommitUser}} - {{avatar .LatestCommitUser 24}} - {{if .LatestCommitUser.FullName}} - <a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> - {{else}} - <a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a> - {{end}} + <th colspan="2" {{if not .LatestCommit}}class="notready"{{end}}> + {{if not .LatestCommit}} + <div class="ui active tiny slow centered inline">…</div> {{else}} - {{if .LatestCommit.Author}} - {{avatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24}} - <strong>{{.LatestCommit.Author.Name}}</strong> - {{end}} - {{end}} - <a rel="nofollow" class="ui sha label {{if .LatestCommit.Signature}} isSigned {{if .LatestCommitVerification.Verified }} isVerified{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}Untrusted{{else}}Unmatched{{end}}{{else if .LatestCommitVerification.Warning}} isWarning{{end}}{{end}}" href="{{.RepoLink}}/commit/{{.LatestCommit.ID}}"> - <span class="shortsha">{{ShortSha .LatestCommit.ID.String}}</span> - {{if .LatestCommit.Signature}} - {{template "repo/shabox_badge" dict "root" $ "verification" .LatestCommitVerification}} + {{if .LatestCommitUser}} + {{avatar .LatestCommitUser 24}} + {{if .LatestCommitUser.FullName}} + <a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> + {{else}} + <a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a> + {{end}} + {{else}} + {{if .LatestCommit.Author}} + {{avatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24}} + <strong>{{.LatestCommit.Author.Name}}</strong> + {{end}} {{end}} - </a> - {{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses "root" $}} - {{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }} - <span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span> - {{if IsMultilineCommitMessage .LatestCommit.Message}} - <button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> - <pre class="commit-body" style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> + <a rel="nofollow" class="ui sha label {{if .LatestCommit.Signature}} isSigned {{if .LatestCommitVerification.Verified }} isVerified{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}Untrusted{{else}}Unmatched{{end}}{{else if .LatestCommitVerification.Warning}} isWarning{{end}}{{end}}" href="{{.RepoLink}}/commit/{{.LatestCommit.ID}}"> + <span class="shortsha">{{ShortSha .LatestCommit.ID.String}}</span> + {{if .LatestCommit.Signature}} + {{template "repo/shabox_badge" dict "root" $ "verification" .LatestCommitVerification}} + {{end}} + </a> + {{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses "root" $}} + {{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }} + <span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span> + {{if IsMultilineCommitMessage .LatestCommit.Message}} + <button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> + <pre class="commit-body" style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> + {{end}} + </span> {{end}} - </span> </th> - <th class="text grey right age">{{if .LatestCommit.Author}}{{TimeSince .LatestCommit.Author.When $.Lang}}{{end}}</th> + <th class="text grey right age">{{if .LatestCommit}}{{if .LatestCommit.Author}}{{TimeSince .LatestCommit.Author.When $.Lang}}{{end}}{{end}}</th> </tr> </thead> <tbody> @@ -43,7 +47,7 @@ {{$entry := $item.Entry}} {{$commit := $item.Commit}} {{$subModuleFile := $item.SubModuleFile}} - <tr> + <tr data-entryname="{{$entry.Name}}" data-ready="{{if $commit}}true{{else}}false{{end}}" class="{{if not $commit}}not{{end}}ready entry"> <td class="name four wide"> <span class="truncate"> {{if $entry.IsSubModule}} @@ -75,10 +79,14 @@ </td> <td class="message nine wide"> <span class="truncate"> - <a href="{{$.RepoLink}}/commit/{{$commit.ID}}" title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a> + {{if $commit}} + <a href="{{$.RepoLink}}/commit/{{$commit.ID}}" title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a> + {{else}} + <div class="ui active tiny slow centered inline">…</div> + {{end}} </span> </td> - <td class="text right age three wide">{{TimeSince $commit.Committer.When $.Lang}}</td> + <td class="text right age three wide">{{if $commit}}{{TimeSince $commit.Committer.When $.Lang}}{{end}}</td> </tr> {{end}} </tbody> diff --git a/web_src/js/features/lastcommitloader.js b/web_src/js/features/lastcommitloader.js new file mode 100644 index 0000000000..964255f229 --- /dev/null +++ b/web_src/js/features/lastcommitloader.js @@ -0,0 +1,40 @@ +const {csrf} = window.config; + +export async function initLastCommitLoader() { + const entryMap = {}; + + const entries = $('table#repo-files-table tr.notready') + .map((_, v) => { + entryMap[$(v).attr('data-entryname')] = $(v); + return $(v).attr('data-entryname'); + }) + .get(); + + if (entries.length === 0) { + return; + } + + const lastCommitLoaderURL = $('table#repo-files-table').data('lastCommitLoaderUrl'); + + if (entries.length > 200) { + $.post(lastCommitLoaderURL, { + _csrf: csrf, + }, (data) => { + $('table#repo-files-table').replaceWith(data); + }); + return; + } + + $.post(lastCommitLoaderURL, { + _csrf: csrf, + 'f': entries, + }, (data) => { + $(data).find('tr').each((_, row) => { + if (row.className === 'commit-list') { + $('table#repo-files-table .commit-list').replaceWith(row); + return; + } + entryMap[$(row).attr('data-entryname')].replaceWith(row); + }); + }); +} diff --git a/web_src/js/index.js b/web_src/js/index.js index 4fda303a3c..d6787e89bf 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -20,6 +20,7 @@ import initTableSort from './features/tablesort.js'; import {createCodeEditor, createMonaco} from './features/codeeditor.js'; import {initMarkupAnchors} from './markup/anchors.js'; import {initNotificationsTable, initNotificationCount} from './features/notification.js'; +import {initLastCommitLoader} from './features/lastcommitloader.js'; import {initStopwatch} from './features/stopwatch.js'; import {showLineButton} from './code/linebutton.js'; import {initMarkupContent, initCommentContent} from './markup/content.js'; @@ -2864,6 +2865,7 @@ $(document).ready(async () => { initContextPopups(); initTableSort(); initNotificationsTable(); + initLastCommitLoader(); initPullRequestMergeInstruction(); initFileViewToggle(); initReleaseEditor(); |