Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

git-gamble

git-gamble
Craft Your Code,
Step by Step

Do all tests pass ?

def hello():
    return 'Hello word'

def test_say_hello_world():
    assert hello() == 'Hello world'

Are you ready to gamble ?

Pass 🟢

You did :

git-gamble --pass

Your gamble was good :

Committed

Fail šŸ”“

You did :

git-gamble --fail

But there was a typo :

 def hello():
-   return 'Hello word'
+   return 'Hello world'

Your gamble was wrong :

Reverted

git-gamble

Watch the demo

Go to the demo

Community

Loved by community

Starred on GitLab

Built by community

GitLab Contributors

Ready to transform your development process ?

Get started with git-gamble !

Theory

Alternatively : you can watch the slides about the theory

TDD

TDD (Test Driven Development) is cool!

It makes sure we develop the right thing, step by step

TDD’s steps

flowchart TB
    start([Start])
    ==> red([Write only 1 failing test])
    ==> green([Make all tests pass])
    ==> refactor([Refactor])
    ==> finish([Finish])

    refactor -.->|Incomplete ?| red

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase
  1. Write only 1 failing test
  2. Make all tests pass
  3. Refactor
  4. Repeat while it’s incomplete
  5. Until finish

Red step

flowchart TB
    subgraph red[Red]
        direction LR

        fix_layout(( )) ==> write_a_test

        write_a_test[Write only 1 failing test]
        ==> run_tests{{Run tests}}

        run_tests
        -.->|Pass\nWrite another test| write_a_test
    end

    start([Start]) === fix_layout

    run_tests ==>|Fail| green([Green])

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase

    classDef fix_layout stroke:white,fill:transparent;
    class fix_layout fix_layout
  1. Write only 1 failing test
  2. Run tests
  3. If the tests pass then write another test
  4. Else if the tests fail then go to the green step

Green step

flowchart TB
    subgraph green[Green]
        direction LR

        fix_layout(( )) ==> write_the_minimum_code

        write_the_minimum_code[Write the minimum code\nto make all tests pass]
        ==> run_tests{{Run tests}}

        run_tests
        -.->|Fail\nTry something else| write_the_minimum_code
    end

    red([Red]) === fix_layout

    run_tests ==>|Pass| refactor([Refactor])

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase

    classDef fix_layout stroke:white,fill:transparent;
    class fix_layout fix_layout
  1. Write the minimum code to make all tests pass
  2. Run tests
  3. If the tests fail then try something else
  4. Else if the tests pass then go to the refactor step

Refactor step

flowchart TB
    subgraph refactor[Refactor]
        direction LR

        rewrite_code[Rewrite code\nwithout changing the behaviour]
        ==> run_tests{{Run tests}}
        -.->|Pass\nAnother things to refactor ?| rewrite_code

        run_tests
        -->|Fail\nChange something else| rewrite_code
    end

    green([Green]) ==> rewrite_code

    run_tests ==>|Pass| finish([Finish])
    run_tests -.->|Pass\nAnother feature to add ?| red([Red])

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase

    classDef fix_layout stroke:white,fill:transparent;
    class fix_layout fix_layout
  1. Rewrite code without changing the behaviour
  2. Run tests
  3. If the tests fail then change something else and repeat the refactor step
  4. Else if the tests pass and
    1. there are another things to refactor then repeat the refactor step
    2. there is another feature to add then go to the red step
    3. there nothing else to do then it’s finished

TCR

TCR (test && commit || revert) is cool!

It encourages doing baby steps, reducing the waste when we are wrong

TCR’s single step

flowchart TB
    gamble{{Gambling on test results}}

    gamble ==>|Pass| commit(Commit)

    gamble -->|Fail| revert(Revert)
  1. Gambling on test results
  2. If the tests pass then commit
  3. If the tests fail then revert
flowchart TB
    start([Start])
    ==> green([Change some code])
    ==> finish([Finish])

    green -.->|Incomplete ?| green

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase
  1. Change some code
  2. Repeat while it’s incomplete
  3. Until finish

But it doesn’t allow us to see the tests failing

So:

  • Maybe we test nothing (assert forgotten)

    def test_should_be_Buzz_given_5():
        input = 5
        actual = fizz_buzz(input)
        # Oops! Assert has been forgotten
    
  • Maybe we are testing something that is not the thing we should be testing

    it("should be Fizz given 3", () => {
      const input = 3;
      const actual = fizzBuzz(input);
      expect(input).toBe("Fizz");
      // Oops! Asserts on the input value instead of the actual value
    });
    

Detailed view

flowchart TB
    subgraph green[Green]
        direction TB

        start([Start])
        ==> change_code[Change some code]
        ==> run_tests{{"Run tests\n<code>test && commit || revert</code>"}}
        ==>|Pass| commit(Commit)
        ==> finish([Finish])

        commit
        -->|Another things to change ?| change_code

        run_tests
        -.->|Fail| revert(Revert)
        -.->|Try something else| change_code
    end

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase
  1. Change some code
  2. Run tests
    test && commit || revert
  3. If the tests fail then
    1. Revert
    2. Try something else
    3. Repeat
  4. If the tests pass then
    1. Commit
    2. If there are another things to change then repeat
    3. If there is nothing else to do then it’s finished

TCRDD

venn-beta
    set TCR
    set TDD
    union TCR,TDD["TCRDD"]

TCRDD = TCR + TDD

TCRDD blends the constraints of the two methods to benefit from their advantages

Therefore, TCRDD makes sure we develop the right thing, step by step, and we are encouraged to do so by baby steps, reducing the waste when we are wrong

TCRDD’s phases

flowchart TB
    red{{Write only 1 failing test}}
    green{{Make all tests pass}}
    refactor{{Refactor}}

    red ~~~ red_commit
    green ~~~ green_commit
    refactor ~~~ refactor_commit

    red ==>|Fail| red_commit(Commit) ==> green
    green ==>|Pass| green_commit(Commit) ==> refactor
    refactor ==>|Pass| refactor_commit(Commit)

    red -->|Pass| red_revert(Revert) --> red
    green -->|Fail| green_revert(Revert) --> green
    refactor -->|Fail| refactor_revert(Revert) --> refactor

    red_revert ~~~ green
    green_revert ~~~ refactor

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase
  1. Red phase
    1. Write only 1 failing test
    2. If the tests pass then
      1. Revert
      2. Retry the red phase
    3. Else if the tests fail then
      1. Commit
      2. Go to the green phase
  2. Green phase
    1. Make all tests pass
    2. If the tests fail then
      1. Revert
      2. Retry the green phase
    3. Else if the tests pass then
      1. Commit
      2. Go to the refactor phase
  3. Refactor phase
    1. Refactor
    2. If the tests fail then
      1. Revert
      2. Retry the refactor phase
    3. Else if the tests pass then
      1. Commit

Red phase

flowchart TB
    subgraph red[Red]
        direction LR

        fix_layout(( )) ==> write_a_test

        write_a_test[Write only 1 test]
        ==> gamble[/Gamble that the test fail\n<code>git gamble --red</code>/]
        ==> run_tests{{Actually run tests}}
        ==>|Fail| commit(Commit)

        run_tests
        -->|Pass| revert(Revert)
        -->|Write another test| write_a_test
    end

    start([Start]) ==> fix_layout

    commit ==> green([Green])

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase

    classDef fix_layout stroke:white,fill:transparent;
    class fix_layout fix_layout
  1. Write only 1 test
  2. Gamble that the test fail
    git gamble --red
  3. Actually run tests
  4. If the tests pass then
    1. Revert
    2. Write another test
  5. Else if the tests fail then
    1. Commit
    2. Go to the green phase

Green phase

flowchart TB
    subgraph green[Green]
        direction LR

        fix_layout(( )) ==> write_the_minimum_code

        write_the_minimum_code[Write the minimum code]
        ==> gamble[/Gamble that the tests pass\n<code>git gamble --green</code>/]
        ==> run_tests{{Actually run tests}}
        ==>|Pass| commit(Commit)

        run_tests
        -->|Fail| revert(Revert)
        -->|Try something else| write_the_minimum_code
    end

    red([Red]) ==> fix_layout

    commit ==> refactor([Refactor])

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase

    classDef fix_layout stroke:white,fill:transparent;
    class fix_layout fix_layout
  1. Write the minimum code
  2. Gamble that the tests pass
    git gamble --green
  3. Actually run tests
  4. If the tests fail then
    1. Revert
    2. Try something else
  5. Else if the tests pass then
    1. Commit
    2. Go to the refactor phase

Refactor phase

flowchart TB
    subgraph refactor[Refactor]
        direction LR

        rewrite_code[Rewrite code\nwithout changing the behaviour]
        ==> gamble[/Gamble that the tests pass\n<code>git gamble --refactor</code>/]
        ==> run_tests{{Actually run tests}}
        ==>|Pass| commit(Commit)
        -.->|Another things to refactor ?| rewrite_code

        run_tests
        -->|Fail| revert(Revert)
        -->|Change something else| rewrite_code
    end

    green([Green]) ==> rewrite_code

    commit ==> finish([Finish])
    commit -.->|Another feature to add ?| red([Red])

    classDef red_phase font-weight:bold,color:black,fill:coral;
    class red red_phase

    classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
    class green green_phase

    classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
    class refactor refactor_phase

    classDef fix_layout stroke:white,fill:transparent;
    class fix_layout fix_layout
  1. Rewrite code without changing the behaviour
  2. Gamble that the tests pass
    git gamble --refactor
  3. Actually run tests
  4. If the tests fail then
    1. Revert
    2. Change something else
  5. Else if the tests pass then
    1. Commit
    2. If there are another things to refactor then go to the refactor phase
    3. Else if there is another feature to add then go to the red phase
    4. Else if there is nothing else to do then it’s finished

git-gamble is a tool that helps to use the TCRDD method

Slides

Some pages of this documentation can be watched as slides presentation

Why ?

You can watch the slides about why baby steps are important

What ?

You can watch the slides about what is the theory

How ?

You can watch the slides to learn how to work with it

History of TCRDD

timeline
    title History of TCRDD
    2018
        : TCR                  `test && commit || revert`       Article                  by Kent Beck 2018-09-28
        : TCRDD          _TDD is dead, long live TCR?_ Article                  by Xavier Detant 2018-12-03
    2019
        : Xavier's TCRDD First commit 2019-08-26
        : git-gamble        First commit 2019-12-07
    2021 : Murex's TCR  First commit 2021-06-16
    2025 : git-bet's TCRDD First commit 2025-10-26

Similar projects

  • tcrdd by Xavier Detant
    • He introduced me to :
  • TCR by Murex
  • git-bet by Youssef Medaghri-Alaoui

Reinvent the wheel

Why reinvent the wheel?

The script of Xavier Detant already works well

Because i would like to learn Rust ĀÆ\_(惄)_/ĀÆ

Installation

The package is available on these repositories, and can be installed as usual if you use one of them

There are several methods to install depending on your operating system:

Installation methodLinuxmacOSWindows
NixYesYesYes
DebianYesNoNo
HomebrewYesYesNo
ChocolateyNoNoYes
MiseYesYesYes
Scoop (community)NoNoYes
CargoYesYesYes
Download the binaryYesNoYes

Improving installation

Installation is currently not really convenient, so contributions are welcome

Feel free to add package to your favourite package repository

Install using Nix

Available on nixpkgs

Packaged for Nixpkgs

Nix is a package manager available for Linux, macOS and Windows (through WSL2)

Requirements

  1. Install Nix

  2. Check the installation with this command :

    nix --version
    

    If it has been well settled, it should output something like this :

    nix (Nix) 2.22.3
    

    Else if it has been badly settled, it should output something like this :

    nix: command not found
    

Install from Nixpkgs

The package is available on nixpkgs

nix-shell --packages git-gamble

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Install from source

Note: the latest version, not yet released, is also available

Use without installing :

export NIX_CONFIG="extra-experimental-features = flakes nix-command"
nix run gitlab:pinage404/git-gamble -- --help

To install it at project level with flake.nix, see example

export NIX_CONFIG="extra-experimental-features = flakes nix-command"
nix flake init --template gitlab:pinage404/git-gamble

DirEnv

To automate the setup of the environment it’s recommended to install DirEnv

Then run this command :

direnv allow

Cachix

To avoid rebuilding from scratch, Cachix can be used

cachix use git-gamble

Update

export NIX_CONFIG="extra-experimental-features = flakes nix-command"
nix flake update

Install on Debian

Debian available on GitLab

It should work on any Debian based distributions : Linux Mint, Ubuntu…

Requirements

git-gamble doesn’t repackage git, it uses the one installed on your system

  1. Install git manually

  2. Make sure it is available in your $PATH, you can check it with this command :

    git --version
    

    If it has been well settled, it should output something like this :

    git version 2.54.0
    

    Else if it has been badly settled, it should output something like this :

    git: command not found
    

Using with old git version

Note : if you are using a very old version of git (below 2.36)

With old git version, git-gamble will fail with this error

git: 'hook' is not a git command. See 'git --help'.

The most similar commands are
        fork
        lock
        root
Failed to execute `git hook run --ignore-missing pre-gamble -- pass` in . returns code 1
The pre-gamble hook returns code 1, but it should be 0
You can test the hook by executing : git hook run --ignore-missing pre-gamble -- pass
No tests have been run, and nothing has been committed or reverted

git 2.36 was released the 2022-04-18, and is now available in most package managers

If you can, try to upgrade git using your package manager

Else, to use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Install

  1. Go to the package registry page

  2. Go to the latest version of git-gamble-debian

  3. Download the latest version git-gamble_2.14.6_amd64.deb

  4. Install package

    Execute this command as root :

    dpkg --install git-gamble*.deb
    

Note: the latest version, not yet released, is also available

This is not really convenient, but a better way could come when GitLab Debian Package Manager MVC will be available

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Update

There is no update mechanism

Uninstall

Execute this command as root :

dpkg --remove git-gamble

Improving installation

Installation is currently not really convenient, so contributions are welcome

Feel free to add package to your favourite package repository

Install using Homebrew on macOS

Homebrew available on GitLab

Requirements

  1. Install Homebrew

  2. Check the installation with this command :

    brew --version
    

    If it has been well settled, it should output something like this :

    Homebrew 4.3.14
    Homebrew/homebrew-core (git revision 7a574d89134; last commit 2024-08-07)
    

    Else if it has been badly settled, it should output something like this :

    brew: command not found
    

Using with old git version

Note : if you are using a very old version of git (below 2.36)

With old git version, git-gamble will fail with this error

git: 'hook' is not a git command. See 'git --help'.

The most similar commands are
        fork
        lock
        root
Failed to execute `git hook run --ignore-missing pre-gamble -- pass` in . returns code 1
The pre-gamble hook returns code 1, but it should be 0
You can test the hook by executing : git hook run --ignore-missing pre-gamble -- pass
No tests have been run, and nothing has been committed or reverted

git 2.36 was released the 2022-04-18, and is now available in most package managers

If you can, try to upgrade git using your package manager

Else, to use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Install

Run these commands :

brew tap pinage404/git-gamble https://gitlab.com/pinage404/git-gamble.git
brew trust pinage404/git-gamble
brew install --HEAD git-gamble

Or add this to the Brewfile :

tap "pinage404/git-gamble", "https://gitlab.com/pinage404/git-gamble.git", trusted: true
brew "pinage404/git-gamble/git-gamble", args: ["HEAD"]

Then run this command :

brew bundle install

Note : it will be a bit long because it will compile the project and download every dependency needed by the compilation ; Check the section to improve installation

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Upgrade

git-gamble has not yet been packaged by Homebrew

To upgrade git-gamble, run this command :

brew reinstall git-gamble

Uninstall

brew uninstall git-gamble
brew untap pinage404/git-gamble

Improving installation

Installation is currently not really convenient, so contributions are welcome

Feel free to add package to your favourite package repository

Homebrew requires repositories to have at least 75 stars and 30 forks

So, please add a star on GitLab

and create a fork for any contribution

Install using Chocolatey on Windows

Chocolatey available on GitLab

Requirements

Chocolatey

  1. Install Chocolatey

  2. Check the installation with this command :

    choco --version
    

    If it has been well settled, it should output something like this :

    2.3.0
    

    Else if it has been badly settled, it should output something like this :

    choco: command not found
    

Git

git-gamble doesn’t repackage git, it uses the one installed on your system

  1. Install git manually

  2. Make sure it is available in your $PATH, you can check it with this command :

    git --version
    

    If it has been well settled, it should output something like this :

    git version 2.54.0
    

    Else if it has been badly settled, it should output something like this :

    git: command not found
    

Using with old git version

Note : if you are using a very old version of git (below 2.36)

With old git version, git-gamble will fail with this error

git: 'hook' is not a git command. See 'git --help'.

The most similar commands are
        fork
        lock
        root
Failed to execute `git hook run --ignore-missing pre-gamble -- pass` in . returns code 1
The pre-gamble hook returns code 1, but it should be 0
You can test the hook by executing : git hook run --ignore-missing pre-gamble -- pass
No tests have been run, and nothing has been committed or reverted

git 2.36 was released the 2022-04-18, and is now available in most package managers

If you can, try to upgrade git using your package manager

Else, to use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Install

Note: If you installed Chocolatey from an administrative shell, make sure to launch all choco commands from an administrative shell as well.

Check the version of Chocolatey installed using this command :

choco --version
  • Chocolatey v2.x

    choco source add --name=git-gamble --source="https://gitlab.com/api/v4/projects/15761766/packages/nuget/index.json"
    choco install git-gamble.portable
    
  • Chocolatey v1.x

    Use the Nuget v2 source :

    choco source add --name=git-gamble --source="https://gitlab.com/api/v4/projects/15761766/packages/nuget/v2"
    choco install git-gamble.portable --version=2.14.6
    

    The Gitlab registry doesn’t seem to fully support package discovery on the v2 source, so the version needs to be specified explicitly. This issue might address this.

Note: the latest version, not yet released, is also available

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Update

choco upgrade git-gamble.portable

With Chocolatey v1.x and/or the Nuget v2 source :

choco upgrade git-gamble.portable --version=2.14.6

Uninstall

choco uninstall git-gamble.portable

You might also want to remove the source :

choco source remove --name=git-gamble

Improving installation

Installation is currently not really convenient, so contributions are welcome

Feel free to add package to your favourite package repository

Install using Mise

Requirements

Mise

  1. Install Mise

  2. Check the installation with this command :

    mise --version
    

    If it has been well settled, it should output something like this :

                  _                                        __
       ____ ___  (_)_______        ___  ____        ____  / /___ _________
      / __ `__ \/ / ___/ _ \______/ _ \/ __ \______/ __ \/ / __ `/ ___/ _ \
     / / / / / / (__  )  __/_____/  __/ / / /_____/ /_/ / / /_/ / /__/  __/
    /_/ /_/ /_/_/____/\___/      \___/_/ /_/     / .___/_/\__,_/\___/\___/
                                                /_/                 by @jdx
    2026.5.12 linux-x64 (1980-01-01)
    mise WARN  mise version 2026.6.9 available
    

    Else if it has been badly settled, it should output something like this :

    mise: command not found
    

Git

git-gamble doesn’t repackage git, it uses the one installed on your system

  1. Install git manually

  2. Make sure it is available in your $PATH, you can check it with this command :

    git --version
    

    If it has been well settled, it should output something like this :

    git version 2.54.0
    

    Else if it has been badly settled, it should output something like this :

    git: command not found
    

Using with old git version

Note : if you are using a very old version of git (below 2.36)

With old git version, git-gamble will fail with this error

git: 'hook' is not a git command. See 'git --help'.

The most similar commands are
        fork
        lock
        root
Failed to execute `git hook run --ignore-missing pre-gamble -- pass` in . returns code 1
The pre-gamble hook returns code 1, but it should be 0
You can test the hook by executing : git hook run --ignore-missing pre-gamble -- pass
No tests have been run, and nothing has been committed or reverted

git 2.36 was released the 2022-04-18, and is now available in most package managers

If you can, try to upgrade git using your package manager

Else, to use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Install

Run this command :

mise use gitlab:pinage404/git-gamble

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Update

mise upgrade gitlab:pinage404/git-gamble --bump

Uninstall

mise unuse gitlab:pinage404/git-gamble

Improving installation

Installation is currently not really convenient, so contributions are welcome

Feel free to add package to your favourite package repository

Install using Scoop on Windows

Some community projects seem to make available git-gamble to the Scoop ecosystem

āš ļø They are not officially endorsed by git-gamble, there is no warranty, use at your own risk, use them if you trust there respective authors āš ļø

Requirements

Scoop

Install Scoop

Git

git-gamble doesn’t repackage git, it uses the one installed on your system

  1. Install git manually

  2. Make sure it is available in your $PATH, you can check it with this command :

    git --version
    

    If it has been well settled, it should output something like this :

    git version 2.54.0
    

    Else if it has been badly settled, it should output something like this :

    git: command not found
    

Using with old git version

Note : if you are using a very old version of git (below 2.36)

With old git version, git-gamble will fail with this error

git: 'hook' is not a git command. See 'git --help'.

The most similar commands are
        fork
        lock
        root
Failed to execute `git hook run --ignore-missing pre-gamble -- pass` in . returns code 1
The pre-gamble hook returns code 1, but it should be 0
You can test the hook by executing : git hook run --ignore-missing pre-gamble -- pass
No tests have been run, and nothing has been committed or reverted

git 2.36 was released the 2022-04-18, and is now available in most package managers

If you can, try to upgrade git using your package manager

Else, to use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Install

Add one of this bucket :

Only add a bucket that you trust āš ļø

Then run these commands :

scoop update
scoop install git-gamble

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Upgrade

It’s probably possible ; Check the section to improve installation

Uninstall

It’s probably possible ; Check the section to improve installation

Improving installation

Installation is currently not really convenient, so contributions are welcome

Feel free to add package to your favourite package repository

If you use Scoop, your help is wanted to improve this page

Install using Cargo

Crate available on Crates.io

Requirements

Cargo

  1. Install Cargo

  2. Check the installation with this command :

    cargo --version
    

    If it has been well settled, it should output something like this :

    cargo 1.80.0 (376290515 2024-07-16)
    

    Else if it has been badly settled, it should output something like this :

    cargo: command not found
    

Git

git-gamble doesn’t repackage git, it uses the one installed on your system

  1. Install git manually

  2. Make sure it is available in your $PATH, you can check it with this command :

    git --version
    

    If it has been well settled, it should output something like this :

    git version 2.54.0
    

    Else if it has been badly settled, it should output something like this :

    git: command not found
    

Using with old git version

Note : if you are using a very old version of git (below 2.36)

With old git version, git-gamble will fail with this error

git: 'hook' is not a git command. See 'git --help'.

The most similar commands are
        fork
        lock
        root
Failed to execute `git hook run --ignore-missing pre-gamble -- pass` in . returns code 1
The pre-gamble hook returns code 1, but it should be 0
You can test the hook by executing : git hook run --ignore-missing pre-gamble -- pass
No tests have been run, and nothing has been committed or reverted

git 2.36 was released the 2022-04-18, and is now available in most package managers

If you can, try to upgrade git using your package manager

Else, to use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Install

  1. Install binary

    • Use cargo-binstall to avoid compilation

      • If binaries are available for your platform
        • It will download binaries
      • Else
        • It will fall back to compilation from source

      Run the following command :

      cargo binstall git-gamble
      
    • If you want to compile from source

      Run this command :

      cargo install git-gamble
      
  2. Add ~/.cargo/bin to your $PATH

    • Fish :

      set --export --append PATH ~/.cargo/bin
      
    • Bash / ZSH :

      export PATH=$PATH:~/.cargo/bin
      

Remove optional features

Cargo have a mechanism to enable or disable features at compile time

By default, git-gamble is compiled with :

To remove these features, install using the following command :

cargo install git-gamble --no-default-features

To enable only one of these features :

  • with_subcommand_hook (requires and includes with_custom_hooks)

    cargo install git-gamble --no-default-features --features "with_subcommand_hook"
    
  • with_custom_hooks

    cargo install git-gamble --no-default-features --features "with_custom_hooks"
    
  • with_subcommand_generate_shell_completions

    cargo install git-gamble --no-default-features --features "with_subcommand_generate_shell_completions"
    

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Update

cargo update --package git-gamble

Uninstall

cargo uninstall git-gamble

Download the binary

Pre-built binaries are available on the releases on GitLab

Requirements

git-gamble doesn’t repackage git, it uses the one installed on your system

  1. Install git manually

  2. Make sure it is available in your $PATH, you can check it with this command :

    git --version
    

    If it has been well settled, it should output something like this :

    git version 2.54.0
    

    Else if it has been badly settled, it should output something like this :

    git: command not found
    

Using with old git version

Note : if you are using a very old version of git (below 2.36)

With old git version, git-gamble will fail with this error

git: 'hook' is not a git command. See 'git --help'.

The most similar commands are
        fork
        lock
        root
Failed to execute `git hook run --ignore-missing pre-gamble -- pass` in . returns code 1
The pre-gamble hook returns code 1, but it should be 0
You can test the hook by executing : git hook run --ignore-missing pre-gamble -- pass
No tests have been run, and nothing has been committed or reverted

git 2.36 was released the 2022-04-18, and is now available in most package managers

If you can, try to upgrade git using your package manager

Else, to use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Install

Only for Linux and Windows x86_64 :

  1. Download the binary on the release page
  2. Rename it to git-gamble
  3. Put it in your $PATH

Note : the latest version, not yet released, is also available :

Check the installation

Check if all have been well settled :

git gamble --version

If it has been well settled , it should output this :

git-gamble 2.14.6

Else if it has been badly settled , it should output this :

git : 'gamble' is not a git command. See 'git --help'.

Update

There is no update mechanism

Uninstall

Just remove the binary from your $PATH

Usage

git-gamble works with all languages and tools and editors

How to use ?

To see all available flags and options

git gamble --help

Dash - between git and gamble may be only needed for --help

git-gamble --help

There are two ways to run your tests using git-gamble :

In both cases, the test command must exit with a 0 status when there are 0 failed tests, anything else is considered as a failure

Environment variable

Setting an environment variable and run only the git gamble command

  1. Start by setting an environment variable with the test command :

    The example below is for running your tests for a Rust project

    export GAMBLE_TEST_COMMAND="cargo test"
    

    Use the appropriate command for your shell :

  2. Write a failing test in your codebase, then :

    git gamble --red
    # or
    git gamble --fail
    
  3. Write the minimum code to make tests pass, then :

    git gamble --green
    # or
    git gamble --pass
    
  4. Refactor your code, then :

    git gamble --refactor
    # or
    git gamble --pass
    

DirEnv

To avoid re-exporting manually the variable at each new terminal, it’s recommended to install DirEnv

Then add in a .envrc file

export GAMBLE_TEST_COMMAND="cargo test"

Then run this command

direnv allow

Repeating the test command

Typing the git gamble command with your test command repetitively

  1. Write a failing test in your codebase, then :

    git gamble --red -- $YOUR_TEST_COMMAND
    

    The example below is for running your tests for a Node.js project that use PNPM

    git gamble --red -- pnpm test
    
  2. Write the minimum code to make tests pass, then :

    git gamble --green -- $YOUR_TEST_COMMAND
    
  3. Refactor your code, then :

    git gamble --refactor -- $YOUR_TEST_COMMAND
    

Demo

For more detailed example, view the demo

Demo

git-gamble’s demo to develop using TCRDD by doing baby steps

On a simple program

git-gamble works with all languages and tools and editor ; this example uses python with pytest and nano

export GAMBLE_TEST_COMMAND='pytest --quiet'

Note : for a simpler demo, test code and production code are in the same file

Alternatively : you can also watch the slides about the demo


First TDD loop āž°

Write a program that says Hello world

First, šŸ”“ Red phase : write a test that fails for the good reason

Asciinema
Alternative text
nano test_hello.py
def hello():
    pass

def test_say_hello_world():
    assert hello() == 'Hello world'

Then, gamble that the tests fail

git gamble --red

Committed

Second, 🟢 Green phase : write the minimum code to pass the tests

Let’s fake it

Asciinema
Alternative text
nano test_hello.py
def hello():
    return 'Hello word'

Then, gamble that the tests pass

git gamble --green

Reverted

Oh ! No !

I made a typo

 def hello():
-    return 'Hello word'
+    return 'Hello world'

Try again

Let’s fake it without typo

Asciinema
Alternative text
nano test_hello.py
def hello():
    return 'Hello world'

Gamble again that the tests pass

git gamble --green

Committed

Yeah !

Third, šŸ”€ Refactor phase : Nothing to refactor yet


Then šŸ” Repeat āž°

Second TDD loop āžæ

Write a program that says Hello to the given name when a name is given

šŸ”“ Red phase

Asciinema
Alternative text
nano test_hello.py
def test_say_hello_name_given_a_name():
    assert hello('Jon') == 'Hello Jon'
git gamble --red

Committed

🟢 Green phase

Add a simple condition to handle both cases

Asciinema
Alternative text
nano test_hello.py
def hello(arg=None):
    if arg:
        return f'Hello {arg}'
    return 'Hello world'
git gamble --green

Committed

šŸ”€ Refactor loop āž°

šŸ”€ Refactor phase

It can be simplified

Asciinema
Alternative text
nano test_hello.py
def hello(arg='world'):
    return f'Hello {arg}'
git gamble --refactor

Committed

Still šŸ”€ Refactor phase : i have something else to refactor āžæ

Better naming

Asciinema
Alternative text
nano test_hello.py
def hello(name='world'):
    return f'Hello {name}'
git gamble --refactor

Committed


And so on… āžæ

šŸ” Repeat until you have tested all the rules, are satisfied and enough confident

Command Line Interface

git gamble --help
Blend TDD (Test Driven Development) + TCR (`test && commit || revert`) to make sure to develop
the right thing 😌, baby step by baby step šŸ‘¶šŸ¦¶

Usage: git-gamble [OPTIONS] <--pass|--fail> -- <TEST_COMMAND>...
       git-gamble [OPTIONS] <COMMAND>

Commands:
  generate-shell-completions  
  hook                        Help to use samples hooks
  help                        Print this message or the help of the given subcommand(s)

Arguments:
  <TEST_COMMAND>...  The command to execute to know the result [env: GAMBLE_TEST_COMMAND=]

Options:
  -g, --pass                               Gamble that tests should pass [aliases: --green, --refactor]
  -r, --fail                               Gamble that tests should fail [aliases: --red]
  -n, --dry-run                            Do not make any changes and run test command
      --no-verify                          Do not run git hooks
  -C, --repository-path <REPOSITORY_PATH>  Repository path [default: .]
  -m, --message <MESSAGE>                  Commit's message [default: ""]
  -e, --edit                               Open editor to edit commit's message
      --fixup <FIXUP>                      Fixes up commit
      --squash <SQUASH>                    Construct a commit message for use with `rebase --autosquash`
  -h, --help                               Print help
  -V, --version                            Print version

Any contributions (feedback, bug report, merge request ...) are welcome
https://git-gamble.is-cool.dev/contributing/index.html

Shell completions

To manually generate shell completions, use this command :

git gamble generate-shell-completions --help
Usage: git-gamble generate-shell-completions [SHELL]

Arguments:
  [SHELL]
          Put generated file here :
          * Bash : add it to your bash profile in ~/.bashrc
          * Fish : see https://fishshell.com/docs/current/completions.html#where-to-put-completions
          * Powershell : see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles
          * Others shells : don't know, MR are welcome
          
          [possible values: bash, elvish, fish, powershell, zsh]

Options:
  -h, --help
          Print help (see a summary with '-h')

Hooks

To use examples hooks, use this command :

git gamble hook --help
Help to use samples hooks

Usage: git-gamble hook <COMMAND>

Commands:
  enable   Enable a sample hook
  disable  Disable a sample hook
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help

Require git 2.54.0 or higher to actually execute hooks

With git 2.53.0 or below, you have to manually setup hooks
https://git-gamble.is-cool.dev/usage/hooks/examples.html#using-with-git-2530-or-below

The available hooks are just examples
Create any others behaviors using hooks using the standard git hook API
https://git-scm.com/docs/git-hook

To have more informations, read the documentation about hooks
https://git-gamble.is-cool.dev/usage/hooks.html

Usage examples

There are several examples with several languages in the nix-sandboxes repository

In each language folder, there is a .envrc file, which contains the variable GAMBLE_TEST_COMMAND, that mainly refers to mask commands that are described in the sibling file maskfile.md

(The links above link to the Rust sandbox just for example)

These are only examples, the tool should work with any languages and tools

Refactoring session

Install watchexec, then :

watchexec -- git gamble --refactor

This is just TCR (without TDD)

Git Aliases

Some examples of git alias

~/.gitconfig or .git/config

[alias]
    # git-gamble aliases
    fail = gamble --fail
    pass = gamble --pass
    faile = gamble --fail --edit
    passe = gamble --pass --edit

    # git-gamble TDD's aliases
    red = gamble --red
    green = gamble --green
    refactor = gamble --refactor
    rede = gamble --red --edit

If you use Fish Shell, it works very well with the plugin enlarge_your_git_alias

Hooks

git-gamble provides his own custom git hooks to enhance the development workflow

These hooks can be used to perform specific actions before and after gambling

Before Gambling (pre-gamble)

The pre-gamble hook is executed before the actual gambling process begins

Sometimes, you want to have certain things imperatively done before gambling on the tests

What if something irrelevant was just missing and that forgetting this was not worth starting again ?

Examples :

  • Formatting
  • Linting

After Gambling (post-gamble)

The post-gamble hook is triggered after the gambling result is known

Sometimes, you want to do actions after the gamble only in some situations

Examples :

  • Wrong gamble (pass fail or fail pass)
    • Suggest to take a break
  • Good gamble (pass pass or fail fail)
    • Congratulate
  • Failing tests (fail fail)
    • Remember to not push on the main branch
  • Passing tests (pass pass)

See more usable examples

If you have more ideas, please share them by opening an issue

Hook Execution and Configuration

Custom hooks are called with arguments :

  • pre-gamble <GAMBLED>
    • pre-gamble hook is executed with one argument <GAMBLED>
  • post-gamble <GAMBLED> <ACTUAL>
    • post-gamble hook is executed with two arguments <GAMBLED> and <ACTUAL>

Where :

  • <GAMBLED> is pass or fail
  • <ACTUAL> is pass or fail

Custom hooks of git-gamble are like any other client-side git hooks :

  • a hook is a file (pre-gamble or post-gamble)

  • a hook must be in the $GIT_DIR/hooks/ folder

    • or in the folder configured by git config core.hooksPath
  • a hook must be executable

    • to make it executable, execute the following command

       chmod +x .git/hooks/*-gamble
      
  • will not be executed if any of these options are used :

    • --no-verify
    • --dry-run

Hooks Execution Lifecycle

The following diagram shows when custom hooks are executed in relation to standard git hooks

flowchart LR
    subgraph git-gamble's hooks' lifecycle
        direction TB
        git-gamble([git-gamble --pass\nOR\ngit-gamble --fail])
        --> pre-gamble[pre-gamble pass\nOR\npre-gamble fail]:::gitGambleHookStyle
        --> git_add([git add --all]):::gitGambleInternalStyle
        --> GAMBLE_TEST_COMMAND([exec $GAMBLE_TEST_COMMAND]):::gitGambleInternalStyle
        --> gamble{Gamble result ?}:::gitGambleInternalStyle

        gamble -->|Gamble success| Success
        subgraph Success
            direction TB

            git_commit([git commit]):::gitGambleInternalStyle
            --> pre-commit:::gitHookStyle
            --> prepare-commit-msg[prepare-commit-msg\n$GIT_DIR/COMMIT_EDITMSG\nmessage]:::gitHookStyle
            --> commit-msg[commit-msg\n$GIT_DIR/COMMIT_EDITMSG]:::gitHookStyle
            --> post-commit:::gitHookStyle
            post-commit --> rewritten?
            rewritten?{{"Last commit rewritten ?\nWhen gambling fail\nafter another gamble fail"}}:::gitGambleInternalStyle
            rewritten? -->|Yes| post-rewrite[post-rewrite\namend]:::gitHookStyle --> post-gamble-success
            rewritten? -->|No| post-gamble-success
            post-gamble-success[post-gamble pass pass\nOR\npost-gamble fail pass]:::gitGambleHookStyle
        end

        gamble -->|Gamble error| Error
        subgraph Error
            direction TB

            git_reset([git reset --hard]):::gitGambleInternalStyle
            --> post-gamble-error[post-gamble pass fail\nOR\npost-gamble fail fail]:::gitGambleHookStyle
        end
    end

    subgraph Legend
        direction TB

        subgraph Legend_[" "]
            direction LR

            command([Command executed\nby user])
            git-gamble_command([Command executed\nby git-gamble]):::gitGambleInternalStyle
            condition{Condition ?}:::gitGambleInternalStyle
        end

        subgraph Hooks
            direction LR

            hook[git's hook]:::gitHookStyle
            hook_with_argument[git's hook\nfirst_argument\nsecond_argument]:::gitHookStyle
            git-gamble_hook_with_argument[git-gamble's hook\nfirst_argument\nsecond_argument]:::gitGambleHookStyle
        end
    end

    classDef gitHookStyle fill:#f05133,color:black,stroke:black;
    classDef gitGambleHookStyle fill:#5a3730,color:white,stroke:white;
    classDef gitGambleInternalStyle fill:#411d16,color:white,stroke:white;
Textual description of the hooks execution lifecycle
  1. User executes the command git-gamble --pass OR git-gamble --fail
  2. git-gamble executes the hook pre-gamble pass OR pre-gamble fail
  3. git-gamble executes the command git add --all
  4. git-gamble executes the command exec $GAMBLE_TEST_COMMAND
  5. When gamble success
    1. git-gamble executes the command git commit
    2. git executes the hook pre-commit
    3. git executes the hook prepare-commit-msg $GIT_DIR/COMMIT_EDITMSG message
    4. git executes the hook commit-msg $GIT_DIR/COMMIT_EDITMSG
    5. git executes the hook post-commit
    6. When gambling fail after another gamble fail
      1. git executes the hook post-rewrite amend
    7. git-gamble executes the hook post-gamble pass pass OR post-gamble fail pass depending on what the user gambled
  6. When gamble error
    1. git-gamble executes the command git reset --hard
    2. git-gamble executes the hook post-gamble pass fail OR post-gamble fail fail depending on what the user gambled

Hooks examples

List of hooks examples

If you have more ideas, please share them by opening an issue

😌 Assistant

A simple assistant that displays tips based on the result of the gamble

With git 2.54.0 or higher, use this hook with this command :

git gamble hook enable assistant

With git 2.53.0 or below, download the file (post-gamble.assistant.sample.sh) and follow these steps

šŸ‘„ Real time collaboration

Specially adapted to near real-time collaboration by pushing when tests pass

With git 2.54.0 or higher, use this hook with this command :

git gamble hook enable real-time-collaboration

With git 2.53.0 or below, download the file (post-gamble.real-time-collaboration.sample.sh) and follow these steps

ā±ļø Time-keeper

If you are using git-time-keeper , these hooks start and stop the time-keeper between gambling

With git 2.54.0 or higher, use this hook with this command :

git gamble hook enable time-keeper

With git 2.53.0 or below, download the following files and follow these steps

šŸ”Š Sound

A hook that plays a random sound based on the result of the gamble

The example uses sound samples from the French series Kaamelott

With git 2.54.0 or higher, use this hook with this command :

git gamble hook enable sound

With git 2.53.0 or below, download the file (post-gamble.sound.sample.sh) and follow these steps

If you want more sounds, set the environment variable KAAMELOTT_FAN=1 but beware, many are vulgar and can upset the most sensitive people

To play the sound using FFMPEG instead of VLC set the variable PLAY_COMMAND="ffplay -v 0 -nodisp -autoexit"

Any command that accepts an HTTP URL to a MP3 should work

Using with git 2.53.0 or below

  1. Download the files

  2. Get the hooks’ path :

    • the path configured by git config set core.hooksPath <PATH>
    • .git/hooks/ folder by default
    HOOKS_PATH="$(git config get core.hooksPath || echo "${GIT_DIR:-.git}/hooks")"
    
  3. Move the files to your project in the hooks’ path

  4. Concatenate the files in a single one and rename to pre-gamble or post-gamble :

    To use several examples, the files must be concatenated

    cat "$HOOKS_PATH"/pre-gamble.*.sample.sh >"$HOOKS_PATH"/pre-gamble
    
    cat "$HOOKS_PATH"/post-gamble.*.sample.sh >"$HOOKS_PATH"/post-gamble
    
  5. Make them executable :

    chmod +x "$HOOKS_PATH"/{pre,post}-gamble
    

Note : with git 2.53.0 or below, the following command will work, but the hook won’t execute

git gamble hook enable <HOOK>

Remove hook

To remove the hook, just remove the file

VSCode

To run git-gamble from VSCode

Setup

Add this in your .vscode/tasks.json

{
	// See https://go.microsoft.com/fwlink/?LinkId=733558
	// for the documentation about the tasks.json format
	"version": "2.0.0",
	"tasks": [
		{
			"label": "Gamble fail",
			"type": "shell",
			"command": "direnv exec . git gamble --fail",
			"group": "test",
		},
		{
			"label": "Gamble pass",
			"type": "shell",
			"command": "direnv exec . git gamble --pass",
			"group": "test",
		},
		{
			"label": "Gamble red",
			"type": "shell",
			"command": "direnv exec . git gamble --red",
			"group": "test",
		},
		{
			"label": "Gamble green",
			"type": "shell",
			"command": "direnv exec . git gamble --green",
			"group": "test",
		},
		{
			"label": "Gamble refactor",
			"type": "shell",
			"command": "direnv exec . git gamble --refactor",
			"group": "test",
		},
	]
}

Remove direnv exec . if you don’t use DirEnv yet

Execute

Using command palette

  1. Open the Command Palette (F1 or CTRL + Shift + P)
  2. Select Tasks: Run Task
  3. Select the task (e.g. Gamble refactor)

Using shortcuts

You can bind keyboard shortcuts

For example

[
    {
        "key": "ctrl+g ctrl+f",
        "command": "workbench.action.tasks.runTask",
        "args": "Gamble fail"
    },
    {
        "key": "ctrl+g ctrl+p",
        "command": "workbench.action.tasks.runTask",
        "args": "Gamble pass"
    },
]

Using buttons in the sidebar

Task Explorer extension can be installed to trigger using click

Frequently Asked Questions

When to use it ?

When NOT to use it ?

  • You don’t really want to try this methodology
    • You will eventually cheat and lose the benefits while keeping some of the drawbacks
  • You don’t know what you want to achieve
  • Your tests :
    • ran very slowly
      • maybe a subset of the tests can be run to accelerate
    • are flaky
    • are not self-validating
  • You want to use double-loop TDD
    • unless you don’t run tests on bigger loops when you run smaller loops

Does it commit failing tests ?

TL;DR history will contain commits with only passing tests (with a nominal usage)

Long story : Yes and no

Yes, it does a commit at every step, including when tests are failing

And no, because it will amend the previous commit when :

  • gambling several times that the test fails
  • gambling that the pass after a gamble that the test fails

Commits with failing tests are temporary and should not end up in the history

Example

Assuming every gamble were the good one, so everything is committed at each command

Doing the following commands :

git gamble --red      --message "first iteration red"
git gamble --red      --message "first iteration red edited" # improve the test
git gamble --green    --message "first iteration green"
git gamble --red      --message "second iteration red"
git gamble --green    --message "second iteration green"
git gamble --refactor --message "second iteration refactor"

Will end with this git history :

gitGraph TB:
    commit id:"initial commit"
    commit id:"first iteration green"
    commit id:"second iteration green"
    commit id:"second iteration refactor"

The full history will look like this :

---
config:
    theme: 'base'
---
gitGraph TB:
    commit id:"initial commit"
    branch GC_1
    commit id:"(Dangling) first iteration red" type:REVERSE
    switch main
    branch GC_2
    commit id:"(Dangling) first iteration red edited" type:REVERSE
    switch main
    commit id:"first iteration green"
    branch GC_3
    commit id:"(Dangling) second iteration red" type:REVERSE
    switch main
    commit id:"second iteration green"
    commit id:"second iteration refactor"

Note : the GC_* branches are just a representation of dangling commits (Dangling) that will eventually be deleted by the garbage collector

The garbage collector is an automatic mechanism in git, nothing special have to be done

What is the default commit message ?

By default, the commit message is empty

Does git allow empty commit message ?

Yes, not by default but given --allow-empty-message

This is an uncommon use case

Why having an empty commit message ?

The TCRDD methodology encourages doing baby steps

When doing so, the commits are so small that almost every time, the only possible message is describing what has been changed which a kind of repetition with the commit’s content

How to avoid empty commit message ?

Give an optional message when gambling

When gambling :

  • add a message with the option --message (or -m) followed by the message

    git gamble --red --message "This is a great message"
    
  • open the default editor to write a message with the option --edit

    git gamble --red --edit
    

It may be a good idea to add a message when enabling a feature flag

Rearrange commits after

Add the end of iterations, the history can be rewritten using git rebase, commits can even be squashed

Feel stuck ?

What if the new test is too complex to pass with just one gamble ?

So it’s often a good idea to :

  1. Take a break āøļø

  2. Slow down ā³

  3. Rollback the failing commit āŖ

    Keep a branch if needed

    git branch keep_it_for_maybe_later
    

    Then actually rollback

    git reset --hard @~
    
  4. Refactor to ā€œmake the change easyā€ šŸ§ šŸ’”

    for each desired change, make the change easy (warning: this may be hard), then make the easy change

    – Kent Beck 2012

  5. Try to do smaller steps šŸ‘¶šŸ¦¶

  6. If needed, try to reapply the failing commit ā©

    git cherry-pick keep_it_for_maybe_later --no-commit && git gamble --red
    

    It may be in conflict depending on the changes made in previous steps āš ļø

What languages are supported ?

git-gamble should work with all languages

It just need a test command that must :

  • exit with a 0 status when there are 0 failed tests
  • anything else is considered as a failure

It is independent of tools and editors

It could be integrated with tools and editors

To see real examples, read the usage examples

Give Love ā¤ļø or Give Feedbacks šŸ—£ļø

Do you like this project ?

Contributing

License ISC

Contributor Covenant

Any contributions (feedback, bug report, merge request …) are welcome

GitLab Contributors Open an issue Open a merge request Forks

No question is too simple

Respect the code of conduct

Follow Keep a Changelog

Add to a package repository

The package is available on these repositories, and can be installed as usual if you use one of them

If the package is missing or not up to date in the repository that you prefer to use you can add it to the X package repository where the X package repository is e.g. Debian, Homebrew, Chocolatey …

Feel free to do it, we don’t plan to do it at the moment, it’s too long to learn and understand how each of them works

If you do it, please file an issue or open an MR to update the documentation

Development

The following sections will help you to contribute with a merge request

dependency status coverage report CodeScene Code Health Gitlab Pipeline Status AppVeyor status for Homebrew built with nix

Development Setup

Nix

The easiest way to get everything needed is to use Nix

This project is built with nix

  1. Install Nix : to install dependencies

  2. Install DirEnv : to install dependencies and set environment variables

  3. Install Git : to get the repository

  4. Get the source

    git clone https://gitlab.com/pinage404/git-gamble
    
  5. Switch to the directory

    cd ./git-gamble
    

    Note : if you only want to edit the documentation or slides, you can directly change the working directory to it, this will avoid downloading dependencies that you don’t need yet in the next step

    cd ./docs
    # or
    # cd ./slides
    
  6. Let DirEnv automagically set up the environment by executing the following command in the project directory

    This will set up all required dependencies

    direnv allow
    

Cachix

To avoid rebuilding from scratch, Cachix can be used

cachix use git-gamble

Troubleshooting

If you get an error like this one when entering in the folder

direnv: loading ~/Project/git-gamble/.envrc
direnv: using nix
direnv: using cached derivation
direnv: eval /home/pinage404/Project/git-gamble/.direnv/cache-.336020.2128d0aa28e
direnv: loading ./script/setup_variables.sh
   Compiling git-gamble v2.4.0-alpha.0 (/home/pinage404/Project/git-gamble)
direnv: ([/nix/store/19arfqh2anf3cxzy8zsiqp08xv6iq6nl-direnv-2.29.0/bin/direnv export fish]) is taking a while to execute. Use CTRL-C to give up.
error: linker `cc` not found
  |
  = note: No such file or directory (os error 2)

error: could not compile `git-gamble` due to previous error

Just run the following command to fix it

direnv reload

Help wanted to permanently fix this

Manual

Please reconsider using Nix because of the following advantages:

  • Ensure that the exact same versions will be installed, the versions that are known to work
  • Ensure the packages are installed in a way that works
  • All packages are installed, not only the rust toolchain, allowing you to work on any part

If you still want to avoid Nix, then follow these instructions:

  1. Install Rustup

  2. Start a new terminal to load the new configuration

  3. Clone and navigate to git-gamble

    git clone https://gitlab.com/pinage404/git-gamble.git
    cd git-gamble
    
  4. Install project-specific Rust toolchain and build the project

    Running a build for the first time will install the toolchain from rust-toolchain.toml:

    cargo build
    
  5. Configure the environment

    DirEnv is the recommended tool to set up the environment. If you don’t want to use it, then install and configure everything that should normally be installed by .envrc

Next, read the other files in the development section.

Commands

To list all available commands execute :

mask help

What is mask ?

Most of the people use make to create users-friendly alias of complexes commands : a task runner

The make syntax is complicated because make is not only a task runner but a whole build system (and because make is old)


mask is an awesome task runner

mask is made to execute complexes commands using the Markdown syntax

So even if the people don’t understand that they could execute them using the mask command, they have a readable documentation of all the available commands because markdown could be rendered in a pretty readable format

Available commands

The list below is a copy of maskfile.md

test

set -o errexit -o nounset -o pipefail -o errtrace

$MASK test shell
$MASK test rust

test shell

set -o errexit -o nounset -o pipefail -o errtrace

./tests/test_scripts.sh
./script/tests/test_generate_completion.sh

test rust

cargo test --features with_log

test rust feature-combinations

cargo feature-combinations --fail-fast test

test rust with_coverage

./script/test_feature_combinations_with_coverage.sh

test rust debug (FILTERS)

export RUST_LOG="trace"
export RUST_TRACE="full"
# export RUST_BACKTRACE="full"
export RUST_TEST_NOCAPTURE="display stdout and stderr"

cargo test \
    --features with_log \
    -- \
    "$FILTERS"

test rust watch

bacon test --features with_log

test rust mutants

cargo mutants
test rust mutants feature-combinations
cargo feature-combinations --summary-only mutants

lint

set -o errexit -o nounset -o pipefail -o errtrace

cargo clippy --all-targets --all-features
cargo check --all-targets --all-features
if command -v masklint >/dev/null; then
    masklint run
fi

lint feature-combinations

set -o errexit -o nounset -o pipefail -o errtrace

cargo feature-combinations --dedupe clippy --all-targets
cargo feature-combinations --dedupe check --all-targets
if command -v masklint >/dev/null; then
    masklint run
fi

format

set -o errexit -o nounset -o pipefail -o errtrace

if command -v nix >/dev/null; then
    # shellcheck disable=SC2046
    nix fmt $(
        find . \
            -name "*.nix"
    )
fi
cargo fix --all-features --allow-dirty --allow-staged
cargo fmt

setup

setup hooks

set -o errexit -o nounset -o pipefail -o errtrace

git config core.hooksPath .config/git/hooks

git gamble hook enable time-keeper
git gamble hook enable assistant
git gamble hook enable sound

git config set hook.format.event pre-gamble
git config set hook.format.command 'sh -c "mask format"'

git config set hook.lint.event pre-gamble
git config set --comment 'lint are just here as a warning' hook.lint.command 'sh -c "mask lint || true"'

docs

docs serve

set -o errexit -o nounset -o pipefail -o errtrace

pushd ./docs/
direnv exec . \
    mask docs serve

slides

slides serve (SLIDE_FILE)

set -o errexit -o nounset -o pipefail -o errtrace

pushd ./slides/
direnv exec . \
    mask slides serve "$SLIDE_FILE"

update

set -o errexit -o nounset -o pipefail -o errtrace

nix flake update
direnv exec . \
    nix develop ".#update_rust_toolchain_to_the_latest_stable_version" --command \
        update_rust_toolchain_to_the_latest_stable_version
direnv exec . \
    cargo update
pushd ./slides
direnv exec . \
    corepack use pnpm@latest
direnv exec . \
    pnpm update --latest

This folder has been set up from the nix-sandboxes’s template

Folders structure

Folders structure and important files, detailing their purpose and primary technologies used

  • maskfile.md : Documents the main commands used in the development workflow
  • src/bin/ : Where the main() functions are, which are the entry points of the applications
    • Uses the clap library for handling command-line arguments
    • Delegates core functionality to git
  • tests/ : Tests the features of the programs
    • Uses assert_cmd for testing command-line program
  • docs/ : Source of this documentation
    • Uses mdBook to generate documentation
      • Primarily from Markdown
      • Some Mermaid for diagrams
      • A few of HTML / CSS / JS
  • slides/ : Contains the source the slides
    • Uses Slidev to create slides
      • Primarily from Markdown
      • With some HTML / CSS / JS / Vue
  • flake.nix → nix/ : Manages development dependencies, packaging, templates…
    • Uses Snowfall/lib to scaffold files within the nix/ directory
  • script/ : Useful scripts to help the release process
  • .config/git/hooks/ : Examples of git hooks

Debug

There are some logs in the programs, pretty_env_logger is used to display them

There are 5 levels of logging (from the lightest to the most verbose) :

  • error
  • warn
  • info
  • debug
  • trace

The git-gamble logs are ā€œhiddenā€ behind the with_log feature

The option --features with_log (or --all-features) must be added to each cargo command for which you want to see logs (e.g.) :

cargo build --all-features
cargo run --all-features
cargo test --all-features

Then, to display logs, add this environment variable :

export RUST_LOG="git_gamble=debug"

To display really everything :

export RUST_LOG="trace"

There are other possibilities for logging in the env_logger documentation

You can also uncomment variables in the setup script

There is also a command that executes a test with all the outputs displayed

mask test rust debug <the test to filter>

Example

mask test rust debug commit_when_tests_fail

Check that the code is properly tested

To verify that the code is actually tested by the tests, run the following command :

mask test rust mutants

To verify with all feature combinations, run the following command :

mask test rust mutants feature-combinations

Warning, this may take many times (~30Ā min)

Refactoring session

At the beginning of the session, just run the following command :

watchexec -- git gamble --refactor

This is just TCR (without TDD)

Test strategy

git-gamble is basically an orchestrator around various git commands

There is very little logic in it

That is why testing the logic separately is not very useful : it is not important to verify that the commands are called with specific options ; what is important is the resulting behaviour, and that is what needs to be tested

For example : verifying that git commit is called with the -m or --message option is not important ; what is important is that the message passed as an argument is correctly recorded in the commit

Currently, the git command present in the $PATH is called directly ; if in the future, a git library implemented in Rust was used instead, the tests would not need to be modified

Making an interface to test the code calling correctly git would be more or like creating a git library

It will still be necessary to test the implementation of this library

Deployment

Just run cargo release (in a wide terminal, in order to have --help not truncated in the usage section)

cargo release

Architectural Decisions Records

The following pages records architectural decisions

They use the Markdown Architectural Decision Records’s template

Binaries organization

Story

We (a colleague [referred to as ā€œheā€ in the following text] and pinage404) used git-gamble at work

He was not used to using TDD or TCR

The fear of losing code led to an antipattern : before gambling, he took a lot of time to read the code carefully, compile it and execute it in his head, which slowed down the group

This strong methodology should lead you to let yourself be carried along by the tools

He recommended limiting the duration of iterations


git-gamble has been used by several groups and pinage404 has seen this antipattern several times

Context and Problem Statement

To solve the problem of iteration duration, another tool has been created since 2022-07-11, but it is neither documented or easily distributed

The first version of git-time-keeper was written in Bash, and works most of the time, in order to have a more stable experience, it will be rewritten in Rust

git-gamble and git-time-keeper are tools that work independently and can be used together for an optimal experience

How to create, maintain and distribute several tools ?

Decision Drivers

  • from the maintainer’s point of view
    • easy to set up
    • easy to maintain
    • easy to distribute
    • avoid duplication of configuration and utilities
  • from the user’s point of view
    • easy to install
    • easy to use
    • easy to understand that each tool can be used separately
    • easy to use tools together for an optimal experience

Considered Options

  • Repos : several independent repositories
  • Workspaces : 1 repository with several Cargo workspaces
  • Binaries : 1 repository with 1 crate containing several binaries

Decision Outcome

Chosen option: ā€œBinariesā€, because this solution seems to have the fewest downsides, see the table below

Pros and Cons of the Options

ReposWorkspacesBinaries
Maintainer
easy to set upGood, easiest, just git cloneBad, need a little of workNeutral, Good if every tool support it
easy to maintainBad, need to maintain several reposGood, that’s what workspaces are forNeutral, easy but risk of confusion between what belongs to which tool
easy to distributeBad, need to re-setup external platformsNeutral if every tool support itNeutral, Good if every tool support it
avoid duplicationBadNeutral, can have a shared CrateGood
User
easy to installBad, need several installationsNeutralNeutral, Good if every tool support it
easy to useNeutralNeutralNeutral
understand tools are independentGoodNeutralNeutral
easy to use tools togetherBadNeutralNeutral
TotalGood 2 Neutral 1 Bad 5 = -3Good 1 Neutral 6 Bad 1 = 0Good 1 Neutral 7 Bad 0 = +1

Pipeline single source of truth

Context and Problem Statement

Same tools, different versions

There is no single source of truth between local development and the pipeline

Tools are defined in different files

The versions of the tools used are not the same, depending on the package manager used to install the tools

Big container images

Debian-based container images are big

Past attempts have been failures to make working build with Alpine based container, because Alpine use Musl instead of GLibC

Example

The image created from ci/rust_with_git.Dockerfile

FROM rust:1.80-slim-bookworm

RUN apt-get --quiet=2 update \
    && apt-get --quiet=2 install --no-install-recommends \
    git \
    && apt-get --quiet=2 clean \
    && rm --force --recursive /var/lib/apt/lists/*

Using the following commands

docker build -f ./ci/rust_with_git.Dockerfile ./ci/
dive sha256:443b8185bfb389687697e4193b569feb40a3e0b5a2b023562f8da279516f5d83
  • Total Image size: 854MB
  • Potential wasted space: 4.7 MB
  • Image efficiency score: 99 %

Bad update process

Update is done manually in each Dockerfile, in the FROM ; which is not convenient

Decision Drivers

  • Open source
  • Price : must be free or very low
  • High reproducibility
  • Ability to use cache (Nix’s cache or GitLab’s cache)
  • Lighter
  • Faster
  • Use of standard tools

Considered Options

Decision Outcome

Replace image building with Kaniko in the pipeline with image building with Nix

Try using GitLab’s Cache or Cachix using Cynerd’s gitlab-ci-nix example (the tool or the idea made by the tool) to save Nix Store.

Consequences

  • Good, single source of truth for the versions used (which ends up to flake.lock)

  • Good, update process is already handled automatically

    Just execute

    nix flake update
    
  • Good, use open source tools

    • Maybe not the Cachix server which will maybe used
  • Good, free

  • Good, high reproducibility

  • Good, cache may be used

  • Bad, the images are twice bigger

    • It could probably be optimized
  • Bad, expect to be slower, because of the image size

    • On self-hosted runner, it will use Docker’s cache
  • Good, don’t use a niche tool

Confirmation

Pros and Cons of the Options

NixCI

NixCI

  • Source not found
    • Not open source ? āŒ
  • Only one dev behind it ?
    • What about the maintainability ? āš ļø
  • The page talks about GitLab integration
    • There is no call to action for GitLab
      • Don’t know how to use it with GitLab 🤷
  • Is it SaaS or Self Hosted ?
    • There are machines that will compute stuff
      • Who pays ? Pricing is unclear šŸ’ø
    • The page talks about self-hostable
      • Don’t found the documentation 🤷
  • Need scaring authorization on GitHub : Act on your behalf 🤨

The page looks more like a roadmap than the documentation of actual functionalities

Doesn’t look trustworthy

GitHub Actions

Need to move to GitHub

GitHub is not Open Source āŒ

Garnix

Garnix

Pricing have a free tier (1 500 minutes/month) that may be enough šŸ’¶

Cachix

Cachix

Already used in the pipeline šŸ‘

Jetify Cache

Jetify Cache

  • Pricing seems to start from $5/month + $0.60 Go/month šŸ’°
  • Don’t found the sources āŒ

Export Nix Store to GitLab CI’s Cache

As the time pass, the cache will grow, it will slow the pipeline šŸ“ˆ

  • Every cache paths will be downloaded and reuploaded at each pipeline, not only the ones that are actually needed for the current job
  • How to do clean unnecessary paths ? 🚮
    • Should an extra step with nix-gc be added ?
    • Without erasing everything periodically ?
šŸ†šVonfry’s versionCynerd’s versionTECHNOFAB’s version
ComplexitySimpler āœ…More complex šŸ‘ŽEven more complex 😵
CacheNaive āŒOptimized āœ…Optimized āœ…
ReusabilityJust an example āŒMade for āœ…Made for āœ…
ReproducibilityNot versioned šŸ‘ŽNot versioned šŸ‘ŽHave releases āœ…

Simple direnv allow

Use container from nixos/nix and run the following command inside

direnv allow

Pro : simple āœ…

Const : don’t use cache, so download everything from scratch at each job āŒ

Install Nix inside GitLab’s job

Won’t use GitLab’s cache āŒ

  1. Download a base image
  2. Install Nix inside GitLab’s job
  3. Install packages using Nix

Seems to be heavy

Build image using Nix

dockerTools.buildImage

dockerTools.buildImage Proof of concept
{
  lib,
  pkgs,
}:

let
  rustupToolchainFile = lib.snowfall.fs.get-file "rust-toolchain.toml";
  rust-toolchain = pkgs.rust-bin.fromRustupToolchainFile rustupToolchainFile;
in
pkgs.dockerTools.buildImage {
  name = "build-image";
  copyToRoot = [
    rust-toolchain
    pkgs.dockerTools.binSh
  ];
}
nix build '.#build-image'
dive --source docker-archive <(gunzip -c ./result)
  • Total Image size: 1.9 GB
  • Potential wasted space: 0 B
  • Image efficiency score: 100 %

dockerTools.buildLayeredImage

dockerTools.buildLayeredImage Proof of concept
{
  lib,
  pkgs,
}:

let
  rustupToolchainFile = lib.snowfall.fs.get-file "rust-toolchain.toml";
  rust-toolchain = pkgs.rust-bin.fromRustupToolchainFile rustupToolchainFile;
in
pkgs.dockerTools.buildLayeredImage {
  name = "build-layered-image";
  contents = [
    rust-toolchain
    pkgs.dockerTools.binSh
  ];
}
nix build '.#build-layered-image'
dive --source docker-archive <(gunzip -c ./result)
  • Total Image size: 1.7 GB
  • Potential wasted space: 0 B
  • Image efficiency score: 100 %
dockerTools.buildLayeredImage Proof of concept limiting max layers

Same when trying to limit max layers

{
  lib,
  pkgs,
}:

let
  rustupToolchainFile = lib.snowfall.fs.get-file "rust-toolchain.toml";
  rust-toolchain = pkgs.rust-bin.fromRustupToolchainFile rustupToolchainFile;
in
pkgs.dockerTools.buildLayeredImage {
  name = "build-layered-image-limit-layers";
  contents = [
    rust-toolchain
    pkgs.dockerTools.binSh
  ];
  maxLayers = 2;
}
nix build '.#build-layered-image-limit-layers'
dive --source docker-archive <(gunzip -c ./result)
  • Total Image size: 1.7 GB
  • Potential wasted space: 0 B
  • Image efficiency score: 100 %
dockerTools.buildLayeredImage Proof of concept without components

Same when trying to remove components to remove the rust’s source

{
  lib,
  pkgs,
}:

let
  rustupToolchainFile = lib.snowfall.fs.get-file "rust-toolchain.toml";
  rustupToolchainFileContent = lib.trivial.importTOML rustupToolchainFile;
  rustupToolchain = rustupToolchainFileContent.toolchain;

  rustupToolchainCleaned = lib.snowfall.attrs.merge-deep [
    rustupToolchain
    { components = [ ]; }
  ];

  rust-toolchain = pkgs.rust-bin.fromRustupToolchain rustupToolchainCleaned;
in
pkgs.dockerTools.buildLayeredImage {
  name = "build-layered-image-no-rust-src";
  contents = [
    rust-toolchain
    pkgs.dockerTools.binSh
  ];
}
nix build '.#build-layered-image-no-rust-src'
dive --source docker-archive <(gunzip -c ./result)
  • Total Image size: 1.7 GB
  • Potential wasted space: 0 B
  • Image efficiency score: 100 %

dockerTools.streamLayeredImage

dockerTools.streamLayeredImage Proof of concept
{
  lib,
  pkgs,
}:

let
  rustupToolchainFile = lib.snowfall.fs.get-file "rust-toolchain.toml";
  rust-toolchain = pkgs.rust-bin.fromRustupToolchainFile rustupToolchainFile;
in
pkgs.dockerTools.streamLayeredImage {
  name = "stream-layered-image";
  contents = [
    rust-toolchain
    pkgs.dockerTools.binSh
  ];
}
nix build '.#stream-layered-image'
./result > stream-layered-image.tar
dive docker-archive://./stream-layered-image.tar
  • Total Image size: 1.7 GB
  • Potential wasted space: 0 B
  • Image efficiency score: 100 %

nix2container

nix2container generate the JSON that describe the container where the layers are derivation linked to the Nix store

The article that explain what does it solve

nix2container Proof of concept
{
  lib,
  pkgs,
}:

let
  rustupToolchainFile = lib.snowfall.fs.get-file "rust-toolchain.toml";
  rust-toolchain = pkgs.rust-bin.fromRustupToolchainFile rustupToolchainFile;
in
pkgs.nix2containerPkgs.nix2container.buildImage {
  name = "nix2container";
  copyToRoot = pkgs.buildEnv {
    name = "root";
    paths = [
      rust-toolchain
      pkgs.dockerTools.binSh
    ];
    pathsToLink = [ "/bin" ];
  };
}
nix run '.#nix2container.copyToDockerDaemon'
dive nix2container:cvlrpzfkvq2y0q99aa8csni1r6znl3bg
  • Total Image size: 1.7 GB
  • Potential wasted space: 0 B
  • Image efficiency score: 100 %

Comparison

buildImagebuildLayeredImagestreamLayeredImagenix2container
Nix StoreGZipped ImageGZipped ImageScript + JSONJSON
Layers1SeveralSeveral1
Image size1.9 GB1.7 GB1.7 GB1.7GB

Rust’s source code, Linux’s headers, locales, documentation and manual pages are included

It could probably be removed with more configuration

More Information


  1. It seems that there is no more easy stuff to do ↩

Support git hook new format

Context and Problem Statement

Git 2.54 introduced a new way to declare git hooks (and also other cool unrelated stuffs)

git-gamble’s current implementation (2.12.1), manually executes git hooks file

Defining git-gamble’s hook with the new format will ignore these hooks (demo)

Decision Drivers

  • easy to implement
  • easy to maintain
  • keep some kind of backward compatibility

Considered Options

Decision Outcome

Chosen options : use internal git-hook run command and add Cargo feature to disable git-gamble’s hooks

It would be easy to implement, easy to maintain

With some documentation, it would allow people using and old version of git to remove git-gamble’s hooks

Testing the combination of all Cargo features, will take some times but could be automatised

Pros and Cons of the Options

Ignore git hook new format

This is the current behavior until git-gamble 2.12.1

  • Good, nothing to do, the easiest solution
  • Bad, users may be confused : some hook (git’s hooks) work, but others (git-gamble’s hook) don’t
  • Bad, this introduces a difference in the behavior of git and git-gamble

Reimplement the support of the new hook format

Current implementation reimplement how hook file works, so it Could be logic to reimplement how new hooks format works

  • Bad, this is the solution is hard to implement
  • Bad, it would be not future-proof, the maintenance may be hard

Use internal git-hook run command

Since git 2.36, there is the command git-hook run

git 2.36 have been released the 2022-04-18

Replacing the current implementation by this command will make git-gamble requiring git 2.36 or above

As the time of writing (2026-05-09), according to Repology, this would break some LTS distributions

Like :

Ignore old distributions

The Long Term Support distributions target servers, not really for daily use like a developer computer

  • Bad, a user with an old git version won’t be able to use git-gamble at all, this would break backward compatibility

Use different strategies based on git’s version

A detection of the git version could be done, to know which strategy to use :

  • git 2.54 or newer ⇒ use git hook run
  • else ⇒ manually execute file

So :

  • Bad, it won’t be easy to implement
  • Bad, it would make the code more complicated for something that would eventually disappear, so bad for the maintenance

Add Cargo feature to disable git-gamble’s hooks

Add a Cargo feature to remove all the hook related stuff at compilation

The feature would be enabled by default

The feature could be disabled and git-gamble compiled to works on old distributions

  • Good, it is easy to implement
  • Neutral, it would require some documentation to explain how to do that and when to do that
  • Bad, testing the combinations of all Cargo features takes more time

Not yet published - Work In Progress

The following pages document work in progress that has not yet been published

All or part of it is subject to change without notice

git-time-keeper

GitLab stars

git-time-keeper is a tool that helps to take baby steps šŸ‘¶šŸ¦¶

git-time-keeper can be used as a companion tool to git-gamble or can be used separately

Setup

With git-gamble

Read the hooks examples documentation

With git

Without git-gamble

# git config --local core.hooksPath ./hooks # if you want to version hooks
HOOKS_PATH=$(git rev-parse --git-path hooks)
mkdir -p "$HOOKS_PATH"
echo "git-time-keeper 'stop'" >>"$HOOKS_PATH/pre-commit"
echo "git-time-keeper 'start'" >>"$HOOKS_PATH/post-commit"
chmod +x "$HOOKS_PATH"/{pre,post}-commit

Set iteration duration

export TIME_KEEPER_MAXIMUM_ITERATION_DURATION=$((3 * 60)) # 3 minutes

Editor auto save

In order to avoid conflict while saving between the content stored on the disk and the content in the editor, it is recommended to enable autosave (at least for the current project)

VSCode

For VSCode, you can set files.autoSave to afterDelay

You can run this command to do it for you

SETTINGS="./.vscode/settings.json"
if [ ! -f "$SETTINGS" ]; then
    echo '{
    "files.autoSave": "afterDelay",
}' >"$SETTINGS"
elif grep 'files.autoSave' "$SETTINGS" >/dev/null; then
    sed -Ei 's@"files.autoSave": "[a-zA-Z]+"@"files.autoSave": "afterDelay"@' "$SETTINGS"
else
    sed -i 's@^{@{\n    "files.autoSave": "afterDelay",@' "$SETTINGS"
fi

How to use ?

When being on time

sequenceDiagram
    actor Dev
    participant git
    participant hooks
    participant git-time-keeper

    Dev->>git: git commit
    git->>hooks: git hook run pre-commit
    hooks-xgit-time-keeper: git time-keeper stop
    git->>hooks: git hook run post-commit
    hooks-)+git-time-keeper: git time-keeper start

    Dev->>git: git commit
    git->>hooks: git hook run pre-commit
    hooks-)git-time-keeper: git time-keeper stop
    git-time-keeper-->-hooks: timer stopped
    git->>hooks: git hook run post-commit
    hooks-xgit-time-keeper: git time-keeper start

When the countdown is missed

sequenceDiagram
    actor Dev
    participant git
    participant hooks
    participant git-time-keeper

    Dev->>git: git commit
    git->>hooks: git hook run pre-commit
    hooks-xgit-time-keeper: git time-keeper stop
    git->>hooks: git hook run post-commit
    hooks-)+git-time-keeper: git time-keeper start
    git-time-keeper-)git-time-keeper: time limit passed
    git-time-keeper-)-git: git restore .

To see all available flags and options

git time-keeper --help

Dash - between git and time-keeper may be only needed for --help

git-time-keeper --help

Usage

git time-keeper --help
Usage: git-time-keeper [OPTIONS]

Options:
  -h, --help                               Print help
  -V, --version                            Print version

Any contributions (feedback, bug report, merge request ...) are welcome
https://gitlab.com/pinage404/git-gamble

Possible states

stateDiagram
    state "Timer is running" as running
    state "git restore ." as clean

    [*] --> running : start
    running --> [*] : stop
    running --> clean : time limit passed
    clean --> [*]

    running --> running : start
    [*] --> [*] : stop

Limitation

Unix compatible only (Linux / macOS) : need sh and kill

Changelog

The format is based on Keep a Changelog and this project adheres to Semantic Versioning

Upcoming release

Full changelog

Version 2.14.6 (2026-07-14)

Technical

  • fix the pipeline

Full changelog

Version 2.14.5 (2026-07-14)

Packaging

  • release archive with binaries (git-gamble and git-time-keeper)

Documentation

Technical

Full changelog

Version 2.14.4 (2026-06-13)

Documentation

Technical

  • fix the pipeline

Full changelog

Version 2.14.3 (2026-06-13)

Packaging

  • release with tag vX.Y.Z instead of version/X.Y.Z
  • fix artifacts upload on release

Documentation

Technical

  • update dependencies

Full changelog

Version 2.14.2 (2026-05-24)

Technical

  • add Cargo features to disable some tests related to specific git’s version
    • with_git_2_36_0
    • with_git_2_54_0
  • help packaging with Nix : less strict text generation in hooks, not using shebang
  • fix AppVeyor’s pipeline (which was broken since 2 years 🫣)
  • update dependencies

Full changelog

Version 2.14.1 (2026-05-21)

Technical

Full changelog

Version 2.14.0 (2026-05-21)

Features

Changes

  • rename Cargo feature from with_shell_completions to with_subcommand_generate_shell_completions
  • rename hook
    • from
      • post-gamble.real_time_collaboration.sample.sh
    • to
      • post-gamble.real-time-collaboration.sample.sh
  • rename git-time-keeper’s git’s hooks
    • from
      • pre-commit.time-keeper.sample.sh
      • post-commit.time-keeper.sample.sh
    • to
      • pre-commit.time-keeper-git-only.sample.sh
      • post-commit.time-keeper-git-only.sample.sh

Documentation

Technical

  • fix pipeline
  • bump dependencies

Full changelog

Version 2.13.1 (2026-05-17)

Technical

  • fix pipeline

Full changelog

Version 2.13.0 (2026-05-17)

Backward compatibility note āš ļø

Starting with git-gamble 2.13.0, it requires git version 2.36 or higher

git 2.36 was released the 2022-04-18, and is now available in most package managers

To use git-gamble with git below 2.36, git-gamble’s hooks must be disabled by installing git-gamble with the option to disable the feature

Features

  • support new git hook format introduced in git 2.54 (and also introduced other cool unrelated stuffs)
  • display more help messages ; Closes Issue #10
    • when the test command can’t be executed
      • like when the test command does not exist or is not found in the $PATH
    • when git can’t be executed
      • like when it is not installed or is not found in the $PATH
  • allow to remove optional features at compile time, they are enabled by default ; this was a first step for Issue #10
    • with_custom_hooks : to use custom hooks
    • with_shell_completions : to enable shell completions

Changes

  • display all errors in stdout instead of stderr
    • previously some errors were displayed on stdout, others on stderr
    • the boundary between a warning and an error is blurry

Fix

  • build slides in the pipeline with Gitlab.org worker

Documentation

Technical

  • .unwrap() and .expect()
    • in source, replace .unwrap() with .expect() to have at least an error message
    • in tests, replace .expect() with .unwrap() a specific message is not needed in tests, because we have the source code just aside
  • upgrade glab from 1.90.0 to 1.97.0
  • bump Rust from 1.94.0 to 1.95.0
  • bump dependencies

Full changelog

Version 2.12.1 (2026-03-25)

Technical

  • upgrade glab from 1.61.0 to 1.90.0 to try to fix the release step in the pipeline

Full changelog

Version 2.12.0 (2026-03-24)

Features

  • display more help messages
    • when a commit fails ; Thanks @wonderbird (Stefan Boos) for the report Issue #9
      • the most likely scenario is when a pre-commit hook fail
      • possible other case (not likely a normal scenario) when an internal command (git) receive a signal (SIGKILL) while running
    • when the test command can’t be parsed
    • when a post-gamble hook fails

Fix

  • since version/2.11.0, when a pre-gamble hook fails, it explains what happened and how to debug
    • before :
      • always pretends that the pre-gamble hook exited with code 1
    • after :
      • displays the pre-gamble hook exited code

Documentation

Contributing

Technical

Full changelog

Version 2.11.0 (2025-06-28)

Features

  • help users to open issues

Fix

Technical

  • bump Rust from 1.85.0 to 1.88.0
  • bump dependencies

Full changelog

Version 2.10.0 (2025-03-18)

Fix

  • fix git-time-keeper when used according to the documentation, an error message was displayed when used with git-gamble
    • note: git-time-keeper is still in beta

Documentation

Distribution

  • Packaged for Nixpkgs starting from 2.9.0
  • update Chocolatey package’s version on release
  • fix Chocolatey link in the release’s artifact
  • remove AppImage distribution Issue #8

Technical

  • remove support of legacy Nix, prefer using Nix Flake
  • use Snowfall Lib to simplify Nix stuff with convention over configuration
  • bump dependencies
  • bump Rust from 1.80 to 1.85.0
  • bump Rust’s edition from 2021 to 2024
  • in the pipeline, container images are generated from Dockerfile to Nix

Full changelog

Version 2.9.0 (2024-08-05)

Features

  • previously, untracked files were not deleted in case of a bad gamble ; now, untracked files are deleted in case of a bad gamble ; Thanks @kevin.hantzen (k.hantzen) for the idea Issue #3
  • previously, gambling --green with a clean repository failed ; now, it warns that there is nothing to commit ; Thanks @contet (Thomas Conte) for the idea

Documentation

Technical

  • bump dependencies
  • bump Rust from 1.78 to 1.80

Full changelog

Version 2.8.0 (2024-06-15)

Features

  • previously, gambling --green with --fixup (e.g. git gamble --green --fixup ":/some commit") just after a git gamble --red failed ; this has been fixed ; Contribution by @korrat (Markus Haug) in MR !5
  • improve manual page

Technical

  • bump Rust from 1.75 to 1.78
  • update dependencies
  • fix flaky test ; Contribution by @korrat (Markus Haug) in MR !6
  • replace NPM with PNPM

Full changelog

Version 2.7.0 (2024-02-08)

Security

  • bump dependencies to fix the high-severity security vulnerability RUSTSEC-2024-0006

Features

  • add human-panic, a tool that encourages users who experience a crash to report it

Documentation

Technical

  • add compose.yml file to help to debug the CI
  • bump container image from Debian Buster to Bookworm
  • bump Rust from 1.73 to 1.75
  • bump dependencies

Full changelog

Version 2.6.0 (2023-11-05)

Features

  • when generating shell completion, if the user doesn’t give the target shell, try to determine the current shell from the environment or default to Bash

Documentation / Distribution

Contributing

Technical

Full changelog

Version 2.5.0 (2022-07-03)

Features

Documentation / Distribution

Contribution

  • setup Gitpod to help contributions

Technical

Full changelog

Version 2.4.0 (2022-04-30)

Features

Documentation / Distribution

Technical

  • bump some dependencies
  • use Nix Flake to generate environment with nix-shell or nix develop
  • use devshell to have commands shortcuts
  • bump Rust from 1.55 to 1.60
  • ignore audit dependency because there is no safe upgrade available
  • release only on main

Full changelog

Version 2.3.0 (2021-10-23)

Documentation

  • auto updated git gamble generate-shell-completions --help in README

Distribution

  • publish Linux and Windows binaries in releases

Technical

  • fix markdownlint issues
  • hardcode TOC generated by Markdown All In One instead of the one generated by GitLab
  • faster CI by removing useless job
  • generate test coverage

Full changelog

Version 2.2.1 (2021-10-03)

  • Fix deployment on crate.io

Full changelog

Version 2.2.0 (2021-10-02)

Features

  • Wrap git-gamble --help based on the terminal size

Documentation

  • auto updated git-gamble --help in README

Technical

  • Improve release binary size thanks to this post
    • from 5 934 760 octets (~6Mo) to 772 416 octets (<1Mo)

Full changelog

Version 2.1.0 (2021-10-01)

Features

  • improve git-gamble --help
    • add color
    • reorder --fail, --pass and --refactor flags
    • add description of the program
    • add link to the repository
  • generate shell completions using the subcommand git gamble generate-shell-completions

Documentation

Technical

  • update release-cli
  • explicitly set rustc as a dependency

Full changelog

Version 2.0.0 (2021-09-25)

Packages repositories

  • āš ļø BREAKING CHANGE āš ļø remove BinTray package publishing

Features

Documentation / Distribution

Technical

  • smaller container image in CI
  • upload only required files to crates.io, thanks to cargo-diet
  • bump Rust from 1.50 to 1.55
  • bump some dependencies
  • remove some linters warnings
  • allow git-gamble to run inside nix-shell
  • explicitly set cargo as a dependency

Others

  • rename default branch from master to main

Full changelog

Version 1.3.0 (2021-03-12)

Technical

Packages repositories

Packages repositories will be moved on GitLab because JFrog will shut down BinTray

Please follow installation instructions for your specific system

  • Affected by this changement
    • āš ļø Automatic update won’t work in the future āš ļø
    • There is not automatic update system at the moment, you already have to manually update at each new version
      • AppImage
  • Not affected by this changement
    • Mac OS X / Homebrew
    • Nix / NixOS
    • Cargo

This will be a kind of āš ļø BREAKING CHANGE āš ļø : 2.0.0 won’t be on BinTray

Others

  • bump image’s version for the CI’s container
  • bump Kaniko’s version to build the CI’s container

Full changelog

Version 1.2.0 (2021-02-20)

Added

Technical

  • remove warning at compile time (while using cargo test) ; Contribution by @bachrc in MR !1
  • improve the speed of the CI
  • improve the way to set up the development environment
  • bump Rust to 1.50
  • bump dependencies

Full changelog

Version 1.1.0 (2020-05-25)

Added

  • custom logo
  • shells completions
    • automatically installed with Debian and Homebrew packages
    • only for Bash, Fish, ZSH

Full changelog

Version 1.0.0 (2020-04-26)

Initial project

With the minimum functionalities required for real life use

Installable on Linux, Mac OS X and Windows

Full changelog

Backlog

  • git workspace support
    • git update-ref should contain a unique identifier to the workspace
      • branch name ?
      • folder path ?
  • gamble hooks
    • branch based development
      • git commit --fixup
      • git rebase --autosquash
    • optional hook to revert if not gambled in a delay
      • git-time-keeper
        • document
        • package
        • distribute
  • like git, flags & options & arguments should be retrieved from CLI or environment variable or config’s file
    • re-use git config to store in file ?
    • repository level config using direnv and environment variable ?
  • stash instead of revert ?
  • shell completion
    • in the package ?
      • Cargo
      • Chocolatey
    • for git gamble not only git-gamble
  • https://gitlab.com/gitlab-org/security-products/analyzers
  • https://gitlab.com/gitlab-org/security-products/ci-templates
  • https://medium.com/@tdeniffel/tcr-variants-test-commit-revert-bf6bd84b17d3
  • https://svgfilters.com/
  • https://github.com/llogiq/mutagen

Distribution / publishing backlog

Technical improvement opportunities

Morty Proxy This is a proxified and sanitized view of the page, visit original site.