以下Git
学习内容,来自官方文档,本文整理学习过程笔记。
Git
鼓励在工作流程中频繁地使用分支与合并。Git
创建分支,只创建了一个可以移动的新指针(指向包含所指对象校验和的文件),HEAD
指针始终指向当前的工作分支。修改当前分支,也仅仅是更改HEAD指针的值,使其指向新分支。
Git分支操作
- 查看当前分支:
git branch
,当前分支前有*
号 - 删除本地
hotfix
分支,git branch -d hotfix
- 删除远程
serverfix
分支:git push origin --delete serverfix
- 查看哪些分支已经合并到当前分支:
git branch --merged
- 查看未合并到当前分支的分支:
git branch --no-merged
- 切换到
master
分支:git checkout master
- 合并
pro
分支到master
分支:git checkout master
、git merge pro
。- 默认使用
Fast Forward
模式合并,不会产生新的commitId
。为保证合并数据的完整性,可通过--no-ff
选项,禁用Fast Forward
模式合并,禁用后合并会生成新的commitId
- 默认使用
- 将本地serverfix分支推送到远程origin仓库中,更新远程仓库的serverfix分支(如果没有会新建该分支):
git push origin serverfix
分支冲突解决
查看有哪些冲突文件:
git status
,查看处于 unmerged paths
状态的文件。Git
会在有冲突的文件中加入标准的冲突解决标记,需要手动打开这些文件来解决冲突。冲突标记实例如下:
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
HEAD
为当前分支的版本,=====
区段的上半部分至HEAD
的内容是当前分支中有冲突的内容,区段的下半部分到iss53
,是iss53
分支中有冲突的内容。为了解决该冲突,必须手动选择使用哪一部分。
在解决完所有文件的冲突后,对每个文件使用git add
来将其标记为冲突已解决。最后,输入 git commit
来完成合并后的提交。
线上Bug处理流程 ###
在基于master
分支进行开发,如遇到线上版本的Bug
,需切换到线上版本分支时,可通过暂存或者修复本地提交的方法保存当前修改内容,然后切换回master
分支,新建hotfix
分支来处理线上问题。处理完毕后,合并hostfix
分支内容到master
分支,然后删除hotfix
分支。
分支切换注意事项
在切换分支时,一定要注意你工作目录的文件会被改变。如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。如果Git
不能干净利落地完成这个认为,它将禁止切换分支,一般会有如下提示:
`Your local changes to the following files would be overwritten by merge`.
针对此问题,有以下两种解决方法:
方法1:保留本地提交修改
1. 暂存当前任务,再进行操作。
* git stash // 暂存当前工作
* xxxx
* git stash pop // 恢复之前工作
2. 将修改提交到本地仓库
* git add .
* git commit -m "message"
方法2:舍弃本地修改
1. 清除未跟踪文件
* git clean -n // 清除文件预览,先确认
* git clean -f // 强制清除文件
变基操作
提取另一分支的修改,在master
分支上应用一次,这种操作叫做变基(rebase
)。该命令可将提交到某一分支上的所有修改都移动到另一分支上,就好像重新播放
一样。操作流程如下:
- 切换到开发分支:
git checkout experiment
- 将该分支的改动移动到
master
分支:git rebase master
- 切换到
mater
分支:git checkout master
- 进行快速合并:
git merge experiment
查看经过变基操作的提交会发现,尽管是并行开发,但是提交历史是一条直线,没有分叉。
适用场景:为了确保在向远程分支推送时,保持提交历史的整洁。
例如:向其他人项目贡献代码时,你先在自己的分支里进行开发,当开发完成时,你需要下将你的代码变基到 origin/master
上,然后再向主项目提交修改,这样的话,该项目的维护者就不需要进行整合工作,只需要快进合并即可。
风险:如果提交存在于你的仓库之外代码,而别人可能基于这些提交进行开发,那么不要执行变基。
远程仓库
git clone
:默认克隆远程仓库中每个文件的所有历史信息,自动设置本地master
分支跟踪远程仓库的master
分支,添加远程仓库(缩写为origin
)关联。
查看本地已配置的远程仓库服务器: git remote -v
,显示本地关联的远程仓库地址以及简写。
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
origin https://github.com/schacon/ticgit (push)
查看远程仓库的详细信息: git remote show <remote>
git fetch
只会将数据下载到你本地仓库,不会自动合并或修改你当前的工作。
如果你的当前分支设置了跟踪远程分支,那么可以用git pull
来抓取并合并远程分支修改到当前分支,即git pull = git fecth + git merge
推送本地修改到远程仓库
当你想分享你的项目时,必须将其推送到远程仓库,可使用 git push <remote> <branch>
语法。
git push origin master,推送本地master分支到origin仓库的master分支。
开源项目协作
以项目贡献者视角:
- 克隆远程主仓库:
git clone <url>
- 新建特性分支,并在此分支上工作:
git checkout -b featureA
- 当此分支上的工作完成后,去原始项目中点击
Fork
按钮,创建一份自己仓库的派生项目。 - 将本地仓库关联上3)中的远端仓库:
git remote add myFork <url>
。其中,myFork
是远端仓库的简写,url
为自己fork
下来的远端仓库地址 - 推送本地分支的修改到远端仓库。
git push -u myFork featureA
- 向原项目的维护者发出拉取请求(
Pull Request
)
如果远端分支已经有更新,为了减少维护者合并过程中的冲突,可将源版本库添加一个新的远端,并从远端抓取内容,合并主分支的内容到开发分支中,修复所有问题并最终重新推送回你提交拉取请求使用的分支。
- 添加远程上游仓库:
git remote add upstream <url>
- 获取远程上游仓库的master分支内容:
git fetch upstream
- 合并远程master分支内容到本地master分支:
git checkout master
git merge upstream/master
- 修复产生的冲突
- 再推送回同一个分支。
git push origin master
以项目维护者视角:
- 在本地仓库添加贡献者(Jessica)的远程分支,
git remote add Jessica git://github.com/jessica/myproject.git
- 拉取远程实现。
git fetch jessica
- 在本地新建分支,并拉取对应分支功能:
- git checkout -b rubyclient jessica/ruby-client。将远程仓库下的
ruby-client
分支内容提取到本地rubyclient
分支。
- git checkout -b rubyclient jessica/ruby-client。将远程仓库下的
- 查看贡献分支与本地分支的差异。
git diff master...rubyclient
其他项目贡献者视角:
- 使用
git fetch origin
,拉取远程修改 - 同步远程修改
- 方法一: 将上游工作合并到当前所在分支:
git merge origin/serverfix
- 方法二:本地新建分支并跟踪远程分支:
git checkout -b servierfix origin/serverfix
。- 该操作很常用,因此,Git提供
git checkout --track origin/serverfix
来简化,更近一步,如果本地无同名分支,且想创建一个与远程分支名字相同的分支,直接git checkout serverfix
即可完成。
- 该操作很常用,因此,Git提供
- 方法一: 将上游工作合并到当前所在分支:
让GitHub公共仓库保持更新
- 切换到master分支:
git checkout master
- 拉起最新项目分支内容:
git pull https://github.com/progit/progit2.git
- 将本地最新master分支内容推送到远端的fork仓库:
git push origin master
上面是从指定远端仓库抓取内容到本地,然后推送修改到GitHub远程仓库。可通过如下设置来简化流程:
- 添加远程仓库并起一个别名
progit
:git remote add progit https://github.com/progit/progit2.git
- 设置本地
master
分支对应的上游仓库信息:git branch --set-upstream-to=progit/master master
- 将默认推送操作的远程仓库信息:
git config --local remote.pushDefault origin
经过上述设置,以后可这样更新:
git checkout master
git pull
: 从源项目仓库拉取更新git push
:将此更新推送到远程fork项目中。
Git搜索工具
git grep -n key_word
:输出Git找到的匹配key_word的行号
想知道某个标示符是什么时候存在的或引入的。
git log -S ZLIB_BUF_MAX --oneline
: 显示新增和删除ZLIB_BUF_MAX
的提交记录
行日志搜索,展示代码中一行或者一个函数的提交历史。例如:查看zlib.c文件中 git_deflate_bound
函数的每一次变更,执行:
- git log -L :git_deflate_bound:zlib.c
Git内部原理
Git
是一个内容寻址文件系统,核心是一个可存储任意数据的键值对数据库。键由数据内容加上特定头部信息计算SHA-1的来的。
在.git
隐藏目录中,有以下重要的文件和目录:
HEAD
文件:指向目前被检出的分支index
文件:保存暂存区信息objects
目录:存储所有数据内容,数据内容校验和的前两个字符用于命名子目录,余下的字符用作文件名。refs
目录:存储指向数据的提交对象的指针
Git
中的数据在内部以数据对象(blob object
)存在,目录以树对象(tree object
)形式存在。
Git
在每一次提交时,会生成提交对象(commit object
),该对象记录提交的快照信息以及父提交对象,这样,可通过最新提交对象往前回溯,寻找提交历史。
上层运行git add
和git commit
时,Git
实际做以下操作:
- 将被改写的文件保存为数据对象,更新暂存区
- 记录树对象
- 创建一个提交对象(包含顶层树对象和本次提交的父提交对象)。
Git
中数据内容的头部结构为:数据格式、空格、是数据内容字节数和一个空字节,以下为一个简单的例子:
"blob 16u0000"
Git
中有三种主要的数据格式:
- blob: 数据内容对象
- tree: 树对象
- commit:提交对象
git gc
命令会收集所有松散对象,并将它们放置到包文件中,将多个包文件合并为一个大的包文件,移除与任何提交都不相关的陈旧对象。