0

We introduced php-cs-fixer into our codebase and I want to rebase an existing feature branch. We enforce a semi-linear history, so every feature branch is rebased before merging without squashing the whole branch.

To keep the history clean I want to run the tool on every commit in the branch, but keep the commits in place and not have the code style change with a later commit, but already be correct in every commit that will later be merged into the main branch.

Using git rebase --exec "./vendor/bin/php-cs-fixer fix" main results in a lot of conflicts that I don't want to fix manually, as it is very error-prone.

MaPePeR
  • 931
  • 9
  • 18

2 Answers2

0

I came up with this semi-automatic approach:

I started with git rebase -i main and replaced every pick operation with an edit operation. (Vim: :%s/pick/edit/)

Instead of conflict resolving manually, I use git checkout REBASE_HEAD ., to replace the working tree with the non-code formatted version and then run the code formatting tool again. (In this example ./vendor/bin/php-cs-fixer fix)

Because the rebase behavior is slightly different if a conflict occurs, you need to follow up with different commands to complete the current commit based on the state:

This command if you encounter a normal "edit" breakpoint:

Looks like

Stopped at abc123...  [Commit Message...]
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue

After checkout and code format, amend current commit and continue rebase:

git commit --amend -a --no-edit && git rebase --continue

Complete one-step command:

git checkout REBASE_HEAD . && ./vendor/bin/php-cs-fixer fix && git commit --amend -a --no-edit && git rebase --continue

This command if you encounter a rebase conflict:

Looks like this (hints might be colored):

Auto-merging [File]
CONFLICT (content): Merge conflict in [File]
error: could not apply abc123... [Commit Message]
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply abc123... [Commit Message]

After checkout and code format, stage changes and let git rebase --continue amend the commit:

git add -u . && GIT_EDITOR=true git rebase --continue

Complete one-step command:

git checkout REBASE_HEAD . && ./vendor/bin/php-cs-fixer fix && git add -u . && GIT_EDITOR=true git rebase --continue

If you use the wrong command, the end result will be the same, but you will lose some commits.

Sadly, I couldn't figure out a way to use git rebase --exec(REBASE_HEAD isn't defined during the exec command?) or a way to automatically use the correct resolve command.

I'm sure there is a better solution, but I couldn't find it, so I present mine here.

MaPePeR
  • 931
  • 9
  • 18
0

I just had the exact same issue and this is one of the first hits on google. I solved it using git-filter-repo and its contrib tool lint-history:

This is a simple program that will run a linting program on all non-binary files in history.

[...]

To run eslint --fix on all .js files in history:

lint-history --relevant 'return filename.endswith(b".js")' eslint --fix

Do note, that those tools rewrite your entire git history so do a backup first. Also, if you already published your changes, you better coordinate the change with your team. I had some issues running it on Windows, but using WSL worked fine.

okolaris
  • 31
  • 3