Skip to content

Instantly share code, notes, and snippets.

@rafifos
Created June 8, 2025 22:56
Show Gist options
  • Select an option

  • Save rafifos/e5b95cfe7ac3d7374ff4c7b6cfa58bf9 to your computer and use it in GitHub Desktop.

Select an option

Save rafifos/e5b95cfe7ac3d7374ff4c7b6cfa58bf9 to your computer and use it in GitHub Desktop.
GitButler Conventional Commits and GitFlow Prompts

GitButler Branch Name Generation using GitFlow and Conventional Commits

Model: Claude Sonnet 4, via GitHub Copilot Chat. See: About Claude in GitHub Copilot Chat

Prompt

I'm writing a custom branch name AI prompt for GitButler, it's going to be fed to Claude (probably 3.5, they don't specify the version, only that it's Anthropic).

This is the default prompt:

User:

Please could you write a branch name for my changes.
A branch name represent a brief description of the changes in the diff (branch).
Branch names should contain no whitespace and instead use dashes to separate words.
Branch names should contain a maximum of 5 words.
Only respond with the branch name.

Here is my git diff:

diff --git a/src/utils/typing.ts b/src/utils/typing.ts
index 1cbfaa2..7aeebcf 100644
--- a/src/utils/typing.ts
+++ b/src/utils/typing.ts
@@ -35,3 +35,10 @@ export function isNonEmptyObject(something: unknown): something >is UnknownObject
    (Object.keys(something).length > 0 || Object.getOwnPropertySymbols(something).>length > 0)
  );
}
+
+export function isArrayOf<T>(

+ something: unknown,
+ check: (value: unknown) => value is T
+): something is T[] {
+ return Array.isArray(something) && something.every(check);
+}

Assistant:

utils-typing-is-array-of-type

I want to follow GitFlow naming conventions with prefixes that align with the Conventional Commits specification I use for commit messages.

The branch name should be structured as follows:

<prefix>/<description>

The prefixes should correlate with the commit types from Conventional Commits:

  1. feature/ - for new features (correlates with feat: commits)
  2. fix/ - for bug fixes (correlates with fix: commits, merging both hotfix and bugfix)
  3. docs/ - for documentation changes (correlates with docs: commits)
  4. style/ - for formatting changes (correlates with style: commits)
  5. refactor/ - for code restructuring (correlates with refactor: commits)
  6. perf/ - for performance improvements (correlates with perf: commits)
  7. test/ - for test additions/updates (correlates with test: commits)
  8. build/ - for build system changes (correlates with build: commits)
  9. ci/ - for CI configuration changes (correlates with ci: commits)
  10. chore/ - for maintenance tasks (correlates with chore: commits)

Branch names should be descriptive but concise, using kebab-case formatting, and should not exceed 5 words in the description part.

And, some documentation from GitButler:

Custom prompts can contain three variables which we will replace with the appropriate values. Those include:

%{diff} - The contents of the diff.
%{commits} - The commit messages (for branch naming context).

Response (Paste in GitButler)

You are generating a Git branch name following GitFlow naming conventions.

REQUIREMENTS:

  • Use GitFlow prefixes: feature/, fix/, docs/, style/, refactor/, perf/, test/, build/, ci/, chore/
  • Branch name format: <prefix>/<description>
  • Description should be 2-5 words maximum
  • Use kebab-case (lowercase with dashes)
  • Be descriptive but concise
  • Only respond with the branch name

PREFIX GUIDELINES:

  • feature/ - new features or enhancements
  • fix/ - bug fixes (both critical and non-critical)
  • docs/ - documentation changes
  • style/ - formatting, missing semicolons, etc (no code change)
  • refactor/ - code restructuring without functional changes
  • perf/ - performance improvements
  • test/ - adding or updating tests
  • build/ - changes to build system or dependencies
  • ci/ - changes to CI configuration
  • chore/ - other maintenance tasks that don't modify src or test files

Git diff:

%{diff}

Commit messages (if available):

%{commits}

Examples to provide additional context

Here are several examples showing how the prompt would work with different types of changes:

Example 1: Feature addition (with commit messages)

Git diff:

diff --git a/src/auth/oauth.ts b/src/auth/oauth.ts
new file mode 100644
index 0000000..1234567
--- /dev/null
+++ b/src/auth/oauth.ts
@@ -0,0 +1,25 @@
+export class OAuthService {
+  async authenticate(provider: string): Promise<string> {
+    // OAuth implementation
+  }
+}

Commit messages:

feat(auth): add OAuth authentication service

Implement OAuth flow for external providers including Google,
GitHub, and Discord. Supports token validation and user creation.

Expected output:

feat/oauth-authentication-service

Example 2: Bug fix (with commit messages)

Git diff:

diff --git a/src/utils/validation.ts b/src/utils/validation.ts
index 1234567..abcdefg 100644
--- a/src/utils/validation.ts
+++ b/src/utils/validation.ts
@@ -5,7 +5,7 @@ export function validateEmail(email: string): boolean {
-  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
+  return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email);
 }

Commit messages:

fix(validation): require minimum TLD length in email regex

Ensure top-level domain has at least 2 characters to prevent
validation of invalid emails like [email protected]

Expected output:

fix/email-validation-regex

Example 3: Documentation (no commit messages)

Git diff:

diff --git a/README.md b/README.md
index 1234567..abcdefg 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,18 @@ npm start
+## Authentication
+
+The application supports multiple authentication methods:
+
+- Email/password login
+- OAuth providers (Google, GitHub, Discord)

Commit messages:

Expected output:

docs/authentication-setup-guide

Example 4: Refactoring (no commit messages)

Git diff:

diff --git a/src/components/UserCard.tsx b/src/components/UserCard.tsx
index 1234567..abcdefg 100644
--- a/src/components/UserCard.tsx
+++ b/src/components/UserCard.tsx
@@ -1,8 +1,8 @@
-  const fullName = user.firstName + ' ' + user.lastName;
+  const fullName = getUserDisplayName(user);
+
+function getUserDisplayName(user: User): string {
+  return `${user.firstName} ${user.lastName}`;
+}

Commit messages:

Expected output:

refactor/user-display-name-logic

Example 5: Performance improvement (with commit messages)

Git diff:

diff --git a/src/api/client.ts b/src/api/client.ts
index 1234567..abcdefg 100644
--- a/src/api/client.ts
+++ b/src/api/client.ts
@@ -1,3 +1,3 @@
-export const API_TIMEOUT = 5000;
+export const API_TIMEOUT = 10000;
-export const MAX_RETRIES = 3;
+export const MAX_RETRIES = 5;

Commit messages:

perf: increase API timeout and retry limits

Improve reliability for slower connections by extending timeout
to 10 seconds and allowing up to 5 retry attempts.

Expected output:

perf/api-timeout-retry-limits

Example 6: Multiple commits (with commit messages)

Git diff:

diff --git a/src/auth/login.ts b/src/auth/login.ts
[... multiple file changes ...]
diff --git a/src/auth/oauth.ts b/src/auth/oauth.ts
[... OAuth implementation ...]
diff --git a/tests/auth.test.ts b/tests/auth.test.ts
[... test additions ...]

Commit messages:

feat(auth): add OAuth login support

Implement OAuth authentication flow for external providers.
Creates user session after successful OAuth token validation.

test(auth): add OAuth integration tests

Add comprehensive test suite for OAuth authentication flow
including provider validation and token handling.

docs(auth): update authentication documentation

Document OAuth setup process and configuration options
for supported providers.

Expected output:

feat/oauth-login-integration

Example 7: Chore/maintenance (no commit messages)

Git diff:

diff --git a/package.json b/package.json
index 1234567..abcdefg 100644
--- a/package.json
+++ b/package.json
@@ -15,8 +15,8 @@
   "dependencies": {
-    "react": "^17.0.0",
-    "typescript": "^4.5.0"
+    "react": "^18.2.0",
+    "typescript": "^5.0.0"
   }

Commit messages:

Expected output:

chore/update-dependencies

GitButler Commit Message Generation using Conventional Commits

Model: Claude Sonnet 4, via GitHub Copilot Chat. See: About Claude in GitHub Copilot Chat

Prompt

I'm writing a custom commit message AI prompt for GitButler, it's going to be fed to Claude (probably 3.5, they don't specify the version, only that it's Anthropic).

This is the default prompt:

User:

Please could you write a commit message for my changes.
Explain what were the changes and why the changes were done.
Focus the most important changes.
Use the present tense.
Use a semantic commit prefix.
Hard wrap lines at 72 characters.
Ensure the title is only 50 characters.
Do not start any lines with the hash symbol.
Only respond with the commit message.

Here is my git diff:

diff --git a/src/utils/typing.ts b/src/utils/typing.ts
index 1cbfaa2..7aeebcf 100644
--- a/src/utils/typing.ts
+++ b/src/utils/typing.ts
@@ -35,3 +35,10 @@ export function isNonEmptyObject(something: unknown): something is UnknownObject
    (Object.keys(something).length > 0 || Object.getOwnPropertySymbols(something).length > 0)
  );
}
+
+export function isArrayOf<T>(
+  something: unknown,
+  check: (value: unknown) => value is T
+): something is T[] {
+  return Array.isArray(something) && something.every(check);
+}

Assistant:

Typing utilities: Check for array of type

Added an utility function to check whether a given value is an array of a specific type.

I want to follow the Conventional Commits v1.0.0 specification.

The commit message should be structured as follows:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

The commit contains the following structural elements, to communicate intent to the consumers of your library:

  1. fix: a commit of the type fix patches a bug in your codebase (this correlates with PATCH in Semantic Versioning).
  2. feat: a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning).
  3. BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type.
  4. types other than fix: and feat: are allowed, for example @commitlint/config-conventional (based on the Angular convention) recommends build:, chore:, ci:, docs:, style:, refactor:, perf:, test:, and others.
  5. footers other than BREAKING CHANGE: <description> may be provided and follow a convention similar to git trailer format.

Additional types are not mandated by the Conventional Commits specification, and have no implicit effect in Semantic Versioning (unless they include a BREAKING CHANGE).

A scope may be provided to a commit's type, to provide additional contextual information and is contained within parenthesis, e.g., feat(parser): add ability to parse arrays.

Here's the full Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

  1. Commits MUST be prefixed with a type, which consists of a noun, feat, fix, etc., followed by the OPTIONAL scope, OPTIONAL !, and REQUIRED terminal colon and space.
  2. The type feat MUST be used when a commit adds a new feature to your application or library.
  3. The type fix MUST be used when a commit represents a bug fix for your application.
  4. A scope MAY be provided after a type. A scope MUST consist of a noun describing a section of the codebase surrounded by parenthesis, e.g., fix(parser):
  5. A description MUST immediately follow the colon and space after the type/scope prefix. The description is a short summary of the code changes, e.g., fix: array parsing issue when multiple spaces were contained in string.
  6. A longer commit body MAY be provided after the short description, providing additional contextual information about the code changes. The body MUST begin one blank line after the description.
  7. A commit body is free-form and MAY consist of any number of newline separated paragraphs.
  8. One or more footers MAY be provided one blank line after the body. Each footer MUST consist of a word token, followed by either a :<space> or <space># separator, followed by a string value (this is inspired by the git trailer convention).
  9. A footer's token MUST use - in place of whitespace characters, e.g., Acked-by (this helps differentiate the footer section from a multi-paragraph body). An exception is made for BREAKING CHANGE, which MAY also be used as a token.
  10. A footer's value MAY contain spaces and newlines, and parsing MUST terminate when the next valid footer token/separator pair is observed.
  11. Breaking changes MUST be indicated in the type/scope prefix of a commit, or as an entry in the footer.
  12. If included as a footer, a breaking change MUST consist of the uppercase text BREAKING CHANGE, followed by a colon, space, and description, e.g., BREAKING CHANGE: environment variables now take precedence over config files.
  13. If included in the type/scope prefix, breaking changes MUST be indicated by a ! immediately before the :. If ! is used, BREAKING CHANGE: MAY be omitted from the footer section, and the commit description SHALL be used to describe the breaking change.
  14. Types other than feat and fix MAY be used in your commit messages, e.g., docs: update ref docs.
  15. The units of information that make up Conventional Commits MUST NOT be treated as case sensitive by implementors, with the exception of BREAKING CHANGE which MUST be uppercase.
  16. BREAKING-CHANGE MUST be synonymous with BREAKING CHANGE, when used as a token in a footer.

And, some examples:

Commit message with description and breaking change footer

feat: allow provided config object to extend other configs

BREAKING CHANGE: `extends` key in config file is now used for extending other config files

Commit message with ! to draw attention to breaking change

feat!: send an email to the customer when a product is shipped

Commit message with scope and ! to draw attention to breaking change

feat(api)!: send an email to the customer when a product is shipped

Commit message with both ! and BREAKING CHANGE footer

chore!: drop support for Node 6

BREAKING CHANGE: use JavaScript features not available in Node 6.

Commit message with no body

docs: correct spelling of CHANGELOG

Commit message with scope

feat(lang): add Polish language

Commit message with multi-paragraph body and multiple footers

fix: prevent racing of requests

Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.

Remove timeouts which were used to mitigate the racing issue but are
obsolete now.

Reviewed-by: Z
Refs: #123

And, finally, some documentation from GitButler:

Custom prompts can contain three variables which we will replace with the appropriate values. Those include:

%{emoji_style} - Instructs the LLM whether or not to make use of GitMoji in the title prefix, based on your settings.
%{brief_style} - Instructs the LLM to not exceed 1 sentence when generating the commit message.
%{diff} - The contents of the diff.
%{branch_name} - The name of the current branch. Available in "Commit Message" custom prompt only.

Response (Paste in GitButler)

You are writing a Git commit message following the Conventional Commits specification.

REQUIREMENTS:

  • Use conventional commit format: <type>[optional scope]: <description>
  • Title MUST be 50 characters or less
  • Use present tense and imperative mood
  • Hard wrap body lines at 72 characters
  • Do not start any lines with # symbol
  • Only respond with the commit message (no explanations)

COMMIT TYPES (use the most appropriate):

  • feat: new feature
  • fix: bug fix
  • docs: documentation changes
  • style: formatting, missing semicolons, etc (no code change)
  • refactor: code change that neither fixes bug nor adds feature
  • perf: performance improvement
  • test: adding/correcting tests
  • build: changes to build system or dependencies
  • ci: changes to CI configuration
  • chore: other changes that don't modify src or test files

OPTIONAL ELEMENTS:

  • Add scope in parentheses if change affects specific component: feat(auth):
  • Add ! after type/scope for breaking changes: feat!: or feat(api)!:
  • Add body paragraph after blank line for additional context
  • Add footer for breaking changes: BREAKING CHANGE: description

%{emoji_style} %{brief_style}

Current branch: %{branch_name}

Git diff:

%{diff}

Examples to provide additional context

Here are several examples showing how the prompt would work with different types of changes:

Example 1: Adding a new feature

Branch name: feat/oauth-login-support

Git diff:

diff --git a/src/auth/login.ts b/src/auth/login.ts
index 1234567..abcdefg 100644
--- a/src/auth/login.ts
+++ b/src/auth/login.ts
@@ -10,6 +10,15 @@ export class AuthService {
     return this.validateCredentials(email, password);
   }
+
+  async loginWithOAuth(provider: string): Promise<User> {
+    const token = await this.oauthService.authenticate(provider);
+    const user = await this.userService.getUserFromToken(token);
+    this.sessionService.createSession(user);
+    return user;
+  }
 }

Expected output:

feat(auth): add OAuth login support

Implement OAuth authentication flow for external providers.
Creates user session after successful OAuth token validation.

Example 2: Bug fix

Branch name: fix/email-validation-regex

Git diff:

diff --git a/src/utils/validation.ts b/src/utils/validation.ts
index 1234567..abcdefg 100644
--- a/src/utils/validation.ts
+++ b/src/utils/validation.ts
@@ -5,7 +5,7 @@ export function validateEmail(email: string): boolean {
   if (!email || typeof email !== 'string') {
     return false;
   }
-  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
+  return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email);
 }

Expected output:

fix(validation): require minimum TLD length in email regex

Ensure top-level domain has at least 2 characters to prevent
validation of invalid emails like [email protected]

Example 3: Breaking change

Branch name: feat/user-profile-restructure

Git diff:

diff --git a/src/api/user.ts b/src/api/user.ts
index 1234567..abcdefg 100644
--- a/src/api/user.ts
+++ b/src/api/user.ts
@@ -1,8 +1,8 @@
 export interface User {
   id: string;
   email: string;
-  name: string;
-  avatar?: string;
+  profile: UserProfile;
 }
+
+export interface UserProfile {
+  firstName: string;
+  lastName: string;
+  avatar?: string;
+}

Expected output:

feat!: restructure user profile data model

BREAKING CHANGE: User interface now uses nested profile object
instead of flat name field. Update to use profile.firstName and
profile.lastName properties.

Example 4: Documentation

Branch name: docs/auth-documentation

Git diff:

diff --git a/README.md b/README.md
index 1234567..abcdefg 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,18 @@ npm install mypackage
 npm start
 ```

+## Authentication
+
+The application supports multiple authentication methods:
+
+- Email/password login
+- OAuth providers (Google, GitHub, Discord)
+- JWT token refresh
+
+Configure OAuth providers in the `.env` file:
+```
+OAUTH_GOOGLE_CLIENT_ID=your_client_id
+```
+

## Contributing

Expected output:

docs: add authentication configuration guide

Document supported auth methods and OAuth setup instructions.

Example 5: Refactoring

Branch name: feat/user-display-name-refactor

Git diff:

diff --git a/src/components/UserCard.tsx b/src/components/UserCard.tsx
index 1234567..abcdefg 100644
--- a/src/components/UserCard.tsx
+++ b/src/components/UserCard.tsx
@@ -1,15 +1,8 @@
-export function UserCard({ user }: { user: User }) {
-  const fullName = user.firstName + ' ' + user.lastName;
-
-  return (
-    <div className="user-card">
-      <img src={user.avatar} alt={fullName} />
-      <h3>{fullName}</h3>
-      <p>{user.email}</p>
-    </div>
-  );
-}
+export function UserCard({ user }: { user: User }) {
+  return (
+    <div className="user-card">
+      <img src={user.avatar} alt={getUserDisplayName(user)} />
+      <h3>{getUserDisplayName(user)}</h3>
+      <p>{user.email}</p>
+    </div>
+  );
+}
+
+function getUserDisplayName(user: User): string {
+  return `${user.firstName} ${user.lastName}`;
+}

Expected output:

refactor(components): extract user display name logic

Move name concatenation to reusable helper function for
consistency across components.

Example 6: Simple one-liner fix

Branch name: fix/api-timeout-increase

Git diff:

diff --git a/src/constants.ts b/src/constants.ts
index 1234567..abcdefg 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,3 +1,3 @@
-export const API_TIMEOUT = 5000;
+export const API_TIMEOUT = 10000;
 export const MAX_RETRIES = 3;

Expected output:

perf: increase API timeout to 10 seconds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment