--- 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 can’t 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 Git’s built-in help: ```bash git help git --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 git commit -m "Initial project version" ``` ## Cloning a repository with git clone To get a copy of an existing repository: ```bash git clone ``` 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 Git’s 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 you’re 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 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 ``` To keep the file in your working directory but remove it from Git tracking (stage an “untrack”): ```bash git rm --cached ``` ## 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 ``` Newer Git also provides `git restore`: ```bash git restore --staged ``` ## Discarding local modifications To discard modifications in the working directory (dangerous; it overwrites your local changes): ```bash git checkout -- ``` Or with `git restore`: ```bash git restore ``` ## 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 you’re 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 ``` Newer Git provides `git switch`: ```bash git switch git switch -c ``` 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 ``` ## Deleting branches after merge After merging a topic branch, you often delete it: ```bash git branch -d ``` ## Merge conflicts and resolution If two branches change the same lines, Git can’t 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 ... >>>>>>> ``` Resolution workflow: 1. Open the file and resolve the conflict by editing. 2. Stage the resolved file (`git add `). 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 git rebase ``` 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 ``` ## Fetching and pulling Fetch downloads data from a remote without merging it into your current work: ```bash git fetch ``` 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 ``` ## Tracking branches upstream To set the upstream (tracking) relationship when pushing: ```bash git push -u origin ``` ## Renaming and removing remotes Rename a remote: ```bash git remote rename ``` Remove a remote: ```bash git remote remove ``` ## Deleting remote branches Delete a branch on the remote: ```bash git push origin --delete ``` # 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 won’t 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 lecture’s 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 colleague’s repository, make changes in a topic branch, and submit a pull request.