@@ -56,35 +56,54 @@ type namedBlob struct {
5656 blob * git.Blob
5757}
5858
59+ // locate a README for a tree in one of the supported paths.
60+ //
61+ // entries is passed to reduce calls to ListEntries(), so
62+ // this has precondition:
63+ //
64+ // entries == ctx.Repo.Commit.SubTree(ctx.Repo.TreePath).ListEntries()
65+ //
5966// FIXME: There has to be a more efficient way of doing this
60- func getReadmeFileFromPath (ctx * context.Context , commit * git.Commit , treePath string ) (* namedBlob , error ) {
61- tree , err := commit .SubTree (treePath )
62- if err != nil {
63- return nil , err
64- }
65-
66- entries , err := tree .ListEntries ()
67- if err != nil {
68- return nil , err
69- }
70-
67+ func findReadmeFileInEntries (ctx * context.Context , entries []* git.TreeEntry ) (* namedBlob , error ) {
7168 // Create a list of extensions in priority order
7269 // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
7370 // 2. Txt files - e.g. README.txt
7471 // 3. No extension - e.g. README
7572 exts := append (localizedExtensions (".md" , ctx .Language ()), ".txt" , "" ) // sorted by priority
7673 extCount := len (exts )
7774 readmeFiles := make ([]* namedBlob , extCount + 1 )
75+
76+ docsEntries := make ([]* git.TreeEntry , 3 ) // (one of docs/, .gitea/ or .github/)
7877 for _ , entry := range entries {
7978 if entry .IsDir () {
79+ // as a special case for the top-level repo introduction README,
80+ // fall back to subfolders, looking for e.g. docs/README.md, .gitea/README.zh-CN.txt, .github/README.txt, ...
81+ // (note that docsEntries is ignored unless we are at the root)
82+ lowerName := strings .ToLower (entry .Name ())
83+ switch lowerName {
84+ case "docs" :
85+ if entry .Name () == "docs" || docsEntries [0 ] == nil {
86+ docsEntries [0 ] = entry
87+ }
88+ case ".gitea" :
89+ if entry .Name () == ".gitea" || docsEntries [1 ] == nil {
90+ docsEntries [1 ] = entry
91+ }
92+ case ".github" :
93+ if entry .Name () == ".github" || docsEntries [2 ] == nil {
94+ docsEntries [2 ] = entry
95+ }
96+ }
8097 continue
8198 }
8299 if i , ok := markup .IsReadmeFileExtension (entry .Name (), exts ... ); ok {
100+ log .Debug ("Potential readme file: %s" , entry .Name ())
83101 if readmeFiles [i ] == nil || base .NaturalSortLess (readmeFiles [i ].name , entry .Blob ().Name ()) {
84102 name := entry .Name ()
85103 isSymlink := entry .IsLink ()
86104 target := entry
87105 if isSymlink {
106+ var err error
88107 target , err = entry .FollowLinks ()
89108 if err != nil && ! git .IsErrBadLink (err ) {
90109 return nil , err
@@ -107,6 +126,33 @@ func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath st
107126 break
108127 }
109128 }
129+
130+ if ctx .Repo .TreePath == "" && readmeFile == nil {
131+ for _ , subTreeEntry := range docsEntries {
132+ if subTreeEntry == nil {
133+ continue
134+ }
135+ subTree := subTreeEntry .Tree ()
136+ if subTree == nil {
137+ // this should be impossible; if subTreeEntry exists so should this.
138+ continue
139+ }
140+ var err error
141+ childEntries , err := subTree .ListEntries ()
142+ if err != nil {
143+ return nil , err
144+ }
145+ readmeFile , err = findReadmeFileInEntries (ctx , childEntries )
146+ if err != nil && ! git .IsErrNotExist (err ) {
147+ return nil , err
148+ }
149+ if readmeFile != nil {
150+ readmeFile .name = subTreeEntry .Name () + "/" + readmeFile .name
151+ break
152+ }
153+ }
154+ }
155+
110156 return readmeFile , nil
111157}
112158
@@ -127,12 +173,20 @@ func renderDirectory(ctx *context.Context, treeLink string) {
127173 ctx .Data ["CanUploadFile" ] = setting .Repository .Upload .Enabled && ! ctx .Repo .Repository .IsArchived
128174 }
129175
130- readmeFile , readmeTreelink := findReadmeFile (ctx , entries , treeLink )
131- if ctx .Written () || readmeFile == nil {
176+ if ctx .Written () {
177+ return
178+ }
179+
180+ readmeFile , err := findReadmeFileInEntries (ctx , entries )
181+ if err != nil {
182+ ctx .ServerError ("findReadmeFileInEntries" , err )
183+ return
184+ }
185+ if readmeFile == nil {
132186 return
133187 }
134188
135- renderReadmeFile (ctx , readmeFile , readmeTreelink )
189+ renderReadmeFile (ctx , readmeFile , treeLink )
136190}
137191
138192// localizedExtensions prepends the provided language code with and without a
@@ -157,89 +211,6 @@ func localizedExtensions(ext, languageCode string) (localizedExts []string) {
157211 return []string {lowerLangCode + ext , ext }
158212}
159213
160- func findReadmeFile (ctx * context.Context , entries git.Entries , treeLink string ) (* namedBlob , string ) {
161- // Create a list of extensions in priority order
162- // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
163- // 2. Txt files - e.g. README.txt
164- // 3. No extension - e.g. README
165- exts := append (localizedExtensions (".md" , ctx .Language ()), ".txt" , "" ) // sorted by priority
166- extCount := len (exts )
167- readmeFiles := make ([]* namedBlob , extCount + 1 )
168-
169- docsEntries := make ([]* git.TreeEntry , 3 ) // (one of docs/, .gitea/ or .github/)
170- for _ , entry := range entries {
171- if entry .IsDir () {
172- lowerName := strings .ToLower (entry .Name ())
173- switch lowerName {
174- case "docs" :
175- if entry .Name () == "docs" || docsEntries [0 ] == nil {
176- docsEntries [0 ] = entry
177- }
178- case ".gitea" :
179- if entry .Name () == ".gitea" || docsEntries [1 ] == nil {
180- docsEntries [1 ] = entry
181- }
182- case ".github" :
183- if entry .Name () == ".github" || docsEntries [2 ] == nil {
184- docsEntries [2 ] = entry
185- }
186- }
187- continue
188- }
189-
190- if i , ok := markup .IsReadmeFileExtension (entry .Name (), exts ... ); ok {
191- log .Debug ("Potential readme file: %s" , entry .Name ())
192- name := entry .Name ()
193- isSymlink := entry .IsLink ()
194- target := entry
195- if isSymlink {
196- var err error
197- target , err = entry .FollowLinks ()
198- if err != nil && ! git .IsErrBadLink (err ) {
199- ctx .ServerError ("FollowLinks" , err )
200- return nil , ""
201- }
202- }
203- if target != nil && (target .IsExecutable () || target .IsRegular ()) {
204- readmeFiles [i ] = & namedBlob {
205- name ,
206- isSymlink ,
207- target .Blob (),
208- }
209- }
210- }
211- }
212-
213- var readmeFile * namedBlob
214- readmeTreelink := treeLink
215- for _ , f := range readmeFiles {
216- if f != nil {
217- readmeFile = f
218- break
219- }
220- }
221-
222- if ctx .Repo .TreePath == "" && readmeFile == nil {
223- for _ , entry := range docsEntries {
224- if entry == nil {
225- continue
226- }
227- var err error
228- readmeFile , err = getReadmeFileFromPath (ctx , ctx .Repo .Commit , entry .GetSubJumpablePathName ())
229- if err != nil {
230- ctx .ServerError ("getReadmeFileFromPath" , err )
231- return nil , ""
232- }
233- if readmeFile != nil {
234- readmeFile .name = entry .Name () + "/" + readmeFile .name
235- readmeTreelink = treeLink + "/" + util .PathEscapeSegments (entry .GetSubJumpablePathName ())
236- break
237- }
238- }
239- }
240- return readmeFile , readmeTreelink
241- }
242-
243214type fileInfo struct {
244215 isTextFile bool
245216 isLFSFile bool
@@ -342,7 +313,7 @@ func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelin
342313 ctx .Data ["EscapeStatus" ], ctx .Data ["FileContent" ], err = markupRender (ctx , & markup.RenderContext {
343314 Ctx : ctx ,
344315 RelativePath : path .Join (ctx .Repo .TreePath , readmeFile .name ), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
345- URLPrefix : readmeTreelink ,
316+ URLPrefix : path . Dir ( readmeTreelink ) ,
346317 Metas : ctx .Repo .Repository .ComposeDocumentMetas (),
347318 GitRepo : ctx .Repo .GitRepo ,
348319 }, rd )
0 commit comments