git diff produces incorrect patch

Viewed 117

I'm trying to produce a patch from a git tag A and master and to then apply the patch as a single commit to a separate branch B (whose last commit is tagged A).

I believe git is missing something when generating the patch that causes it to not apply cleanly.

The file it/my/application/Something.kt in question contains code similar to:

package it.my.application

import io.micronaut.runtime.event.annotation.EventListener
import io.micronaut.transaction.annotation.TransactionalEventListener
import io.micronaut.transaction.annotation.TransactionalEventListener.TransactionPhase
import it.my.application.SomethingFacade
import org.slf4j.LoggerFactory
import javax.inject.Singleton

typealias Something = Int

in master the file was renamed to other/my/application/Something.kt and changed to:

package other.my.application

import io.micronaut.runtime.event.annotation.EventListener
import io.micronaut.transaction.annotation.TransactionalEventListener
import io.micronaut.transaction.annotation.TransactionalEventListener.TransactionPhase
import org.slf4j.LoggerFactory
import javax.inject.Singleton
import javax.transaction.Transactional

typealias SomethingId = Int

Note:

  • the line package it.my.application was changed to package other.my.application
  • the line import it.my.application.SomethingFacade was removed
  • the line import javax.transaction.Transactional was added

Looking at the patch these is the hunk that git generates for this portion of the code:

diff --git a/src/main/kotlin/it/my/application/Something.kt b/src/main/kotlin/other/my/application/Something.kt
similarity index 91%
rename from src/main/kotlin/it/my/application/Something.kt
rename to src/main/kotlin/other/my/application/Something.kt
index ed24736..cb13606 100644
--- a/src/main/kotlin/it/my/application/Something.kt
+++ b/src/main/kotlin/other/my/application/Something.kt
@@ -1,10 +1,11 @@
-package it.my.application
+package other.my.application
 
 import io.micronaut.runtime.event.annotation.EventListener
 import io.micronaut.transaction.annotation.TransactionalEventListener
 import io.micronaut.transaction.annotation.TransactionalEventListener.TransactionPhase
 import org.slf4j.LoggerFactory
 import javax.inject.Singleton
+import javax.transaction.Transactional
 
 typealias SomethingId = Int

As you can see from this hunk:

  • the change of the package line is properly reported
  • the addition of the Transactional import is properly reported
  • no mention of the removal of the SomethingFacade line!

When trying to apply the patch git fails saying that "it does not match"..

I tried generating the patch with -C 2/1/0 and they all fail in some way or another (Note: I'm generating a single patch for the whole directory. With -C 0 I believe this file works but countless other files do not).

I can do git apply --3way and fix stuff manually, but doing this the problem does not get actually caught... the result is:

package other.my.application

import io.micronaut.runtime.event.annotation.EventListener
import io.micronaut.transaction.annotation.TransactionalEventListener
import io.micronaut.transaction.annotation.TransactionalEventListener.TransactionPhase
import it.my.application.SomethingFacade
import org.slf4j.LoggerFactory
import javax.inject.Singleton
import javax.transaction.Transactional

typealias SomethingId = Int

So the line about SomethingFacade is left behind. This is the output of the merge for this file:

Checking patch src/main/kotlin/it/my/application/Something.kt => src/main/kotlin/other/my/application/Something.kt...
error: while searching for:
package it.my.application

import io.micronaut.runtime.event.annotation.EventListener
import io.micronaut.transaction.annotation.TransactionalEventListener
import io.micronaut.transaction.annotation.TransactionalEventListener.TransactionPhase
import org.slf4j.LoggerFactory
import javax.inject.Singleton

typealias SomethingId = Int


error: patch failed: src/main/kotlin/it/my/application/Something.kt:1
Falling back to three-way merge...

Shouldn't git generate either:

--- a/src/main/kotlin/it/my/application/Something.kt
+++ b/src/main/kotlin/other/my/application/Something.kt
@@ -1,10 +1,11 @@
-package it.my.application
+package other.my.application
 
 import io.micronaut.runtime.event.annotation.EventListener
 import io.micronaut.transaction.annotation.TransactionalEventListener
 import io.micronaut.transaction.annotation.TransactionalEventListener.TransactionPhase
-import it.my.application.SomethingFacade
 import org.slf4j.LoggerFactory
 import javax.inject.Singleton
+import javax.transaction.Transactional
 
 typealias SomethingId = Int

or something like:

--- a/src/main/kotlin/it/my/application/Something.kt
+++ b/src/main/kotlin/other/my/application/Something.kt
@@ -1,3 +1,3 @@
-package it.my.application
+package other.my.application
 
 import io.micronaut.runtime.event.annotation.EventListener
@@ -4,7 +4,8 @@
 import io.micronaut.transaction.annotation.TransactionalEventListener
 import io.micronaut.transaction.annotation.TransactionalEventListener.TransactionPhase
-import it.my.application.SomethingFacade
 import org.slf4j.LoggerFactory
@@ -7,10 +8,11 @@
 import javax.inject.Singleton
+import javax.transaction.Transactional
 
 typealias SomethingId = Int

?

Why does it simply ignore the SomethingFacade line?


Additional information (not sure whether this is relevant):

The A tag is on a previous commit of the master branch whose contents are identical to the HEAD of the B branch. The B branch was created from a previous version of master where all the commits were squashed into a single commit.

The point of branch B is that it should hide the commit activity & authors of the original commits. We periodically sync master with B by squashing all new commits into a single commit in B. So branch B and master do not have a common history but for every commit of B there is a commit of master whose content is identical. I'm not sure whether this creates an issue with git diff... I have successfully used this approach in other code bases without issues.

1 Answers

To get that particular diff output, the files that diff is comparing here must have those particular texts as inputs. That is, the pair of files that git diff is comparing here don't have import it.my.application.SomethingFacade in either file.

Note that you can view the two files being compared, as their blob hash IDs are given by the index line:

index ed24736..cb13606 100644

The left-side file is Git internal blob ed24736 and right right-side file is Git internal blob cb13606.

(These two different files may be contained in many different commits, since Git de-duplicates the committed copies that appear in each commit. So there is no telling which commits are being compared here; all we know, from the git diff output, is which blob objects are being compared. But your git diff command will usually specify the two commits to be compared: in

git diff <hash1> <hash2>

for instance, the two hash-es present here are the hash IDs of the commits in question.

If you are using tag names, e.g.:

git diff v1.2 v3.4

you can have Git resolve the tag names to commit hash IDs, if you'd like, with git rev-parse v1.2 and git rev-parse v3.4. Either way, though, we know from the actual git diff output that the files being compared have the two given blob hash IDs.

Related