Skip to content

Commit

Permalink
Fix server rebuilds when adding sub sections especially on Windows
Browse files Browse the repository at this point in the history
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 #12230
  • Loading branch information
bep committed Mar 15, 2024
1 parent f038a51 commit 07b2e53
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 30 deletions.
36 changes: 7 additions & 29 deletions hugolib/hugo_sites_build.go
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand All @@ -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])
}

Expand All @@ -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...)
Expand Down
2 changes: 1 addition & 1 deletion hugolib/pages_capture.go
Expand Up @@ -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()))
}

Expand Down
68 changes: 68 additions & 0 deletions hugolib/site.go
Expand Up @@ -19,6 +19,7 @@ import (
"io"
"mime"
"net/url"
"os"
"path/filepath"
"runtime"
"sort"
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit 07b2e53

Please sign in to comment.