Pregunta

Tenemos el servidor VisualSVN configurado como nuestro servidor Subversion en Windows, y usamos Ankhsvn + TortoiseSVN como clientes en nuestras estaciones de trabajo.

¿Cómo puede configurar el servidor para que requiera que los mensajes de confirmación no estén vacíos?

¿Fue útil?

Solución

VisualSVN Server 3.9 proporciona el VisualSVNServerHooks.exe check-logmessage enlace de confirmación previa que le ayuda a rechazar confirmaciones con mensajes de registro vacíos o cortos. Consulte el artículo KB140: Validación de mensajes de registro de confirmación en el servidor VisualSVN para obtener instrucciones.

Además del VisualSVNServerHooks.exe incorporado, VisualSVN Server y SVN en general utilizan un número de enlaces para realizar tareas como esta.

  • start-commit : se ejecuta antes de que comience la transacción de confirmación, se puede usar para hacer una verificación de permisos especial
  • pre-commit : se ejecuta al final de la transacción, pero antes de la confirmación. A menudo se usa para validar cosas como un mensaje de registro de longitud distinta de cero.
  • post-commit - se ejecuta después de que la transacción ha sido confirmada. Se puede usar para enviar correos electrónicos o hacer copias de seguridad del repositorio.
  • pre-revprop-change : se ejecuta antes de un cambio de propiedad de revisión. Puede usarse para verificar permisos.
  • post-revprop-change : se ejecuta después de un cambio de propiedad de revisión. Se puede usar para enviar por correo electrónico o hacer una copia de seguridad de estos cambios.

Debe usar el enlace pre-commit . Puede escribirlo usted mismo en casi cualquier idioma que admita su plataforma, pero hay varios scripts en la web. Googlear " svn precommit hook para requerir un comentario " Encontré una pareja que parecía que encajarían en la factura:

Otros consejos

Me alegra que hayas hecho esta pregunta. Este es nuestro script de enganche de pre-confirmación escrito en común Lote de Windows . Deniega el compromiso si el mensaje de registro tiene menos de 6 caracteres. Simplemente coloque el pre-commit.bat en su directorio de enlaces.

pre-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

Las respuestas técnicas a su pregunta ya se han dado. Me gustaría agregar la respuesta social, que es: "al establecer estándares de mensajes de compromiso con su equipo y hacer que acuerden (o acepten) razones por las cuales uno necesitaría mensajes de compromiso expresivos " / p>

He visto tantos mensajes de confirmación que decían "parche", "error tipográfico", "arreglar". O similar que he perdido la cuenta.

Realmente, deja en claro a todos por qué los necesitarías.

Ejemplos por razones son:

  • Changenotes generados (bueno, esto en realidad sería una buena herramienta automática para hacer cumplir buenos mensajes si sé que serán (con mi nombre) públicamente visibles, aunque solo sea para el equipo)
  • Problemas con la licencia : es posible que necesites saber el origen del código más adelante, por ejemplo. si desea cambiar la licencia a su código (algunas organizaciones incluso tienen estándares para el formato de mensajes de confirmación; bueno, podría automatizar la verificación para esto, pero no necesariamente obtendría mensajes de confirmación buenos con esto )
  • Interoperabilidad con otras herramientas , por ejemplo. Bugtrackers / sistemas de gestión de problemas que interactúan con su control de versión y extraen información de los mensajes de confirmación.

Espero que esto ayude, además de las respuestas técnicas sobre los ganchos de confirmación previa.

Aquí hay una muestra de dos partes Batch + PowerShell enlace de precompromiso que niega la confirmación de un mensaje de registro con menos de 25 caracteres.

Coloque pre-commit.bat y pre-commit.ps1 en su repositorio hooks , por ejemplo, C:\Repositories\repository\hooks\

pre-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 {

Aquí hay una muestra de dos partes Batch + PowerShell enlace de precompromiso que niega la confirmación de un mensaje de registro con menos de 25 caracteres.

Coloque pre-commit.bat y pre-commit.ps1 en su repositorio hooks , por ejemplo, C:\Repositories\repository\hooks\

pre-commit.ps1

@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%

pre-commit.bat

<*>

Nota 1: pre-commit.bat es el único que puede ser llamado por VisualSVN y luego pre-commit.ps1 es el que es llamado por < código> pre-commit.bat .

Nota 2: pre-commit.bat también puede llamarse pre-commit.cmd .

Nota 3: si experimenta problemas de codificación con algunos caracteres acentuados y la salida [Console] :: Error.WriteLine , agregue, por ejemplo, chcp 1252 en pre-commit.bat , siguiente línea después de @echo off .

.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

pre-commit.bat

<*>

Nota 1: pre-commit.bat es el único que puede ser llamado por VisualSVN y luego pre-commit.ps1 es el que es llamado por < código> pre-commit.bat .

Nota 2: pre-commit.bat también puede llamarse pre-commit.cmd .

Nota 3: si experimenta problemas de codificación con algunos caracteres acentuados y la salida [Console] :: Error.WriteLine , agregue, por ejemplo, chcp 1252 en pre-commit.bat , siguiente línea después de @echo off .

Lo que VisualSVN le ofrece para ingresar como ganchos son " secuencias de comandos de Windows NT " ;, que son básicamente archivos de proceso por lotes.

Escribir if-then-else en archivos por lotes es muy feo y probablemente muy difícil de depurar.

Se parecerá a lo siguiente (buscar pre-commit.bat) (no probado):

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 

Necesita un grep.exe en la ruta,% 1 es la ruta a este repositorio,% 2 el nombre del txn a punto de confirmarse. También eche un vistazo a pre-commit.tmpl en el directorio de ganchos de su repositorio.

Utilizamos la excelente herramienta CS-Script para nuestros ganchos de precompromiso para que podamos escribir scripts en el lenguaje en el que estamos desarrollando. Aquí hay un ejemplo que garantiza que haya un mensaje de confirmación de más de 10 caracteres, y que los archivos .suo y .user no estén registrados. También puede probar las sangrías de tabulación / espacio, o hacer código pequeño para hacer cumplir las normas en el check-in, pero tenga cuidado al hacer que su script haga demasiado, ya que no quiere ralentizar un compromiso.

// 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("<*>quot;);

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

Aquí hay un JScript de shell de Windows que puede usar especificando el enlace como:

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

Es bastante fácil de leer, así que adelante, haz un experimento.

Por cierto, la razón para hacer esto en JScript es que no depende de ninguna otra herramienta (Perl, CygWin, etc.) para instalarse.

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

Utilice este gancho de pre-confirmación en Windows. Está escrito en Windows Batch y utiliza la grep utilidad de línea de comandos para verificar la longitud de confirmación.

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

Recuerde que necesitará una copia de grep, le recomiendo la versión de herramientas gnu .

Nota: Esto solo se aplica a TortoiseSVN

Simplemente haga clic derecho en el nivel superior de su Repositorio. En el menú contextual, seleccione TortoiseSVN, luego Propiedades, para ver este diálogo:

ingrese la descripci&oacute;n de la imagen aqu&iacute;

Haga clic en el botón Nuevo cerca de la esquina inferior derecha y seleccione Tamaños de registro. Ingrese el número de caracteres que desea requerir para Confirmar y Bloquear (10 en el ejemplo a continuación).

ingrese la descripci&oacute;n de la imagen aqu&iacute;

Realice una confirmación desde el directorio de nivel superior que acaba de modificar. Ahora su repositorio requiere que todos los usuarios comenten antes de confirmar los cambios.

Antes de agregar enlaces de confirmación a mi servidor, acabo de distribuir svnprops a los clientes de TortoiseSVN.

Entonces, como alternativa:

En TortoiseSVN - > Nombre de propiedad de propiedades: agregue / establezca tsvn: logminsize adecuadamente.

Esto, por supuesto, no es una garantía para el servidor ya que los clientes / usuarios pueden optar por no hacerlo, pero puede distribuir archivos svnprops si lo desea. De esta forma, los usuarios no tienen que establecer sus propios valores: puede proporcionarlos a todos los usuarios.

Esto también funciona para cosas como bugtraq: configuraciones para vincular cosas de seguimiento de problemas en los registros.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top