Git
Everyday commands
git status
git switch BRANCH_NAME # modern replacement for `git checkout BRANCH_NAME`
git switch -c BRANCH_NAME # create and switch in one step
git log --oneline --graph --decorate -20
git diff # unstaged changes
git diff --staged # what a commit would contain right now
Sync a fork with its upstream
Add the upstream remote once, then repeat the fetch/merge whenever the fork falls behind.
# One-time setup
git remote add upstream UPSTREAM_URL
git remote -v
# Every time you need to catch up
git fetch upstream --tags
git switch main
git merge upstream/main
git push origin main
Two things to watch for:
--tags(plural) is the correct flag;--tagis not agit fetchoption and the command fails.- The upstream default branch is not always
main. Older repositories still usemaster, in which case mergeupstream/master. Confirm withgit remote show upstreamand read theHEAD branchline.
Prefer git rebase upstream/main over merge when the fork has local commits
that have not been pushed anywhere yet — it keeps the history linear. Never
rebase commits that others have already pulled.
Branch from a tag
Useful when a release needs a patch but main has already moved on.
git fetch --tags
git tag # list tags; `git tag -l "v1.*"` to filter
git switch -c BRANCH_NAME TAG_NAME # branch based on the tag
git push -u origin BRANCH_NAME # -u records the upstream for later `git push`
Delete a branch
git branch -d BRANCH_NAME # refuses if the branch is not merged
git branch -D BRANCH_NAME # force delete, discards unmerged commits
git push origin -d BRANCH_NAME # delete the remote branch
git fetch --prune # drop remote-tracking refs that no longer exist
git branch -d only checks whether the branch is merged into the branch you are
currently on, so switch to main first or the check gives a false negative.
Undo
git restore FILE # discard unstaged changes to a file
git restore --staged FILE # unstage but keep the edit
git commit --amend # rewrite the last commit (only if unpushed)
git revert COMMIT # new commit that undoes COMMIT; safe on shared branches
git reset --hard COMMIT # destructive: moves the branch and discards changes
git reflog lists where HEAD has pointed recently and is the way back from a
bad reset or rebase. Entries expire after 90 days by default.
Stash work in progress
git stash push -m "MESSAGE" # add -u to include untracked files
git stash list
git stash pop # apply the newest entry and drop it
git stash apply stash@{1} # apply an older entry and keep it
Intro
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
Setup and Config
Enable ssh-agent if private key with passphrase.
Variable
# Signing key.
USER_EMAIL="[email protected]"
USER_NAME="User Name"
SIGNING_KEY="0x7EF6B94D09F6AAAA"
BRANCH_NAME="next-version"
GIT_LOG_MESSAGE="New commit."
GIT_REPO_URL="https://github.com/user/repo.git"
Config
# Scope: --system root, --global user, --local repo.
# Config user in local scope.
git config --local user.email $USER_EMAIL
git config --local user.name $USER_NAME # "Rojar Smith"
git config --local user.signingkey $SIGNING_KEY
# Credential.
git config --local credential.helper store # save password
git config --local --unset credential.helper # clean
# Show config.
git config --local --list
Basic Snapshotting
add
# --all, -A, Add all files.
git add -A
status
# Show the working tree status.
git status
Branching and Merging
branch
# Create branch.
git branch $BRANCH_NAME
# List branch.
git branch -a
# Rename branch.
git branch -m $BRANCH_NAME
# Delete local branch.
git branch -d $BRANCH_NAME
checkout
# Switch to.
git checkout $BRANCH_NAME
merge
# Merge branches fixes and enhancements on top of the current branch, making an octopus merge.
git merge fixes enhancements
# Merge branch obsolete into the current branch, using ours merge strategy.
git merge -s ours obsolete
git merge --continue
tag
# -a, --annotate
# Make an unsigned, annotated tag object
# -s, --sign
# Make a GPG-signed tag, using the default e-mail address’s key.
# -u <key-id>, --local-user=<key-id>
# Make a GPG-signed tag, using the given key.
git tag -u $SIGNING_KEY -s v1.0 -m $GIT_LOG_MESSAGE
git tag v1.0 $SIGNING_KEY -a -m $GIT_LOG_MESSAGE
Sharing and Updating Projects
push
# Push tag to remote.
git push origin --tags
# Delete remote branch.
git push origin --delete $BRANCH_NAME
# Push branch.
git push origin -u $BRANCH_NAME
Message Tag
fix add change refactor remove revert merge update hotfix disable upgrade
Scenario
Init with existed
git init
git add -A -f # -f force add, disable gitignore
git commit -m "Initial commit"
git remote add origin $GIT_REPO_URL
git push -f origin main
Clean. Revert to last checkout
git reset --hard
git clean -fdx
Revert a single file to a previous version
git log
git checkout <commit> path/to/file
Update original repo
git remote add upstream https://github.com/eclipse/hawkbit.git
git remote -v
git pull upstream master --tag # with tag
error: pathspec ‘XXX‘ did not match any file(s) known to git
git add -A -f
Remote host identification has changed
# remove with:
ssh-keygen -f "/root/.ssh/known_hosts" -R "github.com"
How to check out tag
# git clone full repository
git tag -l # list all tag
git checkout TAG_NAME
git describe --tags # Current tag
Modify commit message
git commit --amend -m "Welcome To Facebook"
# To one step ahead of target COMMIT_HASH
git rebase -i COMMIT_HASH
- pick: keep the commit
- reword: Keep the commit, but I need to modify the commit's Message
- edit: keep the commit, but I want to stop and modify the commit (including modifying files)
- squash: merge this commit with the previous commit
- fixup: merge this commit with the previous commit, but I don’t want to keep the comment information of this commit
- exec: execute shell command
- drop: discard this commit
Modify First Commit Message
git rebase -i --root
Modify Author
git rebase -i HEAD~12
# Mod `pick` to `edit`
# --- Multi times start ---
git commit --amend --author="Rojar Smith <[email protected]>"
git rebase --continue
# --- Multi times end ---
Miscellaneous
## generate
ssh-keygen
(RROJ=PROJECT; IAM=$(whoami); ssh-keygen -t rsa -C "${IAM}@${HOSTNAME}-${RROJ}" -f ~/.ssh/deploy_keys/id_rsa_${RROJ})
ssh-keygen -t rsa -C 'root@vm5' -f ~/.ssh/id_rsa_github
## Using multi key
vi ~/.ssh/config
Host <host_alias>
HostName <hostname_or_ip>
IdentityFile <private_key_path>
# GitHub company
Host github-rs-company
HostName github.com
IdentityFile ~/.ssh/id_rsa_rs_company
git clone git@github-rs-company:rs-company/compony-project.git
# Git ignore file mode (chmod) changes
git config --global core.fileMode false
# Remove last commit
git reset --hard
git clean -f
git reset --hard HEAD^
git push origin -f
git reset main^
# Update tag from original repo
git pull upstream master --tag
# Show diff
git diff
git diff > modify.diff
# Check patch/diff, must Unix(LF), UTF-8, last return line or EOF.
git apply --check path/to/xxx.patch
git apply --check path/to/xxx.diff
# Get commit list between tags in git
git log --pretty=oneline tagA...tagB (i.e. three dots)
# If you just wanted commits reachable from tagB but not tagA:
git log --pretty=oneline tagA..tagB (i.e. two dots)
or
git log --pretty=oneline ^tagA tagB
# Apply patch/diff
git apply path/to/xxx.patch
git apply path/to/xxx.diff
# Push target commit to upstream
git fetch --all
git checkout -b new-branch-name upstream/master
git cherry-pick commit-hash1
git cherry-pick commit-hash2
git push -u origin new-branch-name
# Clone with username/password
git pull https://user:[email protected]/name/repo.git master
# Can a lightweight tag be converted to an annotated tag?
# A lightweight tag is just a 'ref' that points at that commit. You can force-create a new annotated tag on top of the old tag:
git tag -a -f <tagname> <tagname>
As of Git v1.8.2, you need to use --force to replace any tags on a remote with git push, even if you are replacing a lightweight tag with something that is effectively a fast-forward or a true tag object pointing at the same commit as the existing tag reference.
git push --force origin <tagname>
# Change Git remote server url, git remote set-url
git remote set-url origin https://github.com/USERNAME/OTHERREPOSITORY.git
# Did not mod any tracking code but show: You are not currently on a branch.
git pull
git pull origin master
# Your branch is ahead by x commits
git checkout master
git rebase upstream/master
git push origin master -f
# The Solution for a New Branch
git branch my-branch
git reset --hard HEAD~3
Or
git reset --keep HEAD~3
git checkout my-branch
# The Solution for an Existing Branch
git checkout my-branch
git merge master
git checkout master
git reset --hard HEAD~4
Or
git reset --keep HEAD~4
git checkout my-branch
git branch -M main
# Sparse Checkout
mkdir folder
cd folder
git init
git remote add origin [email protected]:rojarsmith/bitdove-hawkbit.git
git config core.sparsecheckout true
echo "docs/*" >> .git/info/sparse-checkout
git pull origin bitdove