git指南

前言

关于版本控制

Version control

版本控制(version control),或称为修订控制(revision control)、源控制(source control),其作用是保存源代码、配置文件和工程文件的修改历史,保证开发人员能够定位错误、回滚版本

大致经历3个阶段:

  1. 保存副本
  2. 集中式修订控制(centralized revision control)
  3. 分布式修订控制(distributed revision control)

最简单的版本控制方式就是备份每次修改后的文件,优点在于操作简单,缺点在于占用存储大,在大文件情况下非常耗时,也不利于多人同时开发

第二和第三种方式都是通过保存文件修改记录来进行版本控制

第二种是集中式修订控制,即设立服务器来保存源代码和所有人修改的记录,上传和回滚版本通过服务器进行

优点在于这样可以保证占用的存储大大减小,同时仅需上传和下载修改记录就能够进行版本上传和更新,减小操作时间,并且能够有效协调地理隔离下的多人开发;管理员能控制开发人员权限

缺点在于仅在中央服务器保存了所有的修改记录,一旦损坏无法修复; 无法在同一版本库中同时进行多方向开发

https://git-scm.com/book/en/v2/images/centralized.png

第三种是分布式修订控制,同一个开发库可以进行多分支开发,客户端也保存了所有分支的修改记录

优点:多分支开发;相比于集中式修订控制,仅需在拉取其他开发人员的变更和自己的提交记录时需要连接服务器,所以一些常用操作更加快捷,比如提交记录,浏览历史和回退操作

缺点:增加了客户端开发库的体积;操作较复杂

https://git-scm.com/book/en/v2/images/distributed.png

使用集中式修订控制方式的版本控制系统:svn

使用分布式修订控制方式的版本控制系统:git

关于git

工作区域

git3个工作区域概念:

  1. 仓库(repository)
  2. 工作目录(working directory)
  3. 暂存区域(staging area)

git仓库是保存项目元数据和所有修改记录的地方(就是.git文件夹)

工作目录保存从仓库中按某个版本取出的文件(和.git同一级目录下的其他文件)

暂存区域在仓库目录下,保存了下次要提交的修改记录

所以工程文件处于3种状态下

  1. 已修改(modified):表示文件已修改
  2. 已暂存(staged):表示已保存修改记录,还未提交到仓库
  3. 已提交(commited):保存在仓库,生成一个新的版本

https://git-scm.com/book/en/v2/images/areas.png

基础操作

帮助

获取命令参数

输入命令直接回车即可

zj@zj-ThinkPad-T470p:~$ git config
usage: git config [<options>]

Config file location
    --global              use global config file
    --system              use system config file
    --local               use repository config file
    -f, --file <file>     use given config file
    --blob <blob-id>      read config from given blob object

Action
    --get                 get value: name [value-regex]
    --get-all             get all values: key [value-regex]
    --get-regexp          get values for regexp: name-regex [value-regex]
    --get-urlmatch        get value specific for the URL: section[.var] URL
    --replace-all         replace all matching variables: name value [value_regex]
    --add                 add a new variable: name value
    --unset               remove a variable: name [value-regex]
    --unset-all           remove all matches: name [value-regex]
    --rename-section      rename section: old-name new-name
    --remove-section      remove a section: name
    -l, --list            list all
    -e, --edit            open an editor
    --get-color           find the color configured: slot [default]
    --get-colorbool       find the color setting: slot [stdout-is-tty]

Type
    --bool                value is "true" or "false"
    --int                 value is decimal number
    --bool-or-int         value is --bool or --int
    --path                value is a path (file or directory name)

Other
    -z, --null            terminate values with NUL byte
    --name-only           show variable names only
    --includes            respect include directives on lookup

获取使用手册

git包含了许多命令,可以通过命令查找使用手册

git help command-name
# 或
git command-name --help
# 或
man git-<command-name>

比如配置环境变量git config,获取其使用手册

git help config
# 或
git config --help
# 或
man git-config

git配置

命令行操作范围

使用命令git config进行配置,各级别设置需要加上可选参数

# 系统级别
git config --system ...
# 全局级别
git config --global ...
# 仓库级别
git config --local ...
打印配置信息
# 打印所有信息
$ git config --list
user.email=505169307@qq.com
user.name=zhujian
core.editor=code
# 打印全局信息
$ git config --global --list
user.email=505169307@qq.com
user.name=zhujian
core.editor=code

常用设置

  1. 设置用户信息
  2. 设置文本编辑器
  3. 移除变量
设置用户信息

每次提交时都需加上用户名和邮箱地址,可以设置成全局变量

git config --global user.name user-name
git config --global user.email email-address

设置节点为user,设置属性为nameemail

设置文本编辑器

git使用系统默认的编辑器,可以设置指定编辑器

# 未设置
git config --global core.editor vim
# 已设置,先删除再添加
git config --global --unset core.editor
git config --global core.editor vim
移除变量
# 移除单个变量
git config --global --unset 属性名
# 移除所有变量
git config --global --unset-all 属性名

配置文件地址

git3个级别的配置文件:

  1. local:本地文件存放在仓库中(.git/config),只针对当前仓库
  2. global:全局文件存放为~/.gitconfig~/.config/git/config,作用于当前用户的所有仓库
  3. system:系统文件存放为/etc/gitconfig,作用于系统的每个用户

其优先级为local > global > system

windows环境下的全局配置文件存放为C:\\User\\$USER\\.gitconfig

编写格式

所有配置文件的编写格式都一样,可参考仓库级别的config文件,按照section和key进行配置

# 仓库级别
$ cat config 
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = git@github.com:zjZSTU/linux-guide.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master
# 全局级别
$ cat .gitconfig 
[user]
    name = zxxx
    email = 50xxxxx.com

获取git仓库

本地新建

在工程目录中输入如下命令

git init

生成一个.git文件夹

远程拉取

从远程服务器(github/gitee等)获取地址后拉取到本地

git clone <repo> [<dir>]

<repo>表示输入地址

git clone https://github.com/zjZSTU/git-guide.git

也可制定路径

git clone https://github.com/zjZSTU/git-guide.git ../git-repo/

或文件夹

git clone https://github.com/zjZSTU/git-guide.git guide

下载并绑定指定分支

git clone --branch [标签名/分支名] [git地址]

提交新版本

在本地创建git仓库后,就可以对文件进行版本控制.文件分为2种状态:已追踪(tracked)和未追踪(untracked)

未追踪(untracked)文件就是未添加到版本控制的文件

已追踪的文件可分为已修改(modified),已暂存(staged)和未修改(unmodified)文件

基本的git工作流程如下:

  1. 在工作目录下添加文件,修改文件或删除文件
  2. 保存文件状态到暂存区域
  3. 提交修改记录到仓库

https://git-scm.com/book/en/v2/images/lifecycle.png

查看文件状态

使用命令git status查看文件状态,同时会给出相应文件状态下的命令

$ git status 
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

$ git status 
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

    modified:   docs/source/index.rst

Untracked files:
(use "git add <file>..." to include in what will be committed)

    .vscode/
    docs/source/git/

no changes added to commit (use "git add" and/or "git commit -a")

仅需查看文件状态

$ git status -s
A  .vscode/settings.json
M docs/source/index.rst
?? docs/source/git/

符号A表示已暂存,符号M表示已修改,符号??表示未追踪

其中已修改文件有如下3种状态

$ git status -s
 M README
MM Rakefile
M  lib/simplegit.rb

符号_M表示已修改还未加入暂存区域,符号MM表示已加入暂存区域的已修改文件又被修改了,符号M_表示添加到暂存区域的已修改文件

添加修改文件

使用命令git add将未追踪文件或已修改文件添加到暂存区域

git add name1 name2 ...
# 添加所有文件
git add *
忽略文件

编辑.gitignore文件,将整个工程文件中不想添加版本控制的文件加入进去

所有空行或者以 # 开头的行都会被 Git 忽略。
可以使用标准的 glob 模式匹配。
匹配模式可以以(/)开头防止递归。
匹配模式可以以(/)结尾指定目录。
要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。

可参考github/gitignore,上面包含了很多不同开发环境下的.gitignore文件

如果git add添加了忽略文件会报错,可以使用-f强制添加

git add -f name1 name2 ...

提交暂存文件

git commit -m '提交信息'

或者git commit然后回车,会打开设置的编辑器进行操作

撤销暂存文件

对添加到暂存区域的文件使用命令

git reset HEAD <file>...

撤销已修改文件

对已修改文件使用命令

git checkout -- <file>...    

标签设置

参考:2.6 Git 基础 - 打标签

gitlog设置的基础上添加了标签设置,其目的是标识重要的提交记录(比如标识发布版本)

下面以opencv版本库为例

标签分类

标签分为两种

  1. 注解标签(annotated tag)
  2. 命名标签(named tag)

注解标签用于版本管理,命名标签用于私有或者临时标注

注解标签包含创建日期、标签名、e-mail、标签信息和可选的GPG签名

命名标签仅包含标签名,所以称为轻量级标签(lightweight tag

创建标签

常用参数如下:

  1. -a, --annotate:生成一个未签名的注解标签对象
  2. -s, --sign:生成一个GPG签名的标签,使用默认e-mail地址作为键
  3. -u <keyid>, --local-user=<keyid>:生成一个GPG签名的标签,使用给定的键
  4. -m <msg>, --message=<msg>:添加标签消息
  5. -F <file>, --file=<file>:带有标签消息的文件
注解标签

使用上述参数-a/-s/-u生成的标签称.git/refs/tags标签,注解标签必须添加消息,可以使用参数-m或者-F,否则会打开一个编辑器

$ git tag -a v1.1 -m "添加一个注解标签"
命名标签

不使用任何参数,直接添加标签名的就是命名标签

$  git tag v1.1
给过去提交记录创建标签

默认给当前记录创建标签,还可以给过去记录创建标签

查看记录

$ git log --pretty=oneline
9e1b1e5389237c2b9f6c7b9d7715d9836c0a5de1 OpenCV 3.4.2
a0baae8a559d31c22ae08976b41cc99e046ba52b Merge pull request #11875 from dkurt:dnn_fix_reshape
9a66331984c5b0f0a71d6208a8237fc410ae923e Merge pull request #11882 from alalek:videoio_vfw_lower_priority
...
...

打标签时指定提交签名(头几位就行了)

$ git tag v0.3 0660c1108f969a -a -m "给过去提交记录打标签"

查看标签

查看所有标签

$ git tag
2.2
...
...
4.0.1
4.0.1-openvino

查看指定系列标签,使用参数-l

# 列出4.x版本
$ git tag -l 4.*
4.0.0
4.0.0-alpha
4.0.0-beta
4.0.0-openvino
4.0.0-rc
4.0.1
4.0.1-openvino

查看某一标签信息

$ git show 标签名
# 对于注解标签而言
$ git show 4.0.0
tag 4.0.0                                                  # 标签名
Tagger: Alexander Alekhin <alexander.a.alekhin@gmail.com>  # 标签者
Date:   Sun Nov 18 09:19:48 2018 +0000                     # 标签日期

OpenCV 4.0.0                                               # 标签信息
...
...
# 对于命名标签而言
$ git show v1.1                                            # 仅显示提交记录
commit a92571f31ddc7ea959b99e129f6c4705a404c36b
Author: zhujian <505169307@qq.com>
Date:   Thu Feb 28 20:37:01 2019 +0800

共享标签

github利用标签进行github release的发布

但是默认情况下不会传输标签到远程托管服务器,需要单独显式推送

$ git push origin v1.0
# 或者全部推送
$ git push origin --tags

文件解析

创建的标签在.git/refs/tags文件夹下

删除标签

本地删除

使用参数-d来删除标签

$ git tag -d v1.1
Deleted tag 'v1.1' (was a92571f)
远程删除

同样需要单独显式删除远程标签

# 删除版本v1.1
$ git push origin :refs/tags/v1.1

切换标签

切换到指定标签,创建分支同时指定标签名

# 利用标签内容创建分支,并切换到分支
$ git checkout -b 分支名 标签名

子模块

参考:7.11 Git 工具 - 子模块

开发个人网站的过程中需要用到主题仓库,所以会在一个仓库的里面嵌套另一个仓库,如果直接将子仓库加入版本管理,很多文件无法进行版本化,所以想到了子模块的操作

创建子模块

使用git submodule命令添加子模块

$ git submodule add git@github.com:zjZSTU/hexo-theme-next.git blogs/themes/next
Cloning into 'blogs/themes/next'...
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 5402 (delta 0), reused 0 (delta 0), pack-reused 5401
Receiving objects: 100% (5402/5402), 5.29 MiB | 1.03 MiB/s, done.
Resolving deltas: 100% (3294/3294), done.
Checking connectivity... done.

除了添加子仓库之外,还生成了一个配置文件.gitmodules

$ git status 
On branch dev
Your branch is ahead of 'origin/dev' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

    new file:   .gitmodules
    new file:   blogs/themes/next

.gitmodules里面保存了子模块的地址和远程仓库链接

$ cat .gitmodules 
[submodule "blogs/themes/next"]
    path = blogs/themes/next
    url = git@github.com:zjZSTU/hexo-theme-next.git

管理子模块

主仓库只能提示子模块中的文件修改,必须子模块自己进行添加和上传

比如在next文件夹内新建文件hellogit

# 对于主模块而言
$ git status 
On branch dev
Your branch is up-to-date with 'origin/dev'.
Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)
    (commit or discard the untracked or modified content in submodules)

    modified:   next (untracked content)

no changes added to commit (use "git add" and/or "git commit -a")
# 对于子模块而言
$ git status 
HEAD detached at 0c5ed6f
Untracked files:
    (use "git add <file>..." to include in what will be committed)

    hellogit

nothing added to commit but untracked files present (use "git add" to track)

克隆子模块

克隆主仓库后子模块的文件夹在但是没有文件,还需要进一步克隆子仓库

# 初始化本地配置文件
$ git submodule init
# 拉取远程仓库
$ git submodule update

集成代码如下:

$ git submodule update --init --recursive

更新子模块

如果子模块有更新,可以进入子模块路径进行更新

$ git pull

或者在主仓库更新所有子模块

$ git submodule foreach git pull 

可能会遇到如下错误:

Entering 'blogs/themes/next'
You are not currently on a branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
    git pull <remote> <branch>
Stopping at 'blogs/themes/next'; script returned non-zero status.
The command "git submodule foreach git pull" failed and exited with 1 during .

需要指定哪个分支,要么修改更新语句如下:

$ git submodule foreach git pull origin master

要么设置远程关联分支

$ git branch --set-upstream-to=origin/remote_branch  your_branch
# 或
git branch --set-upstream your_branch origin/remote_branch

忽略子模块

参考:submodule..ignore

当在子模块执行完成修改提交后,在主模块仍会显示子模块待提交

$ git status 
On branch dev
Your branch is up-to-date with 'origin/dev'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   blogs/themes/next (new commits)

需要修改.gitmoduleignore属性,用于定义何种情况下使用git status或者diff命令会显示子模块的修改状况

4个可选的属性值

  1. all:子模块永远不会被认为是已修改的(但是当它处于暂存阶段时仍旧会在状态和提交命令的输出中)
  2. dirty:子模块工作树上的所有改变都会被忽略,只考虑子模块的HEAD与其在父项目中记录的状态之间的差异
  3. untracked:仅仅子模块未追踪的文件会被忽略。对于已追踪文件的提交差异和修改会显示
  4. none:默认选项。不会忽略所有对于子模块的修改,会显示所有的对于提交差异以及对于已追踪或未追踪的文件修改

所以设置属性ignoreall就能忽略子模块变化

[submodule "blogs/themes/next"]
	path = blogs/themes/next
	url = https://github.com/zjZSTU/hexo-theme-next.git
	ignore = all

[github][gitee]SSH公钥设置

[github]公钥设置

参考:Adding a new SSH key to your GitHub account

登录github,进入个人主页,选择Settings->SSH and GPG keys->New SSH key

填写title,并复制个人主机中的公钥到key

$ cat github_id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDIUsnRNu/+Zeurm2Ty/Xac+q+0y87aHWr0N7fb+gygQK7xRux9UAI7UK3GwP7dwxYT8KTOrxf0tPnI4d/bCtETHcoj0D2UlwMO20FWFaoOocteccJLbmQ9XXsrt05KlJcDGr9L8e9eMyWKj5ZXMOxSwravv+e64XGkFaAmNX4XkCkENlZcIq6w+Fo/xtyPZA0W/oYnBWAvKd937GbKEA1m1rARzo/xGCn8uf76PUlNy2UscujnWbMUYGeLxf5jRCgWD0KZLJsJAsOCxczeRn0EAczgNcgyBbjotDnpTKbP6Xtqvf3TtVty9fgQgs7/oLNb8K+v0XVVR9XsX+DZkqF+JCzIOf+0dpnJiery187rw4MuRIlct+vGM1P+FaiPphh2vMhe5YEaFErrHNTDZiw+LbHCE3Eo9wK9t0wb/yFiHELempoZI+K3Zjv6cnUnl3urXQFHV/RJCj/JHOEA1Q3jvVLa3nETRMfBBEiDuK0YPH6OUac+GSg+ldCVwDydeCa2Z4/OdgZqsoyTU5o4vewbT3vKwmQGfu7BNOmHgnIzMZQsg0JWScDX9/c/DoCaGW95ej68niZ3ICtUWnlYKZVlzg+cyIRzuzSmaa5ecOgzxZ6moW4wRGrvMM94X0HMmWWXXV/WLXiUG2KN1upz7m78zwJ6ZXvh2sWmCe+q+YhJIw== github.com

github上设置的公钥同时具备推送/拉取的权限

密钥失效

参考:About SSH

If you haven't used your SSH key for a year, then GitHub will automatically delete your inactive SSH key as a security precaution. For more information, see "Deleted or missing SSH keys."

github规定自动删除一年内未使用的SSH密钥

[gitee]公钥设置

参考:

公钥管理

SSH 公钥设置

码云可以在个人主页上设置公钥,也可以在个人仓库上设置公钥,这两个地方的读写权限不一致

个人主页

登录码云,选择设置->安全设置->SSH公钥,在添加公钥页面输入标题和公钥即可设置

在个人主页上设置的公钥同时具备推送/拉取的权限

个人仓库

进入仓库页面,选择管理->部署公钥管理->公钥管理,点击添加公钥选项,输入标题和公钥即可

在个人仓库上设置的公钥仅具备拉取仓库代码的功能

测试

参考:Testing your SSH connection

github上设置好公钥之后

SSH
zj-github
d9:fc:d8:02:ee:6c:75:68:a1xxx Added on Feb 4, 2019 Last used within the last week — Read/write 

在本地测试是否成功

$ ssh -T git@github.com
Hi zjZSTU! You've successfully authenticated, but GitHub does not provide shell access.
$ ssh -T git@gitee.com
Hi zjZSTU! You've successfully authenticated, but GITEE.COM does not provide shell access.

下载代码

选择仓库,以ssh协议下载

$ git clone git@github.com:zjZSTU/git-guide.git

这一次会要求你输入设置好的密码(passphrase),之后的拉取代码操作都可以自动认证完成

[ssh][http]传输方式切换

通过修改本地文件,切换代码传输方式

命令修改

查看当前使用SSH还是HTTP

$ git remote -v
origin	git@gitee.com:zjZSTU/zjzstu.gitee.io.git (fetch)
origin	git@gitee.com:zjZSTU/zjzstu.gitee.io.git (push)

当前使用SSH协议进行代码拉取和推送

方式一:仅修改拉取协议为HTTP

$ git remote set-url --push origin https://gitee.com/zjZSTU/zjzstu.gitee.io.git
$ git remote -v
origin	git@gitee.com:zjZSTU/zjzstu.gitee.io.git (fetch)
origin	https://gitee.com/zjZSTU/zjzstu.gitee.io.git (push)

方式二:同时修改拉取和推送协议为HTTP

# 先增加HTTP
$ git remote set-url --add origin https://gitee.com/zjZSTU/zjzstu.gitee.io.git
$ git remote -v
origin	git@gitee.com:zjZSTU/zjzstu.gitee.io.git (fetch)
origin	https://gitee.com/zjZSTU/zjzstu.gitee.io.git (push)
# 再删除SSH
$ git remote set-url --delete origin git@gitee.com:zjZSTU/zjzstu.gitee.io.git
$ git remote -v
origin	https://gitee.com/zjZSTU/zjzstu.gitee.io.git (fetch)
origin	https://gitee.com/zjZSTU/zjzstu.gitee.io.git (push)

文件修改

修改文件.git/configremote小节

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    url = git@gitee.com:zjZSTU/zjzstu.gitee.io.git
[branch "master"]
    remote = origin
    merge = refs/heads/master
[branch "dev"]
    remote = origin
    merge = refs/heads/dev

方式一:仅修改拉取协议为HTTP

添加pushurl = ...remote小节

...
[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    url = git@gitee.com:zjZSTU/zjzstu.gitee.io.git
    pushurl = https://gitee.com/zjZSTU/zjzstu.gitee.io.git
[branch "master"]
...

方式二:同时修改拉取和推送协议为HTTP

修改urlHTTP即可

...
[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    url = https://gitee.com/zjZSTU/zjzstu.gitee.io.git
...

进阶操作

工作目录和裸仓库分离

参考:

GIT_DIR和GIT_WORK_TREE的妙用,工作区和仓储可隔离

通过git自动部署WEB服务上的PHP代码,提交即生效

8.3 自定义 Git - Git 钩子

裸仓库指.git文件夹,工作目录指除.git文件夹外的其他文件

使用场景之一如下:

  1. 本地将修改好的代码上传到远程裸仓库
  2. 裸仓库获取到代码后,通过钩子生成工作目录,放置在其他路径下
  3. 服务器再对工作目录进行操作,比如nginx对静态文件进行托管

.git/hooks文件夹下新建post-receive文件

$ vim post-receive

添加如下内容

#!/bin/sh

git --work-tree=工作目录 --git-dir=裸仓库 checkout -f

授予执行权限

$ sudo chmod +x post-receive

自建服务器

参考:

搭建 Git 服务器

4.4 服务器上的 Git - 配置服务器

在云服务器上自建git服务器,最简单的方式就是创建裸仓库然后用ssh的方式进行代码的拉取和推送,参考创建裸仓库

下面实现一个规范的git服务器,类似第三方服务器(github、coding、gitlab等),其支持sshhttp协议的仓库操作,但不允许进行远程登录

第三方仓库名如下:

  • https://<domain-name>/<user-name>/<repo-name>.git
  • git@<domain-name>:<user-name>/<repo-name>.git

操作步骤如下:

  1. 创建用户git
  2. ssh设置
  3. 下载仓库

创建用户git

$ sudo adduser git
正在添加用户"git"...
正在添加新组"git" (1000)...
正在添加新用户"git" (1000) 到组"git"...
创建主目录"/home/git"...
正在从"/etc/skel"复制文件...
输入新的 UNIX 密码: 
重新输入新的 UNIX 密码: 
passwd: password updated successfully
Changing the user information for git
Enter the new value, or press ENTER for the default
    Full Name []: 
    Room Number []: 
    Work Phone []: 
    Home Phone []: 
    Other []: 
这些信息是否正确? [Y/n]

/data目录下创建目录repositories,然后在里面创建一个裸仓库

# 创建目录
$ sudo mkdir -p /data/repositories
# 设置用户和组
$ sudo chown -R git:git /data/repositories
# 设置可访问权限
$ sudo chmod 755 /data/repositories/
# 创建裸仓库
$ git init --bare hello.git
Initialized empty Git repository in /data/repositories/hello.git/
perl: warning: Falling back to a fallback locale ("en_US.utf8").
$ sudo adduser zhujian
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
    LANGUAGE = (unset),
    LC_ALL = (unset),
    LC_MEASUREMENT = "zh_CN.UTF-8",
    LC_PAPER = "zh_CN.UTF-8",
    LC_MONETARY = "zh_CN.UTF-8",
    LC_NAME = "zh_CN.UTF-8",
    LC_ADDRESS = "zh_CN.UTF-8",
    LC_NUMERIC = "zh_CN.UTF-8",
    LC_TELEPHONE = "zh_CN.UTF-8",
    LC_IDENTIFICATION = "zh_CN.UTF-8",
    LC_TIME = "zh_CN.UTF-8",
    LANG = "en_US.utf8"
    are supported and installed on your system.
perl: warning: Falling back to a fallback locale ("en_US.utf8").
Adding user `zhujian'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+VVpw5kOZBmzJYz/vngYpAAV61Fq9oChSflQFkfzr1sKHRqq2/sqeZD3gzPQZbrKWcHbuGCWyQOvm1gH+67gW+TpUO9DWeeHqo3h5rlCW+ElJcL/q4b+ZBVEmGDjzE+Sg+6wM+izBl5xzHDFeLhN3Yw1OVc2rwQFQ/CD6FSKdL4b5bt0/5rpu65sv7haXjfDMSEsIVgPY5behLzZzoXy81iN4/tPF3cjDsn/x5Yywc60LdslJ5hW5wlozhq1LibUXk9JQu/+5DDZKi8ytMEoe1S7yROvaC/ofJQR22hINnFoLNBC8gSFM2YR+t9oBF0eiAaVwfgddA0+ScYrWA5Yr zj@zj-ThinkPad-T470p ...
Adding new group `zhujian' (1000) ...
Adding new user `zhujian' (1000) with group `zhujian' ...
Creating home directory `/home/zhujian' ...
...
...

提示语言包问题,参考perl: warning: Falling back to a fallback locale (“en_US.UTF-8”)

$ sudo apt install locales-all
git is not in the sudoers file. This incident will be reported.

是因为git没有添加到sudo权限,参考xxx is not in the sudoers file.This incident will be reported.的解决方法

  1. 先切换到ubuntu用户

     $ su ubuntu
    
  2. 赋予sudo文件写权限

     $ sudo chmod u+w /etc/sudoers
    
  3. 修改文件/etc/sudoers

     $ sudo vim /etc/sudoers
     # 添加
     youuser ALL=(ALL) ALL
    

    表示允许用户youuser执行sudo命令(需要输入密码)

  4. 撤销sudo文件写权限

     $ sudo chmod u-w /etc/sudoers
    
  5. 重新切换回原来用户

     $ su git
    

设置ssh连接

将客户端的ssh公钥添加到用户git.ssh/authorized_keys文件中,即可实现ssh连接

$ mkdir .ssh && chmod 700 .ssh
$ touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys
设置git-shell连接

设置用户git仅能操作git相关内容,可以将用户git的登录shell设置为git-shell

#查看用户git登录shell
$ cat /etc/passwd | grep git
git:x:1000:1000:,,,:/home/git:/bin/bash

修改为git-shell

# 查找
$ which git-shell
/usr/bin/git-shell
# 修改
$ sudo vim /etc/passwd
# 修改后
$ cat /etc/passwd | grep git
git:x:1000:1000:,,,:/home/git:/usr/bin/git-shell
publickey denied

ssh连接失败,这个问题很迷,耗了1天时间,网上找了很多资料没有结果,有一个博客主也遇到了同样的问题,他最后解决是因为突然就可以了,我也一样

下载仓库

下载仓库hello.git到本地

$ git clone git@132.232.142.219:/data/repositories/hello.git
Cloning into 'hello'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
$ cd hello/
$ ls -al
total 12
drwxrwxr-x 3 zj zj 4096 3月   9 16:29 .
drwxr-xr-x 4 zj zj 4096 3月   9 16:29 ..
drwxrwxr-x 7 zj zj 4096 3月   9 16:29 .git

添加文件hello.txt,加入版本管理并上传

$ touch hello.txt
$ ls
hello.txt
$ git add hello.txt 
$ git commit -m "hello git"
[master (root-commit) 24265ed] hello git
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hello.txt
$ git push -u origin master 
Counting objects: 3, done.
Writing objects: 100% (3/3), 207 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@132.232.142.219:/data/repositories/hello.git
* [new branch]      master -> master
Branch master set up to track remote branch master from origin.
配置config

参考:干货 | 简单几步搭建一个远程git服务器

使用config文件可以简易仓库下载地址,在本地~/.ssh文件夹下新建配置文件config,添加如下

$ cat config 
host git-server
    user server
    hostname 132.232.142.219
    port 22
    identityfile ~/.ssh/tencent_id_rsa
# 设置访问权限
$ sudo chmod 600 config

注意:用tab键保持文档格式

config文件指定了主机名git-server配置的用户名、ip地址、端口号和私钥地址

添加配置文件后下载仓库地址如下

$ git clone git-server:/data/repositories/hello.git
Cloning into 'hello'...
remote: Counting objects: 3, done.
Receiving objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Checking connectivity... done.

推送到多个远程托管服务器

参考:Git - Pushing code to two remotes [duplicate]

将代码推送到多个远程托管服务器,多重备份保证安全性,比如推送到githubcoding

有两种情况

  1. 同时推送代码到多个仓库
  2. 指定推送的地址,比如本次操作仅推送到github,不推送到coding

首先查看当前已追踪的远程仓库地址

$ git remote -v
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (fetch)
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (push)

当前已追踪了coding服务器上的仓库,需要加入github仓库

针对多个远程仓库的推送,使用SSH传输协议可以提高操作便捷性

同时推送

命令行操作

添加github仓库地址

$ git remote set-url --add --push origin https://github.com/zjZSTU/TEST.git
$ git remote -v
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (fetch)
origin	https://github.com/zjZSTU/TEST.git (push)

再重新添加coding的仓库地址

$ git remote set-url --add --push origin https://git.dev.tencent.com/zjZSTU/TEST.git
$ git remote -v
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (fetch)
origin	https://github.com/zjZSTU/TEST.git (push)
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (push)

这样就可以同时推送到多个远程仓库

$ git push -u origin master 
Username for 'https://github.com': zjZSTU
Password for 'https://zjZSTU@github.com': 
...
...
To https://github.com/zjZSTU/TEST.git
d19a01e..4b76b5d  master -> master
Branch master set up to track remote branch master from origin.
Username for 'https://git.dev.tencent.com': zjZSTU
Password for 'https://zjZSTU@git.dev.tencent.com': 
...
...
To https://git.dev.tencent.com/zjZSTU/TEST.git
d19a01e..4b76b5d  master -> master
Branch master set up to track remote branch master from origin.
文件操作

修改.git文件夹下的CONFIG文件

# 原先
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = https://git.dev.tencent.com/zjZSTU/TEST.git

# 修改为
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = https://git.dev.tencent.com/zjZSTU/TEST.git
pushurl = https://github.com/zjZSTU/TEST.git
pushurl = https://git.dev.tencent.com/zjZSTU/TEST.git

origin小节中添加属性pushurl,设置多个远程仓库地址

指定推送

指定推送操作和同时推送操作能够同时并存在同一个仓库中

默认情况下使用origin作为远程仓库名,可以添加另外的名字,在旗下指定新的远程仓库

当前操作如下:默认origin指向coding,添加github指向github仓库,添加all指向githubcoding

命令行操作

添加github远程

$ git remote add github https://github.com/zjZSTU/TEST.git
$ git remote -v
github	https://github.com/zjZSTU/TEST.git (fetch)
github	https://github.com/zjZSTU/TEST.git (push)
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (fetch)
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (push)

添加all远程

$ git remote add all https://github.com/zjZSTU/TEST.git
$ git remote set-url --add --push all https://git.dev.tencent.com/zjZSTU/TEST.git
$ git remote set-url --add --push all https://github.com/zjZSTU/TEST.git
$ git remote -v
all	https://github.com/zjZSTU/TEST.git (fetch)
all	https://git.dev.tencent.com/zjZSTU/TEST.git (push)
all	https://github.com/zjZSTU/TEST.git (push)
github	https://github.com/zjZSTU/TEST.git (fetch)
github	https://github.com/zjZSTU/TEST.git (push)
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (fetch)
origin	https://git.dev.tencent.com/zjZSTU/TEST.git (push)

上传代码

# 上传到coding
git push -u origin master
# 上传到github
git push -u github master
# 同时上传
git push -u all master
文件操作

修改.git文件夹下的CONFIG文件,添加githuball小节

[remote "github"]
    url = https://github.com/zjZSTU/TEST.git
    fetch = +refs/heads/*:refs/remotes/github/*
[remote "all"]
    url = https://github.com/zjZSTU/TEST.git
    fetch = +refs/heads/*:refs/remotes/all/*
    pushurl = https://git.dev.tencent.com/zjZSTU/TEST.git
    pushurl = https://github.com/zjZSTU/TEST.git

创建裸仓库

参考:4.2 服务器上的 Git - 在服务器上搭建 Git

裸仓库,也就是不包含当前工作目录的仓库,即.git文件夹,可作为服务器git仓库

有两种方式,一是使用本地已存在的仓库,二是新建远程裸仓库

本地导出

从已有仓库中导出.git文件夹,有两种方式

  1. 使用git命令

     git clone --bare my_project my_project.git
    
     $ git clone --bare TEST TEST.git
     Cloning into bare repository 'TEST.git'...
     done.
    
  2. 使用cp命令

     cp -rf my_project/.git my_project.git
    
放置在远程服务器

在服务器上新建文件夹git

$ pwd
/home/ubuntu/git

复制本地裸仓库到服务器

$ scp -r TEST.git ubuntu@132.232.142.219:/home/ubuntu/git/TEST.git

这样在git文件夹下就有了裸仓库TEST.git

$ pwd
/home/ubuntu/git/TEST.git

远程新建

在远程服务器新建文件夹,并在其中初始化为裸仓库

$ mkdir TE.git
$ cd TE.git
$ git init --bare
Initialized empty Git repository in /home/ubuntu/git/TE.git/
# 如果想要同一组内的其他用户也可访问该仓库,添加参数--shared来修改仓库权限
$ git init --bare --shared
Initialized empty shared Git repository in /home/ubuntu/git/TE.git/

克隆裸仓库

github操作方式类似

git clone ubuntu@132.232.142.219:/home/ubuntu/git/TEST.git

本地分支操作

参考:

3.1 Git 分支 - 分支简介

3.2 Git 分支 - 分支的新建与合并

7.3 Git 工具 - 储藏与清理

主要学习内容:

  1. 分支显示、创建和切换
  2. 分支暂存
  3. 分支删除
  4. 分支合并

使用命令

  1. git branch
  2. git checkout
  3. git stash
  4. git merge

分支显示、创建和切换

显示所有分支以及当前所处分支

$ git branch

创建新分支testing

$ git branch testing

切换到新分支testing

$ git checkout testing

创建新分支aa并自动切换

$ git checkout -b aa

分支暂存

在切换分支之前最好先保存当前在暂存区域的内容,使用git stash命令将其保存在一个目录中

暂存当前工作状态

$ git stash

可进行多次暂存操作,查看暂存列表

$ git stash list

重新释放最新的暂存内容

$ git stash apply

指定暂存节点进行释放

$ git stash apply 节点名

清除所有的暂存节点

$ git stash clear

分支删除

删除指定分支

$ git branch -d 分支名

分支合并

合并指定分支到当前分支

$ git merge 分支名

在分支合并出现冲突时可以使用命令回复到之前为合并状态

$ git merge --abort

不合并其他分支的提交历史

# 仅添加文件,不进行提交
$ git merge --squash
# 添加文件并进行提交
$ git merge --no-squash

使用参数--merged--no-merged过滤已合并和未合并到当前分支的分支

远程分支操作

参考:3.5 Git 分支 - 远程分支

学习本地分支与远程分支的交互

  1. 分支显示
  2. 分支同步
  3. 分支推送
  4. 分支跟踪
  5. 分支删除

使用命令

  1. git branch
  2. git fetch
  3. git push
  4. git checkout
  5. git merge

分支显示

显示本地分支和远程分支的关系

# 使用参数-vv列出本地分支是否有跟踪远程分支,并且本地分支是否领先或者落后远程分支
$ git branch -vv

分支同步

获取远程分支数据

$ git fetch 远程仓库 # origin是默认的远程仓库

使用git fetch不会合并远程分支,需要再显式使用git merge命令

使用git pull可实现拉取远程分支数据并合并

$ git pull 远程仓库 本地分支名

分支推送

推送本地分支内容到远程分支

$ git push 远程仓库 本地分支名/远程分支名

如果需要本地分支和远程分支名一致,实现如下:

$ git push 远程仓库 本地分支名

使用参数-u设置要推送的远程分支为本地待跟踪,方便后续拉取代码操作

$ git push -u 远程仓库 本地分支

分支跟踪

设置本地分支想要跟踪的远程分支

$ git checkout -b [branch] [remotename]/[branch]

如果远程分支和本地分支一致,那么使用简易方式

$ git checkout --track [remotename]/[branch]

修改要跟踪的远程分支,使用参数-u--set-upstream-to

$ git branch -u [branch] [remotename]/[branch]
# 同样的分支名
$ git branch -u [remotename]/[branch]

分支删除

删除远程分支

$ git push 远程仓库 --delete 远程分支名

如果远程分支已在本地分支之后,可以不删除远程分支而使用强制方式推送到远程分支,参考[Git高级教程(二)] 远程仓库版本回退方法

$ git push -f

拉取指定远程分支到本地

由于git去中心化的特性,下载远程仓库时会将所有分支都下载下来,不仅耗时而且占用本地磁盘容量

参考git 拉取远程分支到本地,拉取指定远程分支到本地

思路

首先在本地新建git仓库,然后下载指定远程分支,最后创建本地分支并关联到指定远程分支即可

实现

新建仓库

$ mkdir gitrepo
$ cd giterpo
$ git init

拉取远程指定分支

$ git remote add origin https://github.com/zjZSTU/zjzstu.github.com.git
$ git fetch origin dev

新建本地分支并关联到指定远程分支

$ git checkout -b dev origin/dev

remote: error: cannot lock ref ‘refs/heads/master’: ref refs/heads/master

Travis CI上上传文件到远程仓库,出现如下问题

remote: error: cannot lock ref 'refs/heads/master': ref refs/heads/master

起因

我在上传提交时中间中断了

$ git push -u origin dev
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 556 bytes | 0 bytes/s, done.
Total 6 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), completed with 5 local objects.
^C

然后再更新文章后再一次提交,就出现了远程仓库锁定的问题

解决

参考:git: error: cannot lock ref, error: cannot lock ref

将远程仓库下载到本地,然后执行如下命令

git remote prune origin

其作用是同步本地远程分支,删除本地已过时的远程分支

重新触发Travis CI执行,能够成功更新了。

远程仓库

[GitLab]安装

硬件配置

参考:Requirements

官方推荐的硬件配置为:

  1. 2 cores CPU
  2. 8GB RAM

安装

进入gitlab/gitlab-ce查询

首先安装依赖项

$ curl -s https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
Detected operating system as Ubuntu/xenial.
Checking for curl...
Detected curl...
Checking for gpg...
Detected gpg...
Running apt-get update... done.
Installing apt-transport-https... done.
Installing /etc/apt/sources.list.d/gitlab_gitlab-ce.list...done.
Importing packagecloud gpg key... done.
Running apt-get update... done.

The repository is setup! You can now install packages.

然后安装gitlab-ce

# 当前是ubuntu 16.04
$ sudo apt-get install gitlab-ce

也可以下载.deb包后安装

$ sudo dpkg -i gitlab-ce_12.3.5-ce.0_amd64.deb 
Selecting previously unselected package gitlab-ce.
(Reading database ... 368157 files and directories currently installed.)
Preparing to unpack gitlab-ce_12.3.5-ce.0_amd64.deb ...
Unpacking gitlab-ce (12.3.5-ce.0) ...
Setting up gitlab-ce (12.3.5-ce.0) ...
It looks like GitLab has not been configured yet; skipping the upgrade script.

       *.                  *.
      ***                 ***
     *****               *****
    .******             *******
    ********            ********
   ,,,,,,,,,***********,,,,,,,,,
  ,,,,,,,,,,,*********,,,,,,,,,,,
  .,,,,,,,,,,,*******,,,,,,,,,,,,
      ,,,,,,,,,*****,,,,,,,,,.
         ,,,,,,,****,,,,,,
            .,,,***,,,,
                ,*,.
  


     _______ __  __          __
    / ____(_) /_/ /   ____ _/ /_
   / / __/ / __/ /   / __ `/ __ \
  / /_/ / / /_/ /___/ /_/ / /_/ /
  \____/_/\__/_____/\__,_/_.___/
  

Thank you for installing GitLab!
GitLab was unable to detect a valid hostname for your instance.
Please configure a URL for your GitLab instance by setting `external_url`
configuration in /etc/gitlab/gitlab.rb file.
Then, you can start your GitLab instance by running the following command:
  sudo gitlab-ctl reconfigure

For a comprehensive list of configuration options please see the Omnibus GitLab readme
https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md

系统配置

打开文件/etc/gitlab/gitlab.rb,修改external_url为自己的地址

$ sudo cat /etc/gitlab/gitlab.rb | grep external_url
[sudo] password for zj: 
Sorry, try again.
[sudo] password for zj: 
##! For more details on configuring external_url see:
##external_url 'http://gitlab.example.com'
external_url 'http://localhost:8800'
...
...

重新进行gitlab配置

$ sudo gitlab-ctl reconfigure
Starting Chef Client, version 14.13.11
resolving cookbooks for run list: ["gitlab"]
Synchronizing Cookbooks:
  - gitlab (0.0.1)
  - package (0.1.0)
  - redis (0.1.0)
  - postgresql (0.1.0)
  - monitoring (0.1.0)
  - registry (0.1.0)
  - mattermost (0.1.0)
  - consul (0.1.0)
  - gitaly (0.1.0)
  - letsencrypt (0.1.0)
  - nginx (0.1.0)
  - runit (4.3.0)
  - crond (0.1.0)
  - acme (4.0.0)
Installing Cookbook Gems:
Compiling Cookbooks...
...
...
Running handlers:
Running handlers complete
Chef Client finished, 526/1417 resources updated in 02 minutes 32 seconds
gitlab Reconfigured!

登录修改后的网址:http://localhost:8800

_images/gitlab-start.png

管理员root的密码设置完成后就自动跳转到登录页面

可以直接使用root用户登录,也可以注册一个新帐号

[GitLab]命令行启动、停止和重启

  • 启动

    sudo gitlab-ctl start
    
  • 停止

    sudo gitlab-ctl stop
    
  • 重启

    sudo gitlab-ctl restart
    

GitLab导致8080端口冲突

问题复现

安装完GitLab后,修改配置文件/etc/gitlab/gitlab.rb

##external_url 'http://gitlab.example.com'
external_url 'http://localhost:8800'

本以为这样就能修改GitLab端口号为8800了,没想到再次登录8080仍旧出现了GitLab页面

_images/8080.png

问题解析

查询哪个程序监听了8080端口

# netstat -lnp | grep 8080
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      26436/config.ru 

查询相应的进程

# netstat -lnp | grep 26436
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      26436/config.ru 
unix  2      [ ACC ]     STREAM     LISTENING     343538   26436/config.ru     /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket

仍然是GitLab在监听8080端口,参考gitlab 8.13 80 8080端口冲突问题,查看配置文件unicorn.rb

# This file is managed by gitlab-ctl. Manual changes will be
# erased! To change the contents below, edit /etc/gitlab/gitlab.rb
# and run `sudo gitlab-ctl reconfigure`.

# What ports/sockets to listen on, and what options for them.
listen "127.0.0.1:8080", :tcp_nopush => true

默认情况下unicorn同样监听8080端口,查询/etc/gitlab/gitlab.rb中相应的设置

# cat gitlab.rb | grep unicorn
#unicorn['port'] = 8800

解决方案

需要在gitlab.rb上同时修改unicorn监听端口号,修改配置文件/etc/gitlab/gitlab.rb如下

##external_url 'http://gitlab.example.com'
external_url 'http://localhost:8800'
unicorn['port'] = 8801

重新启动GitLab

# gitlab-ctl reconfigure
# gitlab-ctl restart

查询配置文件/var/opt/gitlab/gitlab-rails/etc/unicorn.rb

# cat unicorn.rb | grep listen
# What ports/sockets to listen on, and what options for them.
listen "127.0.0.1:8801", :tcp_nopush => true

测试端口号

$ curl localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused
$ curl localhost:8800
<!DOCTYPE html>
<html>
<head>
  <meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
...
...
# curl localhost:8801
<html><body>You are being <a href="http://localhost:8801/users/sign_in">redirected</a>.</body></html>

[GitLab][nginx]反向代理

当前gitlab登录路径是localhost:8080

$ curl localhost:8800
<html><body>You are being <a href="http://localhost:8800/users/sign_in">redirected</a>.</body></html>

地址会跳转到http://localhost:8800/users/sign_i,下面通过反向代理简化登录地址

调整gitlab登录地址

修改gitlab配置文件/etc/gitlab/gitlab.rb,修改external_url访问路径

external_url 'http://localhost:8800/gitlabs/'

更新gitlab配置

$ gitlab-ctl reconfigure
$ gitlab-ctl restart

nginx配置

修改nginx配置文件/etc/nginx/conf.d/default.conf,新增location

$ cat gitlab.conf 
server {
    ...
    ...
    location /gitlabs/ {
	    proxy_pass http://localhost:8800;
    }
    ...
    ...
}

刷新nginx

$ sudo nginx -t
$ sudo nginx -s reload

之后输入localhost/gitlabs/即可登录gitlab

[Docker]GitLab使用

参考:

GitLab Docker images

gitlab docker Web界面打开反应迟钝的解决办法

GitLab Docker 容器的内存优化之路

gitlab的docker安装,非标准端口,如何处理?

gitlab提供了官方镜像 - gitlab/gitlab-ce

启动容器

运行以下命令启动容器

$ docker run --detach \
  --publish 7010:7010 \
  --publish 7020:22 \
  --name gitlab \
  --restart always \
  --volume /srv/gitlab/config:/etc/gitlab \
  --volume /srv/gitlab/logs:/var/log/gitlab \
  --volume /srv/gitlab/data:/var/opt/gitlab \
  gitlab/gitlab-ce:latest

等待容器状态从Up 1 second (health: starting)变为Up 3 minutes (healthy)

  • 绑定7010端口(作用于后续的external_url
  • 绑定主机7020端口到容器22端口
  • 挂载主机/srv/gitlab文件夹到容器
    • /etc/gitlab:Gitlab配置文件
    • /var/log/gitlab:日志
    • /var/opt/gitlab:应用数据

设置external_url

进入gitlab容器

$ docker exec -it COMTAINER_ID bash

修改配置文件/etc/gitlab/gitlab.rb,添加external_url属性

external_url 'http://IP_ADDRESS:7010'          # 指定IP地址,设置访问地址
nginx['listen_port'] = 7010
gitlab_rails['gitlab_shell_ssh_port'] = 7020

在容器内部重启gitlab服务

$ gitlab-ctl reconfigure
$ gitlab-ctl restart

也可以退出系统后,重新启动容器

$ docker restart CONTAINER_ID

等待容器状态从Restarting (1) 19 seconds agoUp 18 minutes (healthy)

启动浏览器登录http://localhost:7010

_images/gitlab-docker-start.png

[问题]容器一直重启

使用docker logs命令查询

$ docker log CONTAINER_ID
...
...
System Info:
------------
chef_version=14.13.11
platform=ubuntu
platform_version=16.04
ruby=ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
program_name=/opt/gitlab/embedded/bin/chef-client
executable=/opt/gitlab/embedded/bin/chef-client


Running handlers:
There was an error running gitlab-ctl reconfigure:

/etc/gitlab/gitlab.rb:1: unexpected fraction part after numeric literal
external_url = 192.168.0.144:7002
               ^~~~~~~
/etc/gitlab/gitlab.rb:1: syntax error, unexpected tINTEGER, expecting end-of-input
external_url = 192.168.0.144:7002
                         ^~~

Running handlers complete
Chef Client failed. 0 resources updated in 01 seconds

发现是external_url配置失误导致。解决方法如下:

  1. 停止gitlab容器
  2. 修改配置文件(在主机中)/srv/gitlab/config/gitlab.rb
  3. 重新启动容器

[GitLab][Webhook]不允许本地连接

gitlab仓库中设置Webhook,使用本地连接出现如下错误:

Url is blocked: Requests to the local network are not allowed

参考:gitlab使用webhook向jenkins发送请求,报错 Requests to the local network are not allowed

登录root账户,点击Configure Gitlab选项

_images/configure-gitlab.png

进入Settings -> Network,展开Outbound requests,选中Allow requests to the local network from web hooks and services

_images/allow-local-request.png

[GitLab]重置密码

昨天新建的账户,今天突然等不上去了,参考以下文章重置密码

gitlab 重置用户密码

gitlab管理员密码忘记如何强制重置找回

进入gitlab管理后台。当前使用docker版本gitlab,所以先进入容器

$ docker exec -it CONTIANINER_ID bash

切换到git用户

$ su git

登录gitlab-rails

$ gitlab-rails console production
DEPRECATION WARNING: Passing the environment's name as a regular argument is deprecated and will be removed in the next Rails version. Please, use the -e option instead. (called from require at bin/rails:4)
--------------------------------------------------------------------------------
 GitLab:       12.4.2 (393a5bdafa2)
 GitLab Shell: 10.2.0
 PostgreSQL:   10.9
--------------------------------------------------------------------------------
Loading production environment (Rails 5.2.3)
irb(main):001:0>

(上面这一步有点慢),查询用户是否存在

irb(main):001:0> user=User.where(name: "zxxU").first
=> nil
irb(main):002:0> user=User.where(name: "zxxu").first
=> nil
irb(main):003:0> user=User.where(name: "zxxxn").first
=> #<User id:2 @zxxxU>

充值密码并确认

irb(main):005:0> user.password=12345678
=> 12345678
irb(main):006:0> user.password_confirmation=12345678
=> 12345678

保存并退出

irb(main):007:0> user.save
Enqueued ActionMailer::DeliveryJob (Job ID: 2b220e18-d0ac-415e-a1cc-52f31069d3be) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", #<GlobalID:0x00007f4e84a25510 @uri=#<URI::GID gid://gitlab/User/2>>
=> true
irb(main):008:0> quit

[GitLab]受保护分支

强制上传代码到Gitlab仓库的dev分支出错,提示如下:

GitLab: You are not allowed to force push code to a protected branch on this project

参考:解决 GitLab: You are not allowed to force push code to a protected branch on this project问题

是由于分支保护的原因,需要进入仓库setting -> Repository -> Protected Branches

_images/protected-branches.png

允许dev分支能够强制推送

unicorn出错

问题描述

通过docker部署gitlab一段时间后,突然出现502错误

查看容器状态,显示不健康

CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                 PORTS                                                           NAMES
89481faa2ed1        gitlab/gitlab-ce:latest   "/assets/wrapper"        2 weeks ago         Up 7 hours (unhealthy)

浏览容器日志,发现如下错误:

==> /var/log/gitlab/unicorn/unicorn_stdout.log <==
bundler: failed to load command: unicorn (/opt/gitlab/embedded/bin/unicorn)

==> /var/log/gitlab/unicorn/unicorn_stderr.log <==
ArgumentError: Already running on PID:460 (or pid=/opt/gitlab/var/unicorn/unicorn.pid is stale)
  /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:205:in `pid='
  /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:137:in `start'
  /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/unicorn-5.4.1/bin/unicorn:126:in `<top (required)>'
  /opt/gitlab/embedded/bin/unicorn:23:in `load'
  /opt/gitlab/embedded/bin/unicorn:23:in `<top (required)>'

==> /var/log/gitlab/unicorn/current <==
2019-12-18_12:27:47.34406 failed to start a new unicorn master
2019-12-18_12:27:47.35882 starting new unicorn master
2019-12-18_12:27:47.91735 master failed to start, check stderr log for details

问题解析

看样子是unicorn的问题,参考:

gitlab服务器502恢复过程

gitlab docker Web界面打开反应迟钝的解决办法

进入容器内部,查看unicorn状态

$ docker exec -it xxxx bash
# gitlab-ctl tail unicorn

发现每次unicorn显示的PID都不同,修改/etc/gitlab/gitlab.rb,添加

unicorn['listen'] = 'localhost'
unicorn['port'] = 8999

更新并重启后,gitlab服务恢复正常

# gitlab-ctl reconfigure
# gitlab-ctl restart

[GitHub]版本发布

之前通过本地进行版本标记,然后上传到github,在release页面能够查询到相应的版本。参考标签设置

_images/local-release.png

但是查看其他的github项目的release页面,发现发布的版本中还包含了多个压缩文件(比如Windows/Linux/MacOS),可以单独下载

_images/remote-release.png

学习GitHub文档About releasesCreating releases后发现必须通过在线标签设置的方式才能额外上传单独的压缩文件

提交信息规范

引言

参考:

Commit message 和 Change log 编写指南

如何写好 Git commit log?

优雅的提交你的 Git Commit Message

每次进行git提交时,需要写提交说明,规范提交说明的好处如下

  1. 更加结构化的提交历史
  2. 保证每次信息都有确切的含义
  3. 方便直接生成changelog
  4. 方便信息搜索和过滤
  5. 提交日志规范
  6. 提交日志工具
  7. 检查日志工具
  8. 版本化工具
  9. 如何利用git hooks自制工具

Angular消息准则

目前最受开发人员肯定的规范是前端框架Angular提出的提交消息准则

提交格式如下:

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

每次提交可以包含页眉(header)、正文(body)和页脚(footer),每次提交必须包含页眉内容

每次提交的信息不超过100个字符

详细文档:AngularJS Git Commit Message Conventions

页眉设置

页眉的格式指定为提交类型(type)、作用域(scope,可选)和主题(subject)

提交类型

提交类型指定为下面其中一个:

  1. build:对构建系统或者外部依赖项进行了修改
  2. ci:对CI配置文件或脚本进行了修改
  3. docs:对文档进行了修改
  4. feat:增加新的特征
  5. fix:修复bug
  6. pref:提高性能的代码更改
  7. refactor:既不是修复bug也不是添加特征的代码重构
  8. style:不影响代码含义的修改,比如空格、格式化、缺失的分号等
  9. test:增加确实的测试或者矫正已存在的测试
作用域

范围可以是任何指定提交更改位置的内容

主题

主题包括了对本次修改的简洁描述,有以下准则

  1. 使用命令式,现在时态:“改变”不是“已改变”也不是“改变了”
  2. 不要大写首字母
  3. 不在末尾添加句号

正文设置

和主题设置类似,使用命令式、现在时态

应该包含修改的动机以及和之前行为的对比

页脚设置

Breaking changes

不兼容修改指的是本次提交修改了不兼容之前版本的API或者环境变量

所有不兼容修改都必须在页脚中作为中断更改块提到,以BREAKING CHANGE:开头,后跟一个空格或者两个换行符,其余的信息就是对此次修改的描述,修改的理由和修改注释

BREAKING CHANGE: isolate scope bindings definition has changed and
    the inject option for the directive controller injection was removed.
    
    To migrate the code follow the example below:
    
    Before:
    
    。。。
    。。。
    
    After:
    
    。。。
    。。。
    
    The removed `inject` wasn't generaly useful for directives so there should be no code using it.
引用提交的问题

如果本次提交目的是修改issue的话,需要在页脚引用该issue

以关键字Closes开头,比如

Closes #234

如果修改了多个bug,以逗号隔开

Closes #123, #245, #992

回滚设置

当此次提交包含回滚(revert)操作,那么页眉以"revert:"开头,同时在正文中添加"This reverts commit hash",其中hash值表示被回滚前的提交

revert:<type>(<scope>): <subject>
<BLANK LINE>
This reverts commit hash
<other-body>
<BLANK LINE>
<footer>

Conventional提交规范

Conventional Commits(约定式提交)脱胎于Angular提交信息准则,提供了更加通用、简洁和灵活的提交规范

提交格式如下:

<类型>[可选的作用域]: <描述>
# 空一行
[可选的正文]
# 空一行
[可选的脚注]

页眉设置

强制支持的类型名是fixfeat,同样支持Angular准则中推荐的其他类型

常规提交规范还推荐使用类型improvement,表示在不添加新功能或修复bug的情况下改进当前的实现

页尾设置

强制支持在页脚使用BREAKING CHANGES

提交规范

结合RFC2019,在下面规范中使用关键字来指示需求级别

  • MUST(必须)
  • MUST NOT(禁止)
  • REQUIRED(需要)
  • SHALL(应当)
  • SHALL NOT(不应当)
  • SHOULD(应该)
  • SHOULD NOT(不应该)
  • RECOMMENDED(推荐)
  • MAY(可以)
  • OPTIONAL(可选)
  1. 每次提交必须添加类型名为前缀。类型名是一个名词,比如feat、fix,后跟冒号和一个空格
  2. 类型feat必须在提交新特征时使用
  3. 类型fix必须用于修复bug的提交
  4. 类型后面可以添加一个可选的作用域。作用域名是一个短语,表示代码库中的一个小节,比如fix(parser)
  5. 在类型/作用域前缀后面必须跟上一个简短描述,关于本次提交的代码修改,比如fix: 字符串中包含多个空格时的数组分析问题
  6. 可以在描述后面添加一个长的正文,用于提供额外的上下文信息。正文内容必须在短描述后空一行开始
  7. 可以在正文后空一行开始页脚,页脚应该包含这次代码修改的相关问题,比如Fixes #13
  8. 不兼容修改(breaking change)必须放置在提交的页脚或正文部分的最开始处,必须使用大写文本BREAKING CHANGE作为前缀,后跟冒号和一个空格
  9. BREAKING CHANGE:后面必须提供一个描述,关于API的修改,比如BREAKING CHANGE: 环境变量目前优先于配置文件
  10. 页脚必须包含不兼容修改、额外链接、问题引用和其他元信息
  11. 除了featfix以外的类型可以在提交信息中使用

徽章

使用了常规提交规范可以添加徽章在README

[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)

实现示例

只有页眉和页尾

feat: allow provided config object to extend other configs

BREAKING CHANGE: `extends` key in config file is now used for extending other config files

只有页眉

docs: correct spelling of CHANGELOG

使用了作用域

feat(lang): added polish language

修复了bug

fix: minor typos in code

see the issue for details on the typos fixed

fixes issue #12

FAQ

问:在最初开发阶段如何处理提交信息?

建议像已发布产品一样处理。即使是你的软件开发伙伴在使用软件过程中,也想知道那些被修复了,那些是不兼容修改。

问:提交头的类型名是大写还是小写?

无所谓,不过最好保持一致(一直大写或一直小写)

问:如果提交内容适用于多个类型怎么办?

尽可能返回并进行多次提交。常规提交准则的优势在于它有能力驱动我们进行更加组织化的提交和PR

问:这是否是不鼓励快速开发和快速迭代?

它不鼓励以无序的方式快速开发。它帮助你在有多个开发者的多个项目中进行长期开发

问:常规提交准则是否会限制提交的类型?

常规提交准则鼓励开发者使用更多有确切含义的类型。除此以外,准则的灵活性允许开发组提出更多适用于自己的类型,并随着时间的推移更改这些类型

问:这个准则和SemVer的联系?

fix类型应该翻译成PATCH版本。feat类型应该翻译成MINOR版本。如果提交信息中包含不兼容修改,不管哪种类型,都应该翻译成MAJOR版本

问:如何将扩展版本转换为常规提交规范?

我们建议使用SemVer发布您自己对本规范的扩展(并鼓励您进行这些扩展!)

问:如果我不小心使用了错误的提交类型,该怎么办?

  1. 使用了规范中的类型但是没有使用正确的类型,比如使用fix替代了feat

在合并或者发布这个错误之前,推荐使用git rebase -i进行提交历史编辑。发布之后,依据使用的工具或流程进行清理

  1. 当使用了规范外的类型,比如使用了feet替代了feat

如果提交不符合常规提交规范,它只是意味着基于规范的工具将错过这次提交

问:是否我的所有贡献者都需要使用常规提交规范?

如果使用基于squashGit工作流,主管维护者可以在合并时清理提交信息——这不会对普通提交者产生额外的负担。常见的工作流程是让git系统自动从pull requestsquash出提交,并向主维护者提供一份表单,用以在合并时输入适合的git提交信息。

添加提交模板

参考:优雅的提交你的 Git Commit Message

默认模板

在提交时打开编译器,默认会出现一段模板信息,提示你编辑提交信息,同时会提示当前修改的文件

$ git commit

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#   (use "git push" to publish your local commits)
#
# Changes to be committed:
#	modified:   ...
#   deleted:    ...
#

自定义模板

可以自己编辑一个模板文件,比如新建~/.gitmessage,编辑一段关于提交规范的注释

$ vim ~/.gitmessage

# head: <type>(<scope>): <subject>
# - type: feat, fix, docs, style, refactor, test, chore
# - scope: can be empty (eg. if the change is a global or difficult to assign to a single component)
# - subject: start with verb (such as 'change'), 50-character line
#
# body: 72-character wrapped. This should answer:
# * Why was this change necessary?
# * How does it address the problem?
# * Are there any side effects?
#
# footer: 
# - Include a link to the ticket, if any.
# - BREAKING CHANGE
#

修改全局配置文件~/.gitconfig,添加

[commit]
    template = ~/.gitmessage

再次提交时显示的模板如下

# head: <type>(<scope>): <subject>
# - type: feat, fix, docs, style, refactor, test, chore
# - scope: can be empty (eg. if the change is a global or difficult to assign to a single component)
# - subject: start with verb (such as 'change'), 50-character line
#
# body: 72-character wrapped. This should answer:
# * Why was this change necessary?
# * How does it address the problem?
# * Are there any side effects?
#
# footer: 
# - Include a link to the ticket, if any.
# - BREAKING CHANGE
#

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is ahead of 'all/master' by 2 commits.
#   (use "git push" to publish your local commits)
#
# Changes to be committed:
#       new file:   .gitignore
#
# Untracked files:
#       package.json
#

文件解析

在仓库内的.git/COMMIT_EDITMSG文件中保存了最近一次提交的日志(包括模板信息)

提交信息交互工具Commitizen

Commitizen是一个提交日志工具,辅助开发者使用提交规则

预配置

需要安装NodeJS,参考:nodeJS安装

安装Commitizen

# 全局安装
$ npm install -g commitizen

安装Adapter

Commitizen支持多种不同的提交规范,可以安装和配置不同的适配器实现

Conventional Commit规范为例

全局配置(推荐)
# 安装
$ npm install -g cz-conventional-changelog
# 配置
$ echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc
本地配置
# 安装
commitizen init cz-conventional-changelog --save-dev --save-exact

安装完成后,查看是否在package.json中已加入cz-conventional-changelog信息:

...
...
  "devDependencies": {
    "cz-conventional-changelog": "^3.0.2"
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  }
}
测试

安装完成后,使用git-cz替代git commit完成提交操作,

$ git cz
cz-cli@4.0.3, cz-conventional-changelog@3.0.2

? Select the type of change that you're committing: (Use arrow keys)
❯ feat:     A new feature 
  fix:      A bug fix 
  docs:     Documentation only changes 
  style:    Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 
  refactor: A code change that neither fixes a bug nor adds a feature 
  perf:     A code change that improves performance 
  test:     Adding missing tests or correcting existing tests 
(Move up and down to reveal more choices)

通过提示信息辅助你完成标准化的提交日志

git-cz支持git commit所有的参数设置

正文换行使用\n操作,比如正文内容如下:新增事项:\n1. 测试1\n2. 测试2\n3. 测试

build(npm): 加入package.json

新增事项:
1. 测试1
2. 测试2
3. 测试

徽章

README中添加commitizen友好徽章

https://img.shields.io/badge/commitizen-friendly-brightgreen.svg

校验消息工具commitlint+husky

参考:commitlint

使用commitizen可以规范化提交信息,同样的,可以设置工具来检查提交信息是否符合格式要求

commitlint用于检查提交信息

huskyhook工具,作用于git-commitgit-push阶段

使用场景:

  1. 本地信息检查
  2. CI信息检查

commitlint

全局安装
$ npm install -g @commitlint/cli @commitlint/config-conventional
本地安装
$ npm install --save-dev @commitlint/{cli,config-conventional}
配置规范
# 使用conventional规范
$ echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js
测试

需要全局安装才能在命令行使用commitlint

# 错误示例,类型后跟一个冒号和一个空格
$ echo "docs:添加新的文档" | commitlint

⧗   input: docs:添加新的文档
✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]
✖   found 2 problems, 0 warnings 
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )
# 正确示例
$ echo "docs: 添加新的文档" | commitlint

⧗   input: docs: 添加新的文档
✔   found 0 problems, 0 warnings 
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )
# 或者
$ commitlint "docs: asdf"

⧗   input: build(npm): 添加package.json
✔   found 0 problems, 0 warnings 
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )

husky

安装
# 本地
$ npm install --save-dev husky
配置

package.json中添加

// package.json
{
    "husky": {
        "hooks": {
            "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
        }  
    }
}

git提交信息提交commitlint检查,参数-E默认将.git/COMMIT_EDITMSG数据添加到HUSKY_GIT_PARAMS`

测试
# 错误提交提交
$ git commit -m "忽略node_modules"
husky > commit-msg (node v10.15.3)

⧗   input: 忽略node_modules
✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]
✖   found 2 problems, 0 warnings 
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )


husky > commit-msg hook failed (add --no-verify to bypass)
# 正确提交
$ git commit -m "build(npm): 忽略node_modules"
husky > commit-msg (node v10.15.3)

⧗   input: build(npm): 忽略node_modules
✔   found 0 problems, 0 warnings 
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )


[master 8728336] build(npm): 忽略node_modules
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
禁用husky

某一次提交想要禁用husky,可以添加参数--no-verify

$ git commit --no-verify -m "xxx"

CI设置

Travis CI为例

在本地安装

$ npm install --save-dev @commitlint/travis-cli

修改travis.yml

language: node_js
script:
    - commitlint-travis

语义版本规范

semver/semver.org提出一个语义版本规范,用于规范版本的生成和设置

英文版:Semantic Versioning 2.0.0

中文版:语义化版本 2.0.0

优势

  1. 标识当前应用版本信息
  2. 管理代码仓库
  3. 管理外部依赖

版本格式

版本号命名如下:

# 英文版
MAJOR.MINOR.PATCH
# 中文版
主版本号.次版本号.修订号

版本号递增规则:

  1. 主版本号(MAJOR version):出现不兼容的API变化
  2. 次版本号(MINOR version):新增向后兼容的功能
  3. 修订号(补丁版本号,PATCH version):修复向后兼容的bug

规范

  1. 使用语义版本控制的软件**必须(MUST)**声明公共APIAPI可以在代码本身中声明,或者严格存在于文档中。不管怎样,它都应该是精确和全面的
  2. 正常版本号**必须(MUST)采用X.Y.Z格式,其中X、YZ是非负整数,并且必须不(MUST NOT)包含前导零。X是主版本,Y是次版本,Z是补丁版本。每个元素必须(MUST)**以数字形式增加。例如:1.9.0 -> 1.10.0 -> 1.11.0
  3. 一旦发布了版本化的包,就**不能再(MUST NOT)**修改该版本的内容。任何修改都必须作为新版本重新发布
  4. 主版本号为0(0.y.z)表示初始开发阶段,可以执行任意的修改。这个阶段的公共API**不应该(SHOULD NOT)**被视为稳定版。
  5. 从版本1.0.0定义的公共API开始,之后版本号的递增方式依赖于此公共API的更改方式
  6. 只要有向后兼容的bug被修复,就**必须(MUST)**递增补丁版本号Z(x.y.Z | x>0)。错误修复被定义为修复错误行为的内部更改
  7. 只要有向后兼容的功能被引入公共API,就**必须(MUST)递增次版本号Y(x.Y.z | x>0)。只要有任何公共API被标记为舍弃(deprecated),就必须(MUST)递增次版本号。如果在私有代码中引入了大量新功能或改进,其中可以(MAY)包括补丁级别的改变,那么可以(MAY)递增次版本号。当次版本号递增时必须(MUST)**设置补丁版本号为0
  8. 只要有不向后兼容的改变被引入公共API,就**必须(MUST)递增主版本号X(X.y.z | x>0),其中可以(MAY)**包括次版本和补丁级别的修改。当主版本号递增时,**必须(MUST)**设置次版本号和补丁版本号为0
  9. 预发布版本**可以(MAY)通过在补丁版本之后附加连字符(-)和一系列点(.)分隔的标识符来表示。标识符必须(MUST)只包含ASCII字母数字和连字符[0-9a-za-z-]。标识符必须不(MUST NOT)为空。数字标识符必须不(MUST NOT)**包含前导0。预发布版本的优先级低于相关的正常版本。预发布版本表示该版本尚不稳定,可能不满足与其关联的正常版本预期的兼容性要求。比如,1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92
  10. 构建元数据**可以(MAY)通过在补丁或预发布版本之后立即附加一个加号和一系列点分隔的标识符来表示。标识符必须(MUST)只包含ASCII字母数字和连字符[0-9a-za-z-]。标识符必须不(MUST NOT)为空。在确定版本优先级时应该(SHOULD)**忽略构建元数据。因此如果有两个版本仅在构建元数据上有区别,那么这两个版本优先级相同。比如,1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85
  11. 优先级是指在排序时版本之间如何进行比较。**必须(MUST)按顺序将版本分离为主、次、补丁和预发布标识符来计算优先级(构建元数据不包含在优先级中)。当从左到右比较每个标识符时,优先级由第一个差异决定,如下所示:主、次和补丁版本总是用数字进行比较。比如,1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。当主、次和补丁的大小相同时,预编译版本的优先级低于正常版本。比如,1.0.0-alpha < 1.0.0。具有相同主版本、次版本和补丁版本的两个预发布版本的优先级必须(MUST)**通过从左到右比较每个点分隔的标识符来确定,直到发现以下差异:仅由数字组成的标识符用数字进行比较,带字母或连字符的标识符按ASCII排序顺序在词法上进行比较。数字标识符总是优先级低于非数字标识符。如果前面的所有标识符都相同,则拥有更长字段集的预发布版本的优先级高于较小字段集的预发布版本。比如,1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0

FAQ

问:应该如何处理0.y.z初始开发阶段的修订?

最简单的方式是初始开发版本从0.1.0开始,然后在每一个后续的阶段递增次版本号

问:什么时候发布1.0.0版本?

下列情况可用于发布1.0.0版本

  1. 软件已经在生产中使用
  2. 已经有用户依赖于稳定的API
  3. 开发过程中非常担心向后兼容性

问:是否语义化规范不鼓励快速开发和快速迭代?

主版本为0的初始开发阶段就是用于快速开发的。如果每天都在改变API,那么软件应该仍旧停留在0.y.z阶段或者在一个单独的开发分支上处理下一个主版本

问:是否对公共API小小的不兼容修改都要递增主版本,那这样的话很快就能达到42.0.0

这就要考验你的开发经验和开发远见。不应将不兼容的更改轻易地引入具有大量依赖代码的软件中,升级所需的成本可能很高。必须推出主版本以发布不兼容的更改意味着你已经考虑了更改的影响,并评估所涉及的成本/效益比

问:为全部公共API生成文档需要做太多的工作了!

为了其他人的使用而去文档化软件,这是作为职业开发者的责任。管理软件复杂性是保持项目高效的一个非常重要的部分,如果没有人知道如何使用您的软件,或者可以安全地调用哪些方法,这很难做到。从长远来看,语义版本控制以及对定义良好的公共API的坚持可以使每个人都能顺利运行

问:如果不小心在次版本发布了一个不兼容改变该怎么办?

一旦意识到已经破坏了语义化版本规范,修复问题并发布一个新的次要版本,以纠正问题并恢复向后兼容性。**即使在这种情况下,修改已发行的版本也是不可接受的。**如果合适,记录违规版本并告知用户该问题,以便他们了解有问题的版本。

问:如果我更新了依赖,但是没有改变公共API,应该怎么做?

这种方式是兼容的,因为它没有影响公共API。明确依赖于你的包的软件应该有它们自己的依赖规范,作者也会注意到任何冲突。决定是否这个修改是补丁级别还是次版本级别依赖于你更新你的依赖是为了修复bug还是添加新功能。我通常期待额外的代码是为了后一种,这种情况下明显是一个次版本级别递增

问:如果我不小心修改了公共API,但是不兼容于版本号的改变(比如在补丁版本中错误的引入了主版本级别的不兼容修改)?

运用你最好的判断。如果有大量的用户会因为这个公共API的返回而受到影响,那么最好的策略是执行一次主版本发布,即使这次修复严格上被视为是补丁发布。记住,语义版本化就是通过版本号的变化来传达意义。如果这些更改对您的用户很重要,请使用版本号通知他们

问:应该如何处理待舍弃的功能?

舍弃已存在的功能是软件开发的常规动作,也是为了进一步发展所必须的。当你舍弃部分公共API,应该做两件事情:(1)更新文档让用户知道这次改变,(2)发布一个新的次版本,仍旧包含这个舍弃功能。在新的主版本发布中完全移除这些功能之前,应该至少发布一个包含这些舍弃功能的次版本,其目的是让用户能够平滑的迁移到新的API

问:语义规范对版本字符串有大小限制吗?

没有限制,但是255个字符的版本号也太长了一点。此外,特定系统可能会对字符串的大小有限制

自动版本化和生成CHANGELOG工具standard-version

参考:

conventional-changelog/standard-version

Closing issues using keywords

如何维护更新日志

conventioanl-changelog

参考:

conventional-changelog-cli

使用工具conventional-changelog进行CHANGELOG文件的自动生成

安装

全局安装:

$ npm install -g conventional-changelog-cli
使用

基本使用命令如下:

$ conventional-changelog -p angular -i CHANGELOG.md -s
  • 参数-p指定提交信息的规范,有以下选择:angular, atom, codemirror, ember, eslint, express, jquery, jscs or jshint
  • 参数-i指定读取CHANGELOG内容的文件
  • 参数-s表示将新生成的CHANGELOG输出到-i指定的文件中

上述命令将基于上次tag版本后的变更内容添加到CHANGELOG.md文件中,CHANGELOG.md之前的内容不会消失

如果想要重新生成所有版本完整的CHANGELOG内容,使用以下命令:

$ conventional-changelog -p angular -i CHANGELOG.md -s -r 0
  • 参数-r默认为1,设为0将重新生成所有版本的变更信息
快捷方式

参考:五、生成 Change log

在工程package.json中加入以下脚本

    {
      "scripts": {
        "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
      }
    }

运行如下命令即可生成1CHANGELOG

$ npm run changelog

README

如何写好README

README常常是工程的第一个入口,经常不知道写些什么,如何介绍工程的内容。在网上找了资料,发现这是很多人遇到的问题,也有很多热心人士发表了自己的意见

主要参考RichardLitt/standard-readme,里面提出了一整套REAMDE规范,包括编写规范、linter、生成器、徽章以及示例

另外kylelobo/The-Documentation-Compendium如何写好Github中的readme?也给出了很多建议

[译]规范

参考:Specification

README必须满足下面列出的所有要求

注意:标准自述文件是为开源库设计的。尽管它在历史上是为nodenpm项目创建的,但它也适用于其他语言的库和包管理器

要求:

  • 称为README.md(大写)
  • 如果项目支持i18n,则必须相应地命名该文件:README.de.md,其中deBCP 47语言标记。对于命名,请优先考虑语言的非区域子标记。如果只有一个README且语言不是英语,则允许文本中使用不同的语言而无需指定BCP标记:例如README.md可以是德语。在包含多种语言版本的情况下,README.md固定为英语
  • 是一个有效的Markdown文件
  • 章节必须按下面给出的顺序出现。可省略可选部分
  • 除非另有说明,否则每个章节必须具有下面列出的标题。如果README是另一种语言,则必须将标题翻译成该语言
  • 不能包含无法访问链接
  • 如果有代码示例,则应该像在项目的其余部分中对代码进行linted一样对它们进行linted

内容列表

注意:这只是规范的导航指南,并不是任何符合规范的文档定义或强制使用术语
  • 章节
    • 标题(Title
    • 横幅(Banner
    • 徽章(Badges
    • 简短说明(Short Description
    • 详细描述(Long Description
    • 内容列表(Table of Contents
    • 安全(Security
    • 背景(Background
    • 安装(Install
    • 用法(Usage
    • 附加内容(Extra Sections
    • 应用编程接口(API
    • 主要维护人员(Maintainers
    • 致谢(Thanks
    • 参与贡献方式(Contributing
    • 许可证(License
  • 定义

章节

标题
  • 状态(status):必须

  • 必要条件(requirements):

    • 标题必须与repository、folderpackage manager名称匹配,或者它可以有另一个相关的标题,旁边的repository、folderpackage manager标题用斜体字和括号括起来。例如:

      # Standard Readme Style _(standard-readme)_
      

      如果任何文件夹、存储库或包管理器名称不匹配,则在详细描述中必须有一条说明原因

  • 建议(suggestions):符合内容要求,做到显而易见

横幅
  • 状态:可选
  • 必要条件:
    • 不能有自己的标题
    • 必须链接到当前存储库中的本地图像
    • 必须直接出现在标题后面
徽章
  • 状态:可选
  • 必要条件:
    • 不能有自己的标题
    • 必须用换行符分隔
  • 建议:使用http://shields.io或类似服务创建和托管图像
简短说明
  • 状态:必须
  • 必要条件:
    • 不能有自己的标题
    • 小于120个字符
    • >开始
    • 一定是单独一行
    • 必须与包装管理器说明字段中的描述匹配
    • 必须匹配github的描述(如果在github上)
  • 建议(js相关):
    • 使用gh description设置并获取github描述
    • 使用npm show . description显示本地npm包中的描述
详细描述
  • 状态:可选

  • 必要条件:

    • 不能有自己的标题
    • 如果任何文件夹、存储库或包管理器名称不匹配,必须在此处说明原因。参考标题
  • 建议:

    • 如果太长,考虑移动到背景章节

    • 介绍构建存储库的主要原因

    • 应该用宽泛的术语来描述模块,一般只在几段中;模块的例程或方法、冗长的代码示例或其他深入的材料的更多细节应该在后面的章节中给出。

      理想情况下,稍微熟悉模块的人应该能够刷新他们的内存,而不必点击“向下翻页”。当读者继续阅读文档时,他们应该会逐渐获得更多的知识。

      ~Kirrily “Skud” Robert, perlmodstyle

内容列表
  • 状态:必须;对于小于100行的README是可选的
  • 必要条件:
    • 必须链接到文件中的所有markdown章节
    • 必须从第二节开始;不要包含标题或目录标题
    • 必须至少有一个深度:必须捕获所有二级标题(##
  • 建议:
    • 可以捕获三级(###)和四级(####)深度的标题。如果是长目录,这些是可选的
安全
  • 状态:可选
  • 必要条件:
    • 如果有必要强调安全问题,则说明。否则,它应该在Extra Sections
背景
  • 状态:可选
  • 必要条件:
    • 涵盖动机
    • 覆盖抽象依赖项
    • 涵盖知识来源:see also也很合适
安装
  • 状态:默认是必须的,对于文档仓库而言是可选的
  • 必要条件:
    • 使用代码块说明如何安装
  • 子章节(subsections):
    • 依赖(dependencies):如果有不寻常的依赖项或依赖项必须手动安装,必须提出
  • 建议:
    • 链接到编程语言的必备站点:npmjsgodocs
    • 包括安装所需的任何系统特定信息
    • 增加一个updating章节是有用的
用法
  • 状态:默认是必须的,对于文档仓库而言是可选的
  • 必要条件:
    • 使用代码块说明常见用法
    • 如果兼容于cli,用代码块说明其用法
    • 如果可导入,用代码块表示导入功能和用法
  • 子章节:
    • CLI:如果命令行功能存在则是必要的
  • 建议:
    • 覆盖可能影响使用的基本选项:例如,如果是javascript,则覆盖promises/callbacks(对ES6而言)
    • 可以指向示例代码的可执行文件
附加内容
  • 状态:可选
  • 必要条件:
  • 建议:
    • 这不应称为附加内容。可容纳0个或多个章节,每个章节必须有其标题
    • 这应该包含任何其他相关的内容,放在用法之后,应用程序接口之前
    • 具体来说,如果安全章节不够重要,不需要放在上面的话,可以放在这里
应用编程接口
  • 状态:可选
  • 必要条件:
    • 描述开放的函数和对象
  • 建议:
    • 描述签名、返回类型、回调和事件
    • 覆盖类型不明显的地方
    • 描述注意事项
    • 如果使用外部api生成器(如go-doc、js-doc等),那么指向外部api.md文件即可
主要维护人员
  • 状态:可选
  • 必要条件:
    • 列出存储库的维护人员,以及联系他们的一种方式(例如github链接或电子邮件)
  • 建议:
    • 这应该是一个负责这个仓库人员的小名单。这不应该是所有拥有访问权限的人,例如整个组织,而是应该ping并负责管理和维护存储库的人
    • 列出过去的维护者是友好的
致谢
  • 状态:可选
  • 必要条件:
    • 必须称为Thanks, CreditsAcknowledgements的其中一种
  • 建议:
    • 陈述任何对项目开发有重大帮助的人或事
    • 列出公共联系人的超链接(如果可以的话)
参与贡献方式
  • 状态:必须
  • 必要条件:
    • 说明用户可在哪里提问
    • 说明是否接受PR
    • 列出参与的任何要求;例如,对提交进行审核
  • 建议:
    • 如果有的话,链接到CONTRIBUTING文件
    • 尽可能友好
    • 链接到github问题库
    • 链接到行为准则(Code of Conduct)。Coc通常位于贡献部分或文档中,或者设置在整个组织的其他位置,因此可能不需要在每个存储库中包含整个文件。但是,强烈建议始终链接到代码,无论它位于何处
    • 也可以在这里增加一个小节,列出贡献者
许可证
  • 状态:必须
  • 必要条件:
    • 列出许可证全名或标识符,参考SPDX许可证列表。对于未授权的存储库,添加标识UNLICENSED。对于许可证的详细信息,添加see license in <filename>并链接到许可文件。(参考npm设置)
    • 列出许可证拥有者
    • 必须是最后一节
  • 建议:
    • 链接到本地存储库中较长的许可证文件

定义

提供这些定义是为了理清上述使用的任何术语

  • 文档仓库(Documentation repositories):仓库中没有任何功能代码,比如RichardLitt/knowledge

README生成器

RichardLitt/generator-standard-readme提供了一个npm命令行工具,用于生成README文件

安装

npm install --global yo generator-standard-readme

使用

命令行输入

$ yo standard-readme

接下来会提出很多问题,然后生成一个README文件。所有问题如下:

What do you want to name your module?
What is the description of this module?
Do have a banner image?
    Where is the banner image? Ex: 'img/banner.png'
Do you want a TODO dropped where your badges should be?
Do you want a TODO dropped where your long description should be?
Do you need a prioritized security section?
Do you need a background section?
Do you need an API section?
What is the GitHub handle of the main maintainer?
Do you have a CONTRIBUTING.md file?
Are PRs accepted?
Is an MIT license OK?
    What is your license?
Who is the License holder (probably your name)?
Use the current year?
    What years would you like to specify?

示例

$ yo standard-readme
? ==========================================================================
We're constantly looking for ways to make yo better! 
May we anonymously report usage statistics to improve the tool over time? 
More info: https://github.com/yeoman/insight & http://yeoman.io
========================================================================== Yes
? What is the name of your module? zj
? What is the description of this module? yo使用示例
? Do have a banner image? No
? Do you want a standard-readme compliant badge? Yes
? Do you want a TODO dropped where more badges should be? Yes
? Do you want a TODO dropped where your long description should be? Yes
? Do you need a prioritized security section? No
? Do you need a background section? Yes
? Do you need an API section? No
? What is the GitHub handle of the main maintainer? zjZSTU
? Do you have a CONTRIBUTING.md file? No
? Are PRs accepted? Yes
? Is an MIT license OK? Yes
? Who is the License holder (probably your name)? zjZSTU
? Use the current year? Yes
   create README.md

生成文件如下:

# zj                                       // 标题

[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)    // 徽章
TODO: Put more badges here.                // 可以添加更多徽章

> yo使用示例                                // 简短说明

TODO: Fill out this long description.      // 详细说明

## Table of Contents                       // 下面的章节列表,每个章节都可以点击跳转

- [Background](#background)
- [Install](#install)
- [Usage](#usage)
- [Maintainers](#maintainers)
- [Contributing](#contributing)
- [License](#license)

## Background                               // 背景

## Install                                  // 安装

```
```

## Usage                                    // 用法

```
```

## Maintainers                              // 主要维护人员

[@zjZSTU](https://github.com/zjZSTU)

## Contributing                             // 参与贡献方式

PRs accepted.

Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.

## License                                  // 许可证

MIT © 2019 zjZSTU

自定义徽章

经常在README文件中发现许多徽章,很多都是使用http://shields.io或类似服务创建和托管的

自定义

进入shields.io

_images/badge.png

输入标签名和徽章信息,选择颜色即可生成静态SVG图像

https://img.shields.io/badge/ZHUJIAN-BADGE-brightgreen

https://img.shields.io/badge/ZHUJIAN-BADGE-brightgreen

添加链接

在徽章图像上添加链接,点击图像跳转到仓库

[![](https://img.shields.io/badge/ZHUJIAN-BADGE-brightgreen)](https://shields.io)

https://img.shields.io/badge/ZHUJIAN-BADGE-brightgreen