Introduction
Hey, friends! Welcome back, and thank you for joining me. At this point, we’ve created a solid basis for using Git on a daily basis. So far, we’ve installed Git, learned how to use the GUI, gained experience with Git Bash, and resolved conflicts between files. However, there’s another aspect of Git workflows we’ve yet to talk about: working with Git branches.
The Git philosophy
As we discussed in our last tutorial, Git is a very powerful tool for version control as well as paired (or team) programming. Essentially, the philosophy behind Git version control is this:
- Update often, and push often.
- Comment your commits.
- New capabilities should be developed on a dedicated branch.
- When the branch has been validated, it can be merged back into main.
The latter two points go hand in hand, and they become a useful technique for individual developers as well as development teams. Not only does it help preserve the main branch in case your code starts to go sideways, but it also ensures that the main branch only gets edited / added to when needed.
What is a branch?
Imagine your main repository as the trunk of a tree, labeled here as A
. At some point, your repository starts its existence and continues through time.
A-----A
Then, you decide to expand your code base and add a new feature; let’s call this feature B
. To adhere to the Git philosophy, we’ll create a new branch to preserve the purity (and functionality!) of the main branch, A
. Note that the main branch A
and the new branch B
exist in parallel; modifying files on branch B
will not change the files in the main branch A
until we merge our branch B
into main branch A
.
B--------B
A-----/---------A
After we finish developing our new feature, we’ll make sure to validate all of its edge cases, and maybe add some unit tests. Once we’re satisfied with its development, we’ll merge the new branch (B
) back into the main branch (A
). When we do, the feature branch B will no longer exist, and we will be left with only the main branch, A
.
B--------
A-----/---------\-------A
Using this technique, our main branch ends up being modified just the same, but it did so in a very incremental fashion — when we merged our feature branch into the main branch, we added one whole, complete feature to our main branch.
The alternative method — not using a branch — would have resulted in a main branch that breaks during the new feature development. If someone were to pull the main branch, then, they would receive a non-working copy of our code.
Creating a new branch
Similar to our last tutorial, let’s start by pulling our repository from GitHub using Git Bash:
- Open Git Bash (Start -> Git -> Git Bash)
- Change to the desired directory:
cd Desktop\
- Clone the GitHub repository to the specified directory:
git clone <GitHub repo. address>
- Enter the cloned repository
cd GrowWithGit\
- Show existing repository branches:
git branch
- Create a new branch
git branch <new branch name>
- Re-query existing branches (should show our new branch!)
git branch
Okay, let’s take a break and see what we’ve done so far. The first four commands are nothing new to us — we simple cloned our repository from GitHub and moved into that folder. The last two commands explicitly deal with Git’s branching capabilities.
As we stated in our first “graphic”, we start with a single branch — the main
branch (or, in our diagram, branch A
). When we call the git branch command, this is exactly what Git tells us: only one branch exists, and it’s called
.main
In our last command, we create a new branch; we label this branch “branch_B”. After we create the new branch, we re-query Git for the existing branches within our local repository. Git correctly shows us that we now have two branches, main
and branch_B
. For clarity, I explicitly show the commands and output from Git below:
# Check current directory
$ pwd
/c/Users/apung
# Change to my Windows Desktop
$ cd Desktop/
# Clone the GitHub repository to my current directory
$ git clone https://github.com/ajpung/GrowWithGit.git
Cloning into 'GrowWithGit'...
remote: Enumerating objects: 35, done.
remote: Counting objects: 100% (35/35), done.
remote: Compressing objects: 100% (32/32), done.
remote: Total 35 (delta 6), reused 9 (delta 1), pack-reused 0
Receiving objects: 100% (35/35), 7.33 KiB | 2.44 MiB/s, done.
Resolving deltas: 100% (6/6), done.
# Enter the new repository directory
$ cd GrowWithGit/
# List files within the newly cloned repository
$ ls
README.md demo.jl
# Show all existing branches of the cloned repo
$ git branch
* main
# Create a new branch called "branch_B"
$ git branch branch_B
# Show that the new branch exists alongside `main`
$ git branch
branch_B
* main
Working within a branch
Now that we have our two branches, let’s poke around and see how changing the file in one branch effects files in the other. The star (*) beside
tells us that we are currently on the main
branch. To change branches, all we need to do is use Git’s main
checkout
command. In our case, we can switch to branch_B
using the following command: git checkout branch_B
.
# Query existing Git branches
$ git branch
branch_B
* main
# Switch to different branch
$ git checkout branch_B
Switched to branch 'branch_B'
# Confirm branch change
$ git branch
* branch_B
main
Additionally, Git Bash is smart enough to know that we now have multiple branches we could be using. For clarity, Git places the name of our current branch on the outside of our current directory.
Within branch_B
, the list (ls
) command tells us that we have the same files available to us, README.md
and demo.jl
. To modify the README.md file, we’ll use Vim to open the file (vim README.md
), we’ll start editing (i
), and enter a new statement. After adding the statement, we’ll save the changes and quit Vim (:wq
).
---------------- (In Git Bash) -----------------apung@c02310 MINGW64 ~/Desktop/GrowWithGit (branch_B)
$ vim README.md
------------------- (In Vim) -------------------
# GrowWithGit
Growing with Git demo
A new comment, saved on 8/4/2022.
I'm your colleague. I updated!
<<<<<<< HEAD
How's your year?
Today is a new day!
>>>>>>> 3edd9f59c19e2342d9f454aabf22fef88362c5ff
At this point, we need to be very careful of one specific point: We have only changed the README file on branch_B
, and not the main
branch. Although we saved changes while we were on branch_B
, we will get a very confusing answer if we switch to the main branch and look at the same file:
# Confirm current branch (branch_B)
$ git branch
* branch_B
main
# Show contents of the README.md file
$ cat README.md
# GrowWithGit
Growing with Git demo
A new comment, saved on 8/4/2022.
I'm your colleague. I updated!
<<<<<<< HEAD
How's your year?
Today is a new day!
>>>>>>> 3edd9f59c19e2342d9f454aabf22fef88362c5ff
# Switch to main branch
$ git checkout main
Switched to branch 'main'
M README.md
Your branch is up to date with 'origin/main'.
# Confirm switch to main branch
$ git branch
branch_B
* main
# Show contents of README.md file in main branch
$ cat README.md
# GrowWithGit
Growing with Git demo
A new comment, saved on 8/4/2022.
I'm your colleague. I updated!
<<<<<<< HEAD
How's your year?
Today is a new day!
>>>>>>> 3edd9f59c19e2342d9f454aabf22fef88362c5ff
Wait, WHAT?! Why are the changes we made in branch_B
now showing up in the main
branch? It turns out, that our editor, Vim, still has an open reference to the file we just modified. It’s not that the README in main
has been replaced, but when we ask Git to open the file, it uses the same reference as it used when it let us modify the README in branch_B
…this is why it looks the same. In some sense, we’re looking at the exact same file, and not the file we think we’re looking at.
So what’s the fix? If we return to our roots and remember that any time we make changes we need to commit
the changes in order for the files to actually be overwritten in our local repository, then things become considerably less confusing.
In the steps below, let’s do the same commands (after we close our README document), except this time we’ll stage (add
) and commit
our changes to branch_B
.
# Switch to branch_B
$ git checkout branch_B
Switched to branch 'branch_B'
M README.md
# Confirm branch change
$ git branch
* branch_B
main
# Edit/save the README document using Vim
$ vim README.md
# Check status of files in repo on branch_B
$ git status
On branch branch_B
Changes not staged for commit:
modified: README.md
# Stage all changed files
$ git add .
# Commit changed files to local repository
$ git commit -m "Changed the README file on the branch."
[branch_B 7a9fcbc] Changed the README file on the branch.
1 file changed, 1 insertion(+), 1 deletion(-)
# Check that we're still on branch_B
$ git branch
* branch_B
main
# Look at the README file on branch_B
$ cat README.md
# GrowWithGit
Growing with Git demo
A new comment, saved on 8/4/2022.
I'm your colleague. I updated!
<<<<<<< HEAD
How's your year?
Today is a new day!
>>>>>>> 3edd9f59c19e2342d9f454aabf22fef88362c5ff
# Switch over to the main branch
$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
# Confirm that we're on the main branch
$ git branch
branch_B
* main
# Look at the README file on the main branch
$ cat README.md
# GrowWithGit
Growing with Git demo
A new comment, saved on 8/4/2022.
I'm your colleague. I updated!
<<<<<<< HEAD
How's your year?
>>>>>>> 3edd9f59c19e2342d9f454aabf22fef88362c5ff
Merging branches
Perfect! So now we’ve confirmed that we are able to create a new branch, switch over to the new branch, edit a file on the new branch, and commit that file to the new branch without effecting the main branch. So what now?
In order to merge our feature branch (branch_B) into our main branch (main), we want to do the following:
- Use
git checkout
to switch to the branch you want to merge into.- This branch is typically the
main
branch.
- This branch is typically the
- Use
git merge
and specify the name of the other branch to bring into this branch.
Following these steps, our input/output look something like this:
# Confirm current branch as main branch
$ git branch
branch_B
* main
# Merge feature branch **into** main
$ git merge branch_B
Updating 3efe022..7a9fcbc
Fast-forward
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
# Confirm `main` README was updated
$ cat README.md
# GrowWithGit
Growing with Git demo
A new comment, saved on 8/4/2022.
I'm your colleague. I updated!
<<<<<<< HEAD
How's your year?
Today is a new day!
>>>>>>> 3edd9f59c19e2342d9f454aabf22fef88362c5ff
# We no longer need the feature branch. Delete it.
$ git branch -d branch_B
Deleted branch branch_B (was 7a9fcbc).
# Confirm deletion of feature branch
$ git branch
* main
Fantastic! So by merging our feature branch (branch_B
) into our main branch (main
), we’ve confirmed that changes we made in our feature branch were implemented in our main branch — just what we wanted! At this point, we have no need for the feature branch, so we delete it, then confirm that we only have one branch left — the main
branch. In doing so, we reach the third visual we discussed in our section “What is a branch?”
But there’s still one last thing to do! We need to take a step back and realize that all this work was done on our local directory — any changes we’ve made were all local, and still need to pushed to the GitHub repository for our changes to persist for other developers. From here, it’s a cake walk. We simply check the status of our local repo (git status
), then push our updated files to the online repository (git push origin main
).
# Check status of our local repository
$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
# Push our local changes to the online repository
$ git push origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 347 bytes | 347.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/ajpung/GrowWithGit.git
3efe022..7a9fcbc main -> main
Conclusion
Well done, crew! Another Git tutorial under our belt. In this session, we took another look at another highly enabling feature of Git: using branches. After pulling a repository from GitHub, we were able to create a new branch, edit a file on the branch, and show that the changes we made did not impact files on our main branch. We then merged the feature branch back into the main branch, did some house cleaning, and pushed our updated (local) repository back up to our online repository!
Thank you again for dropping by! I look forward to seeing where we got next in our Git series, and I hope you’ll join me there! As usual, please feel free to subscribe for updates, like, or comment on my material. Have a great week, friends!
Get new content delivered directly to your inbox.