devops/materials/07-git.md
2026-01-01 00:00:00 +00:00

559 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: mat-07-git
title: Git
---
# Introduction to source control
## Why version control
When you work on software, you inevitably create multiple versions of the same artifact (source code, docs, config). Source control (version control) is used to:
- track changes over time
- collaborate safely on the same codebase
- keep a detailed history and understand “who changed what, when, and why”
- revert to previous versions
- integrate with development tooling (IDEs, build automation, CI/CD)
## Types of version control systems
Version control systems are commonly grouped into:
- **Local**: versioning kept locally on one machine.
- **Centralized**: a central server stores the history; clients check out working copies.
- **Distributed**: every clone contains the full history; collaboration happens by exchanging commits.
## Git overview
Git is a distributed version control system (DVCS) used to track changes in source code during software development. It supports collaboration between multiple developers, branching and merging, and reverting to previous versions. Git works like a **stream of snapshots**, not deltas.
# Git data model and states
## Snapshots not deltas
Git stores data as a series of snapshots of the project over time. When you commit, Git records a snapshot of what you staged.
## Working directory staging area git directory
A Git project can be viewed as three “places”:
- **Working directory**: your checked-out files where you edit.
- **Staging area** (index): where you build the next commit by selecting specific changes.
- **Git directory** (`.git`): the database where Git stores committed snapshots and metadata.
## File states modified staged committed
Git describes file state using three key states:
- **Modified**: changed in the working directory but not committed.
- **Staged**: marked to go into the next commit snapshot.
- **Committed**: safely stored in the Git database.
## Integrity and sha1
Everything in Git is checksummed. Git uses a SHA-1 checksum to identify content; if file contents change, the checksum changes. This supports integrity: you cant change content “silently” without changing the identifier.
# Getting started
## Installing Git and verifying
After installing Git, verify with:
```bash
git --version
```
## First time setup with git config
Set your identity (used in commits):
```bash
git config --global user.name "Name Surname"
git config --global user.email "user@provider.com"
```
Inspect settings:
```bash
git config --list
git config user.name
git config user.email
```
## Getting help
Use Gits built-in help:
```bash
git help <verb>
git <verb> --help
```
## Getting a repository with git init
To start version control in an existing directory:
```bash
cd /path/to/project
git init
```
This creates a `.git` directory (the repository “skeleton”). To begin tracking existing files and make an initial commit:
```bash
git add <files...>
git commit -m "Initial project version"
```
## Cloning a repository with git clone
To get a copy of an existing repository:
```bash
git clone <url>
```
Unlike a “checkout” in some other systems, cloning pulls down a full copy of nearly all data, including project history.
# Recording changes
## Tracked and untracked files
Files are either:
- **Untracked**: not yet in Gits database.
- **Tracked**: known to Git (from the last snapshot and any staged files). Tracked files can be unmodified, modified, or staged.
## Checking status with git status
Use `git status` to see:
- what branch youre on
- which files are staged
- which files are modified but not staged
- which files are untracked
```bash
git status
```
## Short status format
Use the short format:
```bash
git status -s
```
It uses a two-column code. The left column refers to the staging area; the right column refers to the working directory. Examples:
- `??` untracked file
- `A ` added to staging area
- `M ` modified and staged
- ` M` modified but not staged
- `AM` added to staging area and then modified again
## Staging with git add
Stage new files or stage changes of tracked files:
```bash
git add <file>
git add .
```
Staging is how you choose what will go into the next commit snapshot.
## Viewing changes with git diff
To see what you changed but have **not staged** yet:
```bash
git diff
```
To see what you staged that will go into the next commit:
```bash
git diff --staged
```
(`--cached` is a synonym of `--staged`.)
## Committing with git commit
Create a commit (a snapshot of what you staged):
```bash
git commit -m "message"
```
## Skipping the staging area with git commit -a
For tracked files, you can skip explicit staging and commit modifications directly:
```bash
git commit -a -m "message"
```
## Removing files with git rm
Remove a file from your working directory **and** stage its removal:
```bash
git rm <file>
```
To keep the file in your working directory but remove it from Git tracking (stage an “untrack”):
```bash
git rm --cached <file>
```
## Moving or renaming files with git mv
Rename/move a file:
```bash
git mv oldname newname
```
## Ignoring files with gitignore
To ignore generated files, secrets, build outputs, etc., use a `.gitignore` file. Pattern rules include:
- Blank lines are ignored.
- Lines starting with `#` are comments.
- Standard glob patterns work (`*`, `?`, `[a-z]`).
- A leading `/` matches from the repository root.
- A trailing `/` matches directories.
- `!` negates a pattern.
- `**` can match nested directories.
A repository can have one `.gitignore` at the root, and it can also have additional `.gitignore` files in subdirectories; nested rules apply only under that directory.
## Viewing history with git log
View commit history:
```bash
git log
```
A compact visualization is:
```bash
git log --oneline --decorate --graph --all
```
# Undoing mistakes
## Amending the last commit
To change the last commit (for example, fix the message or add missing staged changes):
```bash
git commit --amend
```
## Unstaging files
If you staged a file but want to unstage it:
```bash
git reset HEAD <file>
```
Newer Git also provides `git restore`:
```bash
git restore --staged <file>
```
## Discarding local modifications
To discard modifications in the working directory (dangerous; it overwrites your local changes):
```bash
git checkout -- <file>
```
Or with `git restore`:
```bash
git restore <file>
```
## Restore versus reset and safety
`git restore` was introduced in Git 2.23.0 as an alternative to using `git reset`/`git checkout` for some common “undo” cases. Commands that discard changes are dangerous: once you overwrite local modifications, recovering them can be difficult.
# Branching
## What a branch is
Branching means you diverge from the main line of development and continue work without “messing” with the main line. Git branching is lightweight and switching branches is fast.
In Git, a **branch** is a lightweight movable pointer to a commit. The default branch name is often `master` (and on GitHub the default branch is commonly `main`).
## HEAD pointer and current branch
Git keeps a special pointer called **HEAD** to know what branch youre currently on. When you commit, the current branch pointer moves forward.
## Creating and switching branches
Create a new branch:
```bash
git branch testing
```
Switch to a branch:
```bash
git checkout testing
```
Create and switch in one step:
```bash
git checkout -b <newbranchname>
```
Newer Git provides `git switch`:
```bash
git switch <branchname>
git switch -c <newbranchname>
```
When you check out another branch, your working directory files change to match the snapshot of that branch.
## Branches are cheap
A branch is represented by a small file that contains the 40-character SHA-1 checksum of the commit it points to. Creating and deleting branches is therefore cheap.
## Divergent history and viewing the graph
If you make commits on different branches, history diverges. You can visualize with:
```bash
git log --oneline --decorate --graph --all
```
# Merging
## Fast forward merges
If the branch you merge has not diverged (your current branch is directly behind it), Git can do a **fast-forward** merge: it just moves the branch pointer forward.
## Three way merges
If branches have diverged, Git performs a **three-way merge** using two branch tips and their common ancestor, creating a new merge commit (with 2+ parents).
Merge example:
```bash
git merge <branchname>
```
## Deleting branches after merge
After merging a topic branch, you often delete it:
```bash
git branch -d <branchname>
```
## Merge conflicts and resolution
If two branches change the same lines, Git cant merge automatically. You will see conflicts (e.g., in `git status`), and the conflicted file contains conflict markers like:
```text
<<<<<<< HEAD
... your current branch ...
=======
... the other branch ...
>>>>>>> <branchname>
```
Resolution workflow:
1. Open the file and resolve the conflict by editing.
2. Stage the resolved file (`git add <file>`).
3. Finish by committing (often `git commit` after a merge conflict).
## Merge versus rebase
Merging and rebasing are two ways of integrating changes from one branch into another.
- **Merge** takes the endpoints of branches and merges them, creating a merge commit (when needed).
- **Rebase** replays commits from one branch onto another base, creating a “cleaner” linear history, but changing the history of the rebased commits.
## Basic rebasing workflow
A common rebase flow is to move a topic branch onto the updated base branch:
```bash
git checkout <topic-branch>
git rebase <base-branch>
```
After resolving any conflicts during a rebase, you continue with:
```bash
git rebase --continue
```
# Remotes
## Remote repositories and origin
A remote repository is a Git repository hosted elsewhere (for example on GitHub). When you clone, Git typically adds a remote named `origin`.
## Showing and adding remotes
Show remotes:
```bash
git remote
git remote -v
```
Add a remote:
```bash
git remote add <shortname> <url>
```
## Fetching and pulling
Fetch downloads data from a remote without merging it into your current work:
```bash
git fetch <remote>
```
Pull fetches and then merges into your current branch (when your branch is set up to track a remote branch):
```bash
git pull
```
## Pushing
Push commits to a remote branch:
```bash
git push <remote> <branch>
```
## Tracking branches upstream
To set the upstream (tracking) relationship when pushing:
```bash
git push -u origin <branch>
```
## Renaming and removing remotes
Rename a remote:
```bash
git remote rename <old> <new>
```
Remove a remote:
```bash
git remote remove <name>
```
## Deleting remote branches
Delete a branch on the remote:
```bash
git push origin --delete <branch>
```
# Tagging and releases
## Lightweight and annotated tags
Tags are typically used to mark releases.
- **Lightweight tag**: just a pointer to a commit.
- **Annotated tag**: stored as a full object with metadata and a message.
Create an annotated tag:
```bash
git tag -a v1.4 -m "my version 1.4"
```
Create a lightweight tag:
```bash
git tag v1.4-lw
```
## Listing and showing tags
List tags:
```bash
git tag
git tag -l "v1.*"
```
Show information for a tag:
```bash
git show v1.4
```
## Checking out tags and detached head
If you check out a tag, Git puts you in a **detached HEAD** state. You can inspect the code, but commits you make wont belong to a named branch unless you create one.
```bash
git checkout v2.0.0
```
# Productivity
## Git aliases
You can define aliases to shorten commands:
```bash
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
```
Aliases can also wrap longer commands, for example:
```bash
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
```
## Credential helpers
To avoid typing credentials repeatedly, Git can use credential helpers (depending on your OS) to cache or store credentials for Git operations.
# Collaboration on GitHub
## Forking and pull requests
A common collaboration pattern on GitHub is:
1. Fork the project (create your copy on GitHub).
2. Clone your fork locally.
3. Add the original repository as an `upstream` remote.
4. Create a topic branch, make commits, and push to your fork.
5. Open a pull request from your fork/topic branch into the upstream repository.
6. Keep your fork updated by fetching from upstream and integrating changes.
## Suggested class assignment and homework workflow
The following sequence matches the lectures suggested lab/homework ideas:
- Create a new repository on GitHub and connect it to a local project.
- Make commits, inspect status/diff/log, and push to GitHub.
- Create a new branch, make changes, and merge back.
- Create a three-branch workflow (for example `develop`, `test`, `production`) and practice merging changes through the branches.
- Fork a colleagues repository, make changes in a topic branch, and submit a pull request.