一、安装GIT
到官网下载GIT:https://git-scm.com/downloads
二、创建仓库
在要创建仓库的文件夹空白地方点击右键
在弹出的菜单中点击"GIT Bash Here", 然后初始化仓库。
$ git init
成功后该文件夹中会多出一个".git"的隐藏文件夹,这个目录是GIT用来跟踪管理版本的,没事千万不要手动乱改这个目录里面的文件,否则,会把git仓库给破坏了。
.git是隐藏文件夹,要设置显示隐藏文件夹后才可见。
1.打开文件夹选项
或
2.设置隐藏文件为显示
记住,确认有.git文件夹存在就行了,不要随便改动里面的内容。
三、GIT的分区和工作流程
- Workspace:工作区
就是程序员编辑代码的地方,就是.git(仓库)所在的那个目录(不是.git里面),所有的改动都在这个区进行,并先在这个区直接起作用。 - Index / Stage:暂存区
改动文件暂时存放的地方,所有要提交到仓库的改动(对工作区中文件的增、删、改)都要先添加(add)到这里,然后再提交(commit)。当然,也可以撤回(checkout)工作区。 - Repository:仓库区(或本地仓库)
每次提交都是一个版本,仓库区保存了之前提交的各个版本,可以push(推送)到远程仓库,也可以恢复(reset、revert、checkout)回工作区。 - Remote:远程仓库
远程仓库保存着由一个或多个作者从其本地仓库推送(push)上去的各个版本及分支。各个作者可以把master(主分支)的最新版本pull(拉取)下来自己本地,然后合并上自己的改动,再push(推送)到远程仓库的master分支上去供其它人使用。每个人都负责自己那部分,然后都把自己的成果合并到master分支,这样就可以轻松地进行分工合作了。
四、把文件添加到暂存区
4.1 先查看仓库当前状态
$ git status
红色的是未添加到暂存区的文件,如果是刚创建的仓库,git status后该文件夹中的所有文件都会是红色,因为这些文件还没加入过暂存区,都是"untracked files"(未被跟踪文件)。
4.2 把单个文件加入暂存区
$ git add source1.c
4.3 把多个文件加入暂存区
$ git add source1.c source2.c source3.c
4.4 把所有文件加入暂存区
$ git add .
添加到暂存区后,查看一下仓库状态,添加后的文件变成绿色
五、清除未加入暂存区的文件
如果想清除新增的文件,或从未track过(加入过暂存区)的文件,可以用下面的命令。
例如:新增了source4.c和source5.c,或者说创建本地仓库后从未加入过暂存区,现在想清除掉。
5.1 查看clean会删除哪些文件
$ git clean -n
5.2 删除当前目录下所有没有track过的文件,不会删除.gitignore文件里面指定的文件夹和文件
$ git clean -f
执行后,文件夹中的source4.c和source5.c会被删除掉,再次git status(查看仓库状态)就看不到这两个文件的提醒了
5.3 删除指定路径下的没有被track过的文件
$ git clean -f <路径>
5.4 删除当前目录下没有被track过的文件和文件夹
$ git clean -df
5.5 删除当前目录下所有没有track过的文件. 不管他是否是.gitignore文件里面指定的文件夹和文件
$ git clean -xf
5.6 添加.gitignore文件
一般我们总会有些文件无需纳入 Git 的管理,不需要添加和提交,也不希望它们总出现在未跟踪文件列表。通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。
例如,有个临时文件叫file1.txt,还有编译过程中生成汇编文件(*.o),我们想忽略掉。
在还没有.gitignore文件的时候,查看状态时会显示这个未跟踪文件。
5.6.1 创建一个.gitignore文件
先看下在window下创建的情形
新建文档后,出现下面文件。
改名。
出错!因为我们要创建名称为空,后缀为.gitignore的文件,而window中创建文件必须得有名称。
那咋整?
方法还是有的,我们可以到git base的黑框中创建。然后用vim或vi编辑器编辑,或者在目录中双击打开编辑也没问题。
创建并编辑好.gitignore文件(编辑过程放在本小节末尾)后,我们再查看一下仓库状态。
可见,之前显示未跟踪的文件已经被忽略掉了。
接下来我们可以把.gitignore添加并提交,这样我们最新版本中就有.gitignore文件了。
但是,如果该文件已经在仓库中,那忽略就无效了。
例如,source1.c已经是仓库中的文件,newfile.txt是新建但已加入暂存区的文件,我们现在要忽略它们。
可见,忽略是无效的,source1.c改动后,还是会有相应的提醒。暂存区的newfile.txt也还在,而且如果继续对newfile.txt有改动,还会出现相应的提醒。
可见,如果要忽略某文件,要先确保此时它不在仓库和暂存区中。
如果它们已经在仓库和暂存区中了,那唯一的办法就是将它们清除出来。
如下图:
下面是vim编辑器编辑的过程。
执行$ vim .gitignore命令后,vim编辑器打开.gitignore文件(空文件),此时是vim编辑器的正常模式,点击i即可进入插入模式(可编辑)。
5.6.2 gitignore文件的写法
1.所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
2.可以使用标准的 glob 模式匹配。
3.匹配模式最后跟反斜杠(/)说明要忽略的是目录。
4.要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。
- 星号(*)匹配零个或多个任意字符;
- [abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);
- 问号(?)只匹配一个任意字符;
- 如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。
例如:
# 此为注释 – 将被 Git 忽略
*.a # 忽略所有 .a 结尾的文件
!lib.a # 但 lib.a 除外
/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
/、/* 和 !
/OBJ/
表示忽略OBJ目录下的所有文件。
/OBJ/
!/OBJ/UWB.sct*
表示忽略OBJ目录下的文件,UWB.sct除外。
“!”用来恢复追踪,但如果文件所在的目录被忽略了,这个文件无法恢复追踪。
* 和 **
/a/*/b 只能匹配a的二级子目录的b, 比如a/x/b
/a/**/b 可以匹配a的任意级子目录的b,比如a/b, a/x/b, a/x/y/b …
六、删除仓库中的文件
6.1 把删除仓库中某文件的请求添加到暂存区,且取消对该文件的跟踪,但不删除源文件
$ git rm --cached source1.c
提醒:如果要清除多个文件,列在后面即可。
执行后:
删除仓库中的文件的请求添加到暂存区,提交后生效。
取消对该文件的跟踪,立刻有效
工作区中的文件及其改动还在,只是这个改动没有添加到暂存区。
6.2 把删除仓库中某文件的请求添加到暂存区,并删除源文件
$ git rm source1.c
提醒:如果要清除多个文件,列在后面即可。
命令执行后:
源文件立即被删除,而删除仓库中的文件是提交后才生效。
七、撤销被跟踪的文件在工作区的变动
7.1 先查看仓库当前状态
$ git status
这个就是工作区中有变动的跟踪文件。
7.2 查看被跟踪的文件的具体改动
$ git diff
7.3 查看某个被跟踪的文件修改了那些内容
$ git diff soure1.c
提醒:如果要查看多个文件,列在后面即可。
红色为删除的内容,绿色是增加的内容。
7.4 查看某类被跟踪的文件修改了那些内容
有时候可能只是改动了一两个文件,但编译后git status时发现一大堆文件有改动(标红),如果直接用git diff,下面的内容多到让你怀疑人生。所以,我们可以针对也看某类或某几类文件的改动。
提醒:如果要查看多个类,列在后面即可。
$ git diff *.c *.h
当然,更好的方法是用.gitignore文件到忽略那些中间文件,不去踪它们。具体操作请看5.6.1小节。
7.5 撤销某被跟踪文件在工作区的变动
$ git checkout -- source1.c
提醒:如果要清除多个文件,列在后面即可。
例如:上图的"modified: source1.c"就表明暂存区中的文件source1.c已发生了改动,可用$ git checkout – source1.c来放弃这个改动。
7.6 撤销所有被跟踪的文件在工作区的变动
$ git checkout .
7.7 取消上次git add后,本地工作区对被跟踪文件所有改动(增加、删除、修改),并删除未跟踪文件
$ git checkout . && git clean -df
例如:当前目录有source1.c、source2.c和source3.c,上次已全部加到暂存区。
后来,增加了source4.c,而且把source3.c的内容改为:333。
查看仓库状态,可以看到,当前目录与上次加入暂存区(git add)相比,所有的改动。
如果想丢弃这些改动,可以用:$ git checkout . && git clean -df
根据上面的内容可知:
$ git checkout . 是丢弃被跟踪的文件在工作区中的改动,如上面的source3.c的改动
$ git clean -df 是丢弃未跟踪过的文件,如上面新增的source4.c
这两个命令执行后,跟踪的文件就可以恢复上次git add后的状态。
八、提交到本地仓库
8.1 提交之前,先查看仓库状态,确保暂存区的文件没有变动
$ git status
上图中,"modified: source1.c"表示该文件有改动,要把这个提示处理掉才能提交。
方式1 把这个改动更新到暂存区,可以用:
$ git add source1.c "或 "$ git add .
方式2 把这个改动丢弃掉,可以用:
$ git checkout -- source1.c
8.2 如果暂存区中有变动的文件,要先用添加命令更新到暂存区
$ git add sorce1.c //把source1.c加到暂存区
或
$ git add . //把当前目录中所有文件加到暂存区
8.3 提交文件(–m后面的是注释)
$ git commit –m “注释内容”
这样只是提交到本地仓库,要推送到远程仓库(github)中保存,要有以下条件或操作:
1.拥有一个github帐号
2.创建github仓库
3.在本地创建SSH KEY,然后添加到github的允许列表中
4.建立本地和github仓库的关联
8.4 修改最近一次提交的注释
$ git commit --amend
8.5 修改之前某次或某几次提交的注释
$ git rebase -i HEAD~2 //修改最近两次提交的注释
下面是执行git rebase -i HEAD~2命令后弹出的窗口
上面刚弹出来的窗口还不能编辑,点“i”即可进入编辑模式。
编辑好指令后,进入修改注释的步骤。
8.6 tap(标签)
在频繁commit的后,会有一长串密密麻麻的提交记录。一旦项目出现问题,需要检查某个节点的代码问题,就会有点头疼。虽然有commit message,但还是有存在查找困难和描述不清的问题。
同大多数 VCS 一样,Git 也可以对某一时间点上的版本打上标签。人们在发布某个软件版本(比如 v1.0 等等)的时候,经常这么做。
打标签的作用,就是给项目的开发节点,加上语义化的名字,也即功能版本的别名。打上标签名的同时,写上附带信息,可以方便项目日后维护过程中的回溯和复查。另外,也可以通过标签记录,大致了解当前项目的向下兼容性、API的修改和迭代情况。
8.6.1 给当前版打标签
使用git tag命令跟上tag名字,直接创建一个tag。
$ git tag v1.0
还可以加上-a参数来创建一个带备注的tag,备注信息由-m指定。
$ git tag -a v1.0 -m "备注信息"
8.6.2 给指定的某个commit号打标签
打tag不必要在head之上,也可在之前的版本上打。
8.6.3 列出已有标签
$ git tag
8.6.4 查看标签的详细信息
$ git show v1.0 //查看v1.0标签的详细信息
8.6.5 切换到某个标签对应的版本
$ git checkout v1.0 //切换到v1.0标签对应的版本
如果你想对v0.5版本上再作修改,进入v0.5版本后是游离状态,你可以在这个状态下创建并切换到一个新的分支,就可以保存你的修改和提交了。
8.6.6 将标签同步到远程仓库
$ git push origin v1.0 //将本地v1.0标签同步到远程仓库
8.6.7 删除标签
8.6.7.1 删除本地标签
$ git tag -d v1.0 //删除本地的v1.0标签
8.6.7.2 删除远程仓库的标签
如果要删除远程仓库的标签,要先删除本地对应的标签,然后再删除远程仓库的标签,否则无效。
$ git push origin :refs/tags/v1.0 //删除远程仓库的v1.0标签
提示:有关远程仓库的内容请看下面的第九和第十讲。
九 、注册帐号、创建仓库、创建SSH KEY
9.1 先到官网注册github账号:https://github.com/
9.2 创建github仓库
9.3 创建SSH KEY
由于你的本地Git仓库和github仓库之间的传输是通过SSH加密的,所有要在本地创建一个SSH KEY然后添加到github允许的列表中。
9.3.1 查看是否有SSH,如果从未创建过,那一定没有
$ cd ~/.ssh
$ ls
检查是否已经存在 id_rsa.pub 或 id_dsa.pub 文件,如果有,则已经有SSH了
9.3.2 创建一个 SSH key
$ ssh-keygen -t rsa -C "注释文字"
代令参数含义:
-t 指定密钥类型,默认是 rsa ,可以省略。
-C 设置注释文字,比如邮箱。
-f 指定密钥文件存储文件名。
以上代码省略了 -f 参数,因此,运行上面那条命令后会让你输入一个文件名,用于保存刚才生成的 SSH key 代码
9.3.3 拷贝 id_rsa.pub 文件的内容
$ clip < ~/.ssh/id_rsa.pub
执行该命令后,SSH KEY的公钥已被复制到粘贴板,到github粘贴即可。
9.3.4 到github打开设置
9.3.5 添加SSH
填上任意title,在Key文本框里黏贴id_rsa.pub文件的内容。
9.3.6 复制远程库的地址
点击github仓库
十、关联远程仓库
10.1.查看远程库的信息
$ git remote
$ git remote –v (-v显示对应的克隆地址)
如果还没关联远程仓库,执行后没有显示任何内容。
10.2.关联远程库
$ git remote add heater_mainboard [email protected]:wuzulie/Demo_hub.git
远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
关联后,再git remote查看远程仓库信息就有内容了,fetch对应的是抓取地址,push是推送的地址。
10.3 删除远程库
$ git remote rm origin
删除后再查看远程库,就没有内容了。
十一、创建与合并分支
几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线的稳定版本。
首先master主分支应该是非常稳定的,也就是用来发布新版本,一般情况下不允许在上面干活。
干活一般情况下在新建的dev(develop)分支上干活,在dev分支上增加的新功能调试稳定后,可以合并到主分支master上来发分布。
如果突然发现一个BUG,可以从master分支上新建一个分支专门修补这个BUG。修补完后,再合并回master分支。一般不在dev分支或者其它新功能开发分支上修补BUG,因为这些分支的代码在编辑过程中,代码一般还不完整,而且很大可能有新BUG,甚至暂时还无法运行。
截止到目前,只有一个分支,即master分支。HEAD指向的就是当前分支的最新版本。
11.1 分支策略
在上面的例子中,我们一直只用到master分支和dev分支。在实际开发中,不可能只有两个分支。那应该还有什么分支,这些分支有什么不同呢?
- 项目中长期存在的两个分支
master:主分支,负责记录上线版本的迭代,该分支代码与线上代码是完全一致的。
develop(dev):开发分支,该分支记录相对稳定的版本,所有的feature分支和bugfix分支都从该分支创建。
- 其它分支为短期分支,其完成功能开发之后需要删除
feature:特性(功能)分支,用于开发新的功能,不同的功能创建不同的功能分支,功能分支开发完成并自测通过之后,需要合并到 develop 分支,之后删除该分支。
bugfix:bug修复分支,用于修复不紧急的bug,普通bug均需要创建bugfix分支开发,开发完成自测没问题后合并到 develop 分支后,删除该分支。
release:发布分支,用于代码上线准备,该分支从develop分支创建,创建之后由测试同学发布到测试环境进行测试,测试过程中发现bug需要开发人员在该release分支上进行bug修复,所有bug修复完后,在上线之前,需要合并该release分支到master分支和develop分支。
hotfix:紧急bug修复分支,该分支只有在紧急情况下使用,从master分支创建,用于紧急修复线上bug:修复完成后,需要合并该分支到master分支以便上线,同时需要再合并到develop分支。
11.2 查看当前所有的分支
$ git branch
11.3 创建分支
如果要修补某BUG,或要增加新功能,往往会创建一个分支出来修改和测试。达成目标后,就可以合并回主分支,然后主分支的版本就会有相应的修改。
$ git branch dev //创建分支dev
创建后,新分支和主分支一模一样,包括仓库区、暂存区、工作区和提交记录也一样。
11.4 切换分支
$ git checkout dev
注意:如果切换分支时,暂存区和工作区有修改,切换可能会出错甚至丢失文件。
如果带修改切换分支,有以下五种情况:
1.仓库中的文件没有改动,暂存区和工作区中变动的文件都是未commit过的
这种情况下可以切换到任何分支,切换后暂存区和工作区的改动也共享过去。之所以说共享,是说你切换过去后,暂存区和工作区的内容一样。而且,如果你在任何一个分支的暂存区或工作区这些未commit过的文件有改动,该改动对其它分支也是起作用的。
2.仓库中的文件有改动,但切换前后的分支该文件内容没有冲突
这种情况下也可以切换,切换后暂存区和工作区的改动也共享过去。这个共享和上面说的一样,就不多说了。
3.仓库中的文件有改动,而且切换前后的分支该文件内容有冲突
这种情况下也切换就会出错了,如下图。
4.当前分支的新文件与要切换的分支仓库中的文件同名,但内容不一样或者该文件还没添加到暂存区
这种情况下也切换就会出错了,如下图。
注意:如果同名文件还没添加到暂存区,管内容是否一样,切换都会出错。
5.当前分支的新文件与要切换的分支仓库中的文件同名,而且内容也一样(可以理解为同一个文件),同时也添加到了暂存区
这种情况下就会丢文件了,如下图。
所以切换分支前要特别注意,一定要确保暂存区是干净的。如果有文件,一种情况是切换不了,得先提交。另一种情况(暂存区中的文件在切换后的分支仓库中也有,而且刚才内容一样)是会丢文件。
当然,如果是用创建并切换命令"$ git checkout –b dev ",那就不管暂存区和工作区的状态是否干净,都不会出错。因为这样不可能有冲突,所以创建并切换过去后,新分支仓库区、暂存区和工作区的内容和新建时master分支一样。
详细请看下一小节(11.5)。
11.5 创建新分支并切换到该分支上
$ git checkout –b dev
这相当于执行下面这两条命令:
$ git branch dev //创建新分支dev
$ git checkout dev //切换到分支dev
查看分支时,带"*"号的分支,表示是当前分支。
如果创建切换之前暂存区和工作区是干净的(与最新版本相比,没有任何改动),那么创建切换之前的工作空间也是干净的。
但如果创建切换之前的工作空间不是干净状态,会怎么处理呢?
可以看出,新分支和创建切换时master分支工作空间的状态是一样的。
我们在11.4小节提过,正常切换分支(与创建并切换不同)时,如果暂存区中的文件不是最新版本中跟踪的文件,切换要去后,将会被删除掉。
那么,如果是创建并切换,怎么处理相同情况呢?
可见,如果是创建并切换,新分支的仓库、暂存区和工作区的内容是一样的,是安全的,不会丢文件。
但是,这里要特别提醒的是,不管是切换还是创建并切换,都尽量不要带着修改内容操作。因为不同分支的功能和任务都是不一样的,你在这个分支修改的内容,就应该在本分支提交。你如果切换到了其它分支,其它分支的修改会破坏之前分支的修改成果。甚至如果在其它分支提交了,之前那个分支的暂存区也空了,之前的修改对本分支没有一点作用。
所以,还是建议养成切换分支前先处理掉所有改动的习惯,不然可能会有大麻烦。
11.6 储藏
工作中可能出现以下两种情况:
1.有时候,我们在某分支的修改只进行到一半,还不想提交,免得提交记录太乱。但是,这时候却有个BUG需要紧急修改,或你想到其它分支写另一部分代码。
2.本来应该在dev分支上写的代码,你写在了master分支。你不想去dev分支重新写,而是把master分支上的写的改动搬到dev分支。
有没有方法呢?还真有,下面看看神奇的储藏功能。
11.6.1 储藏当前工作现场
$ git stash //储藏当前工作现场,不加注释
$ git stash save "注释内容" //储藏当前工作现场,带注释
11.6.2 查看现有的储藏
$ git stash list //查看现有的储藏
11.6.3 查看某条储藏和当前内容的差异
$ git stash show //查看最近的储藏与当前内容的改动概况
$ git stash show -p //查看最近的储藏与当前内容的具体改动
$ git stash show stash@{0} -p //查看编号为0的储藏与当前内容的具体改动
11.6.4 恢复储藏的工作现场
$ git stash pop stash@{index} //恢复储藏的工作现场,并删除该储藏记录
$ git stash apply stash@{index} //恢复储藏的工作现场,但保留该储藏记录
如果不指定编号,直接用$ git stash pop,则恢复的是最近的一条储藏记录。
注意:不要用储藏的编号来判断是哪条储藏,因为编号是会变化的。应该根据储藏的分支及注释来判断。
11.6.5 删除储藏的工作现场
$ git stash drop stash@{index} //删除储藏的工作现场
如果不指定编号,直接用$ git stash drop,则删除的是最近的一条储藏记录。
11.6.6 从储藏中创建新的分支
$ git stash branch newbranch
注意:不要用储藏的编号来判断是哪条储藏,因为编号是会变化的。应该根据储藏的分支及注释来判断。
11.7 不同分支内容独立
在一个分支上所做的修改,仅对当前分支有效,对其它分支不影响。
各分支的改动是该分支独有的,要想让该改动作用于master分支,或者要把master分支上的改动更新到当前分支,就要用到分支的合并,具体请看下面两小节。
对于各分支的提交记录,在刚创建时,会带有master分支当时的所有提交记录。但是在这之前,master分支和各小分支增加的提交记录就不共享了,除非用合并到融合各分支的改动。
11.8 在当前的分支上合并其它分支
$ git merge dev
把dev分支合并到当前分支,当前分支可以是master分支,也可以是其它分支。
我们前面提过,我们新建分支是为了分工合作,一般是不同分支负责不同功能模块。有时新建分支是为了修补BUG或增加功能。无论哪种情况,改完后都是要合并回主(master)分支。
还有一种情况是,某分支的改动过程中,需要更新其它分支的改动到当前分支。
例如,某分支在改动过程中,需要更新主分支上的变动到当前分支,就要把主分支合并到当前分支。
注意:在多人协作开发过程中,如果要更新master分支上其它程序人的改动到当前分支,要先获取远程仓库中master分支的最新版本到本地,然后再合并。
如果在多人协作中要推送改动到远程仓库,也就先获取最新的master版本,然后合并当前分支的改动,最后才能推送到远程仓库,否则会因为本地master分支版本比远程仓库中master分支版本旧而无法推送。
以上的合并都是理想状态,很顺利,然而有时并非如此。
因为前面的合并只在只有一方改动的情况下进行的,那如果在双方都有改动的情况下,会怎么处理呢?
11.9 解决合并分支时的冲突
如果双方都改了同一个文件的不同地方,会怎么处理呢?
那么问题来了,如果,双方都在同一文件同一地方做了改动,会出现什么情况呢?
可以看到,合并果然冲突了。但要注意的是,合并已经完成了,只是有冲突没提交而已。也就是说,master分支已经把dev分支的内容合并过来了,包括有冲突的source1.c的内容也合并过来了。
那么,有冲突的source1.c合并过来是什么样的呢?
可以看出,冲突部分处理方式是两边的修改都保留,然后固定的标识符标出不同分支的修改内容。
"<<<<<<< HEAD"表示当前分支,不一定是主分支
"======="是分隔符
“>>>>>>> dev”:表示dev分支,也就是相对当前分支来说是其它分支
这时候如果你提交,完全不会出错,但内容绝对不是你想要的。因为文件里面冲突部分包涵了两个分支的改动,还包涵了分支标识和分隔符。
这要是程序,编译绝对会出错。
所以,在提交之前,我们要先解决冲突问题。在同一个地方,两个分支都做了更改,我们要在两个更改中选择一个,然后删除掉那些固定的标识符。
很显然,这个只能手动解决,因为机器是不知道你想保留哪个更改的。
解决方法1:到目录中,打开source1.c,然后进行编辑。
假如,在冲突的地方,我们选择的是dev分支更改。那么,我们就将上面划掉的部分删除掉,然后保存。
这是改后的内容,添加到暂存区,然后就可以提交了。
解决方法2:在GIT的命令窗口中,用vi或vim编辑器打开进行编辑
$ vim source1.c 或 $ vi source1.c
执行命令后,就出现vim(或vi)编辑器打开source1.c的窗口,这是vim编辑器的正常模式,如上图。
点"i"后,vim编辑器进入插入模式(可编辑),如上图。
编辑完后,点"Esc",退回正常模式。然后输入英文字符":",就可以进入命令模式。输入wq(write quite),就可以保存并退出,然后添加到暂存区并提交。
11.A 常用的合并方式
11.A.1 --ff与–no-ff
$ git merge --ff dev //使用“Fast forward”(快进)模式合并,默认的,可以不写
$ git merge --no-ff dev //禁用快进模式
通常合并分支时,git一般使用“Fast forward”(快进)模式。在这种模式下,只是把分支的修改内容合并过来,也就是把被合并分支的提交记录合并过来,然后再把指向最新版本的HEAD指向被合并分支合并过来的提交记录中最新那个。在这个过程中没有创建commit,只是把HEAD指向了合并过来的最新提交记录。
例如,master分支只有两条提交记录时,创建dev分支。dev改动并提交4后,合并回master分支。
上图表示dev分支提交了4次,第一次把source1.c的内容改为:111111111,后面每次增加一行数字。
上图表示,合并时,默认使用–ff(快进模式),把dev分支增加的提交合并过来,并把HEAD指向最新提交,合并时并没有提交新的提交。
如果回滚到上一版本,上一版本就是合并过来的版本中的上一版本。
我之所以提醒这一点,是因为如果禁用了快进模式,也就是–no-ff,HEAD指向的是合并时提交的版本,回滚上一版本也不是合并过来版本,而是合并前master分支的最新版本。
我们来看下,用–no-ff(禁用快进模式)来合并会有什么不同。
也许大家都看出来了,不同的地方正是上面文字所说的。
但是,在这里我要提醒一点,快进模式是有条件的。那就是与创建分支那时相比,只是被合并的分支有改动,当前分支没有改动。
如果当前分支在合并前也有改动,然后我们依然指定用–ff(快进模式),会怎样呢?
可见,在双方都有改动的情况下,即使指定用–ff(快进模式)合并也没用,系统依然是用–no-ff(禁用快进模式)合并。
也就是说,只有被合并分支单方面有改动的情况下,快进模式才有效,用–ff和–no-ff才有意义。否则,无论你指定用哪个,系统都会用–no-ff模式进行合并。
无论当前分支是否有改动,都建议大家使用–no-ff模式合并,有两个好处:
1.合并时会有新提交(当然,如果有冲突,系统不会自动提交,需要我们手动解决冲突后手动提交),以后看备注或图形模式($ git log -graph)就知道这个版本是合并后的。
2.版本回滚路线会干净很多,不用管分支中杂乱的提交记录。合并就是一个版本,回滚上一版本就是合并前的版本。
11.A.2 --squash与rebase
对过上面的合并,我们可以发现,无论是–ff还是–no-ff方式,合并后,被合并分支的提交记录与会融入到主分支的提交记录中。如果被合并分支的提交记录很杂乱,那合并后,主分支的提交记录也会变得很不“干净”。
有没有办法,让合并后,主分支这边只多一提交记录呢?
有,试试以下两种方式。
11.A.2.1 --squash
合并时,不融入被合并分支的提交记录,只将被合并分支的最终修改内容合并过来当前分支的工作区和暂存区。合并后不自动提交,程序员需要手动提交。
用法和–ff及–no-ff一样,我们看下效果:
可见,合并后,master分支的提交记录中没有融入dev分支的提交记录,系统也没自动发起合并版本的提交。
所以合并后,我们可以手动提交一个版本,或适当修改后再提交。
但是还有一个问题,那就是在master的提交记录上看不到dev分支修改合并的痕迹。没有dev提交的历史版本,也不知道那个版本是合并dev分支的改动。
这似乎不太好,还有没有方法解决呢?
有,其实还可以这样,请看下一小节。
11.A.2.2 rebase
rebase可以对某一段线性提交历史进行编辑、删除、复制、粘贴和合并等,因此,合理使用rebase命令可以使我们的提交历史干净、简洁。
所以,我们可以在合并分支之前,用rebase将被合并分支的提交记录衍合成一个或有重大改动的几个。
一般衍合成一个就可以了。
例如,创建一个dev分支,该分支单方面对source1.c文件修改并提交了三次。
如果用一般的合并方式:
方式1.$ git merge dev(详情见11.8.1)
master分支没修改,所有按快进(–ff)方式合并,master分支的提交记录上将增加三条提交记录,就是dev分支提交的那三条。
方式2.$ git merge --no-ff dev(详情见11.A.1)
因为指定以禁用快进(–no-ff)方式合并,所有master分支的提交记录上除了增加dev分支提交的那三条记录,还将额外提交一条新记录,一共增加四条记录。
方式3.$ git merge --squash dev (详情见11.A.2.1)
合并后,没有自动提交记录,我们可以手动提交一条记录。但是,master分支的提交记录上看不出dev分支修改合并的痕迹,除非备注里写清楚。
那么,看看我们以下方式会怎样。
dev分支改动并提交了三次。我们想把这三次提交衍合成一次,再把这次衍合的提交合并到主分支。然后在master分支的提交记录上,这个合并过来的记录会明确标明是dev分支修改提交的。
先运行rebase命令,并指明以当前的master分支为基准。或者说是把master分支上的改动同步过来,然后对dev在这个基准上的改动记录进行编辑。
也就是说,把建立dev分支后master分支上的修改会合并过来。然后再对当前分支在这个基准上的修改所有提交记录进行编辑、删除、复制、粘贴和合并等操作,我们现在想进行合并,也就是衍合。
执行上面命令后,会弹出下面的命令编辑窗口。
- pick:保留该commit(缩写:p)
- reword:保留该commit,但我需要修改该commit的注释(缩写:r)
- edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
- squash:将该commit和前一个commit合并(缩写:s)
- fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
- exec:执行shell命令(缩写:x)
- drop:我要丢弃该commit(缩写:d)
我们想衍合成一个提交记录,所以只能有一个提交记录,也就是留一个(pick),其它的用squash合并进去。
保留第一条提交记录(因为squash是和上一条合并,所以我们保留第一条,后面的都可以用squash),后面的都用squash来合并。
命令编辑完保存退出后,系统进行将逐一处理三个提交:
1.将第一个提交与此刻从master分支同步过来的改动合并
2.将第二个提交与上面的结果合并
3.将第三个提交与上面的结果合并
上面的每一步,都会检查冲突情况。
如果该提交选择保留(pick等),还会让我们修改下备注。
先处理第一个记录,首先,检查是否冲突。
没有冲突,则进入这个记录提交的备注编辑窗口。
每条记录都先检查是否冲突,如果没有冲突,就进入备注编辑窗口。如果有冲突,就会停下来让我们手动解决冲突。解决冲突后,我们要手动添加(git add),再继续(git rebase --contiune),就会进入该条记录备注的编辑窗口。记住,add后不用commit,contiune就可以了。保存退出后,就开始合并下一条记录。
下图是编辑第4条记录的备注。
修改一下相关备注(提示一下,所有备注在编辑最后一条时再修改也没问题)。下图是最后一条的备注编辑窗口,保存退出后,衍合就完毕了。建议完毕之前,在最前面加一条备注,作为这条衍合提交的备注,以后以单行(git log --pretty=oneline)方式查看提交记录时显示的就是第一条。如果不在前面加一条总备注,以后以单行(git log --pretty=oneline)方式查看时显示的是是第一个提交记录的备注(加一行:22222那个),这明显不合适。
注意,刚才我们只保留(pick)一个提交记录,所以编辑提交备注就一次。如果我们保留(pick等)多个提交记录,那这一步会重复多次,为每次提交写备注。当然,如果过程中某个提交冲突了,会停下来,我们解决、添加(add)、继续(continue)后,才会继续弹出下一个提交的编辑备注窗口。
衍合成功后,会出现以下信息,至此,rebase命令才算执行完毕。
我们合并看看。
所有修改的内容都合并过去了,而且master分支只增加了一条提交记录,这条记录还标明了修改提交的“作者”。
上面是在dev单方面改动的情况下进行的,那么,如果在master分支也有改动,会时会怎样呢?
很明显,合并后就不是增加一条记录了。而且,提交记录也不在一条回滚线上,有些朋友很不习惯。
还有没有办法解决呢?
有,方法有三个:
1.先将dev所有的修改提交记录衍合成一个(不要以master为基准,不然内容冲突的话很麻烦,dev的修改提交越多越麻烦),然后用git rebase -i master同步master分支的修改过来,最后再合并到master分支。
2.先将master中的改动合并到dev分支,然后再将dev分支的修改提交记录衍合成一个,最后再合并到master。这时的合并可以是快进方式了,不会再提交新记录,只量把dev衍合那条提交记录合并过来。
3.先将dev所有的修改提交记录衍合成一个,然后再将master分支的修改合并过来。因为双方都有修改,所以这次合并肯定会新增一个提交记录,所有又得衍合一次变成一个,最后再合并回master分支。(不推荐)
推荐用方法1,衍合后的版本中(git log 信息)只有dev修改的轨迹。
方法2也可以,只是衍合后的版本中有合并master的轨迹(最后一个提交记录是合并master)。
方法3有点笨,不仅麻烦,而且还有合并master的痕迹,不推荐。
下面演示下方法1:
11.A 删除分支
11.A.1 删除在本地仓库的分支
$ git branch –d 分支名称 (如果该分支没有被合并,会出错并提示:The branch '***' is not fully merged.)
$ git branch –D 分支名称(强制删除分支)
11.A.2 删除远程仓库的分支
$ git push origin --delete 分支名称
十二、推送到远程仓库
12.1 将本地分支的更新、推送到远程主机
$ git push <远程仓库名> <本地分支名>:<远程分支名> (与git pull命令相仿)
注意,分支推送顺序的写法是<来源地>:<目的地>,所以git pull是<远程分支>:<本地分支>,而git push是<本地分支>:<远程分支>。
如果省略远程分支名,则表示将本地分支推送与之存在”追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。
$ git push origin :master
等同于
$ git push origin --delete master
如果当前分支只有一个追踪分支,那么主机名都可以省略
$ git push
如:把当前master分支推送到远程库
$ git push –u(第一次要用-u 以后不需要) origin master
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
上面命令将本地的master分支推送到Demo_hub主机,同时指定Demo_hub为默认主机,后面就可以不加任何参数使用$ git push了。
12.2 不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机
$ git push --all origin
十三、从远程获取最新版本到本地
如果远程仓库中的版本比本地仓库中的版本新,可以用下面两个命令来拉取:
$ git fetch origin master //将远程仓库中master分支的更新拉取到本地,但不合并
$ git pull origin master //将远程仓库中master分支的更新本拉取到本地,而且自动合并
与push的用法相仿,具体操作15.1.3.1小节再展开讲。
十四、版本回滚
14.1 查看所有提交的历史记录
$ git log
git log 后面可加上很多参数,常用的如下:
-p:按补丁显示每个更新间的差异,比下一条- -stat命令信息更全
–stat:显示每次更新的修改文件的统计信息,每个提交都列出了修改过的文件,以及其中添加和移除的行数,并在最后列出所有增减行数小计
–abbrev-commit:仅显示 SHA-1 的前几个字符,而非所有的 40 个字符
–graph:显示ASCII图形表示的分支合并历史
–pretty=oneline:一行显示,只显示哈希值和提交说明(–online本身也可以作为单独的属性)
-n:显示前n条log
14.2 查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作)
14.3 回滚到之前某一版本,且那个版本之后提交的版本都不要了
可以用DEAD指定恢复版本,也可以用版本ID号来指定恢复版本。如果用版本ID号,既可是提交记录(git log)中的ID号,也可以是操作记录(git reflog)中的ID号。
14.3.1 用HEAD指定恢复版本
$ git reset --hard HEAD
HEAD:当前版本
HEAD^:上一个版本
HEAD^^:上上一个版本
HEAD~100:往上第100个版本
14.3.2 用版本ID号恢复版本
$ git reset --hard bf5b760d5a18246d516e7fa62853c6c151623e21
可以看到,用了reset恢复后,ID号为“ba36bfe75e5d7b3c275acd2f0b7928801bf98fd0”的版本没有了。
14.4 恢复某文件到某历史版本
14.4.1 恢复某文件到某历史版本,覆盖回当前工作区和暂存区
$ git checkout 7a655ee57a031c4387d38e2a9042ea9411dd881c -- source4.c
14.4.2 恢复某文件到某历史版本,覆盖回当前暂存区
$ git reset HEAD -- source1.c
14.5 撤销某次提交(该版本不是merge类型),把这个版本的改变撤销回去,但是又想保留之前的所有版本
git revert 撤销某次提交,此次提交之前和之后的 commit 记录($ git log 查看)和 history 记录($ git reflog 查看)都会保留,并且把这次撤销作为一次最新的提交。
git revert是提交一个新的版本,将需要revert的版本的内容再反向修改回去,版本会递增,不影响git revert之前提交的版本。
例如:我们之前提交了三次,最近一次提交的内容是增加source5.c,后来我们发现source5.c是多余的,想撤销这次提交。
提示:不仅仅可以撤销最近一次,可以是commit 记录($ git log 查看)和 history 记录($ git reflog 查看)中的任何一次,操作是一样的。
输入":wq"时记得切换成英文输入法,不然在输入":"时,命令窗口会闪白光,你可能并没意识到是中英文输入法的问题。因为没有其它提示,所有你可能意识不到问题出在那。
完成后,查看一下commit的版本可以发现,之前提交的版本都在,只是新建了一个版本。
与reset的不同点:
1.用reset恢复到某个版本后,比该版本更新的版本全被清理掉。用revert撤销某个版本的提交后,会保留之前所有版本,并新建一个新版本。
2.用reset恢复到某个版本后,内容和那个版本一模一样。用revert撤销某个版本的提交中所做的更改后,内容就是在当前版本的基础上,反向作了该版本的修改。例如该版本修改是增加了source5.c,revert该版本后,内容就是当前版本删除掉source5.c。
查看目录可以发现,source5.c已经被删除掉了。
如果要撤销最近一次提交的修改,可以直接用:
$ git revert HEAD //HEAD指向最近一次提交
例如:刚才我们撤销上一次提交,导致删除了source5.c,后来突然发现source5.c还有用,那就可以再撤销最近一次提交的修改。因为我们刚才撤销上一次撤销后,也进行了提交,也是一个提交记录。完成可以根据这个提交记录再反做一次。
$ git revert 常用参数说明:
$ git revert --no-edit //不启动提交消息编辑器,就是不需要写提交备注
$ git revert -n 或 $ git revert --no-commit //不提交,只是恢复到暂存区
git revert 的其它参数请参考官方文档:https://git-scm.com/docs/git-revert
十五、多人协作开发的三种方式
如今的项目规模越来越大,功能越来越多,需要有一个团队进行开发。如果有多个开发人员共同开发一个项目,如何进行协作的呢。
GitHub 多人协作开发的三种方式:
- fork 方式
- 组织、团队方式
- 合作者方式
15.1 fork方式
大概流程:
1.项目的负责人构建起一个项目的最原始的仓库,称为origin。
源仓库的有两个作用:
1.汇总参与该项目的各个开发者的代码
2.存放趋于稳定和可发布的代码 源仓库应该是受保护的,开发者不应该直接对其进行开发工作。只有项目管理者能对其进行较高权限的操作。
2.开发者fork源仓库
任何开发者都不会对源仓库进行直接的操作,源仓库建立以后,每个开发者需要做的事情就是把源仓库的“复制”一份,作为自己日常开发的仓库。这个复制是fork。
3.把fork过来的仓库clone到本地
4.构建功能分支进行开发
5.向管理员提交pull request
在完成了自己负责的功能(当然,过程中也可能对自己的develop进行了多次合并),经过测试以后,觉得没问题,就可以请求管理员把自己仓库的develop分支合并到源仓库的develop分支中。
6.管理员测试、合并
管理员登陆gitlab,看到了开发者对源仓库发起的pull request。 管理员需要做的事情就是:
1.对开发者的代码进行review。
2.在他的本地测试新建一个测试分支,测试开发者的代码,判断是否同意合并到源仓库的develop中。
上面6个步骤中,很多内容在前面章节已经说过了,其它fork、clone和pull request步骤会在下面说到。
15.1.1 fork
下面是项目负责人管理的仓库。
目前还没有人fork(复制)这个仓库,下面开发者“kejirenshen”进行复制操作。
fork完成后,kejirenshen的仓库中有了该项目。
在github的仓库列表中,也可以看出哪个仓库是fork过来的。
15.1.2 克隆远程仓库到本地
克隆,就是把远程仓库中的仓库原原本本地复制到本地,包括各个分支、提交记录及其所有文件。这是一个从无到有的过程,一般是在换电脑或本地仓库丢失等情况下进行的。
克隆步骤:
1.确保本地的SSH KEY已经添加到github允许的列表中(详情请参考9.3小节)。
2.在希望仓库存放的地方,点右键然后打开Git Base黑框。
3.输入克隆命令。
命令格式如下:
$ git clone <版本库的网址> <本地目录名(可省略)>
如:
方法一(用SSH协议):$ git clone [email protected]:wuzulie/Demo_hub.git(速度最快)
方法二(用HTTPS协议):$ git clone https://github.com/wuzulie/Demo_hub.git
用SSH和HTTPS的区别:
- 使用https url克隆对初学者来说会比较方便,复制https url然后到git
Bash里面直接用clone命令克隆到本地就好了,但是每次fetch和push代码都需要输入账号和密码,这也是https方式的麻烦之处。 - 使用SSH url克隆却需要在克隆之前先配置和添加好SSH key,因此,如果你想要使用SSH url克隆的话,你必须是这个项目的拥有者。否则你是无法添加SSH key的,另外ssh默认是每次fetch和push代码都不需要输入账号和密码,如果你想要每次都输入账号密码才能进行fetch和push也可以另外进行设置。
下面,假定开发者“wuzulie”已经把项目fork到了自己的github仓库,那么接下来他就可以clone到本地进行开发了。
至此,远程仓库克隆完毕,可以进行接下来的开发了。
查看当前目录中的文件(ls -a)和查看提交记录(git log)等操作并不是必要的步骤,我只是想强调一下某些操作的效果。
注意:
如果是用别人电脑,或者自己电脑GIT还没设置过默认用户名,要先设置一下,方便识别不同用户的提交。
$ git config --global user.name "wuzulie" //设置用户名为:wuzulie
$ git config --global user.email "[email protected]" //设置用户邮箱为:[email protected]
有了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然你也可以对某个仓库指定的不同的用户名和邮箱,配置时去掉–global即可,如果针对系统中所有用户,则用–system。
另外说一下上面的origin/master,表示默认选择哪个分支(即克隆时)。默认情况下origin/HEAD指向origin/master分支,比如origin/HEAD -> origin/master 。
这个标明默认分支的远程分支可以删除,也可以修改。
克隆时,默认分支(默认是master)会在本地自动创建。其它分支(如dev)虽然没有自动创建,但对应的远程分支(如origin/dev)已克隆到本地,你可以创建该分支并让其跟踪远程分支中的相应分支。
$ git branch dev origin/dev //创建dev分支,同让其跟踪远程的dev分支
$ git checkout -b dev origin/dev //创建并切换到dev分支,同时让其跟踪远程的dev分支
$ git checkout dev //如果你确定有这个远程分支,可以直接切换过去,系统会自己创建、切换和跟踪。
注意,远程仓库名称并不都是origin。如果该本地仓库是从远程仓库克隆下来的,那远程仓库名称默认是origin。但是如果仓库不是克隆下来的,那这个名称是我们增加远程仓库(git remote add xxx)命名的。一般命名origin,如果你命名或修改成其它名字(如myhub),上面命令中origin/dev就改为myhub/dev。
上面是用克隆实现从无到有的过程,如果本地已经有仓库了,只是可能不是最新版本,那还用克隆吗?请看下一小节。
15.1.3 拉取远程仓库中的最新版本
在团队协作开发中,大家都不断更新远程仓库中的代码,所以我们本地的版本很可能已经不是最新的了。
有时候我们需要获取最新的代码版本:
- 比如,我们负责那部分高度过程中需要用到别人的修改成果。
- 又比如,我们要向远程仓库推送当前分支的修改成果,也得先获取远程仓库中的最新版本到本地与当前分支合并,然后再推送。如果直接推送,很可能在你之前,其它同事已经更新过好几个版本了,你当前在修改的版本比当前远程仓库中的版本旧,推送时会出错的。如果强制推送,会覆盖掉别人的修改成果,会出大问题的。
复习下拉取远程仓库的指令:
$ git fetch origin master //将远程仓库中master分支的更新拉取到本地,但不合并
$ git pull origin master //将远程仓库中master分支的更新本拉取到本地,而且自动合并
假设,上次克隆远程仓库到本地后,有其它同事已经向远程仓库推送了更新的版本。这时候如果我们想把最新版本拉取到本地,就要用到上面两个指令了。
15.1.3.1 pull
如果pull自动合并造成冲突,而你不想解决冲突,想取消合并时,可以用:
$ git merge --abort //取消合并
但是一般不会取消合并,而是都会解决冲突,再添加提交,然后本地本地仓库就更新到最新状态。
15.1.3.2 fetch
合并之前也可以先看看拉取下来的更新对当前版本的改动:
fetch后,可以用以下两和种方式合并:
1.$ git merge FETCH_HEAD //将FETCH_HEAD指向的版本合并到当前分支
2.$ git merge origin/master //将远程仓库的master分支合并到当前分支
然而FETHC_HEAD并不一定是指向远程仓库的master分支,怎么确定呢?
理解 fetch 的关键, 是理解 FETCH_HEAD。FETCH_HEAD指的是:某个分支在服务器上的最新状态。
一般来说, 存在两种情况:
1.如果没有显式的指定远程分支, 则远程分支的master将作为默认的FETCH_HEAD。
2.如果指定了远程分支, 就将这个远程分支作为FETCH_HEAD。
git fetch 使用方式有以下四种:
1.$ git fetch
这一步其实是执行了两个关键操作:
1.创建并更新所有远程分支的本地远程分支。
2.设定当前分支的FETCH_HEAD为远程服务器的master分支 (上面说的第一种情况)。
需要注意的是: 和push不同, fetch会自动获取远程`新加入’的分支。
2.$ git fetch origin
同上, 只不过手动指定了remote为origin。
3.$ git fetch origin master
设定当前分支的 FETCH_HEAD为远程服务器的master分支的最新版本。
这个命令也可以用来测试远程主机的远程某分支是否存在,,如果存在, 返回0,如果不存在,返回128,抛出一个异常。
4.$ git fetch origin master:dev
意思是将远程仓库的master分支更新到本地的dev分支,但不切换过去。如果本地dev分支不存在,则会自动创建。
git fetch origin :dev
等价于:
git fetch origin master:dev
15.1.4 修改并推送到远程仓库的过程
假设,项目协作刚刚开始,远程仓库中保存着最初的版本,各位程序员开始了自己的负责的部分。
15.1.4.1 xiaming的修改和提交(不规范)
xiaming改完提交后,改变的仅仅是本地的dev分支。如果他要把这个修改更新到远程仓库的master分支上,该怎么做呢?
可能有朋友说,很简单啊,把dev分支的修改合并到master分支,然后推送上去就可以了,如下:
推送上去了,看看远程仓库会有什么变化。
点击该提交记录后,即可查看这次提交的详情。
至此,xiaming修改的版本已更新到远程仓库,其它同事就可以看到并使用了。
以上操作过程似乎没问题,其实操作是很不规范的。
1.推送之前没有提取远程仓库中的最新版本到本地合并,如果远程仓库中有更新的版本,会出错。
2.只推送了master分支,dev分支的相关记录并没推上去。我们查看下远程仓库中dev分支的记录看看:
xiaming后来发现了这个问题,进行了补推送送:
dev分支的记录补上去了。但是要注意,上面补推送的操作也是在dev记录没有被其它同事更新的情况下才顺利补推送上去的。如果dev分支已被更新,那推送会出错。如果强推送(git push origin dev --force),会覆盖掉其它同事的更新。除非先把该分支pull到本地,合并,然后再推送。
总之,xiaming的修改推送新版本的操作是很不规范的。那规范的操作过程应该怎样呢?我们看看下一小节xiahua的操作:
15.1.4.2 xiahua的修改和提交(规范)
推送成功,我们看下远程仓库中的变化:
总结一下,规范的修改并更新到远程仓库的操作是:
- 1.将远程仓库克隆到本地
- 2.在本地的dev或其它分支上开发
- 3.拉取远程远程仓库的更新合并到本地master分支上
- 4.将本地的所有修改记录衍合成一个记录
- 5.将master分支的更新同步到dev分支衍合的记录上
- 6.将同步后的dev分支合并到master分支上
- 7.将合并后的master分支和dev分支推送到远程仓库
第一步是第一次获取项目文件时才用到,后面的开发中,会不断地循环第2步到第7步。
所以,平时常用的指令就是:
$ git checkout dev //切换分支
$ git pull origin //拉取远程仓库的更新
$ git rebase -i HEAD~n //编辑最近n条提交记录,一般是衍合成一条
$ git rebase -i master //同步master分支上的更新,并编辑master之外的所有记录
$ git merge dev //合并dev分支到当前(master)分支
$ git push origin master(或dev) //推送master分支和dev分支到远程仓库
15.1.5 pull request
15.2 合作者方式
15.2.1 添加合作者
先打开需要合作开发的仓库,在“Settings”里面可以添加合作者。输入用户名或邮箱都可以添加。
目前github个人免费帐号的私有库只能添加3名合作者,如果想添加更多合作者要用付费帐号,或者用公有库开源。
占击“Add collaborator”后,被邀请者会收到邀请邮件,里面有链接,点击链接后可以接受或拒绝。
邀请者也可以点击下图中的“copy invite link”复制邀请链接发给被邀请者,其点开后可以接受或拒绝。
被邀请者同意后,其github仓库列表中多出该仓库。
15.2.2 合作者流程
接下来的开发过程和fork方式一样,只是不用“pull request”,具体操作流程请参考15.1.2-15.1.4小节。
15.3 组织、团队方式
15.3.1 创建组织
点击github右上角的“+”号,然后点击“New organization”。
step1,填写组织相关信息。
如果选择付费类型,还需要填写付款式等信息,免费类型无需填写。
step1完成后,进入step2。
step3,做一些调查,非必需,可跳过。
至此,组织创建成功。
15.3.2 给组织添加、删除仓库
15.3.2.1 给组织添加仓库
15.3.2.2 删除组织中的仓库
如果要删除仓库,方法和普通仓库一样。
然后滚动到页面底部。
15.3.3 给组织添加成员
弹出邀请窗口后,输入用户名或email即可邀请。
设置该成员的角色。
该用户会收到邀请邮件,里面附有网址,进去后可以接受或拒绝。
对方接受后,将会加入你的组织,如下图。
如果想删除团队中的成员,那就更简单了,点击上图中的叉号即可。
15.3.4 给组织创建团队
填写团队相关信息。
创建成功后如下图。
进入该团队所属的组织也可以查看。
如果该团队负责的任务很多,还可以分子团队。如“航电系统”团队下还可以分“飞控系统”、“显示系统”、“通信系统”及“雷达系统”等团队,都由“航电系统”团队管理。
创建子团队时,只需要在创建时选择父团队即可,其它的和上面步骤并无区别。如果是在父团队内点击的新建,连父团队都不用选择了。
15.3.5 给团队添加仓库
填写仓库名。
15.3.6 设置团队权限
5种权限的说明里面都有英文说明了,大家不懂的翻译一下。
15.3.7 添加、删除团队成员
15.3.7.1 添加团队成员
接下来会有个提醒。
添加后,github会给被添加者发一个通知邮件。这个添加过程无需被添加者同意,因为该用户已经是组织内成员了,添加到哪个团队管理者有权决定。
然后被添加者的github主页上也可以看到该团队及该团队的仓库。
15.3.7.2 删除团队成员
接下来会有一个提醒。
15.3.8 设置团队成员的权限
可以用上一小节的方法,先打勾,然后在下拉菜单中点击“change role”,即可设置权限。
也可以点击该成员的名称,进入该成员的页面,也可以设置角色,即权限。
改变权限后,该成员会收到通知邮件。
15.3.9 团队合作开发
各成员自己的github上都可以看到团队的仓库,可以将该仓库克隆到本地,然后开始写自己负责那部分的代码。
接下来各个团队成员按往常的方式开发就可以了,开发过程和fork方式一样,只是不用“pull request”,具体操作流程请参考15.1.2-15.1.4小节。
15.3.A 讨论交流功能
内容发出后,各成员会收到邮件,如下:
各成员在github团队“Discussions”中也能看到,可以在这里参与讨论交流。
当然了,约宵夜只是开个玩笑,这里讨论的内容是和项目和关的。虽然用微信、QQ等方式都可以讨论,但是一些重要的讨论建议在这里进行。因为在这里讨论的话,讨论记录做为项目资料可以和仓库一起保存在github中,方便以后查看。
至此,git相关的内容已经介绍地差不多了。有兴趣的朋友可以进一步学习一下Gitlab。另外还可以学下git相关辅助工具软件,如GitHub Desktop和Gitflow等。