git merge/rebase feature branch after master rename script

182 Views Asked by At

Background: I am working on a patch for Drupal core (PHP) where module class files that currently use PSR-0 will be moved two directories up, so they will use PSR-4 instead. This will make many people angry, because a lot of pending patches / local feature branches will need a re-roll. I would like to find a way to make this as painless as possible.

There is a big project with a public master branch, and a number of people having their own local feature branches.

On the master branch, we add a script that will move a bunch of files around. Every file that used to be in core/modules/$module/lib/Drupal/$module/Foo/Bar.php will be moved two directories up, so it becomes core/modules/$module/lib/Foo/Bar.php. This happens for every module, so $module can take a number of values.

The local feature branches can modify existing files, but they can also add new files or delete existing ones. They could even rename files. And all of this on the "old" directory structure.
Merge or rebase does in fact save a lot or all of the modifications over to the renamed file - although I am not sure it can be trusted 100%.
But the new files created will stay in the old location, and need to be moved manually.

A more reliable way would be to execute the script on the local branch, for every local commit that is not on master, and then somehow rebase the local branch.

The goal is to have a history where all the local commits look as if they had been done on the new file structure.
E.g. if a file was created in core/modules/views_ui/lib/Drupal/views_ui/NewClass.php, then it should look like it had been created directly in core/modules/views_ui/lib/NewClass.php

What is the best way to automate all this?
Ideally I would provide a script for other developers that they can use without too much manual work.

git filter-branch seems promising, but I would need to use it in a very specific way.

EDIT: diagram to clarify

  master
    m5
    ^
    |
    |
    +
    m4
    ^
    |
    |
    +                                            local dev
  master          rewritten local history         moved
  moved  +--------> new1 +-------> new2 +--------> new3
    m3               ^              ^               ^
    ^                |              |               |
    |move            |move          |move           |move
    |script          |script        |script         |script
    |                |              |               |
    +                |              |               +
 before              +              +           local dev
  move  +---------> old1 +-------> old2 +--------> old3
    m2            old local history
    ^
    |
    |
    +
    m1

EDIT II: The following manual commands would do the trick: (all local changes are in a "core" subdirectory)

git checkout m3
git branch dev-new; git checkout dev-new
rm -r core
git checkout old1 .
php core/scripts/switch-psr4.php
git add -A .; git commit -m"transformed old1"
rm -r core
git checkout old2 .
php core/scripts/switch-psr4.php
git add -A .; git commit -m"transformed old2"
rm -r core
git checkout old3 .
php core/scripts/switch-psr4.php
git add -A .; git commit -m"transformed old3"
2

There are 2 best solutions below

0
On

It seems that a plain git rebase is actually not so bad!

In the scenario in the diagram above, this would be

git checkout dev-local
git rebase origin/master
php move-files-around.php
git add -A .
git commit -m"Move locally added files to their new location"

This can be spiced up with some interactive rebase, to make it appear as if locally added files had been created directly in their new location.

4
On

Yes, git filter-branch is probably the tool you want.

You probably want to use something like:

git filter-branch --tree-filter \
  'git mv core/modules/views_ui/lib/{Drupal/views_ui/,}NewClass.php' \
   -- origin/master..master

An example using the git repository:

$ git co -b foo 1f6fb7ffc344e59589ac794ce7ae47ae7c2cff42
$ git log --name-status -n 3
commit 1f6fb7ffc344e59589ac794ce7ae47ae7c2cff42
Author: Ralf Thielow <[email protected]>
Date:   Fri Nov 8 20:34:35 2013 +0100

    l10n: de.po: improve error message when pushing to unknown upstream

    Signed-off-by: Ralf Thielow <[email protected]>
    Acked-by: Thomas Rast <[email protected]>

M       po/de.po

commit 1d38363d86d16dee58df15a047d448baee6435ba
Author: Ralf Thielow <[email protected]>
Date:   Sat Nov 2 18:58:52 2013 +0100

    l10n: de.po: translate 68 new messages

    Translate 68 new messages came from git.pot update in 727b957
    (l10n: git.pot: v1.8.5 round 1 (68 new, 9 removed)).

    Signed-off-by: Ralf Thielow <[email protected]>
    Acked-by: Thomas Rast <[email protected]>

M       po/de.po

commit 1b12df5262aae78b5fbcdb94e718d19710ce12a5
Author: Ralf Thielow <[email protected]>
Date:   Sat Nov 2 18:56:14 2013 +0100

    po/TEAMS: update Thomas Rast's email address

    Signed-off-by: Ralf Thielow <[email protected]>
    Acked-by: Thomas Rast <[email protected]>

M       po/TEAMS
$ git filter-branch --tree-filter 'git mv po/de.po de.po' -- foo^^^..foo
Rewrite 1f6fb7ffc344e59589ac794ce7ae47ae7c2cff42 (3/3)
Ref 'refs/heads/foo' was rewritten
$ git log --name-status -n 3
commit 4c2629a746d42a0eed79fb7be5fc4f66258c0f3f
Author: Ralf Thielow <[email protected]>
Date:   Fri Nov 8 20:34:35 2013 +0100

    l10n: de.po: improve error message when pushing to unknown upstream

    Signed-off-by: Ralf Thielow <[email protected]>
    Acked-by: Thomas Rast <[email protected]>

M       de.po

commit 67aab1af4cf200998c565e31a75a107469f3da78
Author: Ralf Thielow <[email protected]>
Date:   Sat Nov 2 18:58:52 2013 +0100

    l10n: de.po: translate 68 new messages

    Translate 68 new messages came from git.pot update in 727b957
    (l10n: git.pot: v1.8.5 round 1 (68 new, 9 removed)).

    Signed-off-by: Ralf Thielow <[email protected]>
    Acked-by: Thomas Rast <[email protected]>

M       de.po

commit 31161ea7d16d531dd49f13ac8675b2f9c46ab0eb
Author: Ralf Thielow <[email protected]>
Date:   Sat Nov 2 18:56:14 2013 +0100

    po/TEAMS: update Thomas Rast's email address

    Signed-off-by: Ralf Thielow <[email protected]>
    Acked-by: Thomas Rast <[email protected]>

A       de.po
M       po/TEAMS
D       po/de.po

If I understood you correctly, this is what you want.