micropython-branch-manager 2.0.3__tar.gz → 2.1.1__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.1/.cache/pip/http-v2/a/1/9/5/3/a19537d3cf37c122db841d6fe4cd322bc10d1a558bb00d146b85cb9a +0 -0
- micropython_branch_manager-2.1.1/.cache/pip/http-v2/a/1/9/5/3/a19537d3cf37c122db841d6fe4cd322bc10d1a558bb00d146b85cb9a.body +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/.cache/pip/http-v2/a/d/e/e/f/adeef5b9611702687bbddade5f7c0af65110caf98ba1a0070c9d116e +0 -0
- micropython_branch_manager-2.1.1/.cache/pip/http-v2/a/d/e/e/f/adeef5b9611702687bbddade5f7c0af65110caf98ba1a0070c9d116e.body +0 -0
- micropython_branch_manager-2.1.1/.cache/pip/http-v2/c/9/1/f/5/c91f580d88bf83782e78be42725d05696c3911af8667eb629ddc701e +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/d/8/7/1/b/d871bc55adf567ea2c49a2de02d30bfa1a66046fb62fe2b0b5e5e1b3.body → micropython_branch_manager-2.1.1/.cache/pip/http-v2/c/9/1/f/5/c91f580d88bf83782e78be42725d05696c3911af8667eb629ddc701e.body +9 -16
- micropython_branch_manager-2.1.1/.cache/pip/http-v2/f/9/a/b/3/f9ab31eb3a9dd78406ca10dd83c580077dd9d3c984a8877c762be2f6 +0 -0
- micropython_branch_manager-2.0.3/.cache/pip/http-v2/8/9/6/d/8/896d83792f0d707ff5cb57a45dc80e86c5aaf07748a5d39df95ef61c.body → micropython_branch_manager-2.1.1/.cache/pip/http-v2/f/9/a/b/3/f9ab31eb3a9dd78406ca10dd83c580077dd9d3c984a8877c762be2f6.body +0 -0
- micropython_branch_manager-2.1.1/.cache/pip/selfcheck/fe300af6f7d708c14827daac3afc81fbb8306b73de8dd6e3f1f8ea3b +1 -0
- micropython_branch_manager-2.0.3/README.md → micropython_branch_manager-2.1.1/PKG-INFO +48 -32
- micropython_branch_manager-2.0.3/PKG-INFO → micropython_branch_manager-2.1.1/README.md +32 -44
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/pyproject.toml +6 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/_version.py +2 -2
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/branch_sync.py +11 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/cli.py +28 -10
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/git.py +25 -17
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/pr.py +14 -13
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/rebase.py +126 -40
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/rerere.py +11 -4
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/trailers.py +34 -9
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/update_branch.py +1 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/tests/test_branch_sync.py +19 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/tests/test_git.py +41 -0
- micropython_branch_manager-2.1.1/tests/test_integration.py +314 -0
- micropython_branch_manager-2.1.1/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_integration.py +0 -103
- micropython_branch_manager-2.0.3/tests/test_trailers.py +0 -85
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/.gitignore +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/.gitlab-ci.yml +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/.pre-commit-config.yaml +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/.python-version +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/CLAUDE.md +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/LICENSE +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/docs/gitlab_mr_example.png +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/__init__.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/__main__.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/config.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/exceptions.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/src/micropython_branch_manager/github.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/tests/conftest.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/tests/test_config.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/tests/test_exceptions.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/tests/test_github.py +0 -0
- {micropython_branch_manager-2.0.3 → micropython_branch_manager-2.1.1}/tests/test_update_branch.py +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uv
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.17
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Environment :: Console
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -36,13 +36,6 @@ Project-URL: Repository, https://github.com/astral-sh/uv
|
|
|
36
36
|
|
|
37
37
|
# uv
|
|
38
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
39
|
An extremely fast Python package and project manager, written in Rust.
|
|
47
40
|
|
|
48
41
|
<p align="center">
|
|
@@ -58,13 +51,13 @@ An extremely fast Python package and project manager, written in Rust.
|
|
|
58
51
|
- A single tool to replace `pip`, `pip-tools`, `pipx`, `poetry`, `pyenv`, `twine`, `virtualenv`, and
|
|
59
52
|
more.
|
|
60
53
|
- [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.
|
|
54
|
+
- Provides [comprehensive project management](https://github.com/astral-sh/uv/blob/0.12.17/README.md#projects), with a
|
|
62
55
|
[universal lockfile](https://docs.astral.sh/uv/concepts/projects/layout#the-lockfile).
|
|
63
|
-
- [Runs scripts](https://github.com/astral-sh/uv/blob/0.
|
|
56
|
+
- [Runs scripts](https://github.com/astral-sh/uv/blob/0.12.17/README.md#scripts), with support for
|
|
64
57
|
[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.
|
|
66
|
-
- [Runs and installs](https://github.com/astral-sh/uv/blob/0.
|
|
67
|
-
- Includes a [pip-compatible interface](https://github.com/astral-sh/uv/blob/0.
|
|
58
|
+
- [Installs and manages](https://github.com/astral-sh/uv/blob/0.12.17/README.md#python-versions) Python versions.
|
|
59
|
+
- [Runs and installs](https://github.com/astral-sh/uv/blob/0.12.17/README.md#tools) tools published as Python packages.
|
|
60
|
+
- Includes a [pip-compatible interface](https://github.com/astral-sh/uv/blob/0.12.17/README.md#the-pip-interface) for a performance boost with a familiar
|
|
68
61
|
CLI.
|
|
69
62
|
- Supports Cargo-style [workspaces](https://docs.astral.sh/uv/concepts/projects/workspaces) for
|
|
70
63
|
scalable projects.
|
|
@@ -315,7 +308,7 @@ It's pronounced as "you - vee" ([`/juː viː/`](https://en.wikipedia.org/wiki/He
|
|
|
315
308
|
|
|
316
309
|
#### How should I stylize uv?
|
|
317
310
|
|
|
318
|
-
Just "uv", please. See the [style guide](https://github.com/astral-sh/uv/blob/0.
|
|
311
|
+
Just "uv", please. See the [style guide](https://github.com/astral-sh/uv/blob/0.12.17/STYLE.md#styling-uv) for details.
|
|
319
312
|
|
|
320
313
|
#### What platforms does uv support?
|
|
321
314
|
|
|
@@ -344,9 +337,9 @@ for Windows support.
|
|
|
344
337
|
|
|
345
338
|
uv is licensed under either of
|
|
346
339
|
|
|
347
|
-
- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/astral-sh/uv/blob/0.
|
|
340
|
+
- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/astral-sh/uv/blob/0.12.17/LICENSE-APACHE) or
|
|
348
341
|
<https://www.apache.org/licenses/LICENSE-2.0>)
|
|
349
|
-
- MIT license ([LICENSE-MIT](https://github.com/astral-sh/uv/blob/0.
|
|
342
|
+
- MIT license ([LICENSE-MIT](https://github.com/astral-sh/uv/blob/0.12.17/LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
|
|
350
343
|
|
|
351
344
|
at your option.
|
|
352
345
|
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"key":"/usr/local","last_check":"2026-09-22T00:08:41.848944+00:00","pypi_version":"26.2.1"}
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: micropython-branch-manager
|
|
3
|
+
Version: 2.1.1
|
|
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>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Requires-Dist: cyclopts>=3.0
|
|
13
|
+
Requires-Dist: pydantic>=2.0
|
|
14
|
+
Requires-Dist: tomli-w>=1.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
1
17
|
# micropython-branch-manager
|
|
2
18
|
|
|
3
19
|
Tool for managing MicroPython fork integration branches.
|
|
@@ -8,7 +24,7 @@ Tool for managing MicroPython fork integration branches.
|
|
|
8
24
|
|
|
9
25
|
- Rebasing integration branches onto upstream with preserved merge commits
|
|
10
26
|
- Adding PRs from upstream via merge commits
|
|
11
|
-
- Tracking branch metadata in versioned
|
|
27
|
+
- Tracking branch metadata in a versioned TOML config
|
|
12
28
|
- Safety checks for remote divergence
|
|
13
29
|
- Generating GitLab MR URLs
|
|
14
30
|
|
|
@@ -46,12 +62,16 @@ mbm init --integration-branch main
|
|
|
46
62
|
|
|
47
63
|
### Rebase integration branch
|
|
48
64
|
|
|
49
|
-
|
|
65
|
+
Rebuild the integration branch from upstream and every configured PR that is still open or closed:
|
|
50
66
|
|
|
51
67
|
```bash
|
|
52
68
|
mbm rebase upstream/master
|
|
53
69
|
```
|
|
54
70
|
|
|
71
|
+
Before training rerere or changing branches, mbm asks GitHub for each configured PR's state. A PR reported as merged is skipped: mbm does not fetch, rebase, merge, or push its feature branch. The command prints the skipped branch and PR number.
|
|
72
|
+
|
|
73
|
+
This check requires an installed and authenticated GitHub CLI (`gh`). If GitHub cannot be queried, mbm prints a warning and retains the previous behavior of processing every configured branch.
|
|
74
|
+
|
|
55
75
|
Options:
|
|
56
76
|
- `--local` - Skip fetch and push operations
|
|
57
77
|
- `--dry-run` - Show what would happen without making changes
|
|
@@ -78,12 +98,14 @@ The command will:
|
|
|
78
98
|
|
|
79
99
|
### Sync configuration
|
|
80
100
|
|
|
81
|
-
Update
|
|
101
|
+
Update the TOML config from current merge commit history and look up PR metadata:
|
|
82
102
|
|
|
83
103
|
```bash
|
|
84
104
|
mbm sync <github-username>
|
|
85
105
|
```
|
|
86
106
|
|
|
107
|
+
When GitHub reports that a configured PR is merged, `sync` prints an informational message even if the branch no longer appears in integration merge history. It does not remove the entry from `mbm.toml`; retain or remove it deliberately, for example when a downstream pin must remain during a staged rollout.
|
|
108
|
+
|
|
87
109
|
### Show configuration
|
|
88
110
|
|
|
89
111
|
Display current configuration:
|
|
@@ -111,7 +133,7 @@ mbm init
|
|
|
111
133
|
# - gitlab: git@gitlab.example.com:yourname/micropython.git
|
|
112
134
|
# - upstream: https://github.com/micropython/micropython.git
|
|
113
135
|
#
|
|
114
|
-
#
|
|
136
|
+
# Config: /path/to/mbm.toml
|
|
115
137
|
```
|
|
116
138
|
|
|
117
139
|
### Adding PRs to the integration branch
|
|
@@ -186,34 +208,26 @@ The MR creation URL pre-fills the title and description with all integrated bran
|
|
|
186
208
|
|
|
187
209
|
## Configuration File
|
|
188
210
|
|
|
189
|
-
|
|
211
|
+
`mbm.toml` records the integration branch and the PRs that mbm manages. `mbm init` creates it; `mbm add-pr` and `mbm sync` update it. In parent-repository mode it belongs beside the MicroPython submodule and is intended to be committed with that repository.
|
|
190
212
|
|
|
191
213
|
Example configuration:
|
|
192
214
|
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"name": "feature-branch-1",
|
|
204
|
-
"remote": "andrewleech",
|
|
205
|
-
"pr_url": "https://github.com/micropython/micropython/pull/12345",
|
|
206
|
-
"pr_number": 12345,
|
|
207
|
-
"title": "Add feature X"
|
|
208
|
-
}
|
|
209
|
-
]
|
|
210
|
-
}
|
|
215
|
+
```toml
|
|
216
|
+
# micropython-branch-manager (mbm)
|
|
217
|
+
path = "src/micropython"
|
|
218
|
+
integration_branch = "main"
|
|
219
|
+
|
|
220
|
+
[[branches]]
|
|
221
|
+
name = "feature-branch-1"
|
|
222
|
+
pr_url = "https://github.com/micropython/micropython/pull/12345"
|
|
223
|
+
pr_number = 12345
|
|
224
|
+
title = "Add feature X"
|
|
211
225
|
```
|
|
212
226
|
|
|
213
227
|
Fields:
|
|
214
|
-
- `
|
|
215
|
-
- `
|
|
216
|
-
- `branches` -
|
|
228
|
+
- `path` - Path to the MicroPython submodule, relative to `mbm.toml`
|
|
229
|
+
- `integration_branch` - The branch rebuilt from upstream and configured PRs
|
|
230
|
+
- `branches` - Ordered feature branches and their PR metadata
|
|
217
231
|
|
|
218
232
|
## Workflow
|
|
219
233
|
|
|
@@ -264,14 +278,16 @@ Each feature branch forks directly from `upstream/master`, and all merges flow i
|
|
|
264
278
|
### Typical rebase workflow
|
|
265
279
|
|
|
266
280
|
1. Fetch latest from all remotes
|
|
267
|
-
2.
|
|
268
|
-
3.
|
|
281
|
+
2. Query GitHub for the state of each configured PR
|
|
282
|
+
3. Skip PRs already merged upstream
|
|
283
|
+
4. Create update branch from target (e.g., `upstream/master`)
|
|
284
|
+
5. For each remaining PR in config order:
|
|
269
285
|
- Fetch fresh PR content from upstream
|
|
270
286
|
- Rebase onto target
|
|
271
287
|
- Merge into update branch
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
288
|
+
6. Push remaining feature branches to their GitHub forks
|
|
289
|
+
7. Push update branch to GitLab
|
|
290
|
+
8. Display GitLab MR URL for review
|
|
275
291
|
|
|
276
292
|
### Push strategy
|
|
277
293
|
|
|
@@ -353,7 +369,7 @@ uv run pre-commit run --all-files
|
|
|
353
369
|
|
|
354
370
|
- Python 3.11+
|
|
355
371
|
- Git
|
|
356
|
-
- GitHub CLI (`gh`) - for PR metadata lookup
|
|
372
|
+
- GitHub CLI (`gh`) - for PR metadata lookup and automatic merged-PR skipping
|
|
357
373
|
- Install: https://cli.github.com/
|
|
358
374
|
- Authenticate: `gh auth login`
|
|
359
375
|
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: micropython-branch-manager
|
|
3
|
-
Version: 2.0.3
|
|
4
|
-
Summary: Tool for managing MicroPython fork integration branches
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.11
|
|
8
|
-
Requires-Dist: cyclopts>=3.0
|
|
9
|
-
Requires-Dist: pydantic>=2.0
|
|
10
|
-
Requires-Dist: tomli-w>=1.0
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
|
|
13
1
|
# micropython-branch-manager
|
|
14
2
|
|
|
15
3
|
Tool for managing MicroPython fork integration branches.
|
|
@@ -20,7 +8,7 @@ Tool for managing MicroPython fork integration branches.
|
|
|
20
8
|
|
|
21
9
|
- Rebasing integration branches onto upstream with preserved merge commits
|
|
22
10
|
- Adding PRs from upstream via merge commits
|
|
23
|
-
- Tracking branch metadata in versioned
|
|
11
|
+
- Tracking branch metadata in a versioned TOML config
|
|
24
12
|
- Safety checks for remote divergence
|
|
25
13
|
- Generating GitLab MR URLs
|
|
26
14
|
|
|
@@ -58,12 +46,16 @@ mbm init --integration-branch main
|
|
|
58
46
|
|
|
59
47
|
### Rebase integration branch
|
|
60
48
|
|
|
61
|
-
|
|
49
|
+
Rebuild the integration branch from upstream and every configured PR that is still open or closed:
|
|
62
50
|
|
|
63
51
|
```bash
|
|
64
52
|
mbm rebase upstream/master
|
|
65
53
|
```
|
|
66
54
|
|
|
55
|
+
Before training rerere or changing branches, mbm asks GitHub for each configured PR's state. A PR reported as merged is skipped: mbm does not fetch, rebase, merge, or push its feature branch. The command prints the skipped branch and PR number.
|
|
56
|
+
|
|
57
|
+
This check requires an installed and authenticated GitHub CLI (`gh`). If GitHub cannot be queried, mbm prints a warning and retains the previous behavior of processing every configured branch.
|
|
58
|
+
|
|
67
59
|
Options:
|
|
68
60
|
- `--local` - Skip fetch and push operations
|
|
69
61
|
- `--dry-run` - Show what would happen without making changes
|
|
@@ -90,12 +82,14 @@ The command will:
|
|
|
90
82
|
|
|
91
83
|
### Sync configuration
|
|
92
84
|
|
|
93
|
-
Update
|
|
85
|
+
Update the TOML config from current merge commit history and look up PR metadata:
|
|
94
86
|
|
|
95
87
|
```bash
|
|
96
88
|
mbm sync <github-username>
|
|
97
89
|
```
|
|
98
90
|
|
|
91
|
+
When GitHub reports that a configured PR is merged, `sync` prints an informational message even if the branch no longer appears in integration merge history. It does not remove the entry from `mbm.toml`; retain or remove it deliberately, for example when a downstream pin must remain during a staged rollout.
|
|
92
|
+
|
|
99
93
|
### Show configuration
|
|
100
94
|
|
|
101
95
|
Display current configuration:
|
|
@@ -123,7 +117,7 @@ mbm init
|
|
|
123
117
|
# - gitlab: git@gitlab.example.com:yourname/micropython.git
|
|
124
118
|
# - upstream: https://github.com/micropython/micropython.git
|
|
125
119
|
#
|
|
126
|
-
#
|
|
120
|
+
# Config: /path/to/mbm.toml
|
|
127
121
|
```
|
|
128
122
|
|
|
129
123
|
### Adding PRs to the integration branch
|
|
@@ -198,34 +192,26 @@ The MR creation URL pre-fills the title and description with all integrated bran
|
|
|
198
192
|
|
|
199
193
|
## Configuration File
|
|
200
194
|
|
|
201
|
-
|
|
195
|
+
`mbm.toml` records the integration branch and the PRs that mbm manages. `mbm init` creates it; `mbm add-pr` and `mbm sync` update it. In parent-repository mode it belongs beside the MicroPython submodule and is intended to be committed with that repository.
|
|
202
196
|
|
|
203
197
|
Example configuration:
|
|
204
198
|
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"name": "feature-branch-1",
|
|
216
|
-
"remote": "andrewleech",
|
|
217
|
-
"pr_url": "https://github.com/micropython/micropython/pull/12345",
|
|
218
|
-
"pr_number": 12345,
|
|
219
|
-
"title": "Add feature X"
|
|
220
|
-
}
|
|
221
|
-
]
|
|
222
|
-
}
|
|
199
|
+
```toml
|
|
200
|
+
# micropython-branch-manager (mbm)
|
|
201
|
+
path = "src/micropython"
|
|
202
|
+
integration_branch = "main"
|
|
203
|
+
|
|
204
|
+
[[branches]]
|
|
205
|
+
name = "feature-branch-1"
|
|
206
|
+
pr_url = "https://github.com/micropython/micropython/pull/12345"
|
|
207
|
+
pr_number = 12345
|
|
208
|
+
title = "Add feature X"
|
|
223
209
|
```
|
|
224
210
|
|
|
225
211
|
Fields:
|
|
226
|
-
- `
|
|
227
|
-
- `
|
|
228
|
-
- `branches` -
|
|
212
|
+
- `path` - Path to the MicroPython submodule, relative to `mbm.toml`
|
|
213
|
+
- `integration_branch` - The branch rebuilt from upstream and configured PRs
|
|
214
|
+
- `branches` - Ordered feature branches and their PR metadata
|
|
229
215
|
|
|
230
216
|
## Workflow
|
|
231
217
|
|
|
@@ -276,14 +262,16 @@ Each feature branch forks directly from `upstream/master`, and all merges flow i
|
|
|
276
262
|
### Typical rebase workflow
|
|
277
263
|
|
|
278
264
|
1. Fetch latest from all remotes
|
|
279
|
-
2.
|
|
280
|
-
3.
|
|
265
|
+
2. Query GitHub for the state of each configured PR
|
|
266
|
+
3. Skip PRs already merged upstream
|
|
267
|
+
4. Create update branch from target (e.g., `upstream/master`)
|
|
268
|
+
5. For each remaining PR in config order:
|
|
281
269
|
- Fetch fresh PR content from upstream
|
|
282
270
|
- Rebase onto target
|
|
283
271
|
- Merge into update branch
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
272
|
+
6. Push remaining feature branches to their GitHub forks
|
|
273
|
+
7. Push update branch to GitLab
|
|
274
|
+
8. Display GitLab MR URL for review
|
|
287
275
|
|
|
288
276
|
### Push strategy
|
|
289
277
|
|
|
@@ -365,7 +353,7 @@ uv run pre-commit run --all-files
|
|
|
365
353
|
|
|
366
354
|
- Python 3.11+
|
|
367
355
|
- Git
|
|
368
|
-
- GitHub CLI (`gh`) - for PR metadata lookup
|
|
356
|
+
- GitHub CLI (`gh`) - for PR metadata lookup and automatic merged-PR skipping
|
|
369
357
|
- Install: https://cli.github.com/
|
|
370
358
|
- Authenticate: `gh auth login`
|
|
371
359
|
|
|
@@ -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.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.1.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 1, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -66,6 +66,17 @@ def sync_config_with_branches(
|
|
|
66
66
|
return modified
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def find_merged_branches(
|
|
70
|
+
config: SubmoduleConfig, pr_cache: dict[str, PRInfo]
|
|
71
|
+
) -> list[BranchConfig]:
|
|
72
|
+
"""Return configured branches whose pull requests are merged upstream."""
|
|
73
|
+
return [
|
|
74
|
+
branch
|
|
75
|
+
for branch in config.branches
|
|
76
|
+
if (pr := pr_cache.get(branch.name)) is not None and pr.state == "MERGED"
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
69
80
|
def validate_branches(config: SubmoduleConfig, detected_branches: list[str]) -> list[str]:
|
|
70
81
|
"""Validate config branches against detected branches.
|
|
71
82
|
|
|
@@ -7,9 +7,13 @@ from typing import Annotated
|
|
|
7
7
|
|
|
8
8
|
import cyclopts
|
|
9
9
|
|
|
10
|
-
from .branch_sync import
|
|
10
|
+
from .branch_sync import (
|
|
11
|
+
extract_branches_from_merges,
|
|
12
|
+
find_merged_branches,
|
|
13
|
+
sync_config_with_branches,
|
|
14
|
+
)
|
|
11
15
|
from .config import CONFIG_FILENAME, MBMConfig, SubmoduleConfig, find_config
|
|
12
|
-
from .exceptions import GitHubCLINotFound, MBMError
|
|
16
|
+
from .exceptions import GitHubCLINotFound, GitHubError, MBMError
|
|
13
17
|
from .git import GitRepo
|
|
14
18
|
from .github import GitHubClient
|
|
15
19
|
from .pr import PRManager
|
|
@@ -37,7 +41,7 @@ ConfigPathParam = Annotated[
|
|
|
37
41
|
),
|
|
38
42
|
]
|
|
39
43
|
|
|
40
|
-
_SUBMODULE_CANDIDATES = [Path("src/micropython"), Path("micropython")
|
|
44
|
+
_SUBMODULE_CANDIDATES = [Path("src/micropython"), Path("micropython")]
|
|
41
45
|
|
|
42
46
|
_GITHUB_URL_RE = __import__("re").compile(
|
|
43
47
|
r"(?:https?://github\.com/|git@github\.com:)([^/]+/[^/]+?)(?:\.git)?$"
|
|
@@ -239,8 +243,16 @@ def sync(
|
|
|
239
243
|
raise ValueError("sync requires a config file. Run 'mbm init' first.")
|
|
240
244
|
git = GitRepo(sub_path)
|
|
241
245
|
|
|
242
|
-
# Detect branches
|
|
243
|
-
|
|
246
|
+
# Detect branches — resolve integration branch to a local or remote ref
|
|
247
|
+
integration_ref = git.resolve_branch_ref(sub.integration_branch)
|
|
248
|
+
if not integration_ref:
|
|
249
|
+
print(
|
|
250
|
+
f"WARNING: Integration branch '{sub.integration_branch}' not found locally "
|
|
251
|
+
"or as a remote ref. No branches detected."
|
|
252
|
+
)
|
|
253
|
+
detected = []
|
|
254
|
+
else:
|
|
255
|
+
detected = extract_branches_from_merges(git, "upstream/master", integration_ref)
|
|
244
256
|
|
|
245
257
|
# Detect upstream GitHub repo from remote URL
|
|
246
258
|
from .github import PRInfo
|
|
@@ -253,7 +265,7 @@ def sync(
|
|
|
253
265
|
try:
|
|
254
266
|
github = GitHubClient(repo=gh_repo) if gh_repo else GitHubClient()
|
|
255
267
|
pr_cache = github.get_pr_info_by_branches(gh_author)
|
|
256
|
-
except GitHubCLINotFound:
|
|
268
|
+
except (GitHubCLINotFound, GitHubError):
|
|
257
269
|
pass
|
|
258
270
|
|
|
259
271
|
# Look up individual branches not found in author cache
|
|
@@ -261,9 +273,15 @@ def sync(
|
|
|
261
273
|
all_branches = set(detected) | {b.name for b in sub.branches}
|
|
262
274
|
for branch_name in all_branches:
|
|
263
275
|
if branch_name not in pr_cache:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
276
|
+
try:
|
|
277
|
+
pr = github.find_pr_by_branch(branch_name)
|
|
278
|
+
if pr:
|
|
279
|
+
pr_cache[branch_name] = pr
|
|
280
|
+
except GitHubError:
|
|
281
|
+
pass
|
|
282
|
+
|
|
283
|
+
for branch in find_merged_branches(sub, pr_cache):
|
|
284
|
+
print(f"INFO: {branch.name}: PR #{pr_cache[branch.name].number} merged upstream")
|
|
267
285
|
|
|
268
286
|
# Sync
|
|
269
287
|
if sync_config_with_branches(sub, detected, pr_cache):
|
|
@@ -311,6 +329,6 @@ def main() -> None:
|
|
|
311
329
|
"""Entry point."""
|
|
312
330
|
try:
|
|
313
331
|
app()
|
|
314
|
-
except MBMError as e:
|
|
332
|
+
except (MBMError, ValueError) as e:
|
|
315
333
|
print(f"Error: {e}", file=sys.stderr)
|
|
316
334
|
sys.exit(1)
|
|
@@ -63,20 +63,7 @@ class GitRepo:
|
|
|
63
63
|
def fetch(self, remote: str = "--all") -> None:
|
|
64
64
|
"""Fetch from remote(s)."""
|
|
65
65
|
self.run("submodule", "sync", "--recursive", check=False)
|
|
66
|
-
|
|
67
|
-
cmd = ["git", "fetch", remote]
|
|
68
|
-
result = subprocess.run(
|
|
69
|
-
cmd,
|
|
70
|
-
cwd=self.repo_path,
|
|
71
|
-
capture_output=True,
|
|
72
|
-
text=True,
|
|
73
|
-
)
|
|
74
|
-
if result.returncode != 0:
|
|
75
|
-
stderr = result.stderr.lower()
|
|
76
|
-
if "submodule" not in stderr or (
|
|
77
|
-
"fatal" in stderr and "submodule" not in stderr.split("fatal")[0]
|
|
78
|
-
):
|
|
79
|
-
raise GitError(f"git fetch failed: {result.stderr}")
|
|
66
|
+
self.run("fetch", remote)
|
|
80
67
|
|
|
81
68
|
def checkout(self, ref: str, create: bool = False, force: bool = False) -> None:
|
|
82
69
|
"""Checkout branch or ref."""
|
|
@@ -126,18 +113,23 @@ class GitRepo:
|
|
|
126
113
|
|
|
127
114
|
Returns True if rebase completed successfully.
|
|
128
115
|
"""
|
|
129
|
-
|
|
116
|
+
rebase_merge = self.git_dir() / "rebase-merge"
|
|
117
|
+
for _ in range(100):
|
|
130
118
|
if self._get_conflicting_files():
|
|
131
119
|
return False
|
|
120
|
+
if not rebase_merge.exists():
|
|
121
|
+
return True
|
|
132
122
|
try:
|
|
133
123
|
self.run("rebase", "--continue")
|
|
134
124
|
return True
|
|
135
125
|
except GitError:
|
|
126
|
+
if self._get_conflicting_files():
|
|
127
|
+
return False
|
|
136
128
|
continue
|
|
137
129
|
return False
|
|
138
130
|
|
|
139
|
-
def merge(self, branch: str, message: str, no_ff: bool = True) -> None:
|
|
140
|
-
"""Merge branch with optional no-ff.
|
|
131
|
+
def merge(self, branch: str, message: str, no_ff: bool = True, signoff: bool = False) -> None:
|
|
132
|
+
"""Merge branch with optional no-ff and Signed-off-by trailer.
|
|
141
133
|
|
|
142
134
|
Raises:
|
|
143
135
|
MergeConflictError: If conflicts require manual resolution
|
|
@@ -145,6 +137,8 @@ class GitRepo:
|
|
|
145
137
|
args = ["merge"]
|
|
146
138
|
if no_ff:
|
|
147
139
|
args.append("--no-ff")
|
|
140
|
+
if signoff:
|
|
141
|
+
args.append("--signoff")
|
|
148
142
|
args.extend([branch, "-m", message])
|
|
149
143
|
|
|
150
144
|
try:
|
|
@@ -188,6 +182,20 @@ class GitRepo:
|
|
|
188
182
|
except GitError:
|
|
189
183
|
return False
|
|
190
184
|
|
|
185
|
+
def resolve_branch_ref(self, branch: str) -> str | None:
|
|
186
|
+
"""Resolve a branch name to a usable ref, checking local then remote tracking refs."""
|
|
187
|
+
if self.branch_exists(branch):
|
|
188
|
+
return branch
|
|
189
|
+
result = self.run(
|
|
190
|
+
"for-each-ref",
|
|
191
|
+
"--format=%(refname:short)",
|
|
192
|
+
f"refs/remotes/*/{branch}",
|
|
193
|
+
check=False,
|
|
194
|
+
)
|
|
195
|
+
if result:
|
|
196
|
+
return result.split("\n")[0]
|
|
197
|
+
return None
|
|
198
|
+
|
|
191
199
|
def create_branch(self, name: str, start_point: str) -> None:
|
|
192
200
|
"""Create branch from start point."""
|
|
193
201
|
self.run("branch", name, start_point)
|
|
@@ -74,30 +74,31 @@ class PRManager:
|
|
|
74
74
|
print(f"Fetching PR #{pr_info.number} from upstream...")
|
|
75
75
|
self._fetch_pr(pr_info)
|
|
76
76
|
|
|
77
|
-
# Update config
|
|
78
|
-
print("\nUpdating config...")
|
|
79
|
-
self.sub.branches.append(
|
|
80
|
-
BranchConfig(
|
|
81
|
-
name=pr_info.branch,
|
|
82
|
-
pr_url=pr_info.url,
|
|
83
|
-
pr_number=pr_info.number,
|
|
84
|
-
title=pr_info.title,
|
|
85
|
-
)
|
|
86
|
-
)
|
|
87
|
-
self.mbm_config.save(self.config_path)
|
|
88
|
-
|
|
89
77
|
# Merge into update branch with trailers
|
|
90
78
|
merge_msg = format_merge_message(
|
|
91
79
|
branch_name=pr_info.branch,
|
|
80
|
+
integration=update_branch,
|
|
92
81
|
title=pr_info.title,
|
|
93
82
|
pr_number=pr_info.number,
|
|
94
83
|
pr_url=pr_info.url,
|
|
95
84
|
author=pr_info.author,
|
|
96
85
|
)
|
|
97
86
|
print(f"Merging {pr_info.branch} into {update_branch}...")
|
|
98
|
-
self.git.merge(pr_info.branch, merge_msg)
|
|
87
|
+
self.git.merge(pr_info.branch, merge_msg, signoff=True)
|
|
99
88
|
print("Merge completed successfully")
|
|
100
89
|
|
|
90
|
+
# Update config only after merge succeeds
|
|
91
|
+
print("\nUpdating config...")
|
|
92
|
+
self.sub.branches.append(
|
|
93
|
+
BranchConfig(
|
|
94
|
+
name=pr_info.branch,
|
|
95
|
+
pr_url=pr_info.url,
|
|
96
|
+
pr_number=pr_info.number,
|
|
97
|
+
title=pr_info.title,
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
self.mbm_config.save(self.config_path)
|
|
101
|
+
|
|
101
102
|
# Push update branch to GitLab
|
|
102
103
|
gitlab_remote = find_gitlab_remote(self.git)
|
|
103
104
|
if gitlab_remote:
|