Git & GitHub 简明教程
一、 Git 安装
Linux 上安装 Git
yum -y install git
Mac 上安装 Git
一是使用homebrew安装git。
brew install git
二是去git官网下载文件手动安装。
Windows 上安装 Git
Git 安装步骤
安装在非中文目录下,多数步骤使用默认。
调整 path 环境变量的三个选项,建议选择 use Git from Git Bash only。完全不修改 path 环境变量,仅在 Git Bash 中使用 Git。
library 本地库和远程库的连接方式,使用默认值。
行末换行符转换方式,继续使用默认值。
使用 Git 命令的默认终端,继续使用默认值。
测试git是否安装成功
$ git --version
git version 2.22.0.windows.1
二、 Git 基本操作
1. 创建版本库
先创建一个空目录:
$ mkdir gitest
$ cd gitest/
$ pwd
D:/WorkSpaces/gittest/.git/
通过git init
命令把这个目录变成可以管理的仓库:
$ git init
Initialized empty Git repository in D:/WorkSpaces/gittest/.git/
2. 设置签名
作用:区分不同开发人员的身份。
形式:用户名,邮箱。
注:登录远程库的账号、密码和这个没有任何关系。
# git config
# 项目级别/仓库级别:仅在当前本地库范围内有效。
# 设置用户名和邮箱:
git config user.name evanstark
git config user.email evanstark@aliyun.com
# git config --global
# 系统用户级别:登录当前操作系统用户范围。
# 设置用户名和邮箱:
git config --global user.name evanstark
git config --global user.email evanstark@aliyun.com
# 在命令行中使用不同颜色显示不同内容
git config --global color.ui "auto"
级别优先级
就近原则:项目级别优先于系统用户级别,二者都有时采用项目级别签名
如果只有系统用户级别签名,就以系统用户级别签名为准
不允许二者都没有的情况
签名信息保存位置
项目级别:
.git文件夹下的config文件内
系统级别:
C:/Users/administrator文件夹下 的.gitconfig文件内
3. 添加文件到本地库
编写一个readme.md
文件,内容如下:
Git is a version control system.
Git is free software.
注:文件必须放在gitest
目录(或其子目录)下,因为这是一个Git仓库,在仓库对应的目录下才能找到这个文件。
添加到暂存区:git add [file name]
用法:git add file_name1 file_name2 …
git add readme.md
状态查看:git status
显示当前所在分支,本地库提交历史,当前可提交文件。功能类似于查看警告,查看当前本地库状态,会有提示信息。查看工作区、暂存区状态
-s 或者 -short 以更为简单、紧凑的格式输出
新添加为跟踪的文件有 ?? 标记
新添加到暂存区的文件前面有 A 标记
修改过的文件前面有 M 标记
MM 出现在右边的 M 表示该文件被修改了,但是还没放在暂存区
$ git status -s
A readme.md
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
# git rm --cached <file>将提交到暂存区的文件删除,只剩下工作区文件
new file: readme.md
提交到本地库:git commit -m “commit message” [file name]
用法:git commit -m "文件描述"
$ git commit -m "new creating a readme file"
[master (root-commit) 05b80af] new creating a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.md
附: Git 结构
Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。
**工作区(Working Directory):**写代码,添加文件、修改文件的地方
在电脑里能看到的目录,比如gitest
文件夹就是一个工作区:
**本地库(Repository):**储存每次提交的历史版本
工作区有一个隐藏目录.git
,这个不算工作区,而是Git的本地库。
**暂存区(Stage):**临时存储打算提交尚未提交的文件
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
工作过程:新建文件 在工作区,使用 git add 添加到暂存区,使用 git commit 提交到本地库
4. 修改文件
同添加文件到本地库
5. 比较文件差异
**git diff [文件名]:**将工作区中的文件和暂存区进行比较
**git diff [本地库中历史版本] [文件名]:**将工作区中的文件和本地库历史记录比较
不带文件名比较多个文件
# 显示当前工作目录树和暂存区间的差别
git diff
# 显示暂存区和版本库间的差别
git diff --cached
# 查看已暂存将要添加到下次提交的内容
git diff --cached --staged
# 显示工作目录树和版本库间的差别
git diff HEAD
# 显示工作目录树与版本库中某次提交版本之间的差别
# <start point> 可以是一个提交名称、分支名称或标签名称
git diff <start point>
# 显示版本库中两个版本之间的差别
git diff <start point> <end point>
# 显示差别的相关统计
git diff --stat <start point> [<end point>]
# 显示文件中各个部分的修改者及相关提交信息
git blame <some file>
# 显示文件中各个部分的修改者及相关提交信息,包括在该文件中复制、粘贴和移动内容等方面的情况
git blame -M <some file>
# 显示文件中各部分的修改者及相关提交信息,包括在文件间移动内容方面的情况
git blame -C -C <some file>
# 显示历史时,显示复制和粘贴信息
git log -C -C -p -l <some point>
6. 查看历史版本
查看详细历史版本
git log
多屏显示控制方式:
空格向下翻页
b 向上翻页
q 退出
查看简洁历史版本
git log --pretty=oneline # 显示历史,每个提交显示一行,包括提交注释的第一行
git log --oneline
显示版本历史,以及版本间的内容差异
git log -p
7. 查看git操作历史命令
$ git reflog
369ebbd (HEAD -> master) HEAD@{0}: reset: moving to 369ebbd
f298d92 HEAD@{1}: reset: moving to f298d92
369ebbd (HEAD -> master) HEAD@{2}: reset: moving to 369ebbd
f298d92 HEAD@{3}: reset: moving to HEAD^
369ebbd (HEAD -> master) HEAD@{4}: commit: append GPL
f298d92 HEAD@{5}: commit: add distributed
05b80af HEAD@{6}: commit (initial): new creating a readme file
# HEAD@{移动到当前版本需要多少步}
8. 版本回退
基于索引值操作[推荐]
git reset --hard [局部索引值]
git reset --hard a6ace91
使用^符号:只能后退
git reset --hard HEAD^
# 注:一个^表示后退一步,n 个表示后退 n 步
使用~符号:只能后退
git reset --hard HEAD~n
# 注:表示后退 n 步
reset 命令的三个参数对比
–soft 参数
仅仅在本地库移动 HEAD 指针
–mixed 参数
在本地库移动 HEAD 指针
重置暂存区
–hard 参数
在本地库移动 HEAD 指针
重置暂存区
重置工作区
9. 删除文件
git rm test.txt
git commit -m "remove test.txt"
10. 撤销修改/撤销删除
前提:删除前,文件存在时的状态提交到了本地库。
操作:git reset --hard [指针位置]
删除操作已经提交到本地库:指针位置指向历史记录
删除操作尚未提交到本地库:指针位置使用 HEAD
三、 GitHub 远程仓库
1. 添加远程库
1. 创建远程库
- 登陆GitHub,然后,在右上角找到“Create a new repository”按钮,创建一个新的仓库:
- 在Repository name填入
远程库名称
,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库。
2. 生成ssh密匙
打开 git bash 执行命令,生命公钥和私钥
ssh-keygen -t rsa -C "evanstark@aliyun.com"
执行命令完成后,在window本地用户.ssh目录C:\Users\用户名.ssh下面生成如下名称的公钥和私钥:
id_rsa,id_rsa.pub
3. 配置ssh密匙
![](https://i-blog.csdnimg.cn/blog_migrate/288f9122b6a3b5e3c28cd638d3939e39.png)
cat ~/.ssh/id_rsa.pub
复制内容到Key中,勾选Allow write access,点击Add key按钮保存密匙
![](https://i-blog.csdnimg.cn/blog_migrate/c909fc2b760234524897018a4073d797.png)
2. 连接至远程库
git remote add [别名] [远程地址] # 创建远程库地址别名
git remote -v # 查看当前所有远程地址别名
git push -u [别名] [分支名] # 加上了`-u`参数,Git不但会把本地的`master`分支内容推送的远程新的`master`分支,还会把本地的`master`分支和远程的`master`分支关联起来,在以后的推送或者拉取时就可以简化命令。
# 例:
git remote add origin git@github.com:evanstark/git.git
git push -u origin master
如遇报错:
fatal: remote origin already exists. # origin别名已存在
则:
git remote rm origin
git remote add origin git@github.com:evanstark/git.git
git push -u origin master
3. 推送
git push [别名] [分支名]
# 例:
git push origin master
4. 克隆
git clone [远程地址]
# 例:
git clone git@github.com:evanstark/git.git
5. 团队协作
1. 团队内部协作
![](https://i-blog.csdnimg.cn/blog_migrate/f375b3119f031d17155159b716b11a56.png)
push
甲先创建一个本地库,为了将本地库推送到远程托管中心。于是在代码托管中心创建了空的远程库。然后使用 push 将本地内容推送到远程库。
clone
乙创建本地库,使用 clone 从远程库下载文件到本地库,clone本身带有创建和初始化本地库的功能。
推送权限
因为远程库属于 甲,此时乙无法将本地库文件推送到远程库。需要甲邀请乙加入团队后才可以。
pull
当 乙 将本地库推送到远程库以后,甲可以使用 pull 将远程库文件更新到本地库。
2. 跨团队协作
![](https://i-blog.csdnimg.cn/blog_migrate/f47aeb2c697ba59d6b3c142cf766b4d0.png)
fork
丙是团队外成员,使用 fork 复制一个新的远程库。这个远程库的内容与甲创建的一样,不过所有者是丙。
上传地址
丙从这个远程库 clone 下文件。修改后使用 pull 上传到的地方是丙自己的远程库。甲的远程库内容不受影响。
pull request
丙使用 pull request 发起一个拉取的请求。
merge
甲对丙请求的文件进行审核,审核通过后使用 merge 合并到甲的远程库。
3. 团队成员邀请
“岳不群”其他方式把邀请链接发送给“令狐冲”,“令狐冲”登录自己的 GitHub账号,访问邀请链接。
四、 Git 分支管理
什么是分支?
在版本控制过程中,使用多条线同时推进多个任务
分支的好处?
同时并行推进多个功能开发,提高开发效率
各个分支在开发过程中,如果某一个分支开发失败,不会对其他分支有任 何影响。失败的分支删除重新开始即可
1. 创建分支
git branch [分支名]
# 例:
git branch dev
2. 切换分支
git checkout [分支名]
# 例:
$ git checkout dev
Switched to branch 'dev'
3. 创建并切换分支
git checkout -b [分支名]
# 例:
$ git checkout -b dev
Switched to a new branch 'dev'
4. 查看分支
git branch -v
# 列出远程分支
git branch -r
# 列出所有分支
git branch -a
5. 合并分支
# 第一步:切换到接受修改的分支(被合并,增加新内容)上
git checkout [被合并分支名]
# 第二步:执行 merge 命令
git merge [有新内容分支名]
# 例:
# 把`dev`分支的工作成果合并到`master`分支上:
$ git checkout master
$ git merge dev
Updating 09f920d..be2e7c9
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
6. 删除分支
git branch -d [分支名] # 仅当欲删除的分支已合并到当前分支时
git branch -D <branch to delete> # 不论欲删除的分支是否已经合并到当前分支
# 例:
$ git branch -d dev
Deleted branch dev (was be2e7c9).
7. 分支其他操作
# 创建同名新分支,覆盖已有分支
git branch -f <some existing branch> [<start point>]
# 重命名分支
git branch checkout -m <existing branch name> <new branch name>
# 覆盖某一存在分支
git checkout -M <existing branch name> <new branch name>
# 合并分支但不提交
git merge --no-commit <some branch>
8. 解决冲突
冲突的表现
冲突的解决
第一步:编辑文件,删除特殊符号
第二步:把文件修改到满意的程度,保存退出
第三步:git add [文件名]
第四步:git commit -m “日志信息”
注意:此时 commit 一定不能带具体文件名
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,修改源文件,重新git add/commit解决冲突
git log/status查看冲突
git log --graph查看分支合并图
git log --graph --pretty=oneline --abbrev-commit
9. 分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
合并分支时,加上--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward
合并就看不出来曾经做过合并。
$ git merge --no-ff -m "merge with no-ff" dev
因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去。
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
10. Bug 分支
如何在 开发未完成时保留工作场景,临时开辟bug分支?先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场
git stash 保存现场
$ git stash
Saved working directory and index state WIP on dev: 6224937 add merge
HEAD is now at 6224937 add merge
处理bug ing。。。
现场恢复
$ git stash pop
或者是
$ git stash list
$ git stash apply stash@{0}
11. Feature 分支
git checkout -b feature-vulcan # 新建分支
# 开发完毕
git add vulcan.py # 提交修改到版本库
git status
git commit -m "add feature vulcan" # 提交到分支
git checkout dev # 切回dev,准备合并
# 允许合入,执行merge,
git merge --no-ff -m "add new feature" feature-vulcan
# 不允许合入,删除,
git branch -D feature-vulcan
12. 多人协作
多人协作,通过远程分支的pull与push
git remote -v查看远程仓库,当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。
git push origin master推送分支,就是把该分支上的所有本地提交推送到远程库origin,推送时,要指定本地分支master
git push origin dev 推送dev分支到origin
分支提交的简单规则
master分支是主分支,因此要时刻与远程同步;
dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
git pull
远程分支,本地分支链接关系创建,git branch --set-upstream-to=origin/dev dev
多人协作的工作模式:
首先,可以试图用git push origin <branch-name>
推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull
试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
五、 标签管理
1. 创建标签
git tag <name>
就可以打一个新标签,默认标签是打在最新提交的commit上的;
git tag v0.9 commitId
指定commitid打标签
git tag
查看所有标签
git show <tagname>
查看标签信息
git tag -a v0.1 -m "version 0.1 released" 1094adb
创建带有说明的标签
标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。
2. 删除标签
删除本地未推送标签 git tag -d v0.1
远程删除 git push origin :refs/tags/v0.9
3. 推送标签
git push origin v1.0
推送指定标签
git push origin --tags
一次性推送全部尚未推送到远程的本地标签
六、 搭建远程Git服务器
1. 安装git
sudo apt-get install git
2. 创建一个git用户,用来运行git服务
sudo adduser git
3. 创建证书登录
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。
4. 初始化Git仓库:
先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:
sudo git init --bare sample.git
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:
sudo chown -R git:git sample.git
5. 禁用shell登录:
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。
6. 克隆远程仓库:
现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:
$ git clone git@server:/srv/sample.git
Cloning into 'sample'...warning: You appear to have cloned an empty repository.
七、 Gitlab 服务器搭建过程
官网地址
首页:https://about.gitlab.com/
安装说明:https://about.gitlab.com/installation/
安装命令摘录
sudo yum install -y curl policycoreutils-python openssh-server cronie
sudo lokkit -s http -s ssh
sudo yum install postfix
sudo service postfix start
sudo chkconfig postfix on
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash
sudo EXTERNAL_URL="http://gitlab.example.com" yum -y install gitlab-ee
实际问题:yum 安装 gitlab-ee(或 ce)时,需要联网下载几百 M 的安装文件,非常耗时,所以应提前把所需 RPM 包下载并安装好。
下载地址为:
https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/7/gitlab-ce-10.8.2-ce.0.el7.x86_64.rpm
调整后的安装过程
sudo rpm -ivh /opt/gitlab-ce-10.8.2-ce.0.el7.x86_64.rpm
sudo yum install -y curl policycoreutils-python openssh-server cronie
sudo lokkit -s http -s ssh
sudo yum install postfix
sudo service postfix start
sudo chkconfig postfix on
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
sudo EXTERNAL_URL="http://gitlab.example.com" yum -y install gitlab-ce
当前步骤完成后重启
gitlab 服务操作
初始化配置
gitlab gitlab-ctl reconfigure
启动 gitlab 服务
gitlab-ctl start
停止 gitlab 服务
gitlab-ctl stop
浏览器访问
访问 Linux 服务器 IP 地址即可,如果想访问 EXTERNAL_URL 指定的域名还需要配置域名服务器或本地 hosts 文件。
初次登录时需要为 gitlab 的 root 用户设置密码。
应该会需要停止防火墙服务:
service firewalld stop
八、 连接 Git 和 SVN
Git可以读取Subversion版本库中的数据,并把本地修改发送回Subversion版本库。这是Git区别于其他版本控制工具的必杀技之一。
# 克隆SVN版本库的全部内容
git svn clone <svn repository>
# 克隆具有标准结构的SVN版本库
git svn clone -s <svn repository>
# 克隆非标准结构的SVN版本库
git svn clone -T <trunk path> \
-b <branch path> \
-t <tag path> \
<svn repository>
# 克隆具有标准结构的SVN版本库中的某个版本(比如第2321版)
git svn clone -s -r 2321
# 克隆具有标准结构的SVN版本库,并对SVN中的分支添加前缀
git svn clone -s --prefix svn/ <svn repository>
# 从上游SVN版本库中获得更新的内容,并依次在本地Git版本库中变基本地分支
git svn rebase
# 把修改变化推回上游SVN版本库
git svn dcommit
# 列出所有将推入上游SVN版本库的提交
git svn dcommit -n
# 显示SVN历史记录
git svn log
# 显示文件中各个部分的SVN的修改者及相关提交信息
git svn blame <some file>