Question

I am using CVS and after a merge, I have hundreds of conflicts. None of the conflicts are an issue (they're unimportant changes as a result of keyword expansion differences on the trunk and branch).

I know no changes have been made to the files as this is simply a merge of a vendor branch into the trunk. A few dozen files have been randomly inspected to validate this.

Is there a quick way of resolving all of the conflicts without doing each one manually? (I have TortoiseCVS, WinCVS and the command-line at my disposal).

Was it helpful?

Solution 3

I'm going to answer my own question as the EMACS solution suggested by @jesup was impractical for someone who hasn't used EMACS in 15 years!

In my case, because I knew that the the only conflict was in the expansion of the $LOG$ keywords and I didn't really care about the content of the comments. I saw two possible solutions:

  1. go back to the import of the vendor source and ensure that the keyword expansion is disabled and redo the merge.

  2. be brave and copy all of the vendor files over the conflict files (thus resolving the conflict). In my case, I knew that there had been no changes made by us this was a no-risk option.

I went with 2. A rules-based comparison using BeyondCompare confirmed all of the files had only 'unimportant' changes.

OTHER TIPS

can you do the merge again?

do a

cvs update -kk

before the merge this will not expand the keywords.
The only keyword that will be a problem is the $Log one

You could program a macro to do it in Emacs without too much trouble - if you're used to emacs/elisp, or if you probably could do it without elisp using a Keyboard Macro in emacs, then using ^u^u^u^u^u^x^e (repeat keyboard macro (^x^e) 1024 times; each ^u increases the count by 4x). The macro would be a simple repetition of the commands needed to resolve one conflict in the file. You could also load up all the files with conflicts into buffers, then use elisp or maybe a keyboard macro to resolve conflicts then switch to the next buffer, and repeat that.

I wouldn't be surprised if there's an easier way, but this would work. And even if you have to load up all the files into buffers and then run the keyboard macro, you cando that and be done in a relatively short time.

Pseudo-emacs:

cvs status | grep conflict >/tmp/foo;
load /tmp/foo into an emacs buffer
edit buffer to remove all but the file/pathnames (use keyboard macros!)
load them all into buffers:
^x^(        (start-macro)
^@^e        (mark (or control-space), then end-of-line)
ESC-w       (copy)
^n^a        (next-line, beginning of line (set up for next iteration))
^x^f        (load-file)
^y          (yank saved)
<RETURN>    (load it - you could resolve conflicts right here and save)
^xb         (switch to buffer)
foo<RETURN> (go back to foo)
^x^)        (end macro)
^x^e        (repeat macro once) or
^u^u^u^u^u^x^e (repeat macro 1024 times or until BEEP)

Now you have all the hundreds of files in emacs buffers, and you can set up a macro to grab the next buffer, resolve conflicts, and save it - then repeat that macro N times.

Here is a C++ program I wrote to do this. It compiles in Visual Studio 2008 Express, and probably any other compiler.

This input output is via redirect, which is perhaps not so convenient, but you can write a cmd file to call it, such as

@echo off
copy /y %1 \temp\t
resolve %2 %3 < \temp\t > %1
del \temp\t

The code is

// resolve.cpp
#include "stdafx.h"
#include "string.h"

int main(int argc, char* argv[])
{
    const int MAXWIDTH = 10000;
    char line[MAXWIDTH];

    enum { ECHO, OLD, NEW, ERROR };

    int state = ECHO;
    int num = 0;
    int quiet = 0;
    int pick = OLD;

    for (int i = 1; i < argc; ++i)
    {
        if (!strcmp(argv[i],"-h") || !strcmp(argv[i],"--help") || !strcmp(argv[i],"?"))
        {
            printf("Automatically fix CVS merge conflicts.\n"
                "Options:\n"
                " -n use the bottom code, the new code (default uses top, old code).\n"
                " -q quiet\n"
                " -h help\n"
                "use stdin and stdout for input/output and stderr for status.");
            return 0;
        }
        else if (!strcmp(argv[i],"-n"))
        {
            pick = NEW;
        }
        else if (!strcmp(argv[i],"-q"))
        {
            quiet = 1;
        }
        else
            fprintf(stderr,"unknown option %s\n",argv[i]);
    }

    while (fgets(line, MAXWIDTH, stdin))
    {
        // trim trailing whitespace
        for (int i = strlen(line); i >= 0 && line[i] < ' '; --i) 
            line[i] = 0;

        ++num;
        if (!strncmp(line,"<<<<<<< ",8))
        {
            state = (state==ECHO) ? OLD : ERROR;
            if (!quiet)
                fprintf(stderr,"At line: %d\n", num);
        }
        else if (!strcmp(line,"======="))
        {
            state = (state==OLD) ? NEW : ERROR;
        }
        else if (!strncmp(line,">>>>>>> ",8))
        {
            state = (state==NEW) ? ECHO : ERROR;
        }
        else
        {
            if (state == ECHO || state == pick)
                printf("%s\n",line);
            if (!quiet && state != ECHO)
                fprintf(stderr,"%c%s\n", (state==pick) ? '+' : '-', line);
            continue;
        }
        if (!quiet)
            fprintf(stderr,"%s\n",line);
    }
    return 0;
}

Disclaimer: You might want to verify your output.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top