loading...

g - a wrapper around git with additional feature extension

pcdevil profile image Attila Gonda ・6 min read

I am a longtime CLI fan. I love how the basic Unix commands can be piped through each other to create very robust automation with a few lines of codes.

I also love git, and I use it almost exclusively from command line. But after the learning curve (which was almost 10 years ago as my colleague introduced it to me), I realised I spend too much time on repetitive commands, and it would be nice to extend it with some handy features too!

GitHub logo pcdevil / g

a wrapper around git with additional feature extension.

g

A wrapper around git with additional feature extension See detailed functionality in the Features section!

Table of Contents


Install

Prerequirements

  • unix-like system
  • git (minimum: v1.7+, recommended: v2.23+)
  • zsh

Steps

  1. Clone the repositoy to a desired folder:

    $ git clone git@github.com:pcdevil/g.git ~/.g
  2. Bootstrap it in your .zshrc:

    # Export the destionation folder as G_DIR variable
    export G_DIR=$HOME/.g
    # Add bin folder to the PATH
    export PATH=$G_DIR/bin:$PATH
    # Load the init script of g
    source $G_DIR/g.plugin.zsh
  3. (Optional) Install gitconfig:

    $ git config --global --add include.path ~/.g/gitconfig

    See Predefined git config section for more information about it.

  4. Reload the terminal.

Predefined git config

The project provides an optional gitconfig to extend the default git config. Currently it only…

On that notion, I created a small tool called g! It's a long-term project of mine, and contains lot of subjective decisions based on how I use git. I've recently made it public and added better documentation to help anyone who would like to use it :)

In this post, I'll go through the functionalities to give a high level overview of it!

The entry-point

The git status shows information about the current working tree and it's one of the most used git commands. There are several good practices how to make it more accessible with git st, g st or gst aliases, but I found these solutions less intuitive, and made it even simpler: just run the g command and you'll get the same result while in non-git directories it calls the git help.

The entry-point is not only an alias for the git status, but it's also a wrapper around git and when it is called with argument, it acts according to them. Other functionalities are based on this behaviour.

Aliases

If we talk about how to remove the repetitiveness of git usage, most of the time the solution is the aliases. g provides aliases for common git commands, and some more advanced ones including git commit and git merge!

See the following table for some hints of the available aliases, while the full list is in the documentation:

Alias command git command
g br git branch
g cl git clone
g ci-a git commit --amend
g ci-as git commit --amend --no-edit
g d git diff
g me git merge
g me-ms git merge --no-ff --no-edit
g sh git show
g sw git switch
g sw-m git switch master

Moreover, there are also angular commit and advanced aliases: the first gives shortcut for a below discussed g feature, while the second consist of a one liner log (g l), set origin and push the branch to it (g ph-o), and alternate way to commit where only the message can be specified (g ci).

Added functionalities

While having shortcuts to existing commands is good, g gives several small utilities to enhance git usage - there are mostly related to bootstrap a repository.

Setting the git user

If you are working in an environment where custom e-mail domain is used, and you want to make sure your commits are bound to you nicely, you can use git config to set that up.

To achieve the effect you either have to do it locally in every single repository, or only one time globally. The first is troublesome (declaring the name and e-mail in numerous repositories), while the second option eliminates the portability of the config because and you can't put the .gitconfig in your dotfiles repository.

To resolve this, g set-user (or g su for short) comes in handy. You only set up the username, email and potentially signing key once as a url-matched configuration, then running the g su command sets it on the current repository with no hustle!

Example setup:

$ git config --global 'user.https://github.com.email' 'jakab@gipsz.eu'
$ git config --global 'user.https://example.org.email' 'jakab.gipsz@example.org'
$ git config --global 'user.https://example.org.name' 'jakab.gipsz'
$ git config --global 'user.https://example.org.signingkey' '0000000000000042'

Thanks to the url-matched keys, this won't have any effect on your standard config, allowing to symlink the same config file on several computers.

Example usage:

$ cd my-project
$ g set-user example.org
$ git config user.name
jakap.gipsz
$ git config user.email
jakab.gipsz@example.org
$ git config user.signingkey
0000000000000042

Here the when the command is called, the author changes accordingly to the config without stating your user information! Full details about how the matching config is found or how the signing key is set is in the documentation.

More delicate cloning

Oh My Zsh provides a take command which is basically mkdir && cd. The assumption behind the command is after creating a directory you usually want to use too. For the same reason this can be useful for git too, and g is equipped a command which mimics the original one: g take (g ta as alias) clones a repository then changes the current directory into it.

Running g take has one more benefit: it runs the g set-user command after the clone, which makes g take an end-to-end solution for cloning and immediate usage of a repository.

Change directory relative to the root

On bigger projects I often enter a deep sub-directory to perform an action, and afterwards I want to go back to the root directory.

For that, cd - can help you to change the current directory to the previous one, but if the current directory was a result of several changes, you need to remember how many changes were executed and pass it to it. Oh My Zsh helps a little bit (see cheatsheet), but the burden of remembering how much jumping occurred is still present.

The g cd command helps with this problem:

  • With no argument, it acts like cd and jumps to the home directory - which is interpreted as the root of the repository
  • While passing an argument changes the active directory relative to the root

Example for given argument:

$ cd ~/my-project
$ cd src/routes/api/categories
$ # do some actions
$ g cd test/routes
$ pwd
~/my-project/test/routes

Robust, extensive repository initialisation

Creating a new repository can be tedious:

  1. Init git
  2. Set up the gitignore (and then forget to include a crucial path by accident)
  3. Add readme and license files
  4. Add temp directory
  5. Create the first commit

To make it simpler, I created the g super-init (with g si as alias) command to automatise all these steps and make sure I don't forget anything!

The command has the same procedure as listed above with the following details:

  • The gitignore will be populated with:
    • The output of gitignore.io with Linux, MacOS, Windows, IntelliJ (+all), Vim, and Visual Studio Code related ignores.
    • The ignores related to the specified programming language.
    • a tmp directory and a .env file.
  • A README.md file will be created with the name of the project and the given description (if any).
  • A LICENSE.md file will be created with MIT license.
  • The commit will follow the Angular's Contributing Commit Message Format, in form of build(<projectname>): initial commit.

Additionally, the g super-init acts as g take, meaning it will change the current directory to the new project too.

The programming language and the descriptions can be specified during invocation.

Example usage:

$ g super-init my-project node "My awesome new project."
$ pwd
~/my-project

$ ls -1A
.git
.gitignore
LICENSE.md
README.md
tmp

$ tail -6 .gitignore
# End of https://www.toptal.com/developers/gitignore/api/linux,macos,windows,node,intellij+all,vim,visualstudiocode

### Project ###
/tmp/**
!/tmp/.gitkeep
.env

$ cat README.md
# my-project
My awesome new project.

## License
Available under the [MIT license](LICENSE.md).

Switching to the master main branch

The g switch-main (with an alias of g sw-m) command helps you to change the current branch to the main branch.

In case of needing to change the default branch, you can specify it in the git config with the init.defaultBranch variable - this added to git very recently and g switch-main uses it to line up with the standard usage of git.

Example to change the default branch locally:

$ cd my-project
$ git config --local branch.default gh-pages
$ git switch --create fix/footer-visibility
$ # work and commit
$ g switch-main
$ g # invoking git status
On branch gh-pages
Your branch is up-to-date with 'origin/gh-pages'.

nothing to commit, working tree clean

Closing thoughts

If you made it this far thank you for reading my article, and I hope you find some of these features useful.

Have a nice day!

Posted on by:

pcdevil profile

Attila Gonda

@pcdevil

An atheist cylon who is trying to make it in the world. he/him

Discussion

markdown guide
 

Nice. I'm not a fan of these things myself, because I'm generally not big on character-saving aliases, and I almost never do the things people think are common (like immediately changing to a directory you just created).

I was interested in changing to a directory relative to the project root though, and I put this in my shell startup script:

git() {
  if [ "$1" = "cd" ]; then
    shift
    cd "./$(git rev-parse --show-cdup)$*"
  else
    command git "$@"
  fi
}

You know what, though? I almost never use it!

 

I know what you talking about :)

Often time I use g cd without any argument and couple of weeks ago I started to use CDPATH (available in both zsh and bash):

I can specify regularly used repositories and then jump to their git root from anywhere! so right now i rarely type g cd at all and just type cd project (or simply project with zsh) :)

(edit: wording)