skilleter-thingy 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of skilleter-thingy might be problematic. Click here for more details.
- skilleter_thingy/borger.py +273 -0
- skilleter_thingy/diskspacecheck.py +67 -0
- skilleter_thingy/ggit.py +1 -0
- skilleter_thingy/ggrep.py +1 -0
- skilleter_thingy/git_br.py +7 -0
- skilleter_thingy/git_ca.py +8 -0
- skilleter_thingy/git_cleanup.py +11 -0
- skilleter_thingy/git_co.py +8 -3
- skilleter_thingy/git_common.py +12 -4
- skilleter_thingy/git_hold.py +9 -0
- skilleter_thingy/git_mr.py +11 -0
- skilleter_thingy/git_parent.py +23 -18
- skilleter_thingy/git_retag.py +10 -0
- skilleter_thingy/git_review.py +1 -0
- skilleter_thingy/git_update.py +1 -0
- skilleter_thingy/git_wt.py +2 -0
- skilleter_thingy/gitprompt.py +1 -0
- skilleter_thingy/localphotosync.py +201 -0
- skilleter_thingy/moviemover.py +133 -0
- skilleter_thingy/photodupe.py +135 -0
- skilleter_thingy/phototidier.py +248 -0
- skilleter_thingy/splitpics.py +99 -0
- skilleter_thingy/sysmon.py +435 -0
- skilleter_thingy/thingy/git.py +18 -5
- skilleter_thingy/thingy/git2.py +20 -7
- skilleter_thingy/window_rename.py +92 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/METADATA +46 -1
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/RECORD +32 -23
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/entry_points.txt +9 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/WHEEL +0 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Wrapper for the borg backup command
|
|
5
|
+
|
|
6
|
+
Copyright (C) 2018 John Skilleter
|
|
7
|
+
|
|
8
|
+
TODO: Major tidy-up as this is a translation of a Bash script.
|
|
9
|
+
TODO: Merge with the usb-backup script since both do almost the same job
|
|
10
|
+
TODO: Default configuration file should be named for the hostname
|
|
11
|
+
TODO: Move all configuration data into the configuration file
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
################################################################################
|
|
15
|
+
# Imports
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
import os
|
|
19
|
+
import time
|
|
20
|
+
import argparse
|
|
21
|
+
import configparser
|
|
22
|
+
import subprocess
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
################################################################################
|
|
26
|
+
# Variables
|
|
27
|
+
|
|
28
|
+
DEFAULT_CONFIG_FILE = Path('borger.ini')
|
|
29
|
+
|
|
30
|
+
COMMANDS = ('backup', 'mount', 'umount', 'compact', 'info', 'prune', 'check', 'init')
|
|
31
|
+
|
|
32
|
+
# TODO: NOT USED
|
|
33
|
+
PRUNE_OPTIONS = [
|
|
34
|
+
'--keep-within', '7d',
|
|
35
|
+
'--keep-daily', '30',
|
|
36
|
+
'--keep-weekly', '26',
|
|
37
|
+
'--keep-monthly', '24',
|
|
38
|
+
'--keep-yearly', '10',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
################################################################################
|
|
42
|
+
|
|
43
|
+
def run(args, cmd):
|
|
44
|
+
"""Run a subprocess."""
|
|
45
|
+
|
|
46
|
+
if args.debug:
|
|
47
|
+
cmd_str = ' '.join(cmd)
|
|
48
|
+
print(f'Running "{cmd_str}"')
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
return subprocess.run(cmd, check=True)
|
|
52
|
+
except FileNotFoundError:
|
|
53
|
+
print('Borg backup is not installed')
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
|
|
56
|
+
################################################################################
|
|
57
|
+
|
|
58
|
+
def borg_backup(args, exclude_list):
|
|
59
|
+
"""Perform a backup."""
|
|
60
|
+
|
|
61
|
+
create_options = ['--compression', 'auto,lzma']
|
|
62
|
+
|
|
63
|
+
version = time.strftime('%Y-%m-%d-%H:%M:%S')
|
|
64
|
+
|
|
65
|
+
print(f'Creating backup version {version}')
|
|
66
|
+
|
|
67
|
+
if args.verbose:
|
|
68
|
+
create_options += ['--list', '--filter=AMC']
|
|
69
|
+
|
|
70
|
+
if args.dryrun:
|
|
71
|
+
create_options.append('--dry-run')
|
|
72
|
+
else:
|
|
73
|
+
create_options.append('--stats')
|
|
74
|
+
|
|
75
|
+
exclude_opts = []
|
|
76
|
+
|
|
77
|
+
if exclude_list:
|
|
78
|
+
for exclude in exclude_list:
|
|
79
|
+
exclude_opts += ['--exclude', exclude]
|
|
80
|
+
|
|
81
|
+
os.chdir(args.source)
|
|
82
|
+
|
|
83
|
+
run(args,
|
|
84
|
+
['borg'] + args.options + ['create', f'{str(args.destination)}::{version}', str(args.source)] + create_options +
|
|
85
|
+
['--show-rc', '--one-file-system', '--exclude-caches'] + exclude_opts)
|
|
86
|
+
|
|
87
|
+
################################################################################
|
|
88
|
+
|
|
89
|
+
def borg_prune(args):
|
|
90
|
+
"""Prune the repo by limiting the number of backups stored."""
|
|
91
|
+
|
|
92
|
+
print('Pruning old backups')
|
|
93
|
+
|
|
94
|
+
# Keep all backups for at least 7 days, 1 per day for 30 days, 1 per week for 2 years
|
|
95
|
+
# 1 per month for 4 years and 1 per year for 10 years.
|
|
96
|
+
|
|
97
|
+
run(args, ['borg'] + args.options + ['prune', str(args.destination)] + PRUNE_OPTIONS)
|
|
98
|
+
|
|
99
|
+
################################################################################
|
|
100
|
+
|
|
101
|
+
def borg_compact(args):
|
|
102
|
+
"""Compact the repo."""
|
|
103
|
+
|
|
104
|
+
print('Compacting the backup')
|
|
105
|
+
|
|
106
|
+
# Keep all backups for at least 7 days, 1 per day for 30 days, 1 per week for 2 years
|
|
107
|
+
# 1 per month for 4 years and 1 per year for 10 years.
|
|
108
|
+
|
|
109
|
+
run(args, ['borg'] + args.options + ['compact', str(args.destination)])
|
|
110
|
+
|
|
111
|
+
################################################################################
|
|
112
|
+
|
|
113
|
+
def borg_info(args):
|
|
114
|
+
"""Info."""
|
|
115
|
+
|
|
116
|
+
run(args, ['borg'] + args.options + ['info', str(args.destination)])
|
|
117
|
+
|
|
118
|
+
################################################################################
|
|
119
|
+
|
|
120
|
+
def borg_mount(args):
|
|
121
|
+
"""Mount."""
|
|
122
|
+
|
|
123
|
+
print(f'Mounting Borg backups at {args.mount_dir}')
|
|
124
|
+
|
|
125
|
+
mount = Path(args.mount_dir)
|
|
126
|
+
|
|
127
|
+
if not mount.is_dir():
|
|
128
|
+
mount.mkdir()
|
|
129
|
+
|
|
130
|
+
run(args, ['borg'] + args.options + ['mount', str(args.destination), str(mount)])
|
|
131
|
+
|
|
132
|
+
################################################################################
|
|
133
|
+
|
|
134
|
+
def borg_umount(args):
|
|
135
|
+
"""Unmount."""
|
|
136
|
+
|
|
137
|
+
print('Unmounting {args.mount}')
|
|
138
|
+
|
|
139
|
+
run(args, ['borg'] + args.options + ['umount', str(args.mount)])
|
|
140
|
+
|
|
141
|
+
################################################################################
|
|
142
|
+
|
|
143
|
+
def borg_check(args):
|
|
144
|
+
"""Check the status of a backup."""
|
|
145
|
+
|
|
146
|
+
run(args, ['borg'] + args.options + ['check', str(args.destination)])
|
|
147
|
+
|
|
148
|
+
################################################################################
|
|
149
|
+
|
|
150
|
+
def borg_init(args):
|
|
151
|
+
"""Initialise a backup."""
|
|
152
|
+
|
|
153
|
+
run(args, ['borg'] + args.options + ['init', str(args.destination), '--encryption=none'])
|
|
154
|
+
|
|
155
|
+
################################################################################
|
|
156
|
+
|
|
157
|
+
def process_excludes(exclude_data):
|
|
158
|
+
"""Process the include list from the configuration file."""
|
|
159
|
+
|
|
160
|
+
return exclude_data.replace('%', str(Path.cwd())).split('\n')
|
|
161
|
+
|
|
162
|
+
################################################################################
|
|
163
|
+
|
|
164
|
+
def main():
|
|
165
|
+
"""Entry point."""
|
|
166
|
+
|
|
167
|
+
command_list = ', '.join(COMMANDS)
|
|
168
|
+
|
|
169
|
+
parser = argparse.ArgumentParser(description='Wrapper app for Borg backup to make it easier to use')
|
|
170
|
+
parser.add_argument('--dryrun', '--dry-run', '-D', action='store_true', help='Dry-run comands')
|
|
171
|
+
parser.add_argument('--debug', '-d', action='store_true', help='Debug')
|
|
172
|
+
parser.add_argument('--verbose', '-v', action='store_true', help='Verbosity to the maximum')
|
|
173
|
+
parser.add_argument('--config', '-c', default=None, help='Specify the configuration file')
|
|
174
|
+
parser.add_argument('commands', nargs='+', help=f'One or more commands ({command_list})')
|
|
175
|
+
args = parser.parse_args()
|
|
176
|
+
|
|
177
|
+
# If no config file specified then look in all the usual places
|
|
178
|
+
|
|
179
|
+
if args.config:
|
|
180
|
+
args.config = Path(args.config)
|
|
181
|
+
elif DEFAULT_CONFIG_FILE.is_file():
|
|
182
|
+
args.config = DEFAULT_CONFIG_FILE
|
|
183
|
+
else:
|
|
184
|
+
args.config = Path.home() / DEFAULT_CONFIG_FILE
|
|
185
|
+
|
|
186
|
+
if not args.config.is_file():
|
|
187
|
+
args.config = Path(sys.argv[0]).parent / DEFAULT_CONFIG_FILE
|
|
188
|
+
|
|
189
|
+
# Check that the configuration file exists
|
|
190
|
+
|
|
191
|
+
if not args.config.is_file():
|
|
192
|
+
print(f'Configuration file "{args.config}" not found')
|
|
193
|
+
sys.exit(1)
|
|
194
|
+
|
|
195
|
+
# Default options
|
|
196
|
+
|
|
197
|
+
args.options = []
|
|
198
|
+
|
|
199
|
+
# Read the configuration file
|
|
200
|
+
|
|
201
|
+
config = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
|
|
202
|
+
config.read(args.config)
|
|
203
|
+
|
|
204
|
+
if 'borger' not in config:
|
|
205
|
+
print('Invalid configuration file "args.config"')
|
|
206
|
+
sys.exit(1)
|
|
207
|
+
|
|
208
|
+
exclude = process_excludes(config['borger']['exclude']) if 'exclude' in config['borger'] else []
|
|
209
|
+
|
|
210
|
+
if 'prune' in config['borger']:
|
|
211
|
+
# TODO: Stuff
|
|
212
|
+
print('Parser for the prune option is not implemented yet')
|
|
213
|
+
sys.exit(1)
|
|
214
|
+
|
|
215
|
+
if 'destination' in config['borger']:
|
|
216
|
+
args.destination = config['borger']['destination']
|
|
217
|
+
else:
|
|
218
|
+
print('Destination directory not specified')
|
|
219
|
+
sys.exit(1)
|
|
220
|
+
|
|
221
|
+
if 'source' in config['borger']:
|
|
222
|
+
args.source = Path(config['borger']['source'])
|
|
223
|
+
else:
|
|
224
|
+
print('Source directory not specified')
|
|
225
|
+
sys.exit(1)
|
|
226
|
+
|
|
227
|
+
# Initialise if necessary
|
|
228
|
+
|
|
229
|
+
if args.debug:
|
|
230
|
+
args.options.append('--verbose')
|
|
231
|
+
|
|
232
|
+
if args.verbose:
|
|
233
|
+
args.options.append('--progress')
|
|
234
|
+
|
|
235
|
+
# Decide what to do
|
|
236
|
+
|
|
237
|
+
for command in args.commands:
|
|
238
|
+
if command == 'backup':
|
|
239
|
+
borg_backup(args, exclude)
|
|
240
|
+
elif command == 'mount':
|
|
241
|
+
borg_mount(args)
|
|
242
|
+
elif command == 'umount':
|
|
243
|
+
borg_umount(args)
|
|
244
|
+
elif command == 'info':
|
|
245
|
+
borg_info(args)
|
|
246
|
+
elif command == 'prune':
|
|
247
|
+
borg_prune(args)
|
|
248
|
+
elif command == 'check':
|
|
249
|
+
borg_check(args)
|
|
250
|
+
elif command == 'init':
|
|
251
|
+
borg_init(args)
|
|
252
|
+
elif command == 'compact':
|
|
253
|
+
borg_compact(args)
|
|
254
|
+
else:
|
|
255
|
+
print(f'Unrecognized command: {command}')
|
|
256
|
+
sys.exit(2)
|
|
257
|
+
|
|
258
|
+
################################################################################
|
|
259
|
+
|
|
260
|
+
def borger():
|
|
261
|
+
"""Entry point"""
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
main()
|
|
265
|
+
except KeyboardInterrupt:
|
|
266
|
+
sys.exit(1)
|
|
267
|
+
except BrokenPipeError:
|
|
268
|
+
sys.exit(2)
|
|
269
|
+
|
|
270
|
+
################################################################################
|
|
271
|
+
|
|
272
|
+
if __name__ == '__main__':
|
|
273
|
+
borger()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
################################################################################
|
|
4
|
+
""" Check how much free space is available on all filesystems, ignoring
|
|
5
|
+
read-only filesystems, /dev and tmpfs.
|
|
6
|
+
|
|
7
|
+
Issue a warning if any are above 90% used.
|
|
8
|
+
"""
|
|
9
|
+
################################################################################
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
import argparse
|
|
13
|
+
import psutil
|
|
14
|
+
|
|
15
|
+
################################################################################
|
|
16
|
+
|
|
17
|
+
WARNING_LEVEL = 15
|
|
18
|
+
|
|
19
|
+
################################################################################
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
""" Do everything """
|
|
23
|
+
|
|
24
|
+
parser = argparse.ArgumentParser(description='Check for filesystems that are running low on space')
|
|
25
|
+
parser.add_argument('--level', action='store', type=int, default=WARNING_LEVEL,
|
|
26
|
+
help='Warning if less than this amount of space is available on any writeable, mounted filesystem (default=%d)' % WARNING_LEVEL)
|
|
27
|
+
args = parser.parse_args()
|
|
28
|
+
|
|
29
|
+
if args.level < 0 or args.level > 100:
|
|
30
|
+
print('Invalid value: %d' % args.level)
|
|
31
|
+
sys.exit(3)
|
|
32
|
+
|
|
33
|
+
disks = psutil.disk_partitions()
|
|
34
|
+
devices = []
|
|
35
|
+
warning = []
|
|
36
|
+
|
|
37
|
+
for disk in disks:
|
|
38
|
+
if 'ro' not in disk.opts.split(',') and disk.device not in devices:
|
|
39
|
+
devices.append(disk.device)
|
|
40
|
+
usage = psutil.disk_usage(disk.mountpoint)
|
|
41
|
+
|
|
42
|
+
disk_space = 100 - usage.percent
|
|
43
|
+
|
|
44
|
+
if disk_space < args.level:
|
|
45
|
+
warning.append('%s has only %2.1f%% space available' % (disk.mountpoint, disk_space))
|
|
46
|
+
|
|
47
|
+
if warning:
|
|
48
|
+
print('Filesystems with less than %d%% available space:' % args.level)
|
|
49
|
+
print('\n'.join(warning))
|
|
50
|
+
|
|
51
|
+
################################################################################
|
|
52
|
+
|
|
53
|
+
def diskspacecheck():
|
|
54
|
+
"""Entry point"""
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
main()
|
|
58
|
+
|
|
59
|
+
except KeyboardInterrupt:
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
except BrokenPipeError:
|
|
62
|
+
sys.exit(2)
|
|
63
|
+
|
|
64
|
+
################################################################################
|
|
65
|
+
|
|
66
|
+
if __name__ == '__main__':
|
|
67
|
+
diskspacecheck()
|
skilleter_thingy/ggit.py
CHANGED
skilleter_thingy/ggrep.py
CHANGED
skilleter_thingy/git_br.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"""
|
|
14
14
|
################################################################################
|
|
15
15
|
|
|
16
|
+
import os
|
|
16
17
|
import sys
|
|
17
18
|
import argparse
|
|
18
19
|
import fnmatch
|
|
@@ -21,6 +22,7 @@ import datetime
|
|
|
21
22
|
from dateutil.parser import parse
|
|
22
23
|
from dateutil.relativedelta import relativedelta
|
|
23
24
|
|
|
25
|
+
# TODO: Update to git2
|
|
24
26
|
import thingy.git as git
|
|
25
27
|
import thingy.colour as colour
|
|
26
28
|
|
|
@@ -34,10 +36,15 @@ def parse_command_line():
|
|
|
34
36
|
parser.add_argument('--all', '-a', action='store_true', help='List all branches, including remotes')
|
|
35
37
|
parser.add_argument('--delete', '-d', action='store_true',
|
|
36
38
|
help='Delete the specified branch(es), even if it is the current one (list of branches to delete must be supplied as parameters)')
|
|
39
|
+
parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
|
|
40
|
+
help='Run the command in the specified directory')
|
|
37
41
|
parser.add_argument('branches', nargs='*', help='Filter the list of branches according to one or more patterns')
|
|
38
42
|
|
|
39
43
|
args = parser.parse_args()
|
|
40
44
|
|
|
45
|
+
if args.path:
|
|
46
|
+
os.chdir(args.path[0])
|
|
47
|
+
|
|
41
48
|
if args.delete and not args.branches:
|
|
42
49
|
colour.error('You must specify the branches to delete', prefix=True)
|
|
43
50
|
|
skilleter_thingy/git_ca.py
CHANGED
|
@@ -18,6 +18,7 @@ import sys
|
|
|
18
18
|
import logging
|
|
19
19
|
|
|
20
20
|
import thingy.colour as colour
|
|
21
|
+
# TODO: Update to git2
|
|
21
22
|
import thingy.git as git
|
|
22
23
|
|
|
23
24
|
################################################################################
|
|
@@ -43,6 +44,8 @@ def main():
|
|
|
43
44
|
parser.add_argument('--patch', '-p', action='store_true', help='Use the interactive patch selection interface to chose which changes to commit.')
|
|
44
45
|
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose mode')
|
|
45
46
|
parser.add_argument('--dry-run', '-D', action='store_true', help='Dry-run')
|
|
47
|
+
parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
|
|
48
|
+
help='Run the command in the specified directory')
|
|
46
49
|
|
|
47
50
|
parser.add_argument('files', nargs='*', help='List of files to add to the commit')
|
|
48
51
|
|
|
@@ -54,6 +57,11 @@ def main():
|
|
|
54
57
|
logging.basicConfig(level=logging.INFO)
|
|
55
58
|
logging.info('Debug logging enabled')
|
|
56
59
|
|
|
60
|
+
# Change directory, if specified
|
|
61
|
+
|
|
62
|
+
if args.path:
|
|
63
|
+
os.chdir(args.path[0])
|
|
64
|
+
|
|
57
65
|
# 'Add' implies 'all'
|
|
58
66
|
|
|
59
67
|
if args.everything:
|
skilleter_thingy/git_cleanup.py
CHANGED
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
"""
|
|
10
10
|
################################################################################
|
|
11
11
|
|
|
12
|
+
import os
|
|
12
13
|
import sys
|
|
13
14
|
import argparse
|
|
14
15
|
import logging
|
|
15
16
|
|
|
17
|
+
# TODO: Update to git2
|
|
16
18
|
import thingy.git as git
|
|
17
19
|
import thingy.colour as colour
|
|
18
20
|
|
|
@@ -38,6 +40,8 @@ def parse_command_line():
|
|
|
38
40
|
parser.add_argument('--unmerged', '-u', action='store_true', dest='list_unmerged', help='List branches that have NOT been merged')
|
|
39
41
|
parser.add_argument('--yes', '-y', action='store_true', dest='force', help='Assume "yes" in response to any prompts (e.g. to delete branches)')
|
|
40
42
|
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
|
43
|
+
parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
|
|
44
|
+
help='Run the command in the specified directory')
|
|
41
45
|
|
|
42
46
|
parser.add_argument('branches', nargs='*', help='List of branches to check (default is all branches)')
|
|
43
47
|
|
|
@@ -94,6 +98,11 @@ def main():
|
|
|
94
98
|
if args.debug:
|
|
95
99
|
logging.basicConfig(level=logging.INFO)
|
|
96
100
|
|
|
101
|
+
# Change directory, if specified
|
|
102
|
+
|
|
103
|
+
if args.path:
|
|
104
|
+
os.chdir(args.path[0])
|
|
105
|
+
|
|
97
106
|
# Get the list of all local branches
|
|
98
107
|
|
|
99
108
|
try:
|
|
@@ -280,6 +289,8 @@ def git_cleanup():
|
|
|
280
289
|
sys.exit(1)
|
|
281
290
|
except BrokenPipeError:
|
|
282
291
|
sys.exit(2)
|
|
292
|
+
except git.GitError as exc:
|
|
293
|
+
colour.error(exc.msg, status=exc.status, prefix=True)
|
|
283
294
|
|
|
284
295
|
################################################################################
|
|
285
296
|
|
skilleter_thingy/git_co.py
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"""
|
|
21
21
|
################################################################################
|
|
22
22
|
|
|
23
|
+
import os
|
|
23
24
|
import logging
|
|
24
25
|
import sys
|
|
25
26
|
import argparse
|
|
@@ -68,6 +69,8 @@ def parse_arguments():
|
|
|
68
69
|
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
|
69
70
|
parser.add_argument('branchname', nargs=1, type=str,
|
|
70
71
|
help='The branch name (or a partial name that matches uniquely against a local branch, remote branch, commit ID or tag)')
|
|
72
|
+
parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
|
|
73
|
+
help='Run the command in the specified directory')
|
|
71
74
|
|
|
72
75
|
args = parser.parse_args()
|
|
73
76
|
|
|
@@ -76,6 +79,9 @@ def parse_arguments():
|
|
|
76
79
|
if args.debug:
|
|
77
80
|
logging.basicConfig(level=logging.INFO)
|
|
78
81
|
|
|
82
|
+
if args.path:
|
|
83
|
+
os.chdir(args.path[0])
|
|
84
|
+
|
|
79
85
|
return args
|
|
80
86
|
|
|
81
87
|
################################################################################
|
|
@@ -198,9 +204,6 @@ def main():
|
|
|
198
204
|
else:
|
|
199
205
|
checkout_matching_branch(args, args.branchname[0])
|
|
200
206
|
|
|
201
|
-
except git.GitError as exc:
|
|
202
|
-
colour.error(exc.msg, exc.status)
|
|
203
|
-
|
|
204
207
|
################################################################################
|
|
205
208
|
|
|
206
209
|
def git_co():
|
|
@@ -213,6 +216,8 @@ def git_co():
|
|
|
213
216
|
sys.exit(1)
|
|
214
217
|
except BrokenPipeError:
|
|
215
218
|
sys.exit(2)
|
|
219
|
+
except git.GitError as exc:
|
|
220
|
+
colour.error(exc.msg, status=exc.status, prefix=True)
|
|
216
221
|
|
|
217
222
|
################################################################################
|
|
218
223
|
|
skilleter_thingy/git_common.py
CHANGED
|
@@ -6,10 +6,12 @@ Report the oldest commit in common in the history of two commits
|
|
|
6
6
|
|
|
7
7
|
################################################################################
|
|
8
8
|
|
|
9
|
+
import os
|
|
9
10
|
import sys
|
|
10
11
|
import argparse
|
|
11
12
|
|
|
12
13
|
import thingy.colour as colour
|
|
14
|
+
# TODO: Update to git2
|
|
13
15
|
import thingy.git as git
|
|
14
16
|
|
|
15
17
|
################################################################################
|
|
@@ -21,6 +23,8 @@ def main():
|
|
|
21
23
|
|
|
22
24
|
parser.add_argument('--short', '-s', action='store_true', help='Just output the ancestor commit ID')
|
|
23
25
|
parser.add_argument('--long', '-l', action='store_true', help='Output the log entry for the commit')
|
|
26
|
+
parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
|
|
27
|
+
help='Run the command in the specified directory')
|
|
24
28
|
parser.add_argument('commit1', nargs='?', default='HEAD', help='First commit (default=HEAD)')
|
|
25
29
|
parser.add_argument('commit2', nargs='?', default='master', help='Second commit (default=master)')
|
|
26
30
|
|
|
@@ -29,10 +33,12 @@ def main():
|
|
|
29
33
|
if args.long and args.short:
|
|
30
34
|
colour.error('The [BLUE:--long] and [BLUE:--short] options cannot be used together', prefix=True)
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
# Change directory, if specified
|
|
37
|
+
|
|
38
|
+
if args.path:
|
|
39
|
+
os.chdir(args.path[0])
|
|
40
|
+
|
|
41
|
+
ancestor = git.find_common_ancestor(args.commit1, args.commit2)
|
|
36
42
|
|
|
37
43
|
if args.short:
|
|
38
44
|
print(ancestor)
|
|
@@ -54,6 +60,8 @@ def git_common():
|
|
|
54
60
|
sys.exit(1)
|
|
55
61
|
except BrokenPipeError:
|
|
56
62
|
sys.exit(2)
|
|
63
|
+
except git.GitError as exc:
|
|
64
|
+
colour.error(exc.msg, status=exc.status, prefix=True)
|
|
57
65
|
|
|
58
66
|
################################################################################
|
|
59
67
|
|
skilleter_thingy/git_hold.py
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
"""Archive one or more branches by tagging the branch then deleting it
|
|
3
3
|
The branch tag is 'archive/BRANCH_NAME'"""
|
|
4
4
|
|
|
5
|
+
import os
|
|
5
6
|
import sys
|
|
6
7
|
import argparse
|
|
7
8
|
import fnmatch
|
|
8
9
|
|
|
9
10
|
import thingy.colour as colour
|
|
11
|
+
# TODO: Update to git2
|
|
10
12
|
import thingy.git as git
|
|
11
13
|
|
|
12
14
|
################################################################################
|
|
@@ -118,6 +120,8 @@ def main():
|
|
|
118
120
|
parser = argparse.ArgumentParser(description='Archive, list or recover one or more Git branches')
|
|
119
121
|
parser.add_argument('--list', '-l', action='store_true', help='List archived branches')
|
|
120
122
|
parser.add_argument('--restore', '-r', action='store_true', help='Restore archived branches')
|
|
123
|
+
parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
|
|
124
|
+
help='Run the command in the specified directory')
|
|
121
125
|
parser.add_argument('branches', nargs='*', help='Branches')
|
|
122
126
|
|
|
123
127
|
args = parser.parse_args()
|
|
@@ -128,6 +132,9 @@ def main():
|
|
|
128
132
|
if not args.branches and not args.list:
|
|
129
133
|
colour.error('No branches specified', prefix=True)
|
|
130
134
|
|
|
135
|
+
if args.path:
|
|
136
|
+
os.chdir(args.path[0])
|
|
137
|
+
|
|
131
138
|
if args.list:
|
|
132
139
|
list_archive_branches(args.branches)
|
|
133
140
|
elif args.restore:
|
|
@@ -147,6 +154,8 @@ def git_hold():
|
|
|
147
154
|
sys.exit(1)
|
|
148
155
|
except BrokenPipeError:
|
|
149
156
|
sys.exit(2)
|
|
157
|
+
except git.GitError as exc:
|
|
158
|
+
colour.error(exc.msg, status=exc.status, prefix=True)
|
|
150
159
|
|
|
151
160
|
################################################################################
|
|
152
161
|
|
skilleter_thingy/git_mr.py
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
""" Push to Gitlab and create a merge request at the same time """
|
|
5
5
|
################################################################################
|
|
6
6
|
|
|
7
|
+
import os
|
|
7
8
|
import logging
|
|
8
9
|
import sys
|
|
9
10
|
import argparse
|
|
10
11
|
|
|
12
|
+
# TODO: Update to git2
|
|
11
13
|
import thingy.git as git
|
|
12
14
|
import thingy.colour as colour
|
|
13
15
|
|
|
@@ -27,6 +29,8 @@ def parse_arguments():
|
|
|
27
29
|
parser.add_argument('--parent', '-p', action='store', help='Override the default parent and specify the branch to merge onto')
|
|
28
30
|
parser.add_argument('--reviewer', '-r', action='store', help='Specify the name of the reviewer for the merge request')
|
|
29
31
|
parser.add_argument('--keep', '-k', action='store_true', help='Keep the source branch after the merge (default is to delete it).')
|
|
32
|
+
parser.add_argument('--path', '-C', nargs=1, type=str, default=None,
|
|
33
|
+
help='Run the command in the specified directory')
|
|
30
34
|
|
|
31
35
|
args = parser.parse_args()
|
|
32
36
|
|
|
@@ -35,6 +39,11 @@ def parse_arguments():
|
|
|
35
39
|
if args.debug:
|
|
36
40
|
logging.basicConfig(level=logging.INFO)
|
|
37
41
|
|
|
42
|
+
# Change directory, if specified
|
|
43
|
+
|
|
44
|
+
if args.path:
|
|
45
|
+
os.chdir(args.path[0])
|
|
46
|
+
|
|
38
47
|
return args
|
|
39
48
|
|
|
40
49
|
################################################################################
|
|
@@ -86,6 +95,8 @@ def git_mr():
|
|
|
86
95
|
sys.exit(1)
|
|
87
96
|
except BrokenPipeError:
|
|
88
97
|
sys.exit(2)
|
|
98
|
+
except git.GitError as exc:
|
|
99
|
+
colour.error(exc.msg, status=exc.status, prefix=True)
|
|
89
100
|
|
|
90
101
|
################################################################################
|
|
91
102
|
|