Clean up exclude pattern handling
This commit is contained in:
parent
5cc3d3cbfe
commit
6de77ce987
@ -62,6 +62,9 @@ Detailed list of changes
|
|||||||
|
|
||||||
- macOS: Fix the maximized window not taking up full space when the title bar is hidden or when :opt:`resize_in_steps` is configured (:iss:`6021`)
|
- macOS: Fix the maximized window not taking up full space when the title bar is hidden or when :opt:`resize_in_steps` is configured (:iss:`6021`)
|
||||||
|
|
||||||
|
- ssh kitten: Change the syntax of glob patterns slightly to match common usage
|
||||||
|
elsewhere. Now the syntax is the same a "extendedglob" in most shells.
|
||||||
|
|
||||||
|
|
||||||
0.27.1 [2023-02-07]
|
0.27.1 [2023-02-07]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|||||||
@ -38,10 +38,12 @@ type=list
|
|||||||
A glob pattern. Files with names matching this pattern are excluded from being
|
A glob pattern. Files with names matching this pattern are excluded from being
|
||||||
transferred. Useful when adding directories. Can
|
transferred. Useful when adding directories. Can
|
||||||
be specified multiple times, if any of the patterns match the file will be
|
be specified multiple times, if any of the patterns match the file will be
|
||||||
excluded. To exclude a directory use a pattern like :code:`**/directory_name/**`.
|
excluded. If the pattern includes a :code:`/` then it will match against the full
|
||||||
Based on standard wildcards with the addition that ``/**/`` matches any number of directories
|
path, not just the filename. In such patterns you can use :code:`/**/` to match zero
|
||||||
and patterns starting with a single :code:`*` (as opposed to two asterisks) match any filename prefix.
|
or more directories. For example, to exclude a directory and everything under it use
|
||||||
See the :link:`detailed syntax <https://github.com/bmatcuk/doublestar#patterns>`.
|
:code:`**/directory_name`.
|
||||||
|
See the :link:`detailed syntax <https://github.com/bmatcuk/doublestar#patterns>` for
|
||||||
|
how wildcards match.
|
||||||
|
|
||||||
|
|
||||||
--symlink-strategy
|
--symlink-strategy
|
||||||
@ -52,7 +54,8 @@ the symlink, re-creating it on the remote machine. Setting this to :code:`resolv
|
|||||||
will cause the symlink to be followed and its target used as the file/directory to copy.
|
will cause the symlink to be followed and its target used as the file/directory to copy.
|
||||||
The value of :code:`keep-path` is the same as :code:`resolve` except that the remote
|
The value of :code:`keep-path` is the same as :code:`resolve` except that the remote
|
||||||
file path is derived from the symlink's path instead of the path of the symlink's target.
|
file path is derived from the symlink's path instead of the path of the symlink's target.
|
||||||
Note that this option does not apply to symlinks encountered while recursively copying directories.
|
Note that this option does not apply to symlinks encountered while recursively copying directories,
|
||||||
|
those are always preserved.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,8 @@ print(' '.join(map(str, buf)))'''), lines=13, cols=77)
|
|||||||
os.makedirs(f'{local_home}/d1/d2/d3')
|
os.makedirs(f'{local_home}/d1/d2/d3')
|
||||||
touch('d1/d2/x')
|
touch('d1/d2/x')
|
||||||
touch('d1/d2/w.exclude')
|
touch('d1/d2/w.exclude')
|
||||||
|
os.mkdir(f'{local_home}/d1/r')
|
||||||
|
touch('d1/r/noooo')
|
||||||
os.symlink('d2/x', f'{local_home}/d1/y')
|
os.symlink('d2/x', f'{local_home}/d1/y')
|
||||||
os.symlink('simple-file', f'{local_home}/s1')
|
os.symlink('simple-file', f'{local_home}/s1')
|
||||||
os.symlink('simple-file', f'{local_home}/s2')
|
os.symlink('simple-file', f'{local_home}/s2')
|
||||||
@ -85,7 +87,7 @@ copy s1
|
|||||||
copy --symlink-strategy=keep-path s2
|
copy --symlink-strategy=keep-path s2
|
||||||
copy --dest=a/sfa simple-file
|
copy --dest=a/sfa simple-file
|
||||||
copy --glob g.*
|
copy --glob g.*
|
||||||
copy --exclude **/w.* d1
|
copy --exclude **/w.* --exclude **/r d1
|
||||||
'''
|
'''
|
||||||
self.check_bootstrap(
|
self.check_bootstrap(
|
||||||
sh, remote_home, test_script='env; exit 0', SHELL_INTEGRATION_VALUE='', conf=conf, home=local_home,
|
sh, remote_home, test_script='env; exit 0', SHELL_INTEGRATION_VALUE='', conf=conf, home=local_home,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -230,7 +231,7 @@ type file_unique_id struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func excluded(pattern, path string) bool {
|
func excluded(pattern, path string) bool {
|
||||||
if strings.HasPrefix(pattern, "*") && !strings.HasPrefix(pattern, "**") {
|
if !strings.ContainsRune(pattern, '/') {
|
||||||
path = filepath.Base(path)
|
path = filepath.Base(path)
|
||||||
}
|
}
|
||||||
if matched, err := doublestar.PathMatch(pattern, path); matched && err == nil {
|
if matched, err := doublestar.PathMatch(pattern, path); matched && err == nil {
|
||||||
@ -239,13 +240,13 @@ func excluded(pattern, path string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_file_data(callback func(h *tar.Header, data []byte) error, seen map[file_unique_id]string, local_path, arcname string, exclude_patterns []string, recurse bool) error {
|
func get_file_data(callback func(h *tar.Header, data []byte) error, seen map[file_unique_id]string, local_path, arcname string, exclude_patterns []string) error {
|
||||||
s, err := os.Lstat(local_path)
|
s, err := os.Lstat(local_path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u, ok := s.Sys().(unix.Stat_t)
|
u, ok := s.Sys().(unix.Stat_t)
|
||||||
cb := func(h *tar.Header, data []byte) error {
|
cb := func(h *tar.Header, data []byte, arcname string) error {
|
||||||
h.Name = arcname
|
h.Name = arcname
|
||||||
if h.Typeflag == tar.TypeDir {
|
if h.Typeflag == tar.TypeDir {
|
||||||
h.Name = strings.TrimRight(h.Name, "/") + "/"
|
h.Name = strings.TrimRight(h.Name, "/") + "/"
|
||||||
@ -270,41 +271,57 @@ func get_file_data(callback func(h *tar.Header, data []byte) error, seen map[fil
|
|||||||
err = cb(&tar.Header{
|
err = cb(&tar.Header{
|
||||||
Typeflag: tar.TypeSymlink,
|
Typeflag: tar.TypeSymlink,
|
||||||
Linkname: target,
|
Linkname: target,
|
||||||
}, nil)
|
}, nil, arcname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case fs.ModeDir:
|
case fs.ModeDir:
|
||||||
err = cb(&tar.Header{Typeflag: tar.TypeDir}, nil)
|
local_path = filepath.Clean(local_path)
|
||||||
|
type entry struct {
|
||||||
|
path, arcname string
|
||||||
|
}
|
||||||
|
stack := []entry{{local_path, arcname}}
|
||||||
|
for len(stack) > 0 {
|
||||||
|
x := stack[0]
|
||||||
|
stack = stack[1:]
|
||||||
|
entries, err := os.ReadDir(x.path)
|
||||||
|
if err != nil {
|
||||||
|
if x.path == local_path {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = cb(&tar.Header{Typeflag: tar.TypeDir}, nil, x.arcname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if recurse {
|
for _, e := range entries {
|
||||||
local_path = filepath.Clean(local_path)
|
entry_path := filepath.Join(x.path, e.Name())
|
||||||
return filepath.WalkDir(local_path, func(path string, d fs.DirEntry, werr error) error {
|
aname := path.Join(x.arcname, e.Name())
|
||||||
clean_path := filepath.Clean(path)
|
ok := true
|
||||||
if clean_path == local_path {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, pat := range exclude_patterns {
|
for _, pat := range exclude_patterns {
|
||||||
if excluded(pat, clean_path) {
|
if excluded(pat, entry_path) {
|
||||||
return nil
|
ok = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if werr == nil {
|
if !ok {
|
||||||
rel, err := filepath.Rel(local_path, path)
|
continue
|
||||||
if err == nil {
|
}
|
||||||
aname := filepath.Join(arcname, rel)
|
if e.IsDir() {
|
||||||
return get_file_data(callback, seen, clean_path, aname, nil, false)
|
stack = append(stack, entry{entry_path, aname})
|
||||||
|
} else {
|
||||||
|
err = get_file_data(callback, seen, entry_path, aname, exclude_patterns)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
case 0: // Regular file
|
case 0: // Regular file
|
||||||
fid := file_unique_id{dev: u.Dev, inode: u.Ino}
|
fid := file_unique_id{dev: u.Dev, inode: u.Ino}
|
||||||
if prev, ok := seen[fid]; ok { // Hard link
|
if prev, ok := seen[fid]; ok { // Hard link
|
||||||
err = cb(&tar.Header{Typeflag: tar.TypeLink, Linkname: prev}, nil)
|
err = cb(&tar.Header{Typeflag: tar.TypeLink, Linkname: prev}, nil, arcname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -314,7 +331,7 @@ func get_file_data(callback func(h *tar.Header, data []byte) error, seen map[fil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = cb(&tar.Header{Typeflag: tar.TypeReg}, data)
|
err = cb(&tar.Header{Typeflag: tar.TypeReg}, data, arcname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -327,7 +344,7 @@ func (ci *CopyInstruction) get_file_data(callback func(h *tar.Header, data []byt
|
|||||||
for _, folder_name := range []string{"__pycache__", ".DS_Store"} {
|
for _, folder_name := range []string{"__pycache__", ".DS_Store"} {
|
||||||
ep = append(ep, "**/"+folder_name, "**/"+folder_name+"/**")
|
ep = append(ep, "**/"+folder_name, "**/"+folder_name+"/**")
|
||||||
}
|
}
|
||||||
return get_file_data(callback, seen, ci.local_path, ci.arcname, ep, true)
|
return get_file_data(callback, seen, ci.local_path, ci.arcname, ep)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigSet struct {
|
type ConfigSet struct {
|
||||||
|
|||||||
@ -275,7 +275,7 @@ func make_tarfile(cd *connection_data, get_local_env func(string) (string, bool)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, ci := range cd.host_opts.Copy {
|
for _, ci := range cd.host_opts.Copy {
|
||||||
err = get_file_data(add, seen, ci.local_path, ci.arcname, ci.exclude_patterns, true)
|
err = ci.get_file_data(add, seen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user