Sometimes you are working in a feature branch that was opened a long time ago and, you would like to reorganize all the commits from this feature branch.
This branch normally can be very confusing to read and there are some tips that you should know before doing a rebase (squashing).
The first thing that you should do is to understand how is organized your branch.
$ git log --graph --pretty='%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --all
* de9e97d - (refs/stash) WIP on QT-100-new-feature: 398c952 commit X_0 (2 days ago) <Augusto Morais>
|\
| * 1e21370 - index on QT-100-new-feature: 398c952 commit X_0 (2 days ago) <Augusto Morais>
|/
* 398c952 - (HEAD -> feature/QT-100-new-feature, origin/feature/QT-100-new-feature, origin/feature/QT-200-another-feature) added and updated unit tests (5 months ago) <another_user>
* d6d4b9d - commit X_1 (5 months ago) <another_user>
* 67f9e22 - commit X_2 (5 months ago) <another_user>
* 4d54d51 - commit X_3 (6 months ago) <another_user>
* a5ddc3a - commit X_4 (6 months ago) <another_user>
* a3e944c - commit X_5 (6 months ago) <another_user>
|\
* | 9b046d4 - commit X_6 (7 months ago) <another_user>
* | e3a952c - commit X_7 (7 months ago) <another_user>
* | ca88993 - commit X_8 (7 months ago) <another_user>
* | 88ae514 - commit X_9 (7 months ago) <another_user>
* | 783f0b9 - commit X_10 (7 months ago) <another_user>
* | d90952c - commit X_11 (7 months ago) <another_user>
* | 9e54512 - commit X_12 (7 months ago) <another_user>
* | 2e95f48 - commit X_13 (8 months ago) <another_user>
* | 9eeb7f4 - commit X_14 (8 months ago) <another_user>
* | 2d8629a - commit X_15 (8 months ago) <another_user>
* | 158e5eb - commit X_16 (8 months ago) <another_user>
* | f127068 - commit X_17 (8 months ago) <another_user>
* | 4228271 - commit X_18 (9 months ago) <another_user>
* | b21e1a5 - commit X_19 (9 months ago) <another_user>
* | e0d262f - commit X_20 (10 months ago) <another_user>
* | 3d31768 - commit X_21 (10 months ago) <another_user>
* | 8b70958 - commit X_22 (10 months ago) <another_user>
* | 5d23a5d - commit X_23 (10 months ago) <another_user>
* | d24f107 - commit X_24 (10 months ago) <another_user>
* | 4b2e897 - commit X_25 (10 months ago) <another_user>
* | 5ea44bc - commit X_26 (10 months ago) <another_user>
* | 18675e1 - commit X_27 (11 months ago) <another_user>
* | 171e8dc - commit X_28 (11 months ago) <another_user>
* | e09ae06 - commit X_29 (11 months ago) <another_user>
| | * 2524df0 - (feature/QT-600-new-XYZ) Merge pull request #185 in QT/my_repo from bugfix/QT-300-db-feature to development (13 days ago) <Augusto Morais>
| | * 33fa0d6 - Merge pull request #177 in QT/my_repo from feature/QT-302-feature-y to development (13 days ago) <User X>
| | * c42ac38 - Merge pull request #180 in QT/my_repo from bugfix/QT-410-feature-z to development (13 days ago) <Another User>
As you can see, this can be a bit difficult to understand from the beginning because here we have all commits of
feature/QT-100-new-feature
.
Don't worry, probably you remember - I hope so - that this branch was branched out from the development
and, your task here is to squash/organize all commits that differ from the development
.
Cool, let's see which commits are different from your branch feature/QT-100-new-feature
with the development
.
git log --oneline --decorate origin/development..feature/QT-100-new-feature
398c952 (HEAD -> feature/QT-100-new-feature, origin/feature/QT-100-new-feature, origin/feature/QT-200-another-feature) commit X_0
d6d4b9d commit X_1
67f9e22 commit X_2
4d54d51 commit X_3
a5ddc3a commit X_4
a3e944c commit X_5
9b046d4 commit X_6
e3a952c commit X_7
ca88993 commit X_8
88ae514 commit X_9
783f0b9 commit X_10
d90952c commit X_11
9e54512 commit X_12
2e95f48 commit X_13
9eeb7f4 commit X_14
2d8629a commit X_15
158e5eb commit X_16
f127068 commit X_17
4228271 commit X_18
b21e1a5 commit X_19
e0d262f commit X_20
3d31768 commit X_21
8b70958 commit X_22
5d23a5d commit X_23
d24f107 commit X_24
4b2e897 commit X_25
5ea44bc commit X_26
18675e1 commit X_27
171e8dc commit X_28
e09ae06 commit X_29
Great! You have 30 commits that are different from development
branch. Now, you know exactly what to do, which is
squash all those commits into one.
So how to do it? Easy, just type:
git rebase -i 309ae06
Make sense, right? You will get the first commit that you did in your branch and squash all others.
Well, when you do it you will see that there are a lot of commits that you have no "idea" where come from:
git rebase -i 309ae06
pick 398c952 (HEAD -> feature/QT-100-new-feature, origin/feature/QT-100-new-feature, origin/feature/QT-200-another-feature) commit X_0
pick d6d4b9d commit X_1
pick 67f9e22 commit X_2
pick 4d54d51 commit X_3
pick a5ddc3a commit X_4
pick a3e944c commit X_5
pick 9b046d4 commit X_6
pick e3a952c commit X_7
pick ca88993 commit X_8
pick 88ae514 commit X_9
pick 783f0b9 commit X_10
pick d90952c commit X_11
pick e081ae7 commit Y_45
pick 8210070 commit Y_46
pick e181918 commit Y_47
pick 4d20f60 commit Y_48
pick 1195b20 commit Y_49
pick ec6abee commit Y_410
pick bbddcd2 commit Y_411
pick d0ab673 commit Y_412
pick 1a62d4a commit Y_413
pick b63f02b commit Y_414
pick 9ff3f9e commit Y_415
pick d419820 commit Y_416
pick 9e54512 commit X_12
pick 2e95f48 commit X_13
pick 9eeb7f4 commit X_14
pick 2d8629a commit X_15
pick 158e5eb commit X_16
pick b63f02b Merge pull request #81 in QT/my_repo from bugfix/QT-421-feature_87 to development
pick 9ff3f9e Merge pull request #84 in QT/my_repo from bugfix/QT-423-feature_88 to development
pick d419820 Merge pull request #69 in QT/my_repo from feature/QT-403-feature_97 to development
pick 0df2529 Merge pull request #73 in QT/my_repo from feature/QT-410-featyre_187 to development
pick f127068 commit X_17
pick 4228271 commit X_18
pick b21e1a5 commit X_19
pick e0d262f commit X_20
pick 3d31768 commit X_21
pick 8b70958 commit X_22
....
[CONTINUE tons of commits]
As you can see, you have more than 30 commits and you don't want to find each commit and squash them individually.
So, there are a few ways to do it. First, you need to rebase from the first common ancestor of your feature branch and development.
How can I find this first common ancestor?
$ git merge-base feature/QT-100-new-feature development
571f64bfbbeb138f648cacf0a1c53a3b7e8913f1
Great! this is the common ancestor of both branches. now just rebase using the following command:
git rebase -i `git merge-base origin/development feature/QT-100-new-feature`
or you can just do:
git rebase -i development
Keep in mind that you can have some conflicts. So you need to fix them manually. This can be a bit annoying task and, if you want to always keep your changes on the top, just do:
git rebase -i `git merge-base origin/development feature/QT-100-new-feature` -X ours feature/QT-100-new-feature
Pick the first commit and squash all others.
pick 398c952 commit X_0
squash d6d4b9d commit X_1
squash 67f9e22 commit X_2
squash 4d54d51 commit X_3
squash a5ddc3a commit X_4
squash a3e944c commit X_5
squash 9b046d4 commit X_6
squash e3a952c commit X_7
squash ca88993 commit X_8
squash 88ae514 commit X_9
squash 783f0b9 commit X_10
squash d90952c commit X_11
squash 9e54512 commit X_12
squash 2e95f48 commit X_13
squash 9eeb7f4 commit X_14
squash 2d8629a commit X_15
squash 158e5eb commit X_16
squash f127068 commit X_17
squash 4228271 commit X_18
squash b21e1a5 commit X_19
squash e0d262f commit X_20
squash 3d31768 commit X_21
squash 8b70958 commit X_22
squash 5d23a5d commit X_23
squash d24f107 commit X_24
squash 4b2e897 commit X_25
squash 5ea44bc commit X_26
squash 18675e1 commit X_27
squash 171e8dc commit X_28
squash e09ae06 commit X_29
This doesn't guarantee that you will not have conflicts, so you still need to fix the conflict files manually:
git add file_x
git rm file_y
git rebase --continue
note: maybe this will be necessary do to multiple times.
So, keep going doing it until the last commit.
At the final, you will have your feature branch squashed. To guarantee that everything is ok, just check your branch:
$ git log --oneline --decorate origin/development..feature/QT-100-new-feature
You should see just one commit, which is the squashed one.
If you see something wrong, cancel the process the start again:
$ git reset --hard origin/feature/QT-100-new-feature
note: You are reseting from origin
If everything is fine, you need to push the modifications to the origin:
$ git push origin +feature/QT-100-new-feature
It's important to keep the +
signal otherwise, you will apply to all refs that are pushed. Thus, you can overwrite refs other than your feature branch. So, try to not use --force
.