mirror of
https://github.com/gilbertchen/duplicacy
synced 2025-12-24 04:04:18 +00:00
Restore empty directories
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
.idea
|
||||
duplicacy_main
|
||||
|
||||
@@ -918,7 +918,9 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
|
||||
|
||||
if deleteMode && len(patterns) == 0 {
|
||||
for _, file := range extraFiles {
|
||||
// Reverse the order to make sure directories are empty before being deleted
|
||||
for i := range extraFiles {
|
||||
file := extraFiles[len(extraFiles) - 1 - i]
|
||||
fullPath := joinPath(top, file)
|
||||
os.Remove(fullPath)
|
||||
LOG_INFO("RESTORE_DELETE", "Deleted %s", file)
|
||||
@@ -932,8 +934,6 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
}
|
||||
}
|
||||
|
||||
RemoveEmptyDirectories(top)
|
||||
|
||||
if showStatistics {
|
||||
for _, file := range downloadedFiles {
|
||||
LOG_INFO("DOWNLOAD_DONE", "Downloaded %s (%d)", file.Path, file.Size)
|
||||
|
||||
@@ -104,6 +104,27 @@ func modifyFile(path string, portion float32) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkExistence(t *testing.T, path string, exists bool, isDir bool) {
|
||||
stat, err := os.Stat(path)
|
||||
if exists {
|
||||
if err != nil {
|
||||
t.Errorf("%s does not exist: %v", path, err)
|
||||
} else if isDir {
|
||||
if !stat.Mode().IsDir() {
|
||||
t.Errorf("%s is not a directory", path)
|
||||
}
|
||||
} else {
|
||||
if stat.Mode().IsDir() {
|
||||
t.Errorf("%s is not a file", path)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err == nil || !os.IsNotExist(err) {
|
||||
t.Errorf("%s may exist: %v", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func truncateFile(path string) {
|
||||
file, err := os.OpenFile(path, os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
@@ -261,13 +282,25 @@ func TestBackupManager(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate file2 and add a few empty directories
|
||||
truncateFile(testDir + "/repository1/file2")
|
||||
os.Mkdir(testDir + "/repository1/dir2", 0700)
|
||||
os.Mkdir(testDir + "/repository1/dir2/dir3", 0700)
|
||||
os.Mkdir(testDir + "/repository1/dir4", 0700)
|
||||
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
|
||||
backupManager.Backup(testDir + "/repository1", /*quickMode=*/false, threads, "third", false, false)
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
|
||||
// Create some directories and files under repository2 that will be deleted during restore
|
||||
os.Mkdir(testDir + "/repository2/dir5", 0700)
|
||||
os.Mkdir(testDir + "/repository2/dir5/dir6", 0700)
|
||||
os.Mkdir(testDir + "/repository2/dir7", 0700)
|
||||
createRandomFile(testDir + "/repository2/file4", 100)
|
||||
createRandomFile(testDir + "/repository2/dir5/file5", 100)
|
||||
|
||||
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
|
||||
backupManager.Restore(testDir + "/repository2", 3, /*inPlace=*/true, /*quickMode=*/false, threads, /*overwrite=*/true,
|
||||
/*deleteMode=*/false, /*showStatistics=*/false, /*patterns=*/nil)
|
||||
/*deleteMode=*/true, /*showStatistics=*/false, /*patterns=*/nil)
|
||||
|
||||
for _, f := range []string{ "file1", "file2", "dir1/file3" } {
|
||||
hash1 := getFileHash(testDir + "/repository1/" + f)
|
||||
@@ -277,6 +310,18 @@ func TestBackupManager(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// These files/dirs should not exist because deleteMode == true
|
||||
checkExistence(t, testDir + "/repository2/dir5", false, false);
|
||||
checkExistence(t, testDir + "/repository2/dir5/dir6", false, false);
|
||||
checkExistence(t, testDir + "/repository2/dir7", false, false);
|
||||
checkExistence(t, testDir + "/repository2/file4", false, false);
|
||||
checkExistence(t, testDir + "/repository2/dir5/file5", false, false);
|
||||
|
||||
// These empty dirs should exist
|
||||
checkExistence(t, testDir + "/repository2/dir2", true, true);
|
||||
checkExistence(t, testDir + "/repository2/dir2/dir3", true, true);
|
||||
checkExistence(t, testDir + "/repository2/dir4", true, true);
|
||||
|
||||
// Remove file2 and dir1/file3 and restore them from revision 3
|
||||
os.Remove(testDir + "/repository1/file2")
|
||||
os.Remove(testDir + "/repository1/dir1/file3")
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"os"
|
||||
"bufio"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -190,54 +189,6 @@ func SavePassword(preference Preference, passwordType string, password string) {
|
||||
keyringSet(passwordID, password)
|
||||
}
|
||||
|
||||
// RemoveEmptyDirectories remove all empty subdirectoreies under top.
|
||||
func RemoveEmptyDirectories(top string) {
|
||||
|
||||
stack := make([]string, 0, 256)
|
||||
|
||||
stack = append(stack, top)
|
||||
|
||||
for len(stack) > 0 {
|
||||
|
||||
dir := stack[len(stack) - 1]
|
||||
stack = stack[:len(stack) - 1]
|
||||
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() && file.Name()[0] != '.' {
|
||||
stack = append(stack, path.Join(dir, file.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
if os.Remove(dir) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
dir = path.Dir(dir)
|
||||
for (len(dir) > len(top)) {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
if os.Remove(dir) != nil {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dir = path.Dir(dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The following code was modified from the online article 'Matching Wildcards: An Algorithm', by Kirk J. Krauss,
|
||||
// Dr. Dobb's, August 26, 2008. However, the version in the article doesn't handle cases like matching 'abcccd'
|
||||
// against '*ccd', and the version here fixed that issue.
|
||||
|
||||
Reference in New Issue
Block a user