Git/GitHub 个人经验

我是从 2014 年 5 月 24 日 22:19 CST 起开始使用 GitHub 的。

Git 和 GitHub 是什么就不用介绍了,本文主要谈到目前为止,我使用 Git 和 GitHub 的一些经验分享,随着使用经验的丰富,将会继续补充。

文章最后我小小的发表一下我为什么喜欢 Git 和 GitHub。

说明:Git 本身也只是一种工具,如果只是想知道基本使用,5 分钟教程足够,但是 Git 除了工具本身,也是一份优秀的开源软件,出自 Linus Torvalds 之手,含金量懂的人都应该知道,其源代码也托管在 GitHub:Git

如果有兴趣深入 Git 高级使用和底层原理,还推荐 Pro Git Book

预备知识

1、什么是版本控制?

“版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。”

2、版本控制只能对软件源代码进行版本控制吗?

版本控制虽然常用于对软件源码的版本进行控制,但实际上可以对任何类型的文件进行版本控制。比如图片(图形设计师)、页面布局文件(网页设计师)。

3、版本控制有什么用?

版本控制系统会保存所有修订记录,这使得被版本控制的文件、项目等都可以回溯到之前的某个状态。此外,通过版本控制系统还可以比较文件的变化细节,因此可以用于查找问题的所在,修改的提交者,BUG 的报告者等等。使用版本控制恢复文件到原来状态所付出的工作量是微乎其微的。

4、什么是补丁?补丁有什么用?

文件修订的前后变化。通过应用所有补丁,可以计算出各个版本的文件内容。

5、为什么会出现 CVCS?

为了让不同系统上的开发者协同工作。

6、传统 VCS (本地)是如何保存版本更新记录的?这种方式有什么优缺点?

用数据库保存版本历次更新的差异。

Git 最基本的使用

  • Git 必须掌握的基本用法有哪些?

①获得 Git 仓库 – 仓库的克隆与初始化;

分支的合并

③用多种方式查看 Git日志;

④Git 正常工作流程与分布式工作流程;

⑤比较提交;

⑥Git 标签。

  • 安装好 Git 后首先必须做什么?如何进行初始化的配置?

首先必须配置 user.nameuser.email,它们将成为 commit 时的签名。主要代码为:

$ git config --global user.name "ckwongloy" 
$ git config --global user.email "ckwongloy@gmail.com" 

上述代码执行之后,将在 home directory 下创建一个 ~/.gitconfig 文件,里面记录着刚刚设置的用户信息。全局设置属性 --global 将会影响此用户建立的每个项目。上述命令不加 --global 也可执行,此时创建的是普通用户,新建用户将与全局用户区分,所有用户信息都保存在 ~/.gitconfig 文件中。

不配置以及不适用 git 协议 clone 时将在具体需要使用的情况提示你进行相关设置。


  • 获得仓库的途径有哪两种?各自具体怎么操作?两者有什么相同点和不同点?各自对本地系统有什么影响?

①从已有仓库中克隆:这种方式需要知道已有仓库的 URL,然后通过网络协议访问并获取拷贝。示例代码如下:

$ git clone git://git.kernel.org/pub/scm/git/git.git
$ git clone http(s)://git.kernel.org/pub/scm/git/git.git

以这种方式获得 Git 仓库后,Git 会把 Git URL 里 ".git" 的后辍去掉并做为新克隆项目在本地系统的目录名,如上述代码执行后将会在本机系统创建一个名为 git 的文件夹,该文件夹下就包含了复制过来的目标项目所有源代码和历史纪录。

②创建并初始化一个新的仓库以进行版本控制:假如想要将 project.tar.gz 压缩文件里的项目进行 Git 版本控制,示例代码如下:

$ tar xzf project.tar.gz
$ cd project
$ git init

上述代码执行后 Git 会输出: Initialized empty Git repository in .git。同时,project 目录下会有一个名为 ".git" 的目录被创建,这代表一个仓库被初始化了。

两种方式的不同点在于: 一个是间接从远程网络处取得仓库拷贝并自带 Git 的控制信息 ".git",不再需要初始化就能使用 Git 继续版本控制。而另一个是直接在本地将需要进行版本控制的项目用 Git 初始化然后再进行版本控制。

  • Git 有哪三种工作状态?

①已提交(commited);

②已修改(modified);

③已暂存(staged)。

“已提交表示数据已经安全的保存在本地数据库中。 已修改表示修改了文件,但还没保存到数据库中。 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。”

git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动”。

  • Git 仓库可以用哪些协议访问?具体怎么访问?各有什么不同?什么时候用什么协议?

sshhttp(s)git。git协议相对而言比较快速和有效,但有时候却不得不使用 http 协议,比如有时候防火墙会阻断非 http 请求。

  • Git 正常工作流程是怎样的?

①修改 file1,file2,file3 并将它们添加到缓冲区:

$ git add file1 file2 file3

add 用于文件跟踪,此命令执行后会将 file1,file2,file3 更新的内容添加到索引中,为 commit 他们做准备。

也可以通过 git add -Agit add * 来添加仓库中所有新建或者改动的文件到索引。

②查看有哪些文件将被提交:

$ git diff --cached

此命令只是将已经添加到索引中的文件提交,而且也只有已经添加到索引中的文件才能被提交,如果不加 cached 属性,则显示的是当前所有已做的但没有添加到索引中的修改。

③查看项目状态:

$ git status

④提交已缓存的修改:

$ git commit -m "some comments"

也可以用

$ git commit a

提交,这会把所有修改过的文件(但不包括新建文件)添加到索引同时并提交。

⑤为本次修改添加注释

添加注释的一个小技巧 开头用 50 个字符简要描述本次修改,然后空一行,接着再写修改详情,这样在用工具将注释转化为 email 通知时,自动将开头作为 email 标题,修改详情将作为 email 正文。

注释添加结束后,Git 就会记录一个新的项目版本

注意:必须要有注释,否则会出现推送到 GitHub 的时候出现 Git Bash 中提示成功但是 Github 上没有任何更新的情况

  • Git 跟踪文件有什么特别之处?

Git 跟踪文件改动的命令为 addadd 不仅可以添加已在版本控制中刚做过修改的文件,也可以添加不在版本控制中的新文件,添加两种情况的文件快照并把内容暂存到索引,以准备下次 commit。

  • Git 为什么需要分区?如何创建分区?如何查看分区?如何切换分区?如何删除分区?

分区多用于各种测试,协同开发时的合并。

①创建名为 “test” 的分区:

$ git branch test

②查看所有分区:

$ git branch

(master 为 Git默认创建的主分区,* 表示目前的工作所在的分区)。

③切换分区:

$ git branch checkout "branch_name"

④删除分区:

$ git branch -d branch_name (删除已经合并的分支)
$ git branch -D branch_name(强制删除某分支
  • 分支的切换是否影响到原分支下原文件的修改?或者说一个分支对文件的修改能否改变另一个分支的文件?

不能。

  • 为什么需要合并?什么是合并?如何合并两个分支?合并时冲突怎么办?如何撤销合并?什么是快速向前合并?

多人共同完成一个项目的情况是很普遍的,因此需要将多人的修改集中在一个项目里面,实现共同完成项目。合并就是将所需文件所在的子分支合到当前工作分支下,合并的对象是各分区下文件的改动。举例说明:合并 test 分支到当前工作分区:

$ git merge test

合并过程经常碰到合并冲突 当分支里面的文件修改出现冲突 ,即远程分支和本地分支修改文件的方式不同,则文件不能合并必须先解决:

①查看冲突

$ git diff

冲突的文件带有冲突标识符。合并失败时,git 会在索引和工作树设置特殊标识,并提示如何解决冲突。

②解决冲突: 重新编辑冲突文件,然后删除冲突标识以消除冲突。由于冲突文件已经保存在索引中,所以如果没有解决完冲突同时更新索引,则不可能提交成功。

③重新提交修改:

git commit a

(解决好冲突就按照正常流程添加然后提交该文件)。

④查看已合并内容:

$ gitk

以图形显示项目历史,会显示两个父分支,一个指向当前分支一个指向刚合并进来的分支。

·撤销合并:分 2 种情况下的撤销

①只合并而没提交

$ git reset --hard HEAD

则会放弃当前所有修改而回到合并前的状态;

②已合并并已经提交

$ git reset -- hard ORIG_HEAD 

·快速向前合并(fast forward):

一种特殊情况下的合并,即要合并的两个分支并没有内容上的差异,即某分支的每一次提交都已经存在于另一个分支之中,此时 Git 不会创建任何 commit 而只是将当前分支指向要合并进来的分支。 (通常情况是,一个合并会产生一个合并提交,将两个父分支的每一行内容都合并进来)

  • git pull 命令有什么作用和特点?与 git fetch 有什么相同点和区别?

pull 能从远程分支抓取修改的内容,然后合并到当前分支,而不管在命令中还指定了什么。pull 的目的仓库一般是个裸仓库,如果使用 pull 将修改推送到一个已签出工作树(已签出分支),结果是工作目录里的内容不会被修改同时还将导致不可预料的结果。 git fetch 只能从远程分支抓取修改的内容而不能合并。 它们的共同点是:如果命令的执行结果不是 “快速向前”就会报错。

  • 如何定义一个远程分支的别名缩写?这样做有什么用?

使用 git remote 命令,比如想要定义远程分支 /home/li/test_repo 的缩写为 li ,命令为:

$ git remote add li /home/li/test_repo

这样做下次操作该远程分支时只简写 li 就够了,对于经常操作的远程分支显得十分方便。比如想要从这个远程分支抓取修改的内容简写成这样就可以了:

$ git fetch li
  • 如何通过修改配置参数来给仓库地址起别名?

别名的作用往往是减少每次的书写量。为仓库地址起一个别名,可以在使用 git fetchgit pull 命令时只使用更精短的别名替代之。举例如下:

$cat >>.git/config <<EOF
[remote "pub-repo"]
url = ssh://yourserver.com/~you/ proj.git
EOF

然后就可以用 pub-repo 替代 url 的值了:

$ git push pub-repo master 
  • 代码推送失败了怎么办?失败的原因有哪些?

在使用 git push 上传修改时强制更新 只需在分支前面加一个 + 号:

$ git push ssh://yoursever.com/~you/project.git +master。

此外,如果推送结果不是 fast forward 就会报错:

error:remote is not an ancestor of local…failed…

产生这种情况的原因通常是:

①用 git -reset --head 删除了一个已经发布的提交。

②用 git -commit --amend 替换了一个已经发布的提交。

③用 git -rebase 去 rebase 了一个已经发布的提交。

此外,如果 commit 时没有注释,则会退出提交

  • 如何为标签添加注释?什么是标签对象?如何创建标签对象?如何为标签签名?

标签签名称为 tag message(和commit comment 很类似)。在使用 git tag 命令时,如果命令中没有指定 -m-F ,Git 则会自动打开编辑器等待用户为标签添加注释。

如果带有命令选项 -a-s-u 中的一个就会创建标签对象,一个标签对象需要一个标签注释。一个 git tag 命令被执行时,Git 就会向它的对象库中添加一个标签对象,引用该标签时就会指向该标签对象(而不是指向提交)。

创建标签对象

$ git tag -a stable-1 1b2e1d63ff。

标签对象可以指向任何对象,但大多数都是指向一个提交。

不过在 Linux 内核中,第一个标签对象指的是一个树对象。

为标签签名

有 GPG key 的,首先配置 GPG key – 在 .git/config 或者 .gitconfig 文件中配置 key:

[user]
signingkey = <gpg-key-id>

可以用命令行来配置:

$ git config (--global) user.signingkey <gpg-key-id> 

可以直接用”-s” 参数来创“签名的标签”:

$ git tag -s stable-1 1b2e1d63ff 

如果没有在配置文件中配 GPG key,你可以用 -u 参数直接指定

$ git tag -u <gpg-key-id> stable-1 1b2e1d63ff 

Personal FAQ

  1. permission denied publickey

解决:将 id_rsa 文件拷贝到 .ssh 即可。

  1. 为什么将本地改动推送到远程 GitHub 服务器后,GitHub 上仍然是以前的版本?

  2. 为什么只有在新建 GitHub 后才能 push 本地项目到该新建仓库成功?

  3. Git 如何在一个仓库内创建不同的发布版本?

  4. Git 如何使用 release?

在 GitHub 仓库页面手动创建即可。

  1. 为什么已删除的文件 push 到 github 后,仍然还有原来的文件存在?

这是因为已删除的文件依然被 git 保存在缓冲区中,需要额外执行 git rm 指令将不存在的文件删除,然后 commit -> push 。

  1. 如何批量删除已经删除的文件?

  2. 如何删除已经删除的文件夹?

Git 小细节

都是平时使用中不经意间发现的细节:

  1. 如果没有在项目文件夹根目录下执行 commit的话,提交的只是以当前目录为父目录及其以下的所有改动。

  2. 使用 $ git rm 删除某个文件的时候,如果当前文件夹中没有文件,那么该空文件夹也会被 Git 删除。

  3. 如果文件名有中文,那么如果不用命令而手动删除该文件后,git 记录中的该文件名会有双引号 ““,且无法再用 $ git rm 删除。

  4. Git 对文件的大小写不敏感,比如我将文章 2015-08-28-blog=Jekyll+Git+GitHub+Markdown.html 改为 2015-08-28-og=jekyll+git+gitHub+markdown.html 后,Git 还是会当作同一个文件对待。 ( 跟我在 Windows 下使用 Git 有关? )

  5. .git 文件夹的有无对 git 能否追踪文件无影响。

未完待续… …

我为什么喜欢上 Git/GitHub?

  • 可以很好地替我保存并展示项目

  • 程序员学习圣地

  • 无私开源精神

软件为什么需要开源?
  • 为了加速软件开发,并通过社区同行评审来提高代码质量

  • 为了最大化软件研究的知名度和影响

  • 为了提高软件的传播

  • GitHub 员工的高效率,这里讲个真实经历:

记得前段时间在折腾在 GitHub 上搭建 Blog 的时候,后来知道我的文章模板由于不符合 Jekyll 配置要求,于是收到了 GitHub page build failure 的出错邮件。

我按照给的链接进去找不到相关解释,折腾了半天,于是我回复邮件说能不能给我具体的出错位置。

然后一位叫 Rachel Berry 的 GitHub 员工,在我发完邮件大约 50 分钟后,就将具体的错误位置发给了我。

十分钟后,我找到问题所在并迅速解决了。

此外,以后每次出现 page build failure 的时候,GitHub 都会迅速邮件告诉我具体错误的位置,使我每次解决问题都很快。

所以我觉得,GitHub 公司是一家比较上心的技术,我不得不更加支持。

参考

本文最后修改时间: 2015年09月01日 10:46:04 (完) CC BY-NC-ND 3.0

若您发现文章中的错误,并愿告知于我,或想与我交流,我的联系方式在: Contacts


上一篇 《自学C语言》 听课笔记

All The Best

下一篇 键盘英语:键盘符号的读法