skilleter-thingy 0.0.95__tar.gz → 0.0.96__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 (74) hide show
  1. {skilleter_thingy-0.0.95/skilleter_thingy.egg-info → skilleter_thingy-0.0.96}/PKG-INFO +14 -4
  2. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/README.md +13 -3
  3. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/pyproject.toml +1 -1
  4. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/multigit.py +107 -33
  5. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96/skilleter_thingy.egg-info}/PKG-INFO +14 -4
  6. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/LICENSE +0 -0
  7. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/setup.cfg +0 -0
  8. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/__init__.py +0 -0
  9. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/addpath.py +0 -0
  10. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/borger.py +0 -0
  11. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/box.py +0 -0
  12. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/console_colours.py +0 -0
  13. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/diskspacecheck.py +0 -0
  14. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/docker_purge.py +0 -0
  15. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/ffind.py +0 -0
  16. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/ggit.py +0 -0
  17. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/ggrep.py +0 -0
  18. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_br.py +0 -0
  19. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_ca.py +0 -0
  20. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_cleanup.py +0 -0
  21. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_co.py +0 -0
  22. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_common.py +0 -0
  23. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_hold.py +0 -0
  24. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_mr.py +0 -0
  25. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_parent.py +0 -0
  26. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_review.py +0 -0
  27. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_update.py +0 -0
  28. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/git_wt.py +0 -0
  29. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/gitcmp_helper.py +0 -0
  30. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/gitprompt.py +0 -0
  31. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/gl.py +0 -0
  32. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/gphotosync.py +0 -0
  33. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/linecount.py +0 -0
  34. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/moviemover.py +0 -0
  35. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/photodupe.py +0 -0
  36. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/phototidier.py +0 -0
  37. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/py_audit.py +0 -0
  38. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/readable.py +0 -0
  39. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/remdir.py +0 -0
  40. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/rmdupe.py +0 -0
  41. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/rpylint.py +0 -0
  42. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/splitpics.py +0 -0
  43. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/strreplace.py +0 -0
  44. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/sysmon.py +0 -0
  45. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/tfm.py +0 -0
  46. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/tfparse.py +0 -0
  47. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/__init__.py +0 -0
  48. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/colour.py +0 -0
  49. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/dc_curses.py +0 -0
  50. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/dc_defaults.py +0 -0
  51. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/dc_util.py +0 -0
  52. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/dircolors.py +0 -0
  53. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/docker.py +0 -0
  54. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/files.py +0 -0
  55. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/git.py +0 -0
  56. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/git2.py +0 -0
  57. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/gitlab.py +0 -0
  58. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/path.py +0 -0
  59. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/popup.py +0 -0
  60. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/process.py +0 -0
  61. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/run.py +0 -0
  62. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/tfm_pane.py +0 -0
  63. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/tidy.py +0 -0
  64. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/thingy/venv_template.py +0 -0
  65. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/trimpath.py +0 -0
  66. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/venv_create.py +0 -0
  67. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/window_rename.py +0 -0
  68. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/xchmod.py +0 -0
  69. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy/yamlcheck.py +0 -0
  70. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy.egg-info/SOURCES.txt +0 -0
  71. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy.egg-info/dependency_links.txt +0 -0
  72. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy.egg-info/entry_points.txt +0 -0
  73. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/skilleter_thingy.egg-info/requires.txt +0 -0
  74. {skilleter_thingy-0.0.95 → skilleter_thingy-0.0.96}/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.0.95
3
+ Version: 0.0.96
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
@@ -50,15 +50,21 @@ The multigit command line format is:
50
50
 
51
51
  Where COMMAND is an internal multigit command if it starts with a '+' and is a git command otherwise.
52
52
 
53
- By default, when multigit is invoked with a git command, it runs a the command in each of the working trees selected by the command line options passed to multigit (if no options are specified then the command is run in all the working trees.
53
+ By default, when multigit is invoked with a git command, it runs a the command in each of the working trees selected by the command line options passed to multigit (if no options are specified then the command is run in *all* the working trees.
54
54
 
55
55
  The command takes a number of options that can be used to select the list of working trees that each of the subcommands that it supports runs in:
56
56
 
57
- *--repos / -r* Allows a list of working trees to be specfied, either as the full or relative path, the name or a wildcard.
57
+ *--repos REPO / -r REPO* Allows a list of working trees to be specfied, either by path, name or a wildcard matching either.
58
58
 
59
59
  *--modified / -m* Run only in working trees containing locally modified files
60
60
 
61
- *--branched / -b* Run only working trees where the current branch that is checked out is NOT the default branch
61
+ *--branched / -b* Run only in working trees where the current branch that is checked out is NOT the default branch
62
+
63
+ *--tag TAG / -t TAG* Run only in working trees that are tagged with the specified tag
64
+
65
+ 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`.
66
+
67
+ Multigit tags are stored in the configuration file, not within the working tree and each working tree can have multiple tags.
62
68
 
63
69
  Multigit supports a small list of subcommands, each of which are prefixed with a `+` to distinguish them from Git commands:
64
70
 
@@ -68,6 +74,10 @@ Multigit supports a small list of subcommands, each of which are prefixed with a
68
74
 
69
75
  *+config* - Print the name and location of the multigit configuration file.
70
76
 
77
+ *+tag TAG* - If no tag specified list tags applied to the specified working trees. If a tag *is* specified, then *apply* the tag to the specified working trees.
78
+
79
+ *+untag TAG* - Remove the tag from the specified working trees (do nothing if the tag is not applied in the first place).
80
+
71
81
  Any command not prefixed with '+' is run in each of the working trees (filtered by the various multigit options) as a git command.
72
82
 
73
83
  For example; `multigit -m commit -ab` would run `git commit -a` in each of the working trees that is branched and contains modified files.
@@ -28,15 +28,21 @@ The multigit command line format is:
28
28
 
29
29
  Where COMMAND is an internal multigit command if it starts with a '+' and is a git command otherwise.
30
30
 
31
- By default, when multigit is invoked with a git command, it runs a the command in each of the working trees selected by the command line options passed to multigit (if no options are specified then the command is run in all the working trees.
31
+ By default, when multigit is invoked with a git command, it runs a the command in each of the working trees selected by the command line options passed to multigit (if no options are specified then the command is run in *all* the working trees.
32
32
 
33
33
  The command takes a number of options that can be used to select the list of working trees that each of the subcommands that it supports runs in:
34
34
 
35
- *--repos / -r* Allows a list of working trees to be specfied, either as the full or relative path, the name or a wildcard.
35
+ *--repos REPO / -r REPO* Allows a list of working trees to be specfied, either by path, name or a wildcard matching either.
36
36
 
37
37
  *--modified / -m* Run only in working trees containing locally modified files
38
38
 
39
- *--branched / -b* Run only working trees where the current branch that is checked out is NOT the default branch
39
+ *--branched / -b* Run only in working trees where the current branch that is checked out is NOT the default branch
40
+
41
+ *--tag TAG / -t TAG* Run only in working trees that are tagged with the specified tag
42
+
43
+ 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`.
44
+
45
+ Multigit tags are stored in the configuration file, not within the working tree and each working tree can have multiple tags.
40
46
 
41
47
  Multigit supports a small list of subcommands, each of which are prefixed with a `+` to distinguish them from Git commands:
42
48
 
@@ -46,6 +52,10 @@ Multigit supports a small list of subcommands, each of which are prefixed with a
46
52
 
47
53
  *+config* - Print the name and location of the multigit configuration file.
48
54
 
55
+ *+tag TAG* - If no tag specified list tags applied to the specified working trees. If a tag *is* specified, then *apply* the tag to the specified working trees.
56
+
57
+ *+untag TAG* - Remove the tag from the specified working trees (do nothing if the tag is not applied in the first place).
58
+
49
59
  Any command not prefixed with '+' is run in each of the working trees (filtered by the various multigit options) as a git command.
50
60
 
51
61
  For example; `multigit -m commit -ab` would run `git commit -a` in each of the working trees that is branched and contains modified files.
@@ -7,7 +7,7 @@ name = "skilleter_thingy"
7
7
 
8
8
  # Version must be incremented to install updated Thingy
9
9
 
10
- version = "0.0.95"
10
+ version = "0.0.96"
11
11
 
12
12
  authors = [
13
13
  {name="John Skilleter", email="john@skilleter.org.uk"},
@@ -16,10 +16,12 @@ import thingy.colour as colour
16
16
 
17
17
  # DONE: / Output name of each git repo as it is processed as command sits there seeming to do nothing otherwise.
18
18
  # DONE: Better error-handling - e.g. continue/abort option after failure in one repo
19
+ # DONE: Currently doesn't handle single letter options in concatenated form - e.g. -dv
19
20
  # DONE: Don't save the configuration on exit if it hasn't changed
20
21
  # DONE: Don't use a fixed list of default branch names
21
22
  # 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
22
23
  # DONE: Use the configuration file
24
+ # DONE: command to categorise repos then command line filter to only act on repos in category (in addition to other filtering options) - +tag <TAG> command tags all selected repors and updates the configuration, +untag <TAG> command to remove tags in the same way
23
25
  # DONE: init function
24
26
  # NOPE: Dry-run option - just pass the option to the Git command
25
27
  # NOPE: Is it going to be a problem if the same repo is checked out twice or more in the same workspace - user problem
@@ -29,9 +31,12 @@ import thingy.colour as colour
29
31
  # TODO: Command that takes partial repo name and either returns full path or pops up window to autocomplete until single match found
30
32
  # TODO: Consistent colours in output
31
33
  # TODO: If run in a subdirectory, only process repos in that tree (or have an option to do so)
34
+ # TODO: Option to +dir to return all matches so that caller can select one they want
32
35
  # TODO: Verbose option
36
+ # 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)
33
37
  # TODO: When specifying list of repos, if repo name doesn't contain '/' prefix it with '*'?
34
38
  # TODO: select_git_repos() and +dir should use consist way of selecting repos if possible
39
+ # TODO: +run command to do things other than git commands
35
40
  ################################################################################
36
41
 
37
42
  DEFAULT_CONFIG_FILE = 'multigit.toml'
@@ -43,16 +48,13 @@ DEFAULT_BRANCH = 'DEFAULT'
43
48
 
44
49
  ################################################################################
45
50
 
46
- HELP_INFO = """usage: multigit [-h] [--dryrun] [--debug] [--verbose] [--quiet] [--config CONFIG] [--directory DIRECTORY] [--repos REPOS] [--modified] [--branched]
51
+ HELP_INFO = """usage: multigit [-h] [--verbose] [--quiet] [--config CONFIG] [--directory DIRECTORY] [--repos REPOS] [--modified] [--branched] [--tag TAGS]
47
52
  {+init, +config, +dir, GIT_COMMAND} ...
48
53
 
49
54
  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
50
55
 
51
56
  options:
52
57
  -h, --help show this help message and exit
53
- --dryrun, --dry-run, -D
54
- Dry-run comands
55
- --debug, -d Debug
56
58
  --verbose, -v Verbosity to the maximum
57
59
  --quiet, -q Minimal console output
58
60
  --config CONFIG, -c CONFIG
@@ -63,6 +65,7 @@ options:
63
65
  The repo names to work on (defaults to all repos and can contain shell wildcards and can be issued multiple times on the command line)
64
66
  --modified, -m Select repos that have local modifications
65
67
  --branched, -b Select repos that do not have the default branch checked out
68
+ --tag TAG, -t TAG Select repos that have the specified tag (can be issued multiple times on the command line)
66
69
  --continue, -C Continue if a git command returns an error (by default, executation terminates when a command fails)
67
70
 
68
71
  Sub-commands:
@@ -70,6 +73,8 @@ Sub-commands:
70
73
  +init Build or update the configuration file using the current branch in each repo as the default branch
71
74
  +config Return the name and location of the configuration file
72
75
  +dir Return the location of a working tree, given the repo name, or if no parameter specified, the root directory of the multigit tree
76
+ +tag TAG Apply a configuration tag to repos filtered by the command line options (list configuration tags if no parameter specified)
77
+ +untag TAG Remove a configuration tag to repos filtered by the command line options
73
78
  GIT_COMMAND Any git command, including options and parameters - this is then run in all specified working trees
74
79
 
75
80
  """
@@ -80,19 +85,19 @@ Sub-commands:
80
85
  class Arguments():
81
86
  """Data class to contain command line options and parameters"""
82
87
 
83
- dryrun: bool = False
84
- debug: bool = False
85
88
  quiet: bool = False
86
89
  verbose: bool = False
87
90
  configuration_file: str = DEFAULT_CONFIG_FILE
88
91
  directory: str = '.'
89
92
  repos: list[str] = field(default_factory=list)
93
+ tag: list[str] = field(default_factory=list)
90
94
  modified: bool = False
91
95
  branched: bool = False
92
96
  command: str = None
93
97
  error_continue: bool = False
94
98
  parameters: list[str] = field(default_factory=list)
95
99
  internal_command: bool = False
100
+ config_modified: bool = False
96
101
 
97
102
  ################################################################################
98
103
 
@@ -170,26 +175,27 @@ def find_git_repos(args):
170
175
 
171
176
  def select_git_repos(args, config):
172
177
  """Return git repos from the configuration that match the criteria on the
173
- multigit command line (the --repos, --modified and --branched options)
178
+ multigit command line (the --repos, --tag, --modified and --branched options)
174
179
  or, return them all if no relevant options specified"""
175
180
 
176
- for repo in config.sections():
177
- # If repos are specified, then only match according to wildcards, full
178
- # path or just basename.
181
+ for repo_path in config.sections():
182
+ # If repos are specified, then only match according to exact name match,
183
+ # exact path match or wildcard match
179
184
 
180
185
  if args.repos:
181
186
  for entry in args.repos:
187
+ if config[repo_path]['repo name'] == entry:
188
+ matching = True
189
+ break
190
+
191
+ if repo_path == entry:
192
+ matching = True
193
+ break
194
+
182
195
  if '?' in entry or '*' in entry:
183
- if fnmatch.fnmatch(repo, entry):
196
+ if fnmatch.fnmatch(repo_path, entry) or fnmatch.fnmatch(config[repo_path]['repo name'], entry):
184
197
  matching = True
185
198
  break
186
- elif '/' in entry:
187
- if repo == entry:
188
- matching = True
189
- break
190
- elif os.path.basename(repo) == entry:
191
- matching = True
192
- break
193
199
 
194
200
  else:
195
201
  matching = False
@@ -199,17 +205,32 @@ def select_git_repos(args, config):
199
205
  # If branched specified, only match if the repo is matched _and_ branched
200
206
 
201
207
  if matching and args.branched:
202
- if git.branch(path=repo) == config[repo]['default branch']:
208
+ if git.branch(path=repo_path) == config[repo_path]['default branch']:
203
209
  matching = False
204
210
 
205
211
  # If modified specified, only match if the repo is matched _and_ modified
206
212
 
207
213
  if matching and args.modified:
208
- if not git.status(path=repo):
214
+ if not git.status(path=repo_path):
215
+ matching = False
216
+
217
+ # If tag filtering specified, only match if the repo is tagged with one of the specified tags
218
+
219
+ if matching and args.tag:
220
+ for entry in args.tag:
221
+ try:
222
+ tags = config[repo_path]['tags'].split(',')
223
+ if entry in tags:
224
+ break
225
+ except KeyError:
226
+ pass
227
+ else:
209
228
  matching = False
210
229
 
230
+ # If we have a match, yield the config entry to the caller
231
+
211
232
  if matching:
212
- yield config[repo]
233
+ yield config[repo_path]
213
234
 
214
235
  ################################################################################
215
236
 
@@ -227,8 +248,8 @@ def mg_init(args, config, console):
227
248
 
228
249
  # Sanity checks
229
250
 
230
- if args.modified or args.branched:
231
- error('The "--modified" and "--branched" options cannot be used with the "init" subcommand')
251
+ if args.modified or args.branched or args.tag:
252
+ error('The "--tag", "--modified" and "--branched" options cannot be used with the "init" subcommand')
232
253
  elif not config:
233
254
  error(f'Unable to location configuration file "{args.configuration_file}"')
234
255
 
@@ -253,6 +274,8 @@ def mg_init(args, config, console):
253
274
  else:
254
275
  config[repo]['repo name'] = os.path.basename(repo)
255
276
 
277
+ args.config_modified = True
278
+
256
279
  ################################################################################
257
280
 
258
281
  def mg_dir(args, config, console):
@@ -288,12 +311,15 @@ def mg_dir(args, config, console):
288
311
  elif fnmatch.fnmatch(repo['repo name'], f'*{search_name}*'):
289
312
  wild_location.append(repo.name)
290
313
 
314
+ # Look for a single exact match, a prefix with '*' match or prefix+suffix
315
+
291
316
  destination = None
292
317
  for destinations in (location, wild_prefix_location, wild_location):
293
318
  if len(destinations) == 1:
294
319
  destination = destinations
295
320
  break
296
- elif len(destinations) > 1:
321
+
322
+ if len(destinations) > 1:
297
323
  destination = destinations
298
324
 
299
325
  if not destination:
@@ -308,6 +334,51 @@ def mg_dir(args, config, console):
308
334
 
309
335
  ################################################################################
310
336
 
337
+ def mg_tag(args, config, console):
338
+ """Apply a configuration tag"""
339
+
340
+ _ = console
341
+
342
+ if len(args.parameters) > 1:
343
+ error('The +tag command takes no more than one parameter')
344
+
345
+ for repo in select_git_repos(args, config):
346
+ try:
347
+ tags = repo.get('tags').split(',')
348
+ except AttributeError:
349
+ tags = []
350
+
351
+ if args.parameters:
352
+ if args.parameters[0] not in tags:
353
+ tags.append(args.parameters[0])
354
+ repo['tags'] = ','.join(tags)
355
+ args.config_modified = True
356
+ elif tags:
357
+ colour.write(f'[BOLD:{repo["repo name"]}] - {", ".join(tags)}')
358
+
359
+ ################################################################################
360
+
361
+ def mg_untag(args, config, console):
362
+ """Remove a configuration tag"""
363
+
364
+ _ = console
365
+
366
+ if len(args.parameters) > 1:
367
+ error('The +tag command takes no more than one parameter')
368
+
369
+ for repo in select_git_repos(args, config):
370
+ try:
371
+ tags = repo.get('tags', '').split(',')
372
+ except AttributeError:
373
+ tags = []
374
+
375
+ if args.parameters[0] in tags:
376
+ tags.remove(args.parameters[0])
377
+ repo['tags'] = ','.join(tags)
378
+ args.config_modified = True
379
+
380
+ ################################################################################
381
+
311
382
  def mg_config(args, config, console):
312
383
  """Output the path to the configuration file"""
313
384
 
@@ -359,19 +430,13 @@ def parse_command_line():
359
430
  for c in arg[1:]:
360
431
  argv.append('-' + c)
361
432
 
362
- # Currently doesn't handle single letter options in concatenated form - e.g. -dv
433
+ # Parse the command line
363
434
 
364
435
  i = 1
365
436
  while i < len(argv):
366
437
  param = argv[i]
367
438
 
368
- if param in ('--dryrun', '--dry-run', '-D'):
369
- args.dryrun = True
370
-
371
- elif param in ('--debug', '-d'):
372
- args.debug = True
373
-
374
- elif param in ('--verbose', '-v'):
439
+ if param in ('--verbose', '-v'):
375
440
  args.verbose = True
376
441
 
377
442
  elif param in ('--quiet', '-q'):
@@ -391,6 +456,13 @@ def parse_command_line():
391
456
  except IndexError:
392
457
  error('--repos - missing repo parameter')
393
458
 
459
+ elif param in ('--tag', '-t'):
460
+ try:
461
+ i += 1
462
+ args.tag.append(argv[i])
463
+ except IndexError:
464
+ error('--tag - missing tag parameter')
465
+
394
466
  elif param in ('--modified', '-m'):
395
467
  args.modified = True
396
468
 
@@ -440,6 +512,8 @@ def main():
440
512
  'init': mg_init,
441
513
  'dir': mg_dir,
442
514
  'config': mg_config,
515
+ 'tag': mg_tag,
516
+ 'untag': mg_untag,
443
517
  }
444
518
 
445
519
  args = parse_command_line()
@@ -478,7 +552,7 @@ def main():
478
552
 
479
553
  # Save the updated configuration file if it has changed (currently, only the init command will do this).
480
554
 
481
- if config and args.command == 'init':
555
+ if config and args.config_modified:
482
556
  with open(args.configuration_file, 'w', encoding='utf8') as configfile:
483
557
  config.write(configfile)
484
558
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: skilleter_thingy
3
- Version: 0.0.95
3
+ Version: 0.0.96
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
@@ -50,15 +50,21 @@ The multigit command line format is:
50
50
 
51
51
  Where COMMAND is an internal multigit command if it starts with a '+' and is a git command otherwise.
52
52
 
53
- By default, when multigit is invoked with a git command, it runs a the command in each of the working trees selected by the command line options passed to multigit (if no options are specified then the command is run in all the working trees.
53
+ By default, when multigit is invoked with a git command, it runs a the command in each of the working trees selected by the command line options passed to multigit (if no options are specified then the command is run in *all* the working trees.
54
54
 
55
55
  The command takes a number of options that can be used to select the list of working trees that each of the subcommands that it supports runs in:
56
56
 
57
- *--repos / -r* Allows a list of working trees to be specfied, either as the full or relative path, the name or a wildcard.
57
+ *--repos REPO / -r REPO* Allows a list of working trees to be specfied, either by path, name or a wildcard matching either.
58
58
 
59
59
  *--modified / -m* Run only in working trees containing locally modified files
60
60
 
61
- *--branched / -b* Run only working trees where the current branch that is checked out is NOT the default branch
61
+ *--branched / -b* Run only in working trees where the current branch that is checked out is NOT the default branch
62
+
63
+ *--tag TAG / -t TAG* Run only in working trees that are tagged with the specified tag
64
+
65
+ 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`.
66
+
67
+ Multigit tags are stored in the configuration file, not within the working tree and each working tree can have multiple tags.
62
68
 
63
69
  Multigit supports a small list of subcommands, each of which are prefixed with a `+` to distinguish them from Git commands:
64
70
 
@@ -68,6 +74,10 @@ Multigit supports a small list of subcommands, each of which are prefixed with a
68
74
 
69
75
  *+config* - Print the name and location of the multigit configuration file.
70
76
 
77
+ *+tag TAG* - If no tag specified list tags applied to the specified working trees. If a tag *is* specified, then *apply* the tag to the specified working trees.
78
+
79
+ *+untag TAG* - Remove the tag from the specified working trees (do nothing if the tag is not applied in the first place).
80
+
71
81
  Any command not prefixed with '+' is run in each of the working trees (filtered by the various multigit options) as a git command.
72
82
 
73
83
  For example; `multigit -m commit -ab` would run `git commit -a` in each of the working trees that is branched and contains modified files.