- Commit: a snapshot that captures the state of your project at a particular point in time.
- HEAD: a pointer that usually points to the tip of the currently checked out branch.
- Branch: a series of commits with a pointer that points to the most recent one.
- Working directory: the local workspace hosting your entire project files and directories and where you experiment with new changes.
- Staging area: a place where changed files are added before they can be part of the next commit.
- Repository: the hidden .git directory that is created and used by Git to keep track of changes to your files.
- Remote: the name of a pointer, typically named "origin," that points to the URL of a cloud-hosted repository.
Set up your name and email address:
git config --global user.name "John Doe"
git config --global user.email "john@email.com"
Set up Visual Studio Code as the default text editor:
git config --global editor.core "code --wait"
Initialize a new Git repository:
git init
Clone an existing Git repo:
git clone <url>
Show the current state of your repository.
Run git status -s
to see the output in a shorter format.
Add changed files or previously untracked files from the working directory to the staging area. Use git add .
to add all files at once.
Create a new commit with files that are currently in the staging area.
To add a separate commit message that explains in more detail the changes in your commit, run: git commit -m "commit message" -m "another commit message"
.
You can add all tracked files and commit them in one go by running git commit -am "commit message"
. This only works if you're committing files that are already tracked by Git.
To add new changes to the most recent commit instead of creating a new one, or to modify the commit message run git commit --amend
or git commit --amend -m "new commit message"
after staging your new changes if there are any.
To create an empty commit, run git commit --allow-empty -m “commit message”
.
Show a log of commits for the current repository along with the current position of HEAD (which commit it points to).
A useful variant is git log --oneline
which shows a shortened commit hash alongside the commit message.
You use the --grep
option to search for commits that contain a specific string in their commit message. For example: git log --grep="commit message"
.
You can limit your search to a specific number of commits by using the -n
flag: git log --grep="commit message" -n 5
.
And you can also use -i
flag to make the search case-insensitive.
Create a new branch called <name>
based on the currently checked out branch.
Get a list of all branches in the current repository.
You get the same output by running git branch --list
.
Switch to an existing branch named <branch>
.
Create a new branch <branch>
and then switch to it. This effectively combines git branch <branch>
and git switch <branch>
.
Delete the branch named <branch>
. You can force delete it by using the -D
flag.
You can delete multiple branches by listing them: git branch -d <branch1> <branch2> <branch3>
.
You can't delete a branch if it's currently checked out. You have to switch to a different branch first.
Rename the currently checked out branch to <new-name>
. To rename a different branch: git branch -m <old-branch-name> <new-branch-name>
.
git checkout
is a versatile command that can be very useful sometimes. These are some of the ways you can use it:
git checkout <commit>
: this goes into detached HEAD mode by making HEAD point directly to the commit that you specify (instead of pointing to the tip of a branch). In this mode, you can view and make changes and at any point you may revert your changes by runninggit switch <branch>
orgit switch -
to switch to the branch HEAD was pointing to before using thecheckout
command. If you'd like to keep the changes you make in detached HEAD, you can rungit switch -c <branch>
to create a new branch with your new changes.git checkout HEAD~n
: check out the nth ancestor commit of HEAD.git checkout HEAD <file>
orgit checkout -- <file>
: discard staged and unstaged changes to match HEAD. Similar to runninggit restore
, explained below.
There are two steps required in merging:
- First, you switch to the branch you want to merge changes into.
- Then you run the merge command.
For example, to merge feature
branch into main
:
git switch main
git merge feature
Some useful options:
- To show a list of branches that have been merged into the currently checked out branch:
git branch --merged
- To show a list of branches that have not yet been merged into the currently checked out branch:
git branch --no-merged
- To merge changes in
feature
branch intomain
but using only a single commit:git merge --squash main
. All commits from the branch will be squashed into a single commit. You’ll see all changes have been staged and you’ll have to manually create a new commit.
Fast-Forward Merge
In most cases, the merge will be a fast-forward merge: the base branch (main
) pointer will simply be updated to point to the same commit that the merged branch (feature
) points to.
Non-Fast-Forward Merge
Sometimes the merge isn't exactly straightforward. This happens when two branches diverge.
Suppose that you create a new branch based on main
and call it feature
. Let's say that you add a couple of new commits on feature
and at the same time there's a new commit on main
that you currently don't have. When you try to merge, Git will create a new merge commit.
You can manually create a merge commit by running git merge --no-ff <branch>
. This helps you maintain a clear history of when a branch was merged into another branch.
Resolving Merge Conflicts
Merge conflicts happen when the same part of the file is modified differently on the two merged branches. When this happens, Git will let you decide what to keep and what to remove.
Merge conflicts are resolved manually. Adding files using git add <file>
tells Git that conflicts have been resolved.
Run git merge --abort
after a merge has caused conflicts to return to the pre-merge state. It's important to note that there is no guarantee that uncommitted changes will be reconstructed by running this command.
After resolving conflicts and adding files to the staging area, you may create a commit manually or run git merge --continue
to be prompted for a merge commit message.
You may use git cherry-pick
to merge specific commits from one branch to another.
For example, if you want to merge <commit-hash>
from the branch main
to the branch feature
, you run the following:
# swtich to the target branch first
git switch feature
# now run the cherry-pick command
git cherry-pick <commit-hash>
Running this command may cause merge conflicts. You can resolve them normally. You get the familiar abort and continue options as you do with regular merges.
Show the difference between the working directory (the current state of your files) and the staging area. In other words, this shows you changes to your working directory that you could add to the staging area by running git add .
. The git diff
command accepts different options and arguments:
git diff --staged
orgit diff --cached
: show changes that have been staged relative to the most recent commit.git diff HEAD
: list all changes, staged and unstaged, since the last commit.git diff <file1> <file2>
: show working directory changes to specified files.git diff <branch1> <branch2>
: compare the tip ofbranch1
to the tip ofbranch2
.git diff <commit1> <commit2>
: compare changes between the given commits (which are passed as commit hashes).git diff --staged <commit> <file>
: show changes that have been staged for file<file>
since the commit<commit>
. It basically answers the question: how is<file>
going to be different in the next commit compared to the old commit<commit>
?
Let's say you have some incomplete work on a feature and right now you really want to work on a different feature altogether. You don't want to finish working on that now, and your changes are not yet ready to be committed. What do you do in this case? You stash, of course.
You can stash as many times as you like. Your changes are saved to a stack-like data structure where the top entry is the most recent.
Stash away the current changes (staged and unstaged) and get back to a clean working tree matching the most recent commit. git stash push
does the exact same thing.
There are several variations on the basic stash command:
git stash --keep-index
: stash only unstaged changes, keeping staged changes intact.git stash -p
: interactively choose which chunks of changes to stash.git stash push <file1> <file2>
: stash changes made to the provided file paths in one stash entry.git stash list
: show a list of stashed changes.git stash pop
: reapply the most recently stashed changes to the working directory and remove them from the stash.git stash apply
: reapply the most recently stashed changes to the working directory without removing them from the stashgit stash apply n
: reapply stashed changes saved at indexn
.git stash drop n
: drop/delete stashed changes saved at indexn
.git stash clear
: clear all stashed changes.
Discard unstaged changes made to <file>
and restore its state to the most recent commit. You can specify multiple files git restore <file1> <file2>
or all files git restore .
.
You can also remove staged changes by using the --staged
option.
Reset the current branch to a particular commit and keep the changes in the working directory. If you'd like to discard the changes, simply use the --hard
option.
Warning: any changes you discard with the --hard
option are permanently removed, so this should be used sparingly.
Create a new commit to undo the changes from an earlier commit.
To undo the effects of a revert, you can revert the revert commit. If there have been changes since the revert commit, you'll have to resolve conflicts.
This is the command to use—instead of git reset
—when you want to undo changes that have already been shared with other collaborators on the same project.
If you use git reset
to undo changes that have already been shared, you'll have to force push your changes to the remote repository. This will overwrite the history of the remote repository and could potentially cause problems for others.
GitHub, BitBucket and similar websites make it easy to share code and collaborate on projects. Git has several commands to make this possible.
Use this to clone an existing repository hosted on GitHub or a similar website. When using this command, the remote is set automatically for you.
Sometimes you create a new local repository and then you want to add a "remote" repository to push changes to.
"Origin" is a typical name for a remote repository. For example: git remote add origin https://github.com/yourUsername/yourRepoName.git
.
- To show a list of remotes:
git remote
- To show a list of remotes with URLs:
git remote -v
- To rename an existing remote:
git remote rename <old-name> <new-name>
- To remove a remote:
git remote remove <name>
Push committed changes from your local branch to your remote branch. If no arguments are given, <remote>
defaults to "origin" and <branch>
defaults to the current branch. To push all branches, use the --all
option.
- To show a list of remote branches:
git branch -r
. - To show a list of all branches, including remote branches:
git branch -a
. - To create a local branch from a remote branch:
git switch <branch>
.
Get the latest changes from the remote repository without merging them locally. If no arguments are given, <remote>
defaults to "origin" and <branch>
defaults to the current branch.
Fetch changes then merge them locally. This is equivalent to running git fetch
then running git merge
. With no arguments, <remote>
defaults to "origin" and <branch>
defaults to currently checked out branch.
git rebase
can be used in two ways.
As an alternative to git merge
As an example, let's suppose that you created a new branch, <feature>
, based on main
. While you were working on your new feature, new changes have been added to the main
branch. You want to pull these changes and merge them. But you can use rebase
in this case.
First, switch to your branch: git switch <feature>
. Now run git rebase main
to rebase your branch commits on the latest changes from main
. This command will create new commits to make a linear commit history but these new commits will keep their original metadata, such as author name and date.
Warning: do not rebase commits that you've already shared with other team members. This should only be used locally on changes you haven't shared yet. Otherwise, you'll have to force push your changes to the remote repository which will overwrite the history of the remote repository and cause problems much like what happens when you use git reset
instead of git revert
.
As a way to clean up your commit history
Do you want to delete, squash, rename, or reorder your commits before sharing them? Use interactive rebase for that.
git rebase -i HEAD~n
Running this command will launch your default editor with instructions on what each command does. You must specify how far back you want to go in your commit history by specifying HEAD~n
.
Note: git rebase
uses git cherry-pick
behind the scenes and that might mean that you’ll get more conflicts than when using a regular git merge
.