Git原理与常用命令

一、版本控制系统

1.Git-分布式版本控制系统

由于分布式版本控制系统每一个节点都是独立的系统,所以工作的时候不需要联网。安全性高,某一个电脑坏了可以从其他节点复制,节点间可以相互推送协作。分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

2. SVN-中央版本控制系统

版本库是集中存放在中央服务器的,所以要先从中央服务器取得最新的版本,然后开始工作,完成后推送给中央服务器,必须联网才能工作,受网速的限制,安全性较差,中央服务器出问题会影响所有人的工作

二、Git对象模型

1. SHA

表示项目历史信息的文件,是通过一个40个字符“对象名”索引的:

6ff87c4664981e4397625791c8ea3bbb5f2279a3

每一个“对象名”都是对“对象”内容做SHA1哈希计算得来的,这样就意味着两个不同内容的对象不可能有相同的“对象名”,Git只要比较对象名,就可以很快的判断两个对象是否相同,因为在每个仓库的“对象名”的计算方法都完全一样,如果同样的内容存在两个不同的仓库 中,就会存在相同的“对象名”,Git还可以通过检查对象内容的SHA1的哈希值和“对象名”是否相同,来判断对象内容是否正确。

2. 对象

每个对象(object) 包括三个部分:类型,大小和内容。

大小就是指内容的大小,内容取决于对象的类型,有四种类型的对象:“blob”、“tree”、 “commit” 和"tag"。

(1)“blob”用来存储文件数据,通常是一个文件。

(2)“tree”有点像一个目录,它管理一些“tree”或是 “blob”(就像文件和子目录)。

(3)一个“commit”只指向一个"tree",它用来标记项目某一个特定时间点的状态,它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交(commits)的指针等等。

(4)一个“tag”是来标记某一个提交(commit) 的方法。

git就像是在本机的文件系统之上构建一个小的文件系统。

在这里插入图片描述

3. 对象模型

在这里插入图片描述

4. Git是如何存储对象的

所有的对象都以SHA值为索引用gzip格式压缩存储, 每个对象都包含了对象类型, 大小和内容.Git中存在两种对象 - 松散对象和打包对象。

(1)松散对象

松散对象是一种比较简单格式. 它就是磁盘上的一个存储压缩数据的文件. 每一个对象都被写入一个单独文件中。Git使用SHA值的前两个字符作为子目录名字, 所以一个目录中永远不会包含过多的对象. 文件名则是余下的38个字符。

(2)打包对象

由于Git把每个文件的每个版本都作为一个单独的对象, 它的效率会十分的低. 设想一下在一个数千行的文件中改动一行, Git会把修改后的文件整个存储下来, 很浪费空间,Git使用打包文件(packfile)去节省空间. 在这个格式中, Git只会保存第二个文件中改变了的部分, 然后用一个指针指向第一个文件,对象通常是以松散格式写到磁盘上, 因为这个格式的访问代价比较低. 然后,最终会需要把对象存放到打包格式中去节省磁盘空间,这个工作可以通过git gc来完成. 它使用一个相当复杂的启发式算法去决定哪些文件是最相似的, 然后基于此分析去计算差异. 可以存在多个打包文件, 在必要情况下, 它们可被解包成为松散对象或者重新打包,Git会为每一个打包文件创建一个较小的索引文件. 索引文件中包含了对象在打包文件中的偏移, 以便于通过SHA值来快速找到特定的对象。

5. GIT目录与工作目录

(1)Git目录

‘Git目录’项目存储所有历史和元信息的目录 ,包括所有的对象(commits,trees,blobs,tags), 这些对象指向不同的分支。

(2)工作目录

Git的 ‘工作目录’ 存储着你现在签出(checkout)来用来编辑的文件. 当你在项目的不同分支间切换时, 工作目录里的文件经常会被替换和删除. 所有历史信息都保存在 'Git目录’中 ,工作目录只用来临时保存签出(checkout) 文件的地方, 可以编辑工作目录的文件直到下次提交(commit)为止。

6. Git索引

Git索引是工作目录和项目仓库间的暂存区(staging area). 可以把许多内容的修改一起提交 (commit). 如果创建了一个提交(commit), 那么提交的是当前索引(index)里的内容, 而不是工作目录中的内容。使用 git status 命令是查看索引内容.,运行 git status命令, 就可以看到哪些文件被暂存了(就是在你的 Git索引中), 哪些文件被修改了但是没有暂存, 还有哪些文件没有被跟踪(untracked).

三、Git常用命令

1. Git工作流程

在这里插入图片描述

git status                   # 查看文件变动,查看仓库当前的状态,显示有变更的文件
git add .                    # 添加文件到暂存区
git commit . -m “这是备注信息” # 提交代码到本地库
git push                     # 推送代码到远程仓库
git pull                     # 从远程仓库拉去代码
git reset                    # 撤销提交更改
git checkout 文件名           # 撤销文件更改

2. 创建

git init                                       # 当前目录作为git仓库
git init dir                                   # 指定目录作为git仓库
.git隐藏文件夹

HEAD           文件指示目前被检出的分支
description    用来显示对仓库的描述信息 
config         文件包含项目特有的配置选项 
info           目录包含一个全局性排除文件 
hooks          目录包含客户端或服务端的钩子脚本 
index          文件保存暂存区信息 
objects        目录存储所有数据内容 
refs           目录存储分支的提交对象的指针

3. 基础配置

git config user.name
git config user.email                                   # 查看用户和邮箱
git config --global user.name "你的昵称"
git config --global user.email "你的邮箱"                # 配置用户和邮箱
git config --global --replace-all user.name "your user name"
git config --global --replace-all user.email"your user email"  # 修改用户和邮箱,也可以在.gitconfig配置文件中修改
git config --global credential.helper store # https方式设置永久记住密码,不用每次提交代码都要输入用户名密码,用户主目录的 .gitconfig 文件会新增配置   [credential] helper = store  如果没有 --global ,则会在当前项目下的 .git/config 文件增加配置
git config --list                #查看所有配置
git config -e --global           # 编辑配置文件

4. 远程

git remote                                            # 查看当前配置有哪些远程仓库
git remote -v                                         #  查看当前远程地址
git remote add [shortname] [url]                      # 指定名字添加一个新的远程仓库
git fetch                                             # 从远程仓库下载新分支与数据
git pull                                              # 下载远程代码并合并
git push [alias] [branch]                             # 推送到远程仓库	
git remote rm [别名]                                   # 删除远程仓库
git clone 版本库地址 [本地文件夹名称]                     # HTTP 协议拉取代码的命令格式,需要输入用户名密码
git clone username@host:/path/to/repository           # 克隆远程服务器仓库
ssh-keygen -t rsa -C "youremail@example.com"          # 通过生成SSHkey并配置可以用SSH方式拉去代码

5. 配置多个远端仓库

git remote -v                    # 查看当前远程地址

如果我们想 把代码往多个代码托管平台推送只需要添加一个远程地址即可

git remote add 远程仓库名 远程地址
git push -u github    # 在推送的命令中加上 -u 参数可修改默指定的仓库
git remote set-url --add origin https://github.com/78778443/gittest.git # 往origin中添加多个地址可以实现一次推送到多个仓库

6. 历史记录

git log                            # 查看历史提交记录
git log --oneline                  # 查看历史记录的简洁的版本
git log --reverse --oneline        # 逆向显示
git log --author=xxxx --oneline    # 指定用户
git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges  
#指定时间 --since 和 --before,也可用 --until 和 --after
git blame <file>                   # 以列表形式查看指定文件的历史修改记录

7. 标签

git tag                       # 查看所有标签
git tag -a v1.0               # 创建标签  -a 表示带注解
git tag -a <tagname> -m "xxx标签"    # 指定标签信息命令
git tag -s <tagname> -m "xxx标签"    # PGP签名标签命令
git log --decorate                # 带标签的历史记录
git tag -a v0.9 85fc7e7           # 追加标签
git log --oneline --decorate --graph   # 查看带标签的树

8. 分支管理

git branch                      # 查看本地分支,列出所有本地分支
git branch -a                   # 查看本地的分支和远程的所有分支
git branch   分支名              # 创建分支
git checkout 分支名              # 切换分支,本地没有会自动找到远程的分支在本地创建
git checkout -b test                 # 新建本地分支test,并立即切换到该分支下
git push --set-upstream origin test  # 远程现在还不存在这个分支,第一次推送的时候指定远程分支名
git merge test                       # 合并分支,主分支合并其他分支需要切换到主分支,合并哪个分支就merge哪个分支
git branch -d 分支名                  # 普通删除
git branch -D 分支名                  # 强制删除

9. 删除过期分支

git push origin --delete dev  # 删除远程分支

有些时候我们在本地新建了一个临时分支,或者服务器已经将某一分支删除,本地遗留了很多废弃的分支,当我们 想进行清理的时候,如果一个个删除的话效率非常低;这里教大家使用一种比较简单的方法来清理这些分支;我们 可以根据分支在远程是否存在作为依据,决定是否将它删除,如果不存在则删除,反之则保留

将远程的分支与本地保持一致,直接使用 git pull 是不够的,可以使用 git fetch --prune origin

git fetch --prune origin

Git 会从拉取远程的分支信息与本地的进行对比,当发现远程的分支已经删除,便会对本地的分支进行标注;我们 可以使用 Git 的命令 ,

git branch -vv

在分支列表中,test1 分支后面有一个 :gone 的标识,说明远程分支已经被删除,通过这个标识我们能够很清晰 的知道该需要删除那些分支,删除分支的命令参考如下

git branch -d test1

10. 查看代码修改

git diff                           # 比较工作区变动
git diff --cached 文件              #比较暂存区变动
git diff hash值                    #工作区与指定hash值比较
git diff --cached hash值           #暂存区的改动和指定记录进行对比,命令中加入 --cached 
git diff HEAD                      # 与最新版本库比较,最新的 commit 记录,HEAD 指的是当前所在的分支名
git diff hash值1 hash值2            #比较连个历史记录

11. 比较分支差异

有时候需要比较两个分支有什么不同;比如说在 dev 开发分支上开发代码,由于开发的时间比较长,可能已经忘记在 Dev 分支上提交了代码,有哪些代码在 master 分支还没有的; 这个时候可以先切回到 master 分支,然后通过 git log 查看最后的提交记录,然后在切回到 dev 分支进行人工对比,这种方法明显操作起来繁琐。

查看差异分为两种,具体的代码文件差异,和提交记录的差异;这里我们先来查看代码的具体差异,分别有三种方 式:1. 查看哪些文件被修改了 2. 查看某个文件的代码差异 3. 查看全部的代码差异。

git diff master dev --stat        # 当前的分支与另外一个分支,有哪几个文件不一样   
git diff master dev 文件名         # 显示具体文件修改
git diff master dev               # 显示全部文件的修改
git diff hash1                    # 工作区与历史版本对比
git diff hash1 hash2              # 两个历史版本对比(可跨分支)

除了可以看代码的差异外,有时候还需要查看提交记录的不同,比如说我想查看开发分支比生产分支多了多少提交 记录;通常查看提交记录的不同有下面几种方法:1. 查看 A 分支有但 B 分支没有的记录 2. 单纯的比较两个分支的 差异 3. 显示个记录在某些分支上存在。

git log dev ^master    # 查看 dev 分支中有的记录而在生产分支中没有的记录
git log branch1...branch2  # 查看 两个分支记录有什么不一样
git log --left-right develop...test  # 刚才上面的查看方式其实有一个缺陷,就是没有在每一个记录中显示所属分支,这样如果差异记录太多的时候就会造成不方便,所以我们还可以在命令中加入 --left-right 参数,这样在展示每条差异记录的时候,都能看出这条记录所属分支

12. 文件权限问题

文件权限不同会导致冲突,在 Git 进行代码管理时,如果不想让 Git 记录文件权限,可以通过 git 配置让其忽略文件权限的信息

git config core.filemode false    #当前版本库
git config --global core.fileMode false # 所有版本库

13. 忽略指定文件或目录

在 Git 工作区的根目录下创建一个 .gitignore 文件,把想忽略的文件名或者目录填进去,Git 就会自动忽略这些文件;

.gitignore 写得有问题导致文件无法提交,需要找出来到底哪个规则写错了,可以用 git check-ignore 命令检查:

git check-ignore -v 文件名
git add -f 文件名           # 可以用 -f 强制添加到 Git
!/test/index.java          # 设置反向排除,可以忽略某一个文件夹下的绝大多数文件,保留个别文件

忽略已提交文件:

有的时候,可能一不小心把某一个原本应该忽略的目录提交到了版本控制器中,再使用 .gitignore 文件去忽略的时 候,无法再次将其忽略,因为 git 已经索引了该文件。

这种情况时候,需要先删除掉该文件的缓存,才能让他成功忽略,正确的操作步骤是先在 .gitignore 中设置该文件为忽略,然后执行删除缓存命令

git rm --cached test/index.java

忽略已存在的文件夹:

编辑 .gitignore 文件,在文件中加入要忽略文件夹

删除该文件夹的缓存

git rm -r --cached test3/

接着需要将此修改提交,因为在忽略文件里已经忽略了该文件夹,所以在提交的时候需要注意加上 -f 参数, 表示强制添加

git add -f test3

14.将仓库记录和恢复指定历史位置

用 git commit 提交代码后发现这一次提交的的内容是错误的,需要将代码复原,这个时候常见有两种 做法。 第一种是把代码错误内容修改正确,然后重新使用 commit 提交一次;第二种方法是使用 git reset 命令撤销上一次 错误的 commit 记录。

覆盖代码:第一种比较原始的方法,会多次一次 commit 记录

覆盖记录:

git reset HEAD^   # 撤销最近一次提交git reset commitid  #指定回滚位置

回滚后当前代码没变,里可以使用 git checkout 文件名 来撤销文件修改,撤销修改后再通过 git status 去确认当前状态

(1)本地覆盖远端

在使用 Git 的过程中可能会遇到这样的场景需要覆盖远程仓库,比如说,不小心把错误的代码推送到远程仓库,这 个时候想把错误的代码撤销有两种方式。 第一种是代码层面覆盖,就是我们再次修改代码,把错误的代码纠正,然后再次提交,然后推送到远程仓库中。 第二种是将方式则是将记录覆盖,我们使用 git reset 版本号 命令恢复到上一个版本,然后重新提交,默认情况下 Git 会拒绝你的提交,因为提交的版本号比远程仓库的落后,所以需要用上强制提交方式,也就是本节需要提到 的。

两种方式的区别是第一种方式会在 Git 中记录两个无效的提交,而第二种方式则不会显示错误的提交记录;有些情 况下你可能将一些敏感信息不小心提交上去了,如果第一种处理方式必然会泄露出去,而第二种则可以避免,下面 我将两种方式逐一演示。

使用代码覆盖的方式虽然可以将最新的代码修改回来,但在敏感信息泄露场景下依然不能满足,如果要彻底的让记 录不留痕迹,我们可以使用记录覆盖的方式。

用git reset hash值回复上次版本,接着我们使用 git push将本地仓库推送到远程,提交并没有成功,因为提交的版本号落后于远程版本;如果我们需要提交需要用到强制推送命令

git push -f

Git 默认情况下允许强制推送,但这样也有可能造成一些安全隐患,比如某个开发者做了一些操作,不想让其他人 知道,他就可以使用强制推送的特点来抹掉痕迹,或者直接将整个远程仓库代码恢复到初始化的状态等问题。 我们可以设置远程仓库的强制推送权限

(2)远端覆盖本地

首先拉取远程最新的代码,这里不适用 git pull 而是使用 git fetch,因为 git pull 拉取远程最新分支之后,会自动 对本地分支进行合并,而 git fetch 则只会拉取远程分支不进行自动合并

git fetch

从上图中可以看出已经成功获取远程仓库的信息,接下来我们通过 git log 命令查看提交记录是有改变,以此来验 证 git fetch 不会自动合并的特点

git reset --hard origin/master

在这条命令中, --hard 为 git reset 的选项,它的作用是 reset 之后把当前工作的差异部分丢弃掉,完全与目标一 致, origin/master 则是目标比较分支

(3)恢复删除的分支

在使用 Git 的过程中,有时可能会有一些错误操作,造成分支被删除或者丢失,比如 branch -d test ,想 恢复此分支,可以通过 reflog 来进行恢复,不过前提是,这个 test 分支的信息没有被 git gc 清除。 一般情况下,除非手动执行了 git gc 命令,否则 gc 对那些无用的 object 会保留很长时间后才清除的,reflog 是 Git 提供的一个内部工具,用于记录对 Git 仓库进行的各种操作,可以使用 git reflog show 所有的管理类操作日志。

首先通过日志找到 commitid ,然后通过新建分支的方式,加入 commitid 即可。

git log 命令可以显示所有提交过的版本信息,但对分支的一些管理操作并不会显示出来,这些管理的操作日志并不是没有记录,而是需要使用 git reflog 命令才能显示,显示分支 管理命令参考命令如下:

git reflog show
git branch 分支名称 commit_id           # 创建新分支回复分支

如果不小心使用 git reset 回滚了提交记录,想找回之前的提交记录也是可以的,可以 git reflog 查看操作历史, 找到执行 git reset 命令之前 commitid ,然后 git reset --hard 到那个 commitid 即可。

在某个版本中删除了文件,后来又突然发现需要这个文件,也是可以恢复的,恢复之前首先确定要恢复的文件在哪一个版本(commit)中,假设那个版本号是: 7a4312sd,文件路径为 abc.php 那么参考命如下:

git checkout 7a4312sd abc.php
(4)单独回滚代码

有些情况下我们需要将代码回滚,但是不回滚提交记录,原因可能是需要这些提交记录去追责,也有可能是服务器 设置了不能强制提交等等原因。 此时如果使用 reset 去出去就显得不太合适,操作起来也会略显繁琐,在 Git 中有一个更加适合我们的命令 git revert ,它就可以帮我我们快速解决刚才这个问题。

revert 可以回滚指定的版本的代码,回滚代码后会生成一个新的版本号,记录下整个版本变动流程,就可以用 revert 这种操作方法,下面我们使用 revert 回滚,参考命令如下所示:

git revert 6d8feb147973711d08211f953f3d7c463ee1e88f

在返回的信息中,可以看出已经成功的提交了代码,并没有出现此前使用 git reset 恢复后推送远程仓库提示当前 仓库落后于远程仓库。需要注意的是,在使用 revert 去恢复某个版本代码时,Git 只会撤销指定版本的代码,而不是指定版本后的所 有版本。比如说你提交了 1、2、3 三个版本,当你撤销版本 2 的时候,会生成版本 4,但是不会对版本 3 产生影响。

15. Rebase

git rebase                           #分支历史看起来像没有经过任何合并一样
git rebase –continue                 #解决冲突后继续rebase
git rebase –abort                    #终止rebase,回到rebase之前

这个命令会把当前分支里的每个提交(commit)取消掉,并且把它们临时保存为补丁(patch)(这些补丁放 到“.git/rebase”目录中),然后把分支更新到最新的rebase过来的分支,最后把保存的这些补丁应用到分支上

在这里插入图片描述
在这里插入图片描述

当’mywork’分支更新之后,它会指向这些新创建的提交(commit),而那些老的提交会被丢弃。 如果运行垃圾收集命令 (pruning garbage collection), 这些被丢弃的提交就会删除

16. 储藏

当正在做一项复杂的工作时, 发现了一个和当前工作不相关但是又很讨厌的bug此时想先修复bug再做手头的工作, 那么就可以用 git stash 来保存当前的工作状态, 等你修复完bug后,执行’反储藏’(unstash)操作就可以回到之前的工作里

git stash                                   # 保存本地修改到储藏,重置工作目录和索引
git stash apply                             #  恢复到以前的工作状态

多次使用’git stash’命令, 每执行一次就会把针对当前修改的‘储藏’(stash)添加到储藏队列中. 用’git stash list’命令可以查看你保存的’储藏’(stashes):

git stash listgit stash apply stash@{1}                      # 取出指定的储藏
git stash clear                                # 清空队列

17. 冲突

解决代码冲突需要去编辑冲突的文件,将需要的代码进行保留,不需要的代码给删除,清理完不需要的代码之后需要通知 Git 让它继续执行任务,不同场景的冲突,通知 Git 的方式并不一样,分为以下几种:

  1. git merge 和 git pull 命令导致的冲突,处理完冲突后使用 git commit -a;

  2. git rebase 命令导致的冲突,处理完冲突之后使用 git rebase --continue 或 git rebase --skip ;

  3. git stash apply 命令导致的冲突,处理完冲突之后使用 git add . 即可。

https://share.weiyun.com/ZojSTVYg

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值