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>
+					{{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 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}}
-				<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>
 			</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();