Git的工作原理
每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一链接。
Git中的状态
对于任何一个文件,在 Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)
.git 目录,它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库时,实际拷贝的就是这个目录里面的数据。
基本的 Git 工作流程如下:
1、在工作目录中修改某些文件
2、对修改后的文件进行快照,然后保存到暂存区域。(所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中)
3、提交更新,将保存在暂存区域的文件的文件快照永久转储到 Git 目录中。
Git的配置
1、/etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。
2、~/.gitconfig 文件: 用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。
3、当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。
配置用户信息:
$ git config --global user.name "gcx"$ git config --global user.email xxx@163.com
在工作目录中初始化新仓库
$ git init$ git add$ git commit -m "description"
“git add” 是一个多功能命令,根据目标文件的状态不同,此命令的效果也不同:
-
可以用它开始跟踪新文件
-
把已跟踪的文件放到暂存区
-
合并时把有冲突的文件标记为已解决状态等
仓库的克隆
git clone url
克隆仓库,初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。
在克隆仓库时,Git 通常会自动创建一个名为master 的分支来跟踪 origin/master 。这正是 git push 和 git pull 一开始就能正常工作的原因。
例:git clone
git://github.com/sch/grit.git
mygrit # 指定项目的名字
git status #确定哪些文件当前处于什么状态
git add README # 开始跟踪一个新文件 README ,也可以是一个目录。
其实 git add 的潜台词就是把目标文件快照放入暂存区域,也就是 add file into staged area,同时未曾跟踪过的文件标记为需要跟踪。
可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。
运行了 git add 之后又做了修订的文件,需要重新运行 git add 把最新版本重新暂存起来。
git diff # 查看已暂存和未暂存的更新
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异
git diff --cached # 已经暂存起来的文件和上次提交时的快照之间的差异
给 git commit 加上 -a 选项,Git 就会自动把所有已跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:git commit -a -m "add new content"
从 git 中移除文件
1.手工从目录中删除文件
2.运行 git rm filename
3.然后进行“提交”
注意:如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域删除),但仍然希望保留在当前工作目录中,即仅仅是从跟踪清单中删除
可以运行:git rm --cached readme.txt
在 Git 中对文件改名
git mv oldname newname
运行 git mv 就相当于运行了下面三条命令:
mv README.txt README
git rm README.txt
git add README
查看提交历史
git log 选项
-p :展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新,--stat,仅显示简要的增改行数统计。如:git log -p -2
git log --pretty=format:"%h -%an, %ar : %s"
format 可以定制要显示的记录格式
%h 提交对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 --date=选项定制格式)
%ar 作者的修订日期,按多久以前的方式显示
%cn 提交者的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
其中作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人
当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。
例:
git log --since=2.weeks
#列出所有最近两周内的提交
你可以给出各种时间格式,比如具体的某一天(“2020-12-26”)或者是多久以前(“2 years 1day 3 minutes ago”
)
-num 仅显示最近的n条提交,如 :-2
--since ,--after 仅显示指定时间之后的提交
--until,--before 仅显示指定时间之前的提交
--author 仅显示指定作者相关的提交
--path 仅显示指定路径下文件的相关提交,是一个放在最后位置上的选项 ,如 :-- home/
例:git log --pretty="%h - %s" --author=gitster --since="2020-12-01" --before="2021-01-01" -- t/
2020年12月期间,t 目录下的提交
撤销最近一次的提交重新提交
git commit --amend
有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤销刚才的提交操作,可以使用 --amend 选项重新提交:
如果刚才提交完没有作任何改动,直接运行此命令的话,相当于有机会重新编辑提交说明
如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交:
例:
$ git commit -m "initial commit"$ git add forgotten_file$ git commit --amend
上面的三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。
取消文件的暂存
可以使用 git reset HEAD <file> 的方式取消暂存
例: git reset HEAD test.txt
撤销工作目录中对文件的修改
未暂存:git checkout -- <file>
例: git checkout -- filename
远程仓库的查看
git remote # 查看当前配置有哪些远程仓库
git remote -v # 显示远程仓库以及对应的克隆地址
添加远程仓库
git remote add [name] [url] # 添加新的远程仓库
例:git remote add pb
git://github.com/
从远程仓库抓取数据
①:git fetch [remote-name]
从远程仓库抓取数据到本地:此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。
fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,fetch 操作不会给你一个新的 serverfix 分支,有的只是一个你无法移动的 origin/serverfix 指针。
如果要把该远程分支的内容合并到当前分支,可以运行 git merge origin/serverfix
如果想要一份自己的 serverfix 来开发,可以在远程分支的基础上分化出一个新的分支来:
git checkout -b serverfix origin/serverfix
②:git pull
如果设置了某个分支用来跟踪某个远端仓库的分支,可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支
推送数据到远程仓库
git push [remote-name][branch-name]
注;只有在所克隆的服务器上有写权限,或者同一时刻没有其他人在推数据,这条命令才会如期完成任务。如果在你推数据之前,已经有其他人推送了若干更新,那你的推送操作就会被驳回。你必须先把他们的更新抓取到本地,合并到自己的项目中,然后才可以再次推送。
查看远程仓库详细信息
git remote show [remote-name] # 查看某个远程仓库的详细信息
从上到下它给出了下面的信息:
1、远程仓库的 url 地址
2、如果你是在 issues 分支,就可以用 git pull 命令抓取数据合并到本地。
3、如果你是在 master 分支,就可以用 git pull 命令抓取数据合并到本地。
4、New remote branches:有哪些远程分支还没有同步到本地
5、Stale(过时) tracking branches:哪些已同步到本地的远端分支在远端服务器上已被删除
6、Tracked remote branches :列出了所有处于跟踪状态中的远端分支
7、最后两行:告诉我们运行 git push 时缺省推送的分支是什么
远程仓库的重命名
git remote rename [oldname][newname] # 修改某个远程仓库在本地的简称,比如想要把 pb 改成 paul
例:git remote rename pb paul
对远程仓库的重命名,也会使对应的分支名称发生变化,原来的 pb/master 分支现在变成了 paul/master。
删除远程仓库
git remote rm 远程名 # 需要移除对应的远端仓库
标签管理
列出现有标签的命令非常简单,直接运行 git tag
用特定的搜索模式列出符合条件的标签 :git tag -l 'v1.4.4.*'
新建标签
git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)
轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。
含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GPG 来签署或验证。
git tag -a v1.4 -m "my version 1.4" #新建附注型标签
git tag v1.4-1 # 新建轻量级标签
可以使用 git show 命令查看相应标签的版本信息,并连同显示打标签时的提交对象。
分支操作
暂存操作会对每一个文件计算校验和(即SHA-1字符串),然后把当前版本的文件快照保存到 git 仓库中(git使用 blob 类型的对象存储这些快照),并将校验和加入暂存区。
git commit 新建一个提交对象前,git 会先计算每一个子目录的校验和,然后在 git 仓库中将这些目录保存为树(tree)对象
git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。git 会使用 master 作为分支的默认名字。
补充:
在 Git 中用三种对象来实现对项目文件的跟踪,分别是: blob 、 tree 和 commit。
其中 blob 对象存储的是文件快照(内容)
tree 对象存储的是每个目录下有哪些 blob 对象( 保存着指向 blob 对象的指针 )
commit 对象保存着提交信息,指向父提交对象的指针和指向 tree 对象的指针。
最终用户通过 commit 对象(即分支)即可恢复原来的版本。
创建分支
git branch testing
# 新建一个 testing 分支,运行 git branch 命令,仅仅是建立了一个新的分支,但不会自动切换到这个分支中去。
git 又是如何创建一个新的分支的呢?答案很简单,创建一个新的分支指针
git 是如何知道你当前在哪个分支上工作呢?其实答案也很简单,它保存着一个名为 HEAD 的特别指针(HEAD只是一个文件,里面存储着 commit 对象的校验和)。
切换工作分支
git checkout testing # 切换到 testing 分支,这条命令做了两件事,它把 HEAD 指针移到 testing 分支,并把工作目录中的文件换成了 testing 分支所指向的快照内容。
分支的新建与切换
git checkout -b iss53
# 新建并切换到 iss53 分支,相当于执行:git branch iss53 、git checkout iss53
git checkout -b serverfix origin/serverfix
# 这会切换到新建的 serverfix 本地分支,其内容同远程分支 origin/serverfix 一致,
切换分支的时候最好保持一个清洁的工作区域,否则会切换失败
分支的删除
git branch -d hotfix # 删除 hotfix 分支
简单的分支合并
把 hotfix 分支合并到 master 分支
①:git checkout master # 切换到 master 分支
②:git merge hotfix # 合并 hotfix 分支
合并前:
合并后
换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解决的分歧,所以这种合并过程可以称为快进( Fast forward )。
复杂分支的合并
把 iss53 分支并入 master 分支
如果在不同的分支中都修改了同一个文件的同一部分,git 就无法干净地把两者合到一起。
git 作了合并,但没有提交,它会停下来等你解决冲突。要看看哪些文件在合并时发生冲突,可以用 git status
任何包含未解决冲突的文件都会以 “未合并(unmerged)”的状态列出
同时 git 会在有冲突的文件里加入标准的冲突解决标记
手动解决冲突后,运行 git add 可以把他们标记为已解决的状态
分支的管理
git branch
不加任何参数,它会给出当前所有分支的清单,前面有 * 字符的表示当前所处的分支
git branch -v
查看各个分支最后一个提交对象的信息
git branch --merged
从分支清单中筛选出已经与当前分支合并的分支,也就是说哪些分支是当前分支的直接上游。
git branch --no-merged
查看尚未合并到当前分支的工作分支
对于未合并的分支简单地用 git branch -d 删除该分支会提示错误,因为那样做会丢失数据,如果确实想要删除可以使用 -D
远程分支
远程分支(remote branch)是对远程仓库中的分支的索引,它们是一些无法移动的本地分支;只有在 git 进行网络交互时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置,我们用(远程仓库名)/(分支名)这样的形式表示远程分支。
一次 git 克隆会建立你自己的本地分支 master 和远程分支 origin/master ,并且将他们都指向 origin 上的 master 分支。
可以运行 git fetch origin 来同步远程服务器上的数据到本地。该命令首先找到 origin 是哪个服务器,从上面获取你尚未拥有的数据,更新你本地的数据库,然后把 origin/master 的指针移到它最新的位置上。
推送本地分支
git push [远程仓库名] [分支名]
git push origin serverfix 或者 git push origin serverfix:serverfix
它的意思是,上传我本地的 serverfix 分支到远程仓库去,仍旧称它为 serverfix 分支
通过此语法,你可以把本地分支推送到某个命名不同的远程分支
git push origin serverfix:newbranch
跟踪远程分支
从远程分支 checkout 出来的本地分支,称为跟踪分支(tracking branch)。跟踪分支是一种和某个远程分支有直接联系的本地分支
在跟踪分支里输入 git push ,Git 会自行推断应该向哪个服务器的哪个分支推送数据,在这些分支里运行 git pull 会获取所有远程索引,并 把他们的数据都合并到本地分支中来。
新建跟踪分支
git checkout -b [分支名] [远程名] /[分支名]
1.6.2以上版本的 Git ,还可以用 --track 选项简化
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix
Switched to a new branch "serverfix"
删除远程分支
git push [远程名] :[分支名]
git push origin :serverfix
有种方便记忆这条命令的方法:记住我们提到过的 git push [远程名] [本地分支][远程分支] 语法,如果省略 [本地分支],那就等于是在说“在这里提取空白然后把它变成[远程分支]”。