git 基本操作
Git 基本操作 --> IT 人员必会操作
1,引言
在我们讲解 gIt 的时候我们要先了解一个概念,什么是版本控制?
版本控制是一种记录一个或若干个文件内容变化,以便将来查阅特定版本修订情况的系统。我们可能最常用的就是将一个文件复制成若干份,然后在文件结尾写个标签,来区别每个特定的版本。我们采用这种方式的时候,会在电脑上存放很多个版本的文档,占用资源不说,使用的时候也不是特别方便、
1.1 什么是 GIt?
it是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或大或小的项目(编写一点点程序就可以合并到master主分支上,然后测试运行它)
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必 服务器端软件支持。
1.2 Git 的核心竞争力
1.2.1,直接记录快照,而非差异比较
git 和其他版本控制管理系统的主要差别在于 Git 对待数据的方法。从概念上来说,其他大部分系统以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce等),将他们存储的信息看做是一组基本文件和每个文件随时间逐步累积的差异。
Git 则不按照上面方式对待活保存数据。反之,Git 更像是把数据看作对小型文件系统的一系列快照。在 Git 中,每当你提交更新活保存项目状态时,他基本上就会对当时管理的目录下的全部文件创建一个快照并保存这个快照的索引,为了效率,如果文件没有修改,Git 不在重新储存改文件,而只是保留一个连接指向之前存储的文件。Git 对待数据更像是一个快照流
这是 Git 与几乎其他所有版本控制系统的重要区别。
1.2.2,近乎所有操作都是在本地执行
在 Git 中的绝大多数操作都是需要访问本地文件和资源,一般不需要来自网络上其他计算机的信息。
1.2.3,Git 保证完整性
Git 中所有的数据在存储前都计算校验和,然后以校验和来引用。这意味着不可能在 Git 不知情时更改任何文件内容或者目录内容,这个功能构建 Git 底层,是构成 Git 哲学不可缺失的部分。
Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
1.2.4,Git 一般只添加数据
你执行的 Git 操作,几乎只往 Git 数据库中 添加 数据。 你很难让 Git 执行任何不可逆操作,或者让它以任何方式清除数据。 同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容。但是一旦你提交快照到 Git 中, 就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
1.3,Git的三种状态
Git 有三种状态,你的文件可能处于其中之一: 已提交(committed)、已修改(modified) 和 已暂存(staged)。只要是被 Git 管理的文件都会处于这三种状态之间的一种。
- 已提交(committed):表示数据已经安全的保存在本地数据库中。
- 已修改(modified):表示修改了文件,但还没保存到数据库中。
- 已暂存(staged):表示对一个已经修改文件的当前版本做了标记,使之包含在下次提交的快照中。
基本工作流程:
1,在工作目录中修改文件
2,暂存文件,将文件的快照放入暂存区域
3,提交更新,找到暂存区域的文件,将快照永久性存储的 Git 仓库目录。
1.4,Git和SVN的区别
- Git是分布式的,SVN是集中式的。这是Git和其他非分布式的版本控制系统,例如SVN,CVS等,最核心的区别。(Git是每个工作人员的电脑都可以作为一个仓库,不管你有没有网络,人在哪个地方你都可以正常的工作,但是SVN他所有的代码都集中的放到一个远程仓库里,你必须能访问这个,才可以对代码进行修改提交)
- Git把内容按元数据方式存储,而SVN是按文件目录存储(你在本地什么样,上传到SVN上就是什么样);所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里
- Git分支和SVN 的分不同:分支在 SVN 中一点不特别,就是版本库中的另外一个目录
- Git没有一个全局的版本号。而SVN有:目前为止这是跟SVN相比 Git缺少的最大的一个特征
- git的内容完整性要优于SVN:Git的内容存储使用的事SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏
2,安装 git
2.1,在 Linux 上安装
如果你想在 Linux 上用二进制安装程序来安装 Git,可以使用发行版包含的基础软件包管理工具来安装。
sudo yum install git
基于 Debian 的发行版上:
sudo apt-get install git
2.2,在 Mac 上安装
可以参考官方文件安装手册去安装:https://git-scm.com/download/mac
2.3,在 windows 上安装
和安装其他软件没有什么区别,我们去 git 官网上面下载对应的 windows 版本的软件,一步一步安装即可
3,使用 Git
下面我们开始介绍使用 Git 来进行版本管理,整个流程我会按照工作中一个项目的开发流程来往下面走。
3.1,配置 Git
通过上面的安装,我相信安装这一步对大家都没什么困难,一个软件安装好了,我们要配置下他的一些配置文件,Git 自带一个 git config
的工具来帮助设置控制 Git 外观和行为的配置变量,这些变量存储在三个不同的位置:
- /etc/gitconfig :包含系统上每一个用户及他们仓库的 通用 配置。如果使用带有
--system
选项的git config
时,他会从此文件读取配置变量 - ~/.gitconfig 或 ~/.config/git/config 文件:只针对当前用户。 可以传递 --global 选项让 Git 读写此文件。
- 当前使用仓库的 Git 目录中的 config 文件(就是 .git/config):针对该仓库。
配置文件优先级从低到高排列如下:优先级高的会覆盖优先级低的文件内容
/etc/gitconfig -----> ~/.gitconfig -----> .git/config
3.1.1,配置用户信息
当安装完 Git 应该做的第一件事就是设置我们自己的用户名与邮件地址。因为每一个 Git 的提交都会使用这些信息,并且他会写入到你的每一次提交中,因为这样才知道那个版本是你写的,
//全局配置,针对系统上所有的用户,修改/etc/gitconfig
$ sudo git config --system user.email "zsf18163201@163.com"
$ sudo git config --system user.name "zhangshoufu"
$ sudo git config --system --list
// 当前用户设置
$ git config --global user.email "zsf18163201@163.com"
$ git config --global user.name "zhangshoufu"
$ git config --global --list
//针对当前这个项目
$ git config --local user.email "zsf18163201@163.com"
$ git config --local user.name "zhangshoufu"
$ git config --local --list
3.2,Git 初始化(项目初始化)
我们先创建一个 Git 的工作目录(创建项目写代码的目录),
$ mkdir ./zsf_git_project
项目目录创建好了,我们想让 Git 管理它,肯定要告诉 Git 你要管理那个目录:
//进入到这个目录中
$ cd zsf_git_project
//告诉 Git 要管理那个目录
$ git init
Initialized empty Git repository in /Users/zhumengyuan/zsf_git_project/.git/
// 空仓库创建完成后git_test目录下会生成一个.git隐藏文件夹,仓库 默认包含一个主支,即master分支,默认操作都是在主分支master上进行的
//配置用户信息
$ git config --local user.email "zsf18163201@163.com"
$ git config --local user.name "zhangshoufu"
$ cat .git/config
[user]
email = zsf18163201@163.com
name = zhangshoufu
3.3 Git 的基本操作
3.3.1 检测当前项目中文件的改变情况
$ git status
On branch master //当前所处于的分支
No commits yet
nothing to commit (create/copy files and use "git add" to track)
//创建一个新文件
$ touch 1.go
//在此检测目录中文件的改变情况
$ git status
On branch master
No commits yet
Untracked files: //未被跟踪的文件,只是在工作目录创建了
(use "git add <file>..." to include in what will be committed)
1.go
//你没有提交文件,但是你有存在未跟踪的文件,我们可以使用git add 把这个未跟踪的文件提交到暂存区
nothing added to commit but untracked files present (use "git add" to track)
//发现当前项目下有一个文件发生了改变
紧凑型输出,不显示多余的信息
$ echo "test git status -s " > 1.go
$ touch 1.py
$ git status -s
M 1.go
?? 1.py
说明:git status -s符号的说明
??: 新添加的未跟踪文件前面有 ?? 标记
A : 新添加到暂存区中的文件前面有 A 标记
M : 修改过的文件前面有 M 标记。 你可能注意到了 M 有两个可以出现的位置,出现在右边的 M (红色)表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。
空: 当工作目录和暂存区和本地仓库文件内容一样的时候,什么都不输出
3.3.2 管理文件
//只管理1.go 这个文件
$ git add 1.go
//管理这个项目下面的所有文件
$ git add .
//支持正则,管理.go 的文件,其他的不管理
$ git add *.go
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: 1.go
//如果你认为直接提交的有问题,你可以使用这个命令把他撤销下来
$ git rm --cached 1.go
3.3.3,生成版本信息,把当前暂存区的文件提交到本地仓库
git commit -m "使用 git 生成的第一个版本"
[master (root-commit) 3f3ea2a] 使用 git 生成的第一个版本
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 1.go
勤劳和细心的你一定会发现,每一次用git add提交一个新的文件的时候都会在最下面出现一行create mode 100644 File_Name
这句话,那么100644
这串数字代表什么含义呢?下面经过试验来看一下
100 代表普通文件(regular file)
644 为权限
也可以去这个文件里面查看/usr/share/doc/git-1.8.3.1/technical/index-format.txt
32-bit mode, split into (high to low bits)
4-bit object type
valid values in binary are 1000 (regular file), 1010 (symbolic link)
and 1110 (gitlink)
3-bit unused
9-bit unix permission. Only 0755 and 0644 are valid for regular files.
Symbolic links and gitlinks have value 0 in this field.
3.3.4,查看提交的日志
$ git log
commit 3f3ea2a92fc386383ffd0bd7decacd3e8ec3c06b (HEAD -> master)
Author: zhangshoufu <zsf18163201@163.com>
Date: Wed Jul 15 08:13:26 2020 +0800
使用 git 生成的第一个版本
查看最近一次 commit 提交信息
$ git log -p -1
另外一个常用的选项是 --pretty。 这个选项可以指定使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如用 oneline 将每个提交放在一行显示,查看的提交数很大时非常有用。 另外还有 short,full 和 fuller 可以用,展示的信息或多或少有些不同,请自己动手实践一下看看效果如何。
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
但最有意思的是 format,可以定制要显示的记录格式。 这样的输出对后期提取分析格外有用 — 因为你知道输出的格式不会随着 Git 的更新而发生改变:
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : changed the version number
085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
a11bef0 - Scott Chacon, 6 years ago : first commit
显示所有的更改操作
$ git reflog
图形化显示 log 日志
$ git log --graph
$ git log --graph --pretty=format:"%h %s"
3.3.5 撤销
//测试文件准备
$ cat test_file
test git status -s
test git diff
$ echo 'test git checkout -- ' >> test_file
$ cat test_file
test git status -s
test git diff
test git checkout --
$ git add test_file
$ git diff test_file
$ git diff --cached test_file
diff --git a/test_file b/test_file
index 8072794..d92353f 100644
--- a/test_file
+++ b/test_file
@@ -1,2 +1,3 @@
test git status -s
test git diff
+test git checkout --
//环境模拟成功,现在就是暂存区和本地目录一致,和本地仓库不一致
//从本地仓库撤销到暂存区,撤销到最后一次commit
[root@zsf_node3 git_test]# git reset HEAD test_file
Unstaged changes after reset:
M test_file
//我们可以通过这个命令可以看出开我们已经把文件从本地仓库撤销到暂存区了
[root@zsf_node3 git_test]# git diff
diff --git a/test_file b/test_file
index 8072794..d92353f 100644
--- a/test_file
+++ b/test_file
@@ -1,2 +1,3 @@
test git status -s
test git diff
+test git checkout --
//把文件从暂存区撤销到工作目录
[root@zsf_node3 git_test]# git checkout -- test_file
[root@zsf_node3 git_test]# cat test_file
test git status -s
test git diff
//发现我们刚才追加进去的内容已经不存在了
diff比较的符号说明
+ 文件里面有新增的内容
- 文件里面删除了内容
如果是替换的话,他换先删除原先的行,在把更改后的行增加上去
//从本地仓库拉去指定commit的文件
[root@zsf_node3 git_test]# git reflog //先查看所有commit的记录
e98d2e5 HEAD@{0}: commit: test git diff
bb0deb3 HEAD@{1}: commit: git status -s test
6182c28 HEAD@{2}: commit (initial): test commit add test_file
//直接从本地仓库撤销到工作目录
[root@zsf_node3 git_test]# git reset --hard 6182c28
HEAD is now at 6182c28 test commit add test_file[root@zsf_node3 git_test]# git diff
[root@zsf_node3 git_test]# cat test_file
[root@zsf_node3 git_test]#
3.3.6, 跳过暂存区,直接把文件提交到本地仓库
$ git commit -a -m "test git commit -a"
[master b140339] test git commit -a
1 file changed, 1 insertion(+)
//把所有被git跟踪的文件直接提交到本地仓库
$ git commit -a -m "test git commit -a " test_file
fatal: Paths with -a does not make sense.
// 如果这个文件没有被git跟踪,是不能跳过space暂存区域的
3.3.7,移除文件
从git中移除某个文件,就必须要从已经跟踪文件清单中移除(从暂存区移除),然后commit提交。我们可以使用git rm
命令完成此项工作,并连带冲工作目录中删除指定的文件,这样以后就不会出现在未跟中文件清单中了。
如果只是简单地从工作目录中手工删除文件,运行git status
就会在“Changes not staged for commit”部分(也就是 未暂存清单)
$ rm -f test_file
$ git status
On branch master
Changes not staged for commit: 未提交的更改
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test_file
no changes added to commit (use "git add" and/or "git commit -a")
//然后运行git rm 删除
$ git rm test_file
rm 'test_file'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: test_file
//当下次再提交的时候test_file 这个文件就完成了删除了
$ git commit -a -m "delete test_file"
[master 47a2f61] delete test_file
1 file changed, 1 deletion(-)
delete mode 100644 test_file
$ git status
On branch master
nothing to commit, working tree clean
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(译注:即 force 的首字母)。 这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。
只删除本地仓库里面的,不删除本地工作目录中的文件
$ git rm --cached test_file
rm 'test_file'
$ ls
test_file
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: test_file
Untracked files:
(use "git add <file>..." to include in what will be committed)
test_file
Git 批量删除文件
git rm *.log 支持通配符
3.3.8,移动文件,更改文件名
//更改文件名称
$ git mv test_file file_test
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: test_file -> file_test
$ ls
file_test
执行了上面这个命令相当于执行了下面这三个命令
$ mv test_file file_test
$ git rm test_file
$ git add file_test
3.3.9 切换到指定版本
$ git reflog
$ git reset --hard 版本号
3.3.10 Git操作示意图
4, 远程仓库
我们上面的所有操作都是在自己的本地电脑上面完成的,还是没有办法进行多人协作开发,当前电脑出问题之后那版本就没有了,现在我们改一下我们的套路
我们这边使用Github做演示,gitlab 和他大致相同。
4.1,什么是GitHub
GitHub 是最大的 Git 版本库托管商,是成千上万的开发者和项目能够合作进行的中心。 大部分 Git 版本库都托管在 GitHub,很多开源项目使用 GitHub 实现 Git 托管、问题追踪、代码审查以及其它事情。 所以,尽管这不是 Git 开源项目的直接部分,但如果想要专业地使用 Git,你将不可避免地与 GitHub 打交道,所以这依然是一个绝好的学习机会。
4.2,创建Github账号
你所需要做的第一件事是创建一个免费账户。 直接访问 https://github.com,选择一个未被占用的用户名,提供一个电子邮件地址和密码,点击写着“Sign up for GitHub”的绿色大按钮即可。
4.3,操作
剩下的相关操作可以查看官方文档:https://git-scm.com/book/zh/v2/GitHub-%E8%B4%A6%E6%88%B7%E7%9A%84%E5%88%9B%E5%BB%BA%E5%92%8C%E9%85%8D%E7%BD%AE
5,Git分支
5.1,创建与合并分支
在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master
分支。HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长。
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
你看,Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
真是太神奇了,你看得出来有些提交是通过分支完成的吗?
//创建分支
git branch dev
//切换分支到dev
git checkout dev
// 创建分支并切换到dev分支
git branch -b dev
//查看当前所处的分支
git branch
我们在dev分支上更改下master上面的一个文件
echo "this is dev" >> readme.txt
git add .
git commit -am "test checkout dev"
我们切换到master分支上,发现刚才我们更改的文件内容在这边并未显示,因为我们当前的HEAD 还是指向masterv3版本的,
如果我们想在master上面看到对应的内容,我们需要把dev分支的内容合并到master上
git checkout master
git merge dev
//需要注意,我们当前是把dev合并到master上,所以我们要在master 上面进行merge
5.2 删除分支
当我们知道这个分支没有用的时候,我们可以使用下面命令将其删除
git branch -d dev
通过工具解决冲突
1,安装 beyond compare
2,在 GIt 中配置beyond compare
git config --local merge.tool bc4
git config --local mergetool.path "/usr/local/bin/bcomp"
git config --local mergetool.keepBackup false
3,解决冲突
git mergetool