This project meant to be a tutorial for getting basic/advanced knowledge in using git commands. It also covers how to introduce conventional commits and generate changelog.
Git is a versioning tool, so it is not only contain the latest version of the software, but every version over the time. It stores the changes in commits, which can be stored on multiple branches.
Git uses local-remote repository strategy to add the ability for the developers to write their code in a collaborative way. Git provides tools to create or manipulate changes and how to put the changes of different developers together.
In this workshop, we are going to learn some commands which might be useful during operating with git.
Let's git some knowledge! đ§
To start practicing, it is useful to clone this repository and read the README file. If you want to get hands-on experience you can checkout some of the branches and try the commands.
Table of contents generated with markdown-toc
It is recommended to use several tools:
- the repository itself cloned to your local environment
- Visual Studio Code
- Git Graph VSCode extension
- installed git command đ
Git aliases | Docs
If you want to speed up writing git commands, you should add aliases to git. Frequently used aliases:
git checkout
# setup alias
git config --global alias.co checkout
# checkout existing branch
git co <oldbranch>
# create new branch
git co -b <newbranch>
git commit
# setup alias
git config --global alias.c commit
# create a new commit
git c -m "<message>"
git reset HEAD^1 --soft
# setup alias
git config --global alias.uncommit 'reset HEAD^1 --soft'
# remove one commit and add its changes to the staged
git uncommit
If you want to create new branches, you may know the branch
command.
# create a new branch in the local repository
git branch <new_branch_name>
# move to the new branch
git checkout <new_branch_name>
or the short command with checkout
.
# create a new branch AND move to the new branch
git checkout -b <new_branch_name>
If you want to delete your obsolete branch, you can always append the -D
argument to the branch
command.
# delete the branch locally
git branch -D <obsolete_branch>
If you know that you deleted the branches on the remote repository and you want to apply those changes locally you can use the following command.
git fetch --prune
Standard example when you want to create a new commit with message.
# add everything to stage
git add .
# create a commit
git commit -m "<commit_message>"
or the short version.
# add everything to stage AND create a commit
git commit -am "<commit_message"
If you made a typo in the commit message.
# append your staged changes to the previous commit and also rename it
git commit --amend -m "<updated_commit_message>"
or if you just forgot something adding to the commit, you can append the --no-edit
arguments.
# append your staged changes to the previous commit
git commit --amend --no-edit
Extra
If you have already pushed your commits to the remote repository you can also use the --force
argument with the push
command.
# update the remote history by force
git push origin <remote_branch> --force
đ¨ This will override the remote history, thus it is dangerous and often disabled!
To check the latest commits on the branch you can always use the log
command
# check the last commit
git log -1
# check the last 3 commit
git log -3
or even display the logs in a pretty graph format.
# print a graph based on your local branches and their commit histories
git log --decorate --oneline --graph --all
âšī¸ Don't forget to add it as an alias!
# setup alias
git config --global alias.graph 'log --decorate --oneline --graph --all'
âšī¸ It is advised to install the Git Graph
extension of Visual Studio Code!
In case you made a wrong commit and you want to undo your mistakes, you have several options based on if you have already pushed your commit to the remote repository and/or the commit is not on the top of the branch.
If the commit is on the top of your branch you can reset your commit by using the following command
# delete the last commit and all of its changes
git reset HEAD^1 --hard
if you already pushed to your remote branch you have to apply the deletion on the remote as well.
# update the remote history by force
git push origin <remote_branch> --force
đ¨ This will override the remote history, thus it is dangerous and often disabled!
If that is not the case, then it is advised to use the revert
command. By looking at the hash of the right commit you can revert your changes in no time.
# find the wrong commit
git log
# create new commit with the opposite changes
git revert <commit_hash>
It will create a new commit with the opposite changes of the reverted commit.
If you don't want to commit your changes, because maybe you forgot to do something else before, you can stash your changes by using the stash
command.
# add changes to the stage
git add # . or specific files
# save it for later
git stash
# ...later
# it adds your last stash to the stage and also deletes this stash
git stash pop
âšī¸ If you use the stash more frequently, you can attach name to them.
# stash your staged changes with a specific name
git stash save <stash_name>
# ...later
# to find what is the index of your stash
git stash list
# add the changes of the stash to your stage.
git stash apply <stash_index>
âī¸ Extra: Bisect | link
This is not an often used command but can be useful if you want to find an old commit you suspect to be wrong. By starting this command it will jump back and forth between commits and you have evaluate those states to be good or bad. It will logs the hash of the wrong commit. How should you proceed:
# start the bisect process
git bisect start
# usually if the HEAD commit is wrong
git bisect bad
# find a commit where it worked still well
git bisect good <still_good_commit_id>
The bisect process will start jumping from commit-to-commit along the history and on every commit, you can evaluate if that commit was good or bad. The most effective way is, if the tests cover the bad behaviour. In this case you can just run your test command on every commit the command jumps to.
# run test
yarn test # or npm run test or anything else
# if it worked
git bisect good
# if not
git bisect bad
After this, the process jumps to the next commit and you must evaluate it again with the same strategy. In the end the process will tell you, which commit was the wrongdoer.
# result can be the following
caf8c7eb4f9e2aa392ae15aa2a920859c19c43bf is the first bad commit
commit caf8c7eb4f9e2aa392ae15aa2a920859c19c43bf
Author: csakbalint <[email protected]>
Date: Thu Jul 7 22:49:17 2022 +0200
chore(test): added environment and scripts
jest.config.js | 13 +
package.json | 6 +-
yarn.lock | 1897 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 1902 insertions(+), 14 deletions(-)
create mode 100644 jest.config.js
It's important to provide a good release experience during development, because it helps the other parties to understand, what is the latest state of the software, and how did the code become to that.
âšī¸ This section is not meant to help understanding the whole concept of release strategy but to provide basic tools to introduce it.
It might be useful to checkout the section/conventional-commits
branch, because it contains the necessary changes to introduce the release and versioning
In JavaScript environment, we can use husky, lint-staged, commitlint, conventional-commits, and standard version libraries. With these libraries we can prepare the developers and development environment to be able to create changelog-ready commits and generate releases.
What are these libraries and what are they for:
- husky: add the ability to use git hooks in Node.js environment
- commitlint: enforce naming conventions on commit messages.
- lint-staged (optional): help executing scripts only on taged files. It uses husky under-the-hood.
- standard-version: generate changelog and release commit. It works with conventional commits.
# install dependencies
yarn add -D @commitlint/cli @commitlint/config-conventional
yarn add -D husky lint-staged standard-version
Create commit message hook file
npx husky add .husky/commit-msg "npx --no -- commitlint --edit "${1}"
it will looks like this.
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no -- commitlint --edit "${1}"
âšī¸ It should be an executable file, otherwise the git won't run it.
Add the following scripts to package.json
# extend the scripts in package.json
"release": "standard-version",
"release:minor": "standard-version --release-as minor",
"release:patch": "standard-version --release-as patch",
"release:major": "standard-version --release-as major"
# append config to the end of the
"lint-staged": {
"*.{js,ts}": "eslint --fix"
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
Create .versionrc
{
"types": [
{
"type": "feat",
"section": "Features"
},
{
"type": "fix",
"section": "Bug Fixes"
},
{
"type": "chore",
"hidden": true
},
{
"type": "docs",
"hidden": true
},
{
"type": "style",
"hidden": false
},
{
"type": "refactor",
"hidden": true
},
{
"type": "perf",
"hidden": true
},
{
"type": "test",
"hidden": true
}
],
"commitUrlFormat": "https://github.com/csakbalint/allwin-git-workshop/commit/{{hash}}",
"compareUrlFormat": "https://github.com/csakbalint/allwin-git-workshop/compare/{{previousTag}}...{{currentTag}}"
}
âšī¸ You can configure it freely, based on what you want to be in the changelog.
Create commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
If you want to create a release with the standard-release
library, it will generate a changelog, based on the previous commit history and creates a commit with the first version found in the package.json. It also attach a version tag to it.
For the first you want to execute the following command.
# create your very own first release
yarn release --first release
# push the release commit with the tag to the remote branch
git push origin <remote_branch> --tags
đ¨ It is important to push the tags, in case you are not the only developer making releases, because standard-version rely upon tags during releas.
Whenever you want to create a release, uou must determine, how has your code been changes:
- increase the major version (x.0.0), if any of your existing and used functionalities has been broken.
- increate the minor version (0.x.0), if you introduce new funtionality or feature
- increate the patch version (0.0.x), if fixed one of your broken features.
âšī¸ To fully understand the release process, you should understand the semantic versioning. You can read more about it here.
# increase major version
yarn release:major
# increate minor version
yarn release:minor
# increate patch version
yarn release:patch
It will automatically generates the changes of the changelog, add the option to edit it, then creates a commit and attach a tag.
đ¨ Be aware, standard-version
calculates the changes from the previous tag.
I hope you found this tutorial useful! And keep this in your mind: