| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This script looks at the current commit and branch of the git repository in |
| // which it was run, and finds the contemporary commit in the master branch of |
| // another git repository, whose path is provided on the command line. The |
| // contemporary commit is the one that was public at the time of the last commit |
| // on the master branch before the current commit's branch was created. |
| |
| import 'dart:io'; |
| |
| const bool debugLogging = false; |
| |
| void log(String message) { |
| if (debugLogging) { |
| print(message); |
| } |
| } |
| |
| class Commit { |
| Commit(this.hash, this.timestamp); |
| |
| final String hash; |
| final DateTime timestamp; |
| |
| static String formatArgument = '--format=%H %cI'; |
| |
| static Commit parse(String line) { |
| final int space = line.indexOf(' '); |
| return Commit(line.substring(0, space), DateTime.parse(line.substring(space+1, line.length).trimRight())); |
| } |
| |
| static List<Commit> parseList(String lines) { |
| return lines.split('\n').where((String line) => line.isNotEmpty).map(parse).toList().reversed.toList(); |
| } |
| } |
| |
| String findCommit({ |
| required String primaryRepoDirectory, |
| required String primaryBranch, |
| required String primaryTrunk, |
| required String secondaryRepoDirectory, |
| required String secondaryBranch, |
| }) { |
| final Commit anchor; |
| if (primaryBranch == primaryTrunk) { |
| log('on $primaryTrunk, using last commit as anchor'); |
| anchor = Commit.parse(git(primaryRepoDirectory, <String>['log', Commit.formatArgument, '--max-count=1', primaryBranch, '--'])); |
| } else { |
| final List<Commit> branchCommits = Commit.parseList(git(primaryRepoDirectory, <String>['log', Commit.formatArgument, primaryBranch, '--'])); |
| final List<Commit> trunkCommits = Commit.parseList(git(primaryRepoDirectory, <String>['log', Commit.formatArgument, primaryTrunk, '--'])); |
| if (branchCommits.isEmpty || trunkCommits.isEmpty || branchCommits.first.hash != trunkCommits.first.hash) { |
| throw StateError('Branch $primaryBranch does not seem to have a common history with trunk $primaryTrunk.'); |
| } |
| if (branchCommits.last.hash == trunkCommits.last.hash) { |
| log('$primaryBranch is even with $primaryTrunk, using last commit as anchor'); |
| anchor = trunkCommits.last; |
| } else { |
| int index = 0; |
| while (branchCommits.length > index && trunkCommits.length > index && branchCommits[index].hash == trunkCommits[index].hash) { |
| index += 1; |
| } |
| log('$primaryBranch branched from $primaryTrunk ${branchCommits.length - index} commits ago, trunk has advanced by ${trunkCommits.length - index} commits since then.'); |
| anchor = trunkCommits[index - 1]; |
| } |
| } |
| return git(secondaryRepoDirectory, <String>[ |
| 'log', |
| '--format=%H', |
| '--until=${anchor.timestamp.toIso8601String()}', |
| '--max-count=1', |
| secondaryBranch, |
| '--', |
| ]); |
| } |
| |
| String git(String workingDirectory, List<String> arguments) { |
| final ProcessResult result = Process.runSync('git', arguments, workingDirectory: workingDirectory); |
| if (result.exitCode != 0 || '${result.stderr}'.isNotEmpty) { |
| throw ProcessException('git', arguments, '${result.stdout}${result.stderr}', result.exitCode); |
| } |
| return '${result.stdout}'; |
| } |
| |
| void main(List<String> arguments) { |
| if (arguments.isEmpty || arguments.length > 2 || arguments.contains('--help') || arguments.contains('-h')) { |
| print( |
| 'Usage: dart find_commit.dart [<path-to-primary-repo>] <path-to-secondary-repo>\n' |
| 'This script will find the commit in the secondary repo that was contemporary\n' |
| 'when the commit in the primary repo was created. If that commit is on a\n' |
| "branch, then the date of the branch's creation is used instead.\n" |
| 'If <path-to-primary-repo> is omitted, the current directory is used for the\n' |
| 'primary repo.' |
| ); |
| } else { |
| final String primaryRepo = arguments.length == 1 ? '.' : arguments.first; |
| final String secondaryRepo = arguments.last; |
| print(findCommit( |
| primaryRepoDirectory: primaryRepo, |
| primaryBranch: git(primaryRepo, <String>['rev-parse', '--abbrev-ref', 'HEAD']).trim(), |
| primaryTrunk: 'master', |
| secondaryRepoDirectory: secondaryRepo, |
| secondaryBranch: 'master', |
| ).trim()); |
| } |
| } |