From a01f7187143daf69a25885d342db40d47935103c Mon Sep 17 00:00:00 2001
From: Luke Granger-Brown <git@lukegb.com>
Date: Sat, 16 May 2020 13:55:09 +0100
Subject: [PATCH] Make Mercurial at least slightly usable.

---
 .../com/google/copybara/hg/HgDestination.java | 28 +++++++++----------
 java/com/google/copybara/hg/HgModule.java     | 25 +++++++++++++++++
 java/com/google/copybara/hg/HgRepository.java |  2 +-
 .../google/copybara/util/CommandRunner.java   |  2 +-
 4 files changed, 40 insertions(+), 17 deletions(-)

diff --git a/java/com/google/copybara/hg/HgDestination.java b/java/com/google/copybara/hg/HgDestination.java
index d904781f..b60e4911 100644
--- a/java/com/google/copybara/hg/HgDestination.java
+++ b/java/com/google/copybara/hg/HgDestination.java
@@ -162,7 +162,7 @@ public class HgDestination implements Destination<HgRevision> {
 
     @Override
     public boolean supportsHistory() {
-      throw new UnsupportedOperationException("Not implemented yet");
+      return true;
     }
 
     private HgRepository getRepository() throws RepoException {
@@ -216,7 +216,7 @@ public class HgDestination implements Destination<HgRevision> {
         Path workDir, HgRepository localRepo)
         throws RepoException, IOException {
       // Create a temp archive of the remote repository to compute diff with
-      Path tempArchivePath = generalOptions.getDirFactory().newTempDir("tempArchive");
+      Path tempArchivePath = Files.createTempDirectory(workDir.getParent(), "tempArchive");
       localRepo.archive(tempArchivePath.toString());
 
       // Find excluded files in the archive
@@ -238,24 +238,20 @@ public class HgDestination implements Destination<HgRevision> {
           Operation diffOp = diff.getOperation();
 
           if (diffOp.equals(Operation.ADD)) {
+            Path targetPath = localRepo.getHgDir().getParent().resolve(diff.getName());
+            Files.createDirectories(targetPath.getParent());
             Files.copy(workDir.resolve(diff.getName()),
-                localRepo.getHgDir().resolve(diff.getName()), StandardCopyOption.COPY_ATTRIBUTES);
-            localRepo.hg(localRepo.getHgDir(), "add", diff.getName());
+                targetPath, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
           }
 
           if (diffOp.equals(Operation.MODIFIED)) {
             Files.copy(workDir.resolve(diff.getName()),
-                localRepo.getHgDir().resolve(diff.getName()), StandardCopyOption.REPLACE_EXISTING);
+                localRepo.getHgDir().getParent().resolve(diff.getName()), StandardCopyOption.REPLACE_EXISTING);
           }
 
           if (diffOp.equals(Operation.DELETE)) {
-            try {
-              localRepo.hg(localRepo.getHgDir(), "remove", diff.getName());
-            } catch (RepoException e) {
-              // Ignore a .hg_archival file that is not in the workdir nor in the local repo.
-              if (!e.getMessage().contains(".hg_archival.txt: No such file or directory")) {
-                throw e;
-              }
+            if (!diff.getName().equals(".hg_archival.txt")) {
+              Files.delete(localRepo.getHgDir().getParent().resolve(diff.getName()));
             }
           }
         }
@@ -264,6 +260,8 @@ public class HgDestination implements Destination<HgRevision> {
       } finally {
         FileUtil.deleteRecursively(tempArchivePath);
       }
+
+      localRepo.hg(localRepo.getHgDir().getParent(), "addremove");
     }
 
     /**
@@ -282,7 +280,7 @@ public class HgDestination implements Destination<HgRevision> {
       localRepo.cleanUpdate(remoteFetch);
 
       // Set the default path of the local repo to be the remote repo, so we can push to it
-      Files.write(localRepo.getHgDir().resolve(".hg/hgrc"),
+      Files.write(localRepo.getHgDir().getParent().resolve(".hg/hgrc"),
           String.format("[paths]\ndefault = %s\n", repoUrl).getBytes(StandardCharsets.UTF_8));
 
       console.progress("Hg Destination: Computing diff");
@@ -293,12 +291,12 @@ public class HgDestination implements Destination<HgRevision> {
       ChangeMessage msg = getChangeMessage(transformResult, ORIGIN_LABEL_SEPARATOR);
       String date = transformResult.getTimestamp().format(DateTimeFormatter.RFC_1123_DATE_TIME);
 
-      localRepo.hg(localRepo.getHgDir(), "commit", "--user",
+      localRepo.hg(localRepo.getHgDir().getParent(), "commit", "--user",
           transformResult.getAuthor().toString(), "--date", date,
           "-m", msg.toString());
 
       console.progress(String.format("Hg Destination: Pushing to %s %s", repoUrl, remotePush));
-      localRepo.hg(localRepo.getHgDir(), "push", "--rev", remotePush, repoUrl);
+      localRepo.hg(localRepo.getHgDir().getParent(), "push", "--rev", remotePush, repoUrl);
 
       String tip = localRepo.identify("tip").getGlobalId();
 
diff --git a/java/com/google/copybara/hg/HgModule.java b/java/com/google/copybara/hg/HgModule.java
index d8a22f33..b1ca7ba2 100644
--- a/java/com/google/copybara/hg/HgModule.java
+++ b/java/com/google/copybara/hg/HgModule.java
@@ -19,6 +19,7 @@ package com.google.copybara.hg;
 import static com.google.copybara.config.SkylarkUtil.checkNotEmpty;
 
 import com.google.common.base.Preconditions;
+import com.google.copybara.GeneralOptions;
 import com.google.copybara.Options;
 import com.google.copybara.config.LabelsAwareModule;
 import com.google.copybara.doc.annotations.UsesFlags;
@@ -73,4 +74,28 @@ public class HgModule implements LabelsAwareModule, StarlarkValue {
   public HgOrigin origin(String url, String ref) throws EvalException {
     return HgOrigin.newHgOrigin(options, checkNotEmpty(url, "url"), ref);
   }
+
+  @StarlarkMethod(
+          name = "destination",
+          doc = "<b>EXPERIMENTAL:</b> Defines a standard Mercurial (Hg) destination.",
+          parameters = {
+                  @Param(
+                          name = "url",
+                          type = String.class,
+                          named = true,
+                          doc = "Indicates the URL of the Hg repository"),
+                  @Param(
+                          name = "fetch",
+                          type = String.class,
+                          named = true,
+                          doc = "XXX: ref to fetch?"),
+                  @Param(
+                          name = "push",
+                          type = String.class,
+                          named = true,
+                          doc = "XXX: ref to push?")
+          })
+  public HgDestination destination(String url, String fetch, String push) throws EvalException {
+    return HgDestination.newHgDestination(url, fetch, push, options.get(GeneralOptions.class), options.get(HgOptions.class));
+  }
 }
diff --git a/java/com/google/copybara/hg/HgRepository.java b/java/com/google/copybara/hg/HgRepository.java
index 414f22e7..40bc2663 100644
--- a/java/com/google/copybara/hg/HgRepository.java
+++ b/java/com/google/copybara/hg/HgRepository.java
@@ -141,7 +141,7 @@ public class HgRepository {
     }
 
     try {
-      hg(hgDir, builder.build(), fetchTimeout);
+      hg(hgDir.getParent(), builder.build(), fetchTimeout);
     } catch (RepoException e) {
       if (INVALID_HG_REPOSITORY.matcher(e.getMessage()).find()){
         throw new ValidationException("Repository not found: " + e.getMessage());
diff --git a/java/com/google/copybara/util/CommandRunner.java b/java/com/google/copybara/util/CommandRunner.java
index 14c12987..bf96b493 100644
--- a/java/com/google/copybara/util/CommandRunner.java
+++ b/java/com/google/copybara/util/CommandRunner.java
@@ -185,7 +185,7 @@ public final class CommandRunner {
     String startMsg = ShellUtils.prettyPrintArgv(Arrays.asList(cmd.getCommandLineElements()));
     startMsg = startMsg.length() > MAX_COMMAND_LENGTH
         ? startMsg.substring(0, MAX_COMMAND_LENGTH) + "..." : startMsg;
-    String validStartMsg = "Executing [" + startMsg + "]";
+    String validStartMsg = "Executing [" + startMsg + "] in workdir [" + cmd.getWorkingDirectory() + "]";
     logger.atInfo().log(validStartMsg);
     if (verbose) {
       System.err.println(validStartMsg);
-- 
2.26.2