Git Cheatsheet
"Repos" and "Areas"
- the working copy: where you are editing and building (not committed yet to your repo).
- staging area / index
- local repo: your own repository with your own commit history.
- local "cached" copy of a remote repository.
- remote repo.
The staging area (index) is shared by all branches; committed code (in local repository) is per branch. So it doesn't matter what branch you're on when you add a file.
Working Tree
- Discard changes:
git restore
.
Working Tree <=> Staging Area
- Working Tree => Staging Area:
git add
/git rm
- Staging Area => Working Tree (Unstage):
git reset HEAD
/git restore --staged <file>
Staging Area <=> Local Repo
- Staging Area => Local Repo:
git commit
- Local Repo => Staging Area / Working Tree:
git reset --soft HEAD^
(files are still staged)git reset HEAD^
(files are no longer stage, but still in the working area)git reset --hard HEAD^
(changes are thrown away, not even in the working tree)
Local Repo <=> Remote Repo
- Local Repo => Remote Repo:
git push
- Remote Repo => Local Repo:
git fetch
Concepts
Branch
In Git, a branch is a pointer to one specific commit, while a commit is a snapshot of your repository at a specific point in time.
# show local branches
$ git branch
# show remote branches
$ git branch -r
# show both local and remote branches
$ git branch -a
HEAD
HEAD
: "Where am I right now?"
Most of the time, HEAD
points to a branch name. HEAD
is synonymous with "the last commit in the current branch."
- attached: in the normal state,
HEAD
is attached to a branch. - detached:
HEAD
is pointing directly to a commit instead of a branch.
Workflow
# Pull updates from remote
$ git pull origin main --rebase
# Commit local changes
$ git add -A
$ git commit --amend
$ git commit --amend --no-edit
# Push
$ git push origin main
Checkout a Remote Branch
$ git fetch
$ git checkout test
or
$ git checkout -b <branch> --track <remote>/<branch>
Diff
$ git diff ...origin
Check what's included in a commit:
$ git diff COMMIT~ COMMIT
Config
Config file location:
- Global:
~/.gitconfig
- System:
/etc/gitconfig
- Local:
/git/config
coding
Set a name
$ git config --global user.name "My Name"
$ git config --local user.name "Foo Bar"
Show the name
$ git config --local user.name
Foo Bar
Tags
# check remote tags
$ git ls-remote --tags origin
# check local tags
$ git tag
$ git tag -l
1.0.0
Checkout a tag:
$ git checkout tags/<tag_name>
$ git checkout tags/1.0.0
Remove a tag:
# remove local tag
$ git tag -d <tag_name>
# remove remote tag
$ git push origin :[tag]
$ git push origin :refs/tags/<tag_name>
Get the latest tag:
$ git fetch; git tag --sort=creatordate | head -1`
Get latest tags of a specific version
If you have CI setup to produce new tags frequently, use this to find the latest 5 tags with a specific version (e.g. 1.10.0
).
$ git tag --sort=creatordate | grep 1.10.0 | tail -5
Add All
To add everything to stage, including additions and removals, use
$ git add -A
Undo commits
$ git reset HEAD~1
Now all the changes done in that commit are unstaged and need to be committed again.
Abandon Changes
Abandon changes and revert your tree to the "clean" state of your current branch. Don't use git revert
, use:
# Go back to HEAD
$ git reset --hard HEAD
# Go back to the commit before HEAD
# i.e. abandon the top most commit
$ git reset --hard HEAD^
# Equivalent to "HEAD^"
$ git reset --hard HEAD~1
# Go back two commits before HEAD
$ git reset --hard HEAD~2
# Go back to origin/HEAD
$ git reset --hard origin/HEAD
To revert changes made to your working copy
$ git restore .
To uncommit but does not revert local changes
$ git reset HEAD~
To uncommit and revert local changes
$ git reset HEAD~ --hard
Reset to a remote branch
$ git fetch origin
$ git reset --hard origin/master
In increasing order of dangerous-ness:
--soft
: movesHEAD
but doesn't touch the staging area or the working tree.--mixed
: movesHEAD
and updates the staging area, but not the working tree.--merge
: movesHEAD
, resets the staging area, and tries to move all the changes in your working tree into the new working tree.--hard
: movesHEAD
and adjusts your staging area and working tree to the newHEAD
, throwing away everything.
Upstream Remote
Add your own fork as origin
remote
$ git remote add origin https://github.com/<your_fork>/<project_name>.git
Add the central repo as upstream
remote
$ git remote add upstream https://github.com/<central_repo>/<project_name>.git
To sync up with upstream
$ git fetch upstream
$ git merge upstream/develop
Or
$ git pull upstream develop
Config global user name and email
Global config is in $HOME/.gitconfig
; per repo config is in .git/config
.
$ git config --global user.name <your_name>
$ git config --global user.email <[email protected]>
Commit in Wrong User/Email
If user
is specified in .git/config
as
[user]
name = your_name
email = [email protected]
however if git is using the wrong user settings after commit
Author: your_name <[email protected]>
Check if you have these settings in env
GIT_AUTHOR_NAME
GIT_AUTHOR_EMAIL
GIT_COMMITER_NAME
GIT_COMMITER_EMAIL
Remove those then try again
How do I make Git ignore file mode (chmod) changes?
$ git config core.fileMode false
or
$ git config --global core.filemode false
or open .git/config
and modify the core section.
Force Push to head
Override the remote HEAD
$ git push origin +HEAD^:master
Show Remote Logs
$ git log origin/main
Cherrypick
cherry-pick
: pick one change from anywhere in the repository and will apply it on your local branch.
export BRANCH=<branch to cherrypick>
git fetch origin
git checkout ${BRANCH}
git cherry-pick ${SHA}
git push origin HEAD:refs/for/${BRANCH}
git apply
# generate diff
git diff filename
git diff > my_patch.patch
# apply diff; will fail with conflicts
git apply my_patch.patch
# apply diff; ask to resolve conflicts
git apply --3way my_patch.patch
Remove all untracked files
git clean -f -x
. The -x
option removes all untracked files, including ignored files.
More clean
commands:
git clean -n
: dry run.git clean -f
: force untracked file deletion.git clean -f -d
: remove untracked directories.git clean -f -x
: remove untracked.gitignore
files.-i
: do an interactive git clean.
Get the latest tag
git describe
command finds the most recent reachable tag from a commit.
git describe --abbrev=0
# Get the latest tag of a certain version
git tag --sort=creatordate | grep 1.10.0 | tail -1
1.10.0-xxxx.1783
git fetch vs git pull
git pull
= git fetch
+ git merge
git fetch
: remote repo to local repo.git merge
: local repo to working directory.git pull
: remote repo to local repo AND working directory.
check if local repo is up to date
$ git fetch --dry-run
Tags:
git fetch
by default updates tagsgit pull
by default does NOT update tags, usegit pull --tags
git checkout vs git switch
git switch -c <branch name>
is equivalent to git checkout -b <branch name>
.
To replace two common uses of git checkout
: git switch
to switch to a new branch after creating it if necessary, and git restore
to restore changes from a given commit.
git rebase vs git merge
Both are designed to integrate changes from one branch into another branch.
git merge feature main
will create a new "merge commit" in thefeature
branch to incorporate chagnes in themain
branch.git rebase main
will move thefeature
on top of themain
branch (incorporating all new commits in themain
branch.) Much cleaner project history.
Patch a CL: checkout vs branch vs cherry-pick vs pull
before patch:
local_commit
origin/HEAD
Checkout and create a new branch
$ git fetch foo bar && git checkout -b baz FETCH_HEAD
You will be in a new baz
branch
Checkout
$ git fetch foo bar && git checkout FETCH_HEAD
You will be in a HEAD detached at FETCH_HEAD
branch (use git checkout -b baz
to create a new baz
branch)
Cherry-pick
$ git fetch foo bar && git cherry-pick FETCH_HEAD
You will stay in the main
branch, check git log
:
remote_commit
local_commit
origin/HEAD
Pull and rebase
$ git pull foo bar --rebase
You will stay in the main
branch, check git log
:
local_commit
remote_commit
origin/HEAD
Pull and merge
$ git pull foo bar --no-rebase (i.e. merge)
You will stay in the main
branch, check git log
:
merge_commit
local_commit
remote_commit
origin/HEAD
https vs ssh
You can use either https or ssh to connect to remote repo:
- https:
https://github.com/foo/bar.git
- it will ask for your username and password when you try to connect remote repo
- git:
[email protected]:foo/bar.git
- it uses ssh keys to verify you identity.
- Copy and paste your public key(
~/.ssh/id_rsa.pub
) to Github account - Specify the private key in
~/.ssh/config
as
- Copy and paste your public key(
Host heroku.com
HostName heroku.com
IdentityFile /Users/username/.ssh/heroku_key
To check your remote
$ git remote show origin
* remote origin
Fetch URL: https://github.com/foo/bar.git
Push URL: https://github.com/foo/bar.git
HEAD branch: master
How to fix "detached HEAD"?
Option 1: discard changes and go back. These 2 are equivalent.
$ git switch <branch-name>
$ git checkout <branch-name>
Option 2: create a new branch at the current HEAD.
$ git checkout -b <new-branch-name>
macOS Uses Wrong Git Account
If multiple GitHub accounts are logged in using Mac OS X, the account info might be stroed by Keychain Access. Maven release plugin may fails for using wrong Github account.
[ERROR] Provider message:
[ERROR] The git-push command failed.
[ERROR] Command output:
[ERROR] remote: Permission to <...>/<...>.git denied to <...>.
[ERROR] fatal: unable to access 'https://github.com/<...>/<...>.git/': The requested URL returned error: 403
Solution: find Keychain Access in spotlight or by
Applications->Utilities->Keychain Access.app
Search for github
and remove the records.
Show latest changes in multiple folders
for i in {1..8}; do
cd ~/workspace$i
echo "w$i: `git --no-pager log --pretty=oneline -1`"
done
Show current branch name
$ git rev-parse --abbrev-ref HEAD
main