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!
I think you have following branch structures.
- A [*] (master)
\- B0 - B1 - B2 (messy_branch)
The asterisk *
in the figure shows the current branch.
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)
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)
git checkout master
git checkout -b refactor_branch
- A (master)
|\- B99 (work_branch)
\. [*] (refactor_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)
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)
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!
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.