Как требовать фиксацию сообщений на сервере VisualSVN?

У нас есть VisualSVN Server, настроенный как наш сервер Subversion в Windows, и мы используем Ankhsvn + TortoiseSVN в качестве клиентов на наших рабочих станциях.

Как настроить сервер, чтобы требовать, чтобы сообщения фиксации были не пустыми?

Solutions Collecting From Web of "Как требовать фиксацию сообщений на сервере VisualSVN?"

SVN использует несколько крючков для выполнения таких задач.

  • start-commit run перед началом транзакции фиксации, может использоваться для специальных проверок разрешения
  • pre-commit – выполняется в конце транзакции, но до фиксации. Часто используется для проверки таких вещей, как сообщение журнала с нулевой длиной.
  • post-commit – выполняется после совершения транзакции. Может использоваться для отправки электронной почты или резервного хранилища.
  • pre-revprop-change – выполняется до изменения свойства ревизии. Может использоваться для проверки разрешений.
  • post-revprop-change – выполняется после изменения свойства ревизии. Может использоваться для электронной почты или резервного копирования этих изменений.

Вы должны использовать крюк pre-commit . Вы можете написать это самостоятельно на любом языке, поддерживаемом платформой, но в Интернете есть несколько сценариев. Googling "svn precommit hook, чтобы потребовать комментарий:« Я нашел пару, которая выглядела так, как будто она соответствовала бы счету:

  • Скрипт Perl
  • Скрипт Python

Я рад, что вы задали этот вопрос. Это наш скрипт для предварительного фиксации, написанный в общем пакете Windows . Он отклоняет фиксацию, если сообщение журнала меньше 6 символов. Просто поставьте pre-commit.bat в свой каталог hooks.

предварительно commit.bat

 setlocal enabledelayedexpansion set REPOS=%1 set TXN=%2 set SVNLOOK="%VISUALSVN_SERVER%\bin\svnlook.exe" SET M= REM Concatenate all the lines in the commit message FOR /F "usebackq delims==" %%g IN (`%SVNLOOK% log -t %TXN% %REPOS%`) DO SET M=!M!%%g REM Make sure M is defined SET M=0%M% REM Here the 6 is the length we require IF NOT "%M:~6,1%"=="" goto NORMAL_EXIT :ERROR_TOO_SHORT echo "Commit note must be at least 6 letters" >&2 goto ERROR_EXIT :ERROR_EXIT exit /b 1 REM All checks passed, so allow the commit. :NORMAL_EXIT exit 0 

Технические ответы на ваш вопрос уже даны. Я бы хотел добавить социальный ответ, который заключается в следующем: «Установив стандарты коммитов в вашей команде и получив их, чтобы согласиться (или принять) причины, по которым нужно было бы выразить выразительные сообщения о совершении сделки,

Я видел так много сообщений фиксации, которые говорили «патч», «опечатка», «исправить» или подобное, что я потерял счет.

На самом деле – дайте понять всем, почему они вам понадобятся.

Примерами причин являются:

  • Сгенерированные Changenotes (хорошо – это действительно хороший инструмент для обеспечения хороших сообщений, если я знаю, что они будут (с моим именем) публично видны – если только для команды)
  • Проблемы с лицензией . Возможно, вам потребуется узнать происхождение кода позже, например, если вы хотите изменить лицензию на свой код (у некоторых организаций даже есть стандарты для фиксации форматирования сообщений – ну, вы могли бы автоматизировать проверку для этого, но вы бы не обязательно получать хорошие сообщения с этим сообщением)
  • Взаимодействие с другими инструментами , например системами bugtrackers / управления выпуском, которые взаимодействуют с вашим контролем версий и извлекают информацию из сообщений фиксации.

Надеюсь, что это поможет, а также технические ответы на предвоенные крючки.

Вот пример двухкомпонентного пакета Batch + PowerShell pre-commit, который отрицает фиксацию сообщения журнала длиной менее 25 символов.

Поместите оба pre-commit.bat и pre-commit.ps1 в папку с вашими hooks репозитория, например C:\Repositories\repository\hooks\

предварительно commit.ps1

 # Store hook arguments into variables with mnemonic names $repos = $args[0] $txn = $args[1] # Build path to svnlook.exe $svnlook = "$env:VISUALSVN_SERVER\bin\svnlook.exe" # Get the commit log message $log = (&"$svnlook" log -t $txn $repos) # Check the log message contains non-empty string $datalines = ($log | where {$_.trim() -ne ""}) if ($datalines.length -lt 25) { # Log message is empty. Show the error. [Console]::Error.WriteLine("Commit with empty log message is prohibited.") exit 3 } exit 0 

предварительно commit.bat

 @echo off set PWSH=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe %PWSH% -command $input ^| %1\hooks\pre-commit.ps1 %1 %2 if errorlevel 1 exit %errorlevel% 

Примечание 1: pre-commit.bat является единственным, который может быть вызван VisualSVN, а затем pre-commit.ps1 – это тот, который вызывается pre-commit.bat .

Примечание 2: pre-commit.bat также может быть названо pre-commit.cmd .

Примечание 3: Если вы экспериментируете с проблемами с некоторыми символами с акцентом и выводом [Console]::Error.WriteLine , добавьте, например, chcp 1252 в pre-commit.bat , следующую строку после @echo off .

Что VisualSVN предлагает вам ввести в качестве перехватчиков, это «командные сценарии Windows NT», которые в основном представляют собой пакетные файлы.

Запись if-then-else в пакетных файлах очень уродлива и, вероятно, очень трудно отлаживать.

Он будет выглядеть примерно следующим образом (поиск pre-commit.bat) (не тестировался):

 SVNLOOK.exe log -t "%2" "%1" | grep.exe "[a-zA-Z0-9]" > nul || GOTO ERROR GOTO OK :ERROR ECHO "Please enter comment and then retry commit!" exit 1 :OK exit 0 

Вам нужен grep.exe на пути,% 1 – путь к этому репозиторию,% 2 – имя txn, которое должно быть зафиксировано. Также посмотрите на pre-commit.tmpl в каталоге hooks вашего репозитория.

Мы используем отличный инструмент CS-Script для наших крючков предварительной фиксации, чтобы мы могли писать сценарии на языке, на котором мы разрабатываем. Вот пример, который обеспечивает сообщение фиксации длиной более 10 символов и гарантирует, что .suo и .user файлы не проверяются. Вы также можете проверить наличие отступов в таблице / пробелах или выполнять небольшие требования к стандартам кода при регистрации, но будьте осторожны, чтобы сделать ваш скрипт слишком много, так как вы не хотите замедлять фиксацию ,

 // run from pre-commit.cmd like so: // css.exe /nl /c C:\SVN\Scripts\PreCommit.cs %1 %2 using System; using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; using System.Linq; class PreCommitCS { /// <summary>Controls the procedure flow of this script</summary> public static int Main(string[] args) { if (args.Length < 2) { Console.WriteLine("usage: PreCommit.cs repository-path svn-transaction"); Environment.Exit(2); } try { var proc = new PreCommitCS(args[0], args[1]); proc.RunChecks(); if (proc.MessageBuffer.ToString().Length > 0) { throw new CommitException(String.Format("Pre-commit hook violation\r\n{0}", proc.MessageBuffer.ToString())); } } catch (CommitException ex) { Console.WriteLine(ex.Message); Console.Error.WriteLine(ex.Message); throw ex; } catch (Exception ex) { var message = String.Format("SCRIPT ERROR! : {1}{0}{2}", "\r\n", ex.Message, ex.StackTrace.ToString()); Console.WriteLine(message); Console.Error.WriteLine(message); throw ex; } // return success if we didn't throw return 0; } public string RepoPath { get; set; } public string SvnTx { get; set; } public StringBuilder MessageBuffer { get; set; } /// <summary>Constructor</summary> public PreCommitCS(string repoPath, string svnTx) { this.RepoPath = repoPath; this.SvnTx = svnTx; this.MessageBuffer = new StringBuilder(); } /// <summary>Main logic controller</summary> public void RunChecks() { CheckCommitMessageLength(10); // Uncomment for indent checks /* string[] changedFiles = GetCommitFiles( new string[] { "A", "U" }, new string[] { "*.cs", "*.vb", "*.xml", "*.config", "*.vbhtml", "*.cshtml", "*.as?x" }, new string[] { "*.designer.*", "*.generated.*" } ); EnsureTabIndents(changedFiles); */ CheckForIllegalFileCommits(new string[] {"*.suo", "*.user"}); } private void CheckForIllegalFileCommits(string[] filesToExclude) { string[] illegalFiles = GetCommitFiles( new string[] { "A", "U" }, filesToExclude, new string[] {} ); if (illegalFiles.Length > 0) { Echo(String.Format("You cannot commit the following files: {0}", String.Join(",", illegalFiles))); } } private void EnsureTabIndents(string[] filesToCheck) { foreach (string fileName in filesToCheck) { string contents = GetFileContents(fileName); string[] lines = contents.Replace("\r\n", "\n").Replace("\r", "\n").Split(new string[] { "\n" }, StringSplitOptions.None); var linesWithSpaceIndents = Enumerable.Range(0, lines.Length) .Where(i => lines[i].StartsWith(" ")) .Select(i => i + 1) .Take(11) .ToList(); if (linesWithSpaceIndents.Count > 0) { var message = String.Format("{0} has spaces for indents on line(s): {1}", fileName, String.Join(",", linesWithSpaceIndents)); if (linesWithSpaceIndents.Count > 10) message += "..."; Echo(message); } } } private string GetFileContents(string fileName) { string args = GetSvnLookCommandArgs("cat") + " \"" + fileName + "\""; string svnlookResults = ExecCmd("svnlook", args); return svnlookResults; } private void CheckCommitMessageLength(int minLength) { string args = GetSvnLookCommandArgs("log"); string svnlookResults = ExecCmd("svnlook", args); svnlookResults = (svnlookResults ?? "").Trim(); if (svnlookResults.Length < minLength) { if (svnlookResults.Length > 0) { Echo("Your commit message was too short."); } Echo("Please describe the changes you've made in a commit message in order to successfully commit. Include support ticket number if relevant."); } } private string[] GetCommitFiles(string[] changedIds, string[] includedFiles, string[] exclusions) { string args = GetSvnLookCommandArgs("changed"); string svnlookResults = ExecCmd("svnlook", args); string[] lines = svnlookResults.Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); var includedPatterns = (from a in includedFiles select ConvertWildcardPatternToRegex(a)).ToArray(); var excludedPatterns = (from a in exclusions select ConvertWildcardPatternToRegex(a)).ToArray(); var opts = RegexOptions.IgnoreCase; var results = from line in lines let fileName = line.Substring(1).Trim() let changeId = line.Substring(0, 1).ToUpper() where changedIds.Any(x => x.ToUpper() == changeId) && includedPatterns.Any(x => Regex.IsMatch(fileName, x, opts)) && !excludedPatterns.Any(x => Regex.IsMatch(fileName, x, opts)) select fileName; return results.ToArray(); } private string GetSvnLookCommandArgs(string cmdType) { string args = String.Format("{0} -t {1} \"{2}\"", cmdType, this.SvnTx, this.RepoPath); return args; } /// <summary> /// Executes a command line call and returns the output from stdout. /// Raises an error is stderr has any output. /// </summary> private string ExecCmd(string command, string args) { Process proc = new Process(); proc.StartInfo.FileName = command; proc.StartInfo.Arguments = args; proc.StartInfo.UseShellExecute = false; proc.StartInfo.CreateNoWindow = true; proc.StartInfo.RedirectStandardOutput = true; proc.StartInfo.RedirectStandardError = true; proc.Start(); var stdOut = proc.StandardOutput.ReadToEnd(); var stdErr = proc.StandardError.ReadToEnd(); proc.WaitForExit(); // Do after ReadToEnd() call per: http://chrfalch.blogspot.com/2008/08/processwaitforexit-never-completes.html if (!string.IsNullOrWhiteSpace(stdErr)) { throw new Exception(string.Format("Error: {0}", stdErr)); } return stdOut; } /// <summary> /// Writes the string provided to the Message Buffer - this fails /// the commit and this message is presented to the comitter. /// </summary> private void Echo(object s) { this.MessageBuffer.AppendLine((s == null ? "" : s.ToString())); } /// <summary> /// Takes a wildcard pattern (like *.bat) and converts it to the equivalent RegEx pattern /// </summary> /// <param name="wildcardPattern">The wildcard pattern to convert. Syntax similar to VB's Like operator with the addition of pipe ("|") delimited patterns.</param> /// <returns>A regex pattern that is equivalent to the wildcard pattern supplied</returns> private string ConvertWildcardPatternToRegex(string wildcardPattern) { if (string.IsNullOrEmpty(wildcardPattern)) return ""; // Split on pipe string[] patternParts = wildcardPattern.Split('|'); // Turn into regex pattern that will match the whole string with ^$ StringBuilder patternBuilder = new StringBuilder(); bool firstPass = true; patternBuilder.Append("^"); foreach (string part in patternParts) { string rePattern = Regex.Escape(part); // add support for ?, #, *, [...], and [!...] rePattern = rePattern.Replace("\\[!", "[^"); rePattern = rePattern.Replace("\\[", "["); rePattern = rePattern.Replace("\\]", "]"); rePattern = rePattern.Replace("\\?", "."); rePattern = rePattern.Replace("\\*", ".*"); rePattern = rePattern.Replace("\\#", "\\d"); if (firstPass) { firstPass = false; } else { patternBuilder.Append("|"); } patternBuilder.Append("("); patternBuilder.Append(rePattern); patternBuilder.Append(")"); } patternBuilder.Append("$"); string result = patternBuilder.ToString(); if (!IsValidRegexPattern(result)) { throw new ArgumentException(string.Format("Invalid pattern: {0}", wildcardPattern)); } return result; } private bool IsValidRegexPattern(string pattern) { bool result = true; try { new Regex(pattern); } catch { result = false; } return result; } } public class CommitException : Exception { public CommitException(string message) : base(message) { } } 

Вот сценарий Windows JScript, который можно использовать, указав крюк как:

 %SystemRoot%\System32\CScript.exe //nologo <..path..to..script> %1 %2 

Это довольно легко читать, поэтому экспериментируйте.

Кстати, причина для этого в JScript заключается в том, что он не полагается на какие-либо другие инструменты (Perl, CygWin и т. Д.), Которые будут установлены.

 if (WScript.Arguments.Length < 2) { WScript.StdErr.WriteLine("Repository Hook Error: Missing parameters. Should be REPOS_PATH then TXN_NAME, eg %1 %2 in pre-commit hook"); WScript.Quit(-1); } var oShell = new ActiveXObject("WScript.Shell"); var oFSO = new ActiveXObject("Scripting.FileSystemObject"); var preCommitStdOut = oShell.ExpandEnvironmentStrings("%TEMP%\\PRE-COMMIT." + WScript.Arguments(1) + ".stdout"); var preCommitStdErr = oShell.ExpandEnvironmentStrings("%TEMP%\\PRE-COMMIT." + WScript.Arguments(1) + ".stderr"); var commandLine = "%COMSPEC% /C \"C:\\Program Files\\VisualSVN Server\\bin\\SVNLook.exe\" log -t "; commandLine += WScript.Arguments(1); commandLine += " "; commandLine += WScript.Arguments(0); commandLine += "> " + preCommitStdOut + " 2> " + preCommitStdErr; // Run Synchronously, don't show a window // WScript.Echo("About to run: " + commandLine); var exitCode = oShell.Run(commandLine, 0, true); var fsOUT = oFSO.GetFile(preCommitStdOut).OpenAsTextStream(1); var fsERR = oFSO.GetFile(preCommitStdErr).OpenAsTextStream(1); var stdout = fsOUT && !fsOUT.AtEndOfStream ? fsOUT.ReadAll() : ""; var stderr = fsERR && !fsERR.AtEndOfStream ? fsERR.ReadAll() : ""; if (stderr.length > 0) { WScript.StdErr.WriteLine("Error with SVNLook: " + stderr); WScript.Quit(-2); } // To catch naught commiters who write 'blah' as their commit message if (stdout.length < 5) { WScript.StdErr.WriteLine("Please provide a commit message that describes why you've made these changes."); WScript.Quit(-3); } WScript.Quit(0); 

Используйте этот крюк для фиксации в Windows. Он написан в Windows Batch и использует утилиту командной строки grep для проверки длины фиксации.

 svnlook log -t "%2" "%1" | c:\tools\grep -c "[a-zA-z0-9]" > nul if %ERRORLEVEL% NEQ 1 exit 0 echo Please enter a check-in comment 1>&2 exit 1 

Помните, что вам понадобится копия grep, я рекомендую версию инструментов gnu .

Примечание. Это относится только к TortoiseSVN

Просто щелкните правой кнопкой мыши верхний уровень вашего репозитория. В контекстном меню выберите TortoiseSVN, затем «Свойства», чтобы увидеть это диалоговое окно:

введите описание изображения здесь

Нажмите кнопку «Создать» в правом нижнем углу и выберите «Размеры журнала». Введите количество символов, которое требуется для фиксации и блокировки (10 в примере ниже).

введите описание изображения здесь

Сделайте Commit из каталога верхнего уровня, который вы только что изменили. Теперь ваш репозиторий требует от всех пользователей комментариев перед фиксацией изменений.

Я считаю, что вам придется настроить крюк pre-commit, который будет проверять сообщение.

На самом деле, просто по первому результату, который я получил, был perl pre-commit сценарий, чтобы сделать именно то, что вы намеревались.

Пример перехвата Perl (непроверенный)

Перед добавлением привязок к моему серверу я просто передал svnprops клиентам TortoiseSVN.

Итак, в качестве альтернативы:

В TortoiseSVN -> Свойства имя свойства – добавить / установить tsvn:logminsize соответственно.

Это, конечно же, не является гарантией на сервере, так как клиенты / пользователи могут отказаться от этого, но вы можете распространять файлы svnprops, если хотите. Таким образом, пользователям не нужно устанавливать свои собственные значения – вы можете предоставить их всем пользователям.

Это также работает для таких вещей, как bugtraq: настройки для связывания материала отслеживания проблем в журналах.