This page lists notes on various git commands.
- Change Branches
- Set upstream branch
- Checkout a Tag
- Empty Commits
- Get Remote URL
- Precommit Hooks
- Rebase Onto
- Rebase Commits to a Branch
- Interactive Rebase
- Soft Undo Commits
- Prune Branches
- Stashing Changes
- Branching Strategy
- Discard Changes
- Commit Messages
- git rerere
You can swap back to the last branch you were on by using a dash with checkout. In the following example you will end back on branch-name.
git checkout branch-name
git checkout main
git checkout -When pushing up a new branch for the first time you will want to link the branch to an upstream branch. Using the -u flag is shorthand for doing this. It configures the local branch to track the remote branch as its upstream. This allows future git push and git pull commands to work without specifying the remote and branch names. Your local branch will track changes to origin/feature/branch.
git checkout main # whichever your main branch is
git checkout -b feature/branch
# commits are made
git push -u origin feature/branchYou can checkout git tags if you need a specific version of your code (if you're tagging commits).
For example, you can follow this pattern to fetch the tags and checkout a new branch.
git fetch --all --tags --prune
git checkout -b branch/name tags/<tag_name>You can make commits with no changes or message if needed.
git commit --allow-empty -m "Empty commit"
git commit --allow-empty --allow-empty-message # will be asked to enter a message
git commit --allow-empty --allow-empty-message -m ""The following command will list your repo's remote origin URL.
git config --get remote.origin.urlPrecommit hooks are useful to ensure standard checks are performed before allowing a commit to be made. For example, you may want to run a linter and unit tests, or perform formatting, or even require a commit message in a specific format. There are a number of tools, like husky and lefthook, that can help. It can also be good to prevent commits to the main branch.
Rebasing a branch with your main branch will preserve a linear history of commits. In some cases we branch from a branch while we're in the middle of changes. If the parent branch is merged back to main while we are still working on the child branch we can rebase --onto to essentially update the child's parent branch to main.
git checkout main
git checkout -b feature/parent
# commits are made to parent
git checkout -b feature/child
# commits are made to child
# parent is squash merged into main
git checkout main
git pull
git checkout feature/child
git rebase feature/parent --onto mainAs you work in a feature branch you'll want to periodically pull in changes from main. To preserve a clean history use rebase to pull in the latest changes and replay your local commits on top of them (placing them at the top of your history). Typically, you'll want to do this often, before you push your local branch to your remote server. Once you've pushed to your remote you'll want to merge changes in case others are working from your feature branch as well. Rebasing changes the commit history so it can cause issues when others use the remote branch. Coordinating with your team is important in those situations and you can force push a branch to rewrite the history of the remote when needed.
git checkout main
git fetch origin
git checkout feature/branch
git rebase main
# Then work through resolving conflicts and committing changesYou can perform an interactive rebase when pulling in changes from main. This allows you review and alter commits. This can let you clean up the commit history by allowing you to edit, squash, reorder, or delete commits before they get merged in. Ultimately this can clean up the commit log and history. Since the history will change it's best to only rebase on local branches before pushing to the remote (and then to start using merge to keep the history in place for others).
git checkout main
git pull
git checkout feature/branch
git rebase -i mainAs you review and make changes you can commit them using git commit --amend. You can always exit a rebase with git rebase --abort if needed and start over or go another route.
- Beginner’s Guide to Interactive Rebasing
- Git Rebase --interactive: EXPLAINED
- How to keep your Git history clean with interactive rebase
When you have accidentally committed changes to a branch you can undo them and restage the changes using git reset HEAD^ --soft. You can run this multiple times to continue undoing changes as needed. git reset HEAD~ will also work.
To remove local remote-tracking branches that are no longer on the remote server run git fetch --prune (this won't delete the local branch itself). Separately, the git prune command will delete locally detached commits.
You can stash uncommitted changes and later reapply them in case you need to work on something else before committing. You can stage newly created files and they will be stashed with your changes. Otherwise modified files will be stashed and newly created files won't be. Stash files using the git stash command. You can include a message if needed like so, git stash push -m "work in progress on feature A".
To retrieve the stash you can use git stash apply or git stash pop. Apply will retain your stash in the stash list for later use whereas pop will remove the stash. You can target specific stashes with both commands using the stash@{X} option (where X is replaced with the stash index). Stashes are stored with the most recent on top (last-in, first-out).
I prefer trunk-based development, using short-lived feature branches (not committing directly to main, which is more like the GitHub flow process or as some refer to it as scaled trunk based development). Features can be built up through a series of short-lived feature branches (i.e. creating a skeleton of the work first, then adding more details/services in subsequent PRs, and finally connecting all the pieces toward the end). This allows for faster feedback in PRs which will all be smaller and helps communicate the direction of the work to other developers sooner in case they have feedback. There should typically be 1 or more PRs per day.
I typically use main and develop branches. Work is done against develop and then merged to main periodically for production releases. Hot fixes can be done against main as necessary and merged back to develop (or even done in develop and cherry picked to main depending on the issue). Feature branches should be squash merged into develop and develop should be merged into main to keep the development and production histories intact.
If the main and develop histories conflict preventing a merge, we need to create a branch from main, merge develop into that branch and resolve any conflicts and merge it into main. Then merge main back to develop to resync them.
- Trunk-based development
- Gitflow - A successful Git branching model
- The origins of Trunk-Based Development
- Is GitHub Flow the same as Trunk-based development?
- Trunk Based Development: Introduction
You can use the git checkout command to discard unstaged changes by using the following command.
git checkout -- .This discards all unvectorized or unstaged changes in your current directory and its subdirectories. It completely overwrites your modified tracked files with the last committed versions or whatever is currently in your staging area.
The -- tells git that everything after it is a path, not an option, and the . means "current directory and everything in it." This is a destructive operation—changes are lost and cannot be recovered.
It can be helpful to include ticket numbers in commit messages for searching through later if questions arise. The number can also be referenced in the branch name so changes clearly relate to a given ticket (which can help in PRs, including the title). That can help with multiple things from general research in PR history to cherry picking commits.
For committing code, I like small, incremental changes to be made with short, specific messages describing the change. Typically commits should make only one significant change and not make multiple changes at once.
I've also learned that messages should not include a period at the end of the first line. This is because the first line is really a title line and titles don't have periods at the end. You're also trying to limit the line to 50 characters so removing the period saves a character.
From the git commit man page, "though not required, it’s a good idea to begin the commit message with a single short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough description. The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git. For example, Git-format-patch(1) turns a commit into email, and it uses the title on the Subject line and the rest of the commit in the body."
git rerere stands for "reuse recorded resolution". It is a hidden gem in Git that remembers how you resolved a merge conflict and automatically applies the exact same fix the next time it encounters that same conflict.