skilleter-thingy 0.2.8__py3-none-any.whl → 0.2.9__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/multigit.py +73 -65
- {skilleter_thingy-0.2.8.dist-info → skilleter_thingy-0.2.9.dist-info}/METADATA +1 -1
- {skilleter_thingy-0.2.8.dist-info → skilleter_thingy-0.2.9.dist-info}/RECORD +7 -7
- {skilleter_thingy-0.2.8.dist-info → skilleter_thingy-0.2.9.dist-info}/WHEEL +0 -0
- {skilleter_thingy-0.2.8.dist-info → skilleter_thingy-0.2.9.dist-info}/entry_points.txt +0 -0
- {skilleter_thingy-0.2.8.dist-info → skilleter_thingy-0.2.9.dist-info}/licenses/LICENSE +0 -0
- {skilleter_thingy-0.2.8.dist-info → skilleter_thingy-0.2.9.dist-info}/top_level.txt +0 -0
skilleter_thingy/multigit.py
CHANGED
|
@@ -14,34 +14,6 @@ import thingy.git as git
|
|
|
14
14
|
import thingy.colour as colour
|
|
15
15
|
|
|
16
16
|
################################################################################
|
|
17
|
-
# TODO List:
|
|
18
|
-
#
|
|
19
|
-
# DONE: ***MUST use relative paths in config file, or we can't store in git and clone and use somewhere else!***
|
|
20
|
-
# DONE: / Output name of each working tree as it is processed as command sits there seeming to do nothing otherwise.
|
|
21
|
-
# DONE: Better error-handling - e.g. continue/abort option after failure in one working tree
|
|
22
|
-
# DONE: Currently doesn't handle single letter options in concatenated form - e.g. -dv
|
|
23
|
-
# DONE: Don't save the configuration on exit if it hasn't changed
|
|
24
|
-
# DONE: Don't use a fixed list of default branch names
|
|
25
|
-
# DONE: If the config file isn't in the current directory then search up the directory tree for it but run in the current directory
|
|
26
|
-
# DONE: Use the configuration file
|
|
27
|
-
# DONE: When specifying list of working trees, if name doesn't contain '/' prefix it with '*'?
|
|
28
|
-
# DONE: command to categorise working trees then command line filter to only act on working trees in category (in addition to other filtering options) - +tag <TAG> command tags all selected working trees and updates the configuration, +untag <TAG> command to remove tags in the same way
|
|
29
|
-
# DONE: init function
|
|
30
|
-
# DONE Consistent colours in output
|
|
31
|
-
# DONE:+run command to do things other than git commands
|
|
32
|
-
# DONE: Command that takes partial working tree name and either returns full path or pops up window to autocomplete until single match found - returns list of paths you can pipe into fzf
|
|
33
|
-
#
|
|
34
|
-
# NOPE: Clone to have option to update - as discussed, should be part of init
|
|
35
|
-
# NOPE: Dry-run option - just pass the option to the Git command
|
|
36
|
-
# NOPE: Is it going to be a problem if the same repo is checked out twice or more in the same workspace - user problem
|
|
37
|
-
# NOPE: Pull/fetch - only output after running command and only if something updated - nope, git commands should run as-is without interference
|
|
38
|
-
# NOPE: Switch to tomlkit
|
|
39
|
-
# NOPE: Use PathLib - Don't really see the need
|
|
40
|
-
# NOPE: When we have something with multiple matches display a menu for user to select the one that they one - make it a library routine so can be used, for instance, for branch selection - Use fzf
|
|
41
|
-
# NOPE: Use pygit2 directly - don't see the need
|
|
42
|
-
# NOPE: .init option '--update' to update the configuration file with new working trees and remove ones that are no longer there - init does the removal, and added the '+add' command instead
|
|
43
|
-
# NOPE: Ability to read default configuration options from ~/.config/thingy/multigit.rc - these are insert before argv[1] before argparse called - limited use for this
|
|
44
|
-
#
|
|
45
17
|
# TODO: 2. If run in a subdirectory, only process working trees in that tree (or have an option to do so, or an option _not_ to do so; --all)
|
|
46
18
|
# TODO: 2. select_git_repos() and +dir should use consist way of selecting repos if possible
|
|
47
19
|
# TODO: 3 .init option '--set-default' to update the default branch to the current one for specified working trees
|
|
@@ -87,16 +59,17 @@ options:
|
|
|
87
59
|
--path, -C PATH Run as if the command was started in PATH instead of the current working directory
|
|
88
60
|
|
|
89
61
|
Sub-commands:
|
|
90
|
-
{+clone, +init, +dir, +config, +list, +run, +add, GIT_COMMAND}
|
|
62
|
+
{+clone, +init, +dir, +config, +list, +run, +add, +update, GIT_COMMAND}
|
|
91
63
|
+clone REPO {BRANCH} Clone a repo containing a multigit configuration file, then clone all the child repos and check out the default branch in each
|
|
92
64
|
+init Build or update the configuration file using the current branch in each repo as the default branch
|
|
93
|
-
+config
|
|
94
|
-
+dir
|
|
95
|
-
+list
|
|
65
|
+
+config Output the name and location of the configuration file
|
|
66
|
+
+dir Output the location of a working tree, given the repo name, or if no parameter specified, the root directory of the multigit tree
|
|
67
|
+
+list Output a list of the top level directories of each of the Git repos
|
|
96
68
|
+tag TAG Apply a configuration tag to repos filtered by the command line options (list configuration tags if no parameter specified)
|
|
97
69
|
+untag TAG Remove a configuration tag to repos filtered by the command line options
|
|
98
70
|
+run COMMAND Run the specified command in repos filtered by the command line options
|
|
99
71
|
+add REPO DIR Clone REPO into the DIR directory and add it to the multigit configuration
|
|
72
|
+
+update Clone any repos in the current configuration that do not have working trees
|
|
100
73
|
GIT_COMMAND Any git command, including options and parameters - this is then run in all specified working trees
|
|
101
74
|
|
|
102
75
|
"""
|
|
@@ -168,6 +141,32 @@ def relative_repo_path(args, relative_path=''):
|
|
|
168
141
|
|
|
169
142
|
################################################################################
|
|
170
143
|
|
|
144
|
+
def safe_clone(args, location, origin):
|
|
145
|
+
"""If location exists then fail with an error if it isn't a directory, or
|
|
146
|
+
or is a directory, but isn't a working tree, or is a working tree for a
|
|
147
|
+
different Git repo than remote otherwise do nothing.
|
|
148
|
+
If it doesn't exist, then clone the specified remote there."""
|
|
149
|
+
|
|
150
|
+
if os.path.exists(location):
|
|
151
|
+
if not os.path.isdir(os.path.join(location, '.git')):
|
|
152
|
+
colour.error(f'"[BLUE:{location}]" already exists and is not a Git working tree', prefix=True)
|
|
153
|
+
|
|
154
|
+
remotes = git.remotes(path=location)
|
|
155
|
+
|
|
156
|
+
for remote in remotes:
|
|
157
|
+
if origin == remotes[remote]:
|
|
158
|
+
break
|
|
159
|
+
else:
|
|
160
|
+
colour.error(f'"[BLUE:{location}]" already exists and was not cloned from [BLUE:{origin}]', prefix=True)
|
|
161
|
+
|
|
162
|
+
else:
|
|
163
|
+
if not args.quiet:
|
|
164
|
+
colour.write(f'Cloning [BOLD:{origin}] into [BLUE:{location}]')
|
|
165
|
+
|
|
166
|
+
git.clone(origin, working_tree=location)
|
|
167
|
+
|
|
168
|
+
################################################################################
|
|
169
|
+
|
|
171
170
|
def find_configuration(default_config_file):
|
|
172
171
|
"""If the configuration file name has path elements, try and read it, otherwise
|
|
173
172
|
search up the directory tree looking for the configuration file.
|
|
@@ -311,36 +310,37 @@ def branch_name(name, default_branch):
|
|
|
311
310
|
|
|
312
311
|
################################################################################
|
|
313
312
|
|
|
314
|
-
def add_new_repo(args, config,
|
|
313
|
+
def add_new_repo(args, config, repo_path, default_branch=None):
|
|
315
314
|
"""Add a new configuration entry containing the default branch, remote origin
|
|
316
|
-
(if there is one) and
|
|
315
|
+
(if there is one), name and default branch"""
|
|
317
316
|
|
|
318
|
-
abs_repo_path = absolute_repo_path(args,
|
|
317
|
+
abs_repo_path = absolute_repo_path(args, repo_path)
|
|
319
318
|
|
|
320
|
-
added =
|
|
319
|
+
added = repo_path not in config
|
|
321
320
|
|
|
322
|
-
config[
|
|
321
|
+
config[repo_path] = {}
|
|
323
322
|
|
|
324
|
-
|
|
323
|
+
if not default_branch:
|
|
324
|
+
default_branch = git.branch(path=abs_repo_path)
|
|
325
325
|
|
|
326
326
|
if not default_branch:
|
|
327
|
-
colour.error(f'Unable to determine default branch in [BLUE:{
|
|
327
|
+
colour.error(f'Unable to determine default branch in [BLUE:{abs_repo_path}]', prefix=True)
|
|
328
328
|
|
|
329
|
-
config[
|
|
329
|
+
config[repo_path]['default branch'] = default_branch
|
|
330
330
|
|
|
331
331
|
remote = git.remotes(path=abs_repo_path)
|
|
332
332
|
|
|
333
333
|
if 'origin' in remote:
|
|
334
|
-
config[
|
|
335
|
-
config[
|
|
334
|
+
config[repo_path]['origin'] = remote['origin']
|
|
335
|
+
config[repo_path]['repo name'] = os.path.basename(remote['origin']).removesuffix('.git')
|
|
336
336
|
else:
|
|
337
|
-
config[
|
|
337
|
+
config[repo_path]['repo name'] = os.path.basename(repo_path)
|
|
338
338
|
|
|
339
339
|
if not args.quiet:
|
|
340
340
|
if added:
|
|
341
|
-
colour.write(f'Added [BLUE:{
|
|
341
|
+
colour.write(f'Added [BLUE:{repo_path}] with default branch [BLUE:{default_branch}]')
|
|
342
342
|
else:
|
|
343
|
-
colour.write(f'Reset [BLUE:{
|
|
343
|
+
colour.write(f'Reset [BLUE:{repo_path}] with default branch [BLUE:{default_branch}]')
|
|
344
344
|
|
|
345
345
|
################################################################################
|
|
346
346
|
|
|
@@ -428,14 +428,14 @@ def mg_init(args, config, console):
|
|
|
428
428
|
# Search for .git directories and add any that aren't already in the configuration
|
|
429
429
|
|
|
430
430
|
repo_list = []
|
|
431
|
-
for
|
|
431
|
+
for repo_dir in find_working_trees(args):
|
|
432
432
|
if not args.quiet:
|
|
433
|
-
show_progress(console.columns,
|
|
433
|
+
show_progress(console.columns, repo_dir)
|
|
434
434
|
|
|
435
|
-
repo_list.append(
|
|
435
|
+
repo_list.append(repo_dir)
|
|
436
436
|
|
|
437
|
-
if
|
|
438
|
-
add_new_repo(args, config,
|
|
437
|
+
if repo_dir not in config:
|
|
438
|
+
add_new_repo(args, config, repo_dir)
|
|
439
439
|
|
|
440
440
|
if not args.quiet:
|
|
441
441
|
colour.write(cleareol=True)
|
|
@@ -476,22 +476,9 @@ def mg_add(args, config, console):
|
|
|
476
476
|
repo = args.parameters[0]
|
|
477
477
|
location = args.parameters[1]
|
|
478
478
|
|
|
479
|
-
|
|
480
|
-
if os.path.isdir(os.path.join(location, '.git')):
|
|
481
|
-
remotes = git.remotes(path=location)
|
|
479
|
+
# Attempt to clone it
|
|
482
480
|
|
|
483
|
-
|
|
484
|
-
if repo.endswith('.git') and repo == remotes[remote]:
|
|
485
|
-
break
|
|
486
|
-
elif f'{repo}.git' == remotes[remote]:
|
|
487
|
-
break
|
|
488
|
-
else:
|
|
489
|
-
colour.error(f'"[BLUE:{location}]" already exists and was not cloned from [BLUE:{repo}]', prefix=True)
|
|
490
|
-
else:
|
|
491
|
-
colour.error(f'"[BLUE:{location}]" already exists and is not a Git working tree', prefix=True)
|
|
492
|
-
|
|
493
|
-
else:
|
|
494
|
-
git.clone(repo, working_tree=location)
|
|
481
|
+
safe_clone(args, repo, location)
|
|
495
482
|
|
|
496
483
|
# Add to the configuration
|
|
497
484
|
|
|
@@ -503,6 +490,26 @@ def mg_add(args, config, console):
|
|
|
503
490
|
|
|
504
491
|
################################################################################
|
|
505
492
|
|
|
493
|
+
def mg_update(args, config, console):
|
|
494
|
+
"""Clone any repos in the current configuration that do not have working trees
|
|
495
|
+
Similar to the '+init' command except that it updates an existing multigit
|
|
496
|
+
tree rather than creating one from scratch."""
|
|
497
|
+
|
|
498
|
+
_ = console
|
|
499
|
+
|
|
500
|
+
# Don't allow pointless options
|
|
501
|
+
|
|
502
|
+
if args.modified or args.branched or args.tag:
|
|
503
|
+
colour.error('The "[BOLD:--tag]", "[BOLD:--modified]" and "[BOLD:--branched]" options cannot be used with the "[BOLD:+update]" subcommand', prefix=True)
|
|
504
|
+
|
|
505
|
+
# Now iterate through the repos, cloning any that don't already have working trees
|
|
506
|
+
|
|
507
|
+
for repo in select_git_repos(args, config):
|
|
508
|
+
if repo.name != '.':
|
|
509
|
+
safe_clone(args, repo.name, repo['origin'])
|
|
510
|
+
|
|
511
|
+
################################################################################
|
|
512
|
+
|
|
506
513
|
def mg_dir(args, config, console):
|
|
507
514
|
"""Return the location of a working tree, given the name, or the root directory
|
|
508
515
|
of the tree if not
|
|
@@ -813,6 +820,7 @@ COMMANDS = {
|
|
|
813
820
|
'list': mg_list,
|
|
814
821
|
'run': mg_run,
|
|
815
822
|
'add': mg_add,
|
|
823
|
+
'update': mg_update,
|
|
816
824
|
}
|
|
817
825
|
|
|
818
826
|
def main():
|
|
@@ -21,7 +21,7 @@ skilleter_thingy/gitcmp_helper.py,sha256=MlRimPj-S-ov44ETUvocRvfygs3cArP6WdSJCIJ
|
|
|
21
21
|
skilleter_thingy/gitprompt.py,sha256=SzSMd0EGI7ftPko80Q2PipwbVA-qjU1jsmdpmTCM5GI,8912
|
|
22
22
|
skilleter_thingy/gl.py,sha256=9zbGpKxw6lX9RghLkdy-Q5sZlqtbB3uGFO04qTu1dH8,5954
|
|
23
23
|
skilleter_thingy/linecount.py,sha256=ehTN6VD76i4U5k6dXuYoiqSRHI67_BP-bziklNAJSKY,4309
|
|
24
|
-
skilleter_thingy/multigit.py,sha256=
|
|
24
|
+
skilleter_thingy/multigit.py,sha256=kTxEYinHCH1CQE7hzJF58XH1m-n0NZz5IGOtIJ3nL4Q,33979
|
|
25
25
|
skilleter_thingy/py_audit.py,sha256=4CAdqBAIIVcpTCn_7dGm56bdfGpUtUJofqTGZomClkY,4417
|
|
26
26
|
skilleter_thingy/readable.py,sha256=LcMMOiuzf9j5TsxcMbO0sbj6m1QCuABl91Hrv-YyIww,15422
|
|
27
27
|
skilleter_thingy/remdir.py,sha256=Ueg3a6_m7y50zWykhKk6pcuz4FKPjoLJVPo9gh_dsic,4653
|
|
@@ -51,9 +51,9 @@ skilleter_thingy/thingy/run.py,sha256=mqafCzW9op_xKCt8OY3jJ6YltmoOJGh44vzl667mww
|
|
|
51
51
|
skilleter_thingy/thingy/tfm_pane.py,sha256=XTTpSm71CyQyGmlVLuCthioOwech0jhUiFUXb-chS_Q,19792
|
|
52
52
|
skilleter_thingy/thingy/tidy.py,sha256=AQ2RawsZJg6WHrgayi_ZptFL9occ7suSdCHbU3P-cys,5971
|
|
53
53
|
skilleter_thingy/thingy/venv_template.py,sha256=ZfUvi8qFNGrk7J030Zy57xjwMtfIArJyqa-MqafyjVk,1016
|
|
54
|
-
skilleter_thingy-0.2.
|
|
55
|
-
skilleter_thingy-0.2.
|
|
56
|
-
skilleter_thingy-0.2.
|
|
57
|
-
skilleter_thingy-0.2.
|
|
58
|
-
skilleter_thingy-0.2.
|
|
59
|
-
skilleter_thingy-0.2.
|
|
54
|
+
skilleter_thingy-0.2.9.dist-info/licenses/LICENSE,sha256=ljOS4DjXvqEo5VzGfdaRwgRZPbNScGBmfwyC8PChvmQ,32422
|
|
55
|
+
skilleter_thingy-0.2.9.dist-info/METADATA,sha256=Z3BmA1l9uxKUzFeEnrUWLi1QN5vL5Vqd8BUee6ln6Gg,28913
|
|
56
|
+
skilleter_thingy-0.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
57
|
+
skilleter_thingy-0.2.9.dist-info/entry_points.txt,sha256=MTNWf8jOx8Fy3tSwVLCZPlEyzlDF36odw-IN-cSefP8,1784
|
|
58
|
+
skilleter_thingy-0.2.9.dist-info/top_level.txt,sha256=8-JhgToBBiWURunmvfpSxEvNkDHQQ7r25-aBXtZv61g,17
|
|
59
|
+
skilleter_thingy-0.2.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|