---
title: "Write operations — Git for Laravel | Roundly"
description: "Create branches, files, pull requests, comments, releases, tags and webhooks with validated input objects — writes are never retried."
url: https://roundly-consulting.com/open-source/docs/git-for-laravel/write-operations
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Git for Laravel](https://roundly-consulting.com/open-source/docs/git-for-laravel.md)

# Write operations

All writes take typed input objects and require an authenticated provider — an unauthenticated one throws InvalidCredentialsException before any request is sent:

```php
use RoundlyConsulting\Git\Dto\Input\{NewRepository, NewBranch, NewFile, NewPullRequest, NewComment, NewRelease, NewTag};

$created = Git::github()->createRepository(new NewRepository(name: 'acme', private: true, autoInit: true));

$repo = Git::github()->repo('acme/acme');
$repo->createBranch(new NewBranch('feature/ci', 'main'));                              // the created branch ref
$commit = $repo->createFile(new NewFile('ci.yml', '...', 'Add CI', 'feature/ci'));     // Commit
$pr = $repo->createPullRequest(new NewPullRequest('Add CI', 'feature/ci', 'main', body: 'Adds the CI workflow.'));
$repo->pullRequest($pr->number)->comment('LGTM');   // or $repo->comment(new NewComment($pr->number, 'LGTM'))
$repo->createRelease(new NewRelease('v1.0', 'First release'));
$repo->createTag(new NewTag('v1.0.1', 'main'));                                         // a branch, tag or commit sha
```

## Updating a file

updateFile() replaces an existing file on a branch. Read it first to get the blob sha the update needs:

```php
use RoundlyConsulting\Git\Dto\Input\UpdatedFile;

$repo = Git::github()->repo('acme/acme');
$current = $repo->contents('ci.yml', ref: 'feature/ci');

$commit = $repo->updateFile(new UpdatedFile(
    path: 'ci.yml',
    content: $newYaml,
    message: 'Tune CI',
    branch: 'feature/ci',
    sha: $current->sha,     // the blob being replaced
));
```

## Input objects

Every input validates itself in its constructor and throws InvalidArgumentException, so a malformed write fails at the call site instead of as a forge 422:

| Input | Constructor | Rejected locally when |
| --- | --- | --- |
| `NewRepository` | `name, private = false, description, owner, template, autoInit = false, defaultBranch` | Name is empty, template isn’t owner/repo, or defaultBranch is set without autoInit or a template. |
| `NewBranch` | `name, fromRef` | Either is empty. |
| `NewFile` | `path, content, message, branch` | Path, message or branch is empty. |
| `UpdatedFile` | `path, content, message, branch, sha` | Path, message, branch or sha is empty. |
| `NewPullRequest` | `title, head, base, body = null` | Title, head or base is empty. |
| `NewComment` | `number, body, target = null` | Body is empty. |
| `NewRelease` | `tagName, name, body, draft = false, prerelease = false` | Tag name is empty. |
| `NewTag` | `name, ref` | Either is empty. |
| `NewWebhook` | `url, events = ['push'], secret, active = true` | URL is empty or no event is given. |
| `NewReview` | `event, body, comments = []` | A non-approval review has no body. |
| `NewReviewComment` | `path, line, body, side = Right, startLine` | Path or body is empty, a line is below 1, or startLine isn’t before line. |
| `NewWorkflowDispatch` | `workflow, ref, inputs = []` | The workflow isn’t a numeric id or one .yml / .yaml file name or the ref could leave its scope (OutOfScopeException), or an input isn’t a named string, number or boolean. |

## Worth knowing

- Writes are never retried — a 5xx on a write may already have landed, and a second POST would open a second pull request.
- createBranch() branches from fromRef — a branch, a tag or a commit sha — and returns the new ref, refs/heads/<name>, on GitHub and GitLab alike.
- NewTag’s ref may be a branch, a tag or a sha. GitHub’s tag endpoint takes only a commit sha, so a branch or tag is first resolved to the commit it points at (a full sha is used as is).
- File writes — createFile() and updateFile() — return the commit they made, with its real sha, author and date, on GitHub and GitLab alike.
- A write with no payload — cancelling a workflow run, deleting a webhook — sends no request body at all, never a JSON \[\], which GitHub refuses where it expects an object or nothing.
- For webhooks on your own endpoint, prefer the idempotent repo($path)->webhooks() helper — see Webhooks.

## Comment targets

GitHub comments on issues and pull requests through one endpoint, but GitLab numbers issues and merge requests separately, so a GitLab comment must say which with a CommentTarget. An untargeted GitLab comment throws InvalidArgumentException rather than guessing; ->pullRequest($n)->comment() sets the target for you:

```php
use RoundlyConsulting\Git\Dto\Input\NewComment;
use RoundlyConsulting\Git\Enums\CommentTarget;

$repo = Git::gitlab()->repo('acme/api');

$repo->comment(new NewComment(3, 'Thanks', target: CommentTarget::Issue)); // issue #3
$repo->comment(new NewComment(7, 'LGTM', target: CommentTarget::PullRequest)); // merge request !7
$repo->pullRequest(7)->comment('LGTM');                                     // sets the target for you
```

Bitbucket comments on pull requests only and refuses CommentTarget::Issue with FeatureNotSupportedException.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/git-for-laravel.md)
