skilleter-thingy 0.2.6__py3-none-any.whl → 0.2.8__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/ggit.py +0 -1
- skilleter_thingy/ggrep.py +0 -1
- skilleter_thingy/git_br.py +0 -1
- skilleter_thingy/git_ca.py +0 -1
- skilleter_thingy/git_cleanup.py +1 -2
- skilleter_thingy/git_common.py +0 -1
- skilleter_thingy/git_hold.py +0 -1
- skilleter_thingy/git_mr.py +0 -1
- skilleter_thingy/git_parent.py +0 -1
- skilleter_thingy/git_retag.py +1 -1
- skilleter_thingy/git_review.py +0 -1
- skilleter_thingy/git_update.py +0 -1
- skilleter_thingy/git_wt.py +1 -1
- skilleter_thingy/gitcmp_helper.py +1 -1
- skilleter_thingy/gitprompt.py +0 -1
- skilleter_thingy/multigit.py +26 -35
- skilleter_thingy/rpylint.py +1 -2
- skilleter_thingy/tfparse.py +1 -1
- skilleter_thingy/thingy/docker.py +7 -5
- skilleter_thingy/thingy/files.py +2 -2
- skilleter_thingy/thingy/git.py +259 -187
- skilleter_thingy/thingy/process.py +20 -99
- skilleter_thingy/thingy/run.py +43 -0
- skilleter_thingy/thingy/venv_template.py +1 -1
- skilleter_thingy/trimpath.py +1 -1
- {skilleter_thingy-0.2.6.dist-info → skilleter_thingy-0.2.8.dist-info}/METADATA +1 -1
- skilleter_thingy-0.2.8.dist-info/RECORD +59 -0
- skilleter_thingy/thingy/git2.py +0 -1405
- skilleter_thingy-0.2.6.dist-info/RECORD +0 -60
- {skilleter_thingy-0.2.6.dist-info → skilleter_thingy-0.2.8.dist-info}/WHEEL +0 -0
- {skilleter_thingy-0.2.6.dist-info → skilleter_thingy-0.2.8.dist-info}/entry_points.txt +0 -0
- {skilleter_thingy-0.2.6.dist-info → skilleter_thingy-0.2.8.dist-info}/licenses/LICENSE +0 -0
- {skilleter_thingy-0.2.6.dist-info → skilleter_thingy-0.2.8.dist-info}/top_level.txt +0 -0
skilleter_thingy/thingy/git.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#! /usr/bin/env python3
|
|
2
2
|
|
|
3
3
|
################################################################################
|
|
4
|
-
""" Git module
|
|
4
|
+
""" Git module V2 - now implemented as a wrapper around pygit2 (work in progress)
|
|
5
5
|
|
|
6
|
-
Copyright (C)
|
|
6
|
+
Copyright (C) 2023 John Skilleter
|
|
7
7
|
|
|
8
8
|
Licence: GPL v3 or later
|
|
9
9
|
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* Functions will raise exceptions on error. If the underlying git command
|
|
16
16
|
returns an error, a git.GitError() exception is raised.
|
|
17
17
|
|
|
18
|
-
* TODO: Cache list of branches when git.branches/isbranch called
|
|
19
|
-
* TODO:
|
|
18
|
+
* TODO: [ ] Cache list of branches when git.branches/isbranch called
|
|
19
|
+
* TODO: [ ] API change - git_run_status should raise an exception on failure and just return the git output
|
|
20
20
|
"""
|
|
21
21
|
################################################################################
|
|
22
22
|
|
|
@@ -28,6 +28,8 @@ import logging
|
|
|
28
28
|
import fnmatch
|
|
29
29
|
import subprocess
|
|
30
30
|
|
|
31
|
+
import pygit2
|
|
32
|
+
|
|
31
33
|
import thingy.run as run
|
|
32
34
|
import thingy.gitlab as gitlab
|
|
33
35
|
|
|
@@ -36,6 +38,11 @@ import thingy.gitlab as gitlab
|
|
|
36
38
|
|
|
37
39
|
(WORKTREE, LOCAL, GLOBAL, SYSTEM) = list(range(4))
|
|
38
40
|
|
|
41
|
+
# Options always passed to Git (disable autocorrect to stop it running the wrong command
|
|
42
|
+
# if an invalid command name has been specified).
|
|
43
|
+
|
|
44
|
+
STANDARD_GIT_OPTIONS = ['-c', 'help.autoCorrect=never']
|
|
45
|
+
|
|
39
46
|
# Default default branches (can be overridden in .gitconfig via skilleter-thingy.defaultBranches
|
|
40
47
|
|
|
41
48
|
DEFAULT_DEFAULT_BRANCHES = 'develop,main,master'
|
|
@@ -43,61 +50,84 @@ DEFAULT_DEFAULT_BRANCHES = 'develop,main,master'
|
|
|
43
50
|
################################################################################
|
|
44
51
|
|
|
45
52
|
class GitError(run.RunError):
|
|
46
|
-
"""
|
|
53
|
+
"""Run exception."""
|
|
47
54
|
|
|
48
|
-
def __init__(self, msg,
|
|
49
|
-
super().__init__(msg,
|
|
55
|
+
def __init__(self, msg, exit_status=1):
|
|
56
|
+
super().__init__(msg, exit_status)
|
|
50
57
|
|
|
51
58
|
################################################################################
|
|
52
59
|
|
|
53
|
-
def git(cmd, stdout=None, stderr=None):
|
|
54
|
-
""" Wrapper for
|
|
60
|
+
def git(cmd, stdout=None, stderr=None, path=None):
|
|
61
|
+
""" Wrapper for run.run that raises a GitError instead of RunError
|
|
55
62
|
so that Git module users do not to include the run module just
|
|
56
63
|
to get the exception.
|
|
57
64
|
Optionally redirect stdout and stderr as specified. """
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
git_cmd = ['git'] + STANDARD_GIT_OPTIONS
|
|
67
|
+
|
|
68
|
+
if path:
|
|
69
|
+
git_cmd += ['-C', path]
|
|
70
|
+
|
|
71
|
+
git_cmd += cmd if isinstance(cmd, list) else [cmd]
|
|
72
|
+
|
|
73
|
+
logging.debug('Running %s', ' '.join(git_cmd))
|
|
74
|
+
|
|
75
|
+
sys.stdout.flush()
|
|
76
|
+
sys.stderr.flush()
|
|
60
77
|
|
|
61
78
|
try:
|
|
62
|
-
return run.run(
|
|
79
|
+
return run.run(git_cmd, stdout=stdout, stderr=stderr)
|
|
63
80
|
except run.RunError as exc:
|
|
64
|
-
raise GitError(exc.msg, exc.status)
|
|
81
|
+
raise GitError(exc.msg, exc.status) from exc
|
|
65
82
|
except FileNotFoundError as exc:
|
|
66
83
|
raise GitError(exc, 1)
|
|
67
84
|
|
|
68
85
|
################################################################################
|
|
69
86
|
|
|
70
|
-
def git_run_status(cmd, stdout=None, stderr=None):
|
|
87
|
+
def git_run_status(cmd, stdout=None, stderr=None, path=None, redirect=True):
|
|
71
88
|
""" Wrapper for run.run that returns the output and status, and
|
|
72
89
|
does not raise an exception on error.
|
|
73
90
|
Optionally redirect stdout and stderr as specified. """
|
|
74
91
|
|
|
75
92
|
logging.debug('Running git %s', ' '.join(cmd))
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
94
|
+
git_cmd = ['git'] + STANDARD_GIT_OPTIONS
|
|
95
|
+
|
|
96
|
+
if path:
|
|
97
|
+
git_cmd += ['-C', path]
|
|
98
|
+
|
|
99
|
+
git_cmd += cmd if isinstance(cmd, list) else [cmd]
|
|
100
|
+
|
|
101
|
+
sys.stdout.flush()
|
|
102
|
+
sys.stderr.flush()
|
|
103
|
+
|
|
104
|
+
if redirect:
|
|
105
|
+
result = subprocess.run(git_cmd,
|
|
106
|
+
stdout=stdout or subprocess.PIPE,
|
|
107
|
+
stderr=stderr or subprocess.PIPE,
|
|
108
|
+
text=True, check=False,
|
|
109
|
+
errors='ignore',
|
|
110
|
+
universal_newlines=True)
|
|
111
|
+
else:
|
|
112
|
+
result = subprocess.run(git_cmd, check=False)
|
|
83
113
|
|
|
84
114
|
return (result.stdout or result.stderr), result.returncode
|
|
85
115
|
|
|
86
116
|
################################################################################
|
|
87
117
|
|
|
88
|
-
def clone(reponame,
|
|
118
|
+
def clone(reponame, working_tree=None):
|
|
89
119
|
""" Clone a repo """
|
|
90
120
|
|
|
91
121
|
cmd = ['clone', reponame]
|
|
92
122
|
|
|
93
|
-
if
|
|
94
|
-
cmd.append(
|
|
123
|
+
if working_tree:
|
|
124
|
+
cmd.append(working_tree)
|
|
95
125
|
|
|
96
126
|
return git(cmd)
|
|
97
127
|
|
|
98
128
|
################################################################################
|
|
99
129
|
|
|
100
|
-
def init(reponame, bare=False):
|
|
130
|
+
def init(reponame, bare=False, path=None):
|
|
101
131
|
""" Initialise a new working tree """
|
|
102
132
|
|
|
103
133
|
cmd = ['init']
|
|
@@ -107,11 +137,11 @@ def init(reponame, bare=False):
|
|
|
107
137
|
|
|
108
138
|
cmd.append(reponame)
|
|
109
139
|
|
|
110
|
-
return git(cmd)
|
|
140
|
+
return git(cmd, path=path)
|
|
111
141
|
|
|
112
142
|
################################################################################
|
|
113
143
|
|
|
114
|
-
def iscommit(commit, remote=False, remote_only=False):
|
|
144
|
+
def iscommit(commit, remote=False, remote_only=False, path=None):
|
|
115
145
|
""" Return True if "commit" is a valid SHA1, branch or tag
|
|
116
146
|
If remote==True then if there are no direct matches it will also
|
|
117
147
|
check for a matching remote branch
|
|
@@ -122,7 +152,7 @@ def iscommit(commit, remote=False, remote_only=False):
|
|
|
122
152
|
if not remote_only:
|
|
123
153
|
cmd = ['cat-file', '-t', commit]
|
|
124
154
|
try:
|
|
125
|
-
result = git(cmd)[0]
|
|
155
|
+
result = git(cmd, path=path)[0]
|
|
126
156
|
|
|
127
157
|
return result in ('commit', 'tag')
|
|
128
158
|
except GitError:
|
|
@@ -131,7 +161,7 @@ def iscommit(commit, remote=False, remote_only=False):
|
|
|
131
161
|
# Optionally look for matching remote branch
|
|
132
162
|
|
|
133
163
|
if remote or remote_only:
|
|
134
|
-
for branch in branches(all=True):
|
|
164
|
+
for branch in branches(all=True, path=path):
|
|
135
165
|
if branch.startswith('remotes/'):
|
|
136
166
|
localbranch = '/'.join(branch.split('/')[2:])
|
|
137
167
|
|
|
@@ -142,48 +172,59 @@ def iscommit(commit, remote=False, remote_only=False):
|
|
|
142
172
|
|
|
143
173
|
################################################################################
|
|
144
174
|
|
|
145
|
-
def branch(branchname='HEAD'):
|
|
175
|
+
def branch(branchname='HEAD', path=None):
|
|
146
176
|
""" Return the name of the current git branch or None"""
|
|
147
177
|
|
|
148
178
|
try:
|
|
149
|
-
return git(['symbolic-ref', '--short', '-q', branchname])[0]
|
|
179
|
+
return git(['symbolic-ref', '--short', '-q', branchname], path=path)[0]
|
|
150
180
|
except GitError:
|
|
151
181
|
return None
|
|
152
182
|
|
|
153
183
|
################################################################################
|
|
154
184
|
|
|
155
|
-
def tag():
|
|
185
|
+
def tag(path=None):
|
|
156
186
|
""" If the current commit is tagged, return the tag(s) or None """
|
|
157
187
|
|
|
158
188
|
try:
|
|
159
|
-
return git(['describe', '--tags', '--exact-match'])[0]
|
|
189
|
+
return git(['describe', '--tags', '--exact-match'], path=path)[0]
|
|
160
190
|
except GitError:
|
|
161
191
|
return None
|
|
162
192
|
|
|
163
193
|
################################################################################
|
|
164
194
|
|
|
165
|
-
def tags():
|
|
195
|
+
def tags(path=None):
|
|
166
196
|
""" Return the list of tags in the current repo """
|
|
167
197
|
|
|
168
|
-
return git(['tag'])
|
|
198
|
+
return git(['tag', '--list'], path=path)
|
|
169
199
|
|
|
170
200
|
################################################################################
|
|
171
201
|
|
|
172
|
-
def
|
|
173
|
-
"""
|
|
202
|
+
def tag_delete(tag, push=False, path=None):
|
|
203
|
+
"""Delete a tag, optionally pushing the deletion"""
|
|
204
|
+
|
|
205
|
+
git(['tag', '-d', tag], path=path)
|
|
174
206
|
|
|
175
|
-
|
|
207
|
+
if push:
|
|
208
|
+
git(['push', 'origin', '--delete', tag], path=path)
|
|
176
209
|
|
|
177
210
|
################################################################################
|
|
178
211
|
|
|
179
|
-
def
|
|
180
|
-
"""
|
|
212
|
+
def tag_apply(tag, commit=None, push=False, path=None):
|
|
213
|
+
"""Apply a tag, optionally pushing it"""
|
|
214
|
+
|
|
215
|
+
cmd = ['tag', tag]
|
|
216
|
+
|
|
217
|
+
if commit:
|
|
218
|
+
cmd.append(commit)
|
|
219
|
+
|
|
220
|
+
git(cmd, path=path)
|
|
181
221
|
|
|
182
|
-
|
|
222
|
+
if push:
|
|
223
|
+
git(['push', 'origin', tag], path=path)
|
|
183
224
|
|
|
184
225
|
################################################################################
|
|
185
226
|
|
|
186
|
-
def current_commit(short=False):
|
|
227
|
+
def current_commit(short=False, path=None):
|
|
187
228
|
""" Return the SHA1 of the current commit """
|
|
188
229
|
|
|
189
230
|
cmd = ['rev-parse']
|
|
@@ -193,11 +234,11 @@ def current_commit(short=False):
|
|
|
193
234
|
|
|
194
235
|
cmd.append('HEAD')
|
|
195
236
|
|
|
196
|
-
return git(cmd)[0]
|
|
237
|
+
return git(cmd, path=path)[0]
|
|
197
238
|
|
|
198
239
|
################################################################################
|
|
199
240
|
|
|
200
|
-
def pull(repo=None, all=False):
|
|
241
|
+
def pull(repo=None, all=False, path=None):
|
|
201
242
|
""" Run a git pull """
|
|
202
243
|
|
|
203
244
|
cmd = ['pull']
|
|
@@ -208,11 +249,11 @@ def pull(repo=None, all=False):
|
|
|
208
249
|
if repo:
|
|
209
250
|
cmd.append(repo)
|
|
210
251
|
|
|
211
|
-
return git(cmd)
|
|
252
|
+
return git(cmd, path=path)
|
|
212
253
|
|
|
213
254
|
################################################################################
|
|
214
255
|
|
|
215
|
-
def checkout(branch, create=False, commit=None):
|
|
256
|
+
def checkout(branch, create=False, commit=None, path=None):
|
|
216
257
|
""" Checkout a branch (optionally creating it or creating it from the
|
|
217
258
|
specified commit) """
|
|
218
259
|
|
|
@@ -226,27 +267,27 @@ def checkout(branch, create=False, commit=None):
|
|
|
226
267
|
if commit:
|
|
227
268
|
cmd.append(commit)
|
|
228
269
|
|
|
229
|
-
return git(cmd)
|
|
270
|
+
return git(cmd, path=path)
|
|
230
271
|
|
|
231
272
|
################################################################################
|
|
232
273
|
|
|
233
|
-
def merge(branch):
|
|
274
|
+
def merge(branch, path=None):
|
|
234
275
|
""" Merge a branch """
|
|
235
276
|
|
|
236
277
|
cmd = ['merge', branch]
|
|
237
278
|
|
|
238
|
-
return git(cmd)
|
|
279
|
+
return git(cmd, path=path)
|
|
239
280
|
|
|
240
281
|
################################################################################
|
|
241
282
|
|
|
242
|
-
def abort_merge():
|
|
283
|
+
def abort_merge(path=None):
|
|
243
284
|
""" Abort the current merge """
|
|
244
285
|
|
|
245
|
-
return git(['merge', '--abort'])
|
|
286
|
+
return git(['merge', '--abort'], path=path)
|
|
246
287
|
|
|
247
288
|
################################################################################
|
|
248
289
|
|
|
249
|
-
def set_upstream(branch, upstream=None):
|
|
290
|
+
def set_upstream(branch, upstream=None, path=None):
|
|
250
291
|
""" Set the default upstream branch """
|
|
251
292
|
|
|
252
293
|
if not upstream:
|
|
@@ -254,11 +295,11 @@ def set_upstream(branch, upstream=None):
|
|
|
254
295
|
|
|
255
296
|
cmd = ['branch', f'--set-upstream-to={upstream}', branch]
|
|
256
297
|
|
|
257
|
-
return git(cmd)
|
|
298
|
+
return git(cmd, path=path)
|
|
258
299
|
|
|
259
300
|
################################################################################
|
|
260
301
|
|
|
261
|
-
def fetch(all=False):
|
|
302
|
+
def fetch(all=False, path=None):
|
|
262
303
|
""" Run git fetch """
|
|
263
304
|
|
|
264
305
|
cmd = ['fetch']
|
|
@@ -266,11 +307,11 @@ def fetch(all=False):
|
|
|
266
307
|
if all:
|
|
267
308
|
cmd.append('--all')
|
|
268
309
|
|
|
269
|
-
return git(cmd)
|
|
310
|
+
return git(cmd, path=path)
|
|
270
311
|
|
|
271
312
|
################################################################################
|
|
272
313
|
|
|
273
|
-
def rebase_required(branch, parent):
|
|
314
|
+
def rebase_required(branch, parent, path=None):
|
|
274
315
|
""" Return True if the specified branch needs to be rebased against its
|
|
275
316
|
parent.
|
|
276
317
|
"""
|
|
@@ -278,8 +319,8 @@ def rebase_required(branch, parent):
|
|
|
278
319
|
# Find the latest commit on the parent branch and the most recent commit
|
|
279
320
|
# that both branches have in common.
|
|
280
321
|
|
|
281
|
-
parent_tip = git(['show-ref', '--heads', '-s', parent])
|
|
282
|
-
common_commit = git(['merge-base', parent, branch])
|
|
322
|
+
parent_tip = git(['show-ref', '--heads', '-s', parent], path=path)
|
|
323
|
+
common_commit = git(['merge-base', parent, branch], path=path)
|
|
283
324
|
|
|
284
325
|
# Different commits, so rebase is required
|
|
285
326
|
|
|
@@ -287,83 +328,77 @@ def rebase_required(branch, parent):
|
|
|
287
328
|
|
|
288
329
|
################################################################################
|
|
289
330
|
|
|
290
|
-
def rebase(branch):
|
|
331
|
+
def rebase(branch, path=None):
|
|
291
332
|
""" Rebase the current branch against the specified branch """
|
|
292
333
|
|
|
293
|
-
return git_run_status(['rebase', branch])
|
|
334
|
+
return git_run_status(['rebase', branch], path=path)
|
|
294
335
|
|
|
295
336
|
################################################################################
|
|
296
337
|
|
|
297
|
-
def abort_rebase():
|
|
338
|
+
def abort_rebase(path=None):
|
|
298
339
|
""" Abort the current rebase """
|
|
299
340
|
|
|
300
|
-
return git(['rebase', '--abort'])
|
|
341
|
+
return git(['rebase', '--abort'], path=path)
|
|
301
342
|
|
|
302
343
|
################################################################################
|
|
303
344
|
|
|
304
|
-
def rebasing():
|
|
345
|
+
def rebasing(path=None):
|
|
305
346
|
""" Return True if currently rebasing, False otherwise """
|
|
306
347
|
|
|
307
|
-
gitdir = git_dir()
|
|
348
|
+
gitdir = git_dir(path=path)
|
|
308
349
|
|
|
309
350
|
return os.path.isdir(os.path.join(gitdir, 'rebase-apply')) or \
|
|
310
351
|
os.path.isdir(os.path.join(gitdir, 'rebase-merge'))
|
|
311
352
|
|
|
312
353
|
################################################################################
|
|
313
354
|
|
|
314
|
-
def bisecting():
|
|
355
|
+
def bisecting(path=None):
|
|
315
356
|
""" Return True if currently rebasing, False otherwise """
|
|
316
357
|
|
|
317
|
-
gitdir = git_dir()
|
|
358
|
+
gitdir = git_dir(path=path)
|
|
318
359
|
|
|
319
360
|
return os.path.isfile(os.path.join(gitdir, 'BISECT_START'))
|
|
320
361
|
|
|
321
362
|
################################################################################
|
|
322
363
|
|
|
323
|
-
def merging():
|
|
364
|
+
def merging(path=None):
|
|
324
365
|
""" Return True if currently merging, False otherwise """
|
|
325
366
|
|
|
326
|
-
gitdir = git_dir()
|
|
367
|
+
gitdir = git_dir(path=path)
|
|
327
368
|
|
|
328
369
|
return os.path.isfile(os.path.join(gitdir, 'MERGE_MODE'))
|
|
329
370
|
|
|
330
371
|
################################################################################
|
|
331
372
|
|
|
332
|
-
def remotes():
|
|
373
|
+
def remotes(path=None):
|
|
333
374
|
""" Return the list of git remotes """
|
|
334
375
|
|
|
335
|
-
|
|
376
|
+
repo = pygit2.Repository(git_dir(path=path))
|
|
336
377
|
|
|
337
378
|
git_remotes = {}
|
|
338
379
|
|
|
339
|
-
for
|
|
340
|
-
|
|
341
|
-
remote_name, remote_url = result.split('\t')
|
|
342
|
-
|
|
343
|
-
if remote_url.endswith(' (fetch)'):
|
|
344
|
-
remote_url = remote_url[:-8]
|
|
345
|
-
elif remote_url.endswith(' (push)'):
|
|
346
|
-
remote_url = remote_url[:-7]
|
|
347
|
-
|
|
348
|
-
git_remotes[remote_name] = remote_url
|
|
380
|
+
for name in repo.remotes.names():
|
|
381
|
+
git_remotes[name] = repo.remotes[name].url
|
|
349
382
|
|
|
350
383
|
return git_remotes
|
|
351
384
|
|
|
352
385
|
################################################################################
|
|
353
386
|
|
|
354
|
-
def remote_names():
|
|
387
|
+
def remote_names(path=None):
|
|
355
388
|
""" Return the list of remote names """
|
|
356
389
|
|
|
357
|
-
|
|
390
|
+
repo = pygit2.Repository(git_dir(path=path))
|
|
391
|
+
|
|
392
|
+
results = list(repo.remotes.names())
|
|
358
393
|
|
|
359
394
|
return results
|
|
360
395
|
|
|
361
396
|
################################################################################
|
|
362
397
|
|
|
363
|
-
def project(short=False):
|
|
398
|
+
def project(short=False, path=None):
|
|
364
399
|
""" Return the name of the current git project """
|
|
365
400
|
|
|
366
|
-
git_remotes = remotes()
|
|
401
|
+
git_remotes = remotes(path=path)
|
|
367
402
|
name = ''
|
|
368
403
|
|
|
369
404
|
for remote in git_remotes:
|
|
@@ -389,7 +424,7 @@ def project(short=False):
|
|
|
389
424
|
|
|
390
425
|
################################################################################
|
|
391
426
|
|
|
392
|
-
def status_info(ignored=False, untracked=False):
|
|
427
|
+
def status_info(ignored=False, untracked=False, path=None):
|
|
393
428
|
""" Git status, optionally include files ignored in .gitignore and/or
|
|
394
429
|
untracked files.
|
|
395
430
|
Returns data in the same dictionary format as used by commit_info() """
|
|
@@ -402,7 +437,7 @@ def status_info(ignored=False, untracked=False):
|
|
|
402
437
|
if untracked:
|
|
403
438
|
cmd.append('--untracked-files=all')
|
|
404
439
|
|
|
405
|
-
results = git(cmd)
|
|
440
|
+
results = git(cmd, path=path)
|
|
406
441
|
|
|
407
442
|
# Dictionary of results, indexed by filename where the status is 2 characters
|
|
408
443
|
# the first representing the state of the file in the index and the second the state
|
|
@@ -413,12 +448,12 @@ def status_info(ignored=False, untracked=False):
|
|
|
413
448
|
info = {}
|
|
414
449
|
|
|
415
450
|
if results:
|
|
416
|
-
|
|
451
|
+
result = results[0].split('\0')
|
|
417
452
|
|
|
418
|
-
for
|
|
419
|
-
if len(
|
|
420
|
-
git_status =
|
|
421
|
-
name =
|
|
453
|
+
for r in result:
|
|
454
|
+
if len(r) > 3 and r[2] == ' ':
|
|
455
|
+
git_status = r[0:2]
|
|
456
|
+
name = r[3:]
|
|
422
457
|
|
|
423
458
|
info[name] = git_status
|
|
424
459
|
|
|
@@ -426,7 +461,7 @@ def status_info(ignored=False, untracked=False):
|
|
|
426
461
|
|
|
427
462
|
################################################################################
|
|
428
463
|
|
|
429
|
-
def status(ignored=False, untracked=False):
|
|
464
|
+
def status(ignored=False, untracked=False, path=None):
|
|
430
465
|
""" Git status, optionally include files ignored in .gitignore and/or
|
|
431
466
|
untracked files.
|
|
432
467
|
Similar to status_info, but returns data as a list, rather than a
|
|
@@ -440,7 +475,7 @@ def status(ignored=False, untracked=False):
|
|
|
440
475
|
if untracked:
|
|
441
476
|
cmd.append('--untracked-files=all')
|
|
442
477
|
|
|
443
|
-
results = git(cmd)
|
|
478
|
+
results = git(cmd, path=path)
|
|
444
479
|
|
|
445
480
|
# Nested list of results. For each entry:
|
|
446
481
|
# item 0 is the status where: M=modified, A=added, D=deleted, R=renamed, C=copied, U=unmerged, ?=untracked, !=ignored
|
|
@@ -456,6 +491,9 @@ def status(ignored=False, untracked=False):
|
|
|
456
491
|
stats = []
|
|
457
492
|
stats.append(result[0:2])
|
|
458
493
|
|
|
494
|
+
if not untracked and result[0] == '?':
|
|
495
|
+
continue
|
|
496
|
+
|
|
459
497
|
name = result[3:]
|
|
460
498
|
if ' -> ' in name:
|
|
461
499
|
stats += name.split(' -> ', 1)
|
|
@@ -468,34 +506,39 @@ def status(ignored=False, untracked=False):
|
|
|
468
506
|
|
|
469
507
|
################################################################################
|
|
470
508
|
|
|
471
|
-
def working_tree():
|
|
509
|
+
def working_tree(path=None):
|
|
472
510
|
""" Location of the current working tree or None if we are not in a working tree """
|
|
473
511
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
return
|
|
512
|
+
repo_dir = git_dir(path=path)
|
|
513
|
+
|
|
514
|
+
if repo_dir:
|
|
515
|
+
return os.path.abspath(os.path.join(repo_dir, os.pardir))
|
|
516
|
+
|
|
517
|
+
return None
|
|
478
518
|
|
|
479
519
|
################################################################################
|
|
480
520
|
|
|
481
|
-
def git_dir():
|
|
521
|
+
def git_dir(path=None):
|
|
482
522
|
""" Return the relative path to the .git directory """
|
|
483
523
|
|
|
484
|
-
|
|
524
|
+
if not path:
|
|
525
|
+
path = os.getcwd()
|
|
526
|
+
|
|
527
|
+
return pygit2.discover_repository(path)
|
|
485
528
|
|
|
486
529
|
################################################################################
|
|
487
530
|
|
|
488
|
-
def tree_path(filename):
|
|
531
|
+
def tree_path(filename, path=None):
|
|
489
532
|
""" Normalise a filename (absolute or relative to the current directory)
|
|
490
533
|
so that it is relative to the top-level directory of the working tree """
|
|
491
534
|
|
|
492
|
-
git_tree = working_tree()
|
|
535
|
+
git_tree = working_tree(path=path)
|
|
493
536
|
|
|
494
537
|
return os.path.relpath(filename, git_tree)
|
|
495
538
|
|
|
496
539
|
################################################################################
|
|
497
540
|
|
|
498
|
-
def difftool(commit_1=None, commit_2=None, files=None, tool=None):
|
|
541
|
+
def difftool(commit_1=None, commit_2=None, files=None, tool=None, path=None):
|
|
499
542
|
""" Run git difftool """
|
|
500
543
|
|
|
501
544
|
cmd = ['difftool']
|
|
@@ -517,7 +560,7 @@ def difftool(commit_1=None, commit_2=None, files=None, tool=None):
|
|
|
517
560
|
else:
|
|
518
561
|
cmd += files
|
|
519
562
|
|
|
520
|
-
return git(cmd)
|
|
563
|
+
return git(cmd, path=path)
|
|
521
564
|
|
|
522
565
|
################################################################################
|
|
523
566
|
|
|
@@ -528,7 +571,7 @@ def difftool(commit_1=None, commit_2=None, files=None, tool=None):
|
|
|
528
571
|
_DIFF_OUTPUT_RE = re.compile(r'(-|\d+)\s+(-|\d+)\s+(.*)')
|
|
529
572
|
_DIFF_OUTPUT_RENAME_RE = re.compile(r'(.*)\{(.*) => (.*)\}(.*)')
|
|
530
573
|
|
|
531
|
-
def commit_info(commit_1=None, commit_2=None, paths=None, diff_stats=False):
|
|
574
|
+
def commit_info(commit_1=None, commit_2=None, paths=None, diff_stats=False, path=None):
|
|
532
575
|
""" Return details of changes either in single commit (defaulting to the most
|
|
533
576
|
recent one) or between two commits, optionally restricted a path or paths
|
|
534
577
|
and optionally returning diff statistics, with and/or without taking
|
|
@@ -584,7 +627,7 @@ def commit_info(commit_1=None, commit_2=None, paths=None, diff_stats=False):
|
|
|
584
627
|
else:
|
|
585
628
|
params += paths
|
|
586
629
|
|
|
587
|
-
results = git(['diff', '--name-status'] + params)
|
|
630
|
+
results = git(['diff', '--name-status'] + params, path=path)
|
|
588
631
|
|
|
589
632
|
# Parse the output
|
|
590
633
|
|
|
@@ -611,7 +654,7 @@ def commit_info(commit_1=None, commit_2=None, paths=None, diff_stats=False):
|
|
|
611
654
|
# Run git diff to get stats, and add them to the info
|
|
612
655
|
# TODO: Need to extract old name of renamed files
|
|
613
656
|
|
|
614
|
-
results = git(['diff', '--numstat'] + params)
|
|
657
|
+
results = git(['diff', '--numstat'] + params, path=path)
|
|
615
658
|
|
|
616
659
|
for result in results:
|
|
617
660
|
old_filename, new_filename, lines_ins, lines_del = parse_diff_output(result)
|
|
@@ -621,7 +664,7 @@ def commit_info(commit_1=None, commit_2=None, paths=None, diff_stats=False):
|
|
|
621
664
|
|
|
622
665
|
# Run git diff to get stats ignoring whitespace changes and add them
|
|
623
666
|
|
|
624
|
-
results = git(['diff', '--numstat', '--ignore-all-space', '--ignore-blank-lines'] + params)
|
|
667
|
+
results = git(['diff', '--numstat', '--ignore-all-space', '--ignore-blank-lines'] + params, path=path)
|
|
625
668
|
|
|
626
669
|
for result in results:
|
|
627
670
|
old_filename, new_filename, lines_ins, lines_del = parse_diff_output(result)
|
|
@@ -642,7 +685,7 @@ def commit_info(commit_1=None, commit_2=None, paths=None, diff_stats=False):
|
|
|
642
685
|
|
|
643
686
|
################################################################################
|
|
644
687
|
|
|
645
|
-
def diff(commit=None, renames=True, copies=True, relative=False):
|
|
688
|
+
def diff(commit=None, renames=True, copies=True, relative=False, path=None):
|
|
646
689
|
""" Return a list of differences between two commits, working tree and a commit or working tree and head """
|
|
647
690
|
|
|
648
691
|
if commit:
|
|
@@ -668,17 +711,17 @@ def diff(commit=None, renames=True, copies=True, relative=False):
|
|
|
668
711
|
|
|
669
712
|
cmd += commit
|
|
670
713
|
|
|
671
|
-
return git(cmd)
|
|
714
|
+
return git(cmd, path=path)
|
|
672
715
|
|
|
673
716
|
################################################################################
|
|
674
717
|
|
|
675
|
-
def diff_status(commit1, commit2='HEAD'):
|
|
718
|
+
def diff_status(commit1, commit2='HEAD', path=None):
|
|
676
719
|
""" Return True if there is no difference between the two commits, False otherwise """
|
|
677
720
|
|
|
678
721
|
cmd = ['diff', '--no-patch', '--exit-code', commit1, commit2]
|
|
679
722
|
|
|
680
723
|
try:
|
|
681
|
-
git(cmd)
|
|
724
|
+
git(cmd, path=path)
|
|
682
725
|
except GitError:
|
|
683
726
|
return False
|
|
684
727
|
|
|
@@ -686,30 +729,31 @@ def diff_status(commit1, commit2='HEAD'):
|
|
|
686
729
|
|
|
687
730
|
################################################################################
|
|
688
731
|
|
|
689
|
-
def show(revision, filename, outfile=None):
|
|
732
|
+
def show(revision, filename, outfile=None, path=None):
|
|
690
733
|
""" Return the output from git show revision:filename """
|
|
691
734
|
|
|
692
|
-
return git(['show', f'{revision}:{filename}'], stdout=outfile)
|
|
735
|
+
return git(['show', f'{revision}:{filename}'], stdout=outfile, path=path)
|
|
693
736
|
|
|
694
737
|
################################################################################
|
|
695
738
|
|
|
696
|
-
def add(files):
|
|
739
|
+
def add(files, path=None):
|
|
697
740
|
""" Add file to git """
|
|
698
741
|
|
|
699
|
-
return git(['add'] + files)
|
|
742
|
+
return git(['add'] + files, path=path)
|
|
700
743
|
|
|
701
744
|
################################################################################
|
|
702
745
|
|
|
703
|
-
def rm(files):
|
|
746
|
+
def rm(files, path=None):
|
|
704
747
|
""" Remove files from git """
|
|
705
748
|
|
|
706
|
-
return git(['rm'] + files)
|
|
749
|
+
return git(['rm'] + files, path=path)
|
|
707
750
|
|
|
708
751
|
################################################################################
|
|
709
752
|
|
|
710
753
|
def commit(files=None,
|
|
711
754
|
message=None,
|
|
712
|
-
all=False, amend=False, foreground=False, patch=False, dry_run=False
|
|
755
|
+
all=False, amend=False, foreground=False, patch=False, dry_run=False,
|
|
756
|
+
path=None):
|
|
713
757
|
""" Commit files to git """
|
|
714
758
|
|
|
715
759
|
cmd = ['commit']
|
|
@@ -734,16 +778,17 @@ def commit(files=None,
|
|
|
734
778
|
cmd += ['-m', message]
|
|
735
779
|
|
|
736
780
|
if foreground:
|
|
737
|
-
return git(cmd, stdout=sys.stdout, stderr=sys.stderr)
|
|
781
|
+
return git(cmd, stdout=sys.stdout, stderr=sys.stderr, path=path)
|
|
738
782
|
|
|
739
|
-
return git(cmd)
|
|
783
|
+
return git(cmd, path=path)
|
|
740
784
|
|
|
741
785
|
################################################################################
|
|
742
786
|
|
|
743
787
|
def push(all=False, mirror=False, tags=False, atomic=False, dry_run=False,
|
|
744
788
|
follow_tags=False, receive_pack=False, repo=None, force=False, delete=False,
|
|
745
|
-
prune=False, verbose=False, set_upstream=False, push_options=
|
|
746
|
-
force_with_lease=False, no_verify=False, repository=None, refspec=None
|
|
789
|
+
prune=False, verbose=False, set_upstream=False, push_options=None, signed=None,
|
|
790
|
+
force_with_lease=False, no_verify=False, repository=None, refspec=None,
|
|
791
|
+
path=None):
|
|
747
792
|
""" Push commits to a remote """
|
|
748
793
|
|
|
749
794
|
cmd = ['push']
|
|
@@ -807,18 +852,18 @@ def push(all=False, mirror=False, tags=False, atomic=False, dry_run=False,
|
|
|
807
852
|
for ref in refspec:
|
|
808
853
|
cmd.append(ref)
|
|
809
854
|
|
|
810
|
-
return git(cmd)
|
|
855
|
+
return git(cmd, path=path)
|
|
811
856
|
|
|
812
857
|
################################################################################
|
|
813
858
|
|
|
814
|
-
def reset(sha1):
|
|
859
|
+
def reset(sha1, path=None):
|
|
815
860
|
""" Run git reset """
|
|
816
861
|
|
|
817
|
-
return git(['reset', sha1])
|
|
862
|
+
return git(['reset', sha1], path=path)
|
|
818
863
|
|
|
819
864
|
################################################################################
|
|
820
865
|
|
|
821
|
-
def config_get(section, key, source=None, defaultvalue=None):
|
|
866
|
+
def config_get(section, key, source=None, defaultvalue=None, path=None):
|
|
822
867
|
""" Return the specified configuration entry
|
|
823
868
|
Returns a default value if no matching configuration entry exists """
|
|
824
869
|
|
|
@@ -836,13 +881,13 @@ def config_get(section, key, source=None, defaultvalue=None):
|
|
|
836
881
|
cmd += ['--get', f'{section}.{key}']
|
|
837
882
|
|
|
838
883
|
try:
|
|
839
|
-
return git(cmd)[0]
|
|
884
|
+
return git(cmd, path=path)[0]
|
|
840
885
|
except GitError:
|
|
841
886
|
return defaultvalue
|
|
842
887
|
|
|
843
888
|
################################################################################
|
|
844
889
|
|
|
845
|
-
def config_set(section, key, value, source=None):
|
|
890
|
+
def config_set(section, key, value, source=None, path=None):
|
|
846
891
|
""" Set a configuration entry """
|
|
847
892
|
|
|
848
893
|
cmd = ['config']
|
|
@@ -858,11 +903,11 @@ def config_set(section, key, value, source=None):
|
|
|
858
903
|
|
|
859
904
|
cmd += ['--replace-all', f'{section}.{key}', value]
|
|
860
905
|
|
|
861
|
-
return git(cmd)
|
|
906
|
+
return git(cmd, path=path)
|
|
862
907
|
|
|
863
908
|
################################################################################
|
|
864
909
|
|
|
865
|
-
def config_rm(section, key, source=LOCAL):
|
|
910
|
+
def config_rm(section, key, source=LOCAL, path=None):
|
|
866
911
|
""" Remove a configuration entry """
|
|
867
912
|
|
|
868
913
|
cmd = ['config']
|
|
@@ -874,11 +919,11 @@ def config_rm(section, key, source=LOCAL):
|
|
|
874
919
|
|
|
875
920
|
cmd += ['--unset', f'{section}.{key}']
|
|
876
921
|
|
|
877
|
-
return git(cmd)
|
|
922
|
+
return git(cmd, path=path)
|
|
878
923
|
|
|
879
924
|
################################################################################
|
|
880
925
|
|
|
881
|
-
def ref(fields=('objectname'), sort=None, remotes=False):
|
|
926
|
+
def ref(fields=('objectname'), sort=None, remotes=False, path=None):
|
|
882
927
|
""" Wrapper for git for-each-ref """
|
|
883
928
|
|
|
884
929
|
cmd = ['for-each-ref']
|
|
@@ -895,12 +940,12 @@ def ref(fields=('objectname'), sort=None, remotes=False):
|
|
|
895
940
|
if remotes:
|
|
896
941
|
cmd.append('refs/remotes/origin')
|
|
897
942
|
|
|
898
|
-
for output in git(cmd):
|
|
943
|
+
for output in git(cmd, path=path):
|
|
899
944
|
yield output.split('\0')
|
|
900
945
|
|
|
901
946
|
################################################################################
|
|
902
947
|
|
|
903
|
-
def branches(all=False):
|
|
948
|
+
def branches(all=False, path=None):
|
|
904
949
|
""" Return a list of all the branches in the current repo """
|
|
905
950
|
|
|
906
951
|
cmd = ['branch']
|
|
@@ -909,7 +954,7 @@ def branches(all=False):
|
|
|
909
954
|
cmd.append('--all')
|
|
910
955
|
|
|
911
956
|
results = []
|
|
912
|
-
for output in git(cmd):
|
|
957
|
+
for output in git(cmd, path=path):
|
|
913
958
|
if ' -> ' not in output and '(HEAD detached at ' not in output:
|
|
914
959
|
results.append(output[2:])
|
|
915
960
|
|
|
@@ -917,7 +962,7 @@ def branches(all=False):
|
|
|
917
962
|
|
|
918
963
|
################################################################################
|
|
919
964
|
|
|
920
|
-
def delete_branch(branch, force=False, remote=False):
|
|
965
|
+
def delete_branch(branch, force=False, remote=False, path=None):
|
|
921
966
|
""" Delete a branch, optionally forcefully and/or including the
|
|
922
967
|
remote tracking branch """
|
|
923
968
|
|
|
@@ -931,11 +976,11 @@ def delete_branch(branch, force=False, remote=False):
|
|
|
931
976
|
|
|
932
977
|
cmd.append(branch)
|
|
933
978
|
|
|
934
|
-
return git(cmd)
|
|
979
|
+
return git(cmd, path=path)
|
|
935
980
|
|
|
936
981
|
################################################################################
|
|
937
982
|
|
|
938
|
-
def remote_prune(remote, dry_run=False):
|
|
983
|
+
def remote_prune(remote, dry_run=False, path=None):
|
|
939
984
|
""" Return a list of remote tracking branches that no longer exist on the
|
|
940
985
|
specified remote """
|
|
941
986
|
|
|
@@ -944,7 +989,7 @@ def remote_prune(remote, dry_run=False):
|
|
|
944
989
|
if dry_run:
|
|
945
990
|
cmd.append('--dry-run')
|
|
946
991
|
|
|
947
|
-
results = git(cmd)
|
|
992
|
+
results = git(cmd, path=path)
|
|
948
993
|
|
|
949
994
|
prunable_branches = []
|
|
950
995
|
|
|
@@ -959,43 +1004,43 @@ def remote_prune(remote, dry_run=False):
|
|
|
959
1004
|
|
|
960
1005
|
################################################################################
|
|
961
1006
|
|
|
962
|
-
def get_commits(commit1, commit2):
|
|
1007
|
+
def get_commits(commit1, commit2, path=None):
|
|
963
1008
|
""" Get a list of commits separating two commits """
|
|
964
1009
|
|
|
965
|
-
return git(['rev-list', commit1, f'^{commit2}'])
|
|
1010
|
+
return git(['rev-list', commit1, f'^{commit2}'], path=path)
|
|
966
1011
|
|
|
967
1012
|
################################################################################
|
|
968
1013
|
|
|
969
|
-
def commit_count(commit1, commit2):
|
|
1014
|
+
def commit_count(commit1, commit2, path=None):
|
|
970
1015
|
""" Get a count of the number of commits between two commits """
|
|
971
1016
|
|
|
972
|
-
return int(git(['rev-list', '--count', commit1, f'^{commit2}'])[0])
|
|
1017
|
+
return int(git(['rev-list', '--count', commit1, f'^{commit2}'], path=path)[0])
|
|
973
1018
|
|
|
974
1019
|
################################################################################
|
|
975
1020
|
|
|
976
|
-
def branch_name(branch):
|
|
1021
|
+
def branch_name(branch, path=None):
|
|
977
1022
|
""" Return the full name of a branch given an abbreviation - e.g. @{upstream}
|
|
978
1023
|
for the upstream branch """
|
|
979
1024
|
|
|
980
|
-
return git(['rev-parse', '--abbrev-ref', '--symbolic-full-name', branch])[0]
|
|
1025
|
+
return git(['rev-parse', '--abbrev-ref', '--symbolic-full-name', branch], path=path)[0]
|
|
981
1026
|
|
|
982
1027
|
################################################################################
|
|
983
1028
|
|
|
984
|
-
def author(commit):
|
|
1029
|
+
def author(commit, path=None):
|
|
985
1030
|
""" Return the author of a commit """
|
|
986
1031
|
|
|
987
|
-
return git(['show', '--format=format:%an', commit])[0]
|
|
1032
|
+
return git(['show', '--format=format:%an', commit], path=path)[0]
|
|
988
1033
|
|
|
989
1034
|
################################################################################
|
|
990
1035
|
|
|
991
|
-
def commit_changes(commit='HEAD'):
|
|
1036
|
+
def commit_changes(commit='HEAD', path=None):
|
|
992
1037
|
"""Return a list of the files changed in a commit"""
|
|
993
1038
|
|
|
994
|
-
return git(['show', '--name-only', '--pretty=format:', commit])
|
|
1039
|
+
return git(['show', '--name-only', '--pretty=format:', commit], path=path)
|
|
995
1040
|
|
|
996
1041
|
################################################################################
|
|
997
1042
|
|
|
998
|
-
def files(dir=None):
|
|
1043
|
+
def files(dir=None, path=None):
|
|
999
1044
|
""" Return the output from 'git ls-files' """
|
|
1000
1045
|
|
|
1001
1046
|
cmd = ['ls-files']
|
|
@@ -1003,20 +1048,20 @@ def files(dir=None):
|
|
|
1003
1048
|
if dir:
|
|
1004
1049
|
cmd.append(dir)
|
|
1005
1050
|
|
|
1006
|
-
return git(cmd)
|
|
1051
|
+
return git(cmd, path=path)
|
|
1007
1052
|
|
|
1008
1053
|
################################################################################
|
|
1009
1054
|
|
|
1010
|
-
def stash():
|
|
1055
|
+
def stash(path=None):
|
|
1011
1056
|
""" Return the list of stashed items (if any) """
|
|
1012
1057
|
|
|
1013
1058
|
cmd = ['stash', 'list']
|
|
1014
1059
|
|
|
1015
|
-
return git(cmd)
|
|
1060
|
+
return git(cmd, path=path)
|
|
1016
1061
|
|
|
1017
1062
|
################################################################################
|
|
1018
1063
|
|
|
1019
|
-
def parents(commit=None, ignore=None):
|
|
1064
|
+
def parents(commit=None, ignore=None, path=None):
|
|
1020
1065
|
""" Look at the commits down the history of the specified branch,
|
|
1021
1066
|
looking for another branch or branches that also contain the same commit.
|
|
1022
1067
|
The first found is the parent (or equally-likely parents) of the
|
|
@@ -1028,16 +1073,16 @@ def parents(commit=None, ignore=None):
|
|
|
1028
1073
|
|
|
1029
1074
|
# Get the history of the branch
|
|
1030
1075
|
|
|
1031
|
-
current_branch = commit or branch('HEAD')
|
|
1076
|
+
current_branch = commit or branch('HEAD', path=path)
|
|
1032
1077
|
|
|
1033
|
-
current_history = git(['rev-list', current_branch])
|
|
1078
|
+
current_history = git(['rev-list', current_branch], path=path)
|
|
1034
1079
|
|
|
1035
1080
|
# Look down the commits on the current branch for other branches that have
|
|
1036
1081
|
# the same commit, using the ignore pattern if there is one.
|
|
1037
1082
|
|
|
1038
1083
|
for distance, ancestor in enumerate(current_history):
|
|
1039
1084
|
branches = []
|
|
1040
|
-
for brnch in git(['branch', '--contains', ancestor]):
|
|
1085
|
+
for brnch in git(['branch', '--contains', ancestor], path=path):
|
|
1041
1086
|
brnch = brnch[2:]
|
|
1042
1087
|
if brnch != current_branch and '(HEAD detached at' not in brnch:
|
|
1043
1088
|
if not ignore or (ignore and not fnmatch.fnmatch(brnch, ignore)):
|
|
@@ -1052,13 +1097,11 @@ def parents(commit=None, ignore=None):
|
|
|
1052
1097
|
|
|
1053
1098
|
################################################################################
|
|
1054
1099
|
|
|
1055
|
-
def find_common_ancestor(branch1='HEAD', branch2='master'):
|
|
1100
|
+
def find_common_ancestor(branch1='HEAD', branch2='master', path=None):
|
|
1056
1101
|
""" Find the first (oldest) commit that the two branches have in common
|
|
1057
1102
|
i.e. the point where one branch was forked from the other """
|
|
1058
1103
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
return common
|
|
1104
|
+
return git(['merge-base', branch1, branch2], path=path)[0]
|
|
1062
1105
|
|
|
1063
1106
|
################################################################################
|
|
1064
1107
|
|
|
@@ -1068,7 +1111,7 @@ _GREP_OPTLIST = \
|
|
|
1068
1111
|
(
|
|
1069
1112
|
('color', '--color=always'),
|
|
1070
1113
|
('count', '--count'),
|
|
1071
|
-
('
|
|
1114
|
+
('follow', '--follow'),
|
|
1072
1115
|
('unmatch', '-I'),
|
|
1073
1116
|
('textconf', '--textconv'),
|
|
1074
1117
|
('ignore_case', '--ignore-case'),
|
|
@@ -1084,10 +1127,8 @@ _GREP_OPTLIST = \
|
|
|
1084
1127
|
('files_without_match', '--files-without-match'),
|
|
1085
1128
|
('names_only', '--names-only'),
|
|
1086
1129
|
('null', '--null'),
|
|
1087
|
-
('count', '--count'),
|
|
1088
1130
|
('all_match', '--all-match'),
|
|
1089
1131
|
('quiet', '--quiet'),
|
|
1090
|
-
('color', '--color=always'),
|
|
1091
1132
|
('no_color', '--no-color'),
|
|
1092
1133
|
('break', '--break'),
|
|
1093
1134
|
('heading', '--heading'),
|
|
@@ -1110,7 +1151,7 @@ _GREP_NON_BOOL_OPTLIST = \
|
|
|
1110
1151
|
('parent_basename', '--parent-basename')
|
|
1111
1152
|
)
|
|
1112
1153
|
|
|
1113
|
-
def grep(pattern, git_dir=None, work_tree=None, options=None, wildcards=None):
|
|
1154
|
+
def grep(pattern, git_dir=None, work_tree=None, options=None, wildcards=None, path=None):
|
|
1114
1155
|
""" Run git grep - takes a painfully large number of options passed
|
|
1115
1156
|
as a dictionary. """
|
|
1116
1157
|
|
|
@@ -1147,30 +1188,30 @@ def grep(pattern, git_dir=None, work_tree=None, options=None, wildcards=None):
|
|
|
1147
1188
|
cmd.append('--')
|
|
1148
1189
|
cmd += wildcards
|
|
1149
1190
|
|
|
1150
|
-
return git_run_status(cmd)
|
|
1191
|
+
return git_run_status(cmd, path=path)
|
|
1151
1192
|
|
|
1152
1193
|
################################################################################
|
|
1153
1194
|
|
|
1154
|
-
def isbranch(branchname):
|
|
1195
|
+
def isbranch(branchname, path=None):
|
|
1155
1196
|
""" Return true if the specified branch exists """
|
|
1156
1197
|
|
|
1157
|
-
return branchname in branches(True)
|
|
1198
|
+
return branchname in branches(True, path=path)
|
|
1158
1199
|
|
|
1159
1200
|
################################################################################
|
|
1160
1201
|
|
|
1161
|
-
def default_branch():
|
|
1202
|
+
def default_branch(path=None):
|
|
1162
1203
|
""" Return the name of the default branch, attempting to interrogate GitLab
|
|
1163
1204
|
if the repo appears to have been cloned from there and falling back to
|
|
1164
1205
|
returning whichever one of 'develop', 'main' or 'master' exists. """
|
|
1165
1206
|
|
|
1166
|
-
remote_list = remotes()
|
|
1207
|
+
remote_list = remotes(path=path)
|
|
1167
1208
|
if remote_list:
|
|
1168
1209
|
for name in remote_list:
|
|
1169
1210
|
if 'gitlab' in remote_list[name]:
|
|
1170
1211
|
url = remote_list[name].split('@')[1].split(':')[0]
|
|
1171
1212
|
repo = remote_list[name].split(':')[1]
|
|
1172
1213
|
|
|
1173
|
-
if not url.startswith('http://')
|
|
1214
|
+
if not url.startswith('http://') and not url.startswith('https://'):
|
|
1174
1215
|
url = f'https://{url}'
|
|
1175
1216
|
|
|
1176
1217
|
if repo.endswith('.git'):
|
|
@@ -1183,8 +1224,8 @@ def default_branch():
|
|
|
1183
1224
|
except gitlab.GitLabError:
|
|
1184
1225
|
return None
|
|
1185
1226
|
|
|
1186
|
-
git_branches = branches()
|
|
1187
|
-
default_branches = config_get('skilleter-thingy', 'defaultBranches', defaultvalue=DEFAULT_DEFAULT_BRANCHES).split(',')
|
|
1227
|
+
git_branches = branches(path=path)
|
|
1228
|
+
default_branches = config_get('skilleter-thingy', 'defaultBranches', defaultvalue=DEFAULT_DEFAULT_BRANCHES, path=path).split(',')
|
|
1188
1229
|
|
|
1189
1230
|
for branch in default_branches:
|
|
1190
1231
|
if branch in git_branches:
|
|
@@ -1194,7 +1235,7 @@ def default_branch():
|
|
|
1194
1235
|
|
|
1195
1236
|
################################################################################
|
|
1196
1237
|
|
|
1197
|
-
def matching_branch(branchname, case=False):
|
|
1238
|
+
def matching_branch(branchname, case=False, path=None):
|
|
1198
1239
|
""" Look for a branch matching the specified name and return it
|
|
1199
1240
|
out if it is an exact match or there is only one partial
|
|
1200
1241
|
match. If there are multiple branches that match, return them
|
|
@@ -1206,7 +1247,7 @@ def matching_branch(branchname, case=False):
|
|
|
1206
1247
|
otherwise, it just checks for a branches containing the branchname
|
|
1207
1248
|
as a substring. """
|
|
1208
1249
|
|
|
1209
|
-
all_branches = branches(all=True)
|
|
1250
|
+
all_branches = branches(all=True, path=path)
|
|
1210
1251
|
|
|
1211
1252
|
# Always return exact matches
|
|
1212
1253
|
|
|
@@ -1258,7 +1299,7 @@ def matching_branch(branchname, case=False):
|
|
|
1258
1299
|
|
|
1259
1300
|
################################################################################
|
|
1260
1301
|
|
|
1261
|
-
def update(clean=False, all=False):
|
|
1302
|
+
def update(clean=False, all=False, path=None):
|
|
1262
1303
|
""" Run git update (which is a thingy command, and may end up as a module
|
|
1263
1304
|
but for the moment, we'll treat it as any other git command) """
|
|
1264
1305
|
|
|
@@ -1270,18 +1311,18 @@ def update(clean=False, all=False):
|
|
|
1270
1311
|
if all:
|
|
1271
1312
|
cmd.append('--all')
|
|
1272
1313
|
|
|
1273
|
-
return git(cmd)
|
|
1314
|
+
return git(cmd, path=path)
|
|
1274
1315
|
|
|
1275
1316
|
################################################################################
|
|
1276
1317
|
|
|
1277
|
-
def object_type(name):
|
|
1318
|
+
def object_type(name, path=None):
|
|
1278
1319
|
""" Return the git object type (commit, tag, blob, ...) """
|
|
1279
1320
|
|
|
1280
|
-
return git(['cat-file', '-t', name])[0]
|
|
1321
|
+
return git(['cat-file', '-t', name], path=path)[0]
|
|
1281
1322
|
|
|
1282
1323
|
################################################################################
|
|
1283
1324
|
|
|
1284
|
-
def matching_commit(name):
|
|
1325
|
+
def matching_commit(name, path=None):
|
|
1285
1326
|
""" Similar to matching_branch() (see above).
|
|
1286
1327
|
If the name uniquely matches a branch, return that
|
|
1287
1328
|
If it matches multiple branches return a list
|
|
@@ -1292,12 +1333,12 @@ def matching_commit(name):
|
|
|
1292
1333
|
|
|
1293
1334
|
# First, look for exact matching object
|
|
1294
1335
|
|
|
1295
|
-
if iscommit(name):
|
|
1336
|
+
if iscommit(name, path=path):
|
|
1296
1337
|
return [name]
|
|
1297
1338
|
|
|
1298
1339
|
# Look for at least one matching branch
|
|
1299
1340
|
|
|
1300
|
-
matches = matching_branch(name)
|
|
1341
|
+
matches = matching_branch(name, path=path)
|
|
1301
1342
|
|
|
1302
1343
|
if matches:
|
|
1303
1344
|
return matches
|
|
@@ -1305,7 +1346,7 @@ def matching_commit(name):
|
|
|
1305
1346
|
# Look for at least one matching tag
|
|
1306
1347
|
|
|
1307
1348
|
matches = []
|
|
1308
|
-
for tag in tags():
|
|
1349
|
+
for tag in tags(path=path):
|
|
1309
1350
|
if name in tag:
|
|
1310
1351
|
matches.append(tag)
|
|
1311
1352
|
|
|
@@ -1315,7 +1356,7 @@ def matching_commit(name):
|
|
|
1315
1356
|
# Look for a matching commit
|
|
1316
1357
|
|
|
1317
1358
|
try:
|
|
1318
|
-
commit_type = object_type(name)
|
|
1359
|
+
commit_type = object_type(name, path=path)
|
|
1319
1360
|
|
|
1320
1361
|
if commit_type == 'commit':
|
|
1321
1362
|
matches = [name]
|
|
@@ -1326,7 +1367,7 @@ def matching_commit(name):
|
|
|
1326
1367
|
|
|
1327
1368
|
################################################################################
|
|
1328
1369
|
|
|
1329
|
-
def log(branch1, branch2=None):
|
|
1370
|
+
def log(branch1, branch2=None, path=None):
|
|
1330
1371
|
""" Return the git log between the given commits """
|
|
1331
1372
|
|
|
1332
1373
|
if branch2:
|
|
@@ -1334,7 +1375,38 @@ def log(branch1, branch2=None):
|
|
|
1334
1375
|
else:
|
|
1335
1376
|
cmd = ['log', '-n1', branch1]
|
|
1336
1377
|
|
|
1337
|
-
return git(cmd)
|
|
1378
|
+
return git(cmd, path=path)
|
|
1379
|
+
|
|
1380
|
+
################################################################################
|
|
1381
|
+
|
|
1382
|
+
def clean(recurse=False, force=False, dry_run=False, quiet=False,
|
|
1383
|
+
exclude=None, ignore_rules=False, remove_only_ignored=False, path=None):
|
|
1384
|
+
""" Run git clean """
|
|
1385
|
+
|
|
1386
|
+
cmd = ['clean']
|
|
1387
|
+
|
|
1388
|
+
if recurse:
|
|
1389
|
+
cmd.append('-d')
|
|
1390
|
+
|
|
1391
|
+
if force:
|
|
1392
|
+
cmd.append('--force')
|
|
1393
|
+
|
|
1394
|
+
if dry_run:
|
|
1395
|
+
cmd.append('--dry-run')
|
|
1396
|
+
|
|
1397
|
+
if quiet:
|
|
1398
|
+
cmd.append('--quiet')
|
|
1399
|
+
|
|
1400
|
+
if exclude:
|
|
1401
|
+
cmd += ['--exclude', exclude]
|
|
1402
|
+
|
|
1403
|
+
if ignore_rules:
|
|
1404
|
+
cmd.append('-x')
|
|
1405
|
+
|
|
1406
|
+
if remove_only_ignored:
|
|
1407
|
+
cmd.append('-X')
|
|
1408
|
+
|
|
1409
|
+
return git(cmd, path=path)
|
|
1338
1410
|
|
|
1339
1411
|
################################################################################
|
|
1340
1412
|
|
|
@@ -1376,7 +1448,7 @@ def run_tests():
|
|
|
1376
1448
|
|
|
1377
1449
|
print('')
|
|
1378
1450
|
|
|
1379
|
-
with open('newfile.txt', 'w') as newfile:
|
|
1451
|
+
with open('newfile.txt', 'w', encoding='utf8') as newfile:
|
|
1380
1452
|
newfile.write('THIS IS A TEST')
|
|
1381
1453
|
|
|
1382
1454
|
print('Adding and committing "newfile.txt"')
|
|
@@ -1398,7 +1470,7 @@ def run_tests():
|
|
|
1398
1470
|
print('Commit info for HEAD %s' % commit_info('HEAD'))
|
|
1399
1471
|
|
|
1400
1472
|
except GitError as exc:
|
|
1401
|
-
sys.stderr.write('ERROR:
|
|
1473
|
+
sys.stderr.write(f'ERROR: {exc.msg}')
|
|
1402
1474
|
sys.exit(1)
|
|
1403
1475
|
|
|
1404
1476
|
finally:
|