Reputation: 252
I've found an interresting GIT usecase, in which cherry pick of the set of commits does not produce any conflict, but the rebase of the exactly the same commits does result in conflits. I'm aware it's some specific case, but I would like to understand why that happens.
First of all: I'm working with specific git flow, which can be described as follows:
For instance, the history can look like this:
$ git log --all --oneline --graph
* 4035836 (development) Merge branch 'logging_in' into development
|\
| * 016f383 created the login page
| * 55373c5 implemented the logging in logic
|/
* 2cb0c46 Merge branch 'renamed_contacts' into development
|\
| * 84dc37b updated the contacts page contents
| * 91acf76 renamed the contacts page file
|/
* 2750381 Merge branch 'contacts_page' into development
|\
| * f899247 added contacts page
|/
* 3ca4134 Merge branch 'wellcome_page' into development
|\
| * ac3095c added wellcome page
|/
| * 60e684a (HEAD -> master, production) Merge branch 'logging_in' into development
| * 65afa29 Merge branch 'renamed_contacts' into development
| * 2be22da Merge branch 'contacts_page' into development
| * d709e94 Merge branch 'wellcome_page' into development
|/
* c4c7b06 created readme
As a result, there are two branches, development and production, with exactly the same contents:
$ git diff development..production
$
In theory, rebase of sub-branch of the development branch to release should then work with no issues whatsoever. But it seems it doesn't.
I was working on some task (i.e. task branch branched of the development branch) and I've decided to not include the task in this "release". Thus, I've merged the production branch to master to indicate the current version is completed, created new branch named development_v2 off the master, and I wanted to rebase the task branch to the new development_v2 branch:
* c41d4a9 (rename_wellcome) renamed the wellcome to welcome
* 4035836 (development) Merge branch 'logging_in' into development
(...)
| * 60e684a (HEAD -> master, production, development_v2) Merge branch 'logging_in' into development
(...)
|/
* c4c7b06 created readme
However, when I tried to do the rebase, it produces conflicts:
$ git checkout rename_wellcome
Switched to branch 'rename_wellcome'
$ git rebase development_v2
CONFLICT (rename/delete): contacts.html deleted in HEAD and renamed to about-us.html in 91acf76 (renamed the contacts page file). Version 91acf76 (renamed the contacts page file) of about-us.html left in tree.
error: could not apply 91acf76... renamed the contacts page file
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 91acf76... renamed the contacts page file
But, when I faked the rebase by creating new branch and cherry-picking the rename_wellcome branch contents to that new branch, it all works as expected:
$ git checkout development_v2
Switched to branch 'development_v2'
$ git checkout -b rename_wellcome_v2
Switched to a new branch 'rename_wellcome_v2'
$ git cheery-pick c41d4a9
Removing wellcome.html
[rename_wellcome_v2 0068e97] renamed the wellcome to welcome
Date: Tue Sep 21 20:48:23 2021 +0200
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 welcome.html
delete mode 100644 wellcome.html
Resulting, as expected in:
$ git log --all --oneline --graph
* 0068e97 (HEAD -> rename_wellcome_v2) renamed the wellcome to welcome
* 60e684a (production, master, development_v2) Merge branch 'logging_in' into development
(...)
| * c41d4a9 (rename_wellcome) renamed the wellcome to welcome
| * 4035836 (development) Merge branch 'logging_in' into development
(...)
|/
* c4c7b06 created readme
I understand that renaming (and so with updating the contents) of files causes troubles as in some cases, there can get some file updated in one branch, while on the other does not exist, because it was replaced by the different one.
But I'm wondering, why this happens. I thought rebase and cherry-pick works the same (re-applying the commits/patches), so I'm surprised in one case it tells there are conflicts, but on the other one runs without any conflicts.
Could someone explain why the behaviour differs? And when exactly (what combination of renames/deletes/updates has to happen on each branch)?
Upvotes: 1
Views: 210
Reputation: 30212
The problem is that, because you are keeping master
and development
completely separate from each other and also because of the way you are trying to rebase (just providing the target branch), git is trying to apply all the revisions that are not in master
that are in development
[1].... which is, well, just about everything. So it's not just the tip of renamed_wellcome
that is getting rebased.
In order to pull this off, you need to do this:
git rebase --onto master renamed_wellcome~ renamed_wellcome
[1] git does not consider contents of revisions when checking what has to be rebased. It will only check the revisions themselves... so even if a revision has been applied in master
(by cherry-picking or whatever other means that is not a merge
.. a real one, a squash creates the same headache because it creates a different revision), if it's not the original revision from development
, to git it's just another completely different revision, even if contents and everything else in there is the same. Git can't just do that effort of analysis when rebasing. When just providing the target branch to rebase, git will find the last revision that is present in both branches (source and target branches... which in your case is c4c7b06
) and then it will start applying all the revisions after that from source branch on top of target branch.
Upvotes: 2