Skip to content

Commit e913202

Browse files
committed
Fix server rebuilds when adding sub sections especially on Windows
This commit also optimizes for the case where change events for both file (e.g. `_index.md`) and the container directory comes in the same event batch. While testing this on Windows 11 (ARM64), I notice that Windows behaves a little oddly when dumping a folder of files into the content tree; it works (at least after this commit), but it seems like the event batching behaves differently compared to other OSes (even older Win versions). A related tip would be to try starting the server with polling, to see if that improves the situation, e.g.: ``` hugo server --poll 700ms ``` Fixes gohugoio#12230
1 parent b4bff61 commit e913202

File tree

3 files changed

+76
-30
lines changed

3 files changed

+76
-30
lines changed

hugolib/hugo_sites_build.go

+7-29
Original file line numberDiff line numberDiff line change
@@ -595,8 +595,10 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
595595
return sb.String()
596596
}))
597597

598+
// For a list of events for the different OSes, see the test output in https://github.com/bep/fsnotifyeventlister/.
598599
events = h.fileEventsFilter(events)
599600
events = h.fileEventsTranslate(events)
601+
eventInfos := h.fileEventsApplyInfo(events)
600602

601603
logger := h.Log
602604

@@ -631,36 +633,12 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
631633
addedContentPaths []*paths.Path
632634
)
633635

634-
for _, ev := range events {
635-
removed := false
636-
added := false
637-
638-
if ev.Op&fsnotify.Remove == fsnotify.Remove {
639-
removed = true
640-
}
641-
642-
fi, statErr := h.Fs.Source.Stat(ev.Name)
643-
644-
// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
645-
// Sometimes a rename operation means that file has been renamed other times it means
646-
// it's been updated.
647-
if ev.Op.Has(fsnotify.Rename) {
648-
// If the file is still on disk, it's only been updated, if it's not, it's been moved
649-
if statErr != nil {
650-
removed = true
651-
}
652-
}
653-
if ev.Op.Has(fsnotify.Create) {
654-
added = true
655-
}
656-
657-
isChangedDir := statErr == nil && fi.IsDir()
658-
636+
for _, ev := range eventInfos {
659637
cpss := h.BaseFs.ResolvePaths(ev.Name)
660638
pss := make([]*paths.Path, len(cpss))
661639
for i, cps := range cpss {
662640
p := cps.Path
663-
if removed && !paths.HasExt(p) {
641+
if ev.removed && !paths.HasExt(p) {
664642
// Assume this is a renamed/removed directory.
665643
// For deletes, we walk up the tree to find the container (e.g. branch bundle),
666644
// 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
671649
}
672650

673651
pss[i] = h.Configs.ContentPathParser.Parse(cps.Component, p)
674-
if added && !isChangedDir && cps.Component == files.ComponentFolderContent {
652+
if ev.added && !ev.isChangedDir && cps.Component == files.ComponentFolderContent {
675653
addedContentPaths = append(addedContentPaths, pss[i])
676654
}
677655

@@ -683,9 +661,9 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
683661
}
684662
}
685663

686-
if removed {
664+
if ev.removed {
687665
changedPaths.deleted = append(changedPaths.deleted, pss...)
688-
} else if isChangedDir {
666+
} else if ev.isChangedDir {
689667
changedPaths.changedDirs = append(changedPaths.changedDirs, pss...)
690668
} else {
691669
changedPaths.changedFiles = append(changedPaths.changedFiles, pss...)

hugolib/pages_capture.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func (c *pagesCollector) Collect() (collectErr error) {
161161
// We always start from a directory.
162162
collectErr = c.collectDir(id.p, id.isDir, func(fim hugofs.FileMetaInfo) bool {
163163
if id.delete || id.isDir {
164-
if id.isDir {
164+
if id.isDir && fim.Meta().PathInfo.IsLeafBundle() {
165165
return strings.HasPrefix(fim.Meta().PathInfo.Path(), paths.AddTrailingSlash(id.p.Path()))
166166
}
167167

hugolib/site.go

+68
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"io"
2020
"mime"
2121
"net/url"
22+
"os"
2223
"path/filepath"
2324
"runtime"
2425
"sort"
@@ -426,6 +427,73 @@ func (h *HugoSites) fileEventsFilter(events []fsnotify.Event) []fsnotify.Event {
426427
return events[:n]
427428
}
428429

430+
type fileEventInfo struct {
431+
fsnotify.Event
432+
fi os.FileInfo
433+
added bool
434+
removed bool
435+
isChangedDir bool
436+
}
437+
438+
func (h *HugoSites) fileEventsApplyInfo(events []fsnotify.Event) []fileEventInfo {
439+
var infos []fileEventInfo
440+
for _, ev := range events {
441+
removed := false
442+
added := false
443+
444+
if ev.Op&fsnotify.Remove == fsnotify.Remove {
445+
removed = true
446+
}
447+
448+
fi, statErr := h.Fs.Source.Stat(ev.Name)
449+
450+
// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
451+
// Sometimes a rename operation means that file has been renamed other times it means
452+
// it's been updated.
453+
if ev.Op.Has(fsnotify.Rename) {
454+
// If the file is still on disk, it's only been updated, if it's not, it's been moved
455+
if statErr != nil {
456+
removed = true
457+
}
458+
}
459+
if ev.Op.Has(fsnotify.Create) {
460+
added = true
461+
}
462+
463+
isChangedDir := statErr == nil && fi.IsDir()
464+
465+
infos = append(infos, fileEventInfo{
466+
Event: ev,
467+
fi: fi,
468+
added: added,
469+
removed: removed,
470+
isChangedDir: isChangedDir,
471+
})
472+
}
473+
474+
n := 0
475+
476+
for _, ev := range infos {
477+
// Remove any directories that's also represented by a file.
478+
keep := true
479+
if ev.isChangedDir {
480+
for _, ev2 := range infos {
481+
if ev2.fi != nil && !ev2.fi.IsDir() && filepath.Dir(ev2.Name) == ev.Name {
482+
keep = false
483+
break
484+
}
485+
}
486+
}
487+
if keep {
488+
infos[n] = ev
489+
n++
490+
}
491+
}
492+
infos = infos[:n]
493+
494+
return infos
495+
}
496+
429497
func (h *HugoSites) fileEventsTranslate(events []fsnotify.Event) []fsnotify.Event {
430498
eventMap := make(map[string][]fsnotify.Event)
431499

0 commit comments

Comments
 (0)