git

本文总阅读量

1.开发流程

2.log

优雅的输出git日志

1
git log --decorate --oneline --graph

3.tag

项目每次发一个版本可以打一个tag, 利于代码版本管理

  • $ git tag -a '<tag_name>' -m '<description>' 提交一个tag
  • $ git tag -n1 显示一个 tag
  • $ git push origin --tags 上传tag
  • $ git pull origin --tags 下载合并tag
  • $ git push origin :refs/tags/<tag_name> 删除远程 tags

4.第一次提交使用git push –set-upsteam

在第一次提交时, 建议使用–set-upsteam, 后续在push时就可以直接使用git push, 比如有个项目名叫demo:

  • 使用push的情况:
    1
    2
    3
    4
    # 第一次push
    git push demo
    # 后续push
    git push demo
  • 使用–set-upsteam的情况:
    1
    2
    3
    4
    # 第一次push
    git push demo --set-upsteam
    # 后续push
    git push
  • 简写:
    1
    2
    3
    4
    # 第一次push
    git push -u demo
    # 后续push
    git push

    5.git merge –no-ff

    在使用merge时, 如果分支没有冲突, 则会把被merge分支的指针指到merge的分支。而使用git merge --no-ff则是在新的分支上进行合并, 合并时会保留当前分支的信息。

6.commit规范

每个项目的都应该有个一统一的commit规范,个人比较喜欢的规范是:

1
git commit -m"<issue_id>:<file change>:<operating>:<info>"
  • issue_id: 代表一个issue的id, 在准备写功能或者修复一个bug时,都应该先提一个issue,然后在针对这个issue提交代码
  • file change: 代表文件的变化, 如增加, 删除, 修改;也有人使用+,-,*来分别代表增加, 删除, 修改
  • operating: 代表本次代码变化, 具体有如下几种
    • feat:新功能
    • fix:修复bug
    • doc:文档改变
    • style:代码格式改变
    • refactor:某个已有功能重构
    • perf:性能优化
    • test:增加测试
    • build:改变了build工具 如 grunt换成了 npm
    • revert:撤销上一次的commit
  • info: 简要的说明本次提交信息

git pull 强制覆盖本地

一般我们需要同步线上分支时,需要重新强制拉取线上分支覆盖到本地

1
2
3
4
5
6
7
8
# 从远程拉取所有内容
git fetch --all

# reset 本地代码
git reset --hard origin/master

# 重启拉取对齐
git pull

回退到某个分支

很多时候我们经常需要回退到某个分支,这时就需要git reset,为了保险起见,需要确保在执行git reset前,能知道当前分支的commit或者查看reflog的commit,确保有后悔药可以吃

1
git reset <commit> --hard

git reset简单粗暴,就是把索引指向某个commit,并做对应的操作,使用方法

1
git reset <commit>

具体参数定义如下:

1
2
3
4
5
--soft 回退后a分支修改的代码被保留并标记为add的状态(git status 是绿色的状态)
--mixed 重置索引,但不重置工作树,更改后的文件标记为未提交(add)的状态。默认操作。
--hard 重置索引和工作树,并且a分支修改的所有文件和中间的提交,没提交的代码都被丢弃了。
--merge --hard类似,只不过如果在执行reset命令之前你有改动一些文件并且未提交,merge会保留你的这些修改,hard则不会。【注:如果你的这些修改add过或commit过,merge和hard都将删除你的提交】
--keep --hard类似,执行reset之前改动文件如果是a分支修改了的,会提示你修改了相同的文件,不能合并。如果不是a分支修改的文件,会移除缓存区。git status还是可以看到保持了这些修改。

如果要reset到上个提交,可以直接写

1
git reset --hard HEAD^

移除git的某些commit

执行该命令,会撤销对应的提交,然后再重新提交一个新的提交(该操作可以撤销,如果使用reset则会直接回退到某个commit,除非有reflog记录或记得某个commit id,不然无法撤销).
再执行git revert前,必须执行

1
2
git diff <commit1> HEAD
git diff <commit1>..<commit2>

查看要撤销的代码是否正确,再使用 git revert 可以撤销指定的提交,要撤销一串提交可以用 .. 语法。 注意这是一个前开后闭区间,即不包括 commit1,但包括 commit2。

1
2
3
git revent <commit>
# 可以指定一个前开后闭的区间被移除
git revent <commit>...<commit>

另外一种移除git的某些commit方法

假设有commig v1,v2,v3,v4,v5共5个commit, 下面的操作会移除v3这个commit

1
2
3
4
5
6
# 从 v2 切分支出来
git checkout -b fixing v2
# 合并 v4,保持代码不变
git merge -s ours v4
# 合并 v5(也就是当前罪行的分支)
git merge master

git将一个分支完全覆盖另外一个分支

如下代码,把线上的master分支覆盖本地的develop分支

1
2
3
4
5
6
# 切换到develop
git checkout develop
# 指向origin/master
git reset --hard origin/master
# 强拉
git push -f

git hooks文件指定

git hook文件一般为bash.sh文件, 只要把文件放置于项目的.git/hooks下即可让该项目的git命令享用hook套餐

如果觉得每个项目都需要配置一次很麻烦, 则可以创建一个文件夹存放hooks文件,并调用全局命令git config --global core.hookspath xxx指定.

如果自己的电脑上有自己的项目和公司的项目, 想用两套hook文件, 也还是有办法的:

  • 1.首先自己的项目和公司的项目是在不同的文件夹里面, 比如我自己的项目在/home/so1n/github, 公司的项目在/home/so1n/xxx_gitlab下面.
  • 2.修改~/.gitconfig
    1
    2
    [includeIf "gitdir:~/xxx_gitlab/"]
    path = .gitconfig-xxx
  • 3.创建一个与上面path一样的文件~/.gitconfig-xxx,并写入如下配置, 指定该项目需要的hook文件
    1
    2
    [core]
    hookspath = ~/.git-hooks

    git禁止在master分支push和commit

    本小节来源

作为管理者,在远端将master分支设为保护分支,可以从根源上杜绝直接推送到master的问题, 每个平台的选项有所不同.
作为开发者,在本地的git hook中加配置可以做到在commit和push操作时做对应的检查.

禁止在master分支上Commit

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh

# 指定受保护的分支
protected_branch='master'

# 获取当前的分支
current_branch=$(git rev-parse --symbolic --abbrev-ref HEAD)

# 判断当前分支是否为受保护的分支, 如果是则打印警告, 并且退出
if [ "$protected_branch" == "$current_branch" ]; then
echo ".git/hooks: Do not commit to $current_branch branch"
exit 1
fi

在master分支上Commit时提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/sh

protected_branch='master'
current_branch=$(git rev-parse --symbolic --abbrev-ref HEAD)

if [ "$protected_branch" == "$current_branch" ]; then
# 打印提示 并等待操作...
read -p "You're about to commit to master, is that what you intended? [y|n] " -n 1 -r </dev/tty
echo
# 如果是y则通过, 否则退出
if echo "$REPLY" | grep -E '^[Yy]$' >/dev/null; then
exit 0
fi
exit 1
fi

禁止推送到master分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/sh

protected_branch='master'
remote_branch_prefix="refs/heads/"
protected_remote_branch=$remote_branch_prefix$protected_branch

while read local_ref local_sha remote_ref remote_sha
do
if [ "$protected_remote_branch" == "$remote_ref" ]; then
echo ".git/hooks: Do not commit to $protected_branch branch"
exit 1
fi
done

exit 0

推送时如果commit消息包含WIP则禁止推送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/sh

protected_branch='master'
remote_branch_prefix="refs/heads/"
protected_remote_branch=$remote_branch_prefix$protected_branch

while read local_ref local_sha remote_ref remote_sha
do
if [ "$protected_remote_branch" == "$remote_ref" ]; then
read -p "You're about to push master, is that what you intended? [y|n] " -n 1 -r < /dev/tty
echo
if echo $REPLY | grep -E '^[Yy]$' > /dev/null
then
exit 0 # push will execute
fi
exit 1 # push will not execute
fi
done

exit 0
查看评论