skilleter-thingy 0.1.2__tar.gz → 0.1.3__tar.gz

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 (73) hide show
  1. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/PKG-INFO +5 -1
  2. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/README.md +4 -0
  3. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/pyproject.toml +1 -1
  4. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/multigit.py +155 -47
  5. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/git2.py +3 -3
  6. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy.egg-info/PKG-INFO +5 -1
  7. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/LICENSE +0 -0
  8. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/setup.cfg +0 -0
  9. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/__init__.py +0 -0
  10. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/addpath.py +0 -0
  11. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/borger.py +0 -0
  12. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/console_colours.py +0 -0
  13. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/diskspacecheck.py +0 -0
  14. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/docker_purge.py +0 -0
  15. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/ffind.py +0 -0
  16. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/ggit.py +0 -0
  17. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/ggrep.py +0 -0
  18. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_br.py +0 -0
  19. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_ca.py +0 -0
  20. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_cleanup.py +0 -0
  21. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_co.py +0 -0
  22. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_common.py +0 -0
  23. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_hold.py +0 -0
  24. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_mr.py +0 -0
  25. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_parent.py +0 -0
  26. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_review.py +0 -0
  27. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_update.py +0 -0
  28. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/git_wt.py +0 -0
  29. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/gitcmp_helper.py +0 -0
  30. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/gitprompt.py +0 -0
  31. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/gl.py +0 -0
  32. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/gphotosync.py +0 -0
  33. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/linecount.py +0 -0
  34. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/moviemover.py +0 -0
  35. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/photodupe.py +0 -0
  36. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/phototidier.py +0 -0
  37. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/py_audit.py +0 -0
  38. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/readable.py +0 -0
  39. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/remdir.py +0 -0
  40. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/rmdupe.py +0 -0
  41. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/rpylint.py +0 -0
  42. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/splitpics.py +0 -0
  43. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/strreplace.py +0 -0
  44. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/sysmon.py +0 -0
  45. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/tfm.py +0 -0
  46. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/tfparse.py +0 -0
  47. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/__init__.py +0 -0
  48. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/colour.py +0 -0
  49. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/dc_curses.py +0 -0
  50. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/dc_defaults.py +0 -0
  51. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/dc_util.py +0 -0
  52. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/dircolors.py +0 -0
  53. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/docker.py +0 -0
  54. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/files.py +0 -0
  55. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/git.py +0 -0
  56. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/gitlab.py +0 -0
  57. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/path.py +0 -0
  58. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/popup.py +0 -0
  59. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/process.py +0 -0
  60. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/run.py +0 -0
  61. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/tfm_pane.py +0 -0
  62. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/tidy.py +0 -0
  63. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/thingy/venv_template.py +0 -0
  64. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/trimpath.py +0 -0
  65. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/venv_create.py +0 -0
  66. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/window_rename.py +0 -0
  67. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/xchmod.py +0 -0
  68. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy/yamlcheck.py +0 -0
  69. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy.egg-info/SOURCES.txt +0 -0
  70. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy.egg-info/dependency_links.txt +0 -0
  71. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy.egg-info/entry_points.txt +0 -0
  72. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy.egg-info/requires.txt +0 -0
  73. {skilleter_thingy-0.1.2 → skilleter_thingy-0.1.3}/skilleter_thingy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: skilleter_thingy
3
- Version: 0.1.2
3
+ Version: 0.1.3
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
@@ -89,6 +89,8 @@ The command takes a number of options that can be used to select the list of wor
89
89
 
90
90
  `--tag TAG` / `-t TAG` Run only in working trees that are tagged with the specified tag
91
91
 
92
+ `--subdir` / `-s` Run only in working trees that are in the current directory tree.
93
+
92
94
  These options are AND-ed together, so specifying `--modified --branched --tag WOMBAT` will select only working trees that are modified AND branched AND tagged with `WOMBAT`, but the parameters to the `--repos` and `--tag` options are OR-ed together, so specifying `--tag WOMBAT --tag EMU` will select repos that are tagged as `WOMBAT` *OR* `EMU`.
93
95
 
94
96
  Multigit tags are stored in the configuration file, not within the working tree and each working tree can have multiple tags.
@@ -97,6 +99,8 @@ Multigit tags are stored in the configuration file, not within the working tree
97
99
 
98
100
  Multigit supports a small list of subcommands, each of which are prefixed with a `+` to distinguish them from Git commands:
99
101
 
102
+ `+clone` - Clone a repo containing a multigit configuration file then clone all the repos specified in the configuration, checking out the default branch in each one.
103
+
100
104
  `+init` - Create or update the configuration file
101
105
 
102
106
  `+dir` - Given the name of a working tree, output the location within the multigit tree of that working tree if the name matches uniquely, or the name of the directory where the multigit configuration file resides if no parameter is specified.
@@ -67,6 +67,8 @@ The command takes a number of options that can be used to select the list of wor
67
67
 
68
68
  `--tag TAG` / `-t TAG` Run only in working trees that are tagged with the specified tag
69
69
 
70
+ `--subdir` / `-s` Run only in working trees that are in the current directory tree.
71
+
70
72
  These options are AND-ed together, so specifying `--modified --branched --tag WOMBAT` will select only working trees that are modified AND branched AND tagged with `WOMBAT`, but the parameters to the `--repos` and `--tag` options are OR-ed together, so specifying `--tag WOMBAT --tag EMU` will select repos that are tagged as `WOMBAT` *OR* `EMU`.
71
73
 
72
74
  Multigit tags are stored in the configuration file, not within the working tree and each working tree can have multiple tags.
@@ -75,6 +77,8 @@ Multigit tags are stored in the configuration file, not within the working tree
75
77
 
76
78
  Multigit supports a small list of subcommands, each of which are prefixed with a `+` to distinguish them from Git commands:
77
79
 
80
+ `+clone` - Clone a repo containing a multigit configuration file then clone all the repos specified in the configuration, checking out the default branch in each one.
81
+
78
82
  `+init` - Create or update the configuration file
79
83
 
80
84
  `+dir` - Given the name of a working tree, output the location within the multigit tree of that working tree if the name matches uniquely, or the name of the directory where the multigit configuration file resides if no parameter is specified.
@@ -7,7 +7,7 @@ name = "skilleter_thingy"
7
7
 
8
8
  # Version must be incremented to install updated Thingy
9
9
 
10
- version = "0.1.2"
10
+ version = "0.1.3"
11
11
 
12
12
  authors = [
13
13
  {name="John Skilleter", email="john@skilleter.org.uk"},
@@ -14,6 +14,7 @@ import thingy.colour as colour
14
14
 
15
15
  ################################################################################
16
16
 
17
+ # DONE: ***MUST use relative paths in config file, or we can't store in git and clone and use somewhere else!***
17
18
  # DONE: / Output name of each working tree as it is processed as command sits there seeming to do nothing otherwise.
18
19
  # DONE: Better error-handling - e.g. continue/abort option after failure in one working tree
19
20
  # DONE: Currently doesn't handle single letter options in concatenated form - e.g. -dv
@@ -21,29 +22,30 @@ import thingy.colour as colour
21
22
  # DONE: Don't use a fixed list of default branch names
22
23
  # 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
23
24
  # DONE: Use the configuration file
25
+ # DONE: When specifying list of working trees, if name doesn't contain '/' prefix it with '*'?
24
26
  # 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
25
27
  # DONE: init function
28
+ # NOPE: Clone to have option to update - as discussed, should be part of init
26
29
  # NOPE: Dry-run option - just pass the option to the Git command
27
30
  # NOPE: Is it going to be a problem if the same repo is checked out twice or more in the same workspace - user problem
28
31
  # NOPE: Pull/fetch - only output after running command and only if something updated
29
32
  # NOPE: Switch to tomlkit
30
- # TODO: -j option to run in parallel - yes, but it will only work with non-interactive Git commands
31
- # TODO: Command that takes partial working tree name and either returns full path or pops up window to autocomplete until single match found
32
- # TODO: Consistent colours in output
33
- # TODO: Option to +dir to return all matches so that caller can select one they want
34
- # TODO: Verbose option
35
- # TODO: When filtering by tag, if tag starts with '!' only match if tag isn't present (and don't allow '!' at start of tag otherwise)
36
- # DONE: When specifying list of working trees, if name doesn't contain '/' prefix it with '*'?
37
- # TODO: select_git_repos() and +dir should use consist way of selecting repos if possible
38
- # TODO: +run command to do things other than git commands
39
- # TODO: init option '--update' to update the configuration file with new working trees and remove ones that are no longer there
40
- # TODO: init option '--set-default' to update the default branch to the current one for specified working trees
41
- # TODO: 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)
42
- # TODO: Ability to read default configuration options from ~/.config/thingy/multigit.rc - these are insert before argv[1] before argparse called
43
- # TODO: 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
44
- # TODO: Use pygit2
45
- # TODO: Use PathLib
46
- # TODO: Shell autocomplete for +dir
33
+ # TODO: 2 .init option '--update' to update the configuration file with new working trees and remove ones that are no longer there
34
+ # TODO: 2. +run command to do things other than git commands
35
+ # 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)
36
+ # TODO: 2. select_git_repos() and +dir should use consist way of selecting repos if possible
37
+ # TODO: 3 .init option '--set-default' to update the default branch to the current one for specified working trees
38
+ # TODO: 3. Ability to read default configuration options from ~/.config/thingy/multigit.rc - these are insert before argv[1] before argparse called
39
+ # TODO: 3. Command that takes partial working tree name and either returns full path or pops up window to autocomplete until single match found
40
+ # TODO: 3. Verbose option
41
+ # TODO: 3. When filtering by tag or by repo name, if name starts with '!' only match if tag isn't present or repo name doesn't match (and don't allow '!' at start of tag otherwise)
42
+ # TODO: 4. Option to +dir to return all matches so that caller can select one they want
43
+ # TODO: 4. Shell autocomplete for +dir
44
+ # TODO: 5. -j option to run in parallel - yes, but it will only work with non-interactive Git commands
45
+ # TODO: 5. Consistent colours in output
46
+ # TODO: 6. Use PathLib
47
+ # TODO: 6. 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
48
+ # TODO: 7. Use pygit2 directly
47
49
  ################################################################################
48
50
 
49
51
  DEFAULT_CONFIG_FILE = 'multigit.toml'
@@ -54,9 +56,11 @@ DEFAULT_CONFIG_FILE = 'multigit.toml'
54
56
  DEFAULT_BRANCH = 'DEFAULT'
55
57
 
56
58
  ################################################################################
59
+ # Command line help - we aren't using argparse since it isn't flexible enough to handle arbtirary git
60
+ # commands are parameters so we have to manually create the help and parse the command line
57
61
 
58
62
  HELP_INFO = """usage: multigit [-h] [--verbose] [--quiet] [--config CONFIG] [--repos REPOS] [--modified] [--branched] [--sub] [--tag TAGS]
59
- {+init, +config, +dir, GIT_COMMAND} ...
63
+ {+clone, +init, +config, +dir, GIT_COMMAND} ...
60
64
 
61
65
  Run git commands in multiple Git repos. DISCLAIMER: This is beta-quality software, with missing features and liable to fail with a stack trace, but shouldn't eat your data
62
66
 
@@ -71,11 +75,12 @@ options:
71
75
  --modified, -m Select repos that have local modifications
72
76
  --branched, -b Select repos that do not have the default branch checked out
73
77
  --tag TAG, -t TAG Select repos that have the specified tag (can be issued multiple times on the command line)
74
- --continue, -C Continue if a git command returns an error (by default, executation terminates when a command fails)
75
78
  --sub, -s Select only the repos in the current directory and subdirectories
79
+ --continue, -C Continue if a git command returns an error (by default, executation terminates when a command fails)
76
80
 
77
81
  Sub-commands:
78
- {+init,+dir,+config,GIT_COMMAND}
82
+ {+clone, +init,+dir,+config, GIT_COMMAND}
83
+ +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
79
84
  +init Build or update the configuration file using the current branch in each repo as the default branch
80
85
  +config Return the name and location of the configuration file
81
86
  +dir Return the location of a working tree, given the repo name, or if no parameter specified, the root directory of the multigit tree
@@ -136,6 +141,14 @@ def error(msg, status=1):
136
141
 
137
142
  ################################################################################
138
143
 
144
+ def absolute_repo_path(args, relative_path=''):
145
+ """Given a path relative to the multigit configuration file, return
146
+ the absolute path thereto"""
147
+
148
+ return os.path.join(os.path.dirname(args.configuration_file), relative_path)
149
+
150
+ ################################################################################
151
+
139
152
  def find_configuration(default_config_file):
140
153
  """If the configuration file name has path elements, try and read it, otherwise
141
154
  search up the directory tree looking for the configuration file.
@@ -157,7 +170,8 @@ def find_configuration(default_config_file):
157
170
  ################################################################################
158
171
 
159
172
  def show_progress(width, msg):
160
- """Show a single line progress message"""
173
+ """Show a single line progress message without moving the cursor to the next
174
+ line."""
161
175
 
162
176
  colour.write(msg[:width-1], newline=False, cleareol=True, cr=True)
163
177
 
@@ -176,20 +190,19 @@ def find_working_trees(args):
176
190
 
177
191
  for root, dirs, _ in os.walk(os.path.dirname(args.configuration_file)):
178
192
  if '.git' in dirs:
179
- if root.startswith('./'):
180
- root = root[2:]
193
+ relative_path = os.path.relpath(root)
181
194
 
182
195
  if args.repos:
183
196
  for card in args.repos:
184
- if fnmatch.fnmatch(root, card):
185
- if root not in repos:
186
- yield root
187
- repos.add(root)
197
+ if fnmatch.fnmatch(relative_path, card):
198
+ if relative_path not in repos:
199
+ yield relative_path
200
+ repos.add(relative_path)
188
201
  break
189
202
  else:
190
- if root not in repos:
191
- yield root
192
- repos.add(root)
203
+ if relative_path not in repos:
204
+ yield relative_path
205
+ repos.add(relative_path)
193
206
 
194
207
  ################################################################################
195
208
 
@@ -250,7 +263,7 @@ def select_git_repos(args, config):
250
263
  # If subdirectories specified, only match if the repo is in the current directory tree
251
264
 
252
265
  if matching and args.subdirectories:
253
- repo_path_rel = os.path.relpath(repo_path)
266
+ repo_path_rel = os.path.relpath(absolute_repo_path(args, repo_path))
254
267
 
255
268
  if repo_path_rel == '..' or repo_path_rel.startswith('../'):
256
269
  matching = False
@@ -269,6 +282,77 @@ def branch_name(name, default_branch):
269
282
 
270
283
  ################################################################################
271
284
 
285
+ def mg_clone(args, config, console):
286
+ """Clone a repo, optionally check out a branch and attempt to read the
287
+ configuration file and clone all the repos listed therein, checkouting
288
+ the default branch in each one"""
289
+
290
+ _ = console
291
+
292
+ # Sanity checks
293
+
294
+ if not args.parameters:
295
+ error('The "clone" subcommand takes 1 or 2 parameters - the repo to clone and, optionally, the branch to check out')
296
+
297
+ if args.branched or args.modified:
298
+ error('The "modified" and "branched" options cannot be used with the "clone" subcommand')
299
+
300
+ # Destination directory is the last portion of the repo URL with the extension removed
301
+
302
+ directory = os.path.splitext(os.path.basename(args.parameters[0]))[0]
303
+
304
+ if os.path.exists(directory):
305
+ if os.path.isdir(directory):
306
+ error(f'The "[BLUE:{directory}]" directory already exists')
307
+ else:
308
+ error(f'[BLUE:{directory}]" already exists')
309
+
310
+ # Clone the repo and chdir into it
311
+
312
+ if not args.quiet:
313
+ colour.write(f'Cloning [BOLD:{args.parameters[0]}] into [BLUE:{directory}]')
314
+
315
+ git.clone(args.parameters[0], path=directory)
316
+
317
+ os.chdir(directory)
318
+
319
+ # Optionally checkout a branch, if specified
320
+
321
+ if len(args.parameters) > 1:
322
+ git.checkout(args.parameters[1])
323
+
324
+ # Open the configuration file in the repo (if no configuration file has been specified, use the default)
325
+
326
+ if not args.configuration_file:
327
+ args.configuration_file = args.default_configuration_file
328
+
329
+ if not os.path.isfile(args.configuration_file):
330
+ error(f'Cannot find the configuration file: [BOLD:{args.default_configuration_file}]')
331
+
332
+ config.read(args.configuration_file)
333
+
334
+ # Now iterate through the repos, creating directories and cloning them and checking
335
+ # out the default branch
336
+
337
+ for repo in select_git_repos(args, config):
338
+ if repo.name != '.':
339
+ directory = os.path.dirname(repo.name)
340
+
341
+ if directory:
342
+ os.makedirs(directory, exist_ok=True)
343
+
344
+ if not args.quiet:
345
+ colour.write(f'Cloning [BOLD:{repo["origin"]}] into [BLUE:{directory}]')
346
+
347
+ git.clone(repo['origin'], path=repo.name)
348
+
349
+ if not args.quiet:
350
+ colour.write(f' Checking out [BLUE:{repo["default branch"]}]')
351
+
352
+ git.checkout(repo['default branch'], path=repo.name)
353
+
354
+ ################################################################################
355
+
272
356
  def mg_init(args, config, console):
273
357
  """Create or update the configuration
274
358
  By default, it scans the tree for git directories and adds or updates them
@@ -289,6 +373,9 @@ def mg_init(args, config, console):
289
373
  repo_list.append(repo)
290
374
 
291
375
  if repo not in config:
376
+ # Add a new configuration entry containing the default branch, remote origin
377
+ # and name
378
+
292
379
  default_branch = git.branch(path=repo)
293
380
 
294
381
  config[repo] = {
@@ -331,14 +418,19 @@ def mg_dir(args, config, console):
331
418
  of the tree if not
332
419
  Returns an error unless there is a unique match"""
333
420
 
334
- # DONE: Should return location relative to the current directory or as absolute path
335
-
336
421
  _ = console
337
422
  _ = config
338
423
 
339
424
  if len(args.parameters) > 1:
340
425
  error('The +dir command takes no more than one parameter - the name of the working tree to search for')
341
- elif args.parameters:
426
+
427
+ if args.modified or args.branched or args.tag or args.subdirectories:
428
+ error('The "--tag", "--modified" "--sub", and "--branched" options cannot be used with the "dir" subcommand')
429
+
430
+ # If a parameter is specified, look for matches, otherwise just return the location of the
431
+ # configuration file
432
+
433
+ if args.parameters:
342
434
  location = []
343
435
  wild_prefix_location = []
344
436
  wild_location = []
@@ -377,7 +469,8 @@ def mg_dir(args, config, console):
377
469
  dest_list = "\n\t".join([os.path.relpath(d) for d in destination])
378
470
  error(f'Multiple matches with [BLUE:{search_name}]: \n\t{dest_list}')
379
471
 
380
- colour.write(os.path.join(os.path.dirname(args.configuration_file), destination[0]))
472
+ colour.write(absolute_repo_path(args, destination[0]))
473
+
381
474
  else:
382
475
  colour.write(os.path.dirname(args.configuration_file))
383
476
 
@@ -445,17 +538,23 @@ def run_git_command(args, config, console):
445
538
  """Run a command in each of the working trees, optionally continuing if
446
539
  there's an error"""
447
540
 
448
- _ = config
449
541
  _ = console
450
542
 
451
543
  for repo in select_git_repos(args, config):
452
544
  repo_command = [args.command]
545
+
546
+ # Replace 'DEFAULT' in the command with the default branch in the repo
547
+
453
548
  for cmd in args.parameters:
454
549
  repo_command.append(branch_name(cmd, repo['default branch']))
455
550
 
456
551
  colour.write(f'\n[BOLD:{os.path.relpath(repo.name)}]\n')
457
552
 
458
- _, status = git.git_run_status(repo_command, path=repo.name, redirect=False)
553
+ # Run the command in the workng tree
554
+
555
+ repo_path = absolute_repo_path(args, repo.name)
556
+
557
+ _, status = git.git_run_status(repo_command, path=repo_path, redirect=False)
459
558
 
460
559
  if status and not args.error_continue:
461
560
  sys.exit(status)
@@ -479,7 +578,7 @@ def parse_command_line():
479
578
  for c in arg[1:]:
480
579
  argv.append('-' + c)
481
580
 
482
- # Parse the command line
581
+ # Parse the command line, setting options in the args dataclass appropriately
483
582
 
484
583
  i = 1
485
584
  while i < len(argv):
@@ -525,7 +624,7 @@ def parse_command_line():
525
624
  args.error_continue = True
526
625
 
527
626
  elif param in ('--help', '-h'):
528
- print(HELP_INFO)
627
+ colour.write(HELP_INFO)
529
628
  sys.exit(0)
530
629
 
531
630
  elif param[0] == '-':
@@ -549,8 +648,12 @@ def parse_command_line():
549
648
  except IndexError:
550
649
  error('Missing command')
551
650
 
651
+ # Save the command parameters
652
+
552
653
  args.parameters = argv[i+1:]
553
654
 
655
+ # Locate the configuration file
656
+
554
657
  args.configuration_file = find_configuration(args.default_configuration_file)
555
658
 
556
659
  return args
@@ -558,16 +661,20 @@ def parse_command_line():
558
661
  ################################################################################
559
662
 
560
663
  COMMANDS = {
561
- 'init': mg_init,
562
- 'dir': mg_dir,
563
- 'config': mg_config,
564
- 'tag': mg_tag,
565
- 'untag': mg_untag,
664
+ 'clone': mg_clone,
665
+ 'init': mg_init,
666
+ 'dir': mg_dir,
667
+ 'config': mg_config,
668
+ 'tag': mg_tag,
669
+ 'untag': mg_untag,
566
670
  }
567
671
 
568
672
  def main():
569
673
  """Main function"""
570
674
 
675
+ # Parse the command line and santity check the command to run
676
+ # (if it is an external command we let git worry about it)
677
+
571
678
  args = parse_command_line()
572
679
 
573
680
  if args.internal_command and args.command not in COMMANDS:
@@ -582,12 +689,13 @@ def main():
582
689
  # Otherwise, fail if we can't find the configuration file.
583
690
 
584
691
  if not args.configuration_file:
585
- if args.internal_command and args.command == 'init':
586
- args.configuration_file = os.path.abspath(args.default_configuration_file)
692
+ if args.internal_command:
693
+ if args.command == 'init':
694
+ args.configuration_file = os.path.abspath(args.default_configuration_file)
587
695
  else:
588
696
  error('Cannot locate configuration file')
589
697
 
590
- if os.path.isfile(args.configuration_file):
698
+ if args.configuration_file and os.path.isfile(args.configuration_file):
591
699
  config.read(args.configuration_file)
592
700
 
593
701
  # Get the console size
@@ -109,13 +109,13 @@ def git_run_status(cmd, stdout=None, stderr=None, path=None, redirect=True):
109
109
 
110
110
  ################################################################################
111
111
 
112
- def clone(reponame, directory=None):
112
+ def clone(reponame, path=None):
113
113
  """ Clone a repo """
114
114
 
115
115
  cmd = ['clone', reponame]
116
116
 
117
- if directory:
118
- cmd.append(directory)
117
+ if path:
118
+ cmd.append(path)
119
119
 
120
120
  return git(cmd)
121
121
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: skilleter_thingy
3
- Version: 0.1.2
3
+ Version: 0.1.3
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
@@ -89,6 +89,8 @@ The command takes a number of options that can be used to select the list of wor
89
89
 
90
90
  `--tag TAG` / `-t TAG` Run only in working trees that are tagged with the specified tag
91
91
 
92
+ `--subdir` / `-s` Run only in working trees that are in the current directory tree.
93
+
92
94
  These options are AND-ed together, so specifying `--modified --branched --tag WOMBAT` will select only working trees that are modified AND branched AND tagged with `WOMBAT`, but the parameters to the `--repos` and `--tag` options are OR-ed together, so specifying `--tag WOMBAT --tag EMU` will select repos that are tagged as `WOMBAT` *OR* `EMU`.
93
95
 
94
96
  Multigit tags are stored in the configuration file, not within the working tree and each working tree can have multiple tags.
@@ -97,6 +99,8 @@ Multigit tags are stored in the configuration file, not within the working tree
97
99
 
98
100
  Multigit supports a small list of subcommands, each of which are prefixed with a `+` to distinguish them from Git commands:
99
101
 
102
+ `+clone` - Clone a repo containing a multigit configuration file then clone all the repos specified in the configuration, checking out the default branch in each one.
103
+
100
104
  `+init` - Create or update the configuration file
101
105
 
102
106
  `+dir` - Given the name of a working tree, output the location within the multigit tree of that working tree if the name matches uniquely, or the name of the directory where the multigit configuration file resides if no parameter is specified.