Git学习

1 Git简介

  Git是目前世界上最先进的分布式版本控制系统(没有之一)。用于跟踪和管理项目代码的变化。它允许多个开发者在同一个项目上并行工作,并提供了管理代码版本、合并代码、撤销更改等功能。

1.1 版本控制

1. 手动版本控制

  如果你用Microsoft Word写论文,那你一定有这样的经历:想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的Word文档变成了这样:
在这里插入图片描述
  过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。
看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删,真郁闷。
  更要命的是,有些部分需要你的财务同事帮助填写,于是你把文件Copy到U盘里给她(也可能通过Email发送一份给她),然后,你继续修改Word文件。一天后,同事再把Word文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你作了哪些改动,得把你的改动和她的部分合并,真困难。
  于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?

2.自动版本控制

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

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

  这样,你就结束了手动管理多个“版本”的史前时代,进入到版本控制的20世纪。
  vss:微软
  cvs:开源
  svn:google
  git:分布式版本控制工具(标准)

1.2 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了。

1.3 分布式版本控制

  Linus一直痛恨的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实战

2.1 安装Git

  最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑。不过,慢慢地有人把它移植到了Windows上。现在,Git可以  在Linux、Unix、Mac和Windows这几大平台上正常运行了。
在Windows上使用Git,可以从Git官网直接下载安装程序,(网速慢的同学请移步国内镜像),然后按默认选项安装即可。
  安装完成后,在开始菜单里找到“Git”->“Git ash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!
在这里插入图片描述
  安装完成后,还需要最后一步设置,在命令行输入:

$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
$ git config --global --get user.name #得到user.name的值
$ git config --global -l #列表参数

  因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。
  注意 git config 命令的 --global 参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。

2.2 创建版本库

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

1 创建版本库

cd /e/ #window的git bash中进入e盘
$ mkdir learngit
$ cd learngit
$ pwd
/Users/michael/learngit
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/

  如果你使用Windows系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文。通过 git init 命令把这个目录变成Git可以管理的仓库:
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现
  当前目录下多了一个 .git 的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
  如果你没有看到 .git 目录,那是因为这个目录默认是隐藏的,用 ls -ah 命令就可以看见。
  首先这里再明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。
  不幸的是,Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的,前面我们举的例子只是为了演示,如果要真正使用版本控制系统,就要以纯文本方式编写文件。因为文本是有编码的,比如中文有常用的GBK编码,日文有Shift_JIS编码,如果没有历史遗留问题,强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。
  使用Windows的童鞋要特别注意:千万不要使用Windows自带的记事本编辑任何文本文件。原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符,你会遇到很多不可思议的问题,比如,网页第一行可能会显示一个“?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。建议你下载Notepad++代替记事本,不但功能强大,而且免费!记得把Notepad++的默认编码设置为UTF-8without BOM即可

2 添加文件到版本库

  现在我们编写一个readme.txt文件,内容如下:

Git is a version control system
Git is free sofeware

  一定要放到 learngit 目录下(子目录也行),因为这是一个Git仓库,放到其他地方Git再厉害也找不到这个文件。
  和把大象放到冰箱需要3步相比,把一个文件放到Git仓库只需要两步。
  (1)用命令 git add 告诉Git,把文件添加到仓库:

$ git add readme.txt

  执行上面的命令,没有任何显示,这就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。
  (2)用命令 git commit 告诉Git,把文件提交到仓库:

$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
$ git log
commit 6e4de12122b22ee3145dfa87af974d284f832dd2 (HEAD -> master)
Author: 高昆 <3185342850@qq.com>
Date: Tue Jul 11 09:57:08 2023 +0800
wrote a readme file

  简单解释一下 git commit 命令, -m 后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
  嫌麻烦不想输入 -m “xxx” 行不行?确实有办法可以这么干,但是强烈不建议你这么干,因为输入说明对自己对别人阅读都很重要。实在不想输入说明的童鞋请自行Google,我不告诉你这个参数。
git commit 命令执行成功后会告诉你, 1 file changed :1个文件被改动(我们新添加的readme.txt文件); 2 insertions :插入了两行内容(readme.txt有两行内容)。
  为什么Git添加文件需要 add , commit 一共两步呢?因为 commit 可以一次提交很多文件,所以你可以多次 add 不同的文件,比如:

$ touch file1.txt,file2.txt,file3.txt
$ git add file1.txt
$ git add file2.txt file3.txt 或者git add . #添加所有untrack文件到stage

  使用git status查看git状态

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

在这里插入图片描述
  提交

$ git commit -m "add 3 file"

在这里插入图片描述

总结
git add . 把工作区文件添加到暂存区
git commit -m “xxxx”:把暂存区(stage)文件提交到分支
git status:查看git版本库状态
git log:查看版本提交

2.3 版本控制

2.3.1 修改readme.txt

  修改readme.txt文件,改成如下内容:

Git is a distributed version control system.
Git is free software.

  运行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
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.

  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)的。

2.3.2 版本回退

1.准备三个版本

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

Git is a distributed version control system.
Git is free software distributed under the GPL.

  然后尝试提交:

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

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

版本1:wrote a readme file

Git is a version control system.
Git is free software.

版本2:add three file

file1, file2, file3

版本3:add distributed

Git is a distributed version control system.
Git is free software.

版本4:append GPL

Git is a distributed version control system.
Git is free software distributed under the GPL.

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

$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file

  git log 命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是 append GPL ,上一次是 add distributed ,最早的一次是 wrote a readme file 。
  如果嫌输出信息太多,看得眼花缭乱的,可以试加上 --pretty=oneline 参数:

$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
e475afc93c209a690c39c13a46716e8fa000c366 add distributed
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file

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

2 版本回退

  好了,现在我们启动时光穿梭机,准备把 readme.txt 回退到上一个版本,也就是 add distributed 的那个版本,怎么做呢?
  首先,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 e475afc add distributed

  --hard 参数有啥意义?这个后面再讲,现在你先放心使用。
  看看 readme.txt 的内容是不是版本 add distributed :

$ cat readme.txt
Git is a distributed version control system.
Git is free software.

  果然被还原了。
  还可以继续回退到上一个版本 wrote a readme file ,不过且慢,然我们用 git log 再看看现在版本库的状态:

$ git log
commit e475afc93c209a690c39c13a46716e8fa000c366 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file

  最新的那个版本 append GPL 已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办?
  办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个append GPL 的 commit id 是 1094adb… ,于是就可以指定回到未来的某个版本:

$ git reset --hard 1094a
HEAD is now at 83b0afe append GPL

  版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
  git reset 命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用 HEAD`时,表示最新的版本。

  --soft :仅仅移动本地库 HEAD 指针
  --mixed: 移动本地库 HEAD 指针,重置暂存区
  --hard:移动HEAD指针,重置暂存区,工作区,省略git checkout – file

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

$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.

  果然,我胡汉三又回来了。
  Git的版本回退速度非常快,因为Git在内部有个指向当前版本的 HEAD 指针,当你回退版本的时候,
  Git仅仅是把HEAD从指向 append GPL :

┌────┐
│HEAD│
└────┘
│
└──> ○ append GPL
│
○ add distributed
│
○ wrote a readme file

  改为指向 add distributed :

┌────┐
│HEAD│
└────┘
│
│ ○ append GPL
│ │
└──> ○ add distributed
│
○ wrote a readme file

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

$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file

  终于舒了口气,从输出可知, append GPL 的commit id是 1094adb ,现在,你又可以乘坐时光机回到未来了。

2.3.3 工作区和暂存区

  Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。先来看名词解释。

  1. 工作区(Working Directory)
      就是你在电脑里能看到的目录,比如我的 learngit 文件夹就是一个工作区:
    在这里插入图片描述
  2. 版本库(Repository)
      工作区有一个隐藏目录 .git ,这个不算工作区,而是Git的版本库。
      (1) 版本库基本状态
      Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支 master ,以及指向 master 的一个指针叫 HEAD 。
    在这里插入图片描述

  前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
  第一步是用 git add 把文件添加进去,实际上就是把文件修改添加到暂存区;
  第二步是用 git commit 提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个 master 分支,所以,现在, git
commit 就是往 master 分支上提交更改。
  你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
  先对 readme.txt 做个修改,比如加上一行内容:

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.

  然后,在工作区新增一个 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.txt 和 LICENSE 都添加后,用 git status 再查看一
下:

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

  (2) add两次后的版本库状态

git add .

  执行后git版本库状态如下:
在这里插入图片描述
  所以, git add 命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行 git
commit 就可以一次性把暂存区的所有修改提交到分支。

$ git commit -m "understand how stage works"
[master e43a48b] understand how stage works
2 files changed, 2 insertions(+)
create mode 100644 LICENSE

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

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

(3)commit后的版本库状态
在这里插入图片描述

2.3.4 撤销修改

  上一个小节使用的

git reset --hard HEAD^ #
#等价于
git reset --mixed HEAD^
git checkout -- readme.txt

  --hard:重置工作区和暂存区,修改HEAD指针
  --mixed:重置暂存区,修改HEAD指针
  --soft:只修改HEAD指针
  git checkout:重置工作区到版本库

  自然,你是不会犯错的。不过现在是凌晨两点,你正在赶一份工作报告,你在 readme.txt 中添加了一行:

$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
My stupid boss still prefers SVN.

  在你准备提交前,一杯咖啡起了作用,你猛然发现了 stupid boss 可能会让你丢掉这个月的奖金!既然错误发现得很及时,就可以很容易地纠正它。你可以删掉最后一行,手动把文件恢复到上一个版本的状态。如果用 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会告诉你, git checkout – file 可以丢弃工作区的修改:

$ git checkout -- readme.txt

  命令 git checkout – readme.txt 意思就是,把 readme.txt 文件在工作区的修改全部撤销,这里有
两种情况:
  一种是 readme.txt 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
  一种是 readme.txt 已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状
态。
  总之,就是让这个文件回到最近一次 git commit 或 git add 时的状态。
  现在,看看 readme.txt 的文件内容:

$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.

  文件果然复原了
  git checkout – file 命令中的 – 很重要,没有 – ,就变成了“切换到另一个分支”的命令,我们
在后面的分支管理中会再次遇到 git checkout 命令。
  现在假定是凌晨3点,你不但写了一些胡话,还 git add 到暂存区了:

$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
My stupid boss still prefers SVN.
$ git add readme.txt

  庆幸的是,在 commit 之前,你发现了这个问题。用 git status 查看一下,修改只是添加到了暂
存区,还没有提交:

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

  Git同样告诉我们,用命令 git reset HEAD 可以把暂存区的修改撤销掉(unstage),重新放
回工作区:

$ git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt

  git reset 命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用 HEAD 时,表示最新的版本。

  --soft :仅仅移动本地库 HEAD 指针
  --mixed: 移动本地库 HEAD 指针,重置暂存区
  --hard:移动HEAD指针,重置暂存区,工作区,省略git checkout – file

  再用 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

  还记得如何丢弃工作区的修改吗?

$ git checkout -- readme.txt
$ git status
On branch master
nothing to commit, working tree clean

总结

#撤销修改
1:
git reset --hard HEAD^ readme.txt
--hard
--soft
--mixed
2:
git reset --mixed HEAD readme.txt
git checkout -- readme.txt

2.3.5 删除文件

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

$ git add test.txt
$ git commit -m "add test.txt"
[master b84166e] add test.txt
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 checkout -- <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'
$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt

  现在,文件就从版本库中被删除了。
  小提示:先手动删除文件,然后使用git rm 和git add效果是一样的。
  另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

$ git checkout -- test.txt

  git checkout 其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
  注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!

2.4 总结

  1. 配置Git的全局用户名称和邮箱
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

$ git config --global --get user.name    #得到user.name的值
$ git config --global -l    #列表参数
  1. 创建新版本
cd /e/    #Windows的git bash中进入e盘;在e盘右键点git bash
$ mkdir learngit

$ cd learngit

$ pwd 
/e/learngit

$ git init    #初始化,把目录变为git可以管理的仓库
Initialized empty Git repository in /Users/michael/learngit/.git/
  1. 添加新文件到版本库
 touch readme.txt       #先建文件在learngit下
 # 写入内容
 # Git is a version control system
 # Git is free software
 
 $ git add readme.txt    #将文件加入暂存区 || git add . 添加所有工作区文件到暂存区
 
 $ git commit -m "wrote a readme.txt"    	#把文件提交到分支,并添加说明“wrote a readme.txt”
 
 $ git log    # 查看当前仓库的提交历史记录
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
   wrote a readme file

 $ git status    #查看git状态
  1. 修改版本
$ git diff readme.txt      #查看修改了哪些内容
$ git reset --hard HEAD^    #回退到上一个版本 ,HEAD为当期版本,HEAD^为上一个版本,HEAD^^为上上个版本,以此类推。
/*
git reset --hard HEAD^ #
#等价于
git reset --mixed HEAD^   #命令用于将HEAD指向的提交回退到上一个提交,同时撤销暂存区的更改。
git checkout -- readme.txt
*/

$ git reset --hard  commitId  #退回到指定版本,commitid 可从log查看,输入前几位即可

git log
git log命令用于查看当前仓库的提交历史记录。

执行git log命令后,会显示最新的提交记录及相关信息,包括提交哈希值、作者、日期、提交信息等。按照提交时间从最新的记录开始依次显示。

以下是git log命令的常见选项和用法:

  • git log: 显示提交历史记录。
  • git log --oneline: 以简洁的形式显示提交历史记录(只显示提交哈希值和提交信息)。//git log --pretty=oneline
  • git log --author="John": 只显示指定作者(John)的提交历史记录。
  • git log --since="2021-01-01": 只显示指定时间之后的提交历史记录。
  • git log --until="2021-12-31": 只显示指定时间之前的提交历史记录。
  • git log --grep="bug fix": 只显示包含指定关键词(“bug fix”)的提交历史记录。
    -git reflog":命令用于查看本地仓库的引用日志(reflog),包括分支、HEAD和标签的移动记录。

这些只是git log命令的一些常见选项和用法,还有更多选项和参数可供使用。你可以使用git log --help查看完整的帮助文档以了解更多详情。

git status
git status命令用于查看当前工作区的状态,显示未暂存的修改、已暂存的修改以及未跟踪的文件。

执行git status命令后,会显示以下信息:

  1. 当前分支的名称。
  2. 显示已经修改但尚未暂存的文件,以及未跟踪的文件。
  3. 显示已经暂存但尚未提交的文件。
  4. 如果有分支或提交操作被推送到远程仓库,也会显示相关的提示信息。

以下是一些常见的git status命令输出信息的种类:

  1. “Changes not staged for commit”: 显示在工作区中已经被修改但还未暂存的文件。
  2. “Changes to be committed”: 显示已经添加到暂存区但尚未提交的文件。
  3. “Untracked files”: 显示未被Git跟踪的文件,即没有被添加到暂存区的文件。

通过运行git status命令,你可以查看当前仓库的状态,并根据这些信息进行相应的操作,比如添加、提交或忽略文件等。

git reset --mixed HEAD^
git reset --mixed HEAD^命令用于将HEAD指向的提交回退到上一个提交,同时撤销暂存区的更改。

具体来说,这个命令会执行以下操作:

  1. 将HEAD指向上一个提交(即回退一次提交)。
  2. 撤销暂存区的更改,即将暂存区的文件恢复为与上一个提交相同的状态,但保留工作区中的修改。
  3. 你的修改将保留在工作区中,你可以重新处理这些修改并重新提交。

这个命令通常用于取消上一次提交,撤销最近的提交操作,但保留更改在工作区中,以便你可以对其进行修改或重新提交。

需要注意的是,git reset --mixed既回退了HEAD指针的位置,也改变了暂存区的状态。如果你需要完全丢弃回退的更改,可以使用git reset --hard HEAD^命令。这会将暂存区和工作区都恢复到上一个提交的状态,丢弃回退的更改。

请谨慎使用git reset命令,因为它会修改历史记录。在执行该命令之前,建议先备份重要的更改或在必要时与团队成员进行沟通。

git checkout – readme.txt
git checkout -- readme.txt命令用于撤销对指定文件(此处为readme.txt)的修改,恢复到最新一次提交的状态。

具体来说,这个命令会将工作区中的readme.txt文件恢复到最新的提交版本,丢弃在工作区中对该文件的修改。这意味着任何在工作区未提交的更改都将丢失。

使用git checkout -- <file>命令时,--表示之后的参数都被视为文件名,而不是选项。这就确保了即使文件名以-开头,也不会与选项混淆。

请注意,执行git checkout -- <file>命令会丢失对文件的未提交的修改。在运行该命令之前,请确保你真的要撤销文件的修改并回到最新提交的状态。

如果你想恢复整个工作区到最新提交的状态(包括所有修改的文件),你可以使用git checkout .命令。这将撤销所有文件的修改,并将工作区恢复到最新提交的状态。请谨慎使用该命令,因为它会覆盖所有未提交的修改。

git reset --hard HEAD^
git reset --hard HEAD^命令会将HEAD指向的当前提交回退到上一个提交,并覆盖暂存区和工作区的状态。

具体来说,这个命令会执行以下操作:

  1. 将HEAD指向上一个提交(即回退一次提交)。
  2. 丢弃暂存区的所有更改,恢复为上一个提交的状态。
  3. 丢弃工作区的所有更改,恢复为上一个提交的状态。

通过执行git reset --hard HEAD^命令,你将完全丢弃回退提交之后的所有更改,回到上一个提交的状态。

需要注意的是,git reset --hard会修改仓库的历史记录,并且无法恢复已丢弃的更改。在执行该命令之前,请确保你真的要丢弃所有更改,并且已经备份了重要的修改或与团队成员进行了沟通。

尽量避免在公共仓库或已经推送到远程仓库的分支上使用git reset --hard命令,以免对其他人造成不必要的困扰或数据丢失。

3 远程仓库

3.1 使用远程仓库

  完全可以自己搭建一台运行Git的服务器,不过也可以使用现有的Git远程库,应用比较广泛的如
下:
  github
  gitee

  1. 使用SSH协议:
      ssh基于非对称加密的免密登录原理
    在这里插入图片描述 称加密:密码本是一个
    MD5
    非对称加密,秘钥是一对(公钥/私钥)
    rsa
    第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有
    id_rsa 和 id_rsa.pub 这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell
    (Windows下打开Git Bash),创建SSH Key:
$ ssh-keygen -t rsa -C "3185342850@qq.com"
#用户宿主目录下.ssh c:\Users\Administrator\.ssh\

  你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。
  如果一切顺利的话,可以在用户主目录里找到 .ssh 目录,里面有 id_rsa 和 id_rsa.pub 两个文件,这个就是SSH Key的秘钥对, id_rsa 是私钥,不能泄露出去, id_rsa.pub 是公钥,可以放心地告诉任何人。
  第2步:登陆GitHub,打开“Account settings”,“SSH Keys”页面:
  然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴 id_rsa.pub 文件的内容:
在这里插入图片描述
点“Add Key”,你就应该看到已经添加的Key:
在这里插入图片描述
  为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
  当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
2. HTTPS协议
  当然不配置公钥也可以使用HTTPS协议,在使用HTTPS协议时输入用户名/密码
3. 克隆
  (1)使用https协议

git clone https://gitee.com/halo-dev/halo.git
#第一次访问需要输入gitee的用户名密码
#halo:开源博客系统,基于spring boot和vue

  (2)使用ssh协议

git clone git@gitee.com:halo-dev/halo.git

3.2 添加远程库

首先,登录Gitee,然后在右上角找到“新建仓库”,创建一个新的仓库:
在这里插入图片描述
!注意 不要选择初始化仓库和模板,要创建一个空的仓库
  在Repository name填入 learngit ,其他保持默认设置,点击“创建仓库”按钮,就成功地创建了一个新的Git仓库:
下面是gitee的提示步骤
Git全局设置(上面步骤中已经设置)

git config --global user.name "高昆"
git config --global user.email "3185342850@qq.com"

创建git仓库(上面步骤已经创建)

mkdir learngit
cd learngit
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin git@gitee.com:gao-kun0808/learngit.git
git push -u origin "master"

已有仓库?

cd learngit
git remote add origin git@gitee.com:gao-kun0808/learngit.git
git push -u origin "master"

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

git remote add origin git@gitee.com:gao-kun0808/learngit.git

请千万注意,把上面的 michaelliao 替换成你自己的GitHub账户名,否则,你在本地关联的就是我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的SSH Key公钥不在我的账户列表中。
添加后,远程库的名字就是 origin ,这是Git默认的叫法,也可以改成别的,但是 origin 这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:

$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (20/20), 1.64 KiB | 560.00 KiB/s, done.
Total 20 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:michaelliao/learngit.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

把本地库的内容推送到远程,用 git push命令,实际上是把当前分支 master 推送到远程。
由于远程库是空的,我们第一次推送 master 分支时,加上了 -u 参数,Git不但会把本地的 master分支内容推送的远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来,在以后的推送或者拉取时就可以简化命令。
提交git push -u origin master->省略 git push
拉取代码 git pull
推送成功后,可以立刻在gitee页面中看到远程库的内容已经和本地一模一样:
在这里插入图片描述
从现在起,只要本地作了提交,就可以通过命令:

$ git push origin master

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

SSH警告
当你第一次使用Git的 clone 或者 push 命令连接GitHub时,会得到一个警告:

The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?

这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key
的指纹信息是否真的来自GitHub的服务器,输入 yes 回车即可。
Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:

Warning: Permanently added 'github.com' (RSA) to the list of known hosts.

这个警告只会出现一次,后面的操作就不会有任何警告了。
如果你实在担心有人冒充GitHub服务器,输入 yes 前可以对照GitHub的RSA Key的指纹信息是否与SSH连接给出的一致。

3.3 从远程库克隆

打开另一个窗口模拟其他人下载远程库

cd f:\tmp
git clone git@gitee.com:gao-kun0808/learngit.git #克隆远程库到本地库
$ git log --pretty=oneline
64eb3d86d3b46095c006edd91dcf5f2f97ba4b7b (HEAD -> master, origin/master,
origin/HEAD) remove test.txt
18a4e330ee23f4df88e1cb2de7c3bf42fcd60d57 add test.txt
fc99176b6e2d3b2159b6503429611127b090ade0 understand how stage works
64800dbc350bdb87529df4a1efb5203028086926 append GPL
6793214fbc09a35878b9366a4bc3a62b4ab504dd add distributed
bd875ec1f86c22f203741414a238526bfca26279 add 3 files.
6e4de12122b22ee3145dfa87af974d284f832dd2 wrote a readme file

3.4 分支管理

分支就是git世界的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。
如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN!
在这里插入图片描述
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码
全部写完再一次提交,又存在丢失每天进度的巨大风险。现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
git相对于其他版本控制工具,分支功能非常快

3.4.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 分支:
在这里插入图片描述
下面开始实战。首先,我们创建 dev 分支,然后切换到 dev 分支:

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

git log --oneline
git log --pretty=oneline --abbrev-commit #和上面的命令等价

–pretty=oneline:显示单行
–abbrev-commit:缩写commit id

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

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

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

$ git branch
* dev
  master

git branch 命令会列出所有分支,当前分支前面会标一个 * 号。然后,我们就可以在 dev 分支上正常提交,比如对 readme.txt 做个修改,加上一行:

Creating a new branch is quick.

然后提交

$ git add readme.txt
$ git commit -m "branch test"
[dev b17d20e] branch test
1 file changed, 1 insertion(+)

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

$ git checkout master
Switched to branch 'master'

切换回 master 分支后,再查看一个 readme.txt 文件,刚才添加的内容不见了!因为那个提交是在 dev 分支上,而 master 分支此刻的提交点并没有变:
在这里插入图片描述
现在,我们把 dev 分支的工作成果合并到 master 分支上:

$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)

git merge 命令用于合并指定分支到当前分支。合并后,再查看 readme.txt 的内容,就可以看到,和 dev 分支的最新提交是完全一样的。
注意到上面的 Fast-forward 信息,Git告诉我们,这次合并是“快进模式”,也就是直接把 master 指向 dev 的当前提交,所以合并速度非常快。
当然,也不是每次合并都能 Fast-forward ,我们后面会讲其他方式的合并。
合并完成后,就可以放心地删除 dev 分支了:

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

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

$ git branch
* master

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

总结

  1. git checkout
    git checkout -- file:把版本课的文件重置到工作区(撤销工作区的修改)
    git checkout master:切换到master分支
    git checkout -b dev:创建dev分支,并且切换到dev分支
  2. git branch
    git branch:列表分支分支,有*当前的分支
    git branch dev:创建dev分支,如果切换需要git checkout dev
    git branch -d dev:删除分支
  3. git merge: 合并分支
#master合并dev分支
git checkout master
git merge dev #把dev合并到master

3.4.2 版本冲突

准备新的 feature1 分支,继续我们的新分支开发
创建新分支

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

等价于下面两条命令
git branch feature1 #创建分支
git checkout feature1 #切换分支

git branch #查看分支

修改 readme.txt 最后一行,改为:

Creating a new branch is quick AND simple.

在 feature1 分支上提交:

$ git add readme.txt
$ git commit -m "AND simple"
[feature1 14096d0] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)

切换到 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 文件的最后一行改为:

Creating a new branch is quick & simple.

提交:

$ git add readme.txt
$ git commit -m "& simple"
[master 5dc6824] & simple
1 file changed, 1 insertion(+), 1 deletion(-)

现在, master 分支和 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的内容:

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

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

Creating a new branch is quick and simple.

再提交:

$ git add readme.txt
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed

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

在这里插入图片描述
用带参数的 git log 也可以看到分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit
* cf810e4 (HEAD -> master) conflict fixed
|\
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file

最后,删除 feature1 分支:

$ git branch -d feature1
Deleted branch feature1 (was 14096d0).

3.4.3 分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:
master 分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在 dev 分支上,也就是说, dev 分支是不稳定的,到某个时候,比如1.0版本发布时,再把 dev 分支合并到 master 上,在 master 分支发布1.0版本;
你和你的小伙伴们每个人都在 dev 分支上干活,每个人都有自己的分支,时不时地往 dev 分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
在这里插入图片描述
在这里插入图片描述

#创建本地dev分支(在原来没有dev分支的情况下创建)
git checkout -b dev

#提交dev给服务器
git push origin dev

#创建bob的本地分支
git checkout -b bob

#创建env.txt
touch env.txt

#放到暂存区 然后 提交
git add .
git commit -m "add env bob"

#回到dev,合并bob分支(把bob合并到dev,dev现在有env.txt)
git checkout dev
git merge bob

#上传dev到远程
git push origin dev

在这里插入图片描述

cd f:\mickey

#这条命令会克隆一个名为learngit.git的Git仓库,它位于Gitee(一个Git代码托管平台)上的gao-kun0808用户的账户下
git clone git@gitee.com:gao-kun0808/learngit.git
#注意,只下载master分支,其他分支不会被clone

git checkout -b dev
git branch --set-upstream-to=origin/dev dev
#等价于
git checkout -b dev origin/dev
#运行该命令后,Git会按照origin/dev分支的内容在本地创建一个新的dev分支,并且dev分支会自动与origin/dev关联。这意味着当你在dev分支上运行git pull命令时,Git会从origin/dev拉取更新并将其合并到本地的dev分支上。(此时里面有env.txt文件,但文件中没有内容)
此外,通过使用-b选项,你可以告诉Git在创建dev分支的同时切换到该分支,这样你便可以立即在新的dev分支上开始工作。
#请注意确保你对origin/dev分支具有相应的读取权限,以便成功拉取更新。
#使用这条命令后,您就可以在本地的dev分支上进行开发了。之后,您可以使用git add、git commit等命令来管理您的修改,并可以使用git push命令将这些修改推送到远程的origin/dev分支。

#拉取dev分支,运行 git pull origin dev 命令会将远程仓库 origin 的 dev 分支的最新更改拉取到本地。
git pull origin dev

#创建mickey分支
git checkout -b mickey

#修改env.txt增加内容"mickey env"
vim env.txt

#加到暂存区然后 提交
git add .
git commit -m "update env by mickey"

#将mickey分支合并到dev分支(将mickey的修改同步到dev)
git checkout dev
git merge mickey

#上传到远程库
git push origin dev

最终目的:合并到master分支

#拉取最新代码
git pull origin master
git pull origin dev

#切换到master,再将dev合并到master
git checkout master
git merge dev

#上传
git push origin master

(所有开发人员修改版本都先合并在dev,再由dev合并在master,最后上传)

–no-ff

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest
$ git init
Initialized empty Git repository in E:/atest/.git/

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ touch a.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git add .

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git commit -m "add a.txt"
[master (root-commit) 4c02b25] add a.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git checkout -b dev
Switched to a new branch 'dev'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (dev)
$ echo "add dev change" >> a.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (dev)
$ cat a.txt
add dev change

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (dev)
$ git add .
warning: in the working copy of 'a.txt', LF will be replaced by CRLF the next time Git touches it

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (dev)
$ git commit -m "dev update 1"
[dev c32e7f5] dev update 1
 1 file changed, 1 insertion(+)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (dev)
$ git checkout master
Switched to branch 'master'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git merge dev
Updating 4c02b25..c32e7f5
Fast-forward
 a.txt | 1 +
 1 file changed, 1 insertion(+)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ cat a.txt
add dev change

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ echo "master update2" >> a.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ cat a.txt
add dev change
master update2

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git add .
warning: in the working copy of 'a.txt', LF will be replaced by CRLF the next time Git touches it

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git commit -m "master update 2"
[master 5517164] master update 2
 1 file changed, 1 insertion(+)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git log --oneline --graph
* 5517164 (HEAD -> master) master update 2
* c32e7f5 (dev) dev update 1
* 4c02b25 add a.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git branch -d dev
Deleted branch dev (was c32e7f5).

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/atest (master)
$ git log --oneline --graph
* 5517164 (HEAD -> master) master update 2
* c32e7f5 dev update 1
* 4c02b25 add a.txt

在这里插入图片描述

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest
$ git init
Initialized empty Git repository in E:/btest/.git/

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ touch b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git add .

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git commit -m "add b.txt"
[master (root-commit) 8ec7394] add b.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git checkout -b dev
Switched to a new branch 'dev'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ echo "add dev centent" >> b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ cat b.txt
add dev centent

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git add .
warning: in the working copy of 'b.txt', LF will be replaced by CRLF the next time Git touches it

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git commit -m "dev update 1"
[dev ec40aa8] dev update 1
 1 file changed, 1 insertion(+)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git checkout master
Switched to branch 'master'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git merge --no-ff dev

会弹出界面,让写 合并标识
在这里插入图片描述

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git merge --no-ff dev
Merge made by the 'ort' strategy.
 b.txt | 1 +
 1 file changed, 1 insertion(+)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ echo "master update 1" >> b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ cat b.txt
add dev centent
master update 1

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git add .
warning: in the working copy of 'b.txt', LF will be replaced by CRLF the next time Git touches it

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git commit -m "master update 2"
[master eafea98] master update 2
 1 file changed, 1 insertion(+)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git log --oneline --graph
* eafea98 (HEAD -> master) master update 2
*   ad6a4bb Merge branch 'dev'
|\
| * ec40aa8 (dev) dev update 1
|/
* 8ec7394 add b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git branch -d dev
Deleted branch dev (was ec40aa8).

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git log --oneline --graph
* eafea98 (HEAD -> master) master update 2
*   ad6a4bb Merge branch 'dev'
|\
| * ec40aa8 dev update 1
|/
* 8ec7394 add b.txt

在这里插入图片描述

git merge --no-ff dev

执行"git merge --no-ff dev"命令用于将"dev"分支合并到当前分支,并创建一个新的合并提交。该命令中的"–no-ff"选项表示进行非快速合并(non-fast-forward merge),即创建一个新的合并提交,而不是仅仅将当前分支指针直接移动到"dev"分支的最新提交。

执行该命令的步骤如下:

首先,确保你在要进行合并的目标分支上(可以通过"git branch"命令查看当前所在分支)。
执行命令"git merge --no-ff dev",将"dev"分支合并到当前分支。
如果存在冲突,Git会中止合并过程并将冲突的文件标记为未解决状态。
解决冲突并手动编辑有冲突的文件,然后使用"git add"命令将修改后的文件标记为已解决状态。
继续合并操作,使用"git merge --continue"命令完成合并。
Git会自动创建一个新的合并提交,包含两个分支之间的差异以及解决的冲突。
请注意,在执行合并操作前,建议先将当前分支的工作区和暂存区保存好(可以通过"git stash"命令来保存和恢复更改)。这样可以确保在合并过程中不会丢失任何修改。

3.4.4 bug修改

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git checkout -b dev
Switched to a new branch 'dev'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ echo 'working on statge' >> readme.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git add .
warning: in the working copy of 'readme.txt', LF will be replaced by CRLF the next time Git touches it

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ echo 'working on work space' >> readme.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git stash      #运行该命令后,Git 将保存你的当前修改并将工作目录恢复到上一个 commit 的状态
warning: in the working copy of 'readme.txt', LF will be replaced by CRLF the next time Git touches it
Saved working directory and index state WIP on dev: eafea98 master update 2

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git stash list    #命令用于列出当前 Git 仓库中保存的 stash 列表
stash@{0}: WIP on dev: eafea98 master update 2

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git checkout master
Switched to branch 'master'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git checkout -b issue101
Switched to a new branch 'issue101'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (issue101)
$ ll
total 1
-rw-r--r-- 1 Administrator 197121 33 Jul 13 09:48 b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (issue101)
$ git checkout master
Switched to branch 'master'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ ll
total 1
-rw-r--r-- 1 Administrator 197121 33 Jul 13 09:48 b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git stash pop
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   readme.txt

Dropped refs/stash@{0} (4a26855e05375cdaac784a458b2ac01cf955ca9a)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ ll
total 2
-rw-r--r-- 1 Administrator 197121 33 Jul 13 09:48 b.txt
-rw-r--r-- 1 Administrator 197121 42 Jul 13 11:38 readme.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git stash
Saved working directory and index state WIP on master: eafea98 master update 2

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ ll
total 1
-rw-r--r-- 1 Administrator 197121 33 Jul 13 09:48 b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git checkout issue101
Switched to branch 'issue101'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (issue101)
$ ll
total 1
-rw-r--r-- 1 Administrator 197121 33 Jul 13 09:48 b.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (issue101)
$ echo 'fixed issue 101' >> readme.txt     #相当于修改bug

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (issue101)
$ git add .
warning: in the working copy of 'readme.txt', LF will be replaced by CRLF the next time Git touches it

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (issue101)
$ git commit -m "fixed bug 101"
[issue101 ecbad30] fixed bug 101
 1 file changed, 1 insertion(+)
 create mode 100644 readme.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (issue101)
$ git checkout master
Switched to branch 'master'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git merge issue101
Updating eafea98..ecbad30
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 readme.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git checkout dev
Switched to branch 'dev'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git stash pop
On branch dev
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   readme.txt

Dropped refs/stash@{0} (0987cac6f6439db0f5df815df0e6842b9b8bafb5)

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ ll
total 2
-rw-r--r-- 1 Administrator 197121 33 Jul 13 09:48 b.txt
-rw-r--r-- 1 Administrator 197121 42 Jul 13 11:44 readme.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ cat readme.txt
working on statge
working on work space

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
        readme.txt
Please commit your changes or stash them before you switch branches.
Aborting

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git commit -m "dev xiugai"
[dev 5da3289] dev xiugai
 1 file changed, 2 insertions(+)
 create mode 100644 readme.txt

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (dev)
$ git checkout master
Switched to branch 'master'

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master)
$ git merge dev
Auto-merging readme.txt
CONFLICT (add/add): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

Administrator@MS-YYRYKIDSRVFA MINGW64 /e/btest (master|MERGING)
$ cat readme.txt
<<<<<<< HEAD
fixed issue 101
=======
working on statge
working on work space
>>>>>>> dev

使用 git stash 的基本用法如下:

在你的工作目录中执行 git stash 命令:
git stash (隐藏起来)
如果你有尚未提交的修改,Git 会将这些修改保存到一个新的 stash 中,并将你的工作目录和暂存区回到干净的状态(上一个commit)。

继续你的其他工作,如切换分支或进行其他操作。

当你希望回到原来的分支并恢复暂存的修改时,使用以下命令:

git stash pop
这将从 stash 中恢复你之前保存的修改并将它们应用到你的工作目录中。恢复的修改将被应用到当前分支上。

除了 git stash pop,你还可以使用其他一些相关的命令来管理 stash:

git stash list: 列出当前所有的 stash。
git stash apply: 应用最新的 stash,但不从列表中删除它。
git stash drop: 丢弃最新的 stash。
git stash clear: 删除所有的 stash。
通过 git stash,你可以在需要的时候保存和恢复工作目录的修改,而不需要提交它们。这对于暂时切换分支、修复 bug 或合并代码等场景非常有用。

3.4 标签管理

  1. 创建标签
    在Git中打标签非常简单,首先,切换到需要打标签的分支上
$ git branch
* dev
  master
$ git checkout master
Switched to branch 'master'

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

$ git tag v1.0

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

$ git tag
v1.0

默认标签是打在最新提交的commit上的。如果要给之前的提交打标签,方法是找到历史提交的commit id,然后打上:

$ git log --pretty=oneline --abbrev-commit
12a631b (HEAD -> master, tag: v1.0, origin/master) merged bug fix 101
4c805e2 fix bug 101
e1e9c68 merge with no-ff
f52c633 add merge
cf810e4 conflict fixed
5dc6824 & simple
14096d0 AND simple
b17d20e branch test
d46f35e remove test.txt
b84166e add test.txt
519219b git tracks changes
e43a48b understand how stage works
1094adb append GPL
e475afc add distributed
eaadf4e wrote a readme file

比方说要对add merge这次提交打标签,它对应的commit id是f52c633,敲入命令:

$ git tag v0.9 f52c633

再用命令git tag查看标签:

$ git tag
v0.9
v1.0

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

$ git show v0.9
commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9)
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:56:54 2018 +0800

    add merge

diff --git a/readme.txt b/readme.txt
...

可以看到,v0.9确实打在add merge这次提交上。

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

$ git tag -a v0.1 -m "version 0.1 released" b0fbe25

用命令git show <tagname>可以看到说明文字:

$ git show v0.1
tag v0.1
Tagger: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 22:48:43 2018 +0800

version 0.1 released

commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (tag: v0.1)
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:06:15 2018 +0800

    append GPL

diff --git a/readme.txt b/readme.txt
...

注意:标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。

总结

  • 命令git tag <tagname>用于新建一个标签,默认为HEAD,也可以指定一个commit id;
  • 命令git tag -a <tagname> -m "blablabla..."可以指定标签信息;
  • 命令git tag可以查看所有标签。
  1. 操作标签
    ​ 如果标签打错了,也可以删除
$ git tag -d v0.1
Deleted tag 'v0.1' (was f15b0dd)

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

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

$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
 * [new tag]         v1.0 -> v1.0

​ 或者,一次性推送全部尚未推送到远程的本地标签:

$ git push origin --tags
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
 * [new tag]         v0.9 -> v0.9

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

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

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

$ git push origin :refs/tags/v0.9
To github.com:michaelliao/learngit.git
 - [deleted]         v0.9

​ 要看看是否真的从远程库删除了标签,可以登陆Gitee查看。

小结

  • 命令git push origin <tagname>可以推送一个本地标签;
  • 命令git push origin --tags可以推送全部未推送过的本地标签;
  • 命令git tag -d <tagname>可以删除一个本地标签;
  • 命令git push origin :refs/tags/<tagname>可以删除一个远程标签。

4. 自定义Git

  1. git config自定义配置

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

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

$ git config --global color.ui true

这样,Git会适当地显示不同的颜色,比如git status命令:

文件名就会标上颜色。

  1. 忽略特殊文件

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

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

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

忽略文件的原则是:

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

举个例子:

​ 假设你在Windows下进行Python开发,Windows会自动在有图片的目录下生成隐藏的缩略图文件,如果有自定义目录,目录下就会有Desktop.ini文件,因此你需要忽略Windows自动生成的垃圾文件:

# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini

然后,继续忽略Python编译产生的.pyc.pyodist等文件或目录:

# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build

加上你自己定义的文件,最终得到一个完整的.gitignore文件,内容如下:

# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini

# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build

# My configurations:
db.ini
deploy_key_rsa

​ 最后一步就是把.gitignore也提交到Git,就完成了!当然检验.gitignore的标准是git status命令是不是说working directory clean

​ 使用Windows的童鞋注意了,如果你在资源管理器里新建一个.gitignore文件,它会非常弱智地提示你必须输入文件名,但是在文本编辑器里“保存”或者“另存为”就可以把文件保存为.gitignore了。

​ 有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了:

$ git add App.class
The following paths are ignored by one of your .gitignore files:
App.class
Use -f if you really want to add them.

​ 如果你确实想添加该文件,可以用-f强制添加到Git:

$ git add -f App.class

​ 或者你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore命令检查:

$ git check-ignore -v App.class
.gitignore:3:*.class	App.class

​ Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。

小结

  • 忽略某些文件时,需要编写.gitignore
  • .gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理!
  1. 配置别名

​ 有没有经常敲错命令?比如git statusstatus这个单词真心不好记。

如果敲git st就表示git status那就简单多了,当然这种偷懒的办法我们是极力赞成的。

我们只需要敲一行命令,告诉Git,以后st就表示status

$ git config --global alias.st status

好了,现在敲git st看看效果。

当然还有别的命令可以简写,很多人都用co表示checkoutci表示commitbr表示branch

$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch

以后提交就可以简写成:

$ git ci -m "bala bala bala..."

--global参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用。

撤销修改一节中,我们知道,命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区。既然是一个unstage操作,就可以配置一个unstage别名:

$ git config --global alias.unstage 'reset HEAD'

当你敲入命令:

$ git unstage test.py

实际上Git执行的是:

$ git reset HEAD test.py

配置一个git last,让其显示最后一次提交信息:

$ git config --global alias.last 'log -1'

这样,用git last就能显示最近一次的提交:

$ git last
commit adca45d317e6d8a4b23f9811c3d7b7f0f180bfe2
Merge: bd6ae48 291bea8
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Thu Aug 22 22:49:22 2013 +0800

    merge & fix hello.py

甚至还有人丧心病狂地把lg配置成了:

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 lg的效果:

为什么不早点告诉我?别激动,咱不是为了多记几个英文单词嘛!

配置文件

配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。

配置文件放哪了?每个仓库的Git配置文件都放在.git/config文件中:

$ cat .git/config 
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[remote "origin"]
    url = git@github.com:michaelliao/learngit.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master
[alias]
    last = log -1

别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。

而当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:

$ cat .gitconfig
[alias]
    co = checkout
    ci = commit
    br = branch
    st = status
[user]
    name = Your Name
    email = your@email.com

配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。

5. 在idea中使用git

5.1 自己上传

1.在gitee新建仓库
在这里插入图片描述

  1. 在idea新建项目

在这里插入图片描述

  1. 初始化仓库 (如果在第二步勾选了Create Git repository就不用再执行初始化命令了)
    在这里插入图片描述
    在这里插入图片描述

  2. 修改gitignore
    在这里插入图片描述

  3. 连到远程库
    在这里插入图片描述
    在这里插入图片描述

  4. 新建文件会提示是否加到暂存区在这里插入图片描述

  5. 添加之后
    在这里插入图片描述

  6. 提交Hello.java
    在这里插入图片描述

  7. Push到远程
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

5.2 其他用户克隆

  1. 新建文件夹BoB,并克隆

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

  1. 在Idea打开克隆下来的项目
    在这里插入图片描述
    在这里插入图片描述
    我这里打开BoB是有一些报错的,需要设置一下
    在这里插入图片描述

在这里插入图片描述
问题解决了
在这里插入图片描述
3. 在BoB下新建dev分支 在这里插入图片描述
在这里插入图片描述
然后在dev分支创建一个Bob.java,添加到暂存区并提交
在这里插入图片描述
Push到远程库
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
4. 在原来用户本地新建dev分支(也可以右击master点checkout),然后从远程库pull下来BoB上传的dev分支
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后gitlearn 就可以在本地得到远程库里BoB上传的dev分支里的Bob.java文件了
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值