From c69da2d39c22b333094c41eed318f2cecd0b6c98 Mon Sep 17 00:00:00 2001 From: herrjosh Date: Sun, 20 Jun 2021 14:03:42 -0700 Subject: [PATCH 1/4] update BackupRecord --- cli/delete.go | 2 +- cli/edit.go | 9 +++++++-- core/record.go | 9 ++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cli/delete.go b/cli/delete.go index ec2293f..c4f750d 100644 --- a/cli/delete.go +++ b/cli/delete.go @@ -114,7 +114,7 @@ func deleteRecordCommand(t *core.Timetrace) *cobra.Command { } } - if err := t.BackupRecord(start); err != nil { + if err := t.BackupRecord(*record); err != nil { out.Err("Failed to backup record before deletion: %s", err.Error()) return } diff --git a/cli/edit.go b/cli/edit.go index 30c2e04..2ef9a12 100644 --- a/cli/edit.go +++ b/cli/edit.go @@ -84,9 +84,10 @@ func editRecordCommand(t *core.Timetrace) *cobra.Command { var recordTime time.Time var err error + var rec *core.Record // if more aliases are needed, this should be expanded to a switch if strings.ToLower(args[0]) == "latest" { - rec, err := t.LoadLatestRecord() + rec, err = t.LoadLatestRecord() if err != nil { out.Err("Error on loading last record: %s", err.Error()) return @@ -98,6 +99,10 @@ func editRecordCommand(t *core.Timetrace) *cobra.Command { out.Err("Failed to parse date argument: %s", err.Error()) return } + rec, err = t.LoadRecord(recordTime) + if err != nil { + return + } } if options.Revert { @@ -109,7 +114,7 @@ func editRecordCommand(t *core.Timetrace) *cobra.Command { return } - if err := t.BackupRecord(recordTime); err != nil { + if err := t.BackupRecord(*rec); err != nil { out.Err("Failed to backup record before edit: %s", err.Error()) return } diff --git a/core/record.go b/core/record.go index 314e2bb..37e5cc0 100644 --- a/core/record.go +++ b/core/record.go @@ -93,14 +93,9 @@ func (t *Timetrace) SaveRecord(record Record, force bool) error { } // BackupRecord creates a backup of the given record file -func (t *Timetrace) BackupRecord(recordKey time.Time) error { - path := t.fs.RecordFilepath(recordKey) - record, err := t.loadRecord(path) - if err != nil { - return err - } +func (t *Timetrace) BackupRecord(record Record) error { // create a new .bak filepath from the record struct - backupPath := t.fs.RecordBackupFilepath(recordKey) + backupPath := t.fs.RecordBackupFilepath(record.Start) backupFile, err := os.OpenFile(backupPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { From 8580e2343f8731ef07ed4259b412778ed038ebdc Mon Sep 17 00:00:00 2001 From: herrjosh Date: Sun, 20 Jun 2021 14:36:22 -0700 Subject: [PATCH 2/4] added logic to rename record when start time is changed --- cli/edit.go | 6 ++++++ core/record.go | 42 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/cli/edit.go b/cli/edit.go index 2ef9a12..88c525b 100644 --- a/cli/edit.go +++ b/cli/edit.go @@ -2,6 +2,7 @@ package cli import ( "errors" + "fmt" "strings" "time" @@ -120,6 +121,11 @@ func editRecordCommand(t *core.Timetrace) *cobra.Command { } if options.Minus == "" && options.Plus == "" { + fmt.Printf("Warning: Directly editing the record can lead to undefined behavior.\nIf you change the start time of the record, the underlying file will be renamed automatically. Make sure it doesn't collide with other records. If you want to change the end time, use --minus or --plus instead.\nContinue? ") + if !askForConfirmation() { + out.Info("Editting record aborted.") + return + } out.Info("Opening %s in default editor", recordTime) if err := t.EditRecordManual(recordTime); err != nil { out.Err("Failed to edit record: %s", err.Error()) diff --git a/core/record.go b/core/record.go index 37e5cc0..67ad9f2 100644 --- a/core/record.go +++ b/core/record.go @@ -118,9 +118,19 @@ func (t *Timetrace) RevertRecord(recordKey time.Time) error { return err } - path := t.fs.RecordFilepath(recordKey) + oldPath := t.fs.RecordFilepath(recordKey) + newPath := t.fs.RecordFilepath(record.Start) + if err = os.Rename(oldPath, newPath); err != nil { + return err + } - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + oldBackupPath := t.fs.RecordBackupFilepath(recordKey) + newBackupPath := t.fs.RecordBackupFilepath(record.Start) + if err = os.Rename(oldBackupPath, newBackupPath); err != nil { + return err + } + + file, err := os.OpenFile(newPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return err } @@ -150,10 +160,13 @@ func (t *Timetrace) DeleteRecord(record Record) error { // EditRecordManual opens the record file in the preferred or default editor. func (t *Timetrace) EditRecordManual(recordTime time.Time) error { path := t.fs.RecordFilepath(recordTime) + backupPath := t.fs.RecordBackupFilepath(recordTime) - if _, err := t.loadRecord(path); err != nil { + rec, err := t.loadRecord(path) + if err != nil { return err } + originalStart := rec.Start editor := t.editorFromEnvironment() cmd := exec.Command(editor, path) @@ -161,7 +174,28 @@ func (t *Timetrace) EditRecordManual(recordTime time.Time) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - return cmd.Run() + if err = cmd.Run(); err != nil { + return err + } + + edittedRec, err := t.loadRecord(path) + if err != nil { + return err + } + + newStart := edittedRec.Start + if originalStart == newStart { + return nil + } + + newPath := t.fs.RecordFilepath(newStart) + newBackupPath := t.fs.RecordBackupFilepath(newStart) + + if err = os.Rename(path, newPath); err != nil { + return err + } + + return os.Rename(backupPath, newBackupPath) } // EditRecord loads the record internally, applies the option values and saves the record From cd71b8a81a98022dcda7180d5176db1533b365cd Mon Sep 17 00:00:00 2001 From: pedroangelini Date: Sun, 11 Feb 2024 16:15:45 +0100 Subject: [PATCH 3/4] applying "rename-record-file" from joshuaherrera --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e9a7666..4a67232 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.dll *.so *.dylib +timetrace # IDE directories .idea From 7e5378290d7b988743eb89ff540a4eafe57ea9e2 Mon Sep 17 00:00:00 2001 From: pedroangelini Date: Sun, 18 Feb 2024 15:26:15 +0100 Subject: [PATCH 4/4] avoid renaming backup files and fixing bug in askForConfirmation in windows (newline chars) --- cli/delete.go | 6 +++--- core/record.go | 10 +++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cli/delete.go b/cli/delete.go index 118781b..c633651 100644 --- a/cli/delete.go +++ b/cli/delete.go @@ -167,10 +167,10 @@ func deleteRecordCommand(t *core.Timetrace) *cobra.Command { } func askForConfirmation(msg string) bool { - reader := bufio.NewReader(os.Stdin) + scanner := bufio.NewScanner(os.Stdin) fmt.Fprint(os.Stderr, msg) - s, _ := reader.ReadString('\n') - s = strings.TrimSuffix(s, "\n") + scanner.Scan() + s := scanner.Text() s = strings.ToLower(s) return s == "y" diff --git a/core/record.go b/core/record.go index 56f87b9..b0b7403 100644 --- a/core/record.go +++ b/core/record.go @@ -251,9 +251,10 @@ func (t *Timetrace) DeleteRecordsByProject(key string) error { } // EditRecordManual opens the record file in the preferred or default editor. +// note we don't rename the backup, because it could have inconsistency between +// start time and file name func (t *Timetrace) EditRecordManual(recordTime time.Time) error { path := t.fs.RecordFilepath(recordTime) - backupPath := t.fs.RecordBackupFilepath(recordTime) rec, err := t.loadRecord(path) if err != nil { @@ -282,13 +283,8 @@ func (t *Timetrace) EditRecordManual(recordTime time.Time) error { } newPath := t.fs.RecordFilepath(newStart) - newBackupPath := t.fs.RecordBackupFilepath(newStart) - if err = os.Rename(path, newPath); err != nil { - return err - } - - return os.Rename(backupPath, newBackupPath) + return os.Rename(path, newPath) } // EditRecord loads the record internally, applies the option values and saves the record