Version Control with Git and GitHub/GitLab

Chapter 5
Remote Repositories and Distributed Collaboration

Diploma in DevOps
Version Control with Git and GitHub/GitLab   •   Diploma

Remote Repositories and Distributed Collaboration

Introduction

Git is a distributed version-control system. Unlike a purely centralised system, Git gives each contributor a complete local repository containing project history, branches, and commits. Collaboration occurs when local repositories exchange commits with repositories hosted on a shared service such as GitHub or GitLab, or with another repository reachable over a network.

This distributed model provides speed, offline capability, and flexibility. It also introduces responsibilities. A developer must understand which repository contains which commits, whether a local branch is up to date, how a remote-tracking branch differs from a working branch, and how to respond when two contributors modify related history. Effective collaboration is therefore not simply a matter of issuing git push and git pull. It requires an accurate mental model of repository relationships and disciplined procedures for synchronisation.

This chapter explains how to configure remotes, transfer changes, use tracking branches, collaborate through forks, synchronise repositories, and troubleshoot common problems. The examples use command-line Git, but the same concepts apply when using GitHub or GitLab web interfaces and graphical clients.

Learning Objectives

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

  • explain the roles of local repositories, remote repositories, and remote-tracking branches;
  • add, inspect, rename, and remove Git remotes;
  • distinguish fetch, pull, and push;
  • configure and use upstream tracking branches;
  • publish local branches safely;
  • synchronise a fork with its upstream repository;
  • diagnose non-fast-forward errors, rejected pushes, authentication failures, and merge conflicts;
  • apply professional practices for access control, review, security, and repository governance.

1. The Distributed Repository Model

A Git repository consists of objects and references. Commits are immutable snapshots linked to parent commits. A branch name is a movable reference to a commit. When a repository is copied, its Git objects and references are copied as well; consequently, a clone is not merely a folder containing the latest files. It is an independent repository with its own local branches and configuration.

A remote is a named reference to another repository. The name origin is conventionally assigned to the repository from which a clone was created, but origin has no special technical status. A project can have remotes named origin, upstream, company, or any other valid name.

A typical arrangement is:

  • Local repository: the developer’s repository on a workstation.
  • Remote repository: a repository hosted on GitHub, GitLab, a company server, or another machine.
  • Local branch: a branch such as main or feature/login that the developer actively works on.
  • Remote-tracking branch: a local record such as origin/main showing the last known state of the remote’s main branch.
  • Upstream branch: the remote branch associated with a local branch for default pull and push operations.

The important distinction is that origin/main is not a live network connection. It is a local reference updated when Git communicates with origin, usually through fetch, pull, or some other network operation. If another developer pushes a commit, your local origin/main remains unchanged until you fetch.

A useful conceptual flow is:

Remote repository  ──fetch──>  remote-tracking branch  ──integrate──>  local branch
Local branch       ──push───>  remote repository

Fetching transfers information without changing the current branch or working files. Integration, performed through merge or rebase, incorporates fetched commits into a local branch. Pushing transfers local commits to a remote repository, subject to authentication and server-side policies.

2. Configuring and Inspecting Remotes

After cloning a repository, Git normally creates a remote named origin:

git clone https://github.com/example/inventory.git
cd inventory
git remote -v

A typical result is:

origin  https://github.com/example/inventory.git (fetch)
origin  https://github.com/example/inventory.git (push)

The fetch and push URLs may be identical, but they can also differ. For example, an organisation may permit anonymous or read-only fetching over HTTPS while requiring authenticated SSH for pushing.

To obtain more detailed information, use:

git remote show origin

This command can report the remote URL, tracked branches, local branches configured for pull, and branches that have been removed from the remote.

A remote can be added with:

git remote add upstream https://github.com/example/original-project.git

This is common when a developer has cloned a personal fork named origin and wants to refer to the original project as upstream.

Remote names and URLs can be changed:

git remote rename origin personal

git remote set-url personal [email protected]:student/inventory.git

A remote can be removed without deleting local commits or files:

git remote remove upstream

Removing a remote removes the local configuration and remote-tracking references associated with it. It does not delete the repository hosted at that URL.

HTTPS and SSH

HTTPS URLs commonly use a personal access token, credential manager, or organisation-specific authentication mechanism. Password-based Git authentication is generally disabled by major hosting services. SSH uses a key pair: a private key kept securely by the user and a public key registered with the hosting service.

Neither method should place long-lived secrets in source files or shell history. Personal access tokens should have the minimum necessary permissions, an appropriate expiry date, and secure storage. SSH private keys should be protected with a passphrase and suitable filesystem permissions.

3. Fetching Remote Changes

The command git fetch contacts a remote and downloads new commits, tags, and updated references. It does not merge changes into the current branch.

git fetch origin

To fetch all configured remotes:

git fetch --all

After fetching, a developer can inspect the difference between the current branch and the remote-tracking branch:

git log --oneline --graph --decorate HEAD..origin/main

git diff main origin/main

The notation HEAD..origin/main means commits reachable from origin/main that are not reachable from HEAD. The reverse notation, origin/main..HEAD, identifies local commits not present in the remote-tracking branch.

Fetching is valuable before integration because it separates two decisions:

  1. obtain the remote state; and
  2. decide whether to merge, rebase, inspect, or discard it.

A safer routine before starting work is:

git fetch origin
git status
git log --oneline --graph --decorate --all -n 20

The --all option displays references from all remotes, helping the user understand the repository graph rather than relying only on the current branch.

4. Pulling: Fetch Plus Integration

git pull is a convenience command that normally performs a fetch followed by an integration operation. In simplified form:

git pull ≈ git fetch followed by git merge

However, the exact behaviour depends on configuration and command options. A pull may use merge, rebase, or fast-forward-only integration. Therefore, professionals should know which policy their project expects.

A merge-based pull can be requested explicitly:

git pull --no-rebase origin main

A rebase-based pull can be requested with:

git pull --rebase origin main

A fast-forward-only pull refuses to create a merge commit:

git pull --ff-only origin main

Merge and rebase in collaboration

Suppose local branch main and remote branch origin/main have diverged. Each contains commits absent from the other. A merge creates a new merge commit that joins the two histories. A rebase rewrites the local commits so that they appear after the updated remote tip.

Merge preserves the historical branching structure and is often appropriate for integrating shared branches. Rebase can produce a linear history and is commonly used for unpublished personal work. Rewriting commits that other people have already based work on can cause confusion; therefore, rebase should not be used casually on shared public history.

If a conflict occurs during a merge:

git status
# edit conflicted files
git add path/to/file
git commit

To abandon the merge:

git merge --abort

During a rebase, the corresponding commands are:

git status
# edit conflicted files
git add path/to/file
git rebase --continue

To abandon the rebase:

git rebase --abort

A conflict is not a Git failure. It is a request for a human decision where Git cannot determine which content should remain. The correct resolution must be based on program behaviour, requirements, tests, and communication with affected contributors.

5. Pushing Changes

git push transfers commits and updated references from a local repository to a remote repository:

git push origin main

The remote server accepts the update only if it satisfies its rules. A normal branch update is a fast-forward when the remote branch tip is an ancestor of the proposed new tip. Fast-forward updates do not discard existing remote commits.

When publishing a new local branch, use:

git push -u origin feature/login

The -u or --set-upstream option records the relationship between the local branch and origin/feature/login. Later, git push and git pull can often be used without specifying the remote and branch.

A branch may also be created and published in one workflow:

git switch -c feature/login
git add .
git commit -m "Add login validation"
git push -u origin feature/login

A branch can be deleted from a remote with:

git push origin --delete feature/login

Remote branch deletion is subject to project policy. A branch that has already been merged may be deleted as housekeeping, but a branch containing necessary evidence or an active review should not be removed without agreement.

Force pushing

A force push replaces a remote reference even when the update is not a fast-forward:

git push --force-with-lease origin feature/login

--force-with-lease is safer than --force because it checks that the remote reference still has the value the user expects. It can still overwrite another person’s work if the local expectation is stale or incorrectly configured. Force pushing should generally be limited to private or explicitly coordinated branches. Protected branches such as main should normally reject it.

6. Tracking Branches and Upstream Relationships

A tracking relationship connects a local branch with an upstream remote-tracking branch. It contains two main pieces of information: the remote name and the remote branch name.

Inspect tracking information with:

git branch -vv

A result might show:

* feature/login  a1b2c3d [origin/feature/login: ahead 2] Add validation
  main           d4e5f6a [origin/main] Update documentation

Here, ahead 2 indicates that the local branch contains two commits not contained in the local record of origin/feature/login. The branch may also be behind, or both ahead and behind if histories have diverged.

The upstream can be configured explicitly:

git branch --set-upstream-to=origin/main main

A branch without an upstream can still be pushed by specifying the remote and branch explicitly. Tracking is a convenience and a source of useful status information; it does not itself transfer any data.

A common professional practice is to avoid assuming that an upstream branch is current. Run git fetch before interpreting ahead/behind information or making integration decisions.

7. Forks and Cross-Repository Collaboration

A fork is a server-side copy of a repository under another user’s or organisation’s account. Forks are especially useful when a contributor does not have direct write access to the original repository.

A typical fork workflow is:

  1. Fork the project on GitHub or GitLab.
  2. Clone the fork locally.
  3. Keep the fork as origin.
  4. Add the original project as upstream.
  5. Create a feature branch.
  6. Push the feature branch to the fork.
  7. Open a pull request or merge request against the upstream project.
  8. Respond to review feedback and update the branch.
  9. Synchronise the fork regularly.

Example configuration:

git clone [email protected]:student/inventory.git
cd inventory
git remote add upstream [email protected]:company/inventory.git
git remote -v

To synchronise the local default branch with the upstream project:

git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin main

The --ff-only option prevents an accidental merge commit on the local default branch. If the local branch has diverged, the command stops and requires an explicit decision.

A contributor can then create a fresh feature branch:

git switch -c feature/report-export main
git push -u origin feature/report-export

The pull request or merge request is created on the hosting platform, where maintainers can review commits, run automated checks, discuss design, and approve or reject the proposed change.

Forks support independence, but they also create synchronisation overhead. Stale forks can cause unnecessary conflicts and may expose obsolete dependencies or security fixes. Contributors should regularly fetch from upstream and delete obsolete local and remote branches.

8. A Disciplined Synchronisation Workflow

The following workflow is suitable for a small team using protected integration branches:

git switch main
git fetch origin
git pull --ff-only origin main
git switch -c feature/short-description
# edit files and run tests
git add path/to/changed-files
git commit -m "Implement short description"
git push -u origin feature/short-description

The feature branch is reviewed through a pull request or merge request. Before final integration, the contributor may update the branch:

git fetch origin
git rebase origin/main
# resolve conflicts and run tests
git push --force-with-lease origin feature/short-description

This rebase workflow is appropriate only when the feature branch is controlled by the contributor or the team has agreed that its history may be rewritten. An alternative is to merge origin/main into the feature branch and push normally.

Synchronisation should include more than Git commands. Developers should run unit tests, integration tests, linters, security scans, and relevant build procedures after resolving conflicts. A clean merge does not guarantee a correct application: Git verifies text and object relationships, not business logic or runtime compatibility.

9. Common Distributed Collaboration Problems

9.1 “Updates were rejected because the remote contains work”

This non-fast-forward rejection means the remote branch has commits absent from the local branch. First preserve local work and inspect the situation:

git status
git fetch origin
git log --oneline --graph --decorate --all

Then integrate the remote branch using the team’s chosen policy:

git rebase origin/main

or:

git merge origin/main

After tests pass, push again. Do not immediately force push, because the remote commits may belong to another contributor.

9.2 Authentication or permission failure

Check the remote URL:

git remote -v

For HTTPS, verify the credential manager and token scope. For SSH, test authentication using the hosting provider’s documented SSH test procedure and inspect whether the intended key is loaded. Also verify that the user has permission to the repository and that the target branch is not protected.

9.3 Unrelated histories

Git may refuse to merge two repositories whose histories have no common ancestor. This often results from initialising a new local repository independently and then connecting it to an existing remote. The safest response is to determine which history is authoritative and back up both repositories before integrating. The option --allow-unrelated-histories should be used only when the combination is intentional, such as a carefully planned repository consolidation.

9.4 Detached HEAD

A detached HEAD occurs when HEAD points directly to a commit rather than a local branch, often after checking out a tag or commit. Work committed in this state can be retained by creating a branch:

git switch -c rescue-work

This gives the commits a named reference so they are not lost through normal reference cleanup.

9.5 “Everything up-to-date” but expected changes are absent

Possible causes include committing on a different branch, pushing to a different remote, forgetting to save or stage files, or inspecting the wrong branch on the hosting website. Use:

git status
git branch --show-current
git log --oneline -5
git remote -v
git show --stat HEAD

These commands establish what was committed, where it was committed, and where Git attempted to push it.

9.6 Conflict markers remain in files

Conflict markers such as <<<<<<<, =======, and >>>>>>> must be removed as part of a valid resolution. Search the working tree and run automated tests. Staging a file does not prove that its content is correct; it only tells Git that the conflict has been addressed from the user’s perspective.

10. Case Study: Synchronising a Fork in a Web Platform Team

Background

The fictional BrightCart team maintains an open-source inventory service in a protected GitLab repository. External contributors use forks and merge requests. The default branch is main, and direct pushes to main are prohibited.

Problem

A contributor, Amina, created a fork three weeks earlier and implemented CSV export on a branch named feature/csv-export. During that time, the upstream project added a database migration and corrected a security-related dependency. Amina’s fork is therefore behind upstream. She must update her work without overwriting another contributor’s changes.

Analysis

Amina first inspects repository relationships:

git remote -v

She confirms that origin is her fork and adds the central project as upstream if necessary:

git remote add upstream [email protected]:brightcart/inventory.git

She fetches both sources and examines the graph:

git fetch --all
git log --oneline --graph --decorate --all -n 30

The graph shows that upstream/main contains the security fix and migration, while her feature branch contains three unpublished commits.

Approach and solution

Amina updates her local copy of the upstream default branch:

git switch main
git merge --ff-only upstream/main

She then rebases her feature branch onto the updated base:

git switch feature/csv-export
git rebase main

A conflict occurs in a service file because the migration changed the same method used by CSV export. Amina reads both versions, consults the migration’s tests, edits the file to preserve the security fix and the export behaviour, and continues:

git add src/export_service.py
git rebase --continue

She runs the test suite, migration tests, and static security checks. Because rebase changed the feature branch’s commit identifiers, she updates her fork safely:

git push --force-with-lease origin feature/csv-export

She updates the existing merge request rather than opening a duplicate one.

Outcome

The merge request now contains the latest upstream security changes, passes automated checks, and presents a reviewable feature history. The upstream project can merge it without asking Amina to repeat the synchronisation process.

Learning points

  • A fork and its upstream repository are separate repositories.
  • fetch makes upstream information available without changing the working branch.
  • Rebase is useful for unpublished feature work but rewrites commit identifiers.
  • Conflict resolution must preserve behaviour, not merely produce syntactically valid files.
  • --force-with-lease is preferable to unrestricted force pushing after a coordinated rebase.
  • Security updates should be treated as part of synchronisation, not postponed until after feature completion.

11. Governance, Security, and Professional Practice

Remote collaboration is also a governance activity. Repository administrators should define branch protection, required reviews, status checks, signed commits where appropriate, and rules for force pushes. Permissions should follow least privilege: contributors should receive only the access required for their responsibilities.

Sensitive information must not be committed. This includes passwords, API keys, private certificates, cloud credentials, and personal data. A remote repository is not made safe merely by deleting a secret in a later commit; the value may remain in earlier history, forks, caches, or downloaded clones. Exposed credentials should be revoked and rotated immediately, followed by an appropriate history-remediation process.

Pull requests and merge requests create an auditable decision record. Descriptions should state the problem, solution, testing performed, deployment implications, and known limitations. Reviewers should assess correctness, maintainability, security, compatibility, and operational impact rather than focusing only on line-by-line style.

Git hosting platforms increasingly use automation and AI-assisted review to identify defects, suggest summaries, or generate code. Such tools can improve throughput but cannot replace human responsibility. Automatically generated suggestions may be incomplete, insecure, or inconsistent with project requirements. Proprietary source code and personal data should not be submitted to an AI service unless organisational policy permits it and appropriate contractual and technical safeguards exist.

12. Contemporary Developments and Limitations

Modern Git hosting services provide merge queues, required status checks, dependency update automation, protected environments, and signed provenance records. These capabilities help teams establish a controlled path from local change to production deployment. They do not eliminate the need for sound branching strategy or clear ownership.

Git transfers complete objects as needed and can support large projects, but ordinary Git is not ideal for very large binary files. Git Large File Storage (Git LFS), artifact repositories, or object storage may be more appropriate for videos, compiled packages, datasets, and other large generated files. Teams should distinguish source code from build artifacts and avoid committing generated output unnecessarily.

Distributed collaboration also has limits. Multiple clones can make accidental divergence easy, history rewriting can damage trust, and repository access may depend on external hosting availability. Documentation, automated checks, backup policies, and communication protocols are therefore complements to Git rather than optional extras.

Best-Practice Checklist

  • Give remotes meaningful names, especially origin and upstream in fork workflows.
  • Fetch before assessing whether a branch is current.
  • Prefer explicit integration policies: merge, rebase, or fast-forward-only.
  • Use tracking branches to reduce command repetition, but do not confuse tracking with synchronisation.
  • Create feature branches instead of working directly on protected integration branches.
  • Push early enough to create a recoverable remote copy, but never commit secrets.
  • Use pull requests or merge requests for review and automated validation.
  • Resolve conflicts based on requirements and tests, not blindly by choosing one side.
  • Use --force-with-lease only for coordinated history rewriting.
  • Rotate exposed credentials immediately and record the incident appropriately.
  • Delete obsolete branches only after confirming that they are no longer needed.

Chapter Summary

Git collaboration is based on independent repositories that exchange commits and references. A remote is a named connection to another repository, while a remote-tracking branch is a local record of the last fetched state of a remote branch. git fetch downloads updates without integrating them; git pull fetches and then integrates; and git push publishes local commits subject to authentication and server policies.

Tracking branches associate local branches with upstream remote-tracking branches and simplify routine operations. Forks enable contribution without direct write access, with origin commonly representing the contributor’s fork and upstream representing the central project. Synchronisation requires deliberate fetching, integration, testing, and communication.

Non-fast-forward rejections, authentication errors, detached HEAD states, unrelated histories, and merge conflicts are normal distributed-workflow conditions. They should be diagnosed through repository inspection rather than solved with indiscriminate force pushing. Effective professional practice combines Git commands with branch protection, least-privilege access, secret management, code review, automated testing, and responsible use of AI-assisted tools.

Key Terms

Remote: A named reference to another Git repository.

Origin: The conventional name for the repository from which a clone was created.

Upstream: The branch or repository regarded as the source for synchronisation; in fork workflows it commonly names the central project.

Remote-tracking branch: A local reference, such as origin/main, recording the last fetched state of a remote branch.

Tracking branch: A local branch configured to follow an upstream remote-tracking branch.

Fetch: The operation that downloads commits and updated references without integrating them into the current branch.

Pull: A convenience operation that normally fetches and then merges or rebases.

Push: The operation that transfers local commits and references to a remote repository.

Fast-forward: A reference update in which the old tip is an ancestor of the new tip.

Non-fast-forward rejection: A server refusal because accepting the proposed update would discard or bypass remote history.

Fork: A server-side copy of a repository under another account or organisation.

Merge: An integration operation that combines histories, possibly creating a merge commit.

Rebase: An operation that replays commits onto a new base and rewrites their identifiers.

Merge conflict: A situation in which Git cannot determine how competing changes should be combined.

Protected branch: A branch subject to rules such as required reviews, checks, or restricted pushes.

Pull request: A proposal to merge changes into a target repository or branch, commonly used by GitHub.

Merge request: GitLab’s term for a review and integration proposal.

Force-with-lease: A guarded force push that checks an expected remote reference before replacement.

Least privilege: A security principle granting only the access required for a task.

Repository governance: Policies and controls governing access, review, history, security, and maintenance.

Review Questions

Short Answer Questions

  1. What is the difference between a local branch and a remote-tracking branch?
  2. What does git fetch do, and what does it deliberately not do?
  3. Why is git push -u origin feature/name useful when publishing a new branch?
  4. In a fork workflow, what are the usual roles of origin and upstream?
  5. Why is --force-with-lease safer than --force?

Long Answer Questions

  1. Explain the distributed repository model and describe the movement of commits during fetch, integration, and push.
  2. Compare merge-based and rebase-based synchronisation, including their effects on history and collaboration.
  3. Describe a complete fork-and-merge-request workflow from cloning through final review.
  4. Explain how branch protection, code review, automated checks, and least-privilege access support repository governance.
  5. Develop a troubleshooting procedure for a non-fast-forward push rejection.

Analytical / Application Questions

  1. A developer’s local main branch and origin/main have both acquired unique commits. Design a safe sequence of commands and decisions for integrating the histories without losing work.
  2. A contributor rebases a feature branch after synchronising it with upstream/main, but the push is rejected. Analyse why this occurred and explain when, and under what safeguards, a force-with-lease push may be appropriate.
  3. A team discovers that an API token was committed and pushed to a public fork. Propose an incident-response sequence covering credential revocation, repository history, affected forks, communication, and preventive controls.

Multiple Choice Questions with Answers

Question 1

Which command downloads updates from a remote without integrating them into the current branch?

A. git fetch

B. git merge

C. git push

D. git commit

Correct Answer: A. git fetch

Question 2

In a typical fork workflow, what does the remote named upstream usually represent?

A. The contributor’s personal fork

B. The original central repository

C. The local working directory

D. A temporary stash

Correct Answer: B. The original central repository

Question 3

What is the primary purpose of git push -u origin feature/login?

A. It deletes the feature branch

B. It fetches the central repository

C. It publishes the branch and sets its upstream relationship

D. It rebases the branch automatically

Correct Answer: C. It publishes the branch and sets its upstream relationship

Question 4

Why might a remote reject an ordinary push as non-fast-forward?

A. The local branch has no files

B. The remote branch contains history absent from the local branch

C. The commit message is too short

D. The repository has a remote named origin

Correct Answer: B. The remote branch contains history absent from the local branch

Question 5

Which command is designed to stop a merge in progress and restore the pre-merge state?

A. git merge --abort

B. git merge --continue

C. git push --abort

D. git branch --delete

Correct Answer: A. git merge --abort

Question 6

What does origin/main represent in a local repository?

A. A live connection to the remote server

B. The local working directory for main

C. A local record of the last fetched state of the remote main branch

D. A permanently protected branch

Correct Answer: C. A local record of the last fetched state of the remote main branch

Question 7

Which option is generally safer than unrestricted force pushing after rebasing a private feature branch?

A. --force-with-lease

B. --allow-unrelated-histories

C. --no-verify

D. --delete

Correct Answer: A. --force-with-lease

Question 8

A developer is in detached HEAD state and wants to preserve new commits under a branch name. Which command is appropriate?

A. git switch -c rescue-work

B. git remote remove origin

C. git fetch --delete

D. git clean -fd

Correct Answer: A. git switch -c rescue-work

Question 9

What is the main function of a pull request or merge request?

A. To store passwords securely

B. To propose, review, validate, and integrate changes

C. To replace every local branch

D. To convert a repository into a binary file

Correct Answer: B. To propose, review, validate, and integrate changes

Question 10

Why should an exposed API token be revoked even if the token-containing commit is later deleted from the visible branch?

A. Deleted commits can never be recovered

B. The token may remain in history, clones, forks, or caches

C. Git automatically changes all tokens

D. Revocation is required only for merge conflicts

Correct Answer: B. The token may remain in history, clones, forks, or caches

Version Control with Git and GitHub/GitLab
Chapter 5 of 14