Skip to content

Instantly share code, notes, and snippets.

@spacegoret
Last active November 3, 2025 13:02
Show Gist options
  • Select an option

  • Save spacegoret/3a30f88cda8cc757172849f5198b41da to your computer and use it in GitHub Desktop.

Select an option

Save spacegoret/3a30f88cda8cc757172849f5198b41da to your computer and use it in GitHub Desktop.

Git reference & tips

http://ndpsoftware.com/git-cheatsheet.html

http://www.sbf5.com/~cduan/technical/git/

Vocabulary and concepts

Stash Working directory Index Local repository Remote repository
Workspace Staging area Local tree Upstream repository
Working tree

Reference to primary remote repository: origin

Main branch: master

Current head = latest commit of the current branch: HEAD

Parent commit of the current head: HEAD^

Bare repository: Repository with no working files, index or stash.

Commit objects

A project always has one commit object with no parents. This is the first commit made to the project repository.

Based on the above, you can visualize a repository as a directed acyclic graph of commit objects, with pointers to parent commits always pointing backwards in time, ultimately to the first commit. Starting from any commit, you can walk along the tree by parent commits to see the history of changes that led to that commit.

The idea behind Git is that version control is all about manipulating this graph of commits. Whenever you want to perform some operation to query or manipulate the repository, you should be thinking, “how do I want to query or manipulate the graph of commits?”

Heads

A head is simply a reference to a commit object.

A repository can contain any number of heads.

By default, there is a head in every repository called main (previously master).

At any given time, one head is selected as the “current head.” This head is aliased to HEAD, always in capitals. The current state of the filesystem tree matches HEAD.

Branches

Git is a directed acyclic graph of commits with pointers (or references) to the tip of the branches.

The terms “branch” and “head” are nearly synonymous in Git. Every branch is represented by one head, and every head represents one branch. Sometimes, “branch” will be used to refer to a head and the entire history of ancestor commits preceding that head, whereas “head” will be used to refer exclusively to a single commit object, the most recent commit in the branch.

Different types of branches:

  • Remote branches
  • Local branches
    • Ordinary local branches
    • Local tracking branches (allow to push changes to remote branches)
    • Remote tracking branches (remote repository cache) = remote heads

A branch that tracks a remote branch retains an internal reference to the remote branch. This is a convenience that allows you to avoid typing the name of the remote branch in many situations.

Detached HEAD

Any checkout of a commit that is not at the tip of one of your branches will get you a detached HEAD. When HEAD is detached, commits work like normal, except no named branch gets updated. You can think of this as an anonymous branch. The commits you create will be dangling commits.

If you switch to another branch when you’re in a detached HEAD state, the commits you made in the anonymous branch will only be accessible through individual commit hashes and those commits will not appear when you use “git log”. You can see them with the “git reflog” command.

You should use the “git branch” command. This will create a new head/branch and make the tip of your commits accessible through this new head. “HEAD” will then be an alias to this new head/branch.

For example, if you checkout a "remote branch" without tracking it first, you can end up with a detached HEAD.

Configuration levels

ℹ️

Git configuration can be set at 3 levels:

  • local (repository clone level)
    • default or --local
  • global (user level)
    • --global
  • system (machine level)
    • *–-system*

Commands

git init : Create a new local repository

git clone [remote-repository] [directory] :

  • Create a new directory [directory]
  • Initialize a local repository in [directory]
  • Create a remote repository reference named origin and set it to [remote-repository]
  • Create remote heads (remote branches) in the local repository tracking the branches from the remote repository
  • Create a local branch tracking the currently active remote branch (usually master)

Note: “clone” make a complete copy of the remote repository, but does not create local branches for all branches of the remote repository

Show URL of remotes

git remote -v

git status : List modified files between your working directory and the local repository

git config --global alias.st status

git diff : Show differences between your working directory and the index

git diff --cached : Show differences between the index (aka staging area) and the most recent commit

git diff HEAD : Show the differences between your working directory and the most recent commit in the local repository

git config --global diff.tool kdiff3

git config --global difftool.prompt false

git difftool [file]

git add

git add --patch [file] : Display all changes on a hunk-by-hunk basis

git add --interactive

git mv

git rm

git reset HEAD [file] : Remove files from the index (aka staging area)

git reset --hard : Remove files from the index and restore files in the working directory to match the local repository

git commit

git commit -a : Add modified files (not new files) in the working directory to the index then commit

git commit --amend : Change the commit message of the most recent commit

git fetch [remote-repository] :

  • Retrieves the new commit objects in the remote repository (origin by default)
  • Creates and/or updates the remote heads accordingly

git pull :

  • Do a “git fetch
  • Merge the commits from the remote branch that the current local branch is tracking

Note: you may want to rebase to rewrite your commits and make them a fast forward merge

git push [remote-repository] [remote-head] :

  • Add new commit objects to the remote repository
  • Set [remote-head] to point to the same commit that it points to on the pushing repository local branch
  • Updates the corresponding remote head reference

Note 1: If no arguments are given, all the branches that are set up for tracking will be pushed.

Note 2: The push should result in a fast forward merge on the remote repository. If this is not the case, eg. you have rebased the local branch, then you need to use git push --force-with-lease (works with GitHub)

git log : Show the history of the local repository

git log --name-status : Show commit messages and files changed

git log origin/master..HEAD : Show the history of the local repository which has not been pushed to origin/master yet

git show

git ls-files

git blame

Branches

git branch -a List all branches including remote branches

git branch -vv List local branches, with corresponding tracked remote branches

git ls-remote (if you have cloned using --single-branch or --depth)

git branch [branch] : Create a new branch based on HEAD

git checkout [branch] : Set the current head (HEAD) to [branch] and match the working directory accordingly

Note 1: If there is no local branch named [branch] while there is a remote branch with the name [branch], checkout will create a local branch tracking the remote branch.

Note 2: if there are any uncommitted changes when you run git checkout, Git will behave very strangely. The strangeness is predictable and sometimes useful, but it is best to avoid it. Don’t be afraid to commit half-finished work, because you can clean-up commit history later.

git worktree list -v

git worktree add ../[newfolder] [branch]

Diff between branches

Compare the tips of each branch

Filenames only:

git diff --stat --color branch1..branch2

Shows the diff between branch2 and the common ancestor of branch1 and branch2:

git diff --stat --color branch1...branch2

Files content:

git diff branch1..branch2

git diff branch1...branch2

Commits that exist in branch2 but don’t exist in branch1

git log branch1..branch2

Show diff between local branch and remote branch

Filenames only:

git diff --stat origin/master master

Files content:

git diff origin/master master

git diff origin/master...master

git merge [branch] : Merge [branch] into the current branch

Rebase

git rebase [branch] : Incorporate changes from [branch] into the current branch and make the current branch a descendant of [branch].

Rebase is an alternative to merge to prevent “merge” commits (commits with multiple parents).

Typically, if you do “git rebase master”, you will incorporate the changes from master into the current branch. Then, if you were to merge the current branch into master, it would result in a fast-forward commit.

Rebasing works by transferring each local commit to the updated branch one at a time. This means that you catch merge conflicts on a commit-by-commit basis rather than resolving all of them in one massive merge commit.

But rebase rewrite the commit history so it should be reserved to those 2 cases :

  • on private branches

  • when a remote branch has changed between the time you last merged on the local tracking branch and you pushed

    • If you’re on the master branch, git rebase origin/master then git push --force-with-lease (Do NOT use --force as it does not check if changes were made on the remote branch in the meantime, it will overwrite them)
    • cf.

    Git rebase and force push | GitLab

Squash commits

There are several method to squash commits:

  • During interactive rebase
    • it works on a single branch or between 2 branches
    • For example, squash the 5 last commit of the currently checked out branch: git rebase -i HEAD~5
  • git merge --squash
  • In GitHub (& other platforms), when merging a Pull Request

cf. https://www.git-tower.com/learn/git/faq/git-squash

Easily squash commits from a Git feature branch before PR or merge into main branch

Create a new (2nd) branch

git checkout -b new_clean_branch

Apply all changes and squash in 1 commit

git merge --squash original_messy_branch

Commit & push

git commit -m "Squashed changes from original_messy_branch"

git push

Revert last non pushed commit

NB: when you use the --hard flag to undo a merge, any uncommitted change will be reverted

git reset --hard HEAD^

or

git reset --hard HEAD~1

Abort git stash pop in case of merge conflicts

When using the git stash pop command, there can be merge conflicts. You can move to resolve those conflicts or abort the whole process.

To abort the process when a merge conflict has occurred during git stash pop:

git reset --merge

Misc tips

“dubious ownership” error for shared repository hosted with local protocol

cmd & PowerShell

git config --global --add safe.directory *

Git Bash

git config --global --add safe.directory '***'**

.gitconfig

[safe]
	directory = *

Git & GitHub authentication

Protocols & transports

  • https:// or http:// protocol
  • git protocol
    • SSH transport
    • or file transport

Credential types

  • username & regular password
    • may not work if MFA is enforced
  • SSH key
    • per host

    • Client side

      • Generate private & public key pair
      ssh-keygen -t ed25519 -C "[[email protected]](mailto:[email protected])"
      Host github.com
              Hostname github.com
              IdentityFile=/home/user1/.ssh/private-key-file-for-github
      
      • ~/.ssh/private-key-file-for-host
      • ~/.ssh/public-key-file-for-host.pub
      ssh-ed25519 xxxx username1
      
    • GitHub side

      • Account level
        • Profile > Settings > SSH and GPG keys > New SSH keys > Authentication Key
        • Paste public key
      • Deploy keys per repository
        • Repository > Settings > Deploy keys
        • Paste public key
  • GitHub PAT
    • Fine grained, repository scoped (OAuth)
    • Classic
    • GitHub side:
    • Client side
      • username & use GitHub PAT as the password
  • GitHub OAuth App
    • GitHub side:
      • Profile > Settings > Developer Settings > OAuth Apps
      • and Profile > Settings > Applications > Authorized OAuth Apps
  • GitHub Apps
    • GitHub side:
      • Profile > Settings > Developer Settings > GitHub Apps
      • and Profile > Settings > Applications > Authorized GitHub Apps
ℹ️

GitHub will automatically revoke an OAuth token or personal access token when the token hasn't been used in one year

Credentials storage & helpers

Credentials NOT stored

You need to authenticate every time by providing either:

  • a regular password
    • GitHub, GitLab, etc. accounts (HTTPS protocol)
    • SSH account
  • or a PAT
    • HTTPS protocol

By default credentials are read from standard input, but there are settings to determine an application to input credentials:

  • *GIT_ASKPASS* environment variable
  • *core.askPass* configuration variable
  • *SSH_ASKPASS* & SSH_ASKPASS_REQUIRE environment variables

SSH keys

ℹ️

Support built-in Git client

When using the SSH protocol / transport, you can use SSH key pair with the private key stored for example in the user’s ~/.ssh folder.

ℹ️

NB: If the SSH private key is encrypted using a passphrase, the passphrase will need to be provided

In the repository URL

⚠️

Username and password are stored in plain text, so anyone with access to the repository would be able to see them

⚠️

if the password has special characters, they will need to be escaped to prevent the shell from trying to interpret them

  • when cloning
git clone https://<username>:<password>@gitlab.com/group/project.git
git clone https://[PAT]@github.com/[username]/[repository].git
  • in the git configuration file
url=https://<username>:<password>@<code class="language-shell">gitlab.com/group/project.git

git credential cache

ℹ️

Support built-in Git client

The cache credential helper never writes credentials to disk, although the credentials are accessible using Unix sockets. These sockets are protected using file permissions that are limited to the user who stored them, so generally speaking, they are secure.

git config credential.helper cache

An optional timeout argument can be provided, for example 1 day:

git config credential.helper 'cache --timeout=86400'

git credential store

ℹ️

Support built-in Git client

git config credential.helper store

Store the credentials as plain text in a file. File contents are not encrypted, they are protected using file system access controls to the user that created the file.

By default, the file is stored in the user’s home directory: ~/.git-credentials

git credential manager

ℹ️

Built-in Git for Windows & default for HTTPS remotes.

To be installed separately in Linux and macOS.

GCM handles authentication, including MFA, for the most popular Git hosting service providers:

  • GitHub
  • GitLab
  • Azure DevOps
  • Bitbucket

It can provide interactive (graphical or console) prompts to sign in:

  • either with a browser
    • using local browser or a one-time code (aka. device authentication)
    • This will create an OAuth token
    • cf. GitHub side: Profile > Settings > Applications > Authorized OAuth Apps > Git Credential Manager > Revoke (not to be confused with Profile > Settings > Developer Settings > OAuth Apps )
  • or a provided PAT
  • or provided username & password
ℹ️

Git Credential Manager signs you in to GitHub through GitHub’s “expiring user-to-server token” flow.

Expiring user tokens expire after 8 hours. When you receive a new user-to-server access token, the response will also contain a refresh token, which can be exchanged for a new user token and refresh token. Refresh tokens are valid for 6 months.

So the credential GCM actually hands to Git for each HTTPS operation is an 8‑hour OAuth access token; once it expires, GCM silently uses the stored refresh token to get a new one.

Refresh tokens can be thought of like a password of sorts. When you access a web site and don't yet have a session, you login with some sort of credential or token, like a password. This "token" is exchanged for another -- a session token -- that you can use to repeatedly access resources at that web site without having to login again. In the same way, a refresh token can be used to obtain access tokens.

Manual configuration:

git config credential.helper manager

or

git credential-manager configure

Credentials storage:

  • Windows credential manager
    • Only for HTTPS remotes
    • Credential manager > Windows Credentials
  • macOS user’s Keychain
  • Linux
    • Secret Service API / libsecret
    • including GNOME Keyring
  • GPG-encrypted files
  • Memory (git credential cache)
  • Plain text files
  • DPAPI protected files (Windows only)

Select credential store:

  • git configuration
git config --global credential.credentialStore gpg
  • or environment variable
GCM_CREDENTIAL_STORE="wincredman"

GitHub Desktop

ℹ️

Need to install GitHub Desktop

cf. GitHub side: Profile > Settings > Applications > Authorized OAuth Apps > GitHub Desktop

GitHub CLI credential helper

ℹ️

Install GitHub CLI, for example:

apt install gh

GitHub CLI default authentication mode is a web-based browser flow.

After completion, an OAuth token will be created & will be stored:

  • securely in the system credential store
  • if a credential store is not found or there is an issue using it gh will fallback to writing the token to a plain text file

cf. GitHub side: Profile > Settings > Applications > Authorized OAuth Apps > GitHub CLI

See gh auth status for its stored location.

  • Configuration files
    • ~/.config/gh/config.yml
    • ~/.config/gh/hosts.yml
github.com:
    user: username1
    oauth_token: gho_xxxx
    git_protocol: https
  • Set GitHub CLI as the credential helper for all repositories hosted in github.com
gh auth setup-git --hostname github.com
  • Login
gh auth login
  • Login using a web browser
gh auth login --web
  • Login from another machine’s web browser (aka. device authentication)

    • Use gh auth login
      • or BROWSER=false gh auth login in case it does not work
    • Select Login with a web browser
    • Copy the one-time code
    • From another machine, navigate to https://github.com/login/device
    • Paste the one-time code & approve
  • Login using a PAT

    ℹ️

    Minimum required scopes: gist, read:org, repo

    • provided from standard input
    gh auth login --with-token
  • Check authentication status

gh auth status
  • Change credentials
gh auth switch
  • Clone a repository
$ gh repo clone organization/repo-name
$ gh repo clone git/git
  • List repositories owned by the user of the credentials
gh repo list
  • fetch
$ cd git-clone-folder
$ gh repo sync

GitLab CLI

ℹ️

Need to install GitLab CLI.

Support HTTPS remotes only.

  • Create a PAT with api and write_repository scopes

    glab auth login

Credential helper scope / context configuration

  • User (--global) level

    • possibility to configure per host
    • possibility to configure per username
    git config --global --list

    Example for the GitHub CLI credential helper:

    gh auth login --hostname github.com
    • ~/.gitconfig
    [credential]
            helper = store
    [credential "https://github.com"]
            helper =
            helper = !/usr/bin/gh auth git-credential
    [credential "https://gist.github.com"]
            helper =
            helper = !/usr/bin/gh auth git-credential
    
    [credential "https://github.com"]
    	username = <username>
    
    • Check credentials used for a given URL
    echo url=https://github.com/git/git.git | git credential fill 
  • Per repository

    • use HTTP path in credential helper
    git config --global credential.github.com.useHttpPath true
    • set in each repository’s configuration

      • .git/config
      $ git config user.name "John Doe"
      $ git config user.email "[email protected]"

Remove stored credentials / Revoke credentials

  • git credential cache

    git config --global --unset credential.helper
  • GitHub credential helper

    gh auth logout
    
  • Git credential manager

    • Client side
    git credential-manager erase
    git credential-manager github logout
    • GitHub side

      🚨

      This will revoke the Git Credential Manager token on all machines

      • Profile > Settings > Applications > Authorized OAuth Apps > Git Credential Manager > Revoke
  • Windows credential manager

    • Launch Credential Manager
    • Select Windows Credential
    • Look for entries starting with git:
    • Select / Expand the relevant one
    • Click on Remove
  • macOS Keychain

    • From GUI
      • Launch Keychain Access
      • Search for github.com
      • Find the Internet Password entry for github.com
      • Delete the entry
    • From command line
    $ git credential-osxkeychain erase
    host=github.com
    protocol=https
    > [Press Return]

Git vs SVN

●     offline

●     distributed

●     workflows

●     cheap branching

●     auto merge (SVN < 1.5)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment