前言
本文是小编针对 David Mahler 在 Youtube 上对 Git 介绍进行的简单总结。Git是一种版本控制系统(version system control,简称VSC),它可以记录文件在一段时间内的变化。
Core Concepts
Commit Graph
commit graph(提交图)展示了我们的提交历史信息

Conceptual Area Diagram
Conceptual area diagram(概念区域图)为了便于理解Git在本地上的操作,我们需要了解下方的概念区域图

补充:
working tree 就是我们创建的netauto路径,还包含其中存在的任何子路径,但是不包含.git子路径
.git history 等价于提交图(commit graph), 它被放在隐藏文件.git中。
.git 路径包含构造repo需要的metadata和数据库对象,如果将 .git 发送给某个用户,那么该用户就有了整个 git 项目。
信息配置
# 配置全局用户姓名 |
基本操作
# 创建包含repository(简称repo)的.git路径 |

# 将working tree中的s1文件放到staging area中, 使其变成tracked/将被提交的文件 |

# 接下来创建s1的copy版本s2 |

# 查看staging area和working tree中tracked文件的差别 |

删除文件
# 查看日志的详细信息 |

# 我们编辑了s1之后当时后悔了,将staging area上的s1覆盖working tree 上的s1 |
恢复文件
# 通过日志中的恢复s2文件 |

reset恢复
reset是用来修改提交历史的,它会丢弃掉一些版本历史,而revert是根据历史指定的commit生成一个新的commit,版本的历史不会被破坏。下图分别展示了revert和reset回到提交历史C状态的方法

git reset配合不同的参数,会对三个区域产生不同的影响,比如
- git reset —soft:改变HEAD所指向的commit
- git reset —mixed:改变HEAD所指向的commit并且将staging area区域更新为HEAD所指向的commit里包含的内容(默认参数)
- git reset —hard:改变HEAD所指向的commit并且将staging area以及working tree区域更新为HEAD所指向的commit里包含的内容
下图说明配合不同参数的reset所产生的不同效果

# 找到指定提交信息的哈希码前几位,就可以将该次提交的后的代码恢复到working tree和staging area |
checkout恢复
下部分描述的checkout切换分支会做3件事
- HEAD指向切换分支的最后一次commit
- 将HEAD指向commit里所有文件的sanpshot替换掉staging area区域里面的内容
- staging area区域里面的内容替换到working tree中的内容
注: staging area 里面一直不仅仅是git add之后才有东西,提交之后仍然有,所以staging area里面一直有东西!!!
从上述checkout的执行路径可以看到和reset —hard很像,但是有2个重要差别
- reset会把working directory里面的所有内容替换掉,而checkout不会修改之前在working tree里面修改过的文件
- reset会把branch移动到HEAD所指向的地方,checkout则把HEAD移动到另外的分支,比如有两个分支master和HN,这两个分支指向不同的commit,现在处于HN分支上(HEAD所指向的地方),如果通过git reset master,那么HN就会指向master所指向的commit,如果git checkout master, 那么develop不会动,只有HEAD会移动,HEAD指向master

# 带文件参数的checkout |
reset与checkout的比较

其中head列中的“REF”表示该命令移动了HEAD指向的分支引用,而“HEAD”则表示移动了HEAD自身,wd safe列中的YES表示不会动在work directory上的修改,而NO代表会动在work directory上的修改
补充
# 1. 将staging area中的s1文件变成unstaged/untracked文件,或者说仅删除staging area中的s1,一般在编辑.gitignore的时候可能会用到,因为有时不小心会将本该忽略的文件变成了tracked文件 |
Branch 和 Merge
Branch就是指向提交信息的指针,head 是告诉我们目前切换到哪里的指针,git 会自动创建第一个叫master的分支。
创建分支
git branch SDN # 创建叫SDN的分支 |

ps:图中绿色的分支表明HEAD指针正在指向master分支
切换分支
# 切换到SDN的分支 |


# 在 SDN 分支中对 s1 文件进行编辑之后要进行 commit |

# stage 和 commit 同时进行的快捷方式 |

补充
- 每次切换到一个分支,working area 和 staging area 中的文件都会更新成当前分支下的文件内容
合并分支
fast-forward merge
分支 B 包含分支 A 的所有历史提交信息,换句话说两个分支在一条路径上,对应的 merge 叫 fast-forward merge
# 目标是将master分支merge到SDN分支上 |

3-way merge
分支 B 和分支 A 位于不同的路径上,换句话讲就是从一个分支出发,分成不同路径的分支,再合并到成分支的 merge 方式 叫 3-way merge
# 用3-way merge的方式需要产生新的commit, 一般commit就用系统默认的信息就行 |


删除分支
# 查看合并的分支有哪些(用于判断之后删除哪些分支) |

# 删除SDN分支 |

ps: 如果非要删除没有合并的分支,就可以采用
git branch -D SDN |
Merge 冲突
Merge冲突一般发生在我们在合并分支的时候,不同的分支下同一个文件中同一行内容有着不同的修改
构造冲突
# 创建和切换分支的快捷方式 |

# stage并且commit修改后的s1文件 |

修改前的s1文件内容和两个修改后的s1文件内容比较

从比较图可以看出:
- 相比于base版本的mgmt_ip,master上进行了修改,但是dev上没有进行修改,那么合并的结果就是修改的结果
- 相比于base的vlans,dev上的是green,而master上的是pink,也就是在两个分支上修改了相同的行,这样就产生了第1个冲突
- 相比于base的第1个ports,dev和master进行了相同的修改,所以没有冲突
- 相比于base的第2个ports,dev进行了修改,但是master进行了删除,所以这就产生了第2个冲突
# 将可能产生冲突的分支进行merge |

git status |

# 终止合并(前提是我们的working tree 和 staging area是干净的) |

解决冲突
# 重新进行merge |

我们将其中的冲突修改掉,变成这样婶儿的👇

# 将解决冲突后的s1进行add |

# 将staging area中的s1提交, 提交信息用系统提供的就可以了 |

Detached HEAD
如果HEAD指针没有只想某个分支,而是志向某次commit,我们就是有一个detached HEAD state(分离的头指针状态)
# 根据之前的graph/git log 我们可以找到创建s2的commit对应的sha-1 hash,因此可以将HEAD切换到相应的commit |

# 查看当前的HEAD指针的位置 |

# 创建一个 stage branch |

从上图可以看到创建了一个新的分支,但是HEAD指针并没有指向该分支
git checkout stage |

从上图可以看到HEAD指针不再是detached状态
Git stash
每次前换分支或者merge分支的时候,我们的working tree 和 staging area 都是 clean 的状态,但是有时我们可能没有clean state,那么就可能会出错
# 首先切换回master分支 |

从上图结果可以看到系统针对没有clean state会提供两种方法,第二种方法就是stash(贮藏)
# 快速clean state |

stash之后我们就可以随意checkout与merge分支了
# 我们可以继续进行修改文件然后stash |

# 再次应用最近一次stash的内容 |

# 如果想要指定的stash,而不是最近一次的stash,就使用stash list中的最前面的label来调用相应的内容,最新一次的stash的label数是最小的 |

# 如果想要删除最近的一次stash |
Remotes
Remote 就是另外一个地方的repo, 这个地方可以是github上的repo,也可以是自己机器上的其他repo
GitHub连接
# 将 github 上的 MLStockPrediction.git repo copy/retrieve 到本地 |
Remote
# 查看远程连接有哪些 |

从上图可以看到,origin是我们第一次远程连接repo地址的默认别名
# 构建git提交图的别名 |

从上图可以出了本地的master以外,还多了origin/master,它是remote-tracking branch,这个分支表示origin上的master情况,换句话说github上的master分支和本地的master分支正在指向同一个commit
# 我们不可以再本地checkout到remote-tracking branch |

Fetch 和 Merge
# 我们在github上创建一个新的文件s1并提交, 但是本地不知道最新的提交,接下来需要在本地上获取github上最新的提交信息/更新后的repo |

从上图可以看到本地branch落后于origin/master
# 我们将origin/master mergin into 本地master |

# 查看最新状态 |

# 将最新的commit信息上传/推到github上 |

Fork
假如另外一个同学A也想要加入该repo中,但是他没有向repo写/push的权利,其中一种解决办法就是使用fork,fork可以创造repo的copy版本到同学A的github中,之后同学A就可以通过gi t clone 在本地对该repo进行编辑
添加Remote
# 添加一个额外的远程仓库的完整地址以及相应的别名 |

# 删除某个remote |

# 在新的分支"edit-s2"上编辑s2之后,只将新的分支推到github上 |
补充
当从远端pull代码的时候用origin的前提是远端只有一个master分支,如果有多个分支的话需要指定pull具体的某个分支
git pull & git pull —rebase
假如我们fetch下来的commit graph是有两个分支情况,比如它这样婶儿的👇

将两个分支进行合并有两种方法git merge:将两个分支合并并保留之前每个分支的提交信息

git rebase:将两个分支合并并删除之前mytask所在的分支记录

# git pull = git fetch & git merge
git pull origin
# git pull --rebase = git fetch && git rebase
git pull --rebase origin master
# 如果有冲突的话, 解决冲突后
git add .
# add之后千万不需要commit!!!而是进行下述操作
git rebase --continue