flowchart LR A[working<br/>directory]-.git<br/>add.->B{{staging<br/>area}}-.git<br/>commit.->C([local<br/>repo])-.git<br/>push.->D(remote<br/>repo)
Git
Git is a version control system created by Linus Torvalds, who is also the creator of Linux. Since its first release, Git has largely replaced other version control systems and is used by 93% of software developers worldwide, according to survey results published by StackOverflow.
Thanks to Git, I can sleep💤soundly at night, reassured by the knowledge that all of my progress on all of my ongoing project is recorded. My progress is not only stored on my local computer, but also on Git repository hosting services such as GitHub and GitLab.
A Git repository (repo) is a directory that tracks the changes made to its contents. For more information on GitHub, the largest Git repo host, take a look at the “GitHub for supporting, reusing, contributing, and failing safely” post by Allison Horst and Julie Lowndes on the Openscapes blog.
GitHub and GitLab do not only host repos, but also websites via GitHub Pages and GitLab Pages. My personal site is hosted for free on both GitHub Pages and GitLab Pages.
Before you can benefit from everything GitHub and/or GitLab have to offer, you will need to set up your computer so that you can work locally and/or configure a service like GitHub Codespaces or GitLab Web IDE so you can work remotely in your web browser. In this post, I discuss local development environment setup with the Homebrew package manager and repo setup with the GitHub and GitLab CLI.
If you follow along to end of this post, you will have a repo called dotfiles
that can be used for Codespaces setup.
Setup
Homebrew
Linux, macOS, or Windows Subsystem for Linux (WSL) users can use Homebrew to install everything needed to work through all of the examples in this blog post. First, install Homebrew itself with the latest .pkg
installer for macOS or by running the Example 1 Unix shell code in your terminal.
Example 1
/bin/bash -c "$(curl -fsSL https://\
\
raw.githubusercontent.com/Homebrew/)" install/HEAD/install.sh
If you are not completely satisfied with the integrated terminal built into your preferred source-code editor or the standalone terminal that comes with your operating system (OS), you can use Homebrew to install a new one. The standalone terminal I use most often is iTerm2, which is only for macOS, but I also have the following multi-OS terminals: Alacritty, Hyper, Kitty, and Tabby.
After installing Homebrew, you can run brew
doctor
in your terminal to confirm that everything is set up correctly. If the brew
command is not available, you need to follow the instructions provided after installation to add brew
to your PATH variable.
Once Homebrew is ready, you can run the shell code in Example 2 to create a file called Brewfile
with the echo
shell command and install everything listed in this newly created Brewfile
with the brew
bundle
shell command.
Example 2
echo 'brew "gh"\nbrew "git"
brew "glab"\ncask "github"' > Brewfile
brew bundle
The Brewfile
created by the shell code in Example 2 installs:
- Git,
- the command line interfaces (CLIs) for
- the GitHub Desktop Git Graphical User Interface (GUI).
If you are curious about how I set up my computer, you can take a look at my Brewfile
and other configuration files in my setup
repo on GitHub and GitLab. I will highlight a few configuration files in Section 2.
Repository
Before we get started, you will need a GitHub and/or GitLab account and a way to authenticate into your account(s).
Of the many authentication methods, passkeys stand out because they can function as both a password and two-factor authentication%20is%20an%20electronic%20authentication%20method%20in%20which%20a%20user%20is%20granted%20access%20to%20a%20website%20or%20application%20only%20after%20successfully%20presenting%20two%20or%20more%20pieces%20of%20evidence%20(or%20factors)%20to%20an%20authentication%20mechanism.) (2FA), thus combining the two steps in the 2FA sign-in process into one. Passkeys will certaily GitHub recently announced its plan to make 2FA mandatory for code contributors, which will make passkeys . SSH key.
satisfy both requirements, including SSH keys are still the easiest way to authenticate to GitHub and GitLab in my honest opinion.
You can create a repo using the web interface of https://github.com or https://gitlab.com in your browser, but the best way to start a new project is using the CLI for GitHub or GitLab in your terminal. First, run gh
auth
login
or glab
auth
login
and follow the prompts to authenticate via your web browser or with an authentication token.
The GitHub CLI allows you to add an SSH key to your account during or after authentication. The GitLab CLI does not handle SSH keys during authentication, but has a similar command for adding an SSH key to your GitLab account.
After authentication and SSH key setup, you can run the code in either of the code chunks in Example 3 to set up your local and remote repos and create a Quarto website project in the local repo. You can create shell alias that combine all of the repo creation steps like I did in my .zshrc
.
Example 3
cd # start in home directory
mkdir USERNAME
cd USERNAME
gh repo create REPONAME --add-readme --clone --public
cd REPONAME
cd # start in home directory
mkdir USERNAME
cd USERNAME
glab repo create REPONAME --readme --defaultBranch main --public
cd REPONAME
git pull origin main
git branch --set-upstream-to=origin/main main
To make it easier to backup my repos on both GitHub and GitLab, I set up each local repo to have two origin
remote URLs using the code as shown in Example 4. With this setting, running git
push
in my local repo updates my remote repos on both GitHub and GitLab.
Example 4
git remote add lab git@gitlab.com:maptv/maptv.gitlab.io
git remote add hub git@github.com:maptv/maptv.github.io
git remote set-url --add origin $(git remote get-url lab)
Git workflow
When I want to add or update the content on my site, I go through the steps in the standard Git workflow shown in Figure 1. Every time I “push” a collection of changes called a commit to my GitHub repo on GitHub, a continuous integration (CI) system called GitHub Actions automatically completes the steps required to build and publish my website.
Shell aliases
To make it easier to make incremental changes to my website and frequently release new content, I combined all of the git
shell commands in Figure 1 into shell aliases. You can add shell aliases to a shell configuration file like .bashrc
or .zshrc
on your computer to shorten one or more commands and any associated command arguments.
The git
commit
aliases in the .zshrc
file in my setup
repo on GitHub and GitLab target different groups of files for inclusion in the next commit. For example, cmp
targets staged files, camp
targets tracked files, a.cmp
targets files in the current directory, and aacmp
targets files in the repo.
Example 5 shows the aacmp
alias as an example of the shell alias syntax. The mnemonic device for this alias is add all, commit with a message, and push.
Example 5
.zshrc
alias aacmp="func() { git add --all && git commit --message \
\"$(echo '${*:-$(echo $(git diff --name-status --cached \
| tr "[:space:]" " "))}')\" && git push; }; func"
Aliases like aacmp
allow me to enter free-form commit messages directly on the command line without quotes, e.g. aacmp
edit
first
post
. If you decide to try one of these aliases, please exercise extreme caution as any un-escaped or un-quoted metacharacters may yield surprising effects instead of being included verbatim in the commit message, e.g. *
is replaced by all of the file and directory names in the current directory!
If no commit message is provided after the aliases, a generic commit message is created that includes the change type and name of each changed file. In Section 2.2, I describe how I used this generic commit message approach to further simplify the Git workflow.
Keybindings
An alternative to a shell alias that combines git
commands is to use a keyboard shortcut in a Git Graphical User Interface (GUI) such as GitHub Desktop
or the Git interface in a code editor like VSCode, VSCodium, or RStudio. I use keyboard shortcuts in VSCode and VSCodium to send shell commands to the integrated terminal without moving my focus away from the files I am editing.
I created different shortcuts to control which files are included in each commit: ⌥⇧F for the current file only, ⌥⇧S for already staged files, ⌥⇧T for all tracked files, and ⌥⇧U for all files including untracked files. I also have keyboard shortcuts that affect a specific directory (and all of its subdirectories): ⌥⇧D for the current file’s directory, ⌥⇧. for shell’s current directory, ⌥⇧C for the current working directory according to VSCode/VSCodium, ⌥⇧W for the Workspace directory.
Example 6 displays the ⌥⇧F shortcut as an example of the VSCode/VSCodium shortcut syntax. This shortcut uses the escape code for the return key (\u000D
) to run several git
commands and predefined variables to insert the absolute (${file}
) and relative (${relativeFile}
) path to the currently open file.
Example 6
keybindings.json
{
"key": "shift+alt+f",
"command": "workbench.action.terminal.sendSequence", "args": { "text":
"git add ${file} && git commit -m \"M ${relativeFile}\" && git push\u000D" },
"when": "terminalIsOpen"
}
If you want to set up similar shortcuts for yourself, take a look at my keybindings.json in my setup
repo on GitHub and GitLab. As you create keyboard shortcuts, please be mindful of keybinding conflicts that may arise.
To set up a keyboard shortcut that runs a series of steps rather than a single line of shell code, I suggest you use the VSCode/VSCodium Tasks mechanism, a system designed to automate software build tasks. The default keyboard shortcut to run all tasks in a local or global task.json
file is ⌃⇧B on Linux/Windows or ⌘⇧B on Mac (mnemonic: B is for Build), but you can bind other shortcuts to specific tasks.
If you use a text editor like Vim or Emacs, you can create keybindings for Vim plugins like fugitive or Emacs packages like magit that run through the entire Git workflow. Example 7 shows the Vim+fugitive equivalent of my ⌥⇧F VSCode/VSCodium keybinding.
Example 7
.vimrc
nnoremap <A-S-f> :Gw<bar>G! commit -m "M "%<bar>G! push<CR>
The drawback of my keyboard shortcut approach for the Git workflow is that it produces generic commit messages that are no very informative. Anyone reading the messages will not be able to tell what changes were made and more importantly why the changes were made.
To automatically generate commit messages based on the currently staged changes, we can use a Large Language Model (LLM). Generative artificial intelligence models like LLMs tend to be large in size and have atypical computational requirements.
I really enjoy using Git, especially with shell aliases in my terminal and keyboard shortcuts in my favorite text editors. If we ever collaborate on a project together, you can be sure that I will insist on using Git!