Git's magic to resolve common issues with active repositories

A common problem that I run into when working on active repositories.

Whenever I start developing a new feature, I do a git fetch and git rebase origin/main to make my local main upto date with the remote main. Then I create a new branch using git checkout -b <NEW_BRANCH_NAME>

But when working on repositories with active development where multiple people simultaneously merge their changes to the main branch, it gets a bit messy as the main in the remote gets ahead of the local main and thus the current local branch I work on gets out of sync.

To demonstrate the issue better, let us pick an example repository. I have created a new repo in my GitHub upgraded-octo-waffle

repo_on_gihub.png

I cloned the repository into my local machine using

$ git clone git@github.com:KrishnaRekapalli/upgraded-octo-waffle.git

If the repository is cloned into the local machine sometime ago and if the local main and the remote main branches are out of sync, we can use the following set of commands to check if the local branch needs to be updated and take necessary action i.e. rebase

$ git fetch # fetches the latest changes
$ git status # shows how far behind is the local branch from the remote
$ git rebase origin/main #updates the local branch with all the new changes

Once I have all the latest changes incorporated into my local main branch, I set out to work on my new feature by creating a new branch.

$ git checkout -b KR-my-new-feature

git_new_branch.png

Before changes: before_changes.png

New changes:

new_local_changes.png

Now I commit my new changes to the local branch.

$ git add waffle.py
$ git commit -m "added new method"

Here comes the twist in the story. Now I just realize that a colleague has just merged her changes to the remote/main and they also edited the same file waffle.py. Now, waffle.py looks like this:

colleague_changes_waffle.png

Now I want to incorporate the new changes to my branch. What can I do?

I first try:

$ git fetch
$ git rebase origin/main

The response is: failed_rebase.png

The rebase operation fails (:roll_eyes:) because there are changes to the same file and same locations and Git is not able to figure out a clean way to update waffle.py with my colleague's changes on my feature branch.

Then I abort the rebase operation by doing

$ git rebase --abort

One solution to this problem is:

  1. Undo the last commit
  2. Use the stash option to save my changes
  3. Update the branch with my colleague's changes by using rebase
  4. Use stash pop to apply my changes on the top of the updated branch
  5. Resolve conflicts
  6. Commit the final changes after resolving conflicts
  7. Push my changes to the remote and merge

We will go over this process step-by-step now

1. Undo the last commit

To undo the last commit to my branch, I do

$ git reset --soft HEAD~1

The number 1 indicates that we want to go back one commit and by using the flag, soft we are asking Git to not discard the changes we made.

2. Stash my changes

Now I stash my changes by doing

$ git stash

This step keeps my changes safe and now I can use rebase to update my branch.

3. Update the branch with the latest changes

For this I do

$ git rebase origin/main

Rebase now works without any complaints :smiley:

successful_rebase_after_reset.png

4. Apply my changes again using Stash pop

Now it is time to apply my changes to the feature branch I am working on using stash pop.

$ git stash pop

Now waffle.py will look like: after_stash_pop.png

So Git is expecting us to resolve the conflicts manually as the changes happened at the same place. Although these are different methods, Git is not so smart to figure that out. So it is our job to resolve the conflicts i.e. keep the changes we want and discard the changes we don't.

5. Resolve conflicts

As I want to keep both the methods, I just remove the <<<<<< and ======= and >>>>>>> symbols and commit all the changes to the current branch

6. Commit the overall changes

As all the issues are resolved I simply add and commit my changes.

$ git add waffle.py
$ git commit -m "That was close! All good now :sweat_smile:"
7. Push changes to remote and merge my changes

Now we need to push the local branch to remote so that it can be merged with the main branch.

$ git push -u origin KR-my-new-feature

What this does is creates a new remote branch with the same name as the local one and push all the changes there.

The response of the command: fin.png

Now I can happily create a PR and merge my new feature with the main branch.

I can't resist but end this post with one of xkcd comics on Git. This captures my emotions about git for the most part. While it is a game-changing tool that spearheaded collaborative development, it can take a bit of time to get a hang of the capabilities and for the most part, one may end up using only 10-15% of the core features of Git and there is a lot I don't understand and keep learning as I run into problems :smiley: