📄 prepare-changelog
字号:
} elsif ($match eq '//') { s-//.*--; } else { # ' or " if (!s-$match([^\\]|\\.)*?$match--) { $inQuotedText = $match if /\\$/; warn "mismatched quotes at line $. in $fileName\n" if $inQuotedText eq ""; s-$match.*--; } } } # Find function names. while (m-(\w+|[(){}=:;])-g) { # Open parenthesis. if ($1 eq '(') { $parenthesesDepth++; next; } # Close parenthesis. if ($1 eq ')') { $parenthesesDepth--; next; } # Open brace. if ($1 eq '{') { push(@currentScopes, join(".", @currentIdentifiers)); @currentIdentifiers = (); $bracesDepth++; next; } # Close brace. if ($1 eq '}') { $bracesDepth--; if (@currentFunctionDepths and $bracesDepth == $currentFunctionDepths[$#currentFunctionDepths]) { pop(@currentFunctionDepths); my $currentFunction = pop(@currentFunctionNames); my $start = pop(@currentFunctionStartLines); push(@ranges, [$start, $., $currentFunction]); } pop(@currentScopes); @currentIdentifiers = (); next; } # Semicolon. if ($1 eq ';') { @currentIdentifiers = (); next; } # Function. if ($1 eq 'function') { $functionJustSeen = 1; if ($assignmentJustSeen) { my $currentFunction = join('.', (@currentScopes, @currentIdentifiers)); $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods. push(@currentFunctionNames, $currentFunction); push(@currentFunctionDepths, $bracesDepth); push(@currentFunctionStartLines, $.); } next; } # Getter prefix. if ($1 eq 'get') { $getterJustSeen = 1; next; } # Setter prefix. if ($1 eq 'set') { $setterJustSeen = 1; next; } # Assignment operator. if ($1 eq '=' or $1 eq ':') { $assignmentJustSeen = 1; next; } next if $parenthesesDepth; # Word. $word = $1; $word = "get $word" if $getterJustSeen; $word = "set $word" if $setterJustSeen; if (($functionJustSeen and !$assignmentJustSeen) or $getterJustSeen or $setterJustSeen) { push(@currentIdentifiers, $word); my $currentFunction = join('.', (@currentScopes, @currentIdentifiers)); $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods. push(@currentFunctionNames, $currentFunction); push(@currentFunctionDepths, $bracesDepth); push(@currentFunctionStartLines, $.); } elsif ($word ne 'if' and $word ne 'for' and $word ne 'do' and $word ne 'while' and $word ne 'which' and $word ne 'var') { push(@currentIdentifiers, $word); } $functionJustSeen = 0; $getterJustSeen = 0; $setterJustSeen = 0; $assignmentJustSeen = 0; } } warn "mismatched braces in $fileName\n" if $bracesDepth; warn "mismatched parentheses in $fileName\n" if $parenthesesDepth; return @ranges;}sub processPaths(\@){ my ($paths) = @_; return ("." => 1) if (!@{$paths}); my %result = (); for my $file (@{$paths}) { die "can't handle absolute paths like \"$file\"\n" if File::Spec->file_name_is_absolute($file); die "can't handle empty string path\n" if $file eq ""; die "can't handle path with single quote in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy) my $untouchedFile = $file; $file = canonicalizePath($file); die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|; $result{$file} = 1; } return ("." => 1) if ($result{"."}); # Remove any paths that also have a parent listed. for my $path (keys %result) { for (my $parent = dirname($path); $parent ne '.'; $parent = dirname($parent)) { if ($result{$parent}) { delete $result{$path}; last; } } } return %result;}sub diffFromToString(){ return "" if $isSVN; return $gitCommit if $gitCommit =~ m/.+\.\..+/; return "\"$gitCommit^\" \"$gitCommit\"" if $gitCommit; return "HEAD" if $isGit;}sub diffCommand(@){ my @paths = @_; my $pathsString = "'" . join("' '", @paths) . "'"; my $command; if ($isSVN) { $command = "$SVN diff --diff-cmd diff -x -N $pathsString"; } elsif ($isGit) { $command = "$GIT diff --no-ext-diff -U0 " . diffFromToString(); $command .= " -- $pathsString" unless $gitCommit; } return $command;}sub statusCommand(@){ my @files = @_; my $filesString = "'" . join ("' '", @files) . "'"; my $command; if ($isSVN) { $command = "$SVN stat $filesString"; } elsif ($isGit) { $command = "$GIT diff -r --name-status -C -C -M " . diffFromToString(); $command .= " -- $filesString" unless $gitCommit; } return "$command 2>&1";}sub createPatchCommand($){ my ($changedFilesString) = @_; my $command; if ($isSVN) { $command = "'$FindBin::Bin/svn-create-patch' $changedFilesString"; } elsif ($isGit) { $command = "$GIT diff -C -C -M " . diffFromToString(); $command .= " -- $changedFilesString" unless $gitCommit; } return $command;}sub diffHeaderFormat(){ return qr/^Index: (\S+)$/ if $isSVN; return qr/^diff --git a\/.+ b\/(.+)$/ if $isGit;}sub findOriginalFileFromSvn($){ my ($file) = @_; my $baseUrl; open INFO, "$SVN info . |" or die; while (<INFO>) { if (/^URL: (.+)/) { $baseUrl = $1; last; } } close INFO; my $sourceFile; open INFO, "$SVN info '$file' |" or die; while (<INFO>) { if (/^Copied From URL: (.+)/) { $sourceFile = File::Spec->abs2rel($1, $baseUrl); last; } } close INFO; return $sourceFile;}sub generateFileList(\@\@\%){ my ($changedFiles, $conflictFiles, $functionLists) = @_; print STDERR " Running status to find changed, added, or removed files.\n"; open STAT, "-|", statusCommand(keys %paths) or die "The status failed: $!.\n"; my $inGitCommitSection = 0; while (<STAT>) { my $status; my $original; my $file; if ($isSVN) { if (/^([ACDMR]).{5} (.+)$/) { $status = $1; $file = $2; $original = findOriginalFileFromSvn($file) if substr($_, 3, 1) eq "+"; } else { print; # error output from svn stat } } elsif ($isGit) { if (/^([ADM])\t(.+)$/) { $status = $1; $file = $2; } elsif (/^([CR])[0-9]{1,3}\t([^\t]+)\t([^\t\n]+)$/) { # for example: R90% newfile oldfile $status = $1; $original = $2; $file = $3; } else { print; # error output from git diff } } next unless $status; $file = makeFilePathRelative($file); if (isModifiedStatus($status) || isAddedStatus($status)) { my @components = File::Spec->splitdir($file); if ($components[0] eq "LayoutTests") { $didChangeRegressionTests = 1; push @addedRegressionTests, $file if isAddedStatus($status) && $file =~ /\.([a-zA-Z]+)$/ && $supportedTestExtensions{lc($1)} && !scalar(grep(/^resources$/i, @components)); } push @{$changedFiles}, $file if $components[$#components] ne "ChangeLog"; } elsif (isConflictStatus($status)) { push @{$conflictFiles}, $file; } if (basename($file) ne "ChangeLog") { my $description = statusDescription($status, $original); $functionLists->{$file} = $description if defined $description; } } close STAT;}sub gitConfig($){ return unless $isGit; my ($config) = @_; my $result = `$GIT config $config`; if (($? >> 8) != 0) { $result = `$GIT repo-config $config`; } chomp $result; return $result;}sub isModifiedStatus($){ my ($status) = @_; my %statusCodes = ( "M" => 1, ); return $statusCodes{$status};}sub isAddedStatus($){ my ($status) = @_; my %statusCodes = ( "A" => 1, "C" => $isGit, "R" => 1, ); return $statusCodes{$status};}sub isConflictStatus($){ my ($status) = @_; my %svn = ( "C" => 1, ); my %git = ( "U" => 1, ); return 0 if $gitCommit; # an existing commit cannot have conflicts return $svn{$status} if $isSVN; return $git{$status} if $isGit;}sub statusDescription($$){ my ($status, $original) = @_; my %svn = ( "A" => defined $original ? " Copied from \%s." : " Added.", "D" => " Removed.", "M" => "", "R" => defined $original ? " Replaced with \%s." : " Replaced.", ); my %git = %svn; $git{"A"} = " Added."; $git{"C"} = " Copied from \%s."; $git{"R"} = " Renamed from \%s."; return sprintf($svn{$status}, $original) if $isSVN && exists $svn{$status}; return sprintf($git{$status}, $original) if $isGit && exists $git{$status}; return undef;}sub extractLineRange($){ my ($string) = @_; my ($start, $end) = (-1, -1); if ($isSVN && $string =~ /^\d+(,\d+)?[acd](\d+)(,(\d+))?/) { $start = $2; $end = $4 || $2; } elsif ($isGit && $string =~ /^@@ -\d+(,\d+)? \+(\d+)(,(\d+))? @@/) { $start = $2; $end = defined($4) ? $4 + $2 - 1 : $2; } return ($start, $end);}sub firstDirectoryOrCwd(){ my $dir = "."; my @dirs = keys(%paths); $dir = -d $dirs[0] ? $dirs[0] : dirname($dirs[0]) if @dirs; return $dir;}sub testListForChangeLog(@){ my (@tests) = @_; return "" unless @tests; my $leadString = " Test" . (@tests == 1 ? "" : "s") . ": "; my $list = $leadString; foreach my $i (0..$#tests) { $list .= " " x length($leadString) if $i; my $test = $tests[$i]; $test =~ s/^LayoutTests\///; $list .= "$test\n"; } $list .= "\n"; return $list;}sub reviewerAndDescriptionForGitCommit($){ my ($commit) = @_; my $description = ''; my $reviewer; my @args = qw(rev-list --pretty); push @args, '-1' if $commit !~ m/.+\.\..+/; my $gitLog; { local $/ = undef; open(GIT, "-|", $GIT, @args, $commit) || die; $gitLog = <GIT>; close(GIT); } my @commitLogs = split(/^[Cc]ommit [a-f0-9]{40}/m, $gitLog); shift @commitLogs; # Remove initial blank commit log my $commitLogCount = 0; foreach my $commitLog (@commitLogs) { $description .= "\n" if $commitLogCount; $commitLogCount++; my $inHeader = 1; my @lines = split(/\n/, $commitLog); shift @lines; # Remove initial blank line foreach my $line (@lines) { if ($inHeader) { if (!$line) { $inHeader = 0; } next; } elsif ($line =~ /[Ss]igned-[Oo]ff-[Bb]y: (.+)/) { if (!$reviewer) { $reviewer = $1; } else { $reviewer .= ", " . $1; } } elsif (length $line == 0) { $description = $description . "\n"; } else { $line =~ s/^\s*//; $description = $description . " " . $line . "\n"; } } } if (!$reviewer) { $reviewer = $gitReviewer; } return ($reviewer, $description);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -