svn-3646

Version:

1.6.13

Bug Link:

http://subversion.tigris.org/issues/show_bug.cgi?id=3646

Patch Link:

source code patch: http://svn.apache.org/viewvc?view=revision&revision=949307

Symptom:

Background(‘svn merge –record-only’: modify mergeinfo metafile to mark as merged though the diff are not actually merged. Mergeinfo essentially is a meta-data file to record all the merging history. This feature is used to block the automatic change from merging.)

 

Record-only merging introduces unnecessary, self-referential mergeinfo metafile.

If we want to merge a branch of revision (M+1) into a trunk of revision M, using ‘--record-only merge’, we will have the following lines in mergeinfo file:

Added: svn:mergeinfo

   Merged /t1:r2-M

   Merged /t1-branch:rM+1

 

The line “Merged /t1:r2-M” is unnecessary and wrong!!! It is self-referential and contains the natural history!!!

If we don’t use ‘--record-only’ option, then self-referential information is successfully filtered out.

How it is diagnosed:

reproduced

 

How to reproduce:

Step1) setup the repository

                1-1. make a branch ‘t1-branch’ from the trunk ‘t1’

                            ‘svn cp t1 t1-branch’

                1-2. commit the change-set

                            ‘svn ci –m “branch t1 to t1-branch”’

Step2) expected result

                2-1. merge branch back to the trunk

                            ‘svn merge ^/t1-branch t1’

                2-2. print out mergeinfo metadata by ‘svn diff’

 

Property changes on: t1

___________________________________________________________________

Added: svn:mergeinfo

   Merged /t1-branch:r11                                                     

 

=> the trunk is merged from the branch, ‘t1-branch’ of revision 11.

 

Step3) self-referential mergeinfo from ‘—record-only’ merging.

                3-1. record-only merge branch back to the trunk

                            ‘svn merge –record-only ^/t1-branch t1’

                3-2. print out mergeinfo metadata by ‘svn diff’

 

Property changes on: t1

___________________________________________________________________

Added: svn:mergeinfo

   Merged /t1:r2-10

   Merged /t1-branch:r11

 

*Result: Beside the information of t1-branch, the trunk, ‘t1’ also has its own history, ‘/t1:r2-10’. It’s unnecessary self-referential information.

Root Cause:

Brief:

When running ‘svn merge –record-only’, they are creating self-referential mergeinfo because it fails to filter out the self-referential information.

 

In the buggy case, it contains self-referential mergeinfo because of appending every mergeinfo.

To avoid self-referential mergeinfo, we need to register this mergeinfo to the ‘implicit mergeinfo’ data structure so that ‘filter_natrual_history_from_mergeinfo()’ function can get rid of it.

Detail:

To avoid self-referential mergeinfo, we need to get a ‘implicit mergeinfo’ of self-referential data. With this data, ‘filter_natural_history_from_mergeinfo()’ function can get rid of the self-referential info. In ‘populate_remaining_ranges’ function, there’s a routine that gets the target’s implicit mergeinfo during ‘–record-only merges’.

Callstack:

In the first call of ‘populate_remaining_ranges’ with ‘--record-only’ option, the mergeinfo is always the self-referential info while the arguments ‘url1’ and ‘url2’ are the same.

#0  populate_remaining_ranges (

                               children_with_mergeinfo=0x6abc50,

                               source_root_url=0x6abc78 "svn://127.0.0.1/alpha",

                               url1=0x6a71e8 "svn://127.0.0.1/alpha/t1",

                               revision1=1,

                               url2=0x6a7230 "svn://127.0.0.1/alpha/t1",

                               revision2=2,

                               honor_mergeinfo=1,

                               ra_session=0x6a74a0,

                               parent_merge_src_canon_path=0x6abcc0 "/t1",

                               merge_b=0x7fffffffdca0, pool=0x6abbd8)

at subversion/libsvn_client/merge.c:4116

#1  do_directory_merge (...) at subversion/libsvn_client/merge.c:7826

#2  do_merge (...,   record_only=1,) at subversion/libsvn_client/merge.c:8312

#3  merge_peg_locked (...) at subversion/libsvn_client/merge.c:10100

#4  merge_peg_cb (...) at subversion/libsvn_client/merge.c:10134

#5  svn_wc__call_with_write_lock (...) at subversion/libsvn_wc/lock.c:1724

#6  svn_client_merge_peg3 (...) at subversion/libsvn_client/merge.c:10179

#7  svn_cl__merge (...) at subversion/svn/merge-cmd.c:322

#8  main (argc=5, argv=0x7fffffffe7b8) at subversion/svn/main.c:2296

After calculating the revision range of the merge info, it returns to ‘do_directory_merge’ function. In ‘do_directory_merge’ function, ‘filter_natural_history_from_mergeinfo’ function called by ‘record_mergeinfo_for_dir_merge’.

static svn_error_t *

do_directory_merge(...)

{

   ... ...

  /* If we are honoring mergeinfo, then for each item in

     NOTIFY_B->CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be

     merged, and then merge it.  Otherwise, we just merge what we were asked

     to merge across the whole tree.  */

  SVN_ERR(populate_remaining_ranges(...));

   ... ...

  /* Record mergeinfo where appropriate.*/

  if (record_mergeinfo)

    {

      SVN_ERR(record_mergeinfo_for_dir_merge(&range,

                                             mergeinfo_path,

                                             depth,

                                             squelch_mergeinfo_notifications,

                                             notify_b,

                                             merge_b,

                                             pool));

      ... ...

    }

  return svn_error_return(err);

}

static svn_error_t *

record_mergeinfo_for_dir_merge(...)

{

  ... ...

 

  /* Record mergeinfo on any subtree affected by the merge or for

     every subtree if this is a --record-only merge.  Always record

     mergeinfo on the merge target CHILDREN_WITH_MERGEINFO[0]. */

  for (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++)

    {

      if (...)

        {

          ... ...

        }

      else /* Record mergeinfo on CHILD. */

        {

          ... ...

         

          /* Filter any ranges from each child's natural history before

             setting mergeinfo describing the merge. */

          SVN_ERR(filter_natural_history_from_mergeinfo(

            &child_merge_rangelist, child_merge_src_canon_path,

            child->implicit_mergeinfo, &range, iterpool));

          /* If the mergeinfo has only natural history like self-referential info, then the corresponding revision range is deleted and returns in the following statement. However, the buggy version has no implicit information denoting self-reference, so that it does not return but print out the self-referential info */

          if (child_merge_rangelist->nelts == 0)

            continue;

          ... ...

        }

        ... ...

    } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */

           

  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;

}

 The patch is :

--- subversion/trunk/subversion/libsvn_client/merge.c           2010/05/28 21:40:36            949306

+++ subversion/trunk/subversion/libsvn_client/merge.c        2010/05/28 21:44:18            949307

@@ -4112,6 +4112,9 @@ populate_remaining_ranges(apr_array_head

   apr_pool_t *iterpool;

   int i;

   svn_revnum_t gap_start, gap_end;

+  svn_boolean_t child_inherits_implicit;

+  svn_client__merge_path_t *parent;

+  int parent_index;

 

   iterpool = svn_pool_create(pool);

 

@@ -4125,6 +4128,46 @@ populate_remaining_ranges(apr_array_head

               svn_client__merge_path_t *child =

             APR_ARRAY_IDX(children_with_mergeinfo, i,

                           svn_client__merge_path_t *);

+

+              parent = NULL;

+              svn_pool_clear(iterpool);

+

+              /* Issue #3646 'record-only merges create self-referential

+             mergeinfo'.  Get the merge target's implicit mergeinfo (natural

+             history).  We'll use it later to avoid setting self-referential

+             mergeinfo -- see filter_natural_history_from_mergeinfo(). */

+              if (i == 0) /* First item is always the merge target. */

+                {

/* by calling get_full_mergeinfo, we can make a implicit mergeinfo and this information is used later to filter out by filter_natural_history_from_mergeinfo. In no ‘record-only’ case, ‘get_full_mergeinfo’ function is also called so that implicit information is generated. Consequently, it doesn’t have self-referential info */

+                  SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */

+                                         &(child->implicit_mergeinfo),

+                                             NULL, /* child->indirect_mergeinfo */

+                                         svn_mergeinfo_inherited, ra_session,

+                                         child->abspath,

+                                             MAX(revision1, revision2),

+                                         MIN(revision1, revision2),

+                                         merge_b->ctx, pool, iterpool));

+                }

 

Failure type

wrong result

Is there any log message?

No

Can ErrLog insert a log message?

No