Бывают случаи, когда я хочу быстро выполнить поиск в хранилище Subversion по автору, диапазону ревизий и / или сообщениям о фиксации. Кшиштоф Котович опубликовал в своем блоге сообщения журнала Grep Subversion с помощью svn-grep, который представляет svn-grep , скрипт bash, использующий инструментарий командной строки XML под названием xmlstarlet (xmlstarlet также доступен в Windows ). Сам по себе это довольно полезный скрипт, но он дал мне идею для скрипта на основе Groovy, который мог бы работать на нескольких (всех поддерживаемых JVM) платформах.
searchSvnLog.groovy
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
#!/usr/bin/env groovy // // searchSvnLog.groovy // def cli = new CliBuilder( usage: 'searchSvnLog.groovy -r <revision1> -p <revision2> -a <author> -s <stringInMessage>' ) import org.apache.commons.cli.Option cli.with { h(longOpt: 'help' , 'Usage Information' , required: false ) r(longOpt: 'revision1' , 'First SVN Revision' , args: 1 , required: false ) p(longOpt: 'revision2' , 'Last SVN Revision' , args: 1 , required: false ) a(longOpt: 'author' , 'Revision Author' , args: 1 , required: false ) s(longOpt: 'search' , 'Search String' , args: 1 , required: false ) t(longOpt: 'target' , 'SVN target directory/URL' , args: 1 , required: true ) } def opt = cli.parse(args) if (!opt) return if (opt.h) cli.usage() Integer revision1 = opt.r ? (opt.r as int ) : null Integer revision2 = opt.p ? (opt.p as int ) : null if (revision1 != null && revision2 != null && revision1 > revision2) { println "It makes no sense to search for revisions ${revision1} through ${revision2}." System.exit(- 1 ) } String author = opt.a ? (opt.a as String) : null String search = opt.s ? (opt.s as String) : null String logTarget = opt.t String command = "svn log -r ${revision1 ?: 1} ${revision2 ?: 'HEAD'} ${logTarget} --xml" def proc = command.execute() StringBuilder standard = new StringBuilder() StringBuilder error = new StringBuilder() proc.waitForProcessOutput(standard, error) def returnedCode = proc.exitValue() if (returnedCode != 0 ) { println "ERROR: Returned code ${returnedCode}" } def xmlLogOutput = standard.toString() def log = new XmlSlurper().parseText(xmlLogOutput) def logEntries = new TreeMap<Integer, LogEntry>() log.logentry.each { svnLogEntry -> Integer logRevision = Integer.valueOf(svnLogEntry. @revision as String) String message = svnLogEntry.msg as String String entryAuthor = svnLogEntry.author as String if ( (!revision1 || revision1 <= logRevision) && (!revision2 || revision2 >= logRevision) && (!author || author == entryAuthor) && (!search || message.toLowerCase().contains(search.toLowerCase())) ) { def logEntry = new LogEntry(logRevision, svnLogEntry.author as String, svnLogEntry.date as String, message) logEntries.put(logRevision, logEntry) } } logEntries.each { logEntryRevisionId, logEntry -> println "${logEntryRevisionId} : ${logEntry.author}/${logEntry.date} : ${logEntry.message}" } |
Одна вещь, которая делает этот сценарий намного проще для написания, это возможность команды журнала Subversion записывать свои выходные данные в формате XML с флагом –xml . Хотя XML в последние годы подвергался серьезной критике, одной из вещей, которые мне понравились в его доступности, является широко распространенная поддержка инструментов для написания и чтения XML. Способность Subversion писать определенные типы вывода в XML является хорошим примером этого. Без XML скрипту потребовалось бы написать собственный код синтаксического анализа для анализа нестандартного вывода журнала SVN. Поскольку Subversion поддерживает запись в стандартный формат XML для своего вывода, любой инструмент, поддерживающий XML, может прочитать его. В этом случае я использовал невероятно простую возможность Groovy XML slurping (XML-синтаксический анализ).
В сценарии также используется расширенный класс Process (GDK) Groovy, как я кратко описал в своем недавнем посте «Великолепная простота написания сценариев с помощью Groovy» .
Встроенная в Groovy поддержка командной строки ( CliBuilder ) используется в сценарии для принятия параметров для сужения поиска (таких как применимые ревизии, авторы, которые зафиксировали, или строки для поиска комментариев фиксации). Одним обязательным параметром является «target», который может быть файлом, каталогом или URL-адресом.
Сценарий ссылается на класс Groovy с именем LogEntry, и список кода для этого класса показан далее.
LogEntry.groovy
1
2
3
4
5
6
7
8
|
@groovy .transform.Canonical class LogEntry { int revision String author String date String message } |
Этот простой на вид класс LogEntry
гораздо мощнее, чем может показаться на первый взгляд. Поскольку это Groovy, для четырех атрибутов автоматически доступны методы установки / получения. Благодаря аннотации @Canonical он также поддерживает методы конструктора, equals , hashCode и toString . Другими словами, этот класс, состоящий из менее десяти строк, имеет методы доступа и мутатора, а также методы общего класса, переопределенные для него соответствующим образом.
Вывод
Groovy предлагает множество функций, облегчающих написание сценариев. В этом посте я использовал пример «поиска» --xml
Subversion с помощью команды log
Subversion (и его опции --xml
), чтобы продемонстрировать некоторые из этих полезных функций сценариев Groovy (синтаксический анализ параметров командной строки, встроенная интеграция с операционной системой и простой XML разбор). Кроме того, были использованы некоторые из приятных синтаксических преимуществ Groovy (замыкания, динамическая типизация, заполнители значений GString).