mirror of
https://github.com/zyedidia/micro.git
synced 2024-11-09 16:45:48 +03:00
Improve backup system
This commit introduces several improvements to the backup system. * Backups are made every 8 seconds for buffers that have been modified since the last backup. * The `permbackup` option allows users to specify that backups should be kept permanently. * `The backupdir` option allows users to store backups in a custom directory. Fixes #1641 Fixes #1536 Ref #1539 (removes possibility of race condition for backups)
This commit is contained in:
parent
c5136820c4
commit
a8332fd316
@ -274,7 +274,7 @@ func main() {
|
||||
fmt.Println("Micro encountered an error:", err)
|
||||
// backup all open buffers
|
||||
for _, b := range buffer.OpenBuffers {
|
||||
b.Backup(false)
|
||||
b.Backup()
|
||||
}
|
||||
// Print the stack trace too
|
||||
fmt.Print(errors.Wrap(err, 2).ErrorStack())
|
||||
|
@ -28,29 +28,53 @@ The backup was created on %s, and the file is
|
||||
|
||||
Options: [r]ecover, [i]gnore: `
|
||||
|
||||
var backupRequestChan chan *Buffer
|
||||
|
||||
func backupThread() {
|
||||
for {
|
||||
time.Sleep(time.Second * 8)
|
||||
|
||||
for len(backupRequestChan) > 0 {
|
||||
b := <-backupRequestChan
|
||||
b.Backup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
backupRequestChan = make(chan *Buffer, 10)
|
||||
|
||||
go backupThread()
|
||||
}
|
||||
|
||||
func (b *Buffer) RequestBackup() {
|
||||
if !b.requestedBackup {
|
||||
select {
|
||||
case backupRequestChan <- b:
|
||||
default:
|
||||
// channel is full
|
||||
}
|
||||
b.requestedBackup = true
|
||||
}
|
||||
}
|
||||
|
||||
// Backup saves the current buffer to ConfigDir/backups
|
||||
func (b *Buffer) Backup(checkTime bool) error {
|
||||
func (b *Buffer) Backup() error {
|
||||
if !b.Settings["backup"].(bool) || b.Path == "" || b.Type != BTDefault {
|
||||
return nil
|
||||
}
|
||||
|
||||
if checkTime {
|
||||
sub := time.Now().Sub(b.lastbackup)
|
||||
if sub < time.Duration(backupTime)*time.Millisecond {
|
||||
return nil
|
||||
}
|
||||
backupdir, err := util.ReplaceHome(b.Settings["backupdir"].(string))
|
||||
if len(backupdir) == 0 || err != nil {
|
||||
backupdir = filepath.Join(config.ConfigDir, "backups")
|
||||
}
|
||||
|
||||
b.lastbackup = time.Now()
|
||||
|
||||
backupdir := filepath.Join(config.ConfigDir, "backups")
|
||||
if _, err := os.Stat(backupdir); os.IsNotExist(err) {
|
||||
os.Mkdir(backupdir, os.ModePerm)
|
||||
}
|
||||
|
||||
name := filepath.Join(backupdir, util.EscapePath(b.AbsPath))
|
||||
|
||||
err := overwriteFile(name, encoding.Nop, func(file io.Writer) (e error) {
|
||||
err = overwriteFile(name, encoding.Nop, func(file io.Writer) (e error) {
|
||||
if len(b.lines) == 0 {
|
||||
return
|
||||
}
|
||||
@ -74,12 +98,14 @@ func (b *Buffer) Backup(checkTime bool) error {
|
||||
return
|
||||
}, false)
|
||||
|
||||
b.requestedBackup = false
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveBackup removes any backup file associated with this buffer
|
||||
func (b *Buffer) RemoveBackup() {
|
||||
if !b.Settings["backup"].(bool) || b.Path == "" || b.Type != BTDefault {
|
||||
if !b.Settings["backup"].(bool) || b.Settings["permbackup"].(bool) || b.Path == "" || b.Type != BTDefault {
|
||||
return
|
||||
}
|
||||
f := filepath.Join(config.ConfigDir, "backups", util.EscapePath(b.AbsPath))
|
||||
@ -89,7 +115,7 @@ func (b *Buffer) RemoveBackup() {
|
||||
// ApplyBackup applies the corresponding backup file to this buffer (if one exists)
|
||||
// Returns true if a backup was applied
|
||||
func (b *Buffer) ApplyBackup(fsize int64) bool {
|
||||
if b.Settings["backup"].(bool) && len(b.Path) > 0 && b.Type == BTDefault {
|
||||
if b.Settings["backup"].(bool) && !b.Settings["permbackup"].(bool) && len(b.Path) > 0 && b.Type == BTDefault {
|
||||
backupfile := filepath.Join(config.ConfigDir, "backups", util.EscapePath(b.AbsPath))
|
||||
if info, err := os.Stat(backupfile); err == nil {
|
||||
backup, err := os.Open(backupfile)
|
||||
|
@ -102,9 +102,7 @@ type SharedBuffer struct {
|
||||
diffLock sync.RWMutex
|
||||
diff map[int]DiffStatus
|
||||
|
||||
// counts the number of edits
|
||||
// resets every backupTime edits
|
||||
lastbackup time.Time
|
||||
requestedBackup bool
|
||||
|
||||
// ReloadDisabled allows the user to disable reloads if they
|
||||
// are viewing a file that is constantly changing
|
||||
@ -271,6 +269,7 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
||||
}
|
||||
}
|
||||
|
||||
hasBackup := false
|
||||
if !found {
|
||||
b.SharedBuffer = new(SharedBuffer)
|
||||
b.Type = btype
|
||||
@ -293,7 +292,7 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
||||
b.Settings["encoding"] = "utf-8"
|
||||
}
|
||||
|
||||
hasBackup := b.ApplyBackup(size)
|
||||
hasBackup = b.ApplyBackup(size)
|
||||
|
||||
if !hasBackup {
|
||||
reader := bufio.NewReader(transform.NewReader(r, enc.NewDecoder()))
|
||||
@ -356,7 +355,9 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
||||
if size > LargeFileThreshold {
|
||||
// If the file is larger than LargeFileThreshold fastdirty needs to be on
|
||||
b.Settings["fastdirty"] = true
|
||||
} else {
|
||||
} else if !hasBackup {
|
||||
// since applying a backup does not save the applied backup to disk, we should
|
||||
// not calculate the original hash based on the backup data
|
||||
calcHash(b, &b.origHash)
|
||||
}
|
||||
}
|
||||
@ -425,7 +426,7 @@ func (b *Buffer) Insert(start Loc, text string) {
|
||||
b.EventHandler.active = b.curCursor
|
||||
b.EventHandler.Insert(start, text)
|
||||
|
||||
go b.Backup(true)
|
||||
b.RequestBackup()
|
||||
}
|
||||
}
|
||||
|
||||
@ -436,7 +437,7 @@ func (b *Buffer) Remove(start, end Loc) {
|
||||
b.EventHandler.active = b.curCursor
|
||||
b.EventHandler.Remove(start, end)
|
||||
|
||||
go b.Backup(true)
|
||||
b.RequestBackup()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,6 +247,7 @@ var defaultCommonSettings = map[string]interface{}{
|
||||
"autoindent": true,
|
||||
"autosu": false,
|
||||
"backup": true,
|
||||
"backupdir": "",
|
||||
"basename": false,
|
||||
"colorcolumn": float64(0),
|
||||
"cursorline": true,
|
||||
@ -261,6 +262,7 @@ var defaultCommonSettings = map[string]interface{}{
|
||||
"keepautoindent": false,
|
||||
"matchbrace": true,
|
||||
"mkparents": false,
|
||||
"permbackup": false,
|
||||
"readonly": false,
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
|
@ -37,21 +37,26 @@ Here are the available options:
|
||||
closed cleanly. In the case of a system crash or a micro crash, the contents
|
||||
of the buffer can be recovered automatically by opening the file that was
|
||||
being edited before the crash, or manually by searching for the backup in
|
||||
the backup directory. Backups are made in the background when a buffer is
|
||||
modified and the latest backup is more than 8 seconds old, or when micro
|
||||
detects a crash. It is highly recommended that you leave this feature
|
||||
enabled.
|
||||
the backup directory. Backups are made in the background for newly modified
|
||||
buffers every 8 seconds, or when micro detects a crash.
|
||||
|
||||
default value: `true`
|
||||
|
||||
* `backupdir`: the directory micro should place backups in. For the default
|
||||
value of `""` (empty string), the backup directory will be
|
||||
`ConfigDir/backups`, which is `~/.config/micro/backups` by default. The
|
||||
directory specified for backups will be created if it does not exist.
|
||||
|
||||
default value: `""` (empty string)
|
||||
|
||||
* `basename`: in the infobar and tabbar, show only the basename of the file
|
||||
being edited rather than the full path.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `colorcolumn`: if this is not set to 0, it will display a column at the
|
||||
specified column. This is useful if you want column 80 to be highlighted
|
||||
special for example.
|
||||
specified column. This is useful if you want column 80 to be highlighted
|
||||
special for example.
|
||||
|
||||
default value: `0`
|
||||
|
||||
@ -200,6 +205,13 @@ Here are the available options:
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `permbackup`: this option causes backups (see `backup` option) to be
|
||||
permanently saved. With permanent backups, micro will not remove backups when
|
||||
files are closed and will never apply them to existing files. Use this option
|
||||
if you are interested in manually managing your backup files.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `pluginchannels`: list of URLs pointing to plugin channels for downloading and
|
||||
installing plugins. A plugin channel consists of a json file with links to
|
||||
plugin repos, which store information about plugin versions and download URLs.
|
||||
|
Loading…
Reference in New Issue
Block a user