Allow limiting max conflicts (fixes #2282)

This commit is contained in:
Audrius Butkevicius 2015-10-13 20:50:58 +01:00
parent 2b56961b54
commit 752533489a
6 changed files with 66 additions and 33 deletions

View File

@ -1135,6 +1135,7 @@ angular.module('syncthing.core')
};
$scope.currentFolder.rescanIntervalS = 60;
$scope.currentFolder.minDiskFreePct = 1;
$scope.currentFolder.maxConflicts = -1;
$scope.currentFolder.order = "random";
$scope.currentFolder.fileVersioningSelector = "none";
$scope.currentFolder.trashcanClean = 0;
@ -1156,6 +1157,7 @@ angular.module('syncthing.core')
selectedDevices: {},
rescanIntervalS: 60,
minDiskFreePct: 1,
maxConflicts: -1,
order: "random",
fileVersioningSelector: "none",
trashcanClean: 0,

File diff suppressed because one or more lines are too long

View File

@ -111,6 +111,7 @@ type FolderConfiguration struct {
ScanProgressIntervalS int `xml:"scanProgressIntervalS" json:"scanProgressIntervalS"` // Set to a negative value to disable. Value of 0 will get replaced with value of 2 (default value)
PullerSleepS int `xml:"pullerSleepS" json:"pullerSleepS"`
PullerPauseS int `xml:"pullerPauseS" json:"pullerPauseS"`
MaxConflicts int `xml:"maxConflicts" json:"maxConflicts"`
Invalid string `xml:"-" json:"invalid"` // Set at runtime when there is an error, not saved
}
@ -499,14 +500,6 @@ func ChangeRequiresRestart(from, to Configuration) bool {
return false
}
func convertV10V11(cfg *Configuration) {
// Set minimum disk free of existing folders to 1%
for i := range cfg.Folders {
cfg.Folders[i].MinDiskFreePct = 1
}
cfg.Version = 11
}
func convertV11V12(cfg *Configuration) {
// Change listen address schema
for i, addr := range cfg.Options.ListenAddress {
@ -550,9 +543,22 @@ func convertV11V12(cfg *Configuration) {
cfg.Options.LocalAnnPort = 21027
}
// Set MaxConflicts to unlimited
for i := range cfg.Folders {
cfg.Folders[i].MaxConflicts = -1
}
cfg.Version = 12
}
func convertV10V11(cfg *Configuration) {
// Set minimum disk free of existing folders to 1%
for i := range cfg.Folders {
cfg.Folders[i].MinDiskFreePct = 1
}
cfg.Version = 11
}
func convertV9V10(cfg *Configuration) {
// Enable auto normalization on existing folders.
for i := range cfg.Folders {

View File

@ -100,6 +100,7 @@ func TestDeviceConfig(t *testing.T) {
Hashers: 0,
AutoNormalize: true,
MinDiskFreePct: 1,
MaxConflicts: -1,
},
}
expectedDevices := []DeviceConfiguration{

View File

@ -3,6 +3,7 @@
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR"></device>
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"></device>
<minDiskFreePct>1</minDiskFreePct>
<maxConflicts>-1</maxConflicts>
</folder>
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR" name="node one" compression="metadata">
<address>tcp://a</address>

View File

@ -88,6 +88,7 @@ type rwFolder struct {
pullers int
shortID uint64
order config.PullOrder
maxConflicts int
sleep time.Duration
pause time.Duration
@ -123,6 +124,7 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo
pullers: cfg.Pullers,
shortID: shortID,
order: cfg.Order,
maxConflicts: cfg.MaxConflicts,
stop: make(chan struct{}),
queue: newJobQueue(),
@ -757,7 +759,7 @@ func (p *rwFolder) deleteFile(file protocol.FileInfo) {
// of deleting. Also merge with the version vector we had, to indicate
// we have resolved the conflict.
file.Version = file.Version.Merge(cur.Version)
err = osutil.InWritableDir(moveForConflict, realName)
err = osutil.InWritableDir(p.moveForConflict, realName)
} else if p.versioner != nil {
err = osutil.InWritableDir(p.versioner.Archive, realName)
} else {
@ -1254,7 +1256,7 @@ func (p *rwFolder) performFinish(state *sharedPullerState) error {
// we have resolved the conflict.
state.file.Version = state.file.Version.Merge(state.version)
if err = osutil.InWritableDir(moveForConflict, state.realName); err != nil {
if err = osutil.InWritableDir(p.moveForConflict, state.realName); err != nil {
return err
}
@ -1447,7 +1449,14 @@ func removeDevice(devices []protocol.DeviceID, device protocol.DeviceID) []proto
return devices
}
func moveForConflict(name string) error {
func (p *rwFolder) moveForConflict(name string) error {
if p.maxConflicts == 0 {
if err := osutil.Remove(name); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
ext := filepath.Ext(name)
withoutExt := name[:len(name)-len(ext)]
newName := withoutExt + time.Now().Format(".sync-conflict-20060102-150405") + ext
@ -1457,7 +1466,21 @@ func moveForConflict(name string) error {
// the user has already moved it away, or the conflict was between a
// remote modification and a local delete. In either way it does not
// matter, go ahead as if the move succeeded.
return nil
err = nil
}
if p.maxConflicts > -1 {
matches, gerr := osutil.Glob(withoutExt + ".sync-conflict-????????-??????" + ext)
if gerr == nil && len(matches) > p.maxConflicts {
sort.Sort(sort.Reverse(sort.StringSlice(matches)))
for _, match := range matches[p.maxConflicts:] {
gerr = osutil.Remove(match)
if gerr != nil {
l.Debugln(p, "removing extra conflict", gerr)
}
}
} else if gerr != nil {
l.Debugln(p, "globbing for conflicts", gerr)
}
}
return err
}