https://github.com/sawyerbutton/git_from_intro_to_jump-out-of_pit
https://github.com/sawyerbutton/git_from_intro_to_jump-out-of_pit
Last synced: 5 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/sawyerbutton/git_from_intro_to_jump-out-of_pit
- Owner: sawyerbutton
- License: mit
- Created: 2018-11-07T01:45:10.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-11-09T06:52:17.000Z (over 7 years ago)
- Last Synced: 2025-02-07T04:41:03.182Z (over 1 year ago)
- Size: 326 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Git_From_Intro_To_Jump-Out-Of_Pit
## Git从入门到跳坑
### Git是什么
- Git是一个开源的分布式版本管理系统
> 将这一描述拆分成不同的部分来看
- 控制系统
> 控制系统代表着Git是一个内容跟踪器。这意味着Git可以用来存储内容,而根据Git提供的其他功能来看,Git最常见的用途是存储代码
- 版本控制系统
> 存储在Git中代码将随着代码内容的增加而增加,不同的开发者可以并行地增加Git中的代码,而版本控制系统帮助去处理维护修改和变更的记录
- 分布式版本管理系统
> Git拥有一个存储在服务器上的远程仓库和一个存储在每个开发者机器上的的本地仓库。 这意味着代码并不仅仅存储在中央服务器中,完全的代码拷贝也存在于所有开发者的电脑中。 这也是为什么Git被称为`分布式`版本管理系统的原因
### 为什么Git是需要的
- 现实生活中项目通常有多个开发者并行开发,而Git这样的分布式版本管理工具可以保证在开发者之间没有存在代码冲突
- 除此之外,现实生活中需求的变化也是很正常的状况,版本控制系统允许开发者返回/`回退`到旧版本的代码中去
- 而有时几个并行运行的项目涉及相同的代码库,在这种情况下,Git中的`branch`将会大放异彩
### 如何使用Git
#### 下载Git
- [使用这个链接以获取多操作系统安装Git的教程](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
> 安装完成后使用下述命令验证Git是否安装成功
```bash
git --version
```
#### 创造第一个本地Git仓库
- 在本地创建一个文件夹,比如`simple-git-repo`
- 进入文件夹中并初始化一个本地Git仓库,比如
```bash
cd simple-git-demo
git init
```
- `git init`指令将会初始化出一个本地仓库
- 在本地创建一个`demo.ts`文件, 并向其中添加内容如`const demo = '我是Demo'`
> 创建什么类型的文件在此时并不重要,`ts`文件也可以被替换为`js`,`html`,`java`文件
#### 准备(staging)和提交(commiting)代码
- `staging`原本的含义是舞台,这里我意指为准备就绪
- 提交是将代码添加到本地仓库的的过程
- 在提交代码之前,需要将被提交的代码放置在准备区,而准备区是跟踪了所有需要被提交的代码的地方
- 任何不处于准备区的代码都不会被提交到本地代码库中。换言之这给与了开发者选择性提交内容的能力
> 放置`demo.ts`文件到准备区
```bash
git add demo.ts
```
> 假设你不仅仅有`demo.ts`文件也有其他一些文件`file1`,`file2`,`file3`, 而希望将他们都放置进准备区
```bash
git add file1 file2 file3
```
> 假设你希望将文件夹中所有的文件都放置进准备区(需要注意的是,他会将`所有`的`文件和文件夹`都放入准备区)
```bash
git add .
```
#### Git状态和Git记录
- 修改`demo.ts`文件并增加一行内容`const demo2 = '我是Demo2'`
```typescript
const demo = '我是Demo';
const demo2 = '我是Demo2';
```
> 使用`git status`可以观察到什么文件发生了变化以及在准备区的文件以及一些其他信息(之后再说)
```bash
git status
```
> 控制台将会展示`demo.ts`文件已经被修改但是并没有在准备区就绪
```bash
modified: demo.ts
```
> 使用下述指令将`demo.ts`文件添加至准备区并提交他们
```bash
git add demo.ts
git commit -m "demo.ts file has been modified"
```
- 使用`log`指令可以打印出所有迄今为止的提交
```bash
git log
```
> 事实上,`log`将会显示每个提交的作者,提交的日期和相应的提交消息
#### Git分支
- 默认情况下,`git commit`将会作用于`master`分支,但是什么是`master`分支?
> 分支是指向Git仓库中最新提交的指针
> 换言之,上述操作中的主分支为指向了第二次提交`demo.ts`文件修改的指针
> 分支在多开发者平行开发时是不可或缺的

- 初始化时,提交1和提交2在主分支上完成,在提交2完成后创建了一个新的自分支,提交3和提交4同时分别在主分支和自分支上分别完成
- 主分支和子分支在提交2之后分道扬镳,两者分别拥有不同的代码
- 子分支可以通过`merge`命令合并到主分支中
##### 创建一个本地Git分支
- 使用如下命令创造一个`test`分支
```bash
git branch test
```
- 上述指令只是创造了新的`test`分支,此时我们仍然处在主分支中,通过`checkout`指令可以切换到`test`分支中
```bash
git checkout test
```
- 当需要查看所有的本地分支时可以使用如下指令
```bash
git branch
```
##### 在test分支上修改一些内容
- 在test分支上的`demo.ts`文件中增加内容`const demo3 = '我是test分支的demo'`
- 就绪并提交上述修改
```bash
git add demo.ts
git commit -m "test branch commit"
```
- 上述的提交在`test`分支上完成,现在`test`分支拥有两次提交,比主分支多一次提交,
- 值得记住的是,可以使用`git log`指令查看你在当前分支上的提交记录
#### 合并
- 经过上述的操作,`test`分支先于`master`分支一个提交,现在如果希望将`test`分支的代码带会`master`分支,`git merge`指令就可以大显神威
> 将`test`分支的内容合并到`master`分支中
```bash
git checkout master
git merge test
```
> 再执行上述操作后,如果两个分支之间没有冲突的话,分支的合并已经完成了
> 但是在真实的项目中,可能会存在代码冲突的问题,如何解决冲突?没有方法论,当你越来越多地使用git后,解决冲突将逐渐变得简单轻松
> 现在执行`git log`你会发现主分支现在也拥有了3个提交
### 远程Git仓库
- 迄今为止一直在对本地Git仓库进行处理,但是最终,这些本地仓库的代码将会上传到一个远程仓库中(Gihub,Gitee,Gitlab)
- 一旦代码上传到远程仓库,其他开发者就可以查看和修改这些代码

#### Github
- 作为世界上最大的代码库,作为开发者应当将Github当做自己的另一个本地仓库来维护
- 如何使用Github
1. 前往[Github官网]( https://github.com/)并注册账号
2. 注册成功后,点击`Start a Project`按钮以创建一个新的Git远程仓库
3. 进去仓库后会看到一个连接形如`https://github.com/your_name/your_repo.git`
4. 为了将本地仓库与远程仓库搭建起联系需要使用指令
```bash
git remote add origin [repository url]
```
5. 当本地仓库和远程的连接建立起来后,可以使用如下指令将本地仓库中代码推送到远程仓库中
```bash
git push -u origin master
```
> 这次推送(从含以上来说是上传)会将本地`master`分支上的代码上传到远程仓库中的`master`分支上
##### 一些简单的远程Git指令
- `git pull`指令将会拉取远程代码库中最新的代码到本地代码库中
- `git clone`指令用来复制一个既存的远程仓库到你的本地电脑中,这在尝试学习他人的代码和思路时非常有用
```bash
git clone [repository url]
```
### Git工作流程
- 当有多个开发人员共同参与项目时,正确地遵循Git的工作流程就变得十分重要了
- 假设现在有一个中型项目,一个比较合理的Git流程是

> 配合上述的流程讲述一个故事
- 你是公司的一个开发组长,公司需要你开发一个简化版的淘宝商城专门售卖公司产品
- 整个开发组有四名成员
1. 开发人员1: 一年开发经验
2. 开发人员2: 一年开发经验
3. 开发人员3: 三年开发经验
4. 你: 开发组长
#### 基于Git的开发流程
##### 主分支(Master Branch)
1. 主分支总是应当保持对产品代码的一份拷贝
2. 没有人能被允许直接对主分支进行代码的编写,主分支只应当作为产品代码的拷贝
3. 可以实际编写的代码应当位于其他分支
##### 发布型分支(Release Branch)
1. 当项目被创建的伊始创建一个发布型分支是很重要的,发布型分支创建于主分支
2. 所有和这个项目相关的代码都应该存在于发布型分支,发布型分支从创建操作上并没有什么区别,只是多了一个`release`前缀
3. 在同一代码库上有可能运行多个项目;因此,对于每个项目,都会创建一个单独的发布型分支.假设还有一个并行运行的项目,该项目有一个单独的发布型分支比如`release/project2`
4. 拥有发布型分支的原因是相同的代码库可以并行运行多个项目而项目之间不会存在冲突
##### 功能型分支(Feature Branch)
1. 每当一个新的功能在项目中被创建时都应当创建一个新的功能型分支, 这保证了每个功能都能被独立构建
2. 功能型分支本质上与其他分支无二,只是多了一个前缀`feature`
3. 现在,作为开发组长的你要求开发人员1开发一个登录功能,而这就应当创建一个新的功能型分支`feature/login`, 开发人员1对该功能编写的代码将全部保存于这个分支之中
4. 开发人员2得到一个任务构建一个用户购物车功能,所以他会创建一个新的功能型分支`feature/shoppingCart`
5. 开发人员3被要求开发一个客服与客户对话的功能,所以他需要创建一个功能型分支名为`feature/businessChat`
6. 当前,保证了所有开发者的代码都保存在了其自己创建的功能型分支之中
7. 过了一段时间,开发人员1完成了对于`login`功能的开发,他需要将他的代码从`feature/login`分支发送到`release/project1`分支上, 而这一过程可以通过`pull request`实现
##### Pull Request
- 首先值得注意的是,不要将`pull request`与`git pull`弄混淆了
- 开发人员1不被允许直接将`feature/login`分支的代码push到`release/project`分支上,作为开发组长的你应该在归并该功能前审核`feature/login`的代码,而这一过程则通过`pull request`实现
1. 开发人员1提起了一个`pull request`请求在GitLab上

2. 一旦上述完成,开发人员1需要输入`pull request`的标题和描述,最后点击`提交合并请求`,开发人员1还需要为此拉取请求分配审查人,由于你是开发组长,他将你标记为审查人

3. 开发组长将会审核`feature/login`的代码并在审核完成后将其代码合并到`release/project`分支
##### 代码冲突😠
1. 开发人员2也完成了他的功能部分,并且提起了一个`pull request`请求将`feature/shoppingCart`分支合并到`release/project`分支
2. 由于`release/project`分支已经具有`feature/login`代码,因此发生代码冲突,审查人有责任解决这些代码冲突并合并代码,在这种情况下,你作为开发组长需要解决这些代码冲突并合并代码
3. 开发人员3最后也完成了他的部分,作为一个有经验的开发者,他已经熟知如何处理代码冲突的问题. 他将最新的`release/project`通过`git pull`或者`git merge`合并到自己的分支`feature/businessChat`中并解决了出现的所有冲突,此时`feature/businessChat`分支也包含了`release/project`中的所有代码
4. 最后开发人员3提交了一个`pull request`, 因为冲突已经被提前处理掉了,可以直接将审核后`feature/businessChat`分支内容合并到`release/project`分支中
> 大致上有两种方式解决代码冲突
1. 审查`pull request`的审查人员处理冲突的代码
2. 开发人员确保将发布型分支中的最新代码合并到功能型分支中并自行解决冲突、
### 回归到主分支
- 项目完成后,发布型分支中的代码将会合并到主分支并将代码进行生产部署中
- 因此,生产部署中的代码和主分支中的代码始终保持同步,这也确保了对于任何未来的项目,主分支都能提供最新的代码
### 如何处理混乱的Commit
- 提交里存在一些错误的时候该怎么修复
- 提交历史一团糟应该怎么办,能把提交记录变得更整洁吗
#### 场景1
- 你提交了一堆刚刚写好的文件,但是却在提交完成后意识到输入的提交信息实际上并不清楚足以表达这些提交的信息
- 现在你希望可以修改上一次提交的信息,那么
```bash
git commit --amend -m "new Commit Message"
```
#### 场景2
- 你希望提交六个文件,但是因为某种原因你只提交了五个文件
- 你当然可以选择新建一个提交将第六个文件提交上去,从方法论上这固然没有任何错误
- 但是从维护一个整洁的提交记录的角度考虑,如果能将第六个文件添加到之前的提交中去就更加合理了,那么
```bash
git add file6
git commit --amend --no-edit
```
- `--no-edit`指令意味着提交信息并不进行修改
#### 场景3(虽然并不常见)
- 当在进行提交的时候,每个提交都有一个作者名和作者email作为伴随信息
- 通常来说,当你初次设定Git时这些就会配置好,不需要再提交时担心这些基本的信息
- 但对于特定项目可能希望使用不同的电子邮件和作者,那么需要使用以下命令为该项目配置电子邮件和作者名
```bash
git config user.email “your email id”
```
- 不幸的是,你在初始化项目时也遗忘了配置上述信息并且已经进行了首次提交
- 遇到这种情况时,`amend`命令也可以帮助你解决问题,其可用于更改先前提交的作者
```bash
git commit --amend --author "Author Name "
```
#### 警惕
> 永远只在本地使用`amend`指令,在远程仓库使用`amend`指令会造成不可预期的提交记录混乱
#### 以前的提交记录都是一团乱麻应该怎么办
- 假设你正在编写一段代码并估计大约需要十天才能完成,而在这十天内其他开发人员也将提交代码到远程仓库
- 将本地仓库的代码与远程仓库中的代码保持同步是一种很好的实践,这会在提出`pull request`时避免很多合并冲突
- 因此,你决定每两天从远程存储库中`pull`一次更改以保持本地仓库的更新
- 每次将代码从远程仓库提取到本地仓库时,都会在本地存储库中创建新的`merge commit`,这意味着您的本地提交历史记录将会进行大量的`merge commit`这会使审查人在审核代码时感到困惑
- 你的Git工作流程可能是

- 可以看得出你的提交记录一团乱麻,这时候`rebase`方法就可以起到他的作用了
- 继续用一个例子来说明,先说流程和做法

1. 发布型分支拥有3次提交记录
2. 功能型分支创建于发布型分支的首次提交之后,拥有一次提交记录`发布型提交1`
3. 在功能型分支上创建了两次属于自己的提交记录
4. 目标是将发布型分支的另外两次提交记录并入功能型分支中
5. 假设两个分支的名字分别为`release`和`feature`,使用如下指令进行`rebase`
```bash
git checkout feature
git rebase release
```
- `rebase`的目标是确保`feature`分支获取`release`分支的最新提交
- `rebase`将会挨个逐个添加提交并检查冲突是否存在
- 很抱歉这听起来比较难懂,还是配合图解来说明`rebase`的干了什么

- 步骤1
1. 运行上述命令的那一刻,Feature分支指向Release分支的头部
2. 现在功能分支拥有3个提交,发布型提交1,2,3
3. 你可能会好奇功能型提交1,2在哪里,他们将在之后被使用
- 步骤2
1. Git尝试将功能型提交1添加到功能型分支上
2. 如果在添加功能型提交1到发布型分支3之后没有冲突则添加成功
3. 如果存在存在冲突,那就需要手动解决冲突并在解决后执行下述命令继续`rebase`的流程
```bash
git add fixedfile
git rebase --continue
```
- 步骤3
1. 一旦功能型提交1添加到发布型分支3之后,就像步骤2一样,继续讲功能型分支2添加到功能型分支1之后
2. 如果存在冲突,Git将会通知你解决冲突,之后执行步骤2的指令
3. 在整个`rebase`流程结束后,整个功能型分支将拥有五条提交记录,按顺序分别是`发布型分支1,发布型分支2,发布型分支3,功能型分支1,功能型分支2`
#### 对于merge和rebase的一些探讨
1. `merge`和`rebase`都是git的一部分,两者虽然功能类似但是都拥有更适合的应用场景
2. `merge`指令将会产生一个合并提交,而`rebase`并不产生类似的新的和并提交
3. 在使用远程仓库中的最新代码更新本地代码存储库时,使用`rebase`
4. 在处理`pull request`以将功能型分支与发布型或主分支合并时使用`merge`
5. 使用`rebase`将会修改提交记录,当然初衷是将提交记录整理整洁,但是这也会造成一定的风险
6. 永远不要对远程仓库进行`rebase`操作,你可能会让其他开发者无法识别远程仓库的提交记录或者影响他们`pull`远程仓库最新的代码
### Git错误的打开方式
- 我无法提交代码到远程仓库了,让我做一个`force push`
- 让我在远程仓库上运行`rebase`,使提交历史更整洁
- 让我运行`amend`修改远程仓库中的历史提交
- 上述提到的行为将会对使用Git进行开发的团队造成困扰,下面就来说说Git的错误打开方式及其缘由
#### 强制push到远程仓库
- 假设有两个开发者a,b同时作用于一个branch而b是一个使用git的新手
1. a完成了他的代码并`push`到了远程branch上
2. b也完成了他的代码但是希望`push`到远程branch时失败了
3. b快速搜索了一下网络发现了一条指令`git push -f`并强行将代码提交到了远程branch上
4. a在浏览完成branch时发现他的代码全都不见了,非常恼火
> 这是因为`git push -f`将会强行复写远程仓库上的代码,现存的代码将会被因此丢失
- 对于b开发者的正确git打开方式为
1. 从远程仓库`pull`下最新的代码,并使用`rebase`指令修正提交记录
2. `rebase`完成后,开发者2就可以将自己的代码`push`到远程仓库了
> 需要时刻记住,只在没有其他方法可以处理的情况下使用`force push`,请将其作为最后的手段
> `force push`将覆盖远程存储库中的代码
> 如果所有的开发者遵循合理的Git工作流程,使用`force push`的情况应当永远不会发生
#### 尝试rebase远程仓库
- 还是之前的开发者a和b
1. a完成了一系列的提交并将代码`push`到了远程的功能型分支
2. b`pull`了远程功能型分支的最新代码
3. b也完成了一系列对于本地功能型分支的提交
4. 现在b突发奇想希望将发布型分支的最新代码`rebase`到功能型分支上,这本质上是从远程到本地不同分支的一次`rebase`
5. 昨晚这一切b开心地希望将功能型分支的`push`到远程仓库中,但是Git不允许他这么做因为提交记录已经出现了变化,b遂使用了`force push`
6. a回到工作位希望`pull`下功能型分支的新代码,但是他发现整个提交记录就像晋西北一样乱成了一锅粥,他必须小心谨慎地对待各种代码冲突,哪怕在b的角度这些冲突已经在`rebase`的过程中解决了
> `rebase`远程仓库将改变提交历史记录,并对其他开发人员尝试从远程存储库中提取最新代码时产生困扰
- 作为一个开放者,应永远只在本地进行`rebase`操作,不应将任何本地`rebase`之后的提交记录`push`到远程分支
- 如果已经存在某些提交`push`到了远程功能分支上,使用`merege`而不是`rebase`实现合并的需求,因为`merege`并不会修改提交历史
- 事实上如果按照合理的Git工作流程,只有一个人维护和操作一个功能型分支,那么这样的问题应当不会出现
- 如果只有一个人操作该分支,没有其他开发者去染指该功能型分支,那对远程分支使用`rebase`从结果上看并不会有任何问题,但是从原则上来考虑永远不要对远程分支进行`rebase`操作
#### 尝试修改远程仓库的提交记录
- 又是a和b两个开发者的故事
1. a完成了一个提交记录并将其`push`到了远程分支上,假定该提交记录为`commit 1`
2. b照常`pull`了远程分支的最新记录到本地仓库上
3. b一直都在编写功能并没有将代码提交和`push`到远程上
4. a突然意识到上次的`commit 1`有一个问题,于是修改了本地的提交记录`commit 1`为`commit 2`
5. a希望将本地修改过的记录提交到远程分支上,但是Git并不允许他这么做,因为远程分支已经包含了一个提交记录,于是他也使用了`force push`的方式将新的提交`push`到了远程分支上
6. 现在b希望`pull`一下最新的远程分支上的代码,Git察觉到存在两条不同的提交记录,于是创造了一个`merge commit`,完成合并后b发现本地出现了两条提交记录`commit 1`和`commit 2`,这样的状况完全违背了`amend`操作的初衷
7. 即使b使用了`rebase`重新规划了分支的提交记录,`commit 1`仍然出现在b的本地提交记录中
> `amend`提交会更改提交历史记录,因此当其他开发人员尝试从远程仓库中提取最新代码时,修改远程存储库中的提交会产生困惑的状况
> 最佳实践是仅在本地仓库中修改提交,一旦提交到远程仓库中,最好不要进行任何修改
> 事实上遵循合理的Git工作流程这样的状况将不会发生,如果只有一个人负责一个功能型分支的话,这样的操作也不会产生对他人的困扰
#### 硬重置
- 硬重置用来将代码会溯到提交记录中的某个提交节点
- 硬重置通过指令`git reset --hard`执行
- 除了`hard`类型的`reset`外还有`--soft`和`--mixed`两种重置方法,这两种的危险程度远低于`--hard`
> 假设开发者1正在编写功能型分支并且在本地仓库中有五个提交记录
1. 开发者1正在编写两个文件,并且这两个文件还没有被提交
2. 此时他执行一个指令`git reset --hard `将当前代码状态回滚到`commit 4`的时间点
3. 此时本地分支中的最新提交记录为`commit 4`,而`commit 5`的提交记录丢失了
4. 之前正在编写的两个文件的内容也随之丢失了
5. 开发者1对自己的鲁莽行为感到极度懊恼
6. 万幸的是,通过查阅资料了解到`commit 5`并没有真正意义上消失,而只是丢失对了对于其的引用,通知执行`git reflog`还是可以重新拿到`commit 5`的提交记录的
> 在Git中使用硬重置命令时要非常小心,可能在某些情况下必须使用重置以达成目标,但在继续硬重置之前最好评估一下是否含有其他的解决方案
### 如何遵循Git合理的工作流程
1. 在上面的列表中观察到的一个常见问题是当多个人在同一个分支上工作时会出现问题
2. 使用正确的Git工作流程将确保一次只有一个人在一个功能分支上工作
3. 发布分支将由技术主管或高级开发人员处理,遵从此工作流程可以防止发生一些重大问题。
4. 默认情况下,Git确保开发者不能在远程仓库中进行任何破坏性更改,因此不要违背Git的设计原则
5. 在非常小的项目中使用Git工作流程可能有些杀鸡焉用牛刀,在这些项目中,多个开发人员将在同一分支上工作,虽然一切可能都不那么复杂但是在对远程仓库进行任何重大更改之前,最好先评估一次该操作是否会影响其他开发人员