纸上得来终觉浅,绝知此事要躬行
引言
来到新的公司,第一件事当然是配环境这个毋庸置疑。小组内开发新项目,需要多人合作共同完成,第一件就是找一个好用的版本控制软件。版本控制软件就不在这里介绍了,Git、SVN其实都是有学过和用过的。但是问题依旧存在,那就是之前不管是跟同学组队开发项目或者是在github使用Git,还是在之前那个公司实习的时候用SVN,都只是轻度的使用,而且操作及其不规范,只是做到了提交代码,并没有做到很好的进行版本控制。
因此,在刚来这里的时候,就在提交代码的问题上出现了很大的问题,就是对于规范的流程和操作不熟悉,同时也因为不是自己一个人的项目,不敢放开手脚尝试各种命令的实际效果,只能去询问别人如何解决,所以出现问题不仅处理效率极低,而且也很麻烦别人,结果不好。
好在最困难的日子都已经过去,坑也踩了个七七八八,因为以前有过明明知道这个问题以前遇到过却死活想不起来以前是怎么解决的经历,加上也零零散散地记了不少笔记。就想着抽时间出来把各处的记录整合一下,一来是知识能够系统一点,二来是方便未来忘了解决方案的自己。
学习笔记
Git相比于SVN的优势
-
分布式
SVN是程序集中存放在服务器中,开发下载代码到自己电脑上。git是将完整的版本库存放在各个用户的电脑上,去中心化。
好处就是查看版本记录不受网络的影响,不会因为svn服务器影响自己业务的开发或者回滚进度。 -
存储方式
svn等这些存储数据,主要是通过文件变更列表的方式来存储信息,将保存的信息看做是一组基本文件和每个文件随时间逐步累积的差异。Git采用的是把数据看作是对小型文件系统的一组快照。对保存项目时的全部文件制作一个快照,并保存快照索引。
git有一个元数据,就是.git文件夹里的信息。关于与版本控制有关的信息都在这里面集中存放。
别人的文章里提到svn是每个文件都有一个.svn
文件,管理起来麻烦,并且目录文件忽略的时候设置起来相当麻烦,这个有体会,而在git里只需要维护一个.gitignore
的文件就行了,方便不少。在.gitignore
文件里只要列出要忽略的文件模式,可以过滤掉不需要跟踪的文件
分支简介
之前说过git保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。
在进行提交操作时,git会保存一个提交对象(commit object),该提交对象会包含一个指向暂存内容快照的指针,同时还有一些提交信息和指向父对象的指针。
git仓库中的对象分为三类,一个是提交对象,包含指向前述树对象的指针和提交信息;一种是树对象,记录着目录结构和blob对象索引;blob对象,一般有多个,保存着文件快照
(所以其实git的管理就是创建了一个提交信息的链表)
所以git其实只是管理的提交对象以及其内部存储的文件信息和提交信息,所谓不同的分支其实就是指向不同提交对象节点的一些指针而已。
HEAD指向当前所在的分支,相当于一个分支的别名。指向会在checkout
时修改。
分支管理
-
master
远端的master分支是每个使用者最熟悉的, -
develop
这个分支作为主要的开发分支,被称作集成分支。平行于master分支,当develop这个分支达到稳定,并且准备好发版的时候,就合并到master分支上去,并标注好发布的版本号。 -
feature
这个分支应该是从develop这个分支分出来的,这个是开发新功能所开的分支。当开发一个新功能时,这个功能的目标版本可能是未知的。feature这个分支的精髓在于会和这个功能的开发周期一样长,但最终会合并到develop里或者是被丢弃。这个一般只在开发者的repo里存在,不会放进origin里
Git基本操作
- 常用命令
git branch -r #查看远程分支
git branch -a #查看所有分支
git branch #查看本地分支,后面带有参数的话可以创建本地分支
git branch -vv #这个是查看本地分支与远程分支的映射关系
git checkout #带有参数的话可以切换到那个分支
git checkout -b dev origin/develop #新建一个本地分支dev,与远程分支develop建立映射关系,并切换工作分支到这个分支。
Switched to a new branch 'dev' # Branch 'dev' set up to track remote branch 'develop' from 'origin'.(执行后控制台的输出)
git pull #拉取当前分支代码
git push
git commit -amend #撤销操作
git add * # 多功能命令 跟踪新文件/把已跟踪文件放到暂存区/合并时将有冲突的文件标记位已解决状态
# 对于这个命令的含义应该理解为“添加内容到下一次提交中”
# 如果修改了一个已被跟踪的文件,使用status会出现changes not staged for commit
# 这说明跟踪的文件已经发生了变化,但是没有放进暂存区
# 查看哪些文件处于什么状态
# 主要是显示上次提交过后的修改,以及当前分支与对应远程分支之前的偏离
git status
git diff # 查看尚未暂存的文件更新了那些部分
git diff --cached
git diff --staged # 这两个命令都是查看已暂存的将要添加到下次提交里的内容,后者需要高一些的版本而已
git commit # 这种提交方式会启动文本编辑器输入本次提交的说明
git commit -m # 会将提交命令放在同一行
git commit -a # 直接把所有已经跟踪过的文件暂存起了一并提交
git rm # 从已跟踪文件清单(暂存区域)和工作目录中删除指定的文件
# 如果只是从工作目录中收工删除文件,会在git status时出现 changes mot staged for commit
git rm -f # -f = force 删除之前如果放到暂存区的话应使用这个,防止误删没有添加到快照的数据
git log # 查看提交历史(这个用命令行太麻烦了,不需要)
git reset # 用来取消暂存
git push (origin) (branch) # 将自己的项目推送到远端分支
untracked 未跟踪,意思是git在之前的提交中没有这些文件,git不会自动将文件纳入跟踪范围,这样可以保证自动生成的二进制文件或者缓存文件包含进来(比如python会自动生成的__pycache__文件)
- feature分支的创建与合并
## 合并可以先考虑合并到本地dev,再把本地dev推到远端
# creating a feature branch
git checkout -b myfeature develop
# Switched to a new branch "myfeature"
# merge
git checkout develop
# Swritched to branch 'develop'
git merge --no-ff myfeature
git branch -d myfeature
git push origin develop
- release分支从develop分支的创建
git checkout -b release-1.2 develop
# Switched to a new branch 'release-1.2'
./bump-version.sh 1.2
# Files modified successfully,version bumped 1.2.
$ git commit -a -m "Bumped version number to 1.2"
# [release-1.2 ...] Bumped version number to 1.2
# 1 files changed, 1 insertions(+), 1 deletion(-)
踩过的坑
git push --set-upstream origin feature-htmlReport
推本地分支到远端(在远端没有这个分支的情况下使用)
git push (remote) (branch)
- 已经commit的会在push的时候全部带上,说白了一个是提交一个是推到远端
git push origin ...
和git pull origin...
分别是拉到本地和推到远端,推到远端没有用过,但是推之前很明显应该先拉到本地检查冲突,避免把别人提交的代码覆盖了,而pull之前又需要stash本地的代码,那就是要用到commit了
untracked files
问题
git status # 用这个命令查看当前的git状态,根据这个状态来寻找解决办法,很好用
# 问题是会在控制台显示changes not staged for commit,以及untracked files
# 首先删除跟踪
git rm logs/\*.git
# 发现error,因为the following files have local modifications
(use --cached to keep the file, or -f to force removal)
git rm -f logs/\*.log
# 之后再尝试pull
git pull origin develop
# 依旧出现error,The following untracked working tree files would be overwritten by merge: .... Please move or remove them before you merge.
# 在StackOverflow上找到的解决办法
git add ....(相关文件)
git stash
git pull
(之后就是解决冲突了)
git push
(结束)
出现问题的原因,本地的.gitignore
和其他分支上的不同,导致pull到我本地的时候出现。
由于本地没有追踪这个文件,但是远程追踪了,所以pull会强制覆盖本地没有进入版本控制下的文件。
git fetch
从服务器上抓取本地没有的数据时,不会修改工作目录,而是让你自己合并。
git pull
相当于git fetch
加上git merge
,所以有时候会出现问题。
删除远程分支
git push origin :feature
(git push [远程名] [本地分支]:[远程分支])
# 使用这个命令,在省略本地分支的情况下,意思是用空白代替远程分支,就是删除的意思
其他的情况
Merge branch 'develop' of git.mail.netease.com:datapi/dpmonitor into develop
这种情况是出现了冲突解决冲突后的提交会有的一行merge说明。
实现过程,先把本地的修改commit,之后pull远程,之后解决冲突。这时有可能会发生明明解决了冲突但是还是说你存在的问题,这时使用git add,之后再commit就行了
通过查看git的官方文档发现,在合并时会遇到需要使用三个快照的情况,即你的分支和想要合并的分支都做了修改,这时git会把三方进行一个合并,并将这次合并的结果做了一个提交。就是上述的这种情况。使用ide进行冲突解决的话你会发现有三个窗口,而这种合并提交会有不止一个父提交。
其实实际操作过程中跟上述描述内容也不全相同,因为实际上并没有在本地新建分支,直接在映射到远程develop分支的本地分支上干活,不过也差不多,毕竟不管你本地取的名字是不是叫develop,对于git来说你仍然是一个branch。
在ide里使用git的时候也会遇到一些问题
canot mommit changes due to unresolved conflicts.
出现这个问题的原因,在于使用了命令行参数git pull拉下来的代码里有冲突,手动把冲突部分删除了以后,再使用ide的commit,他不认。
这时可以尝试继续使用命令行的git push来推到远端。当然这个不够稳妥,以我的性格当然不会这么做,那么就可以再用ide的pull按钮拉一次,再合并一次代码,万无一失。
commit
这时默认的提交信息也就变成了
Merge branch 'develop' of git.mail.netease.com:datapi/dpmonitor into develop
nice
总结
希望自己能够尽快彻底地熟悉git命令,做到不需要使用ide提供的功能。(当然有些好用的功能,比如快速解决冲突以及修改高亮这些是不可能不用的,工具毕竟还是为了效率而生的)
特别说一下Git,这个中文版文档是真的很好用了,虽然对于你遇到的某个问题可能在stackoverflow上解决的更快,但是只要你耐心地读完完整的文档,你自然会知道该怎么去做。
其实我也还没有完整的看过一遍,只是阅读了前三章关于git基础和操作的部分。感兴趣、有时间的话,自然是全部读完更好,但是仅作为一个工具,目前已经足够,因为还有太多的东西等着去学,精力不足,对于很多东西没有办法太过深入,只能浅尝辄止,留待将来。
因为整理的时间不够,所以本文看起来很乱,希望谅解。