Skip to content

Dr. Nagender Aneja

Virginia Tech

  • Home
  • Publications
  • Patents
  • Teaching
  • Toggle search form

Introduction to Git

Introduction to Git

Git is a distributed version control system used to track changes to files and coordinate work across repositories. Unlike systems in which a client retrieves only the latest version of a project, Git clones the entire repository, including its complete history. As a result, every clone effectively serves as a full backup of the project data.

This article introduces Git’s core ideas and demonstrates a practical workflow covering repository creation, staging and committing files, viewing history, working with GitHub, cloning and synchronizing repositories, branching, merging, conflict resolution, resetting commits, and using .gitignore.

1. How Git Stores Project History

Traditional version control systems such as CVS, Subversion, and Perforce commonly use a delta-based model. They begin with a complete version of a file and then record a sequence of differences, or deltas, describing how that file changes over time.

Git takes a different approach.

Git treats project history as a stream of snapshots. Each commit represents a snapshot of the project at that moment. When a file has not changed since the previous commit, Git does not need to store another identical copy; instead, it references the previous identical object.

This snapshot-oriented design is one of the central ideas behind Git.

2. Important Characteristics of Git

Many Git operations are performed locally, which means developers can inspect history, compare versions, create branches, and make commits without continuously communicating with a remote server.

Git also emphasizes data integrity. Everything stored by Git is checksummed before being saved and is subsequently referenced using that checksum. The slides describe Git hashes as 40-character hexadecimal SHA-1 values calculated from file or directory contents.

An example hash looks like this:

24b9da6552252987aa493b52f8696cd6d3b00373

Hashes allow Git to uniquely refer to stored objects and commits.

3. Git’s Three States

A file in Git typically moves through three important states:

  • Modified: The file has been changed, but those changes have not yet been committed.
  • Staged: The current version of the modified file has been selected for inclusion in the next commit.
  • Committed: The data has been safely stored in the local Git database.

These states correspond to three major parts of a Git project:

  1. Working tree — where files are edited.
  2. Staging area — where changes are prepared for the next commit.
  3. Git directory (.git) — where Git stores repository information and history.

Conceptually, the workflow is:

Working Tree
    |
    | git add
    v
Staging Area
    |
    | git commit
    v
Git Repository

The diagram on page 8 of the slides illustrates this movement between the working directory, staging area, and .git repository.

4. Installing and Configuring Git

Git can be downloaded from the official Git website. After installation, verify that Git is available with:

git --version

Git also provides built-in manual pages. For example:

git help init

Press q to exit the manual viewer.

Global Configuration

Git allows global settings that apply to repositories on your computer unless a particular repository overrides them.

Set your name:

git config --global user.name "Your Name"

Set your email:

git config --global user.email "your@email.com"

Set the default initial branch:

git config --global init.defaultBranch main

The name and email become part of commit metadata.

To inspect your global configuration:

git config --global --list

5. Creating a Git Repository

Create a project directory and move into it:

mkdir project1
cd project1

Before Git initialization, running:

git status

produces an error because the directory is not yet a Git repository.

Initialize the repository:

git init

Now:

git status

reports that you are on the initial branch, that there are no commits yet, and that the working tree contains nothing to commit.

The .git Directory

Running git init creates a hidden .git directory.

This folder contains Git’s internal repository data, including information such as:

  • commit history,
  • branches,
  • configuration,
  • the staging index.

On Windows, hidden files can be shown through File Explorer. On macOS, hidden files can be toggled using:

Command + Shift + .

The .git directory is what turns an ordinary folder into a Git repository.

6. Tracking a File

Create a file such as README.md.

On macOS:

touch README.md

On Windows PowerShell:

New-Item README.md

Check the repository:

git status

Git initially lists README.md as an untracked file.

Stage the File

Add the file to the staging area:

git add .

Then:

git status

shows README.md under Changes to be committed.

Commit the File

Create the first commit:

git commit -m "updated README.md"

Afterward:

git status

should report:

nothing to commit, working tree clean

A commit records the staged snapshot in the local Git repository.

7. Modifying an Existing File

Suppose README.md is changed to contain content such as:

# Project 1

* This is a sample Project added from Project 1

After editing, run:

git status

Git now reports:

Changes not staged for commit:
    modified: README.md

The file is tracked, but its latest changes have not yet entered the staging area.

To stage and commit explicitly:

git add README.md
git commit -m "Update README"

For already tracked files, Git also supports:

git commit -a -m "Update README"

The slides contrast these two approaches.

8. Viewing Changes with git diff

Before committing a modification, inspect the difference between the working copy and the previously recorded version:

git diff README.md

A Git diff may contain lines such as:

--- a/README.md
+++ b/README.md
-# Title
+# Project 1

The notation means:

  • --- a/README.md refers to the old version.
  • +++ b/README.md refers to the new version.
  • lines beginning with - were removed.
  • lines beginning with + were added.

Git may also show a hunk header such as:

@@ -1 +1,4 @@

This can be interpreted as:

@@ -oldStart,oldCount +newStart,newCount @@

In the example, the old content begins on line 1 with one line, while the new content begins on line 1 with four lines.

Once satisfied with the changes:

git add .
git commit -m "updated README.md"

9. Viewing Commit History

Use:

git log

to display detailed commit history, including commit hashes, authors, timestamps, and messages.

For a shorter history:

git log --oneline

Example:

f6a3cb2 updated README.md
689c559 updated README.md

The shortened hash at the beginning of each line can often be used to reference a commit.

Viewing a Specific Commit

A commit can be inspected using part of its hash:

git show f6a3cb2

You can also refer to commits relative to HEAD:

git show HEAD
git show HEAD~1

HEAD represents the currently checked-out commit, while HEAD~1 refers to its parent.

10. Connecting a Local Repository to GitHub

A local Git repository can be linked to a remote repository hosted on GitHub.

After creating a GitHub repository, add it as a remote:

git remote add origin https://github.com/naneja/project1.git

Here:

  • the URL identifies the remote GitHub repository;
  • origin is the conventional nickname for that remote.

Rename the Branch

If necessary, rename the current branch to main:

git branch -M main

The -M option performs the rename forcefully if required.

Push the Repository

Push the local branch:

git push -u origin main

This uploads the local main branch to the remote named origin.

The -u, or --set-upstream, option tells Git to remember that local main tracks origin/main. After this initial setup, later synchronization can usually use the shorter commands:

git push

or:

git pull

The slides also demonstrate GitHub’s personal access token workflow and the process of creating a private repository on GitHub on pages 33–38.

11. Cloning a Repository

A remote repository can be copied locally with:

git clone https://github.com/naneja/project1 project2

Then:

cd project2
git remote -v

shows the configured remote for both fetching and pushing.

Because Git is distributed, cloning does more than download the current files. The clone contains the repository’s history and branches.

Two separate working directories can therefore point to the same GitHub repository. In the slides, both project1 and project2 are linked to the same naneja/project1 remote repository.

12. Synchronizing Multiple Local Copies

Suppose a new file is created in project1:

touch hello.cpp
git add .
git commit -m "Added hello.cpp"

The local history might then show:

git log --oneline
c4f321a (HEAD -> main) Added hello.cpp
f6a3cb2 (origin/main) updated README.md
689c559 updated README.md

At this point, the new commit exists locally but has not necessarily reached GitHub.

Push it:

git push

Now GitHub contains the update.

However, another clone such as project2 will not automatically receive the change. Before synchronization, it may still contain only README.md.

Update it with:

git pull

The new hello.cpp file then becomes available locally.

This demonstrates a common Git collaboration cycle:

Local repository A
        |
      push
        v
Remote repository
        |
      pull
        v
Local repository B

13. Branching

Branches allow different lines of development to exist independently.

List branches:

git branch

The active branch is marked with *.

Create a Branch

git branch br1

List branches again:

git branch

You might see:

  br1
* main

Switch Branches

Use:

git switch br1

or the older equivalent:

git checkout br1

Create and Switch in One Command

A new branch can be created and checked out immediately:

git switch -c br2

or:

git checkout -b br2

This combines branch creation and branch switching.

14. Files and Branch Independence

Suppose a file is created while on br2:

git switch br2
touch hello_b2.cpp
git add .
git commit -m "hello_br2 added"

If you switch back to main:

git checkout main

the committed hello_b2.cpp file does not appear on main.

A change committed only on one branch remains isolated from other branches until the branches are merged. The slides also note that uncommitted changes may sometimes remain visible when switching branches.

15. Merging Branches

To integrate br2 into main:

git switch main
git merge br2 -m "merged br2"

Once merged, the changes from br2 become part of main.

If the branch is no longer needed:

git branch -D br2

16. Working with Multiple Branches

Git branches can themselves have child branches.

The slides demonstrate a structure in which:

  • m1 branches from main,
  • m11 branches from m1,
  • m111 and m112 branch from m11,
  • m2 branches from main,
  • m21 branches from m2,
  • m3 branches from main.
    For example:
git checkout -b m1
touch hello_m1.cpp
git add .
git commit -m "Added hello_m1.cpp"

A child branch can then be created:

git checkout -b m11
touch hello_m11.cpp
git add .
git commit -m "Added hello_m11.cpp"

To visualize the entire branch structure:

git log --oneline --graph --all

The diagram and terminal output on page 73 show how Git renders several branches as a text-based commit graph.

17. Merge Conflicts

Git can often merge branches automatically. A conflict occurs when incompatible changes affect the same part of a file.

The slides demonstrate this using hello.cpp.

On main:

git switch main

Add:

int main(){}

Then commit:

git add .
git commit -m "added main function"

On br1, modify the same file differently:

git switch br1

Add:

int hello(){}

Then:

git add .
git commit -m "added hello function"

Now attempt the merge:

git switch main
git merge br1

Git may report:

Auto-merging hello.cpp
CONFLICT (content): Merge conflict in hello.cpp
Automatic merge failed; fix conflicts and then commit the result.

Resolving the Conflict

Open the conflicted file, inspect the changes from both branches, and manually decide what the final file should contain.

After correcting it:

git add .
git commit -m "updated hello.cpp"
git push

The important point is that Git identifies the conflict, but the developer determines the correct final content.

18. Undoing the Last Commit with a Hard Reset

To move the current branch back one commit and discard the current changes:

git reset --hard HEAD~1

The slides describe this as a hard reset to the previous commit, with all changes discarded.

Because --hard modifies both repository state and working files, it should be used carefully.

19. Ignoring Files with .gitignore

Not every file in a project should be tracked. Build outputs, operating-system metadata, generated files, and other temporary files can be excluded using .gitignore.

Examples from the slides include:

*.out

Ignore all .out files.

!a.out

Track a.out even though .out files are otherwise ignored.

/TODO

Ignore only the TODO file in the current directory.

build/

Ignore files in directories named build.

doc/*.txt

Ignore .txt files directly inside doc, but not necessarily those in deeper subdirectories.

doc/**/*.pdf

Ignore PDF files inside doc and its subdirectories.

.DS_Store

Ignore macOS .DS_Store metadata files.

20. A Typical Git Workflow

The concepts in the slides can be combined into a standard day-to-day workflow.

First, create or obtain a repository:

git init

or:

git clone <repository-url>

Edit files and inspect the repository:

git status
git diff

Stage selected work:

git add .

Commit it:

git commit -m "Describe the change"

Inspect history:

git log --oneline

Synchronize with a remote:

git pull
git push

Create a feature branch:

git switch -c feature

After development, merge it:

git switch main
git merge feature

This workflow illustrates Git’s central model: developers make changes locally, deliberately stage snapshots, commit them into repository history, organize parallel development through branches, and synchronize repositories through remote services such as GitHub.

Conclusion

Git combines local version control, complete repository history, snapshot-based storage, branching, merging, and remote synchronization into a powerful development workflow.

The most important concepts to remember are:

  • a Git clone contains the repository and its history;
  • files move through modified, staged, and committed states;
  • git add prepares content for a commit;
  • git commit records a snapshot locally;
  • git log and git show inspect history;
  • git push sends local commits to a remote repository;
  • git pull retrieves remote updates;
  • branches isolate lines of development;
  • git merge combines them;
  • merge conflicts require manual resolution;
  • .gitignore prevents unwanted files from being tracked.

Once these ideas become familiar, Git becomes less about memorizing commands and more about understanding how snapshots of a project move through local history, branches, and shared repositories.

Copyright © 2026 Dr. Nagender Aneja.

Powered by PressBook WordPress theme