We may not keep track of what we do each day, but commit does.

Commit information management is a part of daily development that is often overlooked, but when we encounter the following situations, we realize that meaningful commit information is critical to project management.

  • When initiating PR, the commit information speeds up the Code Review process
  • At project Release, Changelog can be generated based on the COMMIT information
  • Commit information can help us locate bugs when they occur in the project
  • When working with multiple people, commit information allows us to understand why others commit code each time

Problems with current project commit information

Problem 1: Fill in the COMMIT information freely

For example, one of our front-end projects currently has the following commit information:

As you can see from the commit information in the red box above, every time the code is committed, the information is filled in randomly by the developer, which leads to increased maintenance costs for the project, and we don’t even know what we committed.

The solution

The idea behind a canonical COMMIT is that when a developer commits a Commit message, it checks the commit message and terminates the commit if it does not meet the specification.

Git hooks are scripts that trigger the execution of certain events (commit, push, etc.) in Git. These hooks help you customize some of the processing logic.

Because this is a front-end project, we chose Husky in the Node.js ecosystem to make it easier to use Git Hooks.

First install Husky

npm i -D husky
Copy the code

Then add the following code to package.json to install Husky.

{
  "scripts": {
    "prepare": "husky install"
  }
}
Copy the code

Prepare is the NPM lifecycle Hook and will be called by NPM after NPM install. This ensures that husky initialization is done locally by each developer when they run NPM Install to install front-end project dependencies.

After husky is initialized we can see the./husky directory in the project root directory. Next we can check the commit information when the developer commits by adding Husky Hooks.

We use Google’s Angular project specification and install the specification validation tool first:

npm i -D @commitlint/config-conventional @commitlint/cli
Copy the code

A large Project like Angular is very strict about the commit information specification, some of which are not suitable for our team, so we need to customize our own set of checking rules.

Set validation rules in the project root directory: create.commitlintrc.js

module.exports = {
    extents:[
        "@commitlint/config-conventional"
    ],
    rules:{
        'body-leading-blank': [1, 'always'],
        'footer-leading-blank': [1, 'always'],
        'header-max-length': [2, 'always', 72],
        'scope-case': [2, 'always', 'lower-case'],
        'subject-case': [
            2,
            'never',
            ['sentence-case', 'start-case', 'pascal-case', 'upper-case']
        ],
        'subject-empty': [2, 'never'],
        'subject-full-stop': [2, 'never', '.'],
        'type-case': [2, 'always', 'lower-case'],
        'type-empty': [2, 'never'],
        'type-enum': [
            2,
            'always',
            [
                'chore',
                'docs',
                'feat',
                'fix',
                'refactor',
                'revert',
                'test'
            ]
        ]
    }
}
Copy the code

Next we will add a Huksy Hook that will trigger the specification check tool when the user enters the commit message. Add the commit-msg file to the.husky directory in the project root directory and type the following:

#! /bin/sh . "$(dirname "$0")/_/husky.sh" npx --no-install commitlint --edit $1Copy the code

One thing to note here is that we use NPX instead of NPM because the commit validation tool is only installed in the current project, and NPM only has two ways to run the library in the project:

  • Use the NPM run-scripts format
  • By specifying the installation path for example./node_modules/commitlint/.bin

This makes scripting complicated and requires importing environment variables, so NPX with –no-install will automatically find the libraries in the project and run them.

With everything in place, when we randomly fill in the commit message again, we get the following error and cannot commit the code.

Once the commit Message is written according to the specification, we can commit the code as normal.

Add –no-verify at Git commit time to skip spec checks for emergencies. But hopefully you’ll never need it.

Problem 2: The commit record is messed up because of different code formats

When checking a commit, the diff reads as follows:

As you can see, the Code on the left and the Code on the right generates a COMMIT record due to different formats. This is because there is no uniform Code format between developers, which seriously affects Code Review and collaborative development.

The solution

Before submitting your code, run the Linter tool to format it.

Our front-end project is based on the Vue Cli 3 scaffolding and comes with a linter tool, which we add to NPM Script.

 "scripts": {
    "lint": "vue-cli-service lint --fix",
    ...
Copy the code

The next thing you need to do is add a Husky Hook to run the lint command you just added before the user commits the commit message.

husky add .husky/pre-commit "npm run lint"
Copy the code

When we commit again, we run Linter to format the code before committing.

Problem 3: There is no meaningful Merge record in the COMMIT record

After continuing to examine the commit information for the project, we found a number of Merge information as follows:

In multi-party collaborations, we run git pull to ensure that the local code is in sync with the remote code before committing it locally and pushing it to the remote server. Git pull is essentially a combination of git fetch and Git merge, which produces the merge record that is committed and pushed to the server along with our commit.

The solution

The git pull command has two rules:

  1. If the remote branch is ahead of the local branch (i.e., the remote repository has updates), and there is no local commit operation, git pull uses the fast-forward mode. This mode does not generate Merge nodes, and does not generate Merge branch XXX of… Information.

  2. If the remote branch is ahead of the local branch (i.e., the remote repository is updated) and the local branch has a commit operation, git pull will require the branch to Merge (i.e., resolve and Merge the conflict), resulting in a Merge branch XXX of… Information.

In case 2, we can replace git pull with git pull –rebase. In rebase mode, no new COMMIT nodes are generated when branches are merged, and no Merge records are recorded.

Git pull defaults to Rebase mode with the following git global Settings:

git config --global pull.rebase true
Copy the code

Problem 4: There are multiple commit records for the same feature or bug

In addition, there are many records of the same commit information:

Or this:

There can be many reasons for such a submission, such as:

  • Polish the document repeatedly
  • Daily submission record
  • Fix the same Bug multiple times

Such multiple commits still lead to confusion of commit information and make Code Review very difficult.

First of all, frequent commits on the developer’s own branch are encouraged. And developers should try to keep each commit “single” so that we can better respond when requirements change.

Here are two best practices for committing:

  1. One Thing, One Commit

When committing a COMMIT, try to ensure that the commit only does one thing, such as implementing a feature or modifying the configuration file.

  1. Don’t commit half your work

When the development task is not complete, do not commit. This is not to say that every commit needs to develop a very complete feature, but rather that it should be broken down into many small but still complete feature points.

For example, we actually encountered A scenario where the product manager said that feature A was not required for this release, but was required for the next release. In this case, we usually find out all the codes related to function A and comment them out, which will cause code confusion, which is difficult to track and manage. If we were in the habit of making a single commit for each feature from the start, we could cherry pick which commit we wanted to combine with the main branch.

The solution

The current problem is that the developer’s own branch does not merge the commit when merging the main branch, resulting in a mess of commit information.

So before merging the code into the multiplayer branch we need to tidy up the commit, merging some of the commits into a single commit.

Here’s an example of a merge COMMIT:

Git log shows that the last three commits are about document content modification

Git rebase -i HEAD~3 git rebase -i HEAD~3

At this point we change the first two commits from pick to squash (or s for short) and close the file.

A file pops up asking us to edit the commit information for each of the three commit merges and fill in the commit information to complete the merge.

Git log again checks the commit record and finds that the commit has been merged.

Problem 5: Artificial commitments to commit rules are unreliable

In software design we often hear convention over configuration, which is convention over configuration. This design paradigm reduces the mental burden of developers because conventions are standards. By following the standards, we will reduce the probability of mistakes.

But artificial conventions are unreliable in project management, so we need tools to constrain developers or push standards.

The solution

With the Commitizen tool, we can simplify the writing of commit messages for developers, lowering the bar for advancing projects.

Installation tools:

npm i -D commitizen cz-conventional-changelog
Copy the code

Then add NPM run-scripts to package.json

 "scripts": {
    "commit": "git-cz",
    ...
Copy the code

Then when we commit the code, we simply run NPM Run commit and use the Commitizen tool to commit

As you can see from the figure above, the Angular submission tool is a bit strict with us and requires a lot of interactive q&A. Our front-end project was not very complex, so we needed to customize the Commitizen submission tool to simplify use.

Install the cz – customizable:

npm i -D cz-customizable
Copy the code

And add the following Settings to package.json:

"config": { 
    "commitizen": { 
        "path": "node_modules/cz-customizable" 
      } 
 }
Copy the code

Finally, we customized the submission tool according to our team situation:

Create.cz-config.js in the project root directory

module.exports = {
      types: [
        {      value: 'feat',      name: 'feat:     A new feature'    },
        {      value: 'fix',      name: 'fix:      A bug fix'    },
        {      value: 'refactor',      name: 'refactor: A code change that neither fixes a bug nor adds a feature'    },
        {      value: 'docs',      name: 'docs:     Documentation only changes'    },
        {      value: 'test',      name: 'test:     Adding missing tests or correcting existing tests'    },
        {      value: 'chore',      name: 'chore:    Other changes that do not modify src or test files'    },
        {      value: 'revert',      name: 'revert:   Reverts a previous commit'    },
      ],
      messages: {
        type: 'Select the type of change that you are committing:\n',
        subject: 'Provide a description of the change:\n',
        footer: 'Does this change affect any open issues? E.g.: #32, #34 (optional):\n',
      },
      skipQuestions:['scope', 'breaking', 'body'],
  }
Copy the code

We defined several common commit types such as feat, Fix, refactor, etc., and simplified the submission process into 3 steps as follows:

conclusion

After this transformation, our COMMIT information has been greatly changed compared with the previous one

Before:

After:

In addition, we can easily generate Changelog through the Changelog tool at release, so that we can make changes to the project at a glance.

Add a changelog command to the scripts of package.json:

 "scripts": {
     "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
     ...
  }
Copy the code

After that, we can get the same changelog as the picture below by NPM run Changelog.

Limiting developers’ commit information essentially increases the professionalism of our software developers, allowing us to be more confident in handling maintenance and collaboration on large projects.