Skip to main content

Command Palette

Search for a command to run...

Day 21: Setting Up Git Repository on Storage Server - Version Control Foundation 🗂️

Published
15 min read
Day 21: Setting Up Git Repository on Storage Server - Version Control Foundation 🗂️

Welcome back! 👋 Day 21 of the 100 Days Cloud DevOps Challenge, and today we're establishing the foundation of version control - creating a Git bare repository on a central server! This is the first step in setting up collaborative development infrastructure. Let's build it! 🎯

🎯 The Mission - Central Git Repository Setup

It's Friday morning, and the development team needs a Git repository for their new project:

📋 TASK TICKET #DEV-6012 - Git Repository Setup
Priority: HIGH
Type: Version Control Infrastructure

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PROJECT: Ecommerce Application Development
Target: Storage Server (ststor01)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

REQUIREMENTS:

1. Package Installation:
   └─ Install Git using yum package manager
   └─ Target: Storage Server

2. Repository Creation:
   └─ Create bare Git repository
   └─ Location: /opt/ecommerce.git
   └─ Type: Bare repository (no working directory)
   └─ CRITICAL: Use exact name "ecommerce.git"

3. Purpose:
   └─ Central repository for team collaboration
   └─ Developers will clone from this location
   └─ All code changes pushed here

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
STATUS: READY TO START
DEADLINE: Today
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This is the foundation of collaborative development! Every GitHub, GitLab, and Bitbucket server uses bare repositories to store code centrally! 🏗️

🤔 Why This Matters - Understanding Git Architecture

The Problem: Multiple Developers, One Codebase

Without Version Control:

Developer 1: working on feature A (local machine)
Developer 2: working on feature B (local machine)
Developer 3: fixing bug C (local machine)

Problems:
❌ How to combine all changes?
❌ What if changes conflict?
❌ How to track who changed what?
❌ How to revert bad changes?
❌ How to work on different features simultaneously?

With Git Repository:

        Central Repository (ststor01)
        /opt/ecommerce.git (bare)
               ↓
    ┌──────────┼──────────┐
    ↓          ↓          ↓
 Dev 1      Dev 2      Dev 3
 clone      clone      clone
   ↓          ↓          ↓
 Feature A  Feature B  Bug Fix C
   ↓          ↓          ↓
 push       push       push
   ↓          ↓          ↓
    └──────────┼──────────┘
               ↓
        Central Repository
        (all changes merged)

Benefits:
✅ All code in one place
✅ Complete change history
✅ Conflict resolution
✅ Parallel development
✅ Easy rollback
✅ Code review workflow

Real stat: 96% of professional developers use Git for version control! It's the industry standard! 📊

Understanding Bare vs Normal Repositories

Normal Repository (For Development):

my-project/
├─ .git/                ← Git database (hidden)
│  ├─ objects/
│  ├─ refs/
│  ├─ config
│  └─ HEAD
├─ index.html           ← Working files
├─ style.css
└─ app.js

Purpose: Local development
Can: Commit changes directly
Has: Working directory + Git database

Bare Repository (For Central Server - What We're Creating):

ecommerce.git/
├─ objects/             ← Git database (exposed)
├─ refs/
├─ config
├─ HEAD
└─ hooks/

Purpose: Central sharing point
Can: Receive pushes, serve clones
Has: Only Git database, NO working directory

Why bare repositories for servers?

  • No working directory - Can't accidentally modify files

  • Accepts pushes - Normal repos reject pushes by default

  • Pure storage - Only stores version history

  • Convention - Everyone knows it's for sharing (.git extension)

  • Efficient - No disk space wasted on working files

Think of it like this:

  • Normal repo = Your personal workspace (office desk)

  • Bare repo = Shared filing cabinet (storage server)

Real-World Git Architecture

How companies use Git:

┌─────────────────────────────────────────────┐
│     GitHub/GitLab/Bitbucket Server          │
│     (Thousands of bare repositories)        │
│     ├─ company/frontend.git (bare)          │
│     ├─ company/backend.git (bare)           │
│     └─ company/mobile.git (bare)            │
└─────────────────────────────────────────────┘
        ↓ clone/push         ↓ clone/push
┌─────────────────┐   ┌─────────────────┐
│   Developer 1   │   │   Developer 2   │
│   (Normal repo) │   │   (Normal repo) │
│   Work locally  │   │   Work locally  │
└─────────────────┘   └─────────────────┘

Our setup is the same principle:

Storage Server (ststor01)
└─ /opt/ecommerce.git (bare) ← What we're creating!
           ↓
    Developer machines
    (clone from here)

🏗️ Understanding the Infrastructure

What We're Building:

┌─────────────────────────────────────────────────┐
│      Storage Server (ststor01)                  │
│      natasha@172.16.238.15                      │
├─────────────────────────────────────────────────┤
│                                                 │
│  Before:                                        │
│  └─ /opt/ (empty)                               │
│                                                 │
│  After our setup:                               │
│  └─ /opt/ecommerce.git/ (bare repository)      │
│     ├─ HEAD                                     │
│     ├─ config (bare = true)                     │
│     ├─ description                              │
│     ├─ hooks/                                   │
│     ├─ info/                                    │
│     ├─ objects/                                 │
│     └─ refs/                                    │
│        ├─ heads/                                │
│        └─ tags/                                 │
│                                                 │
│  Developers will access via:                    │
│  git clone natasha@ststor01:/opt/ecommerce.git  │
│                                                 │
└─────────────────────────────────────────────────┘

How developers will use it:

Step 1: Clone repository
Developer machine:
$ git clone natasha@ststor01:/opt/ecommerce.git
$ cd ecommerce

Step 2: Make changes
$ echo "# Ecommerce App" > README.md
$ git add README.md
$ git commit -m "Initial commit"

Step 3: Push to server
$ git push origin main

Step 4: Other developers pull
Developer 2:
$ git clone natasha@ststor01:/opt/ecommerce.git
$ # Gets all changes!

🛠️ Phase 1: Access Storage Server

Step 1.1: SSH to Storage Server

# From Jump Host, connect to Storage Server
ssh natasha@ststor01
# Password: Bl@kW (or check your lab credentials)

You're now logged in as natasha on the Storage Server!

Step 1.2: Switch to Root User

# Become root to install packages
sudo su -

You're now root! Ready to install Git! ✅

🛠️ Phase 2: Install Git

Step 2.1: Install Git Package

# Install Git using yum
yum install -y git

Output:

Loaded plugins: fastestmirror, ovl
Loading mirror speeds from cached hostfile
Resolving Dependencies
--> Running transaction check
---> Package git.x86_64 0:2.x.x will be installed
...
Installed:
  git.x86_64 0:2.x.x

Complete!

Git installed successfully! 🎉

Step 2.2: Verify Git Installation

# Check Git is installed
git --version

Expected output:

git version 2.39.3

Or similar version number - confirms Git is working!

Check Git location:

which git

Output: /usr/bin/git

Git is ready to use!

🛠️ Phase 3: Create Bare Repository

Step 3.1: Create Bare Repository

# Create bare Git repository at /opt/ecommerce.git
git init --bare /opt/ecommerce.git

Expected output:

Initialized empty Git repository in /opt/ecommerce.git/

Repository created! 🎊

What just happened:

  • git init = Initialize a Git repository

  • --bare = Create bare repository (no working directory)

  • /opt/ecommerce.git = Location and name (EXACT as specified!)

Step 3.2: Verify Repository Was Created

# List directory contents
ls -la /opt/ecommerce.git/

Expected output:

total 16
drwxr-xr-x. 7 root root  119 Nov 26 10:00 .
drwxr-xr-x. 3 root root   28 Nov 26 10:00 ..
drwxr-xr-x. 2 root root    6 Nov 26 10:00 branches
-rw-r--r--. 1 root root   66 Nov 26 10:00 config
-rw-r--r--. 1 root root   73 Nov 26 10:00 description
-rw-r--r--. 1 root root   23 Nov 26 10:00 HEAD
drwxr-xr-x. 2 root root 4096 Nov 26 10:00 hooks
drwxr-xr-x. 2 root root   21 Nov 26 10:00 info
drwxr-xr-x. 4 root root   30 Nov 26 10:00 objects
drwxr-xr-x. 4 root root   31 Nov 26 10:00 refs

Perfect! These are the internals of a Git repository! ✅

Key directories:

  • objects/ - Stores all Git data (commits, files, etc.)

  • refs/ - Stores branch and tag references

  • hooks/ - Scripts that run on Git events

  • config - Repository configuration

  • HEAD - Points to current branch

🛠️ Phase 4: Verify It's a Bare Repository

Step 4.1: Check Repository Configuration

# View repository config
cat /opt/ecommerce.git/config

Expected output:

[core]
        repositoryformatversion = 0
        filemode = true
        bare = true

Key line: bare = true ← This confirms it's a bare repository! ✅

Step 4.2: Verify Using Git Command

# Check if repository is bare
git -C /opt/ecommerce.git config --get core.bare

Output: true

Confirmed: It's a bare repository!

Step 4.3: Check HEAD Reference

# See what HEAD points to
cat /opt/ecommerce.git/HEAD

Expected output:

ref: refs/heads/master

Or for newer Git versions:

ref: refs/heads/main

This is the default branch reference!

Step 4.4: Check Repository Description

# View description file
cat /opt/ecommerce.git/description

Output:

Unnamed repository; edit this file 'description' to name the repository.

You can customize this:

echo "Ecommerce Application Central Repository" > /opt/ecommerce.git/description

🎉 Phase 5: Final Verification

Step 5.1: Complete Directory Check

# Check the repository location and permissions
ls -ld /opt/ecommerce.git

Expected output:

drwxr-xr-x. 7 root root 119 Nov 26 10:00 /opt/ecommerce.git

Permissions: drwxr-xr-x means:

  • d = directory

  • rwx = owner (root) can read/write/execute

  • r-x = group can read/execute

  • r-x = others can read/execute

Step 5.2: Verify Complete Structure

# Show tree structure (if tree is installed)
tree /opt/ecommerce.git

# Or use ls with recursive flag
ls -R /opt/ecommerce.git

Expected structure:

/opt/ecommerce.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   ├── push-to-checkout.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

All components present!

Step 5.3: Test Repository Accessibility

# Try cloning locally to verify it works
cd /tmp
git clone /opt/ecommerce.git test-clone

Expected output:

Cloning into 'test-clone'...
warning: You appear to have cloned an empty repository.
done.

Warning is normal! Repository is empty (no commits yet). ✅

Clean up test:

rm -rf /tmp/test-clone

REPOSITORY SETUP COMPLETE! 🎉🎊🎈

🔍 Understanding What We Built

Repository Anatomy

Let's understand each component:

/opt/ecommerce.git/
│
├─ HEAD
│  └─ Points to current branch (refs/heads/master or main)
│
├─ config
│  └─ Repository settings (bare = true)
│
├─ description
│  └─ Repository description (for GitWeb UI)
│
├─ hooks/
│  └─ Scripts triggered by Git events:
│     ├─ pre-commit (runs before commit)
│     ├─ post-receive (runs after push)
│     └─ ... (many others)
│
├─ info/
│  └─ exclude (like .gitignore but not versioned)
│
├─ objects/
│  └─ Git object database:
│     ├─ Commits (version snapshots)
│     ├─ Trees (directory structures)
│     ├─ Blobs (file contents)
│     └─ Tags (named references)
│
└─ refs/
   ├─ heads/ (branches)
   │  └─ master or main (when first commit made)
   └─ tags/ (version tags)
      └─ v1.0.0 (example)

How Git Stores Data

When developers make commits:

Developer commits:
$ git add file.txt
$ git commit -m "Add feature"

What happens in /opt/ecommerce.git/:
├─ objects/
│  ├─ a1b2c3... (commit object)
│  ├─ d4e5f6... (tree object - directory structure)
│  └─ g7h8i9... (blob object - file.txt content)
│
└─ refs/heads/main
   └─ Points to: a1b2c3... (latest commit)

Every file, every version, is stored as an object!

Why .git Extension?

Convention for bare repositories:

  • project.git = Bare repository (server)

  • project/ = Normal repository (development)

Real-world examples:

The .git immediately tells developers: "This is a bare repository for cloning!"

💡 Key Takeaways

Bare repositories are for central storage, not direct development

Git installation is simple with yum install git

git init --bare creates a bare repository

Exact naming matters - /opt/ecommerce.git not /opt/ecommerce

bare = true in config confirms repository type

No working directory - only Git database files

.git extension is convention for bare repositories

Server-side repos enable team collaboration

Objects directory stores all version history

Refs directory stores branch and tag pointers

Hooks directory allows automated workflows

🎓 Interview Questions

Q1: What's the difference between a normal Git repository and a bare repository? When would you use each?

Answer: A normal repository contains both a working directory (your actual files) and a .git directory (version history). You can edit files, commit changes, and view different versions. A bare repository only contains the .git directory contents - no working files. Key differences: Normal repos are for development (local machines), bare repos are for sharing (servers). Normal repos reject pushes by default to prevent conflicts with working directory. Bare repos accept pushes safely since there's no working directory to conflict with. Use normal repos for: Local development, feature branches, personal projects. Use bare repos for: Central servers (like GitHub), team collaboration hubs, backup repositories. Convention: Bare repos use .git extension (project.git), normal repos don't (project/). Real example: Your laptop has a normal repo where you code. GitHub/GitLab servers have bare repos where you push. This separation prevents accidental modifications on the server and enables proper collaboration workflows.

Q2: Walk me through what happens when a developer clones your newly created bare repository and pushes their first commit.

Answer: Step-by-step flow: 1) Developer clones: git clone natasha@ststor01:/opt/ecommerce.git - Git creates local directory "ecommerce/", copies entire repository structure, sets up "origin" remote pointing to server, checks out default branch (creates working directory). 2) Developer makes changes: Creates files, edits code, stages with git add, commits with git commit -m "Initial commit". 3) Local commit: Git creates objects in local .git/objects/ - commit object (metadata + message), tree object (directory structure), blob objects (file contents). Updates local refs/heads/main to point to new commit. 4) Developer pushes: git push origin main - Local Git connects via SSH to server, sends new objects to /opt/ecommerce.git/objects/, updates /opt/ecommerce.git/refs/heads/main on server to point to new commit. 5) Server accepts: Bare repository receives objects, stores in objects/ directory using content-addressable storage (SHA-1 hashes), updates branch reference. 6) Other developers: Can now git pull and receive the changes, see complete history, continue development. First push is special: Creates the main/master branch on server (before push, refs/heads/ is empty!). All subsequent pushes just add new objects and update references.

Q3: How would you set up access control so multiple developers can push to the repository?

Answer: Several approaches for team access: Method 1: Unix Groups (Simple) - Create git group: groupadd git, add users: usermod -aG git developer1, change repo ownership: chown -R root:git /opt/ecommerce.git, set permissions: chmod -R 775 /opt/ecommerce.git, set SGID bit: find /opt/ecommerce.git -type d -exec chmod g+s {} \; (new files inherit group). Method 2: Git Config (Shared Repository) - git -C /opt/ecommerce.git config core.sharedRepository group - automatically sets correct permissions on new objects. Method 3: SSH Keys (Production Standard) - Each developer generates SSH key: ssh-keygen, adds public key to server's ~/.ssh/authorized_keys, repository accessible via: git clone git@server:/opt/ecommerce.git. Method 4: Gitolite/GitLab (Enterprise) - Install Gitolite for fine-grained access control, per-repository, per-branch permissions, centralized management. Best practice for production: Use SSH keys + tool like GitLab/Gitea, provides web interface, merge requests, CI/CD integration, access audit logs. Security considerations: Never use shared passwords, use SSH keys with passphrases, limit who can force push, enable branch protection for main/production branches, set up hooks for code review requirements.

Q4: Explain what Git hooks are and provide examples of how they could be used in this repository.

Answer: Git hooks are scripts that run automatically when certain Git events occur. Located in /opt/ecommerce.git/hooks/, they enable automated workflows and enforcement of policies. Server-side hooks (for bare repos): pre-receive - Runs before accepting pushed commits. Use cases: Reject commits without proper message format, enforce code review approval, check for security issues, validate commit author. Example: Reject if commit message doesn't have ticket number. update - Runs once per pushed branch. Use cases: Branch protection (prevent force push to main), enforce naming conventions, restrict who can push to protected branches. post-receive - Runs after push is accepted. Use cases: Send notification emails, trigger CI/CD pipeline, update documentation, deploy to staging. Example script: #!/bin/bash + curl -X POST https://ci-server/build. Client-side hooks (for normal repos): pre-commit - Before commit. Check code style, run tests, prevent debug code. commit-msg - Validate commit message format. pre-push - Before push. Run full test suite. Real-world example: Company enforces: pre-receive validates commits are signed, update prevents force push to main, post-receive triggers Jenkins build, notifies Slack channel. Setting up hooks: Create script in hooks/, make executable: chmod +x, test thoroughly (hooks failing blocks operations!). Important: Hooks don't transfer with clone/push - must be set up on each system (server hooks stay on server, client hooks must be distributed).

Q5: What would you do if the repository needs to be migrated to a different server?

Answer: Migration strategy for bare repositories: Method 1: Clone Method (Simplest) - On new server: git clone --bare /path/to/old/server/ecommerce.git /opt/ecommerce.git. This preserves: all commits, all branches, all tags, complete history. Advantages: Clean, simple, built-in Git functionality. Method 2: rsync Method (Exact Copy) - rsync -av /old/path/ecommerce.git /new/path/ecommerce.git. Preserves: everything including hooks, config, permissions. Use when: Custom hooks, specific permissions, configuration must be preserved. Method 3: Bundle Method (Offline Transfer) - Create bundle: git bundle create repo.bundle --all, transfer file to new server, extract: git clone repo.bundle ecommerce.git, convert to bare: git clone --bare ecommerce ecommerce.git. Use when: No direct network access between servers, need to transfer via USB/email. Post-migration steps: 1) Update remote URLs for all developers: git remote set-url origin new-server:/opt/ecommerce.git. 2) Verify all branches: git branch -a. 3) Verify all tags: git tag -l. 4) Test push/pull operations. 5) Set up hooks on new server. 6) Configure permissions. 7) Update CI/CD systems. 8) Update documentation. Zero-downtime migration: Run both servers temporarily, use post-receive hook to mirror pushes, switch developers gradually, retire old server when all migrated. Backup best practice: Before migration, create bundle backup: git bundle create backup.bundle --all - this is a complete, portable backup!

Q6: How does Git store data efficiently when multiple versions of large files exist?

Answer: Git uses several clever techniques: 1) Content-Addressable Storage - Files stored by SHA-1 hash of content, identical files = same hash = stored once only. Example: 10 branches with same README.md = 1 object. 2) Object Types - Blob objects: File contents, Tree objects: Directory structure, Commit objects: Metadata + pointers, Tag objects: Named references. 3) Delta Compression - New version of file stores only differences (delta) from previous version, significantly reduces space. Example: 1MB file, change 10 lines = stores ~1KB delta, not entire 1MB. 4) Packfiles - Git periodically packs objects into packfiles, applies compression algorithms, removes redundancy. Command: git gc (garbage collection). 5) Shallow Clones - Developers can clone with limited history: git clone --depth 1 gets only latest commit, saves bandwidth and space. Real-world efficiency: Linux kernel repository: 1M+ commits, 70K+ files, Full history: ~3GB, Most recent: ~200MB after compression. Why this matters for bare repos: Server stores complete history for all developers, efficient storage = lower costs, faster clones, smaller backups. Storage growth: Repository size grows sub-linearly with commits due to compression. Best practices: Run git gc periodically on server, use .gitignore for large binaries, consider Git LFS for large files (videos, assets), monitor repository size in dashboards.

🌟 Final Thoughts

You've successfully set up a central Git repository! This is the foundation that enables teams to collaborate on code effectively.

What you accomplished:

  • ✅ Installed Git on server using yum

  • ✅ Created bare repository at exact location

  • ✅ Verified repository structure and configuration

  • ✅ Understood bare vs normal repositories

  • ✅ Prepared infrastructure for team development

Real-world impact: This exact setup is how companies manage code:

  • Startups: Self-hosted Git servers in early days

  • Enterprises: Internal GitLab/Bitbucket servers

  • Open source: Thousands of projects on GitHub

  • DevOps: Infrastructure as Code repositories

Next steps for the team: Developers can now:

  1. Clone the repository

  2. Create branches for features

  3. Commit changes locally

  4. Push to central repository

  5. Collaborate with merge requests

  6. Track complete project history

This is professional software development infrastructure! 💪

🚀 What's Next?

Day 21 complete! 🎉 You've established version control infrastructure!

Skills Mastered Today:

  • ✅ Git installation on servers

  • ✅ Bare repository creation

  • ✅ Understanding Git architecture

  • ✅ Repository verification techniques

  • ✅ Version control best practices

Tomorrow, Day 22 awaits with new challenges! Keep building! 💪


Day: 21/100
Challenge: KodeKloud Cloud DevOps
Date: November 26, 2025
Topic: Git Bare Repository Setup on Storage Server

Have you set up Git servers? What version control workflows does your team use? Share your experiences! 🗂️

More from this blog

🚀 DevOps Challenge- KodeKloud Solutions

73 posts