Git & Version Control

Chapter 4
The Essential Git Workflow

Certificate in DevOps
Git & Version Control   •   Certificate

The Essential Git Workflow

Introduction and Context

Git is a distributed version-control system used to record, compare, and coordinate changes to files. It is central to DevOps because software delivery depends on traceability: teams must know what changed, who changed it, why it changed, and how to reproduce a particular version. Git does not merely store files; it stores a sequence of snapshots and the relationships between them.

A Git workflow is the disciplined movement of work through several states: a repository is created or obtained, files are inspected, selected changes are staged, a snapshot is committed, and local work is synchronized with shared repositories. Understanding this workflow is more valuable than memorizing isolated commands because each command affects a different part of Git's data model.

Learning Objectives

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

  • initialize or clone a repository;
  • distinguish the working tree, staging area, and repository history;
  • inspect status and differences;
  • stage, commit, modify, rename, and remove files;
  • write clear and useful commit messages;
  • synchronize basic changes with a remote repository; and
  • apply a safe, traceable workflow in a small DevOps project.

Git’s Three Local Areas

A Git repository is best understood through three local areas:

  1. Working tree: the files currently visible in the project directory. They may contain new, modified, renamed, or deleted content.
  2. Staging area (index): the proposed content for the next commit. It allows a developer to select part of the current work.
  3. Repository history: committed snapshots and their metadata, stored in the .git directory.

A commit records the staged state, not necessarily every change present in the working tree. This distinction is fundamental. A developer may edit three files but stage and commit only one. The remaining changes stay in the working tree.

The usual direction of work is:

working tree → staging area → commit → local history → remote repository

The git status command is the primary instrument for observing these states.

Creating and Obtaining Repositories

Initializing a repository

To begin tracking an existing directory:

mkdir web-service
cd web-service
git init

git init creates a hidden .git directory containing repository metadata. It does not automatically add or commit existing files. A typical first commit is:

git add README.md
git commit -m "Add project README"

A useful .gitignore file should be created before staging project files. It excludes generated artifacts, local configuration, credentials, and operating-system files. For example:

node_modules/
.env
*.log
build/

A .gitignore rule prevents untracked files from being selected by ordinary commands such as git add .; it does not remove a file already tracked by Git. If a tracked file must cease being tracked, use git rm --cached filename and then commit the change.

Cloning a repository

To obtain an existing repository:

git clone https://example.com/team/web-service.git
cd web-service

Cloning normally creates a working directory, a local repository, a remote named origin, and a checked-out branch. The exact default branch name depends on the server and repository configuration.

Useful inspection commands include:

git remote -v
git branch --show-current
git log --oneline

Cloning is different from downloading a ZIP archive: a clone contains version history and remote-tracking information.

Inspecting Repository State

Status

Use:

git status

Status commonly reports:

  • untracked files: present in the working tree but not yet tracked;
  • modified files: tracked files whose working-tree content differs from the index;
  • changes to be committed: staged content differs from the current commit; and
  • changes not staged: working-tree content differs from the staged version.

The concise form is:

git status --short

In this output, the first column describes the index and the second describes the working tree. For example, M means staged modification, M means unstaged modification, and ?? means untracked.

Comparing changes

git diff compares the working tree with the staging area:

git diff

git diff --staged compares the staging area with the latest commit:

git diff --staged

These commands answer different questions. The first asks, “What have I changed but not staged?” The second asks, “What will the next commit contain?” Reviewing both is a professional safeguard against accidental or incomplete commits.

History can be viewed with:

git log --oneline --decorate --graph

For a particular commit, use git show <commit-id>.

Staging Files and Changes

To stage a file:

git add app.py

To stage several named files:

git add app.py README.md

To stage all changes in the current directory, including deletions:

git add .

This command should be used thoughtfully. It may stage generated files or unrelated work if the directory is not cleanly organized. Many teams prefer explicitly naming files or reviewing the result immediately with git diff --staged.

Git can stage only part of a file interactively:

git add -p

This is useful when one file contains two logically independent changes. Small, focused commits improve review, rollback, and troubleshooting.

To remove a file from the staging area while keeping its working-tree changes:

git restore --staged app.py

The older command git reset HEAD app.py may be encountered in existing documentation, but git restore --staged communicates the intention more clearly.

Committing Changes

A commit creates a permanent reference to the staged snapshot in local history:

git commit -m "Add health-check endpoint"

A commit normally contains a snapshot, author information, a timestamp, a parent reference, and a message. Because Git identifies objects using cryptographic hashes, changing committed content changes its identity. A commit is therefore a historical record, not merely a save operation.

A good commit message is specific, concise, and meaningful without requiring the reader to inspect the diff. A useful convention is an imperative subject line, such as:

Add validation for deployment configuration

Avoid vague messages such as update, changes, or fix stuff. A subject should usually describe one coherent change. If more context is required, add a body:

Reject deployments with missing image tags

The validation prevents an empty tag from reaching the release pipeline.

Teams may adopt conventions such as feat:, fix:, or issue identifiers. The convention matters less than consistent, truthful communication. Do not claim that tests were performed if they were not.

A commit should normally be:

  • focused on one logical purpose;
  • internally coherent and preferably buildable;
  • free of secrets and unnecessary generated files; and
  • accompanied by appropriate tests or documentation when relevant.

Modifying, Renaming, and Removing Files

After editing a tracked file, inspect and stage it:

vim README.md
git diff
git add README.md
git diff --staged
git commit -m "Clarify installation instructions"

Git detects renames by comparing content similarity; it does not require a special rename object. Nevertheless, git mv records the intended operation conveniently:

git mv config.txt service-config.txt
git commit -m "Rename service configuration file"

The equivalent sequence is deleting the old path and adding the new one, followed by a commit. Git may display the result as a rename when similarity is sufficiently high.

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

git rm obsolete.txt
git commit -m "Remove obsolete configuration"

To stop tracking a file while retaining it locally:

git rm --cached local-settings.yml

This is appropriate when a file was mistakenly committed and should instead be ignored. If it contains a password or token, removing it in a new commit does not erase it from historical commits; the credential must be revoked and, where necessary, history must be rewritten using an approved procedure.

Synchronizing with a Remote

A remote is a named reference to another repository, commonly called origin. To retrieve remote commits without changing the working tree:

git fetch origin

To incorporate remote changes into the current branch, one common approach is:

git pull --ff-only origin main

A fast-forward update is possible when the local branch has no divergent commits. If divergence exists, Git refuses the operation rather than silently creating an unexpected result. A team may instead use an explicit merge or rebase policy; that policy should be documented because both approaches have different history implications.

To publish local commits:

git push origin main

The first push for a new local branch is often:

git push -u origin feature/health-check

The -u option records the upstream branch, allowing later use of git pull and git push without repeating remote and branch names.

A safe basic synchronization routine is:

git status
git fetch origin
git diff origin/main...HEAD
# integrate according to team policy
git push origin main

Never use force-push casually on a shared branch. If rewriting a branch is explicitly authorized, git push --force-with-lease is safer than --force because it checks that the remote has not advanced unexpectedly.

Worked Workflow Example

Suppose a learner clones a service and must add a readiness endpoint.

git clone https://example.com/team/service.git
cd service
git status

The learner creates README.md documentation and edits server.py. Before committing, the learner checks:

git status
git diff
git add README.md server.py
git diff --staged
git commit -m "Add readiness endpoint documentation"

If the staged diff accidentally includes a debug print, the learner edits the file, checks git diff again, stages the correction, and reviews the staged result. Finally:

git fetch origin
git pull --ff-only origin main
git push origin main

If the pull cannot fast-forward, the learner stops and follows the team’s documented conflict-resolution process rather than overwriting colleagues’ work.

Illustrative Case: The Configuration Incident

Background. A small platform team maintains a deployment repository. A developer adds a local .env file containing a database password and runs git add ..

Problem. The password is staged, while the intended application change is mixed with unrelated log files. The developer commits and pushes both.

Analysis. git status and git diff --staged were not reviewed. The .gitignore file was incomplete, and the commit combined unrelated changes. Removing .env in a later commit would not make the exposed password safe.

Approach. The team immediately revokes and replaces the password, informs the service owner, and prevents further exposure. If repository policy requires it, authorized maintainers remove the secret from history using a reviewed history-rewriting tool. They then add .env to .gitignore, separate the application correction from generated logs, and introduce a pre-commit secret scan.

Outcome. The repository becomes safer and easier to review. The team also adopts a rule that every contributor must inspect git diff --staged before committing.

Learning points. Version control is a governance mechanism as well as a technical tool. Staging review, least-privilege credentials, secret scanning, focused commits, and protected branches reduce operational risk.

Challenges, Limitations, and Best Practices

Git is powerful but does not automatically understand business intent. A textual merge may be syntactically valid yet semantically wrong. Binary files, generated artifacts, large repositories, and submodules require additional practices. Git also does not replace backups, testing, code review, release management, or access control.

Recommended practices include:

  • configure identity with git config --global user.name and user.email;
  • create and maintain a suitable .gitignore;
  • inspect status and staged differences before every commit;
  • make small, focused commits;
  • never commit passwords, private keys, or tokens;
  • synchronize frequently according to team policy;
  • protect important branches and require review where appropriate; and
  • document the project’s branching, merge, and release conventions.

Modern development platforms increasingly use automated checks, dependency scanning, secret detection, and AI-assisted commit-message or code-review suggestions. These tools can improve consistency, but they are not authoritative: AI-generated suggestions may omit security implications or misunderstand intent. Human review remains necessary, especially for credentials, licensing, personal data, and production changes.

Chapter Summary

The essential Git workflow moves changes from the working tree to the staging area, into a local commit, and then to a remote repository. git init creates local repository metadata, while git clone obtains an existing repository and its history. git status, git diff, and git diff --staged provide complementary views of work. git add selects content for a commit; git commit records a coherent snapshot; git mv and git rm manage paths; and git fetch, git pull, and git push support synchronization. Effective commit messages explain intent. Careful staging, secret protection, review, and documented collaboration policies make Git a reliable foundation for DevOps traceability.

Key Terms

Working tree: The checked-out files currently being edited.

Repository: A directory containing project data and Git history.

.git directory: The hidden directory containing local repository metadata.

Index: Another name for the staging area.

Staging area: The proposed content of the next commit.

Commit: A recorded snapshot with metadata and a parent relationship.

Untracked file: A file present locally but not yet tracked by Git.

Remote: A named reference to another repository.

Clone: A local copy of a repository and its history.

Fetch: Download remote references and objects without integrating them.

Pull: Fetch remote changes and integrate them according to configuration or command options.

Push: Publish local commits to a remote repository.

Fast-forward: An update that moves a branch pointer forward without creating a merge commit.

Merge conflict: A situation in which Git cannot automatically reconcile competing changes.

.gitignore: Rules for excluding specified untracked paths from ordinary staging.

Upstream branch: The remote-tracking branch associated with a local branch.

History rewrite: An operation that changes existing commit ancestry or content.

Secret scanning: Automated detection of credentials or sensitive tokens.

Review Questions

Short Answer Questions

  1. What are the working tree, staging area, and repository history?
  2. What is the difference between git diff and git diff --staged?
  3. Why should a .gitignore file be created early in a project?
  4. What does git fetch do without performing a merge?
  5. State two characteristics of an effective commit message.

Long Answer Questions

  1. Explain the complete lifecycle of a change from editing a file to publishing a commit.
  2. Compare git init and git clone, including when each is appropriate.
  3. Explain how Git handles modification, renaming, and removal of files.
  4. Describe a safe procedure for synchronizing local work with a remote main branch.
  5. Discuss why staging review is important for security, quality, and collaboration.

Analytical / Application Questions

  1. A developer runs git add . and discovers that a log file and a local configuration file are staged with the intended source change. What commands and policy changes should be used to correct the situation?
  2. A local branch and its remote branch have diverged. Explain why git pull --ff-only fails and describe how the developer should proceed under a documented team integration policy.
  3. A password was committed and pushed, then deleted in a later commit. Analyze why the password remains a security incident and identify the immediate technical and governance responses.

Multiple Choice Questions with Answers

Question 1

Which area contains changes selected for the next commit?

A. Working tree

B. Staging area

C. Remote repository only

D. Operating-system cache

Correct Answer: B. Staging area

Question 2

What does git diff --staged compare?

A. The working tree with an external backup

B. Two remote repositories

C. The staging area with the latest commit

D. Only untracked files with ignored files

Correct Answer: C. The staging area with the latest commit

Question 3

Which command creates Git metadata in an existing directory?

A. git init

B. git fetch

C. git show

D. git branch --show-current

Correct Answer: A. git init

Question 4

Which command removes a tracked file from both the working tree and the index?

A. git restore --staged file

B. git rm file

C. git clone file

D. git diff file

Correct Answer: B. git rm file

Question 5

What is the principal purpose of git fetch?

A. Delete remote branches

B. Stage local modifications

C. Download remote updates without integrating them

D. Rewrite all local commits

Correct Answer: C. Download remote updates without integrating them

Question 6

Which commit message is most effective?

A. update

B. stuff

C. misc fixes maybe

D. Add validation for deployment configuration

Correct Answer: D. Add validation for deployment configuration

Question 7

What does git rm --cached settings.yml do?

A. Deletes the file permanently from every commit

B. Stops tracking the file while retaining it locally

C. Stages the file for a rename

D. Downloads the file from origin

Correct Answer: B. Stops tracking the file while retaining it locally

Question 8

Why is git diff useful before staging?

A. It shows unstaged working-tree changes

B. It publishes changes to the remote

C. It creates a new branch automatically

D. It removes ignored files

Correct Answer: A. It shows unstaged working-tree changes

Question 9

What is a fast-forward update?

A. A merge that always creates two commits

B. A branch-pointer move with no divergent local commits

C. A forced overwrite of a remote branch

D. A method for encrypting a repository

Correct Answer: B. A branch-pointer move with no divergent local commits

Question 10

Why does deleting a secret in a new commit not fully resolve its exposure?

A. Git cannot store deletions

B. The secret may remain in earlier history

C. .gitignore restores deleted files

D. Remote repositories never store commits

Correct Answer: B. The secret may remain in earlier history

Git & Version Control
Chapter 4 of 14