http://ndpsoftware.com/git-cheatsheet.html
http://www.sbf5.com/~cduan/technical/git/
| 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.
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?”
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.
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.
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.
ℹ️Git configuration can be set at 3 levels:
- local (repository clone level)
- default or
--local
- default or
- global (user level)
--global
- system (machine level)
*–-system*
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
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
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]
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
git log branch1..branch2
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
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--forceas it does not check if changes were made on the remote branch in the meantime, it will overwrite them) - cf.
- If you’re on the master branch, git rebase origin/master then
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
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
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~1When 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
git config --global --add safe.directory *git config --global --add safe.directory '***'**[safe]
directory = *
https://orhttp://protocolgitprotocol- SSH transport
- or file transport
- 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])"- cf. Generating a new SSH key and adding it to the ssh-agent - GitHub Docs
~/.ssh/config
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
- Account level
-
- GitHub PAT
- Fine grained, repository scoped (OAuth)
- Classic
- GitHub side:
- https://github.com/settings/tokens
Profile > Settings > Developer Settings > Personal Access Tokens
- 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 side:
- GitHub Apps
- GitHub side:
Profile > Settings > Developer Settings > GitHub Apps- and
Profile > Settings > Applications > Authorized GitHub Apps
- GitHub side:
GitHub will automatically revoke an OAuth token or personal access token when the token hasn't been used in one year
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_REQUIREenvironment variables
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
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.gitgit 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.gitSupport 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 cacheAn optional timeout argument can be provided, for example 1 day:
git config credential.helper 'cache --timeout=86400'Support built-in Git client
git config credential.helper storeStore 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
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 withProfile > 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 manageror
git credential-manager configureCredentials 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"
ℹ️
Need to install GitHub Desktop
cf. GitHub side: Profile > Settings > Applications > Authorized OAuth Apps > GitHub Desktop
Install GitHub CLI, for example:
apt install ghGitHub 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
ghwill 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 loginin case it does not work
- or
- 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
- Use
-
Login using a PAT
ℹ️Minimum required scopes:
gist,read:org,repo- provided from standard input
gh auth login --with-token
- or provided from environment variable
GH_TOKEN,GITHUB_TOKEN(in order of precedence)
- Login using a PAT (classic) in an organization that uses single sign-on (SSO)
-
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 syncNeed to install GitLab CLI.
Support HTTPS remotes only.
-
Create a PAT with
apiandwrite_repositoryscopesglab auth login
-
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]"
-
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] - From GUI
● offline
● distributed
● workflows
● cheap branching
● auto merge (SVN < 1.5)