Merge branch 'caret_operator'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf0e1f9..5a89ebd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+# 1.1.0
+
+* Add support for the `^` operator for compatible versions according to pub's
+  notion of compatibility. `^1.2.3` is equivalent to `>=1.2.3 <2.0.0`; `^0.1.2`
+  is equivalent to `>=0.1.2 <0.2.0`.
+
+* Add `Version.nextBreaking`, which returns the next version that introduces
+  breaking changes after a given version.
+
+* Add `new VersionConstraint.compatibleWith()`, which returns a range covering
+  all versions compatible with a given version.
+
 # 1.0.0
 
 * Initial release.
diff --git a/README.md b/README.md
index 1cb1527..3f9558f 100644
--- a/README.md
+++ b/README.md
@@ -73,5 +73,24 @@
     specifically selects that unstable version -- they've deliberately opted
     into it.
 
+ *  **There is a notion of compatibility between pre-1.0.0 versions.** Semver
+    deems all pre-1.0.0 versions to be incompatible.  This means that the only
+    way to ensure compatibility when depending on a pre-1.0.0 package is to
+    pin the dependency to an exact version. Pinned version constraints prevent
+    automatic patch and pre-release updates. To avoid this situation, pub
+    defines the "next breaking" version as the version which increments the
+    major version if it's greater than zero, and the minor version otherwise,
+    resets subsequent digits to zero, and strips any pre-release or build
+    suffix.  For example, here are some versions along with their next breaking
+    ones:
+
+    `0.0.3` -> `0.1.0`
+    `0.7.2-alpha` -> `0.8.0`
+    `1.2.3` -> `2.0.0`
+
+    To make use of this, pub defines a "^" operator which yields a version
+    constraint greater than or equal to a given version, but less than its next
+    breaking one.
+
 [pub]: http://pub.dartlang.org/
 [semver]: http://semver.org/
diff --git a/lib/src/patterns.dart b/lib/src/patterns.dart
index 4a22618..8b32900 100644
--- a/lib/src/patterns.dart
+++ b/lib/src/patterns.dart
@@ -17,3 +17,6 @@
 /// Parses a comparison operator ("<", ">", "<=", or ">=") at the beginning of
 /// a string.
 final START_COMPARISON = new RegExp(r"^[<>]=?");
+
+/// The "compatible with" operator.
+const COMPATIBLE_WITH = "^";
diff --git a/lib/src/version.dart b/lib/src/version.dart
index 866d89c..0eb96c7 100644
--- a/lib/src/version.dart
+++ b/lib/src/version.dart
@@ -189,7 +189,7 @@
       return new Version(major, minor, patch);
     }
 
-    return new Version(major + 1, 0, 0);
+    return _incrementMajor();
   }
 
   /// Gets the next minor version number that follows this one.
@@ -202,7 +202,7 @@
       return new Version(major, minor, patch);
     }
 
-    return new Version(major, minor + 1, 0);
+    return _incrementMinor();
   }
 
   /// Gets the next patch version number that follows this one.
@@ -214,9 +214,26 @@
       return new Version(major, minor, patch);
     }
 
-    return new Version(major, minor, patch + 1);
+    return _incrementPatch();
   }
 
+  /// Gets the next breaking version number that follows this one.
+  ///
+  /// Increments [major] if it's greater than zero, otherwise [minor], resets
+  /// subsequent digits to zero, and strips any [preRelease] or [build]
+  /// suffix.
+  Version get nextBreaking {
+    if (major == 0) {
+      return _incrementMinor();
+    }
+
+    return _incrementMajor();
+  }
+
+  Version _incrementMajor() => new Version(major + 1, 0, 0);
+  Version _incrementMinor() => new Version(major, minor + 1, 0);
+  Version _incrementPatch() => new Version(major, minor, patch + 1);
+
   /// Tests if [other] matches this version exactly.
   bool allows(Version other) => this == other;
 
diff --git a/lib/src/version_constraint.dart b/lib/src/version_constraint.dart
index 499892e..cb1c64c 100644
--- a/lib/src/version_constraint.dart
+++ b/lib/src/version_constraint.dart
@@ -23,33 +23,39 @@
 
   /// Parses a version constraint.
   ///
-  /// This string is either "any" or a series of version parts. Each part can
-  /// be one of:
+  /// This string is one of:
   ///
-  ///   * A version string like `1.2.3`. In other words, anything that can be
-  ///     parsed by [Version.parse()].
-  ///   * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a version
-  ///     string.
+  ///   * "any". [any] version.
+  ///   * "^" followed by a version string. Versions compatible with
+  ///     ([VersionConstraint.compatibleWith]) the version.
+  ///   * a series of version parts. Each part can be one of:
+  ///     * A version string like `1.2.3`. In other words, anything that can be
+  ///       parsed by [Version.parse()].
+  ///     * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a
+  ///       version string.
   ///
   /// Whitespace is ignored.
   ///
   /// Examples:
   ///
   ///     any
+  ///     ^0.7.2
+  ///     ^1.0.0-alpha
   ///     1.2.3-alpha
   ///     <=5.1.4
   ///     >2.0.4 <= 2.4.6
   factory VersionConstraint.parse(String text) {
-    // Handle the "any" constraint.
-    if (text.trim() == "any") return new VersionRange();
-
     var originalText = text;
-    var constraints = [];
 
     skipWhitespace() {
       text = text.trim();
     }
 
+    skipWhitespace();
+
+    // Handle the "any" constraint.
+    if (text == "any") return any;
+
     // Try to parse and consume a version number.
     matchVersion() {
       var version = START_VERSION.firstMatch(text);
@@ -83,8 +89,35 @@
       throw "Unreachable.";
     }
 
+    // Try to parse the "^" operator followed by a version.
+    matchCompatibleWith() {
+      if (!text.startsWith(COMPATIBLE_WITH)) return null;
+
+      text = text.substring(COMPATIBLE_WITH.length);
+      skipWhitespace();
+
+      var version = matchVersion();
+      if (version == null) {
+        throw new FormatException('Expected version number after '
+            '"$COMPATIBLE_WITH" in "$originalText", got "$text".');
+      }
+
+      if (text.isNotEmpty) {
+        throw new FormatException('Cannot include other constraints with '
+            '"$COMPATIBLE_WITH" constraint in "$originalText".');
+      }
+
+      return new VersionConstraint.compatibleWith(version);
+    }
+
+    var compatibleWith = matchCompatibleWith();
+    if (compatibleWith != null) return compatibleWith;
+
+    var constraints = [];
+
     while (true) {
       skipWhitespace();
+
       if (text.isEmpty) break;
 
       var version = matchVersion();
@@ -111,6 +144,15 @@
     return new VersionConstraint.intersection(constraints);
   }
 
+  /// Creates a version constraint which allows all versions that are
+  /// backward compatible with [version].
+  ///
+  /// Versions are considered backward compatible with [version] if they
+  /// are greater than or equal to [version], but less than the next breaking
+  /// version ([Version.nextBreaking]) of [version].
+  factory VersionConstraint.compatibleWith(Version version) =>
+      new _CompatibleWithVersionRange(version);
+
   /// Creates a new version constraint that is the intersection of
   /// [constraints].
   ///
@@ -149,3 +191,11 @@
   VersionConstraint intersect(VersionConstraint other) => this;
   String toString() => '<empty>';
 }
+
+class _CompatibleWithVersionRange extends VersionRange {
+  _CompatibleWithVersionRange(Version version) : super(
+      min: version, includeMin: true,
+      max: version.nextBreaking, includeMax: false);
+
+  String toString() => '^$min';
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 63b09e1..b422064 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: pub_semver
-version: 1.0.0
+version: 1.1.0-dev
 author: Dart Team <misc@dartlang.org>
 description: >
  Versions and version constraints implementing pub's versioning policy. This
diff --git a/test/utils.dart b/test/utils.dart
index da89683..d918422 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -9,6 +9,10 @@
 import 'package:pub_semver/pub_semver.dart';
 
 /// Some stock example versions to use in tests.
+final v003 = new Version.parse('0.0.3');
+final v010 = new Version.parse('0.1.0');
+final v072 = new Version.parse('0.7.2');
+final v080 = new Version.parse('0.8.0');
 final v114 = new Version.parse('1.1.4');
 final v123 = new Version.parse('1.2.3');
 final v124 = new Version.parse('1.2.4');
diff --git a/test/version_constraint_test.dart b/test/version_constraint_test.dart
index df8c099..ee10a89 100644
--- a/test/version_constraint_test.dart
+++ b/test/version_constraint_test.dart
@@ -106,7 +106,7 @@
           new Version.parse('3.4.5')));
     });
 
-    test('ignores whitespace around operators', () {
+    test('ignores whitespace around comparison operators', () {
       var constraint = new VersionConstraint.parse(' >1.0.0>=1.2.3 < 1.3.0');
 
       expect(constraint, allows(
@@ -123,11 +123,41 @@
           throwsFormatException);
     });
 
+    test('parses a "^" version', () {
+      expect(new VersionConstraint.parse('^0.0.3'), equals(
+          new VersionConstraint.compatibleWith(v003)));
+
+      expect(new VersionConstraint.parse('^0.7.2'), equals(
+          new VersionConstraint.compatibleWith(v072)));
+
+      expect(new VersionConstraint.parse('^1.2.3'), equals(
+          new VersionConstraint.compatibleWith(v123)));
+
+      var min = new Version.parse('0.7.2-pre+1');
+      expect(new VersionConstraint.parse('^0.7.2-pre+1'), equals(
+          new VersionConstraint.compatibleWith(min)));
+    });
+
+    test('does not allow "^" to be mixed with other constraints', () {
+      expect(() => new VersionConstraint.parse('>=1.2.3 ^1.0.0'),
+          throwsFormatException);
+      expect(() => new VersionConstraint.parse('^1.0.0 <1.2.3'),
+          throwsFormatException);
+    });
+
+    test('ignores whitespace around "^"', () {
+      var constraint = new VersionConstraint.parse(' ^ 1.2.3 ');
+
+      expect(constraint, equals(
+          new VersionConstraint.compatibleWith(v123)));
+    });
+
     test('throws FormatException on a bad string', () {
       var bad = [
          "", "   ",               // Empty string.
          "foo",                   // Bad text.
          ">foo",                  // Bad text after operator.
+         "^foo",                  // Bad text after "^".
          "1.0.0 foo", "1.0.0foo", // Bad text after version.
          "anything",              // Bad text after "any".
          "<>1.0.0",               // Multiple operators.
@@ -140,4 +170,19 @@
       }
     });
   });
+
+  group('compatibleWith()', () {
+    test('returns the range of compatible versions', () {
+      var constraint = new VersionConstraint.compatibleWith(v072);
+
+      expect(constraint, equals(new VersionRange(min: v072, includeMin: true,
+          max: v072.nextBreaking)));
+    });
+
+    test('toString() uses "^"', () {
+      var constraint = new VersionConstraint.compatibleWith(v072);
+
+      expect(constraint.toString(), equals('^0.7.2'));
+    });
+  });
 }
diff --git a/test/version_test.dart b/test/version_test.dart
index b271ecc..3c652ac 100644
--- a/test/version_test.dart
+++ b/test/version_test.dart
@@ -198,6 +198,18 @@
     expect(new Version.parse('1.2.3+patch').nextPatch, equals(v124));
   });
 
+  test('nextBreaking', () {
+    expect(v123.nextBreaking, equals(v200));
+    expect(v072.nextBreaking, equals(v080));
+    expect(v003.nextBreaking, equals(v010));
+
+    // Removes pre-release version if present.
+    expect(new Version.parse('1.2.3-dev').nextBreaking, equals(v200));
+
+    // Strips build suffix.
+    expect(new Version.parse('1.2.3+patch').nextBreaking, equals(v200));
+  });
+
   test('parse()', () {
     expect(new Version.parse('0.0.0'), equals(new Version(0, 0, 0)));
     expect(new Version.parse('12.34.56'), equals(new Version(12, 34, 56)));