diff --git a/CHANGELOG.md b/CHANGELOG.md index 484d9ab7..db51ba5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ Follows [Semantic Versioning](https://semver.org/). +## Unreleased + +### Added + +- Support for storing git-mob settings in a custom git config file via the GITMOB_CONFIG_FILE environment variable. When set, git-mob will use git config --file ... for its settings instead of --global. This is non-breaking and intended for per-machine / dotfiles workflows. (Closes #282) + +### Tests + +- Unit tests added to verify file-scoped vs global config behavior for git-mob settings. + ## git-mob-core 0.10.1 ### Fixed diff --git a/packages/git-mob-core/src/git-mob-api/config-scope.ts b/packages/git-mob-core/src/git-mob-api/config-scope.ts new file mode 100644 index 00000000..8f39bdb1 --- /dev/null +++ b/packages/git-mob-core/src/git-mob-api/config-scope.ts @@ -0,0 +1,8 @@ +export function configScopeFlag(): string { + const file = process.env.GITMOB_CONFIG_FILE; + if (file && file.length > 0) { + // callers should provide an absolute/expanded path + return `--file ${file}`; + } + return '--global'; +} diff --git a/packages/git-mob-core/src/git-mob-api/git-mob-config.ts b/packages/git-mob-core/src/git-mob-api/git-mob-config.ts index 672446c5..7966b76e 100644 --- a/packages/git-mob-core/src/git-mob-api/git-mob-config.ts +++ b/packages/git-mob-core/src/git-mob-api/git-mob-config.ts @@ -1,4 +1,5 @@ import { getConfig, getAllConfig, execCommand } from './exec-command.js'; +import { configScopeFlag } from './config-scope.js'; export async function localTemplate() { const localTemplate = await getConfig('--local git-mob-config.use-local-template'); @@ -6,22 +7,26 @@ export async function localTemplate() { } export async function fetchFromGitHub() { - const githubFetch = await getConfig('--global git-mob-config.github-fetch'); + const scope = configScopeFlag(); + const githubFetch = await getConfig(`${scope} git-mob-config.github-fetch`); return githubFetch === 'true'; } export async function getSetCoAuthors() { - return getAllConfig('--global git-mob.co-author'); + const scope = configScopeFlag(); + return getAllConfig(`${scope} git-mob.co-author`); } export async function addCoAuthor(coAuthor: string) { - const addAuthorQuery = `git config --add --global git-mob.co-author "${coAuthor}"`; + const scope = configScopeFlag(); + const addAuthorQuery = `git config --add ${scope} git-mob.co-author "${coAuthor}"`; return execCommand(addAuthorQuery); } export async function removeGitMobSection() { try { - return await execCommand('git config --global --remove-section git-mob'); + const scope = configScopeFlag(); + return await execCommand(`git config ${scope} --remove-section git-mob`); } catch {} } diff --git a/packages/git-mob-core/test/git-mob-config.spec.ts b/packages/git-mob-core/test/git-mob-config.spec.ts new file mode 100644 index 00000000..e1df5587 --- /dev/null +++ b/packages/git-mob-core/test/git-mob-config.spec.ts @@ -0,0 +1,43 @@ +import test from 'ava'; +import sinon from 'sinon'; +import * as execModule from '../src/git-mob-api/exec-command.js'; +import * as gitMobConfig from '../src/git-mob-api/git-mob-config.js'; + +test.afterEach(() => { + delete process.env.GITMOB_CONFIG_FILE; + sinon.restore(); +}); + +test.serial('addCoAuthor uses --global when GITMOB_CONFIG_FILE not set', async t => { + const stub = sinon.stub(execModule, 'execCommand').resolves(); + await gitMobConfig.addCoAuthor('Jane Doe '); + t.true( + stub.calledWith( + 'git config --add --global git-mob.co-author "Jane Doe "' + ) + ); +}); + +test.serial('addCoAuthor uses --file when GITMOB_CONFIG_FILE is set', async t => { + process.env.GITMOB_CONFIG_FILE = '/tmp/.gitconfig.local'; + const stub = sinon.stub(execModule, 'execCommand').resolves(); + await gitMobConfig.addCoAuthor('Bob Doe '); + t.true( + stub.calledWith( + 'git config --add --file /tmp/.gitconfig.local git-mob.co-author "Bob Doe "' + ) + ); +}); + +test.serial('getSetCoAuthors uses --global when GITMOB_CONFIG_FILE not set', async t => { + const stub = sinon.stub(execModule, 'getAllConfig').resolves([]); + await gitMobConfig.getSetCoAuthors(); + t.true(stub.calledWith('--global git-mob.co-author')); +}); + +test.serial('getSetCoAuthors uses --file when GITMOB_CONFIG_FILE is set', async t => { + process.env.GITMOB_CONFIG_FILE = '/tmp/customgitconfig'; + const stub = sinon.stub(execModule, 'getAllConfig').resolves([]); + await gitMobConfig.getSetCoAuthors(); + t.true(stub.calledWith('--file /tmp/customgitconfig git-mob.co-author')); +}); diff --git a/packages/git-mob/README.md b/packages/git-mob/README.md index d970a590..feadd298 100644 --- a/packages/git-mob/README.md +++ b/packages/git-mob/README.md @@ -6,7 +6,7 @@ _Add co-authors to commits_ when you collaborate on code. Use when pairing with a buddy or mobbing with your team. -Buy Me A Coffee +Buy Me A Coffee [✨ Git Mob VS Code extension](https://github.com/rkotze/git-mob-vs-code) @@ -23,6 +23,7 @@ _Add co-authors to commits_ when you collaborate on code. Use when pairing with - [Git Mob config](#git-mob-config) - [Use local commit template](#use-local-commit-template) - [Enable GitHub author fetch](#enable-github-author-fetch) + - [Use a custom git config file for git-mob settings](#use-a-custom-git-config-file-for-git-mob-settings) - [More commands](#more-commands) - [List all co-authors](#list-all-co-authors) - [Overwrite the main author](#overwrite-the-main-author) @@ -126,9 +127,9 @@ $ git solo Jane Doe ``` -Selected co-authors are **stored globally** meaning when switching between projects your co-authors stay the same\*. +Selected co-authors are **stored globally** meaning when switching between projects your co-authors stay the same*. -**\*Note**: If you've set a **local** commit template in your config then that template will be updated. However, **not** when you switch projects and you will see a warning. You can run `git mob` to update the commit template. [Read more here](https://github.com/rkotze/git-mob/discussions/81) +**\*Note**: If you've set a **local** commit template in your config then set this option to `true`. Only reads from the local git config. The README previously had additional content here. ### Add co-author from GitHub @@ -149,7 +150,7 @@ How to append co-authors to the message when using message flag - `git commit -m 1. Add `prepare-commit-msg` hook file in `.git/hooks` dir. See [hook-examples](https://github.com/rkotze/git-mob/tree/master/hook-examples) 2. The **hook** will need to be executable `chmod +x prepare-commit-msg` -`prepare-commit-msg` will need a script to read the co-authors, which can be done via `git mob-print`. See [hook-examples](https://github.com/rkotze/git-mob/tree/master/hook-examples) folder for working scripts. +`prepare-commit-msg` will need a script to read the co-authors, which can be done via `git mob-print`. See [hook-examples](https://github.com/rkotze/git-mob/tree/master/hook-examples) folder for ... The command `git mob-print` will output to `stdout` the formatted co-authors. @@ -196,6 +197,33 @@ To fetch authors from GitHub you need to enable it using the config. `git config --global git-mob-config.github-fetch true` +### Use a custom git config file for git-mob settings + +By default git-mob stores selected co-authors and related settings in your global Git config. To keep machine-specific settings out of your dotfiles you can tell git-mob to write/read its settings from a custom git config file by setting the GITMOB_CONFIG_FILE environment variable. + +Example: +```bash +export GITMOB_CONFIG_FILE="$HOME/.gitconfig.local" +``` + +When GITMOB_CONFIG_FILE is set, git-mob will use: +- git config --file ... for git-mob settings (for example adding/removing git-mob.co-author), instead of git config --global ... +- repository-local settings (git config --local ...) are unchanged and still operate at the repository scope. + +Notes: +- Provide an absolute/expanded path (the leading `~` is not expanded automatically). +- This is non-breaking: when the env var is not set git-mob continues to use the global config as before. +- Useful for per-machine dotfiles workflows where a machine-specific include is used in ~/.gitconfig. + +Example usage: +```bash +# Add a co-author to the per-machine config file +export GITMOB_CONFIG_FILE="$HOME/.gitconfig.local" +git add-coauthor jd "Jane Doe" jane@example.com +# Or add directly to the file via git config +git config --add --file "$GITMOB_CONFIG_FILE" git-mob.co-author "jd Jane Doe " +``` + ## More commands ### List all co-authors @@ -273,7 +301,7 @@ function git_initials { fi } -export PS1="\$(pwd)\$(git_initials) -> " +export PS1="$(pwd)$(git_initials) -> " ``` #### Fish @@ -299,4 +327,4 @@ end Read our blog post to find out why git-mob exists: [Co-author commits with Git Mob](http://tech.findmypast.com/co-author-commits-with-git-mob) -\* [If you have git-duet installed, you'll need to uninstall it](https://github.com/rkotze/git-mob/issues/2) since it conflicts with the git-solo command. +* [If you have git-duet installed, you'll need to uninstall it](https://github.com/rkotze/git-mob/issues/2) since it conflicts with the git-solo command.