jessehouwing
jessehouwing

Reputation: 114847

Can configure Git to always merge changes to a file in one branch to different files in another?

I'm maintaining a customized version of a Ghost theme on GitHub. it's taking in a regular stream of changes from the main TryGhost/Casper repo which isn't too hard to keep track of.

There is one thing that's bothering me and that has to do with the custom-*.hbs files in my theme. Whenever Ghost updates the post.hbs, I must remember to merge those changes into each custom-*.hbs template as well.

Is there a way I can tell Git that post.hbs's changes must always be merged into multiple files on my end?

The repo, as it is currently, can be found here:

For now, I've resorted to manually diffing the changes and applying them to the related files.

Upvotes: 0

Views: 37

Answers (2)

Felipe Cabargas
Felipe Cabargas

Reputation: 986

Short answer: No. But you can use git-merge-file to achieve something like this:

git merge-file <current-version> <common-ancestor> <other-version>

You can use a third placeholder file to create a "common-ancestor", that should contain the pre-merge main file:

git show HEAD~1:post.hbs > parent-post.hbs

Then running the following will make a 3-way merge:

git merge-file custom-post.hbs parent-post.hbs post.hbs

In order to automatize it you would need to do create a post-merge githook that runs after the regular merge that recreates the common-ancestor file and then merges the changes.

If the custom-* files are identical to their non-custom counterpart, consider using symlinks.

Upvotes: 1

torek
torek

Reputation: 489083

The short answer is no, which is a bit unfortunate.

When Git is doing a true merge—a "merge as a verb", as I like to call it—Git works with three commits: the merge base, which Git finds on its own, and two tip commits. One of the tip commits is always HEAD, by definition, and the other is usually identified by some branch name:

          o--o--X   <-- you-are-here (HEAD)
         /
...--o--*   [merge base]
         \
          o--o--Y   <-- other-branch

All three commits are snapshots of all files, as usual. Git performs the merge—combines the different changes since the merge base—by, in effect, running:

git diff --find-renames <hash-of-*> <hash-of-X>   # what we changed
git diff --find-renames <hash-of-*> <hash-of-Y>   # what they changed

Git then combs through these differences—the change-sets produced by the two diff commands—and pairs up files, so that it knows that file.ext in the base is the same file as file.ext in ours and/or in theirs.

If the rename-detector detects that file.ext in the base has become newname.ext in our commit X, Git knows that it should combine the changes they made to file.ext to the changes we made to newname.ext-vs-file.ext, storing the final result in newname.ext. But this is strictly pairwise. Although git diff supports several "find copies" options, there is no "find copies" option to git merge. Furthermore, there is no "break existing pairing" option (git diff has one, -B with a threshold, similar to -M for rename-finding and -C for copy-finding): if the merge base and one tip contains a file whose path is P, that file-pair is paired for the duration of the merge. If the merge base and the other tip contains a file whose path is P, that file-pair is likewise paired.

Hence, if your merge base contains post.hbs and both tips contain post.hbs, the pairing is strictly post.hbs = post.hbs.

Upvotes: 1

Related Questions