From the perspective of front-end engineering, team collaboration and production deployment, this paper introduces the practical ability of Git that architects need to master.
Outline the preview
The content of this article includes the following aspects:
- Branch Management Strategy
- Commit specification and commit validation
- Misoperation of the withdrawal scheme
- Tag and production environment
- Permanently eliminate 443 Timeout
- Hook implementation deployment?
- Ultimate application: CI/CD
Branch Management Strategy
Git branches are powerful and flexible at the same time. Without a good branch management strategy, team members can merge pushes randomly, resulting in branch chaos, various overwrites, conflicts, and loss issues.
The most popular branch management strategy, also known as Workflow, consists of three main types:
- Git Flow
- GitHub Flow
- GitLab Flow
Our front end team combined with the actual situation, developed their own set of branch management strategy.
We divide the branches into four broad categories:
- dev-*
- develop
- staging
- release
Dev -* is a general term for a group of development branches, including the personal branch, module branch, fix branch, etc., on which team developers work.
Before developing, merge the latest code from the Develop branch. Once the development is complete, you must merge back into the Develop branch via cherry Pick.
Develop is a separate branch, corresponding to the development environment, that keeps the latest and complete development code. It only accepts cherry-pick merges and does not allow merge.
The staging branch corresponds to the test environment. When there are updates to the Develop branch and you are ready to release tests, staging merges the Develop branch through Rebase and releases the latest code to the test server for testers to test.
Once the test finds a problem, go to the dev-* -> develop -> staging process until the test passes.
Release represents the production environment. The latest commit of the Release branch is always synchronized with the online production environment code, which means that the Release branch is always releasable.
When the staging tests pass, the release branch merges the staging branch through Rebase and releases the latest code to the production server.
To summarize the merger rules:
- develop -> (merge) -> dev-*
- dev-* -> (cherry-pick) -> develop
- develop -> (rebase) -> staging
- staging -> (rebase) -> release
Why do you have to use cherry-pick to merge to Develop?
If a merge occurs, a fork is generated. Dev -* has a lot of branches, and merging directly into Develop creates a maze of forks that makes it difficult to keep track of commit progress.
Cherry-pick, on the other hand, merges only the required commit to the Develop branch without bifurcating, keeping the Git commit graph always straight.
Also, after the module development branch is complete, multiple commits need to be merged into a single commit, and then merged into the Develop branch to avoid redundant commits. This is one of the reasons why merge is not used.
Why do you have to use rebase to merge to staging/release?
Rebase. Again, merging does not produce forks. When Develop updates many features to incorporate into staging tests, it’s not possible to merge the commit one by one with cherry-pick. So rebase is a one-time merge, and staging is guaranteed to be fully synchronized with Develop.
The same goes for Release. Once the test passes, rebase can be used to merge staging once, which also ensures full synchronization between staging and release.
Commit specification and commit validation
Commit specifications refer to the description information entered during git commit, which must comply with the unified specifications.
Imagine if a team member’s commit is written randomly, and the rest of the team doesn’t know what feature was completed or what Bug was fixed during collaborative development and review, making it difficult to track progress.
In order to visualize the update content of a commit, the developer community has developed a specification that divides the commit by function, with fixed prefixes such as Fix:, feat:, that mark what the commit does.
At present, mainstream prefixes include the following parts:
build
: represents a build, which is available for release versionsci
: Updates automatic configuration such as CI/CDchore
: Miscellaneous, other changesdocs
: Update documentsfeat
: Common: indicates a new functionfix
: Common: Fixes bugsperf
: Performance optimizationrefactor
: refactoringrevert
: code rollbackstyle
: Style changestest
: Unit test changes
These prefixes have to be written every time you submit them, and many people don’t remember them at first. Here’s a handy tool that automatically generates prefixes. The address is here
First global installation:
npm install -g commitizen cz-conventional-changelog
Copy the code
Create the ~/.czrc file and write the following:
{ "path": "cz-conventional-changelog" }
Copy the code
Git cz: git commit git cz: git commit
Then up and down arrow select the prefix and follow the prompts to easily create a submission that meets the specification.
With the specification, it is not enough to rely on people’s voluntary compliance, but also to verify the submitted information in the process.
At this point, we’ll use something new — git hooks.
Git hooks are used to trigger custom scripts before and after a Git action occurs. These actions include commit, merge, push, etc. You can use these hooks to implement your own business logic at various stages of the Git process.
Git hook is divided into client hook and server hook.
There are four main client hooks:
pre-commit
: Run before submitting information to check the code in the staging areaprepare-commit-msg
: not commonly usedcommit-msg
: Very important, use this hook to check the submission informationpost-commit
: Runs after the submission is complete
Server hooks include:
pre-receive
: Very important, all the checks before push are herepost-receive
: not commonly usedupdate
: not commonly used
Most teams do validation on the client side, so we use the commit-msg hook to validate the COMMIT message on the client side.
Fortunately, there is no need for us to write validation logic manually, and the community has a proven solution: Husky + CommitLint
The Husky is used to create git client hooks, and the CommitLint is used to verify that the commit information complies with the above specification. Together, you can prevent the creation of commits that do not conform to the COMMIT specification and ensure the committed specification at the source.
Use husky + CommitLint here
Misoperation of the withdrawal scheme
Git is frequently used in development to pull push code, which will inevitably lead to wrong operations. Don’t panic at this point, Git supports a rollback scheme for most scenarios, so let’s summarize.
The retractions are basically two commands: reset and revert
git reset
The reset command restores the version according to commitId. Since a commitId is generated for each commit, reset can restore you to any version of history.
A commitId is a version
The format of the reset command is as follows:
$ git reset [option] [commitId]
Copy the code
For example, to withdraw to a commit, the command looks like this:
$ git reset --hard cc7b5be
Copy the code
How did commitId get the command above? Git log () : commitId (); git log () : commitId ();
Option = hard; option = hard; option = hard;
--hard
Undo commit, undo add, delete workspace change code--mixed
: Default parameter. Undo COMMIT, undo ADD, and restore the workspace change code--soft
: Undo commit, do not undo add, restore workspace change code
Be careful here –hard, restoring with this parameter deletes workspace code. If you have uncommitted code in your project, using this parameter will delete it without recovery.
In addition to using commitId restore, Git Reset also provides a shortcut to restore to the last commit:
$ git reset --soft HEAD^
Copy the code
HEAD^ represents the previous commit and can be used more than once.
In fact, the most common misoperation in daily development is this: just after the submission, suddenly found a problem, such as submission information is not written properly, or code changes are missing, then need to withdraw to the last submission, modify the code, and then resubmit.
The process looks something like this:
# 1. Go back to the last commit
$ git reset HEAD^
# 2. Modify code....# 3. Add staging
$ git add .
# 4. Resubmit
$ git commit -m 'fix: ***'
Copy the code
Git also provides a more convenient way to do this:
$ git commit --amend
Copy the code
This command directly modifies the current commit information. If your code changes, it’s much faster and more convenient to run git add and then run this command.
Another very important feature of Reset is that it actually backs up a version.
What does that mean? For example, the current commit, you’ve pushed it to the remote repository; Now that you have withdrawn a commit with reset, the local Git repository is one version behind the remote repository. If you push again, the remote warehouse will refuse, asking you to pull first.
If you need the remote repository to also back up the version, you need the -f parameter to force push, in which case the local code will override the remote code.
Note that the -f parameter is very dangerous! Do not use this parameter if you are not familiar with Git principles and the command line.
So how is it safer to withdraw the previous version of the code and sync to remote?
The solution is the second command: git Revert
git revert
Like reset, revert is a restore version, but they are implemented differently.
In simple terms, reset resets directly to the last commit, and the workspace code is also the last commit code. Revert adds a new submission, but it uses the code from the previous submission.
Therefore, the restored code is the same as the restored code. The difference is that there is a new commit and a reset.
Because revert is always adding a commit, the local repository version can be pushed directly to the remote repository. This improves security by eliminating the need to add a -f parameter to a reset push.
Having said the principle, let’s take a look at the use method:
$ git revert -n [commitId]
Copy the code
Master the principle of use is very simple, as long as a commitId can be.
Tag and production environment
Git supports attaching a tag to a historical commit. This tag is often used to identify important version updates.
The current common practice is to use a tag to represent the production version. When the latest commit passes testing and is ready for release, we can create a tag that represents the production version to be released.
Let’s say I want to send a v1.2.4 version:
$git tag -a v1.2.4-m"My version 1."
Copy the code
You can then view:
$git show v1.2.4 > tag v1.2.4 Tagger: ruims <[email protected]> Date: Sun Sep 26 10:24:302021 +0800 My version 1.2.4Copy the code
Finally, git push the tag to the remote:
$git push Origin v1.2.4Copy the code
Note here that the tag has nothing to do with which branch it is created in; the tag is just an alias for the submission. Git reset, Git revert, git revert
When the production environment has problems and the version needs to be rolled back, you can do the following:
$ git revert [pre-tag]
If the previous version was v1.2.3, then:$git revert v1.2.3Copy the code
In a repository with frequent updates and a large number of commits, tagging versions is cleaner and more readable.
Another way to think about the usefulness of tag.
As mentioned in the branch management policy section above, the Release branch is synchronized with production code. In the continuous deployment of CI/CD (covered below), we listen for the push of the Release branch and trigger an automatic build.
Wouldn’t it be better to listen for tag pushes and trigger automatic builds?
Many uses, still need to be considered.
Permanently eliminate 443 Timeout
Our internal repository is GitHub, which is notoriously slow to pull and push, and even generates an error: 443 Timeout.
So the way we start is we all turn on the VPN. Most of the time the speed was good, but there were occasional hours, or even days, when the code was stuck, seriously affecting the development schedule.
Then it occurred to me that the slow speed and timeout were caused by the wall, such as the home page of GitHub could not be opened. If HTTP or HTTPS is used to access a web site, it will be blocked.
Just do it. GitHub also supports SSH in addition to the default HTTPS protocol. I decided to try cloning code using SSH.
The tricky part about using SSH is that you need to configure a secret – free login. Otherwise, you need to enter the account password each time you pull/push.
GitHub’s official documentation for configuring SSH is here
If you have trouble with English, you can see it here
After generating the public key, open the GitHub homepage, go to Account -> Settings -> SSH and GPG keys -> Add SSH key, and paste the public key into it.
Now, let’s clone the code using SSH protocol, as shown in the following example:
$ git clone [email protected]:[organi-name]/[project-name]
Copy the code
Found instant clone down! A few more pull/push tests, speed up!
No matter which code management platform you use, if you have 443 Timeout problems, try SSH!
Hook implementation deployment?
Use Git Hook to implement deployment, should be hook advanced application.
There are many tools, such as GitHub and GitLab, that provide continuous integration, which is to listen for a branch push and then trigger automatic builds and automatic deployment.
In fact, despite the variety of these tools, the core functionality (listening and building) is still provided by Git. It’s just a better integration of core functions with its own platform.
Let’s ditch these tools today and go back to the basics and implement an automated deployment of a React project using pure Git. Knowing this core logic makes continuous deployment on any other platform less mysterious.
Because this part has a lot of content, we separate out an article with the following address:
Pure Git implements front-end CI/CD
Ultimate application: CI/CD
The words continuous integration, continuous deployment have also been mentioned in some of the above places, and now it’s time to start.
It can be said that all the specification rules written above are for the better design and implementation of this protagonist — CI/CD.
First of all, what is CI/CD?
Continuous Integration CD consists of two parts: Continuous Delivery and Continuous Deployment. Continuous Integration CD consists of two parts: Continuous Delivery and Continuous Deployment.
From a global perspective, CI/CD is a way to automate the process to deliver applications frequently to customers. This process runs through the entire application integration, testing, delivery, and deployment lifecycle, collectively known as the “CI/CD pipeline.”
Although both are automated pipelines like assembly lines, CI and CD have their own roles.
Continuous integration is the frequent integration of code into trunk branches. When new code is submitted, it is automatically built and tested, and when the test is passed, it is automatically merged into the trunk branch, enabling rapid iteration of the product while maintaining high quality.
Continuous delivery is the frequent delivery of new versions of software to the quality team or users for review. If the review passes, the production environment can be released. Continuous delivery requires that code (the latest commit of a branch) be in a releasable state.
Continuous deployment is when code is reviewed and automatically deployed to production. Continuous deployment requires that code (the latest commit of a branch) be ready to deploy.
The only difference between continuous deployment and continuous delivery is whether the deployment to production is automated.
Deployment automation may seem like a small step, but in practice it turns out to be the most difficult part of the CI/CD pipeline to implement.
Why is that? First, from continuous integration to continuous delivery, these steps are implemented by the development team. We worked within the team to produce a new version of the app for release.
Deploying the application to the server, however, is the job of the operations team. To achieve deployment, we need to communicate with the operation and maintenance team. However, the development students do not know the server, and the operation and maintenance students do not know the code, so it is difficult to communicate.
Moreover, operation and maintenance is manual deployment. If we want to achieve automatic deployment, we must have server authority and interact with the server. This is also a big problem, because the operations team is bound to be concerned about security issues, so the push is hampered.
At present, there are many mature CI/CD solutions in the community, such as the old Jenkins, circleci used by React, and GitHub Action which I think is the best, etc. We can integrate these solutions into our own system.
This article is already too long, so LET’s stop here. Next I will post a detailed CI/CD practice for react front-end projects based on GitHub Action. Keep an eye out for my column.
My column: Front-end DevOps