A productive command-line Git workflow for indie app developers

Takuya Matsuyama - Nov 11 '20 - - Dev Community

Hi, it's Takuya.

Git is an essential tool for managing your codebase and change history, even if you are developing an app alone.
I'm a solo developer of Inkdrop which is a plain-text Markdown note-taking app for desktop and mobile platforms.
This app is built on top of Electron for desktop and React Native for mobile.
I'm basically working on terminal with tmux and vim, as I shared my vim setup for coding JavaScript here:

In this article, I'm going to explain my Git workflow.
It is for those who already know the basics of Git.

I also prefer using git on terminal.
I tried various GUI git clients out there but couldn’t get used to any of them because I’m basically happy with the Git command at the moment.
But I’m not saying that everyone should use Git on terminal.
If you already have a favorite Git GUI client app, please go ahead and use it.

My Git setup

  • Command aliases for faster command inputs
  • Commitizen for human and machine-friendly commit messages
  • tig for Git TUI
  • fugitive for using Git on Vim

My Git config is published here:

GitHub logo craftzdog / dotfiles-public

My personal dotfiles

Takuya's dotfiles

Warning: Don’t blindly use my settings unless you know what that entails. Use at your own risk!

Contents

  • vim (NeoVim) config
  • tmux config
  • git config

How to use

Watch the video tutorial.




I'm going to explain more in detail.

Mission: Refurbishing my homepage

I have a good side project in order to show you my Git workflow.
It's been 3 or 4 years since I've built the current my homepage.
So, I'd like to refurbish it and was thinking of its new design.

By the way, do you know MagicaVoxel?
It allows you to make low-poly voxel arts by combining 3D boxes something like Minecraft, characters, towns, kitchens, and things like that without effort.
I built a dog with it:

voxel dog

I also built a "Dev As Life" logo with it so that I can use them for my YouTube channel.
In this renewal, I'd like to use those voxel arts for my new homepage.
But it's not interesting to just put them as pre-rendered images.
I think it would be more fun if they are dynamically rendered and you can move them around.
So, I'm working on it now.

Create and register a git repository for VoxelDog

I successfully exported the model from MagicaVoxel and got it to render on browser.
Now, I'm gonna show you how I create a git repository for this project and manage the code on it.

Here is the voxel dog project.
Let me show you how it looks like right now.
Cute, isn't it? :)

sc02

It is a dog using a laptop on a standing desk.
It's not perfect yet - the color is too light and the shadows are shaggy.
But it just works fine for now, as you can see, you got the camera moving around.
I'm happy with it. I'm gonna store it in a git repository.
Here, I created the one:

GitHub logo craftzdog / voxel-dog

A cute 3D dog rendered with three.js

Voxel Dog using three.js

A 3D dog model rendered with three.js for reproducing the MagicaVoxel's soft shadows.

This project includes 3 rendering examples.

1. Wavefront OBJ

It loads .obj and .mtl files that are exported from MagicaVoxel, using OBJLoader2 and MTLLoader. It doesn't produce any shadows out of the box, so it looks so flat.

Wavefront

2. Percentage closer soft shadows

It renders soft shadows dynamically using a PCSS shader. It works well but its performance is not well, expecially on mobile devices. Besides, the shadows are a bit shaggy.

PCSS

3. Baked textures

The shadows are baked with Cycle engine of Blender beforehand, so the rendering performance can be significantly improved. It achieves the beautiful soft shadows just like MagicaVoxel renderer! But as a result, the glTF file size got quite large due to the high quality textures.

Baked textures


© Takuya Matsuyama. MIT License.




Use Git aliases to shorten commands

When you run git, you typically type 'git'.
But it is kind of annoying to type 'git' every time, so I set an alias for git as 'g'.

  • In fish shell, you can do it with alias g git.
  • In zsh or bash, it's alias g='git'.

Then, let's make a local Git repository.

sc03

It looks nice.
Next, I'm gonna register the GitHub repository as the remote origin:

g remote add origin git@github.com:craftzdog/voxel-dog.git
Enter fullscreen mode Exit fullscreen mode

Okay, now it's been registered.

You run the Git command many times a day.
Say, if you run it 10 times a day, it's three hundred times a month, and then it's more than 3 thousand times a year.
So, you can't type 'git' every time right?
Aliasing 'git' with 'g' helps you type it quickly.

Next, the status command is aliased to 'st' so you can avoid typing 'g status'.
The location of the configuration for this is in the .gitconfig file, at alias section here:

sc04

In this way, status is aliased to st.
I have a lot of other aliases, as you can see, like diff, checkout, commit, push, pull, branch, and so on.
I define aliases for commands that I often use in from two to four letters.
I will explain other aliases later.

None of the files are staged right now. Let's add them all:

g add .
Enter fullscreen mode Exit fullscreen mode

Now that all the files have been staged.

Use commitizen to input nice commit messages

Next, to commit them, you can type g commit as usual,
then you get an editor launched and you can input a commit message. But it's also annoying to do it many times.
So, I'm using a helper tool which is called commitizen.
If you type 'g cz', as you can see, it asks you to select a commit type from the list.

sc05

For this commit, I select 'feat' as it's the first time to commit.
Next, it asks to select what scope this change is:

sc06

And, this time I specify * because it is the first commit and it's related to the whole scope.
Next is to write a short description of this change.
So, it's gonna be like 'Initial commit'.
And then, next is a long description, about the detail of this change. It's optional. As this change doesn't have anything to describe, I leave it empty.
Are there any breaking changes? No.
Does this change affect any open issues? No.

sc07

Then, the change has been committed.
Well, what the commit message looks like is this:

feat(*): initial commit
Enter fullscreen mode Exit fullscreen mode

It's very handy because you don't have to think about the commit message format.
Because commit messages tend to be slipshod.
But, it helps you input well-formatted messages without effort.
All you have to do is to select from the pre-defined commit types. And you can input good commit messages without thinking too much about them.
It's useful not only for teams but also for solo developers.
For example, in my case, when I write release notes of my app Inkdrop by checking the commit history,
here is the desktop version's commit history.

sc08

As you can see, you can easily and quickly understand each commit.
I often forget what I did soon.
I totally don't remember what I did if I see a change that's been made 3 or 4 weeks ago.
But in this way, the commit messages are obvious to understand what type and what scope of the change and even what change you made in detail.
It helps you a lot.

How to quickly view commit logs

Then, back to the voxel dog project,
Speaking of the command that I run like git hist, it's also an alias:

[alias]
  hist = log --pretty=format:\"%Cgreen%h %Creset%cd %Cblue[%cn] %Creset%s%C(yellow)%d%C(reset)\" --graph --date=relative --decorate --all
Enter fullscreen mode Exit fullscreen mode

So, I have hist alias with many options in order to make commit messages beautiful.
You got another alias named llog:

[alias]
  llog = log --graph --name-status --pretty=format:\"%C(red)%h %C(reset)(%cd) %C(green)%an %Creset%s %C(yellow)%d%Creset\" --date=relative
Enter fullscreen mode Exit fullscreen mode

It displays not only commit logs but also which files have been changed.
In my Inkdrop project, you can quickly know which files have been changed:

sc09

The log command is very flexible to format the output.
It's very powerful. I recommend you to check it out on the web and to find your favorite log format.

When you run df alias, you get a commit history:

sc10

And say, you wanna know the detail of this commit of the sidebar change, then if you chose it, it shows the diff:

sc11

The alias looks like this:

[alias]
  df = "!git hist | peco | awk '{print $2}' | xargs -I {} git diff {}^ {}"
Enter fullscreen mode Exit fullscreen mode

It runs git hist and passes the output to peco, which is a command-line that allows you to select a line from stdin, then it passes the selected line to awk to extract the commit hash, then it runs git diff for it.
In a nutshell, it allows you to see a diff of an arbitrary commit that you chose from the commit history.
So you can quickly look into the detail of the commit.

tig - TUI for git

You also got tig command.
It's a reversed name of Git.
With this command, you can interactively select a commit.
It supports vim-like keybindings like this, so it's nice for vimmers.
For example, if you chose this commit by hitting enter key,
it splits the pane and displays the diff here.

sc12

So, I usually use tig to look into the recent change logs when writing release notes for my app.
You can check other commits without running the command many times.
So, it improves your workflow.

Push it to the remote repository

Well, let's push it to the remote repository.
Now that you have one commit in the local repository.
So, I'm gonna push it to the remote:

g ps
Enter fullscreen mode Exit fullscreen mode

Well, it's done.
Looks good.

This ps is an alias as well:

[alias]
  ps = "!git push origin $(git rev-parse --abbrev-ref HEAD)"
Enter fullscreen mode Exit fullscreen mode

It's annoying to type git push origin master. It's too long, right?
So, I made an alias as ps for it.
In this alias, it does some work for you.
It helps you push to the remote branch that has the same name as the current local branch.
If you are on 'master', it pushes from the local 'master' to the remote 'master' branch.

To pull, you got pl alias.

[alias]
  pl = "!git pull origin $(git rev-parse --abbrev-ref HEAD)"
Enter fullscreen mode Exit fullscreen mode

Similarly, it pulls from a remote branch that has the same name as the currently selected local branch.
So you can avoid specifying a branch name when you were working on a branch other than master.
So, it helps your workflow as well.

And also you got br alias which is for branch:

[alias]
  br = branch
Enter fullscreen mode Exit fullscreen mode

So, anyway, I'm talking about you should take advantage of alias.

How to quickly open up GitHub project page

Now I created a git repository on GitHub.
But you don't always have the tab opened, right?
When you want to see an issue or want to look into the detail on GitHub, you can type g open.

sc13

Then, you can quickly open the GitHub repository page on browser from terminal.
g open alias is defined like so:

[alias]
  open = "!hub browse"
Enter fullscreen mode Exit fullscreen mode

It actually runs a shell command hub browse.
So, it's equivalent to running hub browse, and you can get the exact same result.
So, the hub is a command-line tool by GitHub that provides you some GitHub-specific commands for git.
For example, you can clone a repo without specifying a full URL to the repository like so:

g clone craftzdog/dotfiles-public
Enter fullscreen mode Exit fullscreen mode

So, it helps you do some GitHub-specific tasks.
By using this, you can quickly open up a GitHub project page of the remote git repository with git open command, like this.

vim-fugitive for using Git on Vim

I usually code with vim and I often want to use git from vim.
To do that, for example, here is the source code of the voxel dog project that renders a dog using three.js.
Now, the camera is moving around.
Let's rotate the dog itself as well.

sc14

Okay, it started rotating:

sc15

Great, haha.

So, let's commit it.
Check the current git status.
Then, run g d which is an alias for diff.

sc16

Okay, as you can see, it adds a line to rotate the dog.
Like this, I always check the diff before commiting it.
And if it looks ok, I run g cz to run commitizen.
But when you do this, if you added -a option, you can make a commit that includes unstaged changes.

sc17

Then, you got another commit here.
Then, push it with g ps.
Looks good.

Now that I made a change with vim.

Fast-forward to 3 months later, I no longer remember this change - Why I made this change, when I did it, or who did it.
I have no idea.
To check it, run :Gblame on vim like so:

sc18

Then, you get a commit history on the left side and the corresponding lines on the right side.
If you scrolled it, both panes sync.
So, you can see which line corresponds with which commit respectively.
Then, when you hit enter, you can look into the diff.

sc19

So, you found that this commit has been made on 11:53 November 5th in order to rotate the dog.

I often forget what I did months ago.
That way, I can quickly look into the past changes directly on vim without browsing GitHub or running git blame command.

And, if you still don't get it, and you wanna look more into it on GitHub, run :gopen command.
Then, you can open up the file on GitHub immediately.
It refers to the corresponding commit, it comes in handy when you wanna look into diffs with another commit.
Like this, I go back and forth between terminal and browser.

So, how to do this. I use a vim plugin called vim-fugitive.
It's a git wrapper for vim.
It supports various commands as you can see.
Please fiddle with it.
gopen is an alias for gBrowse.
It's just my preference.

Well, that's it!


As you may notice, workflows would look different if you work with a team.
But I guess some tips should be also useful for your teamwork.
That’s pretty much it. Hope it’s helpful for improving your development workflow.
Thank you for reading/watching. See you in the next one.

Subscribe Newsletter

My YouTube channel

See also

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .