Cảm ơn bạn đã đọc và ủng hộ blog KTMT ʘ‿ʘ Từ bây giờ chúng tôi sẽ là kipalog.com !

Comments

1. Giới thiệu

Redis là hệ thống lưu trữ key-value với rất nhiều tính năng và được sử dụng rộng rãi. Redis nổi bật bởi việc hỗ trợ nhiều cấu trúc dữ liệu cơ bản (hash, list, set, sorted set, string), đồng thời cho phép scripting bằng ngôn ngữ lua. Bên cạnh lưu trữ key-value trên RAM với hiệu năng cao, redis còn hỗ trợ lưu trữ dữ liệu trên đĩa cứng (persistent redis) cho phép phục hồi dữ liệu khi gặp sự cố. Ngoài tính năng replicatation (sao chép giữa master-client), tính năng cluster (sao lưu master-master) cũng đang được phát triển . Để sử dụng một cách hiệu quả những tính năng redis hỗ trợ cũng như vận hành redis với hiệu suất cao nhất thì việc am hiểu hệ thống lưu trữ này là điều không thể thiếu. Chính vì lý do này, mình quyết định tìm hiểu mã nguồn redis. Loạt bài viết về redis này tóm tắt những điều mình tìm hiểu được từ việc đọc mã nguồn của redis.

2. Khái quát

Bạn có thể clone mã nguồn redis về máy tính mình bằng câu lệnh dưới đây:

git clone https://github.com/antirez/redis.git
Trước hết là một số thống kê nho nhỏ về redis (tại thời điểm bài viết): * Số lượng file mã nguồn: 55
ls *.c | wc -l
55
  • Số lượng file header: 30
    ls *.h | wc -l
    30
  • Tổng số dòng code: 43829
    wc -l *.[ch]
    341    adlist.c     197   pqsort.c            228   sha1.c          810   dict.c
    93     adlist.h     40    pqsort.h            17    sha1.h          173   dict.h
    435    ae.c         359   pubsub.c            169   slowlog.c       124   endianconv.c
    130    ae_epoll.c   93    rand.c              47    slowlog.h       64    endianconv.h
    315    ae_evport.c  38    rand.h              50    solarisfixes.h  52    fmacros.h
    118    ae.h         1230  rdb.c               530   sort.c          759   help.h
    132    ae_kqueue.c  114   rdb.h               144   syncio.c        483   intset.c
    99     ae_select.c  683   redis-benchmark.c   57    testhelp.h      50    intset.h
    441    anet.c       3008  redis.c             761   t_hash.c        295   lzf_c.c
    60     anet.h       218   redis-check-aof.c   1149  t_list.c        150   lzf_d.c
    1178   aof.c        768   redis-check-dump.c  913   t_set.c         100   lzf.h
    47     asciilogo.h  1556  redis-cli.c         459   t_string.c      159   lzfP.h
    220    bio.c        1517  redis.h             2205  t_zset.c        279   memtest.c
    41     bio.h        52    release.c           520   util.c          323   multi.c
    412    bitops.c     3     release.h           41    util.h          1444  networking.c
    2866   cluster.c    1658  replication.c       1     version.h       128   notify.c
    1726   config.c     198   rio.c               1534  ziplist.c       580   object.c
    195    config.h     104   rio.h               46    ziplist.h
    88     crc16.c      1065  scripting.c         467   zipmap.c
    191    crc64.c      732   sds.c               49    zipmap.h
    8      crc64.h      99    sds.h               351   zmalloc.c
    815    db.c         3160  sentinel.c          85    zmalloc.h
    929    debug.c      261   setproctitle.c
    43829  total

Một số thư viện được sử dụng: jemalloc, linenoise, lua

3. Các modules

Redis bao gồm các module sau:

  • Framework hỗ trợ xử lý bất đồng bộ, networking: ae, anet
  • Mô tả dữ liệu: sds.c, t_hash.c, t_list.c, t_string.c, t_zset.c, object.c, notify.c (pub-sub)
  • Lưu trữ dữ liệu, cơ sở dữ liệu: db.c, dict.c, ziplist.c, zipmap.c, adlist.c
  • Module hỗ trợ IO/persistent redis: rdb.c, aof.c, bio.c, rio.c
  • Utilities: crc16.c, crc64.c, pqsort.c, lzf_c.c, lzf_d.c

Mình sẽ lần lượt giới thiệu các modules trong các bài viết sau. Ở bài viết này, mình sẽ tập trung vào module IO/persistent redis.

4. Persistent redis

Bên cạnh việc lưu key-value trên bộ nhớ RAM, Redis có 2 background threads chuyên làm nhiệm vụ định kỳ ghi dữ liệu lên đĩa cứng.

Có 2 loại file được ghi xuống đĩa cứng:

  • RDB
  • AOF

RDB lưu dữ liệu dưới dạng đã mã hóa. AOF lưu lại toàn bộ dưới liệu dưới dạng command, giống như command mà redis client gửi đến server để thao tác bằng cách ghi đè xuống cuối file.

File rdb có thể coi là một snapshot của cơ sở dữ liệu tại một thời điểm nhất định. File dữ liệu này được dùng với 2 mục đích

  • Cho phép redis có thể phục hồi lại dữ liệu trên memory bằng việc đọc file
  • Bản thân dữ liệu được ghi ra file rdb sẽ được gửi đến các redis slave server, phục vụ mục đích sao lưu server.

Dữ liệu ghi ra file rdb được chỉnh sửa và mã hóa để giảm kích thước ghi trên đĩa, đồng thời tằng tốc độ replication. Cụ thể định dạng của file rdb như sau.

Với những key ngắn, việc dùng 32 bit để mô tả key là thừa thãi, do vậy redis quy định những key ngắn được mã hóa sử dụng 2 bit đầu tiên của 1 byte. Cụ thể: 00|000000 => 00, độ dài dữ liệu mô tả bởi 6 bit 01|000000 00000000 => 01, độ dài dữ liệu là 14 bit 10|000000 [số 32 bit] => 1 chuỗi độ dài 32 bit sẽ theo sau 11|000000 => obj được encode đặc biệt sẽ theo sau byte này. 6 bit sau sẽ xác định kiểu object.

Kiểu object ở đây cụ thể như sau:

rdb.c
1
2
3
4
5
6
7
/* When a length of a string object stored on disk has the first two bits
 * set, the remaining two bits specify a special encoding for the object
 * accordingly to the following defines: */
#define REDIS_RDB_ENC_INT8 0        /* 8 bit signed integer */
#define REDIS_RDB_ENC_INT16 1       /* 16 bit signed integer */
#define REDIS_RDB_ENC_INT32 2       /* 32 bit signed integer */
#define REDIS_RDB_ENC_LZF 3         /* string compressed with FASTLZ */

Với AOF file, các command sẽ được nhóm thành các block. Các block được tổ chức dưới dạng danh sách liên kết. Mỗi block có độ lớn 10MB là vì trong trường hợp redis server chịu tải cao, số lượng key được cập nhật lớn, nếu kích thước buffer lớn, việc realloc buffer dùng cho các command với tốc độ lớn không đảm bảo.

Trong trường hợp file rdb, redis fork 1 process con và thực hiện ghi dữ liệu xuống đĩa cứng sử dụng rio (stream IO).

Trong trường hợp file aof, việc thực hiện ghi dữ liệu là của background threads. Toàn bộ chức năng này được code trong file bio.c (background IO?). Thiết kế background IO này khá đơn giản. Môt loạt thread sẽ chia sẻ 1 job queue và thay nhau đợi việc từ job queue. Mỗi khi có job mới, thread sẽ chạy và thực thi job được mô tả. Có 2 loại job đơn giản:

  • REDIS_BIO_CLOSE_FILE: đóng file
  • REDIS_BIO_AOF_FSYNC: thực hiện việc flush dữ liệu từ buffer của kernel xuống buffer của đĩa cứng.

    process -> job 1 -> job2 -> … background threads

Tạo ra các job là công việc của child process. Để thực hiện ghi dữ liệu ra đĩa cứng, redis sẽ fork ra 1 process con. Process con này sẽ tạo ra việc cho các background threads. Một đặc điểm cùa aof file đấy là dữ liệu trong các block mới sẽ không được ghi trực tiếp vào file aof hiện tại, mà sẽ được ghi vào file tạm thời. Khi việc ghi dữ liệu hoàn thành, redis mới tiến hành ghi đè file tạm lên file thật. Việc này đảm bảo trong trường hợp hệ thống có sự cố, file aof cũ vẫn được duy trì, giúp phục hồi phần nào dữ liệu.

Trong cả 2 trường hợp, redis sử dụng tính năng Copy-on-Write của linux khi fork process con, do vậy hiệu năng không vì fork process con mà suy giảm.

Đến đây, sau khi tìm hiểu về định dạng của 2 files dữ liệu cũng như phương thức ghi dữ liệu của từng loại file, ta vẫn còn những câu hỏi mở về persistent redis như sau:

  • Tần suất ghi dữ liệu là bao nhiêu?
  • Ai chịu trách nhiệm fork process con.

Thực chất redis định nghĩa 1 giá trị gọi là tần số ghi: REDIS_DEFAULT_HZ với giá trị mặc định là 10 (redis.h). Như vậy trong 1s, redis sẽ thực hiện 10 lần việc gọi hàm fork. Toàn bộ thao tác ghi dữ liệu redis và thao tác với các key hết hạn được thực hiện bởi 1 hệ thống các “cron”. Hàm cron thực hiện việc validate các key là: databaseCron. Hàm cron thực hiện ghi dữ liệu là serverCron. Hàm serverCron sẽ được gọi theo cơ chế bất đồng bộ (dùng thư viện bất đồng bộ của chính redis) với tần số 1/1000s. Với REDIS_DEFAULT_HZ là 10, cứ 100 lần gọi, serverCron sẽ thực hiện fork child process 1 lần để ghi dữ liệu xuống bộ đĩa cứng.

5. Tại sao phải fsync

Đến đây chắc bạn đã hiểu phần nào về cơ chế persistent của redis. Tuy nhiên ta vẫn còn 1 câu hỏi nhỏ khá thú vị: tại sao phải flush liên tục như vậy (100ms / lần)? Tại sao không chỉ dùng hàm write/read của kernel và mặc định việc ghi dữ liệu xuống đĩa cứng cho kernel. Để trả lời câu hỏi nhỏ này, ta cần hiểu mối liên quan giữa OS - đĩa cứng - buffer của tầng ứng dụng.

Về mặt trực quan tra có mô hình như sau:

buffer ---(Write) --| (kernel buffer) ---> hard disk buffer ---> đĩa từ.

Một thao tác ghi dùng write/read api của kernel sẽ copy dữ liệu từ buffer tần ứng dụng xuống buffer của kernel. Đây là thao tác cơ bản của write api. Tại buffer của kernel, kernel có toàn quyền quyết định với buffer này như: khi nào ghi, ghi bao nhiêu bytes… Khi kernel ghi dữ liệu (sử dụng các hàm IO của đĩa), dữ liệu sẽ được ghi xuống hard disk buffer và được schedule ghi xuống đĩa từ. Do vậy nếu tại tầng kernel hệ thống gặp sự cố, dữ liệu vẫn có thể bị mất dù rằng write thành công (và tầng ứng dụng không có cách nào biết write không thành công). Bằng việc định kỳ gọi fsync, ứng dụng có thể thoát khỏi sử quản lý của kernel, ghi thằng dữ liệu đang có ở buffer xuống hard disk buffer. Bằng việc gọi fsync, ta tránh khỏi được rủi ro mất dữ liệu do đổ vớ ở tầng ứng dụng. Tất nhiên dữ liệu hard disk vẫn chưa hoàn toàn an toàn (ví dụ trường hợp đĩa cứng bị hỏng).

Đây là cách làm chung của các hệ thống cơ sở dữ liệu RDMS hiện hành.

6. Kết luận

Bài viết giới thiệu khái quát các module redis, đồng thời trình bày cụ thể cơ chế ghi dữ liệu của redis. Bài viết cũng làm rõ hơn ý nghĩa của fsync cũng như quy trình ghi dữ liệu của hệ điều hành. Hy vọng qua bài viết, người đọc hiểu phần nào cơ chế, hành vi của redis, qua đó sử dụng công cụ này hiệu quả hơn.

7. Tham khảo

  1. persistent redis
  2. vm
  3. Understanding Virtual Memory
  4. redis book
Comments

Mở đầu

Trong quá trình dọn dẹp máy tính cá nhân, tôi gặp phải vấn đề là quá nhiều file trùng lặp trong máy tính. Việc có quá nhiều file như vậy gây lãng phí ổ cứng, cộng thêm đôi lúc tôi không còn nhớ là file này đặt ở thư mục này có ý nghĩa gì nữa. Giá mà có công cụ nào đó để tìm tự động các file này cho tôi thì tốt quá nhỉ??

Không cần phải giá mà nữa, không gì bằng làm ra cho mình một công cụ như vậy. Bài viết này sẽ trình bày những bước đầu tiên tôi prototype công cụ tìm kiếm các file trùng lặp. Thông qua bài viêt này, tôi cũng sẽ giới thiệu về những chủ đề sau trong Java: tính hash, thread pooling.

MD5 checksum

MD5 là một thuật toán mã hóa thông điệp (message-digest), về bản chất là một hàm băm (hash) xuất ra một giá trị 128-bit (16-byte). MD5 được sử dụng rất nhiều trong các ứng dụng bảo mật, và cũng được sử dụng để kiểm tra tính toàn vẹn dữ liệu (ví dụ như bạn down một file từ Internet về, thường có một MD5 key đi kèm, để bạn kiểm tra xem file bạn down có chính xác giống nguồn down không.)

Tôi không có ý định đi sâu vào cách tính MD5, bạn có thể tham khảo trên google. Lưu ý là thuật toán MD5 vẫn có thể sinh ra cùng một giá trị hash với 2 file có nội dung khác nhau (tuy nhiên trường hợp này rất hiếm). Để chắc chắn tìm được 2 file có nội dung giống hệt nhau, ta sẽ so sánh theo thứ tự: so sánh giá trị hash, so sánh độ lớn của file, và cuối cùng là so sánh từng byte của hai file để chắc chắn chúng trùng nhau.

Trong Java, hàm băm đã được đưa vào thư viện java.security, thông qua class MessageDigest. Bạn chỉ cần khai báo đối tượng MessageDigest, sau đó chọn phương thức mã hóa MD5, hoặc SHA. Việc bạn phải làm đối với từng file là chuyển file thành byte stream, và update dòng input này vào đối tượng MessageDigest trên, và bạn sẽ nhận được một dòng byte kết quả.

Chi tiết về class MessageDigest, bạn có thể tham khảo tại đây: http://docs.oracle.com/javase/6/docs/api/java/security/MessageDigest.html

MD5.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 public static String getMD5(File file)
  {
      if(!file.exists())
          return "";
      try
      {
          MessageDigest  md =  MessageDigest.getInstance("MD5");

          //read the file into a byte array
          byte[] input = new byte[(int) file.length()];
          InputStream in = new FileInputStream(file);
          in.read(input);
          in.close();
          
          //update the MessageDigest and process
          md.update(input);
          byte[] fileDigest = md.digest();
          
          return ByteArrayToString(fileDigest);
      }
      catch(IOException e )
      {
          e.printStackTrace();
          return "";
      }
      catch(NoSuchAlgorithmException e)
      {
          return "";
      }
  }
  
  private static String ByteArrayToString(byte[] ba)
  {
     StringBuilder sb = new StringBuilder();
     for(byte b: ba)
        sb.append(String.format("%02x", b&0xff));
     return sb.toString();
  }

Theading

Threading, hay concurrency, trong Java là một chủ đề khá phức tạp và tôi cũng chưa đủ khả năng nắm rõ được tất cả. Do vậy, trong bài viết này, tôi xin chỉ đề cập đến kĩ thuật thread pooling để phân bài toán ra thành nhiều tác vụ, chạy trên nhiều luồng khác nhau.

Thread pool là gì? Bạn có thể hiểu nôm na là thay vì cứ gọi một luồng mới cho từng tác vụ khi cần thiết (phương pháp này gặp nhược điểm vì việc tạo thread mới sẽ gặp overhead), ta tạo sẵn một cái “bể” (pool) trong đó khởi động sẵn một loạt các luồng (thread). Các luồng này ngoi ngoi lên để đợi ta ném tác vụ vào để chúng thực hiện. Sau khi thực hiện xong, nó sẽ báo cáo lại kết quả, kết thúc tác vụ, và quay trở lại “bể” để tiếp tục ngoi ngoi chờ tác vụ mới. Chỉ khi nào đóng “bể”, thì các luồng mới bị mất đi.

Trong bài toán tìm file trùng lặp này, tôi sẽ áp dụng thread pooling như sau:

  • Tạo một class DuppFind, implement Runnable class (đây là class ta có thể truyền vào cho pool), đại diện cho tác vụ tìm kiếm. Với mỗi object DuppFind, nó sẽ được truyền vào các tham số gồm có:

– Thư mục bắt đầu tìm kiếm

– Map>: là một map chứa key là md5 và value là một List chứa đường dẫn đến các file có md5 value như vậy

– pool, thuộc class ExcutorService.

  • Mỗi tác vụ sau khi được luồng chọn để thực thi, nó sẽ làm gì? Nhiệm vụ của tác vụ là quét tất cả các file, thư mục có trong thư mục được chỉ định tìm kiếm. Nếu gặp file, nó sẽ tính MD5 cho file đó, và thêm vào Map. Nếu gặp thư mục, tác vụ này sẽ tạo ra một tác vụ DuppFind mới, chỉ định thư mục mới này cho tác vụ DuppFind mới, và ném vào pool (bây giờ bạn đã hiểu tại sao object DuppFind luôn được truyền pool vào rồi đấy:) )

Một vài điểm lưu ý:

  • Mỗi tác vụ phải có trách nhiệm đợi tất cả các tác vụ con nó tạo ra hoàn thành thì mới được chấm dứt. Tôi thực hiện điều này bằng cách sử dụng đối tượng Future được trả lại mỗi lần ném tác vụ mới vào pool. Sau đó chỉ cần gọi polling kết quả từ tác vụ con là được.

  • Với những Collection dùng chung giữa các thread, cần phải dùng những Concurrecy Collection mà Java cung cấp. Cụ thể ở đây, tôi dùng ConcurrentHashMap cho Map và CopyOnWriteArrayList cho List.

Dưới đây là đoạn code cho phần thread pooling:

DuppFind.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class DuppFind implements Runnable{
  private File directory;
  private Map<String, List<String>> md5FileMap;
  private ExecutorService pool;
  
  public DuppFind(File directory, Map<String, List<String>> md5FileMap, ExecutorService pool)
  {
      this.directory = directory;
      this.md5FileMap = md5FileMap;
      this.pool = pool;
  }    

  @Override
  public void run () {
      try
      {
          File[] files = directory.listFiles();
          ArrayList<Future<?>> results = new ArrayList<Future<?>>();
          for(File file : files)
          {
              if(file.isDirectory())
              {
                  //System.out.println("Dir: " + file.getAbsolutePath());
                  DuppFind df = new DuppFind(file, md5FileMap, pool);
                  Future<?> result = pool.submit(df);
                  results.add(result);
              }
              else if(file.isFile())
              {
                  //calculate md5
                  String md5 = MD5Utils.getMD5(file);
                  List<String> listFile = new CopyOnWriteArrayList<String>();
                  if(md5FileMap.get(md5) != null)
                  {
                      System.out.println("Duplicate md5: " + md5 + ", size now: " + md5FileMap.get(md5).size());
                      listFile.addAll(md5FileMap.get(md5));
                  }
                  listFile.add(file.getAbsolutePath());
                  md5FileMap.remove(md5);
                  md5FileMap.put(md5, listFile);
                  System.out.println("Add: File" + file.getAbsolutePath() + ", MD5: " + md5);
              }
          }
          
          //wait until all sub-tasks complete
          for(Future<?> result: results)
          {
              result.get();
          }
      }
      catch(InterruptedException e)
      {
          e.printStackTrace();
      }
      catch(ExecutionException e)
      {
          e.printStackTrace();
      }
  }
}

Toàn bộ code đã ở trên github: https://github.com/viethnguyen/DuppFind

Đánh giá

  • Đoạn code trên chạy chính xác với những thư mục nhỏ, không có quá nhiều thư mục con, cháu… Tuy nhiên, với những thư mục chứa nhiều thư mục con, vì có quá nhiều tác vụ vào thread pool, nên hay xảy ra exception out of memory - heap.

  • Mới chỉ kiểm tra xem 2 file có MD5 trùng nhau không. Nếu trùng nhau rồi, ta cần kiểm tra thêm độ dài 2 file có trùng nhau không, rồi nếu trùng tiếp thì phải check từng byte để chắc chắn nhất có thể (xóa đi khỏi hối hận :P)

  • Ở trên mới hiển thị ra những file nào trùng lặp. Cần cho phép user xóa file trùng lặp trong chương trình.

  • Cần thiết kế GUI!

Bạn thấy đó, còn khá nhiều vấn đề cần giải quyết để có một tool hoàn chỉnh. Những vấn đề này sẽ được cải tiến và được report lại trong một ngày không xa :) Nếu bạn cảm thấy hứng thú, rất welcome contribute :)

Tham khảo

  1. Core Java
  2. Java Concurrency in Practice
  3. Effective Java
git
Comments

Githook là gì

Giống như các hệ thống quản lý version khác, Git cung cấp một cách để gọi những custom script khi một hành động đặc biệt được thực hiện trong git. Có 2 nhóm hook trong git: hook cho client, và hook cho server. Client hook là dành cho những hoạt động xảy ra ở client như commit và merge. Server hook là dành cho những hoạt động xảy ra ở Git server như nhận được một commit push lên từ client. Bài viết này giới thiệu một số cách sử dụng hook cả client side hook

Install hook

Các hooks được lưu trong thư mục hooks của .git folder. Trong phần lớn các project, nó là .git/hooks. Ở chế độ mặc định, Git cung cấp một loạt các examples cho các hooks, các ví dụ này đưa ra khá nhiều chỉ dẫn về inputs của mỗi hook. Tất cả các examples được viết bằng shell script, với một vài mã lệnh Perl, tuy nhiên bạn có thể viết các script này bằng bất cứ ngôn ngữ nào (ví dụ như Python, Ruby). Tất cả các examples đều có tên kết thúc bằng .sample, bạn cần đổi tên các script trước khi chạy

Để enable các hook script, đặt một file trong thư mục hooks với tên tương ứng với tên của hook, và set quyền cho script đó là executable.

Client side hooks

Có rất nhiều loại client side hooks, chúng ta chia chúng theo work flow

Committing-Workflow Hooks

pre-commit hook được gọi đầu tiên, trước khi bạn gõ nội dung của một commit message. Hook này được sử dụng để kiểm tra nội dụng các files được commit. Bạn có thể viết script để kiểm tra coding convetion, hoặc để run test, để chạy static analysis trước khi commit. Nếu script trả về kết quả khác 0, commit sẽ bị loại bỏ. Tuy nhiên bạn có thể bỏ qua chạy hook này khi commit với lệnh git commit --no-verify

prepare-commit-msg là hook được chạy trước khi trình soạn thảo commit message được gọi đế và sau khi message mặc định được tạo ra. Hook này giúp bạn thay đổi default message trước khi commit author thấy nó. Hook này về cơ bản là ít khi hữu dụng, chỉ trừ khi các commit message được sinh ra tự động.

commit-msg hook nhận duy nhất một input là đường dẫn của file chứa commit message. Nếu script này trả về kết quả khác 0, commit sẽ bị loại bỏ. Hook này có tác dụng giúp bạn chuẩn hoá mesage trước khi commit

Sau khi toàn bộ quá trình commit được hoàn tất, post-commit hook sẽ chạy. hook này không nhận tham số đầu vào, nhưng bạn có thể dễ dàng lầy last commit bằng cách gọi git log -1 HEAD. Thông thường, hook này dụng để notification.

Committing-workflow client side scripts được sử dụng và setup bới chính các developer tại máy local của họ. Các developers phải tự maintain chúng, tuy nhiên họ có thể thay đổi chúng bất cứ lúc nào

Email workflow hooks

Bạn có thể set up 3 client side hooks cho email workflow. Tất cả các hooks này đều liên quan đến git am command. Nếu bạn không sử dụng câu lệnh này trong workflow của bạn, bạn có thể bỏ qua phần này. Nếu bạn nhận patch qua email được chuẩn bị bới lệnh git format-patch, thì có thể những hook này sẽ có ích với bạn

Hook đầu tiên là applypatch-msg. Nó nhận một tham số: tên của file tạm chứ nội dụng của commit message. Git sẽ bỏ qua patch nếu hook này trả về giá trị khác 0. Bạn có thể sử dụng hook này để đảm bảo commit message là đúng chuẩn.

Hook tiếp theo khi apply patches thông qua git ampre-applypatch. Hook không nhận giá trị đầu vào và được chay sau khi patch được applied. Vì thế bạn có thể sử dụng hook này để kiểm tra toàn bộ mã nguồn trước khi make commit. VD: chạy test, chạy static analysis, kiểm tra style (hook này tương đối giống pre-commit trong phần trước)

Hook cuối cùng được chạy trong process của git ampost-applypatch. Bạn có thể sử dụng nó để notify cho một group hoặc một author về patch này.

Một vài client hooks khác

pre-rebase hooks run trước khi bạn rebase bất cứ commit nào, và sẽ cả process lại nếu hook trả về giái trị khác 0.

post-checkout được gọi sau khi bạn chạy lệnh git checkout thành công. Bạn có thể sử dụng nó để setup environment, hoặc tự động sinh document sau khi checkout

post-merge hook được chạy sau khi bạn chạy lệnh git merge. Bạn có thể sử dụng để phục hồi lại dữ liệu trong thư mục làm việc mà Git không thể kiểm tra ví dụ như dữ liệu liên quan đến permission.

Kết luận

Bài viết giới thiệu về githooks và một số client hooks thường dùng. Hy vọng bạn tìm thấy một vài thông tin hữu ích giúp bạn tự động hoá một số công việc hàng ngày khi làm việc với git.

Tham khảo

Githook documentation

Copyright © 2015 kỹ thuật máy tính