Iawen's Blog

我喜欢这样自由的随手涂鸦,因为我喜欢风......

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

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。

[root@iawen www-data]# gpg --list-keys 
/root/.gnupg/pubring.gpg
------------------------
pub 2048R/4499CB9F 2018-06-04
uid iawen (publish the project) <iawen@126.com>

之后,可以通过管道传递给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上有至少有两个记录。

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时忽略行尾对比

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. 常见问题收集

8.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

8.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吧,谁用谁爽

8.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