Chapter 2
Version Control and Collaborative Workflows
Diploma in DevOps
CI/CD Pipelines with Jenkins or GitHub Actions
•
Diploma
Version Control and Collaborative Workflows
Introduction and Context
Modern software delivery depends on the ability to change code safely, repeatedly, and collaboratively. Version control provides the historical record and coordination mechanism for those changes, while collaborative workflows define how people propose, review, integrate, and release them. In a CI/CD environment, these practices are more than developer conveniences: they are control points that determine when Jenkins or GitHub Actions starts a pipeline, which changes are permitted to merge, and what evidence is required before software reaches users.
Git is the dominant distributed version-control system used in contemporary DevOps. It records project history through commits, allows developers to work independently through branches, and supports controlled integration through pull requests and merge policies. A well-designed workflow reduces conflicting changes, makes defects easier to trace, and creates reliable links between source code, automated tests, security checks, and deployments.
This chapter treats version control as both a technical system and a governance system. The technical commands matter, but so do naming conventions, review responsibilities, protected branches, and the interpretation of pipeline results.
Learning Objectives
By the end of this chapter, you should be able to:
explain Git repositories, commits, branches, remotes, and tags;
perform essential Git operations safely;
compare feature-branch, Gitflow, and trunk-based development;
create effective pull-request and code-review practices;
apply semantic versioning and useful commit conventions;
design merge policies that act as CI/CD controls;
connect repository events to Jenkins or GitHub Actions pipelines; and
evaluate workflow risks, limitations, and governance requirements.
Git as a Distributed Collaboration System
A Git repository contains the project files together with a directed history of snapshots. A commit identifies a particular state of the tracked files and records metadata such as its author, message, parent commit, and timestamp. Git does not normally store a separate complete copy of every file for every commit; it uses objects and references to represent history efficiently. A branch is a movable reference to a commit, whereas a tag is generally a stable name attached to a particular commit, often used to identify a release.
A developer’s local repository is independent of a remote repository. The remote, commonly hosted on GitHub, GitLab, or another service, is a shared exchange point rather than the repository itself. clone creates a local copy, fetch retrieves remote history without changing the current branch, pull usually fetches and integrates remote changes, and push transfers local commits to a remote.
The working tree is the directory currently being edited. The staging area, also called the index, is the proposed content for the next commit. This separation allows a developer to select only related changes for a commit.
Git changes moving from working tree through staging area and local repository to remote repository
Figure: Git changes moving from working tree through staging area and local repository to remote repository
Figure: The principal flow of changes through Git’s working tree, staging area, local history, and remote repository.
Essential Git Operations
A disciplined sequence commonly begins with git status, which reveals the current branch and uncommitted changes. Developers create or switch branches with commands such as git switch -c feature/payment-validation. They inspect differences using git diff, stage selected files with git add, and create a commit with git commit.
Before publishing work, a developer should update knowledge of the target branch, for example with git fetch origin followed by an appropriate integration method. git push -u origin feature/payment-validation publishes a new branch and establishes its upstream relationship. The exact choice between merge and rebase should follow team policy. Rebasing can produce a simpler linear history, but it rewrites commit identifiers and should not be used casually on shared branches.
A conflict occurs when Git cannot automatically reconcile two changes. Conflict resolution requires understanding the intended behavior, editing the marked files, running tests, staging the resolved files, and completing the merge or rebase. Resolving a conflict by accepting one side without examining the requirement is unsafe. git reflog can often help recover references after an accidental reset or rebase, but it is not a substitute for backups and remote protection.
Branching Strategies
A branching strategy defines how long-lived branches are used and how changes progress toward release. No strategy is universally best; the appropriate choice depends on release frequency, team size, regulatory constraints, and system architecture.
Feature-Branch Workflow
In a feature-branch workflow, each change is developed on a branch created from an integration branch such as main. The branch is pushed to the remote and submitted as a pull request. Automated checks run before review and merge. Feature branches isolate incomplete work and are useful when a change requires several commits or when review is mandatory.
Their principal risk is lifespan. Long-lived branches drift away from main, increasing merge conflicts and integration uncertainty. Small branches, frequent updates, and feature flags reduce this risk.
Gitflow
Gitflow traditionally uses long-lived develop and main branches, together with feature, release, and hotfix branches. It can be suitable for products with scheduled releases and formal stabilization periods. However, it adds process and integration delay. For teams practising continuous delivery, Gitflow may encourage large batches of changes and make the branch used for production different from the branch tested most frequently.
Gitflow should therefore be selected deliberately rather than adopted as a default. A release branch is not automatically evidence of release quality; automated tests and deployment controls remain necessary.
Trunk-Based Development
Trunk-based development centres on a shared trunk, usually main. Developers integrate small changes frequently, using short-lived branches or direct commits where policy permits. Incomplete functionality is hidden behind feature flags, configuration, or incremental implementation. The objective is to keep the trunk in a releasable state.
This model aligns naturally with CI/CD because every integration can trigger validation. It requires fast tests, strong automation, effective code review, and a culture that treats a broken trunk as an urgent problem. Trunk-based development is not the absence of control; it moves control from long-lived branches into automated checks, review, observability, and rapid rollback.
Strategy
Main strength
Main risk
Typical fit
Feature branches
Isolated reviewable work
Branch drift
General team development
Gitflow
Explicit release phases
Process and integration overhead
Scheduled releases
Trunk-based
Frequent integration and rapid feedback
Requires mature automation
Continuous delivery
Pull Requests and Code Review
A pull request (PR) is a proposal to integrate one branch into another. It is both a collaboration space and a machine-readable control point. A useful PR describes the problem, the proposed solution, testing performed, operational effects, security implications, and any migration or rollback considerations. Linking the PR to an issue or requirement improves traceability.
Code review should evaluate behavior, maintainability, security, performance where relevant, test adequacy, and compatibility with project conventions. Reviewers should focus on the change rather than the personality of the author. Comments should distinguish blocking defects from suggestions. Authors should respond constructively and avoid treating approval as a personal judgment.
Review quality is not measured only by the number of comments. A small, comprehensible change reviewed by an appropriate subject-matter expert may be more valuable than a large PR with many superficial comments. Reviewers should be assigned according to code ownership or expertise, while avoiding concentration of all approvals in one person.
A PR is not a replacement for automated testing. Human reviewers are effective at intent, design, and context; automated checks are effective at repeatable verification. The two controls address different failure modes.
Commit Conventions and Traceability
A commit should represent one coherent logical change. Useful commit messages explain intent rather than merely naming a file. A common convention is a structured form such as:
type(scope): concise imperative description
Examples include feat(api): add invoice lookup, fix(auth): reject expired tokens, and docs(runbook): clarify rollback steps. Teams may adopt Conventional Commits or another documented standard. The selected vocabulary might include feat, fix, docs, test, refactor, build, and ci.
Commit conventions support searchable history, release-note generation, and automated version recommendations. They do not prove that a change is correct. A syntactically valid message can still describe a defective implementation. Avoid embedding secrets, passwords, access tokens, or sensitive personal data in commits. Removing a secret in a later commit does not remove it from history; exposure requires credential revocation and appropriate history-remediation procedures.
Semantic Versioning
Semantic Versioning, commonly written as MAJOR.MINOR.PATCH, gives a public API a structured compatibility signal. Under the conventional rules:
increment PATCH for backward-compatible bug fixes;
increment MINOR for backward-compatible functionality; and
increment MAJOR for incompatible public API changes.
The rules apply to a clearly defined public API and assume that compatibility is assessed according to the project’s documented contract. A project must decide what constitutes its API, including command-line interfaces, configuration formats, database schemas, events, and libraries. Pre-release identifiers, such as 2.0.0-rc.1, indicate that compatibility guarantees may not yet be final.
Semantic versioning does not measure quality, security, or the amount of effort involved. A one-line breaking change may require a major version, while a large internal refactor may require only a patch release. Pipeline automation can validate tag format, generate release notes, and publish artifacts, but a human or policy-controlled process must determine whether the compatibility classification is accurate.
Merge Policies as CI/CD Controls
A merge policy defines the conditions under which a branch may enter a protected target branch. Typical controls include:
required pull-request approval by one or more reviewers;
successful unit, integration, lint, build, and security checks;
up-to-date branch status or successful merge-queue validation;
signed commits or verified identities where required;
no unresolved review conversations;
successful deployment to a test environment;
prohibition of direct pushes to protected branches; and
required issue, change, or compliance references.
These controls should be proportionate to risk. Requiring five approvals for every documentation change can create delay without improving safety, while allowing unreviewed changes to authentication code is dangerous. CODEOWNERS-style rules can require subject-matter experts for sensitive paths.
A policy is effective only if it is enforced by the hosting platform or pipeline. A statement in a handbook that “all code must be reviewed” is weaker than a protected branch that technically blocks merging without an approval. Nevertheless, technical enforcement must be accompanied by an emergency procedure, because outages and security incidents may require exceptional changes. Emergency access should be limited, logged, reviewed afterward, and revoked when no longer needed.
Repository Events as Pipeline Triggers
CI/CD systems respond to repository events. In GitHub Actions, workflows may be triggered by events such as push, pull_request, merge_group, workflow_dispatch, or release publication. In Jenkins, a multibranch pipeline can discover branches and pull requests, while webhooks notify Jenkins that new commits or PR activity exists. The pipeline should check out the exact commit associated with the event, not an ambiguous moving reference.
A practical event policy separates validation from release:
Event
Typical pipeline purpose
Deployment authority
Pull-request update
Build, test, lint, scan
Usually no production deployment
Push to protected trunk
Full validation and integration
May deploy to a test environment
Version tag or release
Package and publish artifact
Controlled production promotion
Manual dispatch
Approved operational action
Restricted users and audit trail
Secrets must not be exposed to untrusted pull-request code. In particular, workflows handling contributions from forks require careful treatment of credentials and permissions. A pipeline should use least-privilege tokens, pin or manage third-party actions responsibly, record artifacts and test results, and avoid allowing unreviewed code to modify the workflow that grants it deployment authority.
A strong design makes the path from change to deployment visible: PR checks establish merge eligibility; post-merge checks validate the integrated trunk; release workflows publish immutable artifacts; promotion workflows deploy the artifact that was tested rather than rebuilding different source later.
Applied Case: Northstar Billing Service
Background
Northstar Billing is an illustrative SaaS team maintaining a payment-invoice service. The team has eight developers, releases several times per week, and uses GitHub with GitHub Actions. Production incidents have increased because large feature branches are merged near scheduled releases. Reviewers often approve changes without seeing a reliable integration test result.
Problem Analysis
The team identifies four weaknesses: branches live for several weeks; pull-request checks can be bypassed by direct pushes; release numbers are chosen inconsistently; and the production workflow rebuilds source code rather than promoting the exact tested artifact. These weaknesses create both technical and governance risks.
Approach
Northstar adopts short-lived branches from main and requires a pull request for every production-bound change. Branch protection requires two approvals for payment logic, one approval elsewhere, successful unit and integration tests, a security scan, and a branch that is current with main. A merge queue validates the proposed merge result.
The team introduces commit types such as feat, fix, and ci, and defines the public API and event contracts covered by semantic versioning. A release workflow runs only for signed version tags. It builds an immutable container image, records its digest, and promotes that same digest through staging and production. Feature flags allow incomplete invoice functionality to merge without exposing it to customers.
Jenkins is retained for a legacy deployment job, but its multibranch pipeline is configured to validate PRs and branches while production deployment requires an approved release event. GitHub Actions performs fast checks; Jenkins performs a specialized environment integration test. Status results from both systems are required by branch protection.
Outcome
The team reduces branch age, detects integration defects before merge, and can trace a production image to a specific commit, pull request, review, and test record. The workflow does not eliminate defects, but it makes failures earlier, more visible, and easier to reverse.
Learning Points
The case demonstrates that branching, review, versioning, and pipeline design are interdependent. A protected branch is useful only when checks are meaningful. A release tag is useful only when it identifies an immutable, tested artifact. Automation improves control, but policy must define the decisions being automated.
Challenges, Limitations, and Governance
Version-control workflows can become bureaucratic. Excessive approvals increase cycle time and encourage rubber-stamping. Excessively strict checks can be bypassed during emergencies or lead teams to disable controls. Large repositories may require path-based ownership, repository decomposition, or specialized build systems. Generated files and database migrations need explicit review rules because their risks differ from ordinary source files.
History is not automatically trustworthy. Force-pushing, shared accounts, unreviewed workflow changes, and mutable release tags weaken auditability. Signed commits and protected tags can improve provenance, but they do not establish that the signer reviewed the code or that the build environment was secure. Supply-chain security also requires dependency management, build isolation, artifact integrity, and monitoring.
Ethically and legally, teams must protect credentials, personal data, proprietary code, and regulated information. Repository logs and PR discussions may contain personal or customer information and should follow retention and access policies. AI coding assistants can accelerate development, but generated code requires the same review, testing, licensing assessment, and security analysis as human-written code. Developers should not paste confidential source or credentials into tools whose data-handling terms are unknown.
Best-Practice Checklist
Keep the protected trunk releasable whenever practical.
Make branches short-lived and changes small enough to review.
Use clear commit messages and avoid secrets in history.
Require automated checks that test the integrated result.
Protect workflow definitions and deployment credentials.
Match review requirements to technical and business risk.
Define the public API before applying semantic versioning.
Build once and promote the tested immutable artifact.
Log exceptions and review emergency changes.
Regularly inspect whether controls improve quality rather than merely adding delay.
Chapter Summary
Git provides distributed history, branching, integration, and traceability. The working tree, staging area, local commits, and remote repository form a controlled path for changes. Feature branches offer isolation, Gitflow formalizes release phases, and trunk-based development emphasizes frequent integration; each has conditions under which it is effective.
Pull requests combine human judgment with automated evidence. Commit conventions improve communication and can support release automation, while semantic versioning communicates compatibility only when the public API and compatibility rules are well defined. Protected branches, required reviews, successful CI checks, signed or verified changes, and controlled release events turn repository policy into enforceable CI/CD controls.
Jenkins and GitHub Actions can respond to pull requests, pushes, tags, and manual events, but secure pipeline design requires exact commit selection, least-privilege credentials, safe handling of forked contributions, and promotion of immutable tested artifacts. Effective collaboration is therefore a socio-technical system: tools enforce selected rules, while people define risk, intent, accountability, and ethical boundaries.
Key Terms
Branch: A movable reference to a commit history used to develop or identify a line of work.
Commit: A recorded snapshot of tracked project content with metadata and parent history.
Distributed version control: A model in which each clone contains repository history locally.
Fetch: The operation of retrieving remote references and objects without automatically integrating them.
Feature flag: A runtime or configuration mechanism that controls whether functionality is exposed.
Gitflow: A branching model using long-lived integration and release branches.
Merge: The integration of histories or changes from one line of development into another.
Merge queue: A service that validates proposed changes in the order or combination in which they will enter a protected branch.
Protected branch: A branch with platform-enforced restrictions on merging, pushing, or deleting.
Pull request: A request to review and integrate changes from one branch into another.
Remote: A named repository location used to exchange commits and references.
Semantic versioning: A versioning convention using major, minor, and patch compatibility signals.
Staging area: Git’s index containing the content selected for the next commit.
Tag: A named reference, commonly used to identify a release commit.
Trunk-based development: A workflow centred on frequent integration into a shared trunk.
Upstream branch: The remote-tracking relationship used as the default source or destination for synchronization.
Webhook: An HTTP notification sent when a repository event occurs.
Workflow: A defined sequence of development, review, integration, or delivery activities.
Review Questions
Short Answer Questions
What is the difference between a Git branch and a tag?
Why is the staging area useful when creating commits?
State the usual purpose of a pull request.
What do the major, minor, and patch components communicate in semantic versioning?
Why should production credentials not normally be available to untrusted pull-request code?
Long Answer Questions
Compare feature-branch, Gitflow, and trunk-based development. Identify an appropriate context for each.
Explain how code review and automated CI checks provide complementary controls.
Describe a secure policy for merging changes into a production branch.
Explain how commit conventions and semantic versioning can support release automation while remaining limited indicators of quality.
Design a Jenkins or GitHub Actions workflow that validates pull requests and releases immutable artifacts from version tags.
Analytical / Application Questions
A team has branches that remain open for a month and frequent merge conflicts. Diagnose the workflow problem and recommend practical changes.
A developer pushes a secret to a public repository and then deletes it in a later commit. What risks remain, and what incident-response actions are required?
A payment-service pull request passes unit tests but fails integration tests after merging with the current trunk. Explain how branch protection, merge-queue validation, and artifact promotion could be redesigned to reduce this failure mode.
Multiple Choice Questions with Answers
Question 1
Which Git area contains changes selected for the next commit?
A. Remote repository
B. Staging area
C. Tag namespace
D. Deployment environment
Correct Answer: B. Staging area
Question 2
What is the principal emphasis of trunk-based development?
A. Maintaining many long-lived release branches
B. Delaying integration until a scheduled release
C. Frequently integrating small changes into a shared trunk
D. Allowing production changes without review
Correct Answer: C. Frequently integrating small changes into a shared trunk
Question 3
Which event is most commonly used to validate a proposed change before it enters the target branch?
A. Pull-request update
B. Deletion of a local file
C. Expiration of a user session
D. Changing a desktop setting
Correct Answer: A. Pull-request update
Question 4
Under conventional Semantic Versioning, which component is incremented for a backward-compatible bug fix?
A. Major
B. Minor
C. Patch
D. Release-candidate suffix only
Correct Answer: C. Patch
Question 5
Why is rebasing a shared public branch potentially unsafe?
A. It removes all automated tests
B. It rewrites commit identities and history
C. It permanently disables tags
D. It converts source code into binary files
Correct Answer: B. It rewrites commit identities and history
Question 6
Which control most directly prevents an unapproved direct push to the production branch?
A. A protected-branch rule
B. A longer commit message
C. A local text editor
D. A feature flag
Correct Answer: A. A protected-branch rule
Question 7
What is a principal benefit of building once and promoting the same immutable artifact?
A. It guarantees that no code needs review
B. It ensures the deployed artifact is the one that was tested
C. It eliminates the need for version tags
D. It permits secrets in source code
Correct Answer: B. It ensures the deployed artifact is the one that was tested
Question 8
Which commit message best follows the structured convention described in the chapter?
A. changed stuff
B. update file 7
C. feat(api): add invoice lookup
D. FIX EVERYTHING NOW!!!
Correct Answer: C. feat(api): add invoice lookup
Question 9
What should a team do after accidentally committing an access token?
A. Only delete the latest file copy
B. Rename the branch and continue
C. Revoke or rotate the credential and perform appropriate remediation
D. Add the token to the release notes
Correct Answer: C. Revoke or rotate the credential and perform appropriate remediation
Question 10
Why should workflow credentials be limited according to least privilege?
A. To make commit messages shorter
B. To reduce the impact if pipeline code or credentials are compromised
C. To prevent all pull requests from being created
D. To replace integration testing
Correct Answer: B. To reduce the impact if pipeline code or credentials are compromised
CI/CD Pipelines with Jenkins or GitHub Actions
Chapter 2
of 14