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
2,140 changes: 333 additions & 1,807 deletions package-lock.json

Large diffs are not rendered by default.

33 changes: 23 additions & 10 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ A CLI for writing better commits, following the conventional commits specificati
- Generate conventional commits through a series of prompts
- Highly configurable with sane defaults
- Infers ticket, commit scope, and commit-type from branch for consistent & fast commits
- Consistent branch creation with flexible workflow hooks via `better-branch`
- Consistent branch / worktree creation with flexible workflow hooks via `better-branch`
- Interactive git status/add on commit
- Preview commit messages in color
- Support for git emojis per commit-type
- Configure globally or per repository
- Config validation and error messaging
- [Lightweight](https://bundlejs.com/?q=better-commits&treeshake=%5B*%5D) (17kb)
- [Lightweight](https://bundlejs.com/?q=better-commits&treeshake=%5B*%5D) (34kb)

As a side-effect of formatting messages

Expand All @@ -48,7 +48,7 @@ To run the CLI in your terminal:

```sh
better-commits # Create a new commit
better-branch # Create a new branch
better-branch # Create a new branch / worktree
```

`better-commits` will prompt a series of questions. These prompts will build a commit message, which you can preview, before confirming the commit. - To better understand these prompts and their intention, read [Conventional Commits Summary](https://www.conventionalcommits.org/en/v1.0.0-beta.4/#summary)
Expand Down Expand Up @@ -271,6 +271,8 @@ To create a **repository-specific config**, navigate to the root of your project

/* BRANCH FIELDS */
// Optional shell commands to run before / after creating branches or worktrees
// - Variables: {{USER}}, {{TYPE}}, {{SCOPE}}, {{DESCRIPTION}}, {{TICKET}}, {{BRANCH-VERSION}}, {{CHECKOUT}}
// - Values are shell-quoted automatically: {{TICKET}} --> 'JIRA-123'
Comment on lines +274 to +275
"branch_pre_commands": [],
"branch_post_commands": [],
"worktree_pre_commands": [],
Expand Down Expand Up @@ -371,7 +373,7 @@ Better branch is a secondary feature that works with better commits
- Uses same type-list/prompt from your config
- Enables better-commits to infer type, scope, and ticket
- Caches your username for speedy branching
- Convenient worktree creation
- Convenient worktree creation and hooks

To run the CLI in your terminal:

Expand All @@ -389,15 +391,25 @@ better-branch
> [!TIP]
> By default, `better-branch` will create **worktrees** as a sibling folder. To change this, see `worktrees.base_path`.

### Pre/Post Branch Checkout Hooks
> [!TIP]
> Use worktrees in a repository often?
>
> You can target your **main** folder for **worktree creation** from any folder (root or sibling)
>
> alias create_worktree='better-branch --checkout worktree --git-dir="MAIN_FOLDER/.git" --work-tree="MAIN_FOLDER"'

Optionally configure pre and post checkout commands, for example:
### Pre/Post Branch Checkout Hooks

- checkout and rebase main before branching
- run `npm install` before branching
- run `npm run dev` after branching
Configure commands to run before or after creating a branch or worktree:

See _branch_pre_commands_ and _branch_post_commands_ in default config. (or _worktree_pre_commands_ and _worktree_post_commands_ for creating worktrees)
```jsonc
{
"branch_pre_commands": ["git pull -r origin main"],
"branch_post_commands": ["npm install"],
"worktree_pre_commands": ["echo Creating {{TYPE}} for {{TICKET}}"],
"worktree_post_commands": ["npm run setup -- --ticket={{TICKET}}"],
}
```

## 💡 Tips & Tricks

Expand Down Expand Up @@ -445,6 +457,7 @@ Use CLI flags to pass commit values directly instead of answering prompts.
- Use `--dry-run` to validate the generated `git commit` command without creating a commit.
- Supported commit field flags: `--type`, `--scope`, `--title`, `--body`, `--ticket`, `--closes`, `--deprecates`, `--breaking-title`, `--breaking-body`, `--deprecates-title`, `--deprecates-body`, `--custom-footer`, `--trailer`.
- Supported branch field flags: `--user`, `--type`, `--scope`, `--description`, `--ticket`, `--branch-version`, `--checkout`.
- Values like ticket, scope, and type will still attempt to be inferred where possible when using `--no-interactive`

**Examples**

Expand Down
68 changes: 68 additions & 0 deletions src/branch-hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as p from "@clack/prompts";
import { execSync } from "child_process";
import { InferOutput } from "valibot";
import { branch_flags } from "./branch-args";
import { BranchState, Config } from "./valibot-state";
import { render_hook_command } from "./utils/render-hook-command";

export class BranchHooks {
#pre_commands: string[];
#post_commands: string[];
#shell?: string;

constructor(
config: InferOutput<typeof Config>,
branch_state: InferOutput<typeof BranchState>,
) {
const is_worktree = branch_state.checkout === "worktree";
const pre_commands = is_worktree
? config.worktree_pre_commands
: config.branch_pre_commands;
const post_commands = is_worktree
? config.worktree_post_commands
: config.branch_post_commands;

this.#shell = config.overrides.shell;
this.#pre_commands = this.#render_commands(pre_commands, branch_state);
this.#post_commands = this.#render_commands(post_commands, branch_state);
}

run_pre(): void {
this.#run_commands(
this.#pre_commands,
"Something went wrong when executing pre-commands: ",
);
}

run_post(): void {
this.#run_commands(
this.#post_commands,
"Something went wrong when executing post-commands: ",
);
}

#render_commands(
commands: string[],
branch_state: InferOutput<typeof BranchState>,
): string[] {
return commands.map((command) =>
render_hook_command(command, branch_state),
);
}

#run_commands(commands: string[], error_message: string): void {
commands.forEach((command) => {
if (branch_flags.dry_run) {
p.log.info(`Dry run: ${command}`);
return;
}

try {
execSync(command, { stdio: "inherit", shell: this.#shell });
} catch (err) {
p.log.error(`${error_message}${command}\n${err}`);
throw err;
}
Comment on lines +60 to +65
});
}
}
2 changes: 2 additions & 0 deletions src/default-config-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ export const DEFAULT_CONFIG_TEMPLATE = `{

/* BRANCH FIELDS */
// Optional shell commands to run before / after creating branches or worktrees
// - Variables: {{USER}}, {{TYPE}}, {{SCOPE}}, {{DESCRIPTION}}, {{TICKET}}, {{BRANCH-VERSION}}, {{CHECKOUT}}
// - Values are shell-quoted automatically: {{TICKET}} --> 'JIRA-123'
Comment on lines +186 to +187
"branch_pre_commands": [],
"branch_post_commands": [],
"worktree_pre_commands": [],
Expand Down
Loading
Loading