0

I'm trying to write a custom git merge driver in power shell but I already fail at "doing nothing". That is opening the local file, writing the content back to it as the merge result and returning an exit code of 0.

param (
    [Parameter(Mandatory=$true)][string]$baseFile,
    [Parameter(Mandatory=$true)][string]$localFile,
    [Parameter(Mandatory=$true)][string]$remoteFile
)

$PSDefaultParameterValues['*:Encoding'] = 'utf8'

(Get-Content $localFile) | Out-File $localFile
exit 0

To the .git/config I added:

[merge "custom"]
    name = custom powershell merge driver
    driver = c:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy RemoteSigned -Command ./mergeDriver.ps1 -baseFile %O -localFile %A -remoteFile %B

And the .gitattributes looks like this:

*.txt text
file.txt merge=custom

With file.txt being a utf8 text file to merge.

Every time I try to merge/cherry-pick the file with conflicts I get error: add_cacheinfo failed to refresh for path 'file.txt'; merge aborting.

Example repository

I create a test repository at https://bitbucket.org/Deeem2031/mergedriver_test/src/master/ If you clone it and add the change to .git/config you should be able to reproduce the problem by running git cherry-pick dbb40ba

Things I already noticed

If you remove $PSDefaultParameterValues['*:Encoding'] = 'utf8' or don't touch the file at all i.e. remove (Get-Content $localFile) | Out-File $localFile the error is gone.

It doesn't seem to matter if file.txt is utf8 encoded beforehand or not.

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/out-file does state Input objects are automatically formatted as they would be in the terminal, [...] which does imply it might change things but the only difference I can see (even with a hex-editor) is a added CRLF at the end of the file.

I did find this answer https://stackoverflow.com/a/65967910/6308948 which does sound very similar to my problem but I'm already writing my result to $localFile.

So the obvious question: Why does this error happen?

Deeem2031
  • 75
  • 7
  • 1
    I don't know anything about PowerShell, but in general, any attempt to read from file *f* while also writing *to* file *f* is ... fraught. That is, in general, you would want to do the get-content step first, wait for it to complete, and only *then* begin writing to `$localFile`, lest the writing-to destroy the data before the reading-from is done. – torek Jun 16 '21 at 16:31

1 Answers1

2

The problem is actually in the merge driver, i.e. mergeDriver.ps1.

Git's contract is that the file contents received, and written, by the merge driver are in the "clean" form, i.e. in your case with LF-only (or at least without CR/LF) line endings.

However, (Get-Content $localFile) | Out-File $localFile does not leave the file contents unchanged, despite how it might look like. The reason is that $localFile has no line ending at all in your case. hexdump -C says this about its contents:

00000000  ef bb bf 76 65 72 73 69  6f 6e 20 3d 20 31 35 30  |...version = 150|

And Out-File appends a CR/LF!

So if you change your merge driver to call Out-File -NoNewline instead, it will work without that add_cacheinfo error message.

That is, it will work as intended: that cherry-pick will say that nothing was changed, and ask you if you want to commit nevertheless (which would require the --allow-empty option).

  • 1
    This does clarify the problem and solves the issue with the above example. The only thing I had to add was using `((Get-Content $localFile) -join "\`n") + "\`n" | Set-Content -NoNewline $localFile` so I can handle multiline "file.txt"s which is based on this answer: https://stackoverflow.com/a/48919146/6308948 Thanks – Deeem2031 Jun 17 '21 at 06:44