mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
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 #12230
This commit is contained in:
parent
f038a51b3e
commit
07b2e535be
3 changed files with 76 additions and 30 deletions
|
@ -595,8 +595,10 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||||
return sb.String()
|
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.fileEventsFilter(events)
|
||||||
events = h.fileEventsTranslate(events)
|
events = h.fileEventsTranslate(events)
|
||||||
|
eventInfos := h.fileEventsApplyInfo(events)
|
||||||
|
|
||||||
logger := h.Log
|
logger := h.Log
|
||||||
|
|
||||||
|
@ -631,36 +633,12 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||||
addedContentPaths []*paths.Path
|
addedContentPaths []*paths.Path
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, ev := range events {
|
for _, ev := range eventInfos {
|
||||||
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()
|
|
||||||
|
|
||||||
cpss := h.BaseFs.ResolvePaths(ev.Name)
|
cpss := h.BaseFs.ResolvePaths(ev.Name)
|
||||||
pss := make([]*paths.Path, len(cpss))
|
pss := make([]*paths.Path, len(cpss))
|
||||||
for i, cps := range cpss {
|
for i, cps := range cpss {
|
||||||
p := cps.Path
|
p := cps.Path
|
||||||
if removed && !paths.HasExt(p) {
|
if ev.removed && !paths.HasExt(p) {
|
||||||
// Assume this is a renamed/removed directory.
|
// Assume this is a renamed/removed directory.
|
||||||
// For deletes, we walk up the tree to find the container (e.g. branch bundle),
|
// 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.
|
// 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)
|
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])
|
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...)
|
changedPaths.deleted = append(changedPaths.deleted, pss...)
|
||||||
} else if isChangedDir {
|
} else if ev.isChangedDir {
|
||||||
changedPaths.changedDirs = append(changedPaths.changedDirs, pss...)
|
changedPaths.changedDirs = append(changedPaths.changedDirs, pss...)
|
||||||
} else {
|
} else {
|
||||||
changedPaths.changedFiles = append(changedPaths.changedFiles, pss...)
|
changedPaths.changedFiles = append(changedPaths.changedFiles, pss...)
|
||||||
|
|
|
@ -161,7 +161,7 @@ func (c *pagesCollector) Collect() (collectErr error) {
|
||||||
// We always start from a directory.
|
// We always start from a directory.
|
||||||
collectErr = c.collectDir(id.p, id.isDir, func(fim hugofs.FileMetaInfo) bool {
|
collectErr = c.collectDir(id.p, id.isDir, func(fim hugofs.FileMetaInfo) bool {
|
||||||
if id.delete || id.isDir {
|
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()))
|
return strings.HasPrefix(fim.Meta().PathInfo.Path(), paths.AddTrailingSlash(id.p.Path()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -426,6 +427,73 @@ func (h *HugoSites) fileEventsFilter(events []fsnotify.Event) []fsnotify.Event {
|
||||||
return events[:n]
|
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 {
|
func (h *HugoSites) fileEventsTranslate(events []fsnotify.Event) []fsnotify.Event {
|
||||||
eventMap := make(map[string][]fsnotify.Event)
|
eventMap := make(map[string][]fsnotify.Event)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue