diff --git a/src/future/exec/__tests__/ExecFutureTestCase.php b/src/future/exec/__tests__/ExecFutureTestCase.php
--- a/src/future/exec/__tests__/ExecFutureTestCase.php
+++ b/src/future/exec/__tests__/ExecFutureTestCase.php
@@ -127,6 +127,53 @@
     $this->assertEqual(0, $err);
   }
 
+  public function testEscaping() {
+    $inputs = array(
+      'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
+      '!@#$%^&*()-=_+`~,.<>/?[]{};\':"|',
+      '',
+      ' ',
+      'x y',
+      '%PATH%',
+      '"',
+      '""',
+      'a "b" c',
+      '\\',
+      '\\a',
+      'a\\',
+      '\\a\\',
+      'a\\a',
+      '\\\\',
+      '\\"',
+    );
+
+    $bin = $this->getSupportExecutable('echo');
+
+    foreach ($inputs as $input) {
+      if (!is_array($input)) {
+        $input = array($input);
+      }
+
+      list($stdout) = execx(
+        'php -f %R -- %Ls',
+        $bin,
+        $input);
+
+      $stdout = explode("\n", $stdout);
+      $output = array();
+      foreach ($stdout as $line) {
+        $output[] = stripcslashes($line);
+      }
+
+      $this->assertEqual(
+        $input,
+        $output,
+        pht(
+          'Arguments are preserved for input: %s',
+          implode(' ', $input)));
+    }
+  }
+
   public function testReadBuffering() {
     $str_len_8 = 'abcdefgh';
     $str_len_4 = 'abcd';
diff --git a/src/toolset/workflow/ArcanistVersionWorkflow.php b/src/toolset/workflow/ArcanistVersionWorkflow.php
--- a/src/toolset/workflow/ArcanistVersionWorkflow.php
+++ b/src/toolset/workflow/ArcanistVersionWorkflow.php
@@ -67,14 +67,12 @@
             Filesystem::readablePath($root)));
       }
 
-      // NOTE: Carefully execute these commands in a way that works on Windows
-      // until T8298 is properly fixed. See PHI52.
-
-      list($commit) = $repository_api->execxLocal('log -1 --format=%%H');
+      list($commit) = $repository_api->execxLocal(
+        'log -1 --format=%s',
+        '%ct%x01%H');
       $commit = trim($commit);
 
-      list($timestamp) = $repository_api->execxLocal('log -1 --format=%%ct');
-      $timestamp = trim($timestamp);
+      list($timestamp, $commit) = explode("\1", $commit);
 
       $console->writeOut(
         "%s %s (%s)\n",
diff --git a/src/xsprintf/PhutilCommandString.php b/src/xsprintf/PhutilCommandString.php
--- a/src/xsprintf/PhutilCommandString.php
+++ b/src/xsprintf/PhutilCommandString.php
@@ -6,6 +6,8 @@
   private $escapingMode = false;
 
   const MODE_DEFAULT = 'default';
+  const MODE_LINUX = 'linux';
+  const MODE_WINDOWS = 'windows';
   const MODE_POWERSHELL = 'powershell';
 
   public function __construct(array $argv) {
@@ -46,9 +48,19 @@
   }
 
   public static function escapeArgument($value, $mode) {
+    if ($mode === self::MODE_DEFAULT) {
+      if (phutil_is_windows()) {
+        $mode = self::MODE_WINDOWS;
+      } else {
+        $mode = self::MODE_LINUX;
+      }
+    }
+
     switch ($mode) {
-      case self::MODE_DEFAULT:
-        return escapeshellarg($value);
+      case self::MODE_LINUX:
+        return self::escapeLinux($value);
+      case self::MODE_WINDOWS:
+        return self::escapeWindows($value);
       case self::MODE_POWERSHELL:
         return self::escapePowershell($value);
       default:
@@ -82,4 +94,78 @@
     return '"'.$value.'"';
   }
 
+  private static function escapeLinux($value) {
+    if (strpos($value, "\0") !== false) {
+      throw new Exception(
+        pht(
+          'Command string argument includes a NULL byte. This byte can not '.
+          'be safely escaped in command line arguments in Linux '.
+          'environments.'));
+    }
+
+    // If the argument is nonempty and contains only common printable
+    // characters, we do not need to escape it. This makes debugging
+    // workflows a little more user-friendly by making command output
+    // more readable.
+    if (preg_match('(^[a-zA-Z0-9:/@._+-]+\z)', $value)) {
+      return $value;
+    }
+
+    return escapeshellarg($value);
+  }
+
+  private static function escapeWindows($value) {
+    if (strpos($value, "\0") !== false) {
+      throw new Exception(
+        pht(
+          'Command string argument includes a NULL byte. This byte can not '.
+          'be safely escaped in command line arguments in Windows '.
+          'environments.'));
+    }
+
+    if (!phutil_is_utf8($value)) {
+      throw new Exception(
+        pht(
+          'Command string argument includes text which is not valid UTF-8. '.
+          'This library can not safely escape this sequence in command '.
+          'line arguments in Windows environments.'));
+    }
+
+    $has_backslash = (strpos($value, '\\') !== false);
+    $has_space = (strpos($value, ' ') !== false);
+    $has_quote = (strpos($value, '"') !== false);
+    $is_empty = (strlen($value) === 0);
+
+    // If a backslash appears before another backslash, a double quote, or
+    // the end of the argument, we must escape it. Otherwise, we must leave
+    // it unescaped.
+
+    if ($has_backslash) {
+      $value_v = preg_split('//', $value, -1, PREG_SPLIT_NO_EMPTY);
+      $len = count($value_v);
+      for ($ii = 0; $ii < $len; $ii++) {
+        if ($value_v[$ii] === '\\') {
+          if ($ii + 1 < $len) {
+            $next = $value_v[$ii + 1];
+          } else {
+            $next = null;
+          }
+
+          if ($next === '"' || $next === '\\' || $next === null) {
+            $value_v[$ii] = '\\\\';
+          }
+        }
+      }
+      $value = implode('', $value_v);
+    }
+
+    // Then, escape double quotes by prefixing them with backslashes.
+    if ($has_quote || $has_space || $has_backslash || $is_empty) {
+      $value = addcslashes($value, '"');
+      $value = '"'.$value.'"';
+    }
+
+    return $value;
+  }
+
 }