Skip to content

Latest commit

 

History

History
699 lines (461 loc) · 25.9 KB

File metadata and controls

699 lines (461 loc) · 25.9 KB

git-cl: A Git Subcommand for Changelist Management

git-cl is a command-line tool that brings changelists to Git — like sticky notes for your working directory. It lets you group files into named changelists before staging or committing, acting as a pre-staging review layer for managing multiple parallel changes with intent and clarity.

Changelists can be stashed selectively and promoted to dedicated branches — an approach called late-binding branching.​​​​​​​​​​​​​​​​

Why git-cl?

git-cl is a Git subcommand that lets you group related file changes under a named changelist — similar to changelists in Subversion, but tailored for Git workflows.

Traditional Git workflow vs git-cl workflow comparison

This helps when:

  • Working on several features or fixes in parallel
  • Wanting to break down a messy working directory into logical units
  • Staging or committing changes by intent rather than file

Changelists are saved locally in .git/cl.json. They’re private to your workspace and not shared or committed. Think of them as quick labels on piles of work.

Read more: Why Why git-cl exists for the personal history, and the concept paper for design and related work.

How changelists fit into Git workflows

Changelists act as a layer above Git's staging area — helping you group related edits before deciding what to stage or commit:

Changelists staging flow: from working directory to commit

They work alongside Git's core concepts, not in place of them:

Concept Description Role in Workflow
Changelist A named group of modified files Organise uncommitted work
Staging Area Selects what goes into the next commit (Git docs) Prepare commit content
Branch A line of development with commit history (Git docs) Isolate long-running or shared work

Use changelists as your own labelled workspace, keep unrelated edits separate, or manage overlapping work — all without affecting Git history.

1. Installation

Method 1: Install via pip

pip install git-changelists

This is the easiest method if you already have Python and pip available.

Method 2: Download the script

For the latest stable version, download directly from the git-cl repository:

mkdir -p ~/bin
wget https://raw.githubusercontent.com/BHFock/git-cl/main/git-cl -O ~/bin/git-cl
chmod +x ~/bin/git-cl

Make sure ~/bin is in your $PATH. You can add this line to your shell config file if needed:

export PATH="$HOME/bin:$PATH"

Method 3: Clone the repository

Clone the complete git-cl project including documentation and tests:

git clone https://github.com/BHFock/git-cl.git ~/opt/git-cl
chmod +x ~/opt/git-cl/git-cl

Add the cloned directory to your $PATH:

export PATH="$HOME/opt/git-cl:$PATH"

You can add this line to your shell profile (~/.bashrc, ~/.zshrc, etc.) to make it permanent.

Verify Installation

Then confirm it's working:

git cl --version
git cl help

Git will recognise git-cl as a subcommand: you can now run git cl just like git commit or git status.

↑ Back to top

2. Basic Commands

The following commands are the core of git-cl. Each helps you manage your changelists as you prepare your working directory for staging or commits.

2.1 Add files to a changelist

git cl add <changelist-name> <file1> <file2> ...
  • Groups files under a named changelist.
  • A file can only belong to one changelist at a time; it will be moved if already assigned elsewhere.

Example

git cl add docs README.md docs/index.md

2.2 View status by changelist

git cl status
# or
git cl st
  • Like git status --porcelain, but grouped by changelist.
  • Shows Git’s precise two-letter status codes, grouped under each changelist.
  • Files not assigned to any changelist appear under 'No Changelist'.
Filtering by changelist name

You can pass one or more changelist names to show only those specific groups:

git cl st docs tests

This limits the output to the specified changelists. By default, unassigned files (those in 'No Changelist') are hidden in this mode.

If you want to include unassigned files alongside named changelists, use the --include-no-cl flag:

git cl st docs --include-no-cl

Showing all Git status codes

By default, git cl status shows common status codes (like [M ], [??], [ D]) together with merge-conflict codes ([UU], [AA], etc.), since conflicts require your immediate attention. Less common codes such as type changes ([T ]) are hidden to keep the output readable. To include every Git status code, use the --all flag:

git cl st --all

Example

Example output of git cl st, showing files grouped by changelist with standard Git status codes.

git cl st output showing changelists feature1, feature2, and No Changelist with colour-coded status codes

Hiding untracked files

If your repository has many untracked files that clutter the output, you can hide them with --no-untracked:

git cl st --no-untracked

This is useful when working on a focused task and you only want to see files already known to Git.

2.3 Diff a changelist

git cl diff <changelist-name>
git cl diff <changelist1> <changelist2>
git cl diff <changelist-name> --staged
  • Shows a unified diff (git diff) of the files in the changelist.
  • When you specify multiple changelists, shows a combined diff of all files from those changelists.
  • Uses git diff --cached when --staged is provided.

Example

git cl diff docs          # Show diff for 'docs' changelist only
git cl diff docs tests    # Show combined diff for both 'docs' and 'tests'
git cl diff docs --staged # Show staged changes for 'docs' changelist

2.4 Stage and Unstage a Changelist

Stage a changelist

git cl stage <changelist-name>
  • Stages all tracked files in the changelist.
  • Untracked files ([??]) are ignored unless added with git add.
  • By default, the changelist is kept unless --delete is used.

Example

git cl stage docs
git commit -m "Refactor docs"

Tip: Run git cl diff first if you want to review changes.

Unstage a changelist

git cl unstage <changelist-name>
  • Unstages files from the changelist (i.e. removes them from the index).
  • Only applies to staged files — unchanged or unstaged files are ignored.
  • Files remain in the changelist and your working directory.

Example

git cl unstage docs

This is useful when you've staged something too early and want to pull it back without losing the changelist group.

2.5 Commit a changelist

git cl commit <changelist-name> -m "Message"
git cl commit <changelist-name> -F commit.txt
  • Automatically stages and commits all tracked files in the changelist — no need to run git add or git cl stage first.
  • Untracked files (see status codes) are ignored unless you add them first with git add.
  • The changelist is deleted after committing, unless you use --keep.

Important: This command automatically stages tracked files before committing them, so you don't need to run git add or git cl stage first. However, untracked files ([??]) in the changelist are safely ignored and will remain untracked.

This allows you to commit grouped changes directly, without touching the Git staging area manually.

Example

git cl commit tests -m "Add test environment"
# or
git cl commit tests -F message.txt

If you want to reuse the changelist (e.g. for further edits), use:

git cl commit tests -m "Partial commit" --keep

2.6 Remove files from changelists

git cl remove <file1> <file2> ...
  • Removes the given files from any changelist.
  • Files remain unchanged in your working directory.

2.7 Delete changelists

git cl delete <changelist1> <changelist2> ...
  • Deletes one or more named changelists.
  • Files remain in the working directory and will appear under “No Changelist” next time you run git cl st.

You can also delete all changelists at once with:

git cl delete --all
  • This clears all changelists from your workspace, leaving files untouched.

Only changelist metadata is deleted — no file content or Git history is lost.

2.8 Checkout a Changelist

git cl checkout <changelist-name>
# or
git cl co <changelist-name>
  • Reverts all files in the changelist to their last committed state (HEAD).
  • Useful for discarding local changes by intent, not just by filename.
  • Prompts for confirmation before proceeding.
  • Shows a summary of reverted files.

2.9 Getting Help from the Command Line

You can explore all available commands directly from your terminal:

git cl help

To see help for a specific command — including its available options — add -h after the command name:

git cl commit -h
git cl branch -h

Note: git help cl does not work — because git-cl is an external subcommand and not part of Git's built-in help system. Always use git cl help instead.

↑ Back to top

3. Advanced Commands

These commands build on the Basic Commands - make sure you're familiar with adding files and viewing status first.

3.1 Stash and Unstash Changelists

Sometimes you’re in the middle of a changelist but need to switch branches or pause your work — without committing it.

Stash saves those changes aside, and unstash brings them back later.

Stash a changelist

git cl stash <changelist-name>

Tip: Run stash commands from your repository root to avoid path issues.

  • Saves all unstaged and untracked files from the changelist.
  • Staged files are left alone (so you can commit them separately).
  • The stash is named after your changelist and timestamped.

Unstash a changelist

git cl unstash <changelist-name>
  • Restores the previously stashed changes.
  • Warns if files conflict with current working directory.

Stash all changelists

git cl stash --all
  • Stashes all active changelists at once.

Tip: Your files aren’t deleted — they’re just “put in a box” until you bring them back with unstash.

Stashing and unstashing changelists: stash all, then selectively restore

3.2 Create a branch from a changelist

You can turn a changelist into its own branch in one step — great for separating work mid-feature or starting a dedicated branch for a new idea.

git cl branch <changelist-name> [<branch-name>] [--from <base-branch>]

What happens under the hood:

  1. Saves (stashes) all active changelists.
  2. Creates and checks out the new branch.
  3. Restores only the chosen changelist on that branch — the other changelists remain stashed and can be restored with git cl unstash later.

See Section 4.3 for a worked example.

Branching from a changelist using git cl branch

↑ Back to top

4. Example Workflows

Changelists adapt to your mental model: group parallel tasks by intent, move files through a personal review pipeline, or promote a changelist to its own branch when it outgrows the workspace.

4.1 Changelists as Named Staging Areas

Git gives you one staging area. Everything you git add lands there, waiting for a single commit. That works fine when your changes all belong together — but less well when you’re mid-feature and notice a typo, some whitespace to clean up, or a stray print statement.

Changelists are groups of files you plan to commit together. They’re not commits themselves, and nothing about them touches Git’s index or history until you decide to stage and commit. Unlike the staging area, you can have as many changelists as you need, side by side, all labelled by intent.

Let’s say you’re implementing a feature across src/core.py, src/helpers.py, and tests/test_core.py, and along the way you notice a typo in the README and some whitespace to clean up in a utility file. Group them:

git cl add feature src/core.py src/helpers.py tests/test_core.py
git cl add typo-fix README.md
git cl add whitespace src/utils.py

All three sets of edits live in your working directory at once — you can run tests, see the full picture, keep editing. Nothing is staged yet. At any point, git cl status shows you what’s grouped where:

$ git cl status
feature:
  [ M] src/core.py
  [ M] src/helpers.py
  [ M] tests/test_core.py
typo-fix:
  [ M] README.md
whitespace:
  [ M] src/utils.py

When you’re ready, stage and commit one changelist at a time:

git cl stage feature
git commit -m "Implement core feature"

git cl stage moves all three files of the feature changelist into Git’s staging area in one step; git commit then turns them into a single commit. You can also use git cl commit as a shortcut that stages and commits in one step:

git cl commit typo-fix -m "Fix typo in README"
git cl commit whitespace -m "Clean up whitespace in utils"

Three focused commits, each from its own changelist, without ever switching branches or shuffling the staging area manually.

This is where changelists shine over branches: when the work is small, related in context but unrelated in intent, and doesn’t warrant the overhead of a separate line of development. For larger or longer-running work, branches are still the right tool — and git cl branch helps when a changelist grows into something that deserves one.

4.2 Changelists as Review Stages

Changelists can also track where each file is in your review process. Instead of grouping by feature, you group by review stage — files move from one changelist to the next as they pass each check.

Start with all modified files in review_1:

git cl add review_1 src/core.py src/utils.py tests/test_core.py

Do whatever your first check is — linter, formatting, self-review. As each file passes, move it on:

git cl add review_2 src/core.py

The file is now in review_2 and automatically removed from review_1. Continue through as many stages as you need. git cl status shows the state of every file at a glance:

$ git cl status
review_1:
  [ M] src/utils.py
review_2:
  [ M] tests/test_core.py
review_3:
  [ M] src/core.py

When files reach the final stage, commit just that changelist:

git cl commit review_3 -m "Implement core feature"

What each stage means is up to you — numbered (review_1, review_2), named by what’s been checked (linted, tested, profiled), or by what’s been approved (self_review_ok, ai_review_ok, ready_to_commit). The pattern is the same: changelists as a lightweight state machine, with git cl status as the dashboard.

4.3 Late-Binding Branching with git cl branch

Sometimes you’re deep in the middle of a changelist — maybe tweaking numerical algorithms or refactoring simulation code — and you realise it would be cleaner to finish the work on a separate branch. git cl branch makes that move painless, without committing incomplete work.

Let’s say you’re improving the performance of a weather simulation in Fortran. You’ve grouped your edits into a changelist:

git cl add solver-opt src/solver.f90 src/utils_math.f90

You’re not ready to commit, but you decide this optimisation deserves its own branch. Use:

git cl branch solver-opt

Your workspace will now look like you never left, but you’re on a dedicated branch:

$ git cl st
solver-opt:
  [ M] src/solver.f90
  [ M] src/utils_math.f90

This preserves your work-in-progress and the changelist grouping — so you can pick up right where you left off. Unlike git stash, git cl branch is changelist-aware. You don’t lose file intent or grouping across branches.

If you prefer a custom branch name or base branch, you can specify them:

git cl branch solver-opt feature/fortran-speedup --from main

This workflow is especially handy when experimental work — like optimising a finite-difference solver or changing file I/O routines — starts to grow beyond the original scope of your current branch. It keeps unrelated changes isolated and easy to review later.

For more step-by-step examples, each test script in the test suite can be exported as a standalone shell walkthrough you can follow in your terminal.

↑ Back to top

5. FAQ & Common Pitfalls

How do I move a file from one changelist to another?

Just run git cl add with the new changelist name. You don't need to remove it from the old one manually.

git cl add new-list path/to/file

This automatically reassigns the file to new-list.

Why are changelists deleted after I commit?

git cl commit always deletes the changelist after committing, unless you explicitly use the --keep flag:

git cl commit my-list -m "Implement my feature" --keep

git cl stage, git cl unstage, and git cl checkout behave differently — they keep the changelist by default. If you want to remove it in those cases, pass --delete:

git cl stage my-list
git cl unstage my-list 
git cl checkout my-list --delete

This way, you can choose whether the changelist sticks around after staging or reverting.

Why aren’t untracked files included when I stage or commit a changelist?

git cl only stages or commits files already tracked by Git.

If a file is untracked ([??]), it will show up in git cl st, but won’t be included when staging or committing.

To include it:

git add my-file.txt
git cl stage my-changelist

Are changelists shared between team members?

No. Changelists are stored locally in .git/cl.json and are not version-controlled or shared via Git. They’re like personal sticky notes on your working directory and visible only to you.

Do changelists persist when switching branches?

Yes — changelists are local and independent of branches. This means:

  • You can keep working on the same files across branches
  • But beware: a changelist may reference files that don't exist in the new branch

If things get messy, delete a stale changelist:

git cl delete old-list

How do I pause work in progress without committing?

Use git cl stash <name>, then switch branches and git cl unstash when ready.

Why don’t I see all files in git cl status?

By default, git cl status shows the status codes you normally need to act on — common working-directory states plus merge conflicts — while hiding rare codes like [T ] (type change) to keep output clean. To include every Git status code without filtering, use the --all flag:

git cl status --all

Can I reuse a changelist name later?

Yes. If the changelist was deleted after a stage or commit, you can create a new one with the same name — it's just a label, not a persistent identity.

Can git-cl split changes within a single file across multiple changelists?

No. A file can only belong to one changelist at a time. For patch-level splitting within a file, use git add -p alongside git-cl — the two tools complement each other.

How do I add currently staged files to a changelist?

git-cl doesn't have a dedicated command for this, but you can use git's own tooling:

git cl add my-changelist $(git diff --staged --name-only)

This is useful when you've already staged a meaningful set of changes and want to capture that grouping as a changelist. The files remain staged in git's index — if you also want to unstage them, follow up with git cl unstage my-changelist.

Do changelists work with Git worktrees?

Yes. Each worktree has its own independent set of changelists — changes made in one worktree are not visible in another. This means you can use git-cl freely in each worktree without them interfering with each other.

↑ Back to top

6. Command Summary

6.1 Command Summary Table

Note: Many commands have short aliases (shown in "Alias" column) for faster typing.

Task Command Alias
Add files to a changelist git cl add <name> <files...> git cl a
View grouped status git cl status [--all] [--no-untracked] [--no-color] git cl st
Show diff for changelist(s) git cl diff <name1> [<name2> ...] [--staged]
Stage a changelist git cl stage <name> [--delete]
Unstage a changelist git cl unstage <name> [--delete]
Commit with inline message git cl commit <name> -m "Message" [--keep] git cl ci
Commit using message from file git cl commit <name> -F message.txt [--keep]
Revert changelist to HEAD git cl checkout <name> git cl co
Remove files from changelists git cl remove <file1> <file2> ... git cl rm
Delete changelists git cl delete <name1> <name2> ... git cl del
Stash a changelist git cl stash <name>
Unstash a changelist git cl unstash <name> [--force]
Create branch from changelist git cl branch <name> [<branch>] [--from <base>] git cl br
Show help git cl help

6.2 Git Status Code Reference

git cl status uses standard Git status codes — shown as two-character prefixes (e.g. [M ], [??]).

Common Status Codes

Code Status Description
[??] Untracked New file, not yet tracked by Git
[M ] Staged Change staged and ready to commit
[ M] Unstaged Change made but not yet staged
[MM] Mixed Staged change with additional unstaged modification
[A ] Added New file added and staged
[AM] Added + Modified File added and then further modified
[D ] Deletion (staged) File deletion staged
[ D] Deletion (unstaged) File deleted but not yet staged
[R ] Renamed File renamed and staged
[RM] Renamed + Modified Renamed and then modified before staging
[UU] Unmerged (conflict) Both sides modified; resolve before staging

To show all codes, including rare ones, use:

git cl status --all

Additional Codes

Code Description
[T ] Type change

Colour Key

Colour Meaning
Green Staged changes ([M ], [A ])
Red Unstaged changes ([ M], [ D])
Red Needs attention: unstaged changes ([ M], [ D]) or merge conflicts ([UU], [AA], …)
Magenta Both staged and unstaged ([MM], [AM])
Blue Untracked ([??])

You can disable color output with:

git cl status --no-color

or set NO_COLOR=1 in your environment.

See also: Git status documentation

↑ Back to top