0

I have three branch on my git tree. master contains validated source version, develop contains staging versions, then I have some feature branches.

For now, I have the current strucutre; The numbers represents some individual commit hashes for simplicity.

master              develop     feature
  o----------o----------o----------o
  1          2          3          4

Commit 2 and 3 come from a feature I merged on the develop branch. I then started working on a new feature branch.

I need to rewrite what was done in commit 2 and 3. For this, I need to branch my current work on new-feature from master and re-build the merged branch I had containing commits 2 and 3.

Here is what my git repo tree should look like.

[What I want]
master/develop
      o 1
      |                rewrite-feature
      +----------o-----------o
      |          2           3
      |
      |       feature
      \----------o
                 4

master and develop are pushed. feature is a local branch.

I am the only one working on this project, so I can mess up with the origin if needed without impacting anyone.

I'm really not sure of what I should do, specialy regarding to the pushed master and develop branches.

I think I should rebase the feature branch on commit 1. But I don't understand how to deal with those commits 2 and 3 without messing with the public repository.

4 Answers 4

1

Do not rewrite public history.

The easiest way would be to revert develop to master as a new commit. A simple git checkout master -- . will stage changes reverting commits 2 and 3. Commit those changes to develop and note the commit Id of master that you are reverting too in the commit message. This gives you a history similar to:

master                          feature
  o----------o----------o----------o
  1          2          3\         4
                          \
                           \    develop
                            \------o
                                   5
                                   ^
                                   |
        Reverts commits 2 and 3 ---+

If the feature branch is a private tree (it has not been pushed) then just rebase the feature branch on to develop.

If the feature branch has been pushed, then just do a merge from develop into feature, and don't worry about the commit history. Clean history is nice, but not a requirement. Once branches have been pushed, merge commits are bound to happen. Don't sweat this too much.

1

There are two aspects:

  • Do not rewrite public branches: any branch that belongs to the team (rather than yourself personally) must not be rewritten, except in the direst of emergencies.

    In your situation that would be master and probably also develop.

  • Decide whether feature branches are history or proposals: you can work either way, but it's better to be consistent. In the "clean history" approach, feature branches are proposals and can be rebased and modified up until the point where they're made part of a public branch (main, develop, master, etc). In the "keep history" approach, feature branches are part of history and are merged into the public branch.

So, in your situation, there are two possible approaches:

  • Declare that merging feature1 to develop was an error and undo it; this is especially suitable if it's in the immediate past and nobody else has seen develop yet. It's not really suitable if you've done substantial work on the feature branch, or if the code has gone anywhere else.

    master/develop                  feature2
      o----------o----------o----------o
      1          2          3\         4
                              \
                               \    feature1-fixes
                                \------o
                                       5
    

    There are very limited circumstances in which this will apply.

  • If others have seen develop at commit 3, or if it's just generally part of history now, make a new branch off develop and make bugfixes on that.

    master              develop     feature2
      o----------o----------o----------o
      1          2          3\         4
                              \
                               \    feature1-fixes
                                \------o
                                       5
    

    At that point you're just working on two features in parallel — the new feature2 and the fixes to the already-existing feature1; you can merge/rebase them to develop in either order when they're ready.

0

Is it really important to rewrite the history? I mean, I myself like clean history, but in the end what really matters is the state of the repo not its history.

If it's not that important, just do the fix like any others: create a new feature, fix, commit, push, merge in develop, merge in master. Then rebase the other features on develop.

I don't know if you'll have the graph you want, but if it's really important to fix the history or you just want to:

Rename develop and the feature branch as something else as backup.

  • create a new branch "Master2", rebase interactively and amend the commits.
  • Force push on Master2 on Master
  • Create develop from master.
  • Create featureA. Rebase featureA on oldFeature.
  • Check that everything is OK
  • Delete old branhces
0

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.