async function run(requestedVersion, cliArgs) {
  const tmpDir = path.resolve(os.tmpdir(), 'react-native-git-upgrade');
  const generatorDir = path.resolve(process.cwd(), 'node_modules', 'react-native', 'local-cli', 'generator');
  let projectBackupCreated = false;

  try {
    await checkForUpdates();

    const useYarn = shouldUseYarn(cliArgs, path.resolve(process.cwd()));

    log.info('Read package.json files');
    const {reactNativeNodeModulesPak, reactNodeModulesPak, pak} = readPackageFiles(useYarn);
    const appName = pak.name;
    const currentVersion = reactNativeNodeModulesPak.version;
    const currentReactVersion = reactNodeModulesPak.version;
    const declaredVersion = pak.dependencies['react-native'];
    const declaredReactVersion = pak.dependencies.react;

    const verbose = cliArgs.verbose;

    log.info('Check declared version');
    checkDeclaredVersion(declaredVersion);

    log.info('Check matching versions');
    checkMatchingVersions(currentVersion, declaredVersion, useYarn);

    log.info('Check React peer dependency');
    checkReactPeerDependency(currentVersion, declaredReactVersion);

    log.info('Check that Git is installed');
    checkGitAvailable();

    log.info('Get information from NPM registry');
    const viewCommand = 'npm view react-native@' + (requestedVersion || 'latest') + ' --json';
    const jsonOutput = await exec(viewCommand, verbose);
    const {newVersion, newReactVersionRange} = parseInformationJsonOutput(jsonOutput, requestedVersion);
    // Print which versions we're upgrading to
    log.info('Upgrading to React Native ' + newVersion + (newReactVersionRange ? ', React ' + newReactVersionRange : ''));

    log.info('Setup temporary working directory');
    await setupWorkingDir(tmpDir);

    log.info('Configure Git environment');
    configureGitEnv(tmpDir);

    log.info('Init Git repository');
    await exec('git init', verbose);

    log.info('Add all files to commit');
    await exec('git add .', verbose);

    log.info('Commit current project sources');
    await exec('git commit -m "Project snapshot" --no-verify', verbose);

    log.info('Create a tag before updating sources');
    await exec('git tag project-snapshot', verbose);
    projectBackupCreated = true;

    log.info('Generate old version template');
    await generateTemplates(generatorDir, appName, verbose);

    log.info('Add updated files to commit');
    await exec('git add .', verbose);

    log.info('Commit old version template');
    await exec('git commit -m "Old version" --allow-empty --no-verify', verbose);

    log.info('Install the new version');
    let installCommand;
    if (useYarn) {
      installCommand = 'yarn add';
    } else {
      installCommand = 'npm install --save --color=always';
    }
    installCommand += ' react-native@' + newVersion;
    if (newReactVersionRange && !semver.satisfies(currentReactVersion, newReactVersionRange)) {
      // Install React as well to avoid unmet peer dependency
      installCommand += ' react@' + newReactVersionRange;
    }
    await exec(installCommand, verbose);

    log.info('Generate new version template');
    await generateTemplates(generatorDir, appName, verbose);

    log.info('Add updated files to commit');
    await exec('git add .', verbose);

    log.info('Commit new version template');
    await exec('git commit -m "New version" --allow-empty --no-verify', verbose);

    log.info('Generate the patch between the 2 versions');
    const diffOutput = await exec('git diff --binary --no-color HEAD~1 HEAD', verbose);

    log.info('Save the patch in temp directory');
    const patchPath = path.resolve(tmpDir, `upgrade_${currentVersion}_${newVersion}.patch`);
    fs.writeFileSync(patchPath, diffOutput);

    log.info('Reset the 2 temporary commits');
    await exec('git reset HEAD~2 --hard', verbose);

    try {
      log.info('Apply the patch');
      await exec(`git apply --3way ${patchPath}`, true);
    } catch (err) {
      log.warn(
        'The upgrade process succeeded but there might be conflicts to be resolved. ' +
        'See above for the list of files that have merge conflicts.');
    } finally {
      log.info('Upgrade done');
      if (cliArgs.verbose) {
        log.info(`Temporary working directory: ${tmpDir}`);
      }
    }

  } catch (err) {
    log.error('An error occurred during upgrade:');
    log.error(err.stack);
    if (projectBackupCreated) {
      log.error('Restore initial sources');
      await exec('git checkout project-snapshot --no-verify', true);
    }
  }
}
@param requestedVersion The version argument, e.g. 'react-native-git-upgrade 0.38'.
                        `undefined` if no argument passed.
@param cliArgs Additional arguments parsed using minimist.