How to apply a git diff to a slightly different repo

Viewed 271

I work with two git repos, where one contains some code from the other but under a different path.

e.g. Repo A has a directory src/packages/my-pkg, and Repo B has a directory different-path/my-pkg, and the two directories have (mostly) the same content.

I want to automate a process of copying features/bugfixes from one to the other. So if a file src/packages/my-pkg/some-component/index.js has been modified, the change can be copied to different-path/my-pkg/some-component/index.js in the other repo. However, index.js might be slightly different between the repos, so it will need to be tolerant of that and apply as much of the diff as it can.

Here's the process I've come up with so far:

# Get a diff containing the changes in a feature branch
cd repoA
git diff master new-feature-branch > ../feature.diff

# Convert the file paths in the diff to the other repo's structure
sed -i "" 's+src/packages/my-pkg+different-path/my-pkg+g' ../feature.diff

# Apply to the other repo
cd ../repoB
git apply --reject ../feature.diff  # --reject option tells git to apply the changes it can, and put the rest in .rej files

This works somewhat, but is very sensitive to differences between the repos. For example, when trying to apply this change from Repo A to Repo B:

--- a/different-path/my-pkg/title-bar/view.js
+++ b/different-path/my-pkg/title-bar/view.js
@@ -95,6 +95,7 @@ export default class TitleBar extends Component<Props> {
                         </span>
                     </div>
                 );
+                // A new comment
             case READONLY:
                 return (
                     <Title>

If any of those context lines have been changed in Repo B (e.g. a prop being added to <Title>), git won't be able to match the location and the diff won't apply.

I think that if git could do a 3-way merge between the old/new versions of the file from Repo A, and the file in Repo B, it could better determine where to insert the changes. But I'm not sure how to make that happen.

Anyone know how to solve this?

(Obviously, the ideal solution would be to pull out the shared code into a reusable module. But it's huge, and there are many small differences, so the amount of work involved makes it a non-starter.)

1 Answers

There are two ways to increase the chances that a not-quite-suitable patch can still be applied:

The first form

git apply -3 that.diff

uses the merge machinery to apply the patch and can generate conflicts, but is otherwise very good at applying a patch with mismatching context lines. git apply uses the blob IDs recorded in the patch; for this reason, it is necessary to add the repository where the patch was generated as a remote repository.

The second form

git apply -C2 that.diff

reduces the number of context lines that must match. This also increases the danger that the patch is applied at a wrong spot in case that the context is ambiguous. It is possible to reduce the context to zero lines, but this is very dangerous if the patch only adds text, because the only hints remaining for patch application are line numbers, and they are generally an imprecise hint. Zero context lines are less a problem if the patch contains removed lines, because they do remain as context.

EDIT: To account for the change in path names, -p<n> can be used to strip a number of directories from the paths recorded in the patch, and --directory=foo/bar can be used to tack on a different path instead. This assumes that at least the file name remains the same.

Related