Git is difficult. On top of that, for many new developers, it’s the first tool with a command line interface (CLI) that they use. It can be a bit too much if you’re learning all the following at the same time:
In this article, I’ll show you a few tricks to make your Git experience less painful and more fun!
Git is generous enough to offer many commands to check the state of your repository, yet prudent enough not to overwhelm the beginner with verbosity. In short—you should ask Git to give you the details you need. You have following command for it:
git status
A key command to get a glimpse of where you are in Git. Example outputs:
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
for when you are on clean branch and up to date
$ git status
HEAD detached at abc01e7
nothing to commit, working tree clean
for when you are in detached HEAD state.
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
lorem-ipsum.txt
nothing added to commit but untracked files present (use "git add" to track)
When you have some new files in your working copy.
git show
This is a command that allows you to see changes that happened in a commit. When run without an argument, it shows you the current commit:
$ git show
commit abc01e761cb9cc14c4d5aecae2488810c834c0f9 (HEAD -> main, origin/main, origin/HEAD)
Author: Marcin Wosinek <[email protected]>
Date: Wed Nov 2 11:57:33 2022 +0100
Add lorem ipsum to readme
diff --git a/README.md b/README.md
index 8ae0569..9dca8b4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
# Test
+Lorem ipsum
You get all the details about the commit: its author, time, message and diff of each file that was changed.
You can specify any commit you want to see:
$ git show edd3504
commit edd3504f6edc722482fa4383443fa1729acc9a87
…rest of the output…
Note! This command works on commits. Thus, if you use branch name, it will show the most recent commit from that branch:
$ git show main
commit abc01e761cb9cc14c4d5aecae2488810c834c0f9 (HEAD -> main, origin/main, origin/HEAD)
Author: Marcin Wosinek <[email protected]>
git tree
—a custom alias I recommend to everybodyI recommend defining a tree alias to see the graph of all the branches—you can learn more about it in this article. With it in place, you can run:
$ git tree
* 11f7f3c (test) add test.txt file
| * 2dbd30f (origin/test) add test.txt file
| * e7be203 (test-2) add new file
|/
* abc01e7 (HEAD -> main, origin/main, origin/HEAD) Add lorem ipsum to readme
* edd3504 Add readme
And get a nice overview of the entire repository.
In some cases, Git wants you to provide input to it by editing a temporary file it created. This workflow can be confusing at first, and even more so if you don’t know your default editor. Here’s how to check it by listing Git’s logical variables and filtering them to the one that contains EDITOR
. On my MacOS, it’s vi
:
$ git var -l | grep EDITOR
GIT_EDITOR=vi
Similarly, on Ubuntu:
$ git var -l | grep EDITOR
GIT_EDITOR=editor
On Ubuntu, editor
is a command that starts what is configured as your default text editor. One machine I have access to, it’s nano
:
$ update-alternatives --display editor
editor - auto mode
link best version is /bin/nano
link currently points to /bin/nano
…
No matter your editor, make sure you know how to do the following things:
Vim
, due to the various modes of its interfaceOr, change the editor to something you know how to use.
Git commands, all the attributes, and branch names are long; and the interface accepts no mistakes. The key productivity trick is not to type them whole. In most shells, when you press tab key, the shell either:
shows you all available command auto completes
picks command if there is only one available
So, for example, with the zsh
shell I use, let’s see these options after typing git co<tab>
:
$ git co
Completing main porcelain command
commit -- record changes to repository
Completing ancillary manipulator command
config -- get and set repository or global options
Completing ancillary interrogator command
count-objects -- count unpacked objects and display their disk consumption
Completing plumbing manipulator command
commit-graph -- write and verify Git commit-graph files
commit-tree -- create new commit object
Completing plumbing internal helper command
column -- display data in columns
And just complete the word when I type git com<tab>
:
$ git commit
Similarly, it provides autocomplete for parameters, branch names, etc. It makes a big difference while typing.
Watching someone retype the whole command they used a few moments ago is a painful experience. Most shells allow you to reuse the last command by just using arrow keys. Arrow up brings the most recent command, you type it again to get the previous one, and so on. After finding the one you need, you can edit it to match the operation you’re performing right now.
I sync my local repository with remote all the time—even in my private repos, where I know, I’m working alone. It’s too easy to mess up stuff by not paying attention to what others are changing. And it takes only one command to make sure everything is up to date:
$ git fetch
After running it, you know that every origin/<branch-name>
reference you see locally is in the same place as it is on remote.
Conflicts are sometimes painful to fix, but nothing hurts more than conflicts that could have been easily avoided. One of the common scenarios when people introduce an unnecessary conflict, is when they start a new branch behind the most recent commit. This can be easily avoided by:
Having branches that are merged quickly has a few benefits:
Short-lived branches are a way to progress via small interactions—something I recommend in another article.
I often see beginners coming with problems I mostly don’t have to deal with because I avoid the following things in Git:
git pull
In Git, pull combines two operations into one:
git fetch
, andgit merge
or git rebase
Depending on your configurationI always want to double-check my remote state before doing either merge or rebase. So my typical flow would be:
git fetch
—to get updates from remote,git tree
—the alias I mentioned above, to make sure the repo is in the state I expect it to be,git rebase origin/<branch-name>
—when there are changes remote and locally, orFinally, submodules are a way of embedding a Git repository or repositories inside another. The internal repository maintains its separate history, whereas the containing one keeps only the reference to the origin and the current commit that the internal repo should be at.
My main issue with Git submodules is that they complicate an already difficult problem—version control—and add more layers of complexity.
Git submodules provide an alternative solution for the problem that is better addressed with:
Git has many confusing aspects, but it gets way simpler once you understand it. If you are interested in learning more about Git, sign up here to get updates about my Git-focused content.
Originally published here.