# GIT_Document **Repository Path**: btchc/GIT_Document ## Basic Information - **Project Name**: GIT_Document - **Description**: GIT - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 6 - **Created**: 2016-12-17 - **Last Updated**: 2021-08-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Git 版本控制

[ 本文档由黄超(Seeker),使用 MarkDown 构建而成,转载请注明出处 ]

### 1. 什么是 Git > Git 是当今世界上最先进/最好用的分布式版本控制系统,没有之一 ### 2. 什么是版本控制系统? > #### 版本控制 --- 《维基百科》 > 版本控制是一种软件工程技巧,能在软件开发的过程中,确保由不同人所编辑的同一个代码文件都得到同步. > 版本控制能使项目的设计者,将项目恢复到之前任意的状态,这种选择权在设计过程中特别重要. > 理论上所有的信息记录都可以加上版本控制:利用版本控制来追踪、维护源码、文件以及配置文件等等的改动 ---- ### 3. 版本控制发展史 #### 3.1 文件名方式 早年的软件开发过程,代码管理以手动和邮件等形式,文件命名及保存存在问题 > 如图 : 文件命名方式 > > ![文件命名](./imgs/文件命名.png) > 如图 : 毕业论文版本 > > ![文件名控制版本](./imgs/01.jpg) #### 3.2 集中式 包括库和工作区两部分:在工作区编码,然后上传至库的方式,完成多人协作。 问题:工作机与库机,需要联网才能控制版本,传输速度较慢。 ![集中式版本控制](./imgs/02.png) #### 3.2 分布式 每台电脑都有,工作区和库,可以自己控制版本。数据更加安全,有逻辑上的中心。 ![分布式版本控制](./imgs/03.png) ---- ### 4. 常见版本控制系统 > 如图 : 版本管理器的发展史 ![版本管理器的发展史](./imgs/04.png) > 这张图上,分成了四个时期 : > ● 史前时期:1982年的RCS。现在你可能还能在Unix的发布包中找到它。 > ● 古典时期:1990年的CVS自身缺陷已经过时;1985年的PVCS、1992年的clearcase(费用昂贵、功能复杂沿用至今);微软VVS反人类;Perforace(广泛,谷歌内部最大代码管理器) > ● 中世纪时期:SVN解决了CVS的问题,集中式领域王者。AccuRev(支持分支合并让很多公司拜托cvs和clearcase)。 > ● 文艺复兴:BitKeeper(SUN公司大量使用),2002年Linux内核使用BitKeeper,2005年闭源时有人试图破解BitKeeper,于是出现了Git。 #### Git 问世 Git的第一个版本是Linux之父Linus Torvalds亲手操刀设计和实现的(两周内 用C写完),Linus不仅仅给出一个原始设计,并在向世人介绍Git时强烈批评了CVS和SVN等,Git消除了分支和合并的恐惧。很多大型开源项目由SVN迁移至Git。 2008年[GitHub](https://github.com/)也成为世界最大的SCM系统(软件配置管理),它使用的就是Git版本库的技术.从此Git成为版本控制系统的主流。 GitHub上的著名项目 : Linux内核、安卓、jQuery、Bootstrap、Ruby ... ---- ### 5. Git 的安装 和 基本配置 #### 5.1 安装 > Linux 安装 > > 二进制包(在线) > > yum -y install git // RedHat系列 > > apt-get git install // Debian系列 > > 源码包(官网下载) > Windows 安装 > > Git 在 Windows 使用模拟环境 msysgit > > 下载地址: > > [https://git-for-windows.github.io/](https://git-for-windows.github.io/) > > 注意:如果想让 windows 作为 git 服务器则需要搭建ssh服务。 ---- > 本教程使用 windows 版本来做演示 : > 安装步骤: (如果你不熟悉每个选项的意思,请保持默认的选项) - 以`管理员方式`运行安装包,选择 `是` 安装 ![安装00](./imgs/git00.png) - 同意协议条款 ![安装01](./imgs/git01.png) - 选择安装选项: 安装Git Bash和Git GUI,创建图标(可选) ![安装02](./imgs/git02.png) - 选择 Git 的默认编辑器 (默认为vim,可选取自己熟悉的编辑器) ![安装03](./imgs/git03.png) - 选择: `git form the command line and also form 3rd-party software` (GIT命令行和第三方软件) ![安装04](./imgs/git04.png) - 选择: 选择 OpenSSL 传输 HTTPS 的信息 ![安装05](./imgs/git05.png) - 选择 : 换行符默认为 windows 环境为主 ![安装06](./imgs/git06.png) - 选择 : MinTTY 默认终端 ![安装07](./imgs/git07.png) - 选择 : 系统文件缓存 和 git 的管理器(需要.net v4.5.1) ![安装07](./imgs/git08.png) - 实验选项,暂不选择 ![安装09](./imgs/git09.png) - 点击 Install 安装,开始安装 ![安装10](./imgs/git10.png) - 安装完成,选择运行`git bash`工具 ![安装11](./imgs/git11.png) - Git 运行界面 (命令行提示的是 你电脑的用户名) ![安装12](./imgs/git12.png) ---- #### 5.2 配置 > 无论 Linux 还是 Windows,安装完成后都要初始化 ```sh # 查看 git 版本 git --version # 查看配置 git config -l # 设置用户名和邮箱 git config [--global] user.name "Your Name" git config [--global] user.email "your@email.com" # 中括号内的参数: # --local 本地 # --system 系统 # --global 全局 # 无参,则为当前库配置身份 # 如: git config --global user.name "seeker" git config --global user.email "3300696254@qq.com" ``` ### 6. Git常用操作 #### 6.1 生成新的版本库 1. 新建空目录 2. 进入该目录---单击右键---选择 `Git Bash Here` 3. 弹出 git 的命令行工具 4. 初始化该目录为版本仓库,键入 `git init` 5. 显示 `Initialized empty Git repository in xxxxxx` 6. `ls -a` 查看该目录下出现`.git`的隐藏目录,即版本库 7. 初始化完成 #### 6.2 添加文件到版本库 1. 新建文件 `1.txt` 2. 查看当前版本状态 ```sh # 查看当前版本状态 git status ``` 3. 红字提示有文件未跟踪(未加入版本控制) 4. 在命令行内输入以下,添加文件至版本库: ```sh # 添加文件至缓存区 git add 1.txt # 再次查看版本库状态 git status # 提示有要提交的修改,有一个新增的文件 # 提交文件至版本库 # 直接提交 # 回车后,会打开指定的编辑器编写描述信息 git commit # 填写完描述信息,保存关闭即可 # 或者带参数 -m 直接写入提交的描述信息 git commit -m "新增1.txt" # 新建 2.txt / 3.txt # 多文件添加 git add 2.txt 3.txt # 将缓存区的多次添加一次提交 git commit -m "新增2.txt+3.txt" # ps: # 添加所有改动过的和新增的文件到缓存区 (不包括被删除的文件) git add . # 添加所有文件到缓存区 git add --all git add -A ``` 5. 执行完后,提示提交完成 6. 查看提交后的版本库状态: ```sh $ git status On branch master nothing to commit, working tree clean ``` 7. 至此,最简单的添加文件,到版本库的操作已完成 ---- #### --- PS部分: Git 实现原理 --- 1. 工作区 : 就是程序员日常编写代码的文件夹 2. 版本库 : 负责代码版本控制,就是`.git`隐藏目录 3. 版本库原理图 : ![版本库原理](./imgs/05.png) 版本库包括:暂存区(index/stage),HEAD(指针),分支(默认为 master 主分支)等。 文件提交至版本库总共分两步: 1. `git add filename` # 添加至 stage 缓存区 2. `git commit -m "描述"` # 将 stage 的内容提交至版本库的 master 分支 > 实验: > 1. 修改文件 >>> 查看状态 > 2. 添加到缓存区 >>> 查看状态 > 3. 再次修改 >>> 提交 >>> 查看状态 实验过程如下,观察理解 Git 实现机制: 1. 修改文件 >>> 查看状态: - 修改`1.txt`文件的内容,添加一行`111`,查看状态:`git status` - 提示:`Changes not staged for commit:` (工作区的修改,还没有提交到缓存区) - 显示红字,`modified: 1.txt` 2. 添加到缓存区 >>> 查看状态: - 添加到缓存区`git add .` - 查看状态:`git status` - 提示:`Changes to be committed` (要提交的修改。表示已提交到缓存区,待提交到版本库) - 显示绿字:`modified: 1.txt` 3. 再次修改 >>> 提交 >>> 查看状态 - 继续修改文件:`1.txt`文件的内容,添加一行`222` - 提交版本库:`git commit -m "第1次修改 1.txt"` - 再次查看状态:`git status` - 提示:`Changes not staged for commit`(表示工作区中,还有修改记录,没有被提交到版本库之中) - 显示红字:`modified: 1.txt` 说明: - 第 3 步的提交,只是提交了缓存区内,已经缓存过的内容(即,第二步的`add .`操作) - 第 3 步中的第二次修改文件内容的记录,并没有添加到缓存区,所以版本库与工作区文件不一致 - 此时需要将第二次的修改记录,添加到缓存区,再次提交即可 ```sh git add. git commit -m '第2次修改 1.txt' git status ``` ---- #### 6.3 查看文件修改状态相关 ```sh # 查看当前版本状态(是否修改) git status # 状态简览 git status -s git status --short # 以上命令,将得到一种更为紧凑的格式输出 # ?? 标记表示: 新添加的未跟踪文件 # A 标记表示: 新添加到暂存区中的文件 # M 标记表示: 修改过的文件 # ` M` 表示:(出现在右边)该文件被修改了但是还没放入暂存区 # `M ` 表示:(出现在左边)该文件被修改了并放入了暂存区 # `MM` 表示:(出现两个)暂存区和工作区都有该文件被修改了的记录 ``` 修改文件,测试区别: ```sh # 当 工作区/缓存区/版本库 都一致时,使用以下命令不会有任何提示 # 想知道具体修改了什么地方,可以用以下命令 git diff # 工作区 与 缓存区的区别 git diff --cached # 缓存区 与 版本库的区别 git diff HEAD # 工作区 与 版本库的区别 # 更改工作区的 1.txt,并将更改添加到缓存区 # 然后再次更改工作区的 1.txt,并保存文件的更改 # 再次测试以上三条命令 ``` > 很多编辑器里,都可以使用 git diff 的相关插件,来分析文件的差异。 > 譬如: [sublime插件: GitGutter](https://github.com/jisaacks/GitGutter) / [Sublime Merge](https://www.sublimemerge.com/) 等等.. ---- #### 6.4 Git 日志 ```sh # 查看提交历史 git log # 以简短的方式查看提交日志 git log --oneline # 行为日志,显示所有提交,回滚等.. git reflog # 显示缓存区的所有文件 git ls-files ``` ---- #### 6.5 版本回退 ```sh # 将当前版本重置为 HEAD(通常用于清空缓存区,或 merge 失败回退) git reset --hard HEAD git reset --hard HEAD^ # 回退上一个版本 git reset --hard HEAD^^ # 回退上两个版本 git reset --hard HEAD~n # 回退上n个版本 # 回退到指定版本,commitid 可根据日志获取,'<>'内的id必填 git reset --hard ``` ---- #### 6.6 撤销 > 目的:将尚未提交至版本库的修改撤回 - 情况一 : 文件修改后,尚未添加至缓存区 ```sh # 修改 1.txt 的内容:添加一行 # 在工作区撤销文件的修改 git checkout -- filename # 注意'--'不可缺少。没有'--',就变成了“切换到另一个分支”的命令 ``` - 情况二 : 文件修改后,已添加至缓存区 ```sh # 修改 1.txt 的内容:添加一行,并添加到缓存区 # 撤回添加至缓存区的修改 git checkout HEAD filename ``` ---- #### 6.7 Git 删除 - 删除文件和版本库记录 ```sh # `rm`命令只会删除工作区的文件,不会删除记录 rm 2.txt git status # 提示2.txt 被删除,是否需要提交到版本库 # 如果删错文件,则可以撤回删除(因为此时的删除操作,还未写入到缓存区) git checkout -- 2.txt # 将 2.txt 的删除记录写入到缓存区,并删除文件 git rm 2.txt # 如果还未提交,想要撤回,可使用 `git checkout HEAD 2.txt` # 提交 2.txt 的删除记录到版本库中 git commit -m '删除2.txt' ``` - 删除缓存区的文件 ```sh # 新建 demo.html 文件,并加入到缓存区 git add demo.html git status # 提示有新文件等待提交 # 将 demo.html 文件移出缓存区,但不删除(文件还会保留在工作区) git rm --cached demo.html git status # 提示工作区有文件等待 add # 重新将 demo.html 加入到缓存区 # 将缓存区中的 demo.html 文件移出并删除(工作区不保留文件) # 强制删除选项 -f (force 的首字母) git rm -f demo.html ``` - 其他删除 ```sh # 新建 aaa/111.txt # 新建 aaa/bbb/111.txt # 将文件添加到版本库中 git add . git commit -m '添加aaa目录及文件' # 递归删除目录(删除的是,已被记录到版本库的目录) git rm -r 目录名 git commit -m '删除aaa目录' ``` #### 6.8 Git 流程图 ![Git流程图](./imgs/Git流程图.png) ---- ### 7. 远程仓库 #### 7.1 得到远程的版本库 > 可以使用两种方式来得到远程版本库: * 在某个指定的文件夹下使用,即可得到远端版本库及代码 ```sh git clone <远端版本库url> [本地存放该库的文件夹名] ``` * 手动添加版本库,并拉取文件 ```sh # 初始化本地仓库 git init # 添加远程版本库 可自行取名,默认origin git remote add # 查看远程版本库信息 git remote -v # 查看指定远程版本库信息 git remote show # 删除远程 remote 链接 git remote remove # 下载代码及快速合并 git pull # 下载远程代码不合并 git fetch [remote-name] ``` ---- #### 7.2 推送分支代码 得到远端版本库后,可以在本地按正常的步骤编辑 : 新建或改动文件-->添加至缓存区-->提交到版本库 此时,要想将本地版本库发给远端,只有commit提交是不够的. 还需要下面的操作 : ```sh git push # 上传代码及快速合并 ``` 执行以上代码,会有报错 : 无法直接推送到远端的主分支 此时,可以曲线救国,推送自己的分支到远端即可 : ```sh git push origin master:dev ``` 此时,推送成功! ---- ### 8. Git 分支管理 #### 8.1 查看版本库分支 ```sh # 显示本地分支 git branch # 显示所有分支 git branch -a # 切换到指定分支或标签 git checkout 分支名/标签名 ``` #### 8.2 创建分支/删除分支 ```sh # 新建分支 git branch 分支名 # 创建并切换到dev分支 git checkout -b dev # 删除本地分支 -D 强制删除 git branch -d 分支名 ``` #### 8.3 合并分支 要将 B分支 合并到 A分支里 请切换到A分支内,合并B分支的操作在A分支内进行 ```sh # 合并分支到当前分支 git merge 分支名 ``` #### 8.4 解决合并冲突 > 多分支修改同一文件,合并可能出现冲突。冲突部分用<<<===>>>表示 ![合并冲突](./imgs/06.png) 解决方法: 先手动修改冲突部分,再次提交即可。 #### 8.5 分支管理策略 在实际开发中,我们应该按照几个基本原则进行分支管理: 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活; 那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本; 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。 所以,团队合作的分支看起来就像这样: ![分支管理](./imgs/07.png) #### 8.6 多人协作 多人协作的工作模式通常是这样: 首先,可以试图用`git push origin `推送自己的修改; 如果推送失败,则因为远程分支比你的本地更新,需要先用`git pull`试图合并; 如果合并有冲突,则解决冲突,并在本地提交; 没有冲突或者解决掉冲突后,再用`git push origin `推送就能成功! 如果`git pull`提示`no tracking information`,则说明本地分支和远程分支的链接关系没有创建,用命令`git branch --set-upstream-to origin/` 这就是多人协作的工作模式,一旦熟悉了,就非常简单。 ---- ### 9. 使用代码托管系统 市面上有名的Git托管系统 : - [GitHub](https://github.com/) - [码云](https://gitee.com/) - [CODING](https://coding.net/) #### 9.1 码云 生成SSH密钥及使用 1. 本地打开git bash ,`cd`切换到用户的家目录 2. 使用`pwd`来查看目录是否正确 3. 使用 `ssh-keygen.exe -t rsa` 来生成SHA256的SSH密钥(按回车确认即可) 4. `cd .ssh/` 切换至ssh目录 5. `ls` 查看目录下文件 6. `cat id_rsa.pub` 查看生成的密钥,并复制 7. 打开[码云](https://gitee.com/),登录自己的账户.点选个人资料 8. 选择SSH公钥 9. 在添加公钥界面,将刚刚复制好的密钥粘贴进来,再自己取一个该密钥的名字,以便于区分管理 10. 本地新建一个目录,用于拉取远端版本库 11. 使用 `git init`初始化该目录 12. 使用`git remote add origin ` 来添加远程版本库 13. 使用`git remote -v` 来查看远程版本库信息 14. 使用`git pull origin marster`来拉取版本库及代码 #### 9.2 GitHub 生成SSH密钥及使用 1. 运行 git Bash 客户端,输入如下代码: - `$ cd ~/.ssh` - `$ ls` - 这两个命令就是检查是否已经存在 id_rsa.pub 或 id_dsa.pub 文件,如果文件已经存在,那么你可以跳过步骤2,直接进入步骤3 2. 创建一个 SSH key - `$ ssh-keygen -t rsa -C "your_email@example.com"` - 代码参数含义: - `-t` 指定密钥类型,默认是 rsa ,可以省略。 - `-C` 设置注释文字,比如邮箱。 - `-f` 指定密钥文件存储文件名。 3. 添加你的 SSH key 到 github上面去 - `$ cat ~/.ssh/id_rsa.pub` 4. 测试一下该SSH key 在git Bash 中输入以下代码 - `$ ssh -T git@github.com` - 显示类似如下,表示成功: - `Hi username! You've successfully authenticated` 5. `git clone 远程git仓库地址` ---- ### 10. 文件忽略 一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 `.gitignore` 的文件,列出要忽略的文件模式。 常用规则: - 以斜杠 `/` 开头表示目录 - 以星号 `*` 通配多个字符 - 以问号 `?` 通配单个字符 - 以方括号 `[]` 包含单个字符的匹配列表 - 以叹号 `!` 表示不忽略(跟踪)匹配到的文件或目录 > 配置文件是按行 从上到下 进行规则匹配的, > 这就意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效. > > GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表 > > 你可以在 [Git 忽略文件列表](https://github.com/github/gitignore) 找到它. ### 11. 使用 TortoiseGit 操作 Git > TortoiseGit 俗称 [ GIT小乌龟 ] ---- # Git 常用命令速查 - master 默认主分支 - dev 默认开发分支 > 创建版本库 ```sh # 初始化本地git版本库(创建新仓库) git init # 配置用户名 git config --global user.name "xxx" # 配置邮件 git config --global user.email "xxx@xxx.com" #查看当前配置列表 git config --list # clone远程仓库 git clone ``` > 修改、提交、删除 ```sh # 添加index.php文件到缓存区 git add index.php # 添加所有改动过的文件到缓存区 git add . # 添加所有文件到缓存区 git add --all # 提交缓存区内的文件(回车后需要键入描述:wq保存退出) git commit # 提交缓存区内的文件,并提供描述 git commit -m "描述" # 将add和commit合为一步 git commit -am '描述' # 合并最后一次提交(用于反复修改) git commit --amend -m 'xxx' # 删除index.php文件 git rm index.php # 将index.php文件移出缓存区,但不删除( -r * 递归目录) git rm --cached index.php # 将缓存区中的1.html文件移出并删除 git rm -f 1.html ``` > 查看 ```sh # 查看当前版本状态(是否修改) git status # 查看所有添加到缓存区的变更(工作区与版本库的区别) git diff # 查看工作区文件和库文件区别 git diff index.php # 查看所有已添加到缓存区,但还未commit的变更(缓存区与版本库的区别) git diff --cached # 查看提交历史 git log # 以简短的方式查看提交日志 git log --oneline # 行为日志,显示所有提交,回滚等.. git reflog # 显示缓存区的所有文件 git ls-files ``` > 回退 与 撤销 ```sh # 将当前版本重置为HEAD(通常用于merge失败回退) git reset --hard HEAD git reset --hard HEAD^ # 回退上一个版本 git reset --hard HEAD^^ # 回退上两个版本 git reset --hard HEAD~n # 回退上n个版本 # 回退指定版本,commitid根据log获取 git reset --hard # 在工作区撤销文件的修改 git checkout --filename # 撤回添加至缓存区的修改,不指定filename则撤回所有 git checkout HEAD [filename] ``` > 分支操作 ```sh # 获取远程分支master并merge到当前分支 git pull origin master # 显示本地分支 git branch # 显示所有分支 git branch -a # 切换到指定分支或标签 git checkout 分支名/标签名 # 新建分支 git branch 分支名 # 删除本地分支 -D 强制删除 git branch -d 分支名 ``` > 远程协作 ```sh # 添加远程版本库 git remote add # 查看远程版本库信息 git remote -v # 查看指定远程版本库信息 git remote show # 删除远程remote链接 git remote remove # 重命名远程链接名 git remote rename # 上传代码及快速合并 git push # 获取远端库到本地,但不合并 git fetch origin # 下载代码及快速合并 git pull # 将本地的远端库合并 git merge origin master ``` > Thanks Bye Bye > > > 讲师.黄超.Seeker > > 更多信息,详见: [Pro Git(中文版)](https://gitee.com/progit/index.html)