目录
- Git 文件管理
- 检测文件状态 status
- 跟踪新文件 add
- 提交更新 commit
- 撤销提交 Commit
- Git 校验和
- 历史查看 log
- 版本回退 reset
- git 忽略文件
- Git 分支管理
- Git 提交对象
- Git master分支
- Git 分支管理
- 本地分支管理
- 远程分支管理
- 分支hotfix处理
- Git 工作流
- 常见分支冲突处理
- 分支合并冲突处理
- 本地分支的上游分支
- 拒绝合并不相干的历史
- Git 远程仓库管理
- 远程仓库概念
- 远程仓库验证
- 凭证
- SSH密钥
- 管理远程仓库
- 远程仓库克隆
- 常见的开源协议
- Git 标签管理
- 创建tag
- 删除和检出tag
- Git rebase 的使用
- Git rebase 概念
- Git rebase 原理
- rebase和merge的选择
Git 文件管理
检测文件状态 status
工作区中文件分成 未跟踪 和 已跟踪 两大类,已跟踪的文件是被 Git 管理的文件,它们又可以分成
未修改、已修改 和 已暂存 三种类型。
文件状态检测命令:
git status
Case:
nathanchen@192 test % git status
On branch masterNo commits yetUntracked files:(use "git add <file>..." to include in what will be committed)index.htmlnothing added to commit but untracked files present (use "git add" to track)
以精简的方式显示文件状态:
# 以精简的方式显示文件状态
git status -s
git status --short
未跟踪文件前面有红色的 ?? 标记,例如:
nathanchen@192 test % git status -s
?? index.html
跟踪新文件 add
使用命令 git add 开始跟踪一个文件。 所以,要跟踪 index.html 文件,运行如下的命令即可:
git add index.html
此时再运行 git status 命令,会看到 index.html 文件在 Changes to be committed 这行的下面,说明已被跟踪,并处于暂存状态:
nathanchen@192 test % git status
On branch masterNo commits yetChanges to be committed:(use "git rm --cached <file>..." to unstage)new file: index.html
以精简的方式显示文件的状态:新添加到暂存区中的文件前面有绿色的 A 标记
nathanchen@192 test % git status -s
A index.html
向暂存区中一次性添加多个文件
如果需要被暂存的文件个数比较多,可以使用如下的命令,一次性将所有的新增和修改过的文件加入暂存区:
git add .
提交更新 commit
现在暂存区中有一个 index.html 文件等待被提交到 Git 仓库中进行保存。可以执行 git commit 命令进行提交,其中 -m 选项后面是本次的提交消息,用来对提交的内容做进一步的描述:
git commit -m "新建了index.html文件"
提交成功之后,会显示如下的信息:
nathanchen@192 test % git commit -m "feat: init Project"
[master (root-commit) 3481215] feat: init Project1 file changed, 11 insertions(+)create mode 100644 index.html
提交成功之后,再次检查文件的状态,得到提示如下:
nathanchen@192 test % git status
On branch master
nothing to commit, working tree clean
撤销提交 Commit
撤销最近的一次 commit
,保留更改
如果你想撤销最近的一次提交,但 保留更改(即保持文件在暂存区和工作区未改变),可以使用 git reset
命令:
git reset --soft HEAD~1
解释:
-soft
:撤销提交,但保留更改在暂存区中,允许你重新提交或修改提交信息。HEAD~1
:表示最近一次提交,HEAD~1
是HEAD
(当前分支最新提交)的上一个提交。
撤销最近的一次 commit
,完全删除更改
如果你想撤销最近的提交,并且 删除所有更改(将工作目录恢复到提交前的状态),可以使用 git reset --hard
:
bash复制代码
git reset --hard HEAD~1
解释:
-hard
:撤销提交,同时删除工作目录和暂存区中的所有更改。这个操作是不可逆的,所有未提交的修改将会丢失。
修改最近的一次 commit
信息
如果你只是想修改最近一次提交的 提交信息(而不改变内容),可以使用 git commit --amend
:
git commit --amend
这会打开一个编辑器,让你修改上一次提交的提交信息。
Git 校验和
Git 中所有的数据在存储前都计算校验和,然后以 校验和 来引用。
Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希);
这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来;
历史查看 log
如果希望回顾项目的提交历史,可以使用 git log 这个简单且有效的命令
git log
命令回车后,会显示以下内容
commit 3481215de8c26faad0ebc764b28dcbbc4c2a5a0b (HEAD -> master)
Author: nathan.chen <503400835@qq.com>
Date: Fri Sep 13 16:51:11 2024 +0800feat: init Projectgi
历史内容精简显示:
git log --pretty=oneline
历史内容精简显示,并以图状显示:
git log --pretty=oneline --graph
如果你想查看包含版本回退的历史记录:
# 这个命令可以查看之前所有的commit与reset命令
git reflog --pretty=oneline
版本回退 reset
如果想要进行版本回退,我们需要先知道目前处于哪一个版本:Git通过HEAD指针记录当前版本。
- HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交;
- 理解 HEAD 的最简单方式,就是将它看做 该分支上的最后一次提交 的快照;
我们可以通过HEAD来改变Git目前的版本指向:
- 上一个版本就是HEAD,上上一个版本就是HEAD^;
- 如果是上1000个版本,我们可以使用HEAD~1000;
- 我们可以指定某一个commit id;
# 根据指定的提交 ID 回退到指定的版本
git reset --hard HEAD^
git reset --hard HEAD~1000
git reset --hard 2d44982
git 忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在 未跟踪文件列表。
- 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等;
- 我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件的模式;
在实际开发中,这个文件通常不需要手动创建,在必须的时候添加自己的忽略内容即可;
比如下侧是创建的Vue项目自动创建的忽略文件:
- 包括一些不需要提交的文件、文件夹;
- 包括本地环境变量文件;
- 包括一些日志文件;
- 包括一些编辑器自动生成的文件;
文件 .gitignore 的格式规范如下:
① 以 # 开头的是注释
② 以 / 结尾的是目录
③ 以 / 开头防止递归
④ 以 ! 开头表示取反
⑤ 可以使用 glob 模式进行文件和文件夹的匹配(glob 指简化了的正则表达式)
Git 分支管理
Git 提交对象
这一块主要讲述下 Git 的原理。
在进行提交操作时,Git 会保存一个提交对象(commit object):
该提交对象会包含一个指向暂存内容快照的指针;
该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针;
- 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象;
- 而由多个分支合并产生的提交对象有多个父对象;
接下来,通过实践的方式来探索 Git 提交 的原理。
进入目录下的隐藏文件夹 .git
,所有需要存储的东西都会存储在 git 下的 objects 中:
nathanchen@192 tests2 % ls
index.html
nathanchen@192 tests2 % cd .git
nathanchen@192 .git % ls
HEAD config description hooks info objects refs
首先,我们修改一波 index.html
文件,然后进行git add .
,这样会把所有文件放入到暂缓区中,再次查看对应文件,会发现多了 69 文件夹,里面存放二进制文件。
nathanchen@192 objects % ls
69 info pack
接下来查看文件的种类和对应内容
git cat-file -t file # 查看文件的种类
git cat-file -p file # 查看文件的内容
nathanchen@NathansMacBook-Pro 05 % pwd
/test3/.git/objects/69
nathanchen@192 69 % ls
763a710c37f7ec0e610fb4ae1cbde7e8e7524d
nathanchen@192 69 % git cat-file -t 6976
blob
nathanchen@192 69 % git cat-file -p 6976
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body></body>
</html>%
注:这里的 69 指的是 69 文件夹,而 76 指的69文件夹下的 76 开头的文件。
然后,我们进行一波提交,并在对应文件夹下查看,发现新增了 0a 和 8d 两份文件
nathanchen@NathansMacBook-Pro test3 % git commit -m "1 commit"
[master (root-commit) 8d8182a] 1 commit1 file changed, 11 insertions(+)create mode 100644 index.html
nathanchen@192 objects % ls
0a 69 8d info pack
查看 0a 和 8d 两个文件夹及其对应文件,发现 0a 是包含文件依赖关系的对象,8d 为 Commit 对象
nathanchen@192 0a % git cat-file -t 0a68
tree
nathanchen@192 0a % git cat-file -p 0a68
100644 blob 69763a710c37f7ec0e610fb4ae1cbde7e8e7524d index.html
nathanchen@192 8d % git cat-file -t 8d81
commit
nathanchen@192 objects % git cat-file -p 8d81
tree 0a683a4b7535845bb37e7444ab95404933d547e5
author Captain Drake <9393079+captain_drake@user.noreply.gitee.com> 1726479021 +0800
committer Captain Drake <9393079+captain_drake@user.noreply.gitee.com> 1726479021 +08001 commit
这时候,我们再次通过 log 来进行查看提交历史,会发现 8d81…
正好对应 objects 下新增的文件
nathanchen@NathansMacBook-Pro test3 % git log
commit 8d8182a27fe263a6c20a8136c1dbc7768f988e02 (HEAD -> master)
Author: Captain Drake <9393079+captain_drake@user.noreply.gitee.com>
Date: Mon Sep 16 17:30:21 2024 +08001 commit
(END)
总结:
1-关于 git add 和 git commit 的底层操作
git add
会将对应文件转化为二进制文件保存在 objects
里面。
git commit
会输出一个 Commit 对象,里面包含属性tree,它会指向另一个对象,这个对象包含本次提交中关联对象的映射关系,从而找到在暂存区中的二进制文件。
结合以上案例进行理解:
2-关于每次 Commit 形成的链条
每次 Commit 都会输出 Commit对象,里面包含 tree 的相关信息。除了第一次的 Commit对象,之后的 Commit 对象都会包含 parent 属性,从而指向前一次 Commit,最终形成提交的历史记录。
Git master分支
**概念:**Git 的分支本质上是指向提交节点的可变指针,默认名字是 master(Github上默认是main)
**默认分支:**Git 的默认分支名字是 master,在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。master 分支会在每次提交时自动移动。
如果根据以下去查找 HEAD 文件,会发现HEAD指向了第一次的Commit对象,即8d。
nathanchen@NathansMacBook-Pro logs % pwd
/test3/.git/logs
nathanchen@NathansMacBook-Pro logs % ls
HEAD refs
nathanchen@NathansMacBook-Pro logs % cat HEAD
0000000000000000000000000000000000000000 8d8182a27fe263a6c20a8136c1dbc7768f988e02 Captain Drake <9393079+captain_drake@user.noreply.gitee.com> 1726478021 +0800 commit (initial): 1 commit
Git 分支管理
本地分支管理
**创建新分支:**基于当前分支,创建一个新的分支
git branch 分支名称
**切换分支:**切换到指定的分支上进行开发
git checkout 分支名称
**分支的快速创建和切换:**创建指定名称的新分支,并立即切换到新分支上
git chechout -b 分支名称
Git通过 HEAD 指针指向分支:
比如我们现在有两个分支,一个是 master,还有一个 testing,当我们切换到 testling 时,HEAD会指 testing 分支,如下所示:
**合并分支:**将完成后的代码合并到 master 主分支上:
# 1. 切换到 master 分支
git checkout master
# 2. 在 master 分支上运行 git merge 命令, 将 login 分支的代码合并到 master 分支
git merge login
当把功能分支的代码合并到 master 主分支上以后,就可以使用如下的命令,删除对应的功能分支:
git branch -d 分支名称
注:删除分支,只是删除分支对应的指针,比如下面的图,我们删除 hotfix 分支,只是删除 hotfix 指针,而对应的 hotfix 的提交历史并不会被删除。
远程分支管理
查看远程仓库的分支
git branch -r
如果显示 origin/master
这表示远程仓库中存在一个名为 master
的分支,而 origin
是默认的远程仓库别名。
-
Q:本地和远程都有分支a,之后远程分支a被别人删除了,本地执行git branch -r却发现远程仓库还有分支a,请问原因是什么?
当你本地执行
git branch -r
命令后,仍然看到远程分支a
,即使该分支已经在远程仓库中被别人删除,这种情况的根本原因是 本地的远程分支引用没有更新。Git 中的远程分支引用并不会自动与远程仓库保持实时同步,你需要手动更新本地的远程分支引用。具体原因:
- 远程分支引用未更新:
- Git 会在你执行某些命令时(如
git fetch
或git pull
)同步远程分支信息。但是,如果远程的某个分支(如a
)已经被别人删除,而你本地的远程分支引用没有更新,它依然显示旧的分支列表。 - 也就是说,你本地的远程分支引用可能仍然保留了
a
,即便该分支在远程仓库中已经不存在。
- Git 会在你执行某些命令时(如
- 远程分支删除后本地不会自动清理:
- 当一个远程分支被删除时,本地的 Git 并不会自动删除对应的远程分支引用。这意味着你依然可以在本地看到它,直到你手动更新这些引用。
解决方法:
为了使本地与远程分支的状态保持同步,你需要 清理本地的远程分支引用。可以使用以下命令来删除本地对已不存在的远程分支的引用:
- 使用
git fetch --prune
命令:
这个命令会同步远程分支信息,并且清理本地已经不存在于远程的分支引用。
git fetch --prune
-prune
选项会删除本地远程分支引用中那些已在远程仓库删除的分支。
- 配置自动清理(可选):
如果你希望每次执行
git fetch
时自动清理远程分支引用,可以使用以下命令进行配置:git config --global fetch.prune true
这样,Git 在执行
git fetch
时会自动清理那些在远程仓库中已经删除的分支引用。总结:
远程分支
a
已经在远程仓库中被删除,但本地仍显示它的原因是 Git 没有自动更新远程分支引用。你可以使用git fetch --prune
命令来手动更新并清理本地的远程分支引用。 - 远程分支引用未更新:
推送分支到远程
当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上
git push origin <branch>
跟踪远程分支
当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支;
如果你愿意的话可以设置其他的跟踪分支,可以通过运行 git checkout --track /
如果你尝试检出的分支 (a) 不存在且 (b) 刚好只有一个名字与之匹配的远程分支,那么 Git 就会为你创建一个跟踪分支;
git checkout --track <remote>/<branch>
git checkout <branch>
注:我个人一般直接创个空文件夹,然后 git clone
项目到本地,就会直接有对应的远程分支,不必再去处理上面的跟踪远程分支。
删除远程分支
回忆一下删除本地分支:
git branch -d <branch>
如果某一个远程分支不再使用,我们想要删除掉,可以运行带有 --delete 选项的 git push 命令来删除一个远程分支。
git push origin --delete <branch>
**案例:**创建分支 develop并管理
当我们创建一个新的文件夹,开始克隆项目时,发现并没有develop分支:
nathanchen@192 test2 % git clone https://github.com/NathanCh3n/tests.git
Cloning into 'tests'...
remote: Enumerating objects: 29, done.
remote: Counting objects: 100% (29/29), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 29 (delta 9), reused 26 (delta 6), pack-reused 0 (from 0)
Receiving objects: 100% (29/29), done.
Resolving deltas: 100% (9/9), done.
nathanchen@192 test2 % cd tests
nathanchen@192 tests % git branch
* main
(END)
其实远程仓库有 develop 分支,但是本地并没有,因此,我们可以进行如下操作:
nathanchen@192 tests % git checkout --track origin/develop
branch 'develop' set up to track 'origin/develop'.
Switched to a new branch 'develop'
nathanchen@192 tests % git branch
* developmain
(END)
以上就可以创建,或者可以更简单,采用以下命令:
git checkout develop
以上命令会自动检测本地是否有 develop 分支,如果没有,会检测远程仓库是否有 deveklop 分支,如果有则会自动创建并且跟踪。
分支hotfix处理
背景:我们在c1版本处打了一次tag为v1.0.0,然后往后继续开发,但是发现v1.0.0版本有bug需要修
解决方案:
- 切换到 v1.0.0 处
- 创建 hotfix 分支,并切换到此处
- 修复bug
- 提交并发布版本 v1.0.1
- 切换到 master 分支,并合并 v1.0.1
- (可能会出现的代码冲突问题)解决完问题,提交版本
以下命令供参考:
nathanchen@192 test % git checkout v1.0.0
Note: switching to 'v1.0.0'.You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:git switch -c <new-branch-name>Or undo this operation with:git switch -Turn off this advice by setting config variable advice.detachedHead to falseHEAD is now at d0da935 2 commit
nathanchen@192 test % git checkout -b hotfix
Switched to a new branch 'hotfix'
nathanchen@192 test % git commit -a -m "hotfix1 commit"
[hotfix fdbfe93] hotfix1 commit1 file changed, 1 insertion(+)
nathanchen@192 test % git commit -a -m "hotfix2 commit"
[hotfix 7b9cbaa] hotfix2 commit1 file changed, 1 insertion(+)
nathanchen@192 test % git tag v1.0.1
nathanchen@192 test % git checkout master
Switched to branch 'master'
nathanchen@192 test % git merge v1.0.1
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
nathanchen@192 test % git commit -a -m "5 commit"
[master 6efa863] 5 commit
nathanchen@192 test % git log --pretty=oneline --graph
* 6efa86320cdfa6e058c992f49453f8c9a237497c (HEAD -> master) 5 commit
|\
| * 7b9cbaaa4260d495dea488ed9063b684f77e26f1 (tag: v1.0.1, hotfix) hotfix2 commit
| * fdbfe9389fb0fe2f856ad309313f409a7837372d hotfix1 commit
* | dee281c6edb32182703ea503f3f7c815a8c0b2d4 4 commit
* | c2986000fa1b8c8a3c09bb914daac9a5eea3d631 3 commit
|/
* d0da9356a0638019936371c256e3fab088a65d8a (tag: v1.0.0) 2 commit
* 175f785929ce71fbf17ab3902f615d8dc515eaf0 1 commit
* 3bdf6e521bd61d87df862a6386d190ee829dadf2 init commit
Git 工作流
由于Git上分支的使用的便捷性,产生了很多Git的工作流:
也就是说,在整个项目开发周期的不同阶段,你可以同时拥有多个开放的分支;
你可以定期地把某些主题分支合并入其他分支中;
比如以下的工作流:
master作为主分支
develop作为开发分支,并且有稳定版本时,合并到master分支中
topic作为某一个主题或者功能或者特性的分支进行开发,开发完成后合并到develop分支中
比较常见的 git flow:
理解:
研发主要在 Develop 上进行开发,当开发有一定成果,可以发布时,会把对应版本合并到 Master 上,并打上对应的 tag v1.0。
当研发发现 v1.0 上有重大 bug 时,会建立 Hotfix 分支,当修复完成后,会合并到 Master 分支上,并打上 tag v1.1。同时,也会将 Hotfix 合并到 Develop 分支上,然后继续进行开发。
如果研发需要新增一些新的特性,可以依据 Develop 分支建立一个 Feature 分支。若这些新特性研发完成,则会合并到 Develop 分支上。若未来不需要,则直接删除这个 Feature 分支即可。
当然,有些公司进行开发会用到 Release 分支。在 Develop 分支上进行开发完成后,会创建 Release 分支,测试会在这上面进行功能测试,若有问题则需要进行修复,并发布一个版本。若没有问题,则将 Release 分支合并到 Master 分支和 Develop 分支上。
常见分支冲突处理
分支合并冲突处理
分支合并冲突会通过可视化界面来进行处理,左边是待合并分支,右边是当前分支,下边是合并后的最终代码展示
通过右上角功能区来处理冲突问题,分别是保留此分支代码和与此分支原先代码进行对比
本地分支的上游分支
问题:本地创建了新分支 develop
,经过开发进行提交并推送,发现以下问题
nathanchen@192 tests % git push
fatal: The current branch develop has no upstream branch.
To push the current branch and set the remote as upstream, usegit push --set-upstream origin developTo have this happen automatically for branches without a tracking
upstream, see 'push.autoSetupRemote' in 'git help config'.
原因分析:develop分支没有设置上游分支(upstream branch)。因此,Git 不知道要将本地的 develop
分支推送到哪个远程分支。
解决方案:
git push --set-upstream origin develop
此时再次查看远程分支,会发现远程有 develop
分支:
nathanchen@192 tests % git branch -r
------origin/HEAD -> origin/mainorigin/developorigin/main
(END)
拒绝合并不相干的历史
问题:合并远程分支时,拒绝合并不相干的历史
$ git merge
fatal: refusing to merge unrelated histories
原因:我们将两个不相干的分支进行了合并:
参考:https://stackoverflow.com/questions/37937984/git-refusing-to-merge-unrelated-histories-on-rebase
简单来说就是:过去git merge允许将两个没有共同基础的分支进行合并,这导致了一个后果:新创建的项目可能被一个毫不怀疑的维护者合并了很多没有必要的历史,到一个已经存在的项目中,目前这个命令已经被纠正,但是我们依然可以通过-allow-unrelated-histories选项来逃逸这个限制,来合并两个独立的项目。
Git 远程仓库管理
远程仓库概念
什么是远程仓库(Remote Repository)呢?
目前我们的代码是保存在一个本地仓库中,也就意味着我们只是在进行本地操作;
在真实开发中,我们通常是多人开发的,所以我们会将管理的代码共享到远程仓库中;
那么如何创建一个远程仓库呢?
远程仓库通常是搭建在某一个服务器上的(当然本地也可以,但是本地很难共享);
所以我们需要在Git服务器上搭建一个远程仓库;
目前我们有如下方式可以使用Git服务器:
使用第三方的Git服务器:比如GitHub、Gitee、Gitlab等等;
在自己服务器搭建一个Git服务;
远程仓库验证
常见的远程仓库有哪些呢?目前比较流行使用的是三种:
GitHub:https://github.com/
Gitee:https://gitee.com/
自己搭建Gitlab:http://152.136.185.210:7888/
对于私有的仓库我们想要进行操作,远程仓库会对我们的身份进行验证:
如果没有验证,任何人都可以随意操作仓库是一件非常危险的事情;
目前Git服务器验证手段主要有两种:
方式一:基于HTTP的凭证存储(Credential Storage);
方式二:基于SSH的密钥;
凭证
因为本身HTTP协议是无状态的连接,所以每一个连接都需要用户名和密码:
如果每次都这样操作,那么会非常麻烦; 幸运的是,Git 拥有一个凭证系统来处理这个事情;
下面有一些 Git Crediential 的选项:
选项一:默认所有都不缓存。 每一次连接都会询问你的用户名和密码;
选项二:“cache” 模式会将凭证存放在内存中一段时间。 密码永远不会被存储在磁盘中,并且在15分钟后从内存中清除;
选项三:“store” 模式会将凭证用明文的形式存放在磁盘中,并且永不过期;
选项四:如果你使用的是 Mac,Git 还有一种 “osxkeychain” 模式,它会将凭证缓存到你系统用户的钥匙串中(加密的);
选项五:如果你使用的是 Windows,你可以安装一个叫做 “Git Credential Manager for Windows” 的辅助工具; 可以在 https://github.com/Microsoft/Git-Credential-Manager-for-Windows 下载。
基于 HTTPS 将本地仓库上传到 Github
# 本地与远程仓库建立连接,并给远程仓库起一个别名为origin
git remote add origin https://github.com/CaptainDDDrake/test2.git# 将本地分支重命名为 main
git branch -M main# 第一次提交:需要加上-u,指建立本地到远程的通道
git push -u origin main
SSH密钥
**概念:**Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。
SSH key 的作用:实现本地仓库和 Github 之间免登录的加密数据传输。
SSH key 的好处:免登录身份认证、数据加密传输。
SSH key 由两部分组成,分别是:
① id_rsa(私钥文件,存放于客户端的电脑中即可)
② id_rsa.pub(公钥文件,需要配置到 Github 中
生成 SSH key
① 打开 Git Bash
② 粘贴如下的命令,并将 your_email@example.com 替换为注册 Github 账号时填写的邮箱:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
③ 连续敲击 3 次回车,即可在 C:\Users\用户名文件夹.ssh 目录中生成 id_rsa 和 id_rsa.pub 两个文件
补充:mac下密钥的位置
/Users/你的用户/.ssh/id_rsa
配置 SSH key
① 使用记事本打开 id_rsa.pub 文件,复制里面的文本内容
② 在浏览器中登录 Github,点击头像 -> Settings -> SSH and GPG Keys -> New SSH key
③ 将 id_rsa.pub 文件中的内容,粘贴到 Key 对应的文本框中
④ 在 Title 文本框中任意填写一个名称,来标识这个 Key 从何而来
检测 Github 的 SSH key 是否配置成功
打开 Git Bash,输入如下的命令并回车执行:
ssh -T git@github.com
上述的命令执行成功后,可能会看到如下的提示消息:
输入 yes 之后,如果能看到类似于下面的提示消息,证明 SSH key 已经配置成功了:
常用:基于 SSH 将本地仓库上传到 Github
git init #仓库初始化
git add . #
git commit -m "初始化" #将文件提交到本地# 将本地仓库与远程仓库进行关联,并把远程仓库命名为origin
git remote add origin git@github.com:CaptainDDDrake/proj3.git# 将本地分支 main 重命名为 main
git branch -M main# 将本地仓库中的内容推送到远程的origin仓库中
# 注意:第一次推送分支需要带 -u 参数,此后可以直接使用 git push 推送代码到远程分支
git push -u origin main
管理远程仓库
查看远程地址:
git remote
# -v是—verbose的缩写(冗长的)表示要显示更详细的信息。
git remote –v
输出结果可能如下:
origin git@github.com:NathanCh3n/tests.git (fetch)
origin git@github.com:NathanCh3n/tests.git (push)
这个输出表示:
origin
是远程仓库的别名。git@github.com:NathanCh3n/tests.git
是与origin
别名对应的远程仓库的 URL 地址。(fetch)
表示这个 URL 是用于从远程仓库拉取数据的。(push)
表示这个 URL 是用于将本地数据推送到远程仓库的。
添加远程地址:我们也可以继续添加远程服务器(让本地的仓库和远程服务器仓库建立连接):
git remote add <shortname> <url>
重命名远程地址:
git remote rename gitlab glab
移除远程地址:
git remote remove gitlab
远程仓库克隆
从远程仓库clone代码:将存储库克隆到新创建的目录中:
git clone 地址
将代码push到远程仓库:将本地仓库的代码推送到远程仓库中:
如果是第一次将本地分支推送到远程仓库,需要运行如下的命令:
# -u 表示把本地分支和远程分支进行关联, 只在第一次推送的时候需要带-u
git push -u 远程仓库的别名 本地分支名称:远程分支名称# 例子
git push -u origin payment:pay# 如果远程分支的名称和本地分支名称保持一致,可以对命令进行简化:
git push -u origin payment
注意:那个案例指将payment这个分支推送到远程的origin仓库,同时命名它为pay
注意:第一次推送分支需要带 -u 参数,此后可以直接使用 git push 推送代码到远程分支。
查看远程仓库中所有的分支列表:
通过如下的命令,可以查看远程仓库中,所有的分支列表的信息:
git remote show 远程仓库名称
从远程仓库fetch代码:从远程仓库获取最新的代码
默认情况下是从origin中获取代码;
git fetch
获取到代码后默认并没有合并到本地仓库,我们需要通过merge来合并;
git merge
从远程仓库pull代码:上面的两次操作有点繁琐,我们可以通过一个命令来操作
git pull
git fetch + git merge(rebase)
常见的开源协议
Git 标签管理
**概念:**tag为标签,用来记录版本信息,是提交历史中某一个commit的快照。
对于重大的版本我们常常会打上一个标签,以表示它的重要性:
- Git 可以给仓库历史中的某一个提交打上标签;
- 比较有代表性的是人们会使用这个功能来标记发布结点( v1.0 、 v2.0 等等);
创建tag
**创建标签:**Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated);
附注标签:通过-a选项,并且通过-m添加额外信息;
git tag v1.0
git tag -a v1.1 -m "附注标签"
默认情况下,git push 命令并不会传送标签到远程仓库服务器上。
在创建完标签后你必须显式地推送标签到共享服务器上,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签;
git push orgin v1.0
git push origin --tags
技巧:如果你需要你想往默认分支上进行推送,直接 git push --tags
即可
删除和检出tag
删除本地tag:
要删除掉你本地仓库上的标签,可以使用命令 git tag -d <tagname>
nathanchen@NathansMacBook-Pro tests % git tag -d v1.0.0
Deleted tag 'v1.0.0' (was f22a7f5)
删除远程tag:
要删除远程的tag我们可以通过git push <remote> –delete <tagname>
git push origin --delete v1.1
To http://152.136.185.210:7888/coderwhy/gitremotedemo.git
- [deleted] v1.1
检出tag:
如果你想查看某个标签所指向的文件版本,可以使用 git checkout
命令;
$ git checkout v1.0
Note: switching to 'v1.0'.
注意:通常我们在检出tag的时候还会创建一个对应的分支(分支后续了解)
补充:
切换到tag对应的commit
git checkout v1.0.0
Git rebase 的使用
在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase
Git rebase 概念
**rebase概念:**用来重新应用提交(commits)到新的基础提交上
理解:
- 我们可以将其理解成改变当前分支的base;
- 比如在分支 hotfix 上执行rebase master,那么可以改变 hotfix 的base为master
**作用:**保持提交历史的线性化,避免分叉提交记录。
结合案例理解:
背景:我们需要在 c2 创建 hotfix 分支进行修复 bug
Plan1:采用 merge 来实现
git merge
的操作如下所示,bug修复完后,在 master 上合并 hotfix 分支
通过 log 查看 图结构,如下所示:
nathanchen@192 rebase % git merge hotfix
Merge made by the 'ort' strategy.foo.js | 1 +1 file changed, 1 insertion(+)
nathanchen@192 rebase % git log --pretty=oneline --graph
* 64e94a94dc6464bb42fc0c4f48419479d141d335 (HEAD -> master) Merge branch 'hotfix'
|\
| * 04d6400f44b01ca420ef69bcad1a47e5310d5a6f (hotfix) hotfix commit
* | 8e3ebf3371cf5b2b95dabccfb57828f443df1f79 3 commit
|/
* ec7d26252df6bb104ea15c85e8bbbffa4fe37818 2 commit
* 132fad134c2d9e7de97fcac8d105c416afda613c 1 commit
* c90491eb34784dd0a4f98184c6d44b80a32f4120 init commit
(END)
根据以上来看, 采用git merge
来合并分支会导致分支非线性。
Plan2:采用 rebase 来实现
目前情况如下所示,当前 hotfix 分支的base为c2
我们需要在 hotfix 上进行 rebase 操作,即我们需要在 hotfix 分支上将base改成 master 指向的提交对象c3,命令如下所示:
git rebase master
整体流程如下:
nathanchen@192 rebase % git checkout hotfix
Switched to branch 'hotfix'
nathanchen@192 rebase % git rebase master
Successfully rebased and updated refs/heads/hotfix.
nathanchen@192 rebase % git log --pretty=oneline --graph
* 5a3569383a81896decb866f5333ed0abd2bd2e46 (HEAD -> hotfix) hotifx commit
* 9e5912ce9fe64627394302d8dce03958aa41bc2a (master) 3 commit
* 3971f63e84c77d0266658a61283ceafc543b0c5b 2 commit
* 959e6c99e08492212666970d2af79bc3c4378979 1 commit
* cbddbfed8dc69ddb04b19bb2fef704b6ac94b5cb init commit
(END)
nathanchen@192 rebase % git checkout master
Switched to branch 'master'
nathanchen@192 rebase % git log --pretty=oneline --graph
* 9e5912ce9fe64627394302d8dce03958aa41bc2a (HEAD -> master) 3 commit
* 3971f63e84c77d0266658a61283ceafc543b0c5b 2 commit
* 959e6c99e08492212666970d2af79bc3c4378979 1 commit
* cbddbfed8dc69ddb04b19bb2fef704b6ac94b5cb init commit
此时的git提交图结构如下所示:
我们需要将 master 指向最新的提交对象 hotfix
nathanchen@192 rebase % git merge hotfix
Updating 9e5912c..5a35693
Fast-forwardfoo.js | 3 +++1 file changed, 3 insertions(+)create mode 100644 foo.js
nathanchen@192 rebase % git log --pretty=oneline --graph
* 5a3569383a81896decb866f5333ed0abd2bd2e46 (HEAD -> master, hotfix) hotifx commit
* 9e5912ce9fe64627394302d8dce03958aa41bc2a 3 commit
* 3971f63e84c77d0266658a61283ceafc543b0c5b 2 commit
* 959e6c99e08492212666970d2af79bc3c4378979 1 commit
* cbddbfed8dc69ddb04b19bb2fef704b6ac94b5cb init commit
最终的git提交图结构如下所示:
Git rebase 原理
rebase工作原理:
首先找到这两个分支(即当前分支 hotfix、变基操作的目标基底分支 master) 的最近共同祖先 C2;
然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件;
接着将当前分支指向目标基底 C3;
最后将之前存储的临时文件的修改依序应用;
我们可以再次执行master上的合并操作:
$ git checkout master
$ git merge experiment
rebase和merge的选择
开发中对于rebase和merge的选择:
事实上,rebase和merge是对Git历史的不同处理方法:
merge用于记录git的所有历史,那么分支的历史错综复杂,也全部记录下来;
rebase用于简化历史记录,将两个分支的历史简化,整个历史更加简洁;
了解了rebase的底层原理,就可以根据自己的特定场景选择merge或者rebase。
rebase的黄金法则:永远不要在主分支上使用rebase
- 如果在main上面使用rebase,会造成大量的提交历史在main分支中不同;
- 而多人开发时,其他人依然在原来的main中,对于提交历史来说会有很大的变化;
理解以上:
rebase前
在master上rebase,会导致c3的base变成hotfix2,导致历史混乱。
在hotfix上rebase: