Published 12-15-2023
These are my personal notes on git. Each section starts with a problem or goal, and is further broken down into specific scenarios. This way, the notes are structured “Just in Time” (like asking a friend for help). Instead of “Just in Case” (like reading the manual start-to-finish before driving the car).
You can get started by reading through the Table of Contents or checkout some of the rapid fire tips. Alternatively, bookmark these notes and return when you have a specific goal or problem to solve.
git pull
translates to git fetch
+ git merge
(or git rebase
given -r)git pull -r origin main
to rebase the latest change from main (with 1 command)git fetch
is still useful on its own. Before checking out a teammates remote branch, run git fetch
git fetch
→ git reset --hard origin/main
to “force pull” (use with caution)
git reset HEAD@{1}
(you can undo a bad reset with… reset)git log -g
instead of git reflog
. Personal preference.git checkout -b temporary-branch
before commit / push if you’re worried about breaking somethinggit reset --soft
put your commits back to the staged state. Now you can commit multiple previous commits as one well-written commit. See examplesgit push
will automatically point to git push ORIGIN FEATURE-BRANCH
given this config git config --global push.autoSetupRemote true
git push --force-with-lease
force pushes but prevents you from overwriting others workgit clone repo_name --bare
+ git worktree add your_branch
is great for devs who frequently switch branchesA few assumptions
origin
is the name of your remotemain
is the name of your default branchIs it safe to rebase (strict) - Is your branch only local? (not public)
Is it safe to rebase (simple version) - Are you the only person working on your branch?
If you work on a team where developers checkout their own feature branch, and only they work on it. Go for the simple version. If developers commonly work on each other branches or share long lived branches. Go for the strict version.
How can you guarantee it’s safe to rebase 100% of the time? git checkout -b new-branch-name
. Because now your branch only exists locally… until you push of course.
Rebase Checklist
git branch
→ git log
verify feature branch and existing commits:git add
→ git commit
→ git push origin feature-branch
if you have changes, commitgit pull -r origin main
pullgit add
→ git rebase --continue
git log
verify your workgit push --force
update your feature branchRebase Checklist Explanation & Tips
Make sure you’re on a feature/developer branch. Doing this directly on a main/release branch is a big no-no.
If you like to be thorough (I’m a fan) git log
so you know what the log looked like before your rebase.
Concerned about making a mistake? git checkout -b temporary-branch
. Now any mistakes will be isolated to this private temporary-branch. Skip this step if you’re feeling confident (Don’t worry, I believe in you).
If you have changes then stage, commit and push. git add
, git commit
, git push
. Very important. If you don’t make your own commit before rebase you may accidentally merge the work of another developer into your work and restarting from a mistake becomes more difficult.
Multiple Commits & Rebasing (Situational)
Given multiple commits (and possible merge conflicts). Squashing your commits first in this scenario saves you a lot of time because merge conflicts are compared against each commit.
git pull -r origin main
. Pull -r means git fetch
+ git rebase
, this guarantees you pull the latest commit.
If you have merge conflicts you’ll likely use your editor to resolve them. After resolving, remember these commands. git add
will stage your resolutions, git rebase --continue
will move to the next step. If you make a mistake, you can restart via git rebase --abort
Double check your work. At this point your branch should resemble main + your commits replayed on top.
git push --force
. Don’t fear the force flag. In fact, you need to use force here.
Tip #1
Alternatively, you can use force-with-lease
instead of force
as a safer version of force. This prevents you from accidentally overwriting someone elses work. If you followed the steps, that shouldn’t be an issue, but it doesn’t hurt to use.
Tip #2
git config --global push.autoSetupRemote true
this allows you to type git push
instead of git push origin name-of-your-branch-you-have-checked-out
An Example
git log # Double check existing commits
# Commit changes (if any)
git status
git add .
git commit -m "My sweet new feature"
git push origin my-feature-branch
# End changes
git pull -r origin main
# Fix Conflicts (if any)
git add .
git rebase --continue
# End Conflicts
git log # Verify we did everything correctly
git push origin my-feature-branch --force
I want to undo my commit, but I don’t want to lose those changes
git reset --soft HEAD~1
resets 1 commit back to stagedI want to remove a file from the staged state
git restore --staged PATH_TO_FILE
I committed a file to my remote feature branch, now I want to remove it
git reset --soft HEAD~1
git restore --staged PATH_TO_FILE_TO_REMOVE
git commit
git push --force-with-lease # Your feature branch
I want to undo my commit, and I want to completely remove those changes
git reset --hard HEAD~1
resets 1 commit completelygit push --force
. Do this with caution and follow the same rebase rules above when doing so.Help! I used reset --hard
and I meant to use reset --soft
git reset HEAD@{1}
to undo your latest git operationHow does this work? Run git log -g
or git reflog
. Both commands show a reference log which include entries such as reset. For example, if the very last command you ran was git reset --hard origin/main
you may see: Reflog: HEAD@{1} Reflog message: reset: moving to origin/main.
Reflog is your go to any time you make mistake but don’t have a specific commit to fall back to.
I want to remove all changes and “force pull” from the remote
git fetch
→ git reset --hard origin/your-branch-name
I have multiple commits on my remote feature branch, I want to combine them into 1 commit
git log
count the number of commits you want to squash (using 4 for the example)git reset --soft HEAD~4
git commit -m "your new message"
git push --force
Git Clean Guide (Removing unwanted files)
To dry run a git clean, add the -n
flag
-n
firstI want to remove untracked files
git clean -f
I want to remove untracked folders/directories
git clean -fd
I added something to my .gitignore that I previously commited, now I want to remove it.
I want to remove changes in my working directory
git restore FILE_NAME
I want to remove ignored files
git clean -fX
Notice the capital X, you probably want capital. Lowercase x will remove files like .environment
.
I was working off of another developers branch, they merged to main. Now I have conflicts with their merge commit when I try to merge to main.
There is very likely a more efficient method to do this, but as of today, here’s how I handle it.
git log # Copy each commit hash you've made
# abcd1235
# efgh6789
# lmno1234 → Commit you started from
git checkout main
git pull -r origin main # Now you should have their changes
git checkout -b your-new-feature-branch
# Apply from oldest to most recent
git cherry-pick efgh6789
git cherry-pick abcd1235
# Push
git push origin your-new-feature-branch
💡 You can simplify this process by having 1 commit. If you have multiple commits, see previous section “I want to combine them into 1 commit”
Branch naming conventions
Any variation of this can help define intention. It’s also useful for automating actions, tags, versions and releases
username/type/TICKET-description
username/type/description
type/description
Examples using issue, ticket, or neither
everduin94/fix/44-infer-underscore
everduin94/feat/FLT-10-public-notebooks
everduin94/feat/custom-scopes
Commit message conventions
You can find additional examples and explanations here: Conventional Commits Summary
Below is a conventional commit from better-commits
feat(tools): #3 infer ticket/issue from branch
If commit_type.infer_value_from_branch is enabled,
will attempt to infer ticket/issue from branch name.
Updated documentation with details.
Closes: #3
My workflow - New branch checklist
git checkout main
git pull -r origin main
npm ci
Better Commits is a CLI to achieve all of the above. It’s well documented, flexible, easy to install and I use it every day. It can help you (and your team) create a consistent branch / commit workflow and minimize mistakes across users without enforcing PR lint rules or pre-commit hooks.
Manage multiple working trees attached to the same repository source
In other words, they allow you to have multiple commits checked out at the same time in the same repository.
git clone --bare YOUR_REPO
it’s recommended to use a bare repository with worktrees.
git worktree add main
This will checkout a new worktree (named main) at main’s HEAD.
This is a little confusing, because the worktree name and the branch name will match. — If the branch exists, it will checkout that branch. If it doesn’t exist, it will create a new branch that starts at the HEAD of main/master.
Examples
# main branch
$ git worktree add main
Preparing worktree (checking out 'main')
HEAD is now at c69d66d fix: improve docs (#9)
# existing feature branch
$ git worktree add about-me
Preparing worktree (checking out 'about-me')
HEAD is now at 7d6a74b Fix paths in prod
# new feature branch -- notice commit is same as main
$ git worktree add does-not-exist
Preparing worktree (new branch 'does-not-exist')
HEAD is now at c69d66d fix: improve docs (#9)
Running ls
you can see each worktree is represented by a folder, which are essentailly seperate checkouts with their own state. Concrete example, if you run npm install
in each folder. Each worktree will have it’s own node_modules
.
git worktree remove NAME
“fatal: contains modified or untracked files, use —force to delete it”
git worktree remove --force NAME
If you’re using a branch naming convention, e.g. something like this username/type/ticket-description
. Each /
will be transformed into a nested folder (which may not be desirable).
Using better-commits, you can run the better-branch
command, which supports Worktrees.
Alternatively, git worktree add WORKTREE_NAME -b BRANCH_NAME
to specify a different worktree / branch name manually.