文件版本控制工具git使用
1. 实验类型
验证型,必做实验
2. 实验目的
掌握源代码版本控制工具git的基本使用,理解源代码版本控制的原理;
3. 题目描述
通过一个简单的测试项目的源代码版本控制,实践git的使用,掌握源代码版本控制的意义。
4. 实验要求
基本层次
能使用git工具完成从服务器上将源代码导出,将一个简单的测试项目的代码导出,并完成代码的修改和提交。
提高层次
能解决文件版本冲突问题。
5. 相关知识
Linux环境C程序开发的工具有:
编辑器(vi, emacs等)
编译程序(gcc, make等)
调试程序(gdb等)
版本控制程序(git, svn等)
帮助文件生成(groff等)
软件打包工具(tar, rpm等)
等等,本实验以版本控制程序为例子,对开源软件开发方法进行介绍。
源代码版本控制软件是实现软件配置管理(Software Configuration Management, SCM)的重要工具。软件配置管理的主要目的是实现了团队协作、过程管理、并以透明的方式管理软件产品的开发,以实现提高开发速度、追踪开发过程、提高开发质量的目标。
版本控制软件可以实现软件的多个版本的可跟踪、可管理性。图1是一个简单的软件项目的开发过程的版本模型。图中,方框表示软件项目的不同版本,软件项目的一个版本包含了项目的所有源代码和其他文件;数字表示版本号码。Branch(分支)是开发者所开发的软件代码,通常branch是软件项目中一些具体的功能,比如branch 2-3可能是一个管理软件的用户登录模块,branch 7-8可能是用户帐号管理模块;Trunk(主干)包含了所有branches的最早的版本,是每个branche的开始;Tag(标签)是主干中实现了预期阶段功能(称为里程碑(milestone))的版本;Merge(合并)是开发者将branch版本和trunk版本相融合,是将branch版本的功能添加到trunk版本中的过程;Branch的开发可能结束,例如版本16。Trunk上的版本合并了branches开发的功能,是一些具有完整功能的软件版本,比如版本4合并了用户登录模块,版本10又合并了用户帐号管理模块,这样版本10就具有用户登录模块和用户帐号模块,版本3不具有用户帐号管理模块,版本8不具有用户登录模块。当然,开发者还可以在branch2-3上工作,继续改进登录模块,再合并到trunk中版本10之后的版本中,从而改进整个软件项目,而登录模块的开发也不受其他模块的影响。
一个简单的软件开发版本模型
图1 一个简单的软件开发版本模型
现代软件开发一般是多人的、分布式的开发形式,例如:用户A和用户B可能同时对同一软件项目test中的文件main.c进行了修改,在用户A、B将main.c的修改保存到中心版本库时,需要解决同步的问题。大多数的版本控制软件是采用的是“拷贝-修改-合并”模型(本部分介绍请参考文献[1]中“SVN手册”链接)来解决同步问题,在这种模型里,每一个客户连接项目版本库建立一个个人工作拷贝,也就是版本库中文件和目录的本地映射。用户并行工作,修改各自的工作拷贝,最终,各个私有的拷贝合并在一起,成为最终的版本,这种系统通常可以辅助合并操作,但是最终要靠人工去确定正误。
图2和图3是一个例子(摘自参考文献[1] 中“SVN手册”链接),展示了用户Harry和Sally对代码库(Repository)中文件A的“拷贝-修改-合并”过程:图3-2中Harry和Sally分别拷贝A,生成各自的工作拷贝;然后Harry和Sally分别对A做了修改,修改后对应文件为A’和A”,Sally将A”传送到代码库中,当Harry将自己的修改A’上传时被提示“过时”错误。
“拷贝-修改-合并”模型
图2 “拷贝-修改-合并”模型
接下来,如图3所示,Harry比较代码库中A的新版本A”与自己修改版本的差异,手工修改差异生成文件A*,这个过程叫“合并”,此后,Harry可以将合并后的A*上传到代码库中。Sally以后就可以取出最新的A*文件了。这个过程最核心的部分是人工的“合并”过程,合并必须通过Harry和Sally的交流来确定。
“拷贝-修改-合并”模型(续)
图3 “拷贝-修改-合并”模型(续)
Git是一个开源的分布式版本控制软件,是由Linux之父Linus Torvalds为Linux内核维护工作在2005年开发的源代码版本控制软件。自诞生以来,Git 就以其开源、简单、快捷、分布式、高效等特点,应付了类似 Linux 内核源代码等各种复杂的项目开发需求。如今,Git 已经非常成熟,被广泛接受与使用,越来越多的项目都迁移到 Git 仓库中进行管理。
Git的设计核心是分布式控制管理,目的是为了摆脱对中心仓库的依赖,以支持离线工作。例如(该例来源于IBM developerWorks的“开源分布式版本控制工具 —— Git 之旅”):一个由Alice、Bob、Clair、David 四名成员组成的项目组。其中,除了中心仓库 origin(Git 默认远程仓库名称)之外,每一名成员各自负责一个完整的本地仓库(或者称作本地工作拷贝)。从分布式的观点来看,David 可看成是 Alice 的远程仓库,反过来也是一样。其分布式工作如图4所示。
git分布式工作示意
图4 Git的分布式工作示意
Git拥有强大的分支特性,主要提供四种工作流(来源, 出自atlassian Git Tutorials):
Centralized Workflow
仅有一个主干,没有分支。
Feature Branch Workflow
在开发每个功能时都应该创建一个独立的分支而不只是使用主干。由于每个分支是独立且互不影响,这就意味着主干不会包含分支的修改。
Gitflow Workflow
使用独立的分支来准备发布(preparing),维护(maintaining), 和记录版本(recording releases)。
Forking Workflow
每个开发者都有两个远程仓库:远程私有的仓库和远程共享的仓库。
为了验证两个开发人员使用Git客户端通过远程中心代码库进行版本控制的过程,介绍了两个解决方案:方案一将建立本地访问方式的远程中心代码库,这个代码库与通过网络方式访问远程中心代码库从操作上看几乎没有区别(本次实验内容);方案二使用Gitlab作为远程中心代码库,就是真实的分布式开发的情景(下次实验内容)。
6. 实验设备
实验者需要使用浏览器软件(建议使用Chrome或Firefox),访问实验平台(地址)完成实验。
实验环境为“Oracle Linux 7.4”。
本次实验需要使用字符界面,操作为:点击桌面终端图标名为“Xfce终端”,打开Terminal
7. 实验指导
7.1 Git命令介绍
Git的功能主要由git命令实现,其命令行格式为:
git <subcommand> [options] [args]
其中subcommand为子命令,主要包括:
help
add
commit
push
pull
diff
fetch
checkout
merge
rebase
info
log
status
等。
例如,help子命令用于获取帮助信息:
git help add
请打开命令行窗口使用上述命令,该命令打印出了add子命令的帮助信息。
实验环境中会出现如下错误:
没有 git-add 的手册页条目。
这是实验环境限制,目前没有找到处理的方法。请访问官网上的版主文档(https://git-scm.com/docs)。
Git采用了本地工作目录、暂存、本地代码库和远程代码库实现“拷贝-修改-合并”模型,其存储过程如图6所示:
git存储过程
图6 Git的存储过程
图中,箭头符号中的文字为git的子命令,表示git所进行的操作。
下面,举一个例子来说明Git本地代码库的存储过程。
首先,新建一个空的项目。
cd /root/workspace
ls -l
total 168
drwxr-xr-x 1 1000 1000 170 Oct 21 00:55 example
-rw-r--r-- 1 root root 170060 Oct 21 00:55 fping_4.0.orig.tar.gz
drwxr-xr-x 1 root root 319 Oct 21 00:55 shell
mkdir proj
上述命令为git创建一个专用空目录,作为工作目录(也叫工作拷贝)。
ls -l
total 168
drwxr-xr-x 1 1000 1000 170 Oct 21 00:55 example
-rw-r--r-- 1 root root 170060 Oct 21 00:55 fping_4.0.orig.tar.gz
drwxr-xr-x 1 root root 10 Oct 21 00:55 proj
drwxr-xr-x 1 root root 319 Oct 21 00:55 shell
cd proj
git init
Initialized empty Git repository in /root/workspace/proj/.git/
git init是创建本地代码库的命令,命令行形式为:
git init [-q | --quiet] [--bare] [--template=<template_directory>][--separate-git-dir <git dir>][--shared[=<permissions>]] [directory]
参数directory表示创建代码库的同时创建目录,即省略创建目录的操作。上述过程创建代码库的操作中(第6个命令行),若执行"git init 目录名"命令,则不需要创建目录的操作(第3个命令行)。
选项–bare用于创建远程代码库(登录远程服务进行操作时使用),不使用这个选项表示创建本地代码库。后面使用中用来创建模拟的远程代码库。
可以通过git help init来查看子命令的帮助信息。(实验环境结果错误请参考前述说明)
以上命令创建了一个本地代码库,其实是一个隐藏文件夹,位于本地工作目录中,这里是/root/workspace/proj/.git。本地代码库中的文件与工作目录的文件是不一样的,见稍后对远程代码库的介绍。
注意:以下对代码库的操作均需在本地工作目录中进行,即需要改变工作目录到本地代码库所在的目录中。
ls -al
total 0
drwxr-xr-x 3 root root 26 Oct 21 01:45 .
drwxrwxr-x 3 1002 1002 26 Oct 21 01:45 ..
drwxr-xr-x 7 root root 155 Oct 21 01:45 .git
接下来,创建一个文件readme.txt,内容为“hello, Git!”(使用shell的输出重定向符>,将echo "hello,Git!"命令的执行结果放入readme.txt文件中,详见教材,echo命令用于在命令行中显示一个字符串)
echo "hello,Git!" > readme.txt
ls -al
total 16
drwxr-xr-x 3 root root 48 Oct 21 01:53 .
drwxrwxr-x 3 1002 1002 26 Oct 21 01:45 ..
drwxr-xr-x 7 root root 155 Oct 21 01:45 .git
-rw-r--r-- 1 root root 11 Oct 21 01:53 readme.txt
查看一下工作目录的状态,使用git status命令。
git status
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt
nothing added to commit but untracked files present (use "git add" to track)
输出结果显示,readme.txt处于未跟踪状态(untracked files),也就是说该文件未存入代码库中,仅保存在工作目录中。
要将readme.txt提交到远程代码库,需要进行三步操作(见图6):
git add
用于添加文件到索引中,命令行形式为:
git add 文件....
参数"文件…"表示增加的文件可以是多个,用空格分割文件名。
git commit
用于提交到本地代码库中,命令行形式为:
git commit -m <日志信息> [文件....]
"-m <日志信息>"选项用于为这次提交设置一段描述文字(日志),便于根据日志区别多次提交记录。可以通过git log查看提交日志。
git push
用于将本地代码库提交远程代码库中(远程代码库需要事先建立,方法见后面的7.0的例子,这里的例子中没有提交到远程代码库),命令行形式为:
git push [选项] [远程代码库名称] [分支名称]
“远程代码库名称”是远程代码库具体地址的一个名称,以方便使用,可以通过"git remote -v"命令查看已有的远程代码库名称和对应的地址。这里我们没有远程代码库,所以该命令没有任何输出。
分支名称是使用git branch命令创建的,用于使用分支进行开发。默认情况下存在一个名称为master的分支(后面的描述中称作主干)。
继续示例的步骤,使用git add命令将文件加入暂存区:
git add readme.txt
再次查看状态:
git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: readme.txt
结果显示,readme.txt的状态可以被提交(changes to be committed),表示该文件已存放到暂存中,下一步,需要使用git commit将文件提交到本地代码库。
git commit -m "add readme"
*** Please tell me who you are.
Run
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
to set your account's default identity.
Omit --global to set the identity only in this repository.
fatal: unable to auto-detect email address (got 'root@c9aec9f34a47.(none)')
命令执行错误,提示需要设置用户姓名和邮件地址(用户姓名和邮件地址用于标志提交者,后述git log命令查看的提交日志中需要包括该信息),按照提示运行下面两条命令:
git config --global user.email "test@example.com"
git config --global user.name "5120140000"
git commit -m "add readme"
[master (root-commit) 8770539] add readme
1 file changed, 1 insertion(+)
create mode 100644 readme.txt
显示执行成功,提交了1个文件。
现在,在readme.txt文件上做一些变化。(下面的命令用于往readme.txt文件中新增加一行,使用shell附加重定向符>>)
echo 'hello, again\!' >> readme.txt
查看变化:
git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
结果表明:readme.txt文件发生了变化,但是还没有放入暂存区(Changes not staged),如果需要保存到代码库,可以使用git add命令,然后使用git commit命令;如果需要覆盖本地工作目录中的内容(回滚),使用git checkout命令。
使用git diff命令查看一下本地工作目录和暂存区之间的文件变化:
git diff readme.txt
diff --git a/readme.txt b/readme.txt
index a76b829..3f50efc 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,2 @@
hello,Git!
+hello, again!
结果的“@@ -1 +1,2 @@”中:前面的-1分成两个部分:减号表示第一个文件(即暂存区a中的readme.txt文件),"1"表示第1行,合在一起,就表示下面是第一个文件从第1行开始的连续3行;同样的,”+1,2"分成三个部分:加号表示第二个文件(即工作区b中的readme.txt),“1”表示第1行,"2"表示连续两行,合在一起,表示变动后,成为第二个文件从第1行开始的连续2行。之后是变动的过程。
接下来,把修改放入暂存:
git add readme.txt
再次修改文件:
echo "hello, the 3rd time!" >> readme.txt
查看文件变化:
git diff
diff --git a/readme.txt b/readme.txt
index 3f50efc..47c5bc5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,3 @@
hello,Git!
hello, again!
+hello, the 3rd time!
结果可见:相比暂存中的文件(增加“hello,again!”行后提交的文件),增加了“hello, the 3rd time!”一行。
如果跟本地代码库中相比呢?
git diff HEAD
diff --git a/readme.txt b/readme.txt
index a76b829..47c5bc5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,3 @@
hello,Git!
+hello, again!
+hello, the 3rd time!
结果可见:相比本地代码库中的文件(HEAD表示本地代码库的最新版本),本地工作目录中的文件增加了“hello, again!”和“hello, the 3rd time!”两行。
下面,将文件回滚/恢复到暂存区中的内容:
git checkout -- readme.txt
命令中的"–"用于指明readme.txt是一个文件名称。
cat readme.txt
hello,Git!
hello, again!
git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt
结果可见:暂存区中的文件未提交到本地代码库。
暂存区中的文件回滚为本地代码库中的内容(暂存区中的文件被覆盖):
git reset HEAD
Unstaged changes after reset:
M readme.txt
结果可见,暂存区不再为提交了的本地工作目录中的文件,即本地工作目录中的修改未暂存。
提交本地工作目录的变化到代码库。
git add readme.txt
git commit -m "hello, again"
[master 647dd6e] hello, again
1 file changed, 1 insertion(+)
结果表明,提交到本地代码库成功。
最后,如果需要将本地代码库提交到远程代码库中,则执行git push命令(这里暂时无法使用该命令,需要创建远程代码库(方法见7.1),并且使用git remote命令在本地工作目录中添加啊该远程代码库的地址,或者采用7.1的例子所示,通过git clone命令来保存远程代码库地址)。
注意:参考资料[2]是一个交互式git命令学习网站,对于学习git的基本概念和操作非常直观,建议访问。
7.2 Git命令行进行版本控制
这个例子采用Centralized Workflow,假想一个应用场景,介绍仅使用git命令行进行版本控制的具体方法:
假设有两个作家(分别为A和B)合作完成一篇文章README,这篇文章有两章:Chapter1和Chapter2,两个作家分别各自修改文章README,而修改进度无法保持一致,并且,两个作家需要能够访问到该文章的各个历史版本。
README文件最后的内容为:
Chapter1
First
Second
Chapter2
First
为了模拟真实环境中的远程代码库,需要使用git init子命令生成(在真实项目中,一般由项目管理员在远程服务器上建立,下一个实验将介绍通过gitlab系统创建)。
cd /root/workspace/
git init --bare rep.git
Initialized empty Git repository in /root/workspace/rep.git/
上述命令创建了一个远程代码库,位于“/root/workspace/rep.git”目录中。
这里我们用存放在本地的代码库目录来模拟远程代码库,在实际使用场景中,远程代码库往往存放互联网中的服务器上,使用网络协议访问,但命令行是一致的。
** 注意:区别于建立本地代码库,建立远程代码库需要使用–bare选项 **
ls -al rep.git
total 40
drwxr-xr-x 7 root root 4096 Oct 8 06:44 .
drwxrwxr-x 4 1000 1000 4096 Oct 8 06:44 ..
-rw-r--r-- 1 root root 23 Oct 8 06:44 HEAD
drwxr-xr-x 2 root root 4096 Oct 8 06:44 branches
-rw-r--r-- 1 root root 66 Oct 8 06:44 config
-rw-r--r-- 1 root root 73 Oct 8 06:44 description
drwxr-xr-x 2 root root 4096 Oct 8 06:44 hooks
drwxr-xr-x 2 root root 4096 Oct 8 06:44 info
drwxr-xr-x 4 root root 4096 Oct 8 06:44 objects
drwxr-xr-x 4 root root 4096 Oct 8 06:44 refs
可以看到,远程代码库目录中包含的文件并不是需要进行版本控制的文件本身(此时远程代码库中并没有需要控制的文档)。从这里也能看出远程代码库的目录结构和工作目录的区别,前面部分介绍的本地代码库的目录结构(查看/root/workspace/proj/.git目录结构)跟远程代码库的是相似的。
为了模拟作家A和B分别对文章README进行修改,生成A、B两个工作目录,假设在A目录中的操作用于模拟作家A在一台计算机上对代码库进行操作,同样的,在B目录中的操作用于模拟作家B在一台计算机上对代码库进行操作,两位作家间无法统一提交修改的顺序。
例子采用Centralized Workflow的版本控制流程,如图7所示:
作家合作修改README的流程(主干方式)
图7 作家合作修改README的流程(主干方式)
根据图7中的流程,两位作家利用git所进行的操作步骤主要包括:
(1) 两位作家分别创建工作目录
作家A和B需要“拷贝”远程代码库以创建自己的本地工作拷贝,其中包括本地代码库、索引文件(即暂存文件)和工作目录,注意,本地代码库和索引文件位于工作目录中,是隐藏的(位于工作目录的.git子文件夹中),本地工作拷贝是工作目录中其他可见的内容,后文中,我们不加区别地称该目录为工作目录。
从远程代码库通过“拷贝方式”创建本地工作目录的命令为:
git clone [选项] ADDRESS [本地工作目录]
ADDRESS是远程代码库的路径,本地工作目录如果没有指明,则来自ADDRESS中指出的路径(去掉.git后缀)。
ADDRESS由”协议://主机/目录”构成,如果采用网络连接代码库,则协议部分为http或svn,如:
“http://vlab.cs.swust.edu.cn:8081/linuxCourse/linuxer.git”
,如果采用本地模拟的远程代码库(如本例),则协议部分为file,如:“file:///root/workspace/rep.git” 。
git clone命令实际上根据远程代码库,创建了本地拷贝中的本地代码库、缓存目录以及本地工作目录(本地代码库和缓存目录都在本地工作目录中,且默认不可见,后面不加区别的称呼本地代码库、本地拷贝和本地工作目录),并保存了远程代码库地址的名称为origin,便于使用git push命令提交本地代码库到远程代码库时使用。
作家A创建工作目录,使用如下命令:
git clone file:///root/workspace/rep.git A
Cloning into 'A'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
ls -l
total 168
drwxr-xr-x 3 root root 4096 Oct 8 06:47 A
drwxr-xr-x 3 root root 4096 Oct 8 06:42 proj
drwxr-xr-x 7 root root 4096 Oct 8 06:44 rep.git
可见,生成的工作目录为A,拷贝自远程代码库“file:///notebooks/workspace/rep.git”
作家B创建工作目录,使用如下命令:
git clone file:///notebooks/workspace/rep.git B
Cloning into 'B'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
ls -l
total 168
drwxr-xr-x 3 root root 4096 Oct 8 06:47 A
drwxr-xr-x 3 root root 4096 Oct 8 06:47 B
drwxr-xr-x 3 root root 4096 Oct 8 06:42 proj
drwxr-xr-x 7 root root 4096 Oct 8 06:44 rep.git
(2) 作家A生成初始文章README
模拟作家A进行文章编写:
cd A
git status命令可以查看工作目录中的文件状态,可以是修改过的、新创建的、已经暂存但未提交的等。
git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
结果显示,目前没有东西需要提交。(nothing to commit)
模拟作家A新增加README文件,内容为:
Chapter1
Chapter2
请使用文本编辑器命令vi生成上述文件(/notebooks/workspace/A/README)。查看下文件的内容:
cat README
Chapter1
Chapter2
请试试git status命令,查看下本地工作目录中的文件状态。
作家A增加README到本地代码库中,执行命令:
git add README
git commit -m "Initiate README"
[master (root-commit) 2aead62] Initiate README
1 file changed, 2 insertions(+)
create mode 100644 README
接下来,将本地代码库提交到远程代码库上:
git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 227 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To file:///root/workspace/rep.git
* [new branch] master -> master
git push命令用于将本地代码库提交到origin所指示的远程代码库中,其命令行形式为:
git push [远程代码库名称] [本地分支名]:[远程分支名]
上述命令的执行中,远程代码库的分支名称为master,远程代码库名称为origin(因为前面执行git clone命令时已经默认保存了远程代码库的地址,该地址的名称为origin),可以通过git remote命令查看所有远程代码库的名称和对应的地址。如:
git remote -v
origin file:///root/workspace/rep.git/ (fetch)
origin file:///root/workspace/rep.git/ (push)
上述命令结果表明:远程代码库origin的地址是file:///root/workspace/rep.git/,其中拷贝/抓取(fetch)操作和提交(push)操作的远程代码库地址是一样的。
(3) 作家B更新README的版本1
模拟作家B进行操作。
cd ../B
从代码库上获取更新到工作目录,使用git pull操作,命令行形式为:
git pull <远程主机名> <远程分支名>:<本地分支名>
如果省略本地分支名,则意味着与远程分支名相同。
作家B获取README文件的变化,使用git pull命令:
git pull origin master
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From file:///root/workspace/rep
* branch master -> FETCH_HEAD
* [new branch] master -> origin/master
结果显示从远程代码库file:///root/workspace/rep.git更新本地代码库成功。
ls -l
total 4
-rw-r--r-- 1 root root 18 Oct 8 07:16 README
cat README
Chapter1
Chapter2
结果显示,工作目录文件更新成功。
(4) 作家A修改README文档,形成版本2
cd ../A
模拟作家A进行操作。修改REAME文件为:
Chapter1
First
Chapter2
请打开一个命令行窗口,使用文本编辑器命令vi修改上述文件(/root/workspace/A/README)。
cat README
Chapter1
First
Chapter2
git add README
git commit -m "Add First in Chapter1 by A"
[master 851e614] Add First in Chapter1 by A
1 file changed, 1 insertion(+)
git push origin master
Counting objects: 5, done.
Writing objects: 100% (3/3), 266 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To file:///root/workspace/rep.git
2aead62..851e614 master -> master
将README的更新提交到远程中心代码库。
(5) 作家B得到远程中心代码库上的新版本,在其上进行修改
cd ../B
模拟作家B进行操作。
git pull origin master
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From file:///notebooks/workspace/rep
* branch master -> FETCH_HEAD
2aead62..851e614 master -> origin/master
Updating 2aead62..851e614
Fast-forward
README | 1 +
1 file changed, 1 insertion(+)
作家B进行更新操作("Fast-forward"表示工作目录中的README文件被更新了)。
cat README
Chapter1
First
Chapter2
作家B修改文章为:
Chapter1
1st
Chapter2
First
打开一个命令行窗口,使用文本编辑器命令vi修改上述文件(/root/workspace/B/README)。
cat README
Chapter1
1st
Chapter2
First
(6) 与此同时,作家A修改文章
cd ../A
模拟作家A的操作。作家A修改README文件为:
Chapter1
First
Second
Chapter2
cat README
Chapter1
First
Second
Chapter2
git add README
git commit -m "Add Chapter1 by A"
[master 0c1c56e] Add Chapter1 by A
1 file changed, 1 insertion(+)
git push origin master
Counting objects: 5, done.
Writing objects: 100% (3/3), 267 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To file:///root/workspace/rep.git
851e614..0c1c56e master -> master
作家A提交修改到远程中心代码库。
(7) 作家A提交后,作家B提交(5)中所做的修改
cd ../B
git add README
git commit -m "Modify Chapter1 by B"
[master ce92fc0] Modify Chapter1 by B
1 file changed, 2 insertions(+), 1 deletion(-)
git push origin master
To file:///root/workspace/rep.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'file:///root/workspace/rep.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操作,再执行git push操作。
接下来执行:
git pull origin master
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From file:///root/workspace/rep
* branch master -> FETCH_HEAD
851e614..0c1c56e master -> origin/master
Auto-merging README
CONFLICT (content): Merge conflict in README
Automatic merge failed; fix conflicts and then commit the result.
提示README文件产生了冲突,查看README文件:
cat README
Chapter1
<<<<<<< HEAD
1st
=======
First
Second
>>>>>>> 0c1c56ee3b8bc55136acc11faeeb5431b1890a4d
Chapter2
First
文件中标志了冲突部分:<<<<<<< HEAD和=======之间为B的本地代码库中的内容,即B的修改;=======和>>>>>>>之间是远程代码库中的内容,即A已提交的修改。
打开一个命令行窗口,使用文本编辑器命令vi修改上述文件(/root/workspace/B/README)为:
Chapter1
1st
Chapter2
First
cat README
Chapter1
1st
Chapter2
First
提交更新到远程代码库。
git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commit each, respectively.
(use "git pull" to merge the remote branch into yours)
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: README
no changes added to commit (use "git add" and/or "git commit -a")
结果显示README文件冲突,提示使用git add和git commit命令提交。
git add README
git commit -m "Merged by B"
[master 840f888] Merged by B
git push origin master
Counting objects: 8, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 386 bytes | 0 bytes/s, done.
Total 4 (delta 1), reused 0 (delta 0)
To file:///root/workspace/rep.git
0c1c56e..840f888 master -> master
git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
结果显示冲突解决,提交成功。
(8) 作家A获取文章的更新
cd ../A
模拟作家A操作,执行更新操作:
git pull origin master
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From file:///root/workspace/rep
* branch master -> FETCH_HEAD
0c1c56e..840f888 master -> origin/master
Updating 0c1c56e..840f888
Fast-forward
README | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
结果显示,README已更新(“1 file changed”)。查看该文档:
cat README
Chapter1
1st
Chapter2
First
8 实验任务
请继续7.2的例子(如图7 作家合作修改README的流程(主干方式)),将README文件修改为:
Chapter1
First
Second
Chapter2
First
其中,Chapter1的部分由A完成,Chapter2的部分由B完成。
9 实验思考
1、以7.2中的过程为例子,描述源代码版本控制中的“冲突”是如何产生的?如何解决?给我实验任务的完整代码,不要只给我实验任务的,完全从0开始
最新发布