Table of Contents
Why Version Control Matters
Git is a distributed version control system. It helps you record changes to files, go back to previous versions, experiment safely, and collaborate with others.
Unlike saving many copies of a file like project-final-really-final.txt, Git creates a structured history of changes. It stores snapshots of your project and lets you see who changed what and when.
Git is distributed, which means every clone of a repository contains the full history. You can work offline, commit locally, and synchronize with others later.
Git records text changes extremely well. Commit early and often, with small focused changes and clear messages.
Git Repositories and Basic Concepts
A Git repository is a directory tracked by Git. Inside it, Git stores all history and metadata in a hidden .git directory.
There are two main ideas:
A working directory is your actual files that you edit.
The repository is the internal database of commits, branches, and tags, inside .git.
Every commit forms a node in a history graph. Each commit has a unique hash like a1b2c3d, which Git uses as an identifier. Commits also store a message, an author, and timestamps.
In Git, almost everything is addressed by a hash. You rarely need to type the whole hash. Usually the first 7 characters are enough, for example git show a1b2c3d.
The Three Git Areas
Git tracks changes using three areas: the working tree, the staging area, and the repository.
The working tree is your files on disk. When you edit files, they become modified in the working tree.
The staging area, also called the index, is a temporary area where you prepare changes to be committed. You select which changes go into the next commit.
The repository is where committed snapshots live. After you commit, your changes move from the staging area into the repository history.
You can visualize the flow as:
$$
\text{Working tree} \rightarrow \text{Staging area} \rightarrow \text{Repository (commits)}
$$
A commit only includes what is in the staging area, not every change in your working directory.
Creating and Cloning Repositories
There are two main ways to get a Git repository.
To start a new project in an existing directory, run:
git init
This creates a new .git directory and turns the current directory into a repository. At first, there are no commits until you add files and commit.
To copy an existing repository from somewhere else, such as GitHub, use git clone:
git clone https://github.com/user/project.gitThis command downloads the history and creates a new directory with a local copy. The new directory is a fully functional repository.
The repository you clone from is usually registered as a remote called origin. Remotes will be discussed in more detail when you start collaborating, but for now it is enough to know that origin is the default name for the place you cloned from.
Checking Repository Status
To see what Git thinks about your current directory, use:
git statusThis command shows:
Which branch you are on.
Which files are untracked, modified, or staged.
Hints about which commands to run next.
You will use git status often to understand your current state.
Tracking and Staging Files
New files are untracked. Git ignores them until you explicitly add them.
To start tracking a file and stage it for commit, run:
git add filenameYou can stage multiple files at once:
git add file1 file2 dir/To stage all changes in the repository:
git add .This stages new, modified, and deleted files. For beginners this is convenient, but be careful because it may stage more than you expect.
If you want to stage only parts of a file, you can use interactive staging:
git add -p filenameThis lets you choose individual hunks of changes, which is useful for creating clean, focused commits.
Only changes in the staging area will be included in the next commit. Always run git status before committing to verify what is staged.
Making Commits
A commit records a snapshot of the project. It also has a message that explains why the change was made.
To create a commit of the staged changes, run:
git commit -m "Short, meaningful message"Commit messages should describe the change, not just repeat filenames. For example:
Fix off-by-one error in line counter is better than update file.
If you omit -m, Git will open an editor so you can write a multi-line commit message. A common style is:
First line: short summary, around 50 characters.
Blank line.
Optional detailed description.
Each commit points to a parent commit. Together, they form a chain:
$$
\text{Commit}_1 \rightarrow \text{Commit}_2 \rightarrow \text{Commit}_3
$$
Git uses this chain to reconstruct the state of the project at any point in time.
Viewing History
To see the recent commit history, use:
git logThis shows commits with their hashes, authors, dates, and messages. The default view can be long, so you can use shorter formats, for example:
git log --onelineThis prints a single line per commit, with a short hash and the first line of the commit message.
To see which changes a specific commit introduced, use:
git show <commit-hash>You can use abbreviations like:
git show HEAD
HEAD means the current commit that your working directory is based on.
Inspecting and Comparing Changes
Git can show what has changed before you commit. To see modifications in your working tree that are not staged, run:
git diffTo see what is staged and will be included in the next commit, run:
git diff --cachedThese commands display differences line by line. Deletions are prefixed with a minus sign, additions with a plus sign.
Always review your changes with git diff or git diff --cached before committing. This reduces accidental commits of debug code or secrets.
Undoing and Fixing Common Mistakes
You will make mistakes, and Git provides several tools to correct them.
If you changed a tracked file and want to discard your local modifications and return to the last committed version, run:
git restore filename
If you already staged changes with git add, but want to unstage them while keeping the modifications in your working tree, use:
git restore --staged filenameIf your last commit message is wrong or you forgot to include a file, you can amend the most recent commit:
git commit --amendThis replaces the last commit with a new one, which can have a different message and contents. Use this only on local commits that you have not yet shared with others.
To create a commit that reverts the effect of an earlier one, use:
git revert <commit-hash>
This makes a new commit that applies the opposite changes. git revert is safe even for shared history.
There is also git reset, which can move HEAD and change history. It is powerful but also dangerous when used incorrectly. For beginners, prefer git restore and git revert, which are safer.
Basic Branching
A branch is a movable pointer to a series of commits. Branches let you work on new features or experiments without affecting the main line of development.
To list existing branches, run:
git branchTo create a new branch based on the current commit:
git branch feature-xTo switch to a branch:
git switch feature-xYou can also create and switch to a new branch in one command:
git switch -c feature-x
The branch you are on controls where new commits are added. When you commit on feature-x, that branch moves forward. Other branches remain unchanged.
Basic Merging
When you are done working on a branch and want to bring its changes into another branch, you usually perform a merge.
First, switch to the branch that should receive the changes. For example, to merge feature-x into main:
git switch main
git merge feature-xGit will attempt to combine the changes automatically. If there are no conflicting edits, it creates a merge commit that connects the two lines of history.
If both branches changed the same lines in a file, Git cannot decide which version is correct. This creates a merge conflict. Git marks the conflicting parts in the file and git status shows which files need attention.
To resolve a conflict, open the affected file, edit it to the correct content, then stage the file and complete the merge with a commit.
During a merge, never delete conflict markers like <<<<<<< and >>>>>>> without carefully choosing the correct content. After editing, always run git status to make sure all conflicts are resolved before committing.
Remotes, Push, and Pull
To collaborate with others or back up your work, you use remote repositories, typically on a server or hosting service.
You can list configured remotes with:
git remote -vAfter committing locally, you can send your changes to the default remote with:
git pushUsually, this is shorthand for:
git push origin main
This means push the local main branch to the origin remote. The first time you push a new branch, you may need to set the upstream:
git push -u origin feature-xTo bring remote changes into your local repository, use:
git pull
git pull is equivalent to git fetch followed by a merge. It downloads new commits and then integrates them into your current branch. Later, you may choose to work with git fetch and merge or rebase manually, but for starting out, git pull is sufficient.
Ignoring Files
Some files should not be tracked, such as build artifacts, temporary files, and local editor settings. You can tell Git to ignore them using a .gitignore file.
Create a file named .gitignore in the root of your repository, and list patterns of files and directories to ignore. For example:
# Ignore build outputs
build/
*.o
# Ignore editor settings
.vscode/
*.swp
Git will then treat those paths as untracked and not suggest adding them. If a file is already tracked, adding it to .gitignore does not remove it from the repository. You must remove it with git rm --cached filename if you want it to stop being tracked.
Never commit secrets such as passwords, private keys, or API tokens. Add them to .gitignore or store them using dedicated secret management tools.
Basic Best Practices
For effective use of Git, keep a few simple habits.
Commit frequently, but with each commit focused on one logical change.
Write clear, descriptive commit messages that explain the reason for the change.
Review changes with git diff and git status before each commit and before each push.
Use branches to isolate features and experiments from stable code.
Always pull remote changes before starting new work, especially on shared branches.
These fundamentals will make Git a reliable tool that supports your development on Linux and prepares you for more advanced workflows later.