half-orm-dev 1.0.0a7__tar.gz → 1.0.0a9__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.
Files changed (79) hide show
  1. {half_orm_dev-1.0.0a7/half_orm_dev.egg-info → half_orm_dev-1.0.0a9}/PKG-INFO +1 -1
  2. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/migrate.py +4 -0
  3. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migration_manager.py +86 -27
  4. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/release_manager.py +53 -6
  5. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/repo.py +24 -43
  6. half_orm_dev-1.0.0a9/half_orm_dev/version.txt +1 -0
  7. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9/half_orm_dev.egg-info}/PKG-INFO +1 -1
  8. half_orm_dev-1.0.0a7/half_orm_dev/version.txt +0 -1
  9. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/AUTHORS +0 -0
  10. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/LICENSE +0 -0
  11. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/README.md +0 -0
  12. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/__init__.py +0 -0
  13. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/bootstrap_manager.py +0 -0
  14. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/__init__.py +0 -0
  15. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/__init__.py +0 -0
  16. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/apply.py +0 -0
  17. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/bootstrap.py +0 -0
  18. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/check.py +0 -0
  19. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/clone.py +0 -0
  20. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/init.py +0 -0
  21. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/patch.py +0 -0
  22. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/release.py +0 -0
  23. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/restore.py +0 -0
  24. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/revert_migration.py +0 -0
  25. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/set_git_origin.py +0 -0
  26. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/sync.py +0 -0
  27. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/todo.py +0 -0
  28. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/undo.py +0 -0
  29. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/update.py +0 -0
  30. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/commands/upgrade.py +0 -0
  31. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli/main.py +0 -0
  32. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/cli_extension.py +0 -0
  33. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/database.py +0 -0
  34. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/decorators.py +0 -0
  35. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/file_executor.py +0 -0
  36. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/hgit.py +0 -0
  37. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py +0 -0
  38. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py +0 -0
  39. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/17/4/00_toml_dict_format.py +0 -0
  40. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/17/4/01_add_bootstrap_table.py +0 -0
  41. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/17/4/02_move_patches_to_subdirs.py +0 -0
  42. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/17/5/01_update_pyproject_dependency.py +0 -0
  43. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/18/0/00_add_async_support.py +0 -0
  44. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/0/18/0/01_update_default_tests.py +0 -0
  45. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/migrations/hop/BREAKING_CHANGES-1.0.0.md +0 -0
  46. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/modules.py +0 -0
  47. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/patch_manager.py +0 -0
  48. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/patch_validator.py +0 -0
  49. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
  50. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
  51. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
  52. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/patches/log +0 -0
  53. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
  54. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/release_file.py +0 -0
  55. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/scripts/repair-metadata.py +0 -0
  56. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/.gitignore +0 -0
  57. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/MANIFEST.in +0 -0
  58. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/README +0 -0
  59. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/conftest_template +0 -0
  60. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/git-hooks/pre-commit +0 -0
  61. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
  62. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/init_module_template +0 -0
  63. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/module_stub_template +0 -0
  64. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/module_template_1 +0 -0
  65. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/module_template_2 +0 -0
  66. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/module_template_3 +0 -0
  67. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/pyproject.toml +0 -0
  68. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/relation_test +0 -0
  69. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/sql_adapter +0 -0
  70. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/templates/warning +0 -0
  71. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev/utils.py +0 -0
  72. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev.egg-info/SOURCES.txt +0 -0
  73. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev.egg-info/dependency_links.txt +0 -0
  74. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev.egg-info/entry_points.txt +0 -0
  75. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev.egg-info/requires.txt +0 -0
  76. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/half_orm_dev.egg-info/top_level.txt +0 -0
  77. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/pyproject.toml +0 -0
  78. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/setup.cfg +0 -0
  79. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a9}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a7
3
+ Version: 1.0.0a9
4
4
  Summary: half_orm development Framework.
5
5
  Author-email: Joël Maïzi <joel.maizi@collorg.org>
6
6
  License-Expression: GPL-3.0-or-later
@@ -134,6 +134,10 @@ def migrate(verbose: bool) -> None:
134
134
  click.echo(f" • {error}")
135
135
 
136
136
  click.echo(f"\n✓ Synced .hop/ to active branches")
137
+
138
+ deleted = result.get('orphaned_staged_deleted', [])
139
+ if deleted:
140
+ click.echo(f"✓ Deleted {len(deleted)} orphaned ho-staged branch(es)")
137
141
  else:
138
142
  click.echo(f"✓ {utils.Color.green('Repository is up to date')}")
139
143
 
@@ -313,6 +313,9 @@ class MigrationManager:
313
313
  f"Continuing with migration attempt."
314
314
  )
315
315
 
316
+ # Ensure all active branches are in sync with origin before touching anything
317
+ self._ensure_active_branches_synced()
318
+
316
319
  # Get pending migrations
317
320
  pending = self.get_pending_migrations(current_version, target_version)
318
321
 
@@ -452,6 +455,72 @@ class MigrationManager:
452
455
  except Exception:
453
456
  pass # remote tag may already be gone
454
457
 
458
+ def _ensure_active_branches_synced(self) -> None:
459
+ """Verify all active branches are in sync with origin before migration.
460
+
461
+ Branches that are behind are fast-forwarded automatically (no local commits
462
+ at risk). Branches that are ahead or diverged block the migration — the
463
+ developer must push or resolve before proceeding.
464
+
465
+ Raises:
466
+ MigrationManagerError: if any active branch is ahead or diverged.
467
+ """
468
+ repo = self._repo
469
+ git_repo = repo.hgit._HGit__git_repo
470
+ current_branch = git_repo.active_branch.name
471
+
472
+ try:
473
+ branches_status = repo.hgit.get_active_branches_status()
474
+ except Exception:
475
+ return # can't determine status, proceed cautiously
476
+
477
+ patch_branches = [b['name'] for b in branches_status.get('patch_branches', [])]
478
+ release_branches = [b['name'] for b in branches_status.get('release_branches', [])]
479
+ # ho-staged/* branches are frozen after merge — excluded from sync checks
480
+ active_branches = release_branches + patch_branches
481
+
482
+ blocked = []
483
+ for branch in active_branches:
484
+ try:
485
+ synced, status = repo.hgit.is_branch_synced(branch)
486
+ if synced:
487
+ continue
488
+ if status == 'behind':
489
+ # Fast-forward: no local commits at risk
490
+ repo.hgit.checkout(branch)
491
+ git_repo.git.merge('--ff-only', f'origin/{branch}')
492
+ elif status in ('ahead', 'diverged'):
493
+ blocked.append((branch, status))
494
+ except Exception:
495
+ pass # branch may not exist locally, skip
496
+
497
+ # Return to original branch
498
+ try:
499
+ repo.hgit.checkout(current_branch)
500
+ except Exception:
501
+ pass
502
+
503
+ if blocked:
504
+ ahead = [(b, s) for b, s in blocked if s == 'ahead']
505
+ diverged = [(b, s) for b, s in blocked if s == 'diverged']
506
+ parts = []
507
+ if ahead:
508
+ branch_list = ', '.join(b for b, _ in ahead)
509
+ parts.append(
510
+ f" Branches ahead of origin (unpushed commits) — push first:\n"
511
+ + '\n'.join(f" git push origin {b}" for b, _ in ahead)
512
+ )
513
+ if diverged:
514
+ parts.append(
515
+ f" Branches diverged from origin (local and remote have diverged) "
516
+ f"— rebase or merge to resolve:\n"
517
+ + '\n'.join(f" {b}" for b, _ in diverged)
518
+ )
519
+ raise MigrationManagerError(
520
+ f"Migration blocked: active branches are not in sync with origin.\n"
521
+ + '\n'.join(parts)
522
+ )
523
+
455
524
  def _regenerate_modules_after_migration(
456
525
  self, from_version: str, to_version: str
457
526
  ) -> None:
@@ -468,50 +537,40 @@ class MigrationManager:
468
537
  package_name = repo.name
469
538
  package_dir = str(Path(repo.base_dir) / package_name)
470
539
 
471
- # Regenerate all modules (idempotent thanks to global reset in generate())
472
- _modules.generate(repo)
473
-
474
- # Stage the entire package directory
475
- repo.hgit.add(package_dir)
476
-
477
- # Nothing changed — nothing to do
478
- staged = git_repo.git.diff('--cached', '--name-only')
479
- if not staged.strip():
480
- return
481
-
482
- # Commit on ho-prod
483
540
  commit_msg = (
484
541
  f"[HOP] Regenerate modules (migration {from_version} → {to_version})"
485
542
  )
486
- repo.hgit.commit('-m', commit_msg)
487
- repo.hgit.push_branch('ho-prod')
488
543
 
489
- # Sync to all active branches
544
+ # Collect active branches before moving around
490
545
  try:
491
546
  branches_status = repo.hgit.get_active_branches_status()
492
547
  except Exception:
493
- return
548
+ branches_status = {}
494
549
 
495
550
  patch_branches = [b['name'] for b in branches_status.get('patch_branches', [])]
496
551
  release_branches = [b['name'] for b in branches_status.get('release_branches', [])]
497
- staged_branches = [b['name'] for b in branches_status.get('staged_branches', [])]
498
- target_branches = release_branches + patch_branches + staged_branches
552
+ # ho-staged/* branches are frozen after merge — excluded from module regeneration
553
+ all_branches = ['ho-prod'] + release_branches + patch_branches
499
554
 
500
- sync_msg = (
501
- f"[HOP] Sync modules from ho-prod (migration {from_version} → {to_version})"
502
- )
503
- for branch in target_branches:
555
+ for branch in all_branches:
504
556
  try:
505
557
  repo.hgit.checkout(branch)
506
- git_repo.git.checkout('ho-prod', '--', package_name)
558
+ # generate() reads existing files and preserves developer code sections
559
+ _modules.generate(repo)
507
560
  repo.hgit.add(package_dir)
508
- if not git_repo.git.status('--porcelain').strip():
561
+ if not git_repo.git.diff('--cached', '--name-only').strip():
509
562
  continue
510
- repo.hgit.commit('-m', sync_msg)
511
- repo.hgit.push_branch(branch)
563
+ repo.hgit.commit('-m', commit_msg)
564
+ try:
565
+ repo.hgit.push_branch(branch)
566
+ except Exception as push_err:
567
+ sys.stderr.write(
568
+ f"Warning: could not push {branch} after module regeneration "
569
+ f"(diverged branch?): {push_err}\n"
570
+ )
512
571
  except Exception as e:
513
572
  sys.stderr.write(
514
- f"Warning: could not sync modules to {branch}: {e}\n"
573
+ f"Warning: could not regenerate modules on {branch}: {e}\n"
515
574
  )
516
575
 
517
576
  repo.hgit.checkout('ho-prod')
@@ -2768,13 +2768,17 @@ class ReleaseManager:
2768
2768
  print(f"Warning: Failed to delete release branch {release_branch}: {e}", file=sys.stderr)
2769
2769
 
2770
2770
  # Delete ho-staged/X branches for all patches that were in this release.
2771
- # These were created by merge_patch (renamed from ho-patch/X) and are no
2772
- # longer needed once the release reaches production.
2771
+ # At this point the X.Y.Z-patches.toml is already gone (replaced by
2772
+ # X.Y.Z.txt during production promotion), so we read the .txt file.
2773
2773
  version = release_branch.replace('ho-release/', '')
2774
- release_file = ReleaseFile(version, self._releases_dir)
2775
- if release_file.exists():
2776
- staged_patch_ids = release_file.get_patches(status="staged")
2777
- for patch_id in staged_patch_ids:
2774
+ prod_txt = Path(self._releases_dir) / f"{version}.txt"
2775
+ if prod_txt.exists():
2776
+ patch_ids = [
2777
+ line.strip()
2778
+ for line in prod_txt.read_text(encoding='utf-8').splitlines()
2779
+ if line.strip()
2780
+ ]
2781
+ for patch_id in patch_ids:
2778
2782
  staged_branch = f"ho-staged/{patch_id}"
2779
2783
  try:
2780
2784
  if self._repo.hgit.branch_exists(staged_branch):
@@ -2786,6 +2790,49 @@ class ReleaseManager:
2786
2790
 
2787
2791
  return deleted_branches
2788
2792
 
2793
+ def cleanup_orphaned_staged_branches(self) -> list:
2794
+ """Delete every ho-staged/* branch whose patch ID is in any .txt release file.
2795
+
2796
+ A ho-staged/* branch is orphaned when its release has been promoted to
2797
+ production (the patch ID appears in a X.Y.Z.txt file) but the branch
2798
+ was never cleaned up — e.g. due to an interrupted promotion.
2799
+
2800
+ Returns:
2801
+ List of deleted branch names.
2802
+ """
2803
+ releases_dir = Path(self._releases_dir)
2804
+ git_repo = self._repo.hgit._HGit__git_repo
2805
+
2806
+ # Collect all patch IDs that are in production (.txt files)
2807
+ prod_patch_ids: set = set()
2808
+ for txt_file in releases_dir.glob("*.txt"):
2809
+ for line in txt_file.read_text(encoding='utf-8').splitlines():
2810
+ patch_id = line.strip()
2811
+ if patch_id:
2812
+ prod_patch_ids.add(patch_id)
2813
+
2814
+ if not prod_patch_ids:
2815
+ return []
2816
+
2817
+ deleted = []
2818
+ for branch in list(git_repo.branches):
2819
+ if not branch.name.startswith('ho-staged/'):
2820
+ continue
2821
+ patch_id = branch.name[len('ho-staged/'):]
2822
+ if patch_id not in prod_patch_ids:
2823
+ continue
2824
+ try:
2825
+ self._repo.hgit.delete_local_branch(branch.name)
2826
+ self._repo.hgit.delete_remote_branch(branch.name)
2827
+ deleted.append(branch.name)
2828
+ except Exception as e:
2829
+ print(
2830
+ f"Warning: Failed to delete orphaned {branch.name}: {e}",
2831
+ file=sys.stderr,
2832
+ )
2833
+
2834
+ return deleted
2835
+
2789
2836
  @with_dynamic_branch_lock(lambda self: "ho-prod")
2790
2837
  def promote_to_rc(self) -> dict:
2791
2838
  """
@@ -544,6 +544,14 @@ class Repo:
544
544
  result['migration_run'] = True
545
545
  result['errors'] = migration_result.get('errors', [])
546
546
 
547
+ # Clean up orphaned ho-staged/* branches (patch IDs in .txt = already in production)
548
+ if hasattr(self, 'release_manager'):
549
+ try:
550
+ deleted = self.release_manager.cleanup_orphaned_staged_branches()
551
+ result['orphaned_staged_deleted'] = deleted
552
+ except Exception:
553
+ result['orphaned_staged_deleted'] = []
554
+
547
555
  # Log success if not silent
548
556
  if not silent:
549
557
  if migration_result.get('migrations_applied'):
@@ -638,9 +646,8 @@ class Repo:
638
646
  patch_branches = [b['name'] for b in branches_status.get('patch_branches', [])]
639
647
  release_branches = [b['name'] for b in branches_status.get('release_branches', [])]
640
648
 
641
- # All branches = ho-prod + release branches + patch branches + staged branches
642
- staged_branches = [b['name'] for b in branches_status.get('staged_branches', [])]
643
- all_branches = ['ho-prod'] + release_branches + patch_branches + staged_branches
649
+ # ho-staged/* branches are frozen after merge excluded from sync
650
+ all_branches = ['ho-prod'] + release_branches + patch_branches
644
651
 
645
652
  # Filter release branches to avoid syncing to future versions
646
653
  # Extract version from source branch if it's a release branch
@@ -690,18 +697,10 @@ class Repo:
690
697
  remote_ref = f"origin/{branch}"
691
698
  try:
692
699
  synced, status = self.hgit.is_branch_synced(branch)
693
- if not synced and status == "diverged":
694
- print(
695
- f"Warning: branch {branch} has diverged from origin. "
696
- f"Resetting to origin (source of truth).",
697
- file=sys.stderr
698
- )
699
- # Only reset when the remote is ahead of local ("behind") or
700
- # branches have diverged. Never reset an "ahead" branch —
701
- # that would orphan local commits that have not been pushed
702
- # yet (e.g. the "Create patch directory" commit from
703
- # `hop patch create` before the first push).
704
- if not synced and status in ("behind", "diverged"):
700
+ # Only fast-forward when origin is strictly ahead (no local commits
701
+ # at risk). Never reset on diverged branches — that would destroy
702
+ # unmerged local work.
703
+ if not synced and status == "behind":
705
704
  self.hgit._HGit__git_repo.git.reset('--hard', remote_ref)
706
705
  except GitCommandError:
707
706
  # Remote branch may not exist yet, continue without reset
@@ -1528,28 +1527,8 @@ class Repo:
1528
1527
  result = repo.check_and_update(force_check=True)
1529
1528
  """
1530
1529
 
1531
- # Check cache (only if not forced and silent mode)
1532
- cache_file = Path(self.__base_dir) / '.git' / '.half_orm_check_cache'
1533
- cache_hit = False
1534
1530
 
1535
- if silent and not force_check and cache_file.exists():
1536
- try:
1537
- last_check = float(cache_file.read_text().strip())
1538
- # Check once per day (86400 seconds)
1539
- if time.time() - last_check < 86400:
1540
- cache_hit = True
1541
- return {
1542
- 'hooks': {'installed': False, 'action': 'skipped'},
1543
- 'branches': {},
1544
- 'cache_hit': True
1545
- }
1546
- except (ValueError, IOError):
1547
- pass
1548
-
1549
- # Perform checks
1550
- result = {
1551
- 'cache_hit': False
1552
- }
1531
+ result = {}
1553
1532
 
1554
1533
  # 0. Update ho-prod from remote and fetch all branches
1555
1534
  # This ensures we have the latest hop_version and branch status
@@ -1724,19 +1703,21 @@ class Repo:
1724
1703
 
1725
1704
  result['stale_branches'] = stale_branches_result
1726
1705
 
1706
+ # 3b. Delete orphaned ho-staged/* branches (patch in a .txt = already in production)
1707
+ orphaned_staged_deleted = []
1708
+ if not dry_run and hasattr(self, 'release_manager'):
1709
+ try:
1710
+ orphaned_staged_deleted = self.release_manager.cleanup_orphaned_staged_branches()
1711
+ except Exception:
1712
+ pass # Best effort
1713
+ result['orphaned_staged_deleted'] = orphaned_staged_deleted
1714
+
1727
1715
  # 4. Check version (only for explicit checks, not silent)
1728
1716
  if not silent:
1729
1717
  result['version'] = self._check_version_update()
1730
1718
  else:
1731
1719
  result['version'] = None
1732
1720
 
1733
- # Update cache
1734
- if not dry_run and silent:
1735
- try:
1736
- cache_file.write_text(str(time.time()))
1737
- except IOError:
1738
- pass # Best effort
1739
-
1740
1721
  return result
1741
1722
 
1742
1723
  def _validate_package_name(self, package_name):
@@ -0,0 +1 @@
1
+ 1.0.0-a9
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a7
3
+ Version: 1.0.0a9
4
4
  Summary: half_orm development Framework.
5
5
  Author-email: Joël Maïzi <joel.maizi@collorg.org>
6
6
  License-Expression: GPL-3.0-or-later
@@ -1 +0,0 @@
1
- 1.0.0-a7
File without changes
File without changes
File without changes
File without changes
File without changes