Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

preserve mtime when copying files #1026

Merged
merged 2 commits into from
May 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ var (
"noreverse",
"reverse!",
"ruler",
"preserve",
"smartcase",
"nosmartcase",
"smartcase!",
Expand Down
24 changes: 23 additions & 1 deletion copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"os"
"path/filepath"
"github.com/djherbis/times"
)

func copySize(srcs []string) (int64, error) {
Expand Down Expand Up @@ -33,6 +34,17 @@ func copySize(srcs []string) (int64, error) {
}

func copyFile(src, dst string, info os.FileInfo, nums chan int64) error {
var dst_mode os.FileMode = 0666
preserve_timestamps := false
for _, s := range gOpts.preserve {
switch s {
case "timestamps":
preserve_timestamps = true
case "mode":
dst_mode = info.Mode()
}
}

buf := make([]byte, 4096)

r, err := os.Open(src)
Expand All @@ -41,7 +53,7 @@ func copyFile(src, dst string, info os.FileInfo, nums chan int64) error {
}
defer r.Close()

w, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
w, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, dst_mode)
if err != nil {
return err
}
Expand Down Expand Up @@ -70,6 +82,16 @@ func copyFile(src, dst string, info os.FileInfo, nums chan int64) error {
return err
}

if preserve_timestamps {
ts := times.Get(info)
mtime := info.ModTime();
atime := ts.AccessTime();
if err := os.Chtimes(dst, atime, mtime); err != nil {
os.Remove(dst)
return err
}
}

return nil
}

Expand Down
9 changes: 8 additions & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ The following options can be used to customize the behavior of lf:
number bool (default false)
numberfmt string (default "\033[33m")
period int (default 0)
preserve []string (default "mode")
preview bool (default true)
previewer string (default '')
promptfmt string (default "\033[32;1m%u@%h\033[0m:\033[34;1m%d\033[0m\033[1m%f\033[0m")
Expand Down Expand Up @@ -788,6 +789,12 @@ Note that directories are already updated automatically in many cases.
This option can be useful when there is an external process changing the displayed directory and you are not doing anything in lf.
Periodic checks are disabled when the value of this option is set to zero.

preserve []string (default 'mode')

List of attributes that are preserved when copying files.
Currently supported attributes are 'mode' (i.a. access mode) and 'timestamps' (i.e. modification time and access time).
Note: Preserving other attribute like ownership of change/birth timestamp is desireable, but not portably supported in go.

preview bool (default true)

Show previews of files and directories at the right most pane.
Expand Down Expand Up @@ -1321,7 +1328,7 @@ lf uses its own builtin copy and move operations by default.
These are implemented as asynchronous operations and progress is shown in the bottom ruler.
These commands do not overwrite existing files or directories with the same name.
Instead, a suffix that is compatible with '--backup=numbered' option in GNU cp is added to the new files or directories.
Only file modes are preserved and all other attributes are ignored including ownership, timestamps, context, and xattr.
Only file modes and (some) timestamps can be preserved (see `preserve` option), all other attributes are ignored including ownership, context, and xattr.
Special files such as character and block devices, named pipes, and sockets are skipped and links are not followed.
Moving is performed using the rename operation of the underlying OS.
For cross-device moving, lf falls back to copying and then deletes the original files if there are no errors.
Expand Down
29 changes: 19 additions & 10 deletions docstring.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,21 @@ func (e *setExpr) eval(app *app, args []string) {
}
}
gOpts.ruler = toks
case "preserve":
if e.val == "" {
gOpts.preserve = nil
return
}
toks := strings.Split(e.val, ":")
for _,s := range toks {
switch s {
case "mode", "timestamps":
default:
app.ui.echoerr("preserve: should consist of 'mode' or 'timestamps separated with colon")
return
}
}
gOpts.preserve = toks
case "infotimefmtnew":
gOpts.infotimefmtnew = e.val
case "infotimefmtold":
Expand Down
9 changes: 8 additions & 1 deletion lf.1
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ The following options can be used to customize the behavior of lf:
number bool (default false)
numberfmt string (default "\e033[33m")
period int (default 0)
preserve []string (default "mode")
preview bool (default true)
previewer string (default '')
promptfmt string (default "\e033[32;1m%u@%h\e033[0m:\e033[34;1m%d\e033[0m\e033[1m%f\e033[0m")
Expand Down Expand Up @@ -954,6 +955,12 @@ Format string of the position number for each line.
.PP
Set the interval in seconds for periodic checks of directory updates. This works by periodically calling the 'load' command. Note that directories are already updated automatically in many cases. This option can be useful when there is an external process changing the displayed directory and you are not doing anything in lf. Periodic checks are disabled when the value of this option is set to zero.
.PP
.EX
preserve []string (default 'mode')
.EE
.PP
List of attributes that are preserved when copying files. Currently supported attributes are 'mode' (i.a. access mode) and 'timestamps' (i.e. modification time and access time). Note: Preserving other attribute like ownership of change/birth timestamp is desireable, but not portably supported in go.
.PP
.EX
preview bool (default true)
.EE
Expand Down Expand Up @@ -1549,7 +1556,7 @@ Besides 'send' command, there is a 'quit' command to quit the server when there
.PP
Lastly, there is a 'conn' command to connect the server as a client. This should not be needed for users.
.SH FILE OPERATIONS
lf uses its own builtin copy and move operations by default. These are implemented as asynchronous operations and progress is shown in the bottom ruler. These commands do not overwrite existing files or directories with the same name. Instead, a suffix that is compatible with '--backup=numbered' option in GNU cp is added to the new files or directories. Only file modes are preserved and all other attributes are ignored including ownership, timestamps, context, and xattr. Special files such as character and block devices, named pipes, and sockets are skipped and links are not followed. Moving is performed using the rename operation of the underlying OS. For cross-device moving, lf falls back to copying and then deletes the original files if there are no errors. Operation errors are shown in the message line as well as the log file and they do not preemptively finish the corresponding file operation.
lf uses its own builtin copy and move operations by default. These are implemented as asynchronous operations and progress is shown in the bottom ruler. These commands do not overwrite existing files or directories with the same name. Instead, a suffix that is compatible with '--backup=numbered' option in GNU cp is added to the new files or directories. Only file modes and (some) timestamps can be preserved (see `preserve` option), all other attributes are ignored including ownership, context, and xattr. Special files such as character and block devices, named pipes, and sockets are skipped and links are not followed. Moving is performed using the rename operation of the underlying OS. For cross-device moving, lf falls back to copying and then deletes the original files if there are no errors. Operation errors are shown in the message line as well as the log file and they do not preemptively finish the corresponding file operation.
.PP
File operations can be performed on the current selected file or alternatively on multiple files by selecting them first. When you 'copy' a file, lf doesn't actually copy the file on the disk, but only records its name to a file. The actual file copying takes place when you 'paste'. Similarly 'paste' after a 'cut' operation moves the file.
.PP
Expand Down
2 changes: 2 additions & 0 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ var gOpts struct {
history bool
info []string
ruler []string
preserve []string
shellopts []string
keys map[string]expr
cmdkeys map[string]expr
Expand Down Expand Up @@ -134,6 +135,7 @@ func init() {
gOpts.history = true
gOpts.info = nil
gOpts.ruler = []string{"acc", "progress", "selection", "filter", "ind"}
gOpts.preserve= []string{"mode"}
gOpts.shellopts = nil
gOpts.sortType = sortType{naturalSort, dirfirstSort}
gOpts.tempmarks = "'"
Expand Down