赞
踩
本文为参考和整理其他的博客,主要是为了记录学习过程写的
注:由于git是由linux的创作者发起创作的,因此git是支持linux的所有命令的。因此我们可以直接使用相关linux命令在git bash中进行相关操作。
一、git的基本使用结构
工作区(working directory):简言之就是你工作的区域。对于git而言,就是的本地工作目录。工作区的内容会包含提交到暂存区和版本库(当前提交点)的内容,同时也包含自己的修改内容。具体而言就是你存放代码文件的项目目录。
暂存区(stage area, 又称为索引区index),是git中一个非常重要的概念。是我们把修改提交版本库前的一个过渡阶段。查看GIT自带帮助手册的时候,通常以index来表示暂存区。在工作目录下有一个.git的目录,里面有个index文件,存储着关于暂存区的内容。git add命令将工作区内容添加到暂存区。
本地仓库(local repository),版本控制系统的仓库,存在于本地。当执行git commit命令后,会将暂存区内容提交到仓库之中。在工作区下面有.git的目录,这个目录下的内容不属于工作区,里面便是仓库的数据信息,暂存区相关内容也在其中。这里也可以使用merge或rebase将远程仓库副本合并到本地仓库。图中的只有merge,注意这里也可以使用rebase。
远程版本库(remote repository),与本地仓库概念基本一致,不同之处在于一个存在远程,可用于远程协作,一个却是存在于本地。通过push/pull可实现本地与远程的交互;github,gitee等就属于一种远程版本库。
远程仓库副本,可以理解为存在于本地的远程仓库缓存。如需更新,可通过git fetch/pull命令获取远程仓库内容。使用fech获取时,并未合并到本地仓库,此时可使用git merge实现远程仓库副本与本地仓库的合并。git pull 根据配置的不同,可为git fetch + git merge 或 git fetch + git rebase。rebase和merge的区别可以自己去网上找些资料了解下。
二.、本地库初始化
要使用git的第一步,就是需要进行本地库的初始化。
(1)在任意的目录下右键,然后在选项中选择Git Bash Here,然后通过cd命令进入你需要创建本地仓库的目录,比如我要在f盘下创建一个git_project目录作为本地仓库,就先cd/ f进入f盘目录,然后mkdir git_project 创建目录。然后cd进这个目录。
(2)执行git init命令,主要用来初始化一个空的git本地仓库,执行完该命令这个时候会出现以下的画面
说明在git_project目录下生成了一个.git文件夹,这个文件夹就是git版本库,但是由于该文件夹是隐藏文件夹,从它的.开头的目录结构就可以看出,因此使用ls是无法查看到该目录的,需要使用ls-la或者ls -lA才能查看到。.git目录中的内容可以通过ll .git/查看。
(3)设置签名
设置签名的目的是是为了标识用户,区分不同开发人员的身份,如果想要将本地的项目提交的远程仓库的话,必须要设置签名。但是要注意,这个和远程仓库(代码托管中心)的用户名密码没有任何关系,也就是和你的github,gitee等账号密码没有关系。
签名的形式(例子):
用户名:git_user
Email地址:12345678@qq.com
以上都是随便设置的,这个邮件地址甚至可以不存在,git是不会验证这个邮箱是否可用的,只是用作标识不同用户而已。
设置签名有两种方式,一种是为单个仓库单独设置,这种方式只针对单个仓库、本地库范围内有效;另一种是全局配置,系统用户级别,采用这种方式配置后,当前操作系统的用户范围内的所有仓库都有效。如果对两种方式都进行了配置,那么会优先使用单个仓库配置方式的配置信息,不允许一个都不设置,如果不进行设置,在提交项目时会报错!通常情况下,设置了全局配置,系统用户的级别就足够了,在有特殊需求时再设置项目/仓库级别的。
为单个仓库单独设置签名命令:
git config user.name git_user
git config user.email.12345678@qq.com
为全局,系统用户级别设置签名命令:
git config --global user.name git_user_global
git config --global user.email 87654321@qq.com
那么设置完了我们怎么知道自己已经设置成功,或者怎么查看到我们的设置呢?
对于为单个仓库单独设置的签名,我们可以cat进入到.git目录下的config文件,就可以查看到刚才的设置了。
对于为全局,系统用户级别设置的签名,我们可以在当前用户的home目录或者说家目录中找到,输入cd~命令,可通过pwd命令查看当前目录,发现跳转到了/c/Users/Administrator。
在该目录下,我们通过ll |less命令查看,发现并没有相应的关于git的配置文件,这是因为与git签名相关的配置文件是隐藏的。点击q退出后,输入ls -lA|less,这个时候会发现有一个.gitconfig文件,使用cat命令查看一下,就看到了刚才的配置了。
三。操作git
1.通过git add命令将工作区中的文件往暂存区添加
先在工作区中使用git status查看文件或者文件夹在工作区,暂存区的状态。提示信息为,当前分支为master分支(master分支是默认自动存在的),还没有任何提交记录,也没有需要提交的文件。
在工作区中,也就是本例的/f/git_project中创建一个test.txt文件(vim test.txt),内容写,写完退出后,再次使用git status查看文件或者文件夹在工作区,暂存区的状态。发现此时有红色信息,显示untracked files。
Untracked files:表示只在工作区有的file(文件或文件夹),也就是在暂存区没有该file。Git 不会自动将之纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文件”,因而不用担心把临时文件什么的也归入版本管理。我们需要通过git add test.txt文件将工作区的文件添加到暂存区来暂存文件,来添加追踪。添加后,再使用git status查看,发现红色信息已经变为绿色了。
(注意当git add命令执行后报了一个warning,LF will be replaced by CRLF in test.txt。
这是因为,在Windows和Dos下:使用回车(CR)和换行(LF)两个字符来结束一行,回车+换行(CR+LF),即“\r\n”;而在Unix和mac下:只使用换行(LF)一个字符来结束一行,即“\n”;(最早Mac每行结尾是回车CR 即’\r’,后mac os x 也投奔了 unix)。我们在Windows平台下git add任意Windows平台编辑过的代码文本的换行默认都是CRLF,所以一般git add不会出错。但是如果我们的团队成员是Linux/Mac平台并参与了项目的git提交或者我们Windows平台的某些软件会生成换行是LF的代码文本,那我们再进行git add这个LF换行的文件时,会出现这个警告" LF will be replaced by CRLF in …"。
假如你正在Windows上写程序;又或者你正在和其他人合作,他们在Windows上编程,而你却在其他系统上,在这些情况下,你可能会遇到行尾结束符问题。此问题的全部负面影响如下:
一个直接后果是,Unix/Mac系统下的一个“多行文本”文件在Windows里打开的话,“多行文本”会变成“一行”。(原因:Unix/Mac换行只用了换行符‘\n’,而Windows的换行要求是回车换行符’\r\n’,因此Unix/Mac中的“多行文本”的换行不符合Windows的规则,所以Windows对这些不符合换行规则的“多行文本”全部按照“没有换行”处理,所以导致“多行文本”会变成“一行”)
而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
Linux保存的文件在windows上用记事本看的话会出现黑点。
因此为了防止这些情况的发生,git的Windows 客户端基本都会默认设置 core.autocrlf=true(我们可通过git config core.autocrlf命令查询我们的Windows上该属性是否默认true。如不是true,通过config --global core.autocrlf true命令设置该属性为true),而“core.autocrlf=true”有以下3个功能来避免我们出错:
(A)在“把 modified修改过的文件git add到暂存区stage”时,Git自动把LF转换成CRLF,并给出那条警告”LF will be replaced by CRLF”
(B)在“把modified修改过的文件由暂存区(stage) 提交(commit)到版本库/仓库(repository)”时,Git自动把CRLF转换成LF
©在“用 检出/git checkout切换到指定分支 或 git clone克隆远程版本库”来加载代码时,Git自动把LF转换成CRLF。
(但是由于git只会把提交到暂存区,以及仓库的文件做转换处理,而工作区中的文件不会做处理,因此如果我们是在工作区中,使用git通过vim编辑或者创建文件时用到换行符,此时为linux的换行符lf,我们打开windows查看该文件,依旧是没有进行换行的,但是暂存区以及仓库中的是已经进行过转换的,lf已经转换成crlf了。
如果我们目前是Window平台并出现该警告,啥也别做就行,虽然这个警告难看,但这个警告能保证我们项目团队正常跨系统git操作代码
如果我们是Linux 或 Mac平台,我们不需要©的功能“在检出或克隆远程版本库时,Git自动把LF转换成CRLF”。然而当一个CRLF作为行结束符的文件在我们的Linux 或 Mac平台不小心被引入时,你肯定想让 Git 修正。 所以,你可以通过config --global core.autocrlf input命令把 core.autocrlf 设置成 input 来告诉 Git 在提交(commit)时把CRLF转换成LF,检出(git checkout)时不转换。
这样在 Windows 上的检出(checkout)文件中会保留CRLF,而在 Mac 和 Linux 上,以及版本库中会保留LF,从而保证我们项目团队正常跨系统git操作代码
————————————————
版权声明:本文为CSDN博主「快乐李同学(李俊德-大连理工大学)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wq6ylg08/article/details/88761581
)
changes to be committed表示文件已经从工作区暂存到暂存区了,注意这下面有一个提示(use “git rm --cached …” to unstage)。这个意思是说当我们执行了git add后,如果反悔了,不希望对该文件执行追踪,希望撤销其转移到暂存区,可以通过git rm --cached test.txt来撤销删除提交到缓存区中的文件。然后再通过git status查看。
2.通过git commit命令将暂存区中文件提交到本地仓库
通过输入git commit -m “message”来将暂存区中文件提交到本地仓库。其中-m参数用于告诉git,后面的"message"为此次提交的文件的一些相关描述信息,这个可以按照需要随便填写。例如这是本地的第一次提交,我就可以这样写
git commit -m “my first commit” test.txt。在命令执行后,后续的提示信息中就有关于本次提交的信息,包括刚才指定的相关描述信息,其中的"313f9dd"为本次提交的一个哈希值,用来标识区分每一次的提交。另外当中有一个root-commit信息,这个表示这次提交是本分支的第一个提交记录,这个版本链的结构有关,后面再说。然后再通过git status查看git状态。
如果在git commit后不指定-m参数,那么在执行git commit test.txt时,会跳转到vim编辑器,来让你输入这个相关参数message信息,输入后退出据自动完成commit了。
在提交了以后,如果此时我们去修改工作区中的test.txt文件,然后再使用git status查看git状态。
changes not staged for commit表示我们有没有进行暂存的工作区修改,并且显示红色的警告:modified:test.txt表示该文件经过了修改,z这个修改的操作并没有被提交到本地版本库,然后下面提示我们使用
A. git add …" to update what will be committed 来更新将要被从缓存区commit的内容,也就是将工作区的修改更新到缓存区中。
B.git checkout – …" to discard changes in working directory 来撤销对于工作区中修改的内容。
C.no changes added to commit (use “git add” and/or “git commit -a”)表明可以通过git add更新工作区中的修改到暂存区,也可以直接通过git commit -a不经缓存区直接提交到本地仓库。因为git add是将未跟踪的文件添加跟踪,而此时test.txt先前已经被跟踪过了,因此可以直接git commit -a。已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新(unmodified),已修改(modified)或者已放入暂存区(staged)。新加的文件(即没有被git系统管理的文件)是不能被提交到本地仓库的。建议一般不要使用-a参数,正常的提交还是使用git add先将要改动的文件添加到暂存区,再用git commit 提交到本地版本库。因此,已跟踪的文件,作出修改后,可以直接提交,操作方便。注意git commit -am "message"是针对所有已跟踪文件中的执行修改或删除操作的文件都提交到本地仓库,因此不需要指定某一个文件,否则似乎命令会不被识别。
如果是对于A情况,在git add再次暂存后并且使用git status查看状态,并且git commit 以及git status后如下:
此时红色警告变绿,一切正常,表明修改test.txt文件这个操作已经被保存到缓存区了,但还没有提交到版本库。
如果是对于情况C:
先修改test,txt文件,然后直接使用git commit -a命令提交已经追踪过的文件,不再次添加到暂存区,也就是跳过添加到暂存区这一步,然后使用git status查看。
3.测试版本的前进与后退
(1).查看历史的提交记录可以通过git log 命令查看
A.git log不数显示提交详细信息,可通过鼠标滚轮,空格向下翻页/b向上翻页,pageup/pagedown翻页查看,其中2a50579086637903bd6d67792727887cd4945a55表示提交的哈希值或者说索引值,head->master表示此时分支指针指向的分支为master。
B. git log --pretty=oneline以行简洁显示提交信息
、
C.git log --oneline以更简洁的行形式显示提交信息,哈希值不全显示,只显示几位。而且这种方式有种特点,只显示当前版本以及当前版本之前的版本,当前版本之后的版本不会显示。例如我们从A版本回退到B版本,那么我们就不能看到A版本了。
D.git reflog 在上面oneline的基础上,还显示了当前版本移动到需要的版本需要多少步。
(2).进行版本的前进后退
A.基于索引值操作(推荐使用)
回退:使用git reset --hard [索引号],例如当前版本索引值为2a50579,加入我们要回退到第二次提交也就是my second commit,则执行git reset --hard 33c3c5f。执行前先查看当前版本的内容,发现执行前后内容发生变化,再git reflog发现版本信息也发生了变化。
前进:我们从回退后的版本重新前进回回退前的版本,也就是从当前的33c3c5f前进到2a50579;
使用git reset --hard 2a50579,发现又变回来了。
B.通过异或符号^或者波浪线~进行版本回退
注意使用异或符号^或者波浪线~只能进行版本回退,不能进行版本前进,因此不推荐使用
使用git reset --hard HEAD^表示将HEAD版本指针往回移动一位,就是回退一个版本,如果是两个异或符号,则回退两个版本。以下执行中,在执行了一个版本的回退后,HEAD指针从指向2a50579回退指向了23db35c,实现了回退。
当我们需要回退多个版本时,通过加异或符号明显不现实,回退多少个版本就加多少个异或^符号。因此可以使用波浪线~,git reset --hard HEAD~n,其中n表示回退多少个版本。可以看到执行了git reset --hard HEAD~2后版本指针HEAD由23db35c回退到了13a41c0,回退了两个版本。
实际上git reset命令是有三种参数的,只是hard最常用,而另外的soft和mixed不常用。
git reset --soft:仅仅在本地库移动HEAD指针
git reset --mixed :在本地库中移动HEAD指针,重置暂存区
git reset --hard 在本地库移动HEAD指针,重置暂存区,重置工作区
当我们使用git reset --soft进行版本修改时,通过git reflog可以看到本地库中的版本指针HEAD已经发生了改变,但是cat工作区中的文件会发现没有变化,而且通过git status查看缓存区会发现test.txt文件为绿色的已修改的modified:test.txt提示。这是因为该命令只移动本地库的HEAD版本指针,而暂存区以及工作区中的版本不会改变,也就是出现了版本不一致的情况。因此git会判定为工作区中的文件修改且提交到了缓存区,但还没有提交到本地仓库,因此出现这样的提示。实际上不是工作区和暂存区修改了,而是本地库发生了变化。
当我们使用git reset --mixed进行版本修改时,通过git reflog可以看到本地库中的版本指针HEAD已经发生了变化,但是cat工作区中的文件没有发生变化,然后通过git status查看缓存区会发现test.txt文件为红色警告已修改的modified:test.txt提示。这是因为该命令只移动本地仓库和缓存区的HEAD版本指针,但是工作区中的版本不会改变,也就是出现了版本不一致的情况。git会认为工作区中的文件发生了修改,而还没有同步到缓存区以及本地仓库中,因此出现这样的提示。实际上不是工作区发生了修改,而是本地库以及缓存区发生了变化。
在使用了soft和mixed参数后,会出现版本不一致的情况,我们想要恢复一致的情况只需要使用hard参数,命令git reset --hard HEAD恢复缓存区,工作区为当前本地仓库一样的版本,再使用git status查看就正常了。
3.删除文件以后找回
另外通过vim 创建文件进行演示,vim aaa.txt
然后 git add aaa.txt ;
git commit -m "new aaa,txt“提交到本地库;
然后在工作区通过rm命令执行对aaa.txt的删除,执行ll命令发现该文件已经被删除了。git status查看文件状态,发现删除操作为红色警告deleted: aaa.txt,表明删除aaa.txt这个操作发生在了工作区,我们需要通过git add test.txt命令将这个删除操作添加到缓存区,然后再看git status。然后再通过git commit -m "delete aaa.txt”来将删除操作提交到版本库,再次git status。这个时候这个删除的操作记录就已经被记录在了版本库里了。
但是任何一个版本的操作记录,只要被提交到了版本库,就是属于不会消失的,除非你直接将整个版本库删除,否则你在其中可以找到过去任何一个提交的版本的记录。因此,版本库中的记录属于只会增加,不会减少或者删除的情况。通过 git reflog可以查看到所有的aaa.txt文件版本记录。
那么我们怎么才能恢复删除的aaa.txt呢?我们只需要使用之前学过的git reset --hard b6b6877即可将仓库恢复成过去刚创建aaa.txt的版本了。这个时候执行ls命令发现aaa.txt文件又回来了。、
如果是先将文件提交到了本地库,然后执行了工作区中文件的删除,并且这条删除操作已经被提交到了暂存区,但是还没有提交到本地库,或者说删除操作只在工作区中进行了,没有提交到暂存区以及本地库,也同样是使用这条命令来进行文件的恢复。
如果我们在将新建的文件提交到了暂存区中,但是还没有将文件提交到版本库时,这个时候将工作区中的文件进行了删除,那么我们需要怎么进行文件的回复呢?我们可以使用gitcheckout filename命令恢复文件。如果文件已经提交到了暂存区以及本地库,删除了工作区中的文件,也可以通过git checkout filename来进行恢复。checkout 可以理解为“切回某个文件”的意思,但是要注意,这个命令是对工作区生效的。
总结:删除的文件需要被找回的前提是:在删除之前,把文件的状态成功提交到了本地库或者暂存区,如果在没有提交到本地库或者暂存区的情况下删除了,那么删除的文件就找不回来了。
4.比较文件的变化
我们可以通过git diff命令比较文件前后有没有发生变化。
比如我们aaa.text文件内容本来是test delete,现在我们通过vim命令修改为test compare,然后使用git diff aaa,txt来查看文件前后是否发生变化以及发生了什么变化。如果没有发生变化则无显示,否则会如同下图所示的输出,红色的-delete表示少了delete行内容,l绿色的+compare表示多了compare行内容。
如果我们将这个修改后的aaa.txt通过git add text.txt命令添加到暂存区再执行git diff aaa,txt我们会发现此时没有变化的输出,说明git diff aaa,txt是将工作区与暂存区中的文件进行比较的。
如果我们通过git diff HEAD aaa.txt命令将aaa.txt与当前版本的本地库进行比较,就又可以看到差异了。和历史版本比较同理,通过git diff HEAD^ aaa.txt或者git diff HEAD~n或者git diff 版本号执行。
假如我们此时同时只修改工作区中aaa.txt以及test.txt文件,并且 ,执行git diff命令不带文件名,这个时候就会列出所有前后不一致的文件已经不一致的内容。
同理,我们可以通过git diff HEAD或者git diff HEAD^或者git HEAD~n或者git diff 版本号将工作区中的文件和历史版本或者当前版本进行比较。
5.分支管理
关于分支的含义和好处等不再赘述
(1).如何查看有哪些分支
可通过git branch 来查看目前有哪些本地分支,由于没有创建过分支,因此只有初始的master分支
另外还有
git branch -v查看各个本地分支最后的提交信息‘
git branch -r 查看远程分支
git branch -a查看所有分支,包括远程分支和本地分支
(2).如何创建分支
可以通过git branch 分支名来创建分支,例如可以git branch study 分支。
也可以通过命令git checkout -b mybranch:创建并切换分支
(3).如何切换分支和合并分支
可以通过git checkout 分支名 切换分支,例如切换到study分支:
在切换了分支后,我们就可以在该分支上进行文件的修改,比如原本的aaa.txt内容如下,修改后在分支中提交,再次git branch -v可以发现study分支中的版本已经和master中的不一样了,是刚才提交的版本。而且你打开windows文件管理/f/git_project目录下的文件,随着分支的切换,文件的内容也发生了变化了。
假如现在是在开发过程中,我们觉得study分支的功能已经写好了,那么如何将分支study合并到master分支上呢?(或者将master合并到study都是可以的)
首先需要先将分支切换到需要被合并到的分支,也就是master分支上。然后执行merger命令合并分支,最后通过cat命令查看aaa.txt,发现文件内容已经变为分支中修改的内容。再通过git branch -v发现study和master分支的版本信息一致了。
(4)查看哪些分支合并入当前分支: $ git branch –merged
(5)查看哪些分支未合并入当前分支: $ git branch –no-merged
6.分支冲突
以上是大多数分支正常合并顺利的情况,但是实际开发过程中可能会出现合并冲突的情况。
首先,一个用git 写代码,而且只有一个本地分支的情况下是不会又冲突的.冲突可以说是两个分支的冲突.具体是两个已经提交的分支的相同文件相同位置的的不同操作进行了合并.。不会冲突的习惯是,修改文件之前先merge 别的分支.我在master 分支上创建并提交一个文件,切换到新的b分支上是没有这个文件的.这说明分支之间是相互独立的。通过git merge master 把master上新增的文件给merge 过来.这是不会又冲突的.0+x = x
如果我在新的分支b上把master 对应行的数据给改了,并提交.然后切换到master,我再git merge b -m ‘from b’ 也不会冲突,结果是被b分支的修改给覆盖了.。
如果我切换到分支b ,修改了master 原本那行的代码,提交.然后再切换到master.我不知道b修改了这个文件.我也修改这一行的代码,并提交.好了,“两个分支相同文件相同位置的的不同操作” 我这个时候就在master merge b 或在 b上merge master. 冲突!!! 不解决冲突是没法提交和切换到别的分支的. 反正这个冲突后的文件必须被改动一下.但是如果切换到了分支b修改了master原本的某一行的代码,提交,然后再切换到master,修改了master另一行的代码,并提交,这个时候是不会产生冲突的。
比如现在两个分支的aaa.txt内容是一致的,此时我们先切换到master分支修改aaa.txt并提交修改,然后切换到master修改aaa.txt的同一行,提交,然后将study分支合并到master分支。
这个时候上图就提示我们 Auto-merging aaa.txt conflict,:merge conflict in aaa,txt也就是在自动合并aaa.txt的时候出现了冲突,然后automatic merge failed自动合并失败了,需要我们fix conflicts and them commit the result修复冲突然后将结果提交。然后在最下面的命令行中Administrator@WSNEBUVA9YN3T19 MINGW64 /f/git_project (master|MERGING),括号中的不仅有分支名,还显示了当前处于分支正在合并的状态,如果不解决这个分支冲突,是无法切换到其他分支的。
那么如何解决分支冲突呢?
在svn中,当出现分支冲突时,会产生几个额外的文件,但是在git中则不会,我们打开产生冲突的文件,可以看到被插入了一些奇怪的字符。
我们需要和协同开发的人员进行沟通,看看如果解决双方冲突的内容,然后将aaa.txt文件进行修改到合适的内容。比如我们这样修改(注意特殊符号是没有用的,可以直接删除),直接以master分支的内容为修改结果,然后我们使用git status命令看看修改后文件的状态。可以看到提示有
You have unmerged paths.##你有未合并的路径
(fix conflicts and run "git commit")##修复冲突并运行git commit命令
(use "git merge --abort" to abort the merge)##或者使用git merge --abort来放弃合并分支
Unmerged paths:
(use "git add <file>..." to mark resolution)##使用git add <file>来标记为冲突已解决
both modified: aaa.txt ##冲突内容为两个分支同时修改了aaa.txt
no changes added to commit (use "git add" and/or "git commit -a")
于是我们就按照提示使用git add命令来标记一下已经解决冲突的aaa.txt,然后再用git status查看文件状态。发现此时提示all conflicts fixed but you are still merging所有的冲突都已经被修复,但是你仍然处在合并阶段。需要use git commit to conclude merge用git commit 来结束合并。但是注意这里的git commit提示后并没有带文件名,因此我们在git commit 时不能带文件名否则会报错,在这个master|MERGING状态下只能先解决冲突,完成合并才能提交其余文件,因此默认提交的是引起冲突的文件,所以不需要带文件名。我们通过git commit命令后就完成了分支的合并了,命令行状态也由master|MERGING变为了master。
三.git版本数据管理机制
对于每一次把修改提交到本地库以后产生的新的版本的历史记录,在不同的版本控制系统中由不同的策略来进行保存。在svn中集中式版本控制系统,每一个版本只是保存你所作的修改的那一部分,将来想要某一个历史版本的数据,就拿当前版本的修改和历史版本的修改以及你原始的文件,把他们拼到一块就获得了你想要的版本的数据。svn的这种叫做增量式的版本控制,不会去保存大量的重复的数据,可以很好的节省服务器空间。
而git版本控制系统的工作方式为快照流,每个版本其实是对我们本地库里、工作区里的所有目录所有文件去做一个快照并保存这个快照的索引,来保存成我们的版本。因此git可能会保存有大量重复的数据(不只保存修改了的数据,也保存没修改的数据)。但是如果对于没有修改的文件提交成为了新的版本,也就是文件的哈希值没有改变,git不会重寻存储这个文件,而是只保留一个链接指向之前存储的文件。
对于每一次的提交都会生成一个提交对象,它包含哈希值来唯一标识这次提交,通过git log可以查看到每一次提交的哈希值。然后提交对象呢会包含一个指向一颗树tree的结构的指针,这颗树也会有一个哈希值来唯一标识这棵树。对于每一次提交中形成的新版本中的每一个文件,都会有一个哈希值,这些哈希文件就构成了一个树的结构tree,这个tree包含了每一个哈希文件以及哈希文件的哈希值等信息,具体情况可以参考下图。
对于各个版本的数据的管理,是通过提交对象形成链条来实现的。每一个新建的版本对应的提交对象都会有一个指针指向前一个版本的提交对象。每一个提交对象通过哈希值区分以及判断是否做出了修改、是否是新的版本。
四.git的分支管理机制
当我们初始化本地库的时候,本身就会创建一个master分支,然后HEAD指针会指向master,表明当前位于master分支。如果我们需要创建一个新的分支,在svn中,会选择把文件和目录复制一套来形成新的分支。而在git中是去新建一个指针,去指向某一个版本而不是去复制一个新的版本来形成一个新的分支,节省了空间。由此可得新建的分支哦内容和master分支是一致的。当我们切换分支的时候,切换的是HEAD指针的指向。当某一个分支中提交了新的版本的时候,该分支的指针就去指向这个新的版本,同时HEAD指针仍然指向这个分支。因此git分支管理的本质是创建和移动指针。
但是之前学习的命令git reset HEAD命令来恢复版本数据,同样是HEAD指针,为什么这个命令中的HEAD说的又是指向版本数据呢?
关于HEAD指针的使用和底层可以看这个博主写的,非常详细而且利于理解。
http://www.zsythink.net/archives/3412/
五.远程仓库的使用
由于github服务器在国外,而码云gitee是国人开发的,服务器在国内,浏览速度会更快些,因此使用gitee作为远程仓库使用
1.gitee远程库的账号的注册 (略)
2.在gitee创建一个新的远程库
仓库相关概念说明如下:
仓库名称: 仓库的名称,用于仓库命名
归属:仓库归属账户,可以是个人账号/组织/企业中的一种,创建成功后该账户默认为仓库的拥有者(管理员)
路径:仓库的git访问路径,由用户个性地址+仓库路径名称组成。创建仓库后用户将通过该路径访问仓库。
仓库介绍:仓库的简单介绍
是否开源:设置仓库是否为公开仓库,公开仓库对所有人可见,私有仓库仅限仓库成员可见。
选择语言:仓库主要开发用的编程语言
添加.gitignore:系统默认提供的git忽略提交的文件模板,设置.gitignore后将默认忽略指定目录/文件到仓库(在使用Git的过程中,我们喜欢有的文件比如日志,临时文件,编译的中间文件等不要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交。)
添加开源许可证:如果仓库为公开仓库,可以添加设置仓库的开源协议,作为对当前项目仓库和衍生项目仓库许可约束,开源许可证决定了该开源项目是否对商业友好。
Readme:项目仓库自述文档,通常包含有软件的描述或使用的注意事项。
使用***模板文件初始化仓库:使用Issue或Pull Request文件模板初始化仓库
3.为了关联远程库,我们重新创建一个新的本地库。
我们在/f目录下创建一个gitee_repository目录,然后按照之前的方式初始化本地库。由于我们之前已经设置了全局,系统用户级别的签名,就不需要为仓库单独设置签名了,除非有需要。然后我们新建一个文件helloworld.txt文件,然后添加到暂存区,提交到本地库。
4.将本地库的helloword.txt文件推送到gitee仓库Java_Learning中
那么本地库到底需要将文件推送到哪里呢?我们需要明确推送的地址。在创建完gitee仓库后生成了指向gitee远程仓库的HTTPS链接,我们需要将这个链接保存在git本地,这样就不需要每次推送都输入链接了。具体做法是使用git remote add java https://gitee.com/Sunny_after_rain/Java_Learing.git来将链接保存到git中,其中git remove 为命令本身,java是我们为这个远程库创建的一个别名,后面跟着的是具体的链接。这样以后每次将本地库和远程库文件交互时就可以直接使用这个别名了,别名可以随意起。添加完,我们可以通过git remote -v查看我们添加的所有远程库的别名和链接。fetch表示这个地址用来取回文件的地址,push表创建完gitee仓库后生成的指向gitee远程仓库的HTTPS链接示这个地址用来推送。
明确了要推送的地址,我们就可以开始推送文件了。我们可以通过git push命令推送。 git push的一般形式为 git push <远程主机名> <本地分支名> <远程分支名> ,例如 git push origin master:refs/for/master (注意冒号后空一格,否则提交不会报错但是远程仓库看不到文件),即是将本地的master分支推送到远程主机origin上的对应master分支, origin 是远程主机名。
此处通过命令git push java master 推送。其中git push为命令本身,java为远程库链接别名,master为我们需要推送的分支。这时候会跳出需要登录用户名和密码,这个就是你注册gitee时的用户名和密码,输入后稍等一会儿就提交成功了。该命令此处是省略了远程分支名,只指明了本地分支名,如果远程分支被省略,如上则表示将本地分支推送到与之存在追踪关系的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。也就是如果我推送的分支远程仓库不存在,就会自动帮我们创建。这里说的远程追踪关系可以通过git branch -vv命令查看。如果希望建立远程追踪关系,也就是将当前分支对应起远程分支,可以通过git branch --set-upstream-to=origin/远程分支名 本地分支名 命令建立追踪关系,但是前提是本地和远程仓库都有这个分支才能建立,否则会报错说分支不存在。如果当前有分支希望推送到远程仓库,假如分支名为abc,如果我们使用git push --set-upstream origin abc或者git push,那么这个abc分支在推送到远程仓库的同时,也会为该本地分支和推送后创建的远程分支建立起追踪关系,或者使用git push -u <远程主机名> <本地分支名>命令在push时,本地指定分支就和远程主机的同名分支建立追踪关系。这样以后git push就可以不用带分支名了。取消跟踪可以通过命令git branch --unset-upstream master。如果远程仓库存在当前本地仓库不存在的分支该怎么办》git fetch能拉去到吗?
git push origin :如果当前分支与远程分支存在追踪关系,则本地分支和远程分支都可以省略,将当前分支推送到origin主机的对应分支,也就是如果不存在追踪关系,就必须指明哪个分支需要推送到哪个远程分支。
git push origin :refs/for/master 如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支,等同于 git push origin --delete master
git push:如果当前分支只有一个远程分支并且所有的分支和远程分支都建立起了追踪关系,那么主机名都可以省略,形如 git push,可以使用git branch -r ,查看远程的分支名。如果用git push指令时,存在分支没有跟踪远程分支(没有和远程分支建立联系),那么就会git就会报错。因为当前分支没有追踪远程指定的分支的话,当前分支指定的版本快照不知道要作为服务器哪一个分支的版本快照的子节点。简单来说就是:不知道要推送给哪一个分支.这种不带任何参数的git push,默认只推送当前分支,这叫做simple方式。此外,还有一种matching方式,会推送所有有对应的远程分支的本地分支。Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。如果要修改这个设置,可以采用git config命令。$ git config --global push.default matching或者$ git config --global push.default simple
git push --all origin 当遇到这种情况就是不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要 -all 选项
$ git push --force origin 如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。这时,如果你一定要推送,可以使用–force选项。 上面命令使用–force选项,结果导致远程主机上更新的版本被覆盖。除非你很确定要这样做,否则应该尽量避免使用–force选项。
5.克隆远程仓库
有时候对于一个在远程仓库看到的别人公开的项目,想要下载下来查看时,我们可以通过下载工具直接下载。但是下载工具只是把文件下载下来而已,缺失了原本git的很多功能,例如查看历史记录,更新新版本,pull request bug修复等等。而我们可以通过git的克隆功能,能比下载更简单的就把文件拉取到本地,而且克隆出来的就是一个完整的git本地仓库。
要使用克隆,我们可以在任何一个希望的目录去将这个远程仓库克隆到本地。比如我么你在/f目录下创建一个cloneTest目录,然后在其中执行git clone https://gitee.com/Sunny_after_rain/Java_Learing.git。其中git clone为命令本身,后面的地址就是某个远程仓库的地址,这里我们用回之前创建的远程仓库地址。这个地址和前面提到的一样,不过我们这次需要在远程仓库中点击“克隆/下载”来获取这个链接。命令执行完后,该目录下就会出现JavaLearing文件夹,该文件夹就已经被初始化,并且成为了一个完整的git本地仓库了,而不需要自自行去通过git init命令初始化该仓库。查看该目录内容就和之前创建的仓库内容是一样的。通过git reflog也可以查看到之前的提交记录,说明克隆确实不仅仅是把文件下载下来这么简单。git clone只能克隆远程库的master分支,无法克隆所有的分支。克隆所有分支请看
(1. 找一个干净目录,假设是git_work
2. cd git_work
3. git clone http://github.xxx.com/project/.git ,这样在git_work目录下得到一个project子目录
4. cd project
5. git branch -a,列出所有分支名称如下:
remotes
/origin/devremotes
/origin/release
6. git checkout -b dev origin/dev,作用是checkout远程的dev分支,在本地起名为dev分支,并切换到本地的dev分支
7. git checkout dev,切换回dev分支,并开始开发。)
当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支。 然而,如果你愿意的话可以设置其他的跟踪分支,或是一个在其他远程仓库上的跟踪分支,又或者不跟踪 master 分支。 最简单的实例就是像之前看到的那样,运行 git checkout -b /。 这是一个十分常用的操作所以 Git 提供了 --track 快捷方式:(这种应该是在当前分支使用这个命令就会为当前分支和指定的远程分支建立跟踪关系)
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch ‘serverfix’
由于这个操作太常用了,该捷径本身还有一个捷径。 如果你尝试检出的分支 (a) 不存在且 (b) 刚好只有一个名字与之匹配的远程分支,那么 Git 就会为你创建一个跟踪分支:
$ git checkout serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch ‘serverfix’
如果想要将本地分支与远程分支设置为不同的名字,你可以轻松地使用上一个命令增加一个不同名字的本地分支:
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch ‘sf’
现在,本地分支 sf 会自动从 origin/serverfix 拉取。
6.邀请别人成为团队成员
如果我们在上面克隆了别人的远程仓库到本地仓库后,我们希望在本地的仓库做了修改以后也能推送到别人的远程仓库,我们就需要远程仓库的所有者邀请我们加入他的团队,否则我们是无法git push代码上去的。比如我们先修改hellowold.txt。然后使用git push origin master将修改的文件尝试推送。注意这里的origin是我们拷贝的远程库链接的默认别名,当我们克隆一个远程库后,origin 是git为我们自动创建的默认的远程版本库名链接对应的别名。可以通过git remote -v查看到。当我们命令执行完后,会发现同样需要输入gitee的账号密码,注意,如果这时候我们输入之前创建的账号,用于本身就是仓库的创建者因此必然能push。因此我们需要创建另外的一个账号.在输入后,就会提示没有权限去执行push操作。
7.远程库修改的拉取
一旦远程主机的版本库有了更新(Git术语叫做commit),需要将这些更新取回本地,这时就要用到git fetch命令。
git fetch <远程主机名>命令将某个远程主机的更新,所有分支有(branch)的更新,全部取回本地,包括本地没有的分支,这些分支不是直接取回来合并的,而是一个以下面说到的 / 的形式的指针指向远程仓库存在的。如当前项目有很多人在参与,那么就会有很多分支,那么其他分支的提交也会拉取下来,你得等半天下下来,网速不好就蛋疼了。
git fetch <远程主机名> <分支名> 如果只想取回特定分支的更新,可以指定分支。比如,取回origin主机的master分支,git fetch origin master。也可以一下子指定多个需要拉取的分支,例如git fetch origin master abc。
几种使用方式归纳如下:
关于git fetch不带参数,我的理解是,不需要像git push那样必须要设置好分支的追踪关系也没有必要,因为push是需要分支对应分支push到远程仓库进行合并的,而git fetch则是将远程仓库所有的更新取回来,但不需要合并,是看我们自己需要选择合并的。
注意git中有本地分支和远程分支的概念,本地分支就是我们可以操作的分支,就是普通分支,而远程分支以 / 的形式命名,是你无法移动的本地引用。例如origin/master,它是名为“origin” 的远程分支的本地副本,名为“master”.一旦你进行了网络通信, Git 就会为你移动它们以精确反映远程仓库的状态。请将它们看做书签, 这样可以提醒你该分支在远程仓库中的位置就是你最后一次连接到它们的位置,也就是说如果你不去git fetch或者git pull它是不会发生改变的。 例如,如果你想要看你最后一次与远程仓库 origin 通信时 master 分支的状态,你可以查看 origin/master 分支。 你与同事合作解决一个问题并且他们推送了一个 iss53 分支,你可能有自己的本地 iss53 分支, 然而在服务器上的分支会以 origin/iss53 来表示。
git fetch命令只是从远程的origin的master主分支下载最新的版本到origin/master远程分支上,不会自动合并到本地分支上。比如我们git fetch了发生了变化的远程分分支后,我们cat本地的工作文件是没有发生变化的。这样的好处是我们不必急于直接将远程库的文件合并到本地分支上,我们可以先查看拉取的文件内容来确定是否将更新merge到当前分支。取回的更新,在本地主机上要用"远程主机名/分支名"的形式读取。比如origin主机的master,就要用origin/master读取。例如在git fetch后,我们可以使用git checkout origin/master切换到本地远程分支,然后再去zat查看里面的文件,这个时候文件的内容就和原本的不一样了。
那么我们怎么将从远程仓库取回的数据合并或者说放到我们的本地库呢?
我们可以执行 git merge origin/serverfix 将这些远程分支的内容合并到当前所在的分支比如当前处在本地分支master,这个origin/serverfix就会合并到master分支。另外我们也可以同时将多个分支合并到当前分支,例如git merge origin/master hotfix-2275 hotfix-2276 hotfix-2290。
也可以通过执行git checkout -b serverfix origin/serverfix取远程分支并分化一个一个新的,本地不存在的本地分支,也就是从一个远程跟踪分支检出一个本地分支,而这个新本地分支的内容就是拉取的远程分支的内容,这个命令同时还会自动建立远程跟踪关系,会提示Branch serverfix set up to track remote branch serverfix from origin.Switched to a new branch ‘serverfix’。其中git checkout -b的意思是创建并且跳转到新建的分支。(注意,如果在某个本地分支上直接使用git checkout -b 新分支名,则表示创建一个新的分支,这个分支的内容为当前本地分支)。
实际上,git fetch在取回更新后,会返回一个FETCH_HEAD ,它是一个版本链接,记录在本地的一个文件中(具体为.git/FETCH_HEAD文件),指的是某个branch在服务器上的最新状态,其中每一行对应于远程服务器的一个分支,指向着目前已经从远程仓库取下来的分支的末端版本,我们可以在本地通过它查看刚取回的更新信息。当前分支指向的FETCH_HEAD, 就是这个文件第一行对应的那个分支.一般来说, 存在两种情况:如果没有显式的指定远程分支, 则远程分支的master将作为默认的FETCH_HEAD(例如git fetch);如果指定了远程分支, 就将这个远程分支作为FETCH_HEAD(例如git fetch abc)。我们如果git fetch某个远程分支,那么这个文件中就记录这这个远程分支拉取的一些最新消息;如果git fetch整个远程主机,那么这个文件就记录着拉取的所有远程的分支的最新消息。可以自行打开这个文件查看一下。我们可以使用git log -p FETCH_HEAD来比较本地的仓库和远程仓库的区别,或者单纯使用git log -p 分支名来比较本地分支和远程分支的区别,命令后可跟多个分支名,命令执行后返回的信息包括更新的文件名,更新的作者和时间,以及更新的代码,我们可以通过这些信息来判断是否产生冲突,以确定是否将更新merge到当前分支。另外,FETCH_HEAD还有一个用处就是可以使用命令git merge FETCH_HEAD //将拉取下来的最新内容合并到当前所在的分支中。
其实,想要拉去远程库的修改,还有更简便的方法,不过这种方式虽然也常用,但不推荐。这种方式就是使用git pull命令。
git pull命令的作用是,取回远程主机某个分支的更新,再与本地的指定分支合并。它的完整格式稍稍有点复杂。实质上,这等同于先做git fetch,再做git merge。
比如,取回origin主机的next分支,与本地的master分支合并,需要写成下面这样
$ git pull <远程主机名> <远程分支名>:<本地分支名>,例如$ git pull origin next:master,如果远程分支是与当前分支合并,则冒号后面的部分可以省略“:$ git pull origin next上面命令表示,取回origin/next分支,再与当前分支合并。实质上,这等同于先做git fetch,再做git merge。$ git fetch origin next----> $ git merge origin/next或者git fetch origin next//从远程主机的next分支拉取最新内容
git merge FETCH_HEAD //将拉取下来的最新内容合并到当前所在的分支中
如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名。$ git pull origin。上面命令表示,本地的当前分支自动与对应的origin主机”追踪分支”(remote-tracking branch)进行合并。如果当前分支只有一个追踪分支,连远程主机名都可以省略:$ git pull,也就是一个本地分支可能会跟多个远程分支建立追踪关系,如果一个本地分支只和一个远程分支建立起追踪关系,则可以在这个分支上直接git pull。似乎git pull 命令必须在对应分支中进行pull当前分支对应的远程分支的内容,不能直接也为其他分支pull对应远程分支的内容。
另外,如果远程主机删除了某个分支,默认情况下,git pull 不会在拉取远程分支的时候,删除对应的本地分支。这是为了防止,由于其他人操作了远程主机,导致git pull不知不觉删除了本地分支。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。