Pergunta

Temos VisualSVN servidor configurado como o nosso servidor Subversion no Windows, e usamos AnkhSVN + TortoiseSVN como clientes em nossas estações de trabalho.

Como você pode configurar o servidor para exigir mensagens de confirmação a ser não-vazia?

Foi útil?

Solução

VisualSVN servidor 3.9 fornece a VisualSVNServerHooks.exe check-logmessage pré-commit hook que ajuda você a rejeitar submissões com mensagens de registo vazias ou curtas. Veja o artigo KB140:. Validando as mensagens de log em VisualSVN servidor para obter instruções

Além do um número embutido VisualSVNServerHooks.exe, VisualSVN Server e SVN em usos gerais de ganchos para realizar tarefas como esta.

  • start-commit - executado antes de confirmar a transação começa, pode ser usado para fazer a verificação especial permissão
  • pre-commit - executado no final da transação, mas antes de cometer. Muitas vezes usado para coisas validar tais como uma mensagem de log não comprimento zero.
  • post-commit - é executado depois que a transação foi comprometido. Pode ser usado para enviar e-mails, ou fazer backup de repositório.
  • pre-revprop-change - executado antes de uma revisão alteração da propriedade. Pode ser usado para verificar as permissões.
  • post-revprop-change - corre atrás de uma revisão alteração da propriedade. Pode ser usado para enviar e-mail ou backup essas mudanças.

Você precisa usar o gancho pre-commit. Você pode escrever-se em apenas cerca de qualquer linguagem seus suportes de plataforma, mas há uma série de scripts na web. Pesquisando "svn precommit gancho para exigir comentário" Encontrei um casal que parecia que caberia a conta:

Outras dicas

Estou feliz que você fez esta pergunta. Este é o nosso roteiro pré-commit gancho escrito em comum o Windows Batch . Ele nega comprometer se a mensagem de log é menos de 6 caracteres. Basta colocar o pré commit.bat- para seus ganchos diretório.

pré-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

As respostas técnicas a sua pergunta já foram dadas. Eu gostaria de adicionar a resposta social, que é: "Ao estabelecer cometer normas de mensagem com sua equipe e levá-los a concordar (ou aceitar) razões pelas quais seria necessário expressiva cometer mensagens"

Eu vi tantos cometer mensagens que diziam "patch", "erro de digitação", "correção" ou similar que eu tenho contagem perdida.

Realmente -. Deixe claro para todo mundo por que você precisa deles

Exemplos de razões são:

  • Gerado Changenotes (bem - this'd realmente fazer uma ferramenta automática agradável para impor boas mensagens se eu sei que eles vão estar (com o meu nome) publicamente visível - mesmo que apenas para a equipe)
  • questões Licença : Você pode precisar de saber a origem do código mais tarde, por exemplo, se você quiser alterar a licença para o seu código (Algumas organizações ainda têm padrões para cometer mensagem formatação - bem, você pode automatizar a verificação para isso, mas você não seria necessariamente entrar boa cometer mensagens com este )
  • A interoperabilidade com outras ferramentas , por exemplo, bugtrackers / sistemas de gestão questão que fazem interface com o controle de versão e informações extrato do mensagens cometer.

Espero que ajude, além de as respostas técnicas sobre ganchos precommit.

Aqui está um exemplo de duas partes Batch + PowerShell pré-commit hook que nega cometer uma mensagem de log com menos de 25 caracteres.

Coloque tanto pre-commit.bat e pre-commit.ps1 em seu repositório hooks pasta, por exemplo, C:\Repositories\repository\hooks\

pré-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

pré-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%

Nota 1:. pre-commit.bat é o único que pode ser chamado por VisualSVN e depois pre-commit.ps1 é aquele que é chamado por pre-commit.bat

Nota 2:. pre-commit.bat também pode ser chamado pre-commit.cmd

Nota 3:. Se você experimentar codificação problemas com alguns caracteres acentuados e a saída [Console]::Error.WriteLine, em seguida, adicione por exemplo chcp 1252 em pre-commit.bat, próxima linha após @echo off

oferece o que visualsvn você para entrar como ganchos são "scripts de comando do Windows NT", que são basicamente arquivos em lote.

Escrita if-then-else em arquivos em lote é muito feio e provavelmente muito difícil para depurar.

Ele será parecido com o seguinte (procurar pré-commit.bat) (não testado):

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 

Você precisa de um grep.exe no caminho,% 1 é o caminho para este repositório,% 2 o nome do txn prestes a ser cometido. Também ter um olhar para o pré-commit.tmpl no diretório ganchos de seu repositório.

Nós usamos o excelente CS-Script para nossos pré-commit ganchos para que possamos escrever scripts em a linguagem que estamos fazendo desenvolvimento. Aqui está um exemplo que garante que há uma mensagem de commit mais de 10 caracteres, e assegura que .suo e arquivos .user não são verificados. Você também pode teste para travessões guia / espaço, ou fazer pequena padrões execução de código no check-in, mas tome cuidado para fazer o seu script não muito como você não quer abrandar um commit.

// 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) {
  }
}

Aqui está um Windows Shell JScript que você pode usar, especificando o gancho como:

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

É muito fácil de ler, então vá em frente um experimento.

BTW, a razão de fazer isso em JScript é que ele não depende de quaisquer outras ferramentas (Perl, Cygwin, etc.) a serem instalados.

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);

Use este pre-commit gancho no Windows. Está escrito no Windows Batch e usa grep de linha de comando para verificar o comprimento cometer.

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

Lembre-se que você vai precisar de uma cópia do grep, eu recomendo o gnu ferramentas versão .

Nota: Isso se aplica somente TortoiseSVN

Basta clicar com o botão direito no nível superior de seu repositório. No menu de contexto, selecione TortoiseSVN, em seguida, Propriedades, para ver este diálogo:

enter descri&ccedil;&atilde;o da imagem aqui

Clique no botão New perto do canto inferior direito e selecione Tamanhos Entrar. Digite o número de caracteres que você deseja exigir para Commit e Lock (10 no exemplo abaixo).

enter descri&ccedil;&atilde;o da imagem aqui

Faça um Commit do diretório superior Nível tenha modificado. Agora seu repositório exige que todos os usuários a comentar antes de cometer alterações.

Antes de adicionar cometer ganchos para o meu servidor, eu só svnprops distribuído aos clientes TortoiseSVN.

Assim, como uma alternativa:

Em TortoiseSVN -> Propriedades nome da propriedade -. Add / set tsvn:logminsize adequadamente

Isto, obviamente, não é garantia no servidor como clientes / usuários podem optar por não fazê-lo, mas você pode distribuir arquivos svnprops se quiser. Desta forma, os usuários não tem que definir os seus próprios valores -. Você pode fornecê-los a todos os usuários

Isso também funciona para coisas como bugtraq:. Definições para emissão de rastreamento de link material nos logs

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top