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
git-gambleis a tool that helps to develop the right thing š, baby step by baby step š¶š¦¶git-gambleuses the TCRDD methodgit-gambleworks with all languagesgit-gambleis agitadd-on
Watch the demo
Community
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
- Write only 1 failing test
- Make all tests pass
- Refactor
- Repeat while itās incomplete
- 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
- Write only 1 failing test
- Run tests
- If the tests pass then write another test
- 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
- Write the minimum code to make all tests pass
- Run tests
- If the tests fail then try something else
- 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
- Rewrite code without changing the behaviour
- Run tests
- If the tests fail then change something else and repeat the refactor step
- Else if the tests pass and
- there are another things to refactor then repeat the refactor step
- there is another feature to add then go to the red step
- 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)
- Gambling on test results
- If the tests pass then commit
- 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
- Change some code
- Repeat while itās incomplete
- 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
- Change some code
- Run tests
test && commit || revert - If the tests fail then
- Revert
- Try something else
- Repeat
- If the tests pass then
- Commit
- If there are another things to change then repeat
- 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
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
- Red phase
- Write only 1 failing test
- If the tests pass then
- Revert
- Retry the red phase
- Else if the tests fail then
- Commit
- Go to the green phase
- Green phase
- Make all tests pass
- If the tests fail then
- Revert
- Retry the green phase
- Else if the tests pass then
- Commit
- Go to the refactor phase
- Refactor phase
- Refactor
- If the tests fail then
- Revert
- Retry the refactor phase
- Else if the tests pass then
- 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
- Write only 1 test
- Gamble that the test fail
git gamble --red - Actually run tests
- If the tests pass then
- Revert
- Write another test
- Else if the tests fail then
- Commit
- 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
- Write the minimum code
- Gamble that the tests pass
git gamble --green - Actually run tests
- If the tests fail then
- Revert
- Try something else
- Else if the tests pass then
- Commit
- 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
- Rewrite code without changing the behaviour
- Gamble that the tests pass
git gamble --refactor - Actually run tests
- If the tests fail then
- Revert
- Change something else
- Else if the tests pass then
- Commit
- If there are another things to refactor then go to the refactor phase
- Else if there is another feature to add then go to the red phase
- 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
test && commit || revert- TDD is dead, long live TCR?
- Xavierās TCRDD first commit
- git-gambleās first commit
- Murexās TCR first commit
- git-betās first commit
Similar projects
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 method | Linux | macOS | Windows |
|---|---|---|---|
| Nix | Yes | Yes | Yes |
| Debian | Yes | No | No |
| Homebrew | Yes | Yes | No |
| Chocolatey | No | No | Yes |
| Mise | Yes | Yes | Yes |
| Scoop (community) | No | No | Yes |
| Cargo | Yes | Yes | Yes |
| Download the binary | Yes | No | Yes |
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
Nix is a package manager available for Linux, macOS and Windows (through WSL2)
Requirements
-
Check the installation with this command :
nix --versionIf it has been well settled, it should output something like this :
nix (Nix) 2.22.3Else 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
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
-
Make sure it is available in your
$PATH, you can check it with this command :git --versionIf it has been well settled, it should output something like this :
git version 2.54.0Else 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
-
Go to the latest version of
git-gamble-debian -
Download the latest version
git-gamble_2.14.6_amd64.deb -
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
Requirements
-
Check the installation with this command :
brew --versionIf 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
Requirements
Chocolatey
-
Check the installation with this command :
choco --versionIf it has been well settled, it should output something like this :
2.3.0Else 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
-
Make sure it is available in your
$PATH, you can check it with this command :git --versionIf it has been well settled, it should output something like this :
git version 2.54.0Else 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.6The 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
-
Check the installation with this command :
mise --versionIf 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 availableElse 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
-
Make sure it is available in your
$PATH, you can check it with this command :git --versionIf it has been well settled, it should output something like this :
git version 2.54.0Else 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
-
Make sure it is available in your
$PATH, you can check it with this command :git --versionIf it has been well settled, it should output something like this :
git version 2.54.0Else 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 :
- https://github.com/hoilc/scoop-lemon
- https://github.com/anderlli0053/DEV-tools
- https://github.com/sun2ot/scoop-proxy-cn
- https://github.com/pjx1314/scoop-cn
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
Requirements
Cargo
-
Check the installation with this command :
cargo --versionIf 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
-
Make sure it is available in your
$PATH, you can check it with this command :git --versionIf it has been well settled, it should output something like this :
git version 2.54.0Else 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
-
Install binary
-
Use
cargo-binstallto 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 binaries are available for your platform
-
If you want to compile from source
Run this command :
cargo install git-gamble
-
-
Add
~/.cargo/binto 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 :
- sub-command hook (
with_subcommand_hook)- which requires and includes custom hooks support (
with_custom_hooks)
- which requires and includes custom hooks support (
- shell completions (
with_subcommand_generate_shell_completions)
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 includeswith_custom_hooks)cargo install git-gamble --no-default-features --features "with_subcommand_hook" -
with_custom_hookscargo install git-gamble --no-default-features --features "with_custom_hooks" -
with_subcommand_generate_shell_completionscargo 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
Requirements
git-gamble doesnāt repackage git, it uses the one installed on your system
-
Make sure it is available in your
$PATH, you can check it with this command :git --versionIf it has been well settled, it should output something like this :
git version 2.54.0Else 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 :
- Download the binary on the release page
- Rename it to
git-gamble - 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
-
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 :
-
Write a failing test in your codebase, then :
git gamble --red # or git gamble --fail -
Write the minimum code to make tests pass, then :
git gamble --green # or git gamble --pass -
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
-
Write a failing test in your codebase, then :
git gamble --red -- $YOUR_TEST_COMMANDThe example below is for running your tests for a Node.js project that use PNPM
git gamble --red -- pnpm test -
Write the minimum code to make tests pass, then :
git gamble --green -- $YOUR_TEST_COMMAND -
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
Helloto 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
~/.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 failorfail pass)- Suggest to take a break
- Good gamble (
pass passorfail fail)- Congratulate
- Failing tests (
fail fail)- Remember to not push on the main branch
- Passing tests (
pass pass)- Run acceptance tests to check the status of the Double Loop TDD / ATDD loop
- Suggest to push
- Push automatically
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-gamblehook is executed with one argument<GAMBLED>
post-gamble <GAMBLED> <ACTUAL>post-gamblehook is executed with two arguments<GAMBLED>and<ACTUAL>
Where :
<GAMBLED>ispassorfail<ACTUAL>ispassorfail
Custom hooks of git-gamble are like any other client-side git hooks :
-
a hook is a file (
pre-gambleorpost-gamble) -
a hook must be in the
$GIT_DIR/hooks/folder- or in the folder configured by
git config core.hooksPath
- or in the folder configured by
-
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
- User executes the command
git-gamble --passORgit-gamble --fail git-gambleexecutes the hookpre-gamble passORpre-gamble failgit-gambleexecutes the commandgit add --allgit-gambleexecutes the commandexec $GAMBLE_TEST_COMMAND- When gamble success
git-gambleexecutes the commandgit commitgitexecutes the hookpre-commitgitexecutes the hookprepare-commit-msg $GIT_DIR/COMMIT_EDITMSG messagegitexecutes the hookcommit-msg $GIT_DIR/COMMIT_EDITMSGgitexecutes the hookpost-commit- When gambling
failafter another gamblefailgitexecutes the hookpost-rewrite amend
git-gambleexecutes the hookpost-gamble pass passORpost-gamble fail passdepending on what the user gambled
- When gamble error
git-gambleexecutes the commandgit reset --hardgit-gambleexecutes the hookpost-gamble pass failORpost-gamble fail faildepending 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
-
Download the files
-
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")" - the path configured by
-
Move the files to your project in the hooksā path
-
Concatenate the files in a single one and rename to
pre-gambleorpost-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 -
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
- Open the Command Palette (F1 or CTRL + Shift + P)
- Select
Tasks: Run Task - 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 ?
- Does it commit failing tests ?
- What is the default commit message ?
- Feel stuck ?
- What languages are supported ?
When to use it ?
- You want to :
- You follow the Test F.I.R.S.T. Principles
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
- ran very slowly
- 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 messagegit gamble --red --message "This is a great message" -
open the default editor to write a message with the option
--editgit 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 :
-
Take a break āøļø
-
Slow down ā³
-
Rollback the failing commit āŖ
Keep a branch if needed
git branch keep_it_for_maybe_laterThen actually rollback
git reset --hard @~ -
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
-
Try to do smaller steps š¶š¦¶
-
If needed, try to reapply the failing commit ā©
git cherry-pick keep_it_for_maybe_later --no-commit && git gamble --redIt 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 ?
- If yes, please add a star on GitLab
- If no, please open an issue
to give your feedbacks
Contributing
Any contributions (feedback, bug report, merge request ā¦) are welcome
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
Development Setup
Nix
The easiest way to get everything needed is to use Nix
-
Install Nix : to install dependencies
-
Install DirEnv : to install dependencies and set environment variables
-
Install Git : to get the repository
-
Get the source
git clone https://gitlab.com/pinage404/git-gamble -
Switch to the directory
cd ./git-gambleNote : 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 -
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:
-
Install Rustup
-
Start a new terminal to load the new configuration
-
Clone and navigate to git-gamble
git clone https://gitlab.com/pinage404/git-gamble.git cd git-gamble -
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 -
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
src/bin/: Where themain()functions are, which are the entry points of the applications- Written in Rust
tests/: Tests the features of the programs- Written in Rust
- Uses
assert_cmdfor testing command-line program
- Uses
speculoosfor readable assertions
docs/: Source of this documentationslides/: Contains the source the slides- Uses Slidev to create slides
- Primarily from Markdown
- With some HTML / CSS / JS / Vue
- Uses Slidev to create slides
flake.nixānix/: Manages development dependencies, packaging, templatesā¦- Uses Snowfall/lib to scaffold files within the
nix/directory
- Uses Snowfall/lib to scaffold files within the
script/: Useful scripts to help the release process- Tested using
shunit2
- Tested using
.config/git/hooks/: Examples ofgithooks- Tested using
shunit2
- Tested using
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) :
errorwarninfodebugtrace
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
| Repos | Workspaces | Binaries | |
|---|---|---|---|
| Maintainer | |||
| easy to set up | Good, easiest, just git clone | Bad, need a little of work | Neutral, Good if every tool support it |
| easy to maintain | Bad, need to maintain several repos | Good, thatās what workspaces are for | Neutral, easy but risk of confusion between what belongs to which tool |
| easy to distribute | Bad, need to re-setup external platforms | Neutral if every tool support it | Neutral, Good if every tool support it |
| avoid duplication | Bad | Neutral, can have a shared Crate | Good |
| User | |||
| easy to install | Bad, need several installations | Neutral | Neutral, Good if every tool support it |
| easy to use | Neutral | Neutral | Neutral |
| understand tools are independent | Good | Neutral | Neutral |
| easy to use tools together | Bad | Neutral | Neutral |
| Total | Good 2 Neutral 1 Bad 5 = -3 | Good 1 Neutral 6 Bad 1 = 0 | Good 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
- Using external services
- Using GitLab only
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
- Migration to use Cynerdās cache
- Others migrations have been included in the comparison table above
- Container image size
-
Most images are smaller
Documentation builder Size Dockerfile 344 Mio Nix 28 Mio -
Rust related images are bigger
Rust test coverage Size Dockerfile 940 MB Nix before optimization 1.94 GB Nix after optimization 1 1.19 GB
-
Pros and Cons of the Options
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 š¤·
- There is no call to action for 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 š¤·
- There are machines that will compute stuff
- 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
cachix/install-nix-action- Seems to run the official Nix installer
DeterminateSystems/nix-installer-action&DeterminateSystems/magic-nix-cache-action- Rely on big TypeScript program that does stuff š¤·
GitHub is not Open Source ā
Garnix
Pricing have a free tier (1 500 minutes/month) that may be enough š¶
- Need scaring authorization on GitHub : Act on your behalf š¤Ø
- Need to move to GitHub ā
Cachix
Already used in the pipeline š
- Pricing have a free tier (5Go per project)
- 2.7Go are already used, it may be not enough š
- Cachixās client is open source but donāt found the server ā
- Simple Nix instance ? š¤·
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
-
Vonfryās gitlab-ci-nix example
- By GitLab CI itself, download previous cache, if any ā¬ļø
- Import derivations to Nix Store from cache, if any ⬠ļø
- Do stuff āļø
- Export all derivations from Nix Store to cache ā”ļø
- By GitLab CI itself, upload cache ā¬ļø
-
Cynerdās gitlab-ci-nix example
- By GitLab CI itself, download previous cache, if any ā¬ļø
- Set substituters : local cache, Cachix, S3, SSH, if any ⬠ļø
- Save current derivations
- Do stuff āļø
- List new derivations by comparing current derivations with the saved ones
- Export new derivations from Nix Store to cache ā”ļø
- Upload new derivations to Cachix, S3, SSH, if any ā¬ļø
- By GitLab CI itself, upload cache ā¬ļø
-
TECHNOFABās nix-gitlab-ci template
Declare pipeline using Nix instead of YAML
Seems easy to test locally
- declare the pipeline using Nix
- generate the
.gitlab-ci.yaml - trigger a pipeline from the generated
.gitlab-ci.yaml
The generated pipeline works likely Cynerdās version
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-gcbe added ? - Without erasing everything periodically ?
- Should an extra step with
| š | Vonfryās version | Cynerdās version | TECHNOFABās version |
|---|---|---|---|
| Complexity | Simpler ā | More complex š | Even more complex šµ |
| Cache | Naive ā | Optimized ā | Optimized ā |
| Reusability | Just an example ā | Made for ā | Made for ā |
| Reproducibility | Not 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 ā
- Download a base image
- Install Nix inside GitLabās job
- Install packages using Nix
Seems to be heavy
Build image using Nix
- Interesting blog post explaining how to build image using Nix inside GitLab CI
- NixOS official documentation about
dockerToolsin the too long all in one page heavy manual- Content of the official documentation about
dockerToolsin a lighter page
- Content of the official documentation about
dockerTools.buildImage
dockerTools.buildImage- Produce a container image with one big layer
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- Produce a container image with several ordered layers
- Each layer are stored in the Nix Store
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- Produce a container image with several ordered layers
- Layers are NOT stored in the Nix Store
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
buildImage | buildLayeredImage | streamLayeredImage | nix2container | |
|---|---|---|---|---|
| Nix Store | GZipped Image | GZipped Image | Script + JSON | JSON |
| Layers | 1 | Several | Several | 1 |
| Image size | 1.9 GB | 1.7 GB | 1.7 GB | 1.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
- Ericaās presentation who gave motivation to deep dive to find a solution
- Real world example to build container image from Nix
-
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
- Ignore git hook new format
- Reimplement the support of the new hook format
- Use internal
git-hook runcommand
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 :
- Debian 11 Bullseye which only includes git
2.30.2; end of life : 2026-08-31 - Ubuntu 22.04 Jammy which only includes git
2.34.1; end of life : 2027-04-21
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.54or newer ā usegit 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
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
Version 2.14.6 (2026-07-14)
Technical
- fix the pipeline
Version 2.14.5 (2026-07-14)
Packaging
- release archive with binaries (
git-gambleandgit-time-keeper)
Documentation
Technical
- fix pipeline to deploy on crates.io again
Version 2.14.4 (2026-06-13)
Documentation
- explain how to install with Brew 6
Technical
- fix the pipeline
Version 2.14.3 (2026-06-13)
Packaging
- release with tag
vX.Y.Zinstead ofversion/X.Y.Z - fix artifacts upload on release
Documentation
- add change log
- respect system preference theme
Technical
- update dependencies
Version 2.14.2 (2026-05-24)
Technical
- add Cargo features to disable some tests related to specific
gitās versionwith_git_2_36_0with_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
Version 2.14.1 (2026-05-21)
Technical
- try to fix release (
2fd3f3)
Version 2.14.0 (2026-05-21)
Features
- add
hooksub-command toenable/disableexamples hooks ; Thanks to @udebella (udebella) for the feedback and idea- requires
git2.54.0or higher to actually execute the hooks - add Cargo feature
with_subcommand_hook- change the
defaultfeature to include it
- change the
- requires
Changes
- rename Cargo feature from
with_shell_completionstowith_subcommand_generate_shell_completions - rename hook
- from
post-gamble.real_time_collaboration.sample.sh
- to
post-gamble.real-time-collaboration.sample.sh
- from
- rename
git-time-keeperāsgitās hooks- from
pre-commit.time-keeper.sample.shpost-commit.time-keeper.sample.sh
- to
pre-commit.time-keeper-git-only.sample.shpost-commit.time-keeper-git-only.sample.sh
- from
Documentation
- add how to use hooks examples
- add how to install with Scoop community projects
- add how to install with a
Brewfile - add how to do Double Loop TDD
Technical
- fix pipeline
- bump dependencies
Version 2.13.1 (2026-05-17)
Technical
- fix pipeline
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
githook format introduced ingit2.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
- like when the test command does not exist or is not found in the
- when
gitcanāt be executed- like when it is not installed or is not found in the
$PATH
- like when it is not installed or is not found in the
- when the test command canāt be executed
- 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 hookswith_shell_completions: to enable shell completions
Changes
- display all errors in
stdoutinstead ofstderr- previously some errors were displayed on
stdout, others onstderr - the boundary between a warning and an error is blurry
- previously some errors were displayed on
Fix
- build slides in the pipeline with Gitlab.org worker
Documentation
- explain how to use sample hooks ; Thanks to @udebella (udebella) for the feedback and idea
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
- in source, replace
- upgrade
glabfrom1.90.0to1.97.0 - bump Rust from
1.94.0to1.95.0 - bump dependencies
Version 2.12.1 (2026-03-25)
Technical
- upgrade
glabfrom1.61.0to1.90.0to try to fix the release step in the pipeline
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-commithook fail - possible other case (not likely a normal scenario) when an internal command (
git) receive a signal (SIGKILL) while running
- the most likely scenario is when a
- when the test command canāt be parsed
- when a
post-gamblehook fails
- when a commit fails ; Thanks @wonderbird (Stefan Boos) for the report Issue #9
Fix
- since
version/2.11.0, when apre-gamblehook fails, it explains what happened and how to debug- before :
- always pretends that the
pre-gamblehook exited with code1
- always pretends that the
- after :
- displays the
pre-gamblehook exited code
- displays the
- before :
Documentation
- improve landing page of the documentation
- add a Frequently Asked Questions section
- add an example hook that plays a random sound based on the result of the gamble ; the example uses sound samples from the French series Kaamelott
- improve the explanation of the hook
- add a page with all slides that are useful to make an introduction
- accessibility
- provide textual descriptions of each schema in the theory page
- make the content readable for screen readers in the installation page
- fix many typos
Contributing
- better guide to setup manually when wanting to contribute ; Contribution by @wonderbird (Stefan Boos) in MR !31
- better explain the
maskcommands - describe the test strategy
- describe the folders structure
Technical
- upgrade mdBook to
0.5 - bump Rust from
1.88.0to1.94.0 - bump dependencies
- try to have more idiomatic Rust code ; Thanks @ToF- (ToF) & @etienneCharignon (Ćtienne Charignon) for the idea
- try to avoid panicking in a deep function calls (with
.unwrap()or.expect()) ; Thanks @ToF- (ToF) for the idea- some remaining, it is hard to write tests on them
- split logic between informing / warning / āerroringā a message and displaying it ; Thanks @etienneCharignon (Ćtienne Charignon) for the idea
- try to avoid panicking in a deep function calls (with
Version 2.11.0 (2025-06-28)
Features
- help users to open issues
Fix
- when a
pre-gamblehook fails- before :
git-gamblecrash
- after :
- do not crash
- exit gracefully with code 1
- explain what happened and how to debug
- Co-authored-by: @Anne-Flower (Anne-Flore Bernard)
- before :
Technical
- bump Rust from
1.85.0to1.88.0 - bump dependencies
Version 2.10.0 (2025-03-18)
Fix
- fix
git-time-keeperwhen used according to the documentation, an error message was displayed when used withgit-gamble- note:
git-time-keeperis still in beta
- note:
Documentation
- improve documentation how to install with Chocolatey ; Contribution by @alexandreduch (Alexandre Duchenne) in MR !9
- guide where to put generated files for shell completions for Bash and Powershell ; Contribution by @alexandreduch (Alexandre Duchenne) in MR !9
Distribution
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.80to1.85.0 - bump Rustās
editionfrom2021to2024 - in the pipeline, container images are generated from Dockerfile to Nix
- for more information, read the architectural decision record
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
--greenwith a clean repository failed ; now, it warns that there is nothing to commit ; Thanks @contet (Thomas Conte) for the idea
Documentation
- explicitly list all supported installation methods ; Contribution by @contet (Thomas Conte) in MR !7
Technical
- bump dependencies
- bump Rust from
1.78to1.80
Version 2.8.0 (2024-06-15)
Features
- previously, gambling
--greenwith--fixup(e.g.git gamble --green --fixup ":/some commit") just after agit gamble --redfailed ; this has been fixed ; Contribution by @korrat (Markus Haug) in MR !5 - improve manual page
Technical
- bump Rust from
1.75to1.78 - update dependencies
- fix flaky test ; Contribution by @korrat (Markus Haug) in MR !6
- replace NPM with PNPM
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.ymlfile to help to debug the CI - bump container image from Debian Buster to Bookworm
- bump Rust from
1.73to1.75 - bump dependencies
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
- add a section to invite people to add a star on GitLab
or open open an issue to give feedback
- add Nixās template
- when installing using Nix, add a section to avoid rebuilding using Cachix
- document how to download latest main version of AppImage
- add a repository with several examples with several languages
- improve how to useās section ; Contribution by @mavomo (Michelle Avomo) in MR !3
- fix Windows / Chocolatey installation, ask users to install git manually instead of having an automatic dependency which was not working
- fix GitLabās URL on crates.io
- fix Nix overlay
Contributing
- document how to do refactoring session
Technical
- test refactoring to help readability
- improve release binary size thanks to this repository
- from
952 704 octetsto739 712 octets
- from
- bump container base images
- bump Kaniko
- bump Rust from
1.60to1.73 - bump dependencies
- make compilation faster by combining all tests in a single test binary
- add VS Codeās tasks to use git-gamble inside VS Code
- use Mask to execute commands
- generate shell completions using the
git-gamblebinary itself instead of at build time
Version 2.5.0 (2022-07-03)
Features
- add custom
git-gamblehooks :pre-gambleandpost-gamble
Documentation / Distribution
- add demo
- document how to upgrade when installing using Homebrew
- add Nix Flakeās
overlays - fix changelog in release
Contribution
- setup Gitpod to help contributions
Technical
- bump Rust
editionfrom2018to2021 - replace
structoptbyclapwhich includes the same features - add test Nix build in pipeline
- add Nix formatter
- fix coverage report
- bump Rust VSCodeās Extension Pack that replace Rust by
rust-analyzer - replace
spectral, which is unmaintained, byspeculooshis fork successor
Version 2.4.0 (2022-04-30)
Features
- add
--editoption to edit the commit message when committing which is passed togit commitcommand - add
--fixupoption and--squashoption which are passed togit commitcommand
Documentation / Distribution
Technical
- bump some dependencies
- use Nix Flake to generate environment with
nix-shellornix develop - use
devshellto have commands shortcuts - bump Rust from
1.55to1.60 - ignore audit dependency because there is no safe upgrade available
- release only on main
Version 2.3.0 (2021-10-23)
Documentation
- auto updated
git gamble generate-shell-completions --helpin README
Distribution
- publish Linux and Windows binaries in releases
Technical
- fix
markdownlintissues - hardcode TOC generated by Markdown All In One instead of the one generated by GitLab
- faster CI by removing useless job
- generate test coverage
Version 2.2.1 (2021-10-03)
- Fix deployment on crate.io
Version 2.2.0 (2021-10-02)
Features
- Wrap
git-gamble --helpbased on the terminal size
Documentation
- auto updated
git-gamble --helpin README
Technical
- Improve release binary size thanks to this post
- from
5 934 760 octets(~6Mo) to772 416 octets(<1Mo)
- from
Version 2.1.0 (2021-10-01)
Features
- improve
git-gamble --help- add color
- reorder
--fail,--passand--refactorflags - add description of the program
- add link to the repository
- generate shell completions using the subcommand
git gamble generate-shell-completions
Documentation
- add section that help to debug while crafting git-gamble itself
Technical
- update
release-cli - explicitly set
rustcas a dependency
Version 2.0.0 (2021-09-25)
Packages repositories
- ā ļø BREAKING CHANGE ā ļø remove BinTray package publishing
Features
- add
--no-verifyoption to skip githooks that is passed togit commitcommand ; Contribution by @korrat (Markus Haug) in MR !2
Documentation / Distribution
- improve Nix / NixOS installation instructions
- add shell completions for Nix / NixOS
- add man page for Nix / NixOS
- improve AppImage installation instructions
Technical
- smaller container image in CI
- upload only required files to crates.io, thanks to
cargo-diet - bump Rust from
1.50to1.55 - bump some dependencies
- remove some linters warnings
- allow
git-gambleto run insidenix-shell - explicitly set
cargoas a dependency
Others
- rename default branch from
mastertomain
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
Version 1.2.0 (2021-02-20)
Added
- installation for Nix / NixOS
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
Version 1.1.0 (2020-05-25)
Added
- custom logo
- shells completions
- automatically installed with Debian and Homebrew packages
- only for Bash, Fish, ZSH
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
Backlog
git workspacesupportgit update-refshould contain a unique identifier to the workspace- branch name ?
- folder path ?
- gamble hooks
- branch based development
git commit --fixupgit rebase --autosquash
- optional hook to revert if not gambled in a delay
git-time-keeper- document
- package
- distribute
- branch based development
- like git, flags & options & arguments should be retrieved from CLI or environment variable or configās file
- re-use
git configto store in file ? - repository level config using direnv and environment variable ?
- re-use
- stash instead of revert ?
- shell completion
- in the package ?
- Cargo
- Chocolatey
- for
git gamblenot onlygit-gamble
- in the package ?
- 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
- OS distribution
- Linux
- RPM
cargo-rpmnix bundle
- SnapCraft ?
- FlatPak ?
fpm: tool that help to generate to several packages- Open Build Service seems to be a service that build for every Linux targets
- it can be use
- thought REST
- badly documented
- with
ocsa CLI who seems to be a VCS like SVN (commit = commit + push)- who use the REST API under the hood
- thought REST
- it can be use
- RPM
- Mac OS X
- experiment GitLab Runner on Mac OS X
- run tests
- precompile
- experiment GitLab Runner on Mac OS X
- try to distribute OS less binaries
- Container Image (Docker) ?
- Awesome Rust Deployment
- Awesome Rust Platform Specific
- Linux
- permanent URL to download latest version
- symlinks to URL containing the version name ?
- using GitLab Pages ?
- versioned Homebrew Formula
- Use Cargo-Release to bump version
- how to update sha256 in the versioned file ?
Technical improvement opportunities
- licence check
- cargo licence
- cargo deny
- smaller binary
- cargo bloat
- cargo deps
- cargo machete
- refactor to iterate over
Shellenumās values instead of having an hard-coded array