Git教程

推荐链接:

Git Book - 中文Git Book - 英文

Git 教程 - 菜鸟

Git 易百教程

https://docs.github.com/cn

https://docs.github.com/en

常用命令速查表

image-20250113150521663

实战

彻底清除所有历史提交记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建新分支
git checkout --orphan latest_branch
# 添加所有文件
git add .
# commit代码
git commit -m "update"
# 删除原来的主分支
git branch -D main
# 把当前分支重命名为主分支
git branch -m main
# 最后把代码推送到远程仓库
git push -f origin main
git pull
git pull --rebase

确定清除历史记录的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1、查看提交日志
git log --pretty=oneline

# 2、查看分支信息
# 列出所有本地分支
git branch
# 列出所有远程分支
git branch -r
# 列出所有本地分支和远程分支
git branch -a

# 3、查看 tag 信息
# 查看本地标签
git tag
# 查看远程标签
git ls-remote --tags

最后,登录远程仓库再次确认。

版本控制系统(VCS)

关于版本控制

本地版本控制系统

最流行的一种叫做 RCS,现今许多计算机系统上都还看得到它的踪影。

RCS 工作原理:在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。

1570249691811

缺点:

​ 无法让不同系统上的开发者协同工作。

​ 整个项目的变更历史被保存在单一位置,有丢失所有历史更新记录的风险。例如:磁盘损坏。

集中化的版本控制系统

Centralized Version Control Systems,简称 CVCS。

这类系统,诸如 CVS、Subversion(SVN) 、 Perforce 等,都有一个单一的集中管理的服务器保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法。

1570250254124

优点:

​ 个人都可以在一定程度上看到项目中的其他人正在做些什么。

​ 管理员可以轻松掌控每个开发者的权限。

​ 管理员只需管理这个集中服务器,而不需要在各个客户端上维护本地数据库。

缺点:

​ 如果中央服务器出现单点故障,那么谁都无法提交更新,也就无法协同工作。

​ 整个项目的变更历史被保存在单一位置,有丢失所有历史更新记录的风险。如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问将丢失所有数据,只剩下各自机器上保留的单独快照。

分布式版本控制系统

Distributed Version Control System,简称 DVCS

这类系统,像 Git、Mercurial、Bazaar、Darcs 等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。

1570250520308

优点:

​ 任何一处协同工作用的服务器发生故障,都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

​ 可以指定和若干不同的远端代码仓库进行交互,也就是说,可以在同一个项目中,分别和不同工作小组的人相互协作

​ 可以根据需要设定不同的协作流程,比如层次模型式的工作流

Git 基础概念

Git 初期设定的目标

  • 速度很快
  • 设计简单
  • 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
  • 完全分布式
  • 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

3+n个区域

三个本地区域 + n个远程区域:

683090701_88630

Workspace:工作区
Index/Stage:暂存区、索引(文件内容的哈希值)
Repository:仓库区、本地仓库、存储库、Git仓库
Remote:远程仓库

名词解释:

工作区 : 对项目的某个版本独立提取出来的内容。这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。通过 git init 创建的代码库的所有文件但是不包括 .git 文件(版本库=暂存区+Git仓库)。
暂存区 : 一个文件,保存了下次将提交的文件列表信息,一般在 .git 目录,。通过 git add ./*/*Xxx/Xxxx* 添加的修改,都是进入到暂存区了,肉眼不可见 通过 git status 可以看到修改的状态。

Git仓库 : Git 用来保存项目的元数据和对象数据库的地方。从其它计算机克隆仓库时,拷贝的就是这里的数据。

3 种状态

文件的三种状态:

  • 已修改(modified):做了修改但还没有放到暂存区域
  • 已暂存(staged):做了修改并已放入暂存区域
  • 已提交(committed):Git 仓库中保存着的特定版本文件

什么是修改:新增一行、删除一行、更改某些字符、删一些又加一些、创建一个新文件。

基本的 Git 工作流程:

  • 在工作区添加或修改文件(已修改)
  • 暂存文件,将文件的快照放入暂存区域(已暂存)
  • 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库(已提交)

直接记录快照,而非差异比较

Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。 概念上来区分,其它大部分系统以文件变更列表的方式存储信息。 这类系统(CVS、Subversion、Perforce、Bazaar 等等)将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。存储每个文件与初始版本的差异,如下图所示:

919150744_33189

Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。如下图所示:

480150745_86943

这是 Git 与几乎所有其它版本控制系统的重要区别。

Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。

近乎所有操作都是本地执行

在 Git 中的绝大多数操作都只需要访问本地文件和资源,这意味着在离线或者没有 VPN 时,几乎可以进行任何操作。因为本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。

Git 保证完整性

Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。

Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符 (0-9 和 a-f) 组成字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:

1
80e7c5b72de33d7faee6dcf43a0aaca1e236d638

Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。

Git 一般只添加数据

你执行的 Git 操作,几乎只往 Git 数据库中增加数据。 很难让 Git 执行任何不可逆操作(不可恢复操作),或者让它以任何方式清除数据。同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容;但是一旦提交快照到 Git 中,就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。

更深度探讨 Git 如何保存数据及恢复丢失数据的话题,请参考 Git 修正错误 git reset 命令

Git 工作流

分支开发工作流

分布式工作流程

上面的官方链接介绍了常用的工作流程,但在实际的开发中,你会遇到许多可能适合你的特定工作流程的变种。

官网也给出了一些如何扮演不同工作流程中主要角色的更具体的例子,请参考 Git Book

Git 安装配置

安装 Git

Git for Windows

GitHub for Windows

Git 更新

1
git update-git-for-windows

Git 设置和取消http代理

为HTTP(应用层)协议配置SOCKS(会话层)代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看当前代理设置,
git config --global http.proxy
git config --global https.proxy

# 设置当前代理为 socket5://127.0.0.1:1080
git config --global http.proxy socks5://127.0.0.1:1080
git config --global https.proxy socks5://127.0.0.1:1080

# 设置当前代理为 http://127.0.0.1:1080
git config --global http.proxy 'http://127.0.0.1:1081'
git config --global https.proxy 'http://127.0.0.1:1081'

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

npm config delete proxy

本地保存帐号密码

Git 工具 - 凭证存储

git bash 进入项目目录,输入:

git config --global credential.helper store

然后会在本地生成一个文本,上边记录着账号和密码。当然这些可以不用关心。

使用上述的命令配置好之后,再操作一次 git pull,它会提示输入账号密码,之后就不会提示了。

Git 基本操作

git config

官方: https://git-scm.com/docs/git-config

git config命令用于获取并设置存储库或全局选项。这些变量可以控制Git的外观和操作的各个方面。

配置文件的存储位置

这些变量可以被存储在三个不同的位置:

  1. /etc/gitconfig 文件:包含了适用于操作系统所有用户和所有库的值。如果传递参数选项 --systemgit config,它将明确的读和写这个文件。

    Windows环境中,该文件在Git安装目录下,例如 D:\Program Files\Git\mingw64\etc。

  2. ~/.gitconfig 文件 :具体到当前用户(例如 Administrator)。可以通过传递 --global 选项使 Git 读或写这个特定的文件。

    Windows环境中,该文件在系统当前用户目录下,例如 C:\Users\Administrator。

  3. 位于 .git 目录的config文件 (也就是 .git/config) :无论当前在用的库是什么,特定指向该单一的库。每个级别重写前一个级别的值。因此,在.git/config中的值覆盖了在/etc/gitconfig中的同一个值。

    Windows环境中,该文件在库目录下,例如 D:\git\git-test\.git。

优先级.git/config > ~/.gitconfig > /etc/gitconfig

配置用户名和邮箱

当安装Git后首先要做的事情是设置用户名和e-mail地址。这是非常重要的,因为每次Git提交都会使用该信息。它被永远的嵌入到了你的提交中:

1
2
$ git config --global user.name 'JonSnows'
$ git config --global user.email 'JonSnows@qq.com'

重申一遍,只需要做一次这个设置。如果传递了 --global 选项,那么Git将总是会使用该信息来处理在系统中所做的一切操作。如果希望在一个特定的项目中使用不同的名称或 e-mail 地址,可以在该项目中运行该命令而不要 --global 选项。

1
2
$ git config user.name 'JonSnows'
$ git config user.email 'JonSnows@qq.com'

配置编缉器

可以配置默认的文本编辑器,Git在需要你输入一些消息时会使用该文本编辑器。缺省情况下,Git使用系统的缺省编辑器,这通常可能是 vi 或者 vim 。如果想使用一个不同的文本编辑器,例如:Emacs,可以按照如下操作:

1
$ git config --global core.editor emacs

配置比较工具

另外一个你可能需要配置的有用的选项是缺省的比较工具,它用来解决合并时的冲突。例如,想使用 vimdiff 作为比较工具:

1
$ git config --global merge.tool vimdiff

Git可以接受 kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, 和 opendiff 作为有效的合并工具。也可以设置一个客户端的工具;

检查配置

如果想检查你的设置,可以使用 git config --list 命令来列出Git可以在该处找到的所有的设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$ git config --local --list   # 局部配置,优先级最高
$ git config –-global --list # 全局配置
$ git config –-system --list # 系统配置,优先级最低

$ git config --list # 所有配置
core.symlinks=false
core.autocrlf=true
core.fscache=true
color.diff=auto
color.status=auto
color.branch=auto
color.interactive=true
help.format=html
rebase.autosquash=true
http.sslcainfo=D:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
http.sslbackend=openssl
diff.astextplain.textconv=astextplain
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge --skip -- %f
filter.lfs.process=git-lfs filter-process --skip
filter.lfs.required=true
credential.helper=manager
user.name=JonSnows # user.name
user.email=JonSnows@qq.com # user.email
winupdater.recentlyseenversion=2.23.0.windows.1
core.repositoryformatversion=0
core.filemode=false
core.bare=false
core.logallrefupdates=true
core.symlinks=false
core.ignorecase=true
remote.origin.url=git@github.com:JonSnows/git-test.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
user.name=JonSnowsaaaaaa # user.name 被使用
user.email=JonSnowsaaaaaa@qq.com # user.email 被使用

可能会看到一个关键字出现多次(如这里的:user.name就有两个值),这是因为Git从不同的文件中(例如:/etc/gitconfig以及~/.gitconfig)读取相同的关键字。在这种情况下,对每个唯一的关键字,Git使用最后的那个值。

也可以查看Git认为的一个特定的关键字目前的值,使用如下命令 git config {key}:

1
2
3
4
5
6
$ git config user.name
JonSnowsaaaaaa

Administrator@JonSnows MINGW64 /d/aa/git-test (master)
$ git config user.email
JonSnowsaaaaaa@qq.com

添加/删除配置项

添加配置项

参数

  • -–add

格式: git config [–local|–global|–system] –-add section.key value(默认是添加在 local 配置中)

注意add后面的 section, key, value 一项都不能少,否则添加失败。比如执行:

1
$ git config --add site.name www.JonSnows.com

删除配置项

命令参数

  • -–unset
    格式:git config [–local|–global|–system] –unset section.key
1
2
# 删除 local 配置中的 site.name 配置值
$ git config --local --unset site.name

git help

如果在使用Git时需要帮助,有三种方法可以获得任何 git 命令的手册页(manpage)帮助信息:

1
2
$ git help <verb>
$ git <verb> --help

示例

1
2
$ git help help
$ git config --help

git init

Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。

在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变(不像 SVN 会在每个子目录生成 .svn 目录,Git 只在仓库的根目录生成 .git 目录)。

git init 在目录中创建新的 Git 仓库,可以在任何时候、任何目录中这么做,完全是本地化的。

比如创建 runoob 项目:

1
2
3
4
5
6
7
8
9
git init runoob #使用指定目录作为Git仓库

或者

mkdir runoob
cd runoob
git init #使用当前目录作为Git仓库

ls -a #现在可以看到项目中生成了 .git 子目录

git clone

使用 git clone 拷贝一个 Git 仓库到本地,让自己能够查看该项目,或者进行修改,此操作将复制该项目的全部记录。

1
2
3
git clone [url]

git clone https://github.com/username/RepositoryName.git

默认情况下,Git 会按照 URL 所指示的项目名称创建本地项目目录。 通常就是 URL 最后一个 / 之后的项目名称,即 RepositoryName 。如果想要一个不一样的名字, 可以在该命令后加上想要的名称。

1
git clone https://github.com/username/RepositoryName.git folderName/CustomName	# 指定目录

可以使用不同的协议,包括 ssh, git, https 等。

1
2
3
git clone git@github.com:username/RepositoryName.git CustomName			# SSH协议 速度较快,可以配置公钥免输入密码
git clone git://github.com/username/RepositoryName.git CustomName # GIT协议
git clone https://github.com/username/RepositoryName.git CustomName # HTTPS协议

git add

将文件添加到缓存区(暂存区),缓存区中的文件都是未提交的

1
2
3
4
git add [file1] [file2]      # 添加一个或多个文件到暂存区
git add [dir]          # 添加指定目录到暂存区,包括子目录
git add . # 将当前目录下的所有修改添加到暂存区
git add * # 使用Ant风格,将所有修改添加到暂存区

AntPathMatcher路径匹配器,Ant风格的URL

git status

查看项目的当前状态,简单讲,查看上次提交之后是否有修改,修改是否被缓存(被暂存)

1
2
git status    #详细结果输出
git status -s #简短结果输出

1570006941653

新增文件 new.txt ,并填写内容,再执行 git statusgit status -s ,如下图,表示该文件是新增文件,未添加到缓存,未提交到版本库。

1570007053925

执行 git add 命令添加文件到缓存区,再执行 git statusgit status -s ,如下图,表示该文件是新增文件,无修改未添加到缓存,有修改未提交到版本库。

1570006354885

修改文件内容后,再执行 git statusgit status -s ,如下图,表示该文件是新增文件,有修改未添加到缓存,有修改未提交到版本库。

1570006778565

修改版本库中的文件内容,如下图,表示该文件非新增文件,有修改未添加到缓存,有修改未提交到版本库。

1570029346639

修改版本库中的文件内容,并添加到缓存,如下图,表示该文件非新增文件,无修改未添加到缓存,有修改未提交到版本库。

1570029716814

总结:

控制台有颜色:

红色表示:有修改未添加到缓存,有修改未提交到版本库。

绿色表示:无修改未添加到缓存,有修改未提交到版本库。

控制台无颜色:_ 表示空格,除了 ?? ,第一位非空即绿,第二位非空即红。

?? 表示:该文件是新增文件,未添加到缓存,未提交到版本库。

A_ 表示:该文件是新增文件,无修改未添加到缓存,有修改未提交到版本库。

AM 表示:该文件是新增文件,有修改未添加到缓存,有修改未提交到版本库。

_M 表示:该文件非新增文件,有修改未添加到缓存,有修改未提交到版本库。

M_ 表示:该文件非新增文件,无修改未添加到缓存,有修改未提交到版本库。

git diff

预览差异。执行 git diff 来查看执行 git status 的结果的详细信息。

git status 显示上次提交更新后的更改或者写入缓存的改动, 而 git diff 一行一行地显示这些改动具体是啥。

应用场景1

  • 尚未缓存的改动:git diff
  • 查看已缓存的改动: git diff --cached
  • 查看已缓存的与未缓存的所有改动:git diff HEAD
  • 显示摘要而非整个 diff:git diff --stat

新建文件 new.txt ,写入内容 “已缓存的修改” ,添加到缓存,再写入内容 “未缓存的修改” 。

1570033381991

应用场景2

在分支合并之前,也可以使用 git diff 命令预览差异:

1
2
# 将target_branch分支合并到source_branch分支前预览差异
git diff <source_branch> <target_branch>

git commit

使用 git add 命令将想要快照的内容写入缓存区, 而执行 git commit 将缓存区内容提交到版本库中

Git 为每一次提交都记录提交者的名字和电子邮箱地址,所以 commit 前需要配置用户名和邮箱地址。

1
2
git config --global user.name 'JonSnows'
git config --global user.email JonSnows@qq.com

接下来写入缓存,并提交对项目的所有改动,使用 -m 选项以在命令行中提供提交注释。

1
2
3
git add *
git status -s
git commit -m '第一次版本提交的注释'

如果你没有设置 -m 选项,Git 会尝试为你打开一个编辑器以填写提交信息,退出即可,此时已经完成提交。

如果觉得 git add 提交暂存的流程太过繁琐,Git 也允许用 -a 选项跳过这一步,此命令无法提交新增文件。命令格式如下:

1
2
3
4
5
git commit -am '提交注释'

git commit -a # 会先把所有已经track的文件的改动`git add`进来,然后提交(有点像svn的一次提交,不用先暂存)。对于没有track的文件,还是需要执行`git add <file>` 命令。

git commit --amend # 增补提交,会使用与当前提交节点相同的父节点进行一次新的提交,旧的提交将会被取消。

该命令将改动提交到了 HEAD,但是还没到远端仓库。

git reset HEAD

详解: https://www.yiibai.com/git/git_reset.html

git reset HEAD 命令用于取消已缓存的内容。

1
2
git reset HEAD <file> #取消单个缓存
git reset HEAD #取消所有缓存

将一个新增文件 new2.txt 和 修改文件 new.txt 都提交到暂存区,然后一个个撤销暂存,操作如下:

1570013913033

简而言之,执行 git reset HEAD 以取消之前 git add 的添加。

git stash

https://www.yiibai.com/git/git_stash.html

git rebase

https://www.yiibai.com/git/git_rebase.html

git cherry-pick

https://www.ruanyifeng.com/blog/2020/04/git-cherry-pick.html

git rm

如果只是简单地从工作区中手工删除文件,运行 git status 时就会在 Changes not staged for commit 的提示。

1570014491047

要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除,然后提交。可以用以下命令完成此项工作

1
git rm <file>

如果删除之前修改过并且已经放到暂存区域的文件,会有如下图错误提示,必须要用强制删除选项 -f

1
git rm -f <file>

1570015299841

如果把文件从暂存区域移除,但仍然希望保留在当前工作目录中,换句话说,仅是从跟踪清单中删除,使用 –cached 选项即可。

1
git rm --cached <file>

git rm --cached <file>git reset HEAD <file> 的区别?

git reset HEAD <file> 是取消已缓存的内容,提交到版本库后,此文件无任何改动。

git rm --cached <file> 是把文件从暂存区移除,提交到版本库后,此文件被删除。

git mv

git mv 命令用于移动或重命名一个文件、目录、软连接。

1
git mv <file> <newFile>

1570016354293

Git 分支管理

开始前先创建一个测试目录:

1
2
3
4
5
git init gitdemo
cd gitdemo
touch README # 添加文件
git add README
git commit -m '第一次版本提交'

列出分支

列出分支基本命令:

1
2
3
4
5
6
$ git branch
* master
$ git branch -a # 查看分支,包括已删除的远程分支,不包括已删除的本地分支
* master
remotes/origin/master
remotes/origin/testing

此例的意思就是,我们有一个叫做 master 的分支,并且该分支是当前分支。

当你执行 git init 的时候,默认情况下 Git 就会为你创建 master 分支。

创建分支

创建分支命令:

1
git branch <branchname> #已当前分支内容为基础创建分支

创建 testing 分支:

1
2
3
4
$ git branch testing
$ git branch
* master
testing

当你以此方式在上次提交更新之后创建了新分支,如果后来又有更新提交, 然后又切换到了 testing 分支,Git 将还原你的工作目录到你创建分支时候的样子。

切换分支

切换分支命令:

1
git checkout <branchname>

当切换分支的时候,Git 会用该分支的最后提交的快照替换工作目录(工作区)的内容, 所以多个分支不需要多个目录。

1
2
3
4
5
6
7
8
9
10
11
$ ls
README
$ echo 'runoob.com' > test.txt
$ git add *
$ git commit -m 'add test.txt'
$ ls
README test.txt
$ git checkout testing
Switched to branch 'testing'
$ ls
README

当切换到 testing 分支的时候,添加的新文件 test.txt 被移除了。切换回 master 分支的时候,它们又重新出现了。

1
2
3
4
$ git checkout master
Switched to branch 'master'
$ ls
README test.txt

创建分支+切换

创建新分支并立即切换到该分支下:

1
git checkout -b <branchname> # build

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ git checkout master
$ git checkout -b newtest
Switched to a new branch 'newtest'
$ git rm test.txt
rm 'test.txt'
$ ls
README
$ touch runoob.php # 添加文件
$ git add *
$ git commit -am 'removed test.txt、add runoob.php'
[newtest c1501a2] removed test.txt、add runoob.php
2 files changed, 1 deletion(-)
create mode 100644 runoob.php
delete mode 100644 test.txt
$ ls
README runoob.php
$ git checkout master
Switched to branch 'master'
$ ls
README test.txt

如你所见,我们创建了一个分支,在该分支的上移除了文件 test.txt,并添加了 runoob.php 文件,然后切换回主分支,删除的 test.txt 文件又回来了,且新增加的 runoob.php 不存在主分支中。

使用分支将工作切分开来,从而让我们能够在不同开发环境中做事,并来回切换。

删除分支

删除本地分支

1
git branch -d <branchname>

例如删除 testing 分支:

1
2
3
4
5
6
7
$ git branch
* master
testing
$ git branch -d testing
Deleted branch testing (was 85fc7e7).
$ git branch
* master

删除远程分支

1
git push origin --delete <BranchName>

分支合并

一旦某分支有了独立内容,终究会希望将它合并到主分支。 可以使用以下命令将任何分支合并到当前分支中去:

1
git merge <branchname>

可以多次合并到统一分支, 也可以选择在合并之后直接删除被并入的分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git branch
master
* newtest
$ ls
README runoob.php
$ git checkout master
$ ls
README test.txt
$ git merge newtest
Updating 3e92c19..c1501a2
Fast-forward
runoob.php | 0
test.txt | 1 -
2 files changed, 1 deletion(-)
create mode 100644 runoob.php
delete mode 100644 test.txt
$ ls
README runoob.php

以上实例中我们将 newtest 分支合并到主分支去,test.txt 文件被删除。

合并完后就可以删除分支,删除未合并的分支会给出提示,要求使用 “git branch -D newtest” 命令删除,大写D。

1
2
$ git branch -d newtest
Deleted branch newtest (was c1501a2).

合并冲突

合并并不仅仅是简单的文件添加、移除的操作,Git 也会合并修改。

1
2
3
$ git branch
* master
$ cat runoob.php

首先,创建一个叫做 change_site 的分支,切换过去,我们将 runoob.php 内容改为:

1
2
3
<?php
echo 'runoob';
?>

创建 change_site 分支:

1
2
3
4
5
6
7
8
9
10
11
12
$ git checkout -b change_site
Switched to a new branch 'change_site'
$ ls
README runoob.php
$ vim runoob.php
$ head -3 runoob.php
<?php
echo 'runoob';
?>
$ git commit -am 'changed the runoob.php'
[change_site 7774248] changed the runoob.php
1 file changed, 3 insertions(+)

将修改的内容提交到 change_site 分支中。 现在,切换回 master 分支,我们可以看到 runoob.php 是空文件,我们再次修改 runoob.php 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ git checkout master
Switched to branch 'master'
$ cat runoob.php
$ vim runoob.php # 修改内容如下
$ cat runoob.php
<?php
echo 1;
?>
$ git diff
diff --git a/runoob.php b/runoob.php
index e69de29..ac60739 100644
--- a/runoob.php
+++ b/runoob.php
@@ -0,0 +1,3 @@
+<?php
+echo 1;
+?>
$ git commit -am '修改代码'
[master c68142b] 修改代码
1 file changed, 3 insertions(+)

接下来将 “change_site” 分支合并过来。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git merge change_site
Auto-merging runoob.php
CONFLICT (content): Merge conflict in runoob.php
Automatic merge failed; fix conflicts and then commit the result.

$ cat runoob.php # 打开文件,看到冲突内容
<?php
<<<<<<< HEAD
echo 1;
=======
echo 'runoob';
>>>>>>> change_site
?>

现在已将 “change_site” 分支合并到 “master” 分支,发现一个合并冲突,接下来需要手动修改它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ git diff
diff --cc runoob.php
index ac60739,b63d7d7..0000000
--- a/runoob.php
+++ b/runoob.php
@@@ -1,3 -1,3 +1,7 @@@
<?php
++<<<<<<< HEAD
+echo 1;
++=======
+ echo 'runoob';
++>>>>>>> change_site
?>
$ vim runoob.php
$ cat runoob.php # 修改后的内容
<?php
echo 1;
echo 'runoob';
?>
$ git diff
diff --cc runoob.php
index ac60739,b63d7d7..0000000
--- a/runoob.php
+++ b/runoob.php
@@@ -1,3 -1,3 +1,4 @@@
<?php
+echo 1;
+ echo 'runoob';
?>

在 Git 中,我们可以用 git add 要告诉 Git 文件冲突已经解决

解决合并的文件冲突后,添加到缓存区,并提交结果。

1
2
3
4
5
6
7
$ git status -s
UU runoob.php
$ git add runoob.php
$ git status -s
M runoob.php
$ git commit
[master 88afe0e] Merge branch 'change_site'

Git 查看提交历史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
git log						# 列出历史提交记录
git log --oneline # 简洁版,项目的开发历史
git log --pretty=oneline # 简洁版,项目的开发历史
git log -3 # 显示最近三次的提交
git log --no-merges # 列出历史提交记录,但跳过合并
git log --graph # 何时出现分支、何时归并
git log --reverse # 逆向显示所有日志
git log --author=JonSnows # 查找指定用户提交的记录,用户名区分大小写
git log --author=JonSnows --oneline -5 # JonSnows提交记录的最近5条简洁版


# 如果你要指定日期,可以执行几个选项:--since 和 --before,但是你也可以用 --until 和 --after。
git log --before={3.weeks.ago} --after={2019-10-03} # 在三周前且在 2019-10-03 之后的所有提交

# 查看从 2019-10-03 到 2019-10-04 的所有提交
git log --since={2019-10-03} --until={2019-10-04} # 方式一
git log --oneline --before={2019-10-04} --after={2019-10-03} --no-merges # 方式二

更多 git log 命令可查看: https://git-scm.com/docs/git-log

Git 标签

Git 工具 - 签署工作

Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。

**轻量级标签:**就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。

**含附注标签:**实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。

建议使用含附注标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。

列显标签

1
2
3
4
5
6
7
8
9
$ git tag	# 列出现有标签
v1.0
v1.2

$ git tag -l 'v1.4.2.*' # 用特定的搜索模式列出符合条件的标签
v1.4.2.1
v1.4.2.2
v1.4.2.3
v1.4.2.4

创建标签

注意:处于同一状态的项目可以创建多个标签。为了方便对比,下面示例中在创建标签之前会先做一次修改并提交,这样就避免了多个标签。

多个标签的情况:

1570181890623

轻量级标签

轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标签,一个 -a-s-m 选项都不用,直接给出标签名字即可:

1
2
3
$ git tag v1.0-lw
$ git tag
v1.0-lw

含附注标签

1
2
3
4
5
6
7
# 创建一个含附注类型的标签
# 用 -a 指定标签名字。(译注:取 annotated 的首字母)
# 用 -m 指定标签说明。Git 会将此说明一同保存在标签对象中。如果没有给出该选项,Git 会启动文本编辑软件供你输入标签说明。
$ git tag -a v2.0 -m 'my version 2.0'
$ git tag
v1.0-lw
v2.0

查看标签

使用 git show 命令查看相应标签的版本信息,并连同显示打标签时的提交对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ git show	# 不指定标签时,显示最新的标签,但不会显示标签详细信息
commit 80e7c5b72de33d7faee6dcf43a0aaca1e236d638 (HEAD -> master, tag: v2.0)
Author: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 17:40:33 2019 +0800

v2.0 版本提交

$ git show v1.0-lw # 轻量级标签,只有相应的提交对象摘要
commit 903fa8bfb66086decedfec2b05643e1cdef1e1b0 (tag: v1.0-lw)
Author: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 17:39:33 2019 +0800

v1.0-lw 版本提交

$ git show v2.0 # 含附注标签,不仅有相应的提交对象摘要,还有自身的校验(签名)和信息
tag v2.0
Tagger: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 17:41:10 2019 +0800

my version 2.0

commit 80e7c5b72de33d7faee6dcf43a0aaca1e236d638 (HEAD -> master, tag: v2.0)
Author: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 17:40:33 2019 +0800

v2.0 版本提交

可以看到在提交对象信息上面,列出了此标签提交者提交时间,以及相应的标签说明

删除标签

1
2
3
4
5
$ git tag -d v1.0-lw
Deleted tag 'v1.0-lw' (was 903fa8b)
$ git tag
v2.0
v2.1

签署标签

Git 工具 - 签署工作 ——官方

了解 GPG ——百科

GPG 入门教程 ——阮一峰

如果有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的选项 -a 改为 -s (译注: 取 signed 的首字母)即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ git tag -s v2.1 -m 'my signed 2.1 tag'

$ git show v2.1 # 含附注标签,不仅有相应的提交对象摘要,还有自身的校验(签名)和信息
tag v2.1
Tagger: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 17:48:01 2019 +0800

my signed 2.1 tag
-----BEGIN PGP SIGNATURE-----

iQFEBAABCAAuFiEEvWwz8KF9vaaEDumct8C+21+aReAFAl2XFVEQHGpvbnNub3dz
QHFxLmNvbQAKCRC3wL7bX5pF4GykCACr1gxm1hxxUNVBWRueXL2VqBPRogvOa+lZ
WG0EBGVBFgIC3Wf9+vaI7p9gcjkX3PJKR0Q+gqnkihLutTswf1Hsf6vXzBZNK50b
qY81UcZnu4DyvpCQSU4ZHAzIpjuUxJPaKpNf0QaViYrgawiG9Uvz0u+iMOgDCcf0
Kx6X6BLik6k5wBgDhEAtEDClLRI0mlDtbNJWfCXNDeJd/+x7eR01p/8YEj09rBtm
3hiGAa2gA+NfoX8SsKndJbMGTIN7dc2Y9QgwOH6KQ7w7q2j7rEDAIMUJF1BZC2zi
kfyWQvFPS/3wZXZJnXsdMpscTRXn7wRXm6FRITXbEF726njvL5XY
=5NcP
-----END PGP SIGNATURE-----

commit 80e7c5b72de33d7faee6dcf43a0aaca1e236d638 (HEAD -> master, tag: v2.1, tag: v2.0)
Author: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 17:40:33 2019 +0800

v2.0 版本提交

验证标签

可以使用 git tag -v [tag-name] (译注:取 verify 的首字母)的方式验证已经签署的标签。此命令会调用 GPG 来验证签名,所以你需要有签署者的公钥,存放在 keyring 中,才能验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git tag -v v2.1
gpg: Signature made 2019年10月 4日 17:48:01
gpg: using RSA key BD6C33F0A17DBDA6840EE99CB7C0BEDB5F9A45E0
gpg: issuer "jonsnows@qq.com"
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2021-10-03
gpg: Good signature from "JonSnows <JonSnows@qq.com>" [ultimate]
object 80e7c5b72de33d7faee6dcf43a0aaca1e236d638
type commit
tag v2.1
tagger JonSnows <JonSnows@qq.com> 1570182481 +0800

my signed 2.1 tag

若是没有签署者的公钥,会报告类似下面这样的错误:

1
2
3
4
$ git tag -v v2.1
gpg: Signature made Wed Sep 13 02:08:25 2019 PDT using DSA key ID F3119B9A
gpg: Can't check signature: public key not found
error: could not verify the tag 'v2.1'

后期加注标签

甚至可以在后期对早先的某次提交加注标签。比如在下面展示的提交历史中:

1570186370679

我们忘了在提交 v1.0-lw 版本提交 后为此项目打上版本号 v1.0-lw,没关系,现在也能做。只要在打标签的时候跟上对应提交对象的校验和(或前几位字符)即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ git tag -a v1.0-lw -m '后期加注标签' 903fa8b

# 现在,可以看到已经补上了标签:
$ git tag
v1.0-lw
v2.0
v2.1

$ git show v1.0-lw
tag v1.0-lw
Tagger: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 18:54:47 2019 +0800

后期加注标签

Author: JonSnows <JonSnows@qq.com>
Date: Fri Oct 4 17:39:33 2019 +0800

v1.0-lw 版本提交

1570187084121

分享标签

默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支,运行 git push origin [tagname] 即可:

1
2
3
4
5
6
7
8
9
10
11
$ git push origin v2.1

Enumerating objects: 21, done.
Counting objects: 100% (21/21), done.
Delta compression using up to 6 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (19/19), 2.03 KiB | 414.00 KiB/s, done.
Total 19 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To github.com:JonSnows/git-test.git
* [new tag] v2.1 -> v2.1

回到 Github 发现:

1570188057429

如果要一次推送所有本地新增的标签上去,可以使用 --tags 选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git push origin --tags

Enumerating objects: 2, done.
Counting objects: 100% (2/2), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 305 bytes | 305.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To github.com:JonSnows/git-test.git
* [new tag] v1.0-lw -> v1.0-lw
* [new tag] v2.0 -> v2.0

# 现在,其他人克隆共享仓库或拉取数据同步后,也会看到这些标签。

1570188295589

Git 远程仓库(Github)

Git 并不像 SVN 那样有个中心服务器。

目前我们使用到的 Git 命令都是在本地执行,如果你想通过 Git 分享你的代码或者与其他开发人员合作。 你就需要将数据放到一台其他开发人员能够连接的服务器上。

服务器上的 Git - 协议

Git传输协议

配置SSH密钥

Git传输协议 有很多,要使用 SSH 协议就需要配置 SSH Key; 也可以使用 HTTP 协议的用户名/密码的基础授权,免去设置 SSH 公钥 。

单个Git帐号的配置

使用以下命令生成 SSH Key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ssh-keygen -t rsa -C "youremail@example.com"

$ ssh-keygen -t rsa -f filename

# 更多选项参考 https://man.linuxde.net/ssh-keygen
语法
ssh-keygen(选项)
选项
-b:指定密钥长度;
-e:读取openssh的私钥或者公钥文件;
-C:添加注释;
-f:指定用来保存密钥的文件名;
-i:读取未加密的ssh-v2兼容的私钥/公钥文件,然后在标准输出设备上显示openssh兼容的私钥/公钥;
-l:显示公钥文件的指纹数据;
-N:提供一个新密语;
-P:提供(旧)密语;
-q:静默模式;
-t:指定要创建的密钥类型。

后面的 your_email@youremail.com 改为你在 Github 上注册的邮箱,之后会要求确认路径和输入密码,我们这使用默认的一路回车就行。成功的话会在 ~/ 下生成 .ssh 文件夹,进去,打开 id_rsa.pub,复制里面的 key

回到 github 上,进入 Account => Settings(账户配置)。

左边选择 SSH and GPG keys,然后点击 New SSH key 按钮,title 设置标题(随便填),粘贴刚生成的 key。

1570087237329

为了验证是否成功,在 git bash 下输入以下命令:

1
2
$ ssh -T git@github.com
Hi Jon-Snows! You've successfully authenticated, but GitHub does not provide shell access. #说明已经连接成功

如果是第一次会提示是否continue,输入yes就会看到以上输出,这就表示已成功连上github。

若已经拥有密钥对,就无需重新生成了。只需将私钥放到 ~/.ssh 目录,文件名必须是 id_rsa ,且无扩展名,并在 github 或 gitlab 个人设置里配置 ssh key 即可。

多个Git帐号的配置

推荐链接:

SSH Config 那些你所知道和不知道的事

SSH的config配置之多账号简单管理

生成新 SSH 密钥并添加到 ssh-agent

ssh-agent - 维基百科

同时使用多个 Git 帐号,比如 GithubOSChinaGitlib 等。

~/.ssh/ 目录下,新建一个 config 文件,配置多账户规范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 为SSH(应用层)协议配置SOCKS(会话层)代理:
# 全局代理。-S 使用 SOCKS 代理。-a none 是 NO-AUTH 模式。
# ProxyCommand connect -S 127.0.0.1:1080 -a none %h %p
# 选择代理
Host github_wan github.com bitbucket.org
ProxyCommand connect -S 127.0.0.1:1080 -a none %h %p

Host github_wan # 别名(推荐直接使用HostName),后面配置git remote地址有用
HostName github.com # 要连接的服务器,与别名相同时可忽略。
Port 22
IdentityFile ~/.ssh/id_rsa # 私钥文件,也可以是相对路径

Host github_wan2
HostName github.com
Port 22
IdentityFile ~/.ssh/id_rsa2

Host gitlab_lan
HostName 192.168.3.234
Port 2224
IdentityFile ~/.ssh/id_rsa_lan

使用 ssh 的 ssh-add 命令将密钥添加到 ssh-agent 的高速缓存中,以缓存私钥的密码。这种方式让你只需要解锁一次密钥就可以重复地使用它,这样在会话中就不需要再次输入密钥密码了 。同理 gpg-agent

1
2
3
4
5
6
7
8
9
# Windows PowerShell
$ ssh-agent.exe # 启动ssh-agent服务,服务显示名称未 OpenSSH Authentication Agent,系统默认禁用
$ ssh-add ~/.ssh/id_rsa # A账户的私钥
$ ssh-add ~/.ssh/id_rsa2 # B账户的私钥

# Linux
$ ssh-agent bash
$ ssh-add ~/.ssh/id_rsa
$ ssh-add ~/.ssh/id_rsa2

添加完后,可以使用 ssh-add 查看密钥列表

配置远程库地址:打开项目下的 .git\config 文件并修改 remote url 。在配置 ssh 时,为每个 Hostname 配置了一个 host 的别名,用别名来代替 Hostname:

1
2
3
4
5
6
7
8
9
10
11
12
13
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "gitlab"]
url = ssh://git@gitlab_lan/organization-xy/berserker.git
fetch = +refs/heads/*:refs/remotes/gitlab/*
[remote "github"]
url = git@github_wan:hellozhaolq/test.git
fetch = +refs/heads/*:refs/remotes/github/*

接下来就可以 push 、 pull 了。

配置用户名和邮箱

Git 为每一次提交都记录提交者的名字和电子邮箱地址,所以 commit 前需要配置用户名和邮箱地址。

1
2
3
4
5
6
7
8
git config --local user.name 'JonSnows'
git config --local user.email JonSnows@qq.com

git config --global user.name 'JonSnows'
git config --global user.email JonSnows@qq.com

git config --system user.name 'JonSnows'
git config --system user.email JonSnows@qq.com

创建仓库

然后我们在 github 上创建一个新的仓库

1570087618207

创建成功后,显示信息如下:

1570087727377

以上信息告诉我们,可以在命令行上创建新的存储库、可以从命令行推送现有存储库、可以从另一个存储库导入代码。

检出仓库

执行如下命令以创建一个本地仓库的克隆版本:

1
2
3
4
5
git clone [url]

git clone https://github.com/username/RepositoryName.git

git clone https://github.com/username/RepositoryName.git CustomName

具体参照 git clone 命令。

添加远程库

要添加一个新的远程仓库,可以指定一个别名,以便将来引用,命令格式如下:

1
2
3
4
git remote add [alias] [url]
git remote add origin git@github.com:Jon-Snows/git-test.git

git remote remove origin # 删除别名

远程仓库必须在 Github 中建立,如果添加的远程仓库不存在,push 时会提示如下错误:

1
2
3
4
5
6
7
$ git remote add origin2 git@github.com:Jon-Snows/git-test2.git		#此仓库链接不存在
$ git push origin2 master
ERROR: Repository not found.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

在本地仓库下运行命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ mkdir git-test                     # 创建测试目录
$ cd git-test/ # 进入测试目录
$ echo "# Git 教程 测试" >> README.md # 创建 README.md 文件并写入内容
$ ls # 查看目录下的文件
README.md
$ git init # 初始化
$ git add README.md # 添加文件
$ git commit -m "添加 README.md 文件" # 提交并备注信息
[master (root-commit) 0205aab] 添加 README.md 文件
1 file changed, 1 insertion(+)
create mode 100644 README.md

# 提交到 Github
$ git remote add origin git@github.com:Jon-Snows/git-test.git
$ git push -u origin master

接下返回 Github 创建的仓库,就可以看到文件已上传到 Github上:

1570088832319

查看远程库

查看本地添加的全部远程库

1
2
3
4
5
6
7
$ git remote
origin

# 执行时加上 -v 参数,你还可以看到每个别名的实际链接地址。
$ git remote -v
origin git@github.com:Jon-Snows/git-test.git (fetch)
origin git@github.com:Jon-Snows/git-test.git (push)

查看远程仓库

如果想要查看某一个远程仓库的更多信息,可以使用 git remote show [remote-name] 命令。 如果想以一个特定的缩写名运行这个命令,例如 origin,会得到像下面类似的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Administrator@JonSnows MINGW64 /d/git/git-test (testing)	# testing 是当前所在分支
$ git remote show origin
* remote origin
Fetch URL: git@github.com:JonSnows/git-test.git
Push URL: git@github.com:JonSnows/git-test.git
HEAD branch: master
Remote branches:
master tracked
testing tracked
testing2 tracked
Local branch configured for 'git pull':
master merges with remote master
Local refs configured for 'git push':
master pushes to master (up to date)
testing pushes to testing (up to date)

这些信息非常有用,它告诉我们:

  1. 远程仓库的 URL。
  2. 远程仓库的全部 branch。
  3. 如果运行 git pull,就会抓取所有的远程引用,然后将远程 master 分支合并到本地 master 分支。
  4. 如果在特定的分支上执行 git push 会自动地推送到哪一个远程分支。
  5. 哪些远程分支不在本地,这里是 testing2 分支。

更新并合并

提取远程仓库的两种方式:一、git fetch + git merge。二、git pull。

git fetch + git merge

Git 有两个命令用来提取远程仓库的更新。

1、从远程仓库抓取新推送的所有工作,然后将数据拉取到本地仓库, 它并不会自动合并或修改当前的工作。

git fetch命令

1
2
3
4
5
git fetch [远程主机alias] # 要更新所有分支,命令可以简写为:git fetch

# 默认情况下,git fetch取回所有分支的更新。如果只想取回特定分支的更新,可以指定分支名,如下所示 -
git fetch <远程主机alias> <分支名>
git fetch origin master

该命令执行完后需要执行 git merge 合并远程分支到当前分支。

2、从远端仓库提取数据并尝试合并远程分支到当前分支:

链接: Git 分支 - 分支的新建与合并

1
git merge [alias]/[branch]

该命令就是在执行 git fetch 之后紧接着执行 git merge 合并远程分支到当前所在的任意分支。

如果 git merge 操作没有需要解决的分歧,就叫做 “快进(fast-forward)”。即当试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移)。

测试:

接下来我们在 Github 上点击” README.md” 并在线修改它:

1570090003755

然后在本地更新修改。

1
2
3
4
5
6
7
$ git fetch origin
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:Jon-Snows/git-test
a531b58..560b6f6 master -> origin/master

以上信息 a531b58..560b6f6 master -> origin/master 说明 master 分支已被更新,我们可以使用以下命令将更新同步到本地:

1
2
3
4
5
$ git merge origin/master
Updating a531b58..560b6f6
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)

查看 README.md 文件内容:

1
2
3
$ cat README.md
# Git 教程 测试
第一次修改内容

Git 分支 - 变基 (Merge 与 Rebase)

链接: Git 分支 - 变基只会用 git pull ?有时候你可以尝试更优雅的处理方式

变基:改变基础。将复杂难懂、眼花缭乱的多分支提交历史,整合为一条直线,变基使得提交历史更加整洁。

使用前先了解: 变基的风险用变基解决变基变基 vs. 合并

如果习惯使用 git pull ,同时又希望默认使用选项 --rebase,请参考以下配置:

1
2
3
4
# 更改pull.rebase的默认配置
git config --global pull.rebase false # merge (the default strategy)
git config --global pull.rebase true # rebase
git config --global pull.ff only # fast-forward only 仅快进

git pull

链接: git pull命令

从服务器上抓取全部数据并自动尝试合并远程默认分支当前分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# git pull = git fetch + git merge

$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:Jon-Snows/git-test
941cd6c..65331da master -> origin/master
Updating 941cd6c..65331da
Fast-forward
test.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

要合并其他分支到你的当前分支(例如 master),执行:

1
git merge (branchname)

以上两种情况下,git 都会尝试去自动合并改动。遗憾的是,这可能并非每次都成功,并可能出现冲突(conflicts)。 这时候就需要修改这些文件来手动合并这些冲突(conflicts)。改完之后,需要执行如下命令以将它们标记为合并成功:

1
git add <file>

在合并分支之前,可以使用 git diff 命令预览差异:

1
2
# 将target_branch分支合并到source_branch分支前预览差异
git diff <source_branch> <target_branch>

补充:

1
2
3
4
5
6
7
8
9
# git pull [options] [<repository> [<refspec>…]]
# git pull <远程主机名> <远程分支名>:<本地分支名>

git pull origin master:master # 取回origin主机的master分支,与本地的master分支合并。
git pull origin master # 如果远程分支(master)要与当前分支合并,则冒号后面的部分可以省略。
git pull origin # 如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名。
git pull # 如果当前分支只有一个追踪分支,连远程主机名都可以省略。该命令表示当前分支自动与唯一一个追踪分支进行合并。

git pull --rebase <远程主机名> <远程分支名>:<本地分支名> # 如果合并需要采用rebase模式,可以使用–rebase选项。

还原/回退/撤销

工作区

假如操作失误(当然,这最好永远不要发生),可以使用如下命令还原:

1
git checkout -- <file>

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

1、 readme.txt 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态,即使用 HEAD 中的最新内容替换掉工作区的文件内容;

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

此命令无法还原暂存区的改动,且对新文件无效。

取消缓存使用 git reset HEAD,见上文。

本地

假如想丢弃在本地的所有改动与提交(工作区、暂存区、版本库),可以到服务器上获取最新的版本历史,并将本地主分支指向它:

1
2
3
4
# 丢弃所有本地改动:工作区、暂存区、版本库 这些都是本地操作
git fetch origin
git reset --hard origin/master
git reset --hard <版本号 commit id>

git reset –-soft:回退到某个版本,只取消了commit的信息,代码更改并没有取消,不会恢复到index file一级。如果还要提交,直接commit即可;
git reset -–hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,撤销的commit中所包含的更改被冲掉;

推送到远程仓库

链接: git push命令git push.default 简析

默认配置项

1
2
3
4
5
6
7
8
9
10
# 2.0以前的默认配置项
git config --global push.default matching
# 2.0中的默认配置项
git config --global push.default simple
# 修改默认配置
git config --global push.default nothing # 直接push会出错,需要显式的指出推送的远程分支,例如:git push origin master

# 查看配置
git config --list
git config --system -l

推送新分支与数据到某个远端仓库命令:

1
git push [alias] [branch]

以上命令将 [branch] 分支推送成为 [alias] 远程仓库上的 [branch] 分支,实例如下。

1
2
3
4
5
6
7
8
$ touch test.txt      # 添加文件
$ git add test.txt
$ git commit -m "提交添加的文件"
master 69e702d] 提交添加的文件
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 runoob-test.txt

$ git push origin master # 推送到 Github

重新回到 Github 仓库,可以看到文件已经推送上来了:

补充:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# git push <远程主机名> <本地分支名>:<远程分支名>

git push origin master # 将本地的master分支推送到origin主机的master分支。如果master不存在,则会被新建。

git push origin :master # 如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。
# 等同于
git push origin --delete master

git push # 如果当前分支只有一个追踪分支,那么主机名都可以省略。
git push --all origin # 将本地的所有分支都推送到远程主机
git push origin --tags # git push不会推送标签(tag),除非使用–tags选项。

git push origin HEAD # 将当前分支推送到远程的同名的简单方法
git push origin HEAD:master # 将当前分支推送到源存储库中的远程引用匹配主机。 这种形式方便推送当前分支,而不考虑其本地名称。

git push origin lbranch-1:refs/rbranch-1 # 推送本地分支lbranch-1到新的远程分支rbranch-1:
git push origin lbranch-2:refs/rbranch-1 # 推送lbranch-2到已有的rbranch-1,用于补充rbranch-1:
git push -f origin lbranch-3:refs/rbranch-1 # 用本地分支lbranch-3覆盖远程分支rbranch-1

git push origin HEAD:refs/heads/master #
git push origin HEAD:refs/for/master # refs/for :意义在于我们提交代码到服务器之后是需要经过 code review 之后才能进行merge

删除远程仓库

1
git remote rm [alias]

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git remote -v
origin git@github.com:Jon-Snows/git-test.git (fetch)
origin git@github.com:Jon-Snows/git-test.git (push)

# 添加仓库 origin2
$ git remote add origin2 git@github.com:Jon-Snows/git-test.git

$ git remote -v
origin git@github.com:Jon-Snows/git-test.git (fetch)
origin git@github.com:Jon-Snows/git-test.git (push)
origin2 git@github.com:Jon-Snows/git-test.git (fetch)
origin2 git@github.com:Jon-Snows/git-test.git (push)

# 删除仓库 origin2
$ git remote rm origin2
$ git remote -v
origin git@github.com:Jon-Snows/git-test.git (fetch)
origin git@github.com:Jon-Snows/git-test.git (push)

服务器上的 Git

见官网,Git传输的四种协议、Git服务器搭建、生成SSH秘钥、配置服务器、Git守护进程等

在其它环境中使用 Git

见官网,图形界面工具、各种开发工具中的 Git 。

网上还有很多图形界面工具。