In the first article I talk about the basic of Git versioning.
In this article we'll talk about a revolutionary feature of Git: branch.
What problem does Git branch solves?
Back to our Google Slides example.
To solve the accidentally changed problem, anyone want to the edit group's slide would have to do this:
- Create a copy of the group's Google Slides file
- Work on that copied file
- Ask the leader to review
- Only when the leader agree that changes are merged into the group's file
In order to preserve the original file, anyone wants to work on that file must create a copy of it. In the Git world, it's everyone must create a new branch.
Wait, you said. If 1 people create a copy, working on it, and then the leader upload that into Google Drive is fine, but what if 3 people do it at the same time?
Does the leader have to meticulously copy and paste all changes of each file into a new file and then upload?
In the development world, don't worry, Git will handle that for us!
Call git merge
and Git will automatically merge all changes for us!
If 2 people accidentally make change in the same line of the same file, Git will notify you about that "conflict".
To recap:
- Create a copy = create a new branch
- Merge into one file = merge branch
- 2 people change the same place = conflict (and then have to resolve conflict)
Let's learn how to do all of that with Git in this article.
Basic workflow
In this section, let's talk about what would you do when leader assign you a new task (aka basic workflow).
By default, you're on branch master
. For now let's imagine the master
branch is like the original file that shares to the whole group.
If you have a new task, here's what you need to do:
- Create a new branch (= copy to a new file)
- Do the task then
git add
andgit commit -m
(= create a new version) - Merge back into the
master
branch (= merge back into the original file)
Let's apply that workflow with the previous project in part 1.
View, create, merge branch
In part 1, we created a small project to learn about Git. Here's the current state of that project.
To see what branch that we're on, use the following command:
git branch
Here's the result of mine:
* master
This meant that I'm in branch master
.
Imagine you have a new task: add to index.js
a console.log("bye there")
.
Following the workflow, here are steps to take:
- Create a new branch (= copy to a new file)
- Add to
index.js
aconsole.log("bye there")
(do the job) -
git add
andgit commit -m
(= create a new version) - Merge back into the
master
branch (= merge back into the original file)
Step 1, let's create a new branch. To create a new branch, use git checkout -b
and then name of the branch.
I'll call this branch feature/bye_there
.
git checkout -b feature/bye_there
It's easy to get lost on which branch are we on so let's create a good habit to check what branch are we currently on:
git branch
Here's mine:
* feature/bye_there
master
This meant that currently I have 2 branch master
and feature/bye_there
and I'm in branch feature/bye_there
.
In index.js
, add a console.log("bye there")
:
console.log("hello there");
console.log("bye there");
Now let's commit it:
git add index.js
then
git commit -m "[feature] add bye there"
Here's a diagram explain what we did so far:
Great! We complete this feature and then commit it. Next, let's merge it into the master
branch. In order to do that, we have to take 2 steps:
- Switch to
master
branch:git checkout master
- Merge
feature/bye_there
intomaster
:git merge feature/bye_there
So first, let's switch back to the master
branch:
git checkout master
Next, let's merge the feature/bye_there
branch into master
:
git merge feature/bye_there
If you open index.js
you'll see:
console.log("hello there");
console.log("bye there");
We're successfully merge feature/bye_there
into master
. Here's a little more explanation about git merge
.
When you tell git to merge feature/bye_there
into master
, git will apply all the changes you made in feature/bye_there
into master
.
What changes are we made in feature/bye_there
? It's:
- In
index.js
add line 2 writingconsole.log("bye there")
So here's what git does:
- Apply change: in
index.js
add line 2 writingconsole.log("bye there")
- Add and commit on
master
branch
To recap, here's 5 git commands step-by-step to complete a new task:
git checkout -b [new branch]
git add [file name]
git commit -m [message]
git checkout master
git merge [new branch]
Conflict and resolve conflict
Now you get the basic idea of what to do when having a new task, let's talk about conflict. When do conflict happen and how to resolve conflict? In this section we'll create a situation when conflict occurs and then resolve it.
Imagine you have 2 people that receive 2 new tasks at the same time:
- Task #1: Add
console.log(1)
inmain.js
file - Task #2: Add
console.log(2)
inmain.js
file
This 2 people will create new branch at the same time and do their work at the same time instead of one after another. Here's a diagram explains the different between doing multiple tasks at the same time versus to do it sequentially:
To simulate this, we'll create new branch feature/print_1
, do the task and then git add
and git commit -m
, but not immediately merge into master
, instead from master
create feature/print_2
, do the task and commit on that branch, and only then merge feature/print_1
and feature/print_2
into master
.
If that's a little bit... hard to understand, below it's a diagram explain what we're going to do:
Let's tackle task #1 first. Here are the steps we need to take:
- Create a new branch
- Add file
main.js
withconsole.log(1)
-
git add
andgit commit -m
First, let's create a good habit of checking which branch we're on:
git branch
Here's my result:
feature/bye_there
* master
This means that I'm on the master
branch. Make sure you're on the master
branch before continue.
Next, let's create a new branch called feature/print_1
.
git checkout -b feature/print_1
Let's check what branch are we on:
git branch
Here's my result:
feature/bye_there
* feature/print_1
master
This means that we have 3 branches and I'm on branch feature/print_1
.
Now, complete task #1 by create main.js
file with the following content:
console.log(1)
Now, let's commit this file:
git add main.js
then
git commit -m "[feature] add console log 1"
Normally the last step is to merge feature/print_1
into the master
branch but as we're not going to do it right now. We're simulating 2 people create new branch at the same time and working at the same time, remember?
We've already complete task #1. Let's tackle the task #2.
Here are the steps we need to take:
- Switch to
master
branch - Create a new branch
- Add file
main.js
withconsole.log(2)
First, let's switch to the master
branch:
git checkout master
Now you'll see the main.js
file disappear because in master
branch that file does not exist yet.
Let's create a new branch called feature/print_2
:
git checkout -b feature/print_2
Let's create a good habit of checking what branch are we on:
git branch
Here's my result:
feature/bye_there
feature/print_1
* feature/print_2
master
This means that we have 4 branches and I'm on branch feature/print_2
.
Now, let's do the task #2 by create main.js
file with the following content:
console.log(2)
Next, let's commit this file:
git add main.js
then
git commit -m "[feature] add console log 2"
That's great! We've complete task #2.
Now let's head back to the master
branch and merge feature/print_1
and feature/print_2
into it.
git checkout master
Let's merge feature/print_1
into master
:
git merge feature/print_1
Next, let's merge feature/print_2
into master
:
git merge feature/print_2
This is when conflict occurs. You'll see the following in the command line:
Auto-merging main.js
CONFLICT (add/add): Merge conflict in main.js
Automatic merge failed; fix conflicts and then commit the result.
We have a conflict in main.js
. If you open main.js
, you'll see this:
<<<<<<< HEAD
console.log(1);
=======
console.log(2);
>>>>>>> feature/print_2
If you using VSCode, here's what you see:
Git marks which place have conflict for us by <<<< HEAD
, ======
, >>>>> feature/print_2
. Our job is to make the file correct according to what we're assigned.
But first let's explain why do we have conflict in main.js
, what is "HEAD (current change)" and "incoming change" means.
In short, conflict happened because both feature/print_1
and feature/print_2
in the same main.js
file in the same line have different content (more exactly is changed differently). In feature/print_1
is console.log(1)
. In feature/print_2
is console.log(2)
.
The HEAD refer to which branch we're now: master
branch, while "Incoming change" refer to the branch we want to merge into master
: feature/print_2
.
Here's a more detailed explanation:
First, when we merge feature/print_1
into master
, git will apply the changes in feature/print_1
into master
. So here's what git does:
- Create a new
main.js
file - On that file
main.js
on line 1 writeconsole.log(1)
- Add and commit (in
master
branch)
Next, when we merge feature/print_2
into master
, git will also apply the changes in feature/print_2
into master
. So here's what git tried to do:
- Create a new file
main.js
(already done!) - On
main.js
on line 1 writeconsole.log(2)
- Add and commit (in
master
branch)
But on main.js
line 1 already have console.log(1)
. This is why conflict happened.
So how do we resolve this conflict? In this situation, we have to do both tasks so we should keep both console.log
s.
In main.js
remove the <<<<< HEAD
, =====
, >>>>> feature/print_2
added by git:
console.log(1);
console.log(2);
Now the main.js
file have both console.log
, which is correct for we have to do both task #1 and task #2. Now that we resolve conflict, let's add and commit the main.js
file:
git add main.js
then
git commit -m "Merge branch 'feature/print_2'"
That's it! That's how we resolve conflict!
Explain strange commands from part 1
In part 1, I told you about 2 strange command git checkout master
and git switch -c
. Now I'll explain it.
By now you probably know that first command git checkout master
is to switch to master
branch. But not only you can use it to switch from one branch to another but also to escape preview mode too. If you're on feature/print_1
branch and using git checkout [version hash]
to preview previous, you can use git checkout feature/print_1
to escape it!
The command git switch -c
is to create a new branch from that commit and switch to that branch. That way, you don't have to use git reset --hard
which lose all commits after that.
A fun thought experiment
So that's it for part 2. You've done a great job learning all the basic workflow and commands of git! You can reward yourself a good cup of coffee now!
But I have a bonus thought exercise to get you more familiar with the concept of branch and basic commands of git.
Have you ever seen this meme?
Have you ever heard your designer friend complains about he spend hours to change his design for his customer just to be told: "Hey, I changed my mind, I think the original design is better"? Worse, he had to edit it back into the original design because the amount Ctrl + Z 's are limited.
Now you're a developer and you know Git, what would you hypothetically do if you're in this situation:
- The customer asks you to edit her photo
- Then she asks you to add more dramatic lighting
- Then she asks you to use Liquidfy to make her look thinner
- Then she kind of regret it and asks you to revert it (oh no it looks so fake!)
- Then she asks you to make her hair pink
- Then she asks you to make her hair blue to see which one is better
- Then she says she like pink hair
- Then she asks you to make her eyes look bigger
- She satisfies now, pays you and thanks for your service
If you don't know what Liquidfy does, I have this image which will explain for you:
So with that little bit of Liquidfy technical detail out of the way, hypothetically how can we solve this using git?
Here's a diagram explains what we're going to do:
Assume we have a Photoshop file called cute-photo.psd
:
- The customer asks you to edit her photo
git init
git add cute-photo.psd
git commit -m "initial commit"
- Then she asks you to add more dramatic lighting
git checkout -b feature/dramatic_lighting
git add cute-photo.psd
git commit -m "[feature] add dramatic lighting"
git checkout master
git merge feature/dramatic_lighting
- Then she asks you to use Liquidfy to make her look thinner
git checkout -b feature/liquidfy
git add cute-photo.psd
git commit -m "[feature] liquidfy thinner"
git checkout master
git merge feature/liquidfy
- Then she kind of regret it and asks you to revert it (oh no it looks so fake!)
git reset --hard [commit-hash]
- Then she asks you to make her hair pink
git checkout -b feature/hair_pink
git add cute-photo.psd
git commit -m "[feature] change hair to pink"
- Then she asks you to make her hair blue to see which one is better
git checkout master
git checkout -b feature/hair_blue
git add cute-photo.psd
git commit -m "[feature] change hair to blue
- Then she says she like pink hair
git checkout master
git merge feature/hair_pink
- Then she asks you to make her eyes look bigger
git checkout -b feature/eyes_bigger
git add cute-photo.psd
git commit -m "[feature] make eyes bigger"
git checkout master
git merge feature/eyes_bigger
- She satisfy now, pays you and thanks for your service
You might ask why don't people use Git in design space like Photoshop, Illustrator, Blender, Maya,... Is git only works for text files or something?
No it's not. You can definitely version control a .psd or an .ai,... file. If you git add
, git commit -m
,... it works! The reason people don't do it is that if conflict happen, you can't fix it. Photoshop file is a binary file, which is a bunch of 0's and 1's, you can't read and understand it, let alone resolve conflict.
Conclusion
Let's recap what we've learned so far:
-
git branch
: view current branch we're in -
git checkout [branch name]
: switch to that branch -
git checkout -b [new branch]
: create new branch -
git merge [branch name]
: merge that branch into current branch we're in - Conflict and how to resolve conflict
In part 3 we'll learn about how to work with remote repository like Github, Gitlab: git push
, git pull
, git clone
,...
Credits
If you like the cute fish that I'm using, check out: https://thenounproject.com/browse/collection-icon/stripe-emotions-106667/.
Top comments (0)