Repository: gohugoio/hugo (language: go)

Issue:
Windows: Server does not refresh sub-section content if the section was created after launch
### What version of Hugo are you using (`hugo version`)?

`hugo v0.123.8-5fed9c591b694f314e5939548e11cc3dcb79a79c windows/amd64 BuildDate=2024-03-07T13:14:42Z VendorInfo=gohugoio`

Microsoft Windows [Version 10.0.19045.4046]

### Does this issue reproduce with the latest release?
Yes.

Issue does not appear to be present in `hugo v0.119.0-b84644c008e0dc2c4b67bd69cccf87a41a03937e windows/amd64 BuildDate=2023-09-24T15:20:17Z VendorInfo=gohugoio` which is the previous build I had been using until recently.

### Description

While running `hugo server`, changes made to the content of newly created sub-sections will not be reflected in the browser after they are initially created. Restarting hugo reflects the updated content, and future changes are reflected correctly in any sub-sections which existed prior to hugo being launched.

Since I'm currently working on a site structured as a hierarchical directory, I'm running into this a lot, requiring a restart of `hugo server` each time I add a new sub-section.

### Steps to reproduce
Create new site and theme
```
hugo new site test-site
cd test-site
hugo new theme test-theme
```
Edit `hugo.toml` to add `theme = 'test-theme'`.

Launch `hugo server`
```
hugo server -D
```

The `-D` is not required, but useful as the default archetype is set to draft, so this simplifies the test. Alternatively, edit `archetypes/default.md` to remove `draft = true`.

In another terminal, create a top level section
```
hugo new content/top-section/_index.md
```
Navigate to `http://localhost:1313/top-section/` in your browser and observe the section has been created.

Make a noticeable change to the content of `content/top-section/_index.md` and save the file. Note the change is reflected in the browser.

Create a sub-section
```
hugo new content/top-section/sub-section/_index.md
```
The new section is reflected in the browser. Navigate to the new section.

Make a noticeable change to the content of `content/top-section/sub-section/_index.md` and save the file. 

**Unlike the change made to the top level section, changes made to the sub-section are not reflected in browser, either automatically, or by manually forcing a page refresh. Additional changes made to the file will also not be reflected.**

Also note that the change to the sub section content is also not reflected in the sub-section summary when viewing the top level section.

Terminate and re-launch hugo.
```
hugo server -D
```
Note that the updated content of sub-section is reflected in the browser as expected.

Make a noticeable change to the content of `content/top-section/sub-section/_index.md`. 

This and all further changes to the existing sub-section should now be reflected in the browser.



Discussion / hints:
@jmooring assuming you tested this, what OS did you run it on? I have ran through the above recipe a few times, but it seem to work fine with me on MacOS. 
Tested on Windows, will retest.
I can consistently reproduce this on Windows 11 with hugo v0.124.0-DEV-9ca1de09

Fix patch:
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index ddc712a2d67..7b8a6ef2326 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -595,8 +595,10 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
 		return sb.String()
 	}))
 
+	// For a list of events for the different OSes, see the test output in https://github.com/bep/fsnotifyeventlister/.
 	events = h.fileEventsFilter(events)
 	events = h.fileEventsTranslate(events)
+	eventInfos := h.fileEventsApplyInfo(events)
 
 	logger := h.Log
 
@@ -631,36 +633,12 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
 		addedContentPaths []*paths.Path
 	)
 
-	for _, ev := range events {
-		removed := false
-		added := false
-
-		if ev.Op&fsnotify.Remove == fsnotify.Remove {
-			removed = true
-		}
-
-		fi, statErr := h.Fs.Source.Stat(ev.Name)
-
-		// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
-		// Sometimes a rename operation means that file has been renamed other times it means
-		// it's been updated.
-		if ev.Op.Has(fsnotify.Rename) {
-			// If the file is still on disk, it's only been updated, if it's not, it's been moved
-			if statErr != nil {
-				removed = true
-			}
-		}
-		if ev.Op.Has(fsnotify.Create) {
-			added = true
-		}
-
-		isChangedDir := statErr == nil && fi.IsDir()
-
+	for _, ev := range eventInfos {
 		cpss := h.BaseFs.ResolvePaths(ev.Name)
 		pss := make([]*paths.Path, len(cpss))
 		for i, cps := range cpss {
 			p := cps.Path
-			if removed && !paths.HasExt(p) {
+			if ev.removed && !paths.HasExt(p) {
 				// Assume this is a renamed/removed directory.
 				// For deletes, we walk up the tree to find the container (e.g. branch bundle),
 				// so we will catch this even if it is a file without extension.
@@ -671,7 +649,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
 			}
 
 			pss[i] = h.Configs.ContentPathParser.Parse(cps.Component, p)
-			if added && !isChangedDir && cps.Component == files.ComponentFolderContent {
+			if ev.added && !ev.isChangedDir && cps.Component == files.ComponentFolderContent {
 				addedContentPaths = append(addedContentPaths, pss[i])
 			}
 
@@ -683,9 +661,9 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
 			}
 		}
 
-		if removed {
+		if ev.removed {
 			changedPaths.deleted = append(changedPaths.deleted, pss...)
-		} else if isChangedDir {
+		} else if ev.isChangedDir {
 			changedPaths.changedDirs = append(changedPaths.changedDirs, pss...)
 		} else {
 			changedPaths.changedFiles = append(changedPaths.changedFiles, pss...)
diff --git a/hugolib/pages_capture.go b/hugolib/pages_capture.go
index 4328978db1c..231c2efad15 100644
--- a/hugolib/pages_capture.go
+++ b/hugolib/pages_capture.go
@@ -161,7 +161,7 @@ func (c *pagesCollector) Collect() (collectErr error) {
 				// We always start from a directory.
 				collectErr = c.collectDir(id.p, id.isDir, func(fim hugofs.FileMetaInfo) bool {
 					if id.delete || id.isDir {
-						if id.isDir {
+						if id.isDir && fim.Meta().PathInfo.IsLeafBundle() {
 							return strings.HasPrefix(fim.Meta().PathInfo.Path(), paths.AddTrailingSlash(id.p.Path()))
 						}
 
diff --git a/hugolib/site.go b/hugolib/site.go
index 117e1014421..e7d170d0937 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -19,6 +19,7 @@ import (
 	"io"
 	"mime"
 	"net/url"
+	"os"
 	"path/filepath"
 	"runtime"
 	"sort"
@@ -426,6 +427,73 @@ func (h *HugoSites) fileEventsFilter(events []fsnotify.Event) []fsnotify.Event {
 	return events[:n]
 }
 
+type fileEventInfo struct {
+	fsnotify.Event
+	fi           os.FileInfo
+	added        bool
+	removed      bool
+	isChangedDir bool
+}
+
+func (h *HugoSites) fileEventsApplyInfo(events []fsnotify.Event) []fileEventInfo {
+	var infos []fileEventInfo
+	for _, ev := range events {
+		removed := false
+		added := false
+
+		if ev.Op&fsnotify.Remove == fsnotify.Remove {
+			removed = true
+		}
+
+		fi, statErr := h.Fs.Source.Stat(ev.Name)
+
+		// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
+		// Sometimes a rename operation means that file has been renamed other times it means
+		// it's been updated.
+		if ev.Op.Has(fsnotify.Rename) {
+			// If the file is still on disk, it's only been updated, if it's not, it's been moved
+			if statErr != nil {
+				removed = true
+			}
+		}
+		if ev.Op.Has(fsnotify.Create) {
+			added = true
+		}
+
+		isChangedDir := statErr == nil && fi.IsDir()
+
+		infos = append(infos, fileEventInfo{
+			Event:        ev,
+			fi:           fi,
+			added:        added,
+			removed:      removed,
+			isChangedDir: isChangedDir,
+		})
+	}
+
+	n := 0
+
+	for _, ev := range infos {
+		// Remove any directories that's also represented by a file.
+		keep := true
+		if ev.isChangedDir {
+			for _, ev2 := range infos {
+				if ev2.fi != nil && !ev2.fi.IsDir() && filepath.Dir(ev2.Name) == ev.Name {
+					keep = false
+					break
+				}
+			}
+		}
+		if keep {
+			infos[n] = ev
+			n++
+		}
+	}
+	infos = infos[:n]
+
+	return infos
+}
+
 func (h *HugoSites) fileEventsTranslate(events []fsnotify.Event) []fsnotify.Event {
 	eventMap := make(map[string][]fsnotify.Event)
 


Test patch: