1. 从源码编译安装 Git
wget https://github.com/git/git/archive/v2.24.0.tar.gz
tar xzf v2.24.0.tar.gz && cd `tar tvf v2.21.0.tar.gz | head -1 | awk '{print $6}' | cut -d/ -f1`
make configure
./configure --prefix=/usr
make install
2. Server 和 Client 的设置与操作
2.1 Server
一个远程仓库通常只是一个裸仓库(repository)–即一个没有当前工作目录的仓库。
2.1.1 通讯协议
Git可以使用四种主要的协议来传输资料: 本地协议(Local), HTTP协议, SSH协议及Git协议。
A、本地协议
其中的远程版本库就是硬盘内的另一个目录。克隆本地版本库:
git clone /opt/git/project.git #或
git clone file:///opt/git/project.git
仅指定路径, Git会尝试使用硬链接或直接复制所需要的文件。指定file://, Git会触发平时用于网络传输资料的进程, 那通常是传输效率较低的方法。
B、HTTP 协议
Git通过HTTP通信有两种模式。
- 旧版本的(1.6.6之前)哑HTTP协议
- 新版本的智能HTTP协议: 与SSH及Git协议类似, 只是运行在标准的HTTP/S端口上并且可以使用各种HTTP验证机制, 它即支持像git://协议一样设置匿名服务, 也可以像SSH协议一样提供传输时的授权和加密。
C、SSH 协议
通过SSH协议克隆版本库, 你可以指定一个ssh://的URL:
git clone ssh://user@server/project.git
git clone user@server:project.git #SCP式语法
可以不指定用户, Git会使用当前登录的用户名。
D、Git 协议
这是包含在Git里的一个特殊的守护进程; 它监听一个特定的端口(9418), 类似于SSH服务, 但是访问无需任何授权。要让版本库支持Git协议, 需要先创建一个git-daemon-export-ok 文件。由于没有授权机制, 一旦开放推送操作, 意味着网络上知道这个项目URL的人都可以向项目推送数据。
2.1.2 搭建Git服务器
在开始架设之前, 需要把现有仓库导出为裸仓库。为了通过克隆仓库来创建一个裸仓库, 需要在克隆命令后加上一个–bare 选项。按照惯例, 裸仓库的目录名以.git 结尾, 就像这样:
git clone --bare my_project my_project.git #整体效果大致相当于
cp -Rf my_project/.git my_project.git
#将裸仓库推送到服务器
scp -r my_project.git user@server:/opt/git
#创建一个空的裸仓库
git init --bare
如果一个用户通过SSH连接到服务器, 并且对/opt/git/my_project.git 目录拥有可写权限, 那么他将自动拥有推送权限。 如果到该项目目录中运行git init 命令, 并加上–shared选项, 那么Git 会自动修改该仓库目录的组权限为可写:
git init --bare --shared
如果需要团队里的每个人都对仓库有写权限, 有三种方法:
- 1、给团队里的每个人建立帐号
- 2、在主机上建立一个git 帐户, 让每个需要写权限的人发送一个SSH公钥, 然后将其加入git 帐户的~/.ssh/authorized_keys 文件。
- 3、让SSH服务器通过某个LDAP服务
2.2 客户端
2.2.1 推送本地库到服务端
cd myproject
git init
git add .
git commit -m 'initial commit'
git remote add origin git@192.168.0.86:/data/website/project.git
git push origin master
#同时关联本地分支到远程分支
git branch -vv #查看本地分支
git branch --set-upstream-to=origin/远程分支的名字 本地分支的名字
2.2.2 直接拉取远程仓库
git clone git@192.168.0.86:/data/website/project.git
# git clone --filter=blob:none
3. 基本操作
3.1 创建与合并分支
3.1.1 从master分支创建dev分支并切换到dev分支:
git checkout master
git checkout -b dev
其中, git checkout -b dev 等价于:
git branch dev
git checkout dev
查看本地当前的分支, 分支前面带“*”表示当前分支, 剩下的分支表示本地有的分支:
git branch
查看远程全部的分支, 白色的表示本地有的, 红色的表示本地没有, 仅在远程存在:
git branch -a
3.1.2 修改代码、提交代码(当前的操作是在dev分支上进行)
git add a.html git commit -m "提交文件a.html"
3.1.3 分支合并(将dev合并到master)
git checkout master git merge dev
3.1.4 合并完成后, 删除dev分支.(删除dev分支时, 注意我们当前所在的分支不能是dev分支)
git branch -d dev
3.1.5 删除后, 查看分支(此时看不到dev分支了)
git branch
3.1.6 修改最近一次提交的提交信息
git commit --amend
修改多个提交信息: git rebase -i
3.1.7 删除分支
# 本地强制删除分支issues1234
git branch -D issues1234
# 推到远程
git push origin :issues1234
3.1.8 filter-branch
从每一个提交移除一个文件, 如:
git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
为了让filter-branch 在所有分支上运行, 可以传递 –all 选项。
使一个子目录做为新的根目录 git filter-branch –subdirectory-filter trunk HEAD
3.1.9 总结 : 工作中经常从master创建新的分支, 具体操作如下:
git checkout master
git checkout -b issues1234
git push origin issues1234
git add ..
git commit -m "***"
git push origin issues1234
# 注意: 将本地分支branch1推到远端的branch2操作步骤:
git push origin branch1:branch2
3.2 发布包
3.2.1 为发布打标签
git tag -s v0.0.3 -m 'new tag v0.0.3'
在为发发布打签前, 需要通过运行 gpg --list-keys
找出想要的key。
gpg --list-keys
/root/.gnupg/pubring.gpg
------------------------
pub 2048R/4499CB9F 2018-06-04
uid iawen (publish the project) <iawen@126.com>
# 如果没有, 则需要生成
gpg --generate-key
之后, 可以通过管道传递给git hash-object
来直接将Key 导入到Git 数据库中:
gpg -a --export 4499CB9F | git hash-object -w --stdin
3.2.2 生成一个构建号
git describe
由最近的标签名、自该标签之后的提交数目和你所描述的提交部分SHA-1 值组成。
3.2.3 准备一次发布
git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
git archive master --prefix='project/' --format=zip > `git describe master`.zip
3.2.4 制作提交简报
git shortlog --no-merges master --not v0.0.3
v0.0.3是指上次发布的版本号
3.3 解决冲突
3.3.1 发生冲突的文件
<<<<<<< HEAD Creating a new branch is quick & simple. ======= Creating a new branch is quick AND simple. >>>>>>> feature1
其中, git使用 «««<, =======, »»»> 标记文件中自己和别人产生冲突的部分。
- 在 «««<, ======= 之间为自己的代码;
- =======, »»»> 之间为别人的代码。 如果保留自己的代码, 将别人的代码删掉即可。
3.3.2 冲突解决后提交
git status
git add ***
git commit -m "fix conflict"
git push origin 分支名
3.4 Bug分支
3.4.1 储藏更改:将当前更改的代码储藏起来, 等以后恢复使用
git stash
3.4.2 恢复储藏的代码
# 恢复的同时把stash内容删掉
git stash pop # 或者
git stash apply # 恢复stash, 但是stash内容并不删除 git stash drop //在上面操作的基础上, 以此来删除stash
git stash list #查看全部的stash列表。
3.4.3 将stash空间清空
git stash clear
3.4.4 git stash pop 和 git stash apply 区别
原来git stash pop stash@{id}
命令会在执行后将对应的stash id 从stash list里删除, 而 git stash apply stash@{id}
命令则会继续保存stash id。
3.5 版本回退
3.5.1 回退至上一个版本
git reset --hard HEAD
3.5.2 回退至指定版本
git reset --hard 版本号
3.5.3 查看以往版本号(本地的commit)
git reflog
3.5.4 查看各版本号及信息(所有的commit: 本地commit + 其他同事的commit)
git log
3.6 撤销修改
3.6.1 撤销修改
git checkout -- a.html
分两种情况分析:
还没有执行 git add
操作, 执行上面的操作后, 会恢复到和版本库中一模一样的版本状态。
执行了git add
, 还没执行 git commit
,再执行上面的操作后, 会恢复到git add 结束后的状态
注: 一旦执行了git commit -m "*"
, 就不能再使用上面的命令回退。
3.6.2 对于已经push的版本, 进行回退
A、第一步:
git reset --hard 版本号 # 本地回退到指定的版本
B、第二步:
git push -f origin dev #将远程的也回退到指定版本
3.6.3 本地同步远程删除的分支
git fetch origin -p #用来清除已经没有远程信息的分支, 这样git branch -a 就不会拉取远程已经删除的分支了
3.6.4 删除掉没有与远程分支对应的本地分支
从gitlab上看不到的分支在本地可以通过git branch -a 查到, 删掉没有与远程分支对应的本地分支:
git fetch -p
3.6.5 查看远程库的一些信息, 及与本地分支的信息
git remote show origin
3.7 git stash临时保存本地操作
- 1、使用
git stash
就可以将你当前未提交到本地(和服务器)的代码推入到Git的栈中, 这时候你的工作区间和上一次提交的内容是完全一样的。 - 2、再切换到别的分支改紧急bug。
- 3、改完后, 切到刚才的分支, 使用
git stash apply
将以前一半的工作应用回来。
也许有的人会说, 那我可不可以多次将未提交的代码压入到栈中?答案是可以的。当你多次使用 git stash
命令后, 你的栈里将充满了未提交的代码, 这时候你会对将哪个版本应用回来有些困惑, git stash list 命令可以将当前的Git栈信息打印出来, 你只需要将找到对应的版本号, 例如使用 git stash apply stash@{1} 就可以将你指定版本号为stash@{1}的工作取出来, 当你将所有的栈都应用回来的时候, 可以使用 git stash clear
来将栈清空。
在这里顺便提下 git format-patch-n
, n是具体某个数字, 例如 git format-patch-1 这时便会根据log生成一个对应的补丁, 如果 git format-patch-2 那么便会生成2个补丁, 当然前提是你的log上有至少有两个记录。
3.8 常用的分支管理
3.8.1 正常合并流程
- 开发分支拉取流程: master -> release -> feature
- 提测和bugfix分支合并流程: feature(自测) -> feature/cdp_dev(接入团队额外的分支,开发集成测试) -> release(测试团队回归) -> develop(测试团队测试)
- Hotfix分支拉取合并流程: master -> hotfix -> develop(提测), hotfix -> master(上线)
- 上线分支合并流程: release -> master(正常上线),hotfix -> master(生产问题修复上线)
- 提测时开发提供的pr: release -> develop
- 上线时开发提供的pr: release -> master
- Hotfix开发提供的pr: hotfix -> master
3.8.2 分支冲突强制合并
- develop 拉取 feature_develop_diff
- master 合并到 feature_develop_diff 执行命令: git pull origin feature/feature_master 然后解决冲突,以master代码为主,提交代码
- 提PR: feature_develop_diff -> develop
3.9 子模块
git clone --recursive
# 或者执行git clone后再执行
git submodule update --init
4. 常用设置
4.1 用户设置
git config --global user.email "iawen@iawen.com"
git config --global user.name "iawen"
4.2 常用别名
git config --global alias.st status
git config --global alias.br branch
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.graph 'log --oneline --decorate --graph --all'
git config --global user.signingkey 0A46826A
git config --global core.autocrlf false # 忽略换行
git config --global core.saftcrlf true # 拒绝提交包含混合换行符的文件
git config --global alias.dfw 'diff --ignore-space-change' # 忽略空白行
git config --global merge.renormalize true # merge时忽略行尾对比
4.3 保存密码
- 每次操作都需要输入用户名和密码感觉很繁琐, 解决方法, 在本地的工程文件夹的.git下打开config文件, 添加:
[credential]
helper = store
- 先用git拉一次东西, 拉的时候会提醒你输入帐号的密码。输入正确的帐号和密码后, 等东西拉完以后输入
git config --global credential.helper store
5. 常用方法
5.1 查看引用日志
git reflog
引用日志记录了最近几个月你的HEAD和分支引用所指向的历史。如果想查看仓库中HEAD在5次前所指向的提交, 可以使用@{n}来引用reflog 中输出的提交记录:
git show HEAD@{5}
也可以使用这个语法来查看某个分支在一定时间前的位置, 如:
git show master@{yesterday}
引用日志只存在于本地仓库, 一个记录你在你自己的仓库里做过什么的日志。
5.2 祖先引用
祖先引用是另一种指明提交的方式。如果你在引用的尾部加上一个^, Git会将其解析为该引用的上一个提交。
git show HEAD^
git show 973f552^2
另一种指明祖先提交的方法是~。同样是指向第一父提交, 因此HEAD~和HEAD^是等价的, 区别在于你在后面加数字的时候。HEAD~2代表“第一父提交的第一父提交”, 也就是祖父提交。
5.3 提交区间
最常用的指明提交区间语法是双点: (..)。如果你想看当前开发分支dev 是否落后于master分支, 可以使用:
git log dev..master
# 反之, 再查看当前开发分支dev 是否有未合并到master
git log master..dev
# 查看未推送到远端的内容:
git log origin/master..HEAD
三点区间选择, 可以选择出被两个引用中的一个包含但又不被两者同时包含的提交:
git log master...dev
git log --left-right master...dev
–left-right选项, 显示出每个提交到底处于哪一侧的分支, 会让输出数据更加清晰。注: 结合–pretty=oneline 一起使用
5.4 交互式暂存
git add -i
5.5 储藏与清理
git stash #或git stash save
git stash list
git stash apply #或 git stash apply stash@{2}
#如果不指定, 则认定是最近的储藏
# 移除储藏:
git stash drop stash@{0} #或 git stash pop
git clean
# 被设计为从工作目录移除未被跟踪的文件。如果你改变语音, 你也不一定能找回来那些文件的内容。一个更案例的选项是运行: git stash --all 来移除每一样东西并存放在栈中。
git clean # 去除冗余文件或者清理工作目录(只会移除没有忽略的未跟踪文件)。
git clean -f -d # 移除工作目录中所有未追踪的文件以及空的子目录
git clean -d -n # 演示
5.6 日志搜索
git log -Sxxxx ---oneline
# 行日志搜索, 如查看zlib.c文件中git_deflate_bound函数的每一次变更:
git log _L :git_deflate_bound:zlib.c
# 如果Git无法计算出如何匹配你代码中的函数和方法, 你可以提供一个正则表达式, 如:
git log -L '/unsigned long git_deflate_bound/', /^}/:zlib.c
6. 使用Git 来实现持续集成(CI)
使用Git来完成代码发布, 主要是在服务端的hoot/post-receive 脚本, 下面是一个简单的示例:
#!/bin/bash
unset GIT_DIR #很 重要
PROJECT_DIR=/data/website/note
TMP_CHANGE=/tmp/$$.log
LOG_FILE=/data/logs/note.log
cd $PROJECT_DIR
git add . -A && git stash
git pull origin master >> $TMP_CHANGE
MSG="`date +"[%Y-%m-%d %H:%M:%S]"` sync note content"
# 根据代码变动, 做相应处理
cat $TMP_CHANGE | grep ".go"
if [ $? -eq 0 ]
then
killall mynote
/usr/local/go/bin/go build -o mynote *.go
nohup ${PROJECT_DIR}/mynote 2>&1 >> $LOG_FILE &
MSG="$MSG Recomplie program"
else
# do something
fi
echo $MSG >> $LOG_FILE
rm -rf $TMP_CHANGE
这里需要注意权限问题, 如果用户没有对发布目录的写入权限, 则会出错!!
7. 常用命令详解:
7.1 git log
常用的选项及其释义
选项 | 说明 |
---|---|
-p | 按补丁格式显示每个更新之间的差异 |
–word-diff | 按 word diff 格式显示差异 |
–stat | 显示每次更新的文件修改统计信息 |
–shortstat | 只显示 –stat 中最后的行数修改添加移除统计 |
–name-only | 仅在提交信息后显示已修改的文件清单 |
–name-status | 显示新增、修改、删除的文件清单 |
–abbrev-commit | 仅显示 SHA-1 的前几个字符, 而非所有的 40 个字符 |
–relative-date | 使用较短的相对时间显示(比如, “2 weeks ago”) |
–graph | 显示 ASCII 图形表示的分支合并历史 |
–pretty | 使用其他格式显示历史提交信息。可用的选项包括 oneline, short, full, fuller 和 format(后跟指定格式) |
–oneline | –pretty=oneline –abbrev-commit 的简化用法 |
最有意思的是 format, 可以定制要显示的记录格式, 这样的输出便于后期编程提取分析, 下表列出了常用的格式占位符写法及其代表的意义。
选项 | 说明 |
---|---|
%H | 提交对象(commit)的完整哈希字串 |
%h | 提交对象的简短哈希字串 |
%T | 树对象(tree)的完整哈希字串 |
%t | 树对象的简短哈希字串 |
%P | 父对象(parent)的完整哈希字串 |
%p | 父对象的简短哈希字串 |
%an | 作者(author)的名字 |
%aN | mailmap的作者名字 (.mailmap对应, 详情参照git-shortlog(1)或者git-blame(1)) |
%ae | 作者的电子邮件地址 |
%aE | 作者邮箱 (.mailmap对应, 详情参照git-shortlog(1)或者git-blame(1)) |
%ad | 作者修订日期(可以用 -date= 选项定制格式) |
%aD | 日期, RFC2822格式 |
%ar | 作者修订日期, 按多久以前的方式显示 |
%at | 日期, UNIX timestamp |
%ai | 日期, ISO 8601 格式 |
%cn | 提交者(committer)的名字 |
%cN | 提交者名字 (.mailmap对应, 详情参照git-shortlog(1)或者git-blame(1)) |
%ce | 提交者的电子邮件地址 |
%cE | 提交者 email (.mailmap对应, 详情参照git-shortlog(1)或者git-blame(1)) |
%cd | 提交日期 |
%cD | 提交日期, RFC2822格式 |
%cr | 提交日期, 按多久以前的方式显示 |
%ct | 提交日期, UNIX timestamp |
%ci | 提交日期, ISO 8601 格式 |
%d | ref名称 |
%e | encoding |
%f | sanitized subject line, suitable for a filename |
%b | commit信息内容 |
%s | 提交说明 |
%N | commit notes |
%gD | reflog selector, e.g., refs/stash@{1} |
%gd | shortened reflog selector, e.g., stash@{1} |
%gs | reflog subject |
%Cred | green |
%Creset | 重设颜色 |
%C(…) | 制定颜色, as described in color.branch.* config option |
%m | left, right or boundary mark |
%n | 换行 |
%% | a raw % |
%x00 | print a byte from a hex code |
%w([[,[,]]]) | switch line wrapping, like the -w option of git-shortlog(1) |
示例:
git log --pretty=format:"%C(auto)%h%Creset - %an, %ar : %s" -5 --graph --all
8. 图解git flow开发流程
git flow的工作流程图:
8.1 Git Flow 的常用分支
-
生产分支(master) Master分支是仓库的主分支, 这个分支包含最近发布到生产环境的代码, 最近发布的Release, 这个分支只能从其他分支合并, 不能在这个分支直接修改
-
补丁分支(hotfix) 当我们在生产环境发现新的Bug时候, 我们需要基于master分支创建一个Hotfix分支, 然后在Hotfix分支上修复bug, 完成Hotfix后, 我们要把hotfix分支合并回Master和Develop分支
-
发布分支(release) 当你需要发布一个新功能的时候, 要基于Develop分支创建一个Release分支, 在Release分支测试并修复bug, 完成release后, 把release合并到master和develop分支
-
开发分支(develop) 这个分支是我们的主开发分支, 包含所有要发布到下一个Release的代码, 这个主要合并与其他分支, 比如Feature分支
-
功能分支(feature) feature分支主要是用来开发一个新的功能, 一旦开发完成, 我们合并回Develop分支进入下一个Release
8.2 git flow的具体使用细节
当我们新建git仓库之后, 默认会创建一个主分支也就是master分支, 由于master分支是用于发布生产环境, 所有必须保证master上代码的稳定性, 所以我们不能直接在master分支上修改提交。我们要基于master分支创建一个develop分支, develop分支用于保存开发好的相对稳定的功能, master分支和develop分支是仓库的常驻分支, 一直会保留在仓库中.
当新的开发任务来了之后, 就要编写代码了, 我们尽量不要在develop分支上写代码, 要保证develop分支的相对稳定, 所以这时我要就要基于develop 分支创建一个临时的开发分支, 然后在开发分支上编写代码, 等功能开发完之后我们再把开发分支合并到develop上.
新功能合并到develop分支之后, 我们想把新功能发布到生产环境, 首先基于develop分支创建release分支, 然后在release分支测试完成之后, 把release分别合并到master分支和develop分支.
release分支合并到master分支之后, 在master分支上打标签用于发布:
我们把代码发布到了生产环境, 用户在使用的时候给我们反馈了一个bug, 这时我们需要基于master分支创建一个hotfix 分支, 用于修复bug, bug修好之后, 把hotfix 分支分别合并到master分支和develop分支.
8.3 Git flow工具
如果理解了上面的流程, 完全可以不使用Git flow工具, 但是如果你想更标准化的执行git flow, 可以尝试使用git flow工具
# Mac OS X
$ brew install git-flow
# Linux
$ apt-get install git-flow
# Windows
$ wget -q -O - --no-check-certificate https://github.com/nvie/gitflow/raw/develop/contrib/gitflow-installer.sh | bash
- 初始化:
git flow init
- 开始新Feature:
git flow feature start MYFEATURE
- Publish一个Feature(也就是push到远程):
git flow feature publish MYFEATURE
- 获取Publish的Feature:
git flow feature pull origin MYFEATURE
- 完成一个Feature:
git flow feature finish MYFEATURE
- 开始一个Release:
git flow release start RELEASE [BASE]
- Publish一个Release:
git flow release publish RELEASE
- 发布Release:
git flow release finish RELEASE
别忘了git push --tags
- 开始一个Hotfix:
git flow hotfix start VERSION [BASENAME]
- 发布一个Hotfix:
git flow hotfix finish VERSION
9. 常见问题收集
9.1 RPC failed; curl 18 transfer closed with outstanding read data remaining
remote: Compressing objects: 100% (6/6), done.
error: RPC failed; curl 18 transfer closed with outstanding read data remaining
fatal: the remote end hung up unexpectedly
fatal: early EOF
fatal: index-pack failed
A、原因1: 缓存区溢出
解决方法: 命令行输入git config http.postBuffer 524288000
执行上面命令如果依旧clone失败, 考虑可能
B、原因2: 网络下载速度缓慢
解决方法: 命令行输入
git config --global http.lowSpeedLimit 0
git config --global http.lowSpeedTime 999999
如果依旧clone失败, 则首先浅层clone, 然后更新远程库到本地
git clone --depth=1 http://gitlab.xxx.cn/yyy/zzz.git
git fetch --unshallow
9.2 OpenSSL SSL_read:SSL_ERROR_sysCALL
# 用压缩的方式进行下载(--global设置成了全局, 也可以不设)
git config --global --add core.compression -1
# 增大缓存大小
git config --global http.postBuffer 524288000 # 500MB
git config --global http.postBuffer 1048576000 # 1GB
# 如何1+2解决不了, 那么利用ssh进行下载试试
git clone git://github.com/XX/XXXX.git
# Disable sslVerify
git config --global http.sslVerify "false"
#如果还是不行, 选择Download ZIP吧, 谁用谁爽
9.3 git clone的速度比较慢, 于是找到了办法分享给大家:
git clone特别慢是因为github.global.ssl.fastly.net域名被限制了。 只要找到这个域名对应的ip地址, 然后在hosts文件中加上ip–>域名的映射, 刷新DNS缓存便可。
在网站 https://www.ipaddress.com/ 分别搜索:
github.global.ssl.fastly.net
github.com
得到ip后, 在hosts 绑定, 然后刷新DNS
9.3.1 githubusercontent、github加速镜像
加速地址一览
-
fastgit.org: https://doc.fastgit.org/
-
gitee: https://gitee.com/mirrors
-
GitHub 文件加速: https://gh.api.99988866.xyz/
-
Github 仓库加速: https://github.zhlh6.cn/
-
Github 仓库加速: http://toolwa.com/github/
9.3.2 加速 clone
$ git config --global url."https://hub.fastgit.org".insteadOf https://github.com
$ git clone https://github.com/kubernetes/kubernetes.git
# 查看git配置信息
$ git config --global --list
# 取消设置
$ git config --global --unset url.https://github.com/.insteadof
9.3.3 加速 release
9.3.4 加速 raw
9.4 git: ‘remote-http’ is not a git command. See ‘git –help’
安装 libcurl-devel、curl-devel , 然后重新编译 git:
yum install -y libcurl-devel curl-devel