| // 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()); | |
| } | |
| } |