Git入门教程!

1.Git简介

Git是什么?

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

Git有什么特点?分布式存储、去中心化

那什么是版本控制系统?

如果你用Microsoft Word写过长篇大论,那你一定有这样的经历:

想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,头皮发麻。

过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。

看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删,真郁闷。

更要命的是,有些部分需要你的财务同事帮助填写,于是你把文件Copy到U盘里给她(也可能通过Email发送一份给她),然后,你继续修改Word文件。一天后,同事再把Word文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你作了哪些改动,得把你的改动和她的部分合并,真困难。

于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?

这个软件用起来就应该像这个样子,能记录每次文件的改动:

版本 文件名 用户 说明 日期
1 service.doc 张三 删除了软件服务条款5 7/12 10:38
2 service.doc 张三 增加了License人数限制 7/12 18:09
3 service.doc 李四 财务部门调整了合同金额 7/13 9:51
4 service.doc 张三 延长了免费升级周期 7/14 15:17

这样,就结束了手动管理多个“版本”的史前时代,进入到版本控制的20世纪。

Git的诞生

很多人都知道,Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。

Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?

事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!

也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。

不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。

安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。

Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:

Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。

Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。

历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。

集中式vs分布式

CVS及SVN都是集中式的版本控制系统,而Git是分布式版本控制系统,集中式和分布式版本控制系统有什么区别呢?

先说集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。

集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,这还不得把人给憋死啊。

那分布式版本控制系统与集中式版本控制系统有何不同呢?首先,分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。

在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

当然,Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面。

CVS作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于CVS自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的SVN修正了CVS的一些稳定性问题,是目前用得最多的集中式版本库控制系统。

除了免费的外,还有收费的集中式版本控制系统,比如IBM的ClearCase(以前是Rational公司的,被IBM收购了),特点是安装比Windows还大,运行比蜗牛还慢,能用ClearCase的一般是世界500强,他们有个共同的特点是财大气粗,或者人傻钱多。

微软自己也有一个集中式版本控制系统叫VSS,集成在Visual Studio中。由于其反人类的设计,连微软自己都不好意思用了。

分布式版本控制系统除了Git以及促使Git诞生的BitKeeper外,还有类似Git的Mercurial和Bazaar等。这些分布式版本控制系统各有特点,但最快、最简单也最流行的依然是Git!

2.安装Git

从Git官网直接下载安装程序,然后按默认选项安装即可。

安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!

安装完成后,还需要最后一步设置,在命令行输入:

$ git config --global user.name "Your Name" 

$ git config --global user.email "[email protected]"

注意git config命令的--global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。

3.创建版本库

什么是版本库呢?版本库又名仓库,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

所以,创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录:

$ mkdir learngit
$ cd learngit
$ pwd
D:\learngit

pwd命令用于显示当前目录。在我的Mac上,这个仓库位于D:\earngit

第二步,通过git init命令把这个目录变成Git可以管理的仓库:

$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/

瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。

如果没有看到.git目录,那是因为这个目录默认是隐藏的,windows在查看中修改可见性,linux用ls -ah命令就可以看见。

4.git应用

git提交

创建一个readme.txt文件,在创建完后查看仓库工作区状态:

$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        readme.txt

nothing added to commit but untracked files present (use "git add" to track)

将新建的文件添加到暂存区

$ git add .
warning: LF will be replaced by CRLF in readme.txt.
The file will have its original line endings in your working directory

再次查看仓库工作区状态:

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   readme.txt

提交文件到本地仓库

$ git commit -m '添加第一个文件'
[master (root-commit) 1ac94a8] '添加第一个文件'
 1 file changed, 1 insertion(+)
 create mode 100644 readme.txt

查看仓库工作区状态:

$ git status
On branch master
nothing to commit, working tree clean

git修改

我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.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 checkout -- <file>..." to discard changes in working directory)

	modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

git status命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,readme.txt被修改过了,但还没有准备提交的修改。

虽然Git告诉我们readme.txt被修改了,但如果能看看具体修改了什么内容,自然是很好的。比如你出差回来,第一天上班时,已经记不清上次怎么修改的readme.txt,所以,需要用git diff这个命令看看:

$ git diff readme.txt
warning: LF will be replaced by CRLF in readme.txt.
The file will have its original line endings in your working directory
diff --git a/readme.txt b/readme.txt
index 8c44102..f6b4656 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-第一个git文件
+鹅鹅鹅,曲项向天歌。白毛浮绿水,红掌拨清波。

git diff顾名思义就是查看difference,显示的格式正是Unix通用的diff格式,可以从上面的命令输出看到,我们在第一行添加了一个distributed单词。

知道了对readme.txt作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是git add

$ git add readme.txt

同样没有任何输出。在执行第二步git commit之前,我们再运行git status看看当前仓库的状态:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   readme.txt

git status告诉我们,将要被提交的修改包括readme.txt,下一步,就可以放心地提交了:

$ git commit -m "add distributed"
[master e475afc] add distributed
 1 file changed, 1 insertion(+), 1 deletion(-)

提交后,我们再用git status命令看看仓库的当前状态:

$ git status
On branch master
nothing to commit, working tree clean

Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)的。

git版本回退

现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下:

鸡鸡鸡,尖嘴对天啼。三更呼皓月,五鼓唤晨曦。

然后尝试提交:

$ git add readme.txt
$ git commit -m "咏鸡"
[master 1094adb] 咏鸡
 1 file changed, 1 insertion(+), 1 deletion(-)

像这样,你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失。

现在,我们回顾一下readme.txt文件一共有几个版本被提交到Git仓库里了:

版本1:

第一个git文件

版本2:

鹅鹅鹅,曲项向天歌。白毛浮绿水,红掌拨清波。

版本3:

鸡鸡鸡,尖嘴对天啼。三更呼皓月,五鼓唤晨曦。

当然了,在实际工作中,怎么可能记得一个几千行的文件每次都改了什么内容,不然要版本控制系统干什么。版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log命令查看:

$ git log
commit 21c85dfdb8a9672c6af93615d55ea3f2255f8b6f (HEAD -> master)
Author: liming <[email protected]>
Date:   Tue Mar 9 11:08:21 2021 +0800

    '提交咏鸡'

commit 0445d9fd8ef2ccdbfc0c83782359d1f9e46dd740
Author: liming <[email protected]>
Date:   Tue Mar 9 10:55:00 2021 +0800

    '提交咏鹅'

commit 1ac94a81650592d5d37f21623fa5ab4b32ef2671
Author: liming <[email protected]>
Date:   Tue Mar 9 10:06:24 2021 +0800

    '添加第一个文件'

git log命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是append GPL,上一次是add distributed,最早的一次是wrote a readme file

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:

$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) 咏鸡
e475afc93c209a690c39c13a46716e8fa000c366 咏鹅
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 第一个文件

需要友情提示的是,你看到的一大串类似1094adb...的是commit id(版本号),和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id和我的肯定不一样,以你自己的为准。为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。

每提交一个新版本,实际上Git就会把它们自动串成一条时间线。如果使用可视化工具查看Git历史,就可以更清楚地看到提交历史的时间线。

好了,现在我们启准备把readme.txt回退到上一个版本,也就是提交咏鹅的那个版本,怎么做呢?

首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交1094adb...(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

现在,我们要把当前版本append GPL回退到上一个版本add distributed,就可以使用git reset命令:

$ git reset --hard HEAD^^
HEAD is now at 0445d9f '提交咏鹅'

看看readme.txt的内容是不是版本提交咏鹅

$ cat readme.txt
鹅鹅鹅,曲项向天歌。白毛浮绿水,红掌拨清波。

果然被还原了。

还可以继续回退到上一个版本提交第一个文件,不过且慢,让我们用git log再看看现在版本库的状态:

$ git log
commit 0445d9fd8ef2ccdbfc0c83782359d1f9e46dd740
Author: liming <[email protected]>
Date:   Tue Mar 9 10:55:00 2021 +0800

    '提交咏鹅'

commit 1ac94a81650592d5d37f21623fa5ab4b32ef2671
Author: liming <[email protected]>
Date:   Tue Mar 9 10:06:24 2021 +0800

    '添加第一个文件'

最新的那个版本提交咏鸡已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办?

办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个提交咏鸡commit id21c85df...,于是就可以指定回到未来的某个版本:

$ git reset --hard 21c85df
HEAD is now at 21c85df append GPL

版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

再小心翼翼地看看readme.txt的内容:

$ cat readme.txt

鸡鸡鸡,尖嘴对天啼。三更呼皓月,五鼓唤晨曦。

果然,我又回来了。

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL

┌────┐
│HEAD│
└────┘
   │
   └──> ○ 提交咏鸡
        │
        ○ 提交咏鹅
        │
        ○ 第一个git文件

改为指向提交咏鹅

┌────┐
│HEAD│
└────┘
   │
   │    ○ 提交咏鸡
   │    │
   └──> ○ 提交咏鹅
        │
        ○ 第一个git文件

然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。

现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?

在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^^回退到提交咏鹅版本时,再想恢复到提交咏鸡,就必须找到提交咏鸡的commit id。Git提供了一个命令git reflog用来记录你的每一次命令:

$ git reflog
21c85df (HEAD -> master) HEAD@{0}: reset: moving to 21c85df
0445d9f HEAD@{1}: reset: moving to HEAD^
21c85df (HEAD -> master) HEAD@{2}: reset: moving to HEAD
21c85df (HEAD -> master) HEAD@{3}: reset: moving to HEAD
21c85df (HEAD -> master) HEAD@{4}: commit: '提交咏鸡'
0445d9f HEAD@{5}: commit: '提交咏鹅'
1ac94a8 HEAD@{6}: commit (initial): '添加第一个文件'

终于舒了口气,从输出可知,提交咏鸡的commit id是21c85df,现在,你又可以乘坐时光机回到未来了。

总结一下:

  • HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id

  • 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。

  • 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。

工作区和暂存区

工作区(Working Directory)

就是在电脑里能看到的目录,比如learngit文件夹就是一个工作区。

版本库(Repository)

工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

git-repo

分支和HEAD的概念我们以后再讲。

前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

俗话说,实践出真知。现在,我们再练习一遍,先对readme.txt做个修改,比如加上一行内容:

嗯嗯嗯,是否改变了

然后,在工作区新增一个LICENSE文本文件(内容随便写)。

先用git status查看一下状态:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   readme.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	LICENSE

no changes added to commit (use "git add" and/or "git commit -a")

Git非常清楚地告诉我们,readme.txt被修改了,而LICENSE还从来没有被添加过,所以它的状态是Untracked

现在,使用两次命令git add,把readme.txtLICENSE都添加后,用git status再查看一下:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   LICENSE
	modified:   readme.txt

现在,暂存区的状态就变成这样了:

git-stage

所以,git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。

$ git commit -m '修改和添加'
[master d411afb] '修改和添加'
 2 files changed, 2 insertions(+)
 create mode 100644 LICENSE

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

$ git status
On branch master
nothing to commit, working tree clean

现在版本库变成了这样,暂存区就没有任何内容了:

git-stage-after-commit

管理修改

现在,假定已经完全掌握了暂存区的概念。下面,我们要讨论的就是,为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。

你会问,什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。

为什么说Git管理的是修改,而不是文件呢?我们还是做实验。第一步,对readme.txt做一个修改,比如加一行内容:

$ cat readme.txt

鸡鸡鸡,尖嘴对天啼。三更呼皓月,五鼓唤晨曦。

嗯嗯嗯,是否改变了

是是是,改变了

然后,添加:

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   readme.txt
        
$ git add readme.txt

然后,再修改readme.txt:

$ cat readme.txt

鸡鸡鸡,尖嘴对天啼。三更呼皓月,五鼓唤晨曦。


嗯嗯嗯,是否改变了


是是是,改变了

再次修改

提交:

$ git commit -m '第几次修改呢'
[master 4996a3a] '第几次修改呢'
 1 file changed, 4 insertions(+)

提交后,再看看状态:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

咦,怎么第二次的修改没有被提交?

别激动,我们回顾一下操作过程:

第一次修改 -> git add -> 第二次修改 -> git commit

你看,我们前面讲了,Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。

提交后,用git diff HEAD -- readme.txt命令可以查看工作区和版本库里面最新版本的区别:

$ git diff HEAD -- readme.txt
diff --git a/readme.txt b/readme.txt
index 74a456e..28c531d 100644
--- a/readme.txt
+++ b/readme.txt
@@ -6,3 +6,5 @@


 是是是,改变了
+
+再次修改

可见,第二次修改确实没有被提交。

撤销修改

在修改后,由于多次往暂存区推送,现在readme.txt文件内容如下:

$ cat readme.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:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

可以发现,Git会告诉你,git restore <file>可以丢弃工作区的修改:

$ git restore readme.txt

命令git restore readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:

一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

一种是readme.txt已经添加到暂存区后,又做了修改,现在,撤销修改就回到添加到暂存区后的状态。

总之,就是让这个文件回到最近一次git commitgit add时的状态。

现在,看看readme.txt的文件内容:

$ cat readme.txt

鸡鸡鸡,尖嘴对天啼。三更呼皓月,五鼓唤晨曦。


嗯嗯嗯,是否改变了


是是是,改变了

文件内容果然复原了。

上面说了是工作区的代码修改撤销,如果说提交到的暂存区:

$ cat readme.txt

鸡鸡鸡,尖嘴对天啼。三更呼皓月,五鼓唤晨曦。


嗯嗯嗯,是否改变了


是是是,改变了


暂存区修改后撤销

在commit之前,发现有问题,修改已经添加到了暂存区,还没有提交,使用 git status查看如下:

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   readme.txt


git会提示,用命令git restore --staged <file>可以把暂存区的修改撤掉,重新放回工作区:

$ git restore --staged readme.txt

D:\learngit (master)
$ 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:   readme.txt

此时可以看到代码已经在工作区,可以在撤销工作区修改:

$ git restore readme.txt

D:\learngit (master)
$ git status
On branch master
nothing to commit, working tree clean

小结:

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git restore file

场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git restore --staged <file>,就回到了场景1,第二步按场景1操作。

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

删除文件

在Git中,删除也是一个修改操作,先添加一个新文件test.txt到Git并且提交:

$ git add test.txt
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory

D:\learngit (master)
$ git commit -m '添加测试文件'
[master 51c0418] '添加测试文件'
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt

一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm命令删了:

$ rm test.txt

这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了:

$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    test.txt

no changes added to commit (use "git add" and/or "git commit -a")

现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit

$ git rm test.txt
rm 'test.txt'

D:\learngit (master)
$ git commit -m '删除版本库测试文件'
[master d4849ff] '删除版本库测试文件'
 1 file changed, 1 deletion(-)
 delete mode 100644 test.txt

现在,文件就从版本库中被删除了。

另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

$ git restore <file>

5.远程仓库

1、 创建SSHKEY

-- 仓库内进入Git bash here 然后执行:
ssh-keygen -t rsa -C “github的注册邮箱”

2、 配置激活密钥,使该电脑可对你的Github提交文件

ssh -T [email protected]

3、 gitlab上创建远程仓库

a、打开C:\Users\youruser\.ssh内的id_rsa.pub文件,并复制其内容到gitlab

b、在gitlab页面左侧的导航中选择SSH Keys
 
c、在Key中输入刚刚复制内容,标题随便起,单击Add key。就可以添加key了

d、添加完远程仓库之后。gitlab在右上角找到“Newproject”按钮,创建一个新的仓库

在Project name填入`learngit`,其他保持默认设置,点击“Create project”按钮,就成功地创建了一个新的Git仓库

目前,在GitLab上的这个`learngit`仓库还是空的,GitLab告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitLab仓库。

现在,我们根据GitLab的提示,在本地的`learngit`仓库下运行命令:

$ git remote add origin http://127.0.0.1/liming/learngit.git

添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。

下一步,就可以把本地库的所有内容推送到远程库上:

$ git push origin master
Enumerating objects: 20, done.
Counting objects: 100% (20/20), done.
Delta compression using up to 8 threads
Compressing objects: 100% (14/14), done.
Writing objects: 100% (20/20), 1.81 KiB | 168.00 KiB/s, done.
Total 20 (delta 3), reused 0 (delta 0), pack-reused 0
To http://127.0.0.1/liming/learngit.git
 * [new branch]      master -> master

推送成功后,可以立刻在GitLab页面中看到远程库的内容已经和本地一模一样
从现在起,只要本地作了提交,就可以通过命令:

$ git push origin master

把本地master分支的最新修改推送至GitLab,现在,就拥有了真正的分布式版本库!

删除远程库

如果添加的时候地址写错了,或者就是想删除远程库,可以用git remote rm <name>命令。使用前,建议先用git remote -v查看远程库信息:

$ git remote -v
origin  http://127.0.0.1/liming/learngit.git (fetch)
origin  http://127.0.0.1/liming/learngit.git (push)

然后,根据名字删除,比如删除origin

$ git remote rm origin

此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。

小结

要关联一个远程库,使用命令$ git remote add origin http://127.0.0.1/liming/learngit.git

关联后,使用命令git push origin master第一次推送master分支的所有内容;

此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;

分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!

从远程库克隆

前面讲了先有本地库,后有远程库的时候,如何关联远程库。

现在,假设我们从零开发,那么最好的方式是先创建远程库,然后,从远程库克隆。

首先,登陆GitLab,创建一个新的仓库,名字叫gitskills

我们勾选Initialize this repository with a README,这样GitLab会自动为我们创建一个README.md文件。创建完毕后,可以看到README.md文件:

现在,远程库已经准备好了,下一步是用命令git clone克隆一个本地库:

$ git clone http://127.0.0.1/liming/gitskills.git
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 215 bytes | 16.00 KiB/s, done.

6.分支管理

分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。

但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。

创建与合并分支

版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

git-br-initial

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

git-br-create

你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!

不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

git-br-dev-fd

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

git-br-ff-merge

所以Git合并分支也很快!就改改指针,工作区内容也不变!

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

git-br-rm

接下来,实际操作:

首先,我们创建dev分支,然后切换到dev分支:

$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

然后,用git branch命令查看当前分支:

$ git branch
* dev
  master

git branch命令会列出所有分支,当前分支前面会标一个*号。

$ git status
On branch dev
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        readme.txt

nothing added to commit but untracked files present (use "git add" to track)

D:\gitskills (dev)
$ git add readme.txt
warning: LF will be replaced by CRLF in readme.txt.
The file will have its original line endings in your working directory

D:\gitskills (dev)
$ git commit -m '创建分支dev'
[dev c0a7cbe] '创建分支dev'
 1 file changed, 1 insertion(+)
 create mode 100644 readme.txt

现在,dev分支的工作完成,我们就可以切换回master分支:

$ git checkout master
Switched to branch 'master'

切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:

$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

D:\gitskills (master -> origin)
$ ls
README.md

现在,我们把dev分支的工作成果合并到master分支上:

$ git merge dev
Updating 30f0136..c0a7cbe
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 readme.txt

git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。

注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

合并完成后,就可以放心地删除dev分支了:

$ git branch -d dev
Deleted branch dev (was c0a7cbe).

删除后,查看branch,就只剩下master分支了:

$ git branch
* master

因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

解决冲突

准备一个新的feature1分支,继续我们的新分支开发:

$ git checkout -b feature1
Switched to a new branch 'feature1'

修改readme.txt,改为:

$ cat readme.txt
新建一个分支dev

冲突1

feature1分支上提交:

$ git add .

D:\gitskills (feature1)
$ git commit -m '提交feature1'
[feature1 3c43442] '提交feature1'
 1 file changed, 2 insertions(+)

切换到master分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Git还会自动提示我们当前master分支比远程的master分支要超前1个提交。

master分支上把readme.txt文件的最后一行改为:

D:\gitskills (master -> origin)
$ cat readme.txt

新建一个分支dev

master分支冲突

提交:

$ git add .

D:\gitskills (master -> origin)
$ git commit -m '提交master'
[master 1f8e21e] '提交master'
 1 file changed, 4 insertions(+)

现在,master分支和feature1分支各自都分别有新的提交,变成了这样:

git-br-feature1

这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:

$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

果然冲突了!Git告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

我们可以直接查看readme.txt的内容:

$ cat readme.txt

新建一个分支dev

<<<<<<< HEAD
master分支冲突

=======
冲突1
>>>>>>> feature1

Git用<<<<<<<=======>>>>>>>标记出不同分支的内容,我们修改如下后保存:

$ cat readme.txt

新建一个分支dev


master分支冲突


冲突1

再提交:

$ git add .

D:\gitskills (master -> origin)
$ git commit -m '解决冲突'
[master d6720a6] '解决冲突'

现在,master分支和feature1分支变成了下图所示:

git-br-conflict-merged

用带参数的git log也可以看到分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit
*   d6720a6 (HEAD -> master) '解决冲突'
|\
| * 3c43442 (feature1) '提交feature1'
* | 1f8e21e '提交master'
|/
* c0a7cbe '创建分支dev'
* 30f0136 (origin/master, origin/HEAD) Initial commit

最后,删除feature1分支:

$ git branch -d feature1
Deleted branch feature1 (was 3c43442).

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

7.标签管理

发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。

Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。

Git有commit,为什么还要引入tag?

“请把上周一的那个版本打包发布,commit号是6a5819e…”

“一串乱七八糟的数字不好找!”

如果换一个办法:

“请把上周一的那个版本打包发布,版本号是v1.2”

“好的,按照tag v1.2查找commit就行!”

所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。

创建标签

在Git中打标签非常简单,首先,切换到需要打标签的分支上:

$ git branch
* dev
  master

D:\gitskills (dev)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 4 commits.
  (use "git push" to publish your local commits)

然后,敲命令git tag <name>就可以打一个新标签:

$ git tag v1.0

可以用命令git tag查看所有标签:

$ git tag
v1.0

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?

方法是找到历史提交的commit id,然后打上就可以了:

$ git log --oneline
d6720a6 (HEAD -> master, tag: v1.0, dev) '解决冲突'
1f8e21e '提交master'
3c43442 '提交feature1'
c0a7cbe '创建分支dev'
30f0136 (origin/master, origin/HEAD) Initial commit

比方说要对创建分支dev这次提交打标签,它对应的commit id是c0a7cbe,敲入命令:

$ git tag v0.9 c0a7cbe

再用命令git tag查看标签:

$ git tag
v0.9
v1.0

注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show <tagname>查看标签信息:

$ git show v0.9
commit c0a7cbeea8e04ad36ad0122c945b9e3f7c11833d (tag: v0.9)
Author: liming <[email protected]>
Date:   Wed Mar 10 08:27:05 2021 +0800

    '创建分支dev'

diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..3500ab9
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1 @@
+新建一个分支dev

可以看到,v0.9确实打在创建分支dev这次提交上。

还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:

$ git show v0.1
tag v0.1
Tagger: liming <[email protected]>
Date:   Wed Mar 10 13:40:09 2021 +0800

'打tag操作'

commit 3c43442bad2682eea9c238cc8873d1df957f5007 (tag: v0.1)
Author: liming <[email protected]>
Date:   Wed Mar 10 08:52:27 2021 +0800

    '提交feature1'

diff --git a/readme.txt b/readme.txt
index 3500ab9..01f4272 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,3 @@
 新建一个分支dev

小结

  • 命令git tag <tagname>用于新建一个标签,默认为HEAD,也可以指定一个commit id;
  • 命令git tag -a <tagname> -m "blablabla..."可以指定标签信息;
  • 命令git tag可以查看所有标签。

操作标签

如果标签打错了,也可以删除:

$ git tag -d v0.1
Deleted tag 'v0.1' (was 358be78)

因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。

如果要推送某个标签到远程,使用命令git push origin <tagname>

$ git push origin --tags
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 8 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (12/12), 1.15 KiB | 195.00 KiB/s, done.
Total 12 (delta 1), reused 0 (delta 0), pack-reused 0
To http://127.0.0.1/liming/gitskills.git
 * [new tag]         v0.9 -> v0.9
 * [new tag]         v1.0 -> v1.0

如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:

$ git tag -d v0.9
Deleted tag 'v0.9' (was c0a7cbe)

然后,从远程删除。删除命令也是push,但是格式如下:

$ git push origin :refs/tags/v0.9
To http://127.0.0.1/liming/gitskills.git
 - [deleted]         v0.9

自定义Git

在安装Git后,我们已经配置了user.nameuser.email,实际上,Git还有很多可配置项。

比如,让Git显示颜色,会让命令输出看起来更醒目:

$ git config --global color.ui true

忽略特殊文件

有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次git status都会显示Untracked files ...,有强迫症的童鞋心里肯定不爽。

好在Git考虑到了大家的感受,这个问题解决起来也很简单,在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。

不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore

忽略文件的原则是:

  1. 忽略操作系统自动生成的文件,比如缩略图等;
  2. 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
  3. 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

已提交文件忽略

我们在使用git中,经常会遇到这样的情况:

git仓库中有某个必要的配置文件,这个文件应该存在,但是在每个开发人员的本地都要对他进行修改。这就导致这个文件要被反复修改,容易冲突。要想不冲突,就要每个人提交前,都刻意不提交该文件,很是麻烦。

一般我们这时候就会想到,用.gitignore来忽略这个文件,但是该文件是已经提交过的,并且对于工程是必要的,应该保存在git中的。.gitignore对于这种已提交过的文件是无能为力的。

这时候,就需要使用git update-index --skip-worktree命令了。

该命令的作用是,让git在搜索文件列表时,忽略某个文件,这样该文件即使有修改,git也不会关心。

#例子:

$ git update-index --skip-worktree readme.txt

使用这个命令,时间久了,可能会忘记自己忽略过哪些文件,这时候可以使用git ls-files -v . | grep "^S"命令找出来忽略过的文件。

使用这个命令,时间久了,可能会忘记自己忽略过哪些文件,这时候可以使用git ls-files -v . | grep "^S"命令找出来忽略过的文件。

#例子:

$ git ls-files -v . |grep "^S"
S readme.txt

#输出:S readme.txt
不想继续忽略该文件时,使用git update-index --no-skip-worktree命令,来让git不再忽略该文件。

#例子:

$ git update-index --no-skip-worktree readme.txt

配置代理

#http代理
git config --global http.proxy http://127.0.0.1:8080
git config --global https.proxy https://127.0.0.1:8080

#取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy

#socks代理
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'

虑到了大家的感受,这个问题解决起来也很简单,在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。

不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore

忽略文件的原则是:

  1. 忽略操作系统自动生成的文件,比如缩略图等;
  2. 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
  3. 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

已提交文件忽略

我们在使用git中,经常会遇到这样的情况:

git仓库中有某个必要的配置文件,这个文件应该存在,但是在每个开发人员的本地都要对他进行修改。这就导致这个文件要被反复修改,容易冲突。要想不冲突,就要每个人提交前,都刻意不提交该文件,很是麻烦。

一般我们这时候就会想到,用.gitignore来忽略这个文件,但是该文件是已经提交过的,并且对于工程是必要的,应该保存在git中的。.gitignore对于这种已提交过的文件是无能为力的。

这时候,就需要使用git update-index --skip-worktree命令了。

该命令的作用是,让git在搜索文件列表时,忽略某个文件,这样该文件即使有修改,git也不会关心。

#例子:

$ git update-index --skip-worktree readme.txt

使用这个命令,时间久了,可能会忘记自己忽略过哪些文件,这时候可以使用git ls-files -v . | grep "^S"命令找出来忽略过的文件。

使用这个命令,时间久了,可能会忘记自己忽略过哪些文件,这时候可以使用git ls-files -v . | grep "^S"命令找出来忽略过的文件。

#例子:

$ git ls-files -v . |grep "^S"
S readme.txt

#输出:S readme.txt
不想继续忽略该文件时,使用git update-index --no-skip-worktree命令,来让git不再忽略该文件。

#例子:

$ git update-index --no-skip-worktree readme.txt

配置代理

#http代理
git config --global http.proxy http://127.0.0.1:8080
git config --global https.proxy https://127.0.0.1:8080

#取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy

#socks代理
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'

猜你喜欢

转载自blog.csdn.net/qq_34362409/article/details/115320924