I realise this is an old question, but I feel the existing answers all miss parts of the described scenario. From what I understand, the desired state is:
- Code on the
develop
branch which does not include the changes in commits 2 and 3.
- A branch containing the changes from commits 2 and 3, based on the desired develop.
- A branch containing the changes from commit 4, based on the desired develop.
There are two ways to "undo" changes in git, both of which have their problems:
- You can
reset
the branch, forcibly pointing it to an earlier commit. This closely matches the intuitive requirement: master
and develop
point at the same commit. But this is a case of "rewriting history", which is dangerous if anyone else has seen the original history and based other changes on it.
- You can
revert
the commits, undoing the changes as a new commit. This maintains the original history of the branch, and gets the code into the desired state. But this can also cause problems: when you want to merge the changes (commits 2 and 3) back in again, git will see them as already applied; you have to make new commits with the same change (e.g. with git rebase
) before you can apply them. It can also lead to confusing merge conflicts, with the same changes being applied and reverted at different times.
If you decide that resetting develop
is safe in your particular circumstances, then re-creating the feature branch is trivial: just checkout commit 3 and run git checkout -b some-branch-name
. If you decide to use git revert
, you will need to re-create the commits with git rebase develop
; the resulting commits should look identical, but will have new commit hashes, so won't show as "already applied" when you are ready to merge.
If you reset develop
, you must also rebase the other feature branch to recreate commit 4 without commits 2 and 3 in its history. If you revert, you could instead merge develop into that branch, so that the new revert commit is also in its history. Rebasing leads to the least confusing commit graph, though.
Graphically, the two options are...
Reset develop to commit 1; point one branch at the existing commit 3, and rebase commit 4 giving a new commit 4a:
master, develop
o 1
| rewrite-feature1
+----------o-----------o
| 2 3
|
| feature2
\----------o
4a
Revert commits 2 and 3 on develop, as a new commit 5; rebase both branches onto this, giving new commits 2b, 3b, and 4b:
master develop
o----------o----------o----------o
1 2 3 5
| rewrite-feature1
+----------o-----------o
| 2b 3b
|
| feature2
\----------o
4b
With a potential third option where you don't rebase the second feature branch, instead merging in the revert commit from develop (shown as commit 6):
master develop
o----------o----------o----------o
1 2 3 5
| | rewrite-feature1
| +----------o-----------o
| \ 2b 3b
| \
| \
\------o-------o feature2
4 6