> ## 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.

# createCommit()

> Build and push a commit to a branch using the fluent commit builder.

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  const fs = await import('node:fs/promises');

  const result = await repo
    .createCommit({
      targetBranch: 'main',
      commitMessage: 'Update dashboard docs',
      expectedHeadSha: currentHeadSha, // optional safety check
      author: { name: 'Docs Bot', email: 'docs@example.com' },
    })
    .addFileFromString('docs/changelog.md', '# v2.1.0\n- refresh docs\n')
    .addFile('public/logo.svg', await fs.readFile('assets/logo.svg'))
    .deletePath('docs/legacy.txt')
    .send();

  console.log(result.commitSha);
  console.log(result.refUpdate.newSha);
  console.log(result.refUpdate.oldSha);
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  result = await (
      repo.create_commit(
          target_branch="main",
          commit_message="Update dashboard docs",
          author={"name": "Docs Bot", "email": "docs@example.com"},
      )
      .add_file_from_string("docs/changelog.md", "# v2.1.0\n- refresh docs\n")
      .add_file("public/logo.svg", b"<svg/>")
      .delete_path("docs/legacy.txt")
      .send()
  )

  print(result["commit_sha"])
  print(result["ref_update"]["new_sha"])
  print(result["ref_update"]["old_sha"])
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  // Build a commit and add files
  builder, err := repo.CreateCommit(storage.CommitOptions{
  	TargetBranch:  "main",
  	CommitMessage: "Update dashboard docs",
  	Author:        storage.CommitSignature{Name: "Docs Bot", Email: "docs@example.com"},
  })

  // Add and delete files, then send the commit
  result, err := builder.
  	AddFileFromString("docs/changelog.md", "# v2.1.0\n- refresh docs\n", nil).
  	AddFile("public/logo.svg", bytes.NewReader([]byte("<svg/>")), nil).
  	DeletePath("docs/legacy.txt").
  	Send(context.Background())

  // Inspect the result
  fmt.Println(result.CommitSHA)
  fmt.Println(result.RefUpdate.NewSHA)
  fmt.Println(result.RefUpdate.OldSHA)
  ```
</CodeGroup>

If the backend rejects the update (for example, the branch moved past `expectedHeadSha`),
`repo.createCommit().send()` throws a `RefUpdateError` containing the status, reason, and ref
details.

## Builder Methods

<ParamField path="addFile(...)" type="string">
  Attach bytes, async iterables, readable streams, or buffers.
</ParamField>

<ParamField path="addFileFromString(...)" type="string">
  Add UTF-8 text files.
</ParamField>

<ParamField path="deletePath(...)" type="string">
  Remove files or folders.
</ParamField>

<ParamField path="send()" type="string">
  Finalize the commit and receive metadata about the new commit.
</ParamField>

## Options

<ParamField path="targetBranch" type="string" required>
  Branch name that will receive the commit (for example `main`).
</ParamField>

<ParamField path="commitMessage" type="string" required>
  The commit message.
</ParamField>

<ParamField path="author" type="string" required>
  Provide `name` and `email` for the commit author.
</ParamField>

<ParamField path="expectedHeadSha" type="string">
  Commit SHA that must match the remote tip; omit to fast-forward unconditionally.
</ParamField>

<ParamField path="baseBranch" type="string">
  Mirrors the `base_branch` metadata field. Point to an existing branch whose tip should seed
  `targetBranch` if it does not exist. When bootstrapping a new branch, omit `expectedHeadSha` so
  the service copies from `baseBranch`; if both fields are provided and the branch already exists,
  the `expectedHeadSha` guard still applies.
</ParamField>

<ParamField path="ephemeral" type="string">
  Store the branch under the `refs/namespaces/ephemeral/...` namespace. When enabled, the commit is
  kept out of the primary Git remotes (for example, GitHub) but remains available through storage
  APIs.
</ParamField>

<ParamField path="ephemeralBase" type="string">
  Use alongside `baseBranch` when the seed branch also lives in the ephemeral namespace. Requires
  `baseBranch` to be set.
</ParamField>

<ParamField path="committer" type="string">
  Provide `name` and `email`. If omitted, the author identity is reused.
</ParamField>

<ParamField path="signal" type="string">
  Abort an in-flight upload with `AbortController`.
</ParamField>

<ParamField path="targetRef" type="string">
  Deprecated. Fully qualified ref (for example `refs/heads/main`). Prefer `targetBranch`.
</ParamField>

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

## Response

<ResponseField name="commitSha" type="string">
  The SHA of the created commit
</ResponseField>

<ResponseField name="treeSha" type="string">
  The SHA of the commit's tree object
</ResponseField>

<ResponseField name="targetBranch" type="string">
  The branch that received the commit
</ResponseField>

<ResponseField name="packBytes" type="number">
  Size of the uploaded pack in bytes
</ResponseField>

<ResponseField name="blobCount" type="number">
  Number of blobs in the commit
</ResponseField>

<ResponseField name="refUpdate" type="object">
  Contains `branch`, `oldSha`/`old_sha`, and `newSha`/`new_sha`
</ResponseField>

## Streaming Large Files

Use streams or async generators to upload large files without loading them into memory:

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  import { createReadStream } from 'node:fs';

  // Simplest approach: Node.js ReadableStream
  await repo
    .createCommit({
      targetBranch: 'assets',
      expectedHeadSha: 'abc123...',
      commitMessage: 'Upload latest design bundle',
      author: { name: 'Assets Uploader', email: 'assets@example.com' },
    })
    .addFile('assets/design-kit.zip', createReadStream('/tmp/large-file.zip'))
    .send();

  // Alternative: Async generator for custom chunking
  async function* fileChunks() {
    const fs = await import('node:fs/promises');
    const file = await fs.open('/tmp/large-file.zip', 'r');
    const chunkSize = 1024 * 1024; // 1MB chunks

    try {
      while (true) {
        const buffer = Buffer.alloc(chunkSize);
        const { bytesRead } = await file.read(buffer, 0, chunkSize);
        if (bytesRead === 0) break;
        yield buffer.subarray(0, bytesRead);
      }
    } finally {
      await file.close();
    }
  }

  await repo
    .createCommit({
      targetBranch: 'assets',
      commitMessage: 'Upload with custom chunking',
      author: { name: 'Assets Uploader', email: 'assets@example.com' },
    })
    .addFile('assets/design-kit.zip', fileChunks())
    .send();
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  # Async generator for streaming files in chunks
  async def file_chunks():
      """Generate file chunks asynchronously."""
      with open("/tmp/large-file.zip", "rb") as f:
          while chunk := f.read(1024 * 1024):  # Read 1MB at a time
              yield chunk

  result = await (
      repo.create_commit(
          target_branch="assets",
          expected_head_sha="abc123...",
          commit_message="Upload latest design bundle",
          author={"name": "Assets Uploader", "email": "assets@example.com"},
      )
      .add_file("assets/design-kit.zip", file_chunks())
      .send()
  )
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  // Stream a large file without loading it into memory
  file, err := os.Open("/tmp/large-file.zip")
  defer file.Close()

  // Create a commit and attach the stream
  builder, err := repo.CreateCommit(storage.CommitOptions{
  	TargetBranch:  "assets",
  	CommitMessage: "Upload latest design bundle",
  	Author:        storage.CommitSignature{Name: "Assets Uploader", Email: "assets@example.com"},
  })

  _, err = builder.AddFile("assets/design-kit.zip", file, nil).Send(context.Background())
  ```
</CodeGroup>

The SDK automatically chunks files to 4 MiB segments, so you can stream large assets (videos,
archives, datasets) without buffering them entirely in memory.

## Response

The `send()` method returns the following:
