# Git on the Server ## Purpose & context - Current baseline - You should now be able to handle most day-to-day Git tasks - Why a remote repository is needed for collaboration - Collaboration requires a remote Git repository - While you *can* push/pull directly between individuals’ repositories: - It’s discouraged (easy to confuse what others are working on) - Collaborators need access even if your computer is offline - Preferred approach - Set up an *intermediate* repository that everyone can access - Everyone pushes to / pulls from that common repository - Running a Git server (high-level) - Choose which protocols your server should support - Chapter structure - Protocol options + pros/cons - Typical setups and how to get them running - Hosted options (if you don’t want to self-host) - If you don’t want to run your own server - Skip to “Third Party Hosted Options” - Then continue to the next chapter about distributed workflows - What a “remote repository” usually is - Generally a **bare repository** - No working directory - Used only as a collaboration point - Bare repository in simplest terms - Contents of your project’s `.git` directory - And nothing else ## The Protocols (ways Git transfers data) - Four distinct protocols - **Local** - **HTTP** (Smart + Dumb) - **SSH** - **Git** (git://) - Selection depends on - Authentication requirements - Public vs private access needs - Firewall constraints - Ease of setup/maintenance - Performance expectations ### Local Protocol - Definition - Remote repository is just another directory on the **same host** - Typical use cases - Team shares a filesystem (e.g., **NFS mount**) - Less likely: everyone logs into the **same computer** - Not ideal: all repos on one machine → catastrophic loss risk higher - How to use (clone / add remote) - Clone using a filesystem path - `git clone /srv/git/project.git` - Clone explicitly using `file://` - `git clone file:///srv/git/project.git` - Add as a remote to an existing project - `git remote add local_proj /srv/git/project.git` - Then push/pull using `local_proj` like a network remote - Path vs `file://` behavior - Plain path (no `file://`) - Git tries to use **hardlinks** or directly **copies** needed files - With `file://` - Git uses network-style transfer processes - Generally **less efficient** - Why ever use `file://`? - To get a “clean” copy (leave out extraneous refs/objects) - Often after importing from another VCS (maintenance tasks noted in “Git Internals”) - Recommended in this chapter - Use the normal path (almost always faster) - Pros - Simple to set up - Uses existing filesystem permissions and access - Easy when you already have a shared filesystem - Put a bare repo where everyone can access it - Set read/write permissions like any shared directory - Convenient for quick sharing from someone’s working repo - e.g., `git pull /home/john/project` can be simpler than using a server - Cons - Shared filesystem access can be harder from multiple locations than network access - Example: pushing from home may require mounting a remote disk (slow/difficult) - Performance caveat on shared mounts - “Local” is fast only if data access is fast - NFS can be slower than SSH (even to same server) because SSH lets Git use local disks - Risk of accidental repository damage - Users have full shell access to the “remote” directory - Nothing prevents deleting/modifying internal Git files → corruption risk ### HTTP Protocols - Two modes - **Dumb HTTP** - Older (pre Git 1.6.6) - Simple, generally read-only - **Smart HTTP** - Introduced in Git 1.6.6 - More capable: negotiates transfers intelligently (similar to SSH) - Has become very popular due to usability + efficiency #### Smart HTTP - How it works (conceptually) - Similar to SSH/Git protocols but runs over standard **HTTPS ports** - Can use multiple HTTP authentication mechanisms - Often easier than SSH key setup - Supports username/password prompting - Usability benefits - Single URL can support - Anonymous reads (like `git://`) - Authenticated/encrypted pushes (like SSH) - Client behavior - If push requires auth → server prompts for username/password - Same for reads if configured - Example behavior (hosted services) - For GitHub-like services - The web URL (e.g., `https://github.com/...`) can also be used to clone/push (if authorized) #### Dumb HTTP - When used - Git client falls back to Dumb HTTP if server doesn’t respond with a smart HTTP service - Expectations - Bare Git repository is served as static files by a web server - Setup (read access) - Place bare repo under HTTP document root + enable a `post-update` hook - Example sequence - `cd /var/www/htdocs/` - `git clone --bare /path/to/git_project gitproject.git` - `cd gitproject.git` - `mv hooks/post-update.sample hooks/post-update` - `chmod a+x hooks/post-update` - Why the hook matters - Default `post-update` runs `git update-server-info` - That makes HTTP fetching/cloning work properly - Typically triggered when someone pushes to the repo (often over SSH) - Cloning from Dumb HTTP - `git clone https://example.com/gitproject.git` - Server flexibility - Apache example uses `/var/www/htdocs` - Any static web server works if it can serve the bare repo files - “Git Internals” referenced for details of how the data is served - Common deployment choice - Usually you run either: - Smart HTTP read/write, **or** - Dumb HTTP read-only - Rare to run a mix #### HTTP Pros (focus on Smart HTTP) - Single URL for all access types - Server prompts only when authentication is needed - Username/password authentication - Avoids SSH key generation/upload steps - Especially useful for less sophisticated users or environments where SSH is uncommon - Performance - Fast and efficient (comparable to SSH) - Security options - Serve read-only over HTTPS with encrypted transfer - Can require signed SSL client certificates (stronger client auth) - Firewall friendliness - HTTP/HTTPS ports are commonly allowed through corporate firewalls #### HTTP Cons - Setup complexity - HTTPS Git can be trickier to set up than SSH on some servers - Otherwise, other protocols offer little advantage over Smart HTTP for serving Git content - Credential handling for authenticated pushes - Sometimes more complicated than SSH keys - Mitigations (credential caching tools) - Keychain access (macOS) - Credential Manager (Windows) - “Credential Storage” referenced for secure password caching setup ### SSH Protocol - Why it’s common for self-hosting - SSH is often already installed/configured on servers - If not, it’s generally easy to set up - Provides authenticated, encrypted transport - Clone URL forms - Explicit SSH URL - `git clone ssh://[user@]server/project.git` - scp-like shorthand - `git clone [user@]server:project.git` - Username handling - If not specified, Git assumes your current local username - Pros - Easy to set up (SSH daemon is common; admins often know it; OS tools exist) - Secure - Encrypted + authenticated transfer - Efficient - Data compacted before transfer (like HTTPS/Git/Local) - Cons - No anonymous access - Users need SSH access even for read-only cloning - Not ideal for open source “browse/clone without accounts” - If you want anonymous read-only + authenticated writes - Use SSH for push - Add another protocol for public fetch (e.g., HTTPS or git://) ### Git Protocol (git://) - What it is - A Git-provided daemon - Listens on port **9418** - Similar transfer behavior to SSH but: - **No authentication** - (No encryption/auth overhead) - Repository export control - Repo must contain a file named `git-daemon-export-ok` - Without it, the daemon won’t serve the repository - Push behavior - Typically no pushing - You *can* enable pushes, but it’s insecure: - Anyone who discovers the URL could push - Rare in practice - Pros - Often the fastest network transfer option - Useful for - high-traffic public projects, or - very large projects - when no user authentication is required for read access - Uses SSH-like transfer mechanism without encryption/auth overhead - Cons - No authentication (major downside) - Usually should not be the only access method - Common pairing - Developers: SSH or HTTPS (write access) - Everyone else: `git://` (read-only) - Harder to set up than others - Requires its own daemon - Needs system integration (xinetd, systemd, etc.) - Firewall constraints - Requires port **9418** - Often blocked behind corporate firewalls ## Getting Git on a Server (self-host setup) - Scope note (environment assumptions) - Commands shown are simplified for a Linux-based server - Possible on macOS or Windows servers too - Production deployments will differ: - security measures - OS tooling - Step 1: create/export a bare repository - Requirement - Export an existing repo into a **new bare repository** (no working dir) - Create a bare clone - `git clone --bare my_project my_project.git` - Naming convention - bare repos typically end with `.git` - Rough equivalent (not identical) - `cp -Rf my_project/.git my_project.git` - Result - Git data only (no checked-out snapshot) - Directory dedicated to the repository’s internal data - Step 2: put the bare repository on a server - Example environment - Server: `git.example.com` - Repos stored under: `/srv/git` - SSH access available - Copy bare repo to server - `scp -r my_project.git user@git.example.com:/srv/git` - Cloning for other users (with SSH read access to `/srv/git`) - `git clone user@git.example.com:/srv/git/my_project.git` - Push access rule of thumb - SSH + filesystem write permissions to `/srv/git/my_project.git` ⇒ push access - Group write permissions (recommended) - Run inside the repo: - `git init --bare --shared` - Effects - Adds group write permissions appropriately - Does not remove commits/refs/etc. - Minimal “useful Git server” takeaway - Add SSH-capable accounts for collaborators - Place a bare repository where they have read/write permissions - That’s enough for private collaboration - What later sections add (optional sophistication) - avoid per-user accounts - add public read access - add web UIs - etc. ## Small Setups (few developers / trying Git) - Common pain point - User management + permissions - Some repos read-only for some users - Read/write for others - SSH Access approach - If everyone already has SSH access to a server - Easiest initial setup (almost no additional work) - For more complex access control - Use OS filesystem permissions - If server has no accounts for all writers - Set up SSH access for those users - Assumption stated - If you have a server for this, you likely already have SSH installed and use it to access the server - Ways to grant SSH write access - Option 1: create an account for each person - Straightforward - Can be cumbersome (adduser/useradd + temporary passwords) - Option 2: single shared `git` account using `authorized_keys` - Create one `git` user on server - Collect users’ SSH public keys - Append to `~git/.ssh/authorized_keys` - Everyone connects as `git` - Commit data unaffected by SSH username used to connect - Option 3: centralized auth - LDAP or other central auth source - Any SSH authentication method works if user can get shell access ### Generating an SSH public key (client side) - Purpose - Many Git servers authenticate using SSH public keys - Each user must generate a key pair if they don’t have one - Check for an existing key - SSH keys usually stored in `~/.ssh` - Example checks - `cd ~/.ssh` - `ls` - Look for pairs like - `id_dsa` + `id_dsa.pub` - `id_rsa` + `id_rsa.pub` - Meaning - `.pub` file = public key - non-`.pub` file = private key - Generate a key if missing - Tool - `ssh-keygen` (Linux/macOS SSH package; also included with Git for Windows) - Recommended command shown - `ssh-keygen -o` - Prompts and outputs - Choose file path (default `~/.ssh/id_rsa`) - Enter passphrase twice (optional) - Key is saved as: - private key: `~/.ssh/id_rsa` - public key: `~/.ssh/id_rsa.pub` - Passphrase guidance - Can be empty (no password prompts when using the key) - If you do set a password - use `-o` (more resistant key format than default) - `ssh-agent` can help avoid typing passphrase repeatedly - Sharing the public key - User sends the contents of the `.pub` file to the admin - Example command - `cat ~/.ssh/id_rsa.pub` - Reference link mentioned - GitHub SSH key guide: - `https://docs.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent` ### Setting up the server (authorized_keys method) - Note about automation - Much can be automated with `ssh-copy-id` (instead of manual key install) - Create the `git` user and SSH directory - `sudo adduser git` - `su git` - `cd` - `mkdir .ssh && chmod 700 .ssh` - `touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys` - Add developer public keys - Assumption - Trusted public keys saved to temporary files (e.g., `/tmp/id_rsa.john.pub`) - Append to `authorized_keys` - `cat /tmp/id_rsa.john.pub >> ~/.ssh/authorized_keys` - `cat /tmp/id_rsa.josie.pub >> ~/.ssh/authorized_keys` - `cat /tmp/id_rsa.jessica.pub >> ~/.ssh/authorized_keys` - Create a bare repository on the server - `cd /srv/git` - `mkdir project.git` - `cd project.git` - `git init --bare` - First push into the empty bare repository (example workflow) - Note - Someone must create a bare repo on the server for each new project - Example (on John’s computer) - `cd myproject` - `git init` - `git add .` - `git commit -m 'Initial commit'` - `git remote add origin git@gitserver:/srv/git/project.git` - `git push origin master` - Typical collaboration thereafter - Clone - `git clone git@gitserver:/srv/git/project.git` - Edit/commit/push example - `cd project` - `vim README` - `git commit -am 'Fix for README file'` - `git push origin master` - Restricting interactive shell access (optional) - Default situation - Users can log in and get a shell as `git` - Approach - Change shell in `/etc/passwd` - Use `git-shell` (limited shell bundled with Git) - Allows Git push/pull via SSH - Denies normal interactive shell access - Ensure `git-shell` is listed as a valid shell - Check `/etc/shells` - `cat /etc/shells` - Find `git-shell` - `which git-shell` - Add its path to `/etc/shells` if missing - `sudo -e /etc/shells` - Set login shell for user - `sudo chsh git -s $(which git-shell)` - Result of interactive SSH attempt - `ssh git@gitserver` - Message includes - `fatal: Interactive git shell is not enabled.` - hint about `~/git-shell-commands` existing and being executable/readable - Customize git-shell behavior - Create `~/git-shell-commands` - Possible customizations - restrict accepted Git commands - customize the SSH login rejection message - Reference - `git help shell` - Preventing SSH forwarding features (optional hardening) - Even with `git-shell`, users may still use SSH port forwarding - To prevent, prepend options to each key line in `authorized_keys` - `no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty` - Effect - Git network commands still work - Users can’t get a shell / can’t use those forwarding mechanisms ## Git Daemon (Git protocol: git://) - Purpose - Fast, unauthenticated access to Git data - Security reminder - Not authenticated - Anything served is public within its network - Recommended use - Outside firewall: only for publicly visible projects - Inside firewall: useful for many read-only consumers (e.g., CI/build servers) without managing many SSH keys - Run the daemon (base command) - `git daemon --reuseaddr --base-path=/srv/git/ /srv/git/` - Options - `--reuseaddr` - Restart without waiting for old connections to time out - `--base-path=/srv/git/` - Allows cloning without specifying full paths - Trailing `/srv/git/` - Where to look for repos to export - Firewall - Open port `9418` - Daemonizing & supervising the process - Depends on OS/init system - systemd example (common on modern Linux) - Create `/etc/systemd/system/git-daemon.service`: - `[Unit]` - `Description=Start Git Daemon` - `[Service]` - `ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/srv/git/ /srv/git/` - `Restart=always` - `RestartSec=500ms` - `StandardOutput=syslog` - `StandardError=syslog` - `SyslogIdentifier=git-daemon` - `User=git` - `Group=git` - `[Install]` - `WantedBy=multi-user.target` - Adjust as needed - Ensure the `git` user exists or change user/group - Verify Git binary path is `/usr/bin/git` (change if needed) - Service control - Enable on boot: `systemctl enable git-daemon` - Start: `systemctl start git-daemon` - Stop: `systemctl stop git-daemon` - Other alternatives mentioned - xinetd - sysvinit scripts - anything that daemonizes + monitors the process - Exporting repositories over git:// - Per-repository opt-in via file: - `cd /path/to/project.git` - `touch git-daemon-export-ok` ## Smart HTTP (Git over HTTP with smart negotiation) - Goal - One protocol that can provide both: - authenticated push/pull - unauthenticated read-only access - Mechanism - Enable Git’s CGI script: `git-http-backend` - How it behaves - CGI reads URL path + headers from `git fetch` / `git push` - Determines if client supports Smart HTTP (true for clients since Git 1.6.6) - If client is smart - speaks Smart HTTP - Else - falls back to Dumb HTTP behavior (backward-compatible reads) - Basic Apache setup example - Install Apache + utilities - `sudo apt-get install apache2 apache2-utils` - Enable needed modules - `a2enmod cgi alias env` - Enables: `mod_cgi`, `mod_alias`, `mod_env` - Ensure Apache can read/write repositories - Set group of `/srv/git` to `www-data`: - `chgrp -R www-data /srv/git` - Rationale - Apache CGI runs as `www-data` by default - Apache configuration for `/git` path - Environment variables - `SetEnv GIT_PROJECT_ROOT /srv/git` - `SetEnv GIT_HTTP_EXPORT_ALL` - Route `/git/` to the backend - `ScriptAlias /git/ /usr/lib/git-core/git-http-backend/` - Note about `GIT_HTTP_EXPORT_ALL` - If omitted: - unauthenticated clients can only access repos containing `git-daemon-export-ok` - Authenticate writes (example Auth block) - `` - `AuthType Basic` - `AuthName "Git Access"` - `AuthUserFile /srv/git/.htpasswd` - `Require expr !(%{QUERY_STRING} -strmatch '*service=git-receive-pack*' || %{REQUEST_URI} =~ m#/git-receive-pack$#)` - `Require valid-user` - Create `.htpasswd` (example user `schacon`) - `htpasswd -c /srv/git/.htpasswd schacon` - Notes - Many authentication methods exist in Apache; this is just a simple example - Strongly recommended to use SSL so data is encrypted - Key architectural point - `git-http-backend` handles Git protocol negotiation and data transfer - Authentication is handled by the *web server layer* (Apache or other CGI-capable server) - Web server flexibility - Works with nearly any CGI-capable web server - Reference (Apache auth docs) - `https://httpd.apache.org/docs/current/howto/auth.html` ## GitWeb (simple web UI) - Motivation - After enabling read/write and/or read-only access, you may want a basic web visualizer - What it is - Git includes a CGI script called **GitWeb** - (Figure 49 referenced as the GitWeb UI screenshot) - Quick temporary instance: `git instaweb` - Uses a lightweight web server (e.g., `lighttpd` or `webrick`) - Linux note - `lighttpd` often installed → `git instaweb` may “just work” - macOS note (example) - Ruby (and thus `webrick`) may be a convenient choice - Start with a specified handler - `git instaweb --httpd=webrick` - Starts HTTP server on port `1234` and opens a browser automatically - Example log lines shown include WEBrick and Ruby version info - Stop the server - `git instaweb --httpd=webrick --stop` - Running GitWeb continuously (server deployment) - Option 1: install a distro package - Some distros offer a `gitweb` package (via `apt` or `dnf`) - Option 2: install manually (quick walkthrough) - Clone Git source (GitWeb included) - `git clone git://git.kernel.org/pub/scm/git/git.git` - Build GitWeb with project root configured - `cd git/` - `make GITWEB_PROJECTROOT="/srv/git" prefix=/usr gitweb` - Generates `gitweb.cgi` + static assets (e.g., `static/gitweb.js`) - Deploy to web directory - `sudo cp -Rf gitweb /var/www/` - Apache VirtualHost example (CGI enablement) - `` - `ServerName gitserver` - `DocumentRoot /var/www/gitweb` - `` - `Options +ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch` - `AllowOverride All` - `order allow,deny` - `Allow from all` - `AddHandler cgi-script cgi` - `DirectoryIndex gitweb.cgi` - `` - `` - Result - Visit `http://gitserver/` to browse repositories - Server flexibility - Can be served by any CGI-/Perl-capable web server ## GitLab (modern, fully featured Git server example) - Why mentioned - GitWeb is simplistic - GitLab is a popular open source alternative: - more capable - more complex to install/maintain - database-backed web application ### Installation - Recommended approach - Install via official **Omnibus GitLab** package - Other installation options listed - GitLab Helm chart (Kubernetes) - Dockerized GitLab packages (Docker) - Install from source - Cloud providers / platforms - AWS - Google Cloud Platform - Azure - OpenShift - Digital Ocean - Reference mentioned - GitLab Community Edition (CE) README ### Administration (web UI) - Access method - Browser to GitLab hostname/IP - Log in as admin - Default credentials (must change immediately) - Username: `admin@local.host` - Password: `5iveL!fe` - Entering admin interface - Click “Admin area” icon (top right menu) - (Figure 50 referenced) ### Users - Requirement - Everyone must have a GitLab user account - Account contents - Personal info tied to login data - Namespaces - Each user has a namespace grouping their projects - Example - user `jane`, project `project` - URL: `http://server/jane/project` - Removing accounts (two modes) - Blocking - Prevents login - Preserves namespace data - Commits signed with that email still link to profile - Destroying - Removes user from database and filesystem - Deletes projects/data in their namespace - Removes groups they own - More permanent/destructive; rarely needed - (Figure 51 referenced as user admin screen) ### Groups - Definition - Collection of projects + access control data for those projects - Group namespace - Similar to user namespaces - Example - group `training`, project `materials` - URL: `http://server/training/materials` - Permissions - Group users have permission levels for group and projects - Range example - Guest: issues/chat only - Owner: full control (group, members, projects) - Too numerous to list (GitLab links from admin screen) - (Figure 52 referenced as group admin screen) ### Projects - Meaning - Roughly corresponds to a single Git repository - Namespace association - Every project belongs to exactly one namespace: - user, or - group - Access control behavior - User-owned project - owner directly controls access - Group-owned project - group member permissions apply - Visibility levels (read access control) - Private - owner explicitly grants access to specific users - Internal - visible to any logged-in user - Public - visible to anyone - Applies to both - `git fetch` access - web UI access ### Hooks - Hook support - Project-level hooks - System-level hooks - Behavior - GitLab sends HTTP POST with descriptive JSON when events occur - Purpose - Integrate with automation and tooling - CI servers - chat rooms - deployment tools ### Basic Usage - Create a project - Click “+” icon on toolbar - Provide - project name - namespace - visibility level - Most settings can be changed later - Click “Create Project” - Connect project to local Git workflow - Access methods - HTTPS - SSH - URLs shown at top of project home page - Add remote for an existing local repository (example remote name `gitlab`) - `git remote add gitlab https://server/namespace/project.git` - Or clone if you don’t have a local copy - `git clone https://server/namespace/project.git` - Web UI repository views - Project home: recent activity - Navigation links: files view + commit log ### Working Together - Model 1: direct push access - Add users via project settings → “Members” - Assign access level - “Developer” or above can push commits/branches directly - Model 2: merge requests (more decoupled) - Users with push access - create branch - push commits - open merge request back into `master` (or another branch) - Users without push permission - fork project - push to fork - open merge request from fork into main project - Benefits - owner controls what/when changes merge - supports contributions from untrusted users - Discussion units - Merge requests + issues are main long-lived discussion objects - Merge requests support - line-by-line discussion (lightweight code review) - overall discussion thread - Both can be - assigned to users - organized into milestones - Broader feature note (beyond Git) - Also provides features like - project wikis - system maintenance tools - Operational benefit - after initial setup, little need for config-file edits or SSHing to server - most admin/usage via browser UI ## Third Party Hosted Options - When to choose - You don’t want to set up/maintain your own Git server - Advantages - quick setup - easy project creation - no maintenance/monitoring - even if you self-host internally: - public hosting for open source can be easier for the community to find/contribute - Choosing a host - Many options with different pros/cons - Up-to-date list referenced - GitHosting page on the main Git wiki: - `https://git.wiki.kernel.org/index.php/GitHosting` - GitHub note - GitHub covered in detail in the “GitHub” chapter - It’s the largest Git host, and you may need to interact with GitHub-hosted projects - Many other hosts exist if you prefer alternatives ## Summary (decision guidance) - You have multiple options for running a remote Git repository to collaborate/share work - Self-hosting - Pros - high control - can run within your firewall - Cons - time/effort to set up - ongoing maintenance burden - Hosted services - Pros - easy to set up and maintain - Cons - your code resides on someone else’s servers - some organizations prohibit this - Practical takeaway - Choose the solution (or combination) that fits your organization’s needs