DEV Community

Roy J. Wignarajah
Roy J. Wignarajah

Posted on • Updated on

Modifying Git History with Git Rebasing and Git Amends

Modifying Git History

In Open Source Development, we'll eventually encounter projects with guidelines on how they want their git history and commits to look like. Some guidelines include:

  • Writing commits messages a certain way
  • Restructuring commits so that there are fewer

Additionally, some guidelines may require a topic branch (branch with bugfix or feature) be updated with the latest changes in a project before a Pull Request is submitted.

Clean Git History

There are two commonly used tools to achieve clean git histories:

  1. Amended commits (git commit --amend)
  2. Rebasing (git rebase <branch-to-use-as-new-base>)

Amended Commits

Amended commits allow you to amend a previous commit with a new message or new staged changes. This is useful if your commit message didn't follow a project's commit guideline, if you have a typo in the commit message, or if you forgot to stage changes in your commit. However, if you are locally amending a commit you that was already pushed to GitHub, pushing the amended commit will be rejected due to a non-fast-forward. If you need to push an amended commit that was already pushed to GitHub, you will have to force-push it, using git push origin <branch-with-amended-commit. Forcing pushes is dangerous as it will overwrite what is on GitHub. However, they can be safe when you're working inside a pull request or a branch only you own and work on. I've been taught to never force a push on a master branch or a share branch, but it's safe to use if you're working alone on a topic branch for a feature or bug fix.

Git Rebase

If you started contributing to an open source project a month ago, chances are when you are ready to make your Pull Request your fork will be behind the master branch. git rebase is a way to apply your changes on the updated master branch submitting a pull request or merging the branch. git rebase is good for reducing commit messages and makes a project's git history look cleaner than it would be by merging alone.

Git Rebases are commonly done interactively either in vim (default) or a code editor (requires integration/association). On my local machine I associated Git with Visual Studio Code by running:

git config --global core.editor "code --wait"
Enter fullscreen mode Exit fullscreen mode

Refactoring ctil

Technical Debt

As a program evolves and new features are added, the code becomes more complex, and it becomes easy to lose control of the code. Quickly extending software without managing code quality incurs technical debt, as the code will require time to be improved.

Refactoring

Refactoring is one technique for improving the structure and maintainability of code without altering its behaviour. No features will be added or bugs fixed, only the code quality will be improved. In my lab exercise I learned four (4) ways to refactor an d improve code:

  1. Extracting functions
  2. Extracting classes to avoid duplication
  3. [Renaming variables] to avoid 'bad code smell' (https://refactoring.com/catalog/renameVariable.html)
  4. Splitting code into multiple files

Refactoring Process

For my lab exercise, I refactored the main file in ctil, source.cpp. It's clear to me that I incurred a lot of technical debt as features were added to ctil. Although I initially created modules to contain related methods, the main() method in source.cpp is still too large. Refactoring and reducing the complexity of the main() method was my main goal for this lab exercise. To this end, I made the following changes to the main() method in source.cpp:

  1. Extracted file extension boolean variables into two methods
  2. Renamed variables in extracted methods
  3. Extracted directory creation logic into a new module, fileHandler
File Extensions

Currently, ctil is programmed to convert .txt and .md files to .html files. The main() function previously checked for these extensions and stored them in boolean variables. Instead of storing these variables in main(), I extracted them into two methods in the ctil module:

  • ctil::has_txt_suffix
  • ctil::has_md_suffix

These methods check if a filepath has either extension, both stored as strings in the methods. These strings were formerly called md_suffix and txt_suffix, but were renamed to suffix_md and suffix_txt. It just felt better to have the common word(s) at the beginning of the variable names, and although I don't expect to add more file extensions to ctil, this might be a better naming convention for future maintainability.

File Handling

ctil stores generated files in a folder called ctil by creating a folder using the filesystem library, but this logic is also inside the main() method. It would be better to extract this logic to a new method and new module, so I created a new module called fileHandler and extracted this logic into a method called fileHandler::initializeDirectory(). The main() still contains file handling logic that can be extracted into functions, but I think this was a good start.

Interactive Rebase

After refactoring my program, I had little trouble doing an interactive rebase, as my master branch didn't change at all since I started refactoring on another branch. I was able to squash all my commits using Visual Studio Code. Since I was able to rebase my topic branch, I also had little trouble using Git to change my project's history. During the rebase I did not find any bugs or break my program, however I noticed many ways I could further refactor the main() method.

Top comments (0)