polygit 0.5.0__tar.gz → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: polygit
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Manage multiple git repos from a single .pgit config file.
5
5
  Project-URL: Repository, https://github.com/gschaetz/polygit
6
6
  Author-email: Gary Schaetz <gary@schaetzkc.com>
@@ -49,6 +49,17 @@ pgit push # interactively commit and push dirty repos
49
49
  pgit repos # discover drift between .pgit and the filesystem
50
50
  ```
51
51
 
52
+ `status`, `pull` and `push` accept `--only <platform>` to operate on a single platform
53
+ (e.g. `github`) instead of all of them:
54
+
55
+ ```bash
56
+ pgit status --only gitlab
57
+ pgit pull --only github --dry-run
58
+ pgit push --only gitlab --all
59
+ ```
60
+
61
+ Passing an unknown platform prints an error to stderr and exits 1.
62
+
52
63
  ## Configuration
53
64
 
54
65
  Create a `.pgit` file in the root directory that contains your repos:
@@ -16,6 +16,17 @@ pgit push # interactively commit and push dirty repos
16
16
  pgit repos # discover drift between .pgit and the filesystem
17
17
  ```
18
18
 
19
+ `status`, `pull` and `push` accept `--only <platform>` to operate on a single platform
20
+ (e.g. `github`) instead of all of them:
21
+
22
+ ```bash
23
+ pgit status --only gitlab
24
+ pgit pull --only github --dry-run
25
+ pgit push --only gitlab --all
26
+ ```
27
+
28
+ Passing an unknown platform prints an error to stderr and exits 1.
29
+
19
30
  ## Configuration
20
31
 
21
32
  Create a `.pgit` file in the root directory that contains your repos:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "polygit"
7
- version = "0.5.0"
7
+ version = "0.6.0"
8
8
  description = "Manage multiple git repos from a single .pgit config file."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE.txt" }
@@ -1,6 +1,6 @@
1
1
  import sys
2
2
  import argparse
3
- from .config import find_config, load_config
3
+ from .config import find_config, load_config, HOSTS
4
4
  from .repo import cmd_sync, cmd_status, cmd_pull, cmd_push
5
5
  from .repos import cmd_repos
6
6
 
@@ -16,9 +16,11 @@ def main():
16
16
  status_p = sub.add_parser("status", help="Show repos with uncommitted changes")
17
17
  status_p.add_argument("--json", action="store_true",
18
18
  help="Print dirty repos as a JSON list")
19
+ status_p.add_argument("--only", dest="only_platform", help="Limit to a single platform (e.g. github)")
19
20
  pull_p = sub.add_parser("pull", help="Pull all repos, auto-stashing local changes")
20
21
  pull_p.add_argument("--dry-run", action="store_true",
21
22
  help="Show ahead/behind vs. last-fetched upstream; changes nothing")
23
+ pull_p.add_argument("--only", dest="only_platform", help="Limit to a single platform (e.g. github)")
22
24
 
23
25
  push_p = sub.add_parser("push", help="Commit and push dirty repos")
24
26
  mode_g = push_p.add_mutually_exclusive_group()
@@ -31,6 +33,7 @@ def main():
31
33
  push_p.set_defaults(push_mode="interactive")
32
34
  push_p.add_argument("--dry-run", action="store_true",
33
35
  help="Show the commit/push plan without committing or pushing")
36
+ push_p.add_argument("--only", dest="only_platform", help="Limit to a single platform (e.g. github)")
34
37
 
35
38
  sub.add_parser("repos", help="Discover drift between .pgit and the filesystem")
36
39
 
@@ -49,11 +52,17 @@ def main():
49
52
  base = config_path.parent
50
53
  config = load_config(config_path)
51
54
 
55
+ only_platform = getattr(args, "only_platform", None)
56
+ if only_platform is not None and only_platform not in HOSTS:
57
+ print(f"Error: unknown platform '{only_platform}'. "
58
+ f"Available: {', '.join(sorted(HOSTS))}", file=sys.stderr)
59
+ sys.exit(1)
60
+
52
61
  commands = {
53
62
  "sync": lambda: cmd_sync(config, base),
54
- "status": lambda: cmd_status(config, base, json_output=args.json),
55
- "pull": lambda: cmd_pull(config, base, dry_run=args.dry_run),
56
- "push": lambda: cmd_push(config, base, mode=args.push_mode, dry_run=args.dry_run),
63
+ "status": lambda: cmd_status(config, base, json_output=args.json, only=only_platform),
64
+ "pull": lambda: cmd_pull(config, base, dry_run=args.dry_run, only=only_platform),
65
+ "push": lambda: cmd_push(config, base, mode=args.push_mode, dry_run=args.dry_run, only=only_platform),
57
66
  "repos": lambda: cmd_repos(config, base, config_path),
58
67
  }
59
68
  commands[args.command]()
@@ -89,9 +89,15 @@ def each_repo(config, base):
89
89
  yield platform, local_path, remote_path, base / platform / local_path
90
90
 
91
91
 
92
- def each_existing_repo(config, base):
93
- """Yield only repos that are already cloned locally."""
92
+ def each_existing_repo(config, base, only=None):
93
+ """Yield only repos that are already cloned locally.
94
+
95
+ If *only* is set, only repos under that platform are yielded. The helper
96
+ is used by :func:`cmd_status`, :func:`cmd_pull` and :func:`cmd_push`.
97
+ """
94
98
  for platform, local_path, remote_path, d in each_repo(config, base):
99
+ if only is not None and platform != only:
100
+ continue
95
101
  if (d / ".git").exists():
96
102
  yield platform, local_path, remote_path, d
97
103
 
@@ -195,7 +201,7 @@ def cmd_sync(config, base):
195
201
  f.result()
196
202
 
197
203
 
198
- def cmd_status(config, base, json_output=False):
204
+ def cmd_status(config, base, json_output=False, only=None):
199
205
  results = []
200
206
 
201
207
  def _check(platform, local_path, d):
@@ -205,7 +211,7 @@ def cmd_status(config, base, json_output=False):
205
211
  )
206
212
  return platform, local_path, result.stdout.strip()
207
213
 
208
- repos = list(each_existing_repo(config, base))
214
+ repos = list(each_existing_repo(config, base, only=only))
209
215
  with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
210
216
  futures = {pool.submit(_check, p, lp, d): (p, lp) for p, lp, _, d in repos}
211
217
  for f in as_completed(futures):
@@ -231,18 +237,18 @@ def cmd_status(config, base, json_output=False):
231
237
  print(f"{OK} All repos are clean.")
232
238
 
233
239
 
234
- def cmd_pull(config, base, dry_run=False):
235
- repos = list(each_existing_repo(config, base))
240
+ def cmd_pull(config, base, dry_run=False, only=None):
241
+ repos = list(each_existing_repo(config, base, only=only))
236
242
  with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
237
243
  futures = [pool.submit(_pull_one, p, lp, d, dry_run) for p, lp, _, d in repos]
238
244
  for f in as_completed(futures):
239
245
  f.result()
240
246
 
241
247
 
242
- def cmd_push(config, base, mode="interactive", dry_run=False):
248
+ def cmd_push(config, base, mode="interactive", dry_run=False, only=None):
243
249
  dirty = [
244
250
  (platform, local_path, d)
245
- for platform, local_path, _, d in each_existing_repo(config, base)
251
+ for platform, local_path, _, d in each_existing_repo(config, base, only=only)
246
252
  if is_dirty(d)
247
253
  ]
248
254
 
@@ -5,7 +5,7 @@ import pytest
5
5
  from pathlib import Path
6
6
  from polygit.config import find_config, load_config, write_config
7
7
  from polygit.repos import parse_remote_url, del_nested, _add_to_config
8
- from polygit.repo import walk, _pull_one, cmd_push, cmd_status
8
+ from polygit.repo import walk, _pull_one, cmd_push, cmd_status, cmd_pull
9
9
 
10
10
 
11
11
  # ── parse_remote_url ──────────────────────────────────────────────────────────
@@ -249,3 +249,89 @@ def test_status_default_clean_message(capsys, tmp_path):
249
249
  _make_clone(tmp_path)
250
250
  cmd_status({"github": {"r": None}}, tmp_path)
251
251
  assert "All repos are clean." in capsys.readouterr().out
252
+
253
+
254
+ # ── --only platform filter ───────────────────────────────────────────────────
255
+
256
+ def _gitlab_clone(tmp_path, name="gl", commit=True):
257
+ d = tmp_path / "gitlab" / name
258
+ d.parent.mkdir()
259
+ subprocess.run(["git", "clone", "-q", str(tmp_path / "origin.git"), str(d)],
260
+ check=True, capture_output=True)
261
+ if commit:
262
+ _commit(d, "seed")
263
+ _git(d, "push", "-q", "origin", "HEAD:main")
264
+ return d
265
+
266
+
267
+ def test_status_only_limits_to_platform(capsys, tmp_path):
268
+ github = _make_clone(tmp_path)
269
+ (github / "x.txt").write_text("x")
270
+ _gitlab_clone(tmp_path)
271
+ (tmp_path / "gitlab" / "gl" / "y.txt").write_text("y")
272
+
273
+ cmd_status({"github": {"r": None}, "gitlab": {"gl": None}}, tmp_path, only="github")
274
+
275
+ out = capsys.readouterr().out
276
+ assert "github/r" in out
277
+ assert "gitlab/gl" not in out
278
+
279
+
280
+ def test_status_only_json(capsys, tmp_path):
281
+ github = _make_clone(tmp_path)
282
+ (github / "x.txt").write_text("x")
283
+ _gitlab_clone(tmp_path)
284
+ (tmp_path / "gitlab" / "gl" / "y.txt").write_text("y")
285
+
286
+ cmd_status({"github": {"r": None}, "gitlab": {"gl": None}}, tmp_path,
287
+ json_output=True, only="github")
288
+
289
+ assert json.loads(capsys.readouterr().out) == [
290
+ {"platform": "github", "path": "r", "changed_files": 1}
291
+ ]
292
+
293
+
294
+ def test_pull_only_limits_to_platform(capsys, tmp_path):
295
+ _make_clone(tmp_path, ahead=2)
296
+ _gitlab_clone(tmp_path)
297
+ _commit(tmp_path / "gitlab" / "gl", "ahead")
298
+ _git(tmp_path / "gitlab" / "gl", "push", "-q", "origin", "HEAD:main")
299
+
300
+ cmd_pull({"github": {"r": None}, "gitlab": {"gl": None}}, tmp_path,
301
+ dry_run=True, only="github")
302
+
303
+ out = capsys.readouterr().out
304
+ assert "github/r" in out
305
+ assert "gitlab/gl" not in out
306
+
307
+
308
+ def test_push_only_limits_to_platform(capsys, tmp_path):
309
+ github = _make_clone(tmp_path)
310
+ (github / "g.txt").write_text("x")
311
+ _gitlab_clone(tmp_path)
312
+ (tmp_path / "gitlab" / "gl" / "l.txt").write_text("x")
313
+
314
+ cmd_push({"github": {"r": None}, "gitlab": {"gl": None}}, tmp_path,
315
+ dry_run=True, only="github")
316
+
317
+ out = capsys.readouterr().out
318
+ assert "github/r" in out
319
+ assert "gitlab/gl" not in out
320
+
321
+
322
+ def test_status_unknown_platform_errors(capsys, tmp_path, monkeypatch):
323
+ import sys
324
+ from polygit.cli import main
325
+
326
+ _make_clone(tmp_path)
327
+ (tmp_path / ".pgit").write_text("github:\n r:\n")
328
+ monkeypatch.chdir(tmp_path)
329
+ monkeypatch.setattr(sys, "argv", ["pgit", "status", "--only", "bitbucket"])
330
+
331
+ with pytest.raises(SystemExit) as exc:
332
+ main()
333
+
334
+ assert exc.value.code == 1
335
+ err = capsys.readouterr().err
336
+ assert "bitbucket" in err
337
+ assert "github" in err
@@ -117,7 +117,7 @@ wheels = [
117
117
 
118
118
  [[package]]
119
119
  name = "polygit"
120
- version = "0.5.0"
120
+ version = "0.6.0"
121
121
  source = { editable = "." }
122
122
  dependencies = [
123
123
  { name = "pyyaml" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes