문제

Windows에서 Subversion Server로 VisualSVN 서버를 설정했으며 WorksStations의 클라이언트로 Ankhsvn + TortoisesVN을 사용합니다.

커밋 메시지가 필요하지 않도록 서버를 어떻게 구성 할 수 있습니까?

도움이 되었습니까?

해결책

VisualSVN Server 3.9는 다음을 제공합니다 VisualSVNServerHooks.exe check-logmessage 비어 있거나 짧은 로그 메시지로 커밋을 거부하는 데 도움이되는 사전 커밋 후크. 기사를 참조하십시오 KB140 : VisualSVN 서버에서 커밋 로그 메시지를 확인합니다 지침.

내장 외에 VisualSVNServerHooks.exe, VisualSVN 서버 및 일반적으로 SVN을 사용합니다 후크 수 이와 같은 작업을 수행합니다.

  • start-commit - 커밋 거래가 시작되기 전에 실행되며 특별 권한 점검을 수행하는 데 사용할 수 있습니다.
  • pre-commit - 거래가 끝나지 않지만 커밋 전에 실행하십시오. 종종 제로 길이가 아닌 로그 메시지와 같은 것을 검증하는 데 사용됩니다.
  • post-commit - 거래가 저지른 후 실행됩니다. 이메일을 보내거나 리포지토리 백업에 사용할 수 있습니다.
  • pre-revprop-change - 개정 속성 변경 전에 실행됩니다. 권한을 확인하는 데 사용할 수 있습니다.
  • post-revprop-change - 개정 속성 변경 후에 실행됩니다. 이러한 변경 사항을 이메일로 보내거나 백업하는 데 사용할 수 있습니다.

당신은 그것을 사용해야합니다 pre-commit 훅. 플랫폼이 지원하는 모든 언어로 직접 쓸 수 있지만 웹에는 여러 스크립트가 있습니다. Googling "Svn Precommit 후크"의견이 필요합니다. "

다른 팁

이 질문을 해줘서 다행입니다. 이것은 공동으로 작성된 사전 커밋 후크 스크립트입니다 Windows 배치. 로그 메시지가 6 자 미만인 경우 커밋을 거부합니다. 그냥 넣으십시오 pre-commit.bat 후크 디렉토리에.

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

귀하의 질문에 대한 기술적 답변이 이미 제공되었습니다. 나는 사회적 대답을 추가하고 싶습니다. 나타내는 메시지 커밋 "

나는 "패치", "오타", "수정"또는 유사한 카운트를 잃어버린 많은 커밋 메시지를 보았습니다.

실제로 - 왜 필요한지 모두에게 분명하게하십시오.

이유에 대한 예는 다음과 같습니다.

  • Changenotes를 생성했습니다 (음 - 이것은 실제로 (내 이름으로) 공개적으로 눈에 띄는 것 - 팀을 위해서만)를 알면 좋은 메시지를 시행 할 수있는 좋은 자동 도구를 만들었습니다.
  • 라이센스 문제: 나중에 코드의 원점을 알아야 할 수도 있습니다. 예 : 라이센스를 코드로 변경 해야하는 경우 (일부 조직에는 커밋 메시지 형식 표준이 있어도 확인을 자동화 할 수 있지만 반드시 필요하지 않습니다. 가져 오기 좋은 이것으로 메시지를 커밋하십시오)
  • 다른 도구와 상호 운용성, 예를 들어 버전 제어와 인터페이스하고 커밋 메시지에서 정보를 추출하는 BugTrackers/문제 관리 시스템.

사전 커밋 후크에 대한 기술적 답변에 추가로 도움이되기를 바랍니다.

다음은 두 부분 샘플입니다 배치 + PowerShell 25 자 미만의 로그 메시지를 부정하는 사전 커밋 후크.

둘 다 넣으십시오 pre-commit.bat 그리고 pre-commit.ps1 저장소에 hooks 폴더, 예를 들어 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 {$_.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

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

Note 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을 살펴보십시오.

우리는 우수한 것을 사용합니다 CS 스크립트 우리가 개발하고있는 언어로 스크립트를 쓸 수 있도록 사전 커밋 후크 용 도구는 다음과 같습니다. 다음은 10 자 미만의 커밋 메시지가 있는지 확인하고 .suo 및 .user 파일이 확인되지 않도록하는 예입니다. in. 당신은 또한 탭/우주 들여 쓰기를 테스트하거나 체크인시 소규모 코드 표준 시행을 할 수 있지만, 커밋 속도를 늦추고 싶지 않기 때문에 스크립트를 너무 많이 만들도록 조심하십시오.

// 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 Shell JScript입니다.

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

읽기 쉽기 때문에 실험을 계속하십시오.

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

이 사전 커밋 후크를 Windows에서 사용하십시오. Windows 배치 및 사용으로 작성되었습니다 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에만 적용됩니다

저장소의 최상위 레벨을 마우스 오른쪽 버튼으로 클릭하십시오. 맥락에서 menu에서 tortoisesvn을 선택한 다음 속성을 선택 하여이 대화 상자를 볼 수 있습니다.

enter image description here

오른쪽 하단 근처의 새 버튼을 클릭하고 로그 크기를 선택하십시오. Commit and Lock에 필요한 문자 수를 입력하십시오 (아래 예제).

enter image description here

방금 수정 한 최상위 디렉토리에서 커밋을 수행하십시오. 이제 저장소는 변경 사항을 저지르기 전에 모든 사용자가 의견을 제시해야합니다.

서버에 커밋 후크를 추가하기 전에 SVNProps를 TortoisesVN 클라이언트에 배포했습니다.

따라서 대안으로 :

in tortoisesvn-> 속성 속성 이름 - 추가/세트 tsvn:logminsize 적절하게.

물론 클라이언트/사용자가 선택하지 않도록 선택할 수 있지만 원하는 경우 SVNProps 파일을 배포 할 수 있으므로 서버에 대한 보장은 아닙니다. 이런 식으로 사용자는 자신의 값을 설정할 필요가 없습니다. 모든 사용자에게 제공 할 수 있습니다.

이것은 BugTraq : 로그에서 문제 추적을 링크하기위한 설정과 같은 것들에도 적용됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top