11

Is there any way (even if a hack) to do a comparison of two Sitecore content branches. I have an SXA solution where one site was cloned to create another, but I now want to merge some changes from the original to the new site. Something like Razl which could be fooled into comparing two different branches of the same instance would be perfect! ;-)

One idea would be to serialize the two branches and then compare using WinMerge/BeyondCompare etc. Another idea could be to make packages of the two branches, extract those packages and compare the results.

  • If you have, Sitecore PowerShell Extensions installed then creating packages can be done via two clicks. Then compare packages with WinMerge – Peter Procházka Mar 8 '18 at 16:55
  • What level of granularity are you looking for in the comparison? Field level comparisons? Or item level (ie: missing, same or modified level)? Both? – vandsh Mar 8 '18 at 17:52
  • @vandsh both! I'm only interested in custom (non standard) fields though. – Matthew Dresser Mar 8 '18 at 17:59
  • Would the Compare Servers module on the Marketplace work? I haven't used it so I don't know if you can compare two branches on one database but it might be worth a shot. Other than that I was going to suggest the serialize + winmerge route as that should work. – fettesps Mar 8 '18 at 18:22
15

You may be able to package up both paths of the content tree and use something like WinMerge or Kdiff to do a item by item comparison, which might be quick and dirty but may be hard to wade thru especially in instances of inherited fields. I usually like to see if I can use Sitecore Powershell Extensions to complete potentially repeatable tasks like this. You may or may not have SPE installed but in the event you might, this may help you:

# Site 1 content path
$site1 = "/sitecore/content/Sites/Site1"
# Site 2 content path
$site2 = "/sitecore/content/Sites/Site2"
# Get site relative paths and IDs
$site1Desc = Get-ChildItem  -Path "master:$site1" -Recurse
$site1Lookup = $site1Desc | ForEach-Object { $lookup = @{} } { $lookup[$_.FullPath.Replace($site1, "")] = $_.ID } { $lookup }
$site1LookupRel = $site1Desc | ForEach-Object { $objArr = @() } { 
        $newObj = (New-Object -TypeName PSObject | Add-Member NoteProperty Name $_.Name -PassThru | Add-Member NoteProperty ID $_.ID -PassThru | Add-Member NoteProperty Path $_.FullPath.Replace($site1, "") -PassThru)
        $objArr += $newObj
    } { $objArr }

$site2Desc = Get-ChildItem  -Path "master:$site2" -Recurse
$site2Lookup = $site2Desc | ForEach-Object { $lookup = @{} } { $lookup[$_.FullPath.Replace($site2, "")] = $_.ID } { $lookup }
$site2LookupRel = $site2Desc | ForEach-Object { $objArr = @() } { 
        $newObj = (New-Object -TypeName PSObject | Add-Member NoteProperty Name $_.Name -PassThru | Add-Member NoteProperty ID $_.ID -PassThru | Add-Member NoteProperty Path $_.FullPath.Replace($site2, "") -PassThru)
        $objArr += $newObj
    } { $objArr }

# Compare lists of relative paths/items
$itemComparison = Compare-Object -ReferenceObject $site1LookupRel -DifferenceObject $site2LookupRel -Property Path -IncludeEqual
$matchingItems = $itemComparison | Where-Object { $_.SideIndicator -eq "==" } |Select-Object -Expand Path
$nonMatchingItems = $itemComparison | Where-Object { $_.SideIndicator -ne "==" } |Select-Object -Expand Path
Write-Host " *** Matching Items *** "
foreach($matchingItem in $matchingItems) {
    $site1MatchingItem = $null;
    $site2MatchingItem = $null;
    if($site1Lookup.Contains($matchingItem))
    {
        $site1MatchingItem = Get-Item -Path "master:" -ID $site1Lookup[$matchingItem]
    }

    if($site2Lookup.Contains($matchingItem)){
        $site2MatchingItem = Get-Item -Path "master:" -ID $site2Lookup[$matchingItem]
    }

    if($site1MatchingItem -ne $null -AND $site2MatchingItem -ne $null) {
       $site1ItemFieldValues = Get-ItemField -Item $site1MatchingItem -ReturnType Field -Name "*" | Where-Object {!$_.ContainsStandardValue}  | Select-Object -Property @{Name="SourceID"; Expression={$site1MatchingItem.ID}},Name,Value 
       $site2ItemFieldValues = Get-ItemField -Item $site2MatchingItem -ReturnType Field -Name "*" | Where-Object {!$_.ContainsStandardValue} | Select-Object -Property @{Name="SourceID"; Expression={$site12MatchingItem.ID}},Name,Value 
       if($site1ItemFieldValues -ne $null -AND $site2ItemFieldValues -ne $null){
           Write-Host "$matchingItem does exist in both sites, comparing now - (" $site1MatchingItem.ContentPath " <=>" $site2MatchingItem.ContentPath ")" -ForegroundColor Green
           $comparedItems = Compare-Object -ReferenceObject $site1ItemFieldValues -DifferenceObject $site2ItemFieldValues -Property Name,Value -IncludeEqual -PassThru
           $comparedItems | Format-Table -AutoSize
       }
       else{
           Write-Host "$matchingItem does exist in both sites, but there is nothing to compare - (" $site1MatchingItem.ContentPath " <=>" $site2MatchingItem.ContentPath ")" -ForegroundColor Green
       }
    }
}
Write-Host " *** NonMatching Items *** "
foreach($nonMatchingItem in $nonMatchingItems) {
    $site1MatchingItem = $null;
    $site2MatchingItem = $null;
    if($site1Lookup.Contains($nonMatchingItem))
    {
        $site1MatchingItem = Get-Item -Path "master:" -ID $site1Lookup[$nonMatchingItem]
    }

    if($site2Lookup.Contains($nonMatchingItem)){
        $site2MatchingItem = Get-Item -Path "master:" -ID $site2Lookup[$nonMatchingItem]
    }

    if($site1MatchingItem -ne $null -AND $site2MatchingItem -eq $null){
        Write-Host "$nonMatchingItem exists in Site1 but not Site2 - (" $site1MatchingItem.ContentPath ")" -ForegroundColor Yellow
    }
    elseif($site1MatchingItem -eq $null -AND $site2MatchingItem -ne $null){
        Write-Host "$nonMatchingItem exists in Site2 but not Site1 - (" $site2MatchingItem.ContentPath ")" -ForegroundColor Yellow
    }
}

The above SPE script should grab all content under a Site path and compare it to the other site by site relative paths (path underneath the site node itself). If an item exists in one site and not the other it will show up in the bottom nonMatchingItems and show up in yellow in the output. If they do match, it will show up green and will use the Get-ItemField command to get the fields you specify (in this case * is used to return all fields, in which we then filter based on ContainsStandardValue to filter out items that are currently using Standard Value values) and associated values. It uses the Powershell Compare-Object command output to a table to show you how the fields themselves compare. Output should look something like this:

 *** Matching Items ***
.....
/_content does exist in both sites, but there is nothing to compare - ( /Site1/_content  <=> /Site2/_content )
/_content/Country Landing Intro does exist in both sites, comparing now - ( /Site1/_content/Intro  <=> /Site2/_content/Intro )

SourceID                               Name         Value  SideIndicator
--------                               -----        -----  -------------
{96892835-46BF-437A-8C9E-40B12EC58881} Image Is Map true   ==
{96892835-46BF-437A-8C9E-40B12EC58881} Is Header    true   =>
{98D165DB-25EA-4E80-B9A3-ABCC181A9FAF} Is Header    false  <=


/Cities does exist in both sites, but there is nothing to compare - ( /Site1/Cities  <=> /Site2/Cities )
 *** NonMatching Items ***
/Cities/Second-Test exists in Site1 but not Site2 - ( /Site1/Cities/Second-Test )
/Cities/Second-Test/_content exists in Site1 but not Site2 - ( /Site1/Cities/Second-Test/_content )

This should help as a starting point, I could see extending the matched item comparison (the SideIndicator Value) to go into more detail on the exact field values that differ, but at least identifying which fields differ may help you in the current capacity.

Additional commentary here

6

While Vandsh's PowerShell script is very nice, I think I would use Unicorn and Beyond Compare. You can configure Unicorn to ignore fields as needed. Once your items are serialized, use Beyond Compare to merge your changes. If you have Unicorn's transparent sync enabled it will automatically pull your changes into Sitecore. Otherwise, you can manually initiate the sync with the admin page.

Here is an example of a very simple Unicorn config with a couple of standard fields ignored. Use the readme and comments in the files installed from NuGet for guidance on how to make adjustments for your scenario.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <unicorn>
      <configurations>
        <configuration name="Sample sites" description="Serialize original and cloned site">
          <predicate>
            <include name="Site1" database="master" path="/sitecore/Content/Home" />
            <include name="Site2" database="master" path="/sitecore/Content/Home2" />
          </predicate>
          <fieldFilter>
            <exclude fieldID="{56776EDF-261C-4ABC-9FE7-70C618795239}" note="'__Help link' field on Standard Template" />
            <exclude fieldID="{577F1689-7DE4-4AD2-A15F-7FDC1759285F}" note="'__Long description' field on Standard Template" />
          </fieldFilter>
          <dataProviderConfiguration enableTransparentSync="true" />
          <syncConfiguration updateLinkDatabase="true" updateSearchIndex="true" />
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

When comparing your two folders with Beyond Compare, you may need to use the "Align with..." option in the right-click menu to get the first level to compare properly. At this point, if you use the Compare Contents option, it will show that all of your files are different because of the ID, Parent and Path properties which I don't think you can tell Unicorn to exclude. If you open an individual file diff, however, you can tell Beyond Compare that this is "Unimportant text".

Click the Rules button in the toolbar. Then add regex rules as shown below and select the "Use for all files within parent session" option from the dropdown at the bottom of the dialog.

Compare Rules

Click OK, then go back to your folder compare tab, select all and then use the Compare Contents action. Now Beyond Compare will show the symbol for files who's only differences are this unimportant text.

rules-based folder compare

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.