Domanda

Abbiamo VisualSVN Server configurato come nostro server Subversion su Windows e utilizziamo Ankhsvn + TortoiseSVN come client sulle nostre workstation.

Come si può configurare il server per richiedere che i messaggi di commit non siano vuoti?

È stato utile?

Soluzione

VisualSVN Server 3.9 fornisce l'hook pre-commit VisualSVNServerHooks.exe check-logmessage che consente di rifiutare i commit con messaggi di registro vuoti o brevi. Vedere l'articolo KB140: convalida dei messaggi di log di commit nel server VisualSVN per istruzioni.

Oltre al incorporato VisualSVNServerHooks.exe , VisualSVN Server e SVN in generale utilizzano un numero di hook per eseguire attività come questa.

  • start-commit - eseguito prima dell'inizio della transazione di commit, può essere utilizzato per eseguire controlli speciali delle autorizzazioni
  • pre-commit : eseguito alla fine della transazione, ma prima del commit. Spesso utilizzato per convalidare elementi come un messaggio di registro di lunghezza diversa da zero.
  • post-commit : viene eseguito dopo il commit della transazione. Può essere utilizzato per l'invio di e-mail o il backup del repository.
  • pre-revprop-change : viene eseguito prima di una modifica della proprietà di revisione. Può essere utilizzato per controllare le autorizzazioni.
  • post-revprop-change : viene eseguito dopo una modifica della proprietà di revisione. Può essere utilizzato per inviare via email o eseguire il backup di queste modifiche.

Devi usare l'hook pre-commit . Puoi scriverlo tu stesso in quasi tutte le lingue supportate dalla tua piattaforma, ma sul Web sono disponibili numerosi script. Googling " svn hook precommit per richiedere commenti " Ho trovato una coppia che sembrava adatta al conto:

Altri suggerimenti

Sono contento che tu abbia posto questa domanda. Questo è il nostro script hook pre-commit scritto in comune Windows Batch . Nega il commit se il messaggio di registro è inferiore a 6 caratteri. Basta inserire pre-commit.bat nella directory dei hook.

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

Le risposte tecniche alla tua domanda sono già state fornite. Vorrei aggiungere la risposta social, che è: " Stabilendo gli standard dei messaggi di commit con il tuo team e convincendoli a concordare (o accettare) le ragioni per cui si dovrebbe avere bisogno di espressivo commit dei messaggi "

Ho visto così tanti messaggi di commit che dicevano "patch", "errore di battitura", "correzione" o simili che ho perso il conto.

Davvero: fai capire a tutti perché ne avresti bisogno.

Esempi di motivi sono:

  • Changenotes generati (beh - questo in realtà sarebbe un bel strumento automatico per imporre buoni messaggi se so che saranno (con il mio nome) visibili pubblicamente - se non altro per il team)
  • Problemi di licenza : potrebbe essere necessario conoscere l'origine del codice in un secondo momento, ad es. se vuoi cambiare la licenza per il tuo codice (alcune organizzazioni hanno persino standard per la formattazione dei messaggi di commit - beh, potresti automatizzare il controllo per questo, ma non avresti necessariamente ottenere buoni messaggi di commit con questo )
  • Interoperabilità con altri strumenti , ad es. bugtrackers / sistemi di gestione dei problemi che si interfacciano con il controllo della tua versione ed estraggono informazioni dai messaggi di commit.

Spero che possa essere d'aiuto, oltre alle risposte tecniche sugli hook di precommit.

Ecco un esempio di due parti Batch + PowerShell hook pre-commit che nega il commit di un messaggio di registro con meno di 25 caratteri.

Inserisci entrambi pre-commit.bat e pre-commit.ps1 nel tuo repository hooks , ad es C: \ repository \ repository \ ganci \

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 {

Ecco un esempio di due parti Batch + PowerShell hook pre-commit che nega il commit di un messaggio di registro con meno di 25 caratteri.

Inserisci entrambi pre-commit.bat e pre-commit.ps1 nel tuo repository hooks , ad es C: \ repository \ repository \ ganci \

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 è l'unico che può essere chiamato da VisualSVN e quindi pre-commit.ps1 è quello che viene chiamato da < code> pre-commit.bat .

Nota 2: pre-commit.bat può anche essere chiamato pre-commit.cmd .

Nota 3: se si verificano problemi di codifica con alcuni caratteri accentati e l'output [Console] :: Error.WriteLine , aggiungere ad esempio chcp 1252 in pre-commit.bat , riga successiva dopo @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 è l'unico che può essere chiamato da VisualSVN e quindi pre-commit.ps1 è quello che viene chiamato da < code> pre-commit.bat .

Nota 2: pre-commit.bat può anche essere chiamato pre-commit.cmd .

Nota 3: se si verificano problemi di codifica con alcuni caratteri accentati e l'output [Console] :: Error.WriteLine , aggiungere ad esempio chcp 1252 in pre-commit.bat , riga successiva dopo @echo off .

Ciò che VisualSVN ti offre di inserire come hook sono "script di comandi di Windows NT", che sono fondamentalmente file batch.

Scrivere if-then-else in file batch è molto brutto e probabilmente molto difficile da eseguire il debug.

Sarà simile al seguente (cerca pre-commit.bat) (non testato):

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 

È necessario un grep.exe sul percorso,% 1 è il percorso di questo repository,% 2 il nome del txn che sta per essere eseguito il commit. Dai anche un'occhiata a pre-commit.tmpl nella directory hooks del tuo repository.

Utilizziamo l'eccellente strumento CS-Script per i nostri hook pre-commit in modo da poter scrivere script in la lingua in cui stiamo sviluppando. Ecco un esempio che assicura che ci sia un messaggio di commit più lungo di 10 caratteri e che i file .suo e .user non siano archiviati. Puoi anche verificare i rientri di tabulazione / spazio, oppure fare applicazione di standard per piccoli codici al momento del check-in, ma fai attenzione a fare in modo che lo script non faccia troppo per non rallentare un 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("<*>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) {
  }
}

Ecco un JScript di Windows Shell che puoi usare specificando l'hook come:

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

È abbastanza facile da leggere, quindi vai avanti con un esperimento.

A proposito, il motivo per farlo in JScript è che non si basa su nessun altro strumento (Perl, CygWin, ecc.) da installare.

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

Usa questo hook pre-commit su Windows. È scritto in Windows Batch e utilizza l'utilità della riga di comando grep per verificare la lunghezza del commit.

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

Ricorda che avrai bisogno di una copia di grep, ti consiglio la gnu tools version .

Nota: questo vale solo per TortoiseSVN

Fai semplicemente clic con il pulsante destro del mouse sul livello superiore del tuo repository. Nel menu contestuale selezionare TortoiseSVN, quindi Proprietà, per visualizzare questa finestra di dialogo:

inserisci qui la descrizione dell

Fai clic sul pulsante Nuovo in basso a destra e seleziona Registra dimensioni. Immettere il numero di caratteri che si desidera richiedere per Commit e Lock (10 nell'esempio di seguito).

inserisci qui la descrizione dell

Esegui un commit dalla directory di livello superiore che hai appena modificato. Ora il tuo repository richiede a tutti gli utenti di commentare prima di apportare modifiche.

Prima di aggiungere hook di commit al mio server, ho appena distribuito svnprops ai client TortoiseSVN.

Quindi, in alternativa:

In TortoiseSVN - > Nome proprietà proprietà: aggiungi / imposta tsvn: logminsize in modo appropriato.

Questo ovviamente non è una garanzia sul server poiché i client / utenti possono scegliere di non farlo, ma è possibile distribuire i file svnprops se lo si desidera. In questo modo, gli utenti non devono impostare i propri valori: puoi fornirli a tutti gli utenti.

Funziona anche per cose come bugtraq: impostazioni per collegare elementi di tracciamento dei problemi nei registri.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top