20

I'm creating a merge driver. I have defined a .gitattributes file as follows:

filename merge=mergeStrategy

I have created the merge driver in $PROJECT/.git/config as follows:

[merge "mergeStrategy"]
    name = My merge strategy
    driver = scripts/mergeScript.sh

This works fine locally, but I would like to commit this merge driver to the git repository so that the merge strategy is in effect for everyone.

Is there a way I can add this (or other Git configuration options) to the repository itself?

KurtPreston
  • 1,062
  • 14
  • 28

1 Answers1

13

You could simply add and commit (and push) script/mergeScript (along with the .gitattributes file, of course)

That would work as long as:

  • mergeScript is somehow in the $PATH of the user executing the merge driver.
  • mergeScript is executable, chmod +x
  • mergeScript is without extension, to allow you to later change its content (from a bash shell to a Perl script to a C executable to ...) if needed.

(Thank you, MestreLion, for the last two points, as he mentioned them in the comment)

That was the case for you locally, as I suspect that '.' was in your $PATH, but you cannot assume that for everybody.

However, the local config file (.git/config) won't be pushed/cloned (as Alexandr Priymak points out in the comment), so the users still need to replicate the declaration of the custom merge driver.

This is a basic safety measure, in order for you to not push a potential "harmful" script which would then be automatically executed at the next merge.

An alternative method using Makefile

An another option is to keep all the git drivers and .gitattributes in the version history and also include something that easily activates all the drivers. For example, one project used Makefile that had special target make gitdrivers that activated all the git drivers in the repo.

This is needed because git considers custom drivers as potential security vulnerability and you need to do something to grant trust to any new drivers. The merge drivers and other hooks are executable code running on your user credentials that start automatically as a side-effect on git actions, so obviously extra care must be taken before running that code.

Running code that changes .git/config to activate the drivers is the git style of granting the trust.

In practice, you can use Makefile for that (but you could use whatever build system or script that suits your project). For Ubuntu Linux hosts, you can simply do it like follows (example with three drivers):

In file .gitattributes:

[attr]POFILE merge=merge-po-files
[attr]IMAGE diff=image
[attr]BINARY diff=binary -merge -text

locale/*.po POFILE
data/*.img BINARY
*.png IMAGE

and in Makefile (indent should be done with U+0009 TAB character only but stackoverflow.com doesn't support it here):

developer-dependencies:
        sudo apt install required-package1 required-package2

submodules:
        @echo "Overwriting submodules with committed versions..."
        git submodule sync
        git submodule update --init

gitdrivers: developer-dependencies submodules
        @echo "Overwriting git drivers with current version..."
        git config merge.merge-po-files.driver "./bin/merge-po-files %A %O %B"
        # show rough thumbnails in text mode for images
        git config diff.image.textconv "./bin/image-textconv"
        git config diff.image.binary "true"
        git config diff.image.cachetextconv "true"
        # show some extra information about binary files
        git config diff.binary.textconv "./bin/binary-textconv"
        git config diff.binary.binary "true"
        @echo "All git drivers done."

Where files merge-po-files, image-textconv and binary-textconv are suitable executables in the project subdirectory bin. If you have a project that needs to work on multiple platforms, you could have an extra dependency to build/link correct binary for the current platform.

The example config tells git to render ASCII presentation of images in diffs, merge gettext .PO files with special merge driver and prevent merging selected binary files even if git heuristics declare those as text. Instead require selecting one version or the other for files marked as BINARY. And show special user visible summary for the BINARY files in diffs.

This way all developers just need to run make gitdrivers once after doing the initial clone. And they automatically get updates to drivers that they've already installed when they merge or rebase their working copy later. And if they're not sure if they have the latest drivers, they can simply re-run the same command at any time. Note that the example above will reset all submodules you have which may or may not what you really want.

TL;DR: .git/config is not included in the repository but you can include a script that re-creates suitable config using git config commands. For UNIX-like systems a shell script is great. If your project needs building the code you can hook installing the required drivers to build process if needed. Also note that it's much safer to use git config ... commands instead of trying to directly modify the config file because those commands will keep working even if the file format of that config file changed in the future.

Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 3
    This doesn't save a config settings in git repository. – Oleksandr Pryimak Jan 12 '12 at 17:38
  • @AlexandrPriymak: good point. I have added that important caveat to the answer. – VonC Jan 12 '12 at 18:02
  • 2
    Don't forget to set `mergeScript.sh` as executable using `chmod +x`, otherwise it may (wont?) work. I also **strongly** suggest you **not** to use extensions for executables. Name it simply `mergeScript`, as in the future it might change from shell script to perl, python, or even compiled C executable. – MestreLion Mar 09 '12 at 23:53
  • 7
    This doesn't actually solve the problem. How do people share these merge drivers in real project? – Paul Tarjan May 05 '16 at 07:41
  • @PaulTarjan the config part of a merge driver can be stored in a versioned file of the repo, but you will still have a manual and local config step to do in order to activate those merge drivers for your local cloned repo. – VonC May 05 '16 at 07:44
  • 2
    Well at a company of thousands of engineers, that is sort of a big ask :( – Paul Tarjan May 05 '16 at 18:16
  • I understand, but it is a basic security requirement to *not* copy around configuration. You still can enforce it by rejecting commits which have not benefited from those merge driver (if you can detect their application in the content of the commit alone) – VonC May 05 '16 at 18:21
  • @Top-Master No: a local config in a Git repository is never pushed/pulled. – VonC May 13 '19 at 04:52
  • thanks, just tested and you are right, see also [force-merge-conflict](https://stackoverflow.com/a/5091756/8740349) post. – Top-Master May 13 '19 at 05:17
  • We have git merge drivers in the code repo and `Makefile` with special target `make gitdrivers` that activates all the git drivers in the repo. This is needed because git considers custom drivers as potential security vulnerability and you need to do something to grant trust on your drivers. We use `Makefile` for that but you could use whatever suits your project. The idea is that if you need more drivers, you run `make gitdrivers` again but updates to existing drivers happen automatically. – Mikko Rantalainen Aug 10 '21 at 11:36
  • The `make gitdrivers` first installs all dependencies needed and then does e.g. `git config merge.merge-po-files.driver "./bin/merge-po-files %A %O %B"`. – Mikko Rantalainen Aug 10 '21 at 11:38
  • 1
    @MikkoRantalainen Thank you for this feedback. I have included your comment in the answer for more visibility. – VonC Aug 10 '21 at 12:19
  • @VonC feel free the format my text better and there doesn't need to be attribution for that simple part. The text is a bit hard to follow as-is because I couldn't make it fit in the comment without shortening it quite a bit. – Mikko Rantalainen Aug 10 '21 at 13:49
  • @MikkoRantalainen Actually, you can edit the answer yourself, in order to add more details. That way, you can format it exactly the way it needs to be. I would be interested in reading your non-abbreviated version. – VonC Aug 10 '21 at 14:05
  • @VonC I added a more verbose explanation at the end of the answer. Hopefully that's okay for you. – Mikko Rantalainen Aug 11 '21 at 14:04
  • 1
    @MikkoRantalainen That looks great! Thank you so much. Much clearer and complete that way. – VonC Aug 11 '21 at 14:06