0%

Git学习笔记

前言

本文是小编针对 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 项目。

信息配置

# 配置全局用户姓名
git config --global user.name "huangning"

# 配置全局用户邮箱
git config --global user.email "huangning@123.com"

# 配置局部用户姓名(给某个指定的repo单独配置姓名)
git config --local user.name "huangninglocal"

# 配置局部用户邮箱(给某个指定的repo单独配置邮箱)
git config --local user.email "huangninglocal@qq.com"

# 查看配置列表
git config --list

基本操作

# 创建包含repository(简称repo)的.git路径
git init

# 编辑完新建的s1文件之后查看当前分支以及working tree 和 staging area 中文件的状态
git status

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

# 将staging area中的文件提交到history中,并且提交信息为add s1
git commit -m "add s1"

# 同上,但是可以将提交信息编辑成多行的形式
git commit

# 提交日志,其中最新的提交显示在最顶端
# 提交信息包括: 哈希码、用户名、邮箱、提交时间以及提交描述
git log

# 同上,但是获取和某个文件相关的日志信息
git log -- s1:显示和s1相关的提交日志

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

# 编辑s2并且修改s1
gti status

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

# 将working tree中的多个文件s1, s2放到staging area中, 使其变成tracked/将被提交的文件
git add s1 s2

# 将working tree中首字母为s的文件放到staging area中, 使其变成tracked/将被提交的文件,这个过程较stage
git add s*

# 将working tree中的所有文件放到staging area中
git add .

# 查看staging area 和 history中最近一次commit文件的差别
git diff --staged

删除文件

# 查看日志的详细信息
git log -p

# 从working tree和staging area中同时删除s2
git rm s2

git status

# 我们编辑了s1之后当时后悔了,将staging area上的s1覆盖working tree 上的s1
git checkout -- s1

恢复文件

# 通过日志中的恢复s2文件
# 首先找到从之前提交的信息中找到与s2有关的日志
git log -- s2

reset恢复

reset是用来修改提交历史的,它会丢弃掉一些版本历史,而revert是根据历史指定的commit生成一个新的commit,版本的历史不会被破坏。下图分别展示了revertreset回到提交历史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
git reset --hard 38c5029b

# 恢复到指定提交节点时之后的提交信息就会丢失,如果想要回到之后的提交节点,我们reflog来找到之后的提交信息
git reflog

# 找到指定提交信息的哈希码之后,通过git reset回到之后的提交节点
git reset --hard 98abc5a

# 带文件参数的reset是没有--hard和--soft这两个参数,只有--mixed参数
# 当把s1文件stage到staging area中后悔了,那么只需要将该文件恢复到最近一次commit的状态(也就是HEAD)
git reset s1
git reset HEAD s1
git reset --mixed HEAD s1

# 将文件恢复到历史版本上
reset xxxxx(哈希码) s1
# 这样可以将某个文件只恢复到staging area中,然后commit就相当于把该文件恢复到历史版本了,这样都不需要去修改working tree了

checkout恢复

下部分描述的checkout切换分支会做3件事

  1. HEAD指向切换分支的最后一次commit
  2. 将HEAD指向commit里所有文件的sanpshot替换掉staging area区域里面的内容
  3. staging area区域里面的内容替换到working tree中的内容

注: staging area 里面一直不仅仅是git add之后才有东西,提交之后仍然有,所以staging area里面一直有东西!!!

从上述checkout的执行路径可以看到和reset —hard很像,但是有2个重要差别

  1. reset会把working directory里面的所有内容替换掉,而checkout不会修改之前在working tree里面修改过的文件
  2. reset会把branch移动到HEAD所指向的地方,checkout则把HEAD移动到另外的分支,比如有两个分支master和HN,这两个分支指向不同的commit,现在处于HN分支上(HEAD所指向的地方),如果通过git reset master,那么HN就会指向master所指向的commit,如果git checkout master, 那么develop不会动,只有HEAD会移动,HEAD指向master

# 带文件参数的checkout
# 找到指定提交信息的哈希码前几位, 将s2恢复到working tree和staging area
git checkout 38c5029b -- s2

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文件
git rm --cached s1
# 2. working tree 中不包括 .git 子目录
# 3. git分支可视化
alias graph="git log --all --decorate --oneline --graph"
# 4. 通过编辑 .gitignore 使 working tree 中指定的文件不需要tracked

Branch 和 Merge

Branch就是指向提交信息的指针head 是告诉我们目前切换到哪里的指针,git 会自动创建第一个叫master的分支。

创建分支

git branch SDN   # 创建叫SDN的分支
git branch auth # 创建叫auth的分支
git branch # 查看目前有哪些分支

ps:图中绿色的分支表明HEAD指针正在指向master分支

切换分支

# 切换到SDN的分支
git checkout SDN

# 在 SDN 分支中对 s1 文件进行编辑之后要进行 commit
# commit 和 add 可以写在一行,中间用 ";" 隔开
git add s1; git commit -m "SDN for s1"

# 在SDN分支中的文件中进行了编辑之后,切换到auth的分支
git checkout auth

# stage 和 commit 同时进行的快捷方式
# 将 tracked 且被修改的文件 add 到 staging area 并且被commit
git commit -a -m "auth for s1"

补充

  • 每次切换到一个分支,working area 和 staging area 中的文件都会更新成当前分支下的文件内容

合并分支

fast-forward merge

分支 B 包含分支 A 的所有历史提交信息,换句话说两个分支在一条路径上,对应的 mergefast-forward merge

# 目标是将master分支merge到SDN分支上
# 查看master分支和SDN分支上的不同
git diff master..SDN

# 将 master分支merge到SDN分支上
git merge SDN

3-way merge

分支 B 和分支 A 位于不同的路径上,换句话讲就是从一个分支出发,分成不同路径的分支,再合并到成分支的 merge 方式 叫 3-way merge

# 用3-way merge的方式需要产生新的commit, 一般commit就用系统默认的信息就行
git merge auth

删除分支

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

# 删除SDN分支
git branch -d SDN

# 删除auth的时候会报错,因为我们之前并没有对该分支进行merge
git branch -d SDN

ps: 如果非要删除没有合并的分支,就可以采用

git branch -D SDN

Merge 冲突

Merge冲突一般发生在我们在合并分支的时候,不同的分支下同一个文件中同一行内容有着不同的修改

构造冲突

# 创建和切换分支的快捷方式
git checkout -b dev

# 修改文件s1之后查看一下working tree和staging area中文件的区别
git diff

# stage并且commit修改后的s1文件
git commit -a -m "update s1 VLANS"

# 切换到master分支
git checkout master

# 修改master分支上的s1文件中内容, 查看分支情况
graph

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

从比较图可以看出:

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

git status

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

# 查看当前状态
git status

解决冲突

# 重新进行merge
git merge dev

# 查看现在文件s1的内容
vi s1

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

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

# 查看冲突情况
git status

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

# 查看分支情况
graph

Detached HEAD

如果HEAD指针没有只想某个分支,而是志向某次commit,我们就是有一个detached HEAD state(分离的头指针状态)

# 根据之前的graph/git log 我们可以找到创建s2的commit对应的sha-1 hash,因此可以将HEAD切换到相应的commit
git checkout e724aef

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

# 创建一个 stage branch 
git branch stage

graph

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

git checkout stage

从上图可以看到HEAD指针不再是detached状态

Git stash

每次前换分支或者merge分支的时候,我们的working tree 和 staging area 都是 clean 的状态,但是有时我们可能没有clean state,那么就可能会出错

# 首先切换回master分支
git checkout master

# 对master中的s1文件进行修改,并且要切换分支
git checkout stage

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

# 快速clean state
git stash

stash之后我们就可以随意checkout与merge分支了

# 我们可以继续进行修改文件然后stash
# 查看stash的的列表
git stash list

# 查看每次stash保存的修改记录和之前没修改的区别
git stash list -p

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

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

# 为了更好的识别每一次stash的内容,可以添加相应的注释
git stash save "add yellow vlan"

# 如果想要删除最近的一次stash
git stash pop

# 删除指定label的stash
git stash pop stash@{2}

Remotes

Remote 就是另外一个地方的repo, 这个地方可以是github上的repo,也可以是自己机器上的其他repo

GitHub连接

# 将 github 上的 MLStockPrediction.git repo copy/retrieve 到本地
git clone git@github.com:HuangNing616/MLStockPrediction.git

# 配置指定repo/Local的git username
git config --local user.name "huangStock"
git config --local user.email "huangStock@stock.com"

Remote

# 查看远程连接有哪些
git remote

# 查看完整地址的远程连接有哪些
git remote -v

从上图可以看到,origin是我们第一次远程连接repo地址的默认别名

# 构建git提交图的别名
alias graph="git log --all --decorate --oneline --graph"

graph

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

# 我们不可以再本地checkout到remote-tracking branch
git checkout origin/master

Fetch 和 Merge

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

# 查看目前的状态
git status

从上图可以看到本地branch落后于origin/master

# 我们将origin/master mergin into 本地master
git merge origin/master

# 在本地修改文件s1之后做一次最新的提交
git commit -a -m "modify s1"

# 查看分支提交情况
graph

# 查看最新状态
git status

# 将最新的commit信息上传/推到github上
git push origin master

# 查看分支提交情况
graph

Fork

假如另外一个同学A也想要加入该repo中,但是他没有向repo写/push的权利,其中一种解决办法就是使用fork,fork可以创造repo的copy版本到同学A的github中,之后同学A就可以通过gi t clone 在本地对该repo进行编辑

添加Remote

# 添加一个额外的远程仓库的完整地址以及相应的别名
git remote add upstream git@github.com:HuangNing616/JavaNote.git

# 查看remote情况
git remote -v

# 删除某个remote
git remote remove upstream

# 在提交信息中增加名称叫upstream的remote
git fetch upstream

# 创建一个新的分支
git checkout -b "edit-s2"

# 查看所有本地分支
git branch

# 查看所有远程分支
git branch -r

# 查看包括远程分支的所有分支
git branch -a

# 在新的分支"edit-s2"上编辑s2之后,只将新的分支推到github上
git push origin edit-s2

补充

  • 当从远端pull代码的时候用origin的前提是远端只有一个master分支,如果有多个分支的话需要指定pull具体的某个分支

  • git pull & git pull —rebase

    假如我们fetch下来的commit graph是有两个分支情况,比如它这样婶儿的👇


    将两个分支进行合并有两种方法

    1. git merge:将两个分支合并并保留之前每个分支的提交信息

    2. 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
  1. Introduction to Git - Core Concepts
  2. Introduction to Git - Branching and Merging
  3. Introduction to Git - Remotes