Git cheat sheet
The following are my notes from the git and GitHub bootcamp online course by Colt Steele on Udemy
Intro
Differences between git and git hub
- Git is the version control software that runs locally on your machine. You don’t need an account and you don’t need internet to use it. You can use git without ever touching GitHub
- Github: service that hosts git repositories in the cloud and make it easier to collaborate with other people. You need an account and it is an online place to share work done using git
- You don’t have to use git bash (command line style), you can use GUI like GitKraken
Configuring git
To check if git hub is installed / what version is it
git — version
To see the name associated with your work and configure it:
git config user.namegit config — global user.name “XXX XXXXX”
To configure the email address:
git config — global user.email blah@blah.com
To configure which editor you want to use (example below with notepad++)
https://git-scm.com/book/en/v2/Appendix-C%3A-Git-Commands-Setup-and-Config
git config --global core.editor "'C:\Program Files\Notepad\notepad.exe' -multiInst -notabbar -nosession -noPlugin"
The basics
Git command for adding and committing
git init: initialize a new repository
git status: git information on the current status of a git repository and its content. You can also use it to check if you are within a repo
git add: add one or multiple files to the staging area. i.e git add file1 file2 .
- git add . will stage all changes at once
- you can add a directory git add myDir/
git commit -m “my message”: can be think as a “check point”, we actually commit changes from the staging area.
- documentation recommends to use present tense with imperative for comment: “make X do Y” instead of “I changed X to do Y”
git log: track the various past commits you did. at the top of each commit is a commit hash (a long suite of letters an numbers) which is used to revisit a commit, undo a commit or check out a commit
- you can change the format with git log — pretty=oneline / git log — oneline to simplify the log
- git log shows you only the past commits of your current branch
— amend: used if you forgot to include a file in a commit, or had a typo in the commit’s comment. Only for the previous commit.
# example:git commit -m "xxxxxxxx"git add file_new.txtgit commit --amend
The basic git workflow
Overview: work directory > git add > staging area > git commit > repository
- work on stuff: make new files, edit, delete, etc..
- add changes: group specific changes together, in preparation of committing
- commit: commit everything that was previously added
Atomic Commit: keep each commit focused on a single thing (instead of everything at once). So use git add once you know which modified files can be put together
Ignoring files to track
Using the .gitignore file, you can avoid git tracking. For example, for API keys, credentials, log files, dependencies and package, etc.
To do so, create a file called .gitignore in the root of a repository. Inside the file, we can write patters to tell Git which files/folders to ignore:
- .DS_Store will ignore files named “.DS_Store”
- *.log will ignore any files with .log extension
- folderName/ will ignore an entire directory
Working with branches
Master branch
Branches are alternative timelines for a project. The default branch name is master (recently renamed “main” in 2020). Many people designate the master branch as the “official branch” for the codebase, the one that should always be working. However, from git point of view, the master branch is like any other branch — it doesn’t have to be the “master copy” of your project.
What is HEAD?
When you put git log, you can sometimes see in your past commit HEAD -> master.
HEAD is a pointer that refers to the current “location” in your repository. It points to a particular branch reference. So by default it will refer to the last commit on the master branch.
A good analogy is to think about a book with several bookmark. One bookmark indicates where I am in the book, another bookmark indicates where X is, etc. However, we can only be one to read the book at a given time and this is what HEAD indicates. So you can open the book at any bookmark place (or “checkpoint”) and this is where HEAD will be at.
Branches main commands
git branch: see the list of all existing branches. The current one is noted with “*”
git branch <branch-name>: create a new branch based upon the current HEAD. You are creating and not not moving to the <branch-name> branch
git switch <branch-name> : switch to the <branch-name>. git checkout is the old command to switch branches. It also do more things so to make it easier, git switch is used to switch branch
Note: if you have uncommitted work and that you want to change the branch, then you will lose your uncommitted work (if there is conflict between branch).
git branch — delete <branch-name>: delete a branch. You cannot be on the branch you want to delete
git branch -m <branch-name-new>: rename the branch. You have to be on the branch you want to rename
Merging Branches
Concept
- we merges branches. not specific commits
- we always merge to the current HEAD branch
Steps:
- switch to the branch you want to merge the changes into (the receiving branches)
- use the git merge command to merge changes from a specific branch into the current branch
git merge <branch-to-merge>
Fast forward merging
When you simply create a branch, commit few times and merge, we talk about a fast forward merging (i.e the pointer is just moved forward)
What if we add a commit on master? Merge Commit
If master had several commits while we worked on a separate branch, then we have what is called a merge commit. In this case when we will merge, there are 2 parents for the merging (your branch commit and the master branch commit)
Merging conflict
Steps:
- Open up the file(s) with merge conflicts. Note you have to stay on the same branch, you cannot go to the branch you want to be incorporated
- Edit the file(s) to remove the conflicts. decide which branch content you want to keep in each conflict. Or keep the content from both
- Remove the conflict “markers” in the document
- Add your changes and then make a commit (so no re-merge!)
Git diff
You use git diff to view changes between commits, branches, files, our working directory and more
git diff: By default, lists all the changes in our working directory that are NOT staged for the next commit
git diff HEAD: lists all changes in the working directory tree since your last commit
git diff — staged: list only the staged changes
git diff HEAD [filename] or git diff — staged [filename]: list changes on a specific files
git diff branch1 branch2: list the changes between two branches. The order of the branch does matter
git diff commit1_hash commit2_hash: list changes between two commits
Stashing with Git stash
What problem does git stash solve?
What if you have uncommitted changes on one branch and I switch on another branch? Example case: you have to fix a typo on another branch, etc…
Then two options:
- my changes come with me to the destination branch (if there are no conflict)
- git won’t let me switch if it detects potential conflicts (for example if master was changed in-between)
This is where git stash comes in. If I don’t want to bring the changes with me or if I am not ready to commit, I can use git stash to sort of pause to save my change without actually committing them. I can then return to them later.
Main Commands
git stash: take all uncommitted changes (staged and unstaged) and stash them, reverting the changes in your working copy. It is the same thing than git stash save
git stash pop: remove the most recently stashed changes in your stash and re-apply them to your working copy
git stash apply: apply whatever is stashed away, without removing it from the stash. This can be useful if you want to apply stashed changes to multiple branches
git stash list: you can add multiple stashes onto the stack of stashes. They will all be stashed in the order you added them. This command list them. You can then apply specific stash if you want with git stash apply stash@{2}
git stash drop <stash-id>: to delete a particular stash. for example git stash drop stash@{2}
git stash clear: clear out all stashes
Undoing changes and time traveling
Going to an old commit
git checkout <commit-hash>: going to a previous commit state. You are in a detached HEAD situation. Recall that HEAD points to a branch reference, and it points to the tip of the branch — not to a commit. So HEAD was changed from the last commit of a branch to a previous commit. if you need to re-attached the HEAD, simply switch back to whatever branch you were on before (git switch master for example)
git checkout HEAD~2: refers to 2 commits before HEAD. This is another way to go back to previous commit
git restore: another option.
Discarding changes
git checkout HEAD <filename>: discard any changes in that file, reverting back to HEAD. It means take the <filename> and bring it back to how HEAD is. So if you don’t have anything committed, you can bring back the file to the last commit status.
git restore <filename>: similar to above command, restore the file to the contents in the HEAD. Once done you cannot undo this action. if you want to restore to a specific commit, do git restore — source HEAD~1 home.html; it will restore the content of home.html to its state from the commit prior to HEAD
Unstaging files with restore
git restore — staged <filename>: the file will become unstaged
Undoing commits
git reset <commit-hash>: will reset the repo back to a specific commit. The commits are gone but the changes are still in the working directory. This is a simple “reset”. This is useful if the changes made were supposed to go on a new branch
git reset — hard <commit-hash>: hard reset up to the <commit-hash>. For instance, git reset — hard HEAD~1 will delete the last commit AND associated changes
Reverting commit with git revert
git revert and git reset are kind of similar in that they both “undo” changes, but they accomplish it in different ways
git reset actually moves the branch pointer backwards, eliminating commits
git revert <commit-hash> instead creates a brand new commit which reverses/undos the changes from a commit. Because it results in a new commit, you will be prompted to enter a commit message. This can be useful if you want to reverse some commits that other people already have on their machine. If you want to reverse commits that you haven’t shared with others, use reset and no one will ever know!
GitHub
Cloning
git clone <url>: clone a local repository hosted on GitHub by retrieving all files associated with the repo and initializes a new repo on your machine. Make sure you are not inside a repo when cloning!
SSH Keys
SSH keys are used to connect to GitHub without having to supply your username / password each time you do certain operations like pushing. You can find instruction steps on GitHub website
Creating a repo
Option 1: Existing repo
if you already have an existing repo locally that you want to get on GitHub…
- create a new repo on GitHub
- connect your local repo (add a remote)
- push up your changes to GitHub
Steps
- create an empty repository on GitHub
- set up git remote to tell git about our remote repository on GitHub. A “remote” is simply the destination, i.e the URL where the hosted repository lives.
- git remote add <name> <url>: add a new remote, using the <name>. Conventional name for the URL is origin
- git remote or git remote -v : to check on which remote you are. -v is for verbose
- git remote rename <old> <new>: if needed, rename the remote
- git remote remove <name>: if needed, remove the remote
3. Then push up your changes
git push <remote> <branch>: specify the remote we want to push up AND the specific local branch we want to push up to that remote. For ex: git push origin master. If you push up branches, then you have the possibility on GitHub to switch from master to the other branches. Note that you can decide to push a local branch to a remote branch of a different name: git push <remote> <local-branch>:<remote-branch>
git push -u origin master: the -u option allows us to set the upstream of the branch we are pushing. You can think of this as a link connecting our local branch to a branch on GitHub. Running git push -u origin master sets the upstream of the local master branch so that it tracks the master branch on the origin repo. Basically, once done, we can use git push shorthand which will push our current branch to the upstream (so it creates a shortcut)
Option 2: start from scratch
If you haven’t begun work on your local repo, you can…
- create a brand new repo on GitHub
- clone it down to your machine
- do some work locally
- push up changes to GitHub
Steps
- create a brand new repo on GitHub
- clone it down to our machine: check that you are in a new local folder without any repo and do git clone <URL>
- do some work
- git push origin master
Fetching and pulling
Remote tracking branches
“At the time you last communicated with this remote repository, here is where x branch was pointing”
They follow the pattern <remote>/<branch>.
- origin/master references the state of the master branch on the remote repo named origin
- upstream/logoRedesign references the state of the logoRedesign branch on the remote named upstream (a common remote name)
Run git branch -r to view the remote branches our local repository knows about
You can do git checkout origin/master to check the remote branch pointer. HEAD will be detached
Working with remote branches
When you clone a repo, you only get the master branch, not all the branches that exist. However, if you do git branch -r, you will see them. So the repo knows about it but you don’t have the files. By default, git do a connection with origin/main, but it doesn’t do it with origin/<some-branch>.
In order to work on a remote branch, and have your own branch connected to it, you have to do git switch <remote-branch-name>; name has to be the same
Git fetch
Fetching allows us to download changes from a remote repository, BUT those changes will not be automatically integrated into our working files
It lets you see what others have been working on, without having to merge those changes into your local repo
- from remote repo to local repo = git fetch
- from git repo to workspace = git pull
git fetch <remote>: default remote is origin if not specified. This would fetch all changes from the origin remote repository
git fetch <remote><branch>: retrieve info from a specific branch
For example, if I do git fetch origin master then I will have a new branch origin/master that I can look at but my master branch didn’t change. I can do git checkout origin/master to go check what work those done by my colleague
Note: git is not constantly asking GitHub if they are aligned, so you can see written “your branch is up to date with origin/movies” while in fact it is not true. You will have to do git fetch origin and git status to see if that’s really the case
Git pull
git pull updates our HEAD branch with whatever changes are retrieved from the remote. so go and download data from GitHub AND immediately update my local repo with those changes
Not recommended if you have uncommitted changes
git pull = git fetch + git merge
git pull <remote> <branch-name>: like git pull origin master
Git pull and merge conflict
As a good practice, before you push up anything on GitHub, you should always start by a git pull
Resolving conflicts are done in a similar fashion as seen before
Git collaboration workflow
Centralized workflow
- Everyone works on master/main
- The weakest of all the workflows
Problems:
- lot of time spent resolving conflicts and merging code
- no one can work on anything without disturbing the codebase
- The only way to collaborate on a feature together with another teammate is to push incomplete code to master. Other teammates now have broken code
Feature branches workflow
- very common workflow
- rather than work on master, all new development should be done on separate branches
- treat master branch as the official project history
- master branch won’t contain broken code
- multiple teammates can collaborate on a single feature and share code back and forth without polluting the master branch
Merging in feature branches
We have a couple of options:
- merge at will
- send an email / chat message to discuss if the changes should be merge in
- pull request
Note: there are conventions for branch name. For example feature/XXXX. See tips here
pull requests
Pull requests are built in to products like GitHub and are not native to git. They allow developers to alert team-members to new work that needs to be reviewed. They provide a mechanism to approve or reject the work on a given branch. You can open a pull request on GitHub when you are on a branch (“compare and pull request”)
Workflow looks like this:
- do some work on a feature branch and push it up to GitHub
- open a pull request using the feature branch you just pushed
- wait for the push request to be approved and merged / or discussed
In case of conflicts, you can deal it with normally as for previous cases
Fork and clone: another workflow
Instead of just one centralized GitHub repository, every developer has their own GitHub repository in addition to the “main” repo. Developers make changes and push to their own forks before making pull requests. It is very common for large open-source projects.
Forking
Similar to pull requests, it is not a git feature and was implemented in GitHub. Forking allows us to create personal copies of other people’s repositories. Those copies are a “fork” of the original. So it allows you to push to origin because the origin is now under your name.
My fork becomes the “origin” while I need to set up a new pointer to the original project repo (not the fork) that we will call (by convention) upstream. It allows me to have one place where I can do my changes (the fork) while keeping track of the original repository to pull last changes, etc… So you would push your change to your origin fork and then make a pull request
Summary
- fork the project
- clone the fork
- add upstream remote (git remote add upstream <url>)
- do some work
- push to origin
- open pull request
Rebasing
There are two main ways to use the git rebase command:
- as an alternative to merging
- as a clean up tool
An alternative to merging
If you work on a feature branch and the rest of your team is very active on the master branch, you constantly need to merge your branch into master to be up to date. It will results in dozen of your merged commit being merge to follow master, which are “useless” commits or at least not very informative. The commit history becomes not so clean.
Rebasing fixes this problem by “rewriting history” to create new commits for each of the original feature branch commits. We rebase the feature branch onto the master branch. This moves the entire feature branch so that it BEGINS at the tip of the master branch.
git rebase master
When not rebase
Never rebase commits that have been shared with others. If you have already pushed commits up to GitHub… DO NOT rebase them unless you are positive no one one the team is using those commits
Interactive Rebase
You can use git rebase as a clean up tool to rewrite, delete, rename and even reorder commits (before sharing them)
git rebase -i HEAD~4: -i is for the interactive mode and ~4 means 4 commits ago. We need to specify how far back we want to rewrite commits and you have to rebase on the HEAD the commits are based on
Once used, you will enter in a text editor, seeing a list of commits alongside a list of commands that we can choose from. You can then decide what to do with your commits
- pick: use the commit
- reword: use the commit, but edit the commit message
- edit: use commit but stop for amending
- fixup: use commit contents but meld it into previous commit and discard the commit message
- drop: remove commit
Combining commits with the fixup command
After doing git rebase -i HEAD~4, you can combine 2 commits together:
pick 000001 add project filespick 000002 add bootstrapfixup 000003 forget to add bootstrap js script
000003 will be merged to the upper commit 000002
Dropping commits
You can use “drop” to remove a commit entirely (all changes will be gone)
Git Tags
Tags are pointers that refer to particular points in git history. Tags are most often used to mark version releases in projects (v4.1.0, etc.)
Tags are like branch references that do NOT CHANGE. Once created it always refers to the same commit
2 types of tags:
- lightweight tags: just a name/label that points a particular commit
- annotated tags: store extra meta data including author’s name, email, date, tagging message
Versioning
The semantic versioning spec outlines a standardized versioning system for software releases.
Versions consists of three numbers separated by periods: for example, 2.4.1
- 2: is the major release. Significant changes that is no longer backwards compatible. features may be removed or changed substantially. Minor release and patch release are reset to 0
- 4: is the minor release. New features or functionality have been added, but the project is still backwards compatible. No breaking changes. The new functionality is optional and should not force users to rewrite their own code. Note that the patch number reset to 0 when minor releases increase.
- 1: is the patch release. Patch releases do not contain new features or significant changes. They usually mean bug fixes and other changes that do not impact how the code is used
Typically, the first release is 1.0.0. Prerelease can have things words added like v4.5.1- Beta
Viewing tags
git tag: print the list of all tags in the current repo
We can search for tags that match a particular pattern by using git tag -l and then passing in wildcard pattern. For example: git tag -l “‘beta”
To move to the state of a repo git checkout <tag_name>
To see the differences between 2 tags: git diff v17.0.0 v17.0.1
Creating lightweight tags
git tag <tag_name>: by default git will refer to the commit the HEAD is referencing
Creating annotated tag
git tag -a <tag_name>: it will open your default text editor and prompt you for additional information. Similar to git commit, you can use -m to pass a message directly
git show <tag_name>: to see the metadata information
Other
git tag -a <tag_name> <commit-hash>: tagging previous commits
git tag -f <tag-name>: we cannot reuse the same tag twice, except if you force our tag through with -f
git tag -d <tag-name>: to delete a tag
git push origin — tags: by default, git push doesn’t transfer tags to remote servers. If you need to push up all your tags, you can use the — tags option to git push. Use git push origin v1.5 to push a single tag