【Git】

学习来自于:

文章目录

  • 【前言】
    • 集中式与分布式的区别
    • [git 介绍](#git 介绍)
    • [git 基本组成框架:Workspace、Index / Stage、Repository、Remote](#git 基本组成框架:Workspace、Index / Stage、Repository、Remote)
  • [【常用 git 命令】](#【常用 git 命令】)
    • [新建文件添加到本地仓库:git add、git commit -m](#新建文件添加到本地仓库:git add、git commit -m)
    • [改写提交:git commit --amend](#改写提交:git commit --amend)
    • [查看历史提交日志:git log](#查看历史提交日志:git log)
    • [回滚代码仓库:git reset --hard](#回滚代码仓库:git reset --hard)
    • [git 查看仓库信息:git remote](#git 查看仓库信息:git remote)
    • [查看提交之后文件是否做了改动:git status](#查看提交之后文件是否做了改动:git status)
    • [修改缓存区内容:git add、git commit -m](#修改缓存区内容:git add、git commit -m)
    • [将改动文件添加到缓存区:git add](#将改动文件添加到缓存区:git add)
    • [将所有改动文件添加到缓存区:git add --all、git add .](#将所有改动文件添加到缓存区:git add --all、git add .)
    • [将文件撤销回到最近一次修改的状态:git checkout -- file](#将文件撤销回到最近一次修改的状态:git checkout -- file)
    • [查看单个文件可回滚版本:git log filename](#查看单个文件可回滚版本:git log filename)
    • [查看提交历史:git reflog](#查看提交历史:git reflog)
    • [删除文件:git rm](#删除文件:git rm)
    • [git rm 后恢复文件:git rm、git reset、git checkout](#git rm 后恢复文件:git rm、git reset、git checkout)
    • [git 创建分支:git branch、git checkout](#git 创建分支:git branch、git checkout)
    • [git 查看分支:git branch -a](#git 查看分支:git branch -a)
    • [git 修改分支名称:git branch -m](#git 修改分支名称:git branch -m)
    • [git 切换分支:git checkout](#git 切换分支:git checkout)
    • [git 拉取远程所有分支:git fetch](#git 拉取远程所有分支:git fetch)
    • [git查看不同分支的文件差异:git diff](#git查看不同分支的文件差异:git diff)
    • [git 保存当前工作切换分支:git stash](#git 保存当前工作切换分支:git stash)
    • [git 合并分支:git merge](#git 合并分支:git merge)
    • [git 删除本地分支:git branch --delete](#git 删除本地分支:git branch --delete)
    • [git 删除远程分支:git push origin --delete](#git 删除远程分支:git push origin --delete)
  • 【常用分支介绍】
    • [1 主分支 Master](#1 主分支 Master)
    • [2 开发分支 Develop](#2 开发分支 Develop)
    • [3 临时性分支](#3 临时性分支)
      • [3.1 功能(feature)分支](#3.1 功能(feature)分支)
      • [3.2 预发布(release)分支](#3.2 预发布(release)分支)
      • [3.3 修补 bug(fixbug)分支](#3.3 修补 bug(fixbug)分支)
  • [【Git 版本管理 | 莫烦PYTHON】学习笔记](#【Git 版本管理 | 莫烦PYTHON】学习笔记)
    • [1 Why Git?](#1 Why Git?)
    • [2 第一个版本库 Repository](#2 第一个版本库 Repository)
      • [2.1 创立版本库(init)](#2.1 创立版本库(init))
      • [2.2 添加文件管理 (add)](#2.2 添加文件管理 (add))
      • [2.3 提交改变 (commit)](#2.3 提交改变 (commit))
      • [2.4 流程图](#2.4 流程图)
    • [3 记录修改(log & diff)](#3 记录修改(log & diff))
      • [3.1 修改记录](#3.1 修改记录)
      • [3.2 查看 unstaged(no add)与已 commit 的改变](#3.2 查看 unstaged(no add)与已 commit 的改变)
      • [3.3 查看 staged(已 add)与已commit 的改变](#3.3 查看 staged(已 add)与已commit 的改变)
      • [3.4 查看 staged & unstaged (HEAD)的改变](#3.4 查看 staged & unstaged (HEAD)的改变)
    • [4 回到从前(rest)](#4 回到从前(rest))
      • [4.1 修改已 commit 的版本](#4.1 修改已 commit 的版本)
      • [4.2 reset 回到 add 前(绿变红)](#4.2 reset 回到 add 前(绿变红))
      • [4.3 reset 回到 commit 之前(staged to unstaged)](#4.3 reset 回到 commit 之前(staged to unstaged))
    • [git log](#git log)
  • 【附录】
    • [git clone总是失败解决办法](#git clone总是失败解决办法)

【前言】

Git 是目前为止最好用的分布式版本控制系统

大名鼎鼎的 github 用的就是 git 系统来管理它们的网站,这里需要区分一下,github 和 git 是两个东西,github 是一个社区,git 是一个服务系统,github 只支持 git 分布式系统,所以故名成为 github。

集中式与分布式的区别

除了 git 还有 svn、cvs 这样的版本控制系统,它们的区别在于一个是分布式一个是集中式

集中式就是 svn 和 csv 这样的版本控制系统,分布式是 git

区别在于集中式的版本控制系统每次在写代码时都需要从服务器中拉取一份下来,并且如果服务器丢失了,那么所有的就都丢失了,你本机客户端仅保存当前的版本信息,换句话说,集中式就是把代码放在一个服务器上集中管理,你的所有回滚等操作都需要服务器的支持。

分布式的区别在于,每个人的电脑都是服务器,当你从主仓库拉取一份代码下来后,你的电脑就是服务器,无需担心主仓库被删或者找不到的情况,你可以自由在本地回滚,提交,当你想把自己的代码提交到主仓库时,只需要合并推送到主仓库就可以了,同时你可以把自己的代码新建一份仓库分享给其它人。

像集中式它们都有一个主版本号,所有的版本迭代都以这个版本号为主,而分布式因为每个客户端都是服务器,git 没有固定的版本号,但是有一个由哈希算法算出的 id,用来回滚用的,同时也有一个 master 仓库,这个仓库是一切分支仓库的主仓库,我们可以推送提交到 master 并合并到主仓库上,主仓库的版本号会迭代一次,我们客户端上的 git 版本号无论迭代多少次,都跟 master无关,只有合并时,master 才会迭代一次。

git 介绍

  • 工作区(Working Directory) 就是在电脑里能看到的目录,比如 xxx-master 文件夹就是一个工作区。

  • 版本库(Repository)工作区有一个隐藏目录 .git,是 Git 的版本库。

在版本库中标记为 index 的区域为暂存区,标记为 master 的是 Git 为我们自动创建的第一个分支,代表的是目录树。

此时HEAD实际是指向master分支的一个"游标",所以图示的命令中出现HEAD的地方可以用 master来替换

图中的 objects 标识的区域为 git 的对象库,实际位于.git/objects 目录下。

git 基本组成框架:Workspace、Index / Stage、Repository、Remote

  • Workspace:开发者工作区,也就是你当前写代码的目录,它一般保持的是最新仓库代码。

  • Index / Stage:缓存区 ,最早叫 Stage,现在新版本已经改成 index,位于 .git 目录中,它用来存放临时动作,比如我们做了 git add 或者 git rm,都是把文件提交到缓存区,这是可以撤销的,然后在通过 git commit 将缓存区的内容提交到本地仓库

  • Repository:仓库区,是仓库代码,你所有的提交都在这里,git 会保存好每一个历史版本,存放在仓库区,它可以是服务端的也可以是本地的,因为在分布式中,任何人都可以是主仓库。

  • Remote:远程仓库,只能是别的电脑上的仓库,即服务器仓库。

  • 远程仓库副本,可以理解为存在于本地的远程仓库缓存。如需更新,可通过 git fetch/pull 命令获取远程仓库内容。使用 fech获取时,并未合并到本地仓库,此时可使用 git merge 实现远程仓库副本与本地仓库的合并。git pull 根据配置的不同,可为 git fetch + git merge 或 git fetch + git rebase。

【常用 git 命令】

新建文件添加到本地仓库:git add、git commit -m

git add test.c :将文件添加到缓存区

使用 git add 命令将文件添加到本地仓库的提交缓存,这个时候还不算添加到了本地仓库,我们还需要使用 git commit 命令为其添加修改的描述信息

注意在使用 git commit 时我们只需要简单描述一下我们做了什么,不要像写注释那样写一大堆,不然将来在回滚代码或者查看历史版本时,很难审阅。

我们需要使用 -m 命令来简写描述我们的信息,如果不使用 -m,会调用终端的注释编辑器让你输入描述信息,但是不建议使用,因为注释编辑器比较难用,不舒服。

git commit -m "add new file \"test.c\"":提交到本地仓库

git commit 会为我们生成 40 位的哈希值,用于作为 id,并把刚刚用 git add 添加到提交缓存区里的文件提交到本地仓库中,便于我们回滚,至此,这个文件就已经添加到本地仓库中了,同时本地仓库也迭代了一个版本。


git add 的灵活使用

  • git add [file1] [file2] ... 添加一个或多个文件到暂存区:

  • git add [dir] 添加指定目录到暂存区,包括子目录:

  • git add . 添加当前目录下的所有文件到暂存区:

git add 和 git commit 两个步骤是否可以一步到位

  • git commit -a -m [message] 等价于

    py 复制代码
    git add .
    git commit -m [message]

    更进一步简写为 git commit -am [message]

改写提交:git commit --amend

git commit --amend:重写上一次的提交信息

我们提交了仓库,但是发现写错了,我们可以使用 --amend 长命令选项来改写提交

git commit --amend -m "提交描述" 修改 comment。

git commit --amend 会把暂存区的文件自动加入,可以使用 -a 把工作区的文件也一起加入。

如果有一个新的文件修改,也想提交到上一个commit中(不改动描述信息)

py 复制代码
git add .
git commit --amend --no-edit

注意:这里的之前指最近的 commit,而且没有 push 到远程。

查看历史提交日志:git log

第一行的 commit 是哈希算法算出的 id,正如一开始所说,分布式是没有一个主版本号的,它们都是用 id 来做标志的,同时用master 作为主仓库,其它的分支怎么迭代都不会影响到 master

head->master 代表这次提交到 master 主仓库,如果是 head->分支仓库则代表提交到分支仓库

如果觉得 log 打印内容过多,git log --pretty=oneline 可以 简洁输出

查看某个人或者某几个人的提交

py 复制代码
git log --author="John"
git log --author="John\|Mary"

回滚代码仓库:git reset --hard

py 复制代码
git reset --soft id # 就仅仅将头指针恢复,已经add的暂存区以及工作空间的所有东西都不变。
git reset --mixed id # 就将头恢复掉,已经add的暂存区也会丢失掉,工作空间的代码什么的是不变的。
git reset --hard id # 那么一切就全都恢复了,头变,add 的暂存区消失,代码什么的也恢复到以前状态。

回滚到指定历史版本

py 复制代码
git reset --hard id

回滚当前仓库指向的版本

py 复制代码
git reset --hard HEAD^

^代表上一个版本的意思,HEAD代表当前仓库的指向,当前HEAD指向master,就代表回滚到master上一次提交的版本

py 复制代码
git reset --hard HEAD~3

后面的 ~ 3,代表以当前版本为基数,回滚多少次。HEAD~3 代表回滚 master 前三个版本

git 查看仓库信息:git remote

使用 git remote 可以查看当前仓库名称

查看提交之后文件是否做了改动:git status

A:未修改

AM:修改

Untracked:未提交

modified:新文件,但未提交

修改缓存区内容:git add、git commit -m

我们使用 git add 添加到缓存区的内容后,我们在修改这个文件时,它跟缓冲区内容是没有任何关系的!我们使用 git commit 提交的时,它只会提交缓存区内容

如果想提交第二次修改,我们只需要 git add 一次,然后在使用 git commit 提交就可以了,git会 自动帮我们合并提交

将改动文件添加到缓存区:git add

平时我们可能写代码的时候不可能保证只改动了一个文件,我们切来切去最后都不知道自己改了哪些文件,为了保证所有的文件都能被准确提交,我们可以使用 git add 我们确定修改的文件,当 git add 后在使用 git status 查看一下状态,看看是否有遗漏没有提交的文件

将所有改动文件添加到缓存区:git add --all、git add .

如果你实在不确信哪些文件是改动过的,你只需要使用 git add --all

这个命令会将当前目录下包括子目录下所有改动的文件提交到暂存区,注意只包括改动的文件,不改动的不会放到缓存区。

这个命令还会把删除的文件也提交进去

如你在本地删除了min.c 这个命令会把删除信息也记录进去,然后在提交的时候把仓库里对应的 min.c 也删除掉,也就是说你在本地做的删除操作会被记录,提交仓库时会删除同样的文件,如果不想删除文件,可以使用 git add .,注意后面有一个"." 点的符号,这个命令跟 git add --all 一样,但是不会记录删除操作

最后别忘了 git commit

将文件撤销回到最近一次修改的状态:git checkout -- file

checkout:切换参数,通常用来切换分支仓库

注意这个恢复只能恢复到上一次提交的状态,如你刚提交了这个文件到仓库,随后你修改了它,那么使用这个命令只会回到刚刚提交后的那个状态里,不能回到你还没有提交,但修改的状态中。

注意这个功能不能一直迭代恢复,如你恢复到了修改前的版本,你想再次回滚回滚到修改前在之前的版本是不行的。

查看单个文件可回滚版本:git log filename

当我们想回滚指定文件到指定版本时,需要查看该文件有多少个版本可以回滚时,可以使用 git log filename 命令

即便你更新了一个文件,也会生成一个新的历史版本,注意历史版本里只包含了你更新的文件,你刚刚只 add了min.c 文件,所以新的历史版本里只有更新 min.c 文件,你当前的工作其它文件没有在这个历史版本里。

查看提交历史:git reflog

git reflog 可以查看当前版本库的提交历史,凡是对仓库版本进行迭代的都会出现在这个里面,包括你回滚版本都会出现在这个历史中

删除文件:git rm

如果我们使用普通的命令,rm 删除文件,git 状态会提示你删除了文件,你只需要使用 add / commit 重新提交一次就可以了。

当然你也可以使用 git rm 删除文件,但是也需要使用 git commit 提交一次

git rm 后恢复文件:git rm、git reset、git checkout

此方法仅限 git rm,因为 git rm 会先将文件放入缓存区,且没有使用 commit 提交的情况下

py 复制代码
git rm d.c # 使用git rm删除一个文件
git reset # 重置所有缓存区操作
git checkout d.c # 将文件取消操作

可以看到文件又恢复了

git 创建分支:git branch、git checkout

使用 git checkout -b 参数来创建一个分支,创建完成分支后会自动切换过去

py 复制代码
git checkout -b dev

然后我们在使用 branch 来查看当前属于哪个分支,也就是查看HEAD的指向

py 复制代码
git branch

git checkout -b 等价于

py 复制代码
git branch dev
git checkout dev

git branch 如果后面跟着名字则会创建分支,但不会切换

git checkout 后面如果是分支名称则切换过去

git 查看分支:git branch -a

如果要查看当前所有分支可以使用:git branch -a

git 修改分支名称:git branch -m

git branch -m 分支名 新的分支名

git 切换分支:git checkout

当我们想切换分支可以使用 git checkout 来切换

git checkout 的作用是检出,如果是文件的话,会放弃对文件的缓存区操作,但是要使用 reset 重置一下变更才行。

如果是分支的话会切换过去。

git 拉取远程所有分支:git fetch

当我们在本地进行开发时,有时会发现有些分支看不见

可以使用 git fetch 把远程全部分支拉取下来,同时也包括这些分支的仓库版本,log 日志等,这个操作不会进行合并。

也可以拉取指定分支的最新内容:

py 复制代码
git fetch xxxx

git查看不同分支的文件差异:git diff

py 复制代码
git diff branch1 branch2 --stat //--stat参数,显示两分支简单diff信息
git diff branch1 branch2 //显示两分支详细的diff信息
git diff branch1 branch2 path //显示两分支指定路径下文件的详细diff信息
git diff branch1 branch2 file_name(带路径) //显示两分支指定文件的详细diff信息

注:这里的分支可以是本地的,也可以是远程的(git diff branch1 origin/branch2)

git 保存当前工作切换分支:git stash

Git冲突:Please commit your changes or stash them before you merge

py 复制代码
git stash
git pull
git stash pop

通过 git stash 将工作区恢复到上次提交的内容,同时备份本地所做的修改,之后就可以正常 git pull 了,git pull 完成后,执行 git stash pop 将之前本地做的修改应用到当前工作区。

  • git stash: 备份当前的工作区的内容,从最近的一次提交中读取相关内容,让工作区保证和上次提交的内容一致。同时,将当前的工作区内容保存到 Git 栈中。

  • git stash pop: 从 Git 栈中读取最近一次保存的内容,恢复工作区的相关内容。由于可能存在多个 Stash 的内容,所以用栈来管理,pop 会从最近的一个stash中读取内容并恢复。

  • git stash list: 显示 Git 栈内的所有备份,可以利用这个列表来决定从那个地方恢复。

  • git stash clear: 清空 Git 栈。此时使用 gitg 等图形化工具会发现,原来 stash 的哪些节点都消失了。

  • git stash show 查看栈中最新保存的 stash 和当前目录的差异。

注意 stash 是以栈的方式保存的,先进后出。

一般情况下,我们在修改代码时,突然来了一个新的需求,让我们先去做这个需求,但是此时我们正在写的代码还没有完成,是不可以提交的,所以我们先使用 git stash 保存当前工作状态,在拉取一个分支去这个分支里面干活,干完活之后回到之前的分支,在将工作内容恢复出来继续干活

准确来说,这个命令的作用就是为了解决 git 不提交代码不能切换分支的问题。

git 合并分支:git merge

当我们新建分支并做完工作之后,想要把分支提交至 master,只需要切换到 master 仓库,并执行 git merge 分支名就可以了

如我们在分支中新建了一个 f.c 和 test.c 的文件

然后在使用 git checkout master 切换到 master

在使用 git merge dev 将其合并

这里需要说一点,如果你在任何分支下创建文件,没有提交到仓库,那么它在所有仓库都是可见的,比如你在分支 dev 中创建了一个文件,没有使用 git add 和 git commit 提交,此时你切换到 master,这个文件依旧存在的,因为你创建的文件在工作目录中,你切换仓库时 git 只会更新跟仓库有关的文件,无关的文件依然存放在工作区。

同时我们可以看到历史版本中有分支提交的历史

git 删除本地分支:git branch --delete

py 复制代码
git branch --delete 分支名
git branch -d 分支名 # -d 是 --delete 的缩写,该分支必须完全和它的上游分支 merge 完成,如果没有上游分支,必须要和 HEAD 完全 merge
git branch -D 分支名 # -D 是 --delete --force 的缩写,跳过检查 merge 状态,直接 delete

git 删除远程分支:git push origin --delete

py 复制代码
git push origin --delete 远程分支名

【常用分支介绍】

本小节介绍常用分支,以及创建合并删除的基础操作

在开发中 git 分支的重要性

当我们在开发中,无论做什么操作都建议使用分支,因为在团队开发中,master 只有一个,合作开发里任何人都可以从 master 里拉取代码,拉取时 master 后创建分支,分支名改为你要做的操作,比如修改某某文件,修改什么什么 bug,单词以下划线做分割,然后在提交一个版本

分支名必须简洁,和标题一样,提交的 commit 在简单描述一下就可以了。

如我们的 master 中有个 bug,是内存泄漏

我们可以常见一个分支名为 Memory_Leak, 然后在 commit 里简单描述一下修复了哪个模块的内存泄漏,不要写修复了什么什么代码,什么什么问题导致的,只需要简单描述一下就可以了。

一般情况下,我们都是拉取master后,想要修改功能或者添加功能,都是创建分支,在分支里修改不影响master,如果修改错了代码或者误删之类的,在从master上拉取一份就可以了。

1 主分支 Master

2 开发分支 Develop

主分支只用来发布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做 Develop

Git 创建 Develop 分支的命令:

py 复制代码
git checkout -b develop master

将 Develop 分支发布到Master分支的命令:

py 复制代码
# 切换到Master分支
git checkout master

# 对 Develop 分支进行合并
git merge --no-ff develop

默认情况下,Git 执行"快进式合并"(fast-farward merge),会直接将 Master 分支指向 Develop 分支。

使用 --no-ff 参数后,会执行正常合并,在 Master 分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。

3 临时性分支

3.1 功能(feature)分支

创建

py 复制代码
git checkout -b feature-x develop

合并到 develop 分支

py 复制代码
git checkout develop
git merge --no-ff feature-x

3.2 预发布(release)分支

它是指发布正式版本之前(即合并到 Master 分支之前),我们可能需要有一个预发布的版本进行测试。

创建

py 复制代码
git checkout -b release-1.2 develop

合并到 master

python 复制代码
git checkout master

git merge --no-ff release-1.2

# 对合并生成的新节点,做一个标签
git tag -a 1.2

再合并到 develop 分支

python 复制代码
git checkout develop
git merge --no-ff release-1.2

最后,删除预发布分支

python 复制代码
git branch -d release-1.2

3.3 修补 bug(fixbug)分支

软件正式发布以后,难免会出现 bug。这时就需要创建一个分支,进行 bug 修补。

创建

python 复制代码
git checkout -b fixbug-0.1 master

修补结束后,合并到 master 分支

python 复制代码
git checkout master

git merge --no-ff fixbug-0.1

git tag -a 0.1.1

再合并到 develop 分支:

python 复制代码
git checkout develop

git merge --no-ff fixbug-0.1

最后,删除"修补 bug 分支"

python 复制代码
git branch -d fixbug-0.1

git diff xxx.c

git status .

git checkout media/

git checkout framework/services/drive/

Please commit your changes or stash them before you merge

git stash

git pull

git stash pop

【Git 版本管理 | 莫烦PYTHON】学习笔记

1 Why Git?

Git 是一个分布式版本控制系统. 它的灵活性, 优越性使得它从2005年发布以来. 获得了越来越多的使用和支持.

1)什么时候需要用 Git?

  • 当你已经成为码农, 或者已经在成为码农的路上;
  • 当你觉得代码太多;
  • 当你已经开始用日期或版本号命名的代码文件的时候.

2)什么文件可以被 Git 管理?

  • 文本文件 (.txt) 等;
  • 脚本文件 (.py) 等;
  • 各种基于文本信息的文件.

3)什么文件不能被 Git 管理?

  • 图片文件 (.jpg) 等;
  • MS word (.doc) 等.

windows 系统中下载安装好 git 后,我们使用 git bashgit bash 是 git 在 Windows 上为了方便使用所设置的一个 Unix 的环境. 如果你是 Windows 用户, 之后的教程你也能用这个来学习使用 git.

git中文件状态转换关系如下图:

图片来源:https://morvanzhou.github.io/tutorials/others/git/2-1-repository/

stage the file 是 add

针对本地的一个项目,如果不是从github上下载,则所有文件一开始都属于untracked 状态,否则都属于tracked状态。

  • untracked
  • tracked
    • unstaged
      • unmodified
      • modified
    • staged

2 第一个版本库 Repository

2.1 创立版本库(init)

新建一个文件夹,然后打开 gitgit 的当前目录指向该文件夹,eg cd Desktop/git_test/

为了更好地使用 git, 我们同时也记录每一个施加修改的人. 这样人和修改能够对应上. 所以我们在 git 中添加用户名 user.name 和 用户 email user.email:

py 复制代码
git config --global user.name "Bryant"
git config --global user.email "[email protected]"

可以通过如下指令来查看用户信息

py 复制代码
git config user.name

结果为 Bryant

py 复制代码
git config user.email

结果为 [email protected]

然后我们就能在这个文件夹中建立 git 的管理文件了:

py 复制代码
 git init

通过 ls -a 可以查看管理库的信息(-a 可以查看隐藏的文件夹)

手动打开文件夹看看

至此我们已经建立了一个空的管理仓库。

2.2 添加文件管理 (add)

git status 来查看版本库的状态:

建立一个新的文件,eg 1.py,用 touch,哈哈,参考 Linux learning notes

py 复制代码
touch 1.py

现在我们能用 git status 来查看版本库的状态:

py 复制代码
git status

output 如下

py 复制代码
On branch master  # 在 master 分支

No commits yet

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

        1.py # 1.py 文件没有被加入版本库 (unstaged)

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

现在 1.py 并没有被放入版本库中 (unstaged), 所以我们要使用 add 把它添加进版本库 (staged):

py 复制代码
git add 1.py

然后再查看状态

py 复制代码
git status

output

py 复制代码
On branch master

No commits yet

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

        new file:   1.py

2.3 提交改变 (commit)

我们已经添加好了 1.py 文件, 最后一步就是提交这次的改变, 并在 -m 自定义这次改变的信息:

py 复制代码
git commit -m "create 1.py"

output

py 复制代码
[master (root-commit) 93d6b41] create 1.py
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1.py

2.4 流程图

整个上述过程可以被这张 git 官网上的流程图直观地表现:(untracked、unstaged、staged)

unmodified(window 的 git bash 下显示红色) 和 modified(window 的 git bash 下显示绿色)都为 unstaged 状态,unmodified 到 modified 可通过 add 操作!!!

3 记录修改(log & diff)

在 git 中, 每一次提交(commit)的修改, 都会被单独的保存起来. 也可以说 git 的中的所有文件都是一次次修改累积起来的. 文件好比楼房, 每个 commit 记录 了盖楼需添加或者拿走的材料. 整个施工过程也被记录了下来.

3.1 修改记录

之前我们以 Bryant 的名义对版本库进行了一次修改, 添加了一个 1.py 的文件. 接下来我们就来查看版本库的些施工的过程. 可以看到在 Author 那已经有我的名字和 email 信息了.

py 复制代码
git log

如果我们对1.py文件进行一次修改, 添加这行代码:

py 复制代码
a = 1

然后我们就能在 status 中(git status)看到修改还没被提交的信息了.

所以我们先把这次修改添加 (add) 到可被提交 (commit) 的状态, 然后再提交 (commit) 这次的修改:

py 复制代码
git add 1.py
git commit -m "change 1"

再次查看 log, 现在我们就能看到 create 1.pychange 1 这两条修改信息了. 而且做出这两条 commit 的 ID, 修改的 Author, 修改 Date 也被显示在上面.

py 复制代码
git log

3.2 查看 unstaged(no add)与已 commit 的改变

如果删除一部分代码, 也会被记录上, 比如把 a = 1 改成 a = 2, 再添加一个 b = 1.

如果想要查看这次还没 add (unstaged) 的修改部分 和上个已经 commit 的文件有何不同, 我们将使用

py 复制代码
git diff

output

py 复制代码
diff --git a/1.py b/1.py
index 1337a53..ff7c36c 100644
--- a/1.py
+++ b/1.py
@@ -1 +1,2 @@
-a = 1
+a = 2
+b = 1

3.3 查看 staged(已 add)与已commit 的改变

py 复制代码
git add .   # add 全部修改文件
git status

modified 绿了

git diff --cached 来查看添加后的代码与上次 commit 提交的区别

py 复制代码
git diff --cached

output

py 复制代码
diff --git a/1.py b/1.py
index 1337a53..ff7c36c 100644
--- a/1.py
+++ b/1.py
@@ -1 +1,2 @@
-a = 1
+a = 2
+b = 1

3.4 查看 staged & unstaged (HEAD)的改变

还有种方法让我们可以查看 add 过 (staged) 和 没 add (unstaged) 的修改, 比如我们再修改一下 1.py 但不 add:

py 复制代码
a = 2
b = 1
c = b

目前 a = 2b = 1 已被 add, c = b 是新的修改, 还没被 add.

  • git diff HEAD staged & unstaged
  • git diff unstaged
  • git diff --cached staged
py 复制代码
# 对比三种不同 diff 形式
$ git diff HEAD     # staged & unstaged

@@ -1 +1,3 @@
-a = 1  # 已 staged
+a = 2  # 已 staged
+b = 1  # 已 staged
+c = b  # 还没 add 去 stage (unstaged)
-----------------------
$ git diff          # unstaged

@@ -1,2 +1,3 @@
 a = 2  # 注: 前面没有 +
 b = 1  # 注: 前面没有 +
+c = b  # 还没 add 去 stage (unstaged)
-----------------------
$ git diff --cached # staged

@@ -1 +1,2 @@
-a = 1  # 已 staged
+a = 2  # 已 staged
+b = 1  # 已 staged

哈哈,绕来绕去的还是很麻烦的,建议还是每次修改后(modified 红色,unstaged),直接 add(modified 绿了,staged),然后 commit,再用 git diff --cached 查看与已 commit 的区别!

我们把如上的修改 add 然后 commit ,方便后续实验!

py 复制代码
git add .
git commit -m "change 2"

4 回到从前(rest)

4.1 修改已 commit 的版本

有时候我们总会忘了什么, 比如已经提交了 commit 却发现在这个 commit 中忘了附上另一个文件. 接下来我们模拟这种情况. 上节内容中, 我们最后一个 commitchange 2, 我们将要添加另外一个文件, 将这个修改也 commitchange 2. 所以我们复制 1.py 这个文件, 改名为 2.py. 并把 2.py 变成 staged, 然后使用 --amend 将这次改变合并到之前的 change 2

py 复制代码
git add 2.py
git commit --amend --no-edit   # "--no-edit": 不编辑, 直接合并到上一个 commit
git log --oneline    # "--oneline": 每个 commit 内容显示在一行

原来的 change 2 只有 1.py,现在的 change 2 有 1.py2.py

4.2 reset 回到 add 前(绿变红)

有时我们添加 add 了修改, 但是又后悔, 并想补充一些内容再 add. 这时, 我们有一种方式可以回到 add 之前. 比如在 1.py 文件中添加这一行:

py 复制代码
d = 3

然后 addstaged 再返回到 add 之前:

py 复制代码
git add 1.py
git status -s # "-s": status 的缩写模式,绿了
git reset 1.py # 返回 add 之前
git status -s # 红了

4.3 reset 回到 commit 之前(staged to unstaged)

在穿梭到过去的 commit 之前, 我们必须了解 git 是如何一步一步累加更改的. 我们截取网上的一些图片 http://bramus.github.io/ws2-sws-course-materials/xx.git.html

每个 commit 都有自己的 id 数字号, HEAD 是一个指针, 指引当前的状态是在哪个 commit. 最近的一次 commit 在最右边, 我们如果要回到过去, 就是让 HEAD 回到过去并 reset 此时的 HEAD 到过去的位置.

py 复制代码
# 不管我们之前有没有做了一些 add 工作, 这一步让我们回到 上一次的 commit
git reset --hard HEAD    
py 复制代码
git log --oneline

output

想回到上一个 commit

法一:HEAD^,类推,回到上上个 commit 即为 HEAD^^,那回到上......上(100个)怎么破?手动一百个^?还有一种简便的形式 HEAD~1 回到前一个

py 复制代码
git reset --hard HEAD^

等价于

py 复制代码
git reset --hard HEAD:~

法二: "commit id",找到想要回去 commit 版本的 id(git log --oneline),然后用如下指令,最后一串是 id 号

py 复制代码
git reset --hard 9c1eeb3

输入 git log --oneline 看看

py 复制代码
9c1eeb3 (HEAD -> master) change 1

怎么 change 2 消失了!!! 还有办法挽救消失的 change 2 吗? 我们可以查看 git reflog 里面最近做的所有 HEAD 的改动, 并选择想要挽救的 commit id:

py 复制代码
git reflog

重复 reset 步骤就能回到 commit (amend): change 2 (id=cb09a58) 这一步了:

py 复制代码
git reset --hard cb09a58
git log --oneline

我又回来了 change 2.

git log

  • git log --help

  • git log -- 'filename' 查看该文件相关的commit记录

  • git log -p 'filename' 可以显示该文件每次提交的diff

  • git show 'commit-id' 'filename' 查看某次提交中的某个文件变化

  • gitk --follow 'filename' 以图形化的界面显示文件修改列表

常用选项


表格来自 git log 查看某文件的修改历史

未完待续。。。


【附录】

git clone总是失败解决办法

git clone总是失败解决办法

py 复制代码
git clone https://github.com/xxx.git

提示下载失败,

可以尝试把 https:// 换成 git://

py 复制代码
git clone git://github.com/xxx.git
相关推荐
该换个名儿了2 小时前
git多个commit合并成一个
前端·git
不爱学英文的码字机器2 小时前
[Git] 分布式版本控制 & 远程仓库协作
分布式·git
Stack_guigui3 小时前
git连接本地仓库以及gitee
git·gitee
火车叼位3 小时前
Git 精准移植代码:cherry-pick 简单说明
前端·git
Cynthia-石头7 小时前
Git Github Gitee GitLab
git·gitee·github
ak啊21 小时前
Git 撤销操作完全指南:从工作区到远程仓库的救赎之路
git
小_路1 天前
git 常用命令
git
Dontla1 天前
git引用概念(git reference,git ref)(简化对复杂SHA-1哈希值的管理)(分支引用、标签引用、HEAD引用、远程引用、特殊引用)
git·算法·哈希算法
枫叶落雨2222 天前
Git 使用规范指南
git
德育处主任2 天前
聚沙成塔,三步成书:GitBook极简入门教程
前端·git·电子书