Continuous Integration and Automation with DevOps

Chapter 2
Version Control and Collaborative Development

Diploma in Cloud Computing
Continuous Integration and Automation with DevOps   •   Diploma

Version Control and Collaborative Development

Introduction and Context

Modern software is rarely created by one person working on one computer. Cloud applications, infrastructure code, deployment pipelines, documentation, and configuration files are developed by teams whose members may work in different locations and at different times. A change that improves one component can unintentionally break another. Without a reliable record of change, collaboration becomes dependent on memory, manually copied files, and informal communication.

Version control provides the technical and organizational foundation for solving this problem. It records changes to files, identifies who made them and when, permits earlier versions to be inspected or restored, and enables several people to work on related tasks. Git is the most widely used distributed version-control system in contemporary DevOps practice. Platforms such as GitHub, GitLab, and Bitbucket add collaboration facilities including remote repositories, pull requests, access control, automated checks, and review workflows.

In continuous integration and automation, Git is more than a file archive. It is a source of truth for application code, infrastructure-as-code, test suites, pipeline definitions, and release history. A well-managed repository allows every change to be examined, tested, approved, and connected to a deployable artifact.

Learning Objectives

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

  • explain Git’s data model and distinguish working trees, staging areas, local repositories, and remote repositories;
  • create repositories, make meaningful commits, inspect history, and use tags;
  • create, merge, rebase, and delete branches appropriately;
  • compare trunk-based development with feature-branch workflows;
  • conduct effective pull requests and code reviews;
  • diagnose and resolve merge conflicts safely;
  • use Git techniques for rollback and recovery;
  • apply repository governance, security, and professional collaboration practices.

Version Control Concepts

A version-control system maintains a sequence of states of a project. Each recorded state is associated with a change description and usually with an author, timestamp, and unique identifier. Version control supports four related capabilities:

  1. History: determining what changed and when.
  2. Traceability: linking a change to a person, task, issue, test, or release.
  3. Parallel development: allowing multiple lines of work.
  4. Recovery: inspecting or restoring a known-good state.

Git is a distributed version-control system. Every clone normally contains a complete repository history, not merely a working copy connected to a central server. A developer can commit and inspect history while offline. A remote platform is still important for sharing, collaboration, backup, policy enforcement, and automation, but Git’s fundamental operations are local.

Git’s Four Working Areas

A practical mental model consists of four areas:

AreaPurposeTypical command
Working treeFiles currently being editedgit status
Staging area (index)The exact content selected for the next commitgit add
Local repositoryCommitted history on the developer’s machinegit commit
Remote repositoryShared repository hosted on a servergit push, git fetch

The staging area is important because it permits a developer to construct a focused commit. For example, a file may contain both a bug fix and an unrelated formatting change. The developer can stage only the bug-fix lines and commit them separately, producing a clearer history.

A commit is not simply a compressed copy of a folder. Git stores objects identified by cryptographic object IDs. A commit records a snapshot reference, parent commit reference or references, author and committer information, time, and message. A normal commit has one parent; a merge commit commonly has two or more parents. Branches are movable references to commits, while tags are generally intended to identify significant, stable points such as releases.

Repository Creation and Essential Commands

A repository can be created in a new project or obtained by cloning an existing remote repository.

git init
 git add .
git commit -m "Initial project structure"

The leading space before git add above is not required; commands should be entered without it. A more complete first setup is:

mkdir payments-service
cd payments-service
git init
git add README.md
git commit -m "Add project README"
git branch -M main
git remote add origin https://example.com/team/payments-service.git
git push -u origin main

To obtain an existing repository:

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

Useful inspection commands include:

git status
git log --oneline --graph --decorate --all
git diff
git diff --staged
git show <commit-id>
git remote -v

git diff compares working-tree changes with the index. git diff --staged compares staged content with the current commit. This distinction is essential for verifying what will actually be committed.

Commits as Professional Communication

A high-quality commit should represent one coherent change. Its message should state the change rather than merely the location of the change. “Fix timeout when payment gateway is unavailable” is more informative than “Update service.py.” Commits should be small enough to review but complete enough to build or test whenever practical.

A useful commit usually has:

  • a clear imperative message;
  • a limited and logically related scope;
  • no generated secrets, temporary files, or unrelated formatting changes;
  • tests or documentation appropriate to the change;
  • a relationship to an issue, requirement, or operational concern where applicable.

The .gitignore file prevents selected files from being tracked, such as build output, local environment files, and editor metadata. It does not remove a file that has already been committed. A previously tracked secret must be removed from the repository and, more importantly, revoked or rotated; deleting it in a later commit does not erase it from historical copies.

Branches, Merging, and Rebasing

A branch is a named reference to a line of development. Creating a branch does not duplicate the entire project in the ordinary conceptual sense; it creates a new reference from a commit. A typical workflow is:

git switch -c feature/payment-timeout
# edit files
git add src/ tests/
git commit -m "Handle payment gateway timeouts"
git push -u origin feature/payment-timeout

A branch should have a clear purpose and a limited lifetime. Long-lived branches drift from the main line and increase the probability of conflicts and integration defects.

Merging

Merging combines histories. If the receiving branch has not advanced since the feature branch diverged, Git can perform a fast-forward merge by moving the branch reference. If both branches contain new commits, Git may create a merge commit that records both histories.

git switch main
git pull --ff-only origin main
git merge --no-ff feature/payment-timeout
git push origin main

The exact merge policy depends on team rules. A no-fast-forward merge can preserve the existence of a completed feature in the history, while a fast-forward or squash policy can produce a more linear history. Neither style is universally correct; consistency and traceability matter more than personal preference.

Rebasing

Rebasing replays commits onto a new base. It can produce a linear history and incorporate current changes from the main branch:

git switch feature/payment-timeout
git fetch origin
git rebase origin/main

During a rebase, Git may pause for conflicts. After resolving a file, the developer stages it and continues:

git add path/to/file
git rebase --continue

A rebase changes commit identities because the parent relationship changes. Therefore, developers should not rebase shared commits that others may already have based work upon, unless the team explicitly understands and accepts the consequences. Updating a previously pushed branch after rebase may require git push --force-with-lease, which is safer than unrestricted force-pushing because it checks that the remote reference has not unexpectedly advanced. Protected branches should normally reject force-pushes.

Tags, Releases, and Rollback

A tag is a named reference to a particular commit. Annotated tags are suitable for releases because they include metadata and can be signed:

git tag -a v2.1.0 -m "Release version 2.1.0"
git push origin v2.1.0
git show v2.1.0

Tags should be immutable in practice. Moving a release tag after publication can make deployments irreproducible and weaken audit evidence. A release process should identify the exact commit, build inputs, test results, and deployment environment.

Rollback has several meanings and should be chosen deliberately:

  • Restore files: discard uncommitted local changes, for example with git restore, after confirming that the changes are not needed.
  • Reset a private branch: move a local branch to another commit. git reset --hard can destroy uncommitted work and must be used cautiously.
  • Revert a shared commit: create a new commit that reverses an earlier change. This is generally the safest approach on a shared branch.

For example:

git switch main
git revert <problematic-commit-id>
git push origin main

A rollback of source code does not automatically roll back database migrations, cloud resources, external messages, or already published packages. Operational rollback planning must therefore include compatible data changes, configuration, deployment artifacts, and recovery procedures.

Collaborative Development Models

Trunk-Based Development

In trunk-based development, developers integrate small changes into a shared trunk, commonly named main, at least daily or through very short-lived branches. Automated tests and branch protection are central. Feature flags can hide incomplete functionality while code is integrated incrementally.

Advantages include:

  • frequent integration and rapid feedback;
  • fewer large merge conflicts;
  • direct alignment with continuous integration;
  • reduced branch-maintenance overhead.

Risks include accidental exposure of incomplete behavior, pressure to maintain strong automated tests, and disruption if the trunk is not kept releasable. Feature flags, backward-compatible changes, and disciplined review reduce these risks.

Feature-Branch Workflow

In a feature-branch workflow, each task is developed on a branch and integrated through a pull request. The branch may live for hours or days, depending on the work. This model provides an explicit review boundary and is familiar to many teams.

Its risks are branch divergence, delayed feedback, and large conflict resolution. These risks are reduced by keeping branches short-lived, regularly synchronizing with the trunk, and avoiding unrelated changes.

Choosing a Model

The choice should reflect team size, deployment frequency, regulatory obligations, system architecture, and test maturity. A team may use trunk-based development for application code while using reviewed branches for infrastructure changes requiring additional approval. The important principle is that the workflow should make integration frequent, visible, and safe rather than merely formal.

Pull Requests and Code Review

A pull request (PR), or merge request, proposes integrating changes into a target branch. It is both a technical mechanism and a communication process. A strong PR description explains:

  • the problem and intended outcome;
  • the design and important trade-offs;
  • tests performed and their results;
  • deployment or migration considerations;
  • screenshots, logs, or metrics when useful;
  • known limitations and follow-up work.

Automated checks should normally run before human approval. Typical checks include compilation, unit and integration tests, linting, security scanning, dependency analysis, policy validation, and infrastructure plan review.

A reviewer should evaluate behavior, design, maintainability, security, performance, observability, tests, and operational consequences. Review comments should be specific and respectful. A reviewer might write, “Could this retry path preserve the original request identifier? Otherwise duplicate charging may occur,” rather than making a personal judgment about the author.

Review is not a substitute for testing, and automated testing is not a substitute for review. The two controls identify different classes of risk. Teams should also distinguish blocking defects from optional improvements and resolve all blocking comments before merging.

Repository governance commonly includes:

  • protected main and release branches;
  • required status checks and approvals;
  • CODEOWNERS or domain ownership rules;
  • signed commits or verified identities where appropriate;
  • least-privilege permissions;
  • audit logs and retention rules;
  • secret scanning and dependency alerts.

Merge Conflicts and Their Resolution

A conflict occurs when Git cannot automatically reconcile competing changes, often because two branches modify overlapping lines or because one changes a file while the other deletes it. A conflict is not evidence that one person is wrong; it is a signal that human judgment is required.

A safe resolution process is:

  1. Read the conflict message and identify the common ancestor, current branch, and incoming branch.
  2. Inspect the affected file and the surrounding logic, not just the marker lines.
  3. Consult the requirement and, if necessary, the other author.
  4. Edit the file into the intended final state and remove markers such as <<<<<<<, =======, and >>>>>>>.
  5. Run formatting, unit tests, and relevant integration tests.
  6. Stage the resolution and continue the merge or rebase.
  7. Review the resulting diff before committing or pushing.

For a merge, useful commands include:

git status
git diff
git add <resolved-file>
git commit

For a rebase, use git rebase --continue; to abandon it, use git rebase --abort. During a merge, git merge --abort usually returns the working tree to its pre-merge state. Abort commands should be used when the situation is unclear rather than guessing through a complex conflict.

The technically cleanest conflict resolution can still be functionally incorrect. For example, choosing one side of a configuration file may remove a security setting from the other side. Tests and domain knowledge are therefore essential.

Worked Development Example

Assume a developer must add a request timeout to a cloud payment service.

First, the developer updates the local trunk and creates a short-lived branch:

git switch main
git pull --ff-only origin main
git switch -c feature/payment-timeout

The developer changes the client, adds tests for success, timeout, and retry behavior, then inspects the changes:

git diff
git add src/payment_client.py tests/test_payment_client.py
git diff --staged
git commit -m "Handle payment gateway timeouts"
git push -u origin feature/payment-timeout

The PR runs automated tests and is reviewed by a service owner. While the PR is open, another change reaches main. The developer obtains the latest history and rebases only the private feature branch:

git fetch origin
git rebase origin/main

If a conflict occurs, the developer resolves it with the service owner’s input, reruns tests, and updates the PR. After approval, the platform merges the PR according to repository policy. A release pipeline builds the exact merge commit, publishes an artifact, and creates tag v2.1.0. If production reveals an unsafe timeout behavior, the team can revert the merge commit, deploy the resulting artifact, and investigate the original change without deleting history.

This example illustrates a complete chain: isolated work, explicit commits, automated validation, review, conflict management, reproducible release identification, and controlled recovery.

Illustrative Case Study: Northstar Logistics

Background

Northstar Logistics operates a cloud service that assigns delivery routes. Six developers, a platform engineer, and a product owner maintain the service, its Terraform infrastructure, and a continuous delivery pipeline.

Problem

The team used long-lived branches and merged large batches every two weeks. Integration frequently failed because database migrations, application changes, and infrastructure changes were developed separately. Developers also occasionally committed local configuration files containing credentials.

Analysis

Repository history showed that branches commonly diverged by more than 200 commits. Pull requests were large, reviewers lacked context, and tests ran only after integration. The team had no protected branch policy and used mutable release tags. These practices created technical and governance risk, not merely inconvenience.

Approach and Solution

Northstar adopted short-lived branches with a trunk-oriented policy. Every PR required two reviewers for infrastructure changes, one service-owner approval for application changes, and passing unit, integration, security, and infrastructure-plan checks. The team introduced .gitignore, secret scanning, pre-commit checks, CODEOWNERS, and protected main and release branches.

Database changes were redesigned to be backward compatible: a column could be added before application code used it, and removal occurred only in a later release. Incomplete routing behavior was controlled through feature flags. Releases were tagged from immutable merge commits, and deployment records stored the commit ID and artifact digest. A documented git revert procedure was combined with database and infrastructure rollback plans.

Outcome

Conflicts became smaller and easier to resolve because changes were integrated daily. Reviewers received focused PRs, and failed checks blocked unsafe merges. The team could identify which source commit produced a deployed artifact and could disable a feature without immediately reverting all code. Although the new process required investment in tests and automation, it reduced uncertainty and improved delivery confidence.

Learning Points

The case demonstrates that Git commands alone do not create effective DevOps. Value arises when repository practices, automated verification, branch policy, release identity, security controls, and operational recovery are designed as one system.

Challenges, Limitations, and Governance

Git records content and history, but it does not by itself guarantee correctness, security, or regulatory compliance. Large binary files may require Git Large File Storage or an artifact repository. A repository can contain vulnerable dependencies, insecure code, or personal data even when its history is technically consistent.

Secret management deserves special attention. Credentials should be stored in an approved secret manager and injected at runtime. If a secret enters Git, it must be revoked, replaced, and removed from accessible history according to an incident procedure. History rewriting can reduce exposure but cannot be assumed to erase copies held by clones, caches, or forks.

Organizations should define retention, access, licensing, data-classification, and audit requirements. Contributions may require copyright or open-source license compliance. Automated AI coding tools can assist with suggestions, test generation, and review summaries, but generated code must be reviewed for licensing, security, hallucinated APIs, hidden data disclosure, and maintainability. An AI tool should not be given confidential source or credentials unless organizational policy explicitly permits it.

Best Practices Checklist

  • Keep main buildable and protected.
  • Make commits cohesive, descriptive, and traceable.
  • Review staged content before committing.
  • Keep branches short-lived and synchronize frequently.
  • Prefer git revert for changes already shared on a protected branch.
  • Never use force-push casually; use --force-with-lease only when justified.
  • Require automated checks before merging.
  • Treat conflict resolution as a design decision, followed by testing.
  • Tag immutable release commits and record build artifacts.
  • Never commit secrets, private keys, or unnecessary personal data.
  • Define ownership, approval, retention, and access policies.
  • Connect source history to issues, tests, deployments, and operational evidence.

Chapter Summary

Git is a distributed version-control system that records project history and supports reliable collaboration. Its working tree, staging area, local repository, and remote repository have distinct roles. Meaningful commits, informative history, branches, merges, rebases, tags, and controlled rollback are the basic technical practices.

Trunk-based development emphasizes frequent integration into a stable trunk, while feature-branch workflows provide an explicit review boundary. Both require automation, short-lived divergence, and disciplined branch policies. Pull requests combine automated checks with human assessment of behavior, design, security, and operability. Conflicts require contextual judgment, careful testing, and communication rather than blind selection of one side.

Professional version control also includes protected branches, least privilege, secret management, traceable releases, license and data governance, and recovery planning. In DevOps, Git is not an isolated developer tool: it is a central control point connecting collaboration, continuous integration, deployment, auditability, and operational reliability.

Key Terms

Version control: A system for recording and managing changes to files over time.

Git: A distributed version-control system used to manage source and configuration history.

Repository: The Git-managed project history and associated metadata.

Working tree: The files currently checked out and available for editing.

Staging area: The index containing content selected for the next commit.

Commit: A recorded snapshot and its metadata in repository history.

Branch: A movable named reference to a commit.

Trunk: The principal shared development line, commonly named main.

Remote repository: A repository hosted in a shared service or server.

Clone: A local copy of a repository, normally including its history.

Merge: The operation that combines histories from different lines of development.

Rebase: Replaying commits onto a different base commit.

Tag: A named reference used to identify a significant commit, such as a release.

Pull request: A proposal to integrate changes, accompanied by review and checks.

Code review: Systematic human examination of a proposed change.

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

Rollback: Returning behavior or deployment to a prior safe state.

Feature flag: A control that enables or disables functionality independently of code deployment.

Protected branch: A branch governed by rules such as required reviews and status checks.

Secret scanning: Automated detection of credentials or sensitive tokens in repository content.

Review Questions

Short Answer Questions

  1. What is the difference between the working tree and the staging area?
  2. Why should a commit normally represent one coherent change?
  3. What is the principal risk of rebasing commits that have already been shared?
  4. Why is git revert usually safer than resetting a shared branch?
  5. Name two controls that can protect a production branch.

Long Answer Questions

  1. Explain Git’s distributed model and describe the relationship among a local repository, a remote repository, and a clone.
  2. Compare trunk-based development and feature-branch workflows, including advantages, risks, and conditions for successful use.
  3. Describe a complete pull-request process from branch creation through automated checks, review, merge, and release tagging.
  4. Explain how to resolve a merge conflict safely and why choosing one side without testing can be dangerous.
  5. Discuss how version-control practices contribute to continuous integration, deployment traceability, and operational recovery.

Analytical / Application Questions

  1. A team has weekly integration branches containing many unrelated features, frequent conflicts, and no automated tests. Diagnose the process weaknesses and propose a staged improvement plan.
  2. A production defect was introduced by a merge commit, but the release also included a database migration. Decide whether a simple code revert is sufficient and design a safer recovery approach.
  3. During review, a developer discovers that a feature branch contains a cloud credential in an earlier commit, although the latest file no longer contains it. Explain the security response, repository actions, and preventive controls required.

Multiple Choice Questions with Answers

Question 1

Which Git area contains the exact content selected for the next commit?

A. Working tree

B. Staging area

C. Remote repository

D. Tag database

Correct Answer: B. Staging area

Question 2

Which command creates and switches to a new branch named feature/api?

A. git branch --delete feature/api

B. git merge feature/api

C. git switch -c feature/api

D. git tag feature/api

Correct Answer: C. git switch -c feature/api

Question 3

What is generally the safest way to undo an already shared commit on a protected branch?

A. git revert

B. git reset --hard

C. Delete the repository

D. Force-push an unrelated branch

Correct Answer: A. git revert

Question 4

What is a principal characteristic of trunk-based development?

A. Changes remain on branches for several months

B. Changes are integrated frequently into a shared trunk

C. Human review is prohibited

D. The main branch is never tested

Correct Answer: B. Changes are integrated frequently into a shared trunk

Question 5

What does rebasing do?

A. Deletes all branch history

B. Converts a repository into a remote

C. Replays commits onto a different base

D. Creates an annotated release automatically

Correct Answer: C. Replays commits onto a different base

Question 6

Which item should normally be required before merging a pull request?

A. A passing set of relevant automated checks

B. A mutable production tag

C. An unreviewed force-push

D. A copy of credentials in the repository

Correct Answer: A. A passing set of relevant automated checks

Question 7

What should a developer do after manually resolving conflict markers?

A. Ignore the files and push immediately

B. Run appropriate tests and inspect the resulting diff

C. Delete the branch without consultation

D. Replace the entire repository with the remote copy

Correct Answer: B. Run appropriate tests and inspect the resulting diff

Question 8

Why are annotated tags commonly used for releases?

A. They automatically encrypt every source file

B. They contain release-oriented metadata and identify a commit

C. They prevent all merge conflicts

D. They replace automated tests

Correct Answer: B. They contain release-oriented metadata and identify a commit

Question 9

Which statement about a secret committed to Git is correct?

A. Deleting it in a later commit makes it permanently safe

B. It should be revoked or rotated even if removed later

C. Renaming the file eliminates exposure

D. A branch deletion guarantees that no copy exists

Correct Answer: B. It should be revoked or rotated even if removed later

Question 10

What is the main purpose of a pull request?

A. To propose integration while enabling checks and review

B. To erase the local repository

C. To bypass branch protection

D. To convert source code into a database

Correct Answer: A. To propose integration while enabling checks and review

Continuous Integration and Automation with DevOps
Chapter 2 of 15