hylerrix / university

:mortar_board: my university code & article collection: create & share, thought & works
Creative Commons Attribution Share Alike 4.0 International
45 stars 10 forks source link

[A19]充分了解 Git 并入手单人开发 #29

Open hylerrix opened 7 years ago

hylerrix commented 7 years ago

前注1:本文写作用时 5 小时 55 min,预计阅读时间 15 分钟。 前注2:本文基于学习型写作。查阅+写作+总结+迭代同步进行。


ARTICLE.INTRODUCTION.序


Git 作为以命令行为主的免费开源分布式版本控制系统,说简单也简单,说难也绝非易事。本文以快速上手 Git 单人开发为最终目标,贯穿讲解 Git 背景等相关知识,内容较长,却也循序渐进,以期达到融汇贯通的效果,不足之处还望指点。在看本文之前,你最好能够提前了解 Linux 基本命令的使用。


ABOUT.VCS.了解版本控制系统(VCS)


本地版本控制系统(LVCS)

为了解决复制整个项目目录的方式来保存不同的版本问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。

其中最流行的 RCS 的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。

甚至在流行的 Mac OS X 系统上安装了开发者工具包之后,也可以使用 rcs 命令。

集中化版本控制系统(CVCS)

接下来的问题是,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。这类系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。 

这种做法带来了许多好处:每个人都可以在一定程度上看到项目中的其他人正在做些什么,而管理员也可以轻松掌控每个开发者的权限。但考虑到中央服务器的单点故障、中心数据库所在的磁盘发生损坏等问题,整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。

分布式版本控制系统(DVCS)

于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 在这类系统中,像 Git、Mercurial、Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。 你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。


GET.START.GIT 简史


Git 的由来

同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。

Linux 内核作为参与人数众广的开源项目,绝大多数维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码,却于 2005 年被开发 BitKeeper 的商业公司结束合作关系,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linux Torvalds)基于使用 BitKcheper 时的经验教训,开发出自己的版本系统 -- Git。 他们对新的系统制订了若干目标来能高效管理类似 Linux 内核一样的超大规模项目(速度和数据量):

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

谁在使用 Git

以下是官网上列出的使用 Git 的著名公司、项目,但远不止这些。


DEEP.IN.深入Git


因此,Git 是分布式版本控制系统中很流行的一种, 和其它大部分系统以文件变更列表的方式存储信息对比:后者将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异,而 Git 更像是把数据看作是对小型文件系统的一组快照。

Git 对待数据更像是一个快照流。 

Git 的三个特点

Git 的三种状态

Git 系统上的文件有三种状态已修改(modified)和已暂存(staged)、已提交(committed)。

这里便是 git add, git commit, git push 的原理所在。

由此引入 Git 项目的三个工作区域的概念:

Git 的工作流程

可以用如下命令随时查看 Git 系统中文件的状态:

git status


USE.WAY.Git 使用方式


Git 有多种使用方式。 你可以使用原生的命令行模式,也可以使用 GUI 模式,这些 GUI 软件也能提供多种功能。以下分别介绍这两种模式,首推命令行模式。

使用 Git 的命令行工具【推荐】

只有在命令行模式下你才能执行 Git 的所有命令,而大多数的 GUI 软件只实现了 Git 所有功能的一个子集以降低操作难度。此外,由于每个人的想法与侧重点不同,不同的人常常会安装不同的 GUI 软件,但所有人一定会有命令行工具。

sudo yum install git
sudo apt-get install git

安装 Git 的 GUI 工具

这里只做 Github Desktop 的介绍和推荐,有关 Github 的相关知识点之后提及。

可以看出,Git 的图形化操作比命令行操作用起来要方便也更加直观。多平台的 Github Desktop 将 Git 的图形化操作发挥到了极致。

但重回之前所说,只有在命令行模式下你才能执行 Git 的所有命令,而大多数的 GUI 软件只实现了 Git 所有功能的一个子集以降低操作难度。Git 的 GUI 对使用分布式版本控制的开发者来说还无法普及。


PLATFORM.TOOL.基于 Git 的 Github 代码托管平台


GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub。GitHub 于 2008 年 4 月 10 日正式上线,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。目前,其注册用户已经超过350万,托管版本数量也是非常之多,其中不乏知名开源项目 Ruby on Rails、jQuery 等。

放入自己的开源项目,实战的同时还能帮助需要的人,多么有意义。


SINGLE.DEVELOPMENT.实战 Git 单人开发


于是到了实战环节,基于命令行模式,开始我们的 Git 单人开发。

01.配置 Git 基本信息

Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位置:

每一个级别覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。

在 Windows 系统中,Git 会查找 $HOME 目录下(一般情况下是 C:\Users\$USER )的 .gitconfig 文件。 Git 同样也会寻找 /etc/gitconfig 文件,但只限于 MSys 的根目录下,即安装 Git 时所选的目标位置。

如上图,安装好 Git 之后就应该去配置自己的用户名和邮箱号,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改。很多 GUI 工具都会在第一次运行时帮助你配置这些信息。

git config --global user.name "icorvoh"
git config --global user.email "icorvoh@qq.com"
git list

02.添加 Git 忽略文件

有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件,再比如Node 模块也是这样,只需把项目所需依赖名添加至 package.json 中,克隆到本地后本地安装依赖即可。

这就需要用到 Git 的 .gitignore 文件。Git 对于 .ignore 配置文件是按行从上到下进行规则匹配的,意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效。下面列举几个常用的配置语法。

.gitignore 文件一般不存在,需要在仓库根目录下手动创建。

03.创建本地仓库

选择工作目录创建仓库,这里命名为“gittest”。

mkdir gittest

进入 gittest 文件夹

cd gittest

04.初始化版本库

新建的文件夹并不是 git 仓库,需要在根目录里面进行初始化。

git init

初始化正常情况下应该得到如下命令行反馈:

Initialized empty Git repository in <Git 仓库文件所对应的绝对路径>

初始化成功会发现新的 Git 仓库中多出了一个 .git 隐藏文件夹。用编辑器打开可以看到,.git 隐藏文件夹是 git init 后在当前目录生成的一个管理 git 仓库的文件夹,这里存储着 git 你现在检出(checkout)的文件,当你在项目不同分支切换时,工作目录里的文件经常会被替换和删除。所有历史信息都保存在当前目录中。

05.添加与提交

之前的两步已经建立好了一个空的 Git 仓库,开始添加新文件。

touch README.md
git add README.md
git commit -m "init"

06.创建和删除分支

几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。和许多其他版本控制系统不同,Git 鼓励在工作流程中频繁使用分支与合并,哪怕一天之内进行许多次都没有关系。在创建仓库的时候,master 是“默认的”。在其他分支上进行开发,完成后再将它们合并到主分支上。

创建一个叫做“feature_x”的分支,并切换过去:

git checkout -b feature_X

切换回主分支:

git checkout master

再把新建的分支删掉:

git branch -d feature_x

分支的版本回退:

git reset HEAD~3

除非你将分支推送到远端仓库,不然该分支就是不为他人所见的:

git push origin master

07.更新与合并

要更新你的本地仓库至最新的改动,执行如下命令以在工作目录中获取(fetch)并合并(merge)远端的改动。

git pull

要合并其他分支到你的当前分支,可以执行如下命令。

git merge <branch>

两种情况下,git 都会尝试去自动合并改动。不幸的是,这可能导致产生需要自己手动合并的冲突(conflicts)。改完冲突后,执行以下命令标记合并成功:

git add <filename>

在合并改动之前,可以使用如下命令查看:

git diff <source_branch> <target_branch>

08.日志操作

大雁塔酒店。

git log

09.撤销与修改

用来撤销最后一次 git add files,你也可以用 git reset 撤销所有暂存区域文件。

git reset -- files

把文件从暂存区域复制到工作目录,用来丢弃本地修改。

git checkout -- files

通常,一个合并会产生一个合并提交(commit),把两个父分支里的每一行都合并起来。

git reset --hard HEAD

修改 git commit 时填错的错误信息

git commit --amend

但是,如果当前分支和另一个分支没有内容上的差异(即冲突),git就会执行一个"快速向前(fast-forward)"操作,git不会创建新的提交,只是将当前分支指向合并进来的分支。

10.生成 SSH 密钥

为了使本地仓库和远端安全链接,于是到了这一步。

SSH key提供了一种与 GitHub 通信的方式,通过这种方式,能够在不输入密码的情况下,将GitHub作为自己的remote端服务器,进行版本控制。分为如下三步。

11.创建远端仓库

这里用到的是对 Git 版本控制系统运用最好的全球最大代码托管平台兼同性交友平台 -- Github。创建好自己的 Github 账号并邮箱验证后便可以创建 Git 远端新仓库。这里创建一个和本地同名的仓库“gittest”。

12.绑定本地到远端仓库

由于之前的操作都只是本地的,没有对远端仓库进行任何绑定,需要如下命令。

git remote add origin https://github.com/icorvoh/gittest.git

这里链接到了我的 github 地址的 gittest 仓库。这时我的仓库是这样子的,什么都没有。

13.推送改动

在进行 git commit -m "" 后所有改动已经在本地仓库的 HEAD 中了。执行如下命令以将这些改动提交到远端仓库:

git push origin master

再次刷新 github 远端仓库,发现改动推送成功。

14.克隆远端仓库

从执行如下命令以创建一个本地仓库的克隆版本。

git clone /path/to/repository

如果是远端服务器上的仓库,你的命令会是这个样子。

git clone username@host:/path/to/repository


git 常用命令清单


git init # 初始化本地git仓库(创建新仓库)
git config --global user.name "xxx" # 配置用户名
git config --global user.email "xxx@xxx.com" # 配置邮件
git config --global color.ui true # 
git status等命令自动着色
git config --global color.status autogit config --global color.diff autogit config --global color.branch autogit config --global color.interactive autogit config --global --unset http.proxy # remove proxy configuration on gitgit clone git+ssh://git@192.168.53.168/VT.git # clone远程仓库
git status # 查看当前版本状态(是否修改)
git add xyz # 添加xyz文件至indexgit add . # 增加当前子目录下所有更改过的文件至index
git commit -m 'xxx' # 提交
git commit --amend -m 'xxx' # 合并上一次提交(用于反复修改)
git commit -am 'xxx' # 将add和commit合为一步
git rm xxx # 删除index中的文件
git rm -r * # 递归删除
git log # 显示提交日志
git log -1 # 显示1行日志 -n为n行
git log -5
git log --stat # 显示提交日志及相关变动文件
git log -p -m
git show dfb02e6e4f2f7b573337763e5c0013802e392818 # 显示某个提交的详细内容
git show dfb02 # 可只用commitid的前几位
git show HEAD # 显示HEAD提交日志
git show HEAD^ # 显示HEAD的父(上一个版本)的提交日志 ^^为上两个版本 ^5为上5个版本
git tag # 显示已存在的tag
git tag -a v2.0 -m 'xxx' # 增加v2.0的tag
git show v2.0 # 显示v2.0的日志及详细内容
git log v2.0 # 显示v2.0的日志
git diff # 显示所有未添加至index的变更
git diff --cached # 显示所有已添加index但还未commit的变更
git diff HEAD^ # 比较与上一个版本的差异
git diff HEAD -- ./lib # 比较与HEAD版本lib目录的差异
git diff origin/master..master # 比较远程分支master上有本地分支master上没有的
git diff origin/master..master --stat # 只显示差异的文件,不显示具体内容
git remote add origin git+ssh://git@192.168.53.168/VT.git # 增加远程定义(用于push/pull/fetch)
git branch # 显示本地分支
git branch --contains 50089 # 显示包含提交50089的分支
git branch -a # 显示所有分支
git branch -r # 显示所有原创分支
git branch --merged # 显示所有已合并到当前分支的分支
git branch --no-merged # 显示所有未合并到当前分支的分支
git branch -m master master_copy # 本地分支改名
git checkout -b master_copy # 从当前分支创建新分支master_copy并检出
git checkout -b master master_copy # 上面的完整版
git checkout features/performance # 检出已存在的features/performance分支git checkout --track hotfixes/BJVEP933 # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支
git checkout v2.0 # 检出版本v2.0
git checkout -b devel origin/develop # 从远程分支develop创建新本地分支devel并检出
git checkout -- README # 检出head版本的README文件(可用于修改错误回退)
git merge origin/master # 合并远程master分支至当前分支
git cherry-pick ff44785404a8e # 合并提交ff44785404a8e的修改
git push origin master # 将当前分支push到远程master分支
git push origin :hotfixes/BJVEP933 # 删除远程仓库的hotfixes/BJVEP933分支
git push --tags # 把所有tag推送到远程仓库
git fetch # 获取所有远程分支(不更新本地分支,另需merge)
git fetch --prune # 获取所有原创分支并清除服务器上已删掉的分支
git pull origin master # 获取远程分支master并merge到当前分支
git mv README README2 # 重命名文件README为README2
git reset --hard HEAD # 将当前版本重置为HEAD(通常用于merge失败回退)
git rebase
git branch -d hotfixes/BJVEP933 # 删除分支hotfixes/BJVEP933(本分支修改已合并到其他分支)
git branch -D hotfixes/BJVEP933 # 强制删除分支hotfixes/BJVEP933git ls-files # 列出
git index包含的文件
git show-branch # 图示当前分支历史
git show-branch --all # 图示所有分支历史
git whatchanged # 显示提交历史对应的文件修改
git revert dfb02e6e4f2f7b573337763e5c0013802e392818 # 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818
git ls-tree HEAD # 内部命令:显示某个git对象
git rev-parse v2.0 # 内部命令:显示某个ref对于的SHA1 HASHgit reflog # 显示所有提交,包括孤立节点
git show HEAD@{5}
git show master@{yesterday} # 显示master分支昨天的状态
git log --pretty=format:'%h %s' --graph # 图示提交日志
git show HEAD~3
git show -s --pretty=raw 2be7fcb476git stash # 暂存当前修改,将所有至为HEAD状态
git stash list # 查看所有暂存
git stash show -p stash@{0} # 参考第一次暂存
git stash apply stash@{0} # 应用第一次暂存
git grep "delete from" # 文件中搜索文本“delete from”git grep -e '#define' --and -e SORT_DIRENTgit gcgit fsck
  • Hello,我是韩亦乐,现任本科软工男一枚。软件工程专业的一路学习中,我有很多感悟,也享受持续分享的过程。如果想了解更多或能及时收到我的最新文章,欢迎订阅我的个人微信号:韩亦乐。简书的个人主页中,也有我的订阅号二维码和 Github 项目地址。
  • 本文内部编号“A0D”,同时采用【知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议】进行许可。