1. Git安装
1.1. Linux:
# Debian/Ubuntu等系统
apt-get install libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev
apt-get install git
# Centos/RedHat等系统
yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
yum -y install git-core
1.2. Windows:
直接官网下载安装包安装即可。
安装完后还会附送一个git bash
可以操作Git的终端,在这个终端上面,要使用Linux的指令,譬如cat
等,当然也可以用Windows自带的powershell
等终端进行操作,但使用就是Windows的指令了,譬如type
等。
1.3. Mac os:
最简单的方式就是使用homebrew
安装
brew install git
这种安装方式要先安装homebrew
,也可以直接官网下载安装包安装。
检验是否安装成功的方法:
输入git --version
后,如果输出git version x.xx.x
字样表示安装成功。
2. 与远端仓库交互
Git是一个分布式版本控制系统,一般来说,本地仓库为客户端,远端仓库为服务端。
GitHub、gitee等都是一些服务端Git代码托管平台。下面以GitHub为例,其他gitee等平台操作是差不多的,具体操作可以自行百度。
2.1. 注册账号
GitHub官网按照指引创建一个账号。
2.2. 创建ssh key
由于你的本地 Git 仓库和 GitHub 仓库之间的传输是通过SSH加密的,可以通过密码的形式确认身份。如果不想每次连接时都要输入密码,可以通过公私钥鉴权的方式确认身份。
所以首先通过下面指令在本地创建一个ssh key
ssh-keygen -t rsa -C "your_email_address"
然后一直回车即可。
指令操作成功后,会在用户文件夹(类Unix操作系统为~
,Windows操作系统为%HOMEPATH%
)下生成.ssh
文件夹,其文件结构如下:
.ssh
├── config # 本地ssh的配置信息
├── id_rsa # 私钥文件
├── id_rsa.pub # 公钥存储文件,一行代表一个公钥
└── known_hosts # 已经识别过的主机地址名单,名单中的主机会跳过警告部分
Windows中系统路径含义系统路径(不区分大小写)对应的绝对路径:
- 当前系统盘符:
C:
、%systemdrive%
、%HOMEDRIVE%
- 当前系统目录:
C:\WINDOWS
、%systemroot%
、%Windir%
- 当前用户文件夹:
C:\Administrator
、%UserProfile%
、%HOMEPATH%
- 所有用户文件夹:
C:\ProgramData
、%AllUsersProfile%
- 临时文件夹1:
C:\WINDOWS\Temp
、%temp%
- 临时文件夹2:
%SystemRoot%\TEMP
- 程序文件夹:
C:\Program Files
、%ProgramFiles%
- 程序快速启动设置文件夹:
C:\Administrator\AppData\Roaming
、%AppData%
2.3. 在GitHub中添加我们的公钥
打开GitHub -> Account -> Settings -> SSH and GPG keys -> New SSH key -> Key 输入框中输入id_rsa.pub
中的公钥,如果有多行,取一行就可以 -> Add SSH key -> 添加成功
2.4. 验证鉴权结果
本地输入以下指令
ssh -T git@github.com
如果出现Hi xxx! You've successfully authenticated, but GitHub does not provide shell access.
,说明本地 Git 仓库和 GitHub 仓库已经成功通信。
2.5. GitHub上创建一个仓库
打开GitHub -> New repository -> 填写仓库名称 -> create respsitory -> 创建新项目成功
这样,我们就可以开始我们愉快的Git版本管理之旅了。
3. Git基本指令
3.1. 基础操作类
3.1.1. git config
Git环境变量配置指令
3.1.1.1. 变量作用的范围设置
--global 使用全局配置文件
--system 使用系统级配置文件
--local 使用仓库级配置文件
--worktree 使用工作区级别的配置文件
一般来说,只用到--global
这个参数,这个参数只作用于当前用户的环境变量。该参数下的配置文件为用户文件夹下的.gitconfig
文件,这个文件一开始不会存在,会在配置的时候自动生成。
系统级配置文件位置,类Unix操作系统为/etc/gitconfig
,Windows操作系统Git安装目录下的/etc/gitconfig
仓库级配置文件位置,为仓库目录下的 .git/config
优先级:仓库级>用户级>系统级
当生成新的仓库的时候,会根据先按照优先级顺序生成一份仓库级配置文件;当需要用到这些配置项时,会按照优先级顺序采用配置项。
3.1.1.2. 个人用户设置
这一步是必要的,否则后面创建仓库等操作都会失败
git config --global user.name "account" # 用户名称
git config --global user.email "your_email_address" # 电子信息
git config --local -e # 直接编辑配置文件
3.1.1.3. 其他信息设置
# 设置别名
git config --global alias.co checkout
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
3.1.1.4. 查看配置信息
使用以下指令可以查看已经配置好的参数
git config --list
或者直接打开用户文件夹下的.gitconfig
文件,也可以看到已经配置好的参数
3.1.2. git init
本地初始化一个 Git 仓库
git init # 默认在根目录下生成一个仓库
git init repo # 在repo文件夹下生成一个仓库
初始化一个Git仓库后,会在仓库的根目录下生成一个.git
目录。
这是.git
的目录结构
.git # 版本库
├── COMMIT_EDITMSG
├── HEAD # 版本库指针,指向当前的版本
├── config # 仓库级的配置信息
├── description
├── hooks/ # 里面是一些脚本文件,当执行某些指令后,会自动启用这部分的脚本文件,具体详情见https://git-scm.com/docs/githooks
├── index # staged版本,缓存区,也叫索引
├── info/
├── logs/ # 操作日志文件
├── objects/ # 对象库,包含了创建的各种对象及内容
├── packed-refs
└── refs/ # 版本库信息
仓库根目录下除.git
外是工作区Working tree
3.1.3. git add
把文件添加到缓存区,Working tree -> index
git add . # 目录下所有的文件加入缓存区
git add fn1 fn2 # 把fn1和fn2文件加入缓存区
git add -u # 把之前跟踪过得文件一次性添加到缓存区
有时候目录下有些文件不想加入缓存区,可以在仓库根目录下新建一个.gitignore
文件,可以在这个文件中设置一些关键词,表示把文件添加进缓存区时忽略这些文件。
例如.gitignore
文件设置以下关键词
.DS_Store
*.log
表示.DS_Store
文件和所有.log
后缀的文件不加入缓存区
3.1.4. git rm
从缓存区中删除文件或者同时从工作区和索引中删除文件。
git rm filename # 同时从工作区和索引中删除文件。即本地的文件也被删除了。
git rm --cached filename # 从索引中删除文件。但是本地文件还存在,只是不希望这个文件被版本控制。
git rm -r dir # 删除文件夹
3.1.5. git commit
缓存区数据提交到版本库中,index -> HEAD
git commit # 调用默认的编辑器,一般是vim,输入提交信息,保存既可完成提交
git commit -m “message” # 免去调用编辑器
git commit -a -m “message” # 相当于 git add -u; git commit -m “message”
git commit --amend # 不改变commit-id的情况下,追加提交
使用git commit
命令后,会在本地版本库生成一个40位的哈希值commit-id
,这个commit-id
也可以取哈希值的前7位。
3.2. 文件调试类
3.2.1. git status
查看上次提交之后工作区的修改状态
git status # 以详尽的方式显示修改状态
git status -s # 以精简的方式显示修改状态,M - 被修改,A - 被添加,D - 被删除,R - 重命名,?? - 未被跟踪
3.2.2. git diff
git status
的进阶版,可以细化到文件哪一行被修改了。
git diff # 尚未缓存的改动,Working tree -> index
git diff --cached # 同git diff --staged,已缓存的改动,index -> HEAD
git diff HEAD # 已缓存的与未缓存的所有改动,Working tree -> HEAD
git diff --stat # 显示改动的摘要,输出 x file changed, y insertions(+), z deletions(-) 信息
git diff HEAD^ HEAD # HEAD^表示上上次提交,也可以用`HEAD~1`表示,比较上次提交和上上次提交
git diff commit-id1 commit-id2 # 比较两个历史版本之间的差异
git diff branch1 branch2 # 比较两个分支之间的差异
git diff dir # 查看文件夹的改动
3.2.3. git log
查看提交历史
git log # 全量查看模式
git log -p -2 # 最近两次的提交
git log --stat # 简要显示模式
git log --pretty=[oneline,short,full,fuller] # 使用其他输出格式展示提交日志
3.3. 分支管理类
3.3.1. git branch
创建分支或查看分支
3.3.1.1. 创建分支
git branch dev # 创建名为dev的分支,会并把当前分支的版本库复制一份给新建的分支工作区
git branch -d dev # 删除dev分支,如果在分支中有一些未merge的提交,会删除分支失败
git branch -D dev # 强制删除dev分支
git branch -m old_branch new_branch # 分支重命名
3.3.1.2. 查看分支
git branch # 查看本地分支,其中当前分支为'*'
git branch -r # 查看远程分支
git branch -a # 查看所有分支
3.3.2. git merge
合并分支
git merge branch # 把branch分支合并到当前的分支中
用于合并的分支中有当前分支中没有的文件时,主分支中创建这部分的文件;
用于合并的分支中没有当前分支中有的文件时,主分支中删除这部分的文件;
用于合并的分支中和当前分支中都有的文件不一致时,会引发冲突,需要将两个分支的文件修改至一致后,使用git add
指令后,才能进行合并操作。
其本质是把当前分支的指针指向用于合并的分支的指针。合并后,原来用于合并的分支不会被删除,一般接下来都会把刚刚用来合并的分支删除掉。
合并分支时,如果可能,Git会用Fast forward
模式,这种模式会直接把当前版本变成合并后的版本,即当前版本的信息就会被抹去,如果想要保留当前版本的信息,使用下面指令
git merge --no-ff -m "message" branch
这条指令合并的时候会生成一个新的commit,这样就保留了之前的历史版本。
特别地,新版本不能向老版本方向合并,
git merge old_branch
这条指令是不会生效的。如果
merge
时想要忽略某些文件,根目录下在要被merge的分支上创建.gitattributes
文件,写入要忽略的文件,格式如下echo '\<ignore_file\> merge=ours' > .gitattributes git add .gitattributes
定义merge drive
git config merge.ours.driver true
3.3.3. git checkout
可以用于切换分支或恢复文件
3.3.3.1. 切换分支
git checkout dev # 将分支切换到dev
git checkout -b dev # 如果分支存在则只切换分支,若不存在则创建并切换到dev分支
切换分支的操作只是改变了HEAD
的指针,工作区中已提交的内容会随着分支改变而改变,但缓存区和工作区中未提交的内容是不受影响的,所以你在branch1
中git add
的内容,是可以切换到branch2
中git commit
的。
3.3.3.2. 恢复文件
git checkout [--] filename # 从当前缓存区中还原文件
git checkout [--] . # 从当前缓存区中还原整个目录,这个指令很危险,会清除工作区中未添加到缓存区的改动
git checkout \<branch|commit-id\> filename # 从特定版本中还原文件
git checkout \<branch|commit-id\> . # 从特定版本中还原整个目录,这个指令很危险,不但会清除工作区中未提交的改动,也会清除缓存区中未提交的改动
如果此时某分支名和工作区的某个文件重名xxx,使用git checkout xxx
时,会进行切换分支操作,如果是要进行恢复文件的操作,要使用git checkout -- xxx
指令
3.3.4. git switch
切换分区
git switch dev # 将分支切换到master
git switch -c dev # 如果分支存在则只切换分支,若不存在则创建并切换到dev分支
git checkout
指令对于分支和文件操作的结果是大相径庭的,官方为了解决这种困惑,在2.23
版本的更新中推出了git switch
指令,专门用于切换分区操作。
3.4. 版本控制类
3.4.1. git tag
给仓库历史中的某一个提交打上标签(一般是版本号)
git tag -a v1.0 -m "message" # 最近一次提交打标签
git tag -a v1.0 commit-id # 指定版本打标签
git tag -d v1.0 # 删除标签
3.4.2. git reset
版本回退,回退到老版本后,新版本的数据将会丢失。
git reset <HEAD^|commit-id> # 不加参数回退版本,默认参数为`--mixed`
git reset --soft HEAD^ # 只重置HEAD
git reset --mixed HEAD^ # 重置HEAD和index
git reset --hard HEAD^ # 重置HEAD、index、Working tree
因为reset 的本质是移动 HEAD 以及它所指向的 branch,所以我们也可以使用这个指令实现类似git checkout
指令的功能——git reset other_branch
,把 HEAD 指向其他的branch,只是这样做没有太大的意义,为什么不直接用git checkout
呢?
还有,理论上来说,这个指令回退到老版本后,就不能回到新版本了,但实际上,只要还记得新版本的commit-id
,再次使用git reset commit-id
还是可以回到新版本的,另外,可以使用git reflog
指令找回新版本的commit-id
3.4.3. git revert
版本重做
git revert commit_id # 重做并commit该版本
git revert -n commit_id # 重做后不要commit
和git reset
一样,同样用于版本恢复的,但和git reset
不一样的是,git revert
会生成一个新的版本重新提交一次,同时会保留之前所有的版本,不会删除新的版本。
为了跟远端服务器版本保持一致,版本回退时建议使用git revert
指令!!!
3.4.4. git restore
文件恢复
git restore filename # 从缓存区中恢复文件,相当于`git checkout -- filename`
git restore --stage filename # 从当前版本库中恢复文件到缓存区,相当于`git reset HEAD filename`
git checkout
和git reset
都可以用于文件的恢复,为了统一功能,新版的Git整合成一个指令
3.5. 远程交互类
3.5.1. git remote
关联远程仓库
git remote # 列出已经关联的远程仓库
git remote -v # 列出已经关联的远程仓库的详细信息
git remote add origin git@github.com:account/repo.git # 远程仓库关联到本地,其中,origin是自定义的远程主机名
git remote remove origin # 取消关联
git remote rename old_name new_name # 远程主机名重命名
3.5.2. git fetch
将远程主机的最新内容拉到本地
git fetch origin # 拉取所有的更新
git fetch origin master # 拉取特定分支的更新
git fetch
指令成功后,会创建一个分离指针FETCH_HEAD
3.5.3. git pull
将远程主机的最新内容拉下来并合并,相当于
git fetch
+git merge
git pull # 拉取默认远程主机的默认分支并合并,如果默认远程主机是origin,默认分支是master,则该指令相当于`git fetch origin master;git merge FETCH_HEAD`
git pull origin remote_branch[:local_branch] # 如果远程分支名和本地分支名相同,可以省略本地分支名
3.5.4. git clone
克隆一个远程仓库到本地
git clone [-b dev] git@github.com:account/repo.git [local_dir] # 如果不加`-b`参数,拉取默认的分支
git clone
有三种克隆方式
git clone git@github.com:account/repo.git # SSH协议
git clone git://github.com/account/repo.git # GIT协议
git clone https://github.com/account/repo.git # HTTPS协议
一般来说,采用ssh的传输方式是最快的
克隆完成后,会在目录下生成一个从远程服务器上拉下来的repo
项目
3.5.5. git push
将本地版本库的分支推送到远程服务器上对应的分支
git push origin local_branch[:remote_branch] # 本地和远程服务器的分支名一样时,可以省略远程分支名
git push origin :remote_branch # 省略本地分支名时,表示上传一个空的分支,即删除该远程服务器分支
git push -f # 如果本地使用了`git reset`指令或者远端仓库被多人修改时,重新上传到远端服务器时会报错,可以采用强制上传方法
特别地,这里说明下强制上传的机制。例如现在,本地和远程的仓库的提交记录是一致的,都是a->b->c->d
,现在本地使用git reset
指令回退到c
版本,强制上传后,本地仓库的提交记录是a->b->c
,远程仓库只剩一个提交记录merge commit
,远程仓库丢失了所有版本更新的信息,当别人git pull
的时候,就会把已经做好的东西全部抹掉,这样做是很危险的。所以,git push -f
这个指令一般是禁止使用的。
4. hooks
在 .git
目录下有一个 hooks
文件夹,这个文件夹中的文件都是一些可执行的脚本,用于执行Git指令后进行一些操作。这脚本类型可以使shell、python等等,脚本中的 #!/bin/sh
定义了你的文件将被如何解析。
所有的钩子都只适用于本地,不会随版本更新到远端,一般钩子都会被应用到服务器端,用于接收到更新提交后进行一些额外的操作。
更多钩子的使用方法参考官方文档
特别地,添加了钩子后得添加操作权限,才能正常运行脚本
chmod +x test.git/hooks/xxx
4.1. 服务端
4.1.1. post-receive
服务端接收到 git push
指令,在所有的操作完毕后,调用该脚本。
一般用于创建对应源码仓库,样例如下
#!/bin/bash
git --work-tree=<your_work_tree_path> --git-dir=<your_git_dir> checkout -f
4.1.2. update&post-update
与 post-receive
不一样的是,这两个指令分别在每个分支更新前/后都会使用 git receive-pack
调用一次。
该脚本会传入三个参数,分别为分支名、旧分支的sha1值、新分支的sha1值。其中,分支名一般为 refs/heads/<branch>
样例如下
#!/bin/bash
refname="$1"
oldrev="$2"
newrev="$3"
if [ "$refname" = "refs/heads/<branch>" ]; then
do_something
fi
if [ "$oldrev" = "sha1" ]; then
do_something
fi
5. 创建私有的Git服务器
5.1. 安装Git
# Debian/Ubuntu等系统
apt-get install libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev
apt-get install git
# Centos/RedHat等系统
yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
yum -y install git-core
5.2. 创建一个git用户组和用户
groupadd git
useradd git -g git
passwd git # 设置密码,这一步可以省略
在/home
目录下,会生成一个git
文件夹,这个文件夹就是git用户的工作目录
处于安全考虑,编辑/etc/passwd
,修改以下内容,禁用shell登录
# git:x:1000:1000::/home/git:/bin/bash
git:x:1000:1000::/home/git:/bin/git-shell
就是把bash
换成git-shell
,git-shell
的作用是只要一登录就自动退出。
5.3. 创建证书登录
cd /home/git/
mkdir .ssh
chmod 755 .ssh
touch .ssh/authorized_keys
chmod 644 .ssh/authorized_keys
其中authorized_keys
是用来存我们的公钥的。
5.4. 初始化Git仓库
假设现在开通一个名为account
的Git账户,然后在该账户下创建一个名为test
的仓库,可以按以下流程操作
cd /home/git
mkdir account
chown git:git account/ # 修改文件权限归git用户所有
cd account
git init --bare test.git
chown -R git:git test.git # 修改文件权限归git用户所有
这时,远端的仓库已经建好了,其地址为git@your_ip_adress:account/test.git
,克隆仓库时的指令为git clone git@your_ip_adress:account/test.git
特别地,git init --bare
只是创建一个裸的仓库,这个仓库是不包含工作区的,或者说生成的test.git
目录就是相当于一个.git
目录。
其实上面的教程有点啰嗦了,因为git基于ssh,所以其实可以不用创建git用户,直接在当前用户下创建仓库即可
git init --bare test.git
仓库的地址为
<user>@<your_ip_adress>:<pwd>/test.git
5.5. 创建工作区挂钩
一般来说,远程服务器上的Git仓库只有版本库,是没有工作区的,如果你希望你的服务器可以显式地保存项目的源码,需要配置hooks
目录下的post-receive
文件。采用这种存储方式,可以实现仓库与项目源码分离的作用。
假设现在创建一个test.git.wt
的目录(注意,这里的文件夹不能git项目的前缀,即不可以是 test
,否则会报错),用来存放test.git
的工作区,可以按以下流程操作
mkdir test.git.wt
chown -R git:git test.git.wt
vim test.git/hooks/post-receive
hooks
中写入callback,例如在 post-receive
中写入
#!/bin/bash
git --work-tree=/home/git/citisy/test.git.wt --git-dir=/home/git/citisy/test.git checkout -f
添加可执行权限
chmod +x test.git/hooks/post-receive
当本地执行git push
时,test.git.wt
中就会生成对应的源码。
6. Reference
https://www.jianshu.com/p/79c05c103bdd