Skip to content

Git - Basic Concepts

A Version Control System (VCS) is software that tracks and manages changes to files over time. It allows software developers and teams to collaborate efficiently and maintain a complete history of their project’s evolution.

Key functions of a VCS include:

  • Enabling multiple developers to work on the same project concurrently.
  • Preventing accidental overwriting of changes made by different team members.
  • Recording a detailed history of every change, including who made it, when, and why.
  • Allowing reversion to previous states of the project if needed.

There are primarily two types of VCS:

  • Centralized Version Control Systems (CVCS): Rely on a single central server to store all versions of a project. Examples include Subversion (SVN) and CVS.
  • Distributed Version Control Systems (DVCS): Each developer has a full copy (a clone) of the entire repository, including its history. Git is the most popular DVCS.

This tutorial focuses on Git, a powerful and widely-used Distributed Version Control System.

Understanding Distributed Version Control Systems (DVCS)

Section titled “Understanding Distributed Version Control Systems (DVCS)”

In a CVCS, if the central server fails or is inaccessible, collaboration stops, and there’s a risk of data loss if backups aren’t robust. DVCSs like Git overcome this limitation.

With a DVCS, every collaborator has a complete, independent copy of the repository on their local machine. This means:

  • You can work offline: Commit changes, create branches, view history, and more, without needing a network connection.
  • Enhanced data safety: If the main server (if one is used for collaboration, like GitHub) goes down, any clone can be used to restore it. Every clone is essentially a full backup.
  • Improved performance: Most operations are local and therefore very fast.
  • Network connection is only needed when you want to share your changes with others (push) or get their latest updates (pull/fetch).

Git is released under the GNU General Public License version 2, making it free to use for any project, personal or commercial. Its open-source nature means its source code is available for anyone to inspect, modify, and contribute to.

Git is exceptionally fast. Most operations are performed locally, avoiding network latency. Git stores data as snapshots of the project at a given time, rather than just file differences (deltas), which contributes to its speed, especially for branching and merging. It’s written in C, minimizing runtime overheads, and is highly optimized for performance. Even though it clones the entire repository, Git uses clever compression and storage techniques to keep local repositories small.

Every file and commit in Git is checksummed using a cryptographic hash function (currently SHA-1, with a transition to SHA-256 underway for future enhancements). This checksum is used to identify objects. This means it’s virtually impossible for data to become corrupted or for changes to be made without Git detecting it. The distributed nature also means multiple copies of the repository exist, significantly reducing the chances of data loss.

Branching in Git is lightweight and fast. Unlike some older VCSs where branches are cumbersome, Git branches are simply pointers to a specific commit. This makes it easy to create branches for new features, experiments, or bug fixes without affecting the main line of development. Merging these branches back is also generally straightforward. This capability enables powerful workflows like feature branching, Gitflow, and GitHub Flow.

Git scales well from small, individual projects to very large, complex projects with thousands of collaborators (e.g., the Linux kernel, for which Git was originally developed).

Git is the industry standard for version control. There’s a vast ecosystem of tools (GUIs, IDE integrations), hosting services (GitHub, GitLab, Bitbucket), and a large, active community providing support and resources.

A repository is a collection of all the files and folders that make up your project, along with the entire history of changes. It’s essentially the project’s database. In Git, you’ll have a local repository on your computer and potentially a remote repository (e.g., on GitHub) for collaboration.

Working Directory, Staging Area (Index), and Repository

Section titled “Working Directory, Staging Area (Index), and Repository”

Git has three main states or areas where your files can reside:

  • Working Directory: This is your local checkout of the project files. It’s the directory on your filesystem where you actively modify files.
  • Staging Area (or Index): This is an intermediate area where Git stores information about what will go into your next commit. You selectively add changes from your working directory to the staging area.
  • Local Repository (.git directory): This is where Git permanently stores the history of your project (commits, branches, tags, etc.). When you commit, Git takes the files as they are in the staging area and stores that snapshot permanently to your local repository.

The basic Git workflow is as follows:

  1. You modify files in your working directory.
  2. You selectively choose which changes you want to include in the next commit and add them to the staging area (using git add).
  3. You perform a commit, which takes the files from the staging area and stores that snapshot permanently in your local repository (using git commit).

For example, if you modify feature_a.py and bug_fix_b.py but only want to commit the changes related to feature_a.py first:

$ git add feature_a.py # Stage changes for feature_a.py
$ git commit -m "Implement new aspect of feature A"
# Now you can stage and commit changes for bug_fix_b.py
$ git add bug_fix_b.py
$ git commit -m "Fix critical bug B"

A commit is a snapshot of your project’s staged changes at a specific point in time. Each commit has a unique identifier (a SHA-1 hash), an author, a timestamp, and a commit message describing the changes. Commits are linked chronologically, forming the project’s history. A commit can have one or more parent commits; a commit with multiple parents indicates a merge.

A branch is an independent line of development. Think of it as a movable pointer to a specific commit. When you create a branch, you’re creating a new path for your work, allowing you to develop features or fix bugs in isolation without affecting other branches (like the main development line). The default branch in new repositories is typically named main (historically master).

HEAD is a special pointer that usually refers to the tip (latest commit) of the currently checked-out branch. When you make a new commit, HEAD moves forward with that commit. If you check out a specific commit that’s not the tip of a branch, HEAD points directly to that commit (this is called a ‘detached HEAD’ state).

# To see which branch HEAD is pointing to (if any) and the commit hash:
$ cat .git/HEAD
# Example output: ref: refs/heads/main
# To see the commit hash HEAD points to:
$ git rev-parse HEAD
# Example output: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

A tag is a pointer to a specific commit, used to mark important points in history, like releases (e.g., v1.0, v2.1-beta). Tags, once created, don’t typically move. There are two main types: lightweight (just a pointer) and annotated (stores extra metadata like tagger, date, message, and can be GPG-signed).

Cloning creates a full copy of a remote repository on your local machine. This copy includes all files, branches, tags, and the entire project history. This local copy is a fully functional Git repository.

git fetch downloads commits, files, and refs from a remote repository into your local repository. It updates your remote-tracking branches (e.g., origin/main) but does not modify your local working directory or your local branches. This allows you to review changes before merging them.

git pull is essentially a combination of git fetch followed by git merge (or git rebase if configured). It fetches changes from the remote repository and immediately tries to integrate them into your current local branch.

git push uploads your local commits (from a local branch) to a remote repository, sharing your changes with others. You typically push to a remote branch that corresponds to your local branch.

A remote is a reference to another Git repository that is usually hosted on a server (e.g., on GitHub, GitLab). When you clone a repository, a remote named origin is typically created automatically, pointing to the URL you cloned from. You can have multiple remotes for a single local repository.

# View your current remotes
$ git remote -v
# Example output:
# origin https://github.com/user/project.git (fetch)
# origin https://github.com/user/project.git (push)

While you don’t usually interact with them directly, Git stores its data using a few key object types:

  • Blob (Binary Large Object): Stores the content of a file. It doesn’t contain metadata like filename or path.
  • Tree: Represents a directory. It contains pointers to blobs (files) and other trees (subdirectories), along with their names and modes.

Commits, blobs, and trees are all uniquely identified by their SHA-1 hashes in Git’s object database.

For a deeper dive into Git concepts, refer to the official Git documentation: