Pair harmoniously! Working in a pair doesn't mean you've both lost your
identity. git-duet
helps with blaming/praising by using stuff that's already
in git
without littering your repo history with fictitious user identities. It
does so by utilizing git
s commit committer attributes to store the identity
of the second pair.
Example:
$ cat <<-EOF > ~/.git-authors
authors:
jd: Jane Doe; jane
fb: Frances Bar
email:
domain: awesometown.local
EOF
$ git duet jd fb
GIT_AUTHOR_NAME='Jane Doe'
GIT_AUTHOR_EMAIL='[email protected]'
GIT_COMMITTER_NAME='Frances Bar'
GIT_COMMITTER_EMAIL='[email protected]'
$ touch foo
$ git duet-commit -a -m 'initial commit'
[master (root-commit) ce78563] initial commit
Author: Jane Doe <[email protected]>
1 file changed, 0 insertions( ), 0 deletions(-)
create mode 100644 foo
$ git show --format=full
commit ce7856371e3e3a6d05f1b66af96f086df071e783
Author: Jane Doe <[email protected]>
Commit: Frances Bar <[email protected]>
initial commit
Signed-off-by: Frances Bar <[email protected]>
diff --git a/foo b/foo
new file mode 100644
index 0000000..e69de29
Options:
- See releases page for binary downloads, place in your
$PATH
. - Install using Homebrew from the git-duet homebrew tap (
brew install git-duet/tap/git-duet
) - Build from source:
go install github.com/git-duet/git-duet/...@latest
. This will put the binaries in$GOBIN
, if set, or$GOPATH/bin
(seego help install
for more details).
Make an authors file with email domain, or if you're already using
git pair, just symlink your
~/.pairs
file over to ~/.git-authors
. You can also set a repository
specific authors file by placing a .git-authors
file at the root
of your git
repository.
authors:
jd: Jane Doe; jane
fb: Frances Bar
email:
domain: awesometown.local
git duet
will use the git pair
YAML structure if it has to (the
difference is the top-level key being pairs
instead of authors
) e.g.:
pairs:
jd: Jane Doe; jane
fb: Frances Bar
email:
domain: awesometown.local
If you want your authors file to live somewhere else, just tell
git-duet
about it via the GIT_DUET_AUTHORS_FILE
environmental
variable, e.g.:
export GIT_DUET_AUTHORS_FILE=$HOME/.secret-squirrel/git-authors
# ...
git duet jd am
Set two authors (pairing):
git duet jd fb
Set one author (soloing):
git solo jd
Set arbitrary number of authors:
git as jd # works
git as jd fb rb # also works
Committing (needed to set --signoff
and export environment variables):
$ git duet-commit -v [any other git options]
Reverting (needed to set --signoff
and export environment variables):
git duet-revert -v [any other git options]
Merging (needed to set --signoff
and export environment variables):
git duet-merge -v [any other git options]
Rebasing (resets the committer to the committer of the current pair):
git rebase -i --exec 'git duet-commit --amend'
Suggested aliases:
dci = duet-commit
drv = duet-revert
dmg = duet-merge
drb = rebase -i --exec 'git duet-commit --amend'
Note: git-duet
only sets the configuration to use via git duet-commit
,
git duet-revert
, and git duet-merge
. Using git solo
(or git duet
) will
not effect the configured user.name
and user.email
. This allows git commit
to be used normally outside of git-duet
. You can set an environment
variable, GIT_DUET_SET_GIT_USER_CONFIG
to 1
to override this behavior and
set the user.name
and user.email
fields.
Another option for git rebase
ing with git-duet
is to use
git-duet-rebase.sh courtesy of @pivotaljohn. This
script uses git filter-branch
to update the names of all of the
authors/committers to the currently set pair. It acts on your active branch
(using the passed ref as the start point).
git commit -v
with git < 2.14.0
you'll find that the Co-authored-by
trailer is mistakenly
added after the verbose commit output. Please upgrade git
if you encounter this issue.
Set the environment variable GIT_DUET_CO_AUTHORED_BY
to 1
if you want
to have a "Co-authored-by" trailer for each co-author in your commit message
rather than having a "Signed-off-by" trailer for the committer.
If GIT_DUET_CO_AUTHORED_BY
is set, git duet
will install a prepare-commit-msg
hook file into the local repository by default. If, in addition, GIT_DUET_GLOBAL
is set,
git-duet
will instead install the prepare-commit-msg hook file into git config --global init.templatedir
.
If the value git config --global init.templatedir
is not set, git-duet
will set it
to $HOME/.git-template
.
GIT_DUET_CO_AUTHORED_BY
implicitly sets GIT_DUET_SET_GIT_USER_CONFIG
so that git duet
and git solo
set the author for normal git commands.
The common workflow is as follows:
- run
git duet
(which will install the prepare-commit-msg hook) - if you have
GIT_DUET_GLOBAL
set, rungit init
once in existing repos so that the hook file gets copied from theinit.templatedir
- thereafter, use the normal git commands (i.e.
git commit
,git merge
rather than the git-duet-subcommandsgit duet-commit
,git duet-merge
) - the prepare-commit-msg hook will append a
Co-authored-by
trailer for each co-author
If GIT_DUET_ROTATE_AUTHOR
is set in addition to GIT_DUET_CO_AUTHORED_BY
, git-duet
will install a post-commit hook file
which will swap author and co-author after every commit.
When amending a commit and the co-author has changed, a new Co-authored-by
trailer will get appended for
that co-author. In order to avoid duplicate Co-authored-by
trailers (i.e. trailers with the same co-author),
set git config [--global] trailer.ifexists addIfDifferent
to override the default value addIfDifferentNeighbor
.
If you want to opt out of this feature, unsetting GIT_DUET_CO_AUTHORED_BY
is not sufficient.
You also need to manually delete the prepare-commit-msg (and post-commit) hook file in your repo.
If you're jumping between projects and don't want to think about managing them all individually, you can operate on the global git config:
git solo -g jd
git duet --global jd fb
If you do this habitually, you can set the GIT_DUET_GLOBAL
environment
variable to true
to always operate on the global git config:
export GIT_DUET_GLOBAL=true # consider adding this to your shell profile
git solo jd
You can also set it to false
to always operate on the local config, even if
the global flag is used.
Sometimes while pairing you want to share the authorship love between the
pairs. If you set GIT_DUET_ROTATE_AUTHOR=1
it will swap the author and
committer (if there is one) on every git duet-commit
(after the commit).
This operates on whichever config the authorship was drawn from (e.g. if the
author/committer was set in the repository git config, it will rotate these
even if GIT_DUET_GLOBAL
is specified).
Git duet supports more than 2 people working at a time by specifying more sets of initials:
git duet jd fb zp
If you do not set GIT_DUET_ROTATE_AUTHOR
, then git-duet will use jd and fb
as the author and committer respectively. If you have GIT_DUET_ROTATE_AUTHOR
set then git-duet will rotate with each commit. The first commit will have
jd as the author, and fb as the committer. The second commit will have fb
as the author and zp as the committer and so on.
(Note: This feature uses ,
as the delimiter which will fail to parse
properly if the user's name or e-mail address contains a ,
.)
Additionally, if you set GIT_DUET_ALLOW_MULTIPLE_COMMITTERS
, then git-duet
will add a sign-off trailer in the commit message for every committer that
is specified after the author:
Example:
$ git show --format=full
commit ce7856371e3e3a6d05f1b66af96f086df071e783
Author: Jane Doe <[email protected]>
Commit: Frances Bar <[email protected]>
initial commit
Signed-off-by: Frances Bar <[email protected]>
Signed-off-by: Zubaz Shirts <[email protected]>
diff --git a/foo b/foo
new file mode 100644
index 0000000..e69de29
By default, email addresses are constructed from the first initial and
last name ( or optional username after a ;
) plus email domain, e.g.
with the following authors file:
pairs:
jd: Jane Doe; jane
fb: Frances Bar
email:
domain: eternalstench.bog.local
After invoking:
git duet jd fb
Then the configured email addresses will show up like this:
git config user.email
# -> [email protected]
git config duet.env.git-author-email
# -> [email protected]
git config duet.env.git-committer-email
# -> [email protected]
A custom email template may be provided via the email_template
config
variable. The template should be a valid Go template string (see
http://golang.org/pkg/text/template/). The object passed in has .Name
,
.Username
, and .Initials
.
Additional functions available to template:
toLower(s)
: lowercases stringtoUpper(s)
: uppercases stringsplit(s, d)
: splits string on delimiterreplace(s, old, new, n)
: replacesold
ins
withnew
n
times (setn
to-1
to replace all)
If you need more complex logic, consider using the lookup function described
below and awk
.
Example:
pairs:
jd: Jane Doe
fb: Frances Bar
email_template: '{{with replace .Name " " "-" -1}}{{toLower .}}{{end}}@hamster.local'
After invoking:
git duet jd fb
Then the configured email addresses will show up like this:
git config user.email
# -> [email protected]
git config duet.env.git-author-email
# -> [email protected]
git config duet.env.git-committer-email
# -> [email protected]
If there are any exceptions to either the default format or a provided
email_template
config var, explicitly setting email addresses by
initials is supported.
pairs:
jd: Jane Doe; jane
fb: Frances Bar
email:
domain: awesometown.local
email_addresses:
jd: [email protected]
Then Jane Doe's email will show up like this:
git solo jd
# ...
git config user.email
# -> [email protected]
Alternatively, if you have some other preferred way to look up email addresses by initials, name or username, just use that instead:
export GIT_DUET_EMAIL_LOOKUP_COMMAND="$HOME/bin/custom-ldap-thingy"
# ... do work
git duet jd fb
# ... observe emails being set via the specified executable
The initials, name, and username will be passed as arguments to the lookup executable. Anything written to standard output will be used as the email address:
$HOME/bin/custom-ldap-thingy 'jd' 'Jane Doe' 'jane'
# -> [email protected]
If nothing is returned on standard output, email construction falls back to the decisions described above.
Since there are multiple ways to determine an author or committer's
email, it is important to note the order of precedence used by git-duet
:
- Email lookup executable configured via the
GIT_DUET_EMAIL_LOOKUP_COMMAND
environmental variable - Email lookup from
email_addresses
in your configuration file - Custom email address from Go template defined in
email_template
in your configuration file (see http://golang.org/pkg/text/template/) - The username after the
;
, followed by@
and the configured email domain - The lower-cased first letter of the author or committer's first name,
followed by
.
followed by the lower-cased last name of the author or committer, followed by@
and the configured email domain (e.g.[email protected]
)
If you'd like to regularly remind yourself to set the solo or duet
initials, use git duet-pre-commit
in your pre-commit hook:
(in $REPO_ROOT/.git/hooks/pre-commit)
#!/bin/bash
exec git duet-pre-commit
The duet-pre-commit
command will exit with a non-zero status if the
cached author and committer settings are missing or stale. The default
staleness cutoff is 20 minutes,
but may be configured via the GIT_DUET_SECONDS_AGO_STALE
environmental variable,
which should be an integer of seconds, e.g.:
export GIT_DUET_SECONDS_AGO_STALE=60
# ... do work for more than a minute
git commit -v
# ... pre-commit hook fires
If you want to use the default hook (as shown above), install it while in your repo like so:
git duet-install-hook pre-commit
Don't worry if you forgot you already had a pre-commit
hook installed.
The git duet-install-hook pre-commit
command will refuse to overwrite it.
In order to have the author and committer properly set when committing
via JetBrains IDEs (RubyMine, Intellij, WebStorm, etc.), a git wrapper executable may be used to override any
executions of git commit
. Such an executable is available in the Git
Duet repository, and may be installed somewhere in your $PATH
like so:
curl -Ls -o ~/bin/jetbrains-git-wrapper https://raw.github.com/git-duet/git-duet/master/scripts/jetbrains-git-wrapper
chmod x ~/bin/jetbrains-git-wrapper
Given an install location of ~/bin/jetbrains-git-wrapper
as shown
above, you would then update your JetBrains IDE setting in
Preferences => Version Control => Git to set
Path to Git executable to the full path of
~/bin/jetbrains-git-wrapper
(with the ~
expanded).
See issue #8 for more details.
git-duet for VSCode, has been created by a member of the comunity. Please see the README for instructions/limitations. Please direct any issues to the extentions GitHub repo.
When adding incompatible changes to git-duet
, its major version will be raised.
However, you can try such future changes before updating the major version by setting the environment variable GIT_DUET_DEFAULT_UPDATE
git solo
without arguments removes the duet configuration.git duet
without arguments indicates that you must specify two or more initials.
Differences from ruby git-duet
- Running
git solo
orgit duet
with no initials outputs configuration in same format as when setting (env variables) - Does not set
user.name
anduser.email
(instead only sets namespaced variables) so thatgit commit
continues to work as normal - Template format is now Go's
text/template
See CONTRIBUTING.md.