skilleter-thingy 0.1.18__py3-none-any.whl → 0.1.21__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of skilleter-thingy might be problematic. Click here for more details.
- skilleter_thingy/ffind.py +2 -2
- skilleter_thingy/ggit.py +1 -1
- skilleter_thingy/ggrep.py +2 -1
- skilleter_thingy/git_br.py +3 -3
- skilleter_thingy/git_common.py +2 -2
- skilleter_thingy/git_hold.py +6 -6
- skilleter_thingy/git_parent.py +4 -3
- skilleter_thingy/git_review.py +16 -12
- skilleter_thingy/git_update.py +2 -2
- skilleter_thingy/git_wt.py +5 -5
- skilleter_thingy/gitcmp_helper.py +6 -1
- skilleter_thingy/gphotosync.py +12 -8
- skilleter_thingy/localphotosync.py +77 -358
- skilleter_thingy/multigit.py +30 -34
- skilleter_thingy/photodupe.py +10 -10
- skilleter_thingy/py_audit.py +4 -2
- skilleter_thingy/readable.py +13 -11
- skilleter_thingy/sysmon.py +2 -2
- skilleter_thingy/thingy/colour.py +6 -2
- skilleter_thingy/thingy/dircolors.py +20 -18
- skilleter_thingy/thingy/git2.py +0 -1
- skilleter_thingy/thingy/popup.py +1 -1
- skilleter_thingy/thingy/tfm_pane.py +3 -3
- skilleter_thingy/thingy/tidy.py +14 -13
- skilleter_thingy/trimpath.py +5 -4
- skilleter_thingy/venv_create.py +1 -1
- {skilleter_thingy-0.1.18.dist-info → skilleter_thingy-0.1.21.dist-info}/METADATA +1 -1
- skilleter_thingy-0.1.21.dist-info/PKG-INFO 2 +193 -0
- {skilleter_thingy-0.1.18.dist-info → skilleter_thingy-0.1.21.dist-info}/RECORD +33 -32
- {skilleter_thingy-0.1.18.dist-info → skilleter_thingy-0.1.21.dist-info}/WHEEL +0 -0
- {skilleter_thingy-0.1.18.dist-info → skilleter_thingy-0.1.21.dist-info}/entry_points.txt +0 -0
- {skilleter_thingy-0.1.18.dist-info → skilleter_thingy-0.1.21.dist-info}/licenses/LICENSE +0 -0
- {skilleter_thingy-0.1.18.dist-info → skilleter_thingy-0.1.21.dist-info}/top_level.txt +0 -0
skilleter_thingy/ffind.py
CHANGED
|
@@ -380,10 +380,10 @@ def validate_arguments(args):
|
|
|
380
380
|
|
|
381
381
|
if args.git:
|
|
382
382
|
if git.working_tree() is None:
|
|
383
|
-
colour.error('
|
|
383
|
+
colour.error('The current directory is not inside a git working tree', prefix=True)
|
|
384
384
|
|
|
385
385
|
if args.dir:
|
|
386
|
-
colour.error('
|
|
386
|
+
colour.error('Git does not track directories, so you cannot search for them in a git working tree', prefix=True)
|
|
387
387
|
|
|
388
388
|
if args.verbose:
|
|
389
389
|
print(f'Searching directory "{args.path}" for matches with "{args.patterns}"')
|
skilleter_thingy/ggit.py
CHANGED
|
@@ -55,7 +55,7 @@ def main():
|
|
|
55
55
|
try:
|
|
56
56
|
os.chdir(args.path)
|
|
57
57
|
except FileNotFoundError:
|
|
58
|
-
colour.error(f'
|
|
58
|
+
colour.error(f'Invalid path [BLUE:{args.path}]', prefix=True)
|
|
59
59
|
|
|
60
60
|
# If the current directory is in a working tree and below the top-level
|
|
61
61
|
# directory then we run the git command here as well as in subdirectories
|
skilleter_thingy/ggrep.py
CHANGED
|
@@ -39,7 +39,8 @@ def parse_command_line():
|
|
|
39
39
|
parser.add_argument('--files-with-matches', '-l', action='store_true', help='Show only the names of files that contain matches')
|
|
40
40
|
parser.add_argument('--files-without-matches', '-L', action='store_true', help='Show only the names of files that do NOT contain matches')
|
|
41
41
|
parser.add_argument('--wildcard', '-W', action='append', help='Only search files matching the wildcard(s)')
|
|
42
|
-
parser.add_argument('--only-matching', '-o', action='store_true',
|
|
42
|
+
parser.add_argument('--only-matching', '-o', action='store_true',
|
|
43
|
+
help='Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.')
|
|
43
44
|
parser.add_argument('--no-color', action='store_true', help='Turn off match highlighting')
|
|
44
45
|
parser.add_argument('pattern', action='store', help='Regular expression to search for')
|
|
45
46
|
parser.add_argument('paths', nargs='*', help='Optional list of one or more paths to search')
|
skilleter_thingy/git_br.py
CHANGED
|
@@ -39,7 +39,7 @@ def parse_command_line():
|
|
|
39
39
|
args = parser.parse_args()
|
|
40
40
|
|
|
41
41
|
if args.delete and not args.branches:
|
|
42
|
-
colour.error('
|
|
42
|
+
colour.error('You must specify the branches to delete', prefix=True)
|
|
43
43
|
|
|
44
44
|
return args
|
|
45
45
|
|
|
@@ -105,7 +105,7 @@ def list_branches(branches):
|
|
|
105
105
|
output = []
|
|
106
106
|
|
|
107
107
|
for i, field in enumerate(branch):
|
|
108
|
-
if i==1:
|
|
108
|
+
if i == 1:
|
|
109
109
|
field = parse(field)
|
|
110
110
|
time_str = field.strftime('%H:%M:%S')
|
|
111
111
|
|
|
@@ -172,7 +172,7 @@ def git_br():
|
|
|
172
172
|
except BrokenPipeError:
|
|
173
173
|
sys.exit(2)
|
|
174
174
|
except git.GitError as exc:
|
|
175
|
-
colour.error(
|
|
175
|
+
colour.error(exc.msg, status=exc.status, prefix=True)
|
|
176
176
|
|
|
177
177
|
################################################################################
|
|
178
178
|
|
skilleter_thingy/git_common.py
CHANGED
|
@@ -27,12 +27,12 @@ def main():
|
|
|
27
27
|
args = parser.parse_args()
|
|
28
28
|
|
|
29
29
|
if args.long and args.short:
|
|
30
|
-
colour.error('
|
|
30
|
+
colour.error('The [BLUE:--long] and [BLUE:--short] options cannot be used together', prefix=True)
|
|
31
31
|
|
|
32
32
|
try:
|
|
33
33
|
ancestor = git.find_common_ancestor(args.commit1, args.commit2)
|
|
34
34
|
except git.GitError as exc:
|
|
35
|
-
colour.error(
|
|
35
|
+
colour.error(exc, status=exc.status, prefix=True)
|
|
36
36
|
|
|
37
37
|
if args.short:
|
|
38
38
|
print(ancestor)
|
skilleter_thingy/git_hold.py
CHANGED
|
@@ -48,13 +48,13 @@ def archive_branches(branches):
|
|
|
48
48
|
|
|
49
49
|
for branch in branches:
|
|
50
50
|
if not git.isbranch(branch):
|
|
51
|
-
colour.error(f'
|
|
51
|
+
colour.error(f'Branch {branch} does not exist', prefix=True)
|
|
52
52
|
|
|
53
53
|
if archive_tag_name(branch) in tags:
|
|
54
|
-
colour.error(f'
|
|
54
|
+
colour.error(f'An archive tag already exists for branch {branch}', prefix=True)
|
|
55
55
|
|
|
56
56
|
if branch == current_branch:
|
|
57
|
-
colour.error('
|
|
57
|
+
colour.error('Cannot archive the current branch', prefix=True)
|
|
58
58
|
|
|
59
59
|
for branch in branches:
|
|
60
60
|
tag_name = archive_tag_name(branch)
|
|
@@ -96,7 +96,7 @@ def restore_archive_branches(branches):
|
|
|
96
96
|
|
|
97
97
|
for branch in branches:
|
|
98
98
|
if archive_tag_name(branch) not in tags:
|
|
99
|
-
colour.error(f'
|
|
99
|
+
colour.error(f'Archive branch {branch} does not exist', prefix=True)
|
|
100
100
|
|
|
101
101
|
archive_tag_names = []
|
|
102
102
|
|
|
@@ -123,10 +123,10 @@ def main():
|
|
|
123
123
|
args = parser.parse_args()
|
|
124
124
|
|
|
125
125
|
if args.list and args.restore:
|
|
126
|
-
colour.error('
|
|
126
|
+
colour.error('The list and restore options cannot be specified together', prefix=True)
|
|
127
127
|
|
|
128
128
|
if not args.branches and not args.list:
|
|
129
|
-
colour.error('
|
|
129
|
+
colour.error('No branches specified', prefix=True)
|
|
130
130
|
|
|
131
131
|
if args.list:
|
|
132
132
|
list_archive_branches(args.branches)
|
skilleter_thingy/git_parent.py
CHANGED
|
@@ -23,7 +23,8 @@ def main():
|
|
|
23
23
|
parser = argparse.ArgumentParser(description='Attempt to determine the parent branch for the specified branch (defaulting to the current one)')
|
|
24
24
|
parser.add_argument('--all', '-a', action='store_true', help='Include feature branches as possible parents')
|
|
25
25
|
parser.add_argument('--verbose', '-v', action='store_true', help='Report verbose results (includes number of commits between branch and parent)')
|
|
26
|
-
parser.add_argument('branch', action='store', nargs='?', type=str, default=current_branch,
|
|
26
|
+
parser.add_argument('branch', action='store', nargs='?', type=str, default=current_branch,
|
|
27
|
+
help=f'Branch, commit or commit (defaults to current branch; {current_branch})')
|
|
27
28
|
|
|
28
29
|
args = parser.parse_args()
|
|
29
30
|
|
|
@@ -48,7 +49,7 @@ def main():
|
|
|
48
49
|
parents.append(more)
|
|
49
50
|
|
|
50
51
|
except git.GitError as exc:
|
|
51
|
-
colour.error(
|
|
52
|
+
colour.error(exc.msg, status=exc.status, prefix=True)
|
|
52
53
|
|
|
53
54
|
if parents:
|
|
54
55
|
if args.verbose:
|
|
@@ -59,7 +60,7 @@ def main():
|
|
|
59
60
|
else:
|
|
60
61
|
print(', '.join(parents))
|
|
61
62
|
else:
|
|
62
|
-
colour.error('
|
|
63
|
+
colour.error('Could not determine parent branch\n', prefix=True)
|
|
63
64
|
|
|
64
65
|
################################################################################
|
|
65
66
|
|
skilleter_thingy/git_review.py
CHANGED
|
@@ -228,7 +228,11 @@ class GitReview():
|
|
|
228
228
|
|
|
229
229
|
# Move to the top-level directory in the working tree
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
try:
|
|
232
|
+
self.current_dir = os.getcwd()
|
|
233
|
+
except FileNotFoundError:
|
|
234
|
+
raise GitReviewError('Unable to locate current directory')
|
|
235
|
+
|
|
232
236
|
self.working_tree_dir = git.working_tree()
|
|
233
237
|
|
|
234
238
|
if not self.working_tree_dir:
|
|
@@ -444,7 +448,7 @@ class GitReview():
|
|
|
444
448
|
self.filter_modified = pickle_data.get('filter_modified', self.filter_modified)
|
|
445
449
|
self.sort_order = pickle_data.get('sort_order', self.sort_order)
|
|
446
450
|
self.reverse_sort = pickle_data.get('reverse_sort', self.reverse_sort)
|
|
447
|
-
self.filter_none_whitespace_only= pickle_data.get('filter_none_whitespace_only', self.filter_none_whitespace_only)
|
|
451
|
+
self.filter_none_whitespace_only = pickle_data.get('filter_none_whitespace_only', self.filter_none_whitespace_only)
|
|
448
452
|
self.show_none_whitespace_stats = pickle_data.get('show_none_whitespace_stats', self.show_none_whitespace_stats)
|
|
449
453
|
|
|
450
454
|
# Transfer the reviewed flag for each file in the pickle
|
|
@@ -456,7 +460,7 @@ class GitReview():
|
|
|
456
460
|
newfile['reviewed'] = oldfile['reviewed']
|
|
457
461
|
break
|
|
458
462
|
|
|
459
|
-
except (EOFError, pickle.UnpicklingError, ModuleNotFoundError, AttributeError):
|
|
463
|
+
except (EOFError, pickle.UnpicklingError, ModuleNotFoundError, AttributeError): # TODO: Why did I get ModuleNotFoundError or AttributeError????
|
|
460
464
|
pass
|
|
461
465
|
|
|
462
466
|
self.__constrain_display_parameters()
|
|
@@ -1309,13 +1313,13 @@ def parse_command_line():
|
|
|
1309
1313
|
# Make sure that we're actually in a git working tree
|
|
1310
1314
|
|
|
1311
1315
|
if not git.working_tree():
|
|
1312
|
-
colour.error('
|
|
1316
|
+
colour.error('Not a git repository', prefix=True)
|
|
1313
1317
|
|
|
1314
1318
|
# -C/--change is shorthand for '--commit HEAD^'
|
|
1315
1319
|
|
|
1316
1320
|
if args.change:
|
|
1317
1321
|
if args.commits:
|
|
1318
|
-
colour.error('
|
|
1322
|
+
colour.error('The -C/--change option does not take parameters', prefix=True)
|
|
1319
1323
|
|
|
1320
1324
|
args.commits = ['HEAD^']
|
|
1321
1325
|
|
|
@@ -1345,7 +1349,7 @@ def parse_command_line():
|
|
|
1345
1349
|
paths.append(entry)
|
|
1346
1350
|
parsing_commits = False
|
|
1347
1351
|
else:
|
|
1348
|
-
colour.error(f'
|
|
1352
|
+
colour.error(f'Invalid path/commit: {entry}', prefix=True)
|
|
1349
1353
|
|
|
1350
1354
|
args.commits = commits
|
|
1351
1355
|
args.paths = paths
|
|
@@ -1353,13 +1357,13 @@ def parse_command_line():
|
|
|
1353
1357
|
# Validate the commits & paths
|
|
1354
1358
|
|
|
1355
1359
|
if len(args.commits) > 2:
|
|
1356
|
-
colour.error('
|
|
1360
|
+
colour.error('No more than 2 commits can be specified', prefix=True)
|
|
1357
1361
|
|
|
1358
1362
|
if (args.branch or args.commit) and args.commits:
|
|
1359
|
-
colour.error('
|
|
1363
|
+
colour.error('Additional commits should not be specified in conjunction with the -b/--branch option', prefix=True)
|
|
1360
1364
|
|
|
1361
1365
|
if args.commit and args.branch:
|
|
1362
|
-
colour.error('
|
|
1366
|
+
colour.error('The -c/--commit and -b/--branch options are mutually exclusive', prefix=True)
|
|
1363
1367
|
|
|
1364
1368
|
# If the -c/--commit option is used, then review against its parent
|
|
1365
1369
|
# If the -b/--branch option is used, then review against the oldest common ancestor
|
|
@@ -1369,7 +1373,7 @@ def parse_command_line():
|
|
|
1369
1373
|
try:
|
|
1370
1374
|
args.commits = [git.find_common_ancestor('HEAD', args.branch)]
|
|
1371
1375
|
except git.GitError as exc:
|
|
1372
|
-
colour.error(
|
|
1376
|
+
colour.error(exc, status=exc.status, prefix=True)
|
|
1373
1377
|
|
|
1374
1378
|
elif args.commit:
|
|
1375
1379
|
args.commits = ['%s^' % args.commit, args.commit]
|
|
@@ -1387,9 +1391,9 @@ def parse_command_line():
|
|
|
1387
1391
|
if len(matches) == 1:
|
|
1388
1392
|
args.commits[i] = matches[0]
|
|
1389
1393
|
else:
|
|
1390
|
-
colour.error(f'
|
|
1394
|
+
colour.error(f'Multiple commits match {entry}', prefix=True)
|
|
1391
1395
|
else:
|
|
1392
|
-
colour.error(f'
|
|
1396
|
+
colour.error(f'{entry} is not a valid commit ID', prefix=True)
|
|
1393
1397
|
|
|
1394
1398
|
# Things work easier if we always have two commits to compare
|
|
1395
1399
|
|
skilleter_thingy/git_update.py
CHANGED
|
@@ -85,7 +85,7 @@ def branch_rebase(args, results, branch):
|
|
|
85
85
|
if args.all_parents:
|
|
86
86
|
parents, _ = git.parents()
|
|
87
87
|
else:
|
|
88
|
-
parents, _ = git.parents(ignore='feature/*'
|
|
88
|
+
parents, _ = git.parents(ignore='feature/*')
|
|
89
89
|
|
|
90
90
|
logging.debug('Probable parents of %s: %s', branch, parents)
|
|
91
91
|
|
|
@@ -314,7 +314,7 @@ def main():
|
|
|
314
314
|
|
|
315
315
|
# List of stuff that's been done, to report in the summary
|
|
316
316
|
|
|
317
|
-
results = {'deleted': set(), 'pulled': set(), 'failed': set(), 'rebased': set(), 'unchanged': set(), 'no-tracking': set()
|
|
317
|
+
results = {'deleted': set(), 'pulled': set(), 'failed': set(), 'rebased': set(), 'unchanged': set(), 'no-tracking': set()}
|
|
318
318
|
|
|
319
319
|
to_rebase = set()
|
|
320
320
|
|
skilleter_thingy/git_wt.py
CHANGED
|
@@ -26,15 +26,15 @@ def main():
|
|
|
26
26
|
parser = argparse.ArgumentParser(description='Report top-level directory of the current git working tree.')
|
|
27
27
|
parser.add_argument('--parent', '-p', action='store_true',
|
|
28
28
|
help='If we are already at the top of the working tree, check if the parent directory is in a working tree and output the top-level directory of that tree.')
|
|
29
|
-
parser.add_argument('--dir', '-d', action='store', default=
|
|
29
|
+
parser.add_argument('--dir', '-d', action='store', default=None,
|
|
30
30
|
help='Find the location of the top-level directory in the working tree starting at the specified directory')
|
|
31
31
|
|
|
32
32
|
args = parser.parse_args()
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
sys.stderr.write(
|
|
34
|
+
try:
|
|
35
|
+
start_dir = os.path.abspath(args.dir or os.getcwd())
|
|
36
|
+
except FileNotFoundError:
|
|
37
|
+
sys.stderr.write('Unable to determine initial directory\n')
|
|
38
38
|
sys.exit(1)
|
|
39
39
|
|
|
40
40
|
# Search for a .git directory in the current or parent directories
|
|
@@ -169,7 +169,12 @@ def main():
|
|
|
169
169
|
|
|
170
170
|
# Determine the best way of reporting the path to the file
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
try:
|
|
173
|
+
working_tree_path = os.getcwd()
|
|
174
|
+
except FileNotFoundError:
|
|
175
|
+
sys.stderr.write('Unable to get current working directory')
|
|
176
|
+
sys.exit(2)
|
|
177
|
+
|
|
173
178
|
current_path = os.path.join(working_tree_path, os.getenv('GIT_SUBDIR', ''))
|
|
174
179
|
|
|
175
180
|
current_file_path = os.path.relpath(args.file_path if args.new_name is None else args.new_name, current_path)
|
skilleter_thingy/gphotosync.py
CHANGED
|
@@ -94,7 +94,7 @@ def parse_yyyymm(datestr):
|
|
|
94
94
|
date_match = YYYY_MM_re.fullmatch(datestr)
|
|
95
95
|
|
|
96
96
|
if not date_match:
|
|
97
|
-
colour.error(f'
|
|
97
|
+
colour.error(f'Invalid date: {datestr}', prefix=True)
|
|
98
98
|
|
|
99
99
|
return datetime.date(int(date_match.group(1)), int(date_match.group(2)), day=1)
|
|
100
100
|
|
|
@@ -111,15 +111,19 @@ def parse_command_line():
|
|
|
111
111
|
|
|
112
112
|
parser.add_argument('--verbose', '-v', action='store_true', help='Output verbose status information')
|
|
113
113
|
parser.add_argument('--dryrun', '-D', action='store_true', help='Just list files to be copied, without actually copying them')
|
|
114
|
-
parser.add_argument('--picturedir', '-P', action='store', default=DEFAULT_PHOTO_DIR,
|
|
115
|
-
|
|
114
|
+
parser.add_argument('--picturedir', '-P', action='store', default=DEFAULT_PHOTO_DIR,
|
|
115
|
+
help=f'Location of local picture storage directory (defaults to {DEFAULT_PHOTO_DIR})')
|
|
116
|
+
parser.add_argument('--videodir', '-V', action='store', default=DEFAULT_VIDEO_DIR,
|
|
117
|
+
help=f'Location of local video storage directory (defaults to {DEFAULT_VIDEO_DIR})')
|
|
116
118
|
parser.add_argument('--start', '-s', action='store', default=None, help='Start date (in the form YYYY-MM, defaults to current month)')
|
|
117
119
|
parser.add_argument('--end', '-e', action='store', default=None, help=f'End date (in the form YYYY-MM, defaults to {DEFAULT_MONTHS} before the start date)')
|
|
118
120
|
parser.add_argument('--months', '-m', action='store', type=int, default=None, help='Synchronise this number of months of data (current month included)')
|
|
119
121
|
parser.add_argument('--cache', '-c', action='store', default=DEFAULT_CACHE_DIR, help=f'Cache directory for Google photos (defaults to {DEFAULT_CACHE_DIR})')
|
|
120
|
-
parser.add_argument('--rclone', '-r', action='store', default=DEFAULT_RCLONE_REMOTE,
|
|
122
|
+
parser.add_argument('--rclone', '-r', action='store', default=DEFAULT_RCLONE_REMOTE,
|
|
123
|
+
help=f'rclone remote name for Google photos (defaults to {DEFAULT_RCLONE_REMOTE})')
|
|
121
124
|
parser.add_argument('--no-update', '-N', action='store_true', help='Do not update local cache')
|
|
122
|
-
parser.add_argument('--keep', '-k', action='store', type=int, default=DEFAULT_KEEP,
|
|
125
|
+
parser.add_argument('--keep', '-k', action='store', type=int, default=DEFAULT_KEEP,
|
|
126
|
+
help=f'Keep this number of months before the start date in the cache (defaults to {DEFAULT_KEEP})')
|
|
123
127
|
parser.add_argument('--skip-no-day', '-z', action='store_true', help='Don\'t sync files where the day of the month could not be determined')
|
|
124
128
|
parser.add_argument('action', nargs='*', help='Actions to perform (report or sync)')
|
|
125
129
|
|
|
@@ -387,9 +391,9 @@ def update_cache(args, year, month):
|
|
|
387
391
|
|
|
388
392
|
subprocess.run(cmd, check=True)
|
|
389
393
|
except subprocess.CalledProcessError:
|
|
390
|
-
colour.error(f'
|
|
394
|
+
colour.error(f'Failed to sync Google photos for month [BLUE:{month}] of year [BLUE:{year}]', prefix=True)
|
|
391
395
|
except FileNotFoundError as exc:
|
|
392
|
-
colour.error(
|
|
396
|
+
colour.error(exc, prefix=True)
|
|
393
397
|
|
|
394
398
|
################################################################################
|
|
395
399
|
|
|
@@ -450,7 +454,7 @@ def remove_duplicates(media_files):
|
|
|
450
454
|
# Originals can have upper or lower case extensions, copies only tend to have lower
|
|
451
455
|
# case, so build a lower case to original lookup table
|
|
452
456
|
|
|
453
|
-
names = {name.lower():name for name in media_files}
|
|
457
|
+
names = {name.lower(): name for name in media_files}
|
|
454
458
|
|
|
455
459
|
duplicates = defaultdict(list)
|
|
456
460
|
|