Skip to main content

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; --tag is not a git fetch option and the command fails.
  • The upstream default branch is not always main. Older repositories still use master, in which case merge upstream/master. Confirm with git remote show upstream and read the HEAD branch line.

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