Git 概念
Git 是目前世界上最先进的分布式版本控制系统,版本控制系统主要分为:CVS(集中式的版本控制系统)、SVN(集中式版本控制系统)、BitKeeper(商用)、类似 Git 的 Mercurial 和 Bazaar 等。集中式的版本控制系统:中央服务器是一个版本库 一旦宕掉所有的版本失效。分布式版本控制系统:中央服务器是暂存提交的版本。
Git 安装
Debian 或 Ubuntu Linux:通过一条 sudo apt-get install git
就可以直接完成 Git 的安装,非常简单。
Windows:https://git-scm.com/downloads
下载相应的文件,然后按照提示进行 Git 安装。
Git 示例
使用 Git 创建文件并且提交文件,进入某个磁盘下创建文件 (以windows 为例)。
如上图在磁盘上创建了 GitTest 文件,右击该文件。
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
注意 git config 命令的 –global 参数,该参数表示你这台机器上所有的 Git 仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和 Email 地址,创建 readme.txt 文件。 内容为:
Git is a version control system.
Git is free software.
第一步,用命令 git add
告诉 Git,把文件添加到仓库 git add readme.txt
。
第二步,用命令 git commit
告诉 Git,把文件提交到仓库 git commit -m "wrote a readme file"
。
Git log 查看历史提交信息
git log --pretty=oneline
回退到某个版本,HEAD^ 回退到上一个版本,HEAD^^ 回退到上上一个版本,HEAD~100 回退到往上一百个版本。
git reset --hard HEAD^
git reset --hard HEAD^^
git reset --hard HEAD~100
Git 提供了一个命令 git reflog
用来记录你的每一次命令,可以通过这个命令回退到未来的某个版本。
总结:
HEAD 指向的版本就是当前版本,因此,Git 允许我们在版本的历史之间穿梭,使用命令 git reset --hard commit_id
,穿梭前,用 git log
可以查看提交历史,以便确定要回退到哪个版本。要重返未来,用 git reflog
查看命令历史,以便确定要回到未来的哪个版本。
工作区和暂存区
Git 和其他版本控制系统如 SVN 的一个不同之处就是有暂存区。工作区就是电脑工作的目录,如上图的 GitTest 目录。工作区有一个隐藏目录,git 这个不算工作区,而是 Git 的版本库。
上文中的第一步是用 git add
把文件添加进去,实际上就是把文件修改添加到暂存区;第二步是用 git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
使用 git status
查看一下状态。
git add
命令实际上就是把要提交的所有修改放到暂存区(Stage);执行 git commit
就可以一次性把暂存区的所有修改提交到分支。
管理修改
Git 管理的是修改,当你用 git add
命令后,在工作区的第一次修改被放入暂存区,准备提交。但是,在工作区的第二次修改并没有放入暂存区,git commit
只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
那怎么提交第二次修改呢?你可以继续 git add
再 git commit
。也可以别着急提交第一次修改,先 git add
第二次修改,再 git commit
就相当于把两次修改合并后一块提交了。
撤销修改
命令 git checkout -- readme.txt
意思就是,把 readme.txt
文件在工作区的修改全部撤销。这里有两种情况:一种是 readme.txt
自修改后还没有被放到暂存区,撤销修改就回到和版本库一模一样的状态;一种是 readme.txt
已经添加到暂存区后,又作了修改,撤销修改就回到添加到暂存区后的状态。总之,就是让这个文件回到最近一次 git commit
或 git add
时的状态。
命令 git reset HEAD <file>
可以把暂存区的修改撤销掉(unstage),重新放回工作区。
- 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
git checkout -- file
即可。 - 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令
git reset HEAD <file>
,就回到了场景1,第二步按场景1操作即可。 - 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考
git reset --hard
。
删除文件
在 Git
中,删除也是一个修改操作,我们实战一下,先添加一个新文件 test.txt 到 Git 并且提交。一般情况下,你通常直接在文件管理器中把没用的文件删了,现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令 git rm
删掉,并且 git commit
。另一种情况是删错了,因为版本库里还有,所以可以很轻松地把误删的文件恢复到最新版本:git checkout -- test.txt
。git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以一键还原。命令 git rm
用于删除一个文件,如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
添加远程库
你已经在本地创建了一个 Git 仓库后,又想在 GitHub 创建一个 Git 仓库,并且让这两个仓库进行远程同步。GitHub 上的仓库既可以作为备份,又可以让其他人通过该仓库来协作。
根据 GitHub 的提示,在本地的 git 仓库下运行命令。
git remote add origin git@github.com:账户名/learngit.git
把本地库的所有内容推送到远程库上。
git push -u origin master
由于远程库是空的,我们第一次推送 master 分支时,加上了 -u 参数,Git 不但会把本地的 master 分支内容推送到远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来,在以后的推送或者拉取时就可以简化命令。从现在起,只要本地作了提交,就可以通过命令推送。
git push origin master
克隆远程库
用命令 git clone
克隆一个本地库,git clone git@github.com:账户名/gitskills.git
。
GitHub 给出的地址不止一个,还可以用 https://github.com/michaelliao/gitskills.git
这样的地址。实际上,Git 支持多种协议,默认的 git://
使用 ssh,但也可以使用 https 等其他协议。(Git 支持多种协议,包括 https,但通过 ssh 支持的原生 git 协议速度最快,使用 https 除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放 http 端口的公司内部就无法使用 ssh协议而只能用 https)。
分支管理
Git 的分支是与众不同的,无论你的版本库是 1 个文件还是 1 万个文件,无论创建、切换和删除分支,Git 在 1 秒钟之内就能完成!!!
创建分支
合并分支
删除分支
分支管理的命令如下:git checkout -b dev
相等于 git branch dev 或者 git checkout dev
。用 git branch
命令查看当前分支,git branch
命令会列出所有分支,当前分支前面会标一个*
号。我们把 dev 分支的内容合并到 master 分支上。dev 分支的工作完成,我们就可以切换回 master 分支。
git checkout master
git merge
命令用于合并指定分支到当前分支。合并后,再查看 readme.txt
的内容,和 dev 分支的最新提交是完全一样的。
创建于合并分支管理,Git 鼓励使用分支。
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
......
解决冲突
git checkout -b feature1
git add readme.txt
git commit -m "AND simple"
git checkout master
git commit -m "& simple"
这种情况下,Git 无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。
当 Git 无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。解决冲突就是把 Git 合并失败的文件手动编辑为我们希望的内容,再提交。使用 git log --graph
命令可以看到分支合并图。
分支管理策略
合并分支时,如果可能,Git 会用 Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。如果强制禁用 Fast forward
模式,Git 就会在 merge 时生成一个新的 commit,这样从分支历史上就可以看出分支信息。
下面我们实战一下 --no-ff
方式的 git merge
。
git checkout -b dev
git add readme.txt
git commit -m "add merge"
git checkout master
git merge --no-ff -m "merge with no-ff" dev
git log
Git 还提供了一个 stash 功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作。git checkout -b issue-101
修复代码的分支(仅在于紧急修复 BUG)。一种方式是用 git stash apply
恢复,但是恢复后,stash 内容并不删除,你需要用 git stash drop
来删除;另一种方式是用 git stash pop
,恢复的同时把 stash 内容也删了。
用 git stash list
查看,就看不到任何 stash 内容。
当手头工作没有完成时,先把工作现场 git stash
一下,然后去修复 bug,修复后再 git stash pop
,回到工作现场。开发一个新feature,最好新建一个分支;如果要丢弃一个没有被合并过的分支,可以通过 git branch -D <name>
强行删除。
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,Git 就会把该分支推送到远程库对应的远程分支上:git push origin master
,如果要推送其他分支,比如 dev,则可以使用 git push origin dev
。
多人协作工作模式
首先,可以试图用 git push origin <branch-name>
推送自己的修改;如果推送失败,则因为远程分支比你的本地更新,需要先用 git pull
合并上游最新提交。如果合并有冲突,则解决冲突,并在本地提交;没有冲突或者解决掉冲突后,再用 git push origin <branch-name>
推送就能成功。如果 git pull
提示 no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令 git branch --set-upstream-to <branch-name> origin/<branch-name>
。这就是多人协作的工作模式,一旦熟悉了,就非常简单。
总结使用:
查看远程库信息,使用 `git remote -v`。
本地新建的分支如果不推送到远程,对其他人就是不可见的。
从本地推送分支,使用 `git push origin branch-name`,如果推送失败,先用 `git pull` 抓取远程的新提交。
在本地创建和远程分支对应的分支,使用 `git checkout -b branch-name origin/branch-name`,本地和远程分支的名称最好一致。
建立本地分支和远程分支的关联,使用 `git branch --set-upstream branch-name origin/branch-name`。
从远程抓取分支,使用 `git pull`,如果有冲突,要先处理冲突。
rebase
这就是 rebase 操作的特点,把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了。rebase 操作可以把本地未 push 的分叉提交历史整理成直线。rebase 的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。
标签管理
git tag <name>
就可以打一个新标签。git tag
查看所有标签,对历史的提交进行标签活动,需要找到 commit id
。如 git tag v0.9 f52c633
。git show <tagname>
查看标签信息,还可以创建带有说明的标签,用 -a 指定标签名,-m 指定说明文字。
命令 git tag <tagname>
用于新建一个标签,默认为 HEAD,也可以指定一个 commit id
;命令 git tag -a <tagname> -m "blablabla"
可以指定标签信息;命令 git tag
可以查看所有标签。如果要推送某个标签到远程,使用命令 git push origin <tagname>
。删除已经提交的版本tag,先本地删除 git tag -d v0.9
,然后远程删除 git push origin :refs/tags/v0.9
。
总结:
git push origin <tagname> 可以推送一个本地标签
git push origin --tags 可以推送全部未推送过的本地标签
git tag -d <tagname> 可以删除一个本地标签
git push origin :refs/tags/<tagname>可以删除一个远程标签
Git 仓库
Github
在 GitHub 上,可以任意 Fork 开源仓库。自己拥有 Fork 后的仓库的读写权限,可以推送 pull request 给官方仓库来贡献代码。
码云
GitHub 相比,码云也提供免费的 Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,码云还提供了项目管理、代码托管、文档管理的服务,5 人以下小团队免费。使用码云和使用 GitHub 类似,我们在码云上注册账号并登录后,需要先上传自己的SSH公钥。
附录
常用工具
git config --global alias.st status 状态
git config --global alias.co checkout 切换主目录
git config --global alias.ci commit 提交代码
git config --global alias.br branch 查看分支
git config --global alias.unstage 'reset HEAD'
git reset HEAD file 可以把暂存区的修改撤销掉 unstage 重新放回工作区
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
......
自定义 Git
某文件不需要提交,在 Git 工作区的根目录下创建一个特殊的 .gitignore 文件,然后把要忽略的文件名填进去,Git 就会自动忽略这些文件。 所有配置文件可以直接在线浏览 https://github.com/github/gitignore。忽略文件的原则是忽略操作系统自动生成的文件,比如缩略图等。 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如 Java 编译产生的 .class 文件,忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。.gitignore 文件写完后,也要提交到 Git 中。
总结
通过详细的介绍和示例,教程详细解释了 Git 的基本概念、工作流程、常用命令和常见操作。通过学习教程,可以了解到 Git 的分支管理、合并冲突解决、远程仓库和团队协作等高级功能。总之,这个教程适合任何想要学习 Git 的人,无论是初学者还是有一定经验的开发人员。它是学习和运用 Git 进行版本管理的绝佳指南。