mud-git 0.0.post1.dev1__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.
- mud/__init__.py +18 -0
- mud/app.py +269 -0
- mud/commands.py +27 -0
- mud/config.py +99 -0
- mud/runner.py +500 -0
- mud/settings.py +51 -0
- mud/styles.py +96 -0
- mud/utils.py +126 -0
- mud_git-0.0.post1.dev1.dist-info/LICENSE +21 -0
- mud_git-0.0.post1.dev1.dist-info/METADATA +139 -0
- mud_git-0.0.post1.dev1.dist-info/RECORD +14 -0
- mud_git-0.0.post1.dev1.dist-info/WHEEL +5 -0
- mud_git-0.0.post1.dev1.dist-info/entry_points.txt +2 -0
- mud_git-0.0.post1.dev1.dist-info/top_level.txt +1 -0
mud/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from mud import utils, settings
|
|
4
|
+
from mud.app import App
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def run():
|
|
8
|
+
try:
|
|
9
|
+
utils.settings = settings.Settings(utils.SETTINGS_FILE_NAME)
|
|
10
|
+
|
|
11
|
+
app = App()
|
|
12
|
+
app.run()
|
|
13
|
+
except KeyboardInterrupt:
|
|
14
|
+
utils.print_error('Stopped by user.', 0)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == '__main__':
|
|
18
|
+
run()
|
mud/app.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
import argparse
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
from argparse import ArgumentParser
|
|
8
|
+
|
|
9
|
+
from mud import config
|
|
10
|
+
from mud import utils
|
|
11
|
+
from mud.runner import Runner
|
|
12
|
+
from mud.styles import *
|
|
13
|
+
from mud.commands import *
|
|
14
|
+
from mud.utils import glyphs
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class App:
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.cmd_runner = None
|
|
20
|
+
self.command = None
|
|
21
|
+
self.config = None
|
|
22
|
+
self.parser = self._create_parser()
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def _create_parser() -> ArgumentParser:
|
|
26
|
+
parser = argparse.ArgumentParser(description=f'{BOLD}mud{RESET} allows you to run commands in multiple repositories.')
|
|
27
|
+
subparsers = parser.add_subparsers(dest='command')
|
|
28
|
+
|
|
29
|
+
subparsers.add_parser(LOG[0], aliases=LOG[1:], help='Displays log of latest commit messages for all repositories in a table view.')
|
|
30
|
+
subparsers.add_parser(INFO[0], aliases=INFO[1:], help='Displays branch divergence and working directory changes')
|
|
31
|
+
subparsers.add_parser(INIT[0], aliases=INIT[1:], help=f'Initializes the {BOLD}.mudconfig{RESET} and adds all repositories in this directory to {BOLD}.mudconfig{RESET}.')
|
|
32
|
+
subparsers.add_parser(TAGS[0], aliases=TAGS[1:], help='Displays git tags in repositories.')
|
|
33
|
+
subparsers.add_parser(LABELS[0], aliases=LABELS[1:], help='Displays mud labels across repositories.')
|
|
34
|
+
subparsers.add_parser(STATUS[0], aliases=STATUS[1:], help='Displays working directory changes.')
|
|
35
|
+
subparsers.add_parser(BRANCHES[0], aliases=BRANCHES[1:], help='Displays all branches in repositories.')
|
|
36
|
+
subparsers.add_parser(REMOTE_BRANCHES[0], aliases=REMOTE_BRANCHES[1:], help='Displays all remote branches in repositories.')
|
|
37
|
+
subparsers.add_parser(CONFIGURE[0], aliases=CONFIGURE[1:], help='Runs the interactive configuration wizard.')
|
|
38
|
+
|
|
39
|
+
add_parser = subparsers.add_parser(ADD[0], aliases=ADD[1:], help='Adds repository or labels an existing repository.')
|
|
40
|
+
add_parser.add_argument('label', help='The label to add (optional).', nargs='?', default='', type=str)
|
|
41
|
+
add_parser.add_argument('path', help='Repository to add (optional).', nargs='?', type=str)
|
|
42
|
+
|
|
43
|
+
remove_parser = subparsers.add_parser(REMOVE[0], aliases=REMOVE[1:], help='Removes repository or removes the label from an existing repository.')
|
|
44
|
+
remove_parser.add_argument('label', help='Label to remove from repository (optional).', nargs='?', default='', type=str)
|
|
45
|
+
remove_parser.add_argument('path', help='Repository to remove (optional).', nargs='?', type=str)
|
|
46
|
+
|
|
47
|
+
parser.add_argument(*COMMAND_ATTR, metavar='COMMAND', nargs='?', default='', type=str, help=f'Explicit command argument. Use this when you want to run a command that has a special characters.')
|
|
48
|
+
parser.add_argument(*TABLE_ATTR, metavar='TABLE', nargs='?', default='', type=str, help=f'Switches table view, runs in table view it is disabled in {BOLD}.mudsettings{RESET}.')
|
|
49
|
+
parser.add_argument(*LABEL_PREFIX, metavar='LABEL', nargs='?', default='', type=str, help='Includes repositories with provided label.')
|
|
50
|
+
parser.add_argument(*NOT_LABEL_PREFIX, metavar='NOT_LABEL', nargs='?', default='', type=str, help=f'Excludes repositories with provided label..')
|
|
51
|
+
parser.add_argument(*BRANCH_PREFIX, metavar='BRANCH', nargs='?', default='', type=str, help='Includes repositories on a provided branch.')
|
|
52
|
+
parser.add_argument(*NOT_BRANCH_PREFIX, metavar='NOT_BRANCH', nargs='?', default='', type=str, help='Excludes repositories on a provided branch.')
|
|
53
|
+
parser.add_argument(*MODIFIED_ATTR, action='store_true', help='Filters modified repositories.')
|
|
54
|
+
parser.add_argument(*DIVERGED_ATTR, action='store_true', help='Filters repositories with diverged branches.')
|
|
55
|
+
parser.add_argument(*ASYNC_ATTR, action='store_true', help='Switches asynchronous run feature.')
|
|
56
|
+
parser.add_argument(SET_GLOBAL[0], help=f'Sets {BOLD}.mudconfig{RESET} in the current repository as your fallback {BOLD}.mudconfig{RESET}.', action='store_true')
|
|
57
|
+
parser.add_argument('catch_all', nargs='*', help='Type any commands to execute among repositories.')
|
|
58
|
+
return parser
|
|
59
|
+
|
|
60
|
+
def run(self) -> None:
|
|
61
|
+
# Displays default help message
|
|
62
|
+
if len(sys.argv) == 1 or sys.argv[1] in HELP:
|
|
63
|
+
utils.info()
|
|
64
|
+
print()
|
|
65
|
+
self.parser.print_help()
|
|
66
|
+
return
|
|
67
|
+
# Sets global repository in .mudsettings
|
|
68
|
+
if sys.argv[1] in SET_GLOBAL:
|
|
69
|
+
config_path = os.path.join(os.getcwd(), utils.CONFIG_FILE_NAME)
|
|
70
|
+
if os.path.exists(config_path):
|
|
71
|
+
utils.settings.config.set('mud', 'config_path', config_path)
|
|
72
|
+
utils.settings.save()
|
|
73
|
+
print(f'Current {BOLD}.mudconfig{RESET} set as a global configuration.')
|
|
74
|
+
return
|
|
75
|
+
# Runs configuration wizard
|
|
76
|
+
elif sys.argv[1] in CONFIGURE:
|
|
77
|
+
utils.configure()
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
current_directory = os.getcwd()
|
|
81
|
+
self.config = config.Config()
|
|
82
|
+
|
|
83
|
+
# Discovers repositories in current directory
|
|
84
|
+
if sys.argv[1] in INIT:
|
|
85
|
+
self.init(self.parser.parse_args())
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
self.config.find()
|
|
89
|
+
self._filter_with_arguments()
|
|
90
|
+
|
|
91
|
+
self.cmd_runner = Runner(self.config)
|
|
92
|
+
# Handling commands
|
|
93
|
+
if len(sys.argv) > 1 and sys.argv[1] in [cmd for group in COMMANDS for cmd in group]:
|
|
94
|
+
args = self.parser.parse_args()
|
|
95
|
+
if args.command in INIT:
|
|
96
|
+
os.chdir(current_directory)
|
|
97
|
+
self.init(args)
|
|
98
|
+
elif args.command in ADD:
|
|
99
|
+
self.add(args)
|
|
100
|
+
elif args.command in REMOVE:
|
|
101
|
+
self.remove(args)
|
|
102
|
+
else:
|
|
103
|
+
if len(self.repos) == 0:
|
|
104
|
+
utils.print_error('No repositories are matching this filter.', 1)
|
|
105
|
+
return
|
|
106
|
+
if args.command in INFO:
|
|
107
|
+
self.cmd_runner.info(self.repos)
|
|
108
|
+
elif args.command in LOG:
|
|
109
|
+
self.cmd_runner.log(self.repos)
|
|
110
|
+
elif args.command in REMOTE_BRANCHES:
|
|
111
|
+
self.cmd_runner.remote_branches(self.repos)
|
|
112
|
+
elif args.command in BRANCHES:
|
|
113
|
+
self.cmd_runner.branches(self.repos)
|
|
114
|
+
elif args.command in LABELS:
|
|
115
|
+
self.cmd_runner.labels(self.repos)
|
|
116
|
+
elif args.command in TAGS:
|
|
117
|
+
self.cmd_runner.tags(self.repos)
|
|
118
|
+
elif args.command in STATUS:
|
|
119
|
+
self.cmd_runner.status(self.repos)
|
|
120
|
+
# Handling subcommands
|
|
121
|
+
else:
|
|
122
|
+
del sys.argv[0]
|
|
123
|
+
if self.command is None:
|
|
124
|
+
if len(sys.argv) == 0:
|
|
125
|
+
self.parser.print_help()
|
|
126
|
+
return
|
|
127
|
+
self.command = ' '.join(sys.argv)
|
|
128
|
+
self._parse_aliases()
|
|
129
|
+
try:
|
|
130
|
+
if self.run_async:
|
|
131
|
+
if self.table:
|
|
132
|
+
asyncio.run(self.cmd_runner.run_async_table_view(self.repos.keys(), self.command))
|
|
133
|
+
else:
|
|
134
|
+
asyncio.run(self.cmd_runner.run_async(self.repos.keys(), self.command))
|
|
135
|
+
else:
|
|
136
|
+
self.cmd_runner.run_ordered(self.repos.keys(), self.command)
|
|
137
|
+
except Exception as exception:
|
|
138
|
+
utils.print_error(f'Invalid command. {exception}', 2)
|
|
139
|
+
|
|
140
|
+
def init(self, args) -> None:
|
|
141
|
+
table = utils.get_table(['Path', 'Status'])
|
|
142
|
+
self.config.data = {}
|
|
143
|
+
index = 0
|
|
144
|
+
directories = [d for d in os.listdir('.') if os.path.isdir(d) and os.path.isdir(os.path.join(d, '.git'))]
|
|
145
|
+
for directory in directories:
|
|
146
|
+
if directory in self.config.paths():
|
|
147
|
+
continue
|
|
148
|
+
self.config.add_label(directory, getattr(args, 'label', ''))
|
|
149
|
+
index += 1
|
|
150
|
+
table.add_row([f'{DIM}{directory}{RESET}', f'{GREEN}{glyphs("added")}{RESET}'])
|
|
151
|
+
if index == 0:
|
|
152
|
+
utils.print_error('No git repositories were found in this directory.', 3)
|
|
153
|
+
return
|
|
154
|
+
self.config.save(utils.CONFIG_FILE_NAME)
|
|
155
|
+
utils.print_table(table)
|
|
156
|
+
|
|
157
|
+
def add(self, args) -> None:
|
|
158
|
+
self.config.add_label(args.path, args.label)
|
|
159
|
+
self.config.save(utils.CONFIG_FILE_NAME)
|
|
160
|
+
|
|
161
|
+
def remove(self, args) -> None:
|
|
162
|
+
if args.path:
|
|
163
|
+
self.config.remove_label(args.path, args.label)
|
|
164
|
+
elif args.label:
|
|
165
|
+
self.config.remove_path(args.label)
|
|
166
|
+
else:
|
|
167
|
+
utils.print_error(f'Invalid input. Please provide a value to remove.', 4)
|
|
168
|
+
self.config.save(utils.CONFIG_FILE_NAME)
|
|
169
|
+
|
|
170
|
+
# Filter out repositories if user provided filters
|
|
171
|
+
def _filter_with_arguments(self) -> None:
|
|
172
|
+
self.repos = self.config.data
|
|
173
|
+
self.table = utils.settings.config['mud'].getboolean('run_table', fallback=True)
|
|
174
|
+
self.run_async = utils.settings.config['mud'].getboolean('run_async', fallback=True)
|
|
175
|
+
|
|
176
|
+
for path, labels in self.config.filter_label('ignore', self.config.data).items():
|
|
177
|
+
del self.repos[path]
|
|
178
|
+
include_labels = []
|
|
179
|
+
exclude_labels = []
|
|
180
|
+
include_branches = []
|
|
181
|
+
exclude_branches = []
|
|
182
|
+
modified = False
|
|
183
|
+
diverged = False
|
|
184
|
+
index = 1
|
|
185
|
+
while index < len(sys.argv):
|
|
186
|
+
arg = sys.argv[index]
|
|
187
|
+
if not arg.startswith('-'):
|
|
188
|
+
break
|
|
189
|
+
if any(arg.startswith(prefix) for prefix in LABEL_PREFIX):
|
|
190
|
+
include_labels.append(arg.split('=', 1)[1])
|
|
191
|
+
elif any(arg.startswith(prefix) for prefix in NOT_LABEL_PREFIX):
|
|
192
|
+
exclude_labels.append(arg.split('=', 1)[1])
|
|
193
|
+
elif any(arg.startswith(prefix) for prefix in BRANCH_PREFIX):
|
|
194
|
+
include_branches.append(arg.split('=', 1)[1])
|
|
195
|
+
elif any(arg.startswith(prefix) for prefix in NOT_BRANCH_PREFIX):
|
|
196
|
+
exclude_branches.append(arg.split('=', 1)[1])
|
|
197
|
+
elif arg in MODIFIED_ATTR:
|
|
198
|
+
modified = True
|
|
199
|
+
elif arg in DIVERGED_ATTR:
|
|
200
|
+
diverged = True
|
|
201
|
+
elif arg in TABLE_ATTR:
|
|
202
|
+
self.table = not self.table
|
|
203
|
+
elif arg in ASYNC_ATTR:
|
|
204
|
+
self.run_async = not self.run_async
|
|
205
|
+
elif any(arg.startswith(prefix) for prefix in COMMAND_ATTR):
|
|
206
|
+
self.command = arg.split('=', 1)[1]
|
|
207
|
+
else:
|
|
208
|
+
index += 1
|
|
209
|
+
continue
|
|
210
|
+
del sys.argv[index]
|
|
211
|
+
directory = os.getcwd()
|
|
212
|
+
to_delete = []
|
|
213
|
+
|
|
214
|
+
for repo, labels in self.repos.items():
|
|
215
|
+
abs_path = os.path.join(directory, repo)
|
|
216
|
+
|
|
217
|
+
if not os.path.isdir(abs_path):
|
|
218
|
+
utils.print_error(f'Invalid path {BOLD}{repo}{RESET}.', 12, False)
|
|
219
|
+
to_delete.append(repo)
|
|
220
|
+
continue
|
|
221
|
+
elif not os.path.isdir(os.path.join(abs_path, '.git')):
|
|
222
|
+
utils.print_error(f'{BOLD}.git{RESET} directory not found at target "{repo}".', 13, False)
|
|
223
|
+
to_delete.append(repo)
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
os.chdir(abs_path)
|
|
227
|
+
delete = False
|
|
228
|
+
|
|
229
|
+
if any(include_labels) and not any(item in include_labels for item in labels):
|
|
230
|
+
delete = True
|
|
231
|
+
if any(exclude_labels) and any(item in exclude_labels for item in labels):
|
|
232
|
+
delete = True
|
|
233
|
+
|
|
234
|
+
if not delete:
|
|
235
|
+
try:
|
|
236
|
+
branch = subprocess.check_output('git rev-parse --abbrev-ref HEAD', shell=True, text=True, stderr=subprocess.DEVNULL).splitlines()[0]
|
|
237
|
+
except subprocess.CalledProcessError:
|
|
238
|
+
branch = 'NA'
|
|
239
|
+
if any(include_branches) and branch not in include_branches:
|
|
240
|
+
delete = True
|
|
241
|
+
if any(exclude_branches) and branch in exclude_branches:
|
|
242
|
+
delete = True
|
|
243
|
+
|
|
244
|
+
if not delete and modified:
|
|
245
|
+
status_output = subprocess.check_output('git status --porcelain', shell=True, stderr=subprocess.DEVNULL)
|
|
246
|
+
if not status_output:
|
|
247
|
+
delete = True
|
|
248
|
+
|
|
249
|
+
if not delete and diverged:
|
|
250
|
+
branch_status = subprocess.check_output('git status --branch --porcelain', shell=True, text=True).splitlines()
|
|
251
|
+
if not any('ahead' in line or 'behind' in line for line in branch_status if line.startswith('##')):
|
|
252
|
+
delete = True
|
|
253
|
+
|
|
254
|
+
if delete:
|
|
255
|
+
to_delete.append(repo)
|
|
256
|
+
|
|
257
|
+
for repo in to_delete:
|
|
258
|
+
del self.repos[repo]
|
|
259
|
+
|
|
260
|
+
os.chdir(directory)
|
|
261
|
+
|
|
262
|
+
def _parse_aliases(self) -> None:
|
|
263
|
+
if utils.settings.alias_settings is None:
|
|
264
|
+
return
|
|
265
|
+
for alias, command in dict(utils.settings.alias_settings).items():
|
|
266
|
+
args = self.command.split(' ')
|
|
267
|
+
if args[0] == alias:
|
|
268
|
+
del args[0]
|
|
269
|
+
self.command = ' '.join(command.split(' ') + args)
|
mud/commands.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Commands
|
|
2
|
+
ADD = ['add', 'a']
|
|
3
|
+
REMOVE = ['remove', 'rm']
|
|
4
|
+
LOG = ['log', 'l']
|
|
5
|
+
INFO = ['info', 'i']
|
|
6
|
+
INIT = ['init']
|
|
7
|
+
TAGS = ['tags', 'tag', 't']
|
|
8
|
+
LABELS = ['labels', 'lb']
|
|
9
|
+
STATUS = ['status', 'st']
|
|
10
|
+
BRANCHES = ['branch', 'branches', 'br']
|
|
11
|
+
REMOTE_BRANCHES = ['remote-branch', 'remote-branches', 'rbr']
|
|
12
|
+
HELP = ['help', '--help', '-h']
|
|
13
|
+
CONFIGURE = ['configure', 'config']
|
|
14
|
+
SET_GLOBAL = ['--set-global']
|
|
15
|
+
|
|
16
|
+
COMMANDS = [ADD, REMOVE, LOG, INFO, INIT, TAGS, LABELS, STATUS, BRANCHES, REMOTE_BRANCHES, HELP, CONFIGURE, SET_GLOBAL]
|
|
17
|
+
|
|
18
|
+
# Filters
|
|
19
|
+
ASYNC_ATTR = '-a', '--async'
|
|
20
|
+
TABLE_ATTR = '-t', '--table'
|
|
21
|
+
COMMAND_ATTR = '-c', '--command'
|
|
22
|
+
MODIFIED_ATTR = '-m', '--modified'
|
|
23
|
+
DIVERGED_ATTR = '-d', '--diverged'
|
|
24
|
+
LABEL_PREFIX = '-l=', '--label='
|
|
25
|
+
NOT_LABEL_PREFIX = '-nl=', '--not-label='
|
|
26
|
+
BRANCH_PREFIX = '-b=', '--branch='
|
|
27
|
+
NOT_BRANCH_PREFIX = '-nb=', '--not-branch='
|
mud/config.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import csv
|
|
4
|
+
|
|
5
|
+
from typing import List, Dict
|
|
6
|
+
|
|
7
|
+
from mud import utils
|
|
8
|
+
from mud.styles import *
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Config:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.data = {}
|
|
14
|
+
|
|
15
|
+
def save(self, file_path: str) -> None:
|
|
16
|
+
def _filter_labels(label: str):
|
|
17
|
+
return bool(re.match(r'^\w+$', label))
|
|
18
|
+
|
|
19
|
+
with open(file_path, 'w', newline='') as tsvfile:
|
|
20
|
+
writer = csv.writer(tsvfile, delimiter='\t')
|
|
21
|
+
|
|
22
|
+
for path, labels in self.data.items():
|
|
23
|
+
valid_labels = [label for label in labels if _filter_labels(label)]
|
|
24
|
+
formatted_labels = ','.join(valid_labels) if valid_labels else ''
|
|
25
|
+
writer.writerow([path, formatted_labels])
|
|
26
|
+
|
|
27
|
+
def find(self) -> None:
|
|
28
|
+
if os.path.exists(utils.CONFIG_FILE_NAME):
|
|
29
|
+
self.load(utils.CONFIG_FILE_NAME)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
directory = os.getcwd()
|
|
33
|
+
current_path = directory
|
|
34
|
+
while os.path.dirname(current_path) != current_path:
|
|
35
|
+
os.chdir(current_path)
|
|
36
|
+
if os.path.exists(utils.CONFIG_FILE_NAME):
|
|
37
|
+
self.load(utils.CONFIG_FILE_NAME)
|
|
38
|
+
return utils.CONFIG_FILE_NAME
|
|
39
|
+
current_path = os.path.dirname(current_path)
|
|
40
|
+
|
|
41
|
+
if utils.settings.mud_settings['config_path'] != '' and os.path.exists(
|
|
42
|
+
utils.settings.mud_settings['config_path']):
|
|
43
|
+
directory = os.path.dirname(utils.settings.mud_settings['config_path'])
|
|
44
|
+
os.chdir(directory)
|
|
45
|
+
os.environ['PWD'] = directory
|
|
46
|
+
self.load(utils.CONFIG_FILE_NAME)
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
utils.print_error(f'{BOLD}.mudconfig{RESET} file was not found. Type `mud init` to create configuration file.', 11)
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
def load(self, file_path: str) -> None:
|
|
53
|
+
self.data = {}
|
|
54
|
+
with open(file_path, 'r') as tsvfile:
|
|
55
|
+
reader = csv.reader(tsvfile, delimiter='\t')
|
|
56
|
+
for row in reader:
|
|
57
|
+
path = row[0]
|
|
58
|
+
|
|
59
|
+
if path.startswith('~'):
|
|
60
|
+
path = os.path.expanduser(path)
|
|
61
|
+
|
|
62
|
+
labels = [label.strip() for label in row[1].split(',') if len(row) > 1 and label.strip()] if len(row) > 1 else []
|
|
63
|
+
self.data[path] = labels
|
|
64
|
+
|
|
65
|
+
def paths(self) -> List[str]:
|
|
66
|
+
return list(self.data.keys())
|
|
67
|
+
|
|
68
|
+
def filter_label(self, label: str, repos: Dict[str, List[str]] = None) -> Dict[str, List[str]]:
|
|
69
|
+
if repos is None:
|
|
70
|
+
repos = self.data
|
|
71
|
+
if label == '':
|
|
72
|
+
return repos
|
|
73
|
+
result = {}
|
|
74
|
+
for path, labels in repos.items():
|
|
75
|
+
if label in labels:
|
|
76
|
+
result[path] = labels
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
def add_label(self, path: str, label: str) -> None:
|
|
80
|
+
if path is None:
|
|
81
|
+
path = label
|
|
82
|
+
label = None
|
|
83
|
+
if not os.path.isdir(path):
|
|
84
|
+
utils.print_error(f'Invalid path {BOLD}{path}{RESET}. Remember that path should be relative.', 14)
|
|
85
|
+
return
|
|
86
|
+
if path not in self.data:
|
|
87
|
+
self.data[path] = []
|
|
88
|
+
if label is not None and label not in self.data[path]:
|
|
89
|
+
self.data[path].append(label)
|
|
90
|
+
|
|
91
|
+
def remove_path(self, path: str) -> None:
|
|
92
|
+
if path in self.data:
|
|
93
|
+
del self.data[path]
|
|
94
|
+
|
|
95
|
+
def remove_label(self, path: str, label: str) -> None:
|
|
96
|
+
if path in self.data and label in self.data[path]:
|
|
97
|
+
self.data[path].remove(label)
|
|
98
|
+
if not self.data[path]:
|
|
99
|
+
del self.data[path]
|