?! HowTheFu.cc ?!
<< FFMPEG: Quick Commands
>> Recipe: Baguettes

2023.11.17
Git: Cheat Sheet

Resources:

General Operations


# Setup a repository
> git init
> git remote add origin ssh://user@website/blah.git
> git status                  # List changes / status
> git add [File to push]      # Add changes
> git commit -m "[message]"   # Finalize changes by adding a message
> git push -u origin main     # Initial push. 
                              # "git push" is sufficient afterwards


# Edit commit message
> git commit --amend


# Move files / directories without losing the history
> git mv [to move] [new location]


# Add change onto already made commit
> git add [File(s) with small edit]
> git commit --amend --no-edit


# Show changes since last commit
> git diff


# Show changes of file since last commit
> git diff [path to file]


# Compare two files from two commits
> git diff [commit hash]:[file] [commit hash]:[file]


# Create and push a new branch
> git checkout -b [name]
> ... # Make changes to the branch
> git push -u origin [name]


# Switch branches
> git checkout [branch]


# Remove branches
> git push -d origin [branch]  # Delete remote branch
> git branch -d [branch]       # Delete  local branch


# Temporarily remove changes
# (also possible if you want to temporarily switch branches)
> git stash
> ...
> git stash pop


# Pull changes/commits from another _remote_ branch 
# onto the current one
> git pull origin [branch]


# Pull changes/commits from another _local_ branch 
# onto the current one
> git pull . [branch]


# If you have the same repository in multiple directories, 
# you can also pull the changes locally:
> git pull [path to repository]


# Undo a specific commit 
# Find [hash] using git log, then:
> git revert [hash]


# _Completely_ discard all local changes
> git reset --hard HEAD
> git clean -d -f -i


# Merge changes from a branch onto main
> git checkout main
> git merge [branch]


# Rebase changes from a branch onto main
> git checkout [branch]
> git rebase main


# Remove a commit from your history before pushing
> git rebase -i HEAD~3 # List last 3 commits
# Opens a text editor. Now delete / modify the line of the commit

Sometimes git refuses to do operations for seemingly no reason. In practice, it might be that some operation is in progress without any indication.

Adding another Upstream

This is super useful if you e.g. have your own personal version of a public project in your own git repo, but you want to pull updates from the public project if necessary.

In my case, I cloned the repo and adjusted the repo settings:


> git clone [link to original repo]
> git remote set-url origin [url to personal repo]

And then add the original repository back under another upstream name.


> git remote add [name] [link to original repo]
> git pull [name] [branch] 

Now I can work on my local version without having to constantly remind myself to specify where to push to. In my case, I added an upstream called "upstream". In the rare occasion that I want to pull in updates from the original repo, just do git pull upstream master.

Setting up a Mirror

Recently I started a project with a friend, and each wanted to host an up-to-date repository on a local server. For a very basic mirroring setup, you can configure a "main" repository for your project, and setup your local git repo such that you git push to all of your mirrors automatically.

Simply setup your local repository as follows:


> git init
> git remote set-url --add --push origin [link to main repo]
> git remote set-url --add --push origin [mirror 1]
> ...

While all of the main actions (e.g. git pull) is done on the "main" repository, you git push will automatically also push to each mirror.

Finding the Commit in which the Bug was Introduced

git has a built-in command/process such that you can binary search for the commit in which a bug was introduced:


# Identify 
#  - a "good" commit hash [goodHash] in which the 
#  error is not present, and
#  - a "bad" commit hash [badHash] in which the
#  error is present
# (use "git log" and "git checkout [hash]")
#
# Then start the bisect process:
> git bisect start
> git bisect good [goodHash]
> git bisect bad [badHash]


# Git will then repeatedly switch you onto
# other commits. 
# If the bug _is present_, do:
> git bisect good
# If the bug _is not present_, do:
> git bisect bad


# After doing this a bunch of times,
# git will stop and tell you the commit
# in which the bug was introduced
[hash] is the first bad commit
...


# If you want to abort this process at any point:
> git bisect reset

Aliases

Many git commands have additional options, which provide helpful visualizations and/or denser/quieter outputs. As typing the args each time is annoying, you can either configure your shell, or add aliases to git directly.

Find out where your git config is:


> git config --list --show-origin

And then you can e.g. define aliases for common operations:


[alias]
	# Overview: 
	# 
	# Parameter alias:
  # testA = "commit -A"  
  # -> "git testA"  becomes  "git commit -A"
	#
	# Insert shell commands:
	# testB = "!git log; git push"
	# -> "git testB"  becomes  "git log; git push"

	# Shorthand for simple projects
	update         = "!git add -A; git commit; git push"
	update-no-push = "!git add -A; git commit"

	# Shorthand for quieter/denser outputs
	ll  = "log --oneline"
	llv = "log --oneline --graph --decorate"
	st  = "status -s -b"
	c   = "commit"
	br  = "branch -a -v"
	co  = "checkout"
	cb  = "checkout -b"

Hooks

You can run automatically run a script when a certain git command is issued. In your git project, the script file needs to be placed in ./.git/hooks/ with a name specific to the command you want to hook into (see here for a list of hooks). The folder also already contains a bunch of example scripts.

Here is the pre-commit-hook I use on programming projects. It automatically formats files of a commit before it is finalized (./.git/hooks/pre-commit):


#!/bin/sh

# Get list of changed files
ARGUMENTS=$(git diff-index --name-only --cached HEAD)

# Run formatter on files
[YOUR_FORMATTER] $ARGUMENTS

# Assuming the formatter run without problems,
# add the changes to the commit
git add $ARGUMENTS

# Signal that hook ran successfully
exit 0

Scripts

Don't forget: For very simple projects (e.g. updating this website), you can even automate / simplify your commits if aliases are not sufficient! For this website, I don't care about commit messages, and I keep have to add new posts/files to git. So I use the following script:


#!/bin/sh

# Overview pages
git add byName.html index.html

# Content pages
git add posts/

# Other
git add style.css Icon16.png Icon32.png

# Push
git commit -m "$(date)"
git push

Move files and their history between repositories


# Assume you are currently in [original repo],
# [splitBranch] is just a name for the split
git subtree split -P [to move] -b [splitBranch]


# If you want to move the items onto a new repository
> cd [new repo]
> git checkout [your branch]
> git pull [path to original repo] [splitBranch]


# If you want to move the items into an existing repository
> cd [other repo]
> git checkout [your branch]
> git checkout --orphan [new temporary branch]    
> git pull [path to original repo] [splitBranch]
# Do git mv here if you want to change the file / directory location
> git checkout [your branch] 
> git merge --allow-unrelated-histories [new temporary branch]
> git branch -d [new temporary branch]


<< FFMPEG: Quick Commands
>> Recipe: Baguettes
?! HowTheFu.cc ?!