Git, GitHub & Version Control

Chapter 3
Essential Git Operations

Bachelors Program in Full Stack Development
Git, GitHub & Version Control   •   Bachelor

Essential Git Operations

Introduction

Git is a distributed version control system used to record, examine, and coordinate changes to files. In a professional software project, Git is not merely a backup mechanism. It is a disciplined model of development: developers modify working files, select changes for inclusion, record them as commits, and inspect the resulting history. GitHub and similar platforms build collaboration services around this model, but the essential operations are performed by Git itself.

This chapter develops the operational foundation required for reliable Git use. It explains how to create and configure repositories, understand the relationship between the working tree, staging area, and commit history, track files, create meaningful commits, inspect changes, rename and remove files safely, and exclude generated or sensitive content with .gitignore. The emphasis is not on memorizing isolated commands, but on understanding the state transitions that commands produce.

A disciplined Git workflow improves reproducibility, reviewability, debugging, and collaboration. Conversely, careless staging, vague commit messages, accidental inclusion of secrets, and failure to inspect changes can create technical, security, and organizational problems.

Learning Objectives

By the end of this chapter, you should be able to:

  • create and configure a local Git repository;
  • explain the working tree, staging area, and repository history;
  • use git status, git diff, and git log to inspect project state;
  • track, stage, commit, rename, and remove files;
  • write focused and informative commit messages;
  • construct a practical .gitignore file;
  • distinguish tracked, untracked, modified, staged, and ignored files;
  • apply a disciplined workflow to a realistic software project; and
  • identify common operational, security, and governance risks.

1. Git’s Three-Part State Model

Git operations become easier to understand when the repository is viewed as three connected states.

  1. Working tree: the checked-out files and directories currently visible on the developer’s computer.
  2. Staging area, also called the index: a proposed snapshot containing the exact changes that will enter the next commit.
  3. Repository history: the collection of committed snapshots and their relationships.

A file can therefore be modified in the working tree but not staged, staged but subsequently modified again, or committed and unchanged. The staging area is important because it allows a developer to construct a commit deliberately rather than committing every current change automatically.

Working tree  --git add-->  Staging area  --git commit-->  Repository history
     ^                            |                              |
     |                            |                              |
   edit                         inspect                         git log
     |                            |                              |
     +-------- git restore -------+                              |
                                  +---------- git reset ----------+

The diagram represents common relationships, not a complete list of commands. git add copies selected content into the index. git commit records the staged snapshot. Editing a file after staging creates a difference between the staged version and the working-tree version.

1.1 File States

A file may have one or more relevant states:

  • Untracked: Git sees the file but has no committed or staged version of it.
  • Tracked: Git has information about the file in the index or repository history.
  • Modified: the working-tree content differs from the index or the last commit.
  • Staged: changes have been placed in the index and are candidates for the next commit.
  • Committed: a snapshot containing the file has been recorded in repository history.
  • Ignored: Git is instructed not to report the file as an ordinary untracked file, usually through .gitignore.

“Ignored” does not mean “secret” or “deleted.” A file already tracked by Git generally continues to be tracked even if a matching pattern is later added to .gitignore.

2. Creating and Configuring a Repository

A repository can be created in two common ways. To begin version control in an existing project directory:

git init

This creates a hidden .git directory containing repository metadata. The project files remain in the working tree. The command should normally be run at the project root, because initializing the wrong directory can produce confusing nested repositories.

To copy an existing repository from a hosting service, use cloning, discussed more fully in later chapters:

git clone https://github.com/example/team-project.git

After initialization, inspect the state:

git status

Git identity should be configured before making commits. A global configuration applies to the user across repositories; a local configuration applies only to the current repository.

git config --global user.name "Amina Patel"
git config --global user.email "[email protected]"

For a project requiring a different identity:

git config user.name "Amina Patel (Teaching Project)"
git config user.email "[email protected]"

Review configuration with:

git config --list
git config user.name
git config user.email

Git configuration also controls behavior such as the default branch name, editor, line-ending handling, and aliases. Configuration should be intentional: line-ending settings, for example, can affect whether a file appears changed when its logical content has not changed.

A useful initial sequence is:

mkdir inventory-api
cd inventory-api
git init
git status

The repository is now initialized, but no project snapshot exists until files are staged and committed.

3. Tracking and Staging Files

Suppose the project contains README.md and src/app.js. To ask Git to track them in the next commit, use:

git add README.md src/app.js

To stage all suitable changes in the current directory:

git add .

The shorter command is convenient, but it should not replace inspection. A broad command may stage generated files, credentials, large binaries, or unrelated experimental changes. A professional workflow often stages deliberately named files or reviews the result immediately with git status and git diff --staged.

git add does not mean “add permanently.” It updates the staging area. The content becomes part of repository history only after git commit.

3.1 Partial and Interactive Staging

When a file contains both completed and unfinished work, staging the entire file may create a poor commit. Interactive staging can select portions:

git add -p

Git presents change “hunks” and asks whether each should be staged. This supports atomic commits: commits that represent one coherent logical change. If a hunk combines unrelated edits, a developer may need to edit the file manually or use more advanced patch selection.

3.2 Unstaging Without Losing Work

If a file was staged accidentally, it can be removed from the staging area while preserving the working-tree changes:

git restore --staged README.md

Older workflows commonly use git reset HEAD README.md; the modern command communicates the intent more clearly. Unstaging is not the same as discarding edits.

To discard unstaged changes and restore a file to the staged version:

git restore README.md

This operation can destroy local edits. It should be used only when the developer is certain that the changes are no longer needed. If the goal is to restore the last committed version while also removing staged changes, the exact command and context must be considered carefully; destructive commands should never be used as exploratory shortcuts.

4. Inspecting State with git status

git status is the primary orientation command:

git status

It reports the current branch, staged changes, unstaged changes, and untracked files. Typical categories include:

  • “Changes to be committed”: content in the staging area;
  • “Changes not staged for commit”: tracked files changed in the working tree but not in the index; and
  • “Untracked files”: files Git has not yet been asked to track.

A compact version is useful in busy terminals:

git status --short

The short format commonly uses two columns. The first position describes the index, and the second describes the working tree. For example, M file.js indicates an unstaged modification, while M file.js indicates a staged modification. MM file.js indicates that the file has staged changes and additional unstaged changes. Understanding the two positions prevents the common mistake of assuming that a staged file cannot also have later unstaged edits.

A good habit is to run git status before staging, after staging, and before committing.

5. Comparing Changes with git diff

Git provides different comparisons depending on which states are selected.

git diff

This compares the working tree with the staging area. It shows changes that are not staged.

git diff --staged

This compares the staging area with the most recent commit, showing what the next commit would contain. git diff --cached is an equivalent spelling.

git diff HEAD

This compares the working tree, including staged and unstaged changes, with the current commit. It is useful for reviewing all local modifications relative to HEAD.

A diff uses lines beginning with + and - to represent additions and deletions. These symbols describe textual differences, not necessarily complete semantic changes. A one-line formatting change may produce a large diff if line endings or formatting tools have changed many lines.

Useful variations include:

git diff --stat
git diff -- README.md
git diff HEAD~1 HEAD -- src/app.js

The first summarizes changed files and line counts. The second limits the comparison to a path. The third compares two commits for a particular file. Reviewing the staged diff before committing is one of the strongest safeguards against accidental or incomplete commits.

6. Recording Commits

A commit records the contents currently in the staging area, together with metadata such as author, timestamp, parent commit, and message. The basic operation is:

git commit -m "Add inventory validation"

The commit contains staged changes only. Unstaged modifications remain in the working tree and are not silently included.

A commit should be a coherent unit of change. Examples include:

  • Add validation for negative stock quantities
  • Fix timeout handling in payment client
  • Document local database setup

Weak messages include changes, update, or final version, because they provide little information to reviewers or future maintainers.

6.1 Effective Commit Messages

A useful commit message answers, briefly, “What changed?” and, when necessary, “Why?” A widely used structure is:

Short imperative summary under about 50–72 characters

Optional body explaining motivation, design decisions, limitations,
or compatibility considerations. Wrap long lines for readability.

The imperative mood treats the message as an instruction applied to the code: “Add,” “Fix,” “Remove,” or “Refactor,” rather than “Added” or “Fixing.” Local team conventions may specify a prefix such as feat:, fix:, or an issue identifier.

A commit message is not a substitute for a code review or technical documentation. It should be accurate, specific, and proportionate. A tiny typo fix may need only a summary; a migration or security change may require a body describing consequences and deployment requirements.

6.2 Atomicity and Commit Design

Atomic commits have several advantages:

  • reviewers can understand the purpose more quickly;
  • a faulty change can be reverted with less collateral impact;
  • automated tools can identify the introduction of regressions more effectively; and
  • future developers can locate relevant history using git log.

Atomicity does not mean that every changed line requires its own commit. It means that one commit should represent one logically coherent, buildable or explainable change. If a feature requires a code change and its tests, those files usually belong in the same commit because they form one unit of behavior.

7. Viewing History with git log

The history command is:

git log

It displays commits in reverse chronological order, normally showing the commit identifier, author, date, and message. A concise graph is often more useful:

git log --oneline --graph --decorate --all

Other useful forms include:

git log -5
git log -- README.md
git log -p -- src/app.js
git show <commit-id>

git log -5 limits output to recent commits. A path-limited log shows commits affecting a file or directory. git log -p displays patches, while git show examines one commit and its metadata and changes.

History is an analytical resource. It can reveal when behavior changed, which commit introduced a defect, and why a design decision was made. However, history is only useful when commits are focused and messages are meaningful. Repeated “miscellaneous changes” commits reduce the explanatory value of the repository.

8. Renaming and Removing Files

Git can detect that a file was renamed by comparing content and paths, but explicit commands communicate intent and preserve the operation in the index:

git mv old-name.js new-name.js

This stages the rename. The equivalent lower-level sequence is:

mv old-name.js new-name.js
git add new-name.js
git rm old-name.js

To remove a tracked file from both the working tree and the next commit:

git rm obsolete.js

To stop tracking a file while keeping it locally:

git rm --cached local-config.json

The latter is useful when a file was accidentally tracked and should be excluded in the future. It does not erase copies already present in earlier commits. If the file contains a password or token, the credential must be revoked or rotated, and the history may require specialized remediation. Simply deleting the current copy is not a complete security response.

Directories are not tracked as independent objects; Git tracks files and their paths. An empty directory therefore does not normally appear in a commit. Projects sometimes use a documented placeholder such as .gitkeep, although this is a convention rather than a special Git feature.

9. Ignoring Files with .gitignore

A .gitignore file contains patterns for files that should not normally be treated as untracked project content. Common examples include dependencies, build outputs, operating-system metadata, local environment files, and editor settings.

Example:

# Dependencies
node_modules/

# Build output
dist/
build/

# Local environment and secrets
.env
.env.*
!.env.example

# Operating-system files
.DS_Store
Thumbs.db

# Test coverage
coverage/

A pattern ending in / targets a directory. A leading # introduces a comment. A leading ! negates a pattern, allowing a more specific exception. Patterns can be placed in the repository’s .gitignore, a user-level global excludes file, or other supported locations. Project-relevant rules should generally be committed so that all contributors receive the same baseline behavior.

.gitignore applies primarily to untracked files. If config.json is already tracked, adding config.json to .gitignore does not make Git forget it. Use:

git rm --cached config.json
git commit -m "Stop tracking local configuration"

Then ensure the ignore pattern is present. To diagnose why a path is ignored, use:

git check-ignore -v path/to/file

Ignoring a file is not equivalent to protecting it. Secrets should not be committed, but a robust security process also uses secret managers, access controls, scanning, rotation, and incident response.

10. A Disciplined Development Cycle

A practical local cycle can be expressed as:

  1. begin from a known repository state;
  2. inspect with git status;
  3. make a focused change;
  4. inspect unstaged differences with git diff;
  5. stage selected files or hunks;
  6. inspect the proposed commit with git diff --staged;
  7. run relevant tests or quality checks;
  8. commit with a precise message; and
  9. inspect the resulting history and status.

This process is intentionally repetitive. Repetition creates checkpoints and reduces the probability that unrelated work, credentials, generated files, or incomplete edits enter history.

Applied Case: The Campus Pantry API

Background

A student development team is building a small API for a campus food pantry. The repository contains JavaScript source files, tests, documentation, and local configuration. The team wants a history that can be reviewed by an instructor and continued by a future development team.

Problem

A developer creates server.js, src/stock.js, .env, and a generated coverage/ directory. They also edit the README and begin a second feature in the same working tree. If the developer uses git add . without inspection, the commit may include a secret database connection string, generated files, and unfinished work.

Analysis

The project has three risks. First, .env may contain credentials. Second, coverage output is reproducible and needlessly enlarges the repository. Third, mixing stock validation with README changes and unfinished authentication work would produce a confusing commit.

Approach and Solution

The developer first creates a .gitignore file:

.env
coverage/
node_modules/

They verify the state:

git status

They inspect source changes:

git diff

They stage only the completed feature and documentation:

git add src/stock.js README.md

They review exactly what is proposed:

git diff --staged

After running tests, they commit:

git commit -m "Validate nonnegative pantry stock"

They then check:

git status
git log --oneline -3

Later, they rename src/stock.js to src/inventory.js:

git mv src/stock.js src/inventory.js
git commit -m "Rename stock module to inventory"

Outcome

The repository history contains two focused commits. The secret remains local and is ignored; generated coverage is excluded; unfinished authentication work remains uncommitted; and the rename is easy to understand in the history.

Learning Points

The case demonstrates that Git safety depends on inspection and selection, not merely on command execution. .gitignore prevents routine accidental tracking but does not replace secret management. Staging provides a boundary between current activity and the next permanent historical record.

Challenges, Limitations, and Professional Practice

Git is powerful but not self-explanatory. The staging area can confuse beginners because a file may be partly staged and partly modified. Commands such as git restore can permanently discard work when used without review. Teams should teach recovery techniques, encourage backups for uncommitted work, and avoid destructive commands in scripts unless their effects are explicit.

Line endings, file permissions, case-sensitive paths, generated files, and large binary assets can produce unexpected diffs. Teams should establish repository conventions, use suitable attributes where necessary, and avoid committing build artifacts unless there is a documented reason.

Commit history also has governance implications. Author identity, timestamps, and change descriptions form an audit trail. Developers should not falsify authorship or rewrite shared history without authorization. Privacy should be considered when commit metadata contains personal email addresses. Organizations may enforce signed commits, protected branches, review requirements, and automated secret scanning.

Artificial intelligence tools can assist with drafting commit messages, summarizing diffs, or suggesting .gitignore patterns. However, an AI-generated summary must be checked against the actual staged diff. AI tools may omit security implications, misunderstand intent, or expose proprietary code to an external service. Responsibility for the commit remains with the developer.

Best-Practice Checklist

  • Initialize or clone the repository at the correct project root.
  • Configure an accurate author identity.
  • Inspect git status frequently.
  • Keep commits focused and coherent.
  • Review both git diff and git diff --staged.
  • Prefer precise staging over indiscriminate staging when risk is high.
  • Ignore generated files, local configuration, and secrets.
  • Never assume .gitignore removes previously committed data.
  • Use git mv and git rm when their intent is useful to communicate.
  • Write messages that describe the change and, when needed, its motivation.
  • Run tests before committing completed behavior.
  • Treat destructive commands and history rewriting as deliberate operations.

Chapter Summary

Git manages project evolution through a working tree, staging area, and repository history. git init creates repository metadata, while configuration establishes author identity and local behavior. git status reports the current state; git diff compares unstaged, staged, and committed content; and git log reveals the project’s historical record.

git add places selected content in the staging area, while git commit records that staged snapshot. Focused commits with precise imperative messages improve review, debugging, rollback, and long-term maintenance. git mv, git rm, and git rm --cached express different file-management intentions. .gitignore prevents routine tracking of generated, local, or sensitive files, but it does not remove already tracked content or replace security controls.

A disciplined workflow repeatedly inspects state, stages intentionally, reviews the proposed snapshot, tests the change, and records a coherent commit. This operational discipline is foundational to effective collaboration through GitHub and other repository-hosting platforms.

Key Terms

Working tree: The checked-out files and directories in the local project.

Repository: Git metadata and history associated with a project.

Staging area: The index containing the proposed contents of the next commit.

Commit: A recorded snapshot with metadata and a message.

Tracked file: A file known to Git through the index or repository history.

Untracked file: A file not yet included in Git’s tracked content.

Modified file: A tracked file whose content differs from a comparison state.

Ignored file: A path excluded from ordinary untracked-file reporting by ignore rules.

git init: Command that creates a repository in an existing directory.

git add: Command that updates the staging area with selected content.

git status: Command that reports repository and file states.

git diff: Command that displays differences between repository states.

git commit: Command that records the staged snapshot in history.

git log: Command that displays commit history.

HEAD: A symbolic reference to the currently checked-out commit or branch position.

Atomic commit: A coherent commit representing one logical change.

.gitignore: A file containing patterns for paths Git should normally ignore.

Index: Another name for Git’s staging area.

Repository history: The ordered graph of recorded commits and their relationships.

Review Questions

Short Answer Questions

  1. What are the three principal states in Git’s local workflow?
  2. What is the difference between git diff and git diff --staged?
  3. Why should a developer run git status before committing?
  4. What does git rm --cached filename do?
  5. Why is a vague commit message such as “updates” undesirable?

Long Answer Questions

  1. Explain how a file can be simultaneously staged and modified after it has been staged. Include the commands that can reveal this condition.
  2. Describe a disciplined sequence for creating a commit and explain the purpose of each step.
  3. Explain how .gitignore works and why it does not remove secrets that were committed previously.
  4. Compare git mv with manually moving a file and then using git add and git rm.
  5. Discuss how atomic commits improve code review, debugging, and maintenance.

Analytical / Application Questions

  1. A developer runs git add . and then discovers that .env, coverage/, and unfinished authentication code are staged. Describe a safe sequence for correcting the staging area without losing desired work.
  2. A project history contains ten commits named “fix,” “changes,” and “final.” Analyze two consequences of this history and propose a better commit-message and staging policy.
  3. A password was accidentally committed three days ago and the file is now listed in .gitignore. Explain why the password is still at risk and identify the immediate security and repository-management actions that should follow.

Multiple Choice Questions with Answers

Question 1

Which command shows changes that are staged for the next commit?

A. git diff

B. git diff --staged

C. git status --short

D. git log --stat

Correct Answer: B. git diff --staged

Question 2

What does git add primarily do?

A. Permanently records a commit

B. Deletes untracked files

C. Updates the staging area with selected content

D. Uploads changes to GitHub

Correct Answer: C. Updates the staging area with selected content

Question 3

Which command creates repository metadata in an existing project directory?

A. git init

B. git begin

C. git create

D. git repository

Correct Answer: A. git init

Question 4

A file displays as MM in short status output. What does this normally indicate?

A. The file is ignored and deleted

B. The file is staged and has additional unstaged modifications

C. The file has never been tracked

D. The file exists only in a remote repository

Correct Answer: B. The file is staged and has additional unstaged modifications

Question 5

Which command stops tracking a file while retaining the local copy?

A. git delete file

B. git restore file

C. git rm --cached file

D. git clean --keep file

Correct Answer: C. git rm --cached file

Question 6

What is the primary purpose of an atomic commit?

A. To compress all repository files

B. To represent one coherent logical change

C. To prevent all future merges

D. To guarantee that code has no defects

Correct Answer: B. To represent one coherent logical change

Question 7

Which command is most appropriate for viewing a concise graphical history of all branches?

A. git status --graph

B. git diff --all

C. git log --oneline --graph --decorate --all

D. git history --compact

Correct Answer: C. git log --oneline --graph --decorate --all

Question 8

A path already tracked by Git is added to .gitignore. What normally happens?

A. It is automatically erased from all commits

B. It remains tracked until explicitly removed from tracking

C. It is immediately encrypted

D. Its history is automatically rewritten

Correct Answer: B. It remains tracked until explicitly removed from tracking

Question 9

Which commit message best follows the practices described in the chapter?

A. stuff

B. final changes

C. Add validation for negative stock quantities

D. I changed some files

Correct Answer: C. Add validation for negative stock quantities

Question 10

What does git diff normally compare when no revision is specified?

A. The working tree with the staging area

B. Two remote branches

C. The first and last commits

D. The .gitignore file with the index

Correct Answer: A. The working tree with the staging area

Git, GitHub & Version Control
Chapter 3 of 14