• How (and why!) to keep your Git commit history clean
  • By Kushal Pandya
  • Translation from: The Gold Project
  • This article is permalink: github.com/xitu/gold-m…
  • Translator: DM. Zhong
  • By Leo Lyu and YiLin Wang

How (and why) to keep your Git commit record clean

Git commit records can easily get cluttered. Here’s how to keep them clean!

Commit is one of the key parts of a Git repository, and commit information is the repository’s life log. As the project or repository evolves over time (new features are added, bugs are fixed, architecture is refactored), submission information becomes the place where people can see what has changed in the repository or how it has changed. Therefore, it is important to use short and accurate submissions to reflect internal changes.

Why is a meaningful submission record important?

Git commit information is the fingerprint you leave on the code you write. Whatever code you commit today, you’ll see it a year from now; You’ll appreciate your meaningful, clean submission, and it will make your colleagues’ work easier. When committing separately according to context, it is quicker to find out which commit the Bug was introduced, and it is easier to fix the Bug by going back to the commit that caused it in the first place.

When developing large projects, we often deal with a large number of component changes, including updates, additions, and removals. Ensuring that commit information is well maintained in this scenario can be difficult, especially when the development cycle is days, weeks, or even months. So to make it easier to maintain a commit record, this article will use a number of common scenarios that developers may often encounter when working on a Git repository.

  • Scenario 1: I need to modify the last commit
  • Scenario 2: I need to modify a particular commit once
  • Scenario 3: I need to add, remove, or merge the commit
  • Scenario 4: My submission record is useless, I need to start over!

But before we dive into that, let’s take a quick look at a typical development workflow for our hypothetical Ruby application.

Note: This article assumes that you know the basics of Git, how branches work, how to add uncommitted changes to a branch to the staging area, and how to commit changes. If you’re unfamiliar with these processes, our documentation is a good place to start.

Some day in my life

Right now, we’re working on a small Ruby on Rails project where we need to add a navigation view to the home page, which requires updating and adding a lot of files. Here are the steps of the process breakdown:

  • You start developing a feature, you update a file; Let’s call itapplication_controller.rb
  • This feature also requires you to update one view:index.html.haml
  • You added a section used by the index page:_navigation.html.haml
  • The stylesheet also needs to be updated to reflect what you added:styles.css.scss
  • After these modules are changed, the functionality is complete, and it’s time to update the test files; The files to update are as follows:
    • application_controller_spec.rb
    • navigation_spec.rb
  • The tests have been updated and passed all the test cases as scheduled, and now it’s time to commit the changes!

Because all of these files belong to different architectural domains, we commit changes to these files in isolation from each other to ensure that each commit represents a specific context and is committed in a specific order. I usually prefer the back end -> front end commit sequence: commit the back-end – centric changes first, commit the middle-tier file changes second, and commit the front-end – centric changes last.

  1. application_controller.rb & application_controller_spec.rb;Adding a Navigation Route.
  2. _navigation.html.haml & navigation_spec.rb;Page navigation View.
  3. index.html.haml;Render navigation section.
  4. styles.css.scss;Add styles to the navigation.

After committing the changes, we create a merge request for the branch. Once a merge request has been created, the code is usually reviewed by your colleagues before it is merged into the master branch of the repository. Now let’s look at the different situations you might encounter during code review.

Scenario 1: I need to modify the last commit

Imagine that the code reviewer is reviewing styles.css.scss and suggests a change. In this case, it’s very easy to change because the stylesheet change is the last commit on your branch. Here’s how to handle this situation:

  • You pair directly on your branchstyles.css.scssMake the necessary changes.
  • Once you have made the changes, add them to the staging area for staging; Run the commandgit add styles.css.scss.
  • Once the changes have been added to the staging area, we need to make themaddTo our last submission; Run command:git commit --amend.
    • Decomposition of the command: Here, we usegit commitThe commandModify theLast commit, incorporating any changes in the staging to the last commit.
  • This opens your last commit in your Git defined text editor, which has the commit information to style the navigation.
  • Since we only updated the CSS declaration, we don’t need to change the commit information. You can just save and exit the text editor that Git opened for you, and your changes will be reflected in the commit.

Since you are modifying an existing commit, you need to use the git push –force-with-lease

command to force the changes to your remote repository. This command overwrites the commit to style the navigation in the remote repository using the changes made in our local repository.

One thing to be aware of when you force a branch is that when your branch is a collaborative branch, your forced push may cause problems with other people’s normal push, as there are some new forced pushes on the remote branch. Therefore, you should use this feature wisely. You can learn more about Git push options here.

Scenario 2: I need to modify a particular commit once

In the previous scenario, because we only needed to modify the most recent commit, it was pretty easy to do, but imagine if the code reviewer suggested changing some part of the _navigation.html.haml file. In this scenario, it is the second commit, so the modification is not as straightforward as in the first scenario. Let’s see how to handle this situation:

Each time a change is committed on a branch, a unique SHA1 hash string is used to indicate that the change has been committed. Think of it as a unique ID that distinguishes each submission. You can run the git log command to see all the commits on a branch and their respective SHA1 hashes. After running the command, you can see output similar to the following, with the last commit at the top.

commit aa0a35a867ed2094da60042062e8f3d6000e3952 (HEAD -> add-page-navigation) Author: Kushal Pandya <[email protected]> Date: Wed May 2 15:24:02 2018 + 0530 for navigation to add style commit c22a3fa0c5cdc175f2b8232b9704079d27c619d0 Author: Kushal Pandya <[email protected]> Date: Wed May 2 08:42:52 the navigation part of the 2018 + 0000 rendering commit 4155 df1cdc7be01c98b0773497ff65c22ba1549f Author: Kushal Pandya <[email protected]> Date: Wed May 2 08:42:51 2018 + 0000 page navigation view commit 8 d74af102941aa0b51e1a35b8ad731284e4b5a20 Author: Kushal Pandya <[email protected]> Date: Wed May 2 08:12:20 2018 +0000 Added navigation routeCopy the code

Now it’s the git rebase command’s turn to perform. Whenever we want to modify a particular change commit with the Git rebase command, we first base the HEAD of our branch before the change we want to modify is committed. In this scenario, we need to modify the change commit of the page navigation view.

Now, notice the hash of a change commit before the change we want to modify is committed; Copy the hash and follow these steps:

  • By running a commandgit rebase -i 8d74af102941aa0b51e1a35b8ad731284e4b5a20To base the branch to the previous change commit that we want to modify
    • Decomposition of the commandNow we are using GitrebaseOf the commandInteractive modeBy submitting the SHA1 hash, we can base the branch.
  • This command will run the interactive mode of Git base command and open the text editor to show all commit changes after your base changes have been committed. This is what it looks like:
Pick 4155df1CDc7 Page Navigation view Pick C22a3FA0C5c Render navigation section Pick AA0a35a867e Adds style to navigation# Rebase 8d74af10294.. aa0a35a867e onto 8d74af10294 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Copy the code

Notice that each change is preceded by the word pick, and underneath it, there are all the keywords we can use. Because we want to edit a change commit, we change the command pick 4155df1CDc7 page navigation view to Edit 4155df1CDc7 page navigation view. Save the changes and exit the editor.

Your branch has now been based before the change containing _navigation.html.haml was committed. Open the file and complete the modification requirements in each review feedback. Once you’ve made the changes, save them temporarily using the git add _navigation.html.haml command.

Since we have staged these changes, it’s time to move the branch HEAD back to our original change commit (which also includes all our new commits) and run Git rebase –continue, This will open your default editor in the terminal and show you the commit information for the changes we made during the rebase; Page navigation view. You can change the commit information if you want, but for now we’ll keep it, so save the changes and exit the editor. At this point, Git will reshow all commit changes since the change you just changed was committed and the HEAD of the branch has gone back to the top of our original commit, containing all the new changes you made to one of the commit changes.

Git push –force-with-lease

Scenario 3: I need to add, remove, or merge the commit

A common scenario is when we have just modified some previous commits and resubmitted some new changes. Now let’s simplify these commits as much as possible and merge them with the original commits.

All you have to do is start an interactive base change just like you would in any other scenario.

Pick 4155df1CDc7 Page Navigation View Pick c22a3FA0c5c Render navigation section Pick aa0a35a867e Add style to navigation Pick 62e858a322 Fix a typo pick 5c25eb48c8 Ops another fix pick 7f0718efe9 Fix 2 pick f0ffc19ef7 Argh Another fix!Copy the code

Now suppose you want to merge all those submissions into the C22a3FA0C5c render navigation section. All you need to do is:

  1. Move the change commits you want to merge up so that they are below the final merged change commits.
  2. Commit every change to the schema bypickInstead ofsquashorfixup.

Note: The squash mode retains the modification information in the description. Fixup doesn’t, it just keeps the original commit information.

You end up with something like this:

Pick C22a3FA0c5c Render navigation section Fixup 62e858a322 Fix a typo fixup 5c25eb48c8 Ops Another fixup 7f0718efe9 Fix 2 fixup f0ffc19ef7 Argh Another fix! Pick aa0a35a867e adds a style to the navigationCopy the code

Save your changes and exit the editor, and you’re done! This is the historical submission record after completion:

Pick 4155df1CDc7 Page navigation view Pick 96373C0bcf Render navigation section Pick AA0a35a867e Adds style to navigationCopy the code

As before, all you have to do now is run git push –force-with-lease

and all changes will be pushed.

If you want to completely remove a change commit, rather than squash or fixup, you just type drop or simply delete the line.

To avoid conflict

To avoid conflicts, make sure that your submissions moving up the timeline do not touch the same files that subsequent submissions touch.

Pick 4155DF1CDc7 Page Navigation View Pick C22a3FA0C5c Render navigation section Fixup 62e858a322 Fix a typo# this changes styles.css
fixup 5c25eb48c8 Ops another fix            # this changes image/logo.svg
fixup 7f0718efe9 Fix 2                      # this changes styles.css
fixup f0ffc19ef7 Argh Another fix!          # this changes styles.cssPick aa0a35a867e adds a style to the navigation# this changes index.html (no conflict)
Copy the code

Additional tip: Quickfixup

If you have a clear idea of which changes you want to commit, you don’t have to waste mental energy thinking about “Fix 1”, “Fix 2”,… “Fix 42.”

Step 1: Get acquainted--fixup

After you have staged those changes, use the following command to commit the changes:

git commit --fixup c22a3fa0c5c
Copy the code

(Note that the hash value belongs to the c22a3FA0C5c render navigation section for this change submission)

This results in a submission message like this: Fixup! Render the navigation section.

Step 2: And this guy--autosquash

Through the simple use of interactive base manipulation. You can ask Git to automatically put all fixups in the right place.

git rebase -i 4155df1cdc7 --autosquash

The historical submission record will look like this:

Pick C22a3FA0c5c Render navigation section Fixup 62e858a322 Fix a typo fixup 5c25eb48c8 Ops Another fixup 7f0718efe9 Fix 2 fixup f0ffc19ef7 Argh Another fix! Pick aa0a35a867e adds a style to the navigationCopy the code

All you have to do is review it and move on.

If you feel adventurous, you can do a non-interactive git rebase –autosquash, but only if you like living a life of risk, because you don’t have the opportunity to check out these merged apps before they do.

Scenario 4: My submission record is useless, I need to start over!

If you are working on a large feature, there will usually be a lot of fixes and changes that are frequently submitted as code review feedback. Instead of constantly redesigning branches, we can leave the cleanup of commits until the end of development.

This is a very convenient place to create patch files. In fact, before developers could use Git-based services such as GitLab, patch files were the primary way to share and merge code via email for large open source projects. Suppose you have a branch with a very large number of commits (e.g. Add-page -navigation), which is not very clear about the change history of the warehouse. Here’s how to create a patch file for all the changes you’ve made in this branch:

  • The first step in creating a patch file is to make sure that your branch has a source frommasterAll changes to the branch and no conflicts with those changes.
  • You can be inadd-page-navigationRun when a branch is checked outgit rebase mastergit merge masterTo get everything frommasterChanges made are transferred to your branch.
  • Now create the patch file; rungit diff master add-page-navigation > ~/add_page_navigation.patch.
    • Decomposition of the commandIn this case, we use GitdiffFeatures, queriesmasterBranches andadd-page-navigationThe differences between branches, and thenredirectOutput (through>Symbol) to a file in our user home directory (in*nixSystem operating systems usually~ /)add_page_navigation.patch.
  • You can specify the path, filename and extension you want to save the file.
  • Once the command runs and no errors are seen, a patch file is generated.
  • Now check out themasterBranch; rungit checkout master.
  • Delete a branch from the local repositoryadd-page-navigation; rungit branch -D add-page-navigation. Keep in mind that we have changed this branch in the patch file we created.
  • Now create a new branch with the same name (masterChecked out); rungit checkout -b add-page-navigation.
  • Now, this is a new branch, without any changes you made.
  • Finally, apply your changes from the patch file;git apply ~/add_page_navigation.patch.
  • At this point, all changes are applied to the branch, and they show up as uncommitted, as if all the changes you made were done, but none were actually committed in the branch.
  • Now you can go ahead and submit individual files or files grouped by area of impact in the order you want, using straightforward submission information.

As in the previous scenario, we changed the entire branch, and now it’s time to force push!

conclusion

While we’ve covered most of the common and basic situations that arise in your daily workflow with Git, rewriting Git commit history is a huge topic, and if you’re already familiar with the above suggestions, you can do so in the official Git documentation. Happy Git ‘ing!

If you find any errors in the translation or other areas that need improvement, you are welcome to revise and PR the translation in the Gold Translation program, and you can also get corresponding bonus points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


Diggings translation project is a community for translating quality Internet technical articles from diggings English sharing articles. The content covers the fields of Android, iOS, front end, back end, blockchain, products, design, artificial intelligence and so on. For more high-quality translations, please keep paying attention to The Translation Project, official weibo and zhihu column.