anchor.diff:  
     return completer.future;
   }
  +
  +// See http://git-scm.com/docs/git-ls-remote
  +Future<bool> _gitRemoteHasHead(String remote, String head) {
  +  requireArgumentNotNull(remote, 'remote');
  +  requireArgumentNotNull(head, 'head');
  +  final args = ['ls-remote', '--exit-code', '--heads', remote, head];
  +  return Process.run('git', args)
  +      .transform((ProcessResult pr) {
  +        if(pr.exitCode == 0) {
  +          return true;
  +        } else if(pr.exitCode == 2) {
  +          // per --exit-code, this implies head does not exist
  +          return false;
  +        } else {
  +          throw "git command error. Exit code: ${pr.exitCode}, Error: ${pr.stderr}";
  +        }
  +      });
  +}
  +
  +void _throwIfProcessFailed(TaskContext ctx, ProcessResult pr) {
  +  assert(pr != null);
  +  if(pr.exitCode != 0) {
  +    ctx.error('Process returned a non-zero exit code');
  +    ctx.fine(pr.stdout.trim());
  +    ctx.error(pr.stderr.trim());
  +    ctx.error('Exit code: ${pr.exitCode}');
  +    throw 'Task failed';
  +  }
  +}
  
positive.code: part of hop_runner;
  
  Future<bool> _compileDocs(TaskContext ctx) {
    _assertKnownPath();
    return _getLibs().chain((libs) {
      final args = ['--omit-generation-time', '--out', 'build/doc', '--verbose'];
      args.addAll(libs);
      return startProcess(ctx, "dartdoc", args);
    });
  }
  
  Future<List<String>> _getLibs() {
  
negative.code: 
    lister.onFile = (String file) {
      if(file.endsWith('.dart')) {
        // DARTBUG: http://code.google.com/p/dart/issues/detail?id=5460
        // exclude libs because of issues with dartdoc and sdk libs
        // in this case: unittest and args
        final forbidden = ['test', 'hop', 'tasks'].map((n) => '$n.dart');
        if(forbidden.every((f) => !file.endsWith(f))) {
          libs.add(file);
  