源码管理工具之git的使用
一、git简介
git
是目前世界上被最广泛使用的现代软件版本管理系统。git
本身亦是一个成熟并处于活跃开发状态的开源项目,它最初是由Linux操作系统内核的创造者Linus Torvalds在2005年创造。
二、在Mac OS X上安装git
如果你正在使用Mac做开发,有两种安装git
的方法。
- 安装
homebrew
,然后通过homebrew
安装git
,具体方法请参考homebrew
的文档:http://brew.sh/。 - 直接从
AppStore
安装Xcode
,Xcode
集成了git
。不过默认没有安装,你需要运行Xcode
,选择菜单“Xcode”->“Preferences”
,在弹出窗口中找到“Downloads”
,选择“Command Line Tools”
,点“Install”就可以完成安装了。
三、创建本地仓库
仓库:英文名repository
,可以简单理解成一个目录,这个目录里面的所有文件都可以被git
管理起来,每个文件的修改、删除,git
都能记录,以便任何时刻都可以查找历史记录,或者在回滚到以前修改的状态。
1、首先,选择一个合适的地方,创建一个空目录,执行:
git init
创建新的git
仓库,会发下在当前的创建的目录下面多了一个.git
的目录,这个目录是git
来管理仓库的。
注意:
没事千万不要手动修改这个目录里面的文件,不然就把会git
仓库给破坏了。
2、添加文件到git仓库
把文件添加到仓库:
git add <filename>
git add .
注意:
git add <filename>
是把某一个文件添加到git
的缓存区里面。
git add .
是把所有的文件添加到git
的缓存区里面。
3、把文件提交到本地仓库
git commit -m "代码提交信息"
注意:
-
git commit
命令,一般会在后面加上-m
表示本次提交到本地仓库的记录。 -
commit message
言简意赅,不要写无用信息,这样让别人不能看懂这次提交的意义。 - 在开发时,良好的习惯是根据工作进度及时
commit
,并务必注意附上有意义的commit message
。创建完项目目录后,第一次提交的commit message
一般为Initial commit.
。 - 添加一个新的
Pod
库或pod update
后,要单独提交一个commit
,统一commit message
为pod add xxx
或pod update xxx
。
四、远程仓库
一般使用git
的公司都会有自己的git
服务器或者使用第三方git
服务器,比如coding.net
、码云等。
由于你的本地git
仓库和git
服务器仓库之间的传输是通过SSH
加密的,需要设置:
- 创建
SSH Key
。在用户主目录下,看看有没有.ssh
目录。如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有,可直接跳到下一步。如果没有,打开Shell
,创建SSH Key
:
ssh-keygen -t rsa -C "your.email@example.com" -b 4096
- 将公共SSH密钥复制到剪贴板:
pbcopy < ~/.ssh/id_rsa.pub
- 登陆
git
服务器,打开“Account settings”
,“SSH Keys”
页面: - 点
“Add SSH Key”
,填上任意Title,在Key
文本框里粘贴id_rsa.pub
文件的内容:
注意:
为什么git
服务器需要SSH Key
呢?因为git
服务器需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而git
支持SSH
协议,所以,git
服务器只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,git
服务器允许你添加多个Key
。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key
都添加到git
服务器,就可以在每台电脑上往git
服务器推送了。
五、添加远程仓库
- 登陆
git
服务器,然后,找到“Create a new repo”
按钮,创建一个新的仓库: - 在
Repository name
填入learngit
,其他保持默认设置,点击“Create repository”
按钮,就成功地创建了一个新的git
仓库。
注意:
目前,在git
服务器上的这个learngit
仓库还是空的,git
服务器告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到git
服务器仓库。
通过上图可以看出:
1、如果在本地没有创建仓库,可以通过下面命令从git
服务器clone
出一个新的仓库:
git clone http://michael@git.51fanxing.com:88/fxbest/ios/learngit.git
cd learngit
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master
2、如果本地已经创建了,可以通过下面命令把本地仓库与git
服务器仓库进行关联:
cd existing_folder
git init
git remote add origin http://michael@git.51fanxing.com:88/fxbest/ios/learngit.git
git add .
git commit
git push -u origin master
注意:
1、git remote add origin
表示将本地服务器与git
服务器进行关联。
2、origin
是git
服务器的别名,取什么名字都可以,你也可以在push
时将git
服务器替换为 origin。但为了以后push
方便,我们第一次一般都会先remote add
。
3、git push -u origin master
将你修改或者添加的文件提交到git
服务器。
3、git remote
git remote
命令允许你创建、查看和删除和其它仓库之间的远程连接。
git remote
列出和其他仓库之间的远程连接。
git remote -v
列出和其他仓库之间的远程连接,但同时显示每个连接的URL
。
git remote add <name> <url>
创建一个新的远程仓库连接。在添加之后,可以将<name>
作为<url>
便捷的别名在其他git
命令中使用。
git remote rm <name>
移除名为的远程仓库的连接。
git remote rename <old-name> <new-name>
将远程连接从<old-name>
重命名为<new-name>
。
六、检查仓库状态
1、git status
git status
命令显示工作目录和缓存区的状态。你可以看到哪些更改被缓存了,哪些还没有,以及哪些还未被git
记录。
2、git log
git log
命令查看每次commit
的历史记录。
git log
使用默认格式显示完整地commit
记录,如果输出超过一屏,你可以用空格键来滚动,按q退出。
git log -n <limit>
用<limit>限制提交的数量,比如git log -n 3
只会显示3个提交。
git log --oneline
将每个提交压缩到一行,当你需要查看项目历史的上层情况时这会很有用。
git log --stat
除了git log
信息之外,包含哪些文件被更改了,以及每个文件相对的增删行数。
git log -p
显示代表每个提交的一堆信息,显示每个提交全部的差异(diff),这也是项目历史中最详细的视图。
git log --author="<pattern>"
搜索特定作者的提交。<pattern>
可以是字符串或正则表达式。
git log --grep="<pattern>"
搜索提交信息匹配特定<pattern>
的提交。<pattern>
可以是字符串或正则表达式。
七、git reset、git checkout和git revert
git
仓库有三个主要组成——工作目录,缓存区和提交历史。
git reset
、git checkout
和git revert
是你的git
工具箱中最有用的一些命令。它们都用来撤销代码仓库中的某些更改,而前两个命令不仅可以作用于提交,还可以作用于特定文件。
因为它们非常相似,所以我们经常会搞混,不知道什么场景下该用哪个命令。
1、git reset
git reset
操作会将当前分支的HEAD
指向另外一个commit
记录,这样可以在当前分支上移除部分commit
记录。例如,在hotfix分支上回滚前两次的commit
状态:
git reset HEAD~2
image.png
image.png
当执行git reset
时,hotfix分支上最后两次提交的状态会变成空状态,等到git
执行垃圾回收的时候,hotfix分支上最后两次提交的状态会被回收。
- --soft – 缓存区和工作目录都不会被改变
- --mixed – 默认选项。缓存区和你指定的提交同步,但工作目录不受影响
- --hard – 缓存区和工作目录都同步到你指定的提交
git reset
一般配合这些参数使用,git reset --mixed HEAD
将你当前的改动从缓存区中移除,但是这些改动还留在工作目录中。如果你想完全舍弃你没有提交的改动,你可以使用git reset --hard HEAD
,这是git reset
最常用的两种用法。
注意:
git reset
是不可逆的,因为git reset
操作会重写当前分支的历史,用来撤销缓存区和工作目录的修改。git reset
只适用于本地修改,不能重设服务器上的commit
记录。
2、git checkout
- 切换分支:
git checkout hotfix
注意:
使用git checkout
之前,要把工作区的内容提交到git
服务器或者缓存区。git checkout
是将HEAD
指针从一个分支切换到另一个分支,然后更新工作目录。因为这可能会覆盖本地的修改,git
会强制你提交或者缓存工作目录中的所有更改,不然在checkout的时候这些更改都会丢失。
- 查看文件之前的版本
除了切换分支之外,git checkout
还可以将HEAD
指针移动到当前分支其他的commit
记录。
git checkout HEAD~2
git checkout
更改的是工作目录而不是缓存区
git checkout HEAD~2 xxx
如果你缓存并且提交了checkout
的文件,它具备将某个文件回撤到之前版本的效果,注意它撤销了这个文件后面所有的更改。不影响你仓库的当前状态。你可以在新的快照中像其他文件一样重新提交旧版本。所以,在效果上,git checkout
的这个用法可以用来将单个文件回滚到旧版本。
注意:
git checkout
是可逆的。
3、git revert
git revert
撤销一个commit
记录的同时会创建另一个新的commit
记录,这是一个安全的方法,而不是从项目历史中移除这个提交。这避免了git
丢失项目历史记录,这一点对于你的版本历史和协作的可靠性来说是很重要的。
比如,下面的命令会找出倒数第二个提交,然后创建一个新的提交来撤销这些更改,然后把这个提交加入项目中。
git revert HEAD~2
image.png
image.png
git revert
应该用在你想要在项目历史中移除一整个提交的时候。比如说,你在追踪一个 bug,然后你发现它是由一个提交造成的,这时候撤销就很有用。与其说自己去修复它,然后提交一个新的快照,不如用git revert
,它帮你做了所有的事情。
git revert
回滚了单独一个提交,它没有移除后面的提交记录,同时会创建另一个新的commit
记录。
git reset
回滚了单独一个提交,并且移除后面的提交记录,是不可逆的。
git revert
可以针对历史记录中任何一个提交,而git reset
只能从当前提交向前回滚。比如,你想用git reset
重设一个旧的提交,你不得不移除那个提交后的所有提交,再移除那个提交,然后重新提交后面的所有提交。一般公司不提倡这么做。因此,git revert
可以用在公共分支上,git reset
应该用在私有分支上。
4、git reset
用慎用
当有commit
之后的提交被推送到公共分支上,你绝不应该使用git reset
。使用git reset
回滚了单独一个提交,并且移除后面的提交记录。当团队成员在上面继续开发的提交在协作时会引发严重的问题。当他们试着和你的仓库同步时,他们会发现项目历史的一部分突然消失了。
下面的序列展示了如果你尝试重设公共提交时会发生什么。origin/master
是你本地master
分支对应的中央仓库中的分支。
一旦你在重设之后又增加了新的提交,git
会认为你的本地历史已经和 origin/master
分叉了,同步你的仓库时的合并提交(merge commit)会使你的同事困惑。
5、git clean
git clean
命令将未记录的文件从你的工作目录中移除。它只是提供了一条捷径,因为用git status
查看未记录的文件,然后手动移除它们也很方便。和一般的rm
命令一样,git clean
是无法撤消的,所以在删除未记录的文件之前想清楚,你是否真的要这么做。
git clean
命令经常和git reset --hard
一起使用。记住,git reset
只影响被记录的文件,所以还需要一个单独的命令来清理未被记录的文件。这个两个命令相结合,你就可以将工作目录回到之前特定提交时的状态。
注意:
git clean
也是不可逆的。
八、分支
1、git branch
分支代表了一条独立的开发流水线,git branch
命令允许你创建、列出、重命名和删除分支。一般git branch
和git checkout
、git merge
这两个命令通常紧密地结合在一起使用。一般使用过git
的同学都知道:
-
master
是长期分支,一般用于管理对外发布版本,每个commit
对一个tag
,也就是一个发布版本。 -
develop
是长期分支,一般用于作为日常开发汇总,即开发版的代码 -
feature
是短期分支,一般用于一个新功能的开发。 -
hotfix
是短期分支 ,一般用于正式发布以后,出现bug,需要创建一个分支,进行bug修补。 -
release
是短期分支,一般用于发布正式版本之前(即合并到master
分支之前),需要有的预发布的版本进行测试。release
分支在经历测试之后,测试确认验收,将会被合并的develop
和master
。
git branch
列出仓库中所有分支。
git branch <branch>
创建一个名为 <branch> 的分支。不会自动切换到那个分支去。
git branch -d <branch>
删除指定分支。这是一个安全的操作,git
会阻止你删除包含未合并更改的分支。
git branch -D <branch>
强制删除指定分支,即使包含未合并更改。如果你希望永远删除某条开发线的所有提交,你应该用这个命令。
git branch -m <branch>
将当前分支命名为<branch>。
2、git merge
用于合并指定分支到当前分支,git merge
命令允许你将git branch
创建的多条分支合并成一个。
注意:
合并指定分支到当前分支,当前分支会被更新,以响应合并操作,但目标分支完全不受影响。也就是说git merge
经常和git checkout
一起使用,选择当前分支,然后用git branch -d
删除废弃的目标分支。
git merge <branch>
将指定分支并入当前分支。
git merge --no-ff <branch>
将指定分支并入当前分支,但 总是 生成一个合并提交(即使是快速向前合并)。这可以用来记录仓库中发生的所有合并。
3、git merge几种方法
- 快速向前合并
当当前分支顶端到目标分支路径是线性之时,我们可以采取快速向前合并。git
只需要将当前分支顶端(快速向前地)移动到目标分支顶端,即可整合两个分支的历史,而不需要“真正”合并分支。它在效果上合并了历史,因为目标分支上的提交现在在当前分支可以访问到。比如,some-feature
到master
分支的快速向前合并会是这样的:
- 三路合并
如果分支已经分叉了,那么就无法进行快速向前合并。当和目标分支之间的路径不是线性之时,git
只能执行三路合并。三路合并使用一个专门的提交来合并两个分支的历史。这个术语取自这样一个事实,git
使用三个提交来生成合并提交:两个分支顶端和它们共同的祖先。
很多开发者喜欢使用git rebase
快速向前合并,来合并微小的功能或者修复bug,使用三路合并来整合长期运行的功能。后者导致的合并提交作为两个分支的连接标志。
4、解决冲突
如果你尝试合并的两个分支同一个文件的同一个部分,git
将无法决定使用哪个版本。当这种情况发生时,它会停在合并提交,让你手动解决这些冲突。
当你遇到合并冲突时,运行git status
命令来查看哪些文件存在需要解决的冲突。
当你手动修复完冲突之后,只需对冲突的文件运行git add
告诉git
冲突已解决。然后,运行git commit
生成一个合并提交。
使用git log --graph
命令可以看到分支合并图。
团队合作的分支看起来就像这样:
image.png
注意:
提交冲突只会出现在三路合并中。在快速向前合并中,我们不可能出现冲突的更改。
九、创建标签
一般在公司每一个上线版本都会打一个标签,为了记录和维护,当线上版本出现紧急bug,我们就可以通过git checkout
检出项目,然后创建hotfix分支进行bug的修复。
git tag
查看所有标签
git tag <name>
git tag <name> commit id
用于新建一个标签,默认为HEAD
,也可以指定一个commit id
git show <tagname>
查看标签信息
git push origin <tagname>
可以推送一个本地标签
git push origin --tags
可以推送全部未推送过的本地标签
git tag -d <tagname>
可以删除一个本地标签
git push origin :refs/tags/<tagname>
可以删除一个远程标签
Reference:
https://www.atlassian.com/git/tutorials/merging-vs-rebasing
https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000