2010-03-26 28 views
23

Đến từ CVS, chúng tôi có chính sách cam kết thư phải được gắn thẻ với số lỗi (hậu tố đơn giản "... [9999]"). Một kịch bản CVS kiểm tra điều này trong các cam kết và từ chối cam kết nếu thông điệp không phù hợp.Làm cách nào để chúng tôi xác minh thư cam kết để thúc đẩy?

Lệnh git hook commit-msg thực hiện điều này ở phía nhà phát triển nhưng chúng tôi thấy hữu ích khi có hệ thống tự động kiểm tra và nhắc nhở chúng tôi về điều này.

Trong khi nhấn git, thông báo cam kết không chạy. Có móc khác trong khi đẩy có thể kiểm tra tin nhắn cam kết không?

Làm cách nào để xác minh thư cam kết trong khi nhấn git?

Trả lời

23

Sử dụng móc cập nhật

Bạn biết về móc - vui lòng, đọc documentation về họ! Móc bạn có thể muốn là cập nhật, được chạy một lần cho mỗi ref. (Các móc trước khi nhận được chạy một lần cho toàn bộ đẩy) Có tấn và tấn câu hỏi và câu trả lời về các móc đã có trên SO; tùy thuộc vào những gì bạn muốn làm, bạn có thể có thể tìm thấy hướng dẫn về cách viết móc nếu bạn cần.

Để nhấn mạnh rằng điều này thực sự là có thể, một trích dẫn từ các tài liệu:

móc này có thể được sử dụng để ngăn chặn bản cập nhật bắt buộc trên refs nhất định bằng cách đảm bảo rằng tên đối tượng là một đối tượng cam kết đó là một con cháu của đối tượng commit được đặt tên bởi tên đối tượng cũ. Nghĩa là, để thực thi chính sách "chỉ chuyển tiếp nhanh".

Nó cũng có thể được sử dụng để đăng nhập trạng thái cũ..new.

Và các chi tiết cụ:

Các móc thực hiện một lần cho mỗi ref phải được cập nhật, và có ba thông số:

  • tên của ref đang được cập nhật,
  • sự tên đối tượng cũ được lưu trữ trong ref,
  • và tên đối tượng mới sẽ được lưu trữ trong ref.

Vì vậy, ví dụ, nếu bạn muốn chắc chắn rằng không ai trong số các cam kết đối tượng dài hơn 80 ký tự, một thực hiện rất thô sơ sẽ là:

#!/bin/bash 
long_subject=$(git log --pretty=%s $2..$3 | egrep -m 1 '.{81}') 
if [ -n "$long_subject" ]; then 
    echo "error: commit subject over 80 characters:" 
    echo " $long_subject" 
    exit 1 
fi 

Tất nhiên, đó là một ví dụ đồ chơi; trong trường hợp chung, bạn sẽ sử dụng đầu ra nhật ký chứa thông báo cam kết đầy đủ, chia nhỏ thông tin cho mỗi cam kết và gọi mã xác minh của bạn trên từng thư cam kết riêng lẻ.

Tại sao bạn muốn móc cập nhật

Điều này đã được thảo luận/làm rõ trong nhận xét; đây là một bản tóm tắt.

Móc cập nhật chạy một lần cho mỗi lần nhập. Một ref là một con trỏ tới một đối tượng; trong trường hợp này, chúng ta đang nói về các chi nhánh và các thẻ, và thường chỉ là các nhánh (mọi người không đẩy các thẻ thường xuyên, vì chúng thường chỉ để đánh dấu các phiên bản).

Bây giờ, nếu người dùng đang đẩy bản cập nhật cho hai chi nhánh, hoàn thiện và thực nghiệm:

o - o - o (origin/master) - o - X - o - o (master) 
\ 
    o - o (origin/experimental) - o - o (experimental) 

Giả sử X là "xấu" cam kết, ví dụ một trong đó sẽ thất bại móc cam-msg. Rõ ràng chúng tôi không muốn chấp nhận sự thúc đẩy để làm chủ. Vì vậy, hook cập nhật từ chối điều đó. Nhưng không có gì sai với các cam kết về thử nghiệm! Hook cập nhật chấp nhận cái đó. Vì vậy, nguồn gốc/master vẫn không thay đổi, nhưng nguồn gốc/thực nghiệm được cập nhật:

o - o - o (origin/master) - o - X - o - o (master) 
\ 
    o - o - o - o (origin/experimental, experimental) 

The pre-nhận móc chạy chỉ một lần, chỉ cần trước khi bắt đầu cập nhật refs (trước khi lần đầu tiên móc cập nhật được chạy). Nếu bạn sử dụng nó, bạn sẽ phải làm cho toàn bộ sự thất bại, do đó nói rằng bởi vì có một thông điệp cam kết xấu về chủ, bạn bằng cách nào đó không còn tin tưởng rằng các cam kết thử nghiệm là tốt mặc dù tin nhắn của họ là tốt!

+0

Tôi nghĩ rằng móc mà OP đang tìm kiếm là tiền nhận, vì anh ta muốn từ chối toàn bộ thao tác đẩy tùy thuộc vào thông điệp cam kết. Tuy nhiên, AFAIK, không được nhận trước cũng như không cập nhật nhận được thông báo cam kết làm đầu vào. Vì vậy, việc sử dụng commit-msg có lẽ sẽ là giải pháp tốt nhất. –

+0

@Can: Tôi khá chắc chắn rằng OP muốn cập nhật, chứ không phải trước khi nhận. "Đẩy toàn bộ" có nghĩa là đẩy cho tất cả các chi nhánh. Nếu người dùng cố gắng đẩy cập nhật vào ba chi nhánh và chỉ một người chứa thông báo cam kết không hợp lệ, thì hai người kia vẫn phải được chấp nhận! – Cascabel

+0

@Can: Và không, thông báo cam kết không phải là một phần của đầu vào, nhưng tên đối tượng cũ (mới) (SHA1)). Lưu ý rằng hook cập nhật được thực hiện ngay trước khi các ref được cập nhật (sau khi các đối tượng commit đã được nhận). Do đó, hook có thể sử dụng git log để kiểm tra bất cứ điều gì nó muốn về các cam kết giữa cũ và mới, bao gồm cả các thông điệp cam kết của chúng. – Cascabel

1

Bạn cần tạo một tập lệnh khi nhận trước.

Trong tập lệnh này, bạn nhận được bản sửa đổi cũ và mới. Bạn có thể kiểm tra tất cả các cam kết và trả về false nếu một trong số này là xấu.

5

Bạn có thể làm điều đó với pre-receive hook sau. Như các câu trả lời khác đã lưu ý, đây là một cách tiếp cận bảo thủ, tất cả hoặc không có gì. Lưu ý rằng nó chỉ bảo vệ nhánh master và không có ràng buộc nào đối với các thông điệp cam kết trên các nhánh chủ đề.

#! /usr/bin/perl 

my $errors = 0; 
while (<>) { 
    chomp; 
    next unless my($old,$new) = 
    m[^([0-9a-f]+) \s+ # old SHA-1 
     ([0-9a-f]+) \s+ # new SHA-1 
     refs/heads/master # ref 
     \s* $ ]x; 

    chomp(my @commits = `git rev-list $old..$new`); 
    if ($?) { 
    warn "git rev-list $old..$new failed\n"; 
    ++$errors, next; 
    } 

    foreach my $sha1 (@commits) { 
    my $msg = `git cat-file commit $sha1`; 
    if ($?) { 
     warn "git cat-file commit $sha1 failed"; 
     ++$errors, next; 
    } 

    $msg =~ s/\A.+? ^$ \s+//smx; 
    unless ($msg =~ /\[\d+\]/) { 
     warn "No bug number in $sha1:\n\n" . $msg . "\n"; 
     ++$errors, next; 
    } 
    } 
} 

exit $errors == 0 ? 0 : 1; 

Nó yêu cầu tất cả các cam kết đẩy để có số lỗi ở đâu đó trong thư cam kết tương ứng chứ không chỉ mẹo. Ví dụ:

$ git log --pretty=oneline origin/master..HEAD 
354d783efd7b99ad8666db45d33e30930e4c8bb7 second [123] 
aeb73d00456fc73f5e33129fb0dcb16718536489 no bug number 

$ git push origin master 
Counting objects: 6, done. 
Delta compression using up to 2 threads. 
Compressing objects: 100% (4/4), done. 
Writing objects: 100% (5/5), 489 bytes, done. 
Total 5 (delta 0), reused 0 (delta 0) 
Unpacking objects: 100% (5/5), done. 
No bug number in aeb73d00456fc73f5e33129fb0dcb16718536489: 

no bug number 

To file:///tmp/bare.git 
! [remote rejected] master -> master (pre-receive hook declined) 
error: failed to push some refs to 'file:///tmp/bare.git'

Nói rằng chúng tôi sửa chữa các vấn đề bằng cách đè bẹp hai cam kết với nhau và thúc đẩy kết quả:

$ git rebase -i origin/master 
[...] 

$ git log --pretty=oneline origin/master..HEAD 
74980036dbac95c97f5c6bfd64a1faa4c01dd754 second [123] 

$ git push origin master 
Counting objects: 4, done. 
Delta compression using up to 2 threads. 
Compressing objects: 100% (2/2), done. 
Writing objects: 100% (3/3), 279 bytes, done. 
Total 3 (delta 0), reused 0 (delta 0) 
Unpacking objects: 100% (3/3), done. 
To file:///tmp/bare.git 
    8388e88..7498003 master -> master
2

Đây là một phiên bản python của pre-receive, mà đã cho tôi một thời gian để hoàn thành, hy vọng nó có thể giúp người khác. Tôi chủ yếu sử dụng nó với Trac, nhưng nó có thể dễ dàng sửa đổi cho các mục đích khác.

Tôi cũng đã đưa ra các hướng dẫn để sửa đổi lại thông điệp cam kết lịch sử, điều này phức tạp hơn một chút so với tôi nghĩ.

#!/usr/bin/env python 
import subprocess 

import sys 
import re 

def main(): 
    input = sys.stdin.read() 
    oldrev, newrev, refname = input.split(" ") 
    separator = "----****----" 


    proc = subprocess.Popen(["git", "log", "--format=%H%n%ci%n%s%b%n" + separator, oldrev + ".." + newrev], stdout=subprocess.PIPE) 
    message = proc.stdout.read() 
    commit_list = message.strip().split(separator)[:-1] #discard the last line 

    is_valid = True 

    print "Parsing message:" 
    print message 

    for commit in commit_list: 
     line_list = commit.strip().split("\n") 
     hash = line_list[0] 
     date = line_list[1] 
     content = " ".join(line_list[2:]) 
     if not re.findall("refs *#[0-9]+", content): #check for keyword 
      is_valid = False 

    if not is_valid: 
     print "Please hook a trac ticket when commiting the source code!!!" 
     print "Use this command to change commit message (one commit at a time): " 
     print "1. run: git rebase --interactive " + oldrev + "^" 
     print "2. In the default editor, modify 'pick' to 'edit' in the line whose commit you want to modify" 
     print "3. run: git commit --amend" 
     print "4. modify the commit message" 
     print "5. run: git rebase --continue" 
     print "6. remember to add the ticket number next time!" 
     print "reference: http://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit" 

     sys.exit(1) 

main() 
1

Bạn đã không đề cập đến theo dõi lỗi của bạn là gì, nhưng nếu nó là JIRA thì add-on có tên Commit Policy có thể làm điều này cho bất kỳ mà không cần lập trình.

Bạn có thể thiết lập điều kiện cam kết yêu cầu thông báo cam kết khớp với cụm từ thông dụng. Nếu không, push sẽ bị từ chối và nhà phát triển phải sửa đổi (sửa) thông báo cam kết, sau đó nhấn lại.

Các vấn đề liên quan