Git
Git is a version control system.
Introduction
There are a few basic commands that you should be familiar with. The following table covers 99% of all the commands I use with Git in the command line.
Family | Task | Command |
---|---|---|
Configuration | Set name and email | git config --global user.name "Leo Leung"
|
Branches | Creates a new branch and switch to it immediately | git checkout -b [branch-name]
|
Switches to the branch | git switch -c [branch-name]
| |
Combines the specified branch to the current branch | git merge [branch]
| |
Deletes the specified branch | git branch -d [branch]
| |
Create a new branch from a specific commit | git branch [branch] [commit-id]
| |
Tags | List tags. (-n for description) | git tag -n
|
Fetch tags | git fetch --all --tags
| |
Checkout a specific tag | git checkout tags/[tag] -b [branch]
| |
Synchronizing Changes | Downloads all history from the remote repo | git fetch
|
Combines remote branches to local branch | git merge
| |
Uploads all local branches to the remote repo | git push
| |
Update the local branch with all new commits on the remote repo | git pull (git fetch + git merge)
| |
Changes | Show the git version history on the current branch | git log
|
Show the difference since the last commit | git diff
| |
Show the difference between two branches | git diff [branchA] [branchB]
| |
Add a file to be committed | git add [file]
| |
Create a new commit | git commit
| |
Amend the last commit | git commit --amend
| |
Amend the last commit with different author | git commit --amend --author="Leo Leung <leo@somewhere>"
| |
Mistakes | Resets all changes after the specified commit | git reset [commit]
|
Discards all history after the specified comit | git reset --hard [commit]
| |
Restore a deleted file (staged, before it's committed) | git checkout -f [file]
|
Committing Files
A commit is like a snapshot of the code at a particular time in the repository. It is possible to recover code to a previous commit if something were to happen to the code (such as accidental bugs being introduced, or accidentally deleting or modifying some file).
Similar to SVN where files are included/excluded to a commit, git allows you to stage/unstage files to commit. A file that is staged will be added to the next commit that is made.
Typically, you will want to stage all files for commit which can be done by running:
$ git add -A .
Run git status
to see all files that are staged as well as any changed but unstaged files.
$ git status
To commit all the staged files with a message:
$ git commit -m 'message'
If you need to commit your changes to a separate branch, such as if you started working on a new feature but it isn't quite ready yet, create a new branch and then commit.
$ git checkout -b wip_feature
$ git commit -a
Reverting Changes
To revert all changes in the current repository, use:
## Fetch latest from remote
$ git fetch --all
## Reset all changes fetched previously.
$ git reset --hard HEAD
To revert a specific file from a specific revision, use git checkout
.
$ git checkout <revision> <filename>
Remote repositories
Git allows you to push/pull changes from other remote git repositories. Remote repositories can be in another path or hosted somewhere on the internet.
To see your repository's remotes:
$ git remote -v
To add a new remote:
$ git remote add origin git@gitlab-blah:user/repo.git
To remove a remote:
$ git remote rm origin
If your remote uses SSH, you will most likely need to configure SSH in order to make use of SSH key based authentication. You can read more about this at SSH Configuration File, but the basic idea is to create a SSH configuration file at ~/.ssh/config
defining the remote's host.
Host gitlab-remote
User git
HostName git.example.com
IdentityFile ~/.ssh/gitlab-remote
Branches
Create a new branch
To create a new branch from the master branch
$ git checkout -b new-branch-name
To create a branch from an existing branch
$ git checkout -b new-branch-name existing-branch
Delete a branch
To delete a branch
$ git branch -d existing-branch
Pushing changes upstream
After making a new change in your repository, you may want to push your changes out to other repositories:
$ git push
If you need to specify the remote origin and branch (Eg. remote origin and master branch):
$ git push -u origin master
Tags
List tags
Local tags can be listed with git tag
. Some additional options that are helpful are:
-n
option to show tag descriptions and-l
to limit by pattern.- -
-sort=refname
to sort by name or--sort=version:refname
to sort by version numbers
Remote tags can be listed with git ls-remote --tags [remote]
. Eg: git ls-remote --tags origin
The latest tag available can be found with git describe --tags `git rev-list --tags --max-count=1`
Fetching tags
Remote tags can be fetched into your local repo using git fetch --all --tags
. This will pull all remote tags and make them appear as local tags.
Checkout tags
You can 'switch' into a tag on a particular branch using git checkout tags/[tag] -b [branch]
.
Pulling & Merging
When implementing a feature, it's ideal to separate each feature into its own branch. To test multiple features together, you will need to merge these branches together. Do this using the git pull
command.
## Work on a feature
$ git checkout -b feature-A master
## Do your work and commit
$ git commit -a
$ git push origin feature-A
## Work on another feature
$ git checkout -b feature-B master
$ git commit -a
$ git push origin feature-B
## Then, to test both feature-A and feature-B together:
$ git checkout -b testing master
## Pull one, or multiple branches at once with the pull command.
## You will be asked to merge these commits to your branch.
$ git pull origin feature-A
$ git pull origin feature-B
## At this point, your code now should have the changes from both branches.
Note that git pull does a git fetch
and git merge
as one command. If you do not want to merge, run git fetch
instead.
Merging
After creating a new branch development
and making changes to this branch, you may want to merge the changes on the development
branch back into master
.
development$ git merge master
## Resolve any conflicts
development$ git checkout master
master$ git merge --no-ff development
The --no-ff
flag prevents the merge from executing a fast-forward. This ensures that a new node will be constructed that records the merge event instead of a fast-forwarding on the branch.
Restore a deleted file
If you notice files are missing somewhere and aren't sure which commit triggered its deletion, run the following to determine the last commit that altered this path.
## Missing 'scripts' directory. Find the commit that deleted it.
$ git rev-list -n 1 HEAD -- scripts
2709b067bfc836c471ffa02ba569537e459e5e01
## To restore 'scripts', checkout that path from the previous (^) commit.
$ git checkout 2709b067bfc836c471ffa02ba569537e459e5e01^ -- scripts
See also: https://stackoverflow.com/questions/953481/find-and-restore-a-deleted-file-in-a-git-repository
Rebasing
If for some reason your master has diverged from the upstream repository (such as if changes were made to master simultaneously while you were working on your local copy), you will notice that the history between both repositories are different. A git pull will result in either a message about a diverged branch, or conflicts between the local and remote changes.
The fix is to either:
- Merge your work upstream (
git merge origin/master
), thereby integrating your changes with the upstream repo and then commit your changes, or - Rebase your work on the upstream master using the
git rebase origin/master
command. Alternatively, you can also git pull with the rebase option (git pull --rebase origin/master
) to rebase with the local work as part of the pull. - If you don't care about your changes,
git reset --hard origin/master
will bring your local repo up to speed with the upstream, discarding any changes you've already made.
Identities
Users typically use the global identity when using Git. This is great if you use the same identity across all repos.
In order to use different identities, unset the global identity and require that identities are specified from each repository's configuration. Without specifying any identities, git commits will use the system's hostname and username which is not desired.
# git config --global --unset user.name
# git config --global --unset user.email
# git config --global --unset user.signingkey
# git config --global user.useConfigOnly true
Create each new identity.
See also: www.micah.soy/posts/setting-up-git-identities/
Tasks
Removing a large file from commit history
If someone accidentally committed a large file into git, you can remove it using the BFG Repo-Cleaner (https://rtyley.github.io/bfg-repo-cleaner/) java utility. This tool will look for any deleted files and remove it from the history. In doing so, it will also rewrite all the affected commits.
$ java -jar bfg.jar --strip-blobs-bigger-than 10M my-repo
$ cd my-repo
$ git reflog expire --expire=now --all
$ git gc --prune=now --aggressive
## If you have the repo hosted somewhere, do a force push to overwrite the history there.
$ git push --force
In GitLab, ensure that all affected branches are deleted and then re-pushed. Otherwise, the file might still be lingering even after doing a house cleaning.
Correcting multiple commits' author
If you accidentally committed something with the wrong name and email address, you can correct it with:
$ git commit --amend --author="Leo Leung <leo@example.com>"
If you've made multiple commits with the wrong name and email address, then you'll have to use git filter-branch --commit-filter
to rewrite the history like so:
## Change the author for the past 2 commits
$ FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --commit-filter '
GIT_AUTHOR_NAME="Leo Leung";
GIT_AUTHOR_EMAIL="leo@example.com";
git commit-tree "$@";
' HEAD~2..HEAD
See Also
Self hosted applications:
- Gogs
- GitLab
- Gitea, a fork of Gogs but has feature set that rivals GitLab.
Tools:
- Lazygit - https://github.com/jesseduffield/lazygit
Resources
- GitHub Cheat Sheet: https://github.com/github/training-kit/blob/master/downloads/github-git-cheat-sheet.md
- Commit style guide: http://udacity.github.io/git-styleguide/