四个场景玩转git
参考文献https://mp.weixin.qq.com/s/Km5KuXPETvG0wCGHrvj9Vg
众所周知,Git是目前世界上最先进的分布式版本控制系统(没有之一),同时github是世界上最大的同性交友平台。
通俗的讲git是这样一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?
git的基本操作大致如下
![](https://img.haomeiwen.com/i14301043/a930c83a490f74e0.png)
mac下安装git:
需要先安装Homebrew并且推荐换源成国内镜像
命令行下输入
brew install git
2.创建ssh key
1、设置username和email(github每次commit都会记录他们)
git config --global user.name "USERNAME"
git config --global user.email "EMAIL"
2 、创建ssh key
ssh-keygen -t rsa -C "EMAIL"
由于我之前创建过了,所以不再创建了。
命令行下输入
cat /Users/inf/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDkDRqRUlT75L3AbcfcOd8ReTxGfo6yQttj8unlD6nClSsgWs7LwQYPAjirHlR+u4eqklyUEWv807UHPAt1k36av/IwWBZgkQA
...
这里面的内容就是你的公钥
3、复制这里面的内容(包括ssh-rsa 和最后面你的邮箱),登陆github,点击settings,左侧找到SSH and GPG keys
点击右边的添加
![](https://img.haomeiwen.com/i14301043/f615d4936cefa9d8.png)
将你的公钥粘贴进去
4、验证连接
命令行输入
$ ssh -T git@github.com
Hi WhyCyrils! You've successfully authenticated, but GitHub does not provide shell access.
说明连接成功
接下来用几个场景来说明git的用法
场景一:git本地版本库的基本用法
通常有两种获取 Git 项目仓库的方式:
- 将尚未进行版本控制的本地目录转换为 Git 仓库;
- 从其它服务器 克隆 一个已存在的 Git 仓库。
git init方法
在此我创建了一个AdvancedSE文件夹
cd cd project/AdvancedSE
输入
$ git init
Initialized empty Git repository in /Users/inf/project/AdvancedSE/.git/
该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。
便成功在此目录下初始化了一个git的本地版本库。
如何删除?只需输入命令
rm -rf .git
git clone方法
或者,我在github上新建一个远程库,AdvancedSE
![](https://img.haomeiwen.com/i14301043/59428d10568fd858.png)
同时进入到AdvancedSE文件夹的上一级,删除AdvancedSE
如果我在当前文件夹即AdvancedSE文件夹下输入git clone 命令,这会在当前目录下创建一个名为 “AdvancedSE” 的目录,并在这个目录下初始化一个 .git 文件夹, 从远程仓库拉取下所有数据放入 .git 文件夹,然后从中读取最新版本的文件的拷贝。
这样看起来文件结构就会变成/AdvancedSE/AdvancedSE/。
输入
git clone git@github.com:WhyCyrils/AdvancedSE.git
进入该文件夹AdvancedSE,输入ls查看
$ ls
LICENSE README.md
显示了两个文件LICENSE 和README.md都是创建远程仓库创建好的
项目目录下执行git status命令即可查看Git本地版本库当前 workspace 的状态。
$git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
因为是刚拉取的版本所以显示nothing to commit, working tree clean
- 修改README.md文件的内容,添加一行test1
# AdvancedSE
高级软件工程
test1
如果使用的是vscode,这时候点击版本控制按钮
![](https://img.haomeiwen.com/i14301043/625457778a9558d9.png)
可以看到相比上一个版本比项目的所有更改,即当前 workspace 的状态。
- 输入
git add
.
输入git add .
或者git add README.md
将当前目录或者指定文件添加到暂存区(Index)。
如果需要从暂存区删除,使用
-
git reset HEAD FILES # 指定文件或文件列表
或者 -
git reset HEAD
这两个命令是取消将特定文件(FILES)或者所有文件添加到暂存区(Index),即从暂存区(Index)中删除;只有在暂存区登记的文件才会在提交代码时存入版本库。
如果需要放弃特定文件(FILES)或者所有文件的修改,实际上重新检出特定文件(FILES)或者所有文件到工作区(workspace),使用
-
git checkout -- FILES
# 不要忘记“--” ,不写就把FILES当分支名了 git checkout
注意会覆盖掉已修改未暂存的内容,不希望被覆盖的文件可以先使用git add将其添加到暂存区。
-
输入
git commit -m "test1"
将暂存区的文件提交到仓库,可以看到vscode里面工作树显示的已经是统一了
-
再增加一行test2并添加到暂存区,再用commit命令提交到仓库。
-
输入
git log
查看提交记录,按q退出
commit ec3b1a448e7a01f8bbafd8016d1e15541e4ba54e (HEAD -> main)
Author: inf <14718249608@163.com>
Date: Sun Oct 11 23:05:39 2020 +0800
test2
commit 669462f8e5c496e5ab2139a9202b9d4185362393
Author: inf <14718249608@163.com>
Date: Sun Oct 11 23:00:53 2020 +0800
test1
commit deb72321528df01022480b1e4376fd259509ba45 (origin/main, origin/HEAD)
Author: WhyCyrils <14718249608@163.com>
Date: Sun Oct 11 22:01:58 2020 +0800
Initial commit
(END)
- 输入
git reset --hard HEAD^
$ git reset --hard HEAD^
HEAD is now at 669462f test1
git reset --hard XXX
可以让HEAD
回退到任意一个版本,比如HEAD^
表示HEAD
的前一个版本、HEAD^^
表示HEAD
的前两个版本、HEAD~100
表示HEAD
的前100个版本,也可以用版本号字符串来指定任意一个版本。
- 输入
git reflog
按q退出
669462f (HEAD -> main) HEAD@{0}: reset: moving to HEAD^
ec3b1a4 HEAD@{1}: commit: test2
669462f (HEAD -> main) HEAD@{2}: commit: test1
deb7232 (origin/main, origin/HEAD) HEAD@{3}: clone: from git@github.com:WhyCyrils/AdvancedSE.git
(END)
值得注意的是HEAD只是一个指向特定版本的指针,通过git reset —hard 回退之后,HEAD指向的不是最新的版本,而git log只能查看HEAD及其之前(时间更早)的提交记录。
git reflog
命令可以查看HEAD指向的版本之后的提交记录
8.输入git reset --hard ec3b1a4
$ git reset --hard ec3b1a4
HEAD is now at ec3b1a4 test2
总结
git init # 初始化一个本地版本库
git status # 查看当前工作区(workspace)的状态
git add [FILES] # 把文件添加到暂存区(Index)
git commit -m "wrote a commit log infro” # 把暂存区里的文件提交到仓库
git log # 查看当前HEAD之前的提交记录,便于回到过去
git reset —hard HEAD^^/HEAD~100/commit-id/commit-id的头几个字符 # 回退
git reflog # 可以查看当前HEAD之后的提交记录,便于回到未来
git reset —hard commit-id/commit-id的头几个字符 # 回退
场景二:Git 远程版本库的基本用法
上面已经使用git clone克隆了github上创建的项目
在平时的合作项目中,我们对远程库一般有几种操作
git pull、git push、git merge等
- git clone 克隆一个远程库到本地目录。
- git fetch 下载一个远程存储库数据对象等信息到本地存储库。
- git push 将本地存储库的内容推送到远程存储库。
- git merge 合并两个或多个开发历史记录。
- git pull 从其他存储库或分支抓取并合并到当前存储库的当前分支。
git remote
$ git remote
origin
默认的远程库名称为origin
-
git remote -v
查看更详细的远程库信息
origin git@github.com:WhyCyrils/AdvancedSE.git (fetch)
origin git@github.com:WhyCyrils/AdvancedSE.git (push)
- fetch(抓取)的远程存储库URL
- push(推送)的远程存储库URL
git pull
$ git pull
Already up to date.
由于远程仓库一开始拉取之后就没变化,因此显示Already up to date.
git push
$ git push
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 573 bytes | 573.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:WhyCyrils/AdvancedSE.git
deb7232..ec3b1a4 main -> main
![](https://img.haomeiwen.com/i14301043/dd53ad8d62e7d4af.png)
可以看到test1 和test2都在README.md中更新了。
更新的文件必须由暂存区提交到仓库
即需要 add -> commit->push的顺序
场景三:团队项目中的分叉合并
我们建议团队项目的每一个开发者都采用的工作流程大致如下:
- 克隆或同步最新的代码到本地存储库;
- 为自己的工作创建一个分支,该分支应该只负责单一功能模块或代码模块的版本控制;
- 在该分支上完成某单一功能模块或代码模块的开发工作;
- 最后,将该分支合并到主分支。
如果我们足够细心,那么就会发现克隆到本地仓库的默认分支不叫master了,而是main了。
in ~/project/AdvancedSE on git:main o [23:38:46]
这是因为国外的政治因素,这里不再详细展开
git branch
* main
(END)
2.git checkout -b mybranch
$ git checkout -b mybranch
Switched to a new branch 'mybranch'
创建一个新的分支
git branch
main
* mybranch
(END)
*号表示当前工作区处于mybranch分支
- README.md中新增一行test3,
add
后commit
$ cat README.md
# AdvancedSE
高级软件工程
test1
test2
test3%
注意这一切都发生在mybranch分支
-
git checkout main
切换回main分支
$ cat README.md
# AdvancedSE
高级软件工程
test1
test2
发现没有test3
git merge mybranch
$ git merge mybranch
Updating ec3b1a4..b7a01b3
Fast-forward
README.md | 2 ++
1 file changed, 2 insertions(+)
检查一下
$ cat README.md
# AdvancedSE
高级软件工程
test1
test2
test3
- 使用
git merge --no-ff mybranch
切换到mybranch并在README.md新增test4,并add后commit
git checkout mybranch
git add .
git commit -m "test4"
git checkout main
git merge --no-ff mybranch
最后一句话会弹出vi界面,按一下esc输入:wq
(冒号+wq)
使用--no-ff参数后,会执行正常合并,保留mybranch分支为一段独立的分支线段,在Master分支上生成一个新节点。合并后大致如下示意图。
![](https://img.haomeiwen.com/i14301043/e1b7b57ea5fce986.png)
场景四:Git Rebase
场景四实际就是在场景三团队项目工作流程中增加一步Git Rebase,即在mybranch分支上完成自己的工作之后,为了让 log 记录将来更容易回顾参考,用 git rebase 重新整理一下提交记录。注意不要通过rebase对任何已经提交到远程仓库中的commit进行修改。
Rebase命令git rebase -i [startpoint] [endpoint]
- 切换到mybranch分支,在README.md添加两行test5 test6,每添加一行就add-commit一次。
2.切回main分支git checkout main
,执行一次git push
把之前的test3 test4更新到远程仓库。这时候的commit是test4
- main分支下README.md添加一行test7,并add-commit-push。
执行git log
commit 213d6918f37d753fcdb973406b98fb00f1d64089 (HEAD -> main)
Author: inf <14718249608@163.com>
Date: Mon Oct 12 00:46:20 2020 +0800
test7
commit b1921585db6ddbde285b408ccacecc1dc9371c40 (origin/main, origin/HEAD)
Author: inf <14718249608@163.com>
Date: Mon Oct 12 00:01:29 2020 +0800
test4
commit b7a01b3f13df096e6ea9d0468e07df6e06ab668a
Author: inf <14718249608@163.com>
Date: Sun Oct 11 23:55:11 2020 +0800
test3
...
(commit "test4"由于我rebase了一次,所以精简了一下)
4.切换回mybranch,执行git log
commit 5e5bdc5cd0a0796cb6d044e537bac2cac12fc56a (HEAD -> mybranch)
Author: inf <14718249608@163.com>
Date: Mon Oct 12 00:36:47 2020 +0800
test6
commit f321f464f3511758137ba7e9a467a8dc81c6d7bb
Author: inf <14718249608@163.com>
Date: Mon Oct 12 00:36:13 2020 +0800
test5
commit b1921585db6ddbde285b408ccacecc1dc9371c40 (origin/main, origin/HEAD)
Author: inf <14718249608@163.com>
Date: Mon Oct 12 00:01:29 2020 +0800
test4
...
git rebase -i HEAD^^
pick f321f46 test5
pick 5e5bdc5 test6
-
git rebase main
解决冲突
我这里选择保留双方更改
点采用当前更改,就是保留test7,会好一点 git add .
-
git rebase --continue
这是候仍然要解决冲突
9.继续git add .
和git rebase --continue
$ git rebase --continue
Applying: test6
- git checkout main
$ cat README.md
# AdvancedSE
高级软件工程
test1
test2
test3
test4
test7
11.git merge mybranch
$ git merge mybranch
Updating 213d691..b1f5702
Fast-forward
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
(base)
冲突解决完毕
git push
- 发现我们在这个过程中解决了两次冲突
应该是git rebase -i
命令出现了问题,mybranch重新运行git rebase -i HEAD~3
将下面两个pick 改为s
pick 213d691 test7
s e6ddc8b test5
s b1f5702 test6
最后添加一个commit,完成了wq保存退出
输入git log
commit 57837179707bb1f98060483d8e7d81b671cf2921 (HEAD -> mybranch)
Author: inf <14718249608@163.com>
Date: Mon Oct 12 00:46:20 2020 +0800
combines test
test7
test5
test6
总结
git rebase 作用:
- 将多次commit合并为一次
- 合并分支时只需要解决一次冲突
两次解决冲突是因为git rebase -i
命令出错,并没有将test5 和test6合并为一次提交