odoo-devops-tools 1.25.3__tar.gz → 1.25.4__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.4
2
2
  Name: odoo-devops-tools
3
- Version: 1.25.3
3
+ Version: 1.25.4
4
4
  Summary: Build reproducible Odoo workspaces from a single project definition.
5
5
  Author: Roman Lacko
6
6
  License-Expression: MIT
@@ -789,7 +789,7 @@ Git source syntax: `git+REPO_URL//PATH/TO/PROJECT.ini?ref=REF`.
789
789
  <dd><p>Explicitly select both Odoo and addon sources, regardless of the selected workflow. Unlike <code>--sync</code>, this can fetch Odoo sources even when Docker runs Odoo from the base image.</p></dd>
790
790
 
791
791
  > **Note:**
792
- > Repository sync aborts if a target Git repository has uncommitted changes. Commit, stash or discard changes before syncing. The four sync switches are mutually exclusive; without any of them, <code>setup</code> only regenerates artifacts and does not fetch repositories.
792
+ > Repository sync aborts if a target Git repository has uncommitted changes **or a local branch contains commits absent from the remote branch**. Commit/stash uncommitted changes or push/reconcile unpublished commits before syncing. The four sync switches are mutually exclusive; without any of them, <code>setup</code> only regenerates artifacts and does not fetch repositories.
793
793
 
794
794
  </dl>
795
795
 
@@ -761,7 +761,7 @@ Git source syntax: `git+REPO_URL//PATH/TO/PROJECT.ini?ref=REF`.
761
761
  <dd><p>Explicitly select both Odoo and addon sources, regardless of the selected workflow. Unlike <code>--sync</code>, this can fetch Odoo sources even when Docker runs Odoo from the base image.</p></dd>
762
762
 
763
763
  > **Note:**
764
- > Repository sync aborts if a target Git repository has uncommitted changes. Commit, stash or discard changes before syncing. The four sync switches are mutually exclusive; without any of them, <code>setup</code> only regenerates artifacts and does not fetch repositories.
764
+ > Repository sync aborts if a target Git repository has uncommitted changes **or a local branch contains commits absent from the remote branch**. Commit/stash uncommitted changes or push/reconcile unpublished commits before syncing. The four sync switches are mutually exclusive; without any of them, <code>setup</code> only regenerates artifacts and does not fetch repositories.
765
765
 
766
766
  </dl>
767
767
 
@@ -0,0 +1 @@
1
+ __version__ = "1.25.4"
@@ -2342,6 +2342,61 @@ def ensure_repo(
2342
2342
  _run(cmd)
2343
2343
 
2344
2344
 
2345
+ def _assert_branch_can_fast_forward(repo_dir: Path, branch: str) -> None:
2346
+ """Do not replace a local branch containing commits absent from origin.
2347
+
2348
+ A clean working tree does not imply that all local commits were pushed.
2349
+ Resetting the branch with ``checkout -B`` would otherwise discard its tip.
2350
+ Check the named branch (not HEAD, which might be another branch).
2351
+ """
2352
+ local_ref = f"refs/heads/{branch}"
2353
+ remote_ref = f"refs/remotes/origin/{branch}"
2354
+ present = subprocess.run(
2355
+ ["git", "show-ref", "--verify", "--quiet", local_ref],
2356
+ cwd=str(repo_dir), capture_output=True, text=True,
2357
+ )
2358
+ if present.returncode == 1:
2359
+ return # No existing local branch to overwrite.
2360
+ if present.returncode != 0:
2361
+ raise Exception(f"Failed to inspect local Git branch {branch!r} in {repo_dir}: {present.stderr}")
2362
+
2363
+ def is_fast_forward() -> bool:
2364
+ check = subprocess.run(
2365
+ ["git", "merge-base", "--is-ancestor", local_ref, remote_ref],
2366
+ cwd=str(repo_dir), capture_output=True, text=True,
2367
+ )
2368
+ if check.returncode not in (0, 1):
2369
+ raise Exception(f"Failed to compare Git branches in {repo_dir}: {check.stderr}")
2370
+ return check.returncode == 0
2371
+
2372
+ if is_fast_forward():
2373
+ return
2374
+
2375
+ # With a shallow checkout, the remote tip may have advanced beyond our
2376
+ # shallow boundary. Fetch just enough ancestry to distinguish a normal
2377
+ # fast-forward from unpublished local commits; fail closed if still unsure.
2378
+ if _is_shallow_repo(repo_dir):
2379
+ refspec = f"+refs/heads/{branch}:{remote_ref}"
2380
+ for deepen_by in (1, 8, 32, 128, 512):
2381
+ _run(["git", "fetch", "--deepen", str(deepen_by), "origin", refspec], cwd=repo_dir)
2382
+ if is_fast_forward():
2383
+ return
2384
+ if not _is_shallow_repo(repo_dir):
2385
+ break
2386
+ if _is_shallow_repo(repo_dir):
2387
+ _run(["git", "fetch", "--unshallow", "origin", refspec], cwd=repo_dir)
2388
+ if is_fast_forward():
2389
+ return
2390
+
2391
+ raise Exception(
2392
+ f"Refusing to reset local Git branch {branch!r} in {repo_dir}: "
2393
+ f"it contains commits that are not on origin/{branch} "
2394
+ "(or the remote branch history diverged).\n"
2395
+ "Your commits are preserved. Inspect `git log` and `git status`, "
2396
+ "then push, rebase or resolve the divergence manually before syncing."
2397
+ )
2398
+
2399
+
2345
2400
  def checkout_branch(
2346
2401
  dest: Path,
2347
2402
  branch: str,
@@ -2356,8 +2411,9 @@ def checkout_branch(
2356
2411
  This matches the previous behavior and is suitable for full clones (e.g. addons).
2357
2412
 
2358
2413
  - fetch_all=False: fetch ONLY `origin/<branch>` (optionally shallow via depth),
2359
- then force local branch `<branch>` to match `origin/<branch>`.
2414
+ then fast-forward the local branch to `origin/<branch>`.
2360
2415
  This is intended for the Odoo repo where we want single-branch + shallow clones.
2416
+ - Local commits absent from the remote are never silently discarded.
2361
2417
  - already_fetched=True: reuse the fetch performed by ensure_repo() instead
2362
2418
  of fetching the same branch again.
2363
2419
  """
@@ -2372,29 +2428,16 @@ def checkout_branch(
2372
2428
  _ensure_full_origin_refspec(dest)
2373
2429
  _unshallow_if_needed(dest)
2374
2430
  _run(["git", "fetch", "--all", "--tags", "--prune"], cwd=dest)
2431
+ else:
2432
+ # Narrow fetch: ensure origin/<branch> exists even if origin was cloned
2433
+ # with --single-branch and has a restricted remote.origin.fetch refspec.
2434
+ # ensure_repo() already fetched this ref when already_fetched=True.
2435
+ if not already_fetched:
2436
+ _fetch_branch(dest, branch=branch, depth=depth)
2375
2437
 
2376
- try:
2377
- _run(["git", "rev-parse", "--verify", f"origin/{branch}"], cwd=dest)
2378
- _run(["git", "checkout", "-B", branch, f"origin/{branch}"], cwd=dest)
2379
- assert_clean_worktree(dest)
2380
- _run(["git", "pull", "--ff-only"], cwd=dest)
2381
- return
2382
- except:
2383
- pass
2384
-
2385
- _run(["git", "checkout", branch], cwd=dest)
2386
- return
2387
-
2388
- # Narrow fetch: ensure origin/<branch> exists even if origin was cloned
2389
- # with --single-branch and has a restricted remote.origin.fetch refspec.
2390
- # ensure_repo() already fetched this ref when already_fetched=True.
2391
- if not already_fetched:
2392
- _fetch_branch(dest, branch=branch, depth=depth)
2393
-
2394
- _run(["git", "rev-parse", "--verify", f"origin/{branch}"], cwd=dest)
2438
+ _run(["git", "rev-parse", "--verify", f"refs/remotes/origin/{branch}"], cwd=dest)
2439
+ _assert_branch_can_fast_forward(dest, branch)
2395
2440
  _run(["git", "checkout", "-B", branch, f"origin/{branch}"], cwd=dest)
2396
- # Ensure the working tree exactly matches the remote branch (no pull, no extra refs).
2397
- _run(["git", "reset", "--hard", f"origin/{branch}"], cwd=dest)
2398
2441
  assert_clean_worktree(dest)
2399
2442
 
2400
2443
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: odoo-devops-tools
3
- Version: 1.25.3
3
+ Version: 1.25.4
4
4
  Summary: Build reproducible Odoo workspaces from a single project definition.
5
5
  Author: Roman Lacko
6
6
  License-Expression: MIT
@@ -789,7 +789,7 @@ Git source syntax: `git+REPO_URL//PATH/TO/PROJECT.ini?ref=REF`.
789
789
  <dd><p>Explicitly select both Odoo and addon sources, regardless of the selected workflow. Unlike <code>--sync</code>, this can fetch Odoo sources even when Docker runs Odoo from the base image.</p></dd>
790
790
 
791
791
  > **Note:**
792
- > Repository sync aborts if a target Git repository has uncommitted changes. Commit, stash or discard changes before syncing. The four sync switches are mutually exclusive; without any of them, <code>setup</code> only regenerates artifacts and does not fetch repositories.
792
+ > Repository sync aborts if a target Git repository has uncommitted changes **or a local branch contains commits absent from the remote branch**. Commit/stash uncommitted changes or push/reconcile unpublished commits before syncing. The four sync switches are mutually exclusive; without any of them, <code>setup</code> only regenerates artifacts and does not fetch repositories.
793
793
 
794
794
  </dl>
795
795
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "odoo-devops-tools"
7
- version = "1.25.3"
7
+ version = "1.25.4"
8
8
  description = "Build reproducible Odoo workspaces from a single project definition."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1 +0,0 @@
1
- __version__ = "1.25.3"