Skip to content

Instantly share code, notes, and snippets.

@dagezi
Created November 2, 2017 08:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dagezi/2f20744fd074803c5b7c20c5b75b2a1c to your computer and use it in GitHub Desktop.
Save dagezi/2f20744fd074803c5b7c20c5b75b2a1c to your computer and use it in GitHub Desktop.

Clean up a messy Git branch

It's very desired that every commit in the branch is organized well. One commit changes only one logical part. It shouldn't contain irrevant changes, such as one for UI and another for DB. Such changes must be separated into different commits.

  • It makes review easier.
  • It would help you dividing the big PR to a few logical PRs.
  • It makes easier to remove one logical change.

But branches are often messed up. It could be nothing other than a pile of ad-hoc changes. A change added on a commit is often reverted by another change. Damn it! You've spoiled it!

But git branch is not Sushi. Git still loves you. With the power of git, you can reorganize your branch as though you've developed with perfect plan of your task!

What you have

I think you have following branch structures.

- A  [*] (master)
   \- B0 - B1 - B2 (messy_branch)

The asterisk * in the figure shows the current branch.

Create branch to work

At first, create new branch to avoid breaking the messy branch. Even if it's messy, it must contain something we want.

git checkout messy_branch
git checkout -b work_branch

Then the branch structure would be like followings:

- A  (master)
   \- B0 - B1 - B2 (messy_branch = [*]work_branch)

Squash!

Let's squash the messy branch because every illogical commits will block us from refactoring.

git reset master
git add .   # Stage all necessary files!
git commit -m 'squashed!'

I could see the squashed changes and remove the surely unnecessary ones, like new empty lines or redundant comments.

- A  (master)
  |\- B0 - B1 - B2 (messy_branch)
  \- B99 ([*]work_branch)

Create refactor_branch

git checkout master
git checkout -b refactor_branch
- A (master)
  |\- B99 (work_branch)
   \.     [*] (refactor_branch)

Extract one logical change from work_branch

It's not necessary, but good to see the diff what you have to handle.

git diff work_branch

Then you'll find some chunks you think they are relevant enough to collect into a commit. You can use checkout -p command:

git checkout -p work_branch files_contains_relevant_chuncks...

It'll ask you which chuncks in work_branch you want to add to refactor_branch. So you can stage only a few relevant changes.

On this phase, it's more important not to mix irrelevant chunks into one commit than to collect relevant ones to one commit. If you divided some relevant changes into two or more commits, you can integrate them with rebase -i at the last phase.

Note that you must add binary files with git checkout work_branch - binary_files because checkout -p seems ignoring binary files.

It's good if you can check if the correctness of the commit, by compile or unit test. But it's not necessary. As I said, it's not necessary that all relevant changes are in this commit.

After you staged changes, don't forget commit them with proper messages! Then, you'll have following branches. Note that I omitted messy_branch because we don't touch it anymore.

- A (master)
  |\- B99 (work_branch)
   \- C0  [*] (refactor_branch)

Remove the chuncks from work_branch

git checkout work_branch
git rebase refactor_branch

It removes the moved chunks from work_branch. It might conflicts. Please resolve it anyway.

You can remove trivial unnecessary changes, such as adding empty lines.

After the rebase, you'll have following branches.

Theoretically, the work_branch should work as before. It means you can compile, test and execute it.

- A (master)
   \- C1      (refactor_branch)
       \- B98 [*] (work_branch)

Repeating them

Then you should go back to refactor_branch, extract and rebase work_branch again and again.

Then, you'll have something like following:

- A (master)
   \- C1 - C2 - C3 - C4 - C5(refactor_branch)
                           \- B94 [*] (work_branch)

After the rebase, you might find only unnecessary changes in B95. Then this tough loop has finished. Congratulations!

Tweak commits

Anyhow, you divided into some logical commits. I guess your commits are broken up to too small pieces. Or they're ordered wrongly.

Then finally you can use git rebase -i to reorder and integrate them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment