2021-10-01

Simple Git

Git is a file history version control tool. You can think of it as keeping a backup of every change you make—no need to worry if you mess something up. Git also supports simultaneous collaboration among multiple people, making it ideal for teams working on code together. You won’t lose your unsaved work to someone else’s upload, and if a bug appears, you can trace who introduced it. Open-source projects can make their Git repositories public so others can access the source code, and Git provides a mechanism for others to contribute code to the project.

1. Repository

A Git repository corresponds to a directory that stores your project code. Changes made inside this directory can be recorded and backed up. To create a new local repository, use:

git init

This command turns your current directory into a Git repository.

To download a remote Git repository (e.g., code from an open-source project) to your local machine, use:

git clone <uri>

This creates a new directory named after the project, containing the full contents of the remote repository—including its complete history, so you can view or revert to any previous version.

If you only want the latest code without the full history, use the --depth parameter to limit the number of commits downloaded:

git clone <uri> --depth 1

This creates a shallow clone with only the most recent commit, reducing data transfer.

Git stores history in a hidden .git folder inside the repository directory. This folder contains all Git-related data, including history and branches. Deleting .git erases all version history and turns the directory back into a normal folder.

2. Commit

If you only use Git to download code, the previous section is enough. One of Git’s greatest strengths is backing up change history, where each set of modifications corresponds to a commit. After editing files in your working directory, how do you tell Git to save your changes?

2.1 Stage

First, you stage your changes to mark which files will be included in the next commit. You can stage files that are:

  • Untracked: New files not yet in the repository
  • Modified: Existing files that have been changed
  • Deleted: Existing files that have been removed

Check the status of your files with:

git status

For example, if you create README.md, modify material/base.html, and delete package-lock.json, the output will look like:

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
 modified:   material/base.html
 deleted:    package-lock.json
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 README.md

Stage files using:

git add <file>

For deleted files, you can use either git add or git rm. git rm both stages the deletion and removes the file from your working directory.

After staging, git status will show:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
 new file:   README.md
 modified:   material/base.html
 deleted:    package-lock.json

2.2 Commit

Once all desired changes are staged, commit them with:

git commit -m <msg>

The msg is a short description of your changes. If this is your first commit, Git will ask you to identify yourself:

git config --global user.name <name>
git config --global user.email <email>

These details are attached to all future commits.

The full stage-commit workflow is shown below:

Stage-Commit Workflow

If you need to fix the last commit (e.g., missing files or a typo in the message), stage your corrections and run:

git commit --amend

This overwrites the last commit instead of creating a new one.

2.3 Restore

To unstage a file:

git restore --staged <file>

To revert a modified or deleted file back to its last committed version:

git restore <file>

This permanently discards changes, so use it carefully. Untracked new files can simply be deleted manually.

The restore workflow is shown below:

Restore Workflow

3. Branch

Here’s where it gets more advanced! Git supports a non-linear workflow, which enables powerful team collaboration. For example, you might be building a new feature, then need to quickly fix a critical bug on the main version, then return to your feature while including the bug fix. Branches make this possible.

A Git repository starts with a default branch called master.

3.1 History

Git stores commits in a tree structure. Each commit has a unique SHA-1 hash (a 40-character hexadecimal string) that identifies it. Most commits point to their parent commit.

Example commit history:

Commit History

The HEAD pointer shows your current position. New commits move HEAD and the branch forward.

You can use a short prefix of the SHA-1 if it’s unique, e.g., 6 characters.

View commit history:

git log

View a graphical graph of all branches:

git log --all --graph

3.2 Branch

Create a new branch:

git branch <branch_name>

The new branch starts at your current HEAD.

New Branch

Switch branches:

git checkout <branch_name>

Now new commits belong to this branch.

Checkout Branch

3.2 Merge

To combine changes from one branch into another (e.g., merge dev into master):

git checkout master
git merge <branch_name>

If the branches have not diverged, Git performs a fast-forward merge with no new commit.

If the branches have diverged (both have new commits), Git creates a merge commit with two parents.

Divergent History Merge

If changes overlap, you get a merge conflict:

Auto-merging main.c
CONFLICT (content): Merge conflict in main.c
Automatic merge failed; fix conflicts and then commit the result.

Git marks conflicts in the file:

<<<<<<< HEAD
content from master
=======
content from dev
>>>>>>> dev

Edit the file to resolve conflicts, then:

git add <file>
git commit

Delete a branch when you no longer need it:

git branch -d <branch_name>

3.3 Rebase

For a clean, linear history, use rebase instead of merge. Rebase reapplies your branch’s commits on top of another branch.

On master:

git rebase <branch_name>

Example: git rebase dev reapplies master commits onto dev.

Rebase

If conflicts occur:

  1. Fix the files
  2. git add <file>
  3. git rebase --continue

Rebase rewrites commit hashes. Use rebase for clean history; use merge for preserving actual development steps.

3.4 Reset

restore and amend only affect the latest commit. To view an older commit:

git checkout <commit_sha_1>

To roll back the current branch to an older commit:

git reset [--soft|--hard] <commit_sha_1>
  • Default: keep changes but unstage them
  • --soft: keep changes and stage them
  • --hard: permanently delete changes

You can also use HEAD~x to refer to the x-th commit before HEAD.

Squash multiple commits into one:

git reset --soft HEAD~3
git commit

4. Remote

To collaborate online, connect your local repository to a remote repository.

When you git clone <uri>, Git automatically creates a remote named origin pointing to the URL.

Add a remote manually:

git remote add <remote_name> <uri>

Remove a remote:

git remote rm <remote_name>

Download updates from the remote without merging:

git fetch <remote_name>

4.1 Track

Link a local branch to a remote branch (tracking):

git branch -u <remote_name>/<branch_name>

-u = set upstream.

If the remote branch exists locally, you can simply check it out:

git checkout <branch_name>

Git will automatically set up tracking.

4.2 Pull

To fetch and merge in one step:

git pull [--merge|--rebase]
  • git pull = git fetch + git merge
  • git pull --rebase = git fetch + git rebase

Resolve conflicts the same way as with local branches.

4.3 Push

Upload local commits to the remote:

git push

Always pull before pushing to avoid overwriting others’ work.

Force push (use carefully, e.g., to remove sensitive data):

git push --force

Create a remote branch and set upstream:

git push -u <remote_name> <branch_name>

Delete a remote branch:

git push -d <remote_name> <branch_name>

You need write permission to push.

4.4 GitHub

GitHub is a popular Git hosting platform.

  • Maintainers: People you invite with write access.
  • Contributors: The public can fork your repo, make changes, and open a pull request (PR) for you to review and merge.

Similar platforms: GitLab, Gitee.

5. Submodule

To include another Git repository inside yours (for dependencies), use submodules:

git submodule add <uri>

This creates a .gitmodules file and links the external repo.

Clone a repo with submodules:

git clone <uri> --recursive

Or initialize after cloning:

git submodule update --init [--recursive]

Submodules save storage by referencing external repos instead of copying their code.

Summary

This tutorial covered:

  • Repository: git init, git clone
  • Commit: git add, git commit, git restore, git reset
  • Branch: git branch, git checkout, git merge, git rebase
  • Remote: git remote, git fetch, git pull, git push, GitHub workflow
  • Submodule: Managing dependencies

To learn more about any command:

git <command> --help