githubtakeout 0.1.4__tar.gz → 0.1.6__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.
- {githubtakeout-0.1.4 → githubtakeout-0.1.6}/PKG-INFO +2 -1
- {githubtakeout-0.1.4 → githubtakeout-0.1.6}/pyproject.toml +25 -4
- githubtakeout-0.1.6/src/__init__.py +0 -0
- {githubtakeout-0.1.4 → githubtakeout-0.1.6/src}/githubtakeout.egg-info/PKG-INFO +2 -1
- githubtakeout-0.1.6/src/githubtakeout.egg-info/SOURCES.txt +12 -0
- {githubtakeout-0.1.4 → githubtakeout-0.1.6/src}/githubtakeout.egg-info/requires.txt +1 -0
- githubtakeout-0.1.6/src/githubtakeout.egg-info/top_level.txt +3 -0
- {githubtakeout-0.1.4 → githubtakeout-0.1.6/src}/githubtakeout.py +84 -89
- githubtakeout-0.1.6/src/progress.py +73 -0
- githubtakeout-0.1.4/githubtakeout.egg-info/SOURCES.txt +0 -10
- githubtakeout-0.1.4/githubtakeout.egg-info/top_level.txt +0 -1
- {githubtakeout-0.1.4 → githubtakeout-0.1.6}/LICENSE +0 -0
- {githubtakeout-0.1.4 → githubtakeout-0.1.6}/README.md +0 -0
- {githubtakeout-0.1.4 → githubtakeout-0.1.6}/setup.cfg +0 -0
- {githubtakeout-0.1.4 → githubtakeout-0.1.6/src}/githubtakeout.egg-info/dependency_links.txt +0 -0
- {githubtakeout-0.1.4 → githubtakeout-0.1.6/src}/githubtakeout.egg-info/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: githubtakeout
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Archive public Git Repos and Gists from GitHub
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -20,6 +20,7 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: GitPython==3.1.44
|
|
22
22
|
Requires-Dist: PyGithub==2.6.1
|
|
23
|
+
Requires-Dist: rich==14.0.0
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
# githubtakeout
|
|
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "githubtakeout"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.6"
|
|
8
8
|
description = "Archive public Git Repos and Gists from GitHub"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
11
11
|
authors = [{name = "Corey Goldberg"}]
|
|
12
12
|
maintainers = [{name = "Corey Goldberg"}]
|
|
13
13
|
readme = "README.md"
|
|
14
|
-
requires-python = ">=
|
|
14
|
+
requires-python = ">=3.12"
|
|
15
15
|
keywords = ["git", "github", "archive", "backup", "export", "takeout"]
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Environment :: Console",
|
|
@@ -19,11 +19,12 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python",
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
21
21
|
"Programming Language :: Python :: 3.12",
|
|
22
|
-
"Programming Language :: Python :: 3.13"
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
25
|
"GitPython==3.1.44",
|
|
26
|
-
"PyGithub==2.6.1"
|
|
26
|
+
"PyGithub==2.6.1",
|
|
27
|
+
"rich==14.0.0",
|
|
27
28
|
]
|
|
28
29
|
|
|
29
30
|
[project.scripts]
|
|
@@ -33,3 +34,23 @@ githubtakeout = "githubtakeout:main"
|
|
|
33
34
|
homepage = "https://github.com/cgoldberg/githubtakeout"
|
|
34
35
|
source = "https://github.com/cgoldberg/githubtakeout"
|
|
35
36
|
download = "https://pypi.org/project/githubtakeout"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools]
|
|
39
|
+
package-dir = {"" = "src"}
|
|
40
|
+
|
|
41
|
+
[tool.black]
|
|
42
|
+
line-length = 88
|
|
43
|
+
target-version = ["py312"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
line-length = 88
|
|
47
|
+
respect-gitignore = true
|
|
48
|
+
target-version = "py312"
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
extend-select = ["E4", "E7", "E9", "F", "I", "E501", "RUF022"]
|
|
52
|
+
fixable = ["ALL"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.format]
|
|
55
|
+
docstring-code-format = true
|
|
56
|
+
docstring-code-line-length = 88
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: githubtakeout
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Archive public Git Repos and Gists from GitHub
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -20,6 +20,7 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: GitPython==3.1.44
|
|
22
22
|
Requires-Dist: PyGithub==2.6.1
|
|
23
|
+
Requires-Dist: rich==14.0.0
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
# githubtakeout
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/__init__.py
|
|
5
|
+
src/githubtakeout.py
|
|
6
|
+
src/progress.py
|
|
7
|
+
src/githubtakeout.egg-info/PKG-INFO
|
|
8
|
+
src/githubtakeout.egg-info/SOURCES.txt
|
|
9
|
+
src/githubtakeout.egg-info/dependency_links.txt
|
|
10
|
+
src/githubtakeout.egg-info/entry_points.txt
|
|
11
|
+
src/githubtakeout.egg-info/requires.txt
|
|
12
|
+
src/githubtakeout.egg-info/top_level.txt
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
# Corey Goldberg, 2025
|
|
2
|
+
# License: MIT
|
|
3
|
+
|
|
4
|
+
"""Archive Git Repos and Gists from GitHub"""
|
|
2
5
|
|
|
3
6
|
import argparse
|
|
4
7
|
import getpass
|
|
5
8
|
import logging
|
|
9
|
+
import math
|
|
6
10
|
import os
|
|
7
11
|
import shutil
|
|
8
12
|
import stat
|
|
@@ -10,47 +14,57 @@ import sys
|
|
|
10
14
|
import tarfile
|
|
11
15
|
import urllib
|
|
12
16
|
import zipfile
|
|
13
|
-
|
|
14
17
|
from pathlib import Path
|
|
15
18
|
from timeit import default_timer
|
|
16
19
|
|
|
17
20
|
import git
|
|
18
21
|
import github
|
|
19
22
|
|
|
23
|
+
from progress import GitProgress
|
|
20
24
|
|
|
21
|
-
logging.basicConfig(level=logging.INFO, format=
|
|
25
|
+
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
24
28
|
|
|
29
|
+
def convert_size(size_bytes):
|
|
30
|
+
if size_bytes == 0:
|
|
31
|
+
return "0 B"
|
|
32
|
+
size_name = ("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB")
|
|
33
|
+
i = int(math.floor(math.log(size_bytes, 1024)))
|
|
34
|
+
p = math.pow(1024, i)
|
|
35
|
+
s = round(size_bytes / p, 2)
|
|
36
|
+
return f"{s} {size_name[i]}"
|
|
37
|
+
|
|
38
|
+
|
|
25
39
|
def add_creds(url, username, token):
|
|
26
40
|
if token is None:
|
|
27
41
|
new_url = url
|
|
28
42
|
else:
|
|
29
43
|
username = urllib.parse.quote(username)
|
|
30
44
|
url_parts = urllib.parse.urlparse(url)
|
|
31
|
-
netloc = f
|
|
45
|
+
netloc = f"{username}:{token}@{url_parts.netloc}"
|
|
32
46
|
new_url = urllib.parse.urlunparse(url_parts._replace(netloc=netloc))
|
|
33
47
|
return new_url
|
|
34
48
|
|
|
35
49
|
|
|
36
|
-
def archive(local_repo_dir, archive_format=
|
|
37
|
-
if archive_format not in (
|
|
38
|
-
raise ValueError(f
|
|
50
|
+
def archive(local_repo_dir, archive_format="zip", is_gist=False):
|
|
51
|
+
if archive_format not in ("tar", "zip"):
|
|
52
|
+
raise ValueError(f"{archive_format} is not a valid archive format")
|
|
39
53
|
base_name = os.path.basename(local_repo_dir)
|
|
40
|
-
extension =
|
|
41
|
-
archive_name = f
|
|
54
|
+
extension = "tar.gz" if archive_format == "tar" else archive_format
|
|
55
|
+
archive_name = f"{base_name}.{extension}"
|
|
42
56
|
if is_gist:
|
|
43
|
-
archive_name = f
|
|
57
|
+
archive_name = f"gist-{archive_name}"
|
|
44
58
|
parent_dir = os.path.dirname(local_repo_dir)
|
|
45
59
|
archive_path = os.path.join(parent_dir, archive_name)
|
|
46
|
-
logger.info(f
|
|
47
|
-
if archive_format ==
|
|
48
|
-
with tarfile.open(archive_path,
|
|
60
|
+
logger.info(f"creating archive: {archive_path}")
|
|
61
|
+
if archive_format == "tar":
|
|
62
|
+
with tarfile.open(archive_path, "w:gz") as tar_archive:
|
|
49
63
|
tar_archive.add(local_repo_dir, arcname=base_name)
|
|
50
|
-
elif archive_format ==
|
|
51
|
-
with zipfile.ZipFile(archive_path,
|
|
64
|
+
elif archive_format == "zip":
|
|
65
|
+
with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as zip_archive:
|
|
52
66
|
repo_path = Path(local_repo_dir)
|
|
53
|
-
for entry in repo_path.rglob(
|
|
67
|
+
for entry in repo_path.rglob("*"):
|
|
54
68
|
path = os.path.join(base_name, entry.relative_to(repo_path))
|
|
55
69
|
zip_archive.write(entry, arcname=path)
|
|
56
70
|
return archive_path
|
|
@@ -60,13 +74,17 @@ def clone(repo_url, local_repo_dir, include_history):
|
|
|
60
74
|
try:
|
|
61
75
|
if include_history:
|
|
62
76
|
# full clone
|
|
63
|
-
repo = git.Repo.clone_from(
|
|
77
|
+
repo = git.Repo.clone_from(
|
|
78
|
+
url=repo_url, to_path=local_repo_dir, progress=GitProgress()
|
|
79
|
+
)
|
|
64
80
|
else:
|
|
65
81
|
# shallow clone (no commit history or branches)
|
|
66
|
-
repo = git.Repo.clone_from(
|
|
82
|
+
repo = git.Repo.clone_from(
|
|
83
|
+
url=repo_url, to_path=local_repo_dir, depth=1, progress=GitProgress()
|
|
84
|
+
)
|
|
67
85
|
except git.GitCommandError as e:
|
|
68
86
|
logger.error(e)
|
|
69
|
-
sys.exit(
|
|
87
|
+
sys.exit("error: failed cloning repo")
|
|
70
88
|
finally:
|
|
71
89
|
try:
|
|
72
90
|
# release resources
|
|
@@ -76,8 +94,9 @@ def clone(repo_url, local_repo_dir, include_history):
|
|
|
76
94
|
pass
|
|
77
95
|
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
def clone_and_archive_repo(
|
|
98
|
+
repo_url, local_repo_dir, archive_format, include_history, is_gist=False
|
|
99
|
+
):
|
|
81
100
|
def remove_readonly(func, path, _):
|
|
82
101
|
# This is necessary so rmtree() doesn't fail if there are any readonly
|
|
83
102
|
# dirs/files when trying to delete. This seems to happen after cloning on
|
|
@@ -90,26 +109,27 @@ def clone_and_archive_repo(repo_url, local_repo_dir, archive_format, include_his
|
|
|
90
109
|
os.chmod(path, stat.S_IWRITE)
|
|
91
110
|
# try again
|
|
92
111
|
func(path)
|
|
112
|
+
|
|
93
113
|
try:
|
|
94
114
|
# delete repo if it already exists
|
|
95
115
|
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
96
116
|
except FileNotFoundError:
|
|
97
117
|
pass
|
|
98
|
-
repo_name = urllib.parse.urlparse(repo_url).path.lstrip(
|
|
99
|
-
logger.info(f
|
|
118
|
+
repo_name = urllib.parse.urlparse(repo_url).path.lstrip("/")
|
|
119
|
+
logger.info(f"cloning repo: {repo_name} to {local_repo_dir}")
|
|
100
120
|
start = default_timer()
|
|
101
121
|
clone(repo_url, local_repo_dir, include_history)
|
|
102
122
|
if not include_history:
|
|
103
123
|
# delete the .git directory if we are not saving history
|
|
104
124
|
try:
|
|
105
|
-
git_dir = os.path.join(local_repo_dir,
|
|
125
|
+
git_dir = os.path.join(local_repo_dir, ".git")
|
|
106
126
|
shutil.rmtree(git_dir, onexc=remove_readonly)
|
|
107
127
|
except FileNotFoundError:
|
|
108
128
|
pass
|
|
109
129
|
archive_path = archive(local_repo_dir, archive_format, is_gist)
|
|
110
|
-
|
|
111
|
-
logger.info(f
|
|
112
|
-
logger.info(
|
|
130
|
+
size = convert_size(os.path.getsize(archive_path))
|
|
131
|
+
logger.info(f"archive size: {size}")
|
|
132
|
+
logger.info("deleting repo")
|
|
113
133
|
try:
|
|
114
134
|
# delete repo after archive is created
|
|
115
135
|
shutil.rmtree(local_repo_dir, onexc=remove_readonly)
|
|
@@ -117,18 +137,18 @@ def clone_and_archive_repo(repo_url, local_repo_dir, archive_format, include_his
|
|
|
117
137
|
pass
|
|
118
138
|
elapsed = default_timer() - start
|
|
119
139
|
base_name = os.path.basename(local_repo_dir)
|
|
120
|
-
logger.info(f
|
|
140
|
+
logger.info(f"successfully backed up '{base_name}' repo in {elapsed:.3f} secs\n")
|
|
121
141
|
|
|
122
142
|
|
|
123
143
|
def get_user(username, prompt_token):
|
|
124
144
|
if prompt_token:
|
|
125
|
-
token = getpass.getpass(
|
|
145
|
+
token = getpass.getpass("Token:")
|
|
126
146
|
if not token:
|
|
127
|
-
sys.exit(
|
|
147
|
+
sys.exit("error: auth token cannot be empty")
|
|
128
148
|
auth = github.Auth.Token(token)
|
|
129
149
|
gh = github.Github(auth=auth)
|
|
130
150
|
else:
|
|
131
|
-
token = os.getenv(
|
|
151
|
+
token = os.getenv("GITHUB_TOKEN")
|
|
132
152
|
if token is not None:
|
|
133
153
|
auth = github.Auth.Token(token)
|
|
134
154
|
gh = github.Github(auth=auth)
|
|
@@ -138,8 +158,8 @@ def get_user(username, prompt_token):
|
|
|
138
158
|
try:
|
|
139
159
|
user = gh.get_user(username)
|
|
140
160
|
except github.GithubException as e:
|
|
141
|
-
if e.data[
|
|
142
|
-
sys.exit(f
|
|
161
|
+
if e.data["status"] == "404":
|
|
162
|
+
sys.exit(f"error: user '{username}' not found")
|
|
143
163
|
else:
|
|
144
164
|
raise e
|
|
145
165
|
repos = user.get_repos()
|
|
@@ -147,42 +167,45 @@ def get_user(username, prompt_token):
|
|
|
147
167
|
# you need to be authenticated and then call the API
|
|
148
168
|
# with no username to get private repos
|
|
149
169
|
user = gh.get_user()
|
|
150
|
-
repos = user.get_repos(affiliation=
|
|
170
|
+
repos = user.get_repos(affiliation="owner")
|
|
151
171
|
try:
|
|
152
172
|
# this just makes an API request so we can exit if unauthorized
|
|
153
173
|
_ = repos.totalCount
|
|
154
174
|
except github.GithubException as e:
|
|
155
|
-
if e.data[
|
|
156
|
-
sys.exit(f
|
|
175
|
+
if e.data["status"] == "401":
|
|
176
|
+
sys.exit(f"error: invalid auth token for user '{username}'")
|
|
157
177
|
else:
|
|
158
178
|
raise e
|
|
159
179
|
gists = user.get_gists()
|
|
160
180
|
return user, repos, gists, token
|
|
161
181
|
|
|
162
182
|
|
|
163
|
-
def run(
|
|
164
|
-
|
|
183
|
+
def run(
|
|
184
|
+
username,
|
|
185
|
+
base_dir,
|
|
186
|
+
archive_format,
|
|
187
|
+
include_gists,
|
|
188
|
+
include_history,
|
|
189
|
+
list_only,
|
|
190
|
+
prompt_token,
|
|
191
|
+
):
|
|
192
|
+
working_dir = os.path.join(base_dir, "backups")
|
|
165
193
|
user, repos, gists, token = get_user(username, prompt_token)
|
|
166
194
|
num_repos = repos.totalCount
|
|
167
195
|
if not list_only:
|
|
168
|
-
logger.info(f
|
|
169
|
-
logger.info(f
|
|
196
|
+
logger.info(f"creating archives in: {working_dir}\n")
|
|
197
|
+
logger.info(f"found {num_repos} repos for user '{username}'\n")
|
|
170
198
|
for repo in repos:
|
|
171
199
|
local_repo_dir = os.path.join(working_dir, repo.name)
|
|
172
200
|
url = add_creds(repo.clone_url, username, token)
|
|
173
201
|
if list_only:
|
|
174
202
|
logger.info(url)
|
|
175
203
|
else:
|
|
176
|
-
clone_and_archive_repo(
|
|
177
|
-
url,
|
|
178
|
-
local_repo_dir,
|
|
179
|
-
archive_format,
|
|
180
|
-
include_history
|
|
181
|
-
)
|
|
204
|
+
clone_and_archive_repo(url, local_repo_dir, archive_format, include_history)
|
|
182
205
|
if include_gists:
|
|
183
206
|
num_gists = gists.totalCount
|
|
184
|
-
logger.info(
|
|
185
|
-
logger.info(f
|
|
207
|
+
logger.info("")
|
|
208
|
+
logger.info(f"found {num_gists} gists for user '{username}'\n")
|
|
186
209
|
for gist in gists:
|
|
187
210
|
local_repo_dir = os.path.join(working_dir, gist.id)
|
|
188
211
|
url = add_creds(gist.git_pull_url, username, token)
|
|
@@ -190,55 +213,31 @@ def run(username, base_dir, archive_format, include_gists, include_history, list
|
|
|
190
213
|
logger.info(url)
|
|
191
214
|
else:
|
|
192
215
|
clone_and_archive_repo(
|
|
193
|
-
url,
|
|
194
|
-
local_repo_dir,
|
|
195
|
-
archive_format,
|
|
196
|
-
include_history,
|
|
197
|
-
is_gist=True
|
|
216
|
+
url, local_repo_dir, archive_format, include_history, is_gist=True
|
|
198
217
|
)
|
|
199
218
|
|
|
200
219
|
|
|
201
220
|
def main():
|
|
202
221
|
if sys.version_info < (3, 12):
|
|
203
|
-
sys.exit(
|
|
222
|
+
sys.exit("sorry, this program requires Python 3.12+")
|
|
204
223
|
parser = argparse.ArgumentParser()
|
|
224
|
+
parser.add_argument("username", help="github username")
|
|
225
|
+
parser.add_argument("--dir", default=os.getcwd(), help="output directory")
|
|
226
|
+
parser.add_argument("--format", default="zip", help="archive format (tar, zip)")
|
|
205
227
|
parser.add_argument(
|
|
206
|
-
|
|
207
|
-
help='github username'
|
|
208
|
-
)
|
|
209
|
-
parser.add_argument(
|
|
210
|
-
'--dir',
|
|
211
|
-
default=os.getcwd(),
|
|
212
|
-
help='output directory'
|
|
213
|
-
)
|
|
214
|
-
parser.add_argument(
|
|
215
|
-
'--format',
|
|
216
|
-
default='zip',
|
|
217
|
-
help='archive format (tar, zip)'
|
|
218
|
-
)
|
|
219
|
-
parser.add_argument(
|
|
220
|
-
'--gists',
|
|
221
|
-
action='store_true',
|
|
222
|
-
default=False,
|
|
223
|
-
help='include gists'
|
|
228
|
+
"--gists", action="store_true", default=False, help="include gists"
|
|
224
229
|
)
|
|
225
230
|
parser.add_argument(
|
|
226
|
-
|
|
227
|
-
action=
|
|
231
|
+
"--history",
|
|
232
|
+
action="store_true",
|
|
228
233
|
default=False,
|
|
229
|
-
help=
|
|
234
|
+
help="include commit history and branches (.git directory)",
|
|
230
235
|
)
|
|
231
236
|
parser.add_argument(
|
|
232
|
-
|
|
233
|
-
action='store_true',
|
|
234
|
-
default=False,
|
|
235
|
-
help='list repos only'
|
|
237
|
+
"--list", action="store_true", default=False, help="list repos only"
|
|
236
238
|
)
|
|
237
239
|
parser.add_argument(
|
|
238
|
-
|
|
239
|
-
action='store_true',
|
|
240
|
-
default=False,
|
|
241
|
-
help='prompt for auth token'
|
|
240
|
+
"--token", action="store_true", default=False, help="prompt for auth token"
|
|
242
241
|
)
|
|
243
242
|
args = parser.parse_args()
|
|
244
243
|
try:
|
|
@@ -249,11 +248,7 @@ def main():
|
|
|
249
248
|
include_gists=args.gists,
|
|
250
249
|
include_history=args.history,
|
|
251
250
|
list_only=args.list,
|
|
252
|
-
prompt_token=args.token
|
|
251
|
+
prompt_token=args.token,
|
|
253
252
|
)
|
|
254
253
|
except KeyboardInterrupt:
|
|
255
|
-
sys.exit(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if __name__ == '__main__':
|
|
259
|
-
main()
|
|
254
|
+
sys.exit("\nexiting program ...")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import git
|
|
2
|
+
from rich import console, progress
|
|
3
|
+
|
|
4
|
+
# adapted from: https://stackoverflow.com/a/71285627/16148
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GitProgress(git.RemoteProgress):
|
|
8
|
+
OP_CODES = [
|
|
9
|
+
"BEGIN",
|
|
10
|
+
"CHECKING_OUT",
|
|
11
|
+
"COMPRESSING",
|
|
12
|
+
"COUNTING",
|
|
13
|
+
"END",
|
|
14
|
+
"FINDING_SOURCES",
|
|
15
|
+
"RECEIVING",
|
|
16
|
+
"RESOLVING",
|
|
17
|
+
"WRITING",
|
|
18
|
+
]
|
|
19
|
+
OP_CODE_MAP = {
|
|
20
|
+
getattr(git.RemoteProgress, _op_code): _op_code for _op_code in OP_CODES
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.progressbar = progress.Progress(
|
|
26
|
+
progress.SpinnerColumn(),
|
|
27
|
+
progress.TextColumn("[progress.description]{task.description}"),
|
|
28
|
+
progress.BarColumn(),
|
|
29
|
+
progress.TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
30
|
+
"eta",
|
|
31
|
+
progress.TimeRemainingColumn(),
|
|
32
|
+
progress.TextColumn("{task.fields[message]}"),
|
|
33
|
+
console=console.Console(),
|
|
34
|
+
transient=False,
|
|
35
|
+
)
|
|
36
|
+
self.progressbar.start()
|
|
37
|
+
self.active_task = None
|
|
38
|
+
|
|
39
|
+
def __del__(self):
|
|
40
|
+
self.progressbar.stop()
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def get_curr_op(cls, op_code):
|
|
44
|
+
"""Get OP name from OP code."""
|
|
45
|
+
# Remove BEGIN-flag and END-flag and get op name
|
|
46
|
+
op_code_masked = op_code & cls.OP_MASK
|
|
47
|
+
return cls.OP_CODE_MAP.get(op_code_masked, "?").title()
|
|
48
|
+
|
|
49
|
+
def update(self, op_code, cur_count, max_count=None, message=""):
|
|
50
|
+
# Start new bar on each BEGIN-flag
|
|
51
|
+
if op_code & self.BEGIN:
|
|
52
|
+
self.curr_op = self.get_curr_op(op_code)
|
|
53
|
+
if self.get_curr_op(op_code) == "Receiving":
|
|
54
|
+
self.active_task = self.progressbar.add_task(
|
|
55
|
+
description=self.curr_op,
|
|
56
|
+
total=max_count,
|
|
57
|
+
message=message,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if self.get_curr_op(op_code) == "Receiving":
|
|
61
|
+
self.progressbar.update(
|
|
62
|
+
task_id=self.active_task,
|
|
63
|
+
completed=cur_count,
|
|
64
|
+
message=message,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# End progress monitoring on each END-flag
|
|
68
|
+
if self.get_curr_op(op_code) == "Receiving":
|
|
69
|
+
if op_code & self.END:
|
|
70
|
+
self.progressbar.update(
|
|
71
|
+
task_id=self.active_task,
|
|
72
|
+
message=f"[bright_black]{message}",
|
|
73
|
+
)
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
githubtakeout.py
|
|
4
|
-
pyproject.toml
|
|
5
|
-
githubtakeout.egg-info/PKG-INFO
|
|
6
|
-
githubtakeout.egg-info/SOURCES.txt
|
|
7
|
-
githubtakeout.egg-info/dependency_links.txt
|
|
8
|
-
githubtakeout.egg-info/entry_points.txt
|
|
9
|
-
githubtakeout.egg-info/requires.txt
|
|
10
|
-
githubtakeout.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
githubtakeout
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|