Growing with Git — Branches

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 main tells us that we are currently on the main branch. To change branches, all we need to do is use Git’s 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.
  • 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.

%d bloggers like this: