版本控制工具Git详解

阅读: 评论:0

版本控制工具Git详解

版本控制工具Git详解

一、Git和SVN的区别?

这是一个学Git无法绕开的话题,也是面试的常见题,我猜很多人的回答都是百度上直接背的,有了解过SVN底层的实现原理吗?

SVN是一种集中式版本控制工具,SVN架构如图:

A、B、C三个开发者如果需要提交自己的代码到远程仓库,必须联网(上传),上传之后SVN仓库内部做了什么?

假设用户A提交代码,会将用户A改动过的A.java提交给SVN仓库,仓库中记录的仅仅是变化(增量),对于B.java,C.java等没有h执行操作的文件,则没有增量。

那么引申出两个问题

1、假设无法联网怎么办?

2、假设SVN仓库硬盘坏了怎么办?

 

Git是一个分布式的版本控制工具,其架构如图所示:

 local computer1、local computer2本质上就是一个本地仓库,所以Git无需联网就可以在本地开发实现提交等操作,Server、local computer1、computer2的内容是一样的。并且与SVN不同的是local computer1和local computer2直接可以进行通信。

Git版本控制的底层原理:

对于修改过的文件,如图中的A.java,会在新版本中保存修改过后的文件副本,可以理解为是一个Snapshot(注意:并不是增量文件)。对于没有修改过的文件,则在新版本中保存的是旧版本的引用。

 

二、工作区与暂存区

Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。

工作区(Working Directory):就是你在电脑里能看到的目录。

版本库(Repository):工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

我们把文件往Git版本库里添加的时候,是分两步执行的:

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改

俗话说,实践出真知。现在,我们再练习一遍,先对<做个修改,比如加上一行内容:

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.

然后,在工作区新增一个LICENSE文本文件(内容随便写)。

先用git status查看一下状态:

$ 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:   Untracked files:(use "git add <file>..." to include in what will be committed)LICENSEno changes added to commit (use "git add" and/or "git commit -a")

Git非常清楚地告诉我们,<被修改了,而LICENSE还从来没有被添加过,所以它的状态是Untracked

现在,使用两次命令git add,把<和LICENSE都添加后,用git status再查看一下:

$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file:   LICENSEmodified:   

现在,暂存区的状态就变成这样了:

所以,git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。

$ git commit -m "understand how stage works"
[master e43a48b] understand how stage works2 files changed, 2 insertions(+)create mode 100644 LICENSE

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

$ git status
On branch master
nothing to commit, working tree clean

现在版本库变成了这样,暂存区就没有任何内容了:

本文第二章节以上部分参考自《工作区和暂存区——廖雪峰的官方网站》 

git中包含四种状态:Untracked、Unmodified、modified   [可以通过git status查看当前状态]

1、Untracked一般出现在新建文件时候,表示新建的文件处于未被追踪的状态

2、将新建文件add之后,此时新建文件处于暂存区(Stage),状态为Staged状态

3、当用户commit提交暂存区域的文件到分支时,所有文件将处于Unmodified状态,此时Stage(暂存区是完全干净的)

4、当用户对文件进行修改后,被修改的文件将处于Modified状态,等待add 【重复2-4步骤】

 

三、Git最常用的几个命令

  • remote
  • fetch/pull/push
  • reset
  • checkout
  • log
  • merge

当然还包括我们上一章节所说的git add <file>    和  git commit -m '信息'    和 git status  命令,下面我们将一个个介绍。

3.1  git remote(建立远端连接)

因为我们会存在远端的分支,比如使用gitlab举例来说。

假设我们本地已有一个项目,但是GitLab/GitHub上没有,我现在想将该项目推送上去,该怎么做呢?

1、这是一个比较笨的方法,在远端建立一个项目,从远端仓库clone到本地,将本地代码拷贝到该项目,再分别执行三个命令add->commit->push。

2、这是一个比较明智的做法,步骤如下:

(1)将需要提交项目的目录的项目初始化,打开Git Bash,比如我要将我的目录/User/itcats_cn/git_test项目提交到Gitlab,就cd到git_test,输入git init命令,初始化该项目为git管理的项目,在执行完该命令后,输入ls -a,应该可以看到.git的隐藏文件。

(2)在本地git add <file> 和 git commit -m 'message' 

(3)在Gitlab中创建项目,项目名需要与第(2)步骤中提交的文件<file>名称一致

(4)复制gitlab中的git协议的地址,如git@192.168.0.11:web/test.git

(5)执行命令  git remote add origin git@192.168.0.11:web/test.git  【含义是"add" 本地项目与远端建立联系】

(6)执行命令  git config --list  发现igin.url=git@192.168.0.11:web/test.git,已经关联上了

 

输入git remote -v命令,返回的是一个origin集合,说明用户可以对远端的仓库执行fetch和push操作

origin	git@192.168.0.11:web/test.git (fetch)
origin	git@192.168.0.11:web/test.git (push)

 

3.2  git fetch(版本更新)

从远程仓库获取最新到本地,不会自动merge,Git中从远程的分支获取最新的版本到本地方式如下:

方式一

(1)查看远程仓库

$ git remote -v
eoecn   .git (fetch)
eoecn   .git (push)
origin  .git (fetch)
origin  .git (push)
su@SUCHANGLI /e/eoe_client/android-app (master)

从上面的结果可以看出,远程仓库有两个,一个是eoecn,一个是origin
(2)从远程获取最新版本到本地

$ git fetch origin master
From * branch            master     -> FETCH_HEAD
su@SUCHANGLI /e/eoe_client/android-app (master)

$ git fetch origin master 这句的意思是:从远程的origin仓库的master分支下载代码到本地的origin master

(3)比较本地的仓库和远程参考的区别

$ git log -p master.. origin/master
su@SUCHANGLI /e/eoe_client/android-app (master)

我的本地参考代码和远程代码相同,所以是Already up-to-date

以上的方式有点不好理解,大家可以使用下面的方式,并且很安全

方式二

(1)查看远程分支,和上面的第一步相同
(2)从远程获取最新版本到本地

$ git fetch origin master:temp
From * [new branch]      master     -> temp
su@SUCHANGLI /e/eoe_client/android-app (master)

git fetch origin master:temp 这句命令的意思是:从远程的origin仓库的master分支下载到本地并新建一个分支temp

(3)比较本地的仓库和远程参考的区别

$ git diff temp
su@SUCHANGLI /e/eoe_client/android-app (master)

命令的意思是:比较master分支和temp分支的不同
由于我的没有区别就没有显示其他信息
(4)合并temp分支到master分支

$ git merge temp
Already up-to-date.
su@SUCHANGLI /e/eoe_client/android-app (master)

由于没有区别,所以显示Already up-to-date.
合并的时候可能会出现冲突,有时间了再把如何处理冲突写一篇博客补充上。
(5)如果不想要temp分支了,可以删除此分支

$ git branch -d temp
Deleted branch temp (was d6d48cc).
su@SUCHANGLI /e/eoe_client/android-app (master)

如果该分支没有合并到主分支会报错,可以用以下命令强制删除git branch -D <分支名>

总结:方式二更好理解,更安全,对于pull也可以更新代码到本地,相当于fetch+merge,多人写作的话不够安全。

git fetch章节参考自:《git fetch的简单用法》

 

3.3  git pull(版本更新合并)

git pull origin master

 

3.4  git push(版本推送)

git add .

git commit -m 'update .gitignore'

git push origin master  或者  git push -u origin dev
 

 

3.5  git reset(版本回退)

可以通过git log命令查看历史的commit记录,通过选择复制commit后的SHA1 id进行指定的版本回退,如:

localhost:git_test fatah$ git log
commit 617866c48b75d62b8b33a29fc21eb6d32cff3eb1 (HEAD -> master)
Author: itcats_cn <xxx@qq>
Date:   Tue Aug 6 23:00:27 2019 -0400Add new filecommit 8ea66b2158d45ddd459fc6b538669be5b546bbb0
Author: itcats_cn <xxx@qq>
Date:   Wed Aug 7 10:20:37 2019 +0800init2

目前版本为617866开头的commit id版本号,如果想回退版本,可以通过指定版本号进行回退

git reset --hear '8ea66b2158d45ddd459fc6b538669be5b546bbb0'

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:

localhost:git_test fatah$ git log --pretty=oneline
881667e679a88b557b1014d33e6a46a3c1d0a442 (HEAD -> master, origin/master) add hahaha
617866c48b75d62b8b33a29fc21eb6d32cff3eb1 Add new file

Git的commit id(版本号)和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id和我的肯定不一样,以你自己的为准。为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。

Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。

 

3.6 checkout

作用:

1、切换到新的分支: 一般来说master分支上都是最稳定的版本,我们日常开发的时候在分支开发,开发完毕之后再合并到master中,具体操作如下。

$ git checkout -b dev-0807-workSwitched to a new branch 'dev-0807-work'$ git checkout -b masterSwitched to a new branch 'master'

2、撤销更改 :只能撤销更改过的文件,不能撤销新增的文件或删除的文件

比如我们修改了3.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:   3.txtno changes added to commit (use "git add" and/or "git commit -a")

现在我想撤销3.txt的更改,操作如下:

$ git 
Updated 1 path from the index

此时3.txt的内容就成功被撤销,这样不会影响之前的版本,恢复成非常"干净"的状态

 

3.7  merge

一般我们开发环境都在分支上开发,分支开发完毕时候测试通过则发布,并且在master进行merge合并并发布,这样才能保证master上是最"clean"的状态,但是一般我们将分支合并到Master都是在图形化界面发起的,下面我将展示GitLab中的 Pull Request。

这种merge方法方便code-review,查看改动之处并让参与者、审阅者也可以方便在下面书写评论。 

在确认没有任何问题后,再点击绿色的按钮"Merge',就可以将我们的工作分支合并到master之中。

合并之后,在本地机器执行  git fetch 命令,从远端拉取最新的代码[此时本机处于dev-08-07-work分支]

本机切换到master分支,执行命令 git checkout master

localhost:git_test fatah$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.(use "git pull" to update your local branch)

提示远端的master有两处commit,先pull到本机的orgin/master

localhost:git_test fatah$ git pull origin master
Enter passphrase for key '/Users/fatah/.ssh/id_rsa': 
From 192.168.1.103:web/test* branch            master     -> FETCH_HEAD
Updating 881667e..b117d8f
 | 3 ++-1 file changed, 2 insertions(+), 1 deletion(-)

整个过程我们发现,本地的master并没有push到远端仓库,而是使用分支push到远端仓库中,再使用Request Merge在远端将分支与Master进行合并,在code-view没有问题之后,在远端仓库执行Merge操作。由于本地在fetch最新代码的时候并不会直接合并,所以在本机切换为master分支的时候会提示pull一下,因为有两处commit,在pull成功之后本机master便拥有了最新的master代码。

在merge的时候如何解决冲突呢?

 

3.7.1  merge解决冲突

模拟场景:

(1)比如修改gitlab中的a.txt文件,本地也修改git管理目录下的a.txt文件

(2)本地进行add 和 commit操作

(3)执行git pull origin master时候,发现冲突文件,因为远端和本地两处修改位置一样,到底选用哪一行?

(4)删除冲突部分,其中<<<<HEAD到=====之间的内容为本地仓库内容,后面的内容为远端仓库的内容,两处冲突地方选用哪个可以自己权衡,再修改完之后,输入git status,发现a.txt变为了unmodified状态,重新执行add -> commit -> push操作,发现完成解决冲突。

 

 

本文发布于:2024-01-31 21:48:01,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170670888131579.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:详解   版本   工具   Git
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23