skilleter-thingy 0.0.39__py3-none-any.whl → 0.0.41__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.

Files changed (68) hide show
  1. skilleter_thingy/__init__.py +6 -0
  2. skilleter_thingy/addpath.py +107 -0
  3. skilleter_thingy/borger.py +269 -0
  4. skilleter_thingy/console_colours.py +63 -0
  5. skilleter_thingy/diskspacecheck.py +67 -0
  6. skilleter_thingy/docker_purge.py +113 -0
  7. skilleter_thingy/ffind.py +536 -0
  8. skilleter_thingy/ggit.py +90 -0
  9. skilleter_thingy/ggrep.py +154 -0
  10. skilleter_thingy/git_br.py +180 -0
  11. skilleter_thingy/git_ca.py +142 -0
  12. skilleter_thingy/git_cleanup.py +287 -0
  13. skilleter_thingy/git_co.py +220 -0
  14. skilleter_thingy/git_common.py +61 -0
  15. skilleter_thingy/git_hold.py +154 -0
  16. skilleter_thingy/git_mr.py +92 -0
  17. skilleter_thingy/git_parent.py +77 -0
  18. skilleter_thingy/git_review.py +1428 -0
  19. skilleter_thingy/git_update.py +385 -0
  20. skilleter_thingy/git_wt.py +96 -0
  21. skilleter_thingy/gitcmp_helper.py +322 -0
  22. skilleter_thingy/gitprompt.py +274 -0
  23. skilleter_thingy/gl.py +174 -0
  24. skilleter_thingy/gphotosync.py +610 -0
  25. skilleter_thingy/linecount.py +155 -0
  26. skilleter_thingy/moviemover.py +133 -0
  27. skilleter_thingy/photodupe.py +136 -0
  28. skilleter_thingy/phototidier.py +248 -0
  29. skilleter_thingy/py_audit.py +131 -0
  30. skilleter_thingy/readable.py +270 -0
  31. skilleter_thingy/remdir.py +126 -0
  32. skilleter_thingy/rmdupe.py +550 -0
  33. skilleter_thingy/rpylint.py +91 -0
  34. skilleter_thingy/splitpics.py +99 -0
  35. skilleter_thingy/strreplace.py +82 -0
  36. skilleter_thingy/sysmon.py +435 -0
  37. skilleter_thingy/tfm.py +920 -0
  38. skilleter_thingy/tfparse.py +101 -0
  39. skilleter_thingy/thingy/__init__.py +6 -0
  40. skilleter_thingy/thingy/colour.py +213 -0
  41. skilleter_thingy/thingy/dc_curses.py +278 -0
  42. skilleter_thingy/thingy/dc_defaults.py +221 -0
  43. skilleter_thingy/thingy/dc_util.py +50 -0
  44. skilleter_thingy/thingy/dircolors.py +308 -0
  45. skilleter_thingy/thingy/docker.py +95 -0
  46. skilleter_thingy/thingy/files.py +142 -0
  47. skilleter_thingy/thingy/git.py +1371 -0
  48. skilleter_thingy/thingy/git2.py +1307 -0
  49. skilleter_thingy/thingy/gitlab.py +193 -0
  50. skilleter_thingy/thingy/logger.py +112 -0
  51. skilleter_thingy/thingy/path.py +156 -0
  52. skilleter_thingy/thingy/popup.py +87 -0
  53. skilleter_thingy/thingy/process.py +112 -0
  54. skilleter_thingy/thingy/run.py +334 -0
  55. skilleter_thingy/thingy/tfm_pane.py +595 -0
  56. skilleter_thingy/thingy/tidy.py +160 -0
  57. skilleter_thingy/trimpath.py +84 -0
  58. skilleter_thingy/window_rename.py +92 -0
  59. skilleter_thingy/xchmod.py +125 -0
  60. skilleter_thingy/yamlcheck.py +89 -0
  61. {skilleter_thingy-0.0.39.dist-info → skilleter_thingy-0.0.41.dist-info}/METADATA +1 -1
  62. skilleter_thingy-0.0.41.dist-info/RECORD +66 -0
  63. skilleter_thingy-0.0.41.dist-info/top_level.txt +1 -0
  64. skilleter_thingy-0.0.39.dist-info/RECORD +0 -6
  65. skilleter_thingy-0.0.39.dist-info/top_level.txt +0 -1
  66. {skilleter_thingy-0.0.39.dist-info → skilleter_thingy-0.0.41.dist-info}/LICENSE +0 -0
  67. {skilleter_thingy-0.0.39.dist-info → skilleter_thingy-0.0.41.dist-info}/WHEEL +0 -0
  68. {skilleter_thingy-0.0.39.dist-info → skilleter_thingy-0.0.41.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,385 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """Thingy 'git-update' command - update the repo and rebase one more branches
5
+ against their parent branch, if this can be unambiguously determined.
6
+
7
+ Author: John Skilleter
8
+
9
+ Licence: GPL v3 or later
10
+
11
+ TODO: This is a partial solution - to do things properly, we'd have to work
12
+ out the branch tree structure, start at the bottom, pull and/or rebase
13
+ each one working upwards.
14
+
15
+ As it is, I'm assuming that we don't have a tree, but a bush, with
16
+ most things branched off a main, master or develop branch, so we pull
17
+ fixed branches first then rebase everything in no particular order.
18
+
19
+ TODO: Avoid lots of pulls - should be able to fetch then updated each local branch.
20
+ """
21
+ ################################################################################
22
+
23
+ import os
24
+ import sys
25
+ import argparse
26
+ import fnmatch
27
+ import logging
28
+
29
+ import thingy.git as git
30
+ import thingy.colour as colour
31
+
32
+ ################################################################################
33
+
34
+ def parse_command_line():
35
+ """Parse the command line"""
36
+
37
+ parser = argparse.ArgumentParser(description='Rebase branch(es) against their parent branch, updating both in the process')
38
+
39
+ parser.add_argument('-c', '--cleanup', action='store_true',
40
+ help='After updating a branch, delete it if there are no differences between it and its parent branch')
41
+ parser.add_argument('-a', '--all', action='store_true', help='Update all local branches, not just the current one')
42
+ parser.add_argument('-A', '--everything', action='store_true',
43
+ help='Update all local branches, not just the current one and ignore the default ignore list specified in the Git configuration')
44
+ parser.add_argument('-d', '--default', action='store_true', help='Checkout the main or master branch on completion')
45
+ parser.add_argument('-p', '--parent', action='store', help='Specify the parent branch, rather than trying to work it out')
46
+ parser.add_argument('-P', '--all-parents', action='store_true',
47
+ help='Feature branches are not considered as alternative parents unless this option is specified')
48
+ parser.add_argument('-s', '--stop', action='store_true', help='Stop if a rebase problem occurs, instead of skipping the branch')
49
+ parser.add_argument('-i', '--ignore', action='store', default=None,
50
+ help='List of one or more wildcard branch names not to attempt to update (uses update.ignore from the Git configuration if not specified)')
51
+ parser.add_argument('--verbose', action='store_true', help='Enable verbose output')
52
+ parser.add_argument('--dry-run', action='store_true', help='Report what would be done without actually doing it')
53
+
54
+ return parser.parse_args()
55
+
56
+ ################################################################################
57
+
58
+ class UpdateFailure(Exception):
59
+ """Exception raised when a branch fails to update"""
60
+
61
+ def __init__(self, branchname: str) -> None:
62
+ self.branchname = branchname
63
+ super().__init__()
64
+
65
+ ################################################################################
66
+
67
+ def branch_rebase(args, results, branch):
68
+ """Attempt to rebase a branch"""
69
+
70
+ # Either use the specified parent or try to determine the parent branch
71
+
72
+ if args.parent:
73
+ parent = args.parent
74
+ else:
75
+ try:
76
+ git.checkout(branch)
77
+ except git.GitError as exc:
78
+ colour.error(exc.msg)
79
+ sys.exit(1)
80
+
81
+ parents, _ = git.parents()
82
+
83
+ # Ignore feature branches as potential alternative parents unless told otherwise
84
+ # If they are the only possible parent(s) then we still consider them.
85
+
86
+ if not args.all_parents:
87
+ filtered_parents = [p for p in parents if not p.startswith('feature/')]
88
+
89
+ if filtered_parents:
90
+ parents = filtered_parents
91
+
92
+ # Cheat - if we have multiple possible parents and one is 'develop', 'main' or 'master'
93
+ # choose it.
94
+
95
+ if len(parents) > 1:
96
+ if 'master' in parents:
97
+ parent = 'master'
98
+ if 'main' in parents:
99
+ parent = 'main'
100
+ elif 'develop' in parents:
101
+ parent = 'develop'
102
+ else:
103
+ colour.write('[RED:WARNING]: Unable to rebase [BLUE:%s] branch as unable to determine its parent (could be any of %s)'
104
+ % (branch, ', '.join(parents)), indent=4)
105
+ results['failed'].add(branch)
106
+ return
107
+
108
+ elif len(parents) == 1:
109
+ parent = parents[0]
110
+
111
+ else:
112
+ colour.write(f'[RED:WARNING]: Unable to rebase [BLUE:{branch}] branch as unable to determine its parent (no obvious candidates))', indent=4)
113
+ results['failed'].add(branch)
114
+ return
115
+
116
+ if args.dry_run:
117
+ colour.write(f'[BOLD:Checking if] [BLUE:{branch}] [BOLD:needs to be rebased onto] [BLUE:{parent}]', indent=4)
118
+
119
+ else:
120
+ if parent not in results['pulled'] and parent not in results['unchanged']:
121
+ colour.write(f'[BOLD:Updating] [BLUE:{parent}]')
122
+
123
+ if not branch_pull(args, results, parent):
124
+ return
125
+
126
+ if branch not in results['pulled']:
127
+ if git.iscommit(branch, remote_only=True):
128
+ colour.write(f'[BOLD:Updating] [BLUE:{branch}]')
129
+
130
+ branch_pull(args, results, branch)
131
+ else:
132
+ results['no-tracking'].add(branch)
133
+
134
+ if git.rebase_required(branch, parent):
135
+ colour.write(f'Rebasing [BLUE:{branch}] [BOLD:onto] [BLUE:{parent}]', indent=4)
136
+
137
+ git.checkout(branch)
138
+ output, status = git.rebase(parent)
139
+
140
+ if status:
141
+ colour.write(f'[RED:WARNING]: Unable to rebase [BLUE:{branch}] onto [BLUE:{parent}]', indent=4)
142
+
143
+ if args.verbose:
144
+ colour.write(output)
145
+
146
+ results['failed'].add(branch)
147
+
148
+ if args.stop:
149
+ raise UpdateFailure(branch)
150
+
151
+ git.abort_rebase()
152
+ return
153
+
154
+ results['rebased'].add(branch)
155
+ else:
156
+ colour.write(f'[BLUE:{branch}] is already up-to-date on parent branch [BLUE:{parent}]', indent=4)
157
+
158
+ results['unchanged'].add(branch)
159
+
160
+ if args.cleanup:
161
+ if args.dry_run:
162
+ colour.write(f'[GREEN:Dry-run: Checking to see if {branch} and {parent} are the same - deleting {branch} if they are]', indent=4)
163
+
164
+ elif git.diff_status(branch, parent):
165
+ git.checkout(parent)
166
+ git.delete_branch(branch, force=True)
167
+
168
+ results['deleted'].add(branch)
169
+
170
+ colour.write(f'Deleted branch [BLUE:{branch}] as it is not different to its parent branch ([BLUE:{parent}])', indent=4)
171
+
172
+ ################################################################################
173
+
174
+ def branch_pull(args, results, branch, fail=True):
175
+ """Attempt to update a branch, logging any failure except no remote tracking branch
176
+ unless fail is False"""
177
+
178
+ colour.write(f'Pulling updates for the [BLUE:{branch}] branch', indent=4)
179
+
180
+ if not args.dry_run:
181
+ if branch not in results['pulled'] and branch not in results['unchanged']:
182
+ try:
183
+ git.checkout(branch)
184
+ output = git.pull()
185
+
186
+ colour.write(output, indent=4)
187
+ if output[0] == 'Already up-to-date.':
188
+ results['unchanged'].add(branch)
189
+ else:
190
+ results['pulled'].add(branch)
191
+
192
+ except git.GitError as exc:
193
+ if exc.msg.startswith('There is no tracking information for the current branch.'):
194
+ colour.write(f'[RED:WARNING]: There is no tracking information for the [BLUE:{branch}] branch.', indent=4)
195
+ fail = False
196
+
197
+ elif exc.msg.startswith('Your configuration specifies to merge with the ref'):
198
+ colour.write(f'[RED:WARNING]: The upstream branch no longer exists', indent=4)
199
+ fail = False
200
+
201
+ elif 'no such ref was fetched' in exc.msg:
202
+ colour.write(f'[RED:WARNING]: {exc.msg}', indent=4)
203
+
204
+ else:
205
+ colour.write(f'[RED:ERROR]: Unable to merge upstream changes onto [BLUE:{branch}] branch.', indent=4)
206
+
207
+ if git.merging():
208
+ git.abort_merge()
209
+ elif git.rebasing():
210
+ git.abort_rebase()
211
+
212
+ if fail:
213
+ results['failed'].add(branch)
214
+
215
+ return False
216
+
217
+ return True
218
+
219
+ ################################################################################
220
+
221
+ def fixed_branch(branch):
222
+ """Return True if a branch is 'fixed' (master, develop, release, etc.)
223
+ and shouldn't be rebased automatically"""
224
+
225
+ return branch.startswith(('release/', 'hotfix/')) or \
226
+ branch in ('master', 'main', 'develop') or \
227
+ '/PoC-' in branch
228
+
229
+ ################################################################################
230
+
231
+ def report_branches(msg, branches):
232
+ """Report a list of branches with a message"""
233
+
234
+ colour.write(newline=True)
235
+ colour.write(msg)
236
+
237
+ for branch in branches:
238
+ colour.write(f'[BLUE:{branch}]', indent=4)
239
+
240
+ ################################################################################
241
+
242
+ def main():
243
+ """Entry point"""
244
+
245
+ # Handle the command line
246
+
247
+ args = parse_command_line()
248
+
249
+ # Enable logging if requested
250
+
251
+ if args.verbose:
252
+ logging.basicConfig(level=logging.DEBUG)
253
+
254
+ # Check we are in the right place
255
+
256
+ if not git.working_tree():
257
+ colour.error('Not in a git repo')
258
+
259
+ # Set the default ignore list if none specified and if not using the '-A' option
260
+
261
+ if args.ignore is None and not args.everything:
262
+ args.ignore = git.config_get('update', 'ignore')
263
+
264
+ args.ignore = args.ignore.split(',') if args.ignore else []
265
+
266
+ logging.info('Ignore list: %s', ', '.join(args.ignore))
267
+
268
+ # Make sure we've got no locally-modified files
269
+
270
+ status = git.status_info(ignored=True)
271
+
272
+ for entry in status:
273
+ if status[entry][1] == 'M':
274
+ colour.error('You have unstaged changes - cannot update.')
275
+
276
+ # Get the current branch
277
+
278
+ current_branch = git.branch()
279
+
280
+ if not current_branch:
281
+ colour.error('No branch currently checked out - cannot update.')
282
+
283
+ colour.write(f'[BOLD:Current branch:] [BLUE:{current_branch}]')
284
+
285
+ # Switch the current directory in case it vanishes when we switch branches
286
+
287
+ os.chdir(git.working_tree())
288
+
289
+ # Optionally pull or rebase everything - pull things first, then rebase
290
+ # the rest.
291
+
292
+ branches = git.branches() if args.all or args.everything else [current_branch]
293
+
294
+ logging.info('Updating %s' % ', '.join(branches))
295
+
296
+ # Filter out branches that the user wants to ignore
297
+
298
+ if args.ignore:
299
+ for ignore in args.ignore:
300
+ for name in branches[:]:
301
+ if fnmatch.fnmatch(name, ignore) and name in branches:
302
+ branches.remove(name)
303
+
304
+ if not branches:
305
+ colour.error('No matching branches to update')
306
+
307
+ # List of stuff that's been done, to report in the summary
308
+
309
+ results = {'deleted': set(), 'pulled': set(), 'failed': set(), 'rebased': set(), 'unchanged': set(), 'no-tracking': set() }
310
+
311
+ to_rebase = set()
312
+
313
+ try:
314
+ for branch in branches:
315
+ if fixed_branch(branch):
316
+ branch_pull(args, results, branch)
317
+ else:
318
+ to_rebase.add(branch)
319
+
320
+ for branch in to_rebase:
321
+ branch_rebase(args, results, branch)
322
+
323
+ # Return to the original branch if it still exists or the master
324
+
325
+ all_branches = git.branches()
326
+
327
+ return_branch = current_branch if current_branch in all_branches \
328
+ else 'develop' if 'develop' in all_branches \
329
+ else 'main' if 'main' in all_branches \
330
+ else 'master' if 'master' in all_branches else None
331
+
332
+ if return_branch:
333
+ colour.write('')
334
+ colour.write(f'[BOLD]Checking out the [BLUE:{return_branch}] [BOLD:branch]')
335
+
336
+ if not args.dry_run:
337
+ git.checkout(return_branch)
338
+
339
+ except UpdateFailure as exc:
340
+ update_failed = exc.branchname
341
+
342
+ else:
343
+ update_failed = None
344
+
345
+ for entry in ('rebased', 'unchanged', 'pulled', 'failed', 'no-tracking'):
346
+ results[entry] -= results['deleted']
347
+
348
+ if results['rebased']:
349
+ report_branches('[BOLD:The following branches have been rebased:]', results['rebased'])
350
+
351
+ if results['unchanged']:
352
+ report_branches('[BOLD:The following branches were already up-to-date:]', results['unchanged'])
353
+
354
+ if results['pulled']:
355
+ report_branches('[BOLD:The following branches have been updated:]', results['pulled'])
356
+
357
+ if results['deleted']:
358
+ report_branches('[BOLD:The following branches have been deleted:]', results['deleted'])
359
+
360
+ if results['failed']:
361
+ report_branches('[RED:WARNING:] [BOLD:The following branches failed to update:]', results['failed'])
362
+
363
+ if results['no-tracking']:
364
+ report_branches('[YELLOW:NOTE:] [BOLD:The following branches have been rebased, but no upstream branch exists]', results['no-tracking'])
365
+
366
+ if update_failed:
367
+ colour.write('')
368
+ colour.write(f'Halted during failed rebase of branch [BLUE:{update_failed}]')
369
+
370
+ ################################################################################
371
+
372
+ def git_update():
373
+ """Entry point"""
374
+
375
+ try:
376
+ main()
377
+ except KeyboardInterrupt:
378
+ sys.exit(1)
379
+ except BrokenPipeError:
380
+ sys.exit(2)
381
+
382
+ ################################################################################
383
+
384
+ if __name__ == '__main__':
385
+ git_update()
@@ -0,0 +1,96 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """ Output the top level directory of the git working tree or return
5
+ an error if we are not in a git working tree.
6
+
7
+ Copyright (C) 2017, 2018 John Skilleter
8
+
9
+ Licence: GPL v3 or later
10
+ """
11
+ ################################################################################
12
+
13
+ import sys
14
+ import argparse
15
+ import os
16
+
17
+ import thingy.git2 as git
18
+
19
+ ################################################################################
20
+
21
+ def main():
22
+ """ Main function """
23
+
24
+ # Command line parameters
25
+
26
+ parser = argparse.ArgumentParser(description='Report top-level directory of the current git working tree.')
27
+ parser.add_argument('-p', '--parent', action='store_true',
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('-r', '--repo', action='store_true',
30
+ help='If we are already at the top of the working tree, look for a parent directory with a repo control file (.repo directory or .mrconfig file) and output that directory.')
31
+ parser.add_argument('level', nargs='?', type=int, default=0, help='Number of levels below the top-level directory to report')
32
+ args = parser.parse_args()
33
+ try:
34
+ start_dir = os.getcwd()
35
+ except FileNotFoundError:
36
+ print('Unable to locate current directory')
37
+ sys.exit(1)
38
+
39
+ # Try to get the current working tree
40
+
41
+ working_tree = git.working_tree(start_dir)
42
+
43
+ # If we are in a working tree and also looking for the parent working
44
+ # tree, check if we are at the top of the current tree, and, if so,
45
+ # hop up a level and try again.
46
+
47
+ if args.parent and working_tree:
48
+ current_directory = os.getcwd()
49
+
50
+ if os.path.samefile(working_tree, current_directory):
51
+ os.chdir('..')
52
+
53
+ working_tree = git.working_tree()
54
+
55
+ # If we are also looking for a multi-repo control file or directory, and haven't
56
+ # found the git working tree root scan up the tree until we find one.
57
+
58
+ if args.repo and not working_tree:
59
+ while True:
60
+ working_tree = os.getcwd()
61
+
62
+ if os.path.isdir('.repo') or os.path.isfile('../.mrconfig'):
63
+ break
64
+
65
+ if working_tree == '/':
66
+ sys.exit(2)
67
+
68
+ os.chdir('..')
69
+
70
+ # Output the result, if we have one
71
+
72
+ if args.level:
73
+ start = start_dir.split('/')
74
+ working = working_tree.split('/')
75
+
76
+ working_tree = os.path.join(working_tree, '/'.join(start[len(working):len(working) + int(args.level)]))
77
+
78
+ if working_tree:
79
+ print(working_tree)
80
+
81
+ ################################################################################
82
+
83
+ def git_wt():
84
+ """Entry point"""
85
+
86
+ try:
87
+ main()
88
+ except KeyboardInterrupt:
89
+ sys.exit(1)
90
+ except BrokenPipeError:
91
+ sys.exit(2)
92
+
93
+ ################################################################################
94
+
95
+ if __name__ == '__main__':
96
+ git_wt()