Auto merge of #4568 - Metaswitch:alt-registry-publish, r=withoutboats

Add support for publish to optionally take the index that can be used

This form part of alternative-registries RFC-2141, it allows crates to optionally specify which registries the crate can be be published to.

@carols10cents, one thing that I am unsure about is if there is a plan for publish to still provide index, or for registry to be provided instead. I thought that your general view was that we should move away from the index file. If we do need to map allowed registries to the index then there will be a small amount of extra work required once #4506 is merged.

@withoutboats, happy for this to be merged into your branch if you want, the main reason I did not base it on your branch was due to tests not working on there yet.
diff --git a/src/bin/login.rs b/src/bin/login.rs
index 499980c..d8a0ad4 100644
--- a/src/bin/login.rs
+++ b/src/bin/login.rs
@@ -21,7 +21,7 @@
 }
 
 pub const USAGE: &'static str = "
-Save an api token from the registry locally
+Save an api token from the registry locally. If token is not specified, it will be read from stdin.
 
 Usage:
     cargo login [options] [<token>]
diff --git a/src/bin/yank.rs b/src/bin/yank.rs
index 64eb3a0..ce5dfb7 100644
--- a/src/bin/yank.rs
+++ b/src/bin/yank.rs
@@ -25,18 +25,18 @@
     cargo yank [options] [<crate>]
 
 Options:
-    -h, --help          Print this message
-    --vers VERSION      The version to yank or un-yank
-    --undo              Undo a yank, putting a version back into the index
-    --index INDEX       Registry index to yank from
-    --token TOKEN       API token to use when authenticating
-    -v, --verbose ...   Use verbose output (-vv very verbose/build.rs output)
-    -q, --quiet         No output printed to stdout
-    --color WHEN        Coloring: auto, always, never
-    --frozen            Require Cargo.lock and cache are up to date
-    --locked            Require Cargo.lock is up to date
-    -Z FLAG ...         Unstable (nightly-only) flags to Cargo
-    --registry REGISTRY Registry to use
+    -h, --help               Print this message
+    --vers VERSION           The version to yank or un-yank
+    --undo                   Undo a yank, putting a version back into the index
+    --index INDEX            Registry index to yank from
+    --token TOKEN            API token to use when authenticating
+    -v, --verbose ...        Use verbose output (-vv very verbose/build.rs output)
+    -q, --quiet              No output printed to stdout
+    --color WHEN             Coloring: auto, always, never
+    --frozen                 Require Cargo.lock and cache are up to date
+    --locked                 Require Cargo.lock is up to date
+    -Z FLAG ...              Unstable (nightly-only) flags to Cargo
+    --registry REGISTRY      Registry to use
 
 The yank command removes a previously pushed crate's version from the server's
 index. This command does not delete any data, and the crate will still be
diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs
index 63f20a6..bc299f3 100644
--- a/src/cargo/core/resolver/mod.rs
+++ b/src/cargo/core/resolver/mod.rs
@@ -47,10 +47,11 @@
 
 use std::cmp::Ordering;
 use std::collections::{HashSet, HashMap, BinaryHeap, BTreeMap};
-use std::iter::FromIterator;
 use std::fmt;
+use std::iter::FromIterator;
 use std::ops::Range;
 use std::rc::Rc;
+use std::time::{Instant, Duration};
 
 use semver;
 use url::Url;
@@ -73,6 +74,7 @@
 ///
 /// Each instance of `Resolve` also understands the full set of features used
 /// for each package.
+#[derive(PartialEq)]
 pub struct Resolve {
     graph: Graph<PackageId>,
     replacements: HashMap<PackageId, PackageId>,
@@ -362,7 +364,8 @@
 pub fn resolve(summaries: &[(Summary, Method)],
                replacements: &[(PackageIdSpec, Dependency)],
                registry: &mut Registry,
-               config: Option<&Config>) -> CargoResult<Resolve> {
+               config: Option<&Config>,
+               print_warnings: bool) -> CargoResult<Resolve> {
     let cx = Context {
         resolve_graph: RcList::new(),
         resolve_features: HashMap::new(),
@@ -372,7 +375,7 @@
         warnings: RcList::new(),
     };
     let _p = profile::start("resolving");
-    let cx = activate_deps_loop(cx, registry, summaries)?;
+    let cx = activate_deps_loop(cx, registry, summaries, config)?;
 
     let mut resolve = Resolve {
         graph: cx.graph(),
@@ -398,11 +401,13 @@
 
     // If we have a shell, emit warnings about required deps used as feature.
     if let Some(config) = config {
-        let mut shell = config.shell();
-        let mut warnings = &cx.warnings;
-        while let Some(ref head) = warnings.head {
-            shell.warn(&head.0)?;
-            warnings = &head.1;
+        if print_warnings {
+            let mut shell = config.shell();
+            let mut warnings = &cx.warnings;
+            while let Some(ref head) = warnings.head {
+                shell.warn(&head.0)?;
+                warnings = &head.1;
+            }
         }
     }
 
@@ -420,7 +425,7 @@
             parent: Option<&Summary>,
             candidate: Candidate,
             method: &Method)
-            -> CargoResult<Option<DepsFrame>> {
+            -> CargoResult<Option<(DepsFrame, Duration)>> {
     if let Some(parent) = parent {
         cx.resolve_graph.push(GraphNode::Link(parent.package_id().clone(),
                                            candidate.summary.package_id().clone()));
@@ -448,12 +453,13 @@
         }
     };
 
+    let now = Instant::now();
     let deps = cx.build_deps(registry, &candidate, method)?;
-
-    Ok(Some(DepsFrame {
+    let frame = DepsFrame {
         parent: candidate,
         remaining_siblings: RcVecIter::new(Rc::new(deps)),
-    }))
+    };
+    Ok(Some((frame, now.elapsed())))
 }
 
 struct RcVecIter<T> {
@@ -580,7 +586,8 @@
 /// dependency graph, cx.resolve is returned.
 fn activate_deps_loop<'a>(mut cx: Context<'a>,
                           registry: &mut Registry,
-                          summaries: &[(Summary, Method)])
+                          summaries: &[(Summary, Method)],
+                          config: Option<&Config>)
                           -> CargoResult<Context<'a>> {
     // Note that a `BinaryHeap` is used for the remaining dependencies that need
     // activation. This heap is sorted such that the "largest value" is the most
@@ -594,10 +601,18 @@
     for &(ref summary, ref method) in summaries {
         debug!("initial activation: {}", summary.package_id());
         let candidate = Candidate { summary: summary.clone(), replace: None };
-        remaining_deps.extend(activate(&mut cx, registry, None, candidate,
-                                       method)?);
+        let res = activate(&mut cx, registry, None, candidate, method)?;
+        if let Some((frame, _)) = res {
+            remaining_deps.push(frame);
+        }
     }
 
+    let mut ticks = 0;
+    let start = Instant::now();
+    let time_to_print = Duration::from_millis(500);
+    let mut printed = false;
+    let mut deps_time = Duration::new(0, 0);
+
     // Main resolution loop, this is the workhorse of the resolution algorithm.
     //
     // You'll note that a few stacks are maintained on the side, which might
@@ -612,6 +627,28 @@
     // backtracking states where if we hit an error we can return to in order to
     // attempt to continue resolving.
     while let Some(mut deps_frame) = remaining_deps.pop() {
+
+        // If we spend a lot of time here (we shouldn't in most cases) then give
+        // a bit of a visual indicator as to what we're doing. Only enable this
+        // when stderr is a tty (a human is likely to be watching) to ensure we
+        // get deterministic output otherwise when observed by tools.
+        //
+        // Also note that we hit this loop a lot, so it's fairly performance
+        // sensitive. As a result try to defer a possibly expensive operation
+        // like `Instant::now` by only checking every N iterations of this loop
+        // to amortize the cost of the current time lookup.
+        ticks += 1;
+        if let Some(config) = config {
+            if config.shell().is_err_tty() &&
+                !printed &&
+                ticks % 1000 == 0 &&
+                start.elapsed() - deps_time > time_to_print
+            {
+                printed = true;
+                config.shell().status("Resolving", "dependency graph...")?;
+            }
+        }
+
         let frame = match deps_frame.remaining_siblings.next() {
             Some(sibling) => {
                 let parent = Summary::clone(&deps_frame.parent);
@@ -695,8 +732,11 @@
         };
         trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(),
                candidate.summary.version());
-        remaining_deps.extend(activate(&mut cx, registry, Some(&parent),
-                              candidate, &method)?);
+        let res = activate(&mut cx, registry, Some(&parent), candidate, &method)?;
+        if let Some((frame, dur)) = res {
+            remaining_deps.push(frame);
+            deps_time += dur;
+        }
     }
 
     Ok(cx)
diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs
index 6911339..ade0850 100644
--- a/src/cargo/core/shell.rs
+++ b/src/cargo/core/shell.rs
@@ -2,7 +2,7 @@
 use std::io::prelude::*;
 
 use atty;
-use termcolor::Color::{Green, Red, Yellow};
+use termcolor::Color::{Green, Red, Yellow, Cyan};
 use termcolor::{self, StandardStream, Color, ColorSpec, WriteColor};
 
 use util::errors::CargoResult;
@@ -28,13 +28,17 @@
 impl fmt::Debug for Shell {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match &self.err {
-            &ShellOut::Write(_) => f.debug_struct("Shell")
-                .field("verbosity", &self.verbosity)
-                .finish(),
-            &ShellOut::Stream(_, color_choice) => f.debug_struct("Shell")
-                .field("verbosity", &self.verbosity)
-                .field("color_choice", &color_choice)
-                .finish()
+            &ShellOut::Write(_) => {
+                f.debug_struct("Shell")
+                    .field("verbosity", &self.verbosity)
+                    .finish()
+            }
+            &ShellOut::Stream { color_choice, .. } => {
+                f.debug_struct("Shell")
+                    .field("verbosity", &self.verbosity)
+                    .field("color_choice", &color_choice)
+                    .finish()
+            }
         }
     }
 }
@@ -44,7 +48,11 @@
     /// A plain write object without color support
     Write(Box<Write>),
     /// Color-enabled stdio, with information on whether color should be used
-    Stream(StandardStream, ColorChoice),
+    Stream {
+        stream: StandardStream,
+        tty: bool,
+        color_choice: ColorChoice,
+    },
 }
 
 /// Whether messages should use color output
@@ -63,10 +71,11 @@
     /// output.
     pub fn new() -> Shell {
         Shell {
-            err: ShellOut::Stream(
-                StandardStream::stderr(ColorChoice::CargoAuto.to_termcolor_color_choice()),
-                ColorChoice::CargoAuto,
-            ),
+            err: ShellOut::Stream {
+                stream: StandardStream::stderr(ColorChoice::CargoAuto.to_termcolor_color_choice()),
+                color_choice: ColorChoice::CargoAuto,
+                tty: atty::is(atty::Stream::Stderr),
+            },
             verbosity: Verbosity::Verbose,
         }
     }
@@ -83,7 +92,7 @@
     /// messages follows without color.
     fn print(&mut self,
              status: &fmt::Display,
-             message: &fmt::Display,
+             message: Option<&fmt::Display>,
              color: Color,
              justified: bool) -> CargoResult<()> {
         match self.verbosity {
@@ -94,6 +103,22 @@
         }
     }
 
+    /// Returns the width of the terminal in spaces, if any
+    pub fn err_width(&self) -> Option<usize> {
+        match self.err {
+            ShellOut::Stream { tty: true, .. } => imp::stderr_width(),
+            _ => None,
+        }
+    }
+
+    /// Returns whether stderr is a tty
+    pub fn is_err_tty(&self) -> bool {
+        match self.err {
+            ShellOut::Stream { tty, .. } => tty,
+            _ => false,
+        }
+    }
+
     /// Get a reference to the underlying writer
     pub fn err(&mut self) -> &mut Write {
         self.err.as_write()
@@ -103,7 +128,13 @@
     pub fn status<T, U>(&mut self, status: T, message: U) -> CargoResult<()>
         where T: fmt::Display, U: fmt::Display
     {
-        self.print(&status, &message, Green, true)
+        self.print(&status, Some(&message), Green, true)
+    }
+
+    pub fn status_header<T>(&mut self, status: T) -> CargoResult<()>
+        where T: fmt::Display,
+    {
+        self.print(&status, None, Cyan, true)
     }
 
     /// Shortcut to right-align a status message.
@@ -113,7 +144,7 @@
                                    color: Color) -> CargoResult<()>
         where T: fmt::Display, U: fmt::Display
     {
-        self.print(&status, &message, color, true)
+        self.print(&status, Some(&message), color, true)
     }
 
     /// Run the callback only if we are in verbose mode
@@ -138,14 +169,14 @@
 
     /// Print a red 'error' message
     pub fn error<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
-        self.print(&"error:", &message, Red, false)
+        self.print(&"error:", Some(&message), Red, false)
     }
 
     /// Print an amber 'warning' message
     pub fn warn<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
         match self.verbosity {
             Verbosity::Quiet => Ok(()),
-            _ => self.print(&"warning:", &message, Yellow, false),
+            _ => self.print(&"warning:", Some(&message), Yellow, false),
         }
     }
 
@@ -161,7 +192,7 @@
 
     /// Update the color choice (always, never, or auto) from a string.
     pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> {
-        if let ShellOut::Stream(ref mut err, ref mut cc) =  self.err {
+        if let ShellOut::Stream { ref mut stream, ref mut color_choice, .. } =  self.err {
             let cfg = match color {
                 Some("always") => ColorChoice::Always,
                 Some("never") => ColorChoice::Never,
@@ -172,8 +203,8 @@
                 Some(arg) => bail!("argument for --color must be auto, always, or \
                                     never, but found `{}`", arg),
             };
-            *cc = cfg;
-            *err = StandardStream::stderr(cfg.to_termcolor_color_choice());
+            *color_choice = cfg;
+            *stream = StandardStream::stderr(cfg.to_termcolor_color_choice());
         }
         Ok(())
     }
@@ -184,7 +215,7 @@
     /// has been set to something else.
     pub fn color_choice(&self) -> ColorChoice {
         match self.err {
-            ShellOut::Stream(_, cc) => cc,
+            ShellOut::Stream { color_choice, .. } => color_choice,
             ShellOut::Write(_) => ColorChoice::Never,
         }
     }
@@ -195,22 +226,25 @@
     /// The status can be justified, in which case the max width that will right align is 12 chars.
     fn print(&mut self,
              status: &fmt::Display,
-             message: &fmt::Display,
+             message: Option<&fmt::Display>,
              color: Color,
              justified: bool) -> CargoResult<()> {
         match *self {
-            ShellOut::Stream(ref mut err, _) => {
-                err.reset()?;
-                err.set_color(ColorSpec::new()
+            ShellOut::Stream { ref mut stream, .. } => {
+                stream.reset()?;
+                stream.set_color(ColorSpec::new()
                                     .set_bold(true)
                                     .set_fg(Some(color)))?;
                 if justified {
-                    write!(err, "{:>12}", status)?;
+                    write!(stream, "{:>12}", status)?;
                 } else {
-                    write!(err, "{}", status)?;
+                    write!(stream, "{}", status)?;
                 }
-                err.reset()?;
-                write!(err, " {}\n", message)?;
+                stream.reset()?;
+                match message {
+                    Some(message) => write!(stream, " {}\n", message)?,
+                    None => write!(stream, " ")?,
+                }
             }
             ShellOut::Write(ref mut w) => {
                 if justified {
@@ -218,7 +252,10 @@
                 } else {
                     write!(w, "{}", status)?;
                 }
-                write!(w, " {}\n", message)?;
+                match message {
+                    Some(message) => write!(w, " {}\n", message)?,
+                    None => write!(w, " ")?,
+                }
             }
         }
         Ok(())
@@ -227,7 +264,7 @@
     /// Get this object as a `io::Write`.
     fn as_write(&mut self) -> &mut Write {
         match *self {
-            ShellOut::Stream(ref mut err, _) => err,
+            ShellOut::Stream { ref mut stream, .. } => stream,
             ShellOut::Write(ref mut w) => w,
         }
     }
@@ -249,3 +286,50 @@
         }
     }
 }
+
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+mod imp {
+    use std::mem;
+
+    use libc;
+
+    pub fn stderr_width() -> Option<usize> {
+        unsafe {
+            let mut winsize: libc::winsize = mem::zeroed();
+            if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ, &mut winsize) < 0 {
+                return None
+            }
+            if winsize.ws_col > 0 {
+                Some(winsize.ws_col as usize)
+            } else {
+                None
+            }
+        }
+    }
+}
+
+#[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))]
+mod imp {
+    pub fn stderr_width() -> Option<usize> {
+        None
+    }
+}
+
+#[cfg(windows)]
+mod imp {
+    use std::mem;
+
+    extern crate winapi;
+    extern crate kernel32;
+
+    pub fn stderr_width() -> Option<usize> {
+        unsafe {
+            let stdout = kernel32::GetStdHandle(winapi::STD_ERROR_HANDLE);
+            let mut csbi: winapi::CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
+            if kernel32::GetConsoleScreenBufferInfo(stdout, &mut csbi) == 0 {
+                return None
+            }
+            Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize)
+        }
+    }
+}
diff --git a/src/cargo/ops/cargo_generate_lockfile.rs b/src/cargo/ops/cargo_generate_lockfile.rs
index d07ee96..0d6ebef 100644
--- a/src/cargo/ops/cargo_generate_lockfile.rs
+++ b/src/cargo/ops/cargo_generate_lockfile.rs
@@ -1,5 +1,7 @@
 use std::collections::{BTreeMap, HashSet};
 
+use termcolor::Color::{self, Cyan, Green, Red};
+
 use core::PackageId;
 use core::registry::PackageRegistry;
 use core::{Resolve, SourceId, Workspace};
@@ -83,8 +85,8 @@
                                                   true)?;
 
     // Summarize what is changing for the user.
-    let print_change = |status: &str, msg: String| {
-        opts.config.shell().status(status, msg)
+    let print_change = |status: &str, msg: String, color: Color| {
+        opts.config.shell().status_with_color(status, msg, color)
     };
     for (removed, added) in compare_dependency_graphs(&previous_resolve, &resolve) {
         if removed.len() == 1 && added.len() == 1 {
@@ -94,13 +96,13 @@
             } else {
                 format!("{} -> v{}", removed[0], added[0].version())
             };
-            print_change("Updating", msg)?;
+            print_change("Updating", msg, Green)?;
         } else {
             for package in removed.iter() {
-                print_change("Removing", format!("{}", package))?;
+                print_change("Removing", format!("{}", package), Red)?;
             }
             for package in added.iter() {
-                print_change("Adding", format!("{}", package))?;
+                print_change("Adding", format!("{}", package), Cyan)?;
             }
         }
     }
diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs
index 3a4e7f6..c20d7bf 100644
--- a/src/cargo/ops/cargo_run.rs
+++ b/src/cargo/ops/cargo_run.rs
@@ -17,7 +17,7 @@
             0 => ws.current()?,
             1 => ws.members()
                 .find(|pkg| pkg.name() == xs[0])
-                .ok_or_else(|| 
+                .ok_or_else(||
                     CargoError::from(
                         format!("package `{}` is not a member of the workspace", xs[0]))
                 )?,
@@ -25,25 +25,28 @@
         }
     };
 
-    let mut bins = pkg.manifest().targets().iter().filter(|a| {
+    let bins: Vec<_> = pkg.manifest().targets().iter().filter(|a| {
         !a.is_lib() && !a.is_custom_build() && if !options.filter.is_specific() {
             a.is_bin()
         } else {
             options.filter.matches(a)
         }
-    });
-    if bins.next().is_none() {
+    })
+    .map(|bin| bin.name())
+    .collect();
+
+    if bins.len() == 0 {
         if !options.filter.is_specific() {
             bail!("a bin target must be available for `cargo run`")
         } else {
             // this will be verified in cargo_compile
         }
     }
-    if bins.next().is_some() {
+    if bins.len() > 1 {
         if !options.filter.is_specific() {
             bail!("`cargo run` requires that a project only have one \
                    executable; use the `--bin` option to specify which one \
-                   to run")
+                   to run\navailable binaries: {}", bins.join(", "))
         } else {
             bail!("`cargo run` can run at most one executable, but \
                    multiple were specified")
diff --git a/src/cargo/ops/cargo_rustc/output_depinfo.rs b/src/cargo/ops/cargo_rustc/output_depinfo.rs
index 0a0644d..b07b299 100644
--- a/src/cargo/ops/cargo_rustc/output_depinfo.rs
+++ b/src/cargo/ops/cargo_rustc/output_depinfo.rs
@@ -19,23 +19,32 @@
     relpath.to_str().ok_or_else(|| internal("path not utf-8")).map(|f| f.replace(" ", "\\ "))
 }
 
-fn add_deps_for_unit<'a, 'b>(deps: &mut HashSet<PathBuf>, context: &mut Context<'a, 'b>,
-    unit: &Unit<'a>, visited: &mut HashSet<Unit<'a>>) -> CargoResult<()>
+fn add_deps_for_unit<'a, 'b>(
+    deps: &mut HashSet<PathBuf>,
+    context: &mut Context<'a, 'b>,
+    unit: &Unit<'a>,
+    visited: &mut HashSet<Unit<'a>>,
+)
+    -> CargoResult<()>
 {
     if !visited.insert(*unit) {
         return Ok(());
     }
 
-    // Add dependencies from rustc dep-info output (stored in fingerprint directory)
-    let dep_info_loc = fingerprint::dep_info_loc(context, unit);
-    if let Some(paths) = fingerprint::parse_dep_info(&dep_info_loc)? {
-        for path in paths {
-            deps.insert(path);
+    // units representing the execution of a build script don't actually
+    // generate a dep info file, so we just keep on going below
+    if !unit.profile.run_custom_build {
+        // Add dependencies from rustc dep-info output (stored in fingerprint directory)
+        let dep_info_loc = fingerprint::dep_info_loc(context, unit);
+        if let Some(paths) = fingerprint::parse_dep_info(&dep_info_loc)? {
+            for path in paths {
+                deps.insert(path);
+            }
+        } else {
+            debug!("can't find dep_info for {:?} {:?}",
+                unit.pkg.package_id(), unit.profile);
+            return Err(internal("dep_info missing"));
         }
-    } else {
-        debug!("can't find dep_info for {:?} {:?}",
-            unit.pkg.package_id(), unit.profile);
-        return Err(internal("dep_info missing"));
     }
 
     // Add rerun-if-changed dependencies
diff --git a/src/cargo/ops/lockfile.rs b/src/cargo/ops/lockfile.rs
index 0a7aef5..7368bbf 100644
--- a/src/cargo/ops/lockfile.rs
+++ b/src/cargo/ops/lockfile.rs
@@ -69,7 +69,7 @@
     // If the lockfile contents haven't changed so don't rewrite it. This is
     // helpful on read-only filesystems.
     if let Ok(orig) = orig {
-        if are_equal_lockfiles(orig, &out, ws.config().lock_update_allowed()) {
+        if are_equal_lockfiles(orig, &out, ws) {
             return Ok(())
         }
     }
@@ -91,20 +91,25 @@
     })
 }
 
-fn are_equal_lockfiles(mut orig: String, current: &str, lock_update_allowed: bool) -> bool {
+fn are_equal_lockfiles(mut orig: String, current: &str, ws: &Workspace) -> bool {
     if has_crlf_line_endings(&orig) {
         orig = orig.replace("\r\n", "\n");
     }
 
-    // Old lockfiles have unused `[root]` section,
-    // just ignore it if we are in the `--frozen` mode.
-    if !lock_update_allowed && orig.starts_with("[root]") {
-        orig = orig.replacen("[root]", "[[package]]", 1);
-        match (orig.parse::<toml::Value>(), current.parse::<toml::Value>()) {
-            (Ok(ref a), Ok(ref b)) if a == b => return true,
-            _ => {}
+    // If we want to try and avoid updating the lockfile, parse both and
+    // compare them; since this is somewhat expensive, don't do it in the
+    // common case where we can update lockfiles.
+    if !ws.config().lock_update_allowed() {
+        let res: CargoResult<bool> = (|| {
+            let old: resolver::EncodableResolve = toml::from_str(&orig)?;
+            let new: resolver::EncodableResolve = toml::from_str(current)?;
+            Ok(old.into_resolve(ws)? == new.into_resolve(ws)?)
+        })();
+        if let Ok(true) = res {
+            return true;
         }
     }
+
     current == orig
 }
 
diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs
index f26eb8e..2ccf3b3 100644
--- a/src/cargo/ops/resolve.rs
+++ b/src/cargo/ops/resolve.rs
@@ -257,15 +257,11 @@
         None => root_replace.to_vec(),
     };
 
-    let config = if warn {
-        Some(ws.config())
-    } else {
-        None
-    };
     let mut resolved = resolver::resolve(&summaries,
                                          &replace,
                                          registry,
-                                         config)?;
+                                         Some(ws.config()),
+                                         warn)?;
     resolved.register_used_patches(registry.patches());
     if let Some(previous) = previous {
         resolved.merge_from(previous)?;
diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs
index 505fd24..ed2bc28 100644
--- a/src/cargo/sources/git/utils.rs
+++ b/src/cargo/sources/git/utils.rs
@@ -11,7 +11,7 @@
 use url::Url;
 
 use core::GitReference;
-use util::{ToUrl, internal, Config, network};
+use util::{ToUrl, internal, Config, network, Progress};
 use util::errors::{CargoResult, CargoResultExt, CargoError};
 
 #[derive(PartialEq, Clone, Debug)]
@@ -140,10 +140,6 @@
 }
 
 impl GitDatabase {
-    fn path(&self) -> &Path {
-        &self.path
-    }
-
     pub fn copy_to(&self, rev: GitRevision, dest: &Path, cargo_config: &Config)
                    -> CargoResult<GitCheckout> {
         let checkout = match git2::Repository::open(dest) {
@@ -151,12 +147,12 @@
                 let mut checkout = GitCheckout::new(dest, self, rev, repo);
                 if !checkout.is_fresh() {
                     checkout.fetch(cargo_config)?;
-                    checkout.reset()?;
+                    checkout.reset(cargo_config)?;
                     assert!(checkout.is_fresh());
                 }
                 checkout
             }
-            Err(..) => GitCheckout::clone_into(dest, self, rev)?,
+            Err(..) => GitCheckout::clone_into(dest, self, rev, cargo_config)?,
         };
         checkout.update_submodules(cargo_config)?;
         Ok(checkout)
@@ -220,37 +216,26 @@
         }
     }
 
-    fn clone_into(into: &Path, database: &'a GitDatabase,
-                  revision: GitRevision)
+    fn clone_into(into: &Path,
+                  database: &'a GitDatabase,
+                  revision: GitRevision,
+                  config: &Config)
                   -> CargoResult<GitCheckout<'a>>
     {
-        let repo = GitCheckout::clone_repo(database.path(), into)?;
-        let checkout = GitCheckout::new(into, database, revision, repo);
-        checkout.reset()?;
-        Ok(checkout)
-    }
-
-    fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
         let dirname = into.parent().unwrap();
-
         fs::create_dir_all(&dirname).chain_err(|| {
             format!("Couldn't mkdir {}", dirname.display())
         })?;
-
         if fs::metadata(&into).is_ok() {
             fs::remove_dir_all(into).chain_err(|| {
                 format!("Couldn't rmdir {}", into.display())
             })?;
         }
-
-        let url = source.to_url()?;
-        let url = url.to_string();
-        let repo = git2::Repository::clone(&url, into)
-            .chain_err(|| {
-                internal(format!("failed to clone {} into {}", source.display(),
-                             into.display()))
-        })?;
-        Ok(repo)
+        let repo = git2::Repository::init(into)?;
+        let mut checkout = GitCheckout::new(into, database, revision, repo);
+        checkout.fetch(config)?;
+        checkout.reset(config)?;
+        Ok(checkout)
     }
 
     fn is_fresh(&self) -> bool {
@@ -271,7 +256,7 @@
         Ok(())
     }
 
-    fn reset(&self) -> CargoResult<()> {
+    fn reset(&self, config: &Config) -> CargoResult<()> {
         // If we're interrupted while performing this reset (e.g. we die because
         // of a signal) Cargo needs to be sure to try to check out this repo
         // again on the next go-round.
@@ -284,7 +269,7 @@
         let _ = fs::remove_file(&ok_file);
         info!("reset {} to {}", self.repo.path().display(), self.revision);
         let object = self.repo.find_object(self.revision.0, None)?;
-        self.repo.reset(&object, git2::ResetType::Hard, None)?;
+        reset(&self.repo, &object, config)?;
         File::create(ok_file)?;
         Ok(())
     }
@@ -339,7 +324,7 @@
                 Err(..) => {
                     let path = parent.workdir().unwrap().join(child.path());
                     let _ = fs::remove_dir_all(&path);
-                    git2::Repository::clone(url, &path)?
+                    git2::Repository::init(&path)?
                 }
             };
 
@@ -351,8 +336,8 @@
                                  child.name().unwrap_or(""), url))
             })?;
 
-            repo.find_object(head, None)
-                .and_then(|obj| { repo.reset(&obj, git2::ResetType::Hard, None)})?;
+            let obj = repo.find_object(head, None)?;
+            reset(&repo, &obj, cargo_config)?;
             update_submodules(&repo, cargo_config)
         }
     }
@@ -558,6 +543,18 @@
     })
 }
 
+fn reset(repo: &git2::Repository,
+         obj: &git2::Object,
+         config: &Config) -> CargoResult<()> {
+    let mut pb = Progress::new("Checkout", config);
+    let mut opts = git2::build::CheckoutBuilder::new();
+    opts.progress(|_, cur, max| {
+        drop(pb.tick(cur, max));
+    });
+    repo.reset(obj, git2::ResetType::Hard, Some(&mut opts))?;
+    Ok(())
+}
+
 pub fn fetch(repo: &mut git2::Repository,
              url: &Url,
              refspec: &str,
@@ -588,10 +585,15 @@
     maybe_gc_repo(repo)?;
 
     debug!("doing a fetch for {}", url);
+    let mut progress = Progress::new("Fetch", config);
     with_authentication(url.as_str(), &repo.config()?, |f| {
         let mut cb = git2::RemoteCallbacks::new();
         cb.credentials(f);
 
+        cb.transfer_progress(|stats| {
+            progress.tick(stats.indexed_objects(), stats.total_objects()).is_ok()
+        });
+
         // Create a local anonymous remote in the repository to fetch the url
         let mut remote = repo.remote_anonymous(url.as_str())?;
         let mut opts = git2::FetchOptions::new();
diff --git a/src/cargo/sources/registry/remote.rs b/src/cargo/sources/registry/remote.rs
index 8462261..e40b350 100644
--- a/src/cargo/sources/registry/remote.rs
+++ b/src/cargo/sources/registry/remote.rs
@@ -3,6 +3,7 @@
 use std::io::prelude::*;
 use std::mem;
 use std::path::Path;
+use std::str;
 
 use git2;
 use hex::ToHex;
@@ -14,7 +15,7 @@
 use sources::registry::{RegistryData, RegistryConfig, INDEX_LOCK};
 use util::network;
 use util::{FileLock, Filesystem, LazyCell};
-use util::{Config, Sha256, ToUrl};
+use util::{Config, Sha256, ToUrl, Progress};
 use util::errors::{CargoErrorKind, CargoResult, CargoResultExt};
 
 pub struct RemoteRegistry<'cfg> {
@@ -222,8 +223,13 @@
         network::with_retry(self.config, || {
             state = Sha256::new();
             body = Vec::new();
+            let mut pb = Progress::new("Fetch", self.config);
             {
+                handle.progress(true)?;
                 let mut handle = handle.transfer();
+                handle.progress_function(|dl_total, dl_cur, _, _| {
+                    pb.tick(dl_cur as usize, dl_total as usize).is_ok()
+                })?;
                 handle.write_function(|buf| {
                     state.update(buf);
                     body.extend_from_slice(buf);
diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs
index 5bf967b..f343a6c 100644
--- a/src/cargo/util/config.rs
+++ b/src/cargo/util/config.rs
@@ -630,9 +630,11 @@
     }
 
     pub fn http(&self) -> CargoResult<&RefCell<Easy>> {
-        self.easy.get_or_try_init(|| {
+        let http = self.easy.get_or_try_init(|| {
             ops::http_handle(self).map(RefCell::new)
-        })
+        })?;
+        http.borrow_mut().reset();
+        Ok(http)
     }
 }
 
diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs
index 9c1c9c5..6019331 100644
--- a/src/cargo/util/mod.rs
+++ b/src/cargo/util/mod.rs
@@ -18,6 +18,7 @@
 pub use self::to_url::ToUrl;
 pub use self::vcs::{GitRepo, HgRepo, PijulRepo, FossilRepo};
 pub use self::read2::read2;
+pub use self::progress::Progress;
 
 pub mod config;
 pub mod errors;
@@ -42,3 +43,4 @@
 mod lazy_cell;
 mod flock;
 mod read2;
+mod progress;
diff --git a/src/cargo/util/progress.rs b/src/cargo/util/progress.rs
new file mode 100644
index 0000000..c4a3e03
--- /dev/null
+++ b/src/cargo/util/progress.rs
@@ -0,0 +1,127 @@
+use std::cmp;
+use std::iter;
+use std::time::{Instant, Duration};
+
+use util::{Config, CargoResult};
+
+pub struct Progress<'cfg> {
+    state: Option<State<'cfg>>,
+}
+
+struct State<'cfg> {
+    config: &'cfg Config,
+    width: usize,
+    first: bool,
+    last_update: Instant,
+    name: String,
+    done: bool,
+}
+
+impl<'cfg> Progress<'cfg> {
+    pub fn new(name: &str, cfg: &'cfg Config) -> Progress<'cfg> {
+        Progress {
+            state: cfg.shell().err_width().map(|n| {
+                State {
+                    config: cfg,
+                    width: cmp::min(n, 80),
+                    first: true,
+                    last_update: Instant::now(),
+                    name: name.to_string(),
+                    done: false,
+                }
+            }),
+        }
+    }
+
+    pub fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
+        match self.state {
+            Some(ref mut s) => s.tick(cur, max),
+            None => Ok(())
+        }
+    }
+}
+
+impl<'cfg> State<'cfg> {
+    fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
+        if self.done {
+            return Ok(())
+        }
+
+        // Don't update too often as it can cause excessive performance loss
+        // just putting stuff onto the terminal. We also want to avoid
+        // flickering by not drawing anything that goes away too quickly. As a
+        // result we've got two branches here:
+        //
+        // 1. If we haven't drawn anything, we wait for a period of time to
+        //    actually start drawing to the console. This ensures that
+        //    short-lived operations don't flicker on the console. Currently
+        //    there's a 500ms delay to when we first draw something.
+        // 2. If we've drawn something, then we rate limit ourselves to only
+        //    draw to the console every so often. Currently there's a 100ms
+        //    delay between updates.
+        if self.first {
+            let delay = Duration::from_millis(500);
+            if self.last_update.elapsed() < delay {
+                return Ok(())
+            }
+            self.first = false;
+        } else {
+            let interval = Duration::from_millis(100);
+            if self.last_update.elapsed() < interval {
+                return Ok(())
+            }
+        }
+        self.last_update = Instant::now();
+
+        // Render the percentage at the far right and then figure how long the
+        // progress bar is
+        let pct = (cur as f64) / (max as f64);
+        let pct = if !pct.is_finite() { 0.0 } else { pct };
+        let stats = format!(" {:6.02}%", pct * 100.0);
+        let extra_len = stats.len() + 2 /* [ and ] */ + 15 /* status header */;
+        let display_width = match self.width.checked_sub(extra_len) {
+            Some(n) => n,
+            None => return Ok(()),
+        };
+        let mut string = String::from("[");
+        let hashes = display_width as f64 * pct;
+        let hashes = hashes as usize;
+
+        // Draw the `===>`
+        if hashes > 0 {
+            for _ in 0..hashes-1 {
+                string.push_str("=");
+            }
+            if cur == max {
+                self.done = true;
+                string.push_str("=");
+            } else {
+                string.push_str(">");
+            }
+        }
+
+        // Draw the empty space we have left to do
+        for _ in 0..(display_width - hashes) {
+            string.push_str(" ");
+        }
+        string.push_str("]");
+        string.push_str(&stats);
+
+        // Write out a pretty header, then the progress bar itself, and then
+        // return back to the beginning of the line for the next print.
+        self.config.shell().status_header(&self.name)?;
+        write!(self.config.shell().err(), "{}\r", string)?;
+        Ok(())
+    }
+}
+
+fn clear(width: usize, config: &Config) {
+    let blank = iter::repeat(" ").take(width).collect::<String>();
+    drop(write!(config.shell().err(), "{}\r", blank));
+}
+
+impl<'cfg> Drop for State<'cfg> {
+    fn drop(&mut self) {
+        clear(self.width, self.config);
+    }
+}
diff --git a/src/doc/book/book.toml b/src/doc/book/book.toml
index 1b84b29..1f21e1e 100644
--- a/src/doc/book/book.toml
+++ b/src/doc/book/book.toml
@@ -1,2 +1,2 @@
-title = "The Cargo Manual"
+title = "The Cargo Book"
 author = "Alex Crichton, Steve Klabnik and Carol Nichols, with Contributions from the Rust Community"
diff --git a/src/doc/book/src/faq.md b/src/doc/book/src/faq.md
index 7f13573..7ee2d2b 100644
--- a/src/doc/book/src/faq.md
+++ b/src/doc/book/src/faq.md
@@ -74,7 +74,7 @@
 dependencies][target-deps], and we plan to support more per-platform
 configuration in `Cargo.toml` in the future.
 
-[target-deps]: reference/manifest.html#the-dependencies-section
+[target-deps]: reference/specifying-dependencies.html#platform-specific-dependencies
 
 In the longer-term, we’re looking at ways to conveniently cross-compile
 projects using Cargo.
diff --git a/src/doc/book/src/index.md b/src/doc/book/src/index.md
index 3de0fc1..e3fc99c 100644
--- a/src/doc/book/src/index.md
+++ b/src/doc/book/src/index.md
@@ -1,15 +1,15 @@
-# The Cargo Manual
+# The Cargo Book
 
 ![Cargo Logo](images/Cargo-Logo-Small.png)
 
 Cargo is the [Rust] *package manager*. Cargo downloads your Rust project’s
 dependencies, compiles your project, makes packages, and upload them to
-[crates.io], the Rust *package registry*.
+[crates.io], the Rust community’s *package registry*.
 
 
 ### Sections
 
-**[Getting Started](getting-started.html)**
+**[Getting Started](getting-started/index.html)**
 
 To get started with Cargo, install Cargo (and Rust) and set up your first crate.
 
diff --git a/src/doc/book/src/reference/specifying-dependencies.md b/src/doc/book/src/reference/specifying-dependencies.md
index b4f81fa..e58fa91 100644
--- a/src/doc/book/src/reference/specifying-dependencies.md
+++ b/src/doc/book/src/reference/specifying-dependencies.md
@@ -384,7 +384,7 @@
 and you can find [more documentation about this configuration][config-docs].
 Inside of `.cargo/config` you'll specify a key called `paths`:
 
-[config-docs]: config.html
+[config-docs]: reference/config.html
 
 ```toml
 paths = ["/path/to/uuid"]
diff --git a/tests/dep-info.rs b/tests/dep-info.rs
index 8b060aa..f2f1f82 100644
--- a/tests/dep-info.rs
+++ b/tests/dep-info.rs
@@ -31,6 +31,7 @@
             name = "ex"
             crate-type = ["lib"]
         "#)
+        .file("build.rs", "fn main() {}")
         .file("src/lib.rs", "")
         .file("examples/ex.rs", "")
         .build();
diff --git a/tests/lockfile-compat.rs b/tests/lockfile-compat.rs
index 3f887c9..0c90fd4 100644
--- a/tests/lockfile-compat.rs
+++ b/tests/lockfile-compat.rs
@@ -22,24 +22,24 @@
 
     let expected_lockfile =
 r#"[[package]]
-name = "bar"
+name = "foo"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "zzz"
 version = "0.0.1"
 dependencies = [
  "foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "foo"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [metadata]
 "checksum foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "[..]"
 "#;
 
     let old_lockfile =
 r#"[root]
-name = "bar"
+name = "zzz"
 version = "0.0.1"
 dependencies = [
  "foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -54,7 +54,7 @@
     let p = project("bar")
         .file("Cargo.toml", r#"
             [project]
-            name = "bar"
+            name = "zzz"
             version = "0.0.1"
             authors = []
 
@@ -83,7 +83,7 @@
 
     let old_lockfile =
         r#"[root]
-name = "bar"
+name = "zzz"
 version = "0.0.1"
 dependencies = [
  "foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -101,7 +101,7 @@
     let p = project("bar")
         .file("Cargo.toml", r#"
             [project]
-            name = "bar"
+            name = "zzz"
             version = "0.0.1"
             authors = []
 
diff --git a/tests/required-features.rs b/tests/required-features.rs
index 0f71d6d..233f7f6 100644
--- a/tests/required-features.rs
+++ b/tests/required-features.rs
@@ -996,5 +996,5 @@
     assert_that(p.cargo("run"),
                 execs().with_status(101).with_stderr("\
 error: `cargo run` requires that a project only have one executable; \
-use the `--bin` option to specify which one to run"));
+use the `--bin` option to specify which one to run\navailable binaries: foo1, foo2"));
 }
diff --git a/tests/resolve.rs b/tests/resolve.rs
index 31f7aba..42a67dd 100644
--- a/tests/resolve.rs
+++ b/tests/resolve.rs
@@ -34,7 +34,7 @@
     let mut registry = MyRegistry(registry);
     let summary = Summary::new(pkg.clone(), deps, BTreeMap::new()).unwrap();
     let method = Method::Everything;
-    let resolve = resolver::resolve(&[(summary, method)], &[], &mut registry, None)?;
+    let resolve = resolver::resolve(&[(summary, method)], &[], &mut registry, None, false)?;
     let res = resolve.iter().cloned().collect();
     Ok(res)
 }
diff --git a/tests/run.rs b/tests/run.rs
index 449c91c..efa107c 100644
--- a/tests/run.rs
+++ b/tests/run.rs
@@ -256,7 +256,7 @@
                 execs().with_status(101)
                        .with_stderr("[ERROR] `cargo run` requires that a project only \
                                      have one executable; use the `--bin` option \
-                                     to specify which one to run\n"));
+                                     to specify which one to run\navailable binaries: [..]\n"));
 }
 
 #[test]
@@ -278,7 +278,7 @@
                 execs().with_status(101)
                        .with_stderr("[ERROR] `cargo run` requires that a project only \
                                      have one executable; use the `--bin` option \
-                                     to specify which one to run\n"));
+                                     to specify which one to run\navailable binaries: [..]\n"));
 }
 
 #[test]