Git GOOD
Mastering the "the information manager from hell"
Being able to jump efficiently between different states of a project, collaborate with other devolopers seemlessly and generally understanding the complexity of version control is one of the most empowering skills a programmer can attain.
What is git?
Git is a version control software a tools that allows you to time travel. With git you can freeze certain states of a project in time, your freezer bags are called commits
a commit is a snap shot of all the data contained in a directory (repository).
Base Commands
First you have to initialize git in the directory you want to track
1# Change to the directory you want to track with Git
2cd your-project
3
4# Initialize a new Git repository in the current directory
5git init
6Now make changes and save / commit them
1# Change content in the repository
2touch README.md
3
4# Check the status / discover the differenece between current change and last commit
5git status
6> On branch main
7>
8> No commits yet
9>
10> Untracked files:
11> (use "git add <file>..." to include in what will be committed)
12> README.md
13>
14> nothing added to commit but untracked files present (use "git add" to track)
15
16# Now add changes to the next commit
17git add README.md
18
19# Create / name commit
20git commit -m "docs: add README"
21Commits should be granular and capture logical steps, towards a feature of a fix (commit often or learn it the hard way),
for files and directories that contain sensitive information (such as .env) or are large and easily reproducible (like
node_modules or __pycache__), add them to a .gitignore file to prevent them from being tracked by Git.
1# Create a .gitignore file
2echo "node_modules/" >> .gitignore
3git add .gitignore
4git commit -m "chore: add .gitignore"
5Finally transfer repository state from your local computer to a remote machine / cloud.
1# Add / link the remote repository (github in this example)
2git remote add origin https://github.com/yourusername/your-repo.git
3
4# Push your commit to the remote repository's main branch
5git push -u origin main
6From now on, you can run git push after you have committed to sync your local repository, or run git pull to retrieve
the latest changes from the remote repository.
When you want to retrieve a repository from github use git clone.
1# Clone an existing repository from GitHub
2git clone https://github.com/username/repo.git
3When renaming or moving files or directories use git mv it moves the file and you can still track the differences within as
opposed to deleting one and adding another file.
1# Move the entire folder 'old_folder' to 'new_folder'
2git mv old_folder new_folder
3
4# Move and rename file.txt to docs/guide.md
5git mv file.txt docs/guide.md
6Collaboration
Git is probably best known for enabling multiple devolopers to work on one project simultaneously, this is facilitated by
branches as one might be able to guess by the name branches allow devolopers to branch of from the tree root branch
(often called main or master) to tinker through the stages of creating a feature or fix without interfereing, as others can do
the same from the clean main branch (which should always be in a working state).
1# List all branches (shows which branch you're on)
2git branch
3
4# Create a new branch
5git branch my-new-feature
6
7# Switch to your new branch
8git checkout my-new-feature
9
10# Alternatively, create and switch in one step
11git switch -c feature/my-new-feature
12
13# Make changes, then stage and commit as usual
14echo "Some new feature" > feature.txt
15git add feature.txt
16git commit -m "feat: add new feature"
17
18# Push your branch to the remote repository
19git push -u origin my-new-feature
20Once your work on the branch is complete and tested, you’ll want to merge it back into the main.
1# Switch back to the main branch
2git checkout main
3
4# Pull the latest changes from the remote (optional but recommended)
5git pull
6
7# Merge your branch into main
8git merge my-new-feature
9
10# Push the updated main branch to the remote repository
11git push
12In practice you probably want to merge changes from main first into your branch (ensuring it's up to date with the latest changes) before merging it into main this allows for testing against the latest state, smoother merge into main and a cleaner main history.
1# Make sure you're on your feature branch
2git checkout my-feature
3
4# Update your branch with the latest changes from main
5git fetch origin
6git merge origin/main
7
8# Resolve any conflicts, test your code
9
10# Switch back to main and merge your branch
11git checkout main
12git merge my-feature
13git push
14Alternatively, you can use git rebase origin/main instead of git merge origin/main for a linear history and this is
where the configuration start and this is where the configuration starts.
Configuration
My workflow when it comes to using "the information manager from hell" (as it's creator Linus Torvals called it, in it's first commit) is largely based on minimizing typing (chars & commands).
Git can be configured through the ~/.gitconfig file, I believe the configurability is a largely overlooked part in optimizing your git experience, lets break down my gitconfig.
User Identity & Security
1[user]
2 # name & email asscoiated with your commits
3 name = Mats Julius Funke
4 email = 125814808+matsjfunke@users.noreply.github.com
5 signingkey = ~/.ssh/github_signing # Path to SSH private key used for signing commits
6[commit]
7 gpgsign = true # Automatically sign all commits with your SSH key
8[gpg]
9 format = ssh # Use SSH keys instead of GPG keys for signing commits
10Core Git Behavior
1[core]
2 editor = nvim # Default text editor for commit messages, interactive rebase, fixing merge conflicts etc.
3 pager = delta # fancy diff view (more later)
4[init]
5 defaultBranch = main # Use 'main' instead of 'master' for new repositories (personal preference)
6[rebase]
7 autoSquash = true # Automatically squash commits marked with 'fixup!' or 'squash!'
8 autoStash = true # Automatically stash changes before rebase and pop after
9 updateRefs = true # Update branch pointers that point to rebased commits
10Remote Operations
My personal preference is to rebase on every pull as i've hinted earlier.
1[pull]
2 rebase = true # Always rebase instead of merge when pulling (cleaner history)
3[fetch]
4 prune = true # delete local branch if remote was deleted
5[push]
6 autoSetupRemote = true # Automatically create remote branch on first push of local
7Git Large File Storage configuration
1[filter "lfs"]
2 required = true # LFS is required for this repo
3 clean = git-lfs clean -- %f # Clean filter for storing files
4 smudge = git-lfs smudge -- %f # Smudge filter for retrieving files
5 process = git-lfs filter-process # Process filter for efficiency
6UI / UX
These settings make the outputs look prettier.
1[color]
2 ui = auto # Enable colored output in terminal (auto = only when outputting to terminal)
3[column]
4 ui = auto # Display output in columns
5[delta]
6 line-numbers = true # Show line numbers in delta diff viewer
7[help]
8 autocorrect = prompt # Hint for typos
9[advice]
10 skippedCherryPicks = false # Don't show advice about skipped cherry-picks during rebase
11[tag]
12 sort = version:refname # Sort tags by version number (v1.0, v1.1, v2.0, etc.)
13[branch]
14 sort = -committerdate # Sort branches by most recent commit (newest first)
15The git diff command is great for visualizing the granular changes within a file the syntax highlighting requires installing delta via brew install delta:

List branches ordered and in columns:

Aliases
[alias] allows for setting up convenient shortcuts.
1# Stash operations
2staash = stash --all # Stash everything including untracked files
3sm = stash push -u -m # Stash with message
4
5# Navigation & switching
6sw = switch # Switch branches
7swc = switch -c # Create and switch to new branch
8
9# Status & information
10st = status -s # Consice status output
11lg = log --oneline --graph --decorate --all # Pretty log with graph and all branches
12wtl = worktree list # List all worktrees
13conflicts = diff --name-only --diff-filter=U # show the conflicting files
14
15# Commit operations
16uncommit = reset --soft HEAD~1 # Undo last commit but keep changes staged
17lfg = "!f() { git add . && git commit -m \"$1\" && git push; }; f" # LETS F*CKING GO: Add, commit, push in one command
18
19# Keeping up with remote / Rebasing utils
20p = pull # Simple pull shorthand (will rebase by default)
21pm = pull --no-rebase origin main # pull main with merge
22sync = !git fetch origin && git rebase origin/main # sync branch to remote main (will rebase by default)
23rbc = !git add -A && GIT_EDITOR=true git rebase --continue # continue rebase (with all changes and no commit edit)
24rba = rebase --abort
25
26# GitHub integration
27pr = "!f() { gh pr create --title \"$1\" --body \"\" --fill | tee /dev/tty | grep -oE \"[0-9]+$\" | xargs -I {} gh pr merge {} --auto --merge; }; f" # Create PR (merge when ready)
28dpr = "!f() { gh pr create --draft --title \"$1\" --body \"\" --fill; }; f" # Open draft PR
29mwr = "!gh pr list --head $(git branch --show-current) --json number --jq '.[0].number' | xargs -I {} gh pr merge {} --auto --merge" # merge when ready for current pr of branch
30The git log is really useful the visualize the history and different branches of a repo:

Consice status:

Worktrees
Usually when i'm in the process working on something and I need to switch to e.g. fix something i commit with type wip
like git lfg "wip: xyz is in progress when I don't think my state is commit worthy i stash it git sm "some changes" but for larger project I prefer to have multiple directories of the same repository as its more convenient to
switch in between and you don't need to break your state for a quick fix, this is where git worktrees come in.
Git worktrees are branches in separate directories each directory is linked to the same underlying repository but maintains its own working state, so you can easily jump between features, bug fixes, or experiments without disrupting your main workflow (my go to hotfix choice).
These shell functions are needed since gitconfig doesn't support cd, add them to your .zshrc/.bashrc if you want to use them.
1# git worktree add
2# Creates a new worktree for a branch (creates branch from main if it doesn't exist, or checks out existing local branch; auto-switches main worktree to 'main' if target branch is already checked out there)
3wta() {
4 if [ -z "$1" ]; then
5 echo "Usage: wta <branch-name>"
6 return 1
7 fi
8
9 # Check if branch already exists locally
10 if git show-ref --verify --quiet refs/heads/"$1"; then
11 # Check if branch is already checked out in another worktree
12 if git worktree list | grep -q "\[$1\]"; then
13 # Get the main worktree path (first entry)
14 main_worktree=$(git worktree list | head -n 1 | awk '{print $1}')
15
16 # Check if it's checked out in the main worktree
17 if git worktree list | head -n 1 | grep -q "\[$1\]"; then
18 echo "Branch '$1' is checked out in main worktree. Switching main to 'main' branch..."
19 # Save current directory
20 original_dir=$(pwd)
21 # Switch main worktree to main branch
22 cd "$main_worktree" && git checkout main
23 # Return to original directory
24 cd "$original_dir"
25 else
26 echo "Error: Branch '$1' is already checked out in another worktree"
27 echo "Current worktrees:"
28 git worktree list
29 return 1
30 fi
31 fi
32 # Branch exists locally and not checked out elsewhere, just check it out
33 git worktree add "../$1" "$1" && cd "../$1"
34 echo "Successfully created worktree '../$1' from existing branch and switched to it"
35 else
36 # Branch doesn't exist, create it from main
37 git worktree add -b "$1" "../$1" main && cd "../$1"
38 echo "Successfully created worktree & branch '../$1' and switched to it"
39 fi
40}
41
42# git worktree delete
43wtd() {
44 # If no argument provided, use current directory name
45 if [ -z "$1" ]; then
46 branch_name=$(basename $(pwd))
47 echo "No branch specified, using current directory: $branch_name"
48 else
49 branch_name="$1"
50 fi
51
52 # Get the main repo name by finding the main worktree
53 main_repo_path=$(git worktree list | head -n 1 | awk '{print $1}')
54 repo_name=$(basename "$main_repo_path")
55 echo "Main repo: $repo_name"
56
57 # Check if we're currently in the worktree we want to delete
58 current_dir=$(pwd)
59
60 if [[ "$(basename "$current_dir")" == "$branch_name" ]]; then
61 echo "You're currently in the worktree directory. Switching to main repo..."
62 # Change directory BEFORE removing the worktree
63 cd "../$repo_name"
64
65 # Remove the worktree using the full path
66 git worktree remove "../$branch_name" --force
67
68 # Delete the branch
69 git branch -D "$branch_name"
70
71 echo "Deleted worktree and branch: $branch_name"
72 else
73 # We're not in the target directory, safe to delete
74 git worktree remove "../$branch_name" --force
75 git branch -D "$branch_name"
76 echo "Deleted worktree and branch: $branch_name"
77 fi
78}
79My worktree ergonomics are:
1# add a worktree
2wta hotfix-deployment
3
4# add, commit, push (which auto creates remote branch)
5git lfg "fix: xyz"
6
7# open github pr
8git pr "fix: Hotfix for xyz"
9
10# delte worktree (directory and branch)
11wtd
12
13# list worktrees (verify desired state)
14git wtl
15
16# continue with working on my feature branch
17git lfg "feat: finished feature"
18git pr "feat: Add all functions"
19
20# switch to main
21git sw main
22
23# open new feature branch
24git swc new-feature
25