Git原理及使用
在学习Git的过程中,本人合并以及适当修改了以下几篇文章的内容:
Git环境配置
下载
下载步骤略去,在官网下载即可,参考MAC上Git安装与GitHub基本使用
配置
安装完git后首先要配置git用户名和邮箱,非常重要,因为每次git提交都需要用到该信息,被永远的嵌入到了你的提交中
git config --global user.name "用户名" # 设置用户名
git config --global user.email "邮箱地址" # 设置邮箱email
--global
的作用:
如果加上--global
,则表明这是全局设置,git将总是会使用该配置信息来处理你在系统中所做的一切操作。如果希望在一个特定的项目中使用不同的名称或e-mail地址,你可以在该项目中运行git config
命令而不加--global
选项。总之--global
为全局配置,不加为某个项目的特定配置。
查看配置
git config -l --system # 查看系统配置
git config -l --global # 查看用户配置
git配置实质上都是**本地文件**:(以Mac OS为例)系统配置文件为/usr/local/git/etc/gitconfig
,用户配置文件为~/.gitconfig
(注意,用户配置文件必须在设置了全局配置信息后才会被创建)
Git工作原理
Git工作区域
- 工作目录(Working Directory):平时存放项目代码的地方
- 暂存区(Stage/Index):暂存区,用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息
- 本地仓库 / 资源库(Repository或Git Directory):就是安全存放数据的位置,这里面有你提交到所有版本的数据。其中HEAD指向最新放入仓库的版本(最新一次commit)
- 远程仓库(Remote Directory):远程仓库,托管代码的服务器,可以简单的认为是你项目组中的一台电脑用于远程数据交换
本地区域的一些概念
- Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间。
- WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间。
- .git:存放Git管理信息的目录,初始化仓库(
git init
命令)的时候自动创建。 - Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区。
- Local Repo:本地仓库,一个存放在本地的版本库;HEAD会指示当前的开发分支(branch)。
- Stash:隐藏,是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态。
Git工作流程
- 在工作目录中新建、修改、删除文件
- 将需要进行版本管理的文件放入暂存区域
- 将暂存区域的文件push到git远程仓库
git管理的文件有三种状态:modified(已修改),staged(已暂存),committed(已提交)
Git文件状态
文件的四种状态
- Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到git库, 不参与版本控制. 通过
git add
状态变为Staged
. - Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为
Modified
. 如果使用git rm
移出版本库, 则成为Untracked
文件 - Modified: 文件已修改, 仅仅是修改, 并没有进 行其他的操作. 这个文件也有两个去处, 通过
git add
可进入暂存staged
状态, 使用git checkout
则丢弃修改过, 返回到unmodify
状态, 这个git checkout
即从库中取出文件, 覆盖当前修改 ! - Staged: 暂存状态. 执行
git commit
则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodify
状态. 执行git reset HEAD filename
取消暂存, 文件状态为Modified
Git实操演练
相关常见的操作命令如下图所示
本地仓库的操作
创建本地仓库,有两种方法。一种是创建全新的仓库,另一种是克隆远程仓库。
创建全新仓库
[bxy@Yu]Desktop$ mkdir git_test
[bxy@Yu]Desktop$ cd git_test
[bxy@Yu]git_test$ git init # 初始化Git仓库,执行完成后,会多出一个.git的目录
克隆远程仓库
[bxy@Yu]Desktop$ git clone [远程仓库的url] # 克隆一个项目和它的整个代码历史(版本信息)
接下来,实际操作一个例子(git_test目录已执行过git init
命令)
[bxy@Yu]git_test$ touch hello.java # 新建一个hello.java文件
[bxy@Yu]git_test$ git status # 查看文件状态 也可以在后面加上文件名

[bxy@Yu]git_test$ git add . # 添加所有文件到暂存区
[bxy@Yu]git_test$ git status # 查看文件状态

[bxy@Yu]git_test$ git commit -m "This is my new java file!!" # 提交暂存区中的内容到本地仓库 -m 提交信息
[bxy@Yu]git_test$ git status # 查看文件状态

远程仓库的操作
SSH公钥的生成
在本机生成公钥(id_rsa.pub)和私钥(id_rsa),具体操作流程见我另一篇文章SSH免密登陆操作及相关文件作用,这里不再赘述
Github设置
-
右上角个人头像 -> Settings -> SSH and GPG keys -> New SSH key
将
~/.ssh/id_rsa.pub
文件内容粘贴到「公钥」对应文本框中,输入Title,点击确定后,输入Github密码,添加成功 -
验证链接
ssh -T git@github.com
-
提交本地项目到远程仓库(git push)
新建一个仓库(git_test_rep),复制SSH地址,在命令行键入
git clone [url]
# 在git_test_rep中新建一个文件 [bxy@Yu]git_test_rep$ touch hello.java # 添加到暂存区 [bxy@Yu]git_test_rep$ git add . # 提交到本地仓库 [bxy@Yu]git_test_rep$ git commit -m "First commit" # 推送到远程仓库(Github中) [bxy@Yu]git_test_rep$ git push
Github中刷新,发现新文件已被成功推送
注意:【从Github中克隆下来的仓库项目,在.git目录下的config文件中已配置好远程仓库的位置信息,git push命令后面无需加远程仓库url】
Git操作命令
add
git add . | 添加当前目录的所有文件到暂存区 |
---|---|
git add [dir] | 添加指定目录到暂存区,包括子目录 |
git add [file1] | 添加指定文件到暂存区 |
commit
git commit -m [message] | 交暂存区到本地仓库,message代表说明信息 |
---|---|
git commit [file1] -m [message] | 提交暂存区的指定文件到本地仓库 |
git commit --amend -m [message] | 用一次新的commit,替代上一次提交 |
push
git push [remote] [branch] | 上传本地指定分支到远程仓库 |
---|---|
git push [remote] --force | 强行推送当前分支到远程仓库,即使有冲突 |
git push [remote] --all | 推送所有分支到远程仓库 |
branch
git branch | 列出所有本地分支 |
---|---|
git branch -r | 列出所有远程分支 |
git branch -a | 列出所有本地分支和远程分支 |
git branch [branch-name] | 新建一个分支,但依然停留在当前分支 |
git checkout -b [branch-name] | 新建一个分支,并切换到该分支 |
git branch --track [branch] [remote-branch] | 新建一个分支,与指定的远程分支建立追踪关系 |
git checkout [branch-name] | 切换到指定分支,并更新工作区 |
git branch -d [branch-name] | 删除分支 |
git push origin --delete [branch-name] | 删除远程分支 |
merge
git fetch [remote] | merge之前先拉一下远程仓库最新代码 |
---|---|
git merge [branch] | 合并指定分支到当前分支 |
一般在merge之后,会出现conflict,需要针对冲突情况,手动解除冲突。主要是因为两个用户修改了同一文件的同一块区域。
rebase

rebase又称为衍合,是合并的另外一种选择。
在开始阶段,我们处于new分支上,执行git rebase dev
,那么new分支上新的commit都在master分支上重演一遍,最后checkout切换回到new分支。这一点与merge是一样的,合并前后所处的分支并没有改变。git rebase dev
,通俗的解释就是new分支想站在dev的肩膀上继续下去。rebase也需要手动解决冲突。
rebase与merge的区别
现在我们有这样的两个分支,test和master,提交如下:
D---E test
/
A---B---C---F master复制代码
在master执行git merge test
,然后会得到如下结果:
D--------E
/ \
A---B---C---F----G test, master复制代码
在master执行git rebase test
,然后得到如下结果:
A---B---D---E---C'---F' test, master复制代码
可以看到,merge操作会生成一个新的节点,之前的提交分开显示。而rebase操作不会生成新的节点,是将两个分支融合成一个线性的提交。
如果你想要一个干净的,没有merge commit的线性历史树,那么你应该选择git rebase 如果你想保留完整的历史记录,并且想要避免重写commit history的风险,你应该选择使用git merge
reset

reset命令把当前分支指向另一个位置,并且相应的变动工作区和暂存区。
git reset —soft [commit] | 只改变提交点,暂存区和工作目录的内容都不改变 |
---|---|
git reset —mixed [commit] | 改变提交点,同时改变暂存区的内容 |
git reset —hard [commit] | 暂存区、工作区的内容都会被修改到与提交点完全一致的状态 |
git reset --hard HEAD | 让工作区回到上次提交时的状态 |
revert
git revert
用一个新提交来消除一个历史提交所做的任何修改。

revert与reset的区别
git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。
在回滚这一操作上看,效果差不多。但是在日后继续merge以前的老版本时有区别。因为git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,导致这部分改变不会再次出现,减少冲突。但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入,产生很多冲突。关于这一点,不太理解的可以看这篇文章。
git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容。
diff
git diff | 显示工作区和暂存区的差异 |
---|---|
git diff HEAD | 显示工作区与当前分支最新commit之间的差异 |
其他
git status | 显示有变更的文件 |
---|---|
git status [file-name] | 显示文件的状态 |
git cherry-pick [commit] | 选择一个commit,合并进当前分支 |
Git补充
HEAD的理解
HEAD,它始终指向当前所处分支的最新的提交点。你所处的分支变化了,或者产生了新的提交点,HEAD就会跟着改变。
Git忽略的文件
有些时候我们不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等。
在主目录下建立".gitignore
"文件,此文件有如下规则:
- 文件中的空行或以#开始注释行将不起作用
- 可以使用Linux通配符。例如:星号(*)代表任意多个字符,问号(?)代表一个字符,方括号([abc])代表可选字符范围,大括号({string1,string2,...})代表可选的字符串等。
- 如果名称的最前面有一个感叹号(!),表示例外规则,将不被忽略。
- 如果名称的最前面是一个路径分隔符(/),和此名称同级的其他文件将被忽略,而不包括此名称下面的文件及目录
- 如果名称的最后面是一个路径分隔符(/),表示要忽略的是该名称目录下对应的所有文件及目录
举例:
# 为注释
*.txt #忽略所有 .txt结尾的文件,这样的话上传就不会被选中!
!lib.txt #但lib.txt除外
/temp #仅忽略项目根目录下的所有文件,不会忽略temp目录
build/ #忽略build/目录下的所有文件
doc/*.txt #会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
版本控制
概念
版本控制(Revision Control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。
- 实现跨区域多人协同开发
- 追踪和记载一个或者多个文件的历史记录
- 组织和保护你的源代码和文档
- 统计工作量
- 并行开发、提高开发效率
- 跟踪记录整个软件的开发过程
- 减轻开发人员的负担,节省时间,同时降低人为错误
简单说就是用于管理多人协同开发项目的技术。
没有进行版本控制或者版本控制本身缺乏正确的流程管理,在软件开发过程中将会引入很多问题,如软件代码的一致性、软件内容的冗余、软件过程的事物性、软件开发过程中的并发性、软件源代码的安全性,以及软件的整合等问题。
主流的版本控制器有如下这些:
- Git
- SVN(Subversion)
- CVS(Concurrent Versions System)
- VSS(Micorosoft Visual SourceSafe)
- TFS(Team Foundation Server)
- Visual Studio Online
版本控制产品非常的多(Perforce、Rational ClearCase、RCS(GNU Revision Control System)、Serena Dimention、SVK、BitKeeper、Monotone、Bazaar、Mercurial、SourceGear Vault),现在影响力最大且使用最广泛的是Git与SVN
SVN和Git的区别
SVN 是集中式版本控制系统,版本库是集中放在中央服务器的,而工作的时候,用的都是自己的电脑,所以首先要从中央服务器得到最新的版本,然后工作,完成工作后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,对网络带宽要求较高。

**Git **是分布式版本控制系统,没有中央服务器,每个人的电脑就是一个完整的版本库,工作的时候不需要联网了,因为版本都在自己电脑上。协同的方法是这样的:比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。Git可以直接看到更新了哪些代码和文件!Git是目前世界上最先进的分布式版本控制系统。

参考资料
转载自:https://juejin.cn/post/6979432395925782565