function sync($dryrun = false) {

		$channels = $this->channels();

		$status = [
			// ordered list of versions in processing order
			'history' => [],
			// current version for each channel
			'state' => [],
			// active channels
			'active' => [],
			// checklist of version requirements
			'checklist' => $this->generate_checklist($channels)
		];

		// inject current history
		list($history, $err) = $this->history();

		if ($err !== 0) {
			throw new Panic('Failed to retrieve history.');
		}

		foreach ($history as $entry) {
			if ($entry['hotfix'] === null) {
				$status['state'][$entry['channel']] = $this->binversion($entry['channel'], $entry['version']);
			}
		}

		// generate version history for upgrade
		foreach ($channels as $channel => &$timeline) {
			if (count($timeline['versions']) > 0) {
				end($timeline['versions']);
				$last_version = key($timeline['versions']);
				$this->processhistory($channel, $last_version, $status, $channels);
			}
		}

		// dry run?
		if ($dryrun) {
			// just return the step history
			return [$status['history'], 0];
		}

		$migrations = 0;
		if ( ! empty($status['history'])) {
			// execute the history
			foreach ($status['history'] as $entry) {
				// execute migration
				$this->processmigration($channels, $entry['channel'], $entry['version'], $entry['hotfix']);
				$migrations++;
			}
		}
		else { // no history
			$this->log("No changes required.\n");
			return [$migrations, 0];
		}

		// operation complete
		return [$migrations, 0];

	}