Improve your experience using git cli
Tags: productivity, git, tooling, configuration
Published: 2020-12-12 17:53:56

# Improve your experience using git cli

## :question: Abstract

Not So Basic: Devenv is a series of tutorials in which we aim to have a nice, productive and portable development environment that takes the best of both worlds between Linux and windows.

In this third tutorial "Improve your experience using git cli" we focus on git:
- Installation and basic configuration
- P4merge as visual diff and merge tools
- Some handy aliases
- SSH authentication for GitHub and GitLab and SSH agent

⚠️ This tutorial assumes that you have followed the previous ones, especially [Linux on Windows](https://dev.to/codingones/linux-on-windows-42ma), and are following this tutorial in WSL.

## :memo: Overview

To complete this tutorial, you need to:

- [Install and configure git](#install-and-configure-git)
- [Visual diff and merge](#visual-diff-and-merge)
- [Add command aliases](#add-command-aliases)
- [Check your configuration](#check-your-configuration)
- [Authenticate through SSH](#authenticate-through-ssh)

## Install and configure git

[git](https://git-scm.com/) is a version control system that allows you to track every modification on your codebase. With a distributed paradigm you can use git locally, and sync changes with other repositories whenever you need.

### Install

If `git` is not installed on your machine, you can run:

```bash
sudo apt install git-all
```

Follow the instructions described in this article when you are getting started with `git` CLI, then you can use a lot of [variables](https://git-scm.com/docs/git-config#_variables) to improve your configuration.

You can specify one of the three `git` configuration storage options: `--system`, `--global` or `--local`:
- Global configuration, for all the repositories of the current user. After you run your first git config global command, git creates a `.gitconfig` file for you in your home folder.
- System configuration, for all the repositories of all users and stored in `/etc/gitconfig` file.
- Local, the default scope, stores the configuration for the current repository, in `.git/config` file. this configuration is not tracked.


### Configure User

Git associates all commit with a user, useful when you are working in a team to see the author of a change. You have to configure your username and your email:
```bash
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
```

### Configure editor

Some `git` commands require an editor, you can set your preferred choice with:
```bash
git config --global core.editor vim
```

### Configure line endings

Files use special characters to represent the end of a line so that the text editors can display the next characters in a new line. The problem is that theses special characters differ depending on the operating systems. This can lead to file execution errors. You can use `git` autocrlf option to fix line endings.

`git` documentation recommends setting autocrlf to `true` for Windows users so that it converts line endings to `CRLF` when you check out code.
I would suggest to use `LF` line endings even on Windows to avoid some annoying problems. 
For instance, you may encounter hard to resolve bugs when running a Docker container you build locally because of files containing `CRLF` line endings copied into the Docker image.

You can probably configure your IDE to use `LF` line endings as well, so you can run the following command to make `git` automatically convert `CRLF` to `LF` on commit:

```bash
git config --global core.autocrlf input
```

## Visual diff and merge

Consider using `git` with CLI if you do not have a long experience with `git`, because it invites you to understand how it works under the hood.  
That said, GUI tools can help, especially in case of conflict resolution. Many IDEs can handle that well, but it is also nice to have a tool dedicated for that purpose.

### P4Merge

[P4Merge](https://www.perforce.com/products/helix-core-apps/merge-diff-tool-p4merge) allows you to visualize the differences between files, you can even use it to compare images. It also offers an integration with `git` to show differences between two versions and to resolve conflicts.

[Download](https://www.perforce.com/downloads/visual-merge-tool) and install the version that match your OS. For Windows users using `git` through WSL, download and install the Windows version.

#### Diff tool

`difftool` is a `git` command that can display all the differences between HEAD and a given commit or between two commits. When you use it without arguments, it displays the current unstaged changes. It is common to run it before staging files to check that all the changes are coherent for a single commit.

Define `p4merge` as `git` diff tool, then specify the command to execute.  
Here is an example for a WSL setup that uses `p4merge.exe`, use `p4merge` for other systems:

```bash
git config --global diff.tool p4merge
git config --global difftool.p4merge.cmd 'p4merge.exe "$(wslpath -aw $LOCAL)" "$(wslpath -aw $REMOTE)"'
```

Then disable `difftool` prompt; otherwise your cli will asks you to confirm between each file display:

```bash
git config --global --add difftool.prompt false
```

Now you can go to any `git` repository, add a modification and run:
```bash
git difftool
```
`p4merge` will show you unstaged differences.

#### Merge tool

`mergetool` shares some similarities with `difftool` but it offers the ability to resolve conflict. It displays two conflicted files and help to merge them in a result preview.

Configuration :

```bash
git config --global merge.tool p4merge
git config --global mergetool.p4merge.cmd 'p4merge.exe "$(wslpath -aw $BASE)" "$(wslpath -aw $LOCAL)" "$(wslpath -aw $REMOTE)" "$(wslpath -aw $MERGED)"'
git config --global --add mergetool.prompt false
```

Set `mergetool.keepBackup` to `false` to avoid preserving `.orig` files after performing a merge:
```bash
git config --global --add mergetool.keepBackup false
```

Set `mergetool.trustexitcode` to `true` so that exit code returned by `p4merge` indicates merge success:
```bash
git config --global --add mergetool.trustexitcode true
```
### (Alternative) Use your Intellij IDE
Open your IDE GUI and go to "Tools > Create Command-line Launcher"
be sure to set a path for the script were you have the file permissions rights.
eg : '~/tools/wstorm.sh'

```bash
git config --global diff.tool webstorm
git config --global difftool.webstorm.cmd '~/tools/wstorm.sh diff $LOCAL $REMOTE'
git config --global merge.tool webstorm
git config --global mergetool.webstorm.cmd '~/tools/wstorm.sh merge $LOCAL $REMOTE $BASE $MERGED'
git config --global --add mergetool.prompt false
git config --global --add difftool.prompt false
```

## Add command aliases

When using `git` via CLI, you can save time by setting shortcut aliases for common `git` commands.

`ci` instead of `commit`:
```bash
git config --global --add alias.ci commit
```

`co` instead of `checkout`:
```bash
git config --global --add alias.co checkout
```

`br` instead of `branch`:
```bash
git config --global --add alias.br branch
```

`cl` instead of `clone`:
```bash
git config --global --add alias.cl clone
```

`cp` instead of `cherry-pick`:
```bash
git config --global --add alias.cp cherry-pick
```

`st` instead of `status`, with additional options. `s` to have an output in the short-format, and` b` to show the branch and tracking information:
```bash
git config --global --add alias.st 'status -sb'
```

`ds` instead of `difftool`, with additional `staged` option that runs `p4merge` to show differences between head and staged files:
```bash
git config --global --add alias.ds 'difftool --staged'
```

`last` to show the latest commit:
```bash
git config --global --add alias.last 'log -1 --stat'
```

`unstage` to cancel `git add` actions and reset all staged files:
```bash
git config --global --add alias.unstage 'reset HEAD --'
```

`clear` to remove all the current modifications.  
Warning! You cannot revert this action:
```bash
git config --global --add alias.clear 'checkout .'
```

Finally, `lg` to display `git` history within a nice graph:
```bash
git config --global --add alias.lg 'log --graph --abbrev-commit --date=relative --all --pretty=format:"%C(green)%h%C(reset) -%C(red)%d%C(reset) %s %C(yellow)(%cr) %C(blue)<%an>%C(reset)"'
```
Here are some details about options used in this alias:
- `graph`: adds a graph to represent branches on the left of logged commits
- `abbrev-commit`: displays a short commit hash instead of a full hash
- `date=relative`: displays elapsed time instead of commit date
- `all`: shows all commits in the history of branches and tags
- `pretty=format`: displays one line commits:
  - `%h`: commit hash in green followed by a dash
  - `%d`: branch name in red
  - `%s`: commit message
  - `%cr`: elapsed time in yellow between parentheses
  - `%an`: author name in blue between chevrons

## Check your configuration

You can display the current configuration file with:
```bash
cat ~/.gitconfig
```

The output should look like this:

```bash
[user]
        name = John Doe
        email = johndoe@example.com
[core]
        editor = vim
        autocrlf = input
[diff]
        tool = p4merge
[difftool "p4merge"]
        cmd = p4merge.exe $LOCAL $REMOTE
[difftool]
        prompt = false
[merge]
        tool = p4merge
[mergetool "p4merge"]
        cmd = p4merge.exe $BASE $LOCAL $REMOTE $MERGED
[mergetool]
        prompt = false
        keepBackup = false
        trustexitcode = true
[alias]
        ci = commit
        co = checkout
        br = branch
        cl = clone
        cp = cherry-pick
        st = status -sb
        ds = difftool --staged
        last = log -1 --stat
        unstage = reset HEAD --
        clear = checkout .
        lg = log --graph --pretty=format:\"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %Cblue<%an>%Creset\" --abbrev-commit --date=relative --all
```

You can also use `git config` to list the current configurations:
```bash
git config -l
```

## Authenticate through SSH

You can use SSH to generate a key pair composed of a public and a private part.  
You can add your public key on your `git` repository host, so that it accepts SSH connections, then you can avoid writing your email each time you need to perform an action on your remote repository.

### Generate a key

Run the following command to generate your SSH key pair, then:
- Indicates the file in which to save the key, hit enter to leave the default value
- Type your passphrase twice

```bash
ssh-keygen -t rsa -b 4096 -C johndoe@example.com
```

The key generator set your key pair `id_rsa` and `id_rsa.pub` in `~/.ssh`.
Never share `id_rsa` file, it should stay safe on your computer.

Display the content of public `id_rsa.pub` key and copy the output:
```bash
cat ~/.ssh/id_rsa.pub
```

### Register it in your git repository host

Once you copied your SSH public key, you will register it in your git repository host.

#### GitHub

Log in to your `GitHub` account, then click on the arrow next to your profile picture on the top right of the page and select `Settings` in the drop-down.  
Click on `SSH and GPG keys` on the left of the page.  
Click on `New SSH key` and set the title that describes your machine such as `Home` or `Work` and paste the public key copied in the previous section, then click on `Add SSH key`.

#### GitLab

Log in to your `GitLab` account, then click on the arrow next to your profile picture on the top right of the page and select `Settings` in the drop-down.  
Click on `SSH Keys` on the left of the page.  
Paste the public key copied in the previous section, then set a title that describes your machine such as `Home` or `Work`, finally click on `Add Key`.

#### Try to clone a repository

You can now clone one of your repositories using the SSH link:
```bash
git cl git@host.com:johndoe/my-repository.git
```

The first time you clone from your repository host, `git` asks you to confirm that you want to continue connecting, type `yes`, then you will be asked for the passphrase you entered when generating the SSH key pair.

Try to remove the repository you just cloned, then clone it again, you can see that you will only be asked for your password.

## Type your password once

As you may have noticed, when using SSH link, you do not need to type your email anymore, but you still spend time typing your password.

### Run SSH agent

You can enter your password once for all with the following workflow, first run:
```bash
ssh-agent
```
Then copy and paste the output in your terminal. Once the three commands executed, run:
```bash
ssh-add
```

This makes your SSH identity available for the current session. If you try to clone the repository again, `git` do not asks to type your password.

If you close your terminal, reopen it and try to clone the repository, `git` asks for your password since your session has been cleared.

### SSH agent zsh plugin

You can further simplify the workflow by activating `ssh-agent` plugin in your zsh configuration:
```bash
vim ~/.zshrc
```

Find the line that contains `plugins=`, it is probably not commented with `git` plugin configured. Remove the comment character, the line should look like this:
```bash
plugins=(git)
```

Now add `ssh-agent` plugin next to `git` separated with a whitespace, the line should finally look like this:
```bash
plugins=(git ssh-agent)
```

Save and quit the editor and close your terminal. When you open your terminal again, `ssh-agent` asks you to type your SSH passphrase. You can now use `git` without requiring typing your password. When you close and open your terminal again, you will retrieve your session. But you will have to type your passphrase when you restart your computer.

## Next article

In the next article, we will focus on node.js.

## Contributors

- [Romain Cambonie](https://www.linkedin.com/in/romain-cambonie/)
- [Marc Gavanier](https://www.linkedin.com/in/marc-gavanier/)

## References used

- [git documentation](https://git-scm.com/doc/)
- [p4merge](https://www.perforce.com/products/helix-core-apps/merge-diff-tool-p4merge/)
- [git aliases](https://devtut.github.io/git/aliases.html#list-search-existing-aliases)
- [GitHub](https://github.com/)
- [GitLab](https://gitlab.com/)