Git More --2
这是Pro Git 2.3到2.7的阅读笔记,有点长,可以直接看小结。
查看历史修改
使用git log命令可以查看提交历史:
1 | git log |
你将看到类似如下输出:
1 | commit f5c2e8b9d6e4e2a1b3c4d5e6f7a8b9c0d1e2f3g4 |
每个提交都有一个唯一的SHA-1哈希值、作者信息、日期和提交信息。你可以使用各种选项来定制git log的输出格式,例如:
git log --patch:显示每个提交的差异。实际上,你可以指定查看显示历史的数量,例如git log -p2只显示最近两个提交的差异。git log --stat:显示每个提交的统计信息。git log --pretty:自定义输出格式,常见的格式有oneline、short、full、fuller、reference等,如git log --pretty=oneline。git log --pretty=format:"%h - %an, %ar : %s":自定义输出格式,显示简短的哈希值、作者名、相对时间和提交信息。%H:完整的哈希值%h:简短的哈希值%T:树对象的哈希值%t:简短的树对象哈希值%P:父提交的哈希值%p:简短的父提交哈希值%an:作者名字%ae:作者邮箱%ad:作者日期(可以通过--date=选项定制格式)%ar:相对作者日期(如“2 weeks ago”)%cn:提交者名字%ce:提交者邮箱%cd:提交者日期%cr:相对提交者日期%s:提交说明
这里的--date=选项可以设置日期格式,如--date=relative、--date=iso、--date=rfc、--date=short等。
同时,提交者和作者的区别在于,提交者是实际执行提交操作的人,而作者是代码的原始编写者。
git log --grapgh:以图形化的方式显示分支和合并历史。git log --name-only:只显示修改的文件名。git log --since="2 weeks ago":只显示最近两周的提交。git log --until="2024-01-01":只显示直到指定日期的提交。git log --author="Your Name":只显示指定作者的提交。git log --grep="fix":只显示提交信息中包含指定关键字的提交。git log -S "function_name":只显示添加或删除了指定字符串的提交。
git log --author='Jonio C Hamano' --since'2008-10-01' --until='2008-10-31' --no-merges --pretty=format:'%h %ad | %s%d [%an]' -- t/: 显示2008年10月由Jonio C Hamano提交的非合并提交,格式化输出。其中--no-merges选项用于排除合并提交,-- t/表示只查看t/目录下的文件。
撤销操作
有时你可能忘记提交了一些更改,或者想要修改最近的提交信息。这时你可以使用git commit --amend命令来修改最近的提交:
1 | git commit --amend |
这将打开一个文本编辑器,允许你修改最近的提交信息。如果你想添加更多的更改到最近的提交中,可以先使用git add命令添加更改,然后再运行git commit --amend。最终新的提交将会替代原有的提交。
注意!使用git commit --amend会改变提交的哈希值,因此如果你已经将该提交推送到远程仓库,建议不要使用此命令,以免引起版本冲突。
假如你不小心把不需要提交的文件添加到了暂存区,我们之前介绍过使用git rm --cached <file>命令将其从暂存区移除,而使用git restore --staged <file>命令可实现相似的效果:
1 | git restore --staged <file> |
这将把指定的文件从暂存区移除,但保留工作目录中的更改。
要理解这两个命令的区别,先要理解:Git版本记录的是快照,每一个提交都拥有当前Git所跟踪的所有文件所构成的快照。这些快照一直都呆在暂存区(即cache,staging area)中。git rm --cached命令实际上会直接从暂存区中移除该文件,此时提交快照将会缺失该文件,同时该文件将被标记为未跟踪状态,也就是说,使用了这个命令并提交之后,Git仓库中将不再出现这个文件,但是工作目录中仍然存在,且状态为未跟踪。git restore --staged命令则不会将该文件从暂存区中移除,而是将它恢复至上一次提交的版本,同时保留当前工作目录中的版本,使用这个命令并提交之后,Git仓库中的原文件不会改变,而工作目录中则保持Modified的状态。
假如你想撤销对文件的修改,或者想要恢复成上次提交的样子,可使用git checkout -- <file>命令:
1 | git checkout -- <file> |
这将把工作区中的文件恢复到暂存区中的状态。这个操作是不可逆的危险命令。
从Git 2.23版本开始,推荐使用git restore <file>命令来实现同样的效果:
1 | git restore <file> |
这将把指定的文件恢复到上次提交的状态,丢弃所有未提交的更改。这个操作同样是不可逆的危险命令。
远程仓库
远程仓库(Remote Repository)是托管在网络服务器上的版本仓库。它拥有一套权限系统,允许多个用户协同工作,并且可以通过网络进行访问和操作。远程仓库通常托管在Git服务器上,如GitHub、GitLab、Bitbucket等。但是远程仓库实际上也可以存在于你的本地主机上,但是你依然需要通过标准的拉取、推送和抓取等操作来与之交互。
查看远程仓库
你可以使用git remote命令来查看已经配置的远程仓库。
1 | git clone https://github.com/foo/bar.git |
默认情况下,Git会将克隆的远程仓库命名为origin。你可以使用git remote -v命令来查看远程仓库的详细信息:
1 | git remote -v |
这显示了远程仓库的名称和URL,以及用于抓取和推送的URL。实际上,如果你与多个用户协同工作,可能会有多个远程仓库。比如这样
1 | git remote -v |
添加远程仓库
这些仓库并不都存在于你的本地机器上,而是以链接的方式存在于config文件中。你可以使用git remote add <name> <url>命令来添加一个新的远程仓库,这会向config文件中添加一条新的远程仓库配置:
1 | git remote add charlie https://github.com/charlie/bar.git |
拉取
如果你想要拉取所有alice的仓库中有的但是你没有的信息,可以使用git fetch <remote>命令:
1 | git fetch alice |
这里,“拉取”指的是从远程仓库获取最新的提交和分支信息,但不会自动合并到当前分支。具体来说,git fetch命令会下载远程仓库中的所有分支和标签的最新状态,并将它们存储在本地的远程跟踪分支(即本地对远程仓库某个分支的指向)中,例如alice/feature-x。这样,你可以在本地查看和操作这些远程分支,而不会影响当前的工作分支,也不会影响远程仓库的状态。当然,你也可以选择将这个分支合并到当前分支,或者创建一个新的本地分支来跟踪它。
如果你为当前分支设置了跟踪远程分支,那么你可以使用git pull <remote> <branch>命令来拉取并合并远程分支的更改:
1 | git pull alice feature-x |
这个命令会从alice的feature-x分支拉取最新的更改,并将其合并到当前分支。git pull实际上是git fetch和git merge的组合操作。默认情况下,git clone命令自动设置本地main分支来跟踪远程仓库的main分支,因此你可以直接使用git pull命令来更新本地main分支。
推送
要将本地的更改推送到远程仓库,可以使用git push <remote> <branch local>:<branch remote>命令:
1 | git push alice main:feature-x |
这个命令会将本地的main分支的更改推送到alice的feature-x分支。如果远程分支不存在,Git会自动创建它。你也可以使用简化的命令git push <remote> <branch>,这会将本地分支推送到同名的远程分支:
1 | git push alice main |
这个命令会将本地的main分支推送到alice的main分支。遇到多个推送产生冲突的情况时,Git会拒绝推送操作,并提示你先拉取远程分支的更改并解决冲突,然后再尝试推送。
查看远程仓库状态
如果想要查看远程仓库的状态,可以使用git remote show <remote>命令:
1 | git remote show alice |
它会显示远程仓库的URL、默认分支、跟踪的远程分支以及本地分支与远程分支的对应关系等信息。
重命名与移除
如果你想要重命名一个远程仓库,可以使用git remote rename <old-name> <new-name>命令:
1 | git remote rename alice alice-updated |
如果你想要移除一个远程仓库,可以使用git remote remove <name>命令:
1 | git remote remove bob |
标签
标签(Tag)是用于标记特定提交的引用,通常用于标记发布版本。标签可以是轻量级的(lightweight)或附注的(annotated)。轻量级标签只是一个指向特定提交的指针,而附注标签则包含更多的信息,如标签创建者、日期和标签信息。
列出标签
你可以使用git tag命令来列出所有标签:
1 | git tag |
也可以使用git tag -l "v1.*"命令来列出符合特定模式的标签:
1 | git tag -l "v1.*" |
创建标签
要创建一个轻量级标签,可以使用git tag <tagname>命令:
1 | git tag v2.1 |
这个标签仅仅是一个指向当前提交的指针。
要创建一个附注标签,可以使用git tag -a <tagname> -m "message"命令:
1 | git tag -a v2.2 -m "Version 2.2 release" |
这个标签则是存储在Git数据库中的一个完整对象,包含标签创建者、日期和标签信息,并可以用GPG进行签名以验证标签的真实性。可以使用git show <tagname>命令来查看标签的详细信息:
1 | git show v2.2 |
最后一行显示了标签所指向的提交信息。
后期创建标签
如果你想要为之前的某个提交创建标签,可以在git tag命令后面指定提交的哈希值:
1 | git tag -a v1.5 f5c2e8b -m "Version 1.5 release" |
共享标签
默认情况下,git push命令不会推送标签到远程仓库。要推送标签,可以使用git push <remote> <tagname>命令:
1 | git push origin v2.2 |
你也可以使用git push <remote> --tags命令来推送所有标签:
1 | git push origin --tags |
这样,当其他人拉取远程仓库时,他们也会获得这些标签。
删除标签
如果你想要删除一个本地标签,可以使用git tag -d <tagname>命令:
1 | git tag -d v1.5 |
如果你想要删除一个远程标签,可以使用git push <remote> :refs/tags/<tagname>命令:
1 | git push origin :refs/tags/v1.5 |
或者使用这种简化的语法:
1 | git push origin --delete tag v1.5 |
检出标签
如果你想要查看某个标签所指向的文件版本,可以使用git checkout <tagname>命令:
1 | git checkout v2.0 |
这会将工作目录切换到标签所指向的提交,但会进入“分离头指针(detached HEAD)”状态。在这种状态下,你可以查看文件内容,但不能直接在该状态下进行提交。如果你想要在标签的基础上进行开发,建议创建一个新的分支:
1 | git checkout -b new-branch v2.0 |
如果在此之后又进行了一次提交,v2.0标签仍然指向原来的提交,而new-branch分支则指向新的提交。
Git别名
Git允许你为常用的命令创建别名,以简化操作。你可以使用git config --global alias.<alias-name> '<command>'命令来创建一个全局别名。例如:
1 | git config --global alias.st 'status' |
小结
在本节中,我们介绍了如何使用Git进行版本控制,包括查看提交历史、撤销操作、远程仓库的管理、标签的使用以及创建Git别名等。通过这些操作,你可以更高效地管理代码版本,协同工作,并保持代码的整洁和可追溯性。
下面是一个命令总结:
git log:查看提交历史。git log --oneline:以简洁的方式查看提交历史。git log --graph:以图形化方式查看提交历史。git log --pretty=format:"%h - %an, %ar : %s":自定义提交历史的输出格式。git log --patch:显示每个提交的差异。git log --stat:显示每个提交的统计信息。git log --name-only:只显示修改的文件名。git log --since="2 weeks ago":只显示最近两周的提交。git log --until="2024-01-01":只显示直到指定日期的提交。git log --author="Your Name":只显示指定作者的提交。git log --grep="fix":只显示提交信息中包含指定关键字的提交。git log -S "function_name":只显示添加或删除了指定字符串的提交。git commit --amend:修改最近的提交。git restore --staged <file>:将文件从暂存区移除,但保留工作目录中的更改。git checkout -- <file>:丢弃工作目录中的更改,恢复到上次提交的状态。git remote:查看远程仓库。git remote add <name> <url>:添加一个新的远程仓库。git remote -v:查看远程仓库的详细信息。git remote show <remote>:查看远程仓库的状态。git remote rename <old-name> <new-name>:重命名远程仓库。git remote remove <name>:移除远程仓库。git fetch <remote>:从远程仓库获取最新的提交和分支信息。git pull <remote> <branch>:拉取并合并远程分支的更改。git push <remote> <branch local>:<branch remote>:将本地分支的更改推送到远程仓库。git tag:列出所有标签。git tag <tagname>:创建一个轻量级标签。git tag -a <tagname> -m "message":创建一个附注标签。git tag -l "v1.*":列出符合特定模式的标签。git tag -d <tagname>:删除本地标签。git checkout <tagname>:检出标签所指向的提交。git push <remote> <tagname>:推送标签到远程仓库。git push <remote> --tags:推送所有标签到远程仓库。git config --global alias.<alias-name> '<command>':创建Git命令的别名。
附录:关于git的文件操作
git的文件操作相当之多,比较难以理解。但是实际上,它们都代表的是某种覆盖,也就是用一个版本的文件覆盖这个版本的文件。
Git中,文件的版本仅有三种:HEAD(最近一次提交),INDEX(暂存区),WORKTREE(工作区)。不同的操作实际上使用这三个版本中的一种覆盖另一种,或者直接删除其中一种。下面是根据这个思想制作的一张命令对照表(gpt generated)
| 命令 | 本质操作(树之间的关系) | 实际语义 |
|---|---|---|
git add file |
INDEX ← WORKTREE |
暂存修改 |
git commit |
HEAD ← INDEX |
提交暂存区 |
git restore file |
WORKTREE ← INDEX |
丢弃未暂存修改 |
git restore --staged file |
INDEX ← HEAD |
取消暂存 |
git restore --staged --worktree file |
INDEX ← HEADWORKTREE ← HEAD |
完全回滚文件 |
git checkout -- file |
WORKTREE ← INDEX |
等价 restore |
git reset HEAD file |
INDEX ← HEAD |
取消暂存 |
git reset --hard HEAD |
INDEX ← HEADWORKTREE ← HEAD |
核爆回滚 |
git rm file |
WORKTREE ← ∅INDEX ← ∅ |
删除并暂存 |
git rm --cached file |
INDEX ← ∅ |
只从版本控制删除 |
rm file(shell) |
WORKTREE ← ∅ |
仅删工作区 |
2026年1月18日。