Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/code-storage-go/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -770,10 +770,11 @@ func (r *Repo) PullUpstream(ctx context.Context, options PullUpstreamOptions) er

// CreateBranch creates a new branch.
func (r *Repo) CreateBranch(ctx context.Context, options CreateBranchOptions) (CreateBranchResult, error) {
baseRef := strings.TrimSpace(options.BaseRef)
baseBranch := strings.TrimSpace(options.BaseBranch)
targetBranch := strings.TrimSpace(options.TargetBranch)
if baseBranch == "" {
return CreateBranchResult{}, errors.New("createBranch baseBranch is required")
if baseRef == "" && baseBranch == "" {
return CreateBranchResult{}, errors.New("createBranch baseRef or baseBranch is required")
}
if targetBranch == "" {
return CreateBranchResult{}, errors.New("createBranch targetBranch is required")
Expand All @@ -786,11 +787,15 @@ func (r *Repo) CreateBranch(ctx context.Context, options CreateBranchOptions) (C
}

body := &createBranchRequest{
BaseBranch: baseBranch,
TargetBranch: targetBranch,
BaseIsEphemeral: options.BaseIsEphemeral,
TargetIsEphemeral: options.TargetIsEphemeral,
}
if baseRef != "" {
body.BaseRef = baseRef
} else {
body.BaseBranch = baseBranch
}

resp, err := r.client.api.post(ctx, "repos/branches/create", nil, body, jwtToken, nil)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion packages/code-storage-go/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ type archiveOptions struct {

// createBranchRequest is the JSON body for CreateBranch.
type createBranchRequest struct {
BaseBranch string `json:"base_branch"`
BaseRef string `json:"base_ref,omitempty"`
BaseBranch string `json:"base_branch,omitempty"`
TargetBranch string `json:"target_branch"`
BaseIsEphemeral bool `json:"base_is_ephemeral,omitempty"`
TargetIsEphemeral bool `json:"target_is_ephemeral,omitempty"`
Expand Down
2 changes: 2 additions & 0 deletions packages/code-storage-go/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ type ListBranchesResult struct {
// CreateBranchOptions configures branch creation.
type CreateBranchOptions struct {
InvocationOptions
BaseRef string
// Deprecated: use BaseRef instead.
BaseBranch string
TargetBranch string
BaseIsEphemeral bool
Expand Down
2 changes: 1 addition & 1 deletion packages/code-storage-go/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package storage

const (
PackageName = "code-storage-go-sdk"
PackageVersion = "0.4.0"
PackageVersion = "0.4.1"
)

func userAgent() string {
Expand Down
21 changes: 21 additions & 0 deletions packages/code-storage-python/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,27 @@ async def make_changes():
asyncio.run(make_changes())
```

### Creating a Branch

```python
async def create_preview_branch():
storage = GitStorage({"name": "your-name", "key": "your-key"})
repo = await storage.find_one({"id": "repo-id"})

result = await repo.create_branch(
base_ref="main",
target_branch="preview/demo",
target_is_ephemeral=True,
)

print(result["target_branch"])

asyncio.run(create_preview_branch())
```

Prefer `base_ref` for new code. `base_branch` is still accepted for backwards
compatibility, but it is deprecated.

### Applying a Diff Directly

If you already have a unified diff (for example, generated by `git diff`), you
Expand Down
8 changes: 6 additions & 2 deletions packages/code-storage-python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ print(branches["branches"])

# Create or promote a branch (synchronous Temporal workflow)
branch_result = await repo.create_branch(
base_branch="main",
base_ref="main",
target_branch="feature/preview",
base_is_ephemeral=False, # set True when the base lives in the ephemeral namespace
target_is_ephemeral=True, # set True to create an ephemeral branch
Expand Down Expand Up @@ -465,6 +465,9 @@ result = await repo.promote_ephemeral_branch(
print(result["target_branch"]) # "feature/awesome-change"
```

`promote_ephemeral_branch()` keeps its branch-oriented `base_branch` parameter.
Use `create_branch(base_ref=...)` for new code when you need to choose the base.

**Key points about ephemeral branches:**

- Ephemeral branches are stored separately from regular branches
Expand Down Expand Up @@ -669,7 +672,8 @@ class Repo:
async def create_branch(
self,
*,
base_branch: str,
base_ref: Optional[str] = None,
base_branch: Optional[str] = None, # deprecated
target_branch: str,
base_is_ephemeral: bool = False,
target_is_ephemeral: bool = False,
Expand Down
56 changes: 41 additions & 15 deletions packages/code-storage-python/pierre_storage/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ def normalize_diff_state(raw_state: str) -> DiffFileState:
return state_map.get(leading, DiffFileState.UNKNOWN)


def normalize_optional_ref(value: Optional[str]) -> Optional[str]:
"""Normalize optional branch/ref inputs by trimming blanks to None."""
if value is None:
return None

normalized = value.strip()
return normalized or None


class RepoImpl:
"""Implementation of repository operations."""

Expand Down Expand Up @@ -514,33 +523,55 @@ async def list_branches(
async def create_branch(
self,
*,
base_branch: str,
base_ref: Optional[str] = None,
base_branch: Optional[str] = None,
target_branch: str,
base_is_ephemeral: bool = False,
target_is_ephemeral: bool = False,
ttl: Optional[int] = None,
) -> CreateBranchResult:
"""Create or promote a branch."""
base_branch_clean = base_branch.strip()
"""Create or promote a branch.

Args:
base_ref: Preferred base ref (branch, tag, or commit SHA)
base_branch: Deprecated branch-only base name
target_branch: Target branch name
base_is_ephemeral: Whether the base ref lives in the ephemeral namespace
target_is_ephemeral: Whether to create the target in the ephemeral namespace
ttl: Token TTL in seconds
"""
base_ref_clean = normalize_optional_ref(base_ref)
base_branch_clean = normalize_optional_ref(base_branch)
target_branch_clean = target_branch.strip()

if not base_branch_clean:
raise ValueError("create_branch base_branch is required")
effective_base = base_ref_clean or base_branch_clean
if effective_base is None:
raise ValueError("create_branch base_ref or base_branch is required")
if not target_branch_clean:
raise ValueError("create_branch target_branch is required")

if base_branch_clean is not None:
warnings.warn(
"create_branch base_branch is deprecated; use base_ref instead",
DeprecationWarning,
stacklevel=2,
)

ttl_value = resolve_invocation_ttl_seconds({"ttl": ttl} if ttl is not None else None)
jwt = self.generate_jwt(
self._id,
{"permissions": ["git:write"], "ttl": ttl_value},
)

payload: Dict[str, Any] = {
"base_branch": base_branch_clean,
"target_branch": target_branch_clean,
"base_is_ephemeral": bool(base_is_ephemeral),
"target_is_ephemeral": bool(target_is_ephemeral),
}
if base_ref_clean is not None:
payload["base_ref"] = base_ref_clean
else:
payload["base_branch"] = base_branch_clean

url = f"{self.api_base_url}/api/v{self.api_version}/repos/branches/create"

Expand Down Expand Up @@ -750,19 +781,14 @@ async def promote_ephemeral_branch(
ttl: Optional[int] = None,
) -> CreateBranchResult:
"""Promote an ephemeral branch to a persistent target branch."""
if base_branch is None:
raise ValueError("promote_ephemeral_branch base_branch is required")

base_clean = base_branch.strip()
if not base_clean:
base_clean = normalize_optional_ref(base_branch)
if base_clean is None:
raise ValueError("promote_ephemeral_branch base_branch is required")

target_clean = target_branch.strip() if target_branch is not None else base_clean
if not target_clean:
raise ValueError("promote_ephemeral_branch target_branch is required")
target_clean = normalize_optional_ref(target_branch) or base_clean

return await self.create_branch(
base_branch=base_clean,
base_ref=base_clean,
target_branch=target_clean,
base_is_ephemeral=True,
target_is_ephemeral=False,
Expand Down
8 changes: 6 additions & 2 deletions packages/code-storage-python/pierre_storage/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,17 @@ async def list_branches(
async def create_branch(
self,
*,
base_branch: str,
base_ref: Optional[str] = None,
base_branch: Optional[str] = None,
target_branch: str,
base_is_ephemeral: bool = False,
target_is_ephemeral: bool = False,
ttl: Optional[int] = None,
) -> CreateBranchResult:
"""Create or promote a branch."""
"""Create or promote a branch.

base_branch is deprecated; prefer base_ref.
"""
...

async def list_tags(
Expand Down
2 changes: 1 addition & 1 deletion packages/code-storage-python/pierre_storage/version.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Version information for Pierre Storage SDK."""

PACKAGE_NAME = "code-storage-py-sdk"
PACKAGE_VERSION = "1.3.3"
PACKAGE_VERSION = "1.5.2"


def get_user_agent() -> str:
Expand Down
2 changes: 1 addition & 1 deletion packages/code-storage-python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "pierre-storage"
version = "1.5.1"
version = "1.5.2"
description = "Pierre Git Storage SDK for Python"
readme = "README.md"
license = "MIT"
Expand Down
Loading
Loading