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.

Files changed (32) hide show
  1. skilleter_thingy/borger.py +273 -0
  2. skilleter_thingy/diskspacecheck.py +67 -0
  3. skilleter_thingy/ggit.py +1 -0
  4. skilleter_thingy/ggrep.py +1 -0
  5. skilleter_thingy/git_br.py +7 -0
  6. skilleter_thingy/git_ca.py +8 -0
  7. skilleter_thingy/git_cleanup.py +11 -0
  8. skilleter_thingy/git_co.py +8 -3
  9. skilleter_thingy/git_common.py +12 -4
  10. skilleter_thingy/git_hold.py +9 -0
  11. skilleter_thingy/git_mr.py +11 -0
  12. skilleter_thingy/git_parent.py +23 -18
  13. skilleter_thingy/git_retag.py +10 -0
  14. skilleter_thingy/git_review.py +1 -0
  15. skilleter_thingy/git_update.py +1 -0
  16. skilleter_thingy/git_wt.py +2 -0
  17. skilleter_thingy/gitprompt.py +1 -0
  18. skilleter_thingy/localphotosync.py +201 -0
  19. skilleter_thingy/moviemover.py +133 -0
  20. skilleter_thingy/photodupe.py +135 -0
  21. skilleter_thingy/phototidier.py +248 -0
  22. skilleter_thingy/splitpics.py +99 -0
  23. skilleter_thingy/sysmon.py +435 -0
  24. skilleter_thingy/thingy/git.py +18 -5
  25. skilleter_thingy/thingy/git2.py +20 -7
  26. skilleter_thingy/window_rename.py +92 -0
  27. {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/METADATA +46 -1
  28. {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/RECORD +32 -23
  29. {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/entry_points.txt +9 -0
  30. {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/WHEEL +0 -0
  31. {skilleter_thingy-0.2.0.dist-info → skilleter_thingy-0.2.1.dist-info}/licenses/LICENSE +0 -0
  32. {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
@@ -7,6 +7,7 @@ import os
7
7
  import subprocess
8
8
  import argparse
9
9
 
10
+ # TODO: Update to git2
10
11
  import thingy.git as git
11
12
  import thingy.colour as colour
12
13
 
skilleter_thingy/ggrep.py CHANGED
@@ -14,6 +14,7 @@ import sys
14
14
  import argparse
15
15
 
16
16
  import thingy.colour as colour
17
+ # TODO: Update to git2
17
18
  import thingy.git as git
18
19
 
19
20
  ################################################################################
@@ -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
 
@@ -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:
@@ -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
 
@@ -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
 
@@ -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
- try:
33
- ancestor = git.find_common_ancestor(args.commit1, args.commit2)
34
- except git.GitError as exc:
35
- colour.error(exc, status=exc.status, prefix=True)
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
 
@@ -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
 
@@ -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