com.sangupta.keepwalking.MergeRepo.java Source code

Java tutorial

Introduction

Here is the source code for com.sangupta.keepwalking.MergeRepo.java

Source

/**
 *
 * MergeRepo - allows to merge two different snapshots of the same repository
 * Copyright (c) 2012, Sandeep Gupta
 * 
 * Read more at http://blog.sangupta.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.sangupta.keepwalking;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;

/**
 * Allows to merge two different snapshots of the same repository which are checked
 * in to two different SCM versions.
 *
 * @author sangupta
 */
public class MergeRepo {

    /**
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
        if (args.length != 3) {
            usage();
            return;
        }

        final String previousRepo = args[0];
        final String newerRepo = args[1];
        final String mergedRepo = args[2];

        final File previous = new File(previousRepo);
        final File newer = new File(newerRepo);
        final File merged = new File(mergedRepo);

        if (!(previous.exists() && previous.isDirectory())) {
            System.out.println("The previous version does not exists or is not a directory.");
            return;
        }

        if (!(newer.exists() && newer.isDirectory())) {
            System.out.println("The newer version does not exists or is not a directory.");
            return;
        }

        final IOFileFilter directoryFilter = FileFilterUtils.makeCVSAware(FileFilterUtils.makeSVNAware(null));

        final Collection<File> olderFiles = FileUtils.listFiles(previous, TrueFileFilter.TRUE, directoryFilter);
        final Collection<File> newerFiles = FileUtils.listFiles(newer, TrueFileFilter.TRUE, directoryFilter);

        // build a list of unique paths
        System.out.println("Reading files from older version...");
        List<String> olderPaths = new ArrayList<String>();
        for (File oldFile : olderFiles) {
            olderPaths.add(getRelativePath(oldFile, previous));
        }

        System.out.println("Reading files from newer version...");
        List<String> newerPaths = new ArrayList<String>();
        for (File newerFile : newerFiles) {
            newerPaths.add(getRelativePath(newerFile, newer));
        }

        // find which files have been removed from Perforce depot
        List<String> filesRemoved = new ArrayList<String>(olderPaths);
        filesRemoved.removeAll(newerPaths);
        System.out.println("Files removed in newer version: " + filesRemoved.size());
        for (String removed : filesRemoved) {
            System.out.print("    ");
            System.out.println(removed);
        }

        // find which files have been added in Perforce depot
        List<String> filesAdded = new ArrayList<String>(newerPaths);
        filesAdded.removeAll(olderPaths);
        System.out.println("Files added in newer version: " + filesAdded.size());
        for (String added : filesAdded) {
            System.out.print("    ");
            System.out.println(added);
        }

        // find which files are common 
        // now check if they have modified or not
        newerPaths.retainAll(olderPaths);
        List<String> modified = checkModifiedFiles(newerPaths, previous, newer);
        System.out.println("Files modified in newer version: " + modified.size());
        for (String modify : modified) {
            System.out.print("    ");
            System.out.println(modify);
        }

        // clean any previous existence of merged repo
        System.out.println("Cleaning any previous merged repositories...");
        if (merged.exists() && merged.isDirectory()) {
            FileUtils.deleteDirectory(merged);
        }

        System.out.println("Merging from newer to older repository...");
        // copy the original SVN repo to merged
        FileUtils.copyDirectory(previous, merged);

        // now remove all files that need to be
        for (String removed : filesRemoved) {
            File toRemove = new File(merged, removed);
            toRemove.delete();
        }

        // now add all files that are new in perforce
        for (String added : filesAdded) {
            File toAdd = new File(newer, added);
            File destination = new File(merged, added);
            FileUtils.copyFile(toAdd, destination);
        }

        // now over-write modified files
        for (String changed : modified) {
            File change = new File(newer, changed);
            File destination = new File(merged, changed);
            destination.delete();
            FileUtils.copyFile(change, destination);
        }

        System.out.println("Done merging.");
    }

    private static void usage() {
        System.out.println(
                "RepoMerge: Command-line tool to merge two snapshots of a single repositories that come from ");
        System.out.println(
                "           different sources. Like you have your older repository in SubVersion and the newer");
        System.out.println(
                "           code in Perforce. Now you want to merge the code and bring SVN back to the level of");
        System.out.println("           Perforce.");
        System.out.println("");
        System.out.println("Usage:    $ java com.sangupta.keepwalking.MergeRepo <previous> <newer> <destination>");
        System.out.println("");
        System.out.println("          previous      The folder corresponding to the older repository snapshot.");
        System.out.println("          newer         The folder corresponding to the newer repository snapshot.");
        System.out.println("          destination   The folder where the updated repository will be created.");
    }

    /**
     * Checks if the file with given path is different in two different folders/branches
     *  
     * @param commonPath
     * @param svn
     * @param perforce
     * @return
     * @throws IOException
     */
    private static List<String> checkModifiedFiles(List<String> commonPath, File svn, File perforce)
            throws IOException {
        List<String> changed = new ArrayList<String>();

        for (String filePath : commonPath) {
            File svnFile = new File(svn, filePath);
            File perforceFile = new File(perforce, filePath);

            boolean equal = FileUtils.contentEqualsIgnoreEOL(svnFile, perforceFile,
                    Charset.defaultCharset().name());
            if (!equal) {
                changed.add(filePath);
            }
        }

        return changed;
    }

    /**
     * Extract the relative path of the file from the absolute path, given the parent.
     * 
     * @param file
     * @param parent
     * @return
     */
    private static String getRelativePath(File file, File parent) {
        String path = file.getAbsolutePath();
        String parentPath = parent.getAbsolutePath();
        if (path.startsWith(parentPath)) {
            return path.substring(parentPath.length() + 1);
        }

        return path;
    }

}