Ensure we pass forward-slash paths to Git on Windows (#1877)

Closes #1756

Also clean up to use Platform.isWindows everywhere
diff --git a/lib/src/command_runner.dart b/lib/src/command_runner.dart
index 05dad3c..96eee66 100644
--- a/lib/src/command_runner.dart
+++ b/lib/src/command_runner.dart
@@ -262,7 +262,7 @@
   /// If it isn't, it prints an error message and exits. Completes when the
   /// validation is done.
   Future _validatePlatform() async {
-    if (Platform.operatingSystem != 'windows') return;
+    if (!Platform.isWindows) return;
 
     var result = await runProcess('ver', []);
     if (result.stdout.join('\n').contains('XP')) {
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index 82c085e..f629176 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -662,8 +662,7 @@
   String _createBinStub(Package package, String executable, String script,
       {bool overwrite, String snapshot}) {
     var binStubPath = p.join(_binStubDir, executable);
-
-    if (Platform.operatingSystem == "windows") binStubPath += ".bat";
+    if (Platform.isWindows) binStubPath += ".bat";
 
     // See if the binstub already exists. If so, it's for another package
     // since we already deleted all of this package's binstubs.
@@ -690,7 +689,7 @@
       invocation = "pub global run ${package.name}:$script";
     }
 
-    if (Platform.operatingSystem == "windows") {
+    if (Platform.isWindows) {
       var batch = """
 @echo off
 rem This file was created by pub v${sdk.version}.
@@ -788,7 +787,7 @@
   /// [installed] should be the name of an installed executable that can be used
   /// to test whether accessing it on the path works.
   void _suggestIfNotOnPath(String installed) {
-    if (Platform.operatingSystem == "windows") {
+    if (Platform.isWindows) {
       // See if the shell can find one of the binstubs.
       // "\q" means return exit code 0 if found or 1 if not.
       var result = runProcessSync("where", [r"\q", installed + ".bat"]);
diff --git a/lib/src/io.dart b/lib/src/io.dart
index f153033..93d0ee0 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -293,7 +293,7 @@
         }
 
         if (pathInDir.contains("/.")) return false;
-        if (Platform.operatingSystem != "windows") return true;
+        if (!Platform.isWindows) return true;
         return !pathInDir.contains("\\.");
       })
       .map((entity) => entity.path)
@@ -314,7 +314,7 @@
 /// mitigate that, on Windows, this will retry the operation a few times if it
 /// fails.
 void _attempt(String description, void operation()) {
-  if (Platform.operatingSystem != 'windows') {
+  if (!Platform.isWindows) {
     operation();
     return;
   }
@@ -427,7 +427,7 @@
     // make sure we have a clean absolute path because it will interpret a
     // relative path to be relative to the cwd, not the symlink, and will be
     // confused by forward slashes.
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       target = path.normalize(path.absolute(target));
     } else {
       // If the directory where we're creating the symlink was itself reached
@@ -788,8 +788,7 @@
   // Spawning a process on Windows will not look for the executable in the
   // system path. So, if executable looks like it needs that (i.e. it doesn't
   // have any path separators in it), then spawn it through a shell.
-  if ((Platform.operatingSystem == "windows") &&
-      (executable.indexOf('\\') == -1)) {
+  if (Platform.isWindows && executable.indexOf('\\') == -1) {
     args = ["/c", executable]..addAll(args);
     executable = "cmd";
   }
@@ -843,8 +842,7 @@
 /// Extracts a `.tar.gz` file from [stream] to [destination].
 Future extractTarGz(Stream<List<int>> stream, String destination) async {
   log.fine("Extracting .tar.gz stream to $destination.");
-
-  if (Platform.operatingSystem == "windows") {
+  if (Platform.isWindows) {
     return await _extractTarGzWindows(stream, destination);
   }
 
diff --git a/lib/src/package.dart b/lib/src/package.dart
index 66706f7..f699678 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -239,8 +239,9 @@
       // totally empty directories, but a submodule that's not checked out
       // behaves like one.
       files = files.where((file) => file != './').map((file) {
-        if (Platform.operatingSystem != 'windows') return "$beneath/$file";
-        return "$beneath\\${file.replaceAll("/", "\\")}";
+        return Platform.isWindows
+            ? "$beneath\\${file.replaceAll("/", "\\")}"
+            : "$beneath/$file";
       }).expand((file) {
         if (fileExists(file)) return [file];
         if (!dirExists(file)) return [];
diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart
index 95b619f..8854039 100644
--- a/lib/src/source/git.dart
+++ b/lib/src/source/git.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:io';
 
 import 'package:path/path.dart' as p;
 import 'package:pub_semver/pub_semver.dart';
@@ -242,6 +243,9 @@
     // specially.
     var pubspecPath = p.normalize(p.join(p.fromUri(path), 'pubspec.yaml'));
 
+    // Git doesn't recognize backslashes in paths, even on Windows.
+    if (Platform.isWindows) pubspecPath = pubspecPath.replaceAll("\\", "/");
+
     var lines;
     try {
       lines = await git
diff --git a/lib/src/system_cache.dart b/lib/src/system_cache.dart
index e957fae..a3d2605 100644
--- a/lib/src/system_cache.dart
+++ b/lib/src/system_cache.dart
@@ -34,7 +34,7 @@
   static String defaultDir = (() {
     if (Platform.environment.containsKey('PUB_CACHE')) {
       return Platform.environment['PUB_CACHE'];
-    } else if (Platform.operatingSystem == 'windows') {
+    } else if (Platform.isWindows) {
       var appData = Platform.environment['APPDATA'];
       return p.join(appData, 'Pub', 'Cache');
     } else {
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 8bd242d..77f601c 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -270,7 +270,7 @@
 Set<String> createFileFilter(Iterable<String> files) {
   return files.expand<String>((file) {
     var result = ["/$file"];
-    if (Platform.operatingSystem == 'windows') result.add("\\$file");
+    if (Platform.isWindows) result.add("\\$file");
     return result;
   }).toSet();
 }
@@ -283,7 +283,7 @@
 Set<String> createDirectoryFilter(Iterable<String> dirs) {
   return dirs.expand<String>((dir) {
     var result = ["/$dir/"];
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       result..add("/$dir\\")..add("\\$dir/")..add("\\$dir\\");
     }
     return result;
@@ -473,7 +473,7 @@
 bool get canUseSpecialChars =>
     !runningFromTest &&
     !runningAsTest &&
-    Platform.operatingSystem != 'windows' &&
+    !Platform.isWindows &&
     stdioType(stdout) == StdioType.TERMINAL;
 
 /// Gets a "special" string (ANSI escape or Unicode).
diff --git a/test/global/binstubs/does_not_warn_if_on_path_test.dart b/test/global/binstubs/does_not_warn_if_on_path_test.dart
index 9dc9595..1ecbaf8 100644
--- a/test/global/binstubs/does_not_warn_if_on_path_test.dart
+++ b/test/global/binstubs/does_not_warn_if_on_path_test.dart
@@ -23,7 +23,7 @@
 
     // Add the test's cache bin directory to the path.
     var binDir = p.dirname(Platform.executable);
-    var separator = Platform.operatingSystem == "windows" ? ";" : ":";
+    var separator = Platform.isWindows ? ";" : ":";
     var path = "${Platform.environment["PATH"]}$separator$binDir";
 
     await runPub(
diff --git a/test/global/binstubs/utils.dart b/test/global/binstubs/utils.dart
index cbd9d7e..e8f9e6e 100644
--- a/test/global/binstubs/utils.dart
+++ b/test/global/binstubs/utils.dart
@@ -13,7 +13,7 @@
 /// explicitly includes it.
 Map getEnvironment() {
   var binDir = p.dirname(Platform.executable);
-  var separator = Platform.operatingSystem == "windows" ? ";" : ":";
+  var separator = Platform.isWindows ? ";" : ":";
   var path = "${Platform.environment["PATH"]}$separator$binDir";
 
   var environment = getPubTestEnvironment();
diff --git a/test/io_test.dart b/test/io_test.dart
index 33be4ff..8f928a9 100644
--- a/test/io_test.dart
+++ b/test/io_test.dart
@@ -318,7 +318,7 @@
     });
 
     // Windows doesn't support symlinking to files.
-    if (Platform.operatingSystem != 'windows') {
+    if (!Platform.isWindows) {
       test('returns $forFileSymlink for a symlink to a file', () {
         expect(withTempDir((temp) {
           var targetPath = path.join(temp, "test.txt");
diff --git a/test/real_version_test.dart b/test/real_version_test.dart
index 2df13a6..0a87b18 100644
--- a/test/real_version_test.dart
+++ b/test/real_version_test.dart
@@ -24,8 +24,8 @@
   // the built SDK directory, and not the live pub code directly in the repo.
   test('parse the real SDK "version" file', () async {
     // Get the path to the pub binary in the SDK.
-    var pubPath = path.join(sdk.rootDirectory, 'bin',
-        Platform.operatingSystem == "windows" ? "pub.bat" : "pub");
+    var pubPath = path.join(
+        sdk.rootDirectory, 'bin', Platform.isWindows ? "pub.bat" : "pub");
 
     var pub = await TestProcess.start(pubPath, ['version']);
     expect(pub.stdout, emits(startsWith("Pub")));