shithub: hugo

Download patch

ref: 7e196a82944148ed3f78f334303b452ab2bd4078
parent: e01c340915f971e86fef9a1c013a3183c50e51fc
author: Steve Francia <[email protected]>
date: Mon Jan 11 07:06:52 EST 2016

Handle remove & rename source operations incrementally

--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -546,9 +546,9 @@
 	return nil
 }
 
-func rebuildSite(changes map[string]bool) error {
+func rebuildSite(events []fsnotify.Event) error {
 	startTime := time.Now()
-	err := mainSite.ReBuild(changes)
+	err := mainSite.ReBuild(events)
 	if err != nil {
 		return err
 	}
@@ -585,12 +585,10 @@
 		for {
 			select {
 			case evs := <-watcher.Events:
-				jww.INFO.Println("File System Event:", evs)
+				jww.INFO.Println("Recieved System Events:", evs)
 
-				staticChanged := false
-				dynamicChanged := false
-				staticFilesChanged := make(map[string]bool)
-				dynamicFilesChanged := make(map[string]bool)
+				staticEvents := []fsnotify.Event{} //ev make(map[string]bool)
+				dynamicEvents := []fsnotify.Event{} //make(map[string]bool)
 
 				for _, ev := range evs {
 					ext := filepath.Ext(ev.Name)
@@ -598,10 +596,6 @@
 					if istemp {
 						continue
 					}
-					// renames are always followed with Create/Modify
-					if ev.Op&fsnotify.Rename == fsnotify.Rename {
-						continue
-					}
 
 					// Write and rename operations are often followed by CHMOD.
 					// There may be valid use cases for rebuilding the site on CHMOD,
@@ -615,18 +609,6 @@
 						continue
 					}
 
-					isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(helpers.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, helpers.GetThemesDirPath()))
-					staticChanged = staticChanged || isstatic
-					dynamicChanged = dynamicChanged || !isstatic
-
-					if isstatic {
-						if staticPath, err := helpers.MakeStaticPathRelative(ev.Name); err == nil {
-							staticFilesChanged[staticPath] = true
-						}
-					} else {
-						dynamicFilesChanged[ev.Name] = true
-					}
-
 					// add new directory to watch list
 					if s, err := os.Stat(ev.Name); err == nil && s.Mode().IsDir() {
 						if ev.Op&fsnotify.Create == fsnotify.Create {
@@ -633,9 +615,18 @@
 							watcher.Add(ev.Name)
 						}
 					}
+
+					isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(helpers.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, helpers.GetThemesDirPath()))
+
+					if isstatic {
+						staticEvents = append(staticEvents, ev)
+//						}
+					} else {
+						dynamicEvents = append(dynamicEvents, ev)
+					}
 				}
 
-				if staticChanged {
+				if len(staticEvents) > 0 {
 					jww.FEEDBACK.Printf("Static file changed, syncing\n")
 					if viper.GetBool("ForceSyncStatic") {
 						jww.FEEDBACK.Printf("Syncing all static files\n")
@@ -645,7 +636,6 @@
 							utils.StopOnErr(err, fmt.Sprintf("Error copying static files to %s", helpers.AbsPathify(viper.GetString("PublishDir"))))
 						}
 					} else {
-
 						syncer := fsync.NewSyncer()
 						syncer.NoTimes = viper.GetBool("notimes")
 						syncer.SrcFs = hugofs.SourceFs
@@ -660,25 +650,41 @@
 						staticDir := helpers.GetStaticDirPath()
 						themeStaticDir := helpers.GetThemesDirPath()
 
-						jww.FEEDBACK.Printf("StaticDir '%s'\nThemeStaticDir '%s'\n", staticDir, themeStaticDir)
+						jww.FEEDBACK.Printf("Syncing from: \n \tStaticDir: '%s'\n\tThemeStaticDir: '%s'\n", staticDir, themeStaticDir)
 
-						for path := range staticFilesChanged {
+						for _, ev := range staticEvents {
+							fmt.Println(ev)
+							fromPath := ev.Name
 							var publishPath string
 
-							if strings.HasPrefix(path, staticDir) {
-								publishPath = filepath.Join(publishDir, strings.TrimPrefix(path, staticDir))
-							} else if strings.HasPrefix(path, themeStaticDir) {
-								publishPath = filepath.Join(publishDir, strings.TrimPrefix(path, themeStaticDir))
+							// If we are here we already know the event took place in a static dir
+							relPath, err := helpers.MakeStaticPathRelative(fromPath)
+							if err != nil {
+								fmt.Println(err)
+								continue
 							}
-							jww.FEEDBACK.Printf("Syncing file '%s'", path)
 
-							if _, err := os.Stat(path); err == nil {
-								jww.INFO.Println("syncing from ", path, " to ", publishPath)
-								err := syncer.Sync(publishPath, path)
-								if err != nil {
-									jww.FEEDBACK.Printf("Error on syncing file '%s'\n", path)
-								}
+							if strings.HasPrefix(fromPath, staticDir) {
+								publishPath = filepath.Join(publishDir, strings.TrimPrefix(fromPath, staticDir))
+							} else if strings.HasPrefix(relPath, themeStaticDir) {
+								publishPath = filepath.Join(publishDir, strings.TrimPrefix(fromPath, themeStaticDir))
 							}
+							jww.FEEDBACK.Println("Syncing file", relPath)
+
+							// Due to our approach of layering many directories onto one we can't accurately
+							// remove file not in one of the source directories.
+							// If a file is in the local static dir and also in the theme static dir and we remove
+							// it from one of those locations we expect it to still exist in the destination
+
+							 // if remove or rename ignore
+							if ev.Op&fsnotify.Rename == fsnotify.Rename || ev.Op&fsnotify.Remove == fsnotify.Remove {
+								continue
+							}
+
+							jww.INFO.Println("syncing from ", fromPath, " to ", publishPath)
+							if er := syncer.Sync(publishPath, fromPath); er != nil {
+								jww.ERROR.Printf("Error on syncing file '%s'\n %s\n", relPath, er)
+							}
 						}
 					}
 
@@ -686,8 +692,9 @@
 						// Will block forever trying to write to a channel that nobody is reading if livereload isn't initalized
 
 						// force refresh when more than one file
-						if len(staticFilesChanged) == 1 {
-							for path := range staticFilesChanged {
+						if len(staticEvents) == 1 {
+							for _, ev := range staticEvents {
+								path, _ := helpers.MakeStaticPathRelative(ev.Name)
 								livereload.RefreshPath(path)
 							}
 
@@ -697,14 +704,12 @@
 					}
 				}
 
-				if dynamicChanged {
+				if len(dynamicEvents) >0 {
 					fmt.Print("\nChange detected, rebuilding site\n")
 					const layout = "2006-01-02 15:04 -0700"
 					fmt.Println(time.Now().Format(layout))
-					//TODO here
 
-					//	utils.CheckErr(buildSite(true))
-					rebuildSite(dynamicFilesChanged)
+					rebuildSite(dynamicEvents)
 
 					if !BuildWatch && !viper.GetBool("DisableLiveReload") {
 						// Will block forever trying to write to a channel that nobody is reading if livereload isn't initalized
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -112,20 +112,20 @@
 //	}
 //}
 
-//func (ps Pages) FindPageByFilePath(inPath string) *Page {
-//	for _, x := range ps {
-//		if x.Source.LogicalName() == inPath {
-//			return x
-//		}
-//	}
-//	return nil
-//}
+func (ps Pages) FindPagePosByFilePath(inPath string) int {
+	for i, x := range ps {
+		if x.Source.Path() == inPath {
+			return i
+		}
+	}
+	return -1
+}
 
 // FindPagePos Given a page, it will find the position in Pages
 // will return -1 if not found
 func (ps Pages) FindPagePos(page *Page) int {
 	for i, x := range ps {
-		if x.Source.LogicalName() == page.Source.LogicalName() {
+		if x.Source.Path() == page.Source.Path() {
 			return i
 		}
 	}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -43,6 +43,7 @@
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/nitro"
 	"github.com/spf13/viper"
+	"gopkg.in/fsnotify.v1"
 )
 
 var _ = transform.AbsURL
@@ -426,28 +427,29 @@
 	return nil
 }
 
-func (s *Site) ReBuild(changed map[string]bool) error {
+func (s *Site) ReBuild(events []fsnotify.Event) error {
 	s.timerStep("initialize rebuild")
 	// First we need to determine what changed
 
-	sourceChanged := []string{}
-	tmplChanged := []string{}
-	dataChanged := []string{}
+	sourceChanged := []fsnotify.Event{}
+	tmplChanged := []fsnotify.Event{}
+	dataChanged := []fsnotify.Event{}
+
 	var err error
 
-	for f := range changed {
+	for _, ev := range events {
 		// Need to re-read source
-		if strings.HasPrefix(f, s.absContentDir()) {
-			fmt.Println("Source changed", f)
-			sourceChanged = append(sourceChanged, f)
+		if strings.HasPrefix(ev.Name, s.absContentDir()) {
+			fmt.Println("Source changed", ev)
+			sourceChanged = append(sourceChanged, ev)
 		}
-		if strings.HasPrefix(f, s.absLayoutDir()) || strings.HasPrefix(f, s.absThemeDir()) {
-			fmt.Println("Template changed", f)
-			tmplChanged = append(tmplChanged, f)
+		if strings.HasPrefix(ev.Name, s.absLayoutDir()) || strings.HasPrefix(ev.Name, s.absThemeDir()) {
+			fmt.Println("Template changed", ev)
+			tmplChanged = append(tmplChanged, ev)
 		}
-		if strings.HasPrefix(f, s.absDataDir()) {
-			fmt.Println("Data changed", f)
-			dataChanged = append(dataChanged, f)
+		if strings.HasPrefix(ev.Name, s.absDataDir()) {
+			fmt.Println("Data changed", ev)
+			dataChanged = append(dataChanged,ev)
 		}
 	}
 
@@ -497,8 +499,15 @@
 		go incrementalReadCollator(s, readResults, pageChan, fileConvChan, coordinator, errs)
 		go converterCollator(s, convertResults, errs)
 
-		for _, x := range sourceChanged {
-			file, err := s.ReReadFile(x)
+		for _, ev := range sourceChanged {
+			if ev.Op&fsnotify.Rename == fsnotify.Rename || ev.Op&fsnotify.Remove == fsnotify.Remove {
+				//remove the file & a create will follow
+				path, _ := helpers.GetRelativePath(ev.Name, s.absContentDir())
+				s.RemovePageByPath(path)
+				continue
+			}
+
+			file, err := s.ReReadFile(ev.Name)
 			if err != nil {
 				errs <- err
 			}
@@ -540,7 +549,6 @@
 	if err = s.Render(); err != nil {
 		// Better reporting when the template is missing (commit 2bbecc7b)
 		jww.ERROR.Printf("Error rendering site: %s", err)
-
 		jww.ERROR.Printf("Available templates:")
 		var keys []string
 		for _, template := range s.Tmpl.Templates() {
@@ -1002,6 +1010,23 @@
 
 	if page.IsFuture() {
 		s.futureCount++
+	}
+}
+
+
+func (s *Site) RemovePageByPath(path string) {
+	if i := s.Pages.FindPagePosByFilePath(path); i >= 0 {
+		page := s.Pages[i]
+
+		if page.IsDraft() {
+			s.draftCount--
+		}
+
+		if page.IsFuture() {
+			s.futureCount--
+		}
+
+		s.Pages = append(s.Pages[:i], s.Pages[i+1:]...)
 	}
 }