原来的:
如果我们想快速了解或体验Git的操作,我推荐搭建并去这个网站学习。 它不需要我们安装工具,我们操作的每一步都可以在右侧实时看到。 对于我们学习和理解 Git 如何以及为什么工作是很有帮助的。
图 1. 常见的企业工作流程
主要介绍企业常用的Git工作流程!
Git 流
图片
Github 流
图片
GitLab 流程:
图 2. 日常使用的最佳实践
总结Git的使用方法和日常工作中应该遵循的方法!
使用命令行而不是 GUI
提交应尽可能表达提交修改
使用 .gitignore 文件排除无用文件
基于分支或分支的开发模型
使用发布分支和标签进行版本管理
三、常用命令汇总
您只需要记住 6 个日常使用的命令。
图片
# 工作区 -> 暂存区
$ git add
# 暂存区 -> 本地仓库
$ git commit -m "some info"
# 本地仓库 -> 远程仓库
$ git push origin master # 本地master分支推送到远程origin仓库
# 工作区 <- 暂存区
$ git checkout -- # 暂存区文件内容覆盖工作区文件内容
# 暂存区 <- 本地仓库
$ git reset HEAD # 本地仓库文件内容覆盖暂存区文件内容
# 本地仓库 <- 远程仓库
$ git clone # 克隆远程仓库
$ git fetch upstream master # 拉取远程代码到本地但不应用在当前分支
$ git pull upstream master # 拉取远程代码到本地但应用在当前分支
$ git pull --rebase upstream master # 如果平时使用rebase合并代码则加上
# 工作区 <- 本地仓库
$ git reset # 本地仓库覆盖到工作区(保存回退文件内容修改)
$ git reset --mixed # 本地仓库覆盖到工作区(保存回退文件内容修改)
$ git reset --soft # 本地仓库覆盖到工作区(保留修改并加到暂存区)
$ git reset --hard # 本地仓库覆盖到工作区(不保留修改直接删除掉)
4.配置实用程序参数选项
虽然配置比较简单,但是非常好用!
# 用户信息
$ git config --global user.name "your_name"
$ git config --global user.email "your_email"
# 文本编辑器
$ git config --global core.editor "nvim"
# 分页器
$ git config --global core.pager "more"
# 别名
$ git config --global alias.gs "git status"
# 纠错
$ git config --global help.autocorrect 1
# 不加--global参数的话,则为个人配置
$ git config --list
$ git config user.name
$ git config user.name "your_name"
# 如果在项目中设置,则保存在.git/config文件里面
$ cat .git/config
[user]
name = "your_name"
......
5.合并和变基选项
什么时候用merge操作,什么时候用rebase操作?
支持使用merge的开发者认为仓库的commit history是对实际发生了什么的记录。 这是一份历史文件。 其实它本身就是有价值的,我们不要随意修改。 如果我们改变历史,就等于用“谎言”来掩盖实际发生的事情,这些痕迹应该保留下来。 可能,这不是很好。
# 3rd的两个分支的commit修改相同内容
* 62a322d - (HEAD->master) Merge branch 'hotfix3' into master
|
| * 6fa8f4a - (hotfix3) 3rd commit in hotfix3
* | 548d681 - 3rd commit in master
|/
* 6ba4a08 - 2nd commit
* 22afcc1 - 1st commit
支持使用rebase的开发者,他们认为commit history是项目过程中发生的事情,项目的主干需要非常干净。 但是git查看当前代码版本号,使用merge操作会生成一个merge commit对象,这会在commit history中增加一些非常冗余的内容。
后面我们使用log命令查看提交历史的时候,会发现trunk的提交历史非常尴尬。 比如同一个修改内容被提交了两次,很明显是分支合并导致的问题。
# 3rd的两个分支的commit修改相同内容
* 697167e - (HEAD -> master, hotfix) 3rd commit
* 6ba4a08 - 2nd commit (2 minutes ago)
* 22afcc1 - 1st commit (3 minutes ago)
总的原则是只对未推送或共享给其他人的本地修改历史进行rebase操作,而不会对已推送到仓库的commit记录进行rebase操作。 这样,您就可以享受到两种方法带来的好处。 方便。
6.更新仓库提交历史
Git 提供了一些工具可以帮助我们完善仓库中的提交内容,例如:
在日常开发中,我们为了完成一个功能或者特性,提交了很多commit记录。 但是最后,在提交PR之前,一般情况下,我们应该先整理一下这些提交记录。 有些提交需要合并,或者需要删除等。
# 调整最近五次的提交记录
$ git rebase -i HEAD~5
$ git rebase -i 5af4zd35 # 往前第六次的commit值
reword c2aeb6e 3rd commit
squash 25a3122 4th commit
pick 5d36f1d 5th commit
fixup bd5d32f 6th commit
drop 581e96d 7th commit
# 查看提交历史记录
$ git log
* ce813eb - (HEAD -> master) 5th commit
* aa2f043 - 3rd commit -> modified
* 6c5418f - 2nd commit
* c8f7dea - 1st commit
编号选项列表对应含义的解释
1个
p/选择
使用此提交记录
2个
改写
使用此提交记录; 并修改提交信息
3个
e/编辑
使用此提交记录; rebase 将暂停以允许您修改此提交
4个
小号/壁球
使用此提交记录; 当前提交将与之前的提交合并
5个
f/修正
与壁球选项相同; 但不会保存当前commit的提交信息
6个
x/执行
执行其他shell命令
7
d/下降
删除此提交记录
有时提交后,我们发现提交历史出现了一些问题,这时候我们不想生成新的提交记录,到达修改后的目录。 即修改之前的commit提交记录。
# 不使用分页器
$ git --no-pager log --oneline -1
d5e96d9 (HEAD -> master) say file
# 改变提交信息并加入暂存区
$ echo "hello" > say.txt
$ git add -u
# 改变当前最新一次提交记录
$ git commit --amend
# 改变且息不改变提交信
$ git commit --amend --no-edit
# 改变当前最新一次提交记录并修改信息
$ git commit --amend -m "some_info"
# 不使用分页器
$ git --no-pager log --oneline -1
9e1e0eb (HEAD -> master) say file
我们开发了一个功能,上线的时候产品经理说这个功能有些功能已经不需要了,就是相关功能的提交记录和内容可以忽略/删除。
# 回滚操作(可多次执行回滚操作)
# 彻底上次提交记录;也可是PR的提交记录
# 默认会生成一个类型为reverts的新commit对象
$ git revert 3zj5sldl
我们不想合并整个分支,但是我们需要合并分支的一些提交记录。
# 摘樱桃
$ git cherry-pick -x z562e23d
7.使用重新登录
我们如何取回丢失的内容和记录?
之前我们说过,使用下面的命令回滚内容、强制推送代码、删除本地分支都是非常危险的操作,因为重置之后,我们是没有办法找到之前修改过的内容的。
# 回退
$ git reset --hard
# 推送
$ git push origin master -f
# 分支
$ git branch -D
其实Git给我们留下了一个后门,就是利用relflog命令来找回之前的内容,但是比较麻烦。 原理也很简单,就是当我们使用Git命令操作仓库时,Git会偷偷帮我们记录所有的操作。
# 查看日志记录
$ git --no-pager log --oneline -1
4bc8703 (HEAD -> master) hhhh
# 回退到上次提交
$ git reset --hard HEAD~1
# 查看引用日志记录
$ git reflog
6a89f1b (HEAD -> master) HEAD@{0}: reset: moving to HEAD~1
4bc8703 HEAD@{1}: commit (amend): hhhh
# 找回内容
$ git cherry-pick 4bc8703
8.批量修改历史提交
批量修改历史提交虽然不常用,但了解一下可以节省很多时间!
我们之前学习的命令都是针对修改一个或多个commit提交信息的。 如果我们需要全局修改历史提交怎么办? 当然,Git也支持全局修改历史提交,比如全局修改邮箱地址,或者从全局历史中删除或修改一个文件。
这里我们可以使用filter-brach的方式进行修改,但是建议在使用前新建一个分支。 在上面测试,没有问题,然后在后备箱上操作,防止出问题,背个大锅。
# 创建分支
$ git branch -b testing
# 修改邮箱地址
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" == "escape@escapelife.site" ]; then
GIT_AUTHOR_NAME="escape";
GIT_AUTHOR_EMAIL="escape@gmail.com";
git commit-tree "$@"
else
git commit-tree "$@"
fi' HEAD
9.钩子函数的灵活使用
主要介绍.git/hooks目录下的sample hook函数!
Git中有两种类型,分别对应客户端和服务端的钩子函数。 执行提交和合并等操作时调用的客户端挂钩函数。 服务端钩子函数是服务端收到代码提交后,可以启动代码检查和持续集成的一个步骤。 作为开发者git查看当前代码版本号,我们并不知道如何搭建Git服务器,所以基本不会涉足。
下面是Git自带的hook脚本,但是内置的hook脚本都是以.sample为后缀的,表示没有启用,以example表示。 如需启用,请删除后缀.sample。 钩子脚本的相应内容是使用Shell语法编写的。
➜ ll .git/hooks
total 112
-rwxr-xr-x applypatch-msg.sample
-rwxr-xr-x commit-msg.sample
-rwxr-xr-x fsmonitor-watchman.sample
-rwxr-xr-x post-update.sample
-rwxr-xr-x pre-applypatch.sample
-rwxr-xr-x pre-commit.sample
-rwxr-xr-x pre-merge-commit.sample
-rwxr-xr-x pre-push.sample # 不会推送包含WIP的commit提交
-rwxr-xr-x pre-rebase.sample
-rwxr-xr-x pre-receive.sample
-rwxr-xr-x prepare-commit-msg.sample
-rwxr-xr-x update.sample
其实钩子脚本可以用任何语言来写,只要你让程序返回相应的退出码即可。
正常的代码合并流程是我们本地修改后,提交PR请求并通过Github的CI检查,然后进行代码审查,最后合并到主干。 但是,一个好的习惯是在代码提交之前保证代码不会出现语法错误等基本问题,比如通过flake8和PEP8标准。
这时候我们可以使用pre-commit,一个Github的开源项目,它本质上是一个脚本,给项目添加了一个钩子函数,可以保证我们在提交或者推送代码之前检查代码的质量。
pre-commit-hooks项目包含了目前支持的钩子脚本,即开箱即用的钩子脚本集合。 钩子脚本的相应内容是用Python语法写的。
# 安装方式
$ pip install pre-commit
# 指定hook类型(即在哪里检查)
$ pre-commit install -f --hook-type pre-push
# 配置需要执行的检查
$ cat .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.9.2
hooks:
- id: trailing-whitespace
- id: flake8
# 执行push操作时检查
$ git push origin master
10.快速克隆大型项目
在处理大型项目时,拉取代码会花费大量时间!
如果我们要为Linux或者Python这样的大型项目做贡献,首先遇到的问题就是如何快速的将项目clone到本地。 因为项目提交历史很多,仓库庞大,再加上国内网络的问题,当项目彻底拉下来的时候,我们的积极性可能会降低。
幸运的是,Git 也帮我们想到了这样的问题。 我们可以通过–depth参数值拉取远程仓库上最新的提交历史,不包含项目历史,即.git/objects/目录下的对象只是本地的,不包含生成的对象通过之前的多次修改。
# 克隆不包含之前历史
$ git clone http://xxx.xx.xxx/xxx --depth=1
但是,有时我们可能需要克隆仓库中某个标签版本对应的内容。 如果我们直接使用clone命令无法做到,就需要进行如下操作才能完美解决。
# 克隆特定版本代码
$ git init xxx-15-0-1
$ git remote add origin http://xxx.xx.xxx/xxx
$ git -c protocol.version=2 fetch origin 15.0.1 --depth=1
$ git checkout FETCH_HEAD
上面的效果基本可以满足我们的日常需求,但是很遗憾,你现在接了一个机器学习的项目,里面有大量的lfs文件,现在clone会变得很慢。 您可以使用以下操作来避免它。 Git工具主动拉取lfs文件到达目录。
# 克隆不包含LFS数据
$ GIT_LFS_SKIP_SMUDGE=1 git clone http://xxx.xx.xxx/xxx
11.如何处理工作中断
如果是多渠道运营,也可以高效开发!
比如我们目前在一个分支中给项目添加一个小功能。 这个时候产品经理找到你,说线上环境有bug,需要你修复。 但是,我们添加的小功能目前还没有完成。
如果此时我们直接切换到主分支,那么之前的分支还没有来得及提交的所有内容都会被带到主分支。 这是我们不想看到的情况。 此时我们需要保存之前分支的工作状态,等我们修改完线上的bug后再继续工作。
幸运的是,Git 也帮我们想到了这样的问题。 我们可以使用stash子命令来帮助我们将当前工作区和暂存区的变化保存到栈中。 需要处理的时候,弹出栈中的内容,我们再开发。
➜ git stash -h
usage: git stash list []
or: git stash show [] []
or: git stash drop [-q|--quiet] []
or: git stash ( pop | apply ) [--index] [-q|--quiet] []
or: git stash branch []
or: git stash clear
or: git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [-m|--message ]
[--pathspec-from-file= [--pathspec-file-nul]]
[--] [...]]
or: git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] []
# 存储当前的修改但不用提交commit
$ git stash
# 保存当前状态包括untracked的文件
$ git stash -u
# 展示所有stashes信息
$ git stash list
# 回到某个stash状态
$ git stash apply
# 删除储藏区
$ git stash drop
# 回到最后一个stash的状态并删除这个stash信息
$ git stash pop
# 删除所有的stash信息
$ git stash clear
# 从stash中拿出某个文件的修改
$ git checkout --
其实更安全的方式是将当前所有的修改都推送到远程仓库中。 这样做的好处是我们可以远程备份我们的修改,不用担心本地文件丢失等问题。 当我们需要继续开发的时候,拉下相应的内容,然后想办法补救,比如使用–amend或者reset命令。
# 将工作区和暂存区覆盖最近一次提交
$ git commit --amend
$ git commit --amend -m "some_info"
# 回退到指定版本并记录修改内容(--mixed)
# 本地仓库覆盖到工作区(保存回退文件内容修改)
$ git reset a87f328
$ git reset HEAD~
$ git reset HEAD~2
$ git reset ~2
$ git reset --mixed
# 本地仓库覆盖到工作区(不保留修改直接删除掉)
$ git reset --soft
# 本地仓库覆盖到工作区(保留修改并加到暂存区)
$ git reset --hard
【Linux常用命令速查手册】关注【入门小站】,后台回复“1001”即可领取。
最近的文章