常用git指令


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的指针,工作区中已提交的内容会随着分支改变而改变,但缓存区和工作区中未提交的内容是不受影响的,所以你在branch1git add的内容,是可以切换到branch2git 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 checkoutgit 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-shellgit-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

Git官方文档

廖雪峰的Git教程

菜鸟驿站的Git教程

易百的Git教程

https://www.jianshu.com/p/79c05c103bdd


评论
  目录