Fixing mistakes and resolving conflicts with Git
“Mistakes are a fact of life. It is the response to error that counts.”
– Nikki Giovanni, American poet
Collaboration is an essential part of any successful project. Sharing ideas and feedback, dividing tasks and responsibilities, and removing blockages from your teammates are one of the great benefits of working with others on a project. However, as the project goes on and requirements are implemented, collaboration can also create challenges. Conflicts may arise and mistakes may be made along the way. Thus, it’s important to know how to deal with them quickly and prevent major repercussions from occurring in the future.
In previous Insight Blogs we discussed the importance of having good commit messages and pull requests, and using various tools and extensions to help you improve your workflow. Both are great tools and practices that can prevent and alleviate roadblocks. But issues can still arise. In this blog, we’ll focus on the most common mistakes we’ve encountered, and we’ll explain the benefits of using the git tool and its capabilities for solving issues that are often overlooked or unknown.
If you’re a new developer, we hope these tips will ease your fear when making changes to an existing code base or when working with other people. The reality is that in almost every project, not everything will be perfect. These are some tools you can use when things might have gone off track. Once again, these come from our own experience, and we organized them by thinking of practical situations that can be easily related.
Because the blog is lengthy, we provided an outline of the topics below, so you can choose the ones that work best for your situation.
- Learn to Update Your Commits
- Undoing Your Changes With Git Reset
- Introducing Git Rebase
- Elevate Your Git Game: The Benefits of Git Squash
- How To Solve Rebase Conflicts With Git
- Working With Your Peers
- Rebasing Other’s Branches
- Cherry Picking Commits
- Showcasing differences and applying them
- To Sum Up
1. Learn to Update Your Commits
When I started my first internship, I was overwhelmed by the amount of code I had to go through (even though now it doesn’t seem that bad). I remember my manager sat me down on my first day and said, “Here are some templates we bought. You need to integrate them into our repository and make them work with the framework we use.” As the project went on, I had to show my progress and, thus, I was creating a commit for every change I made, but there were always many questions in the back of my mind: Am I doing it right? What if I change my mind and want to change the message? What if I send out something to the remote branch that I wasn’t supposed to? What if…?
This was tricky because when it comes to code there is not a Ctrl + Z command or something intuitive that allows you to manually change or erase like we are used to when writing documents in Word. When your changes go out there, they remain out there, and there is nothing manual you can do about it. This fact paralyzed me, and therefore I took quite some time just to create commit messages, which was unproductive. Now imagine my reaction when I sent out my first commit message with a mistake! It was bad to say the least.
If only I knew back then that git allows you to easily update commits as needed, and not only that, but it allows you to go beyond the commit message and update things like the author and the date. My life would have been so much easier.
It turns out that the git commit command comes with a handy ‐‐amend option, which back in the time of my internship was something so new and rare. Now it’s one of the most basic things I use, and I’ll give an overview of the most common situations in which I use it.
Add new changes to the same commit message
The ‐‐amend option can be used when you add new changes to your existing commit message. Many projects nowadays—in order to maintain a clean commit history—require you to have a single commit message for each new feature.
In this regard, instead of creating a commit message for every change you make, you can just create an initial commit message, and then you can add new changes to the same message, as needed. To achieve this, you’ll use the git commit ‐‐amend ‐‐no-edit command.
Modify your existing commit message
The previous command is useful to add changes to a single commit easily and rapidly, as needed. Think of it as your base command for creating a pull request. You don’t care how it looks, but rather you want to save your changes as you progress in your work.
Nonetheless, once your new feature is finished and you are ready to create a PR, it’s a good idea to update your commit message with all the important details. To do this you will use the git commit ‐‐amend command, which will open a default editor and allow you to change the commit message as needed.
You can also use this command if you happen to make a mistake on your message by either having the wrong details or having a typo. It’s useful either way.
Changing the date of your commit
If the feature you’re currently working on is taking too much time, chances are your local changes have fallen behind the master branch. If this is the case, you probably want to display your changes last, and to make information consistent, the date for your commit should also be the latest.
Typically, what happens when you first commit your changes is that the date stored corresponds to the time in which the commit was created. To update the date to the current date, you can use the following command:
git commit ‐‐amend ‐‐date=”now”
This will simplify your life because in this way you don’t have to worry about typing the correct date format, the “now” keyword will take care of that for you.
2. Undoing Your Changes with Git Reset
Once again, as you work on collaborative projects it’s important to be agile and responsive to situations. In this regard, it’s not a good idea to get stuck for a long time or get paralyzed for every new mistake you make.
You need to be able to reach out for help when needed and, more importantly, you need to be able to fix your mistakes in a timely manner.
Just as the Ctrl + Z command is probably our best friend when typing a document, the git reset ‐‐hard will be one of your best friends when working with software. Think of this command as being the equivalent to the Ctrl + Z in the sense that it will allow you to return to a previous state, but it has even more capabilities.
Returning to a previous state
If you happen to start out on a project or you and your peers simply like to include different commit messages, it’s probably a good idea to know how to undo your changes.
This can be used when your last changes are not needed anymore because of a change in requirements, or it can be even used to put out fires. To undo your changes, you can use the git reset ‐‐hard HEAD command and adjust it as needed.
If you just want to remove your last commit you can simply run the git reset ‐‐hard HEAD^ command.
Now, if you need to revert more changes and go back even more in time you can do the following:
git reset ‐‐hard HEAD~N
Where N is the number of commits you want to go back. For example, if you want to remove the last three commits from your commit history you can run:
git reset ‐‐hard HEAD~3
Syncing changes with a remote branch
Your local repository should be the place where you allow yourself to make mistakes freely and without regret.
Given that you are probably the only person who will be looking at it, take the time to test new things out. Navigate through your project directory, explore, make changes, and even delete some things out.
You can remain calm as you move things around because you know what? There is a git command that acts as a safeguard in the case you mess things up just as long as you don’t push any changes.
If you have a basic understanding of git, you’ll know that companies use a remote repository in the cloud, which collects the changes from all developers.
Given that the remote repo should only contain valuable code that is either stored on the master branch or other branches, you can use this to your advantage and restore your local repo to have the same state as the remote one.
If your local project is on the master branch and you have made some changes that you want to get rid of entirely, instead of going through those changes one by one, you can just reset your entire local project to have the same state as the remote version, and start from scratch. To do this, you’ll use the following command:
git reset ‐‐hard origin/master
If you happen to be on a different branch, you’ll do the same, but on origin you’ll type your branch name like so:
git reset ‐‐hard origin/
This is especially useful when you are working on a new feature and you happen to switch between two different laptops and are making incremental changes along the way.
3. Introducing Git Rebase
The git rebase is a process that allows you to move or add multiple commits to a base commit of your choice. It’s used to maintain a clean commit history for your project given that it eliminates an additional merge commit message from being created, which happens when you want to retrieve new changes from your master branch.
As a rule of thumb, whenever a new update is added to the remote project and you integrate it locally, you’ll want to place your feature’s commits to the end. This allows your peers to view a linear consistent sequence in the commit history and avoid any unnecessary merge commits from appearing.
Git Merge vs. Git Rebase: Understanding When to Use the Right Tool
Just as Jacob Bennett describes, a rebase is usually applied when you incorporate changes from a remote branch locally, and a merge happens when you are including your changes into the master branch (commonly through a Pull Request, which is approved and merged by one of your peers). This is actually the workflow we use in real life situations and is a common practice that we recommend.
To rebase new changes from the master branch to your local branch, you’ll use the following command:
git rebase master
Keep in mind that you’ll need to be within your local branch for this to work properly.
4. Elevate Your Git Game: The Benefits of Git Squash
Git squash is a powerful tool that can help streamline your git workflow and make it easier to manage your repository. It allows you to take multiple feature or task related commits and consolidate them in a single, clean commit. This can be useful for several reasons, including making it easier to read the repo’s history and reducing clutter.
When you have a long history of commits, it can be difficult to see the overall progress of a project or understand the changes that have been made. By squashing multiple commits into a single commit, you can condense this history and make it easier to see the big picture. Therefore, it’s a good practice to have a single commit per feature. This means that each commit in your repository should represent a distinct, self-contained change or addition to the codebase.
Having a single commit also makes it easier to roll back changes if necessary since you have less items to deal with. Another benefit of squashing commits can be uncovered when you find yourself merging branches. Sometimes we mistakenly merge a branch into a new commit that is part of our current work. You can easily squash the commit generated by the merge and continue your work from a single commit.
5. How to Solve Rebase Conflicts with Git
When you try to rebase changes from one branch to another and git is unable to automatically resolve the differences between them, that’s when a conflict arises. This usually happens when both branches have changes to the same lines of code, or if one branch deleted a file that the other branch has modified.
In such a case, you will need to manually resolve the conflicts before completing the rebase. To do so, you first need to identify the specific lines of code causing the conflict and decide which changes you want to keep and which ones to discard. Nowadays, there are several tools that help developers solve these types of issues. In essence, the tool is a merge conflict editor, like the one found in VS code, which has git support out-of-the-box. These tools allow you to view the conflicting changes side by side and choose which ones you want to keep. Once you have decided which changes to keep, you need to stage them and finish the process by running git rebase ‐‐continue
It’s important to mention that rebase conflicts are seen on a regular basis as they are part of a normal git workflow. They are usually easy to resolve with a little patience and attention to detail. However, if you are having trouble resolving a rebase conflict, remember you can always abort the current action by using either git rebase ‐‐abort, which in return will take you back to before the rebase process started.
6. Working With Your Peers
As you work in a team, chances are that you’ll often find yourself using code from others whose changes have not been approved yet or you’ll be working in pairs on the same feature but not at the same time. This can be somewhat of a hustle to work with, especially since the state of your code will change continuously as new updates appear. Nevertheless, as long as there is good communication between your peers, and you know how to deal with potential conflicts, you’ll be good to go.
Ultimately, the best approach will depend on your specific workflow and the needs of your project. Nonetheless, in the following section, we are going to look deeper into the set of strategies you have in hand to keep up to date with other developers’ changes.
Rebasing Other’s Branches
One of the first approaches you can take to add work from your peers into your local repo is to rebase changes from their branch into yours.
If in your project each user has a different fork, you’ll first have to add your peers’ project as a new remote. To do this, you’ll run the following commands:
git remote add
git remote update
Once this has been made, you can rebase those changes into your local branch like so:
An example command for this situation can be seen as
Cherry Picking Commits
Cherry Picking allows developers to select and apply specific commits from one branch to another, rather than merging the entire branch. This can be useful in a variety of situations, as it gives developers the ability to selectively choose which changes to incorporate into their current work. And the most common situation when it is used is when you need to build a new feature on top of one of your peers.
To use the cherry pick command, you need to first switch to the branch where you want to apply the cherry-picked commit. Then use the following syntax:
This will apply the commit with the specified hash from an external branch to the current branch. Keep in mind that in doing so, this will create a new commit with the same changes as the cherry-picked commit. This new commit will have a different hash and will be treated as a separate commit in the history of the branch.
Another practical situation where cherry picking can be useful is when you need to apply a fix from one branch to multiple branches. Imagine you have a bug that needs to be fixed and you created the fix in a separate branch. With cherry picking, you can apply the fix to multiple branches without having to merge the entire fix branch into each one. This can save a lot of time and effort if you have many branches that need the fix.
Finally, if you ever need to cancel the operation, there is always the possibility to abort it by using the syntax:
git cherry-pick ‐‐abort
Showcasing differences and applying them
Git diff and git apply are commands that allow developers to see the differences between two versions of a codebase and apply them as required. Let’s explore the uses of git diff and git apply with some examples.
Git diff allows you to see the differences between two versions of the code. To use git diff, you can use the following syntax:
git diff <file/branch/commit>
This will show a list of changes that have been made to the file since the last commit, along with line numbers and the specific changes that were made. Nonetheless, you can go even further than that since this command can be also used to show the differences between two branches or even two commits.
Git apply is a command that allows developers to integrate changes from one version of a codebase to another. To make this work, you usually copy the contents displayed by the git diff command into an external file and apply those changes into your current branch by using:
One practical example where this comes very handy is when you are working with a fellow developer on the same branch and want to send out the latest changes so that each of you can work and make progress in your own time.
7. In Summary
In this blog, we explained a number of useful git commands. First, we explained a way for developers to update commits using the ‐‐amend option. This flag can be used to add new changes to the same commit message, modify an existing commit message, and change the date or author of a commit. This is useful to maintain a clean commit history and gives a clear view of the scope when you are ready to open a new pull request.
Next, we explained the git rebase process which allows developers to move or add multiple commits to a base commit of their choice in order to maintain a clean commit history and eliminate unnecessary merge commit messages. We also explained that the git squash tool allows developers to consolidate multiple related commits into a single, clean commit in order to streamline the git workflow and make the repository easier to manage. Next, we explained that the merge conflict editor tool is available to help us solve those annoying rebase conflicts. Finally, we learned about working with peers, the set of commands available to have a clear view of changes and how to integrate them smoothly.
The git commands covered in the blog, such as git commit ‐‐amend, git rebase, and git squash, have proven to be incredibly useful in our everyday workflow. The ability to easily update and modify commits has allowed us to maintain a clean and organized commit history, while the option to rebase and squash commits has helped streamline our workflow and make our repository easier to manage, while being able to resolve conflicts along the way. These commands have greatly improved our productivity and efficiency, and we hope they will be just as beneficial to you in your own projects by allowing you to work with your teammates in a seamless way.