Working with Git without actually getting it

An opinionated guide to Git

Nobody actually understands Git but everyone knows exactly what they need to get their work done.

Git is a pretty complex tool with many features and ways to work with it. I always feel I have now idea how to properly use it but I know just enough that I can use it confidently for my daily work.

I recently talked with more people about some basics of Git and how I use it. Getting started with Git can be scary and you might need some time to gather the knowledge you need to work efficiently without messing up every now and then. It’s totally normal to mess up your local repository, copy your changes and start with a fresh clone of the repository.

Just try it out and learn along the way. That’s how I did it over the years and now I thought why not create a document on the aspects of Git that are important for me to make it easier to get on a path. This is a list of Git features I need daily or every now and then with short explanations. It is not an extensive guide or tutorial for Git. You can find many of them in good quality plenty online.

Overview

Terminology

You should understand some terms to work with this guide:

  • Version control = system responsible for managing changes to computer programs
  • Git = a version control system
  • GitHub = provider which offers Git repository hosting and collaboration tools
  • Repository = data structure that stores metadata (changes) for a set of files or directory structure

Setup

Some steps for getting started with Git for the first time or setting it up on a fresh operating system.

Identity

If you start to use Git for the first time you probably need to set up your identity. Your identity is just a name and an email address that are used for every commit. You typically set up a global identity that is used for all repositories on your device. Make sure you use an email address added to your account on GitHub etc. to allow these service to link your commits to your accounts.

Setting up a global identity is simple. Your type to commands:

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

More information

SSH

You should set up SSH keys on your development machine to use them for authentication when pushing to Git providers like GitHub, Gitlab, Bitbucket. SSH keys consist of a private and a public key. You add the public key to your account on GitHub etc. and the private key stays on you machine and you should take care to not publish or loose it. Every time you run commands with Git remotes like git push origin or git fetch origin Git uses the key for your account instead of your password. GitHub for example deprecated the basic authentication.

For setting up SSH with GitHub follow this document.

Yubikey

I personally prefer having my private key for SSH and GPG on a smartcard like a Yubikey instead of my devices. The main benefit is that I can use different machines more easily without worrying about updating and securing my private key. This is just nice to have.

This repository explains this a bit more and has an excellent guide on how to set it up.

Getting started with a project

If you have set up Git on your machine you can start with a project. For existing projects you usually start by cloning the repository from a remote like GitHub. If you have a project folder with source files you want to use init to set up the Git repository for this project.

Clone

With git clone <URL> you create a local copy of the repository on your development device. With this repository you get the files and the complete history with all changes and commits.

If you have write access to the repository you are cloning, you usually want to use the SSH URL like git@github.com:NiklasMerz/git-examples.git instead of the HTTPS URL to use SSH authentication when pushing your changes.

Init

If you start a new project just use git init in the project folder to initialize a new Git repository. You can then use git add * and git commit to add your files. If you want to push your repository to Git servers like GitHub you can use git remote add origin <URL> to add a new remote and git push origin -u master afterwards to push the changes to the remote for the first time.

Documentation for init and remote

Forking

Forking is commonly used on platforms like GitHub to contribute changes to an open source project. If you clone an open source project you don’t have write access to push changes for obvious reasons. If you create a fork on GitHub first, GitHub creates a copy of the project in your account. You have write access to your own fork and can make changes. If you want to contribute your changes back to the original project you can create a pull requests from your fork. More on that later.

Committing

Small commits with good commit messages are crucial for an efficient use of Git.

Add & Commit

Committing with Git is usually a two step process. After you did a change to a file it is an unstaged change. You add it with git add <filename> or all files with git add * to the staging area. Staged changes are prepared for the commit and you can now commit with git commit.

I often use the shortcut git commit -am "<Commit Message>". This command does many things at once. -a adds all changed files to the staging area. Caution: New files won’t be added and you need to add them before. -m sets the commit message directly from the command and it won’t open a text editor like usual. This has some drawbacks because you don’t really see which files get committed and you cannot properly format the commit message. I use this for small commits and simple changes. For large changes I prefer the normal two step git add and git commit process.

Personally I use mix of Git on the command line and the interface in my IDE (VSCode). Some prefer UI Git clients some just the command line. I feel I get better control with the Git CLI but use what fits best your taste and environment.

Commit Message

I think it’s more important to write the why and not the what in a commit message. You can see what a commit does by looking at the code diff but if you write the reason for a change (especially if it’s a strange fix) in the commit message your future self or colleague will thank you for making it easier to understand. It can be helpful to describe where a change was made (like backend/frontend or specific part of a big project), too. This helps if you scan through a large number of commits looking for something specific. You should always add some words about the change and reference issues from your issue tracker if there are any. GitHub and many other tools have special keywords to link and close issues automatically with commit messages.

Just think about what information could you need to understand the change in the future. Ask yourself why and what.

Push

After you did the commit, it’s just done on your local machine. If you want it to be available on GitHub etc you need to push it. You can push one ore more commits with git push origin <branchname>. Usually your default remote server is called ‘origin’. You can have multiple remotes like your own fork, the upstream project or other hosting providers aside from GitHub.

Documentation about working with remotes

Pull

If you need to get the latest changes from a remote you need to use Git pull.

Just a short examples. You are on the main branch locally and somebody else updated the main branch on GitHub. If you type git pull origin main, the latest changes from GitHub get pulled and possibly Git creates a merge commit if it needs to merge your local changes with the remote changes. Alternatively you can use git pull origin main --rebase to use rebase instead of merge. Read the rebase section to understand what happens there and what you need to take into consideration.

Understanding Change

My number one reason to use Git, aside from a backup, is that I love to look at previous commits to understand how a project developed. I even use it for stuff I did myself just a while back.

Git log

I often look at the history of the project or single files to understand when and why changes came in the project. For simple things git log and git diff <commit hash> are sufficient but most of the time I prefer a UI like GitHubs UI or Git clients like the one in VSCode.

Blame

Git Blame is a useful tool to understand changes to lines in a file. GitHub and other Git services and clients use blame to show short information about the latest commit for each line.

Git blame screenshot GitHub

Tags & Releases

Tags are a simple way to mark specific snapshots and are typically used for versions & releases. GitHub offers additional features for releases where you store files and a description to tags.

More about tags and releases.

Workflows & Branches

Workflows in Git typically involve working with branches, pull requests and git commands to merge branches like git merge and git rebase. There are many ways to implement a development workflow with Git and you can find lots of examples and advice online.

The most common and simplest workflow often found on open source projects on GitHub etc is that you have a default branch (by default main). Features get developed in their own branches and merged to the default branches via pull requests. External contributors of open source projects create their own fork of the project and submit pull requests from their branches to the upstream projects default branch.

If you come from a central source control management system, like SVN, the main difference may be that branches are more heavily used in Git. With SVN typically everyone works on their features and bug fixes and commits them to a default branch “trunk”. With Git your typically work in branches and merge to the default branch once your change is done, reviewed and tested.

The official Git documentation has a chapter about Git branching workflows, too.

Pull requests (Merge requests)

An important way to integrate changes form a branch or fork back to the default branch or upstream project is a pull request. A pull request (or sometimes merge request) is a feature found on Git platforms which allows you to create request for you or project developers to review and merge your code. You can create pull requests for your feature branches or for merging your changes in a fork back to the original project. More about pull requests on GitHub

My workflow for contributing fixes in open source projects is like this:

  • Find project on GitHub and fork it into my account
  • Clone my fork to my PC
  • Create a new branch in my local repository. (I usually don’t touch the default branch in my work and merge changes from the upstream project if needed.)
  • Make changes and commit
  • Push new branch to my fork
  • Create pull request on GitHub. (GitHub usually even prints a URL to create it in your command line after pushing)

Rebase

I am not really an expert or heavy user of Git rebase. You find plenty useful information online about good uses of rebase. A rebase is really useful and powerful but it rewrites the history in Git. If you rewrite the history in your local repository and you want to push that to a remote it might get rejected if you try to rewrite the remotes history. You can solve this by using a force push which can be dangerous and destructive. Check out the Git documentation and help online to not loose your important data.

More about rewriting history

For me there are two use cases where I typically use rebase:

Updating a feature branch

If you work on a feature in a branch and your or someone else commits changes to the main branch you might need to update your feature branch. The feature branch is one commit ahead and one commit behind the main branch. On GitHub it looks like this:

GitHub branches

I often use git rebase main in my feature branches. What Git basically does it takes your commits that are just in the feature branch sets them aside, pulls all commits from main in, and applies your commits again. Now your feature branch is no commits behind the main branch and you can work with the latest code from main.

Cleaning up commits

I commit very often and sometimes with stupid commit messages. An interactive rebase opens a text editor where you can edit commit messages, squash multiple commits to one with a good commit message and much more.

Closing words

This is just how I use Git. Some of this stuff may be bad or wrong. If you got ideas for this post let’s talk

I will update this post as I encounter more features, uses cases and tips. Subscribe to the newsletter to get updates.