Git使用入门
这篇笔记是我看尚硅谷的教程边看教程边总结下来的,有些地方写的很浅,
应该之后会慢慢补充
git同SVN一样,也是一个版本控制软件,实现了版本控制软件具有的「协同修改」、「数据备份」、「权限控制」、「历史记录」、「分支管理」等功能
git最大的优势是拥有SVN没有的「对团队外开发者贡献的代码进行合并审核」的功能,这使得不仅是团队成员而是所有人都可以参与到项目中,这也是GitHub等网站现在如此流行的原因
Git 结构
git分为本地库和远程库,其中远程库在代码托管中心上,可以是GitHub、码云,或者我们自己建的GitLab私服
之前工作也只是在一个master分支下改动,没有用到git最强大的分支管理功能,每次也只是「提交并推送」,可以说和SVN没有区别
这个流程是个轮廓,不保证细节全部准确
Git 本地库
git 在本地库有3个区域,分别是:工作区(working tree)、暂存区(index)、本地仓库
Git 远程库
如果这个库只是团队内成员操作:
如果是跨团队协作就稍微复杂一些:
Git 基本操作
config 设置签名
要使用git首先需要在config中设置签名,签名中的邮箱可以是不存在的,这个只是为了分辨哪个人提交的
config有2个级别,仓库级/全局
- 仓库级,仓库级的优先级比全局高,保存在当前 .git 目录下的 config 文件
$ git config user.name "n031"
$ git config user.email "n031@gmail.com"
- 全局,在家目录(Linux 上是"~")下的 .gitconfig 文件
$ git config --global user.name "n031"
$ git config --global user.email "n031@gmail.com"
add commit 本地库基本操作
$ git init
初始化成功后在目录下会生成一个 .git 隐藏文件
查看当前仓库状态
$ git status
On branch master
# 在maste 分支
No commits yet
# 还没有提交记录[本地库]
nothing to commit (create/copy files and use "git add" to track)
# 也没有什么可提交的(创建/复制文件 并且使用 git add 让 git 去追踪[管理]这个文件)[暂存区,放入暂存区就是被git所追踪]
创建一个good.txt文件,然后再查看状态
$ git status
On branch master
No commits yet
Untracked files:
# 未被追踪文件
(use "git add <file>..." to include in what will be committed)
good.txt
nothing added to commit but untracked files present (use "git add" to track)
现在暂存区是空的,将good.txt添加进去
$ git add good.txt
warning: LF will be replaced by CRLF in good.txt.
# 自动替换成CRLF,如果从windows down下来还是LF
The file will have its original line endings in your working directory
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: good.txt
可以将文件从暂存区移除
git rm --cached good.txt
或者提交到本地仓库
# git commit -m "本次注释" <可选文件名>
$ git commit -m "add good.txt" good.txt
[master (root-commit) c0adbbe] add good.txt
# root-commit根提交,只有一次
1 file changed, 1 insertion(+)
# 1个文件被修改,增加了1行
create mode 100644 good.txt
# 仓库创建了ood.txt文件
$ git status
On branch master
nothing to commit, working tree clean
修改good.txt状态后再查看状态
$ git status
On branch master
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)
# 撤销工作区中的修改
# 之前的版本是git checkout ?
modified: good.txt
no changes added to commit (use "git add" and/or "git commit -a")
git add good.txt
后查看状态,ps:这里假如把good.txt删除了,然后git rm good.txt
也可以把删除这个操作提交到暂存区
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
# 撤销添加到暂存区的修改
# 之前的版本是reset HEAD,删除暂存区全部修改
modified: good.txt
这之中用到的操作
$ git add good.txt # 添加good.txt的修改,工作区->暂存区
$ git rm good.txt # 仅删除good.txt时,添加good.txt的修改,工作区->暂存区
$ git restore good.txt # 如果工作区和暂存区不同,工作区恢复成暂存区的版本
$ git restore --staged good.txt # 如果工作区和暂存区不同,暂存区恢复成工作区的版本
大致猜测暂存区起到一个标准的作用,与工作区对比的作用,让工作区不直接和版本库比较,一些“尝试的改动”比较时就更直观容易也更容易恢复
log 查看版本记录
最基础的:git log
,B
查看上一页,Space
查看下一页,Q
退出
比较简洁且常用的:git log --pretty=oneline
或git log --oneline
,两种结果相同,
log
只显示HEAD
之后的版本,不显示之前的版本(后面会提到)
git reflog
也可以查看版本日志,比git log
多距离当前HEAD
几跳的信息
reset 版本前进后退
- 基于索引值版本重置
$ git reflog
126320d (HEAD -> master) HEAD@{0}: reset: moving to 126320d
f9752a6 HEAD@{1}: reset: moving to @
f9752a6 HEAD@{2}: reset: moving to f9752a6
126320d (HEAD -> master) HEAD@{3}: reset: moving to 126320d
f9752a6 HEAD@{4}: reset: moving to f9752a6
126320d (HEAD -> master) HEAD@{5}: commit: add bad
f9752a6 HEAD@{6}: commit: modify good.txt
9fc37f5 HEAD@{7}: commit (initial): add good.txt
# 注意这里面的一些版本号是相同的,因为只是通过reset操作前进恢复了一些版本,并没有生成新的文件
使用索引恢复实质上就是移动HEAD
指针位置,执行命令
$ git reset --hard 9fc37f5
HEAD
指针就会移动到"9fc37f5"这个版本
- 使用
^
或~
后退版本
^
只能后退版本,不能向前选择版本,通常使用git reset --hard HEAD^
回退到HEAD
之前的一个版本,有几个^
就表示后退几个版本
~
也只能后退版本,~n
就表示后退n步,在^
比较多的时候使用,例如git reset --hard HEAD^^^^^
和git reset --hard HEAD~5
是等价的
hard/soft/mixed 重置版本的区别
- soft
只是在本地库移动下指针,但不会碰工作区和暂存区
将本地库恢复凸显暂存区的变动了
- mixed(默认)
本地库移动下指针,重新设置暂存区,但不重置工作区,所以现在的文件内容会保存
将本地库和暂存区恢复,就凸显工作区的变化了,reset HEAD
可以用来清除暂存区
- hard
本地库移动下指针,重置暂存区,重置工作区,所有之后修改的代码都会被丢弃
正常情况,版本可以使用--hard
恢复,但暂存区和工作区文件就恢复不了了
soft | mixed | hard | |
---|---|---|---|
工作区 working tree | 恢复 | ||
暂存区 index | 恢复 | 恢复 | |
本地库 | 恢复 | 恢复 | 恢复 |
diff 比较文件
git diff
,无参数,表示工作区与暂存区比较
git diff <file>
表示指定哪个文件比较
git diff
可以加分支参数,如git diff HEAD
表示工作区和当前本地库最新版本比较,git diff HEAD^
表示工作区和本地库上一个版本比较
这里比较下bad.txt文件,7/9/10行表示具体内容,git认为的修改就是删除又新增,因为1后面加了个回车,所以是删除1行,新增2行
$ git diff bad.txt
diff --git a/bad.txt b/bad.txt
index 56a6051..7a754f4 100644
--- a/bad.txt
+++ b/bad.txt
@@ -1 +1,2 @@
-1
\ No newline at end of file
+1
+2
\ No newline at end of file
Git 分支操作
branch 创建查看分支
- 创建分支
$ git branch [分支名]
- 查看分支
$ git branch -v
checkout 切换分支
$ git checkout [分支名]
merge 合并分支
- 切换到被合并的分支上
- 执行
merge
命令,例如刚刚创建了一个bak分支,那么就是git merge bak
,具体是git checkout master
切换到master分支git merge bak
合并aaa分支的内容
分支合并冲突
- 例如创建了一个bak分支,然后bak分支和master分支各修改了相同的一行,然后将master合并到bak,就会出现问题
Administrator@IGAME MINGW64 /d/GitHub/guigu (bak)
# 当前是bak分支
$ git merge master
Auto-merging good.txt
CONFLICT (content): Merge conflict in good.txt
Automatic merge failed; fix conflicts and then commit the result.
# 自动合并失败,修复冲突,然后手动commit
Administrator@IGAME MINGW64 /d/GitHub/guigu (bak|MERGING)
# 当前bak分支,但变成MERGING状态
good.txt内容就变成了
aaa
bbb
<<<<<<< HEAD # 冲突开始
ccc edit by bak
======= # 上面是当前分支的内容,下面是合并来的分支的内容
ccc edit by master
>>>>>>> master # 冲突结束
ddd
eee edit by bak # 这句话是bak合并的,并未产生冲突解决
- 解决方法就是手动修改good.txt文件,保存
先用git status
查看下当前状态
$ git status
On branch bak
You have unmerged paths.
# 你有一个未合并的路径
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
# 未合并路径
(use "git add <file>..." to mark resolution)
# 使用"git add <file>..."去标记已解决
both modified: good.txt
no changes added to commit (use "git add" and/or "git commit -a")
git add good.txt
,再查看状态
$ git status
On branch bak
All conflicts fixed but you are still merging.
# 所有冲突都解决得了,但你仍然处于merging状态
(use "git commit" to conclude merge)
# 使用"git commit"解决merging
Changes to be committed:
modified: good.txt
- 然后只需要
git commit
即可提交,这次不用加文件名
GitHub
remote 设置别名
在GitHub中,仓库的地址是一长串很麻烦,不可能每次都输入这一长串东西,所以git提供了一个别名的功能
查看所有git的别名:git remote -v
添加别名
$ git remote add mf https://github.com/lk722722722/MyFirstRepo.git
添加后会发现有2条记录
$ git remote -v
mf https://github.com/lk722722722/MyFirstRepo.git (fetch)
mf https://github.com/lk722722722/MyFirstRepo.git (push)
由于这样设置别名是仓库级的,所以一般都使用origin
表示远端位置
$ git remote rm mf # 删除之前添加的mf别名
$ git remote add origin https://github.com/lk722722722/MyFirstRepo.git # 设置新别名
push 向远端推送 - 需要凭证
由于已经设置好了别名,将本地库文件推送到GitHub只需
$ git push -u origin master
如果创建GitHub仓库的时创建了README.md文件,那本地库推送之前也需要创建此文件,否则将会提示推送失败
$ git push origin master
To https://github.com/lk722722722/MyFirstRepo.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'https://github.com/lk722722722/MyFirstRepo.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
解决方法是先执行一遍合并操作
$ git pull --rebase origin master
clone 克隆
很简单,只需要这样即可,在克隆之前也无需init
初始化仓库
$ git clone https://github.com/lk722722722/MyFirstRepo.git
克隆完,不仅把远端的文件下载到本地,还init
了本地.git
目录,并且创建了origin
别名
远程库的拉取
pull = fetch + merge
fetch 远程库拉取
$ git fetch [远程库地址/远程库地址别名] [远程库分支名]
merge 远程库合并
$ git merge [远程库地址/远程库地址别名]/[远程库分支名]
注意:merge拉取的时候实际上远端的文件已经下载到了本地
远端合并思想和本地库合并相同,只是分支名前多了远程库地址和/
表示和本地不同的另外一个分支
pull 远程库拉取合并
实际上是先fetch
再merge
$ git pull [远程库地址/远程库地址别名] [远程库分支名]
两种拉取的选择
fetch
之后文件已经下载到本地,此时已经可以查看文件,只不过没有合并到我们本地工作的仓库中,so:
- 第一次拉取用
clone
- 之后拉取,文件比较复杂,用
fetch
+merge
- 之后拉取,文件较清晰,用
pull
比较省事
团队合作中的冲突
团队合作中肯定会有冲突,当两个人同时修改一个文件, 只有先推送(push
)的那个人可以推送到远程库里面,后推送的人只能先拉取(pull
)再推送(push
)。拉取的时候,修改的位置有自己的内容,也有另一个的内容,这就必须得我们自己去处理冲突(MERGING
状态)
- 两人分别修改,提交到各自的本地库都没问题(初始化版本A0,HEAD版本A0)
- A先执行,
git push origin master
,没有问题,A把他的版本提交上去了(版本号A,基于parent A0版) - B再执行,
git push origin master
,就出问题了,此时B的版本不是最新版本(版本号B,基于parent A0版,不是最新HEAD版A) - B需要先
git pull origin master
将远端的版本下载下来,此时本地库是MERGING
状态 - B修改代码,
add
,commit
。现在本地库已经是基于A的版本号新提交的一个版本,再执行git push origin master
没有问题(版本号B2,基于parent A/B版) - 如果A还要修改,A也也要
pull
,add
,commit
,push
,否则因为A的当前版本不是基于最新版本修改的(A本地生成的A2,parent版本是A,不是基于HEAD的版本B2)
图例,总之push
的时候要确保本地的版本是基于远端的HEAD版本做的修改,push
才能被接受
SSH免密登录
- 生成秘钥
$ ssh-keygen -t rsa -C xxxxxxx@qq.com
- 之后会生成一个
/.ssh
目录,下面有id_rsa
和id_rsa.pub
两个文件 - 将
id_rsa.pub
复制到GitHub的SSH keys
处,这样就设置好了,以后就不用输入账号密码了 - 这样提交是以ssh协议提交,所以远程仓库的地址变了
$ git push origin_ssh master
IDE
git ignore
IDE会产生很多和IDE版本相关的文件,如果这些文件添加到GIt管理,在团队中每次处理这些文件都将是一件麻烦且毫无意义的是事情,所以需要将这些文件添加到忽略列表中,具体有哪些文件,GitHub列出了一个详细的清单
<!-- 项目地址 -->
https://github.com/github/gitignore
<!-- 与Java相关 -->
https://github.com/github/gitignore/tree/master/Global
https://github.com/github/gitignore/blob/master/Global/Eclipse.gitignore
https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore
配置方法:
在仓库目录下新建一个名为.gitignore
的文件(因为是点开头,没有文件名,没办法直接在windows目录下直接创建,必须通过右键Git Bash,按照linux的方式来新建.gitignore
文件)。 通过将.gitignore
文件添加到仓库,其他开发者更新该文件到本地仓库,以共享同一套忽略规则
在IDEA使用Git(待添加)
这部分尚硅谷的视频中没有,待之后我添加
Git 工作流(待补充)
GitLab(待添加)
待添加