🦄 個人主頁: 小米里的大麥-CSDN博客
🎏 所屬專欄: Linux_小米里的大麥的博客-CSDN博客
🎁 GitHub主頁: 小米里的大麥的 GitHub
?? 操作環境: Visual Studio 2022
文章目錄
- Linux 開發工具(下)
- Linux 項目自動化構建工具 —— `make` / `Makefile`
- 1. 什么是 `make` 和 `Makefile`?
- 為什么需要 `make`?
- 2. `make` 的基本工作原理
- 基本邏輯
- 規則格式
- 3. `make` 的工作流程
- 4. 偽目標 `.PHONY` 與放置位置的討論
- 關于 `.PHONY: clean` 的放置位置
- 5. 常見符號與自動變量的使用
- `@` —— 命令隱藏符
- 自動變量 `$^` 與 `$@`
- 6. 進階技巧(了解)
- 變量定義與使用
- 模式規則
- 傳道解惑
- ==文件 = 文件內容 + 文件屬性==
- Linux 下第一個小程序——進度條
- 1. 換行 vs 回車:鍵盤上的時光機
- 2. 行緩沖區:快遞員的打包習慣
- 實驗驗證與現象分析
- Shell 提示符的輸出行為
- 為什么覆蓋?
- 3. 倒計時實現
- 4. 由于進度條相關文章內容過長,詳見下一篇文章!
- `git ` 的使用
- 一、Git 的本質:時間管理大師的 "時光機"
- 二、Linus Torvalds:被逼出來的創新
- 三、Git 的革命性突破
- 四、Git 的現代發展
- 五、Git 改變了軟件開發的方式
- 六、`git` 的使用([Git 教程](https://liaoxuefeng.com/books/git/introduction/index.html))
- Git 的核心概念
- Git 的工作流程
- 1. 安裝 Git
- 2. 配置 Git
- 3. 創建/初始化倉庫
- 4. 克隆遠程倉庫
- 5. 查看倉庫狀態
- 6. 添加文件到暫存區
- 7. 提交更改
- 8. 查看提交歷史
- 9. 查看遠程倉庫
- 10. 添加/連接遠程倉庫
- 11. 推送本地分支到遠程倉庫
- 12. 拉取遠程倉庫的更新
- 13. 撤銷工作區的修改
- 14. 撤銷暫存區的修改
- 15. 撤銷提交
- 16. 創建標簽
- 17. 查看標簽
- 18. 推送標簽到遠程倉庫
- 19. 查看差異
- 20. 查看遠程分支
- 21. 刪除遠程分支
- 1. Git 只會記錄已添加到暫存區(staging area)的修改
- 2. Git 忽略某些文件:`.gitignore`
- 3. Git 配置文件的管理
- Linux 調試器 —— `gdb` 的使用
- 一、背景知識
- 二、Windows IDE 對應功能
- 三、`gdb` 常用命令
- 1. 查看源代碼
- 2. 運行程序
- 3. 單步執行
- 4. 設置斷點
- 5. 刪除斷點
- 6. 繼續執行
- 7. 查看和修改變量
- 8. 跟蹤變量
- 9. 查看函數調用棧
- 10. 查看局部變量
- 11. 跳轉到指定行
- 12. 退出函數
- 共勉
Linux 開發工具(下)
Linux 項目自動化構建工具 —— make
/ Makefile
1. 什么是 make
和 Makefile
?
在大型軟件項目中,源代碼通常散布在多個文件和目錄中。為了高效地管理這些文件之間的依賴關系并實現自動化編譯,Linux 提供了非常強大的工具 —— make
。make
是一個自動化構建工具,它通過讀取 Makefile
文件中的規則來決定如何編譯和鏈接程序。Makefile
是一個文本文件,其中定義了項目的依賴關系和構建規則。
為什么需要 make
?
- 自動化構建:手動編譯多個源文件并管理它們之間的依賴關系非常繁瑣,
make
可以自動化這一過程。 - 增量編譯:
make
只會重新編譯那些被修改的文件及其依賴項,從而節省編譯時間。 - 跨平臺兼容:
Makefile
可以在不同的平臺上使用,只需稍作修改。
2. make
的基本工作原理
make
的核心功能是根據文件的修改時間自動判斷哪些文件需要重新編譯。具體來說,它會根據文件間的依賴關系,確保只有修改過的部分被重新編譯,避免無謂的重復編譯。
基本邏輯
-
目標文件與依賴關系
make
通過比較目標文件與依賴文件的修改時間來決定是否需要重新編譯。如果目標文件不存在,或者依賴文件較新,make
就會重新執行對應的編譯命令。 -
舉例說明:假設我們有如下的依賴關系:
hello
依賴于hello.o
。hello.o
依賴于hello.s
。hello.s
依賴于hello.i
。hello.i
依賴于hello.c
。
當
hello.c
被修改后,make
會檢查文件的修改時間,并按照依賴關系從hello.c
開始,逐層更新直到最終目標hello
。
規則格式
目標文件:依賴文件命令
例如:
hello: hello.ogcc hello.o -o hello
如果 hello.o
發生了變化,make
會執行 gcc hello.o -o hello
來生成目標文件 hello
。
3. make
的工作流程
Makefile
中的依賴關系往往構成一個“棧式”結構,即從最終目標開始,逐層向下尋找依賴,直到最初的源文件。
- 查找文件:
make
會在當前目錄下查找名為Makefile
或makefile
的文件。 - 確定目標文件:找到文件后,它會查找文件中的第一個目標文件(如示例中的
hello
)并將其作為最終的目標文件。 - 檢查依賴關系:如果目標文件不存在,或者其依賴文件的修改時間比目標文件更新,則執行相應的命令生成目標文件。
- 遞歸處理依賴:如果目標文件的依賴文件(如
hello.o
)不存在,則會進一步在當前文件中查找該依賴文件的規則,并依此進行生成。 - 完成編譯:按照依賴關系一層一層地處理,直到最終生成第一個目標文件。
- 錯誤處理:如果在依賴關系的查找過程中出現錯誤(如最后被依賴的文件找不到),
make
會直接退出并報錯;對于命令執行的錯誤或編譯不成功的情況,make
不會進行處理。
例如下面這個 Makefile
片段展示的完整依賴鏈:
# 目標文件 hello 依賴于 hello.o
hello: hello.o# gcc 用于編譯鏈接生成可執行文件 (Executable)gcc hello.o -o hello# 目標文件 hello.o 依賴于 hello.s
hello.o: hello.s# -c 表示只編譯生成目標文件,不進行鏈接gcc -c hello.s -o hello.o# 目標文件 hello.s 依賴于 hello.i
hello.s: hello.i# -S 表示生成匯編代碼 (Assembly code)gcc -S hello.i -o hello.s# 目標文件 hello.i 依賴于 hello.c
hello.i: hello.c# -E 表示只預處理生成中間文件gcc -E hello.c -o hello.i
- 依賴判斷:當修改了
hello.c
后,hello.i
的時間戳就會落后于hello.c
,從而觸發后續所有目標的重新編譯。 - 自動化依賴推導:
make
會根據目標與依賴之間的時間比較,自動“回溯”整個依賴鏈,直到確定哪些文件需要重新生成。
4. 偽目標 .PHONY
與放置位置的討論
定義:偽目標(如 clean
)一般用于清理工程中的目標文件,它們沒有被第一個目標文件直接或間接關聯。可以通過命令(如 make clean
)顯式執行其后的命令。偽目標的特性是 總是被執行,不會因為文件的存在而被忽略。
例如在 Makefile
中,我們經常會定義一些不對應實際文件的輔助目標,clean
目標通常用于刪除編譯產生的中間文件。為避免與同名文件產生沖突,我們使用 .PHONY
聲明該目標總是需要執行:
.PHONY: clean
clean:@rm -f hello.i hello.s hello.o hello
# 使用 `.PHONY` 聲明后,`make clean` 會始終執行清理命令,即使當前目錄下存在名為 `clean` 的文件。
關于 .PHONY: clean
的放置位置
放在開頭或結尾: 無論將 .PHONY: clean
放在 Makefile
的開頭還是結尾,其功能是相同的,都會告訴 make
“clean” 不是一個真實存在的文件。然而,從 代碼可讀性 和 維護性 的角度來看,通常建議將輔助目標(如 clean
)放在文件的末尾。這樣做可以:
- 保持主構建規則的集中:主要的目標和依賴關系放在上面,便于開發者快速了解構建流程。
- 邏輯分明:清理等輔助目標作為附加功能放在末尾,形成明顯的區分。
5. 常見符號與自動變量的使用
在編寫 Makefile
時,合理使用一些特殊符號和自動變量能使文件更加簡潔與靈活。
@
—— 命令隱藏符
在規則的命令前加上 @
符號,可以在執行時不將該命令打印到終端。例如:
clean:@rm -f hello.i hello.s hello.o hello
- 作用:使得執行
make clean
時不會在終端中顯示rm -f ...
這一行命令,從而使命令輸出更干凈,只顯示必要的信息。
自動變量 $^
與 $@
- $@:代表規則中的目標文件(Target)。
- $^:代表規則中所有的依賴文件(Prerequisites),通常用來減少重復輸入依賴文件列表。
示例: 假設有多個依賴文件構成目標文件,我們可以這樣寫:
hello: hello.o util.o# $^ 表示所有依賴,即 "hello.o util.o"(:右邊部分,若有多個,用空格隔開)# $@ 表示目標,即 "hello"(:左邊部分)gcc $^ -o $@
好處:使用自動變量能讓規則更靈活且易于維護,尤其當依賴項較多時,避免重復書寫目標和依賴文件名稱。
6. 進階技巧(了解)
變量定義與使用
在 Makefile
中,我們可以定義變量來簡化重復的代碼。例如:
CC = gcc
CFLAGS = -Wall -O2myprogram: main.o utils.o$(CC) $^ -o $@main.o: main.c utils.h$(CC) $(CFLAGS) -c main.c -o main.outils.o: utils.c utils.h$(CC) $(CFLAGS) -c utils.c -o utils.o
CC
:定義編譯器為gcc
。CFLAGS
:定義編譯選項為-Wall -O2
。
模式規則
當項目中有多個相似的文件需要編譯時,可以使用模式規則來簡化 Makefile
。例如:
%.o: %.c$(CC) $(CFLAGS) -c $< -o $@
%.o
:表示所有以.o
結尾的目標文件。%.c
:表示所有以.c
結尾的源文件。$<
:表示第一個依賴文件。
傳道解惑
文件 = 文件內容 + 文件屬性
在計算機系統中,文件不僅僅是包含數據內容的容器,它還具有一些屬性,描述了文件的元數據(metadata)。這些屬性包括文件的權限、修改時間、訪問時間、所有者、大小等。
我們可以將文件的概念表示為:文件 = 文件內容 + 文件屬性
1. 文件內容(File Content)
文件內容是文件實際存儲的數據。對于文本文件,它通常是可讀的字符串;對于二進制文件,它可以是任意的數據,如圖像、音頻、視頻或程序代碼等。文件內容是文件的核心部分,用戶創建、編輯和刪除文件時,主要涉及文件內容的操作。
例如,一個文本文件
hello.txt
的內容可能如下:Hello, World!
2. 文件屬性(File Attributes)
文件屬性是描述文件元信息的數據,通常是系統自動管理的。這些屬性并不直接涉及文件的內容,但它們在文件的管理、訪問和權限控制中起著至關重要的作用。常見的文件屬性包括:
- 文件權限(Permissions):指定哪些用戶或用戶組可以讀取、寫入或執行文件。
- 所有者(Owner):文件的創建者或擁有者。
- 創建時間(Creation Time):文件首次創建的時間。
- 修改時間(Modification Time, mtime):文件內容最后一次修改的時間。
- 訪問時間(Access Time, atime):文件最后一次被訪問的時間(無論是讀取、執行還是其他)。
- 改變時間(Change Time, ctime):文件的元數據(如權限、所有者、位置)最后一次修改的時間。
3. 文件屬性詳解
修改時間(
mtime
):修改時間表示文件內容上次修改的時間。每次文件內容改變時,mtime
就會更新。例如,如果你編輯并保存一個文本文件,那么該文件的mtime
就會更新為當前時間。訪問時間(
atime
):訪問時間表示文件上次被訪問的時間。訪問可以是讀取文件內容、執行程序文件等操作。每當文件被讀取時,atime
就會更新。需要注意的是,有些操作系統會進行優化,使得訪問文件時不更新atime
,從而減少磁盤 I/O。例如,使用cat
命令讀取文件內容會更新atime
。改變時間(
ctime
):改變時間表示文件的元數據(如文件權限、文件名、所有者等)最后一次改變的時間。ctime
僅在文件的屬性發生變化時更新,而不是文件內容。例如,如果改變了文件的權限或文件的所有者,ctime
會發生變化。4. 文件操作與屬性變化
修改文件內容: 當我們修改文件內容時,文件的
mtime
會更新,而atime
和ctime
通常不變,除非文件本身被移動、重命名或修改其他元數據。例如,使用echo "new content" > file.txt
命令修改file.txt
文件內容時,文件內容發生了變化,因此mtime
會更新。訪問文件: 當我們訪問文件(例如讀取文件內容)時,文件的
atime
會更新,但mtime
和ctime
不會發生變化。例如,使用cat file.txt
讀取文件內容時,文件的atime
會更新,表示文件被訪問了。修改文件屬性: 當我們修改文件的權限、所有者等屬性時,文件的
ctime
會更新。這個時間是文件元數據的變化標志,而不是文件內容本身的變化。例如,使用chmod
修改文件權限時,ctime
會更新,但mtime
和atime
不會發生變化。5. 文件屬性的查看與修改
在 Linux 系統中,可以使用
ls
命令查看文件的基本屬性(如權限、所有者、時間等):ls -l file.txt
輸出例子:
-rw-r--r-- 1 user user 128 Feb 7 10:15 file.txt
其中:
-rw-r--r--
是文件的權限。1
是硬鏈接數量。user
是文件的所有者。user
是文件的所屬用戶組。128
是文件的大小。Feb 7 10:15
是文件的mtime
(修改時間)。要查看文件的
atime
和ctime
,可以使用stat
命令:stat file.txt
輸出例子:
File: file.txt Size: 128 Blocks: 8 IO Block: 4096 regular file Device: 803h/2051d Inode: 12345678 Links: 1 Access: 2025-01-01 10:00:00.000000000 Modify: 2025-01-01 10:00:00.000000000 Change: 2025-01-01 10:00:00.000000000
Access
:atime
(訪問時間)Modify
:mtime
(修改時間)Change
:ctime
(更改時間)
Linux 下第一個小程序——進度條
1. 換行 vs 回車:鍵盤上的時光機
- 換行(Line Feed,
\n
):光標移動到 下一行,但水平位置不變 。在一些系統中,換行符是\n
,也有的系統中(如 Windows)回車和換行會一起使用。 - 回車(Carriage Return,
\r
):光標回到 當前行的行首,不換行 。在很多操作系統中,回車是用來將光標重置到行首的位置。換行符通常是\r
。
生活場景比喻:想象你用打字機寫文章,打完一行字后需要做兩個動作:
- 換行(Line Feed):把紙向上推一行(對應
\n
) - 回車(Carriage Return):把打印頭移回最左側(對應
\r
)
2. 行緩沖區:快遞員的打包習慣
行緩沖區(Line Buffering)是指輸出流的數據并不是直接輸出到屏幕或終端,而是先存儲在緩沖區中。當緩沖區滿時,數據才會被輸出。這種方式提升了效率,減少了對系統資源的頻繁訪問。在 C 語言中,printf()
是行緩沖的典型例子。它的工作原理是:當你調用 printf()
打印一行文本時,數據并不是立刻顯示,而是先被放到緩沖區中,直到遇到換行符 \n
時,系統才會將緩沖區的內容真正輸出到屏幕上。
生活場景比喻:快遞員不會每收到一個小物件就立刻送貨,而是攢滿一車再出發。 行緩沖區 就像這個 “攢滿一車” 的規則:
- 遇到
\n
換行符時立刻 “送貨”(刷新緩沖區) - 緩沖區滿時自動刷新
- 程序正常結束時也會自動刷新
標準輸出(stdout)在終端中是 行緩沖 的,如果沒有換行符 \n
,緩沖區不會自動刷新,導致內容被覆蓋或丟失。
實驗驗證與現象分析
-
代碼 1:
printf("123456\rAB");
- 現象:只顯示
AB
。實測window
的vs 2022
顯示AB 456
應該是屬于vs
的“個人行為”。 - 原因:
\r
將光標移回行首,但緩沖區未刷新,內容被覆蓋或未輸出。
- 現象:只顯示
-
代碼 2:
printf("123456\rAB\n");
- 現象:終端顯示
AB3456
。 - 原因:
\n
觸發了緩沖區的刷新,AB
覆蓋了12
,然后換行。
- 現象:終端顯示
-
代碼 3:
printf("123456\rAB"); fflush(stdout); // 手動刷新緩沖區
- 現象:終端顯示
AB3456
。細節流程:123456
被輸出到終端,光標停留在6
后面。\r
將光標移動到行首(即1
的位置)。AB
被輸出,覆蓋了前兩個字符12
,此時終端內容為AB3456
,光標停留在3
的位置。
- 原因:
fflush(stdout)
強制刷新緩沖區,AB
覆蓋了12
。
為什么我看到的只有
AB
?我們不妨修改代碼,運行后,3 秒內會看到
AB3456
,隨后提示符會進行覆蓋,這也就是為什么我們看到的只有AB
了:int main() {printf("123456\rAB");fflush(stdout);sleep(3); // 暫停3秒,觀察輸出return 0; }
- 現象:終端顯示
Shell 提示符的輸出行為
- 當程序運行結束后,Shell 會立即在 當前光標位置 輸出提示符(如
[damai@VM-16-11-centos coding]$
)。 - 由于程序結束時,光標停留在
3
的位置,Shell 提示符會從3
的位置開始輸出,覆蓋 掉后面的內容3456
。
為什么覆蓋?
- 終端的工作機制:終端是一個字符設備,它會嚴格按照光標位置輸出內容。如果光標不在行尾,新輸出的內容會從光標位置開始覆蓋已有的內容。
- Shell 提示符的輸出:Shell 提示符不會主動換行,而是從當前光標位置開始輸出。因此,如果程序沒有將光標移動到行尾或換行,提示符就會覆蓋程序輸出的內容。
知道了這些內容,我們就可以嘗試看看下面代碼運行的結果了:
#include <stdio.h>
#include <unistd.h>int main()
{// 實驗1:無換行符,無刷新printf("123456\rAB");sleep(2); // 等待2秒觀察現象printf("\n"); // 換行以刷新緩沖區// 實驗2:有換行符printf("123456\rAB\n");sleep(2); // 等待2秒觀察現象// 實驗3:手動刷新printf("123456\rAB");fflush(stdout); // 手動刷新sleep(2); // 等待2秒觀察現象printf("\n"); // 換行return 0;
}
在來一遍,深刻理解:
-
現象 1:沒有換行符
\n
的情況下調用printf()
:#include <stdio.h>int main() {printf("hello Makefile!");sleep(3);return 0; }
現象:這時 “hello Makefile!” 會先存入緩沖區,不會立刻顯示在屏幕上。直到程序結束,緩沖區中的內容才會被顯示出來。所以程序在運行完
sleep(3)
后,才會看到輸出結果。 -
現象 2:加上換行符
\n
:#include <stdio.h>int main() {printf("hello Makefile!\n");sleep(3);return 0; }
現象:這時,換行符
\n
會強制刷新緩沖區,所以 “hello Makefile!” 會立即輸出,等待 3 秒后程序結束。 -
現象 3:調用
fflush(stdout)
強制刷新緩沖區:#include <stdio.h> #include <unistd.h> int main() {printf("hello Makefile!");fflush(stdout); // 強制刷新緩沖區sleep(3);return 0; }
現象:調用
fflush(stdout)
后,printf()
輸出的內容會立即顯示,無論是否遇到換行符,緩沖區中的內容都會被強制刷新到屏幕上,等待 3 秒后程序結束。
3. 倒計時實現
理解了回車換行和行緩沖區的概念后,我們再來做一個倒計時的實現,相信下面的代碼很容易理解:
int main()
{int cnt = 10;while (cnt >= 0){printf("%d\r", cnt);fflush(stdout);cnt--;sleep(1);}return 0;
}
運行一下,會發現倒計時的顯示效果是:10
→ 90
→ 80
→ 70
→ ……
,而不是預期的 10
→ 9
→ 8
→ ……。數字寬度不一致, 當 cnt
為兩位數 10
時,輸出 10
;而當 cnt
變為一位數(如 9)時,輸出的是 9
。因此,當從兩位數輸出變為一位數輸出時,上一輪輸出中的多余字符(例如 10
中的 1
)依然殘留在屏幕上。我們做以下修改即可:
printf("%2d\r", cnt); // 調整輸出格式:如果 cnt 只有一位數,則會在數字前補空格
再次運行會發現,倒計時站兩個字符,前一個字符空出,只有后一個字符在變化,顯得不太正常,我們接著做修改:
printf("%-2d\r", cnt); // 修改對其規則:左對齊
于是,我們的倒計時就實現了:
#include <stdio.h>
#include <unistd.h> // 包含 sleep 函數int main()
{int cnt = 10; // 初始化倒計時值while (cnt >= 0) // 循環直到倒計時結束{printf("%-2d\r", cnt); // 格式化輸出,左對齊,固定寬度為2,并覆蓋之前輸出中多余的字符fflush(stdout); // 強制刷新緩沖區,確保立即輸出cnt--; // 倒計時減1sleep(1); // 暫停1秒}printf("倒計時結束!\n"); // 倒計時完成后輸出提示信息return 0;
}
4. 由于進度條相關文章內容過長,詳見下一篇文章!
git
的使用
這里會簡單敘述 Git
的誕生與核心價值,然而關于 git
遠遠不止于此,需要大量內容才能將 git
講清楚,所以,未來我會專門出一個專題來對 git
進行詳細的講解!
一、Git 的本質:時間管理大師的 “時光機”
要理解 Git,我們可以想象一個科幻場景:假設你正在寫一本小說,每天創作的新章節都會生成一個獨立的時間膠囊。某天你發現主角設定出了問題,只需打開對應日期的膠囊就能恢復原貌。Git 本質上就是這樣一個分布式時光機。它通過記錄文件的 “快照” 而非差異(如 SVN),讓每個開發者電腦都保存完整的版本庫。例如:
- 張三改實驗報告:李四作為 “版本管家”,每次收到修改都存檔并標注(
git commit
) - 多人協作寫代碼:就像樂隊分聲部排練,Git 確保所有樂譜修改能精準合并(
git merge
)
二、Linus Torvalds:被逼出來的創新
Linux 之父林納斯·托瓦茲(Linus Torvalds)與 Git
的淵源,堪稱技術界的 “復仇爽文”:
在 2002 年以前,lucubrate Linux 的龐大社區還處于一種極度原始的代碼管理狀態,依靠著手工合并代碼來維系項目 Progress。這無疑給參與其中的開發者們帶來了巨大的不便與低效,令人不禁聯想起原始部落艱難求生的畫面。
隨后,商業工具 BitKeeper 的引入仿佛曙光初現,為 Linux 內核的代碼管理帶來了短暫的清朗時期。卻不曾想,因社區成員嘗試逆向工程這一敏感行為,(BitKeeper 開發團隊)憤然剝奪了 Linux 社區的使用授權。正如同好不容易借到的寶藏工具突然被奪走,開發者們再度陷入了深深的困境,這無疑是壓垮駱駝的最后一根稻草。
在這一刻,Linus Torvalds,Linux 之父,心中憤怒與不甘的火焰熊熊燃燒。他深知,若不能尋得有效的解決之道,Linux 項目的未來將滿是荊棘。于是,兩周,僅僅兩周的時間,他憑借著過人的智慧與毅力,完成了 Git 原型的編寫。而一個月內,Linux 內核便成功遷移到這個自主開發的全新系統上,整個過程仿若一場奇跡。
" 就像被房東趕走的窘迫之人,在絕望之中親手蓋出了一棟更豪華的別墅。" —— 網友評價
三、Git 的革命性突破
對比傳統版本控制工具,Git 實現了三大飛躍:
特性 | 集中式(如 SVN) | Git 分布式 |
---|---|---|
網絡依賴 | 必須聯網提交 | 本地即可完成所有操作 |
數據安全 | 中央服務器故障即丟失歷史 | 每個副本都是完整備份 |
分支管理 | 創建/合并分支耗時 | 輕量級分支秒級切換 |
四、Git 的現代發展
Git 自誕生以來,逐漸在全球范圍內普及并成為開發人員的標準工具之一。以下是 Git 現代發展的幾個關鍵點:
1. GitHub 的崛起
2008 年,GitHub 上線,它為 Git 提供了一個強大的托管平臺,使得開發者可以方便地在線托管代碼,并且與其他開發者協作。GitHub 通過引入 Pull Request(PR)功能,讓 Git 的協作模式更加高效,推動了開源社區的快速發展。很多知名的開源項目(如 jQuery、Node.js 等)都遷移到了 GitHub。
2. 中國化適配:Gitee 等本土平臺
由于 GitHub 在中國大陸訪問受到一定限制,本土平臺如 Gitee 等逐漸興起。Gitee 不僅解決了訪問速度的問題,還提供了針對中國開發者的多種本地化功能,使得 Git 在國內的普及更加順利。
3. 可視化工具的普及
隨著 Git 的普及,越來越多的可視化工具如 GitKraken、SourceTree 等應運而生。這些工具通過圖形界面簡化了 Git 的操作,使得即使是沒有命令行經驗的開發者也能輕松上手。Git 的復雜操作變得更加直觀和易用,降低了學習成本。
4. 版本控制的精確性和回溯功能
Git 的最大優勢之一就是其強大的回溯能力。當開發者說“我回滾一下”,實際上就是“導演喊‘Cut!’重拍第 3 幕”。每個 commit
就像電影中的一個鏡頭,都可以精確回溯、查看和恢復,確保每一步的修改都能被追溯和復原。
五、Git 改變了軟件開發的方式
從 Linus Torvalds 的靈感到 Git 成為全球開發者的標準工具,Git 無疑已經成為軟件開發的核心技術之一。Git 的核心價值不僅僅在于它如何管理版本,更在于它如何改變了開發者的工作方式、提高了協作效率,并且推動了開源軟件的發展。
正如 Linus 所說:“Talk is cheap. Show me the code.” Git 用代碼改變了世界,它的出現不僅僅是一個工具的革命,更是一種工作方式、協作方式和思想方式的革命。Git 的設計思想和實踐,使得開發者能夠更加專注于代碼的創作,而不必過度擔心版本管理的問題。
Git 不僅是一個技術工具,更是一種創新精神的體現——快速響應變化、追求效率、關注團隊協作、并持續優化。它的成功,也象征著開源社區強大的生命力和創新能力。
六、git
的使用(Git 教程)
Git 的核心概念
- 倉庫(Repository):Git 倉庫是項目的核心,包含了項目的所有文件和歷史記錄。每個開發者都有一個完整的倉庫副本。
- 提交(Commit):每次提交都是項目的一個快照,記錄了文件的更改和提交信息。
- 分支(Branch):分支是開發中的獨立線路,允許開發者在不同的分支上并行工作。
- 合并(Merge):將不同分支的更改合并到一起,確保代碼的一致性。
- 克隆(Clone):從遠程倉庫復制一個完整的倉庫到本地。
- 拉取(Pull):從遠程倉庫獲取最新的更改并合并到本地分支。
- 推送(Push):將本地的更改上傳到遠程倉庫。
Git 的工作流程
- 初始化倉庫:使用
git init
命令創建一個新的 Git 倉庫。 - 添加文件:使用
git add
命令將文件添加到暫存區。 - 提交更改:使用
git commit
命令將暫存區的更改提交到倉庫。 - 創建分支:使用
git branch
命令創建一個新的分支。 - 切換分支:使用
git checkout
命令切換到不同的分支。 - 合并分支:使用
git merge
命令將不同分支的更改合并到一起。 - 查看歷史:使用
git log
命令查看提交歷史。
我們在 Linux CentOS 7 云服務器上使用 Xshell 連接并操作 Git 時的一些常用的 Git 命令:
1. 安裝 Git
首先,確保你的 CentOS 7 系統上已經安裝了 Git。如果沒有安裝,可以使用以下命令進行安裝:
# 更新 yum 軟件包
sudo yum update -y# 安裝 Git
sudo yum install -y git
安裝完成后,可以通過以下命令檢查 Git 版本,確認安裝成功:
git --version
2. 配置 Git
在 第一次使用 Git 之前,需要配置用戶名和郵箱,這些信息會出現在每次提交的記錄中。
git config --global user.name "your_name" # 設置用戶名
git config --global user.email "your_email@example.com" # 設置郵箱
通過以下命令查看當前的 Git 配置:
git config --list
3. 創建/初始化倉庫
在當前目錄下創建一個新的 Git 倉庫,使用以下命令(這會在當前目錄下生成一個 .git
目錄,用于存儲 Git 的版本控制信息):
git init
4. 克隆遠程倉庫
如果想從遠程倉庫克隆一個項目到本地,可以使用 git clone
命令,這會將遠程倉庫的內容克隆到當前目錄下的一個新文件夾中:
git clone https://github.com/username/repository.git
5. 查看倉庫狀態
使用以下命令可以查看當前倉庫的狀態,包括哪些文件被修改、哪些文件被暫存等:
git status
6. 添加文件到暫存區
在對文件進行修改后,需要將文件添加到暫存區(Stage),以便后續提交:
git add file_name # 例如:git add temp.txt ,添加文件到暫存區
如果想添加所有修改過的文件,可以使用:
git add .
7. 提交更改
將暫存區的文件提交到本地倉庫,-m
選項后面跟的是本次提交的描述信息。描述信息就是提交日志,盡量提交有意義的信息!:
git commit -m "Your commit message" # 例如:git commit -m "第一次提交" ,提交到本地倉庫
8. 查看提交歷史
使用以下命令可以查看當前倉庫的提交歷史:
git log
還可以通過 --oneline
選項簡化輸出:
git log --oneline
9. 查看遠程倉庫
查看當前配置的遠程倉庫:
git remote -v
10. 添加/連接遠程倉庫
添加/連接一個新的遠程倉庫:
git remote add origin 遠程版本庫的URL
# 例如:git remote add origin https://github.com/your_username/your_repo.git
11. 推送本地分支到遠程倉庫
將本地分支的提交推送到遠程倉庫:
git push origin 分支名稱
git push -u origin master # 將 master 分支推送,并設置默認上游分支(常用)
12. 拉取遠程倉庫的更新
從遠程倉庫拉取最新的更改并合并到當前分支:
git pull origin 分支名稱
git pull origin master # 拉取遠程倉庫的最新代碼并合并
13. 撤銷工作區的修改
撤銷工作區中某個文件的修改,恢復到最近一次提交的狀態:
git checkout -- 文件名稱
# 例如:git checkout -- file.txt 丟棄工作區修改,恢復到最后一次提交狀態
14. 撤銷暫存區的修改
將某個文件從暫存區撤回到工作區:
git reset HEAD 文件名稱
15. 撤銷提交
撤銷上一次提交,只回退 commit
,保留代碼修改:
git reset --soft HEAD~1
撤銷最近一次提交,并將更改放回工作區:
git reset --soft HEAD^
如果你想撤銷提交并丟棄更改,可以使用:
git reset --hard HEAD^
16. 創建標簽
創建一個新的標簽:
git tag 標簽名稱
17. 查看標簽
查看所有標簽:
git tag
18. 推送標簽到遠程倉庫
將本地標簽推送到遠程倉庫:
git push origin 標簽名稱
19. 查看差異
查看工作區與暫存區的差異:
git diff
查看暫存區與最新提交的差異:
git diff --cached
20. 查看遠程分支
查看遠程倉庫的所有分支:
git branch -r
21. 刪除遠程分支
刪除遠程倉庫的指定分支:
git push origin --delete 分支名稱
至于分支管理、多人協作和沖突管理等操作暫時用不到,放到以后再說,有興趣可自行百度。
除了前面提到的基本操作,Git
在使用時還存在一些常見的注意事項(這里提到部分內容,如有其他問題需勤于百度):
1. Git 只會記錄已添加到暫存區(staging area)的修改
Git 不會自動跟蹤所有修改。它只會跟蹤你手動添加到暫存區的修改,即 默認記錄修改部分(工作區和暫存區)。也就是通過 git add
添加的文件或更改。
- 工作區(Working Directory):對文件的任何修改都首先體現在工作區。
- 暫存區(Staging Area):通過
git add
命令將修改暫存到 Git 中的暫存區。 - 本地倉庫(Repository):通過
git commit
命令,將暫存區的修改提交到本地倉庫。
關鍵點(工作流程): 只有 git add
命令添加到暫存區的文件才會被 git commit
提交(修改文件 → git add
添加到暫存區 → git commit
提交到倉庫)。
示例:
echo "This is a test" > file.txt # 修改文件
git status # 查看狀態,file.txt 會顯示為修改狀態
git add file.txt # 添加到暫存區
git commit -m "Updated file.txt" # 提交修改
忘記 git add
的后果
如果修改了文件但沒有執行 git add
,然后直接 git commit
,Git 是不會將這些修改提交的。因此,Git 只會記錄已經添加到暫存區的修改。
2. Git 忽略某些文件:.gitignore
如果不希望某些文件被 Git 跟蹤(如編譯出來的二進制文件、IDE 配置文件等),可以使用 .gitignore
文件來指定不需要跟蹤的文件或文件夾。
創建 .gitignore
# 例如,忽略所有 *.log 文件
echo "*.log" >> .gitignore
然后 git add .gitignore
提交 .gitignore
文件!!!
關鍵點: .gitignore
文件是 Git 跟蹤文件的方式之一。
3. Git 配置文件的管理
Git 配置文件分為三種層級:
- 系統級:影響系統上所有用戶的配置,通常位于
/etc/gitconfig
。 - 全局級:影響當前用戶的配置,通常位于
~/.gitconfig
。 - 倉庫級:只對當前倉庫生效,位于倉庫的
.git/config
。
可以通過以下命令查看和修改 Git 配置:
git config --global user.name "Your Name" # 設置全局用戶名
git config --global user.email "you@example.com" # 設置全局郵箱
查看當前配置:
git config --list
Linux 調試器 —— gdb
的使用
一、背景知識
在程序開發過程中,為了有效定位和修復代碼中的錯誤,調試工具起著至關重要的作用。程序的發布方式主要有兩種:debug
模式和 release
模式。
- debug 模式:此模式下編譯生成的程序包含了豐富的調試信息,例如變量名、函數名、源代碼行號等,方便開發者借助調試工具進行錯誤排查。不過,debug 模式通常會增加程序的體積,并且在一定程度上降低運行效率。
- release 模式:旨在提供高性能的可執行程序,編譯時會進行各種優化,剔除不必要的調試信息,以確保程序運行速度和資源利用率最大化。在 Linux 系統中,使用 gcc 或 g++ 編譯器編譯出來的二進制程序,默認是 release 模式。
若要使用 gdb(GNU Debugger)對程序進行調試,必須在源代碼生成二進制程序時,加上 -g
選項。這樣,編譯器會在生成的可執行文件中嵌入調試所需的信息,使得 gdb 能夠準確地關聯程序運行狀態與源代碼。
編譯生成調試信息
假設有一個源文件 temp.c
,-g
告訴編譯器生成調試信息,-o temp
指定輸出文件名。可以通過以下命令編譯程序并生成調試信息:
gcc -g temp.c -o temp
啟動 gdb
gdb
啟動時需要提供一個已經編譯好的二進制文件。假設我們有一個名為 temp
的程序文件,通過以下命令啟動 gdb
準備進行調試:
gdb temp
退出 GDB
退出 GDB 的方式有兩種:
- 輸入
quit
或q
命令(常用)。 - 按下
Ctrl + D
。
二、Windows IDE 對應功能
對于 Windows 用戶習慣的 IDE 調試工具(如 Visual Studio),gdb
也提供了類似的功能。例如:
- 設置斷點:在源代碼中設置斷點,并在程序執行時停止。
- 單步調試:支持單步執行,可以選擇逐行執行代碼,或者進入函數內調試。
- 變量觀察:支持查看和修改變量的值,并可以在調試過程中動態修改它們。
- 堆棧跟蹤:查看當前調用棧以及每個函數的參數。
與 Windows 中的 IDE 相比,gdb
是命令行工具,因此需要通過命令行輸入調試命令,但功能是非常強大的。
三、gdb
常用命令
GDB 提示符的作用
- 當你啟動 GDB 并加載程序后,GDB 會進入交互模式,顯示
(gdb)
提示符。 - 這個提示符表示 GDB 正在等待你輸入命令。
- 你只需要在
(gdb)
后面輸入命令,按回車執行。
例如:
(gdb) break main # (gdb) 是 GDB 的提示符,只需要輸入 break main 并按回車即可
1. 查看源代碼
list
或 l
:查看源代碼。
list <行號>
:從指定行號開始顯示源代碼。list <函數名>
:顯示指定函數的源代碼。- 默認每次顯示 10 行,按回車鍵繼續顯示后續內容。
(gdb) list 10 # 顯示從第 10 行開始的源代碼
(gdb) list # 顯示當前行及接下來的 10 行
(gdb) l main # 顯示 main 函數的源代碼
2. 運行程序
run
或 r
:從頭開始運行程序。
- 如果程序需要參數,可以在
run
后面加上參數:
(gdb) run # 啟動程序的執行,不帶任何命令行參數
(gdb) run arg1 arg2 # 啟動程序的執行,并傳遞兩個命令行參數:arg1 和 arg2
3. 單步執行
next
或n
:單步執行(不進入函數內部)。step
或s
:單步執行(進入函數內部)。
(gdb) next # 單步執行
(gdb) step # 進入函數內部
4. 設置斷點
break
或 b
:設置斷點。
break <行號>
:在指定行設置斷點。break <函數名>
:在函數入口處設置斷點。info breakpoints
:查看當前設置的所有斷點及其狀態。
(gdb) break 20 # 在第 20 行設置斷點
(gdb) break main # 在 main 函數的開頭設置斷點
(gdb) info breakpoints # 查看所有斷點信息
5. 刪除斷點
delete breakpoints
:刪除所有斷點。delete breakpoints n
:刪除序號為n
的斷點。disable breakpoints
:禁用所有斷點,使其在下次調試時不起作用。enable breakpoints
:啟用所有已禁用的斷點。
(gdb) delete breakpoints # 刪除所有斷點
(gdb) delete breakpoints 1 # 刪除序號為 1 的斷點
(gdb) disable breakpoints # 禁用所有斷點
(gdb) enable breakpoints # 啟用所有斷點
6. 繼續執行
continue
或 c
:從當前位置繼續執行,直到遇到下一個斷點或程序結束。
(gdb) continue # 繼續執行程序
7. 查看和修改變量
print
或 p
:打印變量的值。
print <變量名>
:打印變量的值。print <表達式>
:計算并打印表達式的值。
(gdb) print x # 打印變量 x 的值
(gdb) print x + y
set var 變量名=值
:修改變量的值。
(gdb) set var x=10 # 將變量 x 的值修改為 10
8. 跟蹤變量
display <變量名>
:每次程序暫停時,自動打印變量的值。undisplay <編號>
:取消對變量的跟蹤。
(gdb) display x # 每次停止時顯示變量 x 的值
(gdb) undisplay # 取消所有跟蹤變量
9. 查看函數調用棧
backtrace
或 bt
:查看當前函數的調用棧(包括參數和調用位置)。
(gdb) backtrace # 查看函數調用棧
10. 查看局部變量
info locals
:查看當前函數的局部變量。
(gdb) info locals # 查看當前棧幀的局部變量
11. 跳轉到指定行
until <行號>
:跳轉到指定行。
(gdb) until 30 # 跳轉到第30行
12. 退出函數
finish
:執行完當前函數并暫停。
(gdb) finish # 執行完當前函數并暫停
共勉