micropython-branch-manager 2.0.3__tar.gz → 2.1.0__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.
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/6/5/f/8/d/65f8dd134ec79390f6fba2e4328eff4d119f25775d7591b491591c4c +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/d/8/7/1/b/d871bc55adf567ea2c49a2de02d30bfa1a66046fb62fe2b0b5e5e1b3.body → micropython_branch_manager-2.1.0/.cache/pip/http-v2/6/5/f/8/d/65f8dd134ec79390f6fba2e4328eff4d119f25775d7591b491591c4c.body +12 -12
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/7/d/0/2/4/7d024bf0e20d47587f77acc460902dbf3d99051a52119f3e3505b7ae +0 -0
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/7/d/0/2/4/7d024bf0e20d47587f77acc460902dbf3d99051a52119f3e3505b7ae.body +0 -0
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/a/0/c/7/e/a0c7eeba5b18e695eb825961b9f96de9ffb9cda5921f4bffc7f32c58 +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/8/9/6/d/8/896d83792f0d707ff5cb57a45dc80e86c5aaf07748a5d39df95ef61c.body → micropython_branch_manager-2.1.0/.cache/pip/http-v2/a/0/c/7/e/a0c7eeba5b18e695eb825961b9f96de9ffb9cda5921f4bffc7f32c58.body +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/a/d/e/e/f/adeef5b9611702687bbddade5f7c0af65110caf98ba1a0070c9d116e → micropython_branch_manager-2.1.0/.cache/pip/http-v2/a/1/9/5/3/a19537d3cf37c122db841d6fe4cd322bc10d1a558bb00d146b85cb9a +0 -0
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/a/1/9/5/3/a19537d3cf37c122db841d6fe4cd322bc10d1a558bb00d146b85cb9a.body +0 -0
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/a/d/e/e/f/adeef5b9611702687bbddade5f7c0af65110caf98ba1a0070c9d116e +0 -0
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/a/d/e/e/f/adeef5b9611702687bbddade5f7c0af65110caf98ba1a0070c9d116e.body +0 -0
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/e/1/0/a/2/e10a2bbfb31a7de3b9b7a66daabe582666ce7da5b088fe0ee85cdd9b +0 -0
- micropython_branch_manager-2.1.0/.cache/pip/http-v2/e/1/0/a/2/e10a2bbfb31a7de3b9b7a66daabe582666ce7da5b088fe0ee85cdd9b.body +362 -0
- micropython_branch_manager-2.1.0/.cache/pip/selfcheck/fe300af6f7d708c14827daac3afc81fbb8306b73de8dd6e3f1f8ea3b +1 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/PKG-INFO +5 -1
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/pyproject.toml +6 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/_version.py +2 -2
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/git.py +4 -2
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/pr.py +2 -1
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/rebase.py +2 -1
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/rerere.py +11 -3
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/trailers.py +34 -9
- micropython_branch_manager-2.1.0/tests/test_trailers.py +191 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/8/9/6/d/8/896d83792f0d707ff5cb57a45dc80e86c5aaf07748a5d39df95ef61c +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/a/1/9/5/3/a19537d3cf37c122db841d6fe4cd322bc10d1a558bb00d146b85cb9a +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/a/1/9/5/3/a19537d3cf37c122db841d6fe4cd322bc10d1a558bb00d146b85cb9a.body +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/a/d/e/e/f/adeef5b9611702687bbddade5f7c0af65110caf98ba1a0070c9d116e.body +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/d/8/7/1/b/d871bc55adf567ea2c49a2de02d30bfa1a66046fb62fe2b0b5e5e1b3 +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/selfcheck/fe300af6f7d708c14827daac3afc81fbb8306b73de8dd6e3f1f8ea3b +0 -1
- micropython_branch_manager-2.0.3/tests/test_trailers.py +0 -85
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/.gitignore +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/.gitlab-ci.yml +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/.pre-commit-config.yaml +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/.python-version +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/CLAUDE.md +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/LICENSE +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/README.md +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/docs/gitlab_mr_example.png +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/__init__.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/__main__.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/branch_sync.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/cli.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/config.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/exceptions.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/github.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/src/micropython_branch_manager/update_branch.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/conftest.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_branch_sync.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_config.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_exceptions.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_git.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_github.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_integration.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_update_branch.py +0 -0
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uv
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.23
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Environment :: Console
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -37,9 +37,9 @@ Project-URL: Repository, https://github.com/astral-sh/uv
|
|
|
37
37
|
# uv
|
|
38
38
|
|
|
39
39
|
[](https://github.com/astral-sh/uv)
|
|
40
|
-
[](https://pypi.python.org/pypi/uv)
|
|
41
|
+
[](https://pypi.python.org/pypi/uv)
|
|
42
|
+
[](https://pypi.python.org/pypi/uv)
|
|
43
43
|
[](https://github.com/astral-sh/uv/actions)
|
|
44
44
|
[](https://discord.gg/astral-sh)
|
|
45
45
|
|
|
@@ -58,13 +58,13 @@ An extremely fast Python package and project manager, written in Rust.
|
|
|
58
58
|
- A single tool to replace `pip`, `pip-tools`, `pipx`, `poetry`, `pyenv`, `twine`, `virtualenv`, and
|
|
59
59
|
more.
|
|
60
60
|
- [10-100x faster](https://github.com/astral-sh/uv/blob/main/BENCHMARKS.md) than `pip`.
|
|
61
|
-
- Provides [comprehensive project management](https://github.com/astral-sh/uv/blob/0.11.
|
|
61
|
+
- Provides [comprehensive project management](https://github.com/astral-sh/uv/blob/0.11.23/README.md#projects), with a
|
|
62
62
|
[universal lockfile](https://docs.astral.sh/uv/concepts/projects/layout#the-lockfile).
|
|
63
|
-
- [Runs scripts](https://github.com/astral-sh/uv/blob/0.11.
|
|
63
|
+
- [Runs scripts](https://github.com/astral-sh/uv/blob/0.11.23/README.md#scripts), with support for
|
|
64
64
|
[inline dependency metadata](https://docs.astral.sh/uv/guides/scripts#declaring-script-dependencies).
|
|
65
|
-
- [Installs and manages](https://github.com/astral-sh/uv/blob/0.11.
|
|
66
|
-
- [Runs and installs](https://github.com/astral-sh/uv/blob/0.11.
|
|
67
|
-
- Includes a [pip-compatible interface](https://github.com/astral-sh/uv/blob/0.11.
|
|
65
|
+
- [Installs and manages](https://github.com/astral-sh/uv/blob/0.11.23/README.md#python-versions) Python versions.
|
|
66
|
+
- [Runs and installs](https://github.com/astral-sh/uv/blob/0.11.23/README.md#tools) tools published as Python packages.
|
|
67
|
+
- Includes a [pip-compatible interface](https://github.com/astral-sh/uv/blob/0.11.23/README.md#the-pip-interface) for a performance boost with a familiar
|
|
68
68
|
CLI.
|
|
69
69
|
- Supports Cargo-style [workspaces](https://docs.astral.sh/uv/concepts/projects/workspaces) for
|
|
70
70
|
scalable projects.
|
|
@@ -315,7 +315,7 @@ It's pronounced as "you - vee" ([`/juː viː/`](https://en.wikipedia.org/wiki/He
|
|
|
315
315
|
|
|
316
316
|
#### How should I stylize uv?
|
|
317
317
|
|
|
318
|
-
Just "uv", please. See the [style guide](https://github.com/astral-sh/uv/blob/0.11.
|
|
318
|
+
Just "uv", please. See the [style guide](https://github.com/astral-sh/uv/blob/0.11.23/STYLE.md#styling-uv) for details.
|
|
319
319
|
|
|
320
320
|
#### What platforms does uv support?
|
|
321
321
|
|
|
@@ -344,9 +344,9 @@ for Windows support.
|
|
|
344
344
|
|
|
345
345
|
uv is licensed under either of
|
|
346
346
|
|
|
347
|
-
- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/astral-sh/uv/blob/0.11.
|
|
347
|
+
- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/astral-sh/uv/blob/0.11.23/LICENSE-APACHE) or
|
|
348
348
|
<https://www.apache.org/licenses/LICENSE-2.0>)
|
|
349
|
-
- MIT license ([LICENSE-MIT](https://github.com/astral-sh/uv/blob/0.11.
|
|
349
|
+
- MIT license ([LICENSE-MIT](https://github.com/astral-sh/uv/blob/0.11.23/LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
|
|
350
350
|
|
|
351
351
|
at your option.
|
|
352
352
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uv
|
|
3
|
+
Version: 0.11.24
|
|
4
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Rust
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
License-File: LICENSE-APACHE
|
|
23
|
+
License-File: LICENSE-MIT
|
|
24
|
+
Summary: An extremely fast Python package and project manager, written in Rust.
|
|
25
|
+
Keywords: uv,requirements,packaging
|
|
26
|
+
Home-Page: https://pypi.org/project/uv/
|
|
27
|
+
Author-email: "Astral Software Inc." <hey@astral.sh>
|
|
28
|
+
License-Expression: MIT OR Apache-2.0
|
|
29
|
+
Requires-Python: >=3.8
|
|
30
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
31
|
+
Project-URL: Changelog, https://github.com/astral-sh/uv/blob/main/CHANGELOG.md
|
|
32
|
+
Project-URL: Discord, https://discord.gg/astral-sh
|
|
33
|
+
Project-URL: Documentation, https://docs.astral.sh/uv
|
|
34
|
+
Project-URL: Releases, https://github.com/astral-sh/uv/releases
|
|
35
|
+
Project-URL: Repository, https://github.com/astral-sh/uv
|
|
36
|
+
|
|
37
|
+
# uv
|
|
38
|
+
|
|
39
|
+
[](https://github.com/astral-sh/uv)
|
|
40
|
+
[](https://pypi.python.org/pypi/uv)
|
|
41
|
+
[](https://pypi.python.org/pypi/uv)
|
|
42
|
+
[](https://pypi.python.org/pypi/uv)
|
|
43
|
+
[](https://github.com/astral-sh/uv/actions)
|
|
44
|
+
[](https://discord.gg/astral-sh)
|
|
45
|
+
|
|
46
|
+
An extremely fast Python package and project manager, written in Rust.
|
|
47
|
+
|
|
48
|
+
<p align="center">
|
|
49
|
+
<img alt="Shows a bar chart with benchmark results." src="https://github.com/astral-sh/uv/assets/1309177/629e59c0-9c6e-4013-9ad4-adb2bcf5080d">
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<i>Installing <a href="https://trio.readthedocs.io/">Trio</a>'s dependencies with a warm cache.</i>
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
## Highlights
|
|
57
|
+
|
|
58
|
+
- A single tool to replace `pip`, `pip-tools`, `pipx`, `poetry`, `pyenv`, `twine`, `virtualenv`, and
|
|
59
|
+
more.
|
|
60
|
+
- [10-100x faster](https://github.com/astral-sh/uv/blob/main/BENCHMARKS.md) than `pip`.
|
|
61
|
+
- Provides [comprehensive project management](https://github.com/astral-sh/uv/blob/0.11.24/README.md#projects), with a
|
|
62
|
+
[universal lockfile](https://docs.astral.sh/uv/concepts/projects/layout#the-lockfile).
|
|
63
|
+
- [Runs scripts](https://github.com/astral-sh/uv/blob/0.11.24/README.md#scripts), with support for
|
|
64
|
+
[inline dependency metadata](https://docs.astral.sh/uv/guides/scripts#declaring-script-dependencies).
|
|
65
|
+
- [Installs and manages](https://github.com/astral-sh/uv/blob/0.11.24/README.md#python-versions) Python versions.
|
|
66
|
+
- [Runs and installs](https://github.com/astral-sh/uv/blob/0.11.24/README.md#tools) tools published as Python packages.
|
|
67
|
+
- Includes a [pip-compatible interface](https://github.com/astral-sh/uv/blob/0.11.24/README.md#the-pip-interface) for a performance boost with a familiar
|
|
68
|
+
CLI.
|
|
69
|
+
- Supports Cargo-style [workspaces](https://docs.astral.sh/uv/concepts/projects/workspaces) for
|
|
70
|
+
scalable projects.
|
|
71
|
+
- Disk-space efficient, with a [global cache](https://docs.astral.sh/uv/concepts/cache) for
|
|
72
|
+
dependency deduplication.
|
|
73
|
+
- Installable without Rust or Python via `curl` or `pip`.
|
|
74
|
+
- Supports macOS, Linux, and Windows.
|
|
75
|
+
|
|
76
|
+
uv is backed by [Astral](https://astral.sh), the creators of
|
|
77
|
+
[Ruff](https://github.com/astral-sh/ruff) and [ty](https://github.com/astral-sh/ty).
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
Install uv with our standalone installers:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# On macOS and Linux.
|
|
85
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# On Windows.
|
|
90
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Or, from [PyPI](https://pypi.org/project/uv/):
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# With pip.
|
|
97
|
+
pip install uv
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Or pipx.
|
|
102
|
+
pipx install uv
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If installed via the standalone installer, uv can update itself to the latest version:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv self update
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
See the [installation documentation](https://docs.astral.sh/uv/getting-started/installation/) for
|
|
112
|
+
details and alternative installation methods.
|
|
113
|
+
|
|
114
|
+
## Documentation
|
|
115
|
+
|
|
116
|
+
uv's documentation is available at [docs.astral.sh/uv](https://docs.astral.sh/uv).
|
|
117
|
+
|
|
118
|
+
Additionally, the command line reference documentation can be viewed with `uv help`.
|
|
119
|
+
|
|
120
|
+
## Features
|
|
121
|
+
|
|
122
|
+
### Projects
|
|
123
|
+
|
|
124
|
+
uv manages project dependencies and environments, with support for lockfiles, workspaces, and more,
|
|
125
|
+
similar to `rye` or `poetry`:
|
|
126
|
+
|
|
127
|
+
```console
|
|
128
|
+
$ uv init example
|
|
129
|
+
Initialized project `example` at `/home/user/example`
|
|
130
|
+
|
|
131
|
+
$ cd example
|
|
132
|
+
|
|
133
|
+
$ uv add ruff
|
|
134
|
+
Creating virtual environment at: .venv
|
|
135
|
+
Resolved 2 packages in 170ms
|
|
136
|
+
Built example @ file:///home/user/example
|
|
137
|
+
Prepared 2 packages in 627ms
|
|
138
|
+
Installed 2 packages in 1ms
|
|
139
|
+
+ example==0.1.0 (from file:///home/user/example)
|
|
140
|
+
+ ruff==0.5.0
|
|
141
|
+
|
|
142
|
+
$ uv run ruff check
|
|
143
|
+
All checks passed!
|
|
144
|
+
|
|
145
|
+
$ uv lock
|
|
146
|
+
Resolved 2 packages in 0.33ms
|
|
147
|
+
|
|
148
|
+
$ uv sync
|
|
149
|
+
Resolved 2 packages in 0.70ms
|
|
150
|
+
Checked 1 package in 0.02ms
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
See the [project documentation](https://docs.astral.sh/uv/guides/projects/) to get started.
|
|
154
|
+
|
|
155
|
+
uv also supports building and publishing projects, even if they're not managed with uv. See the
|
|
156
|
+
[publish guide](https://docs.astral.sh/uv/guides/publish/) to learn more.
|
|
157
|
+
|
|
158
|
+
### Scripts
|
|
159
|
+
|
|
160
|
+
uv manages dependencies and environments for single-file scripts.
|
|
161
|
+
|
|
162
|
+
Create a new script and add inline metadata declaring its dependencies:
|
|
163
|
+
|
|
164
|
+
```console
|
|
165
|
+
$ echo 'import requests; print(requests.get("https://astral.sh"))' > example.py
|
|
166
|
+
|
|
167
|
+
$ uv add --script example.py requests
|
|
168
|
+
Updated `example.py`
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Then, run the script in an isolated virtual environment:
|
|
172
|
+
|
|
173
|
+
```console
|
|
174
|
+
$ uv run example.py
|
|
175
|
+
Reading inline script metadata from: example.py
|
|
176
|
+
Installed 5 packages in 12ms
|
|
177
|
+
<Response [200]>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
See the [scripts documentation](https://docs.astral.sh/uv/guides/scripts/) to get started.
|
|
181
|
+
|
|
182
|
+
### Tools
|
|
183
|
+
|
|
184
|
+
uv executes and installs command-line tools provided by Python packages, similar to `pipx`.
|
|
185
|
+
|
|
186
|
+
Run a tool in an ephemeral environment using `uvx` (an alias for `uv tool run`):
|
|
187
|
+
|
|
188
|
+
```console
|
|
189
|
+
$ uvx pycowsay 'hello world!'
|
|
190
|
+
Resolved 1 package in 167ms
|
|
191
|
+
Installed 1 package in 9ms
|
|
192
|
+
+ pycowsay==0.0.0.2
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
------------
|
|
196
|
+
< hello world! >
|
|
197
|
+
------------
|
|
198
|
+
\ ^__^
|
|
199
|
+
\ (oo)\_______
|
|
200
|
+
(__)\ )\/\
|
|
201
|
+
||----w |
|
|
202
|
+
|| ||
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Install a tool with `uv tool install`:
|
|
206
|
+
|
|
207
|
+
```console
|
|
208
|
+
$ uv tool install ruff
|
|
209
|
+
Resolved 1 package in 6ms
|
|
210
|
+
Installed 1 package in 2ms
|
|
211
|
+
+ ruff==0.5.0
|
|
212
|
+
Installed 1 executable: ruff
|
|
213
|
+
|
|
214
|
+
$ ruff --version
|
|
215
|
+
ruff 0.5.0
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
See the [tools documentation](https://docs.astral.sh/uv/guides/tools/) to get started.
|
|
219
|
+
|
|
220
|
+
### Python versions
|
|
221
|
+
|
|
222
|
+
uv installs Python and allows quickly switching between versions.
|
|
223
|
+
|
|
224
|
+
Install multiple Python versions:
|
|
225
|
+
|
|
226
|
+
```console
|
|
227
|
+
$ uv python install 3.12 3.13 3.14
|
|
228
|
+
Installed 3 versions in 972ms
|
|
229
|
+
+ cpython-3.12.12-macos-aarch64-none (python3.12)
|
|
230
|
+
+ cpython-3.13.9-macos-aarch64-none (python3.13)
|
|
231
|
+
+ cpython-3.14.0-macos-aarch64-none (python3.14)
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Download Python versions as needed:
|
|
236
|
+
|
|
237
|
+
```console
|
|
238
|
+
$ uv venv --python 3.12.0
|
|
239
|
+
Using Python 3.12.0
|
|
240
|
+
Creating virtual environment at: .venv
|
|
241
|
+
Activate with: source .venv/bin/activate
|
|
242
|
+
|
|
243
|
+
$ uv run --python pypy@3.8 -- python --version
|
|
244
|
+
Python 3.8.16 (a9dbdca6fc3286b0addd2240f11d97d8e8de187a, Dec 29 2022, 11:45:30)
|
|
245
|
+
[PyPy 7.3.11 with GCC Apple LLVM 13.1.6 (clang-1316.0.21.2.5)] on darwin
|
|
246
|
+
Type "help", "copyright", "credits" or "license" for more information.
|
|
247
|
+
>>>>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Use a specific Python version in the current directory:
|
|
251
|
+
|
|
252
|
+
```console
|
|
253
|
+
$ uv python pin 3.11
|
|
254
|
+
Pinned `.python-version` to `3.11`
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
See the [Python installation documentation](https://docs.astral.sh/uv/guides/install-python/) to get
|
|
258
|
+
started.
|
|
259
|
+
|
|
260
|
+
### The pip interface
|
|
261
|
+
|
|
262
|
+
uv provides a drop-in replacement for common `pip`, `pip-tools`, and `virtualenv` commands.
|
|
263
|
+
|
|
264
|
+
uv extends their interfaces with advanced features, such as dependency version overrides,
|
|
265
|
+
platform-independent resolutions, reproducible resolutions, alternative resolution strategies, and
|
|
266
|
+
more.
|
|
267
|
+
|
|
268
|
+
Migrate to uv without changing your existing workflows — and experience a 10-100x speedup — with the
|
|
269
|
+
`uv pip` interface.
|
|
270
|
+
|
|
271
|
+
Compile requirements into a platform-independent requirements file:
|
|
272
|
+
|
|
273
|
+
```console
|
|
274
|
+
$ uv pip compile requirements.in \
|
|
275
|
+
--universal \
|
|
276
|
+
--output-file requirements.txt
|
|
277
|
+
Resolved 43 packages in 12ms
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Create a virtual environment:
|
|
281
|
+
|
|
282
|
+
```console
|
|
283
|
+
$ uv venv
|
|
284
|
+
Using Python 3.12.3
|
|
285
|
+
Creating virtual environment at: .venv
|
|
286
|
+
Activate with: source .venv/bin/activate
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Install the locked requirements:
|
|
290
|
+
|
|
291
|
+
```console
|
|
292
|
+
$ uv pip sync requirements.txt
|
|
293
|
+
Resolved 43 packages in 11ms
|
|
294
|
+
Installed 43 packages in 208ms
|
|
295
|
+
+ babel==2.15.0
|
|
296
|
+
+ black==24.4.2
|
|
297
|
+
+ certifi==2024.7.4
|
|
298
|
+
...
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
See the [pip interface documentation](https://docs.astral.sh/uv/pip/index/) to get started.
|
|
302
|
+
|
|
303
|
+
## Contributing
|
|
304
|
+
|
|
305
|
+
We are passionate about supporting contributors of all levels of experience and would love to see
|
|
306
|
+
you get involved in the project. See the
|
|
307
|
+
[contributing guide](https://github.com/astral-sh/uv?tab=contributing-ov-file#contributing) to get
|
|
308
|
+
started.
|
|
309
|
+
|
|
310
|
+
## FAQ
|
|
311
|
+
|
|
312
|
+
#### How do you pronounce uv?
|
|
313
|
+
|
|
314
|
+
It's pronounced as "you - vee" ([`/juː viː/`](https://en.wikipedia.org/wiki/Help:IPA/English#Key))
|
|
315
|
+
|
|
316
|
+
#### How should I stylize uv?
|
|
317
|
+
|
|
318
|
+
Just "uv", please. See the [style guide](https://github.com/astral-sh/uv/blob/0.11.24/STYLE.md#styling-uv) for details.
|
|
319
|
+
|
|
320
|
+
#### What platforms does uv support?
|
|
321
|
+
|
|
322
|
+
See uv's [platform support](https://docs.astral.sh/uv/reference/platforms/) document.
|
|
323
|
+
|
|
324
|
+
#### Is uv ready for production?
|
|
325
|
+
|
|
326
|
+
Yes, uv is stable and widely used in production. See uv's
|
|
327
|
+
[versioning policy](https://docs.astral.sh/uv/reference/versioning/) document for details.
|
|
328
|
+
|
|
329
|
+
## Acknowledgements
|
|
330
|
+
|
|
331
|
+
uv's dependency resolver uses [PubGrub](https://github.com/pubgrub-rs/pubgrub) under the hood. We're
|
|
332
|
+
grateful to the PubGrub maintainers, especially [Jacob Finkelman](https://github.com/Eh2406), for
|
|
333
|
+
their support.
|
|
334
|
+
|
|
335
|
+
uv's Git implementation is based on [Cargo](https://github.com/rust-lang/cargo).
|
|
336
|
+
|
|
337
|
+
Some of uv's optimizations are inspired by the great work we've seen in [pnpm](https://pnpm.io/),
|
|
338
|
+
[Orogene](https://github.com/orogene/orogene), and [Bun](https://github.com/oven-sh/bun). We've also
|
|
339
|
+
learned a lot from Nathaniel J. Smith's [Posy](https://github.com/njsmith/posy) and adapted its
|
|
340
|
+
[trampoline](https://github.com/njsmith/posy/tree/main/src/trampolines/windows-trampolines/posy-trampoline)
|
|
341
|
+
for Windows support.
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
uv is licensed under either of
|
|
346
|
+
|
|
347
|
+
- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/astral-sh/uv/blob/0.11.24/LICENSE-APACHE) or
|
|
348
|
+
<https://www.apache.org/licenses/LICENSE-2.0>)
|
|
349
|
+
- MIT license ([LICENSE-MIT](https://github.com/astral-sh/uv/blob/0.11.24/LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
|
|
350
|
+
|
|
351
|
+
at your option.
|
|
352
|
+
|
|
353
|
+
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in uv
|
|
354
|
+
by you, as defined in the Apache-2.0 license, shall be dually licensed as above, without any
|
|
355
|
+
additional terms or conditions.
|
|
356
|
+
|
|
357
|
+
<div align="center">
|
|
358
|
+
<a target="_blank" href="https://astral.sh" style="background:none">
|
|
359
|
+
<img src="https://raw.githubusercontent.com/astral-sh/uv/main/assets/svg/Astral.svg" alt="Made by Astral">
|
|
360
|
+
</a>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"key":"/usr/local","last_check":"2026-06-23T19:34:36.579623+00:00","pypi_version":"26.1.2"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: micropython-branch-manager
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Tool for managing MicroPython fork integration branches
|
|
5
|
+
Project-URL: Homepage, https://gitlab.com/alelec/micropython-branch-manager
|
|
6
|
+
Project-URL: Repository, https://gitlab.com/alelec/micropython-branch-manager.git
|
|
7
|
+
Project-URL: Issues, https://gitlab.com/alelec/micropython-branch-manager/-/issues
|
|
8
|
+
Author-email: Andrew Leech <andrew@alelec.net>
|
|
5
9
|
License-Expression: MIT
|
|
6
10
|
License-File: LICENSE
|
|
7
11
|
Requires-Python: >=3.11
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name = "micropython-branch-manager"
|
|
3
3
|
dynamic = ["version"]
|
|
4
4
|
description = "Tool for managing MicroPython fork integration branches"
|
|
5
|
+
authors = [{ name = "Andrew Leech", email = "andrew@alelec.net" }]
|
|
5
6
|
readme = "README.md"
|
|
6
7
|
license = "MIT"
|
|
7
8
|
requires-python = ">=3.11"
|
|
@@ -11,6 +12,11 @@ dependencies = [
|
|
|
11
12
|
"tomli-w>=1.0",
|
|
12
13
|
]
|
|
13
14
|
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://gitlab.com/alelec/micropython-branch-manager"
|
|
17
|
+
Repository = "https://gitlab.com/alelec/micropython-branch-manager.git"
|
|
18
|
+
Issues = "https://gitlab.com/alelec/micropython-branch-manager/-/issues"
|
|
19
|
+
|
|
14
20
|
[project.scripts]
|
|
15
21
|
micropython-branch-manager = "micropython_branch_manager.cli:main"
|
|
16
22
|
mbm = "micropython_branch_manager.cli:main"
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.0
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 1, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -136,8 +136,8 @@ class GitRepo:
|
|
|
136
136
|
continue
|
|
137
137
|
return False
|
|
138
138
|
|
|
139
|
-
def merge(self, branch: str, message: str, no_ff: bool = True) -> None:
|
|
140
|
-
"""Merge branch with optional no-ff.
|
|
139
|
+
def merge(self, branch: str, message: str, no_ff: bool = True, signoff: bool = False) -> None:
|
|
140
|
+
"""Merge branch with optional no-ff and Signed-off-by trailer.
|
|
141
141
|
|
|
142
142
|
Raises:
|
|
143
143
|
MergeConflictError: If conflicts require manual resolution
|
|
@@ -145,6 +145,8 @@ class GitRepo:
|
|
|
145
145
|
args = ["merge"]
|
|
146
146
|
if no_ff:
|
|
147
147
|
args.append("--no-ff")
|
|
148
|
+
if signoff:
|
|
149
|
+
args.append("--signoff")
|
|
148
150
|
args.extend([branch, "-m", message])
|
|
149
151
|
|
|
150
152
|
try:
|
|
@@ -89,13 +89,14 @@ class PRManager:
|
|
|
89
89
|
# Merge into update branch with trailers
|
|
90
90
|
merge_msg = format_merge_message(
|
|
91
91
|
branch_name=pr_info.branch,
|
|
92
|
+
integration=update_branch,
|
|
92
93
|
title=pr_info.title,
|
|
93
94
|
pr_number=pr_info.number,
|
|
94
95
|
pr_url=pr_info.url,
|
|
95
96
|
author=pr_info.author,
|
|
96
97
|
)
|
|
97
98
|
print(f"Merging {pr_info.branch} into {update_branch}...")
|
|
98
|
-
self.git.merge(pr_info.branch, merge_msg)
|
|
99
|
+
self.git.merge(pr_info.branch, merge_msg, signoff=True)
|
|
99
100
|
print("Merge completed successfully")
|
|
100
101
|
|
|
101
102
|
# Push update branch to GitLab
|
|
@@ -144,11 +144,12 @@ class RebaseManager:
|
|
|
144
144
|
self.git.checkout(update_branch)
|
|
145
145
|
merge_msg = format_merge_message(
|
|
146
146
|
branch_name=branch.name,
|
|
147
|
+
integration=update_branch,
|
|
147
148
|
title=branch.title,
|
|
148
149
|
pr_number=branch.pr_number,
|
|
149
150
|
pr_url=branch.pr_url,
|
|
150
151
|
)
|
|
151
|
-
self.git.merge(rebase_branch, merge_msg, no_ff=True)
|
|
152
|
+
self.git.merge(rebase_branch, merge_msg, no_ff=True, signoff=True)
|
|
152
153
|
|
|
153
154
|
# Update feature branch ref
|
|
154
155
|
self.git.run("branch", "-f", branch.name, rebase_branch)
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from .config import BranchConfig
|
|
7
7
|
from .exceptions import GitError
|
|
8
8
|
from .git import GitRepo
|
|
9
|
-
from .trailers import MERGE_PATTERN
|
|
9
|
+
from .trailers import MERGE_PATTERN, format_merge_message
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class RerereTrainer:
|
|
@@ -40,7 +40,7 @@ class RerereTrainer:
|
|
|
40
40
|
worktree_path = Path(tempfile.mkdtemp(prefix="mbm-rerere-"))
|
|
41
41
|
try:
|
|
42
42
|
wt_git = self.git.create_worktree(worktree_path, target)
|
|
43
|
-
self._replay_merges(wt_git, pairs)
|
|
43
|
+
self._replay_merges(wt_git, pairs, integration_branch)
|
|
44
44
|
finally:
|
|
45
45
|
self.git.remove_worktree(worktree_path, force=True)
|
|
46
46
|
# Clean up if directory still exists
|
|
@@ -87,6 +87,7 @@ class RerereTrainer:
|
|
|
87
87
|
self,
|
|
88
88
|
wt_git: GitRepo,
|
|
89
89
|
pairs: list[tuple[BranchConfig, str]],
|
|
90
|
+
integration: str = "integration",
|
|
90
91
|
) -> None:
|
|
91
92
|
"""Replay merges in the worktree, resolving conflicts from the integration branch."""
|
|
92
93
|
for branch, merge_hash in pairs:
|
|
@@ -101,9 +102,16 @@ class RerereTrainer:
|
|
|
101
102
|
wt_git.run(
|
|
102
103
|
"merge",
|
|
103
104
|
"--no-ff",
|
|
105
|
+
"--signoff",
|
|
104
106
|
branch.name,
|
|
105
107
|
"-m",
|
|
106
|
-
|
|
108
|
+
format_merge_message(
|
|
109
|
+
branch.name,
|
|
110
|
+
integration=integration,
|
|
111
|
+
title=branch.title,
|
|
112
|
+
pr_number=branch.pr_number,
|
|
113
|
+
pr_url=branch.pr_url,
|
|
114
|
+
),
|
|
107
115
|
)
|
|
108
116
|
except GitError:
|
|
109
117
|
# Conflict — resolve using the known-good merge commit
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import re
|
|
7
|
+
import textwrap
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
8
9
|
|
|
9
10
|
if TYPE_CHECKING:
|
|
@@ -17,19 +18,40 @@ TRAILER_AUTHOR = "MBM-Author"
|
|
|
17
18
|
MERGE_PATTERN = re.compile(r"Merge branch '([^']+)'")
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
def _merge_component(integration: str) -> str:
|
|
22
|
+
"""Derive a subject-line component from the integration branch name.
|
|
23
|
+
|
|
24
|
+
Strips mbm's working-branch suffix so the component reads as the final
|
|
25
|
+
integration branch (e.g. "tessera_update" -> "tessera").
|
|
26
|
+
"""
|
|
27
|
+
for suffix in ("_update", "_tmp"):
|
|
28
|
+
if integration.endswith(suffix):
|
|
29
|
+
return integration[: -len(suffix)]
|
|
30
|
+
return integration
|
|
31
|
+
|
|
32
|
+
|
|
20
33
|
def format_merge_message(
|
|
21
34
|
branch_name: str,
|
|
35
|
+
integration: str = "integration",
|
|
22
36
|
title: str | None = None,
|
|
23
37
|
pr_number: int | None = None,
|
|
24
38
|
pr_url: str | None = None,
|
|
25
39
|
author: str | None = None,
|
|
26
40
|
) -> str:
|
|
27
|
-
"""Build merge commit message
|
|
28
|
-
|
|
41
|
+
"""Build a MicroPython verifygitlog-compatible merge commit message.
|
|
42
|
+
|
|
43
|
+
The subject is "<integration>: Merge branch '<branch>'." so it satisfies
|
|
44
|
+
verifygitlog's subject rules (component prefix, capitalised first word, ends
|
|
45
|
+
with a period) while keeping the "Merge branch '<name>'" substring that
|
|
46
|
+
MERGE_PATTERN parses back. The optional PR title is wrapped to <=75 columns
|
|
47
|
+
for the body-line-length rule. The Signed-off-by trailer is added by the
|
|
48
|
+
caller via `git merge --signoff` (it uses the committer identity).
|
|
49
|
+
"""
|
|
50
|
+
lines = [f"{_merge_component(integration)}: Merge branch '{branch_name}'."]
|
|
29
51
|
|
|
30
52
|
if title:
|
|
31
53
|
lines.append("")
|
|
32
|
-
lines.append(title)
|
|
54
|
+
lines.append(textwrap.fill(title, width=75))
|
|
33
55
|
|
|
34
56
|
# Add trailers
|
|
35
57
|
trailers = []
|
|
@@ -93,22 +115,25 @@ def extract_branches_with_metadata(
|
|
|
93
115
|
continue
|
|
94
116
|
seen.add(branch_name)
|
|
95
117
|
|
|
96
|
-
# Extract title (second paragraph, before trailers)
|
|
118
|
+
# Extract title (second paragraph, before trailers). The title may be
|
|
119
|
+
# wrapped across several lines (format_merge_message wraps to 75 cols),
|
|
120
|
+
# so collect the whole paragraph and rejoin it.
|
|
97
121
|
title = None
|
|
98
122
|
body_lines = body.split("\n")
|
|
99
123
|
if len(body_lines) > 2 and body_lines[1] == "":
|
|
100
|
-
|
|
124
|
+
title_parts = []
|
|
101
125
|
for line in body_lines[2:]:
|
|
102
126
|
stripped = line.strip()
|
|
103
127
|
if not stripped:
|
|
104
128
|
break
|
|
105
|
-
#
|
|
106
|
-
if any(
|
|
129
|
+
# Stop at the trailer block (MBM-* or the Signed-off-by line).
|
|
130
|
+
if stripped.startswith("Signed-off-by: ") or any(
|
|
107
131
|
stripped.startswith(f"{k}: ") for k in (TRAILER_PR, TRAILER_URL, TRAILER_AUTHOR)
|
|
108
132
|
):
|
|
109
133
|
break
|
|
110
|
-
|
|
111
|
-
|
|
134
|
+
title_parts.append(stripped)
|
|
135
|
+
if title_parts:
|
|
136
|
+
title = " ".join(title_parts)
|
|
112
137
|
|
|
113
138
|
# Parse trailers
|
|
114
139
|
trailers = parse_trailers_from_message(body)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Tests for git trailer support."""
|
|
2
|
+
|
|
3
|
+
from micropython_branch_manager.git import GitRepo
|
|
4
|
+
from micropython_branch_manager.trailers import (
|
|
5
|
+
TRAILER_AUTHOR,
|
|
6
|
+
TRAILER_PR,
|
|
7
|
+
TRAILER_URL,
|
|
8
|
+
extract_branches_with_metadata,
|
|
9
|
+
format_merge_message,
|
|
10
|
+
parse_trailers_from_message,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _init_repo(tmp_path):
|
|
15
|
+
"""Init a real git repo with a base commit on an 'upstream' branch."""
|
|
16
|
+
git = GitRepo(tmp_path)
|
|
17
|
+
git.run("init", "-q")
|
|
18
|
+
git.run("config", "user.name", "Tester")
|
|
19
|
+
git.run("config", "user.email", "tester@example.com")
|
|
20
|
+
git.run("config", "commit.gpgsign", "false")
|
|
21
|
+
(tmp_path / "base.txt").write_text("base\n")
|
|
22
|
+
git.run("add", "-A")
|
|
23
|
+
git.run("commit", "-q", "-m", "base: Initial commit.")
|
|
24
|
+
git.run("branch", "-M", "upstream")
|
|
25
|
+
return git
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _add_feature(git, tmp_path, branch):
|
|
29
|
+
"""Create a feature branch off upstream with one commit."""
|
|
30
|
+
git.run("checkout", "-q", "-b", branch, "upstream")
|
|
31
|
+
(tmp_path / f"{branch}.txt").write_text(f"{branch}\n")
|
|
32
|
+
git.run("add", "-A")
|
|
33
|
+
git.run("commit", "-q", "-m", f"feat: Add {branch}.")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_format_merge_message_minimal():
|
|
37
|
+
"""format_merge_message with just branch name uses the default component."""
|
|
38
|
+
msg = format_merge_message(branch_name="feature-x")
|
|
39
|
+
assert msg == "integration: Merge branch 'feature-x'."
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_format_merge_message_component_from_integration():
|
|
43
|
+
"""The subject component is derived from the integration branch name."""
|
|
44
|
+
msg = format_merge_message(branch_name="feature-x", integration="tessera")
|
|
45
|
+
assert msg == "tessera: Merge branch 'feature-x'."
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_format_merge_message_strips_update_suffix():
|
|
49
|
+
"""An _update/_tmp working-branch suffix is stripped from the component."""
|
|
50
|
+
assert format_merge_message("feature-x", integration="tessera_update").startswith(
|
|
51
|
+
"tessera: Merge branch 'feature-x'."
|
|
52
|
+
)
|
|
53
|
+
assert format_merge_message("feature-x", integration="tessera_tmp").startswith(
|
|
54
|
+
"tessera: Merge branch 'feature-x'."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_format_merge_message_with_title():
|
|
59
|
+
"""format_merge_message includes title and preserves the parseable substring."""
|
|
60
|
+
msg = format_merge_message(branch_name="feature-x", title="Add feature X")
|
|
61
|
+
assert "Merge branch 'feature-x'" in msg
|
|
62
|
+
assert "Add feature X" in msg
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_format_merge_message_wraps_long_title():
|
|
66
|
+
"""A long title is wrapped to <=75 columns for verifygitlog body-line rule."""
|
|
67
|
+
long_title = "Add feature X " * 12
|
|
68
|
+
msg = format_merge_message(branch_name="feature-x", title=long_title.strip())
|
|
69
|
+
body_lines = msg.split("\n")
|
|
70
|
+
assert len(body_lines) > 3 # title spans multiple lines
|
|
71
|
+
assert all(len(line) <= 75 for line in body_lines)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_format_merge_message_full():
|
|
75
|
+
"""format_merge_message with all fields."""
|
|
76
|
+
msg = format_merge_message(
|
|
77
|
+
branch_name="feature-x",
|
|
78
|
+
integration="tessera",
|
|
79
|
+
title="Add feature X",
|
|
80
|
+
pr_number=12345,
|
|
81
|
+
pr_url="https://github.com/micropython/micropython/pull/12345",
|
|
82
|
+
author="testuser",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
lines = msg.split("\n")
|
|
86
|
+
assert lines[0] == "tessera: Merge branch 'feature-x'."
|
|
87
|
+
assert "Add feature X" in msg
|
|
88
|
+
assert f"{TRAILER_PR}: 12345" in msg
|
|
89
|
+
assert f"{TRAILER_URL}: https://github.com/micropython/micropython/pull/12345" in msg
|
|
90
|
+
assert f"{TRAILER_AUTHOR}: testuser" in msg
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_format_merge_message_no_title_with_trailers():
|
|
94
|
+
"""format_merge_message with trailers but no title."""
|
|
95
|
+
msg = format_merge_message(
|
|
96
|
+
branch_name="feature-x",
|
|
97
|
+
pr_number=123,
|
|
98
|
+
)
|
|
99
|
+
assert "Merge branch 'feature-x'" in msg
|
|
100
|
+
assert f"{TRAILER_PR}: 123" in msg
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_parse_trailers_from_message():
|
|
104
|
+
"""parse_trailers_from_message extracts MBM trailers."""
|
|
105
|
+
msg = (
|
|
106
|
+
"Merge branch 'feature-x'\n"
|
|
107
|
+
"\n"
|
|
108
|
+
"Add feature X\n"
|
|
109
|
+
"\n"
|
|
110
|
+
"MBM-PR: 12345\n"
|
|
111
|
+
"MBM-URL: https://github.com/micropython/micropython/pull/12345\n"
|
|
112
|
+
"MBM-Author: testuser\n"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
trailers = parse_trailers_from_message(msg)
|
|
116
|
+
|
|
117
|
+
assert trailers[TRAILER_PR] == "12345"
|
|
118
|
+
assert trailers[TRAILER_URL] == "https://github.com/micropython/micropython/pull/12345"
|
|
119
|
+
assert trailers[TRAILER_AUTHOR] == "testuser"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_parse_trailers_from_message_no_trailers():
|
|
123
|
+
"""parse_trailers_from_message returns empty dict if no trailers."""
|
|
124
|
+
msg = "Merge branch 'feature-x'\n\nAdd feature X\n"
|
|
125
|
+
trailers = parse_trailers_from_message(msg)
|
|
126
|
+
assert trailers == {}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_parse_trailers_from_message_partial():
|
|
130
|
+
"""parse_trailers_from_message handles partial trailers."""
|
|
131
|
+
msg = "Merge branch 'feature-x'\n\nMBM-PR: 42\n"
|
|
132
|
+
trailers = parse_trailers_from_message(msg)
|
|
133
|
+
assert trailers[TRAILER_PR] == "42"
|
|
134
|
+
assert TRAILER_URL not in trailers
|
|
135
|
+
assert TRAILER_AUTHOR not in trailers
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_extract_metadata_roundtrip_wrapped_title(tmp_path):
|
|
139
|
+
"""A real --signoff merge written by format_merge_message round-trips.
|
|
140
|
+
|
|
141
|
+
Exercises the full write->read cycle against real git: the wrapped,
|
|
142
|
+
multi-line title is rejoined and the trailers are recovered, while the
|
|
143
|
+
subject carries the verifygitlog component prefix and a Signed-off-by line.
|
|
144
|
+
"""
|
|
145
|
+
git = _init_repo(tmp_path)
|
|
146
|
+
_add_feature(git, tmp_path, "feature-x")
|
|
147
|
+
git.run("checkout", "-q", "-b", "tessera_update", "upstream")
|
|
148
|
+
|
|
149
|
+
long_title = (
|
|
150
|
+
"Add a deliberately long feature description that exceeds the "
|
|
151
|
+
"seventy-five column wrap width so the title spans several lines"
|
|
152
|
+
)
|
|
153
|
+
merge_msg = format_merge_message(
|
|
154
|
+
branch_name="feature-x",
|
|
155
|
+
integration="tessera_update",
|
|
156
|
+
title=long_title,
|
|
157
|
+
pr_number=12345,
|
|
158
|
+
pr_url="https://github.com/micropython/micropython/pull/12345",
|
|
159
|
+
)
|
|
160
|
+
git.merge("feature-x", merge_msg, no_ff=True, signoff=True)
|
|
161
|
+
|
|
162
|
+
# The written subject is verifygitlog-shaped and signed off.
|
|
163
|
+
assert git.run("log", "-1", "--format=%s", "tessera_update") == (
|
|
164
|
+
"tessera: Merge branch 'feature-x'."
|
|
165
|
+
)
|
|
166
|
+
assert "Signed-off-by: Tester <tester@example.com>" in git.run(
|
|
167
|
+
"log", "-1", "--format=%B", "tessera_update"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
branches = extract_branches_with_metadata(git, "upstream", "tessera_update")
|
|
171
|
+
assert len(branches) == 1
|
|
172
|
+
b = branches[0]
|
|
173
|
+
assert b.name == "feature-x"
|
|
174
|
+
assert b.title == long_title # wrapped lines rejoined
|
|
175
|
+
assert b.pr_number == 12345
|
|
176
|
+
assert b.pr_url == "https://github.com/micropython/micropython/pull/12345"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_extract_metadata_signoff_not_treated_as_title(tmp_path):
|
|
180
|
+
"""A Signed-off-by line on a title-less merge is not parsed as the title."""
|
|
181
|
+
git = _init_repo(tmp_path)
|
|
182
|
+
_add_feature(git, tmp_path, "feature-y")
|
|
183
|
+
git.run("checkout", "-q", "-b", "tessera_update", "upstream")
|
|
184
|
+
|
|
185
|
+
merge_msg = format_merge_message(branch_name="feature-y", integration="tessera_update")
|
|
186
|
+
git.merge("feature-y", merge_msg, no_ff=True, signoff=True)
|
|
187
|
+
|
|
188
|
+
branches = extract_branches_with_metadata(git, "upstream", "tessera_update")
|
|
189
|
+
assert len(branches) == 1
|
|
190
|
+
assert branches[0].name == "feature-y"
|
|
191
|
+
assert branches[0].title is None
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"key":"/usr/local","last_check":"2026-03-25T21:41:43.339896+00:00","pypi_version":"26.0.1"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"""Tests for git trailer support."""
|
|
2
|
-
|
|
3
|
-
from micropython_branch_manager.trailers import (
|
|
4
|
-
TRAILER_AUTHOR,
|
|
5
|
-
TRAILER_PR,
|
|
6
|
-
TRAILER_URL,
|
|
7
|
-
format_merge_message,
|
|
8
|
-
parse_trailers_from_message,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def test_format_merge_message_minimal():
|
|
13
|
-
"""format_merge_message with just branch name."""
|
|
14
|
-
msg = format_merge_message(branch_name="feature-x")
|
|
15
|
-
assert msg == "Merge branch 'feature-x'"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_format_merge_message_with_title():
|
|
19
|
-
"""format_merge_message includes title."""
|
|
20
|
-
msg = format_merge_message(branch_name="feature-x", title="Add feature X")
|
|
21
|
-
assert "Merge branch 'feature-x'" in msg
|
|
22
|
-
assert "Add feature X" in msg
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_format_merge_message_full():
|
|
26
|
-
"""format_merge_message with all fields."""
|
|
27
|
-
msg = format_merge_message(
|
|
28
|
-
branch_name="feature-x",
|
|
29
|
-
title="Add feature X",
|
|
30
|
-
pr_number=12345,
|
|
31
|
-
pr_url="https://github.com/micropython/micropython/pull/12345",
|
|
32
|
-
author="testuser",
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
lines = msg.split("\n")
|
|
36
|
-
assert lines[0] == "Merge branch 'feature-x'"
|
|
37
|
-
assert "Add feature X" in msg
|
|
38
|
-
assert f"{TRAILER_PR}: 12345" in msg
|
|
39
|
-
assert f"{TRAILER_URL}: https://github.com/micropython/micropython/pull/12345" in msg
|
|
40
|
-
assert f"{TRAILER_AUTHOR}: testuser" in msg
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_format_merge_message_no_title_with_trailers():
|
|
44
|
-
"""format_merge_message with trailers but no title."""
|
|
45
|
-
msg = format_merge_message(
|
|
46
|
-
branch_name="feature-x",
|
|
47
|
-
pr_number=123,
|
|
48
|
-
)
|
|
49
|
-
assert "Merge branch 'feature-x'" in msg
|
|
50
|
-
assert f"{TRAILER_PR}: 123" in msg
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def test_parse_trailers_from_message():
|
|
54
|
-
"""parse_trailers_from_message extracts MBM trailers."""
|
|
55
|
-
msg = (
|
|
56
|
-
"Merge branch 'feature-x'\n"
|
|
57
|
-
"\n"
|
|
58
|
-
"Add feature X\n"
|
|
59
|
-
"\n"
|
|
60
|
-
"MBM-PR: 12345\n"
|
|
61
|
-
"MBM-URL: https://github.com/micropython/micropython/pull/12345\n"
|
|
62
|
-
"MBM-Author: testuser\n"
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
trailers = parse_trailers_from_message(msg)
|
|
66
|
-
|
|
67
|
-
assert trailers[TRAILER_PR] == "12345"
|
|
68
|
-
assert trailers[TRAILER_URL] == "https://github.com/micropython/micropython/pull/12345"
|
|
69
|
-
assert trailers[TRAILER_AUTHOR] == "testuser"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_parse_trailers_from_message_no_trailers():
|
|
73
|
-
"""parse_trailers_from_message returns empty dict if no trailers."""
|
|
74
|
-
msg = "Merge branch 'feature-x'\n\nAdd feature X\n"
|
|
75
|
-
trailers = parse_trailers_from_message(msg)
|
|
76
|
-
assert trailers == {}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def test_parse_trailers_from_message_partial():
|
|
80
|
-
"""parse_trailers_from_message handles partial trailers."""
|
|
81
|
-
msg = "Merge branch 'feature-x'\n\nMBM-PR: 42\n"
|
|
82
|
-
trailers = parse_trailers_from_message(msg)
|
|
83
|
-
assert trailers[TRAILER_PR] == "42"
|
|
84
|
-
assert TRAILER_URL not in trailers
|
|
85
|
-
assert TRAILER_AUTHOR not in trailers
|
|
File without changes
|
|
File without changes
|
{micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/.pre-commit-config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/docs/gitlab_mr_example.png
RENAMED
|
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
|
{micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_branch_sync.py
RENAMED
|
File without changes
|
|
File without changes
|
{micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_integration.py
RENAMED
|
File without changes
|
{micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.0}/tests/test_update_branch.py
RENAMED
|
File without changes
|