Как требовать фиксации сообщений на сервере VisualSVN? - PullRequest
49 голосов
/ 29 октября 2008

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

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

Ответы [ 11 ]

65 голосов
/ 12 августа 2009

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

до 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
37 голосов
/ 29 октября 2008

VisualSVN Server 3.9 предоставляет хук предварительной фиксации VisualSVNServerHooks.exe check-logmessage, который помогает отклонять коммиты с пустыми или короткими сообщениями журнала. Инструкции см. В статье KB140: проверка сообщений журнала фиксации на сервере VisualSVN .

Помимо встроенного VisualSVNServerHooks.exe, VisualSVN Server и SVN в целом используют количество хуков для выполнения подобных задач.

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

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

17 голосов
/ 29 октября 2008

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

Я видел столько сообщений о коммите, в которых было указано "patch", "опечатка", "fix" или подобное, что я потерял счет.

Действительно - проясните всем, зачем они вам нужны.

Примеры по причинам:

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

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

6 голосов
/ 19 июля 2012

Вот пример из двух частей Batch + PowerShell ловушка предварительной фиксации, которая запрещает фиксацию сообщения журнала длиной менее 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.

4 голосов
/ 12 октября 2012

Мы используем отличный инструмент 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) {
  }
}
4 голосов
/ 30 октября 2008

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 вашего репозитория.

3 голосов
/ 27 апреля 2014

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

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

enter image description here

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

enter image description here

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

3 голосов
/ 04 августа 2009

Вот JScript оболочки Windows, который вы можете использовать, указав хук как:

%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, e.g. %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);
3 голосов
/ 20 февраля 2009

Используйте этот хук предварительной фиксации в 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 tools .

2 голосов
/ 19 декабря 2008

Прежде чем добавить хуки фиксации на мой сервер, я просто распространил svnprops среди клиентов TortoiseSVN.

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

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

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

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

...