GIT仓库基本使用操作
引言
Git的操作知识可以分成三层:
- 本地仓库的管理(本地搭建git环境,add,commit,版本管理,分支管理等)
- 本地仓库与远程仓库的交互(clone,push,pull,多人协同开发等)
- 远程仓库的管理(部署服务器,数据的备份、迁移、恢复等)
普通开发者需要精通前两层,了解第三层。
1、Git基本知识
1.1 背景
Git是目前世界上最先进的分布式版本控制系统
分布式:不依赖中央服务器 ,更加灵活安全
版本控制:保存历史版本与修改记录,分支管理
Workspace:工作区
Index / Stage:暂存区
Repository:仓库区(或本地仓库)
Remote:远程仓库
1.2 安装Git
Git能在Linux,Max,Windows等平台上运行,以在Windows上使用Git为例:
(1)本地安装环境
从Git官网直接下载安装程序,然后按默认选项安装即可。
(2)安装完成后
git bash :在当前位置打开git命令行
git gui :在当前位置打开git图形化界面
git cmd :管理员模式
说明安装成功。
(3)最后配置自己的用户名与Email地址
config 配置指令
git config
config 配置有system级别 global(用户级别) 和local(当前仓库)三个 设置先从system-》global-》local 底层配置会覆盖顶层配置 分别使用–system/global/local 可以定位到配置文件
查看系统config
git config --system --list
查看当前用户(global)配置
git config --global --list
查看当前仓库配置信息
git config --local --list
如需手动设置则可以使用如下指令
Git global setup
$ git config --global user.name "zhang san"
$ git config --global user.email "zhangsan@163.com"
2、 Git基本操作
2.1 创建仓库
选择一个合适的目录,通过git init
命令把这个目录变成Git可以管理的仓库:
$ git init
然后会发现当前目录下多了一个.git
的目录(默认隐藏),这个目录就是Git的版本库,是Git来跟踪管理版本和分支的,一般不需要手动修改。
2.2 把文件添加到仓库
1、选择待添加的文件。git add <file>
, 若为git add
. 则上传当前文件夹所有文件
$ git add readme.md
2、 用命令git commit -m "<message>"
告诉Git,把文件提交到仓库:
$ git commit -m "wrote a readme file"
这里的message
相当于版本的标志,要起成有意义的名字
注: 可以多次add
不同的文件,最后一次性commit。
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "3 files are added."
此时可以用 git status 查看当前文件区的状态
$ git status
On branch master
nothing to commit, working tree clean
说明当前分支下所有文件均已commit,工作区是“干净”的:
3、 远程仓库
GitHub:提供Git仓库托管服务的网站,只要注册一个GitHub账号,就可以免费获得一个Git远程仓库。
GitLab:仿照GitHub做的一个面向企业的Git仓库软件,方便企业用户部署私有git server。
本地创建了一个Git仓库后,还可以通过GitHub创建一个远程仓库(或部署一个私人GitLab远程仓库),然后让远程仓库与自己的本地仓库进行远程同步,这样,远程仓库既可以作为备份,又可以让其他人通过该仓库来协作开发。
下面以GitHub为例介绍使用远程仓库的基本方法,GitLab是相似的。
3.1 准备工作
(1)GitHub官网注册账号
(2)本地生成密钥/创建ssh key
由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以还需要创建ssh key,然后添加进github的账号里。git bash打开命令行,输入:
$ ssh-keygen -t rsa -C "[username]"
以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。
然后会在用户主目录生成一个 .ssh目录。里面有id_rsa和id_rsa.pub两个文件。两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。
然后在 “ .ssh/ ”目录下操作(先cd ~/.ssh),将公钥复制
$ cat XXXX.pub
这部分不同操作系统上过程会有不同,只要能根据自己情况取得密钥即可。最后,登录github后用户界面–>Settings–>SSH and GPG keys,将刚才复制的密钥粘贴到相应位置即可。完成上面的步骤之后就可以使用ssh来连接Git远程仓库了。
常见问题的解决:
https://www.cnblogs.com/sheldonxu/archive/2012/09/17/2688281.html
https://blog.csdn.net/samxx8/article/details/51497004
https://blog.csdn.net/qq_38335037/article/details/81163312
(3)登陆GitHub,创建一个远程仓库
刚刚创建的远程库还是空的,自带了官方教程。
3.2 从远程库克隆
在本地的合适目录下,使用git clone [你要克隆的远程仓库地址]
命令克隆。
注意到,GitHub给出的地址不止一个。实际上,Git支持多种协议,默认的git://
使用ssh,但也可以使用https等其他协议。
$ git clone https://github.com/user-name/repo-name.git
$ git clone git@github.com/user-name/repo-name.git
本项目服务器:http://10.108.211.249/
,所以远程仓库地址如下:
$ git clone http://10.108.211.249/tencent-project/test.git
$ git clone git@10.108.211.249:tencent-project/test.git
克隆结束后,目录下自动会有一个.git
的隐藏目录,因为是clone来的,所以.git
文件夹里存放着与远程仓库一模一样的版本库记录。clone操作是一个从无到有的克隆操作,再次强调不需要git init初始化。
注:基于ssh协议进行远程仓库连接,经常出现意想不到的坑(比如Please make sure you have the correct access rights
and the repository exists.),没有强迫症的话,建议都用http协议。
3.3 把本地库的内容推送到远程库
1、 将已有的本地仓库关联远程库。方法是git romote add origin [你要上传的远程仓库地址
]
$ git remote add origin http://10.108.211.249/tencent-project/test.git
如出现 fatal: remote origin already exists.
意思是仓库名重复,需要先移除已有关联:
# 查看当前关联
$ git remote -v
# 移除关联origin
$ git remote rm origin
# 也可以尝试修改远程仓库的简写名
$ git remote rename origin old-origin
2、用git push
命令把本地库的内容推送到远程
$ git push -u origin master
这里实际上是把本地当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
关于分支管理后面会更加详细地介绍。
3.4 其他
git push命令详解
git push的一般形式为 git push <远程主机名> <本地分支名> <远程分支名>
, 第一个master是本地分支名,第二个master是远程分支名。
例如 git push origin master:refs/for/master
,即是将本地的master分支推送到远程主机origin上的对应master分支, origin 是远程主机名
- origin是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
- 如果远程分支被省略,如
git push origin master
,则表示将本地分支推送到与之存在追踪关系的远程分支(通常两者同名),如果该远程分支不存在,则会被新建 - 如果本地分支和远程分支都被省略,如
git push origin
,则表示将本地当前分支推送到origin主机的对应分支 - 如果当前分支与多个主机存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数使用Git push。
git push origin --all
,不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机。git push origin --tags
,推送标签
后面项目管理部分会详细讲解。
Gitlab的两个密钥
gitlab有一个坑,那就是它有两个密钥,一个是SSH一个是Deploy。SSH拥有push和pull的权限,而Deploy只能pull不能push。在Gitlab里有两个添加SSH的地方,一个在项目里,一个在Gitlab主页个人资料设置的SSH密钥里。你应该把SSH keys填在 Gitlab主页下的个人资料设置–SSH密钥里,在这里添加的才是SSH。而在项目里添加的是Deploy。 没错,你生成的确实是SSH,讲道理你也应该可以push,但是千算万算你没算到gitlab有个坑。它可能觉得你懂它,所以它没告诉你。
4、 项目管理
Git的版本库(即工作区下的隐藏目录.git
)里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。版本管理与分支管理是Git的重要内容,需要掌握。
4.1 版本库操作的本质
前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add
把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
至于git status
则显示文件、文件夹在工作区、暂存区的状态
每次修改,如果不用git add
添到暂存区,那就不会加入到commit
中,修改不会被提交。
版本管理和分支管理的本质就是文件在工作区、暂存区、版本库之间的互操作。
4.2 版本管理
注:所有的版本控制系统,其实只能跟踪文本文件的改动(如TXT,html,md,各种程序代码等),但是无法跟踪Microsoft的Word文件的改动。真正使用版本控制系统,要以纯文本方式编写文件。文本都是有编码的,为了减少风险,建议都使用标准的UTF-8编码。
4.2.1 提交修改
有时你修改了readme.md文件,需要那修改提交到Git版本库,提交修改和提交新文件是一样的两步。
第一步:git add readme.md
第二步:git commit -m "add distributed"
像这样,你不断对文件进行修改,然后不断提交修改到版本库里, 就好比玩RPG游戏时的游戏存盘。
4.2.2 版本回退
每提交一个新版本,实际上Git就会把它们自动串成一条时间线。版本回退就好比玩游戏时的游戏读盘。
首先,Git必须知道当前版本是哪个版本。
使用git log
查看当前版本库的状态,可以追溯从最近到最远的提交日志。
$ git log
commit e475afc93c209a690c39c13a46716e8fa000c366 (HEAD -> master)
Author: Zhang San <zhangsan@163.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Zhang San <zhangsan@163.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file
包括历代版本的commit id
(版本号),author(提交用户),date(提交时间)。其中版本号是锁定版本的唯一标志。
在Git中,用HEAD
表示当前版本,上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100
。
回退到上一个版本:
$ git reset --hard HEAD^
换到指定版本,用版本号:
$ git reset --hard eaadf
# 版本号没必要写全,前几位就可以了,Git会自动去找。
注:其实HEAD的本质是一个指针,版本回退的本质是改变HEAD指向的版本号。HEAD执行某一个版本号,即是把当前版本定位在哪。
注2:这里如果有参数--hard
,则直接把工作区的内容也修改了,不加--hard
参数,则只是操作了暂存区,不影响工作区的内容。建议不要轻易使用有参数--hard
的版本回退。如果你没有commit
你的本地修改,甚至于你都没有通过git add
追踪过这些文件),使用git reset --hard
,就像自己执行delete命令,后果是毁灭性的。 )
4.2.3 撤销修改
(1)撤销工作区修改
git允许使用git checkout -- file
命令来丢弃工作区的修改:
# 把readme.md文件在工作区的修改全部撤销
$ git checkout -- readme.md
这里有两种情况:
一种是readme.md自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.md已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit
或git add
时的状态。
(2)撤销暂存区修改
git reset
命令既可以回退版本,也可以把暂存区的修改回退到工作区。方法为:
$ git reset HEAD readme.md
这里用HEAD
,表示最新的版本,相当于把暂存区的修改撤销掉(unstage),重新放回工作区。
然后再使用撤销工作区修改的命令
$ git checkout -- readme.md
4.2.4 删除文件
删除也是一个修改。
考虑已经在工作区删除了文件,那么,一般在git操作中有两种场景:
(1)彻底删除
此时工作区和版本库就不一致了,所以先从版本库中删除该文件,然后用commit提交新版本。
$ git rm readme.md
$ git commit -m "remove readme.md"
(2)撤销删除
此时可以从版本库中还原出删除前最近版本的文件
$ git checkout -- readme.md
4.3 分支管理
教程:https://blog.csdn.net/ShuSheng0007/article/details/80791849
在版本回退里,你已经知道,每次提交(每一个版本),Git都把它们串成一条时间线,这条时间线就是一个分支。一开始的时候,git内只有一条分支,叫做主分支,即master分支。分支本质也是指针。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
总结:分支指向提交点,HEAD指向分支,从而唯一确定当前分支的提交点
在实际开发工作中,必须养成良好的项目管理方式。因为分支和提交点的本质都是指针,所以Git创建、合并和删除分支速度都非常快,这是Git在鼓励项目中多使用分支,完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。
4.3.1 基本操作
查看当前所有分支(分支树):
$ git branch
创建分支:
$ git branch <name>
切换分支:
$ git checkout <name>
# 或者
$ git switch <name>
创建+切换分支:
$ git checkout -b <name>
# 或者
$ git switch -c <name>
合并某分支到当前分支:
$ git merge <name>
删除分支:
$ git branch -d <name>
4.3.2 解决冲突
本地合并分支时,可能会有冲突,此时需要先解决冲突
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再在本地提交。
4.3.3 分支管理策略
(1)多人协作
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,当产品要发布的时候,再把dev
分支合并到master
上。团队开发成员每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
(2)bug分支与feature分支
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
软件开发中,总有无穷无尽的新的功能要不断添加进来。添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。而且,如果需要取消新功能也很方便。
4.3.4 分支推送与拉取
当你从远程仓库克隆时,实际上Git自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
。 默认的推送命令是推送主分支:
多人协作的工作模式需要多次在本地仓库与远程仓库(这里的远程仓库是clone在本地版本库的远程仓库,即git服务器上的远程仓库的本地版本)之间推送、拉去和关联分支。
基本操作如下:
(1)推送分支
推送分支,就是把该分支上的所有本地提交推送到远程库。
git push的一般形式为 git push <远程主机名> <本地分支名> <远程分支名>
。
一般远程分支默认与本地分支相同。推送时,指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:
$ git push origin <branch-name>
(2)抓取分支
抓取分支,就是拉取远程分支更新到本地仓库的操作。
git pull的一般形式为git pull <远程主机名> <远程分支名>:<本地分支名>
将远程主机origin的master分支拉取过来,与本地的branchtest分支合并。
比如远程仓库里的学习资料有了新版本,需要把新版本的内容获取下来然后再与本地分支merge(合并)。默认情况下是把远程仓库的master分支拉去到本地当前分支
$ git pull origin
事实上,git pull = git fetch + git merge
,即
$ git pull origin master:branchtest
# 等价于
$ git fetch origin master:brantest
$ git merge brantest
如果想要更加可控一点的话推荐使用fetch + merge。
(3)多人协作基本工作模式
多人协作时,大家都会往master和dev分支上推送各自的修改。如果你同事张三已经向origin/dev
分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送(git push
)。那么git会报推送失败,因为张三的最新提交和你试图推送的提交有冲突,即远程分支比你的本地更新。
解决办法也很简单,先用git pull
把远程的新提交从origin/dev
抓下来,然后,在本地合并,解决冲突, 本地提交,再推送git push
。
$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> dev
git pull
也失败了,提示no tracking information,原因是没有指定本地分支与远程origin/dev
分支的链接,根据提示,设置链接:
$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
(4)常见问题的解决
1、 fatal: refusing to merge unrelated histories
:
git pull
失败,提示拒绝合并不相关的历史。出现这个问题的最主要原因还是在于本地仓库和远程仓库实际上是独立的两个仓库。假如之前是直接clone的方式在本地建立起远程仓库的克隆本地仓库,就不会有这问题了。 解决办法是,改为以clone的方法拉取远端库, 本地要加入的代码放到远端库下载到本地的库, 然后提交上去,相当于执行一次update。另外一种解决办法是改用命令git pull origin master --allow-unrelated-histories
,强制仓库自动融合,比较危险,容易出现意想不到的错误,不建议使用。
2、To do
4.3.5 分支重命名
假设分支名称为oldName
想要修改为 newName
1. 本地分支重命名(还没有推送到远程)
git branch -m oldName newName
2. 远程分支重命名 (已经推送远程-假设本地分支和远程对应分支名称相同)
a. 重命名远程分支对应的本地分支
git branch -m oldName newName
b. 删除远程分支
git push --delete origin oldName
c. 上传新命名的本地分支
git push origin newName
d.把修改后的本地分支与远程分支关联
git branch --set-upstream-to origin/newName
参考链接:
4.3.6更换主分支
设置Github、Gitlab的主分支属性
1.settings->repository->Default Branch: 修改defult分支为其他分支
2.设置Protected Branches为非保护
参考链接:
4.3.7分支的合并
参考链接:
4.4 标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。即一个标签可以唯一确定一个版本提交点。虽然Git已经有commit id
(版本号)了,但是没有可读性,所以引入tag,其实等于用tag代替 commit id
用于更加方便的查找。
注意标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。
用git tag <tag_name>
新建标签:
# 默认在`HEAD`上打上标签
$ git tag v1.0
# 也可以指定一个commit id
$ git tag v0.9 f52c633
查看所有标签
$ git tag
用git show
查看标签信息:
$ git show v0.9
commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:56:54 2018 +0800
commit-message
diff --git a/readme.md b/readme.md
删除标签:
$ git tag -d v0.1
推送标签到远程:
# 推送某个标签到远程
$ git push origin v1.0
# 一次性推送全部尚未推送到远程的本地标签
$ git push origin --tags
删除远程标签:
$ git push origin :refs/tags/v0.9
5、其他
5.1忽略.DS_Store文件
1.什么是.DS_Store?
.DS_Store,用于存储当前文件夹的一些Meta信息。mac上特有的。
2.存在哪些问题?
如果你没有忽略此类文件时,你和你的小伙伴和代码时就会有如下的问题:
error: Your local changes to the following files would be overwritten by merge: .DS_Store
或:
both modified: .DS_Store
3.如何忽略?
-
每个文件夹中创建
.gitignore
文件,写入.DS_Store
和*/.DS_Store
,命令行执行以下命令:touch .gitignore #创建gitignore隱藏文件 vim .gitignore #编辑文件,加入指定文件
-
经常在其他文件夹下面也都会生成.DS_Store文件,所以我们需要全局ignore该文件创建
.gitignore_global
,然后修改它,命令执行如下:touch ~/.gitignore_global vim ~/.gitignore_global
在.gitignore_global中写入:
.DS_Store */.DS_Store
-
在
~/.gitconfig
中引入.gitignore_global
:
git config --global core.excludesfile ~/.gitignore_global
-
若检查发现修改中仍然有.DS_Store文件,则可能原因是在早期的提交中就已将
.DS_Store
文件提交到repository了。如果一个文件已经被提交,那么就算这个文件已经被写到.gitignore
文件中,它的修改仍然会被追踪到。
所以我们需要手动将repository中的.DS_Store文件移除,可以使用以下命令移除:git rm --cached .DS_Store//移除当前文件夹下的.DS_Store文件 find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch //移除文件夹下的所有.DS_Store文件
然后最后再检查一下会发现已经搞定了😊
参考链接