Git

Command

Notes

  • Pull

    • merging

      git pull --merge

      If you pull remote changes with the flag --merge, which is also the default, then your local changes are merged with the remote changes. This results in a merge commit that points to the latest local commit and the latest remote commit.

  • Merge

    • Make sure there was no uncommitted changes before the merge started

    • A fast-forward merge can only be done when the most recent commit on the target branch is an ancestor in the source branch (i.e. the commit at the tip of the receiving branch (main) must appear somewhere in the branch providing the new commits (feat/x) ).

  • Log

    git log

    • --oneline

      Display log in compact one-line format

    • --branches

      Specifiy the branch, current branch if omitted

Revisions

  • <rev>

    Include commits that are reachable from <rev> (i.e. <rev> and its ancestors).

  • ^<rev>

    Exclude commits that are reachable from <rev> (i.e. <rev> and its ancestors).

  • <rev1>..<rev2>

    Include commits that are reachable from <rev2> but exclude those that are reachable from <rev1>. When either <rev1> or <rev2> is omitted, it defaults to HEAD.

  • <rev1>...<rev2>

    Include commits that are reachable from either <rev1> or <rev2> but exclude those that are reachable from both. When either <rev1> or <rev2> is omitted, it defaults to HEAD.

  • <rev>^@, e.g. HEAD^@

    A suffix ^ followed by @ is the same as listing all parents of <rev> (meaning, include anything reachable from its parents, but not the commit itself).

  • <rev>^!, e.g. HEAD^!

    A suffix ^ followed by ! is the same as giving commit <rev> and all its parents prefixed with ^ to exclude them (and their ancestors).

  • <rev>^-<n>, e.g. HEAD^-, HEAD^-2

    Equivalent to <rev>^<n>..<rev>, with <n> = 1 if not given.

G   H   I   J
 \ /     \ /
  D   E   F
   \  |  / \
    \ | /   |
     \|/    |
      B     C
       \   /
        \ /
         A
A =      = A^0
B = A^   = A^1     = A~1
C =      = A^2
D = A^^  = A^1^1   = A~2
E = B^2  = A^^2
F = B^3  = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2  = B^^2    = A^^^2  = A~2^2
I = F^   = B^3^    = A^^3^
J = F^2  = B^3^2   = A^^3^2
ArgsExpanded argumentsSelected commits
DG H D
D FG H I J D F
^G DH D
^D BE I J F B
^D B CE I J F B C
CI J F C
B..C^B CC
B...CB ^F CG H D E B C
B^-B^..B
^B^1 BE I J F B
C^@C^1
FI J F
B^@B^1 B^2 B^3
D E FD G H E F I J
C^!C ^C^@
C ^C^1
C ^FC
B^!B ^B^@
B ^B^1 ^B^2 ^B^3
B ^D ^E ^FB
F^! DF ^I ^J DG H D F

Dotted Range Notations

  • <rev1>..<rev2>

    Examples

    • g log origin..HEAD

      What did I do since I forked from the origin branch?

    • g log HEAD..origin

      What did the origin do since I forked from them?

Cheatsheet

Run Git command from another directory

git -C $directory $command

Check out a specific commit to the working copy in detached HEAD state

  • Notes

    • To keep the changes in detached HEAD state: switching to a new branch
    • To discard the changes in detached HEAD state: switching to an existing branch
  • git checkout <COMMIT_ID>

    or

  • git switch -d <COMMIT_ID> (>= 2.23)

Return to HEAD of previous branch

git switch -` (**>= 2.23**) or a specific branch `git switch <branch_name>

Update and replace the most recent commit

  • git commit --amend (--no-edit / -m <message>)

    • --amend

      The latest commit will be replaced by the new commit.

    • --no-edit

      Keep the same commit message, or provide a new commit message with -m

Commit changes of a specific file or directory

git commit -m <commit_message> -- <file/dir>

Log

Show log of a single file

git log --follow -- <file>

Shows the log of the specified file in spite of renaming

Show log of a single file with diff

git log --follow -p -- <file>

Shows the log of the specified file in spite of renaming, with diff

Show log of most recent commits

g log -$number

e.g. g log -10

Show log of changed file names

g log --name-status

Show summary of log

g shortlog

Show log of commits in hash

g log --format=%H

Show logs with full hashes

g log --no-abbrev

Search log by commit message matching the specified regex

g log --grep="$regex"

Search log by patch text matching the specified regex

  • g log -E -G<regex> --name-status --oneline --source --all

    Use this to list files with contents matching the given regex

  • g log -E -G<regex> --name-status --oneline --source --all | egrep 'refs/.+?\s|$'

Search log by date

  • g log --since 2022-01-01

  • g log --until 2022-01-01

Search log by author

g log --author $author

Use a regex to match author name(s)

Diff the log between local branch and another branch

Recommended before git pull

  • Show diff of each commit

    git log HEAD..origin/master
  • Show commits only

    git log --oneline --graph HEAD..origin/master
  • Show a single combined diff of all commits

    git diff HEAD..origin/master
  • Resources

List all revisions of a file

g rev-list --all -- $file

Show all diff of a file

g log -p -- $file

List all files changed in a commit

g show --oneline --name-status $commit_id

View a file of a specific commit

g show $commit_id:$file

Search file contents by regex pattern in all tracked files

g grep --heading --break -C2 -E "$regex" $(g log -G'direnv' --format=%H)

Use Git log to narrow down commits range

Change remote repo URL

  • git remote -v
  • git remote set-url origin <url>

Stash

  • Work as a stack, FILO

  • Default stash name

    when no <stash> is given, stash@{0} is assumed, otherwise <stash> must be a reference of the form stash@{<revision>}.

Save working copy changes to a new stash with the default name

g stash (push)

Show existing stashed changes

g stash list

Show the changes recorded in the stash entry as a diffstat

  • as diffstat

    g stash show
  • as diff

    g stash show -p

Remove everything in the stash

g stash clear

Apply the most recent pushed stashed changes to working copy

g stash (pop | apply)

Interactively select each hunk (consecutive lines of change in a file) you want to stash

g stash push --patch

Patch

Create a patch file for staged changes

g diff --staged > $patch_file

Create a patch file for a single commit relative to the current branch

g format-patch -1 $commit_id

Create one patch file for each commit relative to a specific branch

g format-patch $branch_name

Create one patch file for all commits consolidated relative to a specific branch

g format-patch $branch_name --stdout > $patch_file

Apply a patch file without committing

g apply $patch_file

Apply a patch file and commit

g am $patch_file

Abort on supplying commit message in Vim

Use :cq to make Vim quit with an error code, and Git will abort the operation.

Revert committed changes

g revert $commit_id

Git will apply changes opposite to the specified commit to reverse the effects by creating a new commit.

More useful on a public branch, as it doesn't alter the existing commit history.

Delete unversioned directories and files

To remove directories

g clean -fd

To remove ignored files

g clean -fX

To remove ignored and non-ignored files

g clean -fx

To remove ignored and non-ignored files interactively

g clean -id

Dry run cleaning unversioned files

g clean -n

Remove a file from repository but keeping local copy

g rm --cached $file

Merge

Check if a fast-foward merge is possible

g merge-base --is-ancestor $(gb --show-current) $branch_to_merge_from

Merge a topic branch with squashed commits

g merge --squash $branch_name
  • All changes will be staged in working copy under the current branch, therefore still need to be committed.
  • Use git commit will put all commit messages in the draft for editing.

Dry-run a merge

  1. git merge --no-commit --no-ff <branch>

    or

    gm --no-commit --no-ff <branch>

    Changes stay in index

  2. git merge --abort

    Reset the index

Choose a merge strategy when merging

Abort a merge process

  • git reset --merge
  • git merge --abort

Merge an upstream repository into your fork

  • Retrieve a local copy of upstream branch to be merged

    g remote add upstream <upstream_git_url>
    g fetch upstream <branch_source>
  • Switch to the branch you want to merge the changes into

    g switch <branch_target>
  • Merge the changes from upstream repo

    g merge upstream/<branch_source>

    or

    g merge '@{u}'

Rebase

Rebase a branch interactively

g rebase -i $branch

Rebase while pulling changes

g pull --rebase

If you pull remote changes with the flag --rebase, then your local changes are reapplied on top of the remote changes.

Link an existing local Git repo to a remote repo

g remote add origin $repo_url
g push -f origin $branch

Convert a short hash to a full hash

g rev-parse $short_hash

Show the name of current branch

g rev-parse --abbrev-ref HEAD

or 2.22+

g branch --show-current

Show the absolute path of the top-level directory of the working tree

g rev-parse --show-toplevel

Create a tag with message

g tag -a $tag -m $message

If message omitted, editor will be opened to input message.

Create a local branch with the specified remote tracking branch

g checkout -b $new_branch $remote_branch

or

g checkout -b $new_branch $remote_branch

Show remote tracking branches for all local branches

g branch -vv

Clone a branch or tag

git clone --depth 1 --branch $tag_name $repo_url

using --depth 1 to only download one commit

Push local tag(s) to remote

git push $remote $tag

Push the specified local tag

or

git push --tags

Push all local tags

Fetch a single tag from remote

g fetch $remote tag $tag_name --no-tags

Fetch all tags from remote

g fetch [$remote] --tags

Delete a remote tag

g push --delete $remote $tag_name

Delete all remote tags

g push --delete $remote $(git tag -l)

List all local tags

g tag -l

List local tags with pattern

g tag -l "$regex"

e.g. g tag -l "v*"

Checkout a local tag

g checkout $tag_name

Checkout a local tag into a branch

g checkout tags/$tag_name -b $branch_name

Delete a local tag

g tag -d $tag_name

Delete all local tags

g tag -d $(git tag -l)

Delete a local branch

g branch -d $localBranchName

Delete a remote branch

g push origin --delete $remoteBranchName

Unstage a file and keep local changes

  • git restore --staged <FILE>
  • git reset <FILE>

Unstage a file and discard local changes

g restore $file

Delete all commits since the specified commit

git reset --hard $commit_id
  • Discard all changes (both uncommited and committed) since the specified COMMIT ID
  • can be used to reset unfinished merge

Delete the last commit from history

git reset --hard HEAD~1

Restore a historic version of a file

git restore --source $commit_id $file

Recover a lost commit

  • git reflog

    This command displays all HEAD changes, so you can get any lost COMMIT_ID.

    And use the COMMIT_ID to recover any changes by git restore -s <COMMIT_ID> <DIR/FILE>

Config

List Git config (globally or locally)

git config -l [--global|--local]

Display Git config variables

man git config

Display remote repo URL

git config remote.origin.url

Turn off oh-my-zsh repo status

  • git config --add oh-my-zsh.hide-status 1
  • git config --add oh-my-zsh.hide-dirty 1

Check if a file is being ignored

  • git check-ignore -v <file1 [file2]...>

    If ignored, output would show the matched pattern with line number in Git ignore file.

    If not ignored, no output would display.

Ignore a directory

  • Add the directory name to .gitignore followed by a slash

    directory-to-ignore/

List versioned files

git ls-files -v <file1 [file2]...>

List modified files

git ls-files -m <file1 [file2]...>

Rename a local branch

# Switch to the branch to be renamed
git switch $branch_to_be_renamed
 
# Rename the branch
git branch -m $new_branch_name

Cherry-pick commits from another branch

Update from remote upstream branch

git stash
git pull --rebase
git stash pop

Submodule

Submodule - Add a submodule

  • g submodule add -b <branch> <repo-URL> <folder-name>

  • Submodule added before

    fatal: A git directory for 'playground-messaging-http-simulator' is found locally with remote(s):
      origin        git@gitlab.com:playground-messaging/playground-messaging-http-simulator.git
    If you want to reuse this local git directory instead of cloning again from
      git@gitlab.com:playground-messaging/playground-messaging-http-simulator.git
    use the '--force' option. If the local git directory is not the correct repo
    or you are unsure what this means choose another name with the '--name' option.

    In this case, use --force option, you will see something similar to:

    Reactivating local git directory for submodule 'playground-messaging-http-simulator'

Submodule - Remove a submodule

  • Steps

    1. Remove submodule directory under parent repo directory: rm -rf <submodule-dir>
    2. Remove submodule directory from index: g rm --cached <submodule-dir>
    3. Remove submodule directory under .git directory: rm -rf .git/modules/<submodule-name>
    4. Remove submodule entry with name <submodule-name> from file .git/config
    5. Remove submodule entry with name <submodule-name> from file .gitmodules under parent repo directory.
  • Stack Overflow - Git submodule add: "a git directory is found locally" issue (opens in a new tab)

Submodule - Commit changes when a submodule is changed

  • When a submodule has new commits, the parent repo needs to be updated with the latest commit.

LFS

LFS - Initialize Git LFS

git lfs install

LFS - Track files of a pattern with Git LFS

  • Always execute the "track" command from the root of your project. The reason for this advice is that patterns are relative to the folder in which you ran the command.
  • The $file_pattern is case sensitive.
  • The $file_pattern will be added to .gitattributes file.
  • Use git lfs status and git lfs ls-files to verify if the pattern is working as expected.
git lfs track $file_pattern

LFS - List all files tracked by Git LFS

git lfs ls-files -adls
# Output
filepath: media/image/photo/select1.JPG
    size: 5456070
checkout: true
download: true
     oid: sha256 817b09f77dc45ba9351daab70e5735a41af7d3b60524690ab314e0c1ac069cf3
 version: https://git-lfs.github.com/spec/v1

LFS - Get Git LFS environment

git lfs env
# Output
git-lfs/3.2.0 (GitHub; linux amd64; go 1.19.4)
git version 2.43.0
 
Endpoint=https://gitlab.com/some-repo.git/info/lfs (auth=none)
  SSH=git@gitlab.com:some-repo.git
LocalWorkingDir=/local-repo-dir
LocalGitDir=/local-repo-dir/.git
LocalGitStorageDir=/local-repo-dir/.git
LocalMediaDir=/local-repo-dir/.git/lfs/objects
LocalReferenceDirs=
TempDir=/local-repo-dir/.git/lfs/tmp
ConcurrentTransfers=8
TusTransfers=false
BasicTransfersOnly=false
SkipDownloadErrors=false
FetchRecentAlways=false
FetchRecentRefsDays=7
FetchRecentCommitsDays=0
FetchRecentRefsIncludeRemotes=true
PruneOffsetDays=3
PruneVerifyRemoteAlways=false
PruneRemoteName=origin
LfsStorageDir=/local-repo-dir/.git/lfs
AccessDownload=none
AccessUpload=none
DownloadTransfers=basic,lfs-standalone-file,ssh
UploadTransfers=basic,lfs-standalone-file,ssh
GIT_EXEC_PATH=/git-installation/libexec/git-core
git config filter.lfs.process = "git-lfs filter-process"
git config filter.lfs.smudge = "git-lfs smudge -- %f"
git config filter.lfs.clean = "git-lfs clean -- %f"

LFS - Get Git LFS status

g lfs status
# Output
On branch develop
Objects to be pushed to origin/develop:
 
Objects to be committed:
 
        .gitattributes (Git: ed72c32 -> Git: fbc4da5)
        media/image/photo/_DSC9253.JPG (LFS: b737ca9)
 
Objects not staged for commit:

GitHub CLI

List all Gists

gh gist list -L 50

Edit a Gist locally

gh gist edit $ID

Add a local file to a Gist

gh gist edit $ID -a $file

Create a Gist based on a local file

gh gist create -f $file_name_on_Gist -d $description < $local_file

git-extras

Gitflow

  • develop branch to include all feature development

    main -> develop

  • Release preparation

    develop -> release

  • New feature

    develop -> feature

  • Bug fix

    main -> hotfix

  • When a feature is complete, it is merged into develop

  • When the release branch is done, it is merged into develop and main

  • Once the hotfix is complete

    • hotfix => develop
    • hotfix => main

Commit message management

Message format

[$Tag] $Subject

Message tags

TagDescription
featA new feature
fixA bug fix
docsDocumentation only changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance
testAdding missing tests or correcting existing tests
plumbingChanges to the build process or auxiliary tools and libraries such as documentation generation

Resources

Resources - Line endings