Git基础 学习总结
什么是版本控制,版本迭代,新的版本!版本管理器
为什么学习使用Git?
版本控制(Revisoin control)是一种在开发的过程中用于管理我们的文件,目录或工程的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。
- 实现跨区域多人协同开发
- 追踪和记载一个或多个文件的历史记录
- 组织和保护你的源代码和文档
- 统计工作量
- 并行开发,提高工作效率
- 跟踪记录整个软件的开发过程
- 减轻开发人员的负担,节省时间,同时降低人为错误
简单一点来说就是用于管理多人协同开发项目的技术
Git使用场景
-
命令行
在我们的CMD终端(Powershell)可以使用Git.
-
代码编辑器和IDE(现在大多数IDE会为我们提供git功能)
-
图形用户界面(专门为Git提供,可以在Git官网进行下载 Git官网)
如何打开git
在新建的文件夹目录(Git仓库相当于一个文件目录)下点击鼠标右键 点击 open GIt bash here
![外链图片转存失败,源站可能有防盗链
也可以在开始菜单里找到Git -> Git Bash
,弹出一个类似命令行的东西,但是需要在该目录下新建一个文件夹进行下一步操作
查看Git版本
在命令提示符下输入如下命令可查看当前GIt的版本
git --version
Git的第一步
因为Git是分布式版本控制系统,所以,每个机器都是要自报家门:你的名字和Email地址
$ git config --global user.name "Your Name"
$ git config --global user.name "email@example.com"
注意
git config
命令的--global
参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
创建版本库
什么是版本库呢?版本库又名仓库(Repository),你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
如果是按上述第一种方法打开git,直接通过git init
命令把这个目录变成Git可以管理的仓库
$ git init
Initialized empty Git repository in /d/learngit/.git/
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个.git
的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
如果你没有看到
.git
目录,那是因为这个目录默认是隐藏的,用ls -ah
命令就可以看见。
如果是第二种方法打开git,需要找个合适的地方,创建一个空目录
$ mkdir learngit
$ cd learngit
$ pwd
/d/learngit
pwd
命令用于显示当前目录。在我的电脑上,这个仓库位于/d/learngit
。
之后再进行前者操作
设置编辑器和行结束符
-
设置默认编辑器(git的默认编辑器是vim,为了更舒服的编辑效果,将vs code设为默认编辑器)
在
git Bash
中输入如下命令$ git config --global core.editor "code --wait"
这会将 VS Code 设置为 Git 的全局默认编辑器。当你使用 Git 进行操作时(如提交信息、合并冲突等),VS Code 将会作为编辑器打开。
检查设置
你可以通过如下命令来检查是否成功设置
$ git config --global --get core.editor
如果显示
code --wait
,说明设置成功。使用如下命令可以查看我们的配置文件
$ git config --global -e
!
打开该文件后,会出现一个等待提示
将
.gitconfig
文件关闭后可执行下一步操作
先来解释一下Windows和macOS|Linux系统的行结束符的差异
-
在windows系统下:使用的是回车符加换行符(
CRLF
),具体表现为\r\n
-
在macOS和Linux系统下:使用的是换行符(
LF
),具体表现为\n
因此,当一个项目在不同操作系统之间共享时,行结束符的不同可能会导致不必要的文件变更,即使文件内容并没有真正改变。
为了处理这种差异,Git提供了
core.autorlf
选项,可以自动处理行结束符转换。
Git的自动行结束符转换
-
在Windiws上设置:
$ git config --global core.autocelf true
这会在你提交文件时将
CRLF
转换为LF
,并在检出文件时将LF
转换回CRLF
。这样,Git 仓库中的文件将始终使用LF
,而工作目录中的文件则使用CRLF
。 -
在macOS|Linux上设置:
$ git config --global core.autocrlf input
这会在你提交文件时将
CRLF
转换为LF
,但在检出文件时不做任何转换。工作目录和仓库中的文件都使用LF
。 -
关闭自动转换(如果希望自己手动控制):
$ git config --global core.autocrlf false
这种设置下,Git 不会自动转换行结束符,你需要确保自己管理好行结束符的使用。
注意:如果你的项目中已经存在行结束符的问题,比如因为开发者使用了不同的操作系统导致的混乱,可以使用以下步骤进行修复:
在仓库中强制统一行结束符
-
首先,配置
.gitattributes
文件来确保文件类型的行结束符一致性,如:* text = auto
这样会自动根据操作系统处理文本文件的行结束符。
重新格式化文件
-
执行以下命令,强制Git重新规范文件的行结束符:
$ git rm --cached -r $ git reset --hard
这会移除暂存区的所有文件,然后重新使用新的配置重新添加
提交变更
-
最后,将规范后的文件提交到仓库:
$ git add . $ git commit -m "Normalize line endings"
通过上述操作,我们已经做好预备工作了,接下来就是Git的常用操作
提交文件到仓库
-
首先我们使用如下命令创建两个新文件并且写入内容
$ echo hello > file1.txt $ echo hello > file2.txt
echo
命令不是git命令,它是用于将内容写入文件的标准Unix或Linux命令上面这两个文件一定要放到
Learngit
目录下(子目录也行),因为这是一个Git仓库,放到其他地方Git再厉害也找不到这个文件因为在第一次在目录中初始化Git仓库时,Git不会自动跟踪文件,所以我们必须指示它跟踪它们,所以我们先运行
git status
产看工作状态$ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) file1.txt file2.txt nothing added to commit but untracked files present (use "git add" to track)
我们可以发现标红的文件还未跟踪,因为它们还不在暂存区
和把大象放进冰箱需要三步相比,把一个文件或多个文件放入Git仓库只需要两步
-
第一步,用
git add
告诉GIt,把文件添加到暂存区:#可以将文件一个一个放入仓库 $ git add file1.txt $ git add file2.txt #也可以将所有满足通配符匹配的文件放入仓库 $ git add *.txt
执行上面的命令,发现没有任何显示,这就对了,Unix的哲学就是**“没有消息就是好消息”**,说明已经添加成功了
-
第二步,用
git commit
告诉Git,把文件提交到仓库:$ git commit -m "add file1 and file2" [master (root-commit) 96f35b2] add file1 and file2 2 files changed, 2 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt
简单解释一下
git commit
命令,-m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。git commit
命令执行成功之后会告示你,2 files change
:2个文件被改动(我们新添加的file.txt和file2.txt文件);2 instertions
: 插入了两行内容(file1.txt和file2.txt中各有一行内容)为什么Git添加文件需要
add
,commit
一共两步呢?因为commit
可以一次提交很多文件,所以你可以多次add
不同的文件,比如:$ git add file1.txt $ git add file2.txt file3.txt $ git commit -m "add 3 files."
文件差异
我们已经成功发添加并且提交了file1.txt和file2.txt文件,当我们修改目录下file1.txt的内容:
$ echo World! >> file1.txt
现在,运行git status
命令看看结果:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file1.txt
no changes added to commit (use "git add" and/or "git commit -a")
git status
命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,file1.txt
被修改过了,但还没有准备提交的修改。
虽然Git告诉我们file1.txt
文件被修改了,但如果能看看具体修改了什么内容,自然是更改的。比如你休假两周从国外回来,第一天上班,已经记不清上次修改了什么,需要用git diff
这个命令看看:
$ git diff file1.txt
warning: in the working copy of 'file1.txt', LF will be replaced by CRLF the next time Git touches it
diff --git a/file1.txt b/file1.txt
index ce01362..2043f18 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1,2 @@
hello
+World!
此命令用于查看工作区中文件与暂存区中文件的差异、暂存区中文件与仓库区中文件的差异 (此时需要使用 --staged
参数)。
如上所示,输入 git diff
命令后,它就指出工作区中 file1.txt
与暂存区中 file1.txt
的差异 ( diff
命令输出中,使用 a/
、b/
标识不同区内的文件,a
用于标识具有较旧内容的文件,b
用于标识具有较新内容的文件)。
知道了对file1.txt
文件做了什么修改,再将它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是git add
:
$ git add file1.txt
同样没有任何输出。在执行第二步git commit
之前,我们再运行git status
看看当前仓库的状态:
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file1.txt
git status
告诉我们,将要被提交的修改包括file1.txt
,下一步,就可以放心地提交了:
$ git commit -m"add World!"
[master b14e175] add World!
1 file changed, 1 insertion(+)
提交后,我们再用git status
命令查看仓库当前状态:
$ git status
On branch master
nothing to commit, working tree clean
Git告诉我们当前没有需要提交的修改,而且,工作目录是干净的。
回退版本
在弄明白版本回退前,我们先再次修改file1.txt文件
$ cat file1.txt
hello
World!
test
然后提交修改:
$ git add file1.txt
$ git commit -m "add test"
[master 580055a] add test
1 file changed, 1 insertion(+)
像这样,你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit
。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit
恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
让咱们来回顾一下file1.txt
文件一共有几个版本被提交到Git仓库里了:
版本1:add file1 and add file2
Hello
版本2:add World!
Hello
World!
版本3:add test
Hello
World
test
在实际工作中,我们怎么可能记住一个一千多行的文件每次都修改了哪些内容,不然要版本控制系统干啥,版本控制系统 肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log
命令查看:
$ git log
commit 580055a992098e39908a055baf83e3bc499066fa (HEAD -> master)
Author: Jay <wenyangzheng669@gmail.com>
Date: Fri Aug 16 09:05:59 2024 +0800
add test
commit b14e1753944a5e2551a3e50dd61de9addc43a429
Author: Jay <wenyangzheng669@gmail.com>
Date: Thu Aug 15 16:54:55 2024 +0800
add World!
commit 96f35b2622fbe957c8c1876fddf96dc5bc451a35
Author: Jay <wenyangzheng669@gmail.com>
Date: Thu Aug 15 15:40:36 2024 +0800
git log
命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是add test
,上一次是add World!
最早一次是add file1 and file2
。
如果觉得眼花缭乱,可以试试加上--petty=oneline
参数:
$ git log --pretty=oneline
580055a992098e39908a055baf83e3bc499066fa (HEAD -> master) add test
b14e1753944a5e2551a3e50dd61de9addc43a429 add World!
96f35b2622fbe957c8c1876fddf96dc5bc451a35 add file1 and file2
好了,我们开始讲解重头戏部分了,准备把file1.txt
回退到上个版本,也就是add World!
的那个版本
在这之前,我们必须知道当前版本是哪个版本,在Git中,用HEAD
表示当前版本,也就是最新提交的580055a...
(每个人的提交ID都是不一样的),上个版本就是HEAD^
上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
现在,我们要把当前版本add test
回退到上一个版本add World!
,就可以使用git reset
命令:
$ git reset --hard HEAD^
HEAD is now at b14e175 add World!
--hard
参数有啥意义?--hard
会回退到上个版本的已提交状态,而--soft
会回退到上个版本的未提交状态,--mixed
会回退到上个版本已添加但未提交的状态。现在,先放心使用--hard
。
看看file1.txt的内容是不是版本add World!
:
$ cat file1.txt
hello
World!
果然还是被还原了。
我们还可以继续回到上一个版本file1 and file2
,可以一直进行往前面回退,先让我们用git log
再看看现在版本库的状态:
$ git log
commit b14e1753944a5e2551a3e50dd61de9addc43a429 (HEAD -> master)
Author: Jay <wenyangzheng669@gmail.com>
Date: Thu Aug 15 16:54:55 2024 +0800
add World!
commit 96f35b2622fbe957c8c1876fddf96dc5bc451a35
Author: Jay <wenyangzheng669@gmail.com>
Date: Thu Aug 15 15:40:36 2024 +0800
add file1 and file2
可以发现,我们最新修改的那个add test
版本不见了,好比你从21世纪坐着哆啦A梦的时光穿梭机来到了19世纪,想再回去已经回不去了,怎么办?
其实还是有办法的,只要我们的窗口没被关掉,就可以一直顺着上面找,找回add test
的commit id
是580055a...
,于是就可以指定回到未来的某个版本:
$ git reset --hard 58005
HEAD is now at 580055a add test
版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写一两位,因为Git可以能找到多个版本号,就无法确定是哪一个了。
我们再查看file1.txt
的内容:
$ cat file1.txt
hello
World!
test
果然,还原到了最新修改了版本。
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD从指向add test
:
┌────┐
│HEAD│
└────┘
│
└──▶ ○ add test
│
○ add World!
│
○ add file1 and file2
刚刚我们做的回退操作其实都是在改变HEAD
指针的指向,有点C语言的味了,现在我们改为指向`add World!:
┌────┐
│HEAD│
└────┘
│
│ ○ add test
│ │
└──▶ ○ add World!
│
○ add file1 and file2
然后顺便把工作区的文件更新了。所以你让HEAD
指向哪个版本,你就把当前版本定位在哪。
现在,你回退到了某个版本,关掉电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id
怎么办?
在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^
回退到add World!
版本时,再想恢复到append GPL
,就必须找到add test
的commit id。Git提供了一个命令git reflog
用来记录你的每一次命令
$ git reflog
580055a (HEAD -> master) HEAD@{0}: reset: moving to 58005
b14e175 HEAD@{1}: reset: moving to HEAD^
3fb928e HEAD@{2}: commit (amend): add test
580055a (HEAD -> master) HEAD@{3}: commit: add test
b14e175 HEAD@{4}: commit: add World!
96f35b2 HEAD@{5}: commit (initial): add file1 and file2
工作区和暂存区
Git工作区和暂存区:
- 工作区:
工作区是本地计算机上的项目目录,你在这里进行文件的创建,修改和删除操作。工作区包含了当前项目的所有文件和子目录。
- 暂存区:
英文叫stage或insex。一般放在 `.git`目录下的index文件(,git/index)中,所以我们把暂存区有时也叫索引
- 版本库:
工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。
下面这个图简单说明了工作区和版本库之间的关系:
撤销修改
如果我们不小心在文件中添加了错误信息,但是好险及时被我们发现了,例如我们在file1.txt
文件中添加了这么一行内容:
$ cat file1.txt
hello
World!
test
Jay
I am a strong boy
如果发现得及时,就可以很容易纠正它,可以删除最后一行,手动把文件恢复到上一个版本的状态。用git status
查看一下:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file1.txt
no changes added to commit (use "git add" and/or "git commit -a")
可以发现,Git会告诉我们,git restore -- file
可以丢弃工作区的修改:
$ git restore -- file1.txt
命令git restore -- readme.txt
意思就是,把readme.txt
文件在工作区的修改全部撤销,这里有两种情况:
一种是file1.txt
自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是file1.txt
已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit
或git add
时的状态。
再看看file1.txt
的文件内容:
$ cat file1.txt
hello
World!
test
Jay
如果我们不小心把错误信息还git add
到暂存区了:
$ cat file1.txt
hello
World!
test
Jay
I am a strong boy
$ git add
在git commit
之前我们发现了这个问题
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file1.txt
Git告诉我们,用命令git restore --staged <file>
可以把暂存区的修改撤销掉(unstage),重新返回工作区:
$ git restore --staged file1.txt
git restore
命令既可以回退版本,也可以把暂存区的修改回退到工作区
再用git status
查看,现在暂存区是干净的,工作区有修改:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file1.txt
no changes added to commit (use "git add" and/or "git commit -a")
再重复上一阶段撤销修改的操作:
$ git restore -- file1.txt
$ git status
On branch master
nothing to commit, working tree clean
这就OK了!
注意:这些操作只是在你没有将文件发生的修改推送到远程,如果推送到远程仓库就麻烦了。
远程仓库
远程仓库是指托管在因特网或其他网络中你的项目的版本库。借助于远程仓库,我们可方便实现多人合作项目开发。
为完成协作开发,我们需要管理此远程仓库。此节首先给出管理仓库命令之间的关系,随后详细介绍他们。
-
git remote [-v]
此命令用于查看你所配置的远程服务器名。如果使用参数
-v
,则不仅可看到远程服务器名,也可看到远程服务器的具体 URL。
git remote
命令除查看配置的远程服务器信息外,也可执行与远程服务器配置相关的若干功能。
git remote add
:添加远程服务器。git remote show
:显示指定远程服务器的详细信息。git remote rm
:删除指定远程服务器。git remote rename
:指定远程服务器重命名。
-
git fetch
此命令用于从远程服务器
serverName
中拉取本地仓库中所没有的数据。需要注意的是:该命令只会拉取数据至本地仓库,而不会将其合并至工作区。git pull
命令不仅会拉取数据至本地仓库,同时将其合并至工作区。 -
git push
此命令用于将本地仓库当前分支推送至远程服务器
serverName
的branch
分支。
从远程仓库克隆
-
git colne
该命令会将远程仓库克隆到本地
$ $ git clone git@github.com:wyz609/gitkill.git Cloning into 'gitkill'... remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) Receiving objects: 100% (3/3), done.
进入
gitkill
目录瞅瞅,可以看见只有一个单调的README.md
文件$ cd gitkill/ $ ls README.md
如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。
你也许还注意到,GitHub给出的地址不止一个,还可以用
https://github.com/wyz609/gitskill.git
这样的地址。实际上,Git支持多种协议,默认的git://
使用ssh
,但也可以使用https
等其他协议。使用
https
除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http
端口的公司内部就无法使用ssh
协议而只能用https
。