文章目录
- 参考资料:
- 安装GIt
- 在windows上安装Git
- 创建版本库
- 时光机穿梭
- 版本回退
- 工作区和暂存区
- 管理修改
- 撤销修改
- 删除文件
- 远程仓库
- 添加远程库
- 删除远程库
- 从远程库克隆
- 分支管理
- 创建与合并分支
- 解决冲突
- 分支管理策略
- 分支策略
- Bug分支
- 使用`cherry-pick`命令复制提交到当前分支
- Feature分支
- 多人协作
- 推送分支
- 抓取分支
- 多人协作的工作模式
- Rebase
- 标签管理
- 创建标签
- 操作标签
- 使用Github
- 使用Gitee
- 自定义Git
- 忽略特殊文件
- 配置别名
- 配置文件
- 搭建Git服务器
- 使用SourceTree(可视化git界面)
参考资料:
廖雪峰的官方网站-Git:https://www.liaoxuefeng.com/wiki/896043488029600
安装GIt
在windows上安装Git
- 在Git官网:
https://git-scm.com/downloads
找到安装包并完成安装。 - 安装完成后,右键-git-git bash here可以打开git命令行
- 设置用户名和密码
$ git config --global user.name "Your Name"
$ git config --global user.email ”email@example.com“
git config
命令的--global
参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
email可以不是真的。
创建版本库
为什么Git比其他版本控制系统设计得更优秀?因为Git跟踪并管理的是修改,而非文件。
Git在有些情形下不区分大小写,在有些情形下区分大小写,所以尽可能严格按大小写来
如果你使用Windows系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文。
- 使用
git init
命令可以初始化一个Git版本库。 - 添加文件到Git版本库分两步:
1.使用git add <file>
命令可以将文件提交到暂存区。
2.使用git commit -m <message>
命令可以将文件从暂存区提交到版本库。
可以多次使用
git add <file>
命令添加多个文件
可以使用git add .
命令添加目录下的所有文件
git commit
命令的-m
参数后面输入的是本次提交的说明。
git init
是一种初始化版本库的方法,还有一种方法是git clone
,可以将github
上别人开源的代码克隆到本地并进行版本管理。
$ git clone https://github.com/jquery/jquery.git
时光机穿梭
- 使用
git status
命令可以查询工作区状态:文件是否修改,是否进入暂存区。 - 使用
git diff
命令可以查看修改内容。
git diff
是查看diffference,显示的格式是Unix通用的diff格式。
版本回退
- 在Git中,用
HEAD
指向当前版本,HEAD^
指向上个版本,HEAD^^
指向上上个版本,HEAD~100
指向往上数100个版本。 - 使用命令
git reset --hard HEAD^
可以回到上个版本 - 也可以使用命令
git reset --hard commit_id
在版本间穿梭 - 穿梭前,可以使用
git log
命令查看提交历史,以便确定要回退的版本。 - 要重返未来,可以用
git reflog
查看命令历史,以便确定未来版本的版本号。
git log
命令显示从最近到最远的提交日志
形如commit 9fa66a...
的是commit id
(版本号)
可以在git log
命令后面加上--pretty=oneline
参数,这样输出的内容会比较简略
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD的指向改变,并把工作区的文件更新
版本号没必要写全,一般只需要写前5-7位即可
工作区和暂存区
- 版本库(Repository):隐藏目录.git,它是Git的版本库。
- 工作区(Working Directory):除.git外的文件和目录属于工作区。
- 版本库里有三个东西非常重要:
1.暂存区(Stage/Index):git add
命令就是将文件提交到暂存区。
2.master分支:创建Git版本库时,Git会自动创建唯一一个master
分支。
3.HEAD指针:GIt会自动创建一个一开始指向master的HEAD
指针。
管理修改
- Git跟踪并管理的是修改,而非文件
- 每次修改,如果不用
git add
添加到暂存区,则不会加入到commit
中
撤销修改
- 使用
git restore [--source=<>] [--staged] [--worktree] <pathspec>
命令(参考git restore文档) - 在未指定source的情况下
– 如果有”--staged
‘和”--worktree
“,则将’HEAD
’的内容恢复到工作区和暂存区
–如果只有“--staged
”,则将’HEAD
’的内容恢复到暂存区
–如果只用”--worktree
“,则将暂存区(’index
‘)的内容恢复到工作区
–如果没有”--staged
‘和”--worktree
“,则默认将暂存区(’index
‘)的内容恢复到工作区 - 在指定source的情况下,则恢复源为source表示的版本库的内容
如果恢复源是暂存区,但暂存区没有内容,其实表示暂存区和HEAD是一致的,则从HEAD恢复内容
# 几个例子
git restore --staged readme.txt # 会将HEAD的内容恢复到暂存区
git restore readme.txt # 会将暂存区的内容恢复到工作区
git restore --source HEAD --staged readme.txt # 会将HEAD的内容恢复到工作区
git restore --source 17539a readme.txt # 会将”17539a“标识的版本的内容恢复到工作区
git restore --staged --worktree readme.txt # 会将HEAD的内容恢复到暂存区和工作区
几个场景:
- 场景1:如果修改了文件,但还没有提交到暂存区,想将HEAD(暂存区)的内容恢复到工作区
git restore readme.txt
- 场景2:如果修改了文件,但已经提交到暂存区了,想将HEAD的内容恢复到暂存区和工作区
# 方法一:先将HEAD恢复到暂存区,再将暂存区恢复到工作区
git restore --staged readme.txt
git retore readme.txt
# 方法二:同时将HEAD恢复到暂存区和工作区
git restore --staged --workttree readme.txt
- 场景3:如果修改了文件,且已经提交到版本库了,想将上个版本的内容恢复到当前HEAD
# 直接版本回退
git reset --hard HEAD^
删除文件
- 在Git中,删除也是一个修改操作。
- 添加一个新文件test.txt,并提交到版本库。
- 使用"
git rm test.txt
"命令:可以删除工作区的test.txt
文件,同时将此次删除操作提交到暂存区,然后只需要commit
,则版本库中test.txt
文件也删除了。
git rm test.txt
git commit -m "remove test.txt"
- 如果版本库中还有
test.txt
文件,则可以使用”git restore
“命令撤销删除。
git restore --staged test.txt # 从版本库恢复test.txt文件到暂存区
git restore test.txt
注意这里不能直接使用"git restore test.txt"恢复test.txt文件,因为暂存区已经没有test.txt文件了
从来没有被添加到版本库就被删除的文件,是无法恢复的
远程仓库
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。
一般是找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。
GitHub就可以提供Git仓库托管服务的,所以,只要注册一个GitHub账号,就可以免费获得Git远程仓库。
- 第一步:使用以下命令生成SSH Key:
ssh-keygen -t rsa -C "youremail@example.com"
后面的
your_email@youremail.com
改为你在 Github 上注册的邮箱,之后会要求确认路径和输入密码,使用默认的一路回车即可。
这样就会在家目录~/下生成.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件,前者是私钥(不能泄露),后者是公钥
- 第二步:登陆
Github
,打开account=>settings
,左边选择SSH and GPG keys
,点击New SSH key
按钮,title随便填,在文本框内粘贴id_rsa.pub
文件中的内容。点击“Add Key
”。
为什么Github需要SSH Key,因为因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
在GitHub上免费托管的Git仓库,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放进去。
添加远程库
- 在Github上创建一个名为learngit的仓库
GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。
- 在本地的
learngit
仓库下运行命令:
$ git remote add origin https://github.com/Nkehougaosuni/learngit.git
# 注意,把上面的michaelliao替换成你自己的GitHub账户名
添加后,远程库的名字就是origin,这是Git默认的叫法,可以改成别的,但一般不建议。
- 把本地所有内容推到远程库上
$ git push -u origin master
用git push命令,实际上是把当前分支master推送到远程。
加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令(以后只需要git pull
就可以拉取)。
- 以后只需要通过命令就可以提交最新修改到Github
$ git push origin master
删除远程库
- 使用
git remote -v
可以查看远程库信息 - 使用git remote rm 命令可以删除远程库,如
$ git remote rm origin
这里的“删除”是指解除了本地和远程的绑定关系,并不是物理上删除了远程库。要真正删除远程库,需要登陆GIthub,在后台页面选择删除。
从远程库克隆
现在考虑先创建远程库,再从远程库克隆的情形。
- 创建远程库-在github上新建一个名为
gitskills
的仓库,并勾选Initialize this repository with a README
。 - 使用
git clone
命令克隆本地库
$ git clone git@github.com:Nkehougaosuni/gitskills.git
GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。
使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。
分支管理
创建与合并分支
- 创建
dev
分支,并切换到dev
分支
$ git switch -c dev
# 参数`-c`表示创建并切换,相当于下面两条命令
$ git branch dev
$ git switch dev
- 使用
git branch
命令查看当前分支
$ git branch
* devmaster
git branch
命令会列出所有分支,当前分支前会标一个*
号
- 使用
git merge dev
命令将dev
分支的工作成果合并到当前分支(master
分支)上
$ git merge dev # 将dev分支合并到当前分支(master分支)上
Updating 150ffb8..2e9a1a2
Fast-forwardk.txt | 0readme.txt | 3 ++-2 files changed, 2 insertions(+), 1 deletion(-)delete mode 100644 k.txt
git merge
命令用于合并指定分支到当前分支
Fast-forward
信息表示这次合并是“快进模式”,即直接把master指向dev的当前提交。
事实上还有别的合并方式
- 使用
git branch -d dev
命令删除dev
分支
$ git branch -d dev
Deleted branch dev (was 2e9a1a2).
不能删除当前所处的分支
Git鼓励使用分支完成某个任务,合并后再删掉分支。
在一个分支有工作尚未add或者commit,此时切换到其他分支,会出现问题。详情可以参考https://blog.csdn.net/w522301629/article/details/81331273
解决冲突
合并分支可能会存在冲突。假如在新建的feature1
分支上对readme.txt文件进行修改并提交。并切换到master
分支上对readme.txt文件进行修改并提交
这种情况下,git无法执行
快速合并
,只能试图把各自的修改合并起来,但这种合并可能有冲突。
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
使用
git status
可以告诉我们冲突的文件,然后必须手动解决冲突之后再提交。
Git用<<<<<<<,=======,>>>>>>>
标记出不同分支的内容
解决冲突并提交后的结果如下图。
使用git log --graph --pretty=oneline --abbrev-commit
也可以看到分支的合并情况。
最后删除feature1分支
分支管理策略
合并分支的时候,git会尽可能使用fast forward
模式,这种模式下,删除分支后,会丢掉分支信息。
合并分支时,加上--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出曾做过合并,而fast forward
合并则看不出来曾做过合并。
$ git merge --no-ff -m "merge with no-ff" dev # 在master分支下对dev分支进行普通合并
因为普通合并会创建一个新的commit,所以要加上-m参数,将commit的描述写进去。
此时通过git log --graph --pretty=oneline --abbrev-commit
命令就可以查看分支历史
$ git log --graph --pretty=oneline --abbrev-commit
分支策略
master
分支应该是非常稳定的,仅用来发布新版本,平时不能在上面干活。
dev
分支是不稳定的。每个人都应该有自己的分支,并时不时往dev
分支上合并。
Bug分支
当在dev
分支上工作,但工作还没有commit
时(工作只进行到一半,无法提交),此时在master
分支上忽然有一个必须要修复的bug。这时候,可以使用git stash
命令将工作现场保存起来。
$ git stash -u
git stash -u
可以将包括工作区新增的文件也保存起来。详细可以参考:https://blog.csdn.net/lamp_yang_3533/article/details/80370380
对于切换分支开发,一般使用stash命令,不太建议用commit,如果用commit,会导致很多无效的提交,到时候追溯会比较麻烦
然后就可以放心在需要修改bug的分支上(如master
分支)创建临时分支(如issue-101
)来修改bug
$ git switch master
$ git switch -c issue-101
修改完bug后,提交,并切换到master分支完成对issue-101分支的合并。
# 修改readme.txt文件
$ git add readme.txt
$ git commit -m "fix bug 101“
[issue-101 e05ecd5] fix bug 1011 file changed, 1 insertion(+), 1 deletion(-)
$ git switch master
$ git merge --no-ff -m "mergerd bug fix 101" issue-101
$ git branch -d issue-101 # 删除issue-101
记住修改bug提交的commit-id号,后面复制这次提交需要用到。
然后重新回到dev分支,使用git stash pop
命令或者git stash apply
命令恢复工作现场。
$ git switch dev
$ git stash pop
使用cherry-pick
命令复制提交到当前分支
dev分支是早期从master分支上分出来的,所以这个bug其实在dev分支上也存在。
可以使用cherry-pick命令修复同样的bug
$ git branch
$ git cherry-pick e05ecd5 # 复制一个特定的提交到当前分支
cherry pick命令可以让我们能复制一个或多个特定的提交到当前分支
使用git cherry-pick
前要保证工作区和暂存区是干净的,所以建议先使用stash命令,再使用git cherry-pick
命令。
关于git cherry-pick
的用法可以参考详解 git cherry-pick用法
Feature分支
- 新增一个功能时,最好新建一个
feature
分支,在上面开发,完成后,再合并,最后删除该分支。 - 要丢弃一个没有被合并过的分支,可以通过
git branch -D <分支名>
来强行删除
多人协作
- 从远程仓库克隆时,实际上默认Git是把本地的
master
分支和远程的master
分支对应起来,并且,远程仓库的默认名称是origin
。 - 使用
git remote -v
命令可以查看远程库信息。
$ git remote -v
origin https://github.com/Nkehougaosuni/learngit.git (fetch)
origin https://github.com/Nkehougaosuni/learngit.git (push)
显示了可以抓取和推送的origin地址,如果没有推送权限,就看不到push地址
推送分支
- 推送分支,就是把改分支上的所有本地提交推送到远程库。推送时,要指定本地分支。
- 使用
git push origin master
命令将本地的master分支推送到远程库。
$ git push origin master # 推送master分支到远程库
$ git push origin dev # 推送dev分支到远程库
并非所有本地分支都一定要往远程推送
master分支是主分支,要时刻与远程同步
dev分支是开发分支,团队所有成员都需要在上面工作,所以需要和远程同步
bug分支只用于本地修改bug,没必要推到远程
feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发
抓取分支
- 在另一台电脑(配置SSHkey并添加到Github)或同一台电脑的另一个目录下克隆远程库
$ git clone ...
此时只能看到master分支,但是是可以通过
switch
命令切换过去到其他分支的,具体有哪些分支存在可以通过branch -r
命令查看
- 使用
git switch dev
切换到dev
分支
$ git switch dev
Switched to a new branch 'dev'
branch 'dev' set up to track 'origin/dev'.
这个命令会自动将dev分支关联(跟踪到)到远程库的dev分支
- 使用
git pull origin <远程分支名>
能将远程的分支合并到当前分支
$ git pull origin dev
如果当前分支和远程分支已经建立起了跟踪链接,则可以简写为git pull
多人协作的工作模式
- 使用git push origin <分支名>推送自己的修改
- 如果推送失败,则因为远程分支比你的本地更新,需要先使用
git pull
试图合并 - 如果合并有冲突,则解决冲突,并在本地提交
- 没有冲突或者解决掉冲突后,再用git push origin 推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
Rebase
- 本来两次提交是在master主线上的
- 但因为别人在我们前面推送了远程分支,所以我们无法直接push,需要先
git pull
,而pull的时候又产生了冲突。 - 修改完冲突并再次提交时,使用git log可以看到多了两次提交,一次是冲突合并的提交(在一条新的分叉上),一次是修改完冲突的提交
这是因为pull实际上是git fetch+git merge,而merge如果产生冲突,会在一个分叉上生成一个冲突合并的提交,然后需要手动解决冲突并在当前分支产生一个提交。
- 而使用
git pull -r
则实际上是git fetch+git rebase
:git先将master指针指向拉取分支,然后根据当前分支有几个提交,就生成rebase的几个阶段。
每个阶段中如果有冲突,都需要解决冲突并提交,然后再使用git rebase --continue命令进入下一个阶段。
- 第一阶段有冲突,修改并提交,然后使用git rebase --continue
- 第二阶段有冲突,修改并提交,然后使用git rebase --continue
- 这时,可以发现,使用
rebase
命令可以把本地分叉的提交历史整理成直线 - 注意:使用rebase时要特别注意,git的文档特别提示了:只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作。因为因为rebase会改变提交历史记录(及commit id等),这会影响到别人使用这一远程仓库。
关于
git merge
和git rebase
的区别可以参考:https://blog.csdn.net/michaelshare/article/details/79108233
标签管理
- 标签是版本库的快照。对应某个commit。
创建标签
- 使用
git tag -a 标签名 -m "说明文字" commit_id
可以在当前分支打标签,commit_id
不填,则默认打在最新提交的commit
上
-a指定标签名,可写可不写
- 使用
git tag
查看所有已经打上的标签
标签是按照字母顺序排列的
- 使用
git show <标签名>
可以查看标签信息。
标签总是和某个commit挂钩,如果某个commit即出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。
标签是指向commit的死指针,分支是指向commit的活指针
操作标签
- 使用
git tag -d 标签名
可以删除本地标签
创建的标签都只存储在本地,不会自动推送到远程
- 使用
git push origin <标签名>
可以将某个本地标签推送到远程 - 使用
git push origin --tages
可以一次性推送全部尚未推送到远程的本地标签 - 使用
git push origin :refs/tags/标签名
可以删除远程的标签
使用Github
- 在GitHub上,可以任意Fork开源仓库;
- 自己拥有Fork后的仓库的读写权限;
- 可以推送pull request给官方仓库来贡献代码。
使用Gitee
略
自定义Git
忽略特殊文件
- 忽略某些文件时,需要编写
.gitignore
文件 - 忽略文件的原则是:
忽略操作系统自动生成的文件,比如缩略图等;
忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
各种软件的gitignore文件可以参考:https://github.com/github/gitignore
- 使用
git add -f 文件名
可以突破.gitignore
的限制强行提交 - 在
.gitignore
文件中要排除指定文件的语法是:! 文件名
配置别名
- 使用
git config --global alias.st status
就可以用git st
代替git status
命令 - 将lg配置成如下:
$ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
配置文件
- 参数–global是针对当前用户起作用,不加则只对当前仓库起作用
- 当前仓库的Git配置文件在
.git/config
文件中 - 当前用户的Git配置文件在用户主目录的隐藏文件
.gitconfig
中
$ cat ~/.gitconfig
[filter "lfs"]required = trueclean = git-lfs clean -- %fsmudge = git-lfs smudge -- %fprocess = git-lfs filter-process
[user]name = Nkehougaosuniemail = 124226697+Nkehougaosuni@users.noreply.github.com
[alias]st = statuslg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
搭建Git服务器
- 搭建Git服务器需要准备一台Linux机器
- 第一步:安装git:
$ sudo apt-get install git
- 第二步:创建一个git用户,用来运行git服务:
$ sudo adduser git
- 第三步:创建证书登录:
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub
文件,把所有公钥导入到/home/git/.ssh/authorized_keys
文件里,一行一个 - 第四步:初始化Git仓库:
先选定一个目录作为Git仓库,假定是/srv/sample.git
,在/srv目录下输入命令:
$ sudo git init --bare sample.git
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:
$ sudo chown -R git:git sample.git
- 第五步:禁用shell登录:
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行
$ git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
$ git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。
- 第六步:克隆远程仓库:
现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:
$ git clone git@server:/srv/sample.git