- Published on
Mastering Git
Git is an atomic version control system that allows us to keep a very specific tracking of our code or whatever plain text documents you want.
Awesome, but why should I use git?
When you have some code and you need to change it, how do you save the last version?
You might use something like this:
- myCode.js
- myCode_v2.js
- myCode_v3.js
Terrible!
That horrible way of organization has a ton of problems:
- Saves all the code again, when you only need the new changes.
- You can't go back easily.
- You can't track changes between versions.
- You can't work with others.
Git solve all these problems and many more. Let's take it a look.
Table of Contents
Getting started
Installation
The installation is kind of different across operating systems. So we will take care of each one separately.
Mac OS
It's quite simple in mac. You only need to go to the git page, download the .pkg installler, open it and follow the instructions.
Windows
Again, go to the git page, download the .exe installer, open it, and follow the instructions.
It should be configured by default, but doble check the settings in the installer. You need to install git bash and open ssl in order to get git to work.
Linux
If you are using linux, you might know more than me.
First, it is a good practice to update and upgrade your package manager. Then, you can install git and you shouldn't have any problem.
# Example with apt-get, which is the package manager for ubuntu, debian and others distributions based on those.
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install git
Configuration
Git works almost out of the box. The only thing that you need to do, is to tell it what's your name and email.
From now on, we will use the terminal for everything.
If you are a windows user, you might want to use git bash.
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
First steps
First of all, you need to initialize a git repository inside the folder that you want to track.
$ git init
That command creates two different areas: staging and repository.
Staging
It is a RAM space and a bridge between our non-tracked code and our repository.
Repository
Here is where our tracked code lives.
Tracking code
When you have a new change in your code, and you want to track it, you need to add it to the staging area.
$ git add webpack.config.js
Finally, you need to commit it to the repository with a descriptive message of what you have changed.
$ git commit -m "Webpack configuration"
Branches
Each branch is a different timeline of the code.
Imagine you are working on frontend features and your partner on backend features. So you might have two branches: frontend
and backend
.
By default, all your changes are going to a branch called master
.
# Create branch
$ git checkout -b my-new-branch
# Delete branch locally
# -d will delete the branch only if it has already been pushed and merged with the remote branch.
# Use -D instead if you want to force the branch to be deleted, even if it hasn't been pushed and merged yet.
$ git checkout -d my-new-branch
# Delete branch remotely
$ git push origin --delete my-new-branch
Take a look at the changes
You can see the whole history of a file with the command show
$ git show webpack.config.js
If you need to compare two specific commits you can do it with the command diff
$ git diff id-commit-x id-commit-y
In order to see the id of each commit, take a look at the log.
$ git log
Go back to a previous version
Git gives us a couple of possibilities to go back in time.
# Go back but keep the added changes (This will erase all the changes made after the selected commit)
git reset id-commit --soft
# Go back and erase added changes (This will erase all the changes made after the selected commit)
git reset id-commit --hard
# Go back without erasing anything
git checkout id-commit
# Go back a specific file
git checkout id-commit webpack.config.js
Git WorkFlow
Usually you are going to have two main branches: master
and develop
.
Master is where production code lives and develop where staging code does.
Remember that staging as environment isn’t the same as the git area called with the same name. As environment, it’s the middle place between production and local. Where we can test all the features as a user before doing the final deploy.
On the other hand, the project could have hundreds of branches. Let’s see how we can deal with them in an organized way.
New features
In order to keep things tidy and simple, you might want to create a new branch for each new feature that you need to code.
# Create a new branch called Feature/Auth
$ git checkout -b Feature/Auth
Once you’ve finished coding. You might want to see those changes in staging or production. Therefore you need to merge it with either master,
develop
or whatever the deploy branch is called in your project.
# Move to master
$ git checkout master
# Merge branch
$ git merge Feature/Auth
Fix
We usually call hotfix
to the branch where we work on killing those bugs that need immediate attention.
$ git checkout -b hotfix
Again, once you are ready, merge changes with master
or develop
.
# Move to master
$ git checkout master
# Merge branch
$ git merge hotfix
Save the repository in an online server
Let’s use GitHub for this example, but keep in mind that it's sort of similar in the other platforms.
I’m pretty sure that you can create an account on your own so let’s skip that step.
Once you have an account, you need to create a repository clicking the green button and filling the required fields.
Don’t worry about .gitignore
and Readme
files right now. We will get there soon.
Asymmetric encryption
The best and most secure way to connect our computers with GitHub is with asymmetric encryption.
In summary, you need two keys. A public one and a private one. Github will have your public key and your computer will have the private one.
Every piece of information that travels across the internet will be encrypted with those keys. The only way to decode it, is having them both.
Don’t worry about the difficult words right now. Just follow my steps in order to configure the whole environment.
First of all, we will create a set of keys running a command in the terminal. Keys, will be called id_rsa
(Private) and id_rsa.pub
(Public) and will live in a new folder called .ssh
in the home of your computer. Also, the command will ask you a passphrase.
Second of all, we will turn on the ssh keys agent, which is a process that do all the magic behind the scenes.
Then, we will add our private key to that agent.
Finally, we will add the public key on GitHub.
Let’s do it.
Things are kind of different in Mac. So let’s start with windows and linux:
# Generate ssh keys
$ ssh-keygen -t rsa -b 4096 -C "your@email.com"
# Turn on ssh keys agent in your machine
$ eval $(ssh-agent -s)
# Add keys to the agent
$ ssh-add ~/.ssh/id_rsa
Now for mac users:
# Generate ssh keys
$ ssh-keygen -t rsa -b 4096 -C "your@email.com"
# Turn on ssh keys agent in your machine
$ eval "$(ssh-agent -s)"
# Create a config file inside the new .ssh folder
$ touch ~/.ssh/config
# Add this content to the file.
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_rsa
# Add keys to the agent
$ ssh-add -K ~/.ssh/id_rsa
Now, let’s add the public key on GitHub. First, copy the content of the public key. You can see it with cat
.
$ cat ~/.ssh/id_rsa.pub
There is a specific menu for ssh into Github settings: https://github.com/settings/keys.
Add a SSH key, put a title and paste your public key.
Title usually is the model of the computer.
Pulling and pushing code
Going back to the terminal and in order to be able to push your committed changes, you have to set up an origin
.
The origin
, tells git where to find the remote repository.
$ git remote add origin https://github.com/maximomartinezsoria/app.git
Remember to use your own repository’s link
Finally, let’s push our code.
The first time that you push a repository, you will need to tell git which origin to use.
$ git push -u origin master
From now on, you can keep it simple and just push without specifying anything.
$ git push
If the repository already had some code before your first push, git would throw an error. You will need to allow git to merge the local history with the upcoming history.
$ git pull origin master —allow-unrelated-histories
If you’re working with a team, they might push their code to the same repository.
Next time that you need to push, git will tell you that there is code that you don’t have in your local repository. So you will need to pull it down.
# Download changes and merge them with local code automatically
$ git pull
Gitignore
Sometimes, we need git to avoid tracking some files. For instance, we don’t want to track compiled files or node_modules
folder.
.gitignore
, is a file that tells git what not to track.
Let’s create an example:
Create a .gitignore
file in the root of your project and paste this content:
# env files and node_modules folder should not be tracked by git
.env
/node_modules
Now, add everything with git add -A
and you'll notice that the files specified in .gitignore
are not included.
Readme
Readme files are the ones that you can see at the end of each GitHub repository. They usually contain highlights of the documentation or how to set up the project.
Here you can see an example: https://github.com/maximomartinezsoria/minimalist-spa-router/blob/master/Readme.md
Tags
Tags are what we usually use to save a reference in some part of the repository timeline.
For instance is the way that we have to reference versions.
# Create a tag
$ git tag -a tag-name commit-id
# Delete a tag locally
$ git tag -d tag-name
# Delete a tag from the remote repository
$ git tag -d tag-name
$ git push origin :refs/tags/tag-name.
# Show tags
$ git tag
# Show tags with more info
$ git show-ref --tags
# Push tags
$ git push origin --tags
Rebase
Previously in this article, we used merge
to join two branches. If you take a look at the log, you will see both branches, the commits on them and the point where they were merged.
With rebase
, we are able to merge two branches and erase the history.
What rebase
actually does is to show the commits that were made in a branch as if they were made in another.
It's quite simple to get that result. Just make two rebases, one from the branch that you don't want anymore and another from the branch that is going to keep all the changes.
$ git checkout experiment
$ git reabase master
$ git checkout master
$ git rebase experiment
Keep in mind that we use Git in order to have a complete history of all our project's changes. Using this kind of things might be considered as a bad practice, because you are changing that history and making it not completely real.
Stash
Imagine this situation:
You are working in a feature. You coded a lot. Eventually, you need to take a look at something in another branch, but you are not ready to commit your changes. So, what can you do?
Stash
allows us to save current not commited changes in a temporary place.
# Save not commited changes in a temporary place
$ git stash
Now, you can go to another branch and do whatever you want.
When you want to bring those changes back, you just pop them.
# Bring stashed changes back
$ git stash pop
You can also see the list of all your stashed code and erase it if you don't need that code anymore.
# List stashs
$ git stash list
# Erase stashs
$ git stash drop
Another cool thing about stash, is that you can take your current changes and go to another branch without a commit.
# Create a new branch called 'new-feature' with the current changes.
$ git stash branch new-feature
Removing files
Git clean, delete those new files that are not tracked yet.
# See what files will be deleted but don't erase anything
$ git clean --dry-run
# Delete the files listed by the previous command
$ git clean -f
Git rm, allows us to delete tracked files.
# Delete file from staging area but keep it in your computer
$ git rm --cached
# Delete file from staging area and from your computer
$ git rm --force
Git cherry-pick
Imagine that you are working in that awesome new feature. As a good practice, you are coding in a new branch. Eventually, you need to deploy one commit of that branch. But you haven’t finished your new feature yet. So you need to merge your feature branch with master but want only one commit.
It's kind of a weird situation, but it could happen. Git, solves this problem with something called cherry-pick
# Look for the id of a commit. Remember that you should run this command into the branch where the commit has been done.
$ git log
# Go to master branch
$ git checkout master
# Merge only one commit of a branch to master
# That strange string is the id of a commit 🙂
$ git cherry-pick 4fd534
There you go. Now you should see that commit in the master's log.
Just like with rebase, be carefull with this command and remember that you are changing the history.
Amending commits
Amend a commit, means that you commit a change to the very last commit that you did.
Let's say that you wrote lots of console.logs
but forgot to erase them before the commit.
Ok, it's not a problem. Go back to the code, erase them and commit again:
# add changes
$ git add -A
# amend last commit
$ git commit --amend
Take a look at the log and you'll see only one commit.
Clone
Git has been made for teams.
If you want to get into a project that has already been started, you will need to clone it.
# Example with SSH
$ git clone git@github.com:user/repository.git
# Example with HTTPS
$ git clone https://github.com/user/repository.git
Note: you will find the link in GitHub, GitLab, Bitbucket or the service where the project is hosted.
Git reflog
There is a command that remembers everything. Even though you have used rebase, cherry pick or another of those commands that changes the history shown in git log; git rebase knows that you've done all of that crazy things.
$ git reflog
Search
Git allows us to search throughout our project with 2 commands.
For searching in the files: git grep
.
For searching in commits: git log -S
.
# Look for all p tags
$ git grep "<p>"
# Look for the commit where we change colors
$ git log -S "change colors"
Some interesting flags for grep command:
-n
: returns the specific line where the word is.-c
: counts how many times was the word written.
Complete log
We've already talked about git log, but it is much more powerful than expected.
Let's see some flags:
--oneline
: shows each result in one line. Useful to see more information in less space.--all
: shows the logs of all the references: branches, tags.--graph
: draws lines to illustrate the relationship of the different branches.--decorate
: a little bit of colors for the graph.
Now, let's put it all together.
$ git log --all --graph --decorate --oneline
Aliases
Git allows us to create an alias for a command.
If you know about linux aliases, it's the same idea.
$ git config --global alias.name "command"
# Example
$ git config --global alias.tree "log --all --graph --decorate --oneline"
# Trying it
$ git tree
Git is really big
You have now a really good understanding of git and how to use it. But Git is really big. I encourage you to read the docs to learn a lot more about it.