Category Archives: Git

Git Cheatsheet

I had these notes buried deep in a subdirectory on one of my servers. Figured I should post them on WordPress for convenience. Granted, this is not the shortest cheatsheet, but relatively speaking I’d say it’s a succinct explanation of the basics of Git.

FWIW I resisted Git for almost a decade, mainly because I was a successful solo developer long before Git was created in 2005. I used a version control system at IBM in the 90s, CMVC + TVFS. But for my own business projects, I never even considered using version control. And I still think it’s overkill for many small projects. As a rule of thumb, if you have more than one or two developers, you will probably benefit from Git.

Git is not hard to learn. Don’t be intimidated by things like “rebasing” and “stashing” as these are features you will never need, in my opinion. Once you get going with Git, the whole system makes a lot of sense.

The first thing you want to do is create a subdirectory that you want to work in. So let’s create a “go-cheatsheet” directory as an example project, just because I’m working on writing a Go cheatsheet for my own reference.


mkdir go-cheatsheet
cd go-cheatsheet


There’s a good chance you already have git installed, so I’m going to skip the installation step.

Next, a lot of tutorials recommend configuring git globally. However, you might want to use a different email address for different projects, in which case you should use the “–local” setting. So when you set your name and address globally, I would just think of those as defaults and then you can be more specific later on if you have a project that merits a different “local” email address.

Each configuration level overrides values of the previous level. Global overrides System settings and Local overrides Global settings: Local > Global > System. In most cases, you’re the only user and you won’t need “system” defaults.


git config --global user.name "PJ Brunet"
git config --global user.email "pj@pjbrunet.com"


Now we initialize Git which builds a hidden “.git” directory. The .git directory contains all the internal files Git needs to operate. However, in most cases you won’t ever need to look in there.

Warning: Before you init, you probably want to make sure you’re not the root user, otherwise the .git directory will have root permissions and I’m thinking that could cause problems for you later on.


git init
ls -lA


Notice the woke info about changing the name of your master branch, you can safely ignore it. The ‘ls -lA’ just shows you the hidden .git folder.

Now let’s check the status of our project.


git status


The output looks like this, because I have a file created in there already called cheatsheet.go

On branch master

No commits yet

Untracked files:
  (use "git add ..." to include in what will be committed)
	cheatsheet.go

nothing added to commit but untracked files present (use "git add" to track)


Untracked means the file isn’t “staged” yet. Before any code is added to the master branch, it needs to be staged first. In other words, Git sees the file in the directory, but it doesn’t know what you want to do with it yet.

The workflow is something like this: untracked > staged (ready to be committed) > committed (officially in the codebase) > pushed (to a remote server.) The last push isn’t absolutely necessary, unless your job requires it, for example to collaborate with other developers. Or maybe you’re pushing your code to a service like GitLab as a backup, or to share with the public.

Thinking of Git as a backup system, it was designed as a decentralized system for resiliency. So when you push your code to another Git server (like GitLab for example) you can think of that remote server as a “separate but equal” node that’s still fully functional if your laptop or PC is destroyed. In other words, it’s not a hierarchical network, ih2t’s a decentralized network where individual Git servers push and pull code back and forth between each other, and usually the goal of that is to keep everyone up to date with the latest changes.

Moving on, let’s add cheatsheet.go because we want to add it to the master branch. The easiest way was already explained by the “git status” output. You can also use wildcards.


git add '*.go'


You can also add files interactively with the “-i” parameter, but it’s not as cool as it sounds. If you want a more fancy way to add multiple files, I recommend lazygit by Jesse Duffield. It’s super easy, you can stage and unstage files by clicking on them, or pressing the spacebar. Could it be any easier? No. You could spend hours searching for the best GUI Git client, but nothing could be easier than lazygit.

In any case, I’m going to assume we have all of our latest changes staged now. Any time you change a file, Git switches it back to “untracked.” Just remember to “add” the necessary files to the staging area before you “commit” them to the master branch, like when you’re done making a change. I usually type “git status” to see what files I’m about to commit.

Also note, Git doesn’t scan your entire computer for changes. It’s just looking for changes under the directory you ran “git init” which is also called your “working tree.” So for example, you could “git init” in projects/go-cheatsheet and “git init” again in projects/python-cheatsheet and each codebase would be completely independent. If instead you ran “git init” in the “projects” directory, then you would have one codebase for both projects.

Ask yourself, “Should I include everything in Git? Or just the files I need?” Every situation is a little different. It depends on the project, and to a degree it’s up to you. Maybe you want to stage the entire WordPress codebase, including your plugin. Or maybe you would rather go to example.com/wp-content/plugins/my-plugin and initialize Git there to only add files immediately relevant to your plugin.

Now let’s “commit” our first file, the one we just staged. You can think of a commit as a photograph of your stage, a moment frozen in time. And bonus, you get to add a message.


git commit -m "Start of my Go cheatsheet"


The output:

[master (root-commit) d147c9c] Start of my Go cheatsheet
 1 file changed, 78 insertions(+)
 create mode 100644 cheatsheet.go


Let’s talk about commit messages for a moment. Here are some tips and notes I collected.

A commit message should explain:

– Why is this change necessary?
– What problem does this commit solve?
– Briefly describe at a high level what was done.
– Will this change have side-effects?
– This commit will (your message here)

Focus on why, not how. Your code should explain how.

Not everyone agrees on this, but in general you don’t want too many changes per commit.

If you want to undo a change/commit, I don’t recommend using “git revert.” It’s better to revert the change in your code with a comment explaining the whole story of what happened, and the comment could even refer to the commit id that wasn’t needed, just in case somebody attempts the same idea in the future. For example, let’s say you add a feature, it works perfect, but the client decides they don’t want that feature. But next year they change their mind and want the feature back. It happens.

What if you accidentally commit something stupid? I would just leave it alone and add a new commit saying, “This completes the previous commit where I made xyz mistake.” Like a blockchain, think of commits as additive only. Trying to rewrite history to save face is not worth it.

Commit message formatting:

– Capitalize the first letter.
– Don’t end commit messages with a period.
– Keep it under 50 characters.

If one line is not enough, there’s also the “subject / body” format:

– Subject format is the same as a one-line commit message.
– Separate subject from body with a blank line.
– Wrap the body at 72 characters.

Here’s a good quote I found somewhere:

A well-crafted Git commit message is the best way to communicate context about a change to fellow developers (and indeed to their future selves). A diff will tell you what changed, but the commit message can properly tell you why. A commit message shows whether a developer is a good collaborator. Understanding why something happened months or years ago becomes not only possible but efficient. A project’s long-term success rests (among other things) on its maintainability, and a maintainer has few tools more powerful than the project’s log. It’s worth taking the time to learn how to care for one properly. What may be a hassle at first soon becomes habit, and eventually a source of pride and productivity for all involved.

Moving on, let’s look at our log so far:

git log

Here’s a less verbose way:

git shortlog

Preparing to Push

The next step is optional, but typically we want to “push” our code to a remote Git server for safekeeping. I’m using GitLab and it’s telling me: “You won’t be able to pull or push repositories via SSH until you add an SSH key to your profile.” Assuming you already have an account setup there, the link to add your key should be something like https://gitlab.com/-/profile/keys and the GitLab instructions are helpful explaining how to do this.

As of today, GitLab recommends an ED2551 key type:

ssh-keygen -t ed25519 -C "pj@pjbrunet.com"

Now we’ll configure git to add our remote repository.

git remote add gitlab git@gitlab.com:cts-llc/go-cheatsheet.git

For future reference, we can “pull” from and “push” to this “gitlab” repository. In most tutorials, the name would be called “origin”. But since it’s not an origin and more of a destination, I figure “gitlab” is more specific.

To see the remotes we have saved:

git remote -v

If we want to remove one of them, in this case the origin:

git remote remove origin

Let’s finally do a push. The name of our remote is “gitlab” and I believe “master” here refers to the name of the branch we want to push to on the remote server. The -u tells Git to remember our remote name, so that next time we can simply run “git push”.

git push -u gitlab master

Or you can be specific every time:

git push gitlab master

Since this is our first time pushing, the cryptographic handshake needs our thumbs-up approval. Type “yes” to approve.

Are you sure you want to continue connecting (yes/no/[fingerprint])? yes