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.

@@ -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 Return the name and location of the configuration file
94
- +dir Return the location of a working tree, given the repo name, or if no parameter specified, the root directory of the multigit tree
95
- +list Return a list of the top level directories of each of the Git repos
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, repo):
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 name"""
315
+ (if there is one), name and default branch"""
317
316
 
318
- abs_repo_path = absolute_repo_path(args, repo)
317
+ abs_repo_path = absolute_repo_path(args, repo_path)
319
318
 
320
- added = repo not in config
319
+ added = repo_path not in config
321
320
 
322
- config[repo] = {}
321
+ config[repo_path] = {}
323
322
 
324
- default_branch = git.branch(path=abs_repo_path)
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:{repo}]', prefix=True)
327
+ colour.error(f'Unable to determine default branch in [BLUE:{abs_repo_path}]', prefix=True)
328
328
 
329
- config[repo]['default branch'] = default_branch
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[repo]['origin'] = remote['origin']
335
- config[repo]['repo name'] = os.path.basename(remote['origin']).removesuffix('.git')
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[repo]['repo name'] = os.path.basename(repo)
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:{repo}] with default branch [BLUE:{default_branch}]')
341
+ colour.write(f'Added [BLUE:{repo_path}] with default branch [BLUE:{default_branch}]')
342
342
  else:
343
- colour.write(f'Reset [BLUE:{repo}] with default branch [BLUE:{default_branch}]')
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 repo in find_working_trees(args):
431
+ for repo_dir in find_working_trees(args):
432
432
  if not args.quiet:
433
- show_progress(console.columns, repo)
433
+ show_progress(console.columns, repo_dir)
434
434
 
435
- repo_list.append(repo)
435
+ repo_list.append(repo_dir)
436
436
 
437
- if repo not in config:
438
- add_new_repo(args, config, repo)
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
- if os.path.exists(location):
480
- if os.path.isdir(os.path.join(location, '.git')):
481
- remotes = git.remotes(path=location)
479
+ # Attempt to clone it
482
480
 
483
- for remote in remotes:
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():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skilleter_thingy
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: A collection of useful utilities, mainly aimed at making Git more friendly
5
5
  Author-email: John Skilleter <john@skilleter.org.uk>
6
6
  Project-URL: Home, https://skilleter.org.uk
@@ -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=FDB4MwsCE0DNbRsUXXxVIsFkVrRtFRlljwyy0Q72kYs,34914
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.8.dist-info/licenses/LICENSE,sha256=ljOS4DjXvqEo5VzGfdaRwgRZPbNScGBmfwyC8PChvmQ,32422
55
- skilleter_thingy-0.2.8.dist-info/METADATA,sha256=KO5mUx8lFuo9HCvU6_7mh8ojr7FRv8mZOjwht8RU1FQ,28913
56
- skilleter_thingy-0.2.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- skilleter_thingy-0.2.8.dist-info/entry_points.txt,sha256=MTNWf8jOx8Fy3tSwVLCZPlEyzlDF36odw-IN-cSefP8,1784
58
- skilleter_thingy-0.2.8.dist-info/top_level.txt,sha256=8-JhgToBBiWURunmvfpSxEvNkDHQQ7r25-aBXtZv61g,17
59
- skilleter_thingy-0.2.8.dist-info/RECORD,,
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,,