lib/versioner: Restore for all versioners, cross-device support (#5514)
* lib/versioner: Restore for all versioners, cross-device support Fixes #4631 Fixes #4586 Fixes #1634 Fixes #5338 Fixes #5419
This commit is contained in:
committed by
GitHub
parent
2984d40641
commit
0ca1f26ff8
@@ -103,3 +103,55 @@ func IsParent(path, parent string) bool {
|
||||
}
|
||||
return strings.HasPrefix(path, parent)
|
||||
}
|
||||
|
||||
func CommonPrefix(first, second string) string {
|
||||
if filepath.IsAbs(first) != filepath.IsAbs(second) {
|
||||
// Whatever
|
||||
return ""
|
||||
}
|
||||
|
||||
firstParts := strings.Split(filepath.Clean(first), string(PathSeparator))
|
||||
secondParts := strings.Split(filepath.Clean(second), string(PathSeparator))
|
||||
|
||||
isAbs := filepath.IsAbs(first) && filepath.IsAbs(second)
|
||||
|
||||
count := len(firstParts)
|
||||
if len(secondParts) < len(firstParts) {
|
||||
count = len(secondParts)
|
||||
}
|
||||
|
||||
common := make([]string, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
if firstParts[i] != secondParts[i] {
|
||||
break
|
||||
}
|
||||
common = append(common, firstParts[i])
|
||||
}
|
||||
|
||||
if isAbs {
|
||||
if runtime.GOOS == "windows" && isVolumeNameOnly(common) {
|
||||
// Because strings.Split strips out path separators, if we're at the volume name, we end up without a separator
|
||||
// Wedge an empty element to be joined with.
|
||||
common = append(common, "")
|
||||
} else if len(common) == 1 {
|
||||
// If isAbs on non Windows, first element in both first and second is "", hence joining that returns nothing.
|
||||
return string(PathSeparator)
|
||||
}
|
||||
}
|
||||
|
||||
// This should only be true on Windows when drive letters are different or when paths are relative.
|
||||
// In case of UNC paths we should end up with more than a single element hence joining is fine
|
||||
if len(common) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// This has to be strings.Join, because filepath.Join([]string{"", "", "?", "C:", "Audrius"}...) returns garbage
|
||||
result := strings.Join(common, string(PathSeparator))
|
||||
return filepath.Clean(result)
|
||||
}
|
||||
|
||||
func isVolumeNameOnly(parts []string) bool {
|
||||
isNormalVolumeName := len(parts) == 1 && strings.HasSuffix(parts[0], ":")
|
||||
isUNCVolumeName := len(parts) == 4 && strings.HasSuffix(parts[3], ":")
|
||||
return isNormalVolumeName || isUNCVolumeName
|
||||
}
|
||||
|
||||
46
lib/fs/util_test.go
Normal file
46
lib/fs/util_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2019 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommonPrefix(t *testing.T) {
|
||||
test := func(first, second, expect string) {
|
||||
t.Helper()
|
||||
res := CommonPrefix(first, second)
|
||||
if res != expect {
|
||||
t.Errorf("Expected %s got %s", expect, res)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
test(`c:\Audrius\Downloads`, `c:\Audrius\Docs`, `c:\Audrius`)
|
||||
test(`c:\Audrius\Downloads`, `C:\Audrius\Docs`, ``) // Case differences :(
|
||||
test(`C:\Audrius-a\Downloads`, `C:\Audrius-b\Docs`, `C:\`)
|
||||
test(`\\?\C:\Audrius-a\Downloads`, `\\?\C:\Audrius-b\Docs`, `\\?\C:\`)
|
||||
test(`\\?\C:\Audrius\Downloads`, `\\?\C:\Audrius\Docs`, `\\?\C:\Audrius`)
|
||||
test(`Audrius-a\Downloads`, `Audrius-b\Docs`, ``)
|
||||
test(`Audrius\Downloads`, `Audrius\Docs`, `Audrius`)
|
||||
test(`c:\Audrius\Downloads`, `Audrius\Docs`, ``)
|
||||
test(`c:\`, `c:\`, `c:\`)
|
||||
test(`\\?\c:\`, `\\?\c:\`, `\\?\c:\`)
|
||||
} else {
|
||||
test(`/Audrius/Downloads`, `/Audrius/Docs`, `/Audrius`)
|
||||
test(`/Audrius\Downloads`, `/Audrius\Docs`, `/`)
|
||||
test(`/Audrius-a/Downloads`, `/Audrius-b/Docs`, `/`)
|
||||
test(`Audrius\Downloads`, `Audrius\Docs`, ``) // Windows separators
|
||||
test(`Audrius/Downloads`, `Audrius/Docs`, `Audrius`)
|
||||
test(`Audrius-a\Downloads`, `Audrius-b\Docs`, ``)
|
||||
test(`/Audrius/Downloads`, `Audrius/Docs`, ``)
|
||||
test(`/`, `/`, `/`)
|
||||
}
|
||||
test(`Audrius`, `Audrius`, `Audrius`)
|
||||
test(`.`, `.`, `.`)
|
||||
}
|
||||
Reference in New Issue
Block a user