0

For a repository like this:

init---A---A2---...---[D---B---merge]---...---master
                        \      /
                         C---B2

where B and B2 have conflict and was solved in merge commit.

now I want to join commit A and A2 to one commit A. And I tried

git rebase -i <commit id of init>

and change

pick xxxxxxx A
pick xxxxxxx A2
...

to

pick xxxxxxx A
squash xxxxxxx A2
...

but conflicts appeared when applying B2, and I have to deal with the conflicts manually just like what I have done in merge.

Actually, there are so many structures like or even more complex than in square brackets in the whole commit tree. So it's not realistic to deal with all the conflicts manually.

The problem is that when I use rebase, the whole commit tree will merged into one line and it will not deal with the conflict automatically by applying the commit merge.

So what can I do to join only the commits A and A2 but still remain all the commit structure after A2 unchanged so that the repository 'looks' the same after the operation?

To make some further explanation, in commit A there's some sensitive information added in a test file and it was removed in commit A2. So I can't publish this project if I can't remove these information permanently. So is there any alternative solution to this situation?

Dizzam
  • 23
  • 1
  • 4
  • 1
    Possible duplicate of [Combine the first two commits of a Git repository?](https://stackoverflow.com/questions/435646/combine-the-first-two-commits-of-a-git-repository) – yarwest Dec 28 '17 at 09:19
  • 1
    In short, you cannot. Once you touched a commit, every children will no longer be the same. The best you can do is to apply the changes of children again so it "looks" the same. Given you have already have hundreds of commit and branches afterwards, there is no easy way to do the rebase work you want. Not to mention you need to have every clone of your repository clone again. Not realistic. – Adrian Shum Dec 28 '17 at 10:06
  • the problem is not to join the first commit, but to deal with hundred of the commits left behind, which already have so many merges and commit trees. So maybe @Adrian Shum is right, I cannot do that and i'd better give up – Dizzam Dec 30 '17 at 00:24
  • well i just wonder if there is some smart tools that can help me "to apply the changes of children again so it 'looks' the same" – Dizzam Dec 30 '17 at 00:26
  • Because even if there's hundreds of commits but the problem actually only occurs when it comes to merges of two commit trees. After rebase, two commit trees will join together and I have to deal with the conflict manually again. So is there any command to rebase the commit but still preserve all the commit trees – Dizzam Dec 30 '17 at 00:51
  • @Dizzam I added another way to join commit B and C to one commit, you can have a try. – Marina Liu Jan 02 '18 at 06:41

2 Answers2

1

To join (or 'squash' as it's called) you need to interactively rebase. If the commits you want to alter are the first two, you can rebase from the root of the repo.

This would look a little something like:

git rebase -i --root
pick hash X
squash hash Y
pick hash Z

The -i means you will be doing it interactively. --root means starting from the root of the repo.

After that initial command you will see a list of commits with the top one being the first commit. To merge the first two commits you can simply keep the first one (with the pick keyword) and merge the previous one into it (using the squash keyword). You could also edit the first commit message by replacing pick with reword.

After you've done this, you should leave all the other commits be and confirm.

More information can be found here

yarwest
  • 873
  • 8
  • 19
0

You can also use use steps to join the two early commits together.

Assume the commit you want to join is on master branch, and the commit history as:

A---B---C---D---…---X  master

To join commit B and commit C together while keeping the commit history after commit C (commits from D to X), you can use below commands:

git checkout -b temp <commit id for A>
git merge <commit id for C> --squash
git commit -m 'join B and C together'

Now the commit history will be (commit M is the squash merged commit, join commit B and commit C together):

A---B---C---D---…---X  master
 \
  M  temp

Then you can rebase the following commits after commit C:

git rebase --onto temp <commit id for C> master
git push -f origin master
git branch -D temp

Now the commit history on master branch will be (commit M is the commit join commit B and commit C together):

A---M---D'---…---X'  master
Marina Liu
  • 36,876
  • 5
  • 61
  • 74
  • Thanks for your answer and I have tried to follow your instruction. But a conflict occurred during the rebase process. And I couldn't continue the process because there would be more conflicts appearing after I solved this one. I don't know why this occurs but I guess it has something to do with a same file operation in different commit trees. And it is also not realistic to deal with all the conflict manually. – Dizzam Jan 03 '18 at 08:06
  • there shouldn't has conflicts during rebase? Did you has merge conflicts during `git merge --squash` command, or made any changes after merging? – Marina Liu Jan 03 '18 at 08:15
  • And also please make sure you rebase with the correct commit to start, as the command `git rebase --onto temp C master` (it's commit **C** here). – Marina Liu Jan 03 '18 at 08:16
  • Can you double check the commit for git rebase --onto is correct (last comment mentioned)? If the commit is for commit C, there shouldn't has any conflicts. – Marina Liu Jan 04 '18 at 07:22
  • Thanks for your patient answering. I have double checked the command but the conflicting problem still appear. And I have made some detailed explanation about my problem. Hope it will help to make a better understanding of my problem – Dizzam Jan 05 '18 at 03:36