[darcs-devel] hunk moves as a new kind of prim patch

Ben Franksen ben.franksen at online.de
Thu Feb 15 21:04:42 UTC 2018


Am 15.02.2018 um 07:48 schrieb Ganesh Sittampalam:
> On 14/02/2018 23:34, Ben Franksen wrote:
> 
>> If we had primitive "hunk move" patches we could avoid conflicts in many
>> cases.
> 
> I'd love to have this, and spent a while thinking about it some years
> ago. I think the commute rules will be quite tricky to get right, as
> there are lots of cases, e.g. overlaps between either source or
> destination of the move. Getting the boundary conditions (do touching
> patches commute or not) is also quite tricky even for hunk patches, but
> we can err on the side of being conservative.

Yes, that was my thought, too. And yes, the side conditions are a bit
trickier than I thought. More on that below.

BTW:

I have a question regarding the existing hunk commutation behavior. When
I experimented with the example I sent, I noticed something strange:

Keeping with the notation in my original message, if i have

  A: abc -> acb

i.e. move "b" to the end, versus

  B: abc -> abd

i.e. change the third line to "d", then A and B currently conflict. I
wonder why this is the case. To further test this, I have split A into
the two hunks (one that removes "b" and one that re-adds it) and
recorded them separately. I already get the conflict for the first of
the two patches, i.e. the one that removes the second line (abc -> ac).

I don't see why there is a conflict here. Could/should we relax the
rules? What could go wrong?

> The way I was thinking about the commute rules before was to break the
> move down into a "cut" and a "paste" operation - I don't think those
> should be actual primitive patch types, but it helps to think about them
> that way when working out the rules.

Exactly what I am doing right now. I have this code now:

{-
The fields of HunkMove are:

  - file name where data is removed
  - line number where data is removed
  - file name where data is inserted
  - line number where data is inserted
  - the data

The semantics is left-to-right, i.e. first remove data, then insert
data. Thus, in case of a hunk move inside the same file, the line number
where the data is inserted is the line number *after* the data has been
removed. IOW

    apply (HunkMove fo lo fn ln h)
  =
    apply (FP fo (Hunk lo h []) :> FP fn (Hunk ln [] h))
  =
    apply (FP fo (Hunk lo h [])) >> apply (FP fn (Hunk ln [] h))

This makes for very simple instances Apply and Invert.
-}

data HunkMove = HM !FileName !Int !FileName !Int [B.ByteString]
  deriving (Eq, Ord)

The instance Invert simply swaps (fo,lo) with (fn,ln).

For commutes involving HunkMoves we can allow everything that is allowed
now, in particular, for the three cases HunkMove :> Hunk, Hunk :>
HunkMove, and HunkMove :> HunkMove we can copy the (good) cases implied
by splitting HunkMoves into two (semantically equivalent) Hunks.

In addition, commutation also succeeds whenever the add-part of the Hunk
on the left hand side is contained in the remove-part of the HunkMove.
Analogously, whenever the remove-part of a hunk on the right hand side
is fully contained in the add-part of the HunkMove on the left hand side.

> I think we'd definitely want moving between files as you mention, that
> means you can really express code refactorings properly.

Thanks for the encouragement. I am on it...

Cheers
Ben



More information about the darcs-devel mailing list