Author:onceday date:2022年8月8日
满满长路有人对你微笑过嘛…
Modified date:2022年11月22日,完成第一章部分内容。
windows安装可参考文章:git简易配置_onceday_CSDN博客
參考文档:
在分布式的版本控制系统之前,存在集中式的版本控制系统,如CVCS
(Centralized Version Control Systems),其存在一个最大的问题,即安全性和可靠性受限,需要时刻联网。
对于分布式版本控制系统(Distributed Version Control System),其每个客户端都会完整的保留整个历史记录。所以无需联网便可工作,此外还能保证数据安全,可以用任何一个镜像出来的本地仓库恢复。
其它大部分系统以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce、Bazaar 等等) 将它们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异,它们通常称作基于差异 (delta-based)的版本控制。
Git将数据看成是文件系统的快照,在Git中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照,并保存这个索引。如果文件没有变化,则不会重新储存该文件,而是只保留一个链接指向之前存储的文件。
在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。因此Git的速度较需要联网的版本管理系统,具有较大的优势。
Git 中所有的数据在存储前都计算校验和,然后以校验和来引用,这是Git核心的组件,可以发现文件被更改或者损坏。
Git用以计算校验和的机制叫做SHA-1散列(Hash,哈希)。这是一个由40个十六进制字符(0-9和a-f)组成的字符串,基于Git中文件的内容或目录结构计算出来。
24b9da6552252987aa493b52f8696cd6d3b00373
实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
最重要的一点,几乎所有Git操作,都是往Git数据库中添加数据,很难使用Git从数据库中删除数据,也就是说Git几乎不会执行任何可能导致文件不可恢复的操作。这意味着,一旦提交了快照到Git中,就难以再丢失数据。
Git会对每一个文件赋予下面三种状态中的某一种:
因此Git项目拥有三个阶段:工作区、暂存区以及Git目录。
这三阶段具体作用如下:
具体的工作步骤如下:
在每台计算机上,都需要至少配置一次Git环境,如用户名和邮箱、SSH秘钥等。
git configuration
有三个地方存储(以Linux系统为例,其他系统可类推):
/etc/gitconfig
文件,包含系统上每一个用户及他们仓库的通用配置,如果在执行git config
时带上--system
选项,那么它就会读取该文件中的配置变量。~/.gitconfig
或~/.config/git/config
文件,只针对当前用户。执行git config
时带上--global
选项即可。.git/config
,默认情况使用该选项,也可以执行git config
时带上--local
选项。上述不同级别的配置可以覆盖,即.git/config
可以覆盖全局配置,靠近实际仓库的配置优先生效。
使用下面命令即可查看当前系统上配置情况及其对应的文件:
PS C:\Users\admin> git config --list --show-origin
file:"C:\\ProgramData/Git/config" core.symlinks=false
file:"C:\\ProgramData/Git/config" core.autocrlf=true
......
file:"C:\\Program Files\\Git\\mingw64/etc/gitconfig" credential.helper=manager
file:C:/Users/admin/.gitconfig [email protected]
file:C:/Users/admin/.gitconfig user.name=Once Day
配置信息很简单,如下:
git config --global user.name "Once Day"
git config --global user.email "[email protected]"
其中user.name
和user.email
是key
关键字,可以如下查询关键字的信息:
git config --list //列出Git所有能找到的全部配置
git config --show-origin rerere.autoUpdate //列出最后一个生效的配置
因为git
会从多个文件中读取同一个配置变量的不同值,因此可能会看到一些意外之值,所以可以查一下,具体哪个配置文件最后生效的了。
使用以下方式可获取帮助文档:
git help
git --help
如git help config
即可获取帮助文档,这些信息是离线的。如果只想看可用选项的快速参考,可用git
,如git add -h
。
基础的Git使用,包括以下步骤:
此外,还有配置Git来忽略指定文件,迅速简单地撤销错误操作,浏览历史文件,不同提交(commits)之间的差异以及推送(push)和拉取(pull)文件。
通常有两种获取Git项目仓库的方式:
将尚未进行版本控制的本地目录转换为Git仓库
git init
从其他服务器克隆一个已存在的Git仓库
git clone
git clone
命令默认下会克隆远程git仓库中的每一个文件的每一个版本,相当于有了一个远程仓库的数据备份。
git clone https://github.com/libgit2/libgit2 [mylibgit]
如果在克隆远程仓库的时候,自定义本地仓库的名字(mylibgit
),你可以通过额外的参数指定新的目录名。
除了https
协议之外,还可以使用git
和ssh
协议。
工作区中的文件有两种基础状态:traced
和untraced
。
traced
是已跟踪的文件,表明其被纳入了版本控制里面,上一次快照中有它们的记录,在工作一段时间后,她们的状态可能是未修改、已修改或以放入暂存区。
下面是工作区内文件的可能状态以及对应操作:
使用git status
查看当前工作区的状态。
$ git status
On branch work
Your branch is up-to-date with 'document/work'.
no changes added to commit (use "git add" and/or "git commit -a")
上面说明当前工作目录下十分干净,所有已追踪文件在上次提交之后都未被更改过,并且当前目录下也没有任何处于未跟踪状态的新文件。
可以新增几个文件,然后再使用git status
:
ubuntu->c-code:$ git status
On branch master
No commits yet
Untracked files:
(use "git add ..." to include in what will be committed)
syscall
syscall.c
syscall1
syscall1.c
te
te.c
nothing added to commit but untracked files present (use "git add" to track)
使用git add
可以开始跟踪一个文件,比如下面文件就都添加到了暂存状态:
ubuntu->c-code:$ git add .
ubuntu->c-code:$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached ..." to unstage)
new file: syscall
new file: syscall.c
new file: syscall1
new file: syscall1.c
new file: te
new file: te.c
git add
的参数可以是文件或者目录的路径,如果参数是目录,那么该命令将递归地跟踪该目录下的所有文件。
如果此时又更改了上述的几个文件,那么它们的状态也会变成modified
状态,提交的文件版本总是add
时的版本,而不是当前修改的最新版本。
git status
可以添加参数git status -s
来缩短命令的输出:
ubuntu->c-code:$ git status -s
A syscall
A syscall.c
A syscall1
A syscall1.c
A te
AM te.c
标志有两栏,暂存区在第一栏,即第一竖列的A,第二行是工作区,M
表示修改过的文件,??
表示未追踪的文件。
有一些文件无需纳入Git的管理,也不用出现在未追踪文件列表。如一些自动生成的文件,比如日志文件、或者编译过程中创建的临时文件等。可以在根目录下创建.gitignore
文件,用于标识出这些文件。
$ cat ,gitignore
*.[oa]
*~
第一行告诉Git忽略所有以.o
或.a
结尾的文件,一般这类对象文件和存档文件都是编译过程中出现的。第二行告诉Git忽略所有名字以波浪符(~)结尾的文件,许多文件编辑软件使用这样的文件名保存副本。
gitignore
支持以下的格式规范:
#
开头的行都会被Git忽略。!
取反。glob模式值shell所使用的简化的正则表达式:
*
匹配零个或多个任意字符[abc]
匹配任何一个列在方括号中的字符?
可以只匹配一个任意字符。[0-9]
表示匹配所有0到9的数字。**
表示匹配任意中间目录,比如a/**/z
可以匹配a/z
,a/b/z
,a/b/c/z
等。注意,.gitignore
文件可以递归生效到所有子目录,但也可以在子目录中设置单独.gitignore
文件,该文件仅在子目录中生效。
可以直接使用git diff
查看尚未暂存的文件更新了哪些部分,不加参数直接输入git diff
。
ubuntu->c-code:$ git diff
diff --git a/te.c b/te.c
index e5ab3ab..440d905 100644
--- a/te.c
+++ b/te.c
@@ -36,3 +36,5 @@ int main(void) {
}
printf("hello:%lu\n.", sizeof(struct te2));
}
+
+
diff
显示的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容。若要查看已暂存的将要提交到下次提交里的内容,可以使用git diff --staged/cached
命令,它比对已暂存文件与最后一次提交的文件差异。
当确定完文件更改之后,使用git commit
来提交更改:
git commit
然后会启动默认的文件编辑器来输入提交说明,注释行(#开头行)将被忽略:
first commit
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Committer: qcloud
#
# On branch master
#
# Initial commit
......
文本编辑器可以自行指定,使用git config --global core.editor
。一般读取Shell的环境变量EDITOR
。
直接使用git commit
时,也可以直接输入提交信息:
git commit -m "First commit"
每一次提交,都是对项目的一次快照,会被纳入版本管理,以后也都可以回到这个状态。
也可以跳过暂存区域这一步,从而快速提交文档,git commit -a
,Git就会自动把所有已跟踪过的文件暂存起来一并提交。
有时候一些修改已提交到暂存区域中,使用git rm
文件可以从暂存清单中移除某些修改(文件),然后再提交。
如果一个文件被修改了,直接gir rm
它,将会报错:
ubuntu->c-code:$ git rm te.c
error: the following file has local modifications:
te.c
(use --cached to keep the file, or -f to force removal)
这是一个安全特性,用于防止误删尚未添加到快照的数据,这样的数据无法被Git恢复。可以使用git rm -f
来强制删除。
上面的操作会删除当前工作区中的文件,并暂存删除操作这个记录(但未提交到Git中)。
另外一个情况是,想把文件从Git仓库(暂存区域)中移除,但仍然希望保留在当前工作目录中。简单来说,希望文件保留在磁盘上,但是并不想让Git继续跟踪。
git rm
命令后面也可以使用glob
模式,一次匹配大量的文件或目录名字。
可以使用git mv
命令进行移动和重命名操作,如:
git mv file_from file_to
然后使用git status
查看操作的情况:
ubuntu->c-code:$ git mv syscall1 syscall3
ubuntu->c-code:$ git status
On branch master
Changes to be committed:
(use "git restore --staged ..." to unstage)
renamed: syscall1 -> syscall3
git mv
相当于下面三条命令,他们的操作是等价的:
ubuntu->c-code:$ mv syscall1 syscall3
ubuntu->c-code:$ git rm syscall1
ubuntu->c-code:$ git add syscall3
使用git log
可以查看过往提交的历史:
ubuntu->c-code:$ git log
commit 12b18cb74b10f8533010716790c2c2d6480f53d1 (HEAD -> master)
Author: qcloud
Date: Mon Jan 9 13:48:05 2023 +0800
first commit
在默认的情况下git log
会按时间先后顺序列出所有的提交,最近的更新排在最上面。该命令会列出每个提交的SHA-1校验和、作者的名字和电子邮件地址、提交时间以及提交说明。
使用git log -p/--patch -2
,显示每次提交所引入的差异,也可以限制显示的日志条目数量,例如-2
选项来只显示最近的两次提交。
可以使用git log --pretty=online/short/full/fuller
,它们展示信息的格式基本一致,但是详尽程度不一。
使用git log --pretty=format:"%h - %an, %ar : %s"
可以定制记录的显示格式,这样的输出对后期提取分析格外有用,可以维持固定的格式输出。下面是常用的格式占位符的写法和其代表的意义:
选项 | 说明 |
---|---|
%H | 提交的完整哈希值 |
%h | 提交的简写哈希值 |
%T | 树的完整哈希值 |
%t | 树的简写哈希值 |
%P | 父提交的完整哈希值 |
%p | 父提交的简写哈希值 |
%an | 作者名字 |
%ae | 作者的电子邮件地址 |
%ad | 作者修订日期(可以用–date=选项来定制格式) |
%ar | 作者修订日期,按多久以前的方式显示 |
%cn | 提交者的名字 |
%ce | 提交者的电子邮件 |
%cd | 提交日期 |
%cr | 提交日期(距今多长时间) |
%s | 提交说明 |
注意,作者指的是实际做出修改的人,提交者指的是最后将此工作成果提交到仓库的人。
如果上面的选项再加上--graph
结合使用,这个选项添加了一些ASCII字符串来形象展示分支和合并历史。
PS D:\mydream> git log --pretty=format:"%h - %an, %ar : %s" --graph
* 420e524 - onceday, 7 days ago : finish linux log
* 8f76f18 - onceday, 7 days ago : Merge branch 'work' of gitee.com:onceday/mylove into work
|\
| * a217c9f - Once Day, 7 days ago : save
* | c1fa194 - onceday, 10 days ago : add linux log
|/
git log
还可以采用下面的方式来限制git log
的输出:
选项 | 说明 |
---|---|
- |
仅显示最近的n条提交 |
–since,–after | 仅显示指定时间之后的提交 |
–until,–before | 仅显示指定时间之前的提交 |
–author | 仅显示作者匹配指定字符串的提交 |
–committer | 仅显示提交者匹配指定字符串的提交 |
–grep | 仅显示提交说明中包含指定字符串的提交 |
-S | 仅显示添加或删除内容匹配指定字符串的提交 |
例如,使用git log --since=1.weeks
来输出最近1周内的提交记录:
PS D:\mydream> git log --since=1.weeks
commit 003729dc24de66fda7d7a2f2f4140dcffe41e10f
Author: onceday <[email protected]>
Date: Sun Jan 8 23:52:34 2023 +0800
add go language basic
commit a69d6abe9d13a26a4642f1c797f9237f06bde9f3
Author: onceday <[email protected]>
Date: Sun Jan 8 19:11:51 2023 +0800
finished linux kernel syscall summary
上面过滤条件可以组合使用。
在任何一个阶段,你都有可能想要撤销某些操作。部分撤销工作可能导致之前的工作丢失,务必慎重处理。
当我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。此时,可以运行带有--amend
选项的提交命令来重新提交。
git commit --amend
这个命令会将暂存区中的文件提交,如果自上次提交以来你还未做任何修改,那么快照会保持不变,而你所修改的只是提交信息。
启动文本编辑器后,你可以看到之前的提交信息,编辑后会覆盖保存原来的提交信息。
--amend
修复提交,其操作类似于用一个新的提交替换旧的提交,因此旧的提交就像从未存在一样,并不出现在仓库的历史中,这同样也会造成一些风险。
如果想从暂存区中取消某个暂存的文件,可以使用git reset HEAD
来取消暂存。
ubuntu->c-code:$ git status
On branch master
Changes to be committed:
(use "git restore --staged ..." to unstage)
new file: syscall
modified: te.c
ubuntu->c-code:$ git reset HEAD te.c
Unstaged changes after reset:
M te.c
也可以使用提示中的git restore --staged
命令来操作。
如果更改了某个文件之后,不想保留对某个文件的更改,想把它还原成上次提交的时的样子,那么可以使用git restore file
来操作。也可以使用git checkout file
来回退文件。
注意这两个回退命令的风险,一旦撤销本地上的任何修改,Git会用最近提交的版本覆盖它,由于更改没有被提交,因此也就无法找回。
如果想查看远程仓库,可以直接使用git remote
命令,它会列出你指定的每一个远程服务器的简写,如果你已经克隆了自己的仓库,那么至少应该能看到origin。
PS D:\mydream> git remote
document
加上-v
选项,会额外显示需要读写远程仓库使用的Git保存的简写与其对应的URL。
PS D:\mydream> git remote -v
document [email protected]:onceday/mylove.git (fetch)
document [email protected]:onceday/mylove.git (push)
如果你的远程仓库不止一个,该命令会将它们全部列出来。
使用下面的命令即可以添加一个新的远程仓库:
git remote add
然后可以使用字符串pb来代替整个URL,例如拉取信息(不合并)git fetch pb
。
git fetch
这个命令会访问远程仓库,从中拉取所有还没有的数据。执行完成后,将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
如果当前分支设置了跟踪远程分支,那么可以用git pull
命令来自动抓取后合并该远程分支到当前分支。
使用git push
命令可以推送分支到远程服务器:git push
。要推送成功,必须拥有克隆服务器的写入权限,并且之前没有人推送过,这条命令才能生效。
使用git remote show
可以查看一个仓库的详细信息:
PS D:\mydream> git remote show document
* remote document
Fetch URL: [email protected]:onceday/mylove.git
Push URL: [email protected]:onceday/mylove.git
HEAD branch: master
Remote branches:
master tracked
mybook tracked
work tracked
work_pc tracked
Local branch configured for 'git pull':
work merges with remote work
Local refs configured for 'git push':
master pushes to master (local out of date)
work pushes to work (up to date)
work_pc pushes to work_pc (local out of date)
上面的信息包括:远程仓库的URL,跟踪分支,本地可git pull
分支,本地可git push
分支和远程引用的关系。还可能包含不存在于本地分支,从服务器上已移除的远程分支。
可以使用git remote rename
来修改一个远程仓库的简写名,例如想要将pb
重命名为paul
,可以用git remote rename
来操作。例如:
git remote rename pb paul
如果想移除一个远程仓库,已经从服务器上搬走了,或不在想使用某一个特定的镜像,可以使用git remote remove/git remote rm
。当删除了一个远程仓库,那么所有和这个远程仓库相关的远程跟踪分支以及分支信息也会一起被删除。
像其他版本控制系统(VCS)一样,Git可以给仓库历史中某一个提交打上标签,以示重要。比较有代表性的是人们会使用这个功能来标记发布节点。在本节中,你将会学习如何列出已有的标签,如何创建和删除新的标签。
列出标签:
ubuntu->linux_old1:$ git tag
v2.6.11
v2.6.11-tree
v2.6.12
v2.6.12-rc2
......
使用git tag -l
/git tag --list
可以加上匹配标签名的通配模式。
ubuntu->linux_old1:$ git tag -l v4*
v4.0
v4.0-rc1
v4.0-rc2
v4.0-rc3
v4.0-rc4
......
Git支持两种标签:
轻量标签很像一个不会改变的分支,它只是某个特定提交的引用。附注标签是存储在Git数据库中的一个完整对象,它们是可以被校验,其中包含打标签者的名字、电子邮件地址、日期时间。此外还有一个标签信息,并且可以使用GNU Privacy Guard (GPG)签名并验证。通常会建议创建附注标签,这样也可以拥有以上的所有信息。
常见附注标签如下即可,需要使用-a
选项来制定:
ubuntu->c-code:$ git tag -a v1.88 -m "my version 1.88"
ubuntu->c-code:$ git tag
v1.88
-m
选项指定了一条将会存储在标签中的信息,如果没有为附注标签指定一条信息,Git会启动编辑器要求你输入信息。
通过使用git show
命令可以看到标签信息和与之对应的提交信息。
ubuntu->c-code:$ git show v1.88
tag v1.88
Tagger: qcloud
Date: Thu Jan 12 17:07:46 2023 +0800
my version 1.88
commit 9b402a92ac1b0f4362f024604e487f0f52526fb8 (HEAD -> master, tag: v1.88)
Author: qcloud
Date: Mon Jan 9 13:48:05 2023 +0800
first commit, version2
......
输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息。
轻量标签本质上是将提交检验和存储到一个文件中,没有保存任何其他信息,创建轻量便签,只需要提供标签名字:
ubuntu->c-code:$ git tag v2.0-lw
ubuntu->c-code:$ git tag
v1.88
v2.0-lw
如果需要对过去的提交打标签,可以使用git log --pretty=oneline
打印出最近的提交记录,然后使用指定提交的检验和可以打上标签。
git tag -a v1.2 9fceb02
共享标签,git push
命令并不会传送标签到远程仓库服务器上,在创建完便签后必须显示地推送标签到共享服务器上。如下:
git push origin
如果想一次性推送很多标签,也可以使用带有--tags
选项的git push
命令,这将会把所有不在远程仓库服务器上的便签全部推送到那里。推送便签时并不会区分轻量便签和附注便签,没有简单选项能够让你只选择强制推送一种标签。
删除本地标签可以使用下面的命令:
git tag -d
如果需要更新远程仓库,比如同步移除这个便签信息,必须使用git push
来更新的远程仓库。
上面这种操作的含义是,将冒号前面的空值推送到远程标签名,从而高效地删除。也可以使用第二种更直观的删除远程标签的方式:
git push origin --delete
如果想查看某个标签所指向的文件版本,可以使用git checkout
命令,此时仓库会处于(detached HEAD)分离头指针状态。在该状态下,如果做了某些更改然后提交它们,便签不会发生变化,但是新提交将不属于任何分支,并且将无法访问,只能通过确切的提交哈希才能访问。
这个时候可以通过设置git checkout -b version2 v2.0.0
来创建一个新的分支。
git checkout -b version2 v2.0.0
Git不会在输入部分命令时自动推断出想要的命令。如果不想每次都输入完整的Git命令,可以通过git config文件来轻松地为每一个命令设置一个别名。下面是一些例子:
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
要输入git commit
时,只需要输入git ci
。创建别名非常有用,可以将复杂的事情简单化。
git config --global alias.unstage 'reset HEAD --'
当创建上面所示的别名之后,下面两个命令就是等价的:
git unstage fileA
git reset HEAD -- fileA
下面例子创建了一个last
别名命令,可以看到最后一次提交:
PS D:\mydream> git config --global alias.last 'log -1 HEAD'
PS D:\mydream> git last
commit e27c3c347263bf2ab7018aae54bd9e8129e71a2c
Merge: b758878 558ec5e
Author: Once Day
Date: Thu Jan 12 18:29:45 2023 +0800
Merge branch 'work' of gitee.com:onceday/mylove into work
如果,想替换外部命令,而不是一个Git子命令,可以在命令前面加上!
符号,如果是自己要写一些与Git仓库协作的工具的话,那会很有用。如下所示:
git config --global alias.visual "!gitk"
git commit
时,只需要输入git ci
。创建别名非常有用,可以将复杂的事情简单化。
git config --global alias.unstage 'reset HEAD --'
当创建上面所示的别名之后,下面两个命令就是等价的:
git unstage fileA
git reset HEAD -- fileA
下面例子创建了一个last
别名命令,可以看到最后一次提交:
PS D:\mydream> git config --global alias.last 'log -1 HEAD'
PS D:\mydream> git last
commit e27c3c347263bf2ab7018aae54bd9e8129e71a2c
Merge: b758878 558ec5e
Author: Once Day
Date: Thu Jan 12 18:29:45 2023 +0800
Merge branch 'work' of gitee.com:onceday/mylove into work
如果,想替换外部命令,而不是一个Git子命令,可以在命令前面加上!
符号,如果是自己要写一些与Git仓库协作的工具的话,那会很有用。如下所示:
git config --global alias.visual "!gitk"