Git原理与情景化【一】

Git原理与情境化

Git前置知识

Git的功能介绍

我们经常会听到Git,说能够管理代码、管理工作流程等等。那么通过这些我们可以知道,Git其实就是一个管理器,不过,是一个分布式的管理器,管理本地的文件还有服务器上的远程的文件,可以让本地与远程连接。这样我们可以将代码推送到远程完成共享。下面简单介绍一下Git的主要功能。

首先是最主要的功能。Git仓库的文件管理功能,Git会建立文件版本管理。本地上有工作目录、暂存区、本地仓库,通过内容寻址,完成版本迭代管理、新建分支然后基于分支重新开发等功能。(插入相关的原理)

其次,Git另外一个功能就是创建远程仓库,一个远程仓库可以供多个人协作,然后在本地分支开发完成之后推送到远程的仓库供大家协作开发。(这个部分需要一些指令)

基于远程仓库还有Git的分支特性定制相关的工作流,每个公司都不一样的,有Git Flow工作流。

提交过程

请添加图片描述

  • 工作目录就是我们执行git init的目录所在,也是我们执行文件操作的地方。新建的文件会出现在这里。

  • 暂存区和本地仓库都是在.git目录,两者就是用来存取数据的

  • 远程仓库在服务器上的代码仓库,可以拉取文件还有推送文件。

git所存储的都是一系列的文件快照,然后git来跟踪这些文件快照,发现哪个文件快照有变化它就会提示你需要添加到暂存区或是提交到本地仓库来保证你的工作目录是干净的——所谓干净意思就是和暂存区、本地仓库保持一致。

所以,git中的文件有几个状态:

  • 文件被跟踪状态,也就是已经提交到本地的仓库的文件——已经被记住了

  • 文件未被跟踪,比如你在工作区新建一个文件,因为本地仓库没有相关信息——没有被记住,是外来的。

对于被跟踪的文件我们可以使用git status查看当前的状态——是否发生改变需要提交等等,当然,我们也可以指定不跟踪哪些文件,新建一个.gitignore文件,里面指定要忽略的文件类型。

文件存储方式

请添加图片描述

Git首先将工作目录的文件全部记录,形成一个个blob对象——对应文件快照,只要文件内容相同,那就是一个blob,然后通过tree的结构将blob对象组织起来,最后用链表将树串联形成链式关系。

其中,几个对象之间的关系:工作区的文件会记录到tree中,然后commit会把tree放进链表中。

请添加图片描述

所以,我们可以知道,一个文件修改后,会产生两个blob,修改次数越多,产生的blob就越多。但是,并不是说修改越多内存占用越大,Git往磁盘保存对象有自己的方式。

Git 往磁盘保存对象时默认使用的格式叫松散对象 (loose object) 格式。Git 时不时地将这些对象打包至一个叫 packfile 的二进制文件以节省空间并提高效率。当仓库中有太多的松散对象,或是手工调用 git gc 命令,或推送至远程服务器时,Git 都会这样做.

将功能按层次分解来介绍

请添加图片描述

常用用法:

# 查看工作区和暂存区的状态
$ git status
# 将工作区的文件提交到暂存区
$ git add .
# 提交到本地仓库
$ git commit -m "本次提交阐明"
# add和commit的合并,便捷写法(未追踪的文件无奈间接提交到暂存区/本地仓库)
$ git commit -am "本次提交阐明"
# 将本地分支和远程分支进行关联
$ git push -u origin branchName
# 将本地仓库的文件推送到远程分支
$ git push
# 拉取远程分支的代码
$ git pull origin branchName
# 合并分支
$ git merge branchName
# 查看本地领有哪些分支
$ git branch
# 查看所有分支(包含远程分支和本地分支)
$ git branch -a
# 切换分支
$ git checkout branchName
# 长期将工作区文件的批改保留至堆栈中
$ git stash
# 将之前保留至堆栈中的文件取出来
$ git stash pop
初始化与工作目录
初始化 git init
配置与别名
# 配置全局用户
$ git config --global user.name "用户名"
$ git config --global user.email "git账号"
# 配置别名
$ git config --global alias.co checkout
$ git config --global alias.ss status
$ git config --global alias.cm commit
$ git config --global alias.br branch
$ git config --global alias.rg reflog
# 这里只是丑化 log 的输入,理论应用时能够在 git lg 前面加命令参数,如:git lg -10 显示最近10条提交
$ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
# 删除全局配置
$ git config --global --unset alias.xxx
$ git config --global --unset user.xxx
查看
# 查看系统配置
$ git config --list
# 查看用户配置
$ cat ~/.gitconfig
# 查看以后我的项目的 git 配置
$ cat .git/config
# 查看暂存区的文件
$ git ls-files
# 查看本地 git 命令历史
$ git reflog
# 查看所有 git 命令
$ git --help -a
# 查看以后 HEAD 指向
$ cat .git/HEAD
日志
# 查看提交历史
$ git log --oneline
          --grep="关键字"
          --graph
          --all
          --author "username"
          --reverse
          -num
          -p
          --before=  1  day/1  week/1  "2019-06-06"
          --after= "2019-06-06"
          --stat
          --abbrev-commit
          --pretty=format:"xxx"

# oneline -> 将日志记录一行一行的显示
# grep="关键字" -> 查找日志记录中(commit提交时的正文)与关键字无关的记录
# graph -> 记录图形化显示 !!!
# all -> 将所有记录都具体的显示进去
# author "username" -> 查找这个作者提交的记录
# reverse -> commit 提交记录程序翻转
# before -> 查找规定的工夫(如:1天/1周)之前的记录
# num -> git log -10 显示最近10次提交 !!!
# stat -> 显示每次更新的文件批改统计信息,会列出具体文件列表 !!!
# abbrev-commit -> 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符 !!!
# pretty=format:"xxx" ->  能够定制要显示的记录格局 !!!
# p -> 显示每次提交所引入的差别(按 补丁 的格局输入)!!!
# *示意一个commit |示意分支后退 /示意分叉 |/示意新分支
git stash
git stash
git pull
git stash pop
git push

介绍一下git stash和git stash pop

git stash可以将当前的修改保存起来,并使得代码回退到之前的clean状态

git stash pop将之前保存的修改释放出来,与当前版本合并,可能会引发冲突,需要手动解决冲突

# 将所有未提交的批改(提交到暂存区)保留至堆栈中
$ git stash
# 给本次存储加个备注,以防工夫久了忘了
$ git stash save "存储"
# 存储未追踪的文件
$ git stash -u

# 查看存储记录
$ git stash list

在 Windows 上和 PowerShell 中,须要加双引号
# 复原后,stash 记录并不删除
$ git stash apply "stash@{index}"
# 复原的同时把 stash 记录也删了
$ git stash pop "stash@{index}"
# 删除 stash 记录
$ git stash drop "stash@{index}"
# 删除所有存储的进度
$ git stash clear
# 查看以后记录中批改了哪些文件
$ git stash show "stash@{index}"
# 查看以后记录中批改了哪些文件的内容
$ git stash show -p "stash@{index}"
工作目录到暂存区
git status

查看工作区和暂存区的状态

git add
# 增加指定文件到暂存区(追踪新增的指定文件)
$ git add [file1] [file2] ...
# 增加指定目录到暂存区,包含子目录
$ git add [dir]
# 增加当前目录的所有文件到暂存区(追踪所有新增的文件)
$ git add .
# 删除工作区/暂存区的文件
$ git rm [file1] [file2] ...
# 进行追踪指定文件,但该文件会保留在工作区
$ git rm --cached [file]
# 改名工作区/暂存区的文件
$ git mv [file-original] [file-renamed]
git rm
# 删除暂存区和工作区的文件
$ git rm filename
# 只删除暂存区的文件,不会删除工作区的文件
$ git rm --cached filename
暂存区到本地仓库
git commit
# 将暂存区的文件提交到本地仓库并增加提交阐明
$ git commit -m "本次提交的阐明"

# add 和 commit 的合并,便捷写法
# 和 git add -u 命令一样,未跟踪的文件是无奈提交下来的
$ git commit -am "本次提交的阐明"

# 跳过验证持续提交
$ git commit --no-verify
$ git commit -n

# 编辑器会弹出上一次提交的信息,能够在这里批改提交信息
$ git commit --amend
# 修复提交,同时批改提交信息
$ git commit --amend -m "本次提交的阐明"
# 退出 --no-edit 标记会修复提交但不批改提交信息,编辑器不会弹出上一次提交的信息
$ git commit --amend --no-edit
git tag
# 默认在 HEAD 上创立一个标签
$ git tag v1.0
# 指定一个 commit id 创立一个标签
$ git tag v0.9 f52c633
# 创立带有阐明的标签,用 -a 指定标签名,-m 指定阐明文字
$ git tag -a v0.1 -m "version 0.1 released"

# 查看所有标签
# 留神:标签不是按工夫程序列出,而是按字母排序的。
$ git tag

# 查看单个标签具体信息
$ git show <tagname>

# 推送一个本地标签
$ git push origin <tagname>
# 推送全副未推送过的本地标签
$ git push origin --tags

# 删除本地标签
# 因为创立的标签都只存储在本地,不会主动推送到远程。
# 所以,打错的标签能够在本地平安删除。
$ git tag -d v0.1
# 删除一个远程标签(先删除本地 tag ,而后再删除远程 tag)
$ git push origin :refs/tags/<tagname>
本地仓库到远程仓库
  • 为什么有 origin/master ? 这个其实就是远程分支的命名规范:

    / 即为 远程仓库的名字/分支的名字,大多数人的主要远程仓库命名为origin,因为当我们使用clone的时候,Git默认帮我们把远程仓库的名称设置为origin了

pull & push [起源地:目的地]
# 将本地仓库的文件推送到远程分支
# 如果远程仓库没有这个分支,会新建一个同名的远程分支
# 如果省略远程分支名,则示意两者同名
$ git push <远程主机名> <本地分支名>:<远程分支名>
$ git push origin branchname

# 如果省略本地分支名,则示意删除指定的远程分支
# 因为这等同于推送一个空的本地分支到远程分支。
$ git push origin :master
# 等同于
$ git push origin --delete master

# 建设以后分支和远程分支的追踪关系
$ git push -u origin master
# 如果以后分支与远程分支之间存在追踪关系
# 则能够省略分支和 -u
$ git push

# 不论是否存在对应的远程分支,将本地的所有分支都推送到远程主机
$ git push --all origin

# 拉取所有远程分支到本地镜像仓库中
$ git pull
# 拉取并合并我的项目其余人员的一个分支
$ git pull origin branchname
# 等同于 fetch + merge
$ git fetch origin branchName
$ git merge origin/branchName

# 如果远程主机的版本比本地版本更新,推送时 Git 会报错,要求先在本地做 git pull 合并差别,
# 而后再推送到远程主机。这时,如果你肯定要推送,能够应用 –-force 选项
# (尽量避免应用)
$ git push --force origin | git push -f origin
git fetch
管理
git branch

我想基于这个提交以及所有父提交进行新的工作

# 查看本地分支
$ git branch | git branch -l
# 查看远程分支
$ git branch -r
# 查看所有分支(本地分支+远程分支)
$ git branch -a
# 查看所有分支并带上最新的提交信息
$ git branch -av
# 查看本地分支对应的远程分支
$ git branch -vv

# 新建分支
# 在别的分支下新建一个分支,新分支会复制以后分支的内容
# 留神:如果以后分支有批改,然而没有提交到仓库,此时批改的内容是不会被复制到新分支的
$ git branch branchname
# 切换分支(切换分支时,本地工作区,仓库都会相应切换到对应分支的内容)
$ git checkout branchname
# 创立一个 aaa 分支,并切换到该分支 (新建分支和切换分支的简写)
$ git checkout -b aaa
# 能够看做是基于 master 分支创立一个 aaa 分支,并切换到该分支
$ git checkout -b aaa master

# 新建一条空分支(详情请看问题列表)
$ git checkout --orphan emptyBranchName
$ git rm -rf .

# 删除本地分支,会阻止删除蕴含未合并更改的分支
$ git branch -d branchname
# 强制删除一个本地分支,即便蕴含未合并更改的分支
$ git branch -D branchname
# 删除远程分支
# 推送一个空分支到远程分支,其实就相当于删除远程分支
$ git push origin  :远程分支名
# 或者
$ git push origin --delete 远程分支名

# 批改以后分支名
$ git branch -m branchname
git merge
git merge  要合并进来的分支           # 合并指定分支到当前分支

git merge --abort               # 取消当前合并,重建合并前状态

git merge dev -Xtheirs          # 以合并dev分支到当前分支,有冲突则以dev分支为准

# 可以通过HEAD指针merge相关代码之后,最后合并master
git merget
git rebase

这个是衍合,就是取出一系列提交(一整串),增加到某一个分支上

git rebase 要合并的分支 被合并的分支
git rebase 要合并进来的分支名称
git remote
# 查看所有远程主机
$ git remote
# 查看关联的远程仓库的详细信息
$ git remote -v
# 删除远程仓库的 “关联”
$ git remote rm projectname
# 设置远程仓库的 “关联”
$ git remote set-url origin <newurl>
git diff
# 查看工作区和暂存区单个文件的比照
$ git diff filename
# 查看工作区和暂存区所有文件的比照
$ git diff
# 查看工作区和暂存区所有文件的比照,并显示出所有有差别的文件列表
$ git diff --stat
# 留神:
# 1.你批改了某个文件,然而没有提交到暂存区,这时候会有比照的内容
# 一旦提交到暂存区,就不会有比照的内容(因为暂存区曾经更新)
# 2.如果你新建了一个文件,然而没有提交到暂存区,这时候 diff 是没有后果的

# 查看暂存区与上次提交到本地仓库的快照(即最新提交到本地仓库的快照)的比照
$ git diff --cached/--staged
# 查看工作区与上次提交到本地仓库的快照(即最新提交到本地仓库的快照)的比照
$ git diff branchname
# 查看工作区与 HEAD 指向(默认以后分支最新的提交)的比照
$ git diff HEAD

# 查看两个本地分支中某一个文件的比照
$ git diff branchname..branchname filename
# 查看两个本地分支所有的比照
$ git diff branchname..branchname
# 查看远程分支和本地分支的比照
$ git diff origin/branchname..branchname
# 查看远程分支和远程分支的比照
$ git diff origin/branchname..origin/branchname

# 查看两个 commit 的比照
$ git diff commit1..commit2
git checkout
# 复原暂存区的指定文件到工作区
$ git checkout <filename>
# 复原暂存区的所有文件到工作区
$ git checkout .

# 回滚到最近的一次提交
# 如果批改某些文件后,没有提交到暂存区,此时的回滚是回滚到上一次提交
# 如果是曾经将批改的文件提交到仓库了,这时再用这个命令回滚有效
# 因为回滚到的是之前本人批改后提交的版本
$ git checkout HEAD
$ git checkout HEAD -- filename
# 回滚到最近一次提交的上一个版本
$ git checkout HEAD^
# 回滚到最近一次提交的上2个版本
$ git checkout HEAD^^

# 切换分支,在这里也能够看做是回到我的项目「以后」状态的形式
$ git checkout <以后你正在应用的分支>
# 切换到某个指定的 commit 版本
$ git checkout <commit_id>
# 切换指定 tag
$ git checkout <tag>
git reset

针对暂存区和工作区——只用于本地的修改

# 从暂存区撤销特定文件,但不扭转工作区。它会勾销这个文件的暂存,而不笼罩任何更改
$ git reset <fileName>
# 重置暂存区最近的一次提交,但工作区的文件不变
$ git reset
# 等价于
$ git reset HEAD (默认)
# 重置暂存区与工作区,回退到最近一次提交的版本内容
$ git reset --hard
# 重置暂存区与工作区,回退到最近一次提交的上一个版本
$ git reset --hard HEAD^

# 将以后分支的指针指向为指定 commit(该提交之后的提交都会被移除),同时重置暂存区,但工作区不变
$ git reset <commit>
# 等价于
$ git reset --mixed  <commit>

# 将以后分支的指针指向为指定 commit(该提交之后的提交都会被移除),但放弃暂存区和工作区不变
$ git reset --soft  <commit>
# 将以后分支的指针指向为指定 commit(该提交之后的提交都会被移除),同时重置暂存区、工作区
$ git reset --hard  <commit>
git revert

这个针对远程的撤销,比如原来的提交是c1,然后我提交了上去变成了c2,但是我执行revert撤销,本质上是在c2后面追加生成一个新提交c2’,这个c2’和c1的内容是一样的。

# 生成一个撤销最近的一次提交的新提交
$ git revert HEAD
# 生成一个撤销最近一次提交的上一次提交的新提交
$ git revert HEAD^
# 生成一个撤销最近一次提交的上两次提交的新提交
$ git revert HEAD^^
# 生成一个撤销最近一次提交的上n次提交的新提交
$ git revert HEAD~num
# 生成一个撤销指定提交版本的新提交
$ git revert <commit_id>
# 生成一个撤销指定提交版本的新提交,执行时不关上默认编辑器,间接应用 Git 主动生成的提交信息
$ git revert <commit_id> --no-edit
git submodule子模块
# 在主我的项目中增加子项目,URL 为子模块的门路,path 为该子模块存储的目录门路
git submodule add [URL] [Path]
# 克隆含有子项目的主我的项目
git clone [URL]
# 当你在克隆这样的我的项目时,默认会蕴含该子项目的目录,但该目录中还没有任何文件
# 初始化本地配置文件
git submodule init
# 从以后我的项目中抓取所有数据并检出父我的项目中列出的适合的提交
git submodule update
# 等价于 git submodule init && git submodule updategit submodule update --init
# 主动初始化并更新仓库中的每一个子模块, 包含可能存在的嵌套子模块git clone --recurse-submodules [URL]
冲突
逻辑冲突

git自动处理(合并/应用补丁)成功,但是逻辑上是有问题的。

比如另外一个人修改了文件名,但我还使用老的文件名,这种情况下自动处理是能成功的,但实际上是有问题的。

又比如,函数返回值含义变化,但我还使用老的含义,这种情况自动处理成功,但可能隐藏着重大BUG。

这种问题,主要通过自动化测试来保障。所以最好是能够写出比较完备的自动化测试用例。

这种冲突的解决,就是做一次BUG修正。不是真正解决git报告的冲突。

内容冲突

两个用户修改了同一个文件的同一块区域,git会报告内容冲突。这种也比较常见.

树冲突

文件名修改造成的冲突,称为树冲突。

比如,a用户把文件改名为a.c,b用户把同一个文件改名为b.c,那么b将这两个commit合并时,会产生冲突。

$ git status
added by us: b.c
both deleted: origin-name.c
added by them: a.c

如果最终确定用b.c,那么解决办法如下:

git rm a.c
git rm origin-name.c
git add b.c
git commit

执行前面两个git rm时,会告警“file-name : needs merge”,可以不必理会。

树冲突也可以用git mergetool来解决,但整个解决过程是在交互式问答中完成的,用d 删除不要的文件,用c保留需要的文件。

最后执行git commit提交即可。

  1. 首先是merge同一个文件的时候,代码发生修改产生冲突

情境化操作

建立本地分支自己尝试

1. 远程新建仓库

2. 本地设置账户邮箱

3. 当前文件夹初始化 git init

4. 开始创建与修改 touch read.md

5. git add .

6. git commit -m "first try"

7. git remote add origin https://gitee.com/me.git

8. git push -u origin "master"  #将本地的推送到远程中去

这个时候如果想在别的地方拿下自己的代码

1. #新建文件夹
2. git init
3. git clone + URL  #克隆到本地
4. #开发中
5. git add .
6. git commit -m "second try"
7. git push -u origin "master"

8. git push 就可以了
#如果是拿下来
1. git pull origin

本地代码已经开发一天了,然后第二天想拉取包含同事提交的最新的代码

这个时候是什么情况呢?就是我的本地仓库已经发生变更,然后,远程仓库上面同事也已经推送上去了,我需要先合并同事的提交,依据对方的提交来完成我接下来的开发。

着眼于远程仓库还有本地仓库变化来看,首先远程仓库比我们之前拉取下来时,多了一个提交(这个时同事开发完提交的)——发生了历史偏离;另外,我们的远程仓库比当时拉取下来多了一个提交(这个提交是我们开发完的,沿着这个开发接下来需要和同事的代码相互配合)。

所以,首先需要更新我的本地仓库,确保和远程的一样:

# 第一个方法是git pull
git pull
# 或者
git pull --rebase
#这个方法会将远程仓库那个同事提交的而你又没有的提交下载下来,然后和你的新提交合并
# 相当于 fetch 然后 rebase

#也可以使用merge
git fetch origin 远程分支:本地分支
git merge 本地分支 origin/远程分支

这样我们的分支就和远程同步了,可以继续开发,开发完提交到远程仓库

# 开发中
git add .
git commit -m "second day"
git push

突然出现一个想法,我想试试,但是我不想改变我原本的代码

这个时候可以新建分支,然后修改之后测试,如果测试结果是自己想要的效果,可以讲分支合并,或者直接rebase——取出相应一系列提交合并到某一个分支上。

git branch 新分支名字
git checkout 分支名字

#或者
git checkout -b 新分支名字

# 然后开始开发

我推送到远程分支,但是发现自己弄错了,怎么补救

针对重置的问题,在本地仓库的应该使用git reset , 在远程仓库应该使用git revert。

git reset --hard #如果回退到上一次提交之前,使用这个

# 回退远程仓库的只能是:生成一个撤销最近一次提交的新提交——相当于撤销了修改了
git revert HEAD
#这个命令会在本地完成回退,需要重新提交
git push
# 这样子远程就修改过来了

我发现有人更改我负责部分的代码并且推送到远程仓库,让我的本地仓库与远程仓库不一样而无法完成推送

这个部分是文件冲突,拉取不下来是因为本地仓库和和远程仓库不一样,文件冲突了,需要解决掉这个冲突才可以完成推送。一般冲突会拉取到本地,解决冲突之后再推送上去。

一般发生冲突而无法pull时,

git stash  #会将当前的保存起来,回退到之前clean的状态
git pull
git stash pop  #将之前保存的修改释放出来,与当前版本合并
#这里可能会报冲突,就在这里直接解决冲突就行了,冲突解决完再push上去就可以了

git push

我想将本地的foo分支推送到远程的master分支,或者远程的master分支拉取到本地的foo分支

这部分属于远程跟踪分支,可以自己指定源分支还有目标分支

# 自己指定分支映射
git branch -u origin/master foo #本地的foo会跟踪远程的master
#也可以通过这样跟踪
git checkout -b foo origin/master # 新建foo并且跟踪远程的master


git pull origin master:foo # 拉取远程的master分支到本地的foo分支上
#如果本地没有这个foo分支,就会自动新建

git fetch origin :foo #本地没有foo会在本地创建一个foo分支

git push origin foo:master # 推送本地的master到远程的foo分支上,而远程没有这个分支会自动新建
git push origin :foo #不指定参数会将远程仓库上的foo删除

参考

git原理

方法介绍

git方法总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值