Git 是一种分布式版本控制系统,即依靠任何一个拷贝了这个项目的设备都可以进行恢复到任何一个版本,每一次克隆都是对项目的完整备份。
Git 的三种状态
- 已提交(committed)表示数据已经安全地保存在本地数据库中
- 已修改(modified)表示修改了文件,但还没保存到数据库中
- 已暂存(staged)表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
Git 的三个区域
- 工作区 是对项目的某个版本提取出来的内容。通常是用
git clone
拷贝下来的,放在磁盘上供修改或者使用。 - 暂存区 是一个文件,用于保存下一次需要提交的文件列表信息。
- Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。
初次运行Git的配置 配置用户名和邮箱
js
git config --global user.name "BoLin"
git config --global user.email szwtsdsoftware@163.com
使用了--global
的情况下,这个配置是全局生效的。
Git 基础
获取Git仓库
方式 1 将本地的尚未进行版本控制的本地目录转换为Git仓库
- 进入尚未进行版本管理的但准备要进行版本管理的目录,例:
cd my_project
- 在命令行执行
git init
- 如果这个文件夹是一个非空文件夹,里面有*.c文件和LICENSE.txt文件,那么我们可以执行命令将文件加入暂存区 ,例:
git add *.c git | add LICENSE.txt | git commit -m 'initial project version'
- 现在已经得到了一个存在被追踪文件与初始提交的 Git 仓库
方式 2 从服务器中克隆(clone)一个已经存在的Git仓库(如我们平时使用的一些GitHub上面的库)
git clone https://github.com/libgit2/libgit2
这个命令的作用是将github上面的这个项目克隆到本地,执行完成会在执行这个命令的目录创建一个文件夹libgit2
git clone https://github.com/libgit2/libgit2 mylibgit
这个命令和上面命令的作用相同,但是会将libgit2
文件夹名称改成mylibgit
记录每次更新到仓库
工作目录下的文件都是这两种状态
- 已跟踪 指这个文件被纳入了版本控制,上一次快照中有它们的记录,工作一段时间后,状态可能是未修改或者已经修改或者已暂存区。(从服务器中拷贝下来的所有文件都已经被纳入了版本控制,都属于未修改状态)
- 未跟踪 未纳入版本控制的其它文件。
检查当前文件的状态
git status
可以查看当前文件的状态,输出如下:
js
On branch main // 指明分支
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: main.cpp
new file: readme.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
a.out
git status -s
命令的输出会更简洁,输出如下:
js
A main.cpp
AM readme.txt
?? a.out
- A代表已经暂存
- M代表已经修改
- AM代表已经有暂存版本同时也有修改版本,修改版本还没有添加到暂存
- ??表示新添加没有跟踪的文件
跟踪新文件
git add README.txt
可以将README.txt 这个文件纳入跟踪 git add .
可以将这个文件下的所有文件和文件夹递归纳入跟踪
忽略文件
我们有一些文件不需要进行版本管理,也不希望它们总出现在未跟踪文件列表,那么我们可以创建一个名为 .gitignore 的文件,告诉Git忽视掉一些文件。
例子:
js
*.out
*~
第一行的作用是忽视后缀为.out
的文件,我们再执行git status -s
会显示如下:
js
A main.cpp
AM readme.txt
?? .gitignore
这时候?? a.out
就不见了
文件 .gitignore 的规范:
- 所有空行或者以 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
- 匹配模式可以以(/)开头防止递归。
- 匹配模式可以以(/)结尾指定目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。
例子:
js
# 忽略所有的 .a 文件
*.a
# 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO
# 忽略任何目录下名为 build 的文件夹
build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf
查看已暂存和未暂存的修改
git diff
可以查看暂存版本和未暂存版本的差异,输出如下:
js
**diff --git a/readme.txt b/readme.txt******
**index f7cd3bf..cdaabc9 100644******
**--- a/readme.txt******
**+++ b/readme.txt******
@@ -1 +1,3 @@
这一个Git测试项目
+
+修改测试
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。
git diff --staged
可以查看已暂存的将要添加到下次提交里的内容和现在已经提交的内容的差异。输出如下:
js
****new file mode 100644******
**index 0000000..2443569******
**--- /dev/null******
**+++ b/main.cpp******
@@ -0,0 +1,6 @@
+#include <iostream>
+
+int main(int argc,char * argv[]){
+ std::cout << "hello,world";
+ return 0;
+}
**diff --git a/readme.txt b/readme.txt******
**new file mode 100644******
**index 0000000..f7cd3bf******
**--- /dev/null******
**+++ b/readme.txt******
@@ -0,0 +1 @@
+这一个Git测试项目**
git diff --cached
查看已经暂存起来的变化
提交更新
最后使用 git commit
进行提交。直接使用这个命令的情况下会跳转到vim编辑器里面让你输入提交的内容,如果不希望这样的情况下可以直接使用命令git commit -m "第一次提交"
。输出如下:
js
[main (root-commit) c64f7ea] 第一次提交
2 files changed, 7 insertions(+)
create mode 100644 main.cpp
create mode 100644 readme.txt
请记住,提交时记录的是放在暂存区域的快照。 任何还未暂存文件的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。
跳过使用暂存区域
我们可以使用命令一次性将已经跟踪过的但是做了修改但是还没有提交的文件一次进行提交,不用执行git add [FILE_NAME]
,git commit -a -m '提交信息'
。
移除文件
假设说我们不想在跟踪readme.txt 文件了,那么我们可以执行git rm readme.txt
将readme.txt文件从跟踪列表中移除。
如果说readme.txt 文件是已修改,未暂存,已跟踪的状态,那么我们需要执行git rm -f readme.txt
才可以将文件进行移除。
如果我们想把一个文件从暂存区+仓库中移除,那么我们可以执行git rm --cached README
。
如果说我们不小心把log目添加到暂存区里面了,那么我们可以执行git rm log/\*.log
。
练习+测试 :
- 假设1.0 版本将readme.txt 文件添加到了暂存区,但是1.1 版本将readme.txt 文件删除 了,那么还可以恢复吗?
移动文件
执行git mv main.cpp app.cpp
命令可以将本地文件main.cpp 重命名为app.cpp,并且添加到暂存区。
相当于三条命令
js
mv README.md README
git rm README.md
git add README
这是我们执行git status
,输出如下:
js
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: main.cpp -> app.cpp
deleted: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
查看提交记录
通过 git log
命令我们可以看到提交记录,输出如下:
js
commit c64f7ea1cdd302bb1819fa388d21c89fd59156d5 (**HEAD ->** **main**)
Author: wtbolin <szwtsdsoftware@163.com>
Date: Sun Aug 17 12:17:44 2025 +0800
第一次提交
查看最近两次的提交,git log -p -2
提交的简略统计信息,git log --stat
DIY显示提交,git log --pretty=format:"%h - %an, %ar : %s"
DIY的表:
选项 | 说明 |
---|---|
%H | 提交的完整哈希值 |
%h | 提交的简写哈希值 |
%T | 树的完整哈希值 |
%t | 树的简写哈希值 |
%P | 父提交的完整哈希值 |
%p | 父提交的简写哈希值 |
%an | 作者名字 |
%ae | 作者的电子邮件地址 |
%ad | 作者修订日期(可以用 --date=选项 来定制格式) |
%ar | 作者修订日期,按多久以前的方式显示 |
%cn | 提交者的名字 |
%ce | 提交者的电子邮件地址 |
%cd | 提交日期 |
%cr | 提交日期(距今多长时间) |
%s | 提交说明 |
撤销操作
假设我们进行了一次提交,但是有一个文件我们没有添加到这次提交中,我们可以使用git commit --amend
合并到这次提交。
js
git commit -m 'initial commit'
git add forgotten_file
git commit --amend
最终你只会看到一次提交结果,这相当于用新的提交覆盖旧的提交,保证你的历史的干净,不至于出现"啊,忘了添加一个文件"之类的历史。
取消暂存的文件
我们可以使用 git restore --staged [FILE]
来撤销一个操作,以撤销删除操作为例:
执行 git status
js
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: main.cpp -> app.cpp
deleted: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
执行 git restore --staged readme.txt
执行 git status
js
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: main.cpp -> app.cpp
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
执行git restore readme.txt
,这时候被删除的readme.txt文件就被恢复了
撤销对文件的修改
我们修改一下 app.cpp 文件,然后执行 git status
js
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: app.cpp
no changes added to commit (use "git add" and/or "git commit -a")
这时候我们如果想撤销之前所做的修改,可以执行 git checkout app.cpp
,这是后git会拿最近的提交去覆盖这个文件。
注意:执行git checkout app.cpp
之后,原来对这个文件做的修改就彻底找不回来了,所以要慎重
远程仓库的使用
git remote -v
命令可以显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
js
origin https://github.com/-/-.git (fetch)
origin https://github.com/-/-.git (push)
我们可以执行 git remote add pb https://github.com/paulboone/ticgit
添加一个远程仓库
从远程仓库中获取数据
执行 git fetch <remote>
,这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。这个命令只会将文件下载到你的本地仓库,但是不会修改你的工作区,你需要手动合并
如果希望拉去远程分支并且和本地分支进行合并的情况下,请执行git pull
。
推送到远程分支
git push <remote> <branch>
命令可以将本地分支推送到远程分支。但是这个命令需要你对仓库有写权限+在你提交之前没有别的用户进行提交,如果有别的用户进行提交,那么你需要去拉取最新的仓库进行合并,然后再执行push命令进行推送。
查看某个远程仓库
git remote show <remote>
可以查看远程仓库信息
js
* remote origin
Fetch URL: https://github.com/-/-.git
Push URL: https://github.com/-/-.git
HEAD branch: main
Remote branches:
main tracked
newComputorBranch tracked
Local branches configured for 'git pull':
main merges with remote main
newComputorBranch merges with remote newComputorBranch
Local refs configured for 'git push':
main pushes to main (fast-forwardable)
newComputorBranch pushes to newComputorBranch (up to date)
远程仓库的重命名与移除
git remote rename pb paul
可以对remote进行重命名,这同样也会修改你所有远程跟踪的分支名字。 那些过去引用 pb/master 的现在会引用paul/master。
git remote remove paul
可以移除一个远程仓库。一旦你使用这种方式删除了一个远程仓库,那么所有和这个远程仓库相关的远程跟踪分支以及配置信息也会一起被删除。
打标签 先忽视,暂时可能不会用到
git tag
可以显示标签。
如果只对 1.8.5 系列感兴趣,可以运行:git tag -l "v1.8.5*"
Git别名
主要是简化命令
我们执行 git config --global alias.st status
之后,git st
就和git status
等价了