> ## Documentation Index
> Fetch the complete documentation index at: https://code.storage/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# merge()

> Merge one branch into another with merge, fast-forward-only, or fast-forward-preferred behavior.

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  const result = await repo.merge({
    sourceBranch: 'feature/preview',
    sourceIsEphemeral: true,
    targetBranch: 'main',
    targetIsEphemeral: false,
    expectedTargetSha: currentMainSha, // optional optimistic concurrency guard
    strategy: 'merge', // "merge" | "ff_only" | "ff_prefer"
    commitMessage: 'Merge feature/preview', // optional
    author: { name: 'Merge Bot', email: 'merge@example.com' }, // optional
    committer: { name: 'Merge Bot', email: 'merge@example.com' }, // optional
    allowUnrelatedHistories: false, // optional
    squash: false, // optional, incompatible with "ff_only"
  });

  console.log(result.result); // "merge_commit", "fast_forward", "no_op", or "unknown"
  console.log(result.target.oldSha);
  console.log(result.target.newSha);
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  result = await repo.merge(
      source_branch="feature/preview",
      source_is_ephemeral=True,
      target_branch="main",
      target_is_ephemeral=False,
      expected_target_sha=current_main_sha,
      strategy="merge",
      commit_message="Merge feature/preview",
      author={"name": "Merge Bot", "email": "merge@example.com"},
      committer={"name": "Merge Bot", "email": "merge@example.com"},
      allow_unrelated_histories=False,
      squash=False,  # optional, incompatible with "ff_only"
  )

  print(result["result"])  # "merge_commit", "fast_forward", "no_op", "squash", or "unknown"
  print(result["target"]["old_sha"])
  print(result["target"]["new_sha"])
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  result, err := repo.Merge(context.Background(), storage.MergeOptions{
  	SourceBranch:            "feature/preview",
  	SourceIsEphemeral:       true,
  	TargetBranch:            "main",
  	TargetIsEphemeral:       false,
  	ExpectedTargetSHA:       currentMainSHA,
  	Strategy:                storage.MergeStrategyMerge,
  	CommitMessage:           "Merge feature/preview",
  	Author:                  &storage.CommitSignature{Name: "Merge Bot", Email: "merge@example.com"},
  	Committer:               &storage.CommitSignature{Name: "Merge Bot", Email: "merge@example.com"},
  	AllowUnrelatedHistories: false,
  	Squash:                  false, // optional, incompatible with MergeStrategyFFOnly
  })

  fmt.Println(result.Result)
  fmt.Println(result.Target.OldSHA)
  fmt.Println(result.Target.NewSHA)
  ```
</CodeGroup>

Source and target branches can independently live in the default or ephemeral namespace. Use
`expectedTargetSha` when you want an optimistic concurrency check before updating the target branch.

## Concurrency behavior

`merge()` has two target-tip modes:

* Provide `expectedTargetSha` when the target branch must still point at a specific commit. If the
  target moved, the merge fails with `409`.
* Omit `expectedTargetSha` when you want Code Storage to merge into the current target tip. For
  native Code Storage targets, the gateway may retry stale target or repository movement internally
  while keeping the originally resolved source commit pinned.

Automatic retry is useful when many workers are merging into the same target branch. Retries do not
chase new source branch commits: once the first merge attempt resolves the source tip, every retry
merges that same source commit against the latest target tip.

<Note>
  Automatic stale-target retry is disabled for GitHub-backed public targets. Those merges use the
  same single-attempt behavior as a strict target-tip guard.
</Note>

## Options

<ParamField path="sourceBranch" type="string" required>
  Source branch name to merge from.
</ParamField>

<ParamField path="sourceIsEphemeral" type="boolean">
  Set to `true` when the source branch lives in the ephemeral namespace.
</ParamField>

<ParamField path="targetBranch" type="string" required>
  Target branch name to merge into.
</ParamField>

<ParamField path="targetIsEphemeral" type="boolean">
  Set to `true` when the target branch lives in the ephemeral namespace.
</ParamField>

<ParamField path="strategy" type="string" required>
  Merge strategy. Must be one of `merge`, `ff_only`, or `ff_prefer`.
</ParamField>

<ParamField path="expectedTargetSha" type="string">
  Commit SHA that must match the current target tip before the merge is applied. Providing this
  field enables strict optimistic concurrency and disables automatic stale-target retry.
</ParamField>

<ParamField path="commitMessage" type="string">
  Optional merge commit message. Used when the backend creates a merge commit.
</ParamField>

<ParamField path="author" type="object">
  Optional commit identity with `name` and `email`. Required together when provided.
</ParamField>

<ParamField path="committer" type="object">
  Optional committer identity with `name` and `email`. Required together when provided.
</ParamField>

<ParamField path="allowUnrelatedHistories" type="boolean">
  Set to `true` to allow merging branches without a shared history.
</ParamField>

<ParamField path="squash" type="boolean">
  Collapse the source into a single new commit whose only parent is the current target tip.
  Incompatible with the `ff_only` strategy.
</ParamField>

<ParamField path="refPolicies" type="object[]">
  Ordered per-ref policy rules (`{ pattern, ops? }`) embedded in the per-call JWT. First match wins, evaluated against the target ref. Python: `ref_policies`. Go: `RefPolicies` with type `storage.RefPolicyList`. See the [Branch Protection guide](/docs/guides/branch-protection).
</ParamField>

## Response

<ResponseField name="result" type="string">
  Merge outcome: `merge_commit`, `fast_forward`, `no_op`, or `unknown`. The Python and Go SDKs also
  surface `squash`. The TypeScript SDK normalizes `squash` to `merge_commit` for semver
  compatibility.
</ResponseField>

<ResponseField name="commitSha" type="string">
  Commit SHA reported for the merge result.
</ResponseField>

<ResponseField name="treeSha" type="string">
  Tree SHA for the resulting commit.
</ResponseField>

<ResponseField name="source" type="object">
  Source ref metadata with `branch`, `ephemeral`, and `sha`.
</ResponseField>

<ResponseField name="target" type="object">
  Target ref metadata with `branch`, `ephemeral`, and `oldSha`/`old_sha` plus `newSha`/`new_sha`.
</ResponseField>

<ResponseField name="mergeBaseSha" type="string">
  Merge base SHA when the backend reports one.
</ResponseField>

<ResponseField name="promotedCommits" type="number">
  Number of commits promoted onto the target branch.
</ResponseField>

## Errors

Merge conflicts and retry exhaustion are surfaced as `ApiError`/`APIError`. The raw error body is
preserved, so callers can inspect `conflict_type` and any backend fields included with the response.

```json theme={"theme":{"light":"github-light","dark":"min-dark"}}
{
  "error": "merge conflict",
  "conflict_type": "merge_conflict",
  "conflict_paths": ["docs/example.md"],
  "merge_base_sha": "a2d127e6a4d54bb7390de828a99e36411f0c84df"
}
```

`conflict_type` can be:

* `merge_conflict`: Git found file-level conflicts. `conflict_paths` and `merge_base_sha` may be
  present.
* `target_moved`: the target branch kept moving during automatic retry. Retry the merge later.
* `repo_changed`: repository state changed during automatic retry. Retry the merge later.
