Kaynağa Gözat

Add Changes.ChangesLayers()

This calculates the difference between a set of layers and a
directory tree.
Alexander Larsson 11 yıl önce
ebeveyn
işleme
ad0a6a03e3
1 değiştirilmiş dosya ile 94 ekleme ve 1 silme
  1. 94 1
      changes.go

+ 94 - 1
changes.go

@@ -235,11 +235,88 @@ func (info *FileInfo)Changes(oldInfo *FileInfo) []Change {
 }
 
 
-func collectFileInfo(sourceDir string) (*FileInfo, error) {
+func newRootFileInfo() *FileInfo {
 	root := &FileInfo {
 		name: "/",
 		children: make(map[string]*FileInfo),
 	}
+	return root
+}
+
+func applyLayer(root *FileInfo, layer string) error {
+	err := filepath.Walk(layer, func(layerPath string, f os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
+		// Skip root
+		if layerPath == layer {
+			return nil
+		}
+
+		// rebase path
+		relPath, err := filepath.Rel(layer, layerPath)
+		if err != nil {
+			return err
+		}
+		relPath = filepath.Join("/", relPath)
+
+		// Skip AUFS metadata
+		if matched, err := filepath.Match("/.wh..wh.*", relPath); err != nil || matched {
+			if err != nil || !f.IsDir() {
+				return err
+			}
+			return filepath.SkipDir
+		}
+
+		var layerStat syscall.Stat_t
+		err = syscall.Lstat(layerPath, &layerStat)
+		if err != nil {
+			return err
+		}
+
+		file := filepath.Base(relPath)
+		// If there is a whiteout, then the file was removed
+		if strings.HasPrefix(file, ".wh.") {
+			originalFile := file[len(".wh."):]
+			deletePath := filepath.Join(filepath.Dir(relPath), originalFile)
+
+			root.Remove(deletePath)
+		} else {
+			// Added or changed file
+			existing := root.LookUp(relPath)
+			if existing != nil {
+				// Changed file
+				existing.stat = layerStat
+				if !existing.isDir() {
+					// Changed from dir to non-dir, delete all previous files
+					existing.children = make(map[string]*FileInfo)
+				}
+			} else {
+				// Added file
+				parent := root.LookUp(filepath.Dir(relPath))
+				if parent == nil {
+					return fmt.Errorf("collectFileInfo: Unexpectedly no parent for %s", relPath)
+				}
+
+				info := &FileInfo {
+					name: filepath.Base(relPath),
+					children: make(map[string]*FileInfo),
+					parent: parent,
+					stat: layerStat,
+				}
+
+				parent.children[info.name] = info
+			}
+		}
+		return nil
+	})
+	return err
+}
+
+
+func collectFileInfo(sourceDir string) (*FileInfo, error) {
+	root := newRootFileInfo()
 
 	err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error {
 		if err != nil {
@@ -282,6 +359,22 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) {
 	return root, nil
 }
 
+func ChangesLayers(newDir string, layers []string) ([]Change, error) {
+	newRoot, err := collectFileInfo(newDir)
+	if err != nil {
+		return nil, err
+	}
+	oldRoot := newRootFileInfo()
+	for i := len(layers)-1; i >= 0; i-- {
+		layer := layers[i]
+		if err = applyLayer(oldRoot, layer); err != nil {
+			return nil, err
+		}
+	}
+
+	return newRoot.Changes(oldRoot), nil
+}
+
 func ChangesDirs(newDir, oldDir string) ([]Change, error) {
 	oldRoot, err := collectFileInfo(oldDir)
 	if err != nil {