Search code examples
patchrebasedarcs

How to re-order dependent changes in darcs?


In darcs, what if I want to re-order to the top (or just throw away) a patch which other patches depend on (i.e., change the same file)?

In git, I'd simply do a git rebase -i <UNTOUCHED-REVISION> and reorder or throw away some changes; then git would in a dumb fashion try to apply the old changes to the new variant of the tree one by one, and ask me to resolve the arising conflicts.

In darcs, I see no way to force it to ignore the dependencies between patches. If I obliterate or suspend (or unrecord) a patch which other patches depend on, darcs refuses to do it. (Because it wants to behave in a clever manner.)


Solution

  • I had the following plan to do this:

    1. suspend the dependent patches (those which depend on the patch which I want to re-order or throw away).
    2. somehow unrecord the patch to re-order and save it somewhere else (perhaps in another "darcs branch", i.e., a copy of the repo) and revert the changes to the working directory so that the working directory is clean.
    3. after that, unsuspend the suspended patches up to the state where I want to insert the re-ordered patch. (Resolving all arising conflicts.)
    4. apply the saved patch (perhaps, by pulling from the saved "branch", i.e., the copy of the darcs repo which I have saved on step 2).
    5. unsuspend all remaining patches. (Resolving all arising conflicts.)

    Here are some notes on how all this has proceeded.

    1. suspend the dependent patches

    If among the patches dependent on the patch you want to re-order there is a single "minimal" one (according to the graph of dependencies), then you simply give the command to suspend it (and after that there will be no "active" patches which would depend on the re-ordered patch):

    darcs suspend -h <MINIMAL-DEPENDENT-HASH>
    

    (and confirm the suspension of all other dependent patches).

    Of course, if there are several minimal dependent patches, you have to suspend each (each's subgraph will be asked for suspension).

    2. save and revert the unwanted changes

    Preparation

    First, I look at the changes I'm going to work with:

    darcs diff -h 61fbb4aeac9e69cf30d232eda274c18194d7a8d9 --diff-command='emacs -f ediff-directories-with-ancestor-command %1 %2'
    

    (The change was logically simple, but diff showed it in a complicated way, so, here, I launched Emacs's ediff via a special function I had written for this purpose.)

    I saw that the change included some cleanup and the addition of a new feature. So, the plan is now to split it: unrecord the patch, and record two patches instead.

    darcs unrec -h 61fbb4aeac9e69cf30d232eda274c18194d7a8d9
    

    Now, I can view and (perhaps, work) with the changes with ediff, too.

    darcs diff --diff-command='emacs -f ediff-directories-with-ancestor-command %1 %2'
    

    First, I record the cleanup patch (selecting and editing only the relevant hunks). Then, the added action:

    darcs rec -m 'examples/Process.hs: code cleanup (present as a sequence of actions and error handling)'
    darcs rec -m 'examples/Process.hs: print the C program (from the AST) after the check (as gcc -E does)'
    

    Saving as a patch (a variant)

    One could use darcs obliterate -o or -O to save the obliterated change, and then restore it with darcs apply (according to that advice).

    But I proceeded differently:

    Saving as a branch (a variant)

    Cloning didn't work for a repo with suspended patches:

    ~/TOOLS/prog/language-c $ darcs clone . ../language-c_printAST
    
    darcs failed:  Can't clone a repository with a rebase in progress
    ~/TOOLS/prog/language-c $ 
    

    So, let's make a copy (and check whether we would be allowed to pull from it):

    ~/TOOLS/prog/language-c $ cp -a ../language-c ../language-c_printAST
    ~/TOOLS/prog/language-c $ darcs pull ../language-c_printAST
    
    darcs failed:  Incompatibility with repository /home/imz/TOOLS/prog/language-c_printAST:
    Cannot transfer patches from a repository where a rebase is in progress
    ~/TOOLS/prog/language-c $ cd ../language-c_printAST/
    ~/TOOLS/prog/language-c_printAST $ darcs rebase obliterate
    <...>
    Really obliterate all undecided patches? y
    Rebase finished!
    ~/TOOLS/prog/language-c_printAST $ cd ../language-c
    ~/TOOLS/prog/language-c $ darcs pull ../language-c_printAST
    HINT: if you want to change the default remote repository to
          /home/imz/TOOLS/prog/language-c_printAST,
          quit now and issue the same command with the --set-default flag.
    No remote patches to pull in!
    ~/TOOLS/prog/language-c $ 
    

    Ok, good, so we will pull from that repo later.

    Revert the changes

    Throw away the unwanted (or re-ordered) patch:

    darcs obliterate
    

    3. Unsuspend the patches that should come before it

    It's a good idea to make a backup copy of the repo at this point, because you might screw something up during the unsuspending and resolving conflicts.

    Unsuspend the patches that should come before it. (Unfortunately, external merge tool is not supported in unsuspend.) It's better to unsuspend them one by one as you'll have to resolve the conflicts caused by each one and amend the patch:

    darcs rebase unsuspend
    # resolve conflicts
    darcs amend
    # repeat for the remaining patches you want to have
    

    4. Apply the saved patch

    darcs pull ../../language-c_printAST
    # resolve conflicts
    darcs amend
    

    5. Unsuspend all remaining patches.

    (Again, it's better to do this one by one.)

    darcs rebase unsuspend
    # resolve conflicts
    darcs amend
    # repeat for the remaining patches you want to have
    

    (For some reason, on the first attempt, I lost a hunk in the last unsuspended patch. But I repeated everything in a copy of the backup copy, and there I reached the wished final state.)