跟着小程一起聊聊GIT那点事-阿里云开发者社区

开发者社区> 中生代技术> 正文

跟着小程一起聊聊GIT那点事

简介:
一、GIT发展史

同生活中的许多伟大事件一样,Git 诞生于一个极富纷争大举创新的年代。Linux 内核开源项目有着为数众广的参与者。绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。到 2002 年,整个项目组开始启用分布式版本控制系统 BitKeeper 来管理和维护代码。

到 2005 年的时候,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了免费使用 BitKeeper 的权力。这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds )不得不吸取教训,只有开发一套属于自己的版本控制系统才不至于重蹈覆辙。他们对新的系统订了若干目标:

  • 速度

  • 简单的设计

  • 对非线性开发模式的强力支持(允许上千个并行开发的分支)

  • 完全分布式

  • 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。它的速度飞快,极其适合管理大项目,它还有着令人难以置信的非线性分支管理系统,可以应付各种复杂的项目开发需求。

二、如何安装GIT
1、Ubuntu上安装    $ sudo apt-get install git-core2、Windows上安装
    有个叫做 msysGit 的项目提供了安装包,参考:
    http://code.google.com/p/msysgit 3、从源码安装
    Git的工作需要调用curl,zlib,openssl,expat,libiconv等库的代码,所有要先安装这些依赖工具。    $ sudo apt-get install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
    再从下面的 Git 官方站点下载最新版本源代码 :
    http://git-scm.com/download
    然后编译并安装,例如:    $ tar -zxf git-1.6.0.5.tar.gz 
    $ cd git-1.6.0.5 
    $ make prefix=/usr/local all 
    $ sudo make prefix=/usr/local install 

    现在已经可以用 git 命令了,例如把 Git 项目仓库克隆到本地,以便日后更新: 
    $ git clone git://git.kernel.org/pub/scm/git/git.git
三、运行GIT前的配置

一般在新的系统上,我们都需要先配置下自己的 Git 工作环境。配置工作只需一次,以后升级时还会沿用现在的配置。当然,如果需要,你随时可以用相同的命令修改已有的配置。

Git 提供了一个叫做 git config 的工具,专门用来配置或读取相应的工作环境变量。而正是由这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方:

  • /etc/gitconfig文件:
    系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。

  • ~/.gitconfig文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。

  • 当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖/etc/gitconfig 中的同名变量。

第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录:

$ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com

如果用了 --global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 --global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。

接下来要设置的是默认使用的文本编辑器。Git 需要你输入一些额外消息的时候,会自动调用一个外部文本编辑器给你用。默认会使用操作系统指定的默认编辑器,一般可能会是 Vi 或者 Vim。如果你有其他偏好,比如 Emacs 的话,可以重新设置:
$ git config --global core.editor emacs

还有一个比较常用的是,在解决合并冲突时使用哪种差异分析工具。比如要改用 vimdiff 的话:
$ git config --global merge.tool vimdiff
Git可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合并工具的输出信息。当然,你也可以指定使用自己开发的工具。

查看配置信息
要检查已有的配置信息,可以使用 git config --list 命令:

$ git config --list user.name=Scott Chacon 
user.email=schacon@gmail.com

也可以直接查阅某个环境变量的设定,只要把特定的名字跟在后面即可,例如:

git config user.name 
Scott Chacon
四、获取GIT帮助

想了解 Git 的各种命令该怎么用,可以阅读它们的使用帮助,方法有三:

$ git help <verb> $ git <verb> --help $ man git-<verb>

比如,要学习 config 命令可以怎么用,运行:
$ git help config

我们随时都可以浏览这些帮助信息而无需连网。不过,要是你觉得还不够,可以到 Frenode IRC 服务器(irc.freenode.net)上的 #git 或 #github 频道寻求他人帮助。这两个频道上总有着上百号人,大多都有着丰富的 git 知识,并且乐于助人。

五、Git中文件的状态和基本工作流程

对于任何一个文件,在 Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。

由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的本地数据目录,工作目录以及暂存区域。

640?wx_fmt=png&wxfrom=5&wx_lazy=1


每个项目都有一个 git 目录,它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。

从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从 git 目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑。

所谓的暂存区域只不过是个简单的文件,一般都放在 git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。

基本的 Git 工作流程如下所示:

  • 在工作目录中修改某些文件。

  • 对这些修改了的文件作快照,并保存到暂存区域。

  • 提交更新,将保存在暂存区域的文件快照转储到 git 目录中。

所以,我们可以从文件所处的位置来判断状态:如果是 git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。

六、Git常用命令
git init
git clone
git add <file>
git commit
git diff
git diff --cached
git rm <file>
git rm <file> --cached
git mv <file> <newfile>
git status
git log
git commit --amend
git reset HEAD <file>
git reset --soft HEAD~n
git reset --hard HEAD~n
git checkout -- <file>
git checkout <file>
git remote
git remote add [shortname] [url]git fetch [remotename]git push [remotename] [localbranch]:[remotebranch]git remote show [remotename]git remote rename [remotename] [new-remotename]git remote rm [remotename]git tag
git tag -a [tagname] –m [comments]git push [remotename] [tagname]git push [remotename] –tags
git branch [branchname]git checkout [branchname]git branch -d [branchname]git merge [branchname]git branch
git checkout -b [branchname] [remotename]/[branchname]git push [remotename] :[remotebranch]git rebase [branchname]git pull
七、Git命令使用举例
  • 从当前目录初始化
    要对现有的某个项目开始用 Git 管理,只需到此项目所在的目录,执行:
    git init
    初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。

  • 从现有仓库克隆,如克隆git的代码库
    git clone git://git.kernel.org/pub/scm/git/git.git

  • 跟踪新文件和暂存已修改文件
    git add <file>

  • 提交更新
    git commit
    这种方式会启动文本编辑器以便输入本次提交的说明。也可以使用 -m 参数后跟提交说明的方式,在一行命令中提交更新:
    git commit -m “Initial commit of test repo”

  • 查看已暂存和未暂存的修改
    git diff
    此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容。若要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用 git diff --cached 命令

  • 移除文件
    要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。
    git rm <file>
    另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 可以用--cached选项
    git rm --cached <file>

  • 移动文件
    git mv <file> <newfile>

  • 检查当前文件状态
    git status

  • 查看提交历史
    git log

  • 修改最后一次提交
    有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以使用 --amend 选项重新提交:
    git commit --amend
    如果刚才提交完没有作任何改动,直接运行此命令的话,相当于有机会重新编辑提交说明,而所提交的文件快照和之前的一样。如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交。

  • 取消已经暂存的文件
    git reset HEAD <file>

  • 取消对文件的修改
    git checkout -- <file>
    git checkout <file> 一般与上面的命令效果相同,但如果有一个分支名与文件名相同,就不一样了。加--来消除歧义。

  • 查看当前的远程库
    git remote
    也可以加上 -v 选项,显示对应的克隆地址
    git remote -v

  • 添加远程仓库
    git remote add [shortname] [url]

  • 从远程仓库抓取数据
    git fetch [remotename]
    此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支,一探究竟。

  • 推送数据到远程仓库
    git push [remotename] [localbranch]:[remotebranch]

  • 查看远程仓库信息
    git remote show [remotename]

  • 远程仓库的删除和重命名

    git remote rename [remotename] [new-remotename]git remote rm [remotename]
  • 显示已有的标签
    git tag

  • 新建标签
    git tag -a [tagname] –m [comments]
    如果想为以前的某次提交打标签,只要在打标签的时候跟上对应提交对象的校验和(或前几位字符)即可 。

  • 用某个标签新建分支
    git checkout –b [branchname] [tagname]

  • 分享标签
    默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。
    git push [remotename] [tagname]
    如果要一次推送所有(本地新增的)标签上去,可以使用 --tags 选项:
    git push [remotename] –tags

  • 创建分支
    git branch [branchname]

  • 切换分支
    git checkout [branchname]

  • 新建并切换到该分支
    git checkout -b [branchname]

  • 删除分支
    git branch -d [branchname]

  • 合并分支
    git merge [branchname]
    以上命令将[branchname]分支合并到当前分支

  • 查看分支
    git branch

  • 远程分支和创建跟踪分支
    远程分支(remote branch)是对远程仓库状态的索引。它们是一些无法移动的本地分支;只有在进行 Git 的网络活动时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支。从远程分支检出的本地分支成为跟踪分支。
    git checkout -b [branchname] [remotename]/[branchname]
    或者 git checkout --track [remotename]/[branchname]

  • 删除远程分支
    git branch -r -d origin/[branchname]

  • 衍合
    git rebase [branchname]

  • 从远程仓库抓取数据并merge
    git pull

八、Git分支

在 Git 中提交时,会保存一个提交(commit)对象,它包含一个指向暂存内容快照的指针,作者和相关附属信息,以及一定数量(也可能没有)指向该提交对象直接祖先的指针:第一次提交是没有直接祖先的,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。

为直观起见,我们假设在工作目录中有三个文件,准备将它们暂存后提交。暂存操作会对每一个文件计算校验和(即SHA-1 哈希字串),然后把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 类型的对象存储这些快照),并将校验和加入暂存区域。现在,Git 仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象 。

概念上来说,仓库中的各个对象保存的数据和相互关系看起来 如下所示:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

多次提交后,仓库历史如下:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。

640?wx_fmt=png&wxfrom=5&wx_lazy=1


Git 通过个git branch命令创建分支,比如新建一个testing分支:
git branch testing
这会在当前commit对象上新建一个分支指针, 同时Git保存着一个名为HEAD的特别指针,来让Git知道当前在哪个分支上工作。如下:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

要切换到其他分支,可以执行命令git checkout,如切换到新建的testing分支:

git checkout testing
这样HEAD就指向testing分支,如下:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

此时在testing分支上工作,如果有新的提交,提交后的结果如下:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

此时切换回master分支再提交后的结果如下:

640?wx_fmt=png&wxfrom=5&wx_lazy=1


九、Git分支合并与衍合

如将experiment分支合并回master分支执行以下命令:

git checkout mastergit merge experiment

合并前后如下所示:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

有时候合并操作并不会如此顺利。如果你修改了两个待合并分支里同一个文件的同一部分,Git 就无法干净地把两者合到一起,这种问题只能由人来解决。如果你想用一个有图形界面的工具来解决这些问题,不妨运行 git mergetool,它会调用一个可视化的合并工具并引导你解决所有冲突。确认所有冲突都解决后,可以用 git commit 来完成这次合并提交。

其实,合并两个分支还有另外一个选择:你可以把在 C3 里产生的变化补丁重新在 C4 的基础上打一遍。在 Git 里,这种操作叫做衍合(rebase)。有了 rebase 命令,就可以把在一个分支里提交的改变在另一个分支里重放一遍。

git checkout experiment
git rebase master

衍合前后如下所示:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

再进行一次快进:

git checkout mastergit merge experiment

结果如下所示:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

现在,合并后的 C3(即现在的 C3’)所指的快照,同三方合并例子中的 C5 所指的快照内容一模一样了。最后整合得到的结果没有任何区别,但衍合能产生一个更为整洁的提交历史。如果视察一个衍合过的分支的历史记录,看起来更清楚:仿佛所有修改都是先后进行的,尽管实际上它们原来是同时发生的。

但衍合也并不是完美无缺的,一句话可以总结这点:

永远不要衍合那些已经推送到公共仓库的更新

(全文完)


来源:中生代技术

原文链接

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
官网链接