Git 和 GitHub 学习笔记

版本控制

版本控制(Revision Control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。

在使用版本控制软件之前,我们往往使用文件和文件夹配合后缀版本编号来管理版本的迭代,而版本控制软件则是可以帮助我们实施高效科学版本控制的一种工具。

版本控制分类

  • 本地版本控制:记录文件每次的更新,可以对每个版本做一个快照,或是记录补丁文件,仅仅适合个人使用,几乎已经被淘汰。如RCS(Revision Control System)。
    img

  • 集中版本控制:有一个单一的集中管理服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。常见的例如 SVN(Subversion),CVS(Concurrent Versions System)。

140221095037232

  • 分布式版本控制:相比集中版本控制客户端并不只是提取最新的文件快照,而是把原始的代码仓库完整地镜像下来,所有版本信息仓库全部同步到本地的每个用户,这样就可以在本地查看所有版本历史,可以离线在本地提交,只需在连网时 push 到远程仓库即可。Git 就是一个典型的分布式版本控制系统。
    img

  • 集中式和分布式版本控制的区别:

    • 集中式所有的版本数据都保存在服务器上,客户机只有某个特定版本的特定文件,依赖通信环境以及服务器的高可靠性。
    • 分布式每个用户都有服务器所有版本的副本,不依赖网络和中央服务器,但是增加了本地存储空间的占用,Git 通过快照索引大大减少了空间的占用。

Git 的安装和配置

安装

进入官网下载对应的版本一路 next 即可,安装成功后在资源管理器任意目录空白处鼠标右键可以看到 Git Bash Here 的选项,可以很方便的在任意路径打开 Bash 终端。

image-20201125205733637

因为 Git 这款软件上是从 Linux 移植而来,通过安装 Git for Windows 我们不仅获得了 Git 这个工具,还拥有了一个 Git Bash 终端以及一些移植而来的 Linux 命令,这让我们可以在 Git bash 终端中运行一些常见 Linux 命令工具,因此建议以后使用 Git Bash 终端来进行 Git 相关操作,顺便还可以熟悉常用的 Linux 命令。

配置

Git 在安装目录(系统配置),用户主目录下(全局配置),仓库目录下(项目配置)存在三级配置文件。 配置文件名为.gitconfig.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#查看当前的 Git 配置
git config --list

#查看系统 config
git config --system --list
  
#查看当前用户(global)配置
git config --global --list

#编辑当前仓库的 Git 配置文件
$ git config -e

#编辑当前全局的 Git 配置文件
$ git config -e --global

当你安装 Git 后首先要做的事情是设置你的用户名称和 e-mail 地址。
如果查看配置中没有 user.nameuser.email 项,需要使用以下命令设置用户名与邮箱,每次 Git 提交都会附带上这些信息。

1
2
git config --global user.name <user name>
git config --global user.email <user name>

Git 操作

Git 中的四大区域

img

  • Workspace:工作区,对应着资源管理器中的一个目录,这个目录由 Git 进行管理
  • Index / Stage:暂存区,用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息
  • Repository:仓库区(或本地仓库),里面有本地提交的所有版本的数据。其中存在一个 HEAD 指针指向当前开发的分支最新更改,不一定指向最新的提交,因为可能回滚。
  • Remote:远程仓库,托管代码的服务器中的仓库。

四个区域的转化关系如图,History 对应的就是 Repository 区。

img

创建仓库

新建一个需要 Git 进行管理的项目目录,鼠标右键点击 Git Bash Here,在此处打开 Git Bash 终端。

  1. 使用git status先查看一次当前目录的 git 状态,命令行错误提示当前目录这不是一个 git 仓库,就是改目录还没有被 git 管理起来。

  2. 输入git init使用初始化命令将目录初始化,初始化本地仓库时,会在当前目录创建一个名为 .git 的隐藏文件夹,里面存放了一些 git 相关的文件。

  3. 输入git status再查看一次当前目录的 git 状态,命令行提示当前我们在 Git 为我们自动创建的 master 分支上,并且没有当前没有任何提交,说明当前目录已经被 git 管理起来了。
    image-20201126105322809

  4. 使用 touch demo1.txt 在当前目录新建一个文件,用git status查看 git 状态,显示我们刚刚新加的文件没有被跟踪(Untracked)。

  5. 使用 git add . 将该目录所有文件跟踪起来,并提交暂存区,然后再用git status查看 git 状态,提示有一个更改待提交。因为刚才只是把文件添加到暂存区,还没有提交到本地仓库。
    image-20201126110128293

  1. 使用git commit -m "create demo1.txt",将暂存区中的修改提交到远程仓库 master 分支,-m 后面的字符串消息用来提示本次提交的修改,不可省略。
  2. 再使用git status查看工作区状态,提示没有更改要提交,这样就完成了一个完整创建仓库初始化,创建新文件并提交的流程。
    image-20201126111336942

日志

  1. 使用touch再创建几个新文件并通过git addgit commit,提交到本地仓库 master 分支,这样就有了多次提交记录。
    image-20201126120803460
  2. 使用git log可以查看当前分支的提交日志,按提交日期由近及远打印,最上面对应的是最近一次提交。其中 commit 后面的一长串字符串是该次提交的 commit id,通过这个 id 索引可以得到该次提交发生时整个仓库的文件内容。 当日志记录过多的时候一页展示不下时会自动分页,终端左下角会有一个冒号,按「空格」键下一页,按「b」上一页,按「q」退出查看,到尾页左下角显示(END)。
    image-20201126120826493
  3. 使用git log --oneline查看精简后的日志,以一行显示。
  4. 使用git reflog查看所有分支的历史提交精简日志,HEAD@ 后面大括号中的数字是当前开发分支和历史更改间的修改次数。这个命令而不仅仅是当前分支的历史提交,还显示整个仓库中其他分支历史提交,因为我们没有创建其他分支所以还是只有 3 条记录。
    image-20201126122357668

回滚

会滚到暂存区

  1. 如果我们在工作区的某个文件进行了一些修改操作,但还没有提交到暂存区中,想回滚到暂存区之前的状态,可以使用git checkout <file name>回滚指定文件。也可以使用git checkout .回滚整个文件夹,注意:这个操作会清除工作区中所有未添加到暂存区的改动。
    image-20201126132124585

回滚到某次提交

  1. 如果修改只是通过 add 添加到了暂存区,但还没有提交到本地仓库,可以使git reset --hard HEAD,因为此时 HEAD 指针还没有移动,可以直接回滚到 HEAD 指针处。

  2. 如果已经提交到本地仓库,则要使用git reset --hard后面跟上 commit id 就可以回滚到指定版本,一般只输入 commit id 的前几位就足够区分该 id,没必要输入完整的 commit id,一般输入前 7 位,最少要输入 4 位。git reset --hard中 hard 参数会让本地库中 HEAD 指针移动的同时,重置本地仓库、暂存区和暂存区,而 mixed 参数只让本地仓库和暂存区进行回滚,soft 参数只让本地仓库重置,使用最多的就是 hard 参数。
    image-20201126123405823

  3. ls查看当前目录下的文件,发现仓库已经回滚到第一次提交时的状态,只剩下 demo1.txt 文件通过git log也只能查看到第一次提交历史。也就是说连日志记录也回滚了,回滚前的一部分日志也消失了,要查看完整的回滚日志记录,应该像上图使用git reflog
    image-20201126123756270

  4. 前进版本,和回滚一样,利用git reset --hard加上 commit id 也可以前进到回滚前某一次提交的版本。
    image-20201126124647709

    比较

  5. 使用git diff [file name]将工作区的文件内容和暂存区中文件内容做按行比较,显示差异,后面加上文件名只比较指定文件。
    image-20201126143234024

  6. 使用git diff <commit id | HEAD> [file name]将工作区和某次提交时的本地仓库中进行按行比较。
    image-20201126142142484

  7. 使用git checkout 将工作区、暂存区,与 HEAD 指向本地库中的文件内容做汇总比较,看是否已经全部同步。

分支

创建一个新的分支,相当于把原来分支的内容复制了一份到新分支上,可以在新分支上做各种功能的开发,等功能开发完毕可以再选择合并到原来的分支,这样便可以在不影响原分支的情况下添加各种新功能,这样可以多个分支并行开发,互不影响,提高开发效率。
img

新建分支

  1. 使用git branch [-v]查看所有本地分支,分支前面的「*」代表当前所在分支。
  2. git branch <branch-name> [commit-id] 从指定提交处创建一个新的分支,后面没有跟 commit id 则默认从 HEAD 指针处创建一份新的分支。
  3. git checkout <branch-name> 切换到指定分支。
    image-20201126151411013
  4. 在 dev 分支中新建一个 demo4.txt 文件,add 到暂存区后 commit,可以看到 HEAD 指针已经指向了当前开发分支的最新修改。
    image-20201126152247294

合并分支

  1. 切换回 master 分支,dev 分支中的修改并没有影响到 master 分支。
    image-20201126152547025
  2. 使用git merge <branch name>将另外一个分支合并到当前分支。
    image-20201126153034605

合并冲突

  1. 当两个分支对了同一文件的同一行位置做了不同的修改时,直接合并这两个分支就会产生冲突,分支会变成 MERGING 状态。
    image-20201126154608101
  2. 提示合并出现冲突后,工作区的冲突文件其实已经被修改,但这个文件中保留了两个分支中的冲突代码,并通过分隔符来告诉发生冲突的代码分别属于哪个分支,这时候就需要手动修改冲突的文件来解决冲突。
    image-20201126155056631
  3. 修改后先 add 冲突的文件,来告诉 Git 系统已经解决把冲突解决,然后再 commit 整个工作区,合并才是真正的完成,MERGING 状态消失。注意:不能只单独 commit 冲突的文件。image-20201126160111169

GitHub

前面我们只是把 Git 当作一个本地版本控制系统来使用,接下来学习和远程仓库相关的操作。GitHub,Gitee 给我们提供免费的远程仓库托管服务,我们可以在上面免费创建远程仓库,也可以选择选择自己搭建 GitLab 这样的代码托管平台。进行接下来操作之前先到 GitHub 注册一个账号。

将本地仓库上传到远程仓库

  1. 初始化一个本地仓库
  2. 在 GitHub 上新建一个空的远程仓库,仓库名建议和本地工作区目录名一样,这样可以让我们方便把本地的工作区目录和远程的仓库对应起来。
  3. 在 GitHub 上打开仓库可以得到远程仓库的 https 地址
    image-20201126161814967
  4. 给远程仓库的 https 地址取一个别名,常用为 origin,方便后面的操作
    image-20201126162243228
  5. git push命令向远程仓库推送本地仓库的内容,第一次git push 会弹出一个窗口让在先登录 GitHub 账号,之后 Windows 会保存凭据自动登录,如果想要推送到远端另一个账号需要先在 Windows 凭据管理器删除原先的凭据。
    image-20201126162749682
  6. 打开 GitHub 对应仓库页面,本地仓库的内容已经同步了上去。
    image-20201126163609357

将远程仓库同步到本地仓库

  1. 打开一个 GitHub 仓库页面,找到仓库的 clone 地址并复制
    image-20201126163709535
  2. 任意目录下右键打开 Git Bash 终端,使用git clone命令来克隆远程仓库到本地,并自动创建远程仓库链接的别名
    image-20201126164201012
    image-20201126164549289

团队开发

image-20201125161147659

  1. 项目经理在 GitHub 的仓库设置中向其他团队成员的 GitHub 账户发送邀请链接。
    image-20201126165218019
  2. 被邀请用户登录 Github 打开邀请链接同意邀请成为协作者。
  3. 协作成员 clone 整个项目到本地后在本就可以直接在本地开发,完成后可以直接利用自己的 GItHub 直接 push 到该仓库中。

远程冲突

  1. 由于其他协作开发者的对同一文件的更改,可能导致将本地库 push 到远程仓库时产生冲突,这时候远程仓库会拒绝 push。
  2. 需要先把远程仓库 pull 下来,会和本地仓库自动合并,产生冲突文件,修改好冲突文件后,经过 add 和 commit 操作后再 push 到远程仓库。
  3. 为了尽可能的避免冲突每次开发前应该先把远程的仓库 pull 到本地更新完本地仓库后再进行开发,当然更为谨慎的做法是先 fetch 远程库,在checkout 进入远程库,确认改动后再手动 merge,pull = fetch + merge。

跨团队合作

  1. 先在 GitHub 上 fork 一份目标远程仓库,然后 clone 到本地。至于 clone 上游仓库还是自己的账户里面的 fork 下来的仓库无所谓。使用diff命令比较两次不同地址 clone 下来的文件除了 .git 目录中记录的 clone 日期和 url不一样,其余文件均一样,当然前提是 fork 下来的仓库和原来的上游仓库是同步的,没有落后(behind)版本。GitHub 中 fork 的仓库是原来仓库 fork 时的副本,相当于快照,不会自动更新,需要手动在 fetch 原始上游仓库后合并到本地,再push回远程。详见Syncing a forkGitHub设置fork仓库和原始仓库同步
  2. 因为没有权利提交到上游仓库,每一个小版本提交到自己账户下 fork 而来的仓库进行开发。
  3. 开发完毕后在 GitHub 上创建一个新的 Pull Request。
  4. 等上游审核完毕后,就可以把代码合并进原来的项目了

使用 SSH 连接到远程仓库

  1. 先使用cd ~切换到当前用户目录,然后输入命令ssh-keygen -t rsa -C "HP-OMEN",按三次回车确认,会在用户目录生成一个.shh的文件夹,里面存放了公钥和私钥。 -C 后面的参数是生成的秘钥的的注释(comment to identify the key),相当于是这串钥匙的名字,可以任意,不一定为邮箱。
  2. 使用命令cat ~/.ssh/id_rsa.pub打开公钥文件,复制整行,粘贴进GitHub 中。的个人 setting 页面,左侧找到 SSH and GPG keys 选项,点击 New SSH KEy 按钮,进去粘贴公钥,并命名,完成公钥绑定。
    image-20201125173736244
  3. 使用命令ssh -T git@github.com测试是否配置成功。第一次连接到陌生主机需要输入 yes 后回车确认,配置成功后可以使用 ssh 的方式来访问远程仓库。
    image-20201125174610157
  4. 通过 ssh 地址连接远程仓库 push 操作不用输入密码,缺点是一台电脑只能连接到远端的一个GitHub账户。

在 IDEA 中集成 Git 操作

  1. 打开 IDEA 设置界面,配置 Git 的安装路径。
    image-20201125175556213

  2. 使用 Git 管理该项目并进行本地库的初始化操作,在 IDEA 项目根目录会生成一个 .git 目录
    image-20201126180751582

  3. 也可以在 IDEA 中利用 Git 从远程仓库直接克隆一个项目,输入GitHub 仓库地址,或者绑定自己的 GitHub 账号,就可以直接在下拉框中选择自己账号中的仓库clone,点击 clone,等待一会 IDEA 会提示是否在新窗口中打开 clone 下来的项目。
    image-20201125183134274
    image-20201125193050152

  4. 在 IDEA 设置界面中,可以取消 Git 对该项目的管理。
    image-20201126181027784

常用 git 命令补充

下面列举了工作中常用到的 git 命令,更多 git 命令请参考阮一峰:常用 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#查看工作目录的状态;
git status

#查看工作目录的状态的精简信息,s=summary,总结,b=braanch,分支
git status -sb

#让 git 开始管理当前目录并初始化
git init

#将指定文件提交到暂存区;用通配符「*」可以把当前目录下所有文件添加到暂存区
git add <file name>...

#将当前目录的所有文件到暂存区,效果同 git add *
git add .

#提交整个暂存区到本地仓库,message 这次提交的备注信息,必须输入,没有直接输入,则会自动打开一个空文本用来输入备注信息
git commit -m <message>

#查看历史提交日志
git log

#显示当前分支的版本提交历史,但每次提交以一行显示
git log --pretty=oneline

#显示当前分支的版本提交历史,但每次提交以一行显示,并且 commit-id 只显示前面 7 位
git log --oneline

#显示整个仓库所有分支的最近提交历史,还包括每个提交距离工作分支的距离,每次提交以一行显示,并且 commit-id 只显示前面 7 位
git reflog

#将本地库的指针指定指向 commit-id 这次提交,重置这次暂存区与工作区,不指定 commit 默认与最近一次 commit 保持一致;
git reset --hard [commit-id]

#恢复某个commit的指定文件到暂存区和工作区
git checkout <commit id> <file>

#恢复暂存区的所有文件到工作区
git checkout .

#恢复暂存区的指定文件到工作区
git checkout <file>

#将工作区和暂存区的进行按行比较,显示暂存区和工作区的差异
git diff

#显示工作区与当前 HEAD 分支之间的差异
git diff HEAD

#列出所有本地分支,分支前面的「*」代表当前所在分支
git brach

#新建一个分支,但依然停留在当前分支
git branch <branch-name>

#新建一个指向指定 commit 的分支,
git branch <branch-name> <commit-id>

#切换到指定分支,并更新工作区
git checkout <branch-name>

#合并指定分支到当前分支,使用前应该配合 checkout 切换到合适的分支
git merge <branch-name>

#查看远程仓库,还可以用来看仓库的别名
git remote -v

#给远程某一个仓库添加地址别名,只在当前仓库配置中生效
git remote add <alias> <url>

# 取回远程仓库的变化,并与指定分支合并
git pull [url | alias] <branch name>

#从远程克隆一个项目和它的整个代码历史
git clone [url]

#将本地指定分支往远程仓库推送
git push [url | alias] <branch name>


#把远程仓库的内容拉取到本地创建一个副本仓库,这个仓库是远程仓库的一个拷贝,区别仅仅是已经下载到本地而已,不会自动更新工作区,需要手动使用 merge 命令来手动把本地仓库的某个分支和已经下载到本地的远程仓库副本的某个分支合并在一起达到 pull 的效果。pull = fetch + merge,工作中最好先fetch 下来,使用 diff 比较检查两个分支再 merge,而不是直接pull。
git fetch [url | alias]

#如果在本地已经有了仓库,并且里面有一些文件,想把远程仓库的文件添加进来,由于两个仓库没有共同的祖先,需要进行无关联拉取。
git pull [url | alias] master --allow-unrelated-histories

#删除指定的远程分支
git push origin --delete master

#冒号前不指定本地分支,就推送一个空的本地分支到远程分支,也可以达到上面一条删除的效果
git push origin :master

参考

  1. B站视频教程:赵姗姗教你玩转Git
  2. 狂神聊git
  3. 阮一峰:常用 Git 命令清单

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!