Tools to push code back upstream.
diff --git a/libcore/tools/integrate/Android.mk b/libcore/tools/integrate/Android.mk
index 629a5fd..f0f25b3 100644
--- a/libcore/tools/integrate/Android.mk
+++ b/libcore/tools/integrate/Android.mk
@@ -7,9 +7,9 @@
 	Filesystem.java \
 	Git.java \
 	Module.java \
-	Modules.java \
 	MappedDirectory.java \
 	PullHarmonyCode.java \
+	PushAndroidCode.java \
 	Svn.java
 
 LOCAL_MODULE:= integrate
diff --git a/libcore/tools/integrate/Filesystem.java b/libcore/tools/integrate/Filesystem.java
index a9c1789..4b296a0 100644
--- a/libcore/tools/integrate/Filesystem.java
+++ b/libcore/tools/integrate/Filesystem.java
@@ -32,11 +32,28 @@
      * directory is nonempty.
      */
     public int moveContents(String source, String target) {
+        return copyContents(true, source, target);
+    }
+
+    /**
+     * Copies all of the files in {@code source} to {@code target}, one at a
+     * time. Unlike {@code move}, this approach works even if the target
+     * directory is nonempty.
+     */
+    public int copyContents(String source, String target) {
+        return copyContents(false, source, target);
+    }
+
+    private int copyContents(boolean move, String source, String target) {
         List<String> files = new Command("find", source, "-type", "f") .execute();
         for (String file : files) {
             String targetFile = target + "/" + file.substring(source.length());
             mkdir(parent(targetFile));
-            new Command("mv", "-i", file, targetFile).execute();
+            if (move) {
+                new Command("mv", "-i", file, targetFile).execute();
+            } else {
+                new Command("cp", file, targetFile).execute();
+            }
         }
         return files.size();
     }
diff --git a/libcore/tools/integrate/Module.java b/libcore/tools/integrate/Module.java
index 5cb7035..63d35a2 100644
--- a/libcore/tools/integrate/Module.java
+++ b/libcore/tools/integrate/Module.java
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -22,6 +25,47 @@
  */
 class Module {
 
+    static final Map<String, Module> VALUES;
+    static {
+        Map<String, Module> valuesMutable = new LinkedHashMap<String, Module>();
+
+        String svnRoot = "http://svn.apache.org/repos/asf/harmony/enhanced/classlib/trunk/modules";
+        valuesMutable.put("archive", new Module.Builder(svnRoot, "archive")
+                .mapDirectory("archive/src/main/native/archive/shared",
+                        "archive/src/main/native")
+                .mapDirectory("archive/src/main/native/zip/shared",
+                        "archive/src/main/native")
+                .build());
+
+        valuesMutable.put("crypto", new Module.Builder(svnRoot, "crypto")
+                .mapDirectory("crypto/src/test/api/java.injected/javax",
+                        "crypto/src/test/java/org/apache/harmony/crypto/tests/javax")
+                .mapDirectory("crypto/src/test/api/java",
+                        "crypto/src/test/java")
+                .mapDirectory("crypto/src/test/resources/serialization",
+                        "crypto/src/test/java/serialization")
+                .mapDirectory("crypto/src/test/support/common/java",
+                        "crypto/src/test/java")
+                .build());
+
+        valuesMutable.put("regex", new Module.Builder(svnRoot, "regex").build());
+
+        valuesMutable.put("security", new Module.Builder(svnRoot, "security")
+                .mapDirectory("security/src/main/java/common",
+                        "security/src/main/java")
+                .mapDirectory("security/src/main/java/unix/org",
+                        "security/src/main/java/org")
+                .mapDirectory("security/src/test/api/java",
+                        "security/src/test/java")
+                .build());
+
+        valuesMutable.put("text", new Module.Builder(svnRoot, "text").build());
+
+        valuesMutable.put("x-net", new Module.Builder(svnRoot, "x-net").build());
+
+        VALUES = Collections.unmodifiableMap(valuesMutable);
+    }
+
     private final String svnBaseUrl;
     private final String path;
     private final Set<MappedDirectory> mappedDirectories;
diff --git a/libcore/tools/integrate/Modules.java b/libcore/tools/integrate/Modules.java
deleted file mode 100644
index 2475852..0000000
--- a/libcore/tools/integrate/Modules.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * 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.
- */
-
-/**
- * Constants that define modules shared by Harmony and Dalvik.
- */
-public class Modules {
-
-    private static final String SVN_ROOT
-            = "http://svn.apache.org/repos/asf/harmony/enhanced/classlib/trunk/modules";
-
-    public static final Module ARCHIVE = new Module.Builder(SVN_ROOT, "archive")
-            .mapDirectory("archive/src/main/native/archive/shared",
-                    "archive/src/main/native")
-            .mapDirectory("archive/src/main/native/zip/shared",
-                    "archive/src/main/native")
-            .build();
-
-    public static final Module CRYPTO = new Module.Builder(SVN_ROOT, "crypto")
-            .mapDirectory("crypto/src/test/api/java.injected/javax",
-                    "crypto/src/test/java/org/apache/harmony/crypto/tests/javax")
-            .mapDirectory("crypto/src/test/api/java",
-                    "crypto/src/test/java")
-            .mapDirectory("crypto/src/test/resources/serialization",
-                    "crypto/src/test/java/serialization")
-            .mapDirectory("crypto/src/test/support/common/java",
-                    "crypto/src/test/java")
-            .build();
-
-    public static final Module REGEX
-            = new Module.Builder(SVN_ROOT, "regex").build();
-
-    public static final Module SECURITY = new Module.Builder(SVN_ROOT, "security")
-            .mapDirectory("security/src/main/java/common",
-                    "security/src/main/java")
-            .mapDirectory("security/src/main/java/unix/org",
-                    "security/src/main/java/org")
-            .mapDirectory("security/src/test/api/java",
-                    "security/src/test/java")
-            .build();
-
-    public static final Module TEXT
-            = new Module.Builder(SVN_ROOT, "text").build();
-
-    public static final Module X_NET
-            = new Module.Builder(SVN_ROOT, "x-net").build();
-
-    // TODO: add the other modules
-}
diff --git a/libcore/tools/integrate/PullHarmonyCode.java b/libcore/tools/integrate/PullHarmonyCode.java
index 6710801..ce019d4 100644
--- a/libcore/tools/integrate/PullHarmonyCode.java
+++ b/libcore/tools/integrate/PullHarmonyCode.java
@@ -102,8 +102,61 @@
         }
     }
 
+
     public static void main(String[] args) {
-//        new PullHarmonyCode(527399, 802921).pull(Modules.CRYPTO);
-        new PullHarmonyCode(772995, 802921).pull(Modules.ARCHIVE);
+        if (args.length < 3) {
+            printUsage();
+            return;
+        }
+
+        int currentSvnRev = Integer.parseInt(args[0]);
+        int targetSvnRev = Integer.parseInt(args[1]);
+
+        if (currentSvnRev < 527399 || targetSvnRev <= currentSvnRev) {
+            System.out.println("Invalid SVN revision range: "
+                    + currentSvnRev + ".." + targetSvnRev);
+            return;
+        }
+
+        Module module = Module.VALUES.get(args[2]);
+        if (module == null) {
+            System.out.println("No such module: " + args[2]);
+            return;
+        }
+
+        PullHarmonyCode puller = new PullHarmonyCode(currentSvnRev, targetSvnRev);
+        puller.pull(module);
+    }
+
+    private static void printUsage() {
+        System.out.println("This tool will prepare a three-way merge between the latest Harmony");
+        System.out.println("the latest Dalvik, and their common ancestor. It downloads both old");
+        System.out.println("and new versions of Harmony code from SVN for better merge results.");
+        System.out.println();
+        System.out.println("Usage: PullHarmonyCode <current_rev> <target_rev> <module>...");
+        System.out.println();
+        System.out.println("  <current_rev>  is the SVN revision of the Harmony code that was");
+        System.out.println("                 most recently integrated into Dalvik. This should");
+        System.out.println("                 be a number greater than 527399. The current");
+        System.out.println("                 revision for each module is tracked at");
+        System.out.println("                 http://go/dalvik/harmony");
+        System.out.println();
+        System.out.println("    <target_rev> is the SVN revision of the Harmony code to be");
+        System.out.println("                 merged into Dalvik. This should be a number greater");
+        System.out.println("                 than <current_rev>. The latest Harmony revision is");
+        System.out.println("                 tracked at");
+        System.out.println("                 http://svn.apache.org/viewvc/harmony/?root=Apache-SVN");
+        System.out.println();
+        System.out.println("        <module> is one of " + Module.VALUES.keySet());
+        System.out.println();
+        System.out.println("This program must be executed from within the dalvik/libcore directory");
+        System.out.println("of an Android git client. Such a client must be synced and contain no");
+        System.out.println("uncommitted changes. Upon termination, a new Git branch with the");
+        System.out.println("integrated changes will be active. This branch may require some manual");
+        System.out.println("merging.");
+        System.out.println();
+        System.out.println("Example usage:");
+        System.out.println("  java -cp ../../out/host/linux-x86/framework/integrate.jar PullAndroidCode \\");
+        System.out.println("    527399  802921 security");
     }
 }
diff --git a/libcore/tools/integrate/PushAndroidCode.java b/libcore/tools/integrate/PushAndroidCode.java
new file mode 100644
index 0000000..c0002f5
--- /dev/null
+++ b/libcore/tools/integrate/PushAndroidCode.java
@@ -0,0 +1,87 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+import java.util.UUID;
+
+/**
+ * Copy the current Android sourcecode into Apache Harmony, where it can be
+ * reviewed and submitted to their SVN. Only run this script after first merging
+ * the latest harmony code into Android.
+ */
+public class PushAndroidCode {
+
+    private final String androidPath;
+    private final String harmonyPath;
+
+    public PushAndroidCode(String androidPath, String harmonyPath) {
+        this.androidPath = androidPath;
+        this.harmonyPath = harmonyPath;
+    }
+
+    public void push(Module module) {
+        Filesystem filesystem = new Filesystem();
+
+        // copy android code to a temp directory that is laid out like Harmony
+        String temp = "/tmp/" + UUID.randomUUID();
+        filesystem.mkdir(temp);
+        filesystem.copyContents(androidPath + "/" + module.path(),
+                temp + "/" + module.path());
+        for (MappedDirectory mappedDirectory : module.getMappedDirectories()) {
+            filesystem.moveContents(
+                    temp + "/" + mappedDirectory.gitPath(),
+                    temp + "/" + mappedDirectory.svnPath());
+        }
+
+        // clobber files from harmony with their Android equivalents
+        filesystem.copyContents(temp + "/" + module.path(),
+                harmonyPath + "/" + module.path());
+    }
+
+    public static void main(String[] args) {
+        if (args.length < 3) {
+            printUsage();
+            return;
+        }
+
+        String androidPath = args[0] + "/dalvik/libcore";
+        String harmonyPath = args[1] + "/working_classlib/modules";
+
+        // TODO: validate directories?
+        
+        Module[] modules = new Module[args.length - 2];
+        for (int i = 0; i < modules.length; i++) {
+            modules[i] = Module.VALUES.get(args[i+2]);
+            if (modules[i] == null) {
+                System.out.println("No such module: " + args[i+2]);
+                return;
+            }
+        }
+
+        PushAndroidCode pusher = new PushAndroidCode(androidPath, harmonyPath);
+        for (Module module : modules) {
+            pusher.push(module);
+        }
+    }
+
+    private static void printUsage() {
+        System.out.println("This tool will clobber Harmony's core libraries with Android's copy");
+        System.out.println("so that a patch can be submitted upstream.");
+        System.out.println();
+        System.out.println("Usage: PushAndroidCode <android_root> <harmony_root> <module>...");
+        System.out.println();
+        System.out.println("  <android_root> is the android git client directory that contains dalvik");
+        System.out.println("                 This should hold an up-to-date checkout of Android. The");
+        System.out.println("                 target modules should also be up-to-date with respect to");
+        System.out.println("                 Harmony; use the PullHarmonyCode tool first if necessary.");
+        System.out.println();
+        System.out.println("  <harmony_root> is the android client directory that contains working_classlib.");
+        System.out.println("                 This should hold an up-to-date checkout of Harmony.");
+        System.out.println();
+        System.out.println("  <module> is one of " + Module.VALUES.keySet());
+        System.out.println();
+        System.out.println("Example usage:");
+        System.out.println("  java -cp out/host/linux-x86/framework/integrate.jar PushAndroidCode \\");
+        System.out.println("    /usr/local/google/jesse/clients/jessewilson_g1 \\");
+        System.out.println("    /usr/local/google/jesse/clients/jessewilson_h0/trunk \\");
+        System.out.println("    crypto");
+    }
+}