@codebend3r/che 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +424 -0
- package/bin/che.py +869 -0
- package/bin/commands.py +872 -0
- package/bin/drives/eject-all-drives.py +203 -0
- package/bin/drives/mount-all-drives.py +140 -0
- package/bin/drives/ping-nas.py +242 -0
- package/bin/files/compress-folders.py +188 -0
- package/bin/files/delete-by-ext.py +108 -0
- package/bin/files/delete-empty-folders.py +136 -0
- package/bin/files/delete-smb-files.py +105 -0
- package/bin/files/files-under-size.py +132 -0
- package/bin/files/find-largest-files.py +102 -0
- package/bin/files/make-alpha-dir.py +63 -0
- package/bin/git/all-actions.py +545 -0
- package/bin/git/checkout-my-branches.py +147 -0
- package/bin/git/clean-stale-branches.py +161 -0
- package/bin/git/prune-worktrees.py +143 -0
- package/bin/git/sync-all-branches.py +368 -0
- package/bin/git/update-local-branches.py +195 -0
- package/bin/install.py +1515 -0
- package/bin/shellgen.py +782 -0
- package/bin/system/btop-launch.py +114 -0
- package/bin/system/update-brew.py +146 -0
- package/bin/tui.py +648 -0
- package/bin/utils.py +836 -0
- package/bin/video/delete-duplicate-videos.py +313 -0
- package/bin/video/detect-green-magenta-videos.py +268 -0
- package/bin/video/find-movie-by-year.py +84 -0
- package/bin/video/find-video-mkv-issues.py +242 -0
- package/bin/video/fix-codecs.py +209 -0
- package/bin/video/largest-tv-shows.py +175 -0
- package/bin/video/remove-metadata.py +126 -0
- package/bin/video/rename-video-file.py +329 -0
- package/bin/video/scan-videos-audio-language.py +110 -0
- package/bin/video/show-codecs.py +111 -0
- package/bin/video/validate-video-files.py +114 -0
- package/bin/video/video-list.py +120 -0
- package/package.json +54 -0
- package/shell/che.bash +382 -0
- package/shell/che.fish +282 -0
- package/shell/che.zsh +389 -0
- package/shell/completions/_che +146 -0
- package/shell/completions/che.bash +107 -0
- package/shell/completions/che.fish +371 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""⬇️ For each local branch with an upstream, rebase onto origin.
|
|
3
|
+
|
|
4
|
+
Conflicts are aborted and reported; the script keeps going.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
update-local-branches.py [--limit=N] [--dry-run]
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
--limit=N Only update the N most recently committed branches
|
|
11
|
+
--dry-run List branches that would be updated, change nothing
|
|
12
|
+
--help Show help
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
22
|
+
|
|
23
|
+
from utils import (
|
|
24
|
+
add_bool_flag,
|
|
25
|
+
build_parser,
|
|
26
|
+
info,
|
|
27
|
+
log,
|
|
28
|
+
note,
|
|
29
|
+
require_binary,
|
|
30
|
+
run,
|
|
31
|
+
run_cli,
|
|
32
|
+
run_lines,
|
|
33
|
+
run_output,
|
|
34
|
+
success,
|
|
35
|
+
warning,
|
|
36
|
+
worktree_branch_map,
|
|
37
|
+
worktree_is_clean,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__version__ = "3.1.0"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def branches_with_upstream() -> list[str]:
|
|
44
|
+
"""Local branches that have an upstream, most recently committed first.
|
|
45
|
+
|
|
46
|
+
The separator is \\x01, not the shell version's ``|``. A branch name may
|
|
47
|
+
legally contain a pipe (``git check-ref-format`` allows it), and splitting
|
|
48
|
+
on ``|`` then treating the whole remainder as the upstream reports such a
|
|
49
|
+
branch as *having* an upstream when it does not - after which
|
|
50
|
+
``git pull --rebase`` fails on it and the run exits non-zero. Control
|
|
51
|
+
characters are illegal in ref names, so \\x01 cannot collide.
|
|
52
|
+
"""
|
|
53
|
+
found = []
|
|
54
|
+
for line in run_lines(
|
|
55
|
+
[
|
|
56
|
+
"git",
|
|
57
|
+
"for-each-ref",
|
|
58
|
+
"--sort=-committerdate",
|
|
59
|
+
"--format=%(refname:short)%01%(upstream)",
|
|
60
|
+
"refs/heads/",
|
|
61
|
+
]
|
|
62
|
+
):
|
|
63
|
+
branch, _, upstream = line.partition("\x01")
|
|
64
|
+
if branch and upstream:
|
|
65
|
+
found.append(branch)
|
|
66
|
+
return found
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main(argv: list[str] | None = None) -> int:
|
|
70
|
+
parser = build_parser(
|
|
71
|
+
prog="update-local-branches",
|
|
72
|
+
description="⬇️ Rebase every local branch that has an upstream onto origin.",
|
|
73
|
+
)
|
|
74
|
+
# Kept as a string, not `type=int`: argparse's own int conversion would
|
|
75
|
+
# own the failure and exit before the shell version's message could print.
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--limit",
|
|
78
|
+
default="0",
|
|
79
|
+
metavar="N",
|
|
80
|
+
help="Only update the N most recently committed branches",
|
|
81
|
+
)
|
|
82
|
+
add_bool_flag(
|
|
83
|
+
parser,
|
|
84
|
+
"--dry-run",
|
|
85
|
+
help="List branches that would be updated, change nothing",
|
|
86
|
+
)
|
|
87
|
+
args = parser.parse_args(argv)
|
|
88
|
+
|
|
89
|
+
if not re.fullmatch(r"[0-9]+", args.limit):
|
|
90
|
+
warning(f"❌ --limit must be a non-negative integer (got: {args.limit})")
|
|
91
|
+
return 1
|
|
92
|
+
limit = int(args.limit)
|
|
93
|
+
|
|
94
|
+
require_binary("git")
|
|
95
|
+
|
|
96
|
+
header_suffix = " 🌵 (dry run)" if args.dry_run else ""
|
|
97
|
+
note("═══════════════════════════════════════════")
|
|
98
|
+
note(f"⬇️ update-local-branches{header_suffix}")
|
|
99
|
+
note("═══════════════════════════════════════════")
|
|
100
|
+
|
|
101
|
+
original_branch = run_output(
|
|
102
|
+
["git", "symbolic-ref", "--quiet", "--short", "HEAD"], check=False
|
|
103
|
+
).strip()
|
|
104
|
+
if not original_branch:
|
|
105
|
+
warning("❌ Detached HEAD — check out a branch first.")
|
|
106
|
+
return 1
|
|
107
|
+
|
|
108
|
+
if not args.dry_run and run_output(["git", "status", "--porcelain"]):
|
|
109
|
+
warning("❌ Working tree is dirty. Commit or stash before running. 🧺")
|
|
110
|
+
return 1
|
|
111
|
+
|
|
112
|
+
info("📡 Fetching remotes...")
|
|
113
|
+
run(["git", "fetch", "--all", "--quiet"])
|
|
114
|
+
|
|
115
|
+
branches = branches_with_upstream()
|
|
116
|
+
if limit > 0:
|
|
117
|
+
branches = branches[:limit]
|
|
118
|
+
|
|
119
|
+
if not branches:
|
|
120
|
+
info("ℹ️ No local branches with upstreams.") # noqa: RUF001
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
if args.dry_run:
|
|
124
|
+
info(f"🌵 Would update {len(branches)} branch(es):")
|
|
125
|
+
for branch in branches:
|
|
126
|
+
info(f" • 🌿 {branch}")
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
succeeded: list[str] = []
|
|
130
|
+
failed: list[str] = []
|
|
131
|
+
|
|
132
|
+
# One `git worktree list` for the whole run, not one per branch as the
|
|
133
|
+
# shell's per-branch `worktree_for_branch` did.
|
|
134
|
+
pinned_by_branch = worktree_branch_map()
|
|
135
|
+
here = Path.cwd().resolve()
|
|
136
|
+
|
|
137
|
+
for branch in branches:
|
|
138
|
+
# A branch checked out in another worktree cannot be checked out here —
|
|
139
|
+
# git refuses with "already used by worktree". That's a mechanical
|
|
140
|
+
# limit, not a conflict, so update it in place rather than counting it
|
|
141
|
+
# as a failure.
|
|
142
|
+
pinned = pinned_by_branch.get(branch, "")
|
|
143
|
+
|
|
144
|
+
if pinned and Path(pinned).resolve() != here:
|
|
145
|
+
if not worktree_is_clean(pinned):
|
|
146
|
+
warning(f" ⏭️ {branch} — worktree is dirty ({pinned}), skipping")
|
|
147
|
+
failed.append(branch)
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
log(f"⬇️ Rebasing {branch} in its worktree ({pinned})")
|
|
151
|
+
rebase = run(["git", "-C", pinned, "pull", "--rebase", "--quiet"], check=False)
|
|
152
|
+
if rebase.returncode == 0:
|
|
153
|
+
success(f" ✅ {branch} up to date (in {pinned})")
|
|
154
|
+
succeeded.append(branch)
|
|
155
|
+
else:
|
|
156
|
+
warning(" ❌ rebase failed — aborting and moving on")
|
|
157
|
+
run(["git", "-C", pinned, "rebase", "--abort"], check=False, capture=True)
|
|
158
|
+
failed.append(branch)
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
info(f"📂 Switching to 🌿 {branch}")
|
|
162
|
+
checkout = run(["git", "checkout", "--quiet", branch], check=False, capture=True)
|
|
163
|
+
if checkout.returncode != 0:
|
|
164
|
+
warning(" ✗ checkout failed, skipping")
|
|
165
|
+
failed.append(branch)
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
log(f"⬇️ Rebasing {branch} onto upstream")
|
|
169
|
+
pull = run(["git", "pull", "--rebase", "--quiet"], check=False)
|
|
170
|
+
if pull.returncode == 0:
|
|
171
|
+
success(f" ✅ {branch} up to date")
|
|
172
|
+
succeeded.append(branch)
|
|
173
|
+
else:
|
|
174
|
+
warning(" ❌ rebase failed — aborting and moving on")
|
|
175
|
+
run(["git", "rebase", "--abort"], check=False, capture=True)
|
|
176
|
+
failed.append(branch)
|
|
177
|
+
|
|
178
|
+
info(f"🔁 Returning to 🌿 {original_branch}")
|
|
179
|
+
run(["git", "checkout", "--quiet", original_branch])
|
|
180
|
+
|
|
181
|
+
print()
|
|
182
|
+
success("═══ summary ═══")
|
|
183
|
+
success(f"✅ Updated: {len(succeeded)}")
|
|
184
|
+
if failed:
|
|
185
|
+
warning(f"❌ Failed: {len(failed)}")
|
|
186
|
+
for branch in failed:
|
|
187
|
+
warning(f" • 💥 {branch}")
|
|
188
|
+
return 1
|
|
189
|
+
|
|
190
|
+
success("🎉 All clean!")
|
|
191
|
+
return 0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
run_cli(main)
|