{"id":13688579,"url":"https://github.com/git-duet/git-duet","last_synced_at":"2025-05-01T19:31:08.489Z","repository":{"id":32674470,"uuid":"36262960","full_name":"git-duet/git-duet","owner":"git-duet","description":"Support for pairing with git","archived":false,"fork":false,"pushed_at":"2023-10-07T17:24:43.000Z","size":476,"stargazers_count":421,"open_issues_count":21,"forks_count":39,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-11-12T12:47:46.117Z","etag":null,"topics":["command-line-tool","git","git-duet","go","hacktoberfest","pair-programming","pairing"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/git-duet.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":"rafecolton"}},"created_at":"2015-05-26T00:56:36.000Z","updated_at":"2024-10-22T15:41:45.000Z","dependencies_parsed_at":"2024-01-17T06:08:54.717Z","dependency_job_id":"b78d2e35-fa30-4b92-9328-6acc438bf03d","html_url":"https://github.com/git-duet/git-duet","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/git-duet%2Fgit-duet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/git-duet%2Fgit-duet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/git-duet%2Fgit-duet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/git-duet%2Fgit-duet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/git-duet","download_url":"https://codeload.github.com/git-duet/git-duet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251932660,"owners_count":21667189,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-line-tool","git","git-duet","go","hacktoberfest","pair-programming","pairing"],"created_at":"2024-08-02T15:01:16.966Z","updated_at":"2025-05-01T19:31:07.535Z","avatar_url":"https://github.com/git-duet.png","language":"Shell","funding_links":["https://github.com/sponsors/rafecolton"],"categories":["Shell"],"sub_categories":[],"readme":"# git-duet\n\n[![Build Status](https://travis-ci.com/git-duet/git-duet.svg?branch=master)](https://travis-ci.com/git-duet/git-duet)\n\nPair harmoniously!  Working in a pair doesn't mean you've both lost your\nidentity.  `git-duet` helps with blaming/praising by using stuff that's already\nin `git` without littering your repo history with fictitious user identities. It\ndoes so by utilizing `git`s commit committer attributes to store the identity\nof the second pair.\n\nExample:\n```\n$ cat \u003c\u003c-EOF \u003e ~/.git-authors\nauthors:\n  jd: Jane Doe; jane\n  fb: Frances Bar\nemail:\n  domain: awesometown.local\nEOF\n\n$ git duet jd fb\nGIT_AUTHOR_NAME='Jane Doe'\nGIT_AUTHOR_EMAIL='jane@awesometown.local'\nGIT_COMMITTER_NAME='Frances Bar'\nGIT_COMMITTER_EMAIL='f.bar@awesometown.local'\n\n$ touch foo\n\n$ git duet-commit -a -m 'initial commit'\n[master (root-commit) ce78563] initial commit\n Author: Jane Doe \u003cjane@awesometown.local\u003e\n 1 file changed, 0 insertions(+), 0 deletions(-)\n create mode 100644 foo\n\n$ git show --format=full\ncommit ce7856371e3e3a6d05f1b66af96f086df071e783\nAuthor: Jane Doe \u003cjane@awesometown.local\u003e\nCommit: Frances Bar \u003cf.bar@awesometown.local\u003e\n\n    initial commit\n\n    Signed-off-by: Frances Bar \u003cf.bar@awesometown.local\u003e\n\ndiff --git a/foo b/foo\nnew file mode 100644\nindex 0000000..e69de29\n```\n\n## Installation\n\nOptions:\n\n1. See releases page for binary downloads, place in your `$PATH`.\n1. Install using Homebrew from the [git-duet homebrew tap](https://github.com/git-duet/homebrew-tap) (`brew install git-duet/tap/git-duet`)\n1. Build from source: `go install github.com/git-duet/git-duet/...@latest`.\n   This will put the binaries in `$GOBIN`, if set, or `$GOPATH/bin` (see `go\n   help install` for more details).\n\n## Usage\n\n### Setup\n\nMake an authors file with email domain, or if you're already using\n[git pair](https://github.com/pivotal/git_scripts), just symlink your\n`~/.pairs` file over to `~/.git-authors`. You can also set a repository\nspecific authors file by placing a `.git-authors` file at the root\nof your `git` repository.\n\n``` yaml\nauthors:\n  jd: Jane Doe; jane\n  fb: Frances Bar\nemail:\n  domain: awesometown.local\n```\n\n`git duet` will use the `git pair` YAML structure if it has to (the\ndifference is the top-level key being `pairs` instead of `authors`) e.g.:\n\n``` yaml\npairs:\n  jd: Jane Doe; jane\n  fb: Frances Bar\nemail:\n  domain: awesometown.local\n```\n\nIf you want your authors file to live somewhere else, just tell\n`git-duet` about it via the `GIT_DUET_AUTHORS_FILE` environmental\nvariable, e.g.:\n\n``` bash\nexport GIT_DUET_AUTHORS_FILE=$HOME/.secret-squirrel/git-authors\n# ...\ngit duet jd am\n```\n\n### Workflow\n\nSet two authors (pairing):\n\n``` bash\ngit duet jd fb\n```\n\nSet one author (soloing):\n\n``` bash\ngit solo jd\n```\n\nSet arbitrary number of authors:\n\n```bash\ngit as jd # works\ngit as jd fb rb # also works\n```\n\nCommitting (needed to set `--signoff` and export environment variables):\n\n``` bash\n$ git duet-commit -v [any other git options]\n```\n\nReverting (needed to set `--signoff` and export environment variables):\n\n``` bash\ngit duet-revert -v [any other git options]\n```\n\nMerging (needed to set `--signoff` and export environment variables):\n\n``` bash\ngit duet-merge -v [any other git options]\n```\n\nRebasing (resets the committer to the committer of the current pair):\n\n```bash\ngit rebase -i --exec 'git duet-commit --amend'\n```\n\nSuggested aliases:\n\n```\ndci = duet-commit\ndrv = duet-revert\ndmg = duet-merge\ndrb = rebase -i --exec 'git duet-commit --amend'\n```\n\n**Note:** `git-duet` only sets the configuration to use via `git duet-commit`,\n`git duet-revert`, and `git duet-merge`. Using `git solo` (or `git duet`) will\nnot effect the configured `user.name` and `user.email`.  This allows `git\ncommit` to be used normally outside of `git-duet`. You can set an environment\nvariable, `GIT_DUET_SET_GIT_USER_CONFIG` to `1` to override this behavior and\nset the `user.name` and `user.email` fields.\n\nAnother option for `git rebase`ing with `git-duet` is to use\n[git-duet-rebase.sh](scripts/git-duet-rebase.sh) courtesy of @pivotaljohn. This\nscript uses `git filter-branch` to update the names of all of the\nauthors/committers to the currently set pair. It acts on your active branch\n(using the passed ref as the start point).\n\n### \"Co-authored-by\" trailer support\n\n:warning: If you use `git commit -v` with `git \u003c 2.14.0` you'll find that the `Co-authored-by` trailer is mistakenly\nadded after the verbose commit output. Please upgrade `git` if you encounter this issue.\n\nSet the environment variable `GIT_DUET_CO_AUTHORED_BY` to `1` if you want\nto have a \"Co-authored-by\" trailer for each co-author in your commit message\nrather than having a \"Signed-off-by\" trailer for the committer.\n\nIf `GIT_DUET_CO_AUTHORED_BY` is set, `git duet` will install a prepare-commit-msg\nhook file into the local repository by default. If, in addition, `GIT_DUET_GLOBAL` is set,\n`git-duet` will instead install the prepare-commit-msg hook file into `git config --global init.templatedir`.\nIf the value `git config --global init.templatedir` is not set, `git-duet` will set it\nto `$HOME/.git-template`.\n\n`GIT_DUET_CO_AUTHORED_BY` implicitly sets `GIT_DUET_SET_GIT_USER_CONFIG`\nso that `git duet` and `git solo` set the author for normal git commands.\n\nThe common workflow is as follows:\n- run `git duet` (which will install the prepare-commit-msg hook)\n- if you have `GIT_DUET_GLOBAL` set, run `git init` once in existing repos\nso that the hook file gets copied from the `init.templatedir`\n- thereafter, use the normal git commands (i.e. `git commit`, `git merge` rather than\nthe git-duet-subcommands `git duet-commit`, `git duet-merge`)\n- the prepare-commit-msg hook will append a `Co-authored-by` trailer for each co-author\n\nIf `GIT_DUET_ROTATE_AUTHOR` is set in addition to `GIT_DUET_CO_AUTHORED_BY`, `git-duet` will install a post-commit hook file\nwhich will swap author and co-author after every commit.\n\nWhen amending a commit and the co-author has changed, a new `Co-authored-by` trailer will get appended for\nthat co-author. In order to avoid duplicate `Co-authored-by` trailers (i.e. trailers with the same co-author),\nset `git config [--global] trailer.ifexists addIfDifferent` to  override the default value `addIfDifferentNeighbor`.\n\nIf you want to opt out of this feature, unsetting `GIT_DUET_CO_AUTHORED_BY` is not sufficient.\nYou also need to manually delete the prepare-commit-msg (and post-commit) hook file in your repo.\n\n### Global Config Support\n\nIf you're jumping between projects and don't want to think about\nmanaging them all individually, you can operate on the global git\nconfig:\n\n``` bash\ngit solo -g jd\ngit duet --global jd fb\n```\n\nIf you do this habitually, you can set the `GIT_DUET_GLOBAL` environment\nvariable to `true` to always operate on the global git config:\n\n``` bash\nexport GIT_DUET_GLOBAL=true # consider adding this to your shell profile\ngit solo jd\n```\n\nYou can also set it to `false` to always operate on the local config, even if\nthe global flag is used.\n\n### Rotating author/committer support\n\nSometimes while pairing you want to share the authorship love between the\npairs. If you set `GIT_DUET_ROTATE_AUTHOR=1` it will swap the author and\ncommitter (if there is one) on every `git duet-commit` (after the commit).\n\nThis operates on whichever config the authorship was drawn from (e.g. if the\nauthor/committer was set in the repository git config, it will rotate these\neven if `GIT_DUET_GLOBAL` is specified).\n\n### Mobbing support\n\nGit duet supports more than 2 people working at a time by specifying more sets\nof initials:\n\n``` bash\ngit duet jd fb zp\n```\n\nIf you do not set `GIT_DUET_ROTATE_AUTHOR`, then git-duet will use jd and fb\nas the author and committer respectively. If you have `GIT_DUET_ROTATE_AUTHOR`\nset then git-duet will rotate with each commit. The first commit will have\njd as the author, and fb as the committer. The second commit will have fb\nas the author and zp as the committer and so on.\n(*Note:* This feature uses `,` as the delimiter which will fail to parse\nproperly if the user's name or e-mail address contains a `,`.)\n\nAdditionally, if you set `GIT_DUET_ALLOW_MULTIPLE_COMMITTERS`, then git-duet\nwill add a sign-off trailer in the commit message for every committer that\nis specified after the author:\n\nExample:\n```\n$ git show --format=full\ncommit ce7856371e3e3a6d05f1b66af96f086df071e783\nAuthor: Jane Doe \u003cjane@awesometown.local\u003e\nCommit: Frances Bar \u003cf.bar@awesometown.local\u003e\n\n    initial commit\n\n    Signed-off-by: Frances Bar \u003cf.bar@awesometown.local\u003e\n    Signed-off-by: Zubaz Shirts \u003cz.shirts@pika.info.local\u003e\n\ndiff --git a/foo b/foo\nnew file mode 100644\nindex 0000000..e69de29\n```\n\n### Email Configuration\n\nBy default, email addresses are constructed from the first initial and\nlast name ( *or* optional username after a `;`) plus email domain, e.g.\nwith the following authors file:\n\n``` yaml\npairs:\n  jd: Jane Doe; jane\n  fb: Frances Bar\nemail:\n  domain: eternalstench.bog.local\n```\n\nAfter invoking:\n\n``` bash\ngit duet jd fb\n```\n\nThen the configured email addresses will show up like this:\n\n``` bash\ngit config user.email\n# -\u003e jane@eternalstench.bog.local\ngit config duet.env.git-author-email\n# -\u003e jane@eternalstench.bog.local\ngit config duet.env.git-committer-email\n# -\u003e f.bar@eternalstench.bog.local\n```\n\nA custom email template may be provided via the `email_template` config\nvariable.  The template should be a valid Go template string (see\nhttp://golang.org/pkg/text/template/). The object passed in has `.Name`,\n`.Username`, and `.Initials`.\n\nAdditional functions available to template:\n- `toLower(s)`: lowercases string\n- `toUpper(s)`: uppercases string\n- `split(s, d)`: splits string on delimiter\n- `replace(s, old, new, n)`: replaces `old` in `s` with `new` `n` times (set `n` to `-1` to replace all)\n\nIf you need more complex logic, consider using the lookup function described\nbelow and `awk`.\n\nExample:\n\n``` yaml\npairs:\n  jd: Jane Doe\n  fb: Frances Bar\nemail_template: '{{with replace .Name \" \" \"-\" -1}}{{toLower .}}{{end}}@hamster.local'\n```\n\nAfter invoking:\n\n``` bash\ngit duet jd fb\n```\n\nThen the configured email addresses will show up like this:\n\n``` bash\ngit config user.email\n# -\u003e jane-doe@hamster.local\ngit config duet.env.git-author-email\n# -\u003e jane-doe@hamster.local\ngit config duet.env.git-committer-email\n# -\u003e frances-bar@hamster.local\n```\n\nIf there are any exceptions to either the default format or a provided\n`email_template` config var, explicitly setting email addresses by\ninitials is supported.\n\n``` yaml\npairs:\n  jd: Jane Doe; jane\n  fb: Frances Bar\nemail:\n  domain: awesometown.local\nemail_addresses:\n  jd: jane@awesome.local\n```\n\nThen Jane Doe's email will show up like this:\n\n``` bash\ngit solo jd\n# ...\ngit config user.email\n# -\u003e jane@awesome.local\n```\n\nAlternatively, if you have some other preferred way to look up email\naddresses by initials, name or username, just use that instead:\n\n``` bash\nexport GIT_DUET_EMAIL_LOOKUP_COMMAND=\"$HOME/bin/custom-ldap-thingy\"\n# ... do work\ngit duet jd fb\n# ... observe emails being set via the specified executable\n```\n\nThe initials, name, and username will be passed as arguments to the\nlookup executable.  Anything written to standard output will be used as\nthe email address:\n\n``` bash\n$HOME/bin/custom-ldap-thingy 'jd' 'Jane Doe' 'jane'\n# -\u003e doej@behemoth.company.local\n```\n\nIf nothing is returned on standard output, email construction falls back\nto the decisions described above.\n\n#### Order of Precedence\n\nSince there are multiple ways to determine an author or committer's\nemail, it is important to note the order of precedence used by `git-duet`:\n\n1. Email lookup executable configured via the\n   `GIT_DUET_EMAIL_LOOKUP_COMMAND` environmental variable\n2. Email lookup from `email_addresses` in your configuration file\n3. Custom email address from Go template defined in `email_template` in\n   your configuration file (see http://golang.org/pkg/text/template/)\n4. The username after the `;`, followed by `@` and the configured email\n   domain\n5. The lower-cased first letter of the author or committer's first name,\n   followed by `.` followed by the lower-cased last name of the author\nor committer, followed by `@` and the configured email domain (e.g.\n`f.bar@baz.local`)\n\n### Git hook integration\n\nIf you'd like to regularly remind yourself to set the solo or duet\ninitials, use `git duet-pre-commit` in your pre-commit hook:\n\n*(in $REPO_ROOT/.git/hooks/pre-commit)*\n\n``` bash\n#!/bin/bash\nexec git duet-pre-commit\n```\n\nThe `duet-pre-commit` command will exit with a non-zero status if the\ncached author and committer settings are missing or stale.  The default\nstaleness cutoff is [20 minutes](http://en.wikipedia.org/wiki/Pomodoro_Technique),\nbut may be configured via the `GIT_DUET_SECONDS_AGO_STALE` environmental variable,\nwhich should be an integer of seconds, e.g.:\n\n``` bash\nexport GIT_DUET_SECONDS_AGO_STALE=60\n# ... do work for more than a minute\ngit commit -v\n# ... pre-commit hook fires\n```\n\nIf you want to use the default hook (as shown above), install it while\nin your repo like so:\n\n``` bash\ngit duet-install-hook pre-commit\n```\n\nDon't worry if you forgot you already had a `pre-commit` hook installed.\nThe `git duet-install-hook pre-commit` command will refuse to overwrite it.\n\n### JetBrains IDE integration\n\nIn order to have the author and committer properly set when committing\nvia JetBrains IDEs (RubyMine, Intellij, WebStorm, etc.), a git wrapper executable may be used to override any\nexecutions of `git commit`.  Such an executable is available in the Git\nDuet repository, and may be installed somewhere in your `$PATH` like so:\n\n``` bash\ncurl -Ls -o ~/bin/jetbrains-git-wrapper https://raw.github.com/git-duet/git-duet/master/scripts/jetbrains-git-wrapper\nchmod +x ~/bin/jetbrains-git-wrapper\n```\n\nGiven an install location of `~/bin/jetbrains-git-wrapper` as shown\nabove, you would then update your JetBrains IDE setting in\n*Preferences* =\u0026gt; *Version Control* =\u0026gt; *Git* to set\n**Path to Git executable** to the full path of\n `~/bin/jetbrains-git-wrapper` (with the `~` expanded).\nSee issue #8 for more details.\n\n### VSCode extention\n\n[git-duet for VSCode](https://marketplace.visualstudio.com/items?itemName=PhilAlsford.git-duet-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](https://github.com/philals/git-duet-vscode). \n\n### Incompatible future updates\nWhen adding incompatible changes to `git-duet`, its major version will be raised. \n\nHowever, you can try such future changes before updating the major version by setting the environment variable `GIT_DUET_DEFAULT_UPDATE`\n- `git solo` without arguments removes the duet configuration. \n- `git duet` without arguments indicates that you must specify two or more initials.\n\n## Differences from ruby [`git-duet`](http://github.com/meatballhat/git-duet)\n- Running `git solo` or `git duet` with no initials outputs configuration in\n  same format as when setting (env variables)\n- Does not set `user.name` and `user.email` (instead only sets namespaced\n  variables) so that `git commit` continues to work as normal\n- Template format is now Go's `text/template`\n\n### Developing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgit-duet%2Fgit-duet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgit-duet%2Fgit-duet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgit-duet%2Fgit-duet/lists"}