Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a proper patch when removing a function and its docblock with git

Tags:

git

When you have docblocks above multiple functions removing a function creates a sub-optimal patch.

index.js:

/**
 * Function foo description.
 */
function foo() {}

/**
 * Function bar description.
 */
function bar() {}

Removing function foo with its docblock generates the following patch:

diff --git a/index.js b/index.js
index f4e18ef..933004f 100644
--- a/index.js
+++ b/index.js
@@ -1,9 +1,4 @@
 /**
- * Function foo description.
- */
-function foo() {}
-
-/**
  * Function bar description.
  */
 function bar() {}

This means that any merge that brings with it commits that touch the space between function foo and function bar now result in a conflict. For example imagine we created a branch feature-1 before removing foo, and in index.js added a function foobar between the two. The conflict would look as follows:

/**
<<<<<<< HEAD
=======
 * Function foo description.
 */
function foo() {}

/**
 * Function foobar description.
 */
function foobar() {}

/**
>>>>>>> feature-1
 * Function bar description.
 */
function bar() {}

I imagine there would be no issue if the /** was grabbed from the top instead. I'm sure there's a good reason for git to prefer removing from the end but I'd like to force it to grab it from the start. Is there a way to easily do this? Or is manual patch editing the only way?

like image 906
alextes Avatar asked Oct 18 '22 00:10

alextes


1 Answers

It's far from perfect, but the new --compaction-heuristic in Git 2.9 often does what you want. See this blog post for details. You can configure it to be on by default, but given that it sometimes makes things worse, I have not done so:

git config --global diff.compactionHeuristic true

Your Git version must be at least 2.9 for this to have any effect.

One flaw in the current implementation is that it quite literally requires a blank line above the modified section. Starting at the top of the file is not sufficient. For instance, suppose we start with:

block:
This file has
three blocks.

block:
There is a blank line
between each.

block:
This is the third
block.

If we delete the middle block, the default diff retains the second block: line and deletes the third block: line. Turning on compaction moves the diff hunk up until it reaches the blank line above the second block:, which is what we want.

Unfortunately, if we delete the first block, the compaction heuristic attempts to move the diff hunk up to include the first block: line, but fails because it hits the top of the file, where there is no blank line above it (as there is no line at all above it). It therefore leaves the first word block: in place and deletes the second.

(Fixing this would require only that the compaction algorithm provide a "virtual line zero" that is blank. Note that the problem itself never occurs at the end of the file since the default diff favors deletion of later lines. Meanwhile, an ugly workaround is to leave a blank line at the top of each file, just so that compaction can see it.)

like image 135
torek Avatar answered Oct 21 '22 05:10

torek