This lecture aims at providing a brief review of Version Control Systems, why they are needed, how they work, and the most prominent and popular version control software that is available today for professional collaborative programming.

Motivation

When I started scientific research and programming in the early years of my graduate studies, the directory containing my first project looked something like the following figure.

An example of bad coding style and poor project maintenance; The figure shows part of the content of a directory dedicated to a research project. Each folder contains one version of the project and its corresponding codes and results at a given time (i.e., one deliverable). Obviously, tracking minor development stages of the project leading to each of the deliverables would take a lot of work with such a project management method.

Clearly, there are many disadvantages and caveats with maintaining the structure of your project and codes similar to the content of the above figure:

  1. Data redundancy and storage inefficiency
    Every time you want to develop your existing project and take it to the next level, you have to make a complete copy of the last version of your project to start the new development (i.e., the next version of your project). Obviously, the new version of your project will have a lot in common with the previous version. But, each one has its dedicated separate folder. So, there is a lot of code and data redundancy in using such an approach to keeping track of your project’s version history.

  2. High risk of errors and irreversible mistakes
    It is easy to forget which directory you are in using this method and to write to the wrong file accidentally or to overwrite files you did not mean to mistakenly.

  3. Difficulty in tracking minor developments in the project
    In professional programming, all minor developments and changes to a project must be tracked and well-documented (only the major versions of the project (called deliverable in programming terminology) are supposed to be available to non-developers or customers. Therefore, one has to create a new complete copy of the project with its dedicated directory to keep track of every minor stage of the program development. However, since this is practically impossible, the programmer or the researcher has to either skip documenting all minor improvements and, therefore, lose track of detailed changes in the code or otherwise worsen the first caveat mentioned above by generating more and more redundant copies of the same codes and data for the sake of keeping track of all minor stages of the development.

  4. Ambiguity in version differences
    With this primitive method of project version control, it is not clear what the main differences between different versions of the project are unless very detailed documentation is provided along with each version detailing the differences between the current, its ancestor (i.e., the previous version of the project) and its descendant (i.e., the next version of the project), or parallel independent versions of the project (i.e., the project branches).

  5. Sharing and collaboration difficulties
    Since the entire project is locally stored and accessible to one programmer or researcher, any collaborative effort will be extremely difficult, and require manual duplication and syncing of the project with other developers.

  6. Potential risk of complete data loss:
    Since the project is stored locally, on a single computer or storage device, there is always a high risk of losing the entire project due to hardware failure or other unpredicted events.

Fortunately, several smart, powerful solutions and software have been developed, collectively known as Version Control System (VCS) for project management, in particular, since the dawn of the new millennium.

What is Version Control System (VCS)?

Version control is a system (e.g., software) that records changes to a file or set of files over time so that you can recall specific versions later.1 Depending on how the project is maintained and shared with others, VCS is classified into three categories:

  1. Local VCS
    The simplest form of project version control, and probably the oldest method, is local VCS where the VCS software keeps detailed track of the evolution of individual files in the project only on a local storage device or computer. A prime example of such version control software is the GNU Revision Control System (RCS). Such VCS is able to avoid problems 1-4 mentioned above. The following figure illustrates the inner workings of a local VCS.

    Local Version Control

    Major Caveats:
    1. There is no easy way for collaboration and synchronization between team members.
    2. There is a potential risk of complete data loss.


  2. Centralized VCS (CVCS)
    Since collaborative effort is at the heart of almost every large-scale project, a new generation of VCS has been developed that go beyond the capabilities of local VCS. These VCSs have a central repository that is accessible to all members of the project’s team. This central repository contains all of the project’s information, and its development history, and clients (team members) can check out and work on individual files of the project. For many years this style of VCS used to be the dominant method of software version control. Prime examples include Concurrent Versions System (CVS), Perforce Helix and Apache Subversion(SVN). The following figure illustrates the inner workings of a CVCS.

    Centralized Version Control

    Major Caveats:
    1. Since the central database of the project is centralized, and team members have only snapshots of a specific version of the project, any failure in the central database would halt the individual and the collaborative works of all team members as no one can submit further developments to the central database.
    2. Also, if the central database fails with no backups, then the entire history of the project is lost.

  3. Distributed VCS (DVCS)
    In order to resolve the caveats of the VCS above, several new VCS, known as distributed VCS, have been developed within the first decade of the new millennium. With these VCS, each client has a copy of the central database at their local storage device. Thus, every clone of the central repository with each client is a full backup of the project’s data and history. Consequently, if the central server/repository fails for any reason, the local repository of the team member that has the most recent clone of the project database can be used to restore the central repository. This VCS paradigm is currently the most popular class of VCS. We will be using a major implementation of it known as Git here. Other major DVCS implementations include Mercurial, Bazaar, and Darcs. The following figure illustrates the inner working of a DVCS.

    Distributed Version Control

A complete list of all major VCS software implementations can be found here.

Git/Mercurial version control system

Git is a DVCS that grew out of frustration between the Linux kernel developing community and a proprietary VCS software company that maintained and indexed Linux kernel development. Similar to another major DVCS implementation Mercurial, Git is designed with the following goals in mind:

  • Speed.
  • Simple design.
  • Strong support for non-linear development (thousands of parallel branches).
  • Fully distributed.
  • Able to handle large projects like the Linux kernel efficiently (speed and data size).

Git/Mercurial project indexing method

VCS software, such as SVN and Bazaar, indexes the new developments of a given project by tracking the changes in individual project files, similar to the diagram below.

File-based project indexing method common to some VCS software such as CVS, Subversion, Perforce, and Bazaar. (Diagram is adopted from Git documentation).

By contrast, both Git and Mercurial generate a complete snapshot of the project at any moment the project is committed to the local repository for version control. Even if some files have not changed in the new version of the project, they will still exist in the newly generated version. However, both Git and Mercurial have been designed smart enough to realize changes to individual files and then store the files effectively without redundancy. Git and Mercurial generate the project’s complete history by a series of snapshots, a flow similar to the following illustration.

Snapshot-based project indexing method that is used in Git and Mercurial VCS software. (Diagram is adopted from Git documentation).

Each project’s file in Git/Mercurial indexing process can have one of the three possible states:

  1. modified, but not staged: This is when the user modifies a project file, but Git/Mercurial has no track of the file changes. If the file is lost or removed unexpectedly, then Git cannot recover the file.

  2. staged for commit to the repository: When the developer modifies a file, they can add it to the Git staging area to be later committed permanently to the repository. The staging area is a file, generally contained in the project’s repository (.git directory), that stores information about what will go into the next commit to the repository. The staging area is also sometimes referred to as the index.

  3. committed to the repository: Once the staged files are committed to the repository, they become a permanent part of it. They can be later extracted (i.e., checked out) for review or further development.

These three file states comprise an essential integral part of Git and Mercurial. The following figure illustrates these three file states.

A diagram illustrating the project-indexing process of Git and Mercurial VCS software. Each project file can reside in three different states: working directory, staging area, or in the local repository. (Diagram is adopted from Git documentation).

Remote repository options for your Git/Mercurial projects

Git can be downloaded and installed from here for Windows, Mac, or Linux systems. The installation guide is available here. Mercurial can be downloaded and installed from here for Windows, Mac, or other operating systems. For collaborative projects, you will need a central (remote) project repository to which all team members submit their latest developments. Even if you are working on a project alone, it is always a good idea to submit all of your project developments to a remote repository to avoid unpredicted data loss and publicly showcase your work. Three popular online repositories for version-controlled projects are:

  1. GitHub, which is also an Internet hosting service, works exclusively for projects that are indexed using Git VCS. Today, all public projects (visible to everyone on the web) on GitHub are free-of-charge. For private projects, you will have to pay a monthly fee. However, GitHub Student offers a micro-account especially for students with five private repositories free of charge (normally $7/month) while you’re a student.

  2. Bitbucket, which used to exclusively host Mercurial-indexed projects. But since 2011, it can also host Git-indexed projects. Currently, Bitbucket stores all public or private projects (up to 5 users) free of charge.
  3. GitLab, is another rather-new Git-repository manager that provides developer tools comparable wth GitHub and even more. At the moment, it is not as popular as GitHub. However, it is used by many major companies and industries worldwide for software version control (e.g., NASA, IBM, Boeing, CERN, Oracle, Sony, …).

GitHub and Bitbucket are particularly useful for education as they provide notable services and options for educational accounts and purposes. The following is a concise comparison between the general features and services of GitHub and Bitbucket.


Feature Bitbucket GitHub
Supported VCS Mercurial, Git Git
Public repos Free, unlimited Free, unlimited
Private repos Free up to 5 users Starts at $7/month for unlimited users
Popular projects hosted Adium, Mailchimp, Opera, Python, Django Bootstrap, Node.js, jQuery, Rails, Homebrew
Notable Extra features External authentication via GitHub, Twitter, Facebook, Google Two-factor authentication, GitHub Pages, GitHub Gists

Setting up your first Git project on GitHub

Here, we will use Git as our version control software, and GitHub as our central (remote) repository. Follow the guidelines below to create your first Git project on GitHub.

  1. Go to GitHub Student and create your student account.
  2. Download and install Git on your computer. Here is a set of instructions written to simply the installation and interaction process with Git for you.

Setting up your Git identity

The first thing you should do when you install Git is to set your username and email address. This is important because every Git commit uses this information, and this information is permanently baked into the commits that will make to your project’s repository. To set your name and email globally for all Git projects once and for all on your computer, use the following Git commands:

git config --global user.name "Amir Shahmoradi"
git config --global user.email shahmoradi@utexas.edu

You need to do this only once if you pass the --global option because then Git will always use the provided information for anything you do with Git on your system. Later on, if you want to override this information with a different name or email address for some specific projects, you can run the above same Git commands, but without the --global optional flag, only when you are in your specific project’s directory.

Two methods of project initialization

Now at this point, there are two ways for project initialization. We will go through both here. A good detailed tutorial can be found here.

Remote project initialization (on GitHub)

With this method you first initialize an empty project on GitHub, and then clone the project from the remote (GitHub) repository to your local device, using the git clone command, discussed later below. To create a remote repository on GitHub, login to your GitHub account and follow the instructions on this page.

Local project initialization

Local project initialization is done by the following git command:

git init

This command will initialize an empty repository in the current directory. If you check the directory containing your repository, you will notice that a new hidden .git folder has been added to the directory. This folder will contain all of the histories of your new project. Anything in the directory where the hidden .git folder exists will be indexed as part of the project.
In order to generate the project’s repository in a different directory, use

git init <directory name>

Even if the requested directory does not exist, git will first automatically create the requested directory and initialize an empty project inside of it. That’s it! Now all you need to do is add or develop your project files inside the project folder.

Local device and remote repository SSH connection


Why is SSH needed?
Now every time may you want to clone your remote private repository to your local device or push your local repository to a remote server (your GitHub account), or basically do any communication between the local and remote repositories, Git will ask you to authenticate your connection by supplying your GitHub username and password. Note that this security check is necessary since you want to make sure everyone is able to push unwanted changes to your projects or mess with them without your prior permission. However, this rather annoying user/pass request by Git can be resolved by setting up an SSH secure connection between your local device and the remote repository.
SSH, also known as Secure Shell or Secure Socket Shell, is a network protocol that gives users a secure way to access a computer over an unsecured network. SSH may also refer to the suite of utilities that implement the SSH protocol.

How does it work?
Think of your local device as a person with a set of keys. Suppose your remote repository is a home with an entrance keyhole that matches one person’s keys. In that case, the person (i.e., your local device) can access the home’s interior (i.e., your remote repository).

How is it set up?
You can generate keys for your local device by following the instructions given here. Once you have generated the key and the keyhole, you can add a copy of the keyhole to your GitHub account by following the instructions given here.

Here is an example educational video explaining the SSH key setup process:



Main Git commands

As of today, Git has more than 150 commands. For a complete list, you can type the following on your Git bash command window,

git help -a

To see a list of all possible Git commands. Note that every Git command begins with git. Despite the long list of Git commands, you will likely use only five of them every day. While the rest of the commands are also important, with these five basic commands, you will be at least able to develop and index your project and communicate with your remote repository and colleagues. In order to understand these five, we will have to recall the three states of a project’s files, described earlier above.

git status: Check the status of the project

First, you can check the status of your git project at any time by typing the following command in the git-bash prompt window, assuming the command line is already in the project’s directory.

git status

git add: Add files to the staging area

In order to add any of the modified or new files to the staging area, use

git add <file name>

To add all modified files (including file removals as well as changes) to the staging area, use

git add -A

or,

git add --all

This command is equivalent to

git add .
git add -u

The first command stages the new and modified files but not the deleted files. The second command stages the modified and deleted files without the newly-created files.

The complete documentation for git add can be found here.

git commit: Commit the staged files to the local repository

The stages files can be committed (i.e., submitted permanently) to the local repository by

git commit

once this command is entered, a new page for log message will open on the command line, which contains the list of all files to be committed, and a place at the top of the page to add comments regarding this version of files that are being committed to the repository. To add a comment, press i to enter the insert mode of the vim text editor. Once you add your comment to the text file, press ESC button to exit the insert mode, and write :wq on the command line to write the comment file (i.e., save it) and quit vim text-editor.

If you use,

git commit -a

or,

git commit --all

Both commands act as a shortcut to first automatically stage files that have been modified and deleted and then commit them to the repository, but not the new files that you have not yet told Git about. Another helpful flag is -m, with which you can enter a short message on the command line as the log message of the commit, and therefore no new page will open up for entering the log message,

git commit -m "This is a test commit!"

The -a and -m flags can be combined to automatically stage and commit the files together,

git commit -am "This is a test commit!"

But keep in mind that with this staging method, the new files will neither be staged nor committed to the repository.

The complete documentation for git commit can be found here.

Using Notepad++ and Sublime along with Git to commit changes to Git repositories

In recent years, Notepad++ has become a popular general-purpose text editor on Windows operating systems. As a result, Git has recently provided users with the optional feature that enables Git commits messages to be crafted via Notepad++ software instead of the lower-level command-line text-editing environment vim. To set Notepad++ as your default text editor for Git, you will need to first download and install Notepad++. Then on the Git Bash command-line type the following commands to set the default text editor of Git to Notepad++,

git config --global core.editor "C:\Program Files (x86)\Notepad++\notepad++.exe"

Note that you must modify the address provided above to point to the location where notepad++.exe exists on your device. Notepad++ is only available on Windows systems. If you prefer to use text editors other than vim on other operating-system platforms, you can follow the same command above but change the address to point to the path of the text editor of your choice on your device. For example, if you are using Mac device and you’d like to use Sublime text editor, you can install Sublime first and then type the following command on the Git Bash command line,

git config --global core.editor "/Applications/'Sublime Text.app'/Contents/SharedSupport/bin/subl -n -w"

Again, you will have to change the example address provided in the command above, in the double-quotation marks, to the path pointing to the location where Sublime is installed on your device.

git push: Push the changes in the local repository to the remote repository

Now, to push all your commits from the local repository to the remote repository, to place them permanently there and be able to share them with others, use

git push --all

We have not yet talked about git branches, but what the above command does is that it orders Git to push all branches of the project to the remote repository. Later, we will talk more about flags that can follow git push command.

The complete documentation for git push can be found here.

git pull: Pull the latest project revision from the remote to the local repository

Sometimes, your project collaborators might be working simultaneously but independently on the project. In such cases, whenever you want to restart your work on your project, you should first pull the latest revision of the project – that is not available yet in your local repository – from the remote to your local repository. To do so, you can use

git pull

There are some essential details about git pull, which we will delve into later in the following sections.

The full documentation for git pull can be found here.

Summary of main Git commands

All in all, the most trivial, but also the most helpful git commands are the following:

git status
git add --all
git commit
git push --all

for submitting your changes to the local and remote repositories, and

git pull

for syncing your local with the remote repository. Remember that you have to use these commands in the above forms, inside the directory of the project.

Getting help with Git commands

There are three ways to request information about Git commands on the Git-bash command line:

git help <command name>
git <command name> --help
man git-<command name>

Note that the last command, man is not a Git command, but a Bash environment command. Just as a reminder, all Git commands begin with the word git.

Viewing the Git commit history

You can check the history of your project’s commits to the repository using the Git command git log. For example, here is a snippet from the output of git log on my laptop screen:

git log
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

commit b45ddd780c18736bac42ff27aade301a6b0f09cb
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 10:08:53 2017 -0600

    minor edit

commit b6f4fb69d97a396bde7159c78424100704328634
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 21:03:28 2017 -0600

    *.txt files removed

commit 961ff2d4ac68081a21864b2f1b067b43424d3342
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 21:02:00 2017 -0600

    update

commit 8fd3c49e90d96f4bcef2302c7b227f06ad7a5250
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:36:45 2017 -0600

    minor

commit cd0ed7def385ba29ea134bcc18a4ad0873d9f5ee
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:32:51 2017 -0600

    minor

commit 8096668868ef64189f788669d5f1838cd944f9b6
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:28:22 2017 -0600

    minor

commit c52ba6adea474625cce1493896ecaac40cb976af
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:24:23 2017 -0600

    minor

commit ab5a17f9e913e55cae0f971fd9b485a0a6e8dec3
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:16:27 2017 -0600

    minor

commit ce5c2421db6e76a338a0e6922acead9100a7c4c4
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:10:19 2017 -0600

    minor

commit 4a7d27d81260c2e2a29b6259e6c97befc065c069
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:05:14 2017 -0600

:

A long list of options can be used with the git log command to display exactly what you need on the Bash environment screen. Some of the most useful options are discussed below.
To lists the differences between different commits of the same file, use -p flag:

git log -p

To list only a limited count of the most recent commits, use -<number of commits to display> flag. For example:

git log -2
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

commit b45ddd780c18736bac42ff27aade301a6b0f09cb
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 10:08:53 2017 -0600

    minor edit

To list the statistics of the commits, use,

git log --stat -1
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

 README.md                |   1 +
 results/tables/README.md |   8 ++-
 results/tables/tgm.mat   | Bin 0 -> 2550889 bytes
 src/defineProject.m      |   3 +-
 src/main.m               |  82 +++++++++------------------
 src/writeTables.m        |  75 ++++++++++++++++++++++++
 9 files changed, 140 insertions(+), 56 deletions(-)


The log data can be even represented in formatted style via the flag --pretty=format: "<the desired format>``. For example,

git log -3 --pretty=format:"%h - %an, %ar : %s"
264043e - Amir Shahmoradi, 7 days ago : data tables for 1D model added.
b45ddd7 - Amir Shahmoradi, 7 days ago : minor edit
b6f4fb6 - Amir Shahmoradi, 3 weeks ago : *.txt files removed

A list of available format options is provided below:

Some useful options for git log --pretty=format:"<options>". 2
Option Description of Output
%H Commit hash
%h Abbreviated commit hash
%T Tree hash
%t Abbreviated tree hash
%P Parent hashes
%p Abbreviated parent hashes
%an Author name
%ae Author email
%ad Author date (format respects the --date=option)
%ar Author date, relative
%cn Committer name
%ce Committer email
%cd Committer date
%cr Committer date, relative
%s Subject


A list of useful options for git log is given in the table below.

Table 2: Common flags for git log command. 1
Option Description
-p Show the patch introduced with each commit.
--stat Show statistics for files modified in each commit.
--shortstat Display only the changed/insertions/deletions line from the --stat command.
--name-only Show the list of files modified after the commit information.
--name-status Show the list of files affected with added/modified/deleted information as well.
--abbrev-commit Show only the first few characters of the SHA-1 checksum instead of all 40.
--relative-date Display the date in a relative format (for example, "2 weeks ago") instead of using the full date format.
--graph Display an ASCII graph of the branch and merge history beside the log output.
--pretty Show commits in an alternate format. Options include oneline, short, full, fuller, and format (where you specify your own format).


A list of useful options for git log commands that can change the output format is given in the table below.

Table 3. Flags limiting the output of git log command. 1
Option Description
-<n> Show only the last n commits (e.g., -2, -3, ...).
--since, --after Limit the commits to those made after the specified date.
--until, --before Limit the commits to those made before the specified date.
--author Only show commits in which the author entry matches the specified string.
--committer Only show commits in which the committer entry matches the specified string.
--grep Only show commits with a commit message containing the string.
-S Only show commits adding or removing code matching the string.


For example, to list the commit history since a specific time in the past, use --since flag:

git log --since=2.weeks
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

commit b45ddd780c18736bac42ff27aade301a6b0f09cb
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 10:08:53 2017 -0600

    minor edit

or, with the following date format:

git log --since="2017-01-15"

Undoing, amending, and tagging

Suppose you want to undo the latest addition of files to the staging area of your Git repository. The command that you need to use to clean the stage area is,

git reset

This command is the opposite of git add. There are a lot of options that can accompany this Git command, details of which can be found in Git documentation. However, be careful when using this command, especially with flag --hard, which can also result in changes in your working directory, thus leading to involuntary loss of latest uncommitted files.

Another useful command is,

git commit --amend

which is used when you forget to add some files to your latest commit, and now, you would like to add the latest content of your project’s staging area to the last commit by amending it.

The Git command git tag allows you to tag specific important commits in your project’s commit history, for example, a final release of your project (e.g., software). If you just type git tag on Git bash command line, it will list all tags in the project, in alphabetical order,

git tag
v1.0
v1.1
v2.0

You can create a tag for the latest commit to your repository by using,

git tag -a v3.0 -m "new version of the project"

This will add a new tag pointing to the latest commit in the project with the name v3.0 and the message new version of the project. Now if you want to display the information about a specific tag, use git show,

git show v1.0
tag v1.0
Tagger: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 1 18:08:00 2017 -0600

first tag

commit 25129a074aece69d234838764e7df63ed1fb3023
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Fri Dec 27 17:12:08 2016 -0600

    Update README.md

You can also create a lightweight tag by avoiding -a, -s, or -m flags in your tag command,

git tag v4.0

Annotated tags (created with -a flag) are meant for significant project versions, such as a software release, while lightweight tags are meant for private or temporary object labels.
For more information about git tag, see Git documentation.

Working with remote Repositories

In order to see which remote servers you have configured for your project, you can run the following Git command,

git remote
origin

In the output above, origin is the default name that Git gives to the server from which you cloned your project. If you also specify -v flag, Git will show you the URLs that Git has stored corresponding to the remote’s short name (e.g., origin). This URL is used to read from or write to the remote repository.

git remote -v
origin  git@github.com:shahmoradi/foo.git (fetch)
origin  git@github.com:shahmoradi/foo.git (push)

To see more information about the remote repository of your project, you can use,

git remote show origin
* remote origin
  Fetch URL: git@github.com:shahmoradi/foo.git
  Push  URL: git@github.com:shahmoradi/foo.git
  HEAD branch: main
  Remote branches:
    development tracked
    main        tracked
  Local branch configured for 'git pull':
    main merges with remote main
  Local ref configured for 'git push':
    main pushes to main (up to date)

The above information indicates that the current HEAD branch of the project is on main branch, and the remote branches on the remote repository are main and development.

You can find more information about git remote command in Git documentation.

Git branching

Branching is a powerful idea in the Version Control System, which significantly facilitates collaboration or parallel development of a project. Suppose you already have a project that has reached its first release version, meaning that it works fine. Now, you like to add a new feature to your project. To do this and avoid further committing to your original project, you can create a new branch of the project devoted explicitly to adding the new feature. See the following figure for an example of Git branching.

An example illustration of Git branching for a project with three branches: main, development, topic.

When you create a Git project, the single default branch in your project is main. So, for example, if you search for your project branches, you’d see,

git branch
* main

If you have already created other branches in your project, for example, development, then you may see a Git message like the following,

git branch
* development
  main

The * behind the branch name indicates that the project HEAD (i.e., your working directory) points to that branch. This means all commits will be made to that branch highlighted by *. The working directory points to the development branch in the example above.

Creating a new branch

To create a new branch in your project, use,

git branch <branch name>

For example,

git branch development

When you create a new branch, Git creates an exact duplicate of the current branch that you are in, but with the requested name for the new branch.

Checking out a branch

In order to switch to a specific Git branch in your project, use,

git checkout <branch name>

For example,

git checkout development
Switched to branch 'development'

A shortcut for both creating a new branch from the current branch and switching to the new branch automatically is to use,

git checkout -b <branch name>
git checkout -b test
Switched to a new branch 'test'

Suppose you have made significant progress on your project development branch, and now you want to add the newly-developed feature in the development branch to your main branch. All you need to do is,

git checkout main
git merge development

By doing this, you first switch to the main branch, then request Git to merge all changes made to the project on the development branch to be copied and transferred to the main branch. For example, if I have added a single test.txt file, and committed the change to the development branch, then upon merging it with the main branch, Git will display a message like the following,

git merge development
Updating 25129a0..57ed7c2
Fast-forward
 test.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 test.txt

Suppose there has been no change in the development branch since branching it from the main branch. Then upon merging, you would get a message like the following,

git merge development
Already up-to-date.

Occasionally, conflicts may arise between branches upon merging. This happens when a common file between the two branches, has been modified on both branches in parallel. In such cases, you will need to resolve the conflict by choosing one file edit in one branch and discarding the other. The following is an example merge conflict message by Git.

git merge development
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

For example, if you use git status command, the following is the typical message that will be displayed,

git status
On branch test
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   test.txt

no changes added to commit (use "git add" and/or "git commit -a")

To resolve the conflict, open the conflicting file. There you will see segments of the file from both versions, which cause the conflict. Edit the file as you wish. Then, perform the normal git add and git commit on the file to resolve the conflict and complete the merge process.

In addition to git merge, there is another more elegant and powerful Git command, git rebase for integrating changes in different branches into a single branch. But, the subject goes beyond the goals of this class, and the interested reader is referred to Git documentation for this command and this pedagogical Git page for git rebase.

Deleting a branch

Normally, when a branch merge is done, the development branch is no longer needed, so it is always wise and cleaner to delete the unwanted branches using the command git branch -d <branch name>. To do this, check out the same branch on which you just performed git merge, then delete the other unwanted branch,

git checkout main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)
Switched to branch 'main'
git branch -d test
Deleted branch hotfix (3a0874c).

Note that you cannot delete a branch while you are on it (i.e., while it is checked out). If you do so, Git will print out an error message like the following on-screen,

git branch -d test
error: Cannot delete branch 'test' checked out at 'C:/Users/Amir/git/foo'

You cannot delete a branch from another branch with which you have not performed the merging either. In this case, Git will print the following error,

git branch -d test
error: The branch 'test' is not fully merged.
If you are sure you want to delete it, run 'git branch -D test'.


Footnotes

1. For a thorough review of VCS and particularly Git, see the Git documentation.
2. See Git documentation for more details and updates.

Exercise

  1. Version-control using Git and GitHub: web-link

  2. Simple GitHub page from README.md: web-link