Git Architecture and TerminologyIntroductionGit is a distributed version control system designed to record changes to files, coordinate work among people, and preserve an inspectable history of a project. Although Git is often introduced through commands such as git add, git commit, and git push, effective use requires a mental model of the structures behind those commands. This chapter explains Git’s architecture and vocabulary. It distinguishes the working tree, staging area, local repository, and remote repository; explains commits, objects, hashes, references, HEAD, branches, and tags; and develops the directed acyclic graph model that represents project history. These concepts form the foundation for later topics such as branching strategies, merging, rebasing, collaboration, and DevOps automation. Learning ObjectivesBy the end of this chapter, you should be able to: - distinguish the working tree, staging area, local repository, and remote repository;
- explain how Git stores commits and related objects;
- describe the purpose of hashes, references,
HEAD, branches, and tags; - interpret Git history as a directed acyclic graph;
- trace the effect of common commands on Git’s internal state;
- use fundamental inspection commands to diagnose repository state;
- identify sound practices for reliable and auditable version control.
1. Git as a Distributed Object DatabaseA conventional centralized version control system commonly treats a server as the primary repository. Git instead gives each clone a complete local repository, including its history and metadata. A remote repository is an additional copy used for collaboration, backup, and integration; it is not the fundamental source of Git’s ability to record history. Git’s architecture can be understood as four related areas: | Area | Main purpose | Typical contents |
|---|
| Working tree | Files currently checked out for editing | Ordinary project files and directories | | Staging area | Proposed contents of the next commit | An index describing selected file versions | | Local repository | Permanent local history and objects | Commits, trees, blobs, and references | | Remote repository | Shared repository accessed over a network | Another Git repository, often hosted centrally |
The four areas are related but distinct. Editing a file changes the working tree. Running git add records a selected version in the staging area. Running git commit creates a new commit in the local repository. Running git push transfers selected objects and updates a remote reference, subject to permissions and the remote’s current state. A useful conceptual flow is: working tree → staging area → local repository → remote repository
This is not a one-way pipeline. Files can be restored from the index or repository into the working tree, and information can be fetched from a remote into the local repository. The flow describes common movement of proposed project states, not irreversible stages. 2. The Working TreeThe working tree is the directory containing the checked-out files that a developer can inspect and edit. It is the visible part of a Git project. A working tree may contain tracked files, untracked files, ignored files, generated artifacts, and temporary content. A tracked file is one that Git knows about through the index or repository history. An untracked file exists in the working directory but has not been included in the staging area. An ignored file is deliberately excluded from normal status reporting according to patterns in .gitignore, the repository’s exclude file, or a user-wide ignore configuration. The working tree is not itself the complete history. If a developer edits a tracked file, Git does not immediately create a historical record. The edit remains an uncommitted difference until it is staged and committed. git status compares the working tree with the staging area and compares the staging area with the current commit. It therefore reports two conceptually different kinds of change:
- changes not staged for commit: differences between the working tree and index;
- changes staged for commit: differences between the index and the current
HEAD commit.
This distinction is essential. A file can contain both staged and unstaged changes. For example, a developer may stage a corrected function, then continue editing the same file. The next commit would contain only the staged version unless the later edits are staged as well. 3. The Staging Area and the IndexGit’s staging area is also called the index. It is a structured record of the file versions proposed for the next commit. The index is not merely a temporary list of filenames: it records paths and the object identifiers of the content selected for those paths, along with metadata needed by Git. The staging area provides precision. A developer can select part of a working-tree change with git add -p, stage several files while leaving others out, or prepare a coherent commit from a larger set of edits. This supports the professional practice of making each commit focused and understandable. The command git add file.txt does not mean “add this file forever.” It means “update the index with the current version of this path.” If the file is later edited, the index retains the earlier staged version until it is updated again. Useful commands include: | Command | Function |
|---|
git add path | Stage the current content of a path | git add -p | Interactively stage selected hunks | git diff | Compare working tree with index | git diff --cached | Compare index with HEAD | git restore path | Restore a path in the working tree from the index | git restore --staged path | Remove a path from the index while retaining working-tree content |
The staging area is especially important in continuous integration workflows. A commit should represent a deliberate, reviewable unit of change rather than an accidental snapshot of everything in a developer’s directory. 4. The Local Repository and .gitThe local repository is normally stored in a hidden .git directory at the project root. It contains Git’s object database, references, configuration, logs, and index. The exact internal layout can vary across Git versions and repository formats, so users should interact through Git commands rather than directly modifying internal files. A repository may be created with git init or obtained with git clone. A clone normally creates a working tree, a local repository, and remote-tracking references describing the repository from which it was cloned. The local repository provides several important capabilities: - offline history inspection;
- local commits and branching;
- comparison between versions;
- recovery through references and reflogs in many situations;
- preparation of objects for later transfer to a remote.
A commit is local until it is transferred to another repository. Conversely, a commit visible on a hosting platform may not yet exist in a developer’s local repository unless it has been fetched. 5. Remote Repositories and Remote-Tracking ReferencesA remote repository is another Git repository accessible through a transport such as HTTPS or SSH. It may be hosted by a service or maintained on an organization’s infrastructure. The name origin is conventionally assigned to the remote created by cloning, but it is only a name and can be changed. git fetch origin obtains objects and updates remote-tracking references such as origin/main. Fetching does not normally modify the current working tree or local branch. It updates the local record of what the remote branch pointed to at the time of the fetch.
git pull is commonly understood as fetch followed by an integration operation, usually merge or rebase depending on configuration. Because it combines two operations, professional users should understand its configured behavior rather than treating it as a completely opaque command.
git push origin main asks the remote to accept objects reachable from the local main reference and to move the remote branch if the update is permitted. A non-fast-forward rejection commonly means that the remote branch has advanced in a way that the proposed update would overwrite. The rejection protects shared history.
Remote-tracking references are local references. origin/main is not a live network connection; it is a local pointer updated by fetch and related commands. 6. Git ObjectsGit stores content in an object database. The fundamental object types are blobs, trees, commits, and annotated tags. 6.1 BlobsA blob stores file content without the filename. Two files with identical content can therefore refer to the same blob, even if they have different paths. A blob does not by itself say where the content appears in a project or which commit uses it. 6.2 TreesA tree represents a directory structure. It maps names to object identifiers and records whether each entry is a file, subdirectory, or certain special type. A tree can point to blobs for files and to other trees for directories. 6.3 CommitsA commit records a project snapshot indirectly through a tree object. It also stores metadata such as author, committer, timestamps, message, and one or more parent commit identifiers. A normal commit has one parent; a merge commit commonly has two or more parents; the initial commit has none. A commit is therefore more than a textual difference. It identifies a complete project state through its root tree and connects that state to earlier history through parent links. Git can calculate differences between snapshots, but the stored model is primarily snapshot-based. 6.4 Annotated Tag ObjectsAn annotated tag is an object containing a reference to another object, commonly a commit, together with a tag name, tagger, date, message, and optionally a signature. A lightweight tag is simply a reference directly pointing to an object and does not create a separate tag object. 7. Hashes and Object IdentityGit identifies objects using a cryptographic object identifier derived from the object’s type, size, and content. Historically, Git repositories commonly used SHA-1 identifiers; Git also supports SHA-256 repository formats. The exact identifier algorithm depends on repository configuration and compatibility requirements. An identifier is commonly displayed as a hexadecimal string. It is not a sequential revision number and does not indicate that one commit is “number 10” after another. If the content or relevant metadata of an object changes, its identifier changes. This makes object identity content-addressed. A commit’s identifier depends on information including its tree, parent identifier(s), author and committer metadata, timestamps, and message. Consequently, changing a commit message or parent relationship creates a different commit, even if the file snapshot is unchanged. Short identifiers, such as those shown by git log --oneline, are abbreviations. They are convenient when unambiguous within a repository, but full identifiers are preferable in scripts and formal records when ambiguity or cross-repository precision matters. Hashing supports integrity checking, but it should not be described as an absolute guarantee against every possible attack. Git’s security properties depend on the object format, hash algorithm, transport security, signatures, access controls, and operational practices. 8. References, HEAD, Branches, and TagsA reference, or ref, is a human-readable name that points to an object, usually a commit. References avoid requiring users to remember long object identifiers. Common reference namespaces include: refs/heads/: local branches;refs/remotes/: remote-tracking branches;refs/tags/: tags;HEAD: a symbolic or direct reference identifying the currently selected position.
A branch is best understood as a movable reference to a commit, not as a separate copy of the project. When a new commit is made on a branch, the branch reference advances to the new commit. Other branches remain where they were. HEAD identifies the currently checked-out commit or branch. In the usual attached state, HEAD symbolically refers to a local branch, such as refs/heads/main; the branch then points to a commit. In detached-HEAD state, HEAD points directly to a commit rather than to a branch. Work can be inspected or committed in this state, but new commits may become difficult to find unless a branch or tag is created.
A tag is generally intended to identify a significant point, such as a release. Unlike a branch, a tag is normally treated as stable and is not automatically advanced by later commits. Annotated tags are often preferred for releases because they carry metadata and can be signed. Useful inspection commands include git show-ref, git branch -avv, git tag, git rev-parse HEAD, and git symbolic-ref --short HEAD when HEAD is attached. 9. Git History as a Directed Acyclic GraphGit history is naturally modeled as a directed acyclic graph, or DAG. Each commit is a node. A parent relationship is a directed edge from a commit to its parent. The graph is directed because parent links have a defined direction, and acyclic because a commit cannot ultimately be its own ancestor. The DAG model explains several Git behaviors: - a linear sequence has one parent per commit;
- branching occurs when multiple references point to different descendants of a common ancestor;
- merging creates a commit with multiple parents;
- ancestry is based on reachability through parent links;
- a branch name is only a movable pointer into the graph.
Suppose commit A is followed by commit B on main. A branch feature is created at B, then receives commit C, while main receives commit D. The graph contains A → B → C and A → B → D, where arrows represent movement from child toward parent when discussing parent links. A merge commit M may have parents C and D, combining the two lines of development. The terminology “ahead” and “behind” describes reachability between references. If every commit reachable from main is also reachable from feature, and feature has additional commits, feature is ahead of main. These descriptions depend on the selected comparison and do not necessarily indicate quality or correctness. Fast-Forward UpdatesA fast-forward occurs when the target reference is an ancestor of the source reference. The target reference can move forward without creating a new merge commit. No existing commits are rewritten; only the reference changes. Merge CommitsWhen two lines of history have diverged, a merge may create a new commit with both tips as parents. Conflict resolution determines the contents of the merge commit. The graph retains both historical lines. RebasingRebasing copies a series of commits onto a different base by creating new commits with new parent relationships and therefore new identifiers. The resulting history may appear linear, but the original commits are not transformed in place. Rebasing shared commits can disrupt collaborators because references no longer point to the same commit identities. 10. State Transitions Through Common CommandsConsider a repository whose current branch main points to commit A. - The developer edits
app.py. The working tree differs from the index; main and HEAD still point to A. - The developer runs
git add app.py. The index now records the edited content; HEAD and main still point to A. - The developer runs
git commit -m "Improve input validation". Git creates a tree and commit object, then moves main and attached HEAD to the new commit B. - The developer runs
git push origin main. Objects needed by the remote are transferred, and the remote’s main reference is requested to advance to B.
This sequence shows why git add and git commit are separate. Staging selects content; committing records a new historical node. A practical diagnostic sequence is: git status
git diff
git diff --cached
git log --oneline --decorate --graph --all
git branch -avv
git remote -v These commands reveal local modifications, staged changes, graph structure, reference positions, and remote configuration without altering project history. 11. Applied Case: A Small DevOps TeamBackgroundIllustrative case: a three-person DevOps team maintains deployment scripts for a web service. The production branch is main; feature work occurs on short-lived branches. The team uses a hosted remote repository and continuous integration checks. ProblemA developer edits a deployment script, stages it, then makes additional debugging changes without staging them. A colleague merges another feature into the remote main. The first developer attempts to push and receives a non-fast-forward rejection. AnalysisThe index contains the first version of the deployment script, while the working tree contains both the staged version and later unstaged edits. The local main points to commit L, but the remote-tracking reference origin/main indicates that the remote has advanced to commit R. The local and remote histories have diverged. The push is rejected because moving the remote branch directly to L would discard the remote commit reachable from R. Git protects the shared reference rather than silently overwriting it. Approach and SolutionThe developer first inspects state using git status, git diff, and git diff --cached. The unstaged debugging edits are either staged as part of the same coherent change or saved separately. The developer then fetches the remote: git fetch origin After reviewing the graph, the developer integrates the updated remote branch, using either a merge or a team-approved rebase workflow. Conflicts, if present, are resolved and tested. The resulting history is then pushed. If the workflow requires a force update after rebasing, the safer form is a checked force push such as --force-with-lease, used only with team agreement. OutcomeThe final remote branch contains both the colleague’s work and the validated deployment improvement. The commit history records the integration method, and no remote commit is silently discarded. Learning Points- Staged and unstaged changes can coexist in one file.
- A remote-tracking reference is evidence of the last fetched remote state, not a live pointer.
- Push rejection is a protection mechanism.
- Fetching before integration improves visibility.
- History-changing operations require coordination when commits are shared.
12. Challenges, Governance, and Best PracticesGit’s flexibility creates responsibilities. Force-pushing can remove references to commits from a shared branch. Deleting branches or tags can weaken traceability. Secrets committed into history may remain recoverable even after a later deletion. Therefore, credentials should never be placed in repositories, and exposed secrets require revocation rather than merely editing the latest file. Recommended practices include: - write focused commits with meaningful messages;
- inspect staged content before committing;
- use
.gitignore for generated files, local settings, and sensitive local artifacts; - protect important branches with review and status checks;
- fetch regularly and understand divergence before pushing;
- use signed commits or tags where provenance requirements justify them;
- avoid rewriting public history unless a documented process permits it;
- use
--force-with-lease rather than unrestricted force-pushes when rewriting is necessary; - treat repository permissions, branch protection, and audit logs as governance controls;
- test locally and through continuous integration before release.
Git is also relevant to AI-assisted software development. AI tools may generate or modify files, but Git remains the mechanism for reviewing provenance, isolating changes, reverting errors, and assigning accountability. Generated code should be reviewed for security, licensing, correctness, and unintended inclusion of confidential data. A commit records what was committed; it does not prove that the content was independently verified. Chapter SummaryGit is a distributed version control system in which each clone normally contains a complete local repository. The working tree contains editable files, the staging area selects content for the next commit, the local repository stores history, and a remote repository provides a collaboration point. Git stores content as blobs, directory structures as trees, historical snapshots as commits, and optional metadata-rich tags as tag objects. Object identifiers are content-addressed hashes whose values depend on object content and metadata. References provide human-readable names for objects. Branches are movable references, tags identify significant points, and HEAD identifies the currently selected branch or commit. Detached HEAD state occurs when HEAD points directly to a commit. Commits and parent links form a directed acyclic graph. Branching creates multiple lines of development, merging creates multi-parent commits, fast-forwarding moves a reference without a new commit, and rebasing creates new commits with different parent relationships. Understanding these structures enables safe collaboration, meaningful history, reliable troubleshooting, and effective DevOps automation. Key TermsWorking tree: The checked-out directory containing files available for inspection and editing. Staging area: The index containing the file versions proposed for the next commit. Index: Git’s structured staging-area file and data structure. Local repository: The repository stored locally, including objects, references, configuration, and history. Remote repository: Another Git repository accessed for collaboration, exchange, or backup. Blob: A Git object containing file content without its pathname. Tree: A Git object representing directory structure and entries. Commit: An object recording a project snapshot, metadata, and parent commit references. Hash: An object identifier derived from Git object information and content. Reference: A human-readable name pointing to an object, usually a commit. HEAD: The symbolic or direct reference identifying the current checkout position. Branch: A movable reference to a commit, normally used to represent a line of development. Tag: A reference identifying a significant object, often a release point. Annotated tag: A tag object containing metadata and pointing to another object. Remote-tracking branch: A local reference recording a remote branch’s last fetched position. DAG: A directed acyclic graph representing commits and parent relationships. Fast-forward: A reference update in which the old target is an ancestor of the new target. Merge commit: A commit with multiple parents that integrates divergent histories. Rebase: An operation that recreates commits with a different base and new identifiers. Detached HEAD: A state in which HEAD points directly to a commit rather than a local branch. Review QuestionsShort Answer Questions- What is the difference between the working tree and the staging area?
- Why is a Git branch described as a movable reference?
- What information does a commit contain besides file content?
- What is the purpose of a remote-tracking reference such as
origin/main? - Define a detached
HEAD state.
Long Answer Questions- Explain the relationship among the working tree, staging area, local repository, and remote repository, using the commands
git add, git commit, git fetch, and git push. - Describe Git’s blob, tree, commit, and annotated tag objects and explain how they relate to one another.
- Explain how hashes provide object identity and why changing commit metadata creates a new commit identifier.
- Compare fast-forward merging, merge commits, and rebasing in terms of graph structure and collaboration risks.
- Explain Git’s directed acyclic graph model and show how branching and merging are represented.
Analytical / Application Questions- A file has both staged and unstaged changes. Which version will the next commit contain, and which commands can be used to inspect each version?
- A push is rejected because the remote branch has advanced. Analyze the cause and propose a safe sequence of diagnostic and integration actions.
- A team wants to mark production releases and prevent release identifiers from moving accidentally. Compare lightweight and annotated tags and recommend an approach.
Multiple Choice Questions with AnswersQuestion 1Which Git area contains the files that a developer is currently editing? A. Working tree B. Object database C. Remote-tracking namespace D. Tag database Correct Answer: A. Working tree Question 2What does git add primarily do? A. Uploads commits to a remote B. Updates the staging area with selected content C. Creates a remote repository D. Deletes the current branch Correct Answer: B. Updates the staging area with selected content Question 3Which Git object represents directory structure and maps names to objects? A. Blob B. Commit C. Tree D. Reference Correct Answer: C. Tree Question 4What does a normal branch name generally point to? A. A movable reference to a commit B. A copy of the entire repository C. Only the working-tree directory D. A network connection Correct Answer: A. A movable reference to a commit Question 5What is the usual meaning of origin/main? A. The currently checked-out local branch B. A local record of the last fetched state of the remote main branch C. An annotated release tag D. The staging area for main Correct Answer: B. A local record of the last fetched state of the remote main branch Question 6When is HEAD detached? A. When the index is empty B. When a remote cannot be contacted C. When HEAD points directly to a commit rather than a local branch D. When a branch has two parents Correct Answer: C. When HEAD points directly to a commit rather than a local branch Question 7Which feature makes Git history a directed acyclic graph? A. Files are stored alphabetically B. Commits have directed parent relationships with no cycles C. Branches always have exactly one commit D. Remote repositories use encryption Correct Answer: B. Commits have directed parent relationships with no cycles Question 8What happens during a fast-forward update? A. A new merge commit is always created B. The target reference moves to a descendant without creating a merge commit C. All commits are rewritten D. The working tree is deleted Correct Answer: B. The target reference moves to a descendant without creating a merge commit Question 9Why might a remote reject a normal push? A. The local repository contains a staging area B. The proposed update could discard commits already reachable from the remote branch C. The commit has a parent D. The branch name contains letters Correct Answer: B. The proposed update could discard commits already reachable from the remote branch Question 10What is a principal difference between rebasing and merging? A. Rebasing recreates commits with new parent relationships, while merging may create a multi-parent commit B. Merging cannot create commits C. Rebasing changes only filenames and never commit identifiers D. Merging is possible only with remote repositories Correct Answer: A. Rebasing recreates commits with new parent relationships, while merging may create a multi-parent commit
|