版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统.
关于版本控制分为三种:本地版本控制系统,如rcs;集中化的版本控制系统,如CVS、SVN;分布式版本控制系统,如Git。
Git基础要点
Git和其它版本控制系统的主要差别在于:Git只关心文件数据的整体是否发生变化,而大多数其它系统则只关心文件内容的具体差异。
对于任何一个文件,在Git内都只有三种状态:已提交(committed)、已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。
每个项目都有一个git目录,它是Git用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。
所谓的暂存区域只不过是个简单的文件,一般都放在git目录中。有时候人们会把这个文件叫做索引文件。
基本的Git工作流程:(1)、在工作目录中修改某些文件;(2)、对这些修改了的文件作快照,并保存到暂存区域;(3)、提交更新,将保存在暂存区域的文件快照转储到git目录中。
对于已安装的Git,第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次Git提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录:
- $ git config --global user.name "Spring"
- $ git config --global user.email Spring@163.com
如果用了--global选项,那么更改的配置文件就是位于你用户主目录的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉--global选项重新配置即可,新的设定保存在当前项目的./git/config文件里。
Git常用命令
有两种取得Git项目仓库的方法。
第一种是在现存的目录下,通过导入所有文件来创建新的Git仓库:(1)、从当前目录初始化:$ git init ,初始化后,在当前目录下会出现一个名为.git的目录,所有Git需要的数据和资源都存放在这个目录中;(2)、如果当前目录下有几个文件想要纳入版本控制,需要先用git add命令告诉Git开始对这些文件进行跟踪,然后提交:- $ git add README
- $ git commit -m 'initial project version'
其中README文件是已经存在在当前目录中的。git add后可以接要跟踪的文件或目录的路径。如果是目录的话,就说明要递归跟踪所有该目录下的文件。
第二种是从已有的Git仓库克隆出一个新的镜像仓库来,如:
- $ git clone git://github.com/schacon/grit.git
- $ git clone git://github.com/schacon/grit.git mygrit
工作目录下面的所有文件都不外乎两种状态:已跟踪或未跟踪。已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。
检查当前文件状态:
- $ git status
忽略某些文件:可以创建一个名为.gitignore的文件,列出要忽略的文件模式。
要查看尚未暂存的文件更新了哪些部分 ,不加参数直接输入:
- $ git diff
如要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用:
- $ git diff --cached
- $ git commit
- $ git commit -m "commit message"
提交更新:现在的暂存区域已经准备妥当可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有git add 过,否则提交的时候不会记录这些还没暂存起来的变化。所以,每次准备提交前,先用git status 看下,是不是都已暂存起来了,然后再运行提交命令git commit。
跳过使用暂存区域:只要在提交的时候,给git commit加上-a选项,Git就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add步骤。
移除文件:要从Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。可以用git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了:
- $ git rm README
移动文件:要在Git中对文件改名:
- $ git mv file_from file_to
- $ mv file_from file_to
- $ git rm file_from
- $ git add file_to
- $ git log
常用”-p”选项展开显示每次提交的内容差异,用-2则仅显示最近的两次更新。”--stat”仅显示简要的增改行数统计。”--pretty”选项,可以指定使用完全不同于默认格式的方式展开提交历史,比如用online将每个提交放在一行显示:
- $ git log --pretty=oneline
- git log --pretty=format:"%h - %an, %ar : %s"
下面列出一些其它常用的选项及其释义:
“-p”:按补丁格式显示每个更新之间的差异。
“--stat”: 显示每次更新的文件修改统计信息。
“--shortstat”: 只显示--stat 中最后的行数修改添加移除统计。
“--name-only”: 仅在提交信息后显示已修改的文件清单。
“--name-status”:显示新增、修改、删除的文件清单。
“--abbrev-commit”:仅显示SHA-1 的前几个字符,而非所有的40 个字符。
“--relative-date”:使用较短的相对时间显示(比如,“2 weeks ago”)。
“--graph”: 显示ASCII 图形表示的分支合并历史。
“--pretty”: 使用其他格式显示历史提交信息。可用的选项包括oneline,short,full,fuller 和format(后跟指定格式)。
“-(n)”: 仅显示最近的n 条提交
--since,--after: 仅显示指定时间之后的提交,$ git log --since=2.weeks
--until,--before: 仅显示指定时间之前的提交。
--author: 仅显示指定作者相关的提交。
--committer”仅显示指定提交者相关的提交。使用图形化工具查阅提交历史:gitk,它是用Tcl/Tk写成的,基本上相当于git log命令的可视化版本,凡是git log可以用的选项也都能用在gitk上。在项目工作目录中输入
- $ gitk
- $ sudo apt-get install gitk
修改最后一次提交:想要撤销刚才的提交操作,可以使用--amend选项重新提交:
- $ git commit --amend
- $ git commit -m 'initial commit'
- $ git add forgotten_file
- $ git commit --amend
取消已经暂存的文件:
- $ git reset HEAD test.txt
- $ git checkout -- test.txt
远程仓库的使用:远程仓库是指托管在网络上的项目仓库。管理远程仓库的工作,包括添加远程库,移除废弃的远程库,管理各式远程库分支,定义是否跟踪这些分支等。
查看当前的远程库:要查看当前配置有哪些远程仓库,可以用
- $ git remote
添加远程仓库:要添加一个新的远程仓库,可以指定一个简单的名字,以便将来引用,运行
- $ git remote add [shortname] [url]
- $ git fetch [remote-name]
- $ git pull
推送数据到远程仓库:
- $ git push [remote-name] [branch-name]
查看远程仓库信息:
- $ git remote show [remote-name]
远程仓库的删除和重命名:可以用git remote rename命令修改某个远程仓库的简短名称,比如想把pb改成paul,可以这样运行:
- $ git remote rename pb paul
- $ git remote rm paul
打标签:同大多数VCS 一样,Git 也可以对某一时间点上的版本打上标签。人们在发布某个软件版本(比如v1.0 等等)的时候,经常这么做。
列出已有的标签:
- $ git tag
- $ git tag -l 'v1.4.2.*'
含附注的标签:
- $ git tag -a v1.0 -m 'my version 1.0'
可以使用git show命令查看相应标签的版本信息,并连同显示打标签时的提交对象:
- $ git show v1.0
签署标签:如果你有自己的私钥,还可以用GPG来签署标签,只需要把之前的-a改为-s即可。
轻量级标签:轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标签,一个-a,-s 或-m 选项都不用,直接给出标签名字即可
- $ git tag v2.0
验证标签:可以使用
- $ git tag -v [tag-name]
后期加注标签:可以在后期对早先的某次提交加注标签。只要在打标签的时候跟上对应提交对象的校验和(或前几位字符)即可:
- $ git tag -a v1.3 d06e3de00
- $ git push origin [tagname]
- $ git push origin --tags
Git分支
Git中的分支,其实本质上仅仅是个指向commit 对象的可变指针。Git会使用master 作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次提交对象的master 分支,它在每次提交的时候都会自动向前移动。
创建一个新的分支,即创建一个新的分支指针。比如创建一个testing分支,可以使用git branch命令:
- $ git branch testing
- $ git checkout testing
- $ git checkout -b testing
- $ git branch testing
- $ git checkout testing
由于Git中的分支实际上仅是一个包含所指对象校验和(40个字符长度SHA-1字串)的文件,所以创建和销毁一个分支就变得非常廉价。说白了,新建一个分支就是向一个文件写入41 个字节(外加一个换行符)那么简单,当然也就很快了。转换分支的时候最好保持一个清洁的工作区域。Git会把工作目录的内容恢复为检出某分支时它所指向的那个commit 的快照。它会自动添加、删除和修改文件以确保目录的内容和你上次提交时完全一样。
分支合并:用git merge命令来进行合并,
- $ git merge testing
- $ git branch -d testing
冲突的合并:要看看哪些文件在合并时发生冲突,可以用
- $ git status
- $ git add
- $ git commit
如果想用一个有图形界面的工具来解决冲突,可以运行
- $ git mergetool
分支管理:git branch命令不仅仅能创建和删除分支,如果不加任何参数,它会给出当前所有分支的清单。若要查看各个分支最后一次commit的信息,运行,
- $ git branch -v
- $ git branch --merge
- $ git branch -D testing
分支式工作流程:长期分支、特性分支
特性分支:在任何规模的项目中都可以使用特性(Topic)分支。一个特性分支是指一个短期的,用来实现单一特性或与其相关工作的分支。
请务必牢记这些分支全部都是本地分支,这一点很重要。当你在使用分支及合并的时候,一切都是在你自己的Git仓库中进行----完全不涉及与服务器的交互。
远程分支(remote branch):是对远程仓库状态的索引。它们是一些无法移动的本地分支;只有在进行Git的网络活动时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。
推送:要想和其他人分享某个分支,你需要把它推送到一个你拥有写权限的远程仓库。你的本地分支不会被自动同步到你引入的远程分支中,除非你明确执行推送操作。换句话说,对于无意分享的,你尽可以保留为私人分支,而只推送那些协同工作的特性分支。可以运行
- $ git push (远程仓库名) (分支名)
跟踪分支:从远程分支检出的本地分支,称为跟踪分支(tracking branch)。跟踪分支是一种和远程分支有直接联系的本地分支。在跟踪分支里输入
- $ git push
- $ git pull
在克隆仓库时,Git通常会自动创建一个master分支来跟踪origin/master。这正是git push和git pull一开始就能正常工作的原因。当然,你可以随心所欲地设定为其它跟踪分支,比如origin 上除了master 之外的其它分支,
- $ git checkout -b [分支名] [远程名]/[分支名]
删除远程分支:如果不再需要某个远程分支了,比如搞定了某个特性并把它合并进了远程的master 分支(或任何其他存放稳定代码的地方),可以用这个非常无厘头的语法来删除它:
- $ git push [远程名]:[分支名]
衍合:把一个分支整合到另一个分支的办法有两种:merge(合并)和rebase(衍合)。
有了rebase命令,就可以把在一个分支里提交的改变在另一个分支里重放一遍。如,
- $ git checkout testing
- $ git rebase master
- $ git checkout master
- $ git merge testing
- $ git rebase master testing
- $ git checkout master
- $ git merge testing
服务器上的Git
建立一个大家都可以访问的共享仓库,从那里推送和拉取数据。我们将把这个仓库称为“Git 服务器”。远程仓库通常只是一个纯仓库(bare repository)----一个没有当前工作目录的仓库。因为该仓库只是一个合作媒介,所以不需要从一个处于已从硬盘上检出状态的快照;仓库里仅仅是Git 的数据。简单的说,纯仓库是你项目里.git 目录的内容,别无他物。
协议:Git可以使用四种主要的协议来传输数据:本地传输,SSH协议,Git协议和HTTP 协议。
在服务器部署Git:开始架设Git 服务器的时候,需要把一个现存的仓库导出为新的纯仓库----不包含当前工作目录的仓库。方法非常直截了当。把一个仓库克隆为纯仓库,可以使用clone 命令的--bare 选项。纯仓库的目录名以.git 结尾,如
- $ git clone -bare my_project my_project.git
有了仓库的纯副本以后,剩下的就是把它放在服务器上并设定相关的协议。假设一个域名为git.example.com的服务器已经架设好,并可以通过SSH 访问,而你想把所有的Git仓库储存在/opt/git 目录下。只要把纯仓库复制上去:
- $ scp -r my_project.git user@git.example.com:/opt/git
- $ git clone user@git.example.com:/opt/git/my_project.git
- $ ssh user@git.example.com
- $ cd /opt/git/my_project.git
- $ git init --bare --shared
Git托管服务:可供选择的托管服务数量繁多,如GitHub。
分布式Git
分布式工作流程:同传统的集中式版本控制系统(CVCS)不同,开发者之间的协作方式因Git 的分布式特性而变得更为灵活多样。在集中式系统上,每个开发者就像是连接在集线器上的节点,彼此的工作方式大体相像。而在Git 网络中,每个开发者同时扮演着节点和集线器的角色,这就是说,每一个开发者都可以将自己的代码贡献到另外一个开发者的仓库中,或者建立自己的公共仓库,让其他开发者基于自己的工作开始,为自己的仓库贡献代码。于是,Git 的分布式协作便可以衍生出种种不同的工作流程。以上内容摘自于《Pro Git》
实际工作中常用命令汇总:
1. 查看远程分支:$ git branch -a
2. 查看本地分支:$ git branch
3. 切换分支:$ git checkout 分支名
4. 更新到指定版本:$ git reset --hard 39c9064fe34a1331e3624fbbef33a3f66df8f6ba
5. 更新到上个版本:$ git reset --hard HEAD^
6. 将远程分支下载到本地:$ git checkout -b branch1 origin/branch1
7. 创建新分支,并立即切换到此新分支:$ git checkout -b branchname
8. 从远程分支下载指定的分支:$ git pull origin branchname
9. 查看某个文件作了哪些修改:$ git diff src/a.cpp
10. 在合并进仓库之前,修改上次的提交:$ git commit --amend
11. 撤销为提交的修改:$ git checkout -- a.cpp
12. 查看某个文件详细的修改信息:$ git blame src/a.cpp