skilleter-thingy 0.0.69__tar.gz → 0.0.70__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of skilleter-thingy might be problematic. Click here for more details.
- {skilleter_thingy-0.0.69/skilleter_thingy.egg-info → skilleter_thingy-0.0.70}/PKG-INFO +2 -1
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/pyproject.toml +3 -1
- skilleter_thingy-0.0.70/skilleter_thingy/multigit.py +244 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/git2.py +42 -32
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70/skilleter_thingy.egg-info}/PKG-INFO +2 -1
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy.egg-info/SOURCES.txt +1 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy.egg-info/entry_points.txt +1 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy.egg-info/requires.txt +1 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/LICENSE +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/README.md +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/setup.cfg +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/__init__.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/addpath.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/borger.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/box.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/console_colours.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/diskspacecheck.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/docker_purge.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/ffind.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/ggit.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/ggrep.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_br.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_ca.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_cleanup.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_co.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_common.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_hold.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_mr.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_parent.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_review.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_update.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/git_wt.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/gitcmp_helper.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/gitprompt.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/gl.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/gphotosync.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/linecount.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/moviemover.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/photodupe.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/phototidier.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/py_audit.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/readable.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/remdir.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/rmdupe.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/rpylint.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/splitpics.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/strreplace.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/sysmon.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/tfm.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/tfparse.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/__init__.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/colour.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/dc_curses.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/dc_defaults.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/dc_util.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/dircolors.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/docker.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/files.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/git.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/gitlab.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/path.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/popup.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/process.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/run.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/tfm_pane.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/tidy.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/venv_template.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/trimpath.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/venv_create.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/window_rename.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/xchmod.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/yamlcheck.py +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy.egg-info/dependency_links.txt +0 -0
- {skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: skilleter_thingy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.70
|
|
4
4
|
Summary: A collection of useful utilities, mainly aimed at making Git more friendly
|
|
5
5
|
Author-email: John Skilleter <john@skilleter.org.uk>
|
|
6
6
|
Project-URL: Home, https://skilleter.org.uk
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pyaml
|
|
|
18
18
|
Requires-Dist: pygit2
|
|
19
19
|
Requires-Dist: python-dateutil
|
|
20
20
|
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: tomlkit
|
|
21
22
|
|
|
22
23
|
# Thingy
|
|
23
24
|
|
|
@@ -7,7 +7,7 @@ name = "skilleter_thingy"
|
|
|
7
7
|
|
|
8
8
|
# Version must be incremented to install updated Thingy
|
|
9
9
|
|
|
10
|
-
version = "0.0.
|
|
10
|
+
version = "0.0.70"
|
|
11
11
|
|
|
12
12
|
authors = [
|
|
13
13
|
{name="John Skilleter", email="john@skilleter.org.uk"},
|
|
@@ -34,6 +34,7 @@ dependencies = [
|
|
|
34
34
|
"pygit2",
|
|
35
35
|
"python-dateutil",
|
|
36
36
|
"requests",
|
|
37
|
+
"tomlkit",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
[project.urls]
|
|
@@ -64,6 +65,7 @@ gitprompt = "skilleter_thingy:gitprompt.gitprompt"
|
|
|
64
65
|
gl = "skilleter_thingy:gl.gl"
|
|
65
66
|
gphotosync = "skilleter_thingy:gphotosync.gphotosync"
|
|
66
67
|
linecount = "skilleter_thingy:linecount.linecount"
|
|
68
|
+
mg = "skilleter_thingy:mg.mg"
|
|
67
69
|
moviemover = "skilleter_thingy:moviemover.moviemover"
|
|
68
70
|
photodupe = "skilleter_thingy:photodupe.photodupe"
|
|
69
71
|
phototidier = "skilleter_thingy:phototidier.phototidier"
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""mg - MultiGit - utility for managing multiple Git repos in a hierarchical directory tree"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import argparse
|
|
8
|
+
|
|
9
|
+
import tomlkit
|
|
10
|
+
|
|
11
|
+
import thingy.git2 as git
|
|
12
|
+
import thingy.colour as colour
|
|
13
|
+
|
|
14
|
+
################################################################################
|
|
15
|
+
|
|
16
|
+
"""Configuration file format:
|
|
17
|
+
|
|
18
|
+
[default]
|
|
19
|
+
# Default settings
|
|
20
|
+
default branch = name
|
|
21
|
+
|
|
22
|
+
[repos]
|
|
23
|
+
name = path
|
|
24
|
+
default branch = name
|
|
25
|
+
|
|
26
|
+
[git-repo-location] # Either absolute or relative to the directory where the configuration file is found
|
|
27
|
+
# Repo-specific settings to override default section
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# TODO: -j option to run in parallel
|
|
31
|
+
# TODO: init function
|
|
32
|
+
# TODO: Use the configuration file
|
|
33
|
+
# TODO: Don't use a fixed list of default branch names
|
|
34
|
+
# TODO: Output name of each git repo as it is processed as command sits there seeming to do nothing otherwise.
|
|
35
|
+
|
|
36
|
+
################################################################################
|
|
37
|
+
|
|
38
|
+
DEFAULT_CONFIG_FILE = 'multigit.toml'
|
|
39
|
+
|
|
40
|
+
DEFAULT_BRANCHES = ('main', 'scv-poc', 'master')
|
|
41
|
+
|
|
42
|
+
################################################################################
|
|
43
|
+
|
|
44
|
+
def error(msg, status=1):
|
|
45
|
+
"""Quit with an error"""
|
|
46
|
+
|
|
47
|
+
sys.stderr.write(f'{msg}\n')
|
|
48
|
+
sys.exit(status)
|
|
49
|
+
|
|
50
|
+
################################################################################
|
|
51
|
+
|
|
52
|
+
def show_progress(width, msg):
|
|
53
|
+
"""Show a single line progress message"""
|
|
54
|
+
|
|
55
|
+
name = msg[:width-1]
|
|
56
|
+
|
|
57
|
+
colour.write(f'{name}', newline=False)
|
|
58
|
+
|
|
59
|
+
if len(name) < width-1:
|
|
60
|
+
colour.write(' '*(width-len(name)), newline=False)
|
|
61
|
+
|
|
62
|
+
colour.write('\r', newline=False)
|
|
63
|
+
|
|
64
|
+
################################################################################
|
|
65
|
+
|
|
66
|
+
def find_git_repos(directory):
|
|
67
|
+
"""Locate and return a list of '.git' directory parent directories in the
|
|
68
|
+
specified path"""
|
|
69
|
+
|
|
70
|
+
git_repos = []
|
|
71
|
+
|
|
72
|
+
for root, dirs, _ in os.walk(directory):
|
|
73
|
+
if '.git' in dirs:
|
|
74
|
+
git_repos.append(root)
|
|
75
|
+
|
|
76
|
+
return git_repos
|
|
77
|
+
|
|
78
|
+
################################################################################
|
|
79
|
+
|
|
80
|
+
def mg_init(args, config, console):
|
|
81
|
+
"""Create or update the configuration"""
|
|
82
|
+
|
|
83
|
+
error('Not used - yet!')
|
|
84
|
+
|
|
85
|
+
if config:
|
|
86
|
+
print(f'Updating existing multigit configuration file - {args.config}')
|
|
87
|
+
error('Not supported yet')
|
|
88
|
+
else:
|
|
89
|
+
print(f'Creating new multigit configuration file - {args.config}')
|
|
90
|
+
|
|
91
|
+
# Search for .git directories
|
|
92
|
+
|
|
93
|
+
git_repos = find_git_repos(args.directory)
|
|
94
|
+
|
|
95
|
+
################################################################################
|
|
96
|
+
|
|
97
|
+
def mg_status(args, config, console):
|
|
98
|
+
"""Report Git status for any repo that has a non-empty status"""
|
|
99
|
+
|
|
100
|
+
for repo in find_git_repos(args.directory):
|
|
101
|
+
if not args.quiet:
|
|
102
|
+
show_progress(console.columns, repo)
|
|
103
|
+
|
|
104
|
+
status = git.status(path=repo)
|
|
105
|
+
branch = git.branch(path=repo)
|
|
106
|
+
|
|
107
|
+
if status or branch not in DEFAULT_BRANCHES:
|
|
108
|
+
if branch in DEFAULT_BRANCHES:
|
|
109
|
+
colour.write(f'[BOLD:{repo}]')
|
|
110
|
+
else:
|
|
111
|
+
colour.write(f'[BOLD:{repo}] - branch: [BLUE:{branch}]')
|
|
112
|
+
|
|
113
|
+
for entry in status:
|
|
114
|
+
if entry[0] == '??':
|
|
115
|
+
colour.write(f' Untracked: [BLUE:{entry[1]}]')
|
|
116
|
+
else:
|
|
117
|
+
colour.write(f' [BLUE:{entry}]')
|
|
118
|
+
|
|
119
|
+
colour.write()
|
|
120
|
+
|
|
121
|
+
################################################################################
|
|
122
|
+
|
|
123
|
+
def mg_fetch(args, config, console):
|
|
124
|
+
"""Run git fetch everywhere"""
|
|
125
|
+
|
|
126
|
+
for repo in find_git_repos(args.directory):
|
|
127
|
+
if not args.quiet:
|
|
128
|
+
show_progress(console.columns, repo)
|
|
129
|
+
|
|
130
|
+
result = git.fetch(path=repo)
|
|
131
|
+
|
|
132
|
+
if result:
|
|
133
|
+
colour.write(f'[BOLD:{repo}]')
|
|
134
|
+
for item in result:
|
|
135
|
+
if item.startswith('From '):
|
|
136
|
+
colour.write(f' [BLUE:{item}]')
|
|
137
|
+
else:
|
|
138
|
+
colour.write(f' {item}')
|
|
139
|
+
|
|
140
|
+
colour.write()
|
|
141
|
+
|
|
142
|
+
################################################################################
|
|
143
|
+
|
|
144
|
+
def mg_pull(args, config, console):
|
|
145
|
+
"""Run git pull everywhere"""
|
|
146
|
+
|
|
147
|
+
for repo in find_git_repos(args.directory):
|
|
148
|
+
if not args.quiet:
|
|
149
|
+
show_progress(console.columns, repo)
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
result = git.pull(path=repo)
|
|
153
|
+
except git.GitError as exc:
|
|
154
|
+
error(f'Error in {repo}: {exc}')
|
|
155
|
+
|
|
156
|
+
if result and result[0] != 'Already up-to-date.':
|
|
157
|
+
colour.write(f'[BOLD:{repo}]')
|
|
158
|
+
for item in result:
|
|
159
|
+
if item.startswith('Updating'):
|
|
160
|
+
colour.write(f' [BLUE:{item}]')
|
|
161
|
+
else:
|
|
162
|
+
colour.write(f' {item}')
|
|
163
|
+
|
|
164
|
+
colour.write()
|
|
165
|
+
|
|
166
|
+
################################################################################
|
|
167
|
+
|
|
168
|
+
def mg_push(args, config, console):
|
|
169
|
+
"""Run git push everywhere where the current branch isn't one of the defaults
|
|
170
|
+
and where the most recent commit was the current user and was on the branch
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
# TODO: Add option for force-push?
|
|
174
|
+
# TODO: Add option for manual confirmation?
|
|
175
|
+
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
################################################################################
|
|
179
|
+
|
|
180
|
+
def main():
|
|
181
|
+
"""Main function"""
|
|
182
|
+
|
|
183
|
+
commands = {
|
|
184
|
+
'init': mg_init,
|
|
185
|
+
'status': mg_status,
|
|
186
|
+
'fetch': mg_fetch,
|
|
187
|
+
'pull': mg_pull,
|
|
188
|
+
'push': mg_push,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# Parse args in the form COMMAND OPTIONS SUBCOMMAND SUBCOMMAND_OPTIONS PARAMETERS
|
|
192
|
+
|
|
193
|
+
parser = argparse.ArgumentParser(description='Gitlab commands')
|
|
194
|
+
|
|
195
|
+
parser.add_argument('--dryrun', '--dry-run', '-D', action='store_true', help='Dry-run comands')
|
|
196
|
+
parser.add_argument('--debug', '-d', action='store_true', help='Debug')
|
|
197
|
+
parser.add_argument('--verbose', '-v', action='store_true', help='Verbosity to the maximum')
|
|
198
|
+
parser.add_argument('--quiet', '-q', action='store_true', help='Minimal console output')
|
|
199
|
+
parser.add_argument('--config', '-c', action='store', default=DEFAULT_CONFIG_FILE, help=f'The configuration file (defaults to {DEFAULT_CONFIG_FILE})')
|
|
200
|
+
parser.add_argument('--directory', '--dir', action='store', default='.', help='The top-level directory of the multigit tree (defaults to the current directory)')
|
|
201
|
+
|
|
202
|
+
subparsers = parser.add_subparsers(dest='command')
|
|
203
|
+
|
|
204
|
+
# Subcommands - currently just init, status, fetch, pull, push, with more to come
|
|
205
|
+
|
|
206
|
+
parser_init = subparsers.add_parser('init', help='')
|
|
207
|
+
|
|
208
|
+
parser_status = subparsers.add_parser('status', help='Report git status in every repo that has one')
|
|
209
|
+
parser_fetch = subparsers.add_parser('fetch', help='Run git fetch in every repo')
|
|
210
|
+
parser_pull = subparsers.add_parser('pull', help='Run git pull in every repo')
|
|
211
|
+
parser_push = subparsers.add_parser('push', help='Run git push in every repo where the current branch isn\'t the default and the most recent commit was by the current user')
|
|
212
|
+
|
|
213
|
+
# Parse the command line
|
|
214
|
+
|
|
215
|
+
args = parser.parse_args()
|
|
216
|
+
|
|
217
|
+
# If the configuration file exists, read it
|
|
218
|
+
|
|
219
|
+
config = tomlkit.loads(args.config) if os.path.isfile(args.config) else None
|
|
220
|
+
|
|
221
|
+
# Get the console size
|
|
222
|
+
|
|
223
|
+
console = os.get_terminal_size()
|
|
224
|
+
|
|
225
|
+
# Run the subcommand
|
|
226
|
+
|
|
227
|
+
commands[args.command](args, config, console)
|
|
228
|
+
|
|
229
|
+
################################################################################
|
|
230
|
+
|
|
231
|
+
def mg():
|
|
232
|
+
"""Entry point"""
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
main()
|
|
236
|
+
except KeyboardInterrupt:
|
|
237
|
+
sys.exit(1)
|
|
238
|
+
except BrokenPipeError:
|
|
239
|
+
sys.exit(2)
|
|
240
|
+
|
|
241
|
+
################################################################################
|
|
242
|
+
|
|
243
|
+
if __name__ == '__main__':
|
|
244
|
+
mg()
|
|
@@ -42,12 +42,12 @@ import thingy.gitlab as gitlab
|
|
|
42
42
|
class GitError(run.RunError):
|
|
43
43
|
""" Run exception """
|
|
44
44
|
|
|
45
|
-
def __init__(self, msg,
|
|
46
|
-
super().__init__(msg,
|
|
45
|
+
def __init__(self, msg, exit_status=1):
|
|
46
|
+
super().__init__(msg, exit_status)
|
|
47
47
|
|
|
48
48
|
################################################################################
|
|
49
49
|
|
|
50
|
-
def git(cmd, stdout=None, stderr=None):
|
|
50
|
+
def git(cmd, stdout=None, stderr=None, path=None):
|
|
51
51
|
""" Wrapper for run.run that raises a GitError instead of RunError
|
|
52
52
|
so that Git module users do not to include the run module just
|
|
53
53
|
to get the exception.
|
|
@@ -55,24 +55,34 @@ def git(cmd, stdout=None, stderr=None):
|
|
|
55
55
|
|
|
56
56
|
logging.debug('Running git %s', ' '.join(cmd))
|
|
57
57
|
|
|
58
|
+
git_cmd = ['git']
|
|
59
|
+
|
|
60
|
+
if path:
|
|
61
|
+
git_cmd += ['-C', path]
|
|
62
|
+
|
|
58
63
|
try:
|
|
59
|
-
return run.run(
|
|
64
|
+
return run.run(git_cmd + cmd, stdout=stdout, stderr=stderr)
|
|
60
65
|
except run.RunError as exc:
|
|
61
66
|
raise GitError(exc.msg, exc.status)
|
|
62
67
|
|
|
63
68
|
################################################################################
|
|
64
69
|
|
|
65
|
-
def git_run_status(cmd, stdout=None, stderr=None):
|
|
70
|
+
def git_run_status(cmd, stdout=None, stderr=None, path=None):
|
|
66
71
|
""" Wrapper for run.run that returns the output and status, and
|
|
67
72
|
does not raise an exception on error.
|
|
68
73
|
Optionally redirect stdout and stderr as specified. """
|
|
69
74
|
|
|
70
75
|
logging.debug('Running git %s', ' '.join(cmd))
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
git_cmd = ['git']
|
|
78
|
+
|
|
79
|
+
if path:
|
|
80
|
+
git_cmd += ['-C', path]
|
|
81
|
+
|
|
82
|
+
result = subprocess.run(git_cmd + cmd,
|
|
73
83
|
stdout=stdout or subprocess.PIPE,
|
|
74
|
-
stderr=
|
|
75
|
-
text=True,
|
|
84
|
+
stderr=stderr or subprocess.PIPE,
|
|
85
|
+
text=True, check=False,
|
|
76
86
|
errors='ignore',
|
|
77
87
|
universal_newlines=True)
|
|
78
88
|
|
|
@@ -137,11 +147,11 @@ def iscommit(commit, remote=False, remote_only=False):
|
|
|
137
147
|
|
|
138
148
|
################################################################################
|
|
139
149
|
|
|
140
|
-
def branch(branchname='HEAD'):
|
|
150
|
+
def branch(branchname='HEAD', path=None):
|
|
141
151
|
""" Return the name of the current git branch or None"""
|
|
142
152
|
|
|
143
153
|
try:
|
|
144
|
-
return git(['symbolic-ref', '--short', '-q', branchname])[0]
|
|
154
|
+
return git(['symbolic-ref', '--short', '-q', branchname], path=path)[0]
|
|
145
155
|
except GitError:
|
|
146
156
|
return None
|
|
147
157
|
|
|
@@ -178,7 +188,7 @@ def current_commit(short=False):
|
|
|
178
188
|
|
|
179
189
|
################################################################################
|
|
180
190
|
|
|
181
|
-
def pull(repo=None, all=False):
|
|
191
|
+
def pull(repo=None, all=False, path=None):
|
|
182
192
|
""" Run a git pull """
|
|
183
193
|
|
|
184
194
|
cmd = ['pull']
|
|
@@ -189,7 +199,7 @@ def pull(repo=None, all=False):
|
|
|
189
199
|
if repo:
|
|
190
200
|
cmd.append(repo)
|
|
191
201
|
|
|
192
|
-
return git(cmd)
|
|
202
|
+
return git(cmd, path=path)
|
|
193
203
|
|
|
194
204
|
################################################################################
|
|
195
205
|
|
|
@@ -227,15 +237,15 @@ def set_upstream(branch, upstream=None):
|
|
|
227
237
|
""" Set the default upstream branch """
|
|
228
238
|
|
|
229
239
|
if not upstream:
|
|
230
|
-
upstream = 'origin
|
|
240
|
+
upstream = f'origin/{branch}'
|
|
231
241
|
|
|
232
|
-
cmd = ['branch', '--set-upstream-to
|
|
242
|
+
cmd = ['branch', f'--set-upstream-to={upstream}', branch]
|
|
233
243
|
|
|
234
244
|
return git(cmd)
|
|
235
245
|
|
|
236
246
|
################################################################################
|
|
237
247
|
|
|
238
|
-
def fetch(all=False):
|
|
248
|
+
def fetch(all=False, path=None):
|
|
239
249
|
""" Run git fetch """
|
|
240
250
|
|
|
241
251
|
cmd = ['fetch']
|
|
@@ -243,7 +253,7 @@ def fetch(all=False):
|
|
|
243
253
|
if all:
|
|
244
254
|
cmd.append('--all')
|
|
245
255
|
|
|
246
|
-
return git(cmd)
|
|
256
|
+
return git(cmd, path=path)
|
|
247
257
|
|
|
248
258
|
################################################################################
|
|
249
259
|
|
|
@@ -310,9 +320,9 @@ def remotes():
|
|
|
310
320
|
""" Return the list of git remotes """
|
|
311
321
|
|
|
312
322
|
repo = pygit2.Repository(os.getcwd())
|
|
313
|
-
|
|
323
|
+
|
|
314
324
|
git_remotes = {}
|
|
315
|
-
|
|
325
|
+
|
|
316
326
|
for name in repo.remotes.names():
|
|
317
327
|
git_remotes[name] = repo.remotes[name].url
|
|
318
328
|
|
|
@@ -324,7 +334,7 @@ def remote_names():
|
|
|
324
334
|
""" Return the list of remote names """
|
|
325
335
|
|
|
326
336
|
repo = pygit2.Repository(os.getcwd())
|
|
327
|
-
|
|
337
|
+
|
|
328
338
|
results = list(repo.remotes.names())
|
|
329
339
|
|
|
330
340
|
return results
|
|
@@ -343,7 +353,7 @@ def project(short=False):
|
|
|
343
353
|
name = git_remotes[remote].split('//')[-1].split('/', 1)[-1]
|
|
344
354
|
break
|
|
345
355
|
|
|
346
|
-
|
|
356
|
+
if '@' in git_remotes[remote]:
|
|
347
357
|
name = git_remotes[remote].split(':')[-1]
|
|
348
358
|
break
|
|
349
359
|
|
|
@@ -386,10 +396,10 @@ def status_info(ignored=False, untracked=False):
|
|
|
386
396
|
if results:
|
|
387
397
|
result = results[0].split('\0')
|
|
388
398
|
|
|
389
|
-
for
|
|
390
|
-
if len(
|
|
391
|
-
git_status =
|
|
392
|
-
name =
|
|
399
|
+
for r in result:
|
|
400
|
+
if len(r) > 3 and r[2] == ' ':
|
|
401
|
+
git_status = r[0:2]
|
|
402
|
+
name = r[3:]
|
|
393
403
|
|
|
394
404
|
info[name] = git_status
|
|
395
405
|
|
|
@@ -397,7 +407,7 @@ def status_info(ignored=False, untracked=False):
|
|
|
397
407
|
|
|
398
408
|
################################################################################
|
|
399
409
|
|
|
400
|
-
def status(ignored=False, untracked=False):
|
|
410
|
+
def status(ignored=False, untracked=False, path=None):
|
|
401
411
|
""" Git status, optionally include files ignored in .gitignore and/or
|
|
402
412
|
untracked files.
|
|
403
413
|
Similar to status_info, but returns data as a list, rather than a
|
|
@@ -411,7 +421,7 @@ def status(ignored=False, untracked=False):
|
|
|
411
421
|
if untracked:
|
|
412
422
|
cmd.append('--untracked-files=all')
|
|
413
423
|
|
|
414
|
-
results = git(cmd)
|
|
424
|
+
results = git(cmd, path=path)
|
|
415
425
|
|
|
416
426
|
# Nested list of results. For each entry:
|
|
417
427
|
# item 0 is the status where: M=modified, A=added, D=deleted, R=renamed, C=copied, U=unmerged, ?=untracked, !=ignored
|
|
@@ -592,7 +602,7 @@ def diff_status(commit1, commit2='HEAD'):
|
|
|
592
602
|
def show(revision, filename, outfile=None):
|
|
593
603
|
""" Return the output from git show revision:filename """
|
|
594
604
|
|
|
595
|
-
return git(['show', '
|
|
605
|
+
return git(['show', f'{revision}:{filename}'], stdout=outfile)
|
|
596
606
|
|
|
597
607
|
################################################################################
|
|
598
608
|
|
|
@@ -732,7 +742,7 @@ def config_get(section, key, source=LOCAL, defaultvalue=None):
|
|
|
732
742
|
elif source == SYSTEM:
|
|
733
743
|
cmd.append('--system')
|
|
734
744
|
|
|
735
|
-
cmd += ['--get', '
|
|
745
|
+
cmd += ['--get', f'{section}.{key}']
|
|
736
746
|
|
|
737
747
|
try:
|
|
738
748
|
return git(cmd)[0]
|
|
@@ -751,7 +761,7 @@ def config_set(section, key, value, source=LOCAL):
|
|
|
751
761
|
elif source == SYSTEM:
|
|
752
762
|
cmd.append('--system')
|
|
753
763
|
|
|
754
|
-
cmd += ['--replace-all', '
|
|
764
|
+
cmd += ['--replace-all', f'{section}.{key}', value]
|
|
755
765
|
|
|
756
766
|
return git(cmd)
|
|
757
767
|
|
|
@@ -767,7 +777,7 @@ def config_rm(section, key, source=LOCAL):
|
|
|
767
777
|
elif source == SYSTEM:
|
|
768
778
|
cmd.append('--system')
|
|
769
779
|
|
|
770
|
-
cmd += ['--unset', '
|
|
780
|
+
cmd += ['--unset', f'{section}.{key}']
|
|
771
781
|
|
|
772
782
|
return git(cmd)
|
|
773
783
|
|
|
@@ -779,7 +789,7 @@ def ref(fields=('objectname'), sort=None, remotes=False):
|
|
|
779
789
|
cmd = ['for-each-ref']
|
|
780
790
|
|
|
781
791
|
if sort:
|
|
782
|
-
cmd.append('--sort
|
|
792
|
+
cmd.append(f'--sort={sort}')
|
|
783
793
|
|
|
784
794
|
field_list = []
|
|
785
795
|
for field in fields:
|
|
@@ -857,7 +867,7 @@ def remote_prune(remote, dry_run=False):
|
|
|
857
867
|
def get_commits(commit1, commit2):
|
|
858
868
|
""" Get a list of commits separating two commits """
|
|
859
869
|
|
|
860
|
-
return git(['rev-list', commit1, '
|
|
870
|
+
return git(['rev-list', commit1, f'^{commit2}'])
|
|
861
871
|
|
|
862
872
|
################################################################################
|
|
863
873
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: skilleter_thingy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.70
|
|
4
4
|
Summary: A collection of useful utilities, mainly aimed at making Git more friendly
|
|
5
5
|
Author-email: John Skilleter <john@skilleter.org.uk>
|
|
6
6
|
Project-URL: Home, https://skilleter.org.uk
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pyaml
|
|
|
18
18
|
Requires-Dist: pygit2
|
|
19
19
|
Requires-Dist: python-dateutil
|
|
20
20
|
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: tomlkit
|
|
21
22
|
|
|
22
23
|
# Thingy
|
|
23
24
|
|
{skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy.egg-info/entry_points.txt
RENAMED
|
@@ -23,6 +23,7 @@ gitprompt = skilleter_thingy:gitprompt.gitprompt
|
|
|
23
23
|
gl = skilleter_thingy:gl.gl
|
|
24
24
|
gphotosync = skilleter_thingy:gphotosync.gphotosync
|
|
25
25
|
linecount = skilleter_thingy:linecount.linecount
|
|
26
|
+
mg = skilleter_thingy:mg.mg
|
|
26
27
|
moviemover = skilleter_thingy:moviemover.moviemover
|
|
27
28
|
photodupe = skilleter_thingy:photodupe.photodupe
|
|
28
29
|
phototidier = skilleter_thingy:phototidier.phototidier
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy/thingy/venv_template.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{skilleter_thingy-0.0.69 → skilleter_thingy-0.0.70}/skilleter_thingy.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|