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

@ -79,17 +79,18 @@ type rwFolder struct {
progressEmitter *ProgressEmitter
virtualMtimeRepo *db.VirtualMtimeRepo
folder string
dir string
scanIntv time.Duration
versioner versioner.Versioner
ignorePerms bool
copiers int
pullers int
shortID uint64
order config.PullOrder
sleep time.Duration
pause time.Duration
folder string
dir string
scanIntv time.Duration
versioner versioner.Versioner
ignorePerms bool
copiers int
pullers int
shortID uint64
order config.PullOrder
maxConflicts int
sleep time.Duration
pause time.Duration
stop chan struct{}
queue *jobQueue
@ -115,14 +116,15 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo
progressEmitter: m.progressEmitter,
virtualMtimeRepo: db.NewVirtualMtimeRepo(m.db, cfg.ID),
folder: cfg.ID,
dir: cfg.Path(),
scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second,
ignorePerms: cfg.IgnorePerms,
copiers: cfg.Copiers,
pullers: cfg.Pullers,
shortID: shortID,
order: cfg.Order,
folder: cfg.ID,
dir: cfg.Path(),
scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second,
ignorePerms: cfg.IgnorePerms,
copiers: cfg.Copiers,
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
}