Repository: gohugoio/hugo (language: go)

Issue:
Unexpected mount behavior
I'm not sure how to better describe this, as I don't understand what is actually happening.

I am trying to use mounts to fill in missing translations.

Reference: <https://discourse.gohugo.io/t/37225>

Example (updated 2022-03-19T10:24:37-07:00):

```bash
git clone --single-branch -b hugo-forum-topic-37225 https://github.com/jmooring/hugo-testing hugo-forum-topic-37225
cd hugo-forum-topic-37225
hugo server
```


Discussion / hints:

Fix patch:
diff --git a/hugofs/fileinfo.go b/hugofs/fileinfo.go
index 40955545aee..29ac3c240e8 100644
--- a/hugofs/fileinfo.go
+++ b/hugofs/fileinfo.go
@@ -60,7 +60,6 @@ type FileMeta struct {
 	Module     string
 
 	Weight     int
-	Ordinal    int
 	IsOrdered  bool
 	IsSymlink  bool
 	IsRootFile bool
diff --git a/hugofs/filter_fs.go b/hugofs/filter_fs.go
index 9da63bbb794..ec3897d9e2b 100644
--- a/hugofs/filter_fs.go
+++ b/hugofs/filter_fs.go
@@ -47,10 +47,9 @@ func NewLanguageFs(langs map[string]int, fs afero.Fs) (afero.Fs, error) {
 			lang := meta.Lang
 
 			fileLang, translationBaseName, translationBaseNameWithExt := langInfoFrom(langs, fi.Name())
-			weight := 0
+			weight := meta.Weight
 
 			if fileLang != "" {
-				weight = 1
 				if fileLang == lang {
 					// Give priority to myfile.sv.txt inside the sv filesystem.
 					weight++
@@ -63,7 +62,6 @@ func NewLanguageFs(langs map[string]int, fs afero.Fs) (afero.Fs, error) {
 				&FileMeta{
 					Lang:                       lang,
 					Weight:                     weight,
-					Ordinal:                    langs[lang],
 					TranslationBaseName:        translationBaseName,
 					TranslationBaseNameWithExt: translationBaseNameWithExt,
 					Classifier:                 files.ClassifyContentFile(fi.Name(), meta.OpenFunc),
diff --git a/hugofs/language_composite_fs.go b/hugofs/language_composite_fs.go
index 09c4540a97b..65bc89e718a 100644
--- a/hugofs/language_composite_fs.go
+++ b/hugofs/language_composite_fs.go
@@ -15,7 +15,6 @@ package hugofs
 
 import (
 	"os"
-	"path"
 
 	"github.com/spf13/afero"
 )
@@ -56,32 +55,20 @@ func (fs *languageCompositeFs) Open(name string) (afero.File, error) {
 // LanguageDirsMerger implements the afero.DirsMerger interface, which is used
 // to merge two directories.
 var LanguageDirsMerger = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
-	m := make(map[string]FileMetaInfo)
-
-	getKey := func(fim FileMetaInfo) string {
-		return path.Join(fim.Meta().Lang, fim.Name())
-	}
-
-	for _, fi := range lofi {
-		fim := fi.(FileMetaInfo)
-		m[getKey(fim)] = fim
-	}
-
-	for _, fi := range bofi {
-		fim := fi.(FileMetaInfo)
-		key := getKey(fim)
-		_, found := m[key]
+	for _, fi1 := range bofi {
+		fim1 := fi1.(FileMetaInfo)
+		var found bool
+		for _, fi2 := range lofi {
+			fim2 := fi2.(FileMetaInfo)
+			if fi1.Name() == fi2.Name() && fim1.Meta().Lang == fim2.Meta().Lang {
+				found = true
+				break
+			}
+		}
 		if !found {
-			m[key] = fim
+			lofi = append(lofi, fi1)
 		}
 	}
 
-	merged := make([]os.FileInfo, len(m))
-	i := 0
-	for _, v := range m {
-		merged[i] = v
-		i++
-	}
-
-	return merged, nil
+	return lofi, nil
 }
diff --git a/hugofs/walk.go b/hugofs/walk.go
index c02d366713f..26f1b74e038 100644
--- a/hugofs/walk.go
+++ b/hugofs/walk.go
@@ -224,13 +224,8 @@ func (w *Walkway) walk(path string, info FileMetaInfo, dirEntries []FileMetaInfo
 				// to the closest one.
 				fiw, fjw := fim.Weight, fjm.Weight
 				if fiw != fjw {
-					return fiw > fjw
-				}
 
-				// Explicit order set.
-				fio, fjo := fim.Ordinal, fjm.Ordinal
-				if fio != fjo {
-					return fio < fjo
+					return fiw > fjw
 				}
 
 				// When we walk into a symlink, we keep the reference to
diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go
index 2e32932c66b..6e3f88a4be1 100644
--- a/hugolib/filesystems/basefs.go
+++ b/hugolib/filesystems/basefs.go
@@ -602,6 +602,7 @@ func (b *sourceFilesystemsBuilder) createMainOverlayFs(p *paths.Paths) (*filesys
 			Module:        mod,
 			dir:           dir,
 			isMainProject: isMainProject,
+			ordinal:       j,
 		}
 		j++
 	}
@@ -635,12 +636,12 @@ func (b *sourceFilesystemsBuilder) createModFs(
 		return md.dir, paths.AbsPathify(md.dir, path)
 	}
 
-	for _, mount := range md.Mounts() {
+	for i, mount := range md.Mounts() {
 
-		mountWeight := 1
-		if md.isMainProject {
-			mountWeight++
-		}
+		// Add more weight to early mounts.
+		// When two mounts contain the same filename,
+		// the first entry wins.
+		mountWeight := (10 + md.ordinal) * (len(md.Mounts()) - i)
 
 		inclusionFilter, err := glob.NewFilenameFilter(
 			types.ToStringSlicePreserveString(mount.IncludeFiles),
@@ -829,6 +830,7 @@ type mountsDescriptor struct {
 	modules.Module
 	dir           string
 	isMainProject bool
+	ordinal       int
 }
 
 func (b *sourceFilesystemsBuilder) createOverlayFs(collector *filesystemsCollector, mounts []mountsDescriptor) error {


Test patch:
diff --git a/hugolib/language_content_dir_test.go b/hugolib/language_content_dir_test.go
index 6825315664b..54187822068 100644
--- a/hugolib/language_content_dir_test.go
+++ b/hugolib/language_content_dir_test.go
@@ -403,3 +403,124 @@ Page: /fr/event/page2/|ev-fr2
 Page: /fr/other/page1/|other-fr1
 Page: /fr/other/page2/|other-fr2`)
 }
+
+// Issue 9693
+func TestContentMountMerge(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- config.toml --
+baseURL = 'https://example.org/'
+languageCode = 'en-us'
+title = 'Hugo Forum Topic #37225'
+theme = 'mytheme'
+
+disableKinds = ['sitemap','RSS','taxonomy','term']
+defaultContentLanguage = 'en'
+defaultContentLanguageInSubdir = true
+
+[languages.en]
+languageName = 'English'
+weight = 1
+[languages.de]
+languageName = 'Deutsch'
+weight = 2
+[languages.nl]
+languageName = 'Nederlands'
+weight = 3
+
+# EN content
+[[module.mounts]]
+source = 'content/en'
+target = 'content'
+lang = 'en'
+
+# DE content
+[[module.mounts]]
+source = 'content/de'
+target = 'content'
+lang = 'de'
+
+# This fills in the gaps in DE content with EN content
+[[module.mounts]]
+source = 'content/en'
+target = 'content'
+lang = 'de'
+
+# NL content
+[[module.mounts]]
+source = 'content/nl'
+target = 'content'
+lang = 'nl'
+
+# This should fill in the gaps in NL content with EN content
+[[module.mounts]]
+source = 'content/en'
+target = 'content'
+lang = 'nl'
+
+-- content/de/_index.md --
+---
+title: "home (de)"
+---
+-- content/de/p1.md --
+---
+title: "p1 (de)"
+---
+-- content/en/_index.md --
+---
+title: "home (en)"
+---
+-- content/en/p1.md --
+---
+title: "p1 (en)"
+---
+-- content/en/p2.md --
+---
+title: "p2 (en)"
+---
+-- content/en/p3.md --
+---
+title: "p3 (en)"
+---
+-- content/nl/_index.md --
+---
+title: "home (nl)"
+---
+-- content/nl/p1.md --
+---
+title: "p1 (nl)"
+---
+-- content/nl/p3.md --
+---
+title: "p3 (nl)"
+---
+-- layouts/home.html --
+{{ .Title }}: {{ site.Language.Lang }}: {{ range site.RegularPages }}{{ .Title }}|{{ end }}:END
+-- themes/mytheme/config.toml --
+[[module.mounts]]
+source = 'content/nlt'
+target = 'content'
+lang = 'nl'
+-- themes/mytheme/content/nlt/p3.md --
+---
+title: "p3 theme (nl)"
+---
+-- themes/mytheme/content/nlt/p4.md --
+---
+title: "p4 theme (nl)"
+---
+`
+
+	b := NewIntegrationTestBuilder(
+		IntegrationTestConfig{
+			T:           t,
+			TxtarString: files,
+		},
+	).Build()
+
+	b.AssertFileContent("public/nl/index.html", `home (nl): nl: p1 (nl)|p2 (en)|p3 (nl)|p4 theme (nl)|:END`)
+	b.AssertFileContent("public/de/index.html", `home (de): de: p1 (de)|p2 (en)|p3 (en)|:END`)
+	b.AssertFileContent("public/en/index.html", `home (en): en: p1 (en)|p2 (en)|p3 (en)|:END`)
+
+}