-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
1,322 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
# gitprompt | ||
|
||
gitprompt is a configurable, fast and zero-dependencies* way of getting the | ||
current git status to be displayed in the `PROMPT`. | ||
|
||
Displays: | ||
|
||
- Current branch / sha1 | ||
- Untracked files | ||
- Modified files | ||
- Staged files | ||
- Commits behind / ahead remote | ||
|
||
When executed, gitprompt gets the git status of the current working directory | ||
then prints it according to the format specified. If the current working | ||
directory is not part of a git repository, gitprompt | ||
exits with code `0` and no output. | ||
|
||
`*` git is required | ||
|
||
## Configuration | ||
|
||
The output is configured with `-format` or the `GITPROMPT_FORMAT` environment | ||
variable. If both are set, the flag takes precedence. | ||
|
||
A very bare-bones format can look like this: | ||
|
||
``` | ||
gitprompt -format="%h >%s ↓%b ↑%a @%c +%m %u" | ||
# or | ||
export GITPROMPT_FORMAT="%h >%s ↓%b ↑%a @%c +%m %u" | ||
gitprompt | ||
``` | ||
|
||
Characters that don't have a special meaning are printed as usual _(unicode | ||
characters are fine, go crazy with emojis if that's your thing)_. | ||
|
||
### Data | ||
|
||
Various data from git can be displayed in the output. Data tokens are prefixed | ||
with `%`: | ||
|
||
| token | explanation | | ||
|-------|-----------------------------------| | ||
| `%h` | Current branch or sha1 | | ||
| `%s` | Number of files staged | | ||
| `%b` | Number of commits behind remote | | ||
| `%a` | Number of commits ahead of remote | | ||
| `%c` | Number of conflicts | | ||
| `%m` | Number of files modified | | ||
| `%u` | Number of untracked files | | ||
|
||
Normally `%h` displays the current branch (`master`) but if you're detached | ||
from `HEAD`, it will display the current sha1. Only first 7 characters of the | ||
sha1 are displayed. | ||
|
||
### Colors | ||
|
||
The color can be set with color tokens, prefixed with `#`: | ||
|
||
| token | color | | ||
|-------|-------------------| | ||
| `#k` | Black | | ||
| `#r` | Red | | ||
| `#g` | Green | | ||
| `#y` | Yellow | | ||
| `#b` | Blue | | ||
| `#m` | Magenta | | ||
| `#c` | Cyan | | ||
| `#w` | White | | ||
| `#K` | Highlight Black | | ||
| `#R` | Highlight Red | | ||
| `#G` | Highlight Green | | ||
| `#Y` | Highlight Yellow | | ||
| `#B` | Highlight Blue | | ||
| `#M` | Highlight Magenta | | ||
| `#C` | Highlight Cyan | | ||
| `#W` | Highlight White | | ||
|
||
The color is set until another color overrides it, or a group ends (see below). | ||
If a color was set when gitprompt is done, it will add a color reset escape | ||
code at the end, meaning text after gitprompt won't have the color applied. | ||
|
||
### Text attributes | ||
|
||
The text attributes can be set with attribute tokens, prefixed with `@`: | ||
|
||
| token | attribute | | ||
|-------|-----------------------| | ||
| `@b` | Set bold | | ||
| `@B` | Clear bold | | ||
| `@f` | Set faint/dim color | | ||
| `@F` | Clear faint/dim color | | ||
| `@i` | Set italic | | ||
| `@I` | Clear italic | | ||
|
||
As with colors, if an attribute was set when gitprompt is done, an additional | ||
escape code is automatically added to clear it. | ||
|
||
### Groups | ||
|
||
Groups can be used for adding logic to the format. A group's output is only | ||
printed if at least one item in the group has data. | ||
|
||
| token | action | | ||
|-------|-------------| | ||
| `[` | start group | | ||
| `]` | end group | | ||
|
||
Consider the following: | ||
|
||
``` | ||
%h[ %a][ %m] | ||
``` | ||
|
||
With `head=master, ahead=3, modified=0`, this will print `master 3` since there | ||
were not modified files. Note the space that's included in the group, if | ||
spacing should be added when the group is present, the spacing should be added | ||
to the group itself. | ||
|
||
Colors and attributes are also scoped to a group, meaning they won't leak | ||
outside so there's no need to reset colors: | ||
|
||
``` | ||
[#r behind: %b] - [#g ahead: %a] | ||
``` | ||
|
||
This prints `behind` in red, `-` without any formatting, and `ahead` in green. | ||
|
||
``` | ||
#c%h[ >%s][ ↓%b ↑%a] | ||
``` | ||
|
||
This prints the current branch/sha1 in cyan, then number of staged files (if | ||
not zero), then commits behind and ahead (if both are not zero). This allows | ||
for symmetry, if it's desired to show `master >1 ↓0 ↑2` instead of `master >1 | ||
↑2`. | ||
|
||
### Complete example | ||
|
||
Putting everything together, a complex format may look something like this: | ||
|
||
``` | ||
gitprompt -format="#B(@b#R%h[#y >%s][#m ↓%b ↑%a][#r x%c][#g +%m][#y ϟ%u]#B)" | ||
``` | ||
|
||
- `(` in highlight blue | ||
- Current branch/sha1 in bold red | ||
- If there are staged files, number of staged files in yellow, prefixed with `>` | ||
- If there are commits ahead or behind, show them with arrows in magenta | ||
- If there are conflicts, show them in red, prefixed with `x` | ||
- If files have been modified since the previous commit, show `+3` for 3 modified files | ||
- If files are untracked (added since last commit), show a lightning and the number in yellow | ||
- `)` in highlight blue | ||
- _any text printed after gitprompt will have all formatting cleared_ | ||
|
||
## Download | ||
|
||
### MacOS | ||
|
||
Install [Homebrew] | ||
|
||
`TODO(akupila): brew cask` | ||
|
||
## Configure your shell | ||
|
||
### zsh | ||
|
||
gitprompt needs to execute as a part the `PROMPT`, add this to your `~/.zshrc`: | ||
|
||
``` | ||
PROMPT='$(gitprompt)' | ||
``` | ||
|
||
Now reload the config (`source ~/.zshrc`) and gitprompt should show up. Of | ||
course you're free to add anything else here too, just execute `gitprompt` | ||
where you want the status. | ||
|
||
|
||
|
||
[Homebrew]: https://brew.sh/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"os" | ||
|
||
"github.com/akupila/gitprompt" | ||
) | ||
|
||
const defaultFormat = "#B([@b#R%h][#y ›%s][#m ↓%b][#m ↑%a][#r x%c][#g +%m][#y %u]#B)" | ||
|
||
type formatFlag struct { | ||
set bool | ||
value string | ||
} | ||
|
||
func (f *formatFlag) Set(v string) error { | ||
f.set = true | ||
f.value = v | ||
return nil | ||
} | ||
|
||
func (f *formatFlag) String() string { | ||
if f.set { | ||
return f.value | ||
} | ||
|
||
if envVar := os.Getenv("GITPROMPT_FORMAT"); envVar != "" { | ||
return envVar | ||
} | ||
|
||
return defaultFormat | ||
} | ||
|
||
var format formatFlag | ||
|
||
func main() { | ||
flag.Var(&format, "format", formatHelp()) | ||
flag.Parse() | ||
gitprompt.Exec(format.String()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/akupila/gitprompt" | ||
) | ||
|
||
var exampleStatus = &gitprompt.GitStatus{ | ||
Branch: "master", | ||
Sha: "0455b83f923a40f0b485665c44aa068bc25029f5", | ||
Untracked: 1, | ||
Modified: 2, | ||
Staged: 3, | ||
Conflicts: 4, | ||
Ahead: 5, | ||
Behind: 6, | ||
} | ||
|
||
var formatHelp = func() string { | ||
return strings.TrimSpace(fmt.Sprintf(` | ||
How to format output | ||
Default format is: %q | ||
Example result: %s | ||
Data: | ||
%%h Current branch or SHA1 | ||
%%s Number of files staged | ||
%%b Number of commits behind remote | ||
%%a Number of commits ahead of remote | ||
%%c Number of conflicts | ||
%%m Number of files modified | ||
%%u Number of untracked files | ||
Colors: | ||
#k Black | ||
#r Red | ||
#g Green | ||
#y Yellow | ||
#b Blue | ||
#m Magenta | ||
#c Cyan | ||
#w White | ||
#K Highlight Black | ||
#R Highlight Red | ||
#G Highlight Green | ||
#Y Highlight Yellow | ||
#B Highlight Blue | ||
#M Highlight Magenta | ||
#C Highlight Cyan | ||
#W Highlight White | ||
Text attributes: | ||
@b Set bold | ||
@B Clear bold | ||
@f Set faint/dim color | ||
@F Clear faint/dim color | ||
@i Set italic | ||
@I Clear italic | ||
`, defaultFormat, gitprompt.Print(exampleStatus, defaultFormat))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package gitprompt | ||
|
||
import ( | ||
"bytes" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
type formatter struct { | ||
color uint8 | ||
currentColor uint8 | ||
attr uint8 | ||
currentAttr uint8 | ||
} | ||
|
||
func (f *formatter) setColor(c uint8) { | ||
f.color = c | ||
} | ||
|
||
func (f *formatter) clearColor() { | ||
f.color = 0 | ||
} | ||
|
||
func (f *formatter) setAttribute(a uint8) { | ||
f.attr |= (1 << a) | ||
} | ||
|
||
func (f *formatter) clearAttribute(a uint8) { | ||
f.attr &= ^(1 << a) | ||
} | ||
|
||
func (f *formatter) attributeSet(a uint8) bool { | ||
return (f.attr & (1 << a)) != 0 | ||
} | ||
|
||
func (f *formatter) clearAttributes() { | ||
f.attr = 0 | ||
} | ||
|
||
func (f *formatter) printANSI(b *bytes.Buffer) { | ||
if f.color == f.currentColor && f.attr == f.currentAttr { | ||
return | ||
} | ||
b.WriteString("\x1b[") | ||
if f.color == 0 && f.attr == 0 { | ||
// reset all | ||
b.WriteString("0m") | ||
f.currentColor = 0 | ||
f.currentAttr = 0 | ||
return | ||
} | ||
mm := []string{} | ||
aAdded, aRemoved := attrDiff(f.currentAttr, f.attr) | ||
if len(aRemoved) > 0 { | ||
mm = append(mm, "0") | ||
var i uint8 = 1 | ||
for ; i < 8; i++ { | ||
if f.attributeSet(i) { | ||
mm = append(mm, strconv.Itoa(int(i))) | ||
} | ||
} | ||
} else if len(aAdded) > 0 { | ||
for _, a := range aAdded { | ||
mm = append(mm, strconv.Itoa(int(a))) | ||
} | ||
} | ||
if f.color != f.currentColor || len(aRemoved) > 0 { | ||
mm = append(mm, strconv.Itoa(int(f.color))) | ||
} | ||
b.WriteString(strings.Join(mm, ";")) | ||
b.WriteString("m") | ||
f.currentColor = f.color | ||
f.currentAttr = f.attr | ||
|
||
} | ||
|
||
func attrDiff(a, b uint8) ([]uint8, []uint8) { | ||
added := []uint8{} | ||
removed := []uint8{} | ||
var i uint8 | ||
for ; i < 8; i++ { | ||
inA := (a & (1 << i)) != 0 | ||
inB := (b & (1 << i)) != 0 | ||
if inA && inB { | ||
continue | ||
} | ||
if inB { | ||
added = append(added, i) | ||
continue | ||
} | ||
if inA { | ||
removed = append(removed, i) | ||
} | ||
} | ||
return added, removed | ||
} |
Oops, something went wrong.