前言
Git作為目前最流行的代碼版本管理工具,是DevOps工具鍊中的核心工具之一。DevOps工程師是Git的重度使用者,難免在使用中會碰到一些從文檔層面不易了解或解決的問題,需要從源碼層面進行分析。本小文旨在了解Git源碼基礎架構,能快速定位需要分析的代碼子產品,并通過IDE進行調試分析。
Git代碼結構
common main
git源碼中包含好多個git相關程式,他們的啟動過程是一緻的,可以複用一套代碼。git為此設計了一個 common main結構,原文說明如下:
* There are certain house-keeping tasks that need to be performed at
the very beginning of any Git program, and programs that are not
built-in commands had to do them exactly the same way as "git"
potty does. It was easy to make mistakes in one-off standalone
programs (like test helpers). A common "main()" function that
calls cmd_main() of individual program has been introduced to
make it harder to make mistakes.
具體的實作是:
1、在common-main.c中定義通用的main函數,裡面調用 cmd_main
2、各個git程式在編譯時連結上面這個編譯出來的cmd-main.o
3、各個git程式定義各自的cmd_main
builtin commands
使用過git的同學都知道,git有很多子指令,譬如 checkout, add, commit, status, push等等。執行 git xxx 和執行 git-xxx 的效果是一樣的。
這些子指令在git源碼裡叫builtin,我們來看看 builtin 是怎麼實作的。
首先看編譯過程,從頂層的Makefile看到如下生成語句:
$(BUILT_INS): git$X
$(QUIET_BUILT_IN)$(RM) $@ && \
ln $< $@ 2>/dev/null || \
ln -s $< $@ 2>/dev/null || \
cp $< $@
builtin是git的連結或是拷貝而來的,實際是一個東西。
在代碼層面,git.c 維護一個builtin的清單,
static struct cmd_struct commands[] = {
{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
{ "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
{ "annotate", cmd_annotate, RUN_SETUP },
{ "apply", cmd_apply, RUN_SETUP_GENTLY },
{ "archive", cmd_archive },
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP },
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
{ "cat-file", cmd_cat_file, RUN_SETUP },
... ...
}
cmd_struct的第一個字段是子指令的名稱,第二個字段是具體執行函數。get_builtin 函數根據名稱找到結構,然後run_builtin執行結構裡的指令。
所有的builtin的源碼都在builtin子目錄下,譬如builtin/add.c 對應 git add 子指令,裡面定義cmd_add函數執行具體的操作
git代碼調試
調試的前提是能編譯通過,是以需要先安裝依賴的包:
$ sudo apt-get install libssl-dev zlib1g-dev libcurl4-openssl-dev
還有個小細節,git的源碼預設是按-O2編譯的,會導緻在Eclipse調試的時候檢視變量值顯示 optimized out, 很不友善,是以需要更改編譯選項。
編譯過程是通過Makefile組織的,我們需要修改Makefile。Makefile裡有引入configure生成的configure.mak.autogen, 裡面也有編譯選項,是以需要同時改configure和Makefile
$ edit configure and Makefile , from -O2 to -O0
$ ./configure
$ make clean
$ make all