Git (Cheat Sheet)

Previous version: gist.github.com

Topics Commands
Setup and Config git , config , help
Getting and Creating Projects init , clone
Basic Snapshotting add , status , diff , commit , notes , restore , reset , rm , mv
Branching and Merging branch , checkout , switch , merge , mergetool , log , stash , tag , worktree
Sharing and Updating Projects fetch , pull , push , remote , submodule
Inspection and Comparison show , log , diff , difftool , range-diff , shortlog , describe
Patching apply , cherry-pick , diff , rebase , revert
Debugging bisect , blame , grep

Source: git-scm.com

Typical workflows

Reset local branch to match remote branch

For example, I have a fork of wagmi-dev/viem at 0xarthurxyz/viem and I want my main branch (in 0xarthurxyz/viem) to match the main branch (in wagmi-dev/viem) exactly. I want 0 files changed, 0 insertions, 0 deletions, 0 commits ahead, 0 commits behind.

That way, I can create a new branch from main and work on it without having to worry about merging changes from wagmi-dev/viem.

Step 1: Fetch the Upstream Repository

If you haven’t already configured the upstream repository as a remote, you need to add it. The upstream repository is the original repository from which you forked.

# Check if upstream is already configured
git remote -v

# If not add it
git remote add upstream [email protected]:wagmi-dev/viem.git  
#              ^name    ^url

After adding the remote, you fetch the changes:

git fetch upstream
#         ^name

Step 2: Reset Your Main Branch

You need to make sure you’re on your main branch:

git checkout main
#            ^branch

Then, you reset your main branch to the state of the main branch of the upstream repository:

git reset --hard upstream/main
#                ^name    ^branch

git reset --hard moves the current branch’s tip to the specified commit, in this case, the tip of the upstream/main branch. The --hard flag tells Git to also reset the staging area and the working directory to match. This effectively discards all commits in your local main branch that are not in the upstream main branch.

Step 3: Force Push to Your Fork

After resetting your local branch, you can now force push this state to your GitHub fork:

git push origin main --force
#        ^name  ^branch

git push updates the remote branch with your local branch’s current state. The --force flag is necessary here because you’re rewriting the commit history of the main branch on the remote. This can be destructive, as it will replace the history on GitHub with the history from the upstream repository. Since you’re working alone on this fork, it’s safe to do so.

Considerations:

  • Be cautious with --force: Force pushing is a powerful feature that should be used with caution. It can permanently erase commits if used incorrectly. Always ensure that you’re force pushing to the correct branch and repository.

  • Backup your work: Before performing operations that rewrite history, such as a hard reset, it’s a good practice to create a backup branch of your current state, just in case you need to refer back to it.

  • Open Pull Requests: If you have open pull requests from your fork, resetting branches can affect them. Since you’re working alone, this may not be a concern, but it’s something to keep in mind when collaborating with others.

Branches

Open branch from remote repository

git checkout --track origin/my-branch-name

Source:  Git Branches: List, Create, Switch to, Merge, Push, & Delete

Remotes

In Git, “remotes” are pointers to versions of your repository that are hosted on the internet or on a network somewhere, as opposed to being on your local machine.

When you work with Git, you have a local repository that lives on your computer. This repository contains all of your project’s files and the history of changes made to those files. However, when you want to share your code with others or synchronize it across multiple computers, you need a way to connect your local repository to a remote location.

Under the hood:

  • Configuration Files: Remotes are configured in the .git/config file of your local repository. This file contains the URLs and settings for your remotes.

  • References: Remotes work with references, which are pointers to commits. These are stored in a subdirectory of the .git directory called refs/remotes/.

  • Network Protocols: Git supports several protocols for communicating with remotes, including HTTP, HTTPS, SSH, and Git’s own protocol.

  • Security: When you push to or pull from a remote, Git ensures the integrity of the data transfer. With SSH, for example, the data is encrypted.

  • Pruning: Over time, a remote repository may have branches deleted. To clean up these stale references, you can use git fetch with the --prune option.

  • Tags: Remotes also keep track of tags, which are like snapshots of your repository at a certain point in time. You can push tags to remotes with git push --tags.

  • Branch Tracking: Local branches can track remote branches. This means changes in the remote branch can be pulled into the local branch with a simple git pull, without specifying the remote name and branch.

Source: ChatGPT

Existing remotes

When you clone a repository, Git automatically adds a remote called origin. This points to the location from which you cloned the repository. You can see which remotes are configured with:

git remote -v

The -v flag stands for “verbose” and tells Git to show the URLs that each remote points to.

For example, in my viem fork I have the following remotes:

~/Documents/0xarthurxyz/viem main $ git remote -v
origin	[email protected]:0xarthurxyz/viem.git (fetch)
origin	[email protected]:0xarthurxyz/viem.git (push)
upstream	https://github.com/wagmi-dev/viem.git (fetch)
upstream	https://github.com/wagmi-dev/viem.git (push)

The output is expected and does not indicate duplicate remotes. In Git, remote refers to a remote repository that your local repository can track. Each remote can have multiple URLs associated with it for different actions—typically at least for fetching and pushing changes.

In the output above, there are two remotes configured for your local repository:

  1. origin: This is the default name Git gives to the remote from which you cloned your repo. It has two URLs listed, one for fetching (git fetch, git pull) and one for pushing (git push). Both actions are using the SSH protocol, as indicated by the [email protected] syntax.

  2. upstream: This is the remote you’ve added manually, which points to the original repository (often the one you forked from). Like origin, it has a URL for fetching and one for pushing. In this case, both actions are using the HTTPS protocol, which you can tell by the https:// prefix.

Having both origin and upstream is common when you have a forked repository:

  • You would use origin to push and pull changes to and from your fork.
  • You would use upstream to fetch changes from the original repository (which you’ve forked) to keep your local repository and your fork up-to-date with the original.

You have what you need to interact with both your fork (origin) and the original repository (upstream).

Source: ChatGPT

Add remote

If you want to track another repository in addition to the one you cloned from (for example, the original repository when you’re working on a fork), you can add a new remote:

git remote add <name> <url>

For example, I cloned the repository wagmi-dev/viem as 0xarthurxyz/viem and I want to track the original repository in my local repository. In that case I can add a new remote (arbitrarily called upstream) that lives at the following URL https://github.com/wagmi-dev/viem.git

git remote add upstream [email protected]:wagmi-dev/viem.git
#              ^name    ^url

This command tells Git to add a new remote called upstream pointing to the URL of the original repository. Remotes are like bookmarks for repositories that allow you to track the same project in different locations.

Source: ChatGPT

Remove remote

To remove a remote, you use:

git remote remove <name>

For example, to remove a remote (arbitrarily called upstream), you run:

git remote remove upstream
#                 ^name

It’s common to remove a remote when you no longer want to track a repository. It’s

Fetching from a remote

To bring in the history and changes from a remote repository, you use:

git fetch <name>

This command downloads objects and refs from the remote but doesn’t merge any changes into your working files.

For example, after adding the wagmi-dev/viem remote as “upstream”, you fetch the changes from the remote by running:

git fetch upstream
#         ^name

git fetch communicates with the upstream repository and downloads the commits, files, and refs to your local repository. This step ensures that you have the latest history of the upstream project, but it doesn’t modify your working directory or your current branch. In other words, this command downloads objects and refs from the remote but doesn’t merge any changes into your working files.

Source: ChatGPT

Pushing to a remote

When you have commits in your local repository that you want to share with the remote, you use:

git push <name> <branch>

This command sends your commits to the remote repository so others can see and pull them.

Source: ChatGPT

Pulling from a remote

When you want to update your local branch with changes from the remote, you use:

git pull <name> <branch>

This is effectively a git fetch followed by a git merge.

Logs

Commit graph

git log --oneline --graph --decorate --all

This command shows you the commit history with all branches and tags decorated with their names, presented in a graph structure to illustrate forks and merges.

For example, in my viem fork I have the following commit graph:

~/Documents/0xarthurxyz/viem main $ git log --oneline --graph --decorate --all

*   721fc122 (HEAD -> main, origin/main, origin/HEAD) Merge branch 'wagmi-dev:main' into main
|\  
| * 291e9ba2 chore: version package (#1459)
| * 46213902 fix: undefined nativeCurrency - fixes #1457
* | ec233f4b Merge branch 'wagmi-dev:main' into main
|\| 
| * 6ae86cee chore: version package (#1456)
| * 880345ca chore: update snapshots
| * e40006aa feat: add Kava Testnet and edited Kava Mainnet RPC (#1453)
| * 95991301 fix: event name on `watchContractEvent` fallback
| * dd8bccc7 fix: celo rpc block type
| * e6796672 chore: version package (#1449)
| * 494dc538 chore: bump celo test coverage
| * c0da695a fix: celo-specific transaction (CIP64) regressions (#1434)
| * c2fab4a7 fix: zksync formatters (#1448)

Commits

Changing the last commit messsage

If you simply want to modify your last commit message, that’s easy:

git commit --amend

The command above loads the previous commit message into an editor session, where you can make changes to the message, save those changes and exit. When you save and close the editor, the editor writes a new commit containing that updated commit message and makes it your new last commit.

Source: git-scm.com

Changing the last commit content

If you want to change the actual content of your last commit, the process works basically the same way:

git add file # stage the changes you want to add
#       ^file name
git commit --amend # replace the last commit with the staged changes

First make the changes you think you forgot, stage those changes, and the subsequent  git commit --amend replaces that last commit with your new, improved commit.

NOTE You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it.

Source: git-scm.com

Patching