Git实战

学习Git有一段时间了,一路上也一直在写有关于Git方面的文章,但总觉得不是我想要的,就是感觉有点肉肉的,不够直接,不够马上出效果,所以才有了这篇文章,当然这文章可能会不断更新和修正,希望读者可以作为一个工具文章使用,我也会努力将其优化的更加的符合工作场景

说明

1、在[…]中的内容,需要根据实际情况进行修改

如何本地与远程建立信任联系?

要解决这个问题,需要使用SSH秘钥的方式,接下来我就讲一下怎么进行配置。

1.在本地生成私钥和公钥

1
2
3
git config --global user.name "username"//用户昵称
git config --global user.email "emailAddress"//github或者gitlib的邮箱地址
ssh-keygen -t rsa -C "emailAddress"//github或者gitlib邮箱账户地址

2.将电脑上的公钥与远程仓库进行绑定

本地会在上面的步骤中会生成一个id_rsa.pub(默认情况是这个名称),将该文件中的内容copy到远程仓库github或gitlib的settings的SSH配置选项中。

Tittle名称自由发挥

克隆工程

将远程的数据复制一份到本地

1
2
#【仓库copy地址】
git clone [git@rep.xx.com:zoeminghong/hello.git] [工程别名]

本地新建Git工程

现在打算将本地的工程,放到Git仓库进行托管了,并且远程Git仓库已经创建了该项目的工程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#本地初始化工程,会生成一个.git文件
git init

#将本地的工程与远程仓库中的项目进行关联(不用关心项目名不一致的问题)
#此时本地工程与远程仓库已经建立了联系
git remote add origin [git@rep.xx.com:zoeminghong/hello.git]

#去除远程仓库绑定
git remote remove origin

#将本地所有文件添加到Git中,进行监管
git add .

#将内容提交 【提交注释】
git commit -m "[...]"

#将本地的内容同步到远程仓库中
git push -u origin master

显示某一个特定的提交的日志

1
git show [十六进制码]

查看提交图

1
git log --graph --pretty=oneline --abbrev-commit

查看冲突未处理的文件列表

1
git ls-files -u

本地代码与远程代码冲突问题

本地代码未commit的前提下,解决与远程代码冲突问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
git pull #失败
#将当前修改进行暂存起来
git stash
#
git stash save "[注释]"

#获取最新的远程仓库代码
git pull

#恢复暂存的内容
git stash pop

#stash其他操作
#恢复最近一次save的原工作区内容,,并删除stash记录
git stash pop
#恢复最近一次save的原工作区内容,但不删除stash记录
git stash apply [指定版本]
#删除stash记录
git stash drop
#获取暂存列表
git stash list

但,上面的也可能存在问题,由于本地存在未被追踪的文件,并且远程仓库pull的时候也存在同名的文件,就会存在pull失败,在这种情况下,在git stash后面追加 --include-untracked,会将远程的文件与本地的文件融合

stash只会保存当前索引和工作目录的状态,其保存的是add和commit的中间状态,如果还没有被git追踪的文件,是不会被记录的

如果我对某文件进行了修改,但我不想要push到远程仓库,同时我又想获取最新的修改记录

1
2
git stash save
git pull --rebase

如果暂存内容现在不想在当前分支恢复了,而是想单独起一个分支

1
git stash branch [newBranchName]

想要查看当前工作区与暂存状态内容区别

1
git stash show -p stash{0}

本地代码已经commit后,解决与远程代码冲突问题

1
2
3
4
5
6
7
8
# 获取远端库最新信息 【分支名称】
git fetch origin [master]

# 做比较
git diff [本地分支名] origin/[远程分支名]

# 拉取最新代码,同时会让你merge冲突
git pull

方法2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 获取最新代码到tmp分支上 [远程的分支:本地分支]
git fetch origin [master:tmp]


# 当前分支与tmp进行比较
git diff tmp


# 修改冲突部分,进行本地commit操作
git add .


git commit -m "[...]"


# 将tmp中内容合并到当前分支中
git merge tmp


# 删除分支
git branch -d tmp

删除文件

保留副本操作

1
git rm --cache [文件名]

直接文件删除

1
git rm [文件名]

后悔药

还原到最近的版本,废弃本地做的修改(当前文件修改没有进行add操作的时候)

1
git checkout -- [文件名]

取消已经暂存的文件(撤销先前”git add”的操作)

1
2
#当前HEAD,返回到上一次commit点,不会有任何日志记录
git reset HEAD --hard
1
git reset HEAD [文件名]

回退所有内容到上一个提交点

1
2
#最近内容已经commit的情况下
git reset HEAD^ --hard

回退这个文件的版本到上一个版本

1
2
#最近内容已经commit的情况下
git reset HEAD^ [文件名]

将本地的状态回退到和远程的一样

1
git reset –-hard origin/[分支名]

回退到某个版本

1
2
3
# 获取所有的HEAD更改信息的sha1值
git reflog
git reset [SHA1]

回退到上一次提交的状态,按照某一次的commit完全反向的进行一次commit.(代码回滚到上个版本,并提交git)

1
git revert HEAD

使用reset是不会有日志记录的,revert则会要提交一个记录点

修改最新的提交信息(修改提交的注释信息)

1
git commit --amend

reset与revert的使用说明

reset一般用于本地还没有push到远端的时候,revert则用于想要远端也进行记录回退操作的时候,也就是说在push之后。

1、如果你已经push到远程仓库,reset删除指定commit以后,你git push可能导致一大堆冲突.但是revert 并不会

2、如果现有分支和历史分支需要合并的时候,reset 恢复部分的代码依然会出现在历史分支里.但是revert 方向提交的commit 并不会出现在历史分支里。

本地分支与远程分支相连

本地创建了一个分支,远程也有一个分支,进行两者关联

1
git checkout -b [本地分支名] origin/[远程分支名]

Tag使用

我们在开发的时候,可能存在线上发布了一个版本,需要给这个版本代码打上一个标签,到时候可以方便回退到这个版本

1
2
3
4
5
6
7
8
9
10
# 创建tag 【tag名】
git tag v1.0
(git tag -a [v1.4] -m ['my version 1.4'])

# 查看存在的tag
git tag
(git tag -l ['v1.4.2.*'])

# 将tag更新到远程,直接的push是不会将tag同步上去
git push origin --tags

接下来就讲解回退到具体的tag

1
2
3
4
5
6
7
8
9
10
11
12
# 保存当前编程环境
git stash

# 切换回某个tag(v1.0)
git show v1.0

#【sha1】
git reset --hard [2da7ef1]

# 创建分支来保存tag的数据,tag只是一个节点的标记,无法承载数据的修改记录,【分支名】
git checkout -b [branchName] [tagName]
# 接着你就可以在这里改啊改了

切换回主干或其他分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 切换分支
git checkout master

# 日志记录
git reflog

# 显示stash列表
git stash list

# 恢复之前的工作环境代码
git stash apply

# 删除stash
git stash drop

分支与主干合并

1
2
3
4
5
6
7
8
git add .
git commit -m "v1.1"
git checkout [bill]
# master分支最新的代码合并到bill分支上
git rebase [master]
git checkout master
# bill分支合并到当前分支【分支名】
git merge [bill]

关于代码的比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 显示暂存区和工作区的差异
git diff
git diff [filename]

# 显示暂存区和上一个commit的差异【文件名】
git diff --cached [hello.txt]
git diff --cached [HEAD或者SHA1] [filename]

# 显示工作区与当前分支最新commit之间的差异
git diff HEAD
git diff [HEAD或分支名] [filename]

# 显示两次提交之间的差异【分支名】
git diff [first-branch]...[second-branch]
git diff [SHA1] [SHA1] [filename]

#分支之间的差异
#分支之间的差异
git diff [分支1] [分支2]
git diff [分支1]..[分支2]
#指定文件
git diff [分支1]:[file1] [分支2]:[file2]

#查看指定提交范围内的所有变更文件情况
git diff --stat master~[范围值] [分支名]
git diff --stat master~5 tmp
//还可以值查看具体某一个文件
git diff --stat master~5 tmp test.txt

定位哪个提交点导致文件出现问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#先确定范围
git bisect start
git bisect bad #一般都是当前HEAD是坏提交【sha1】
git bisect good a794f9bd96f06b57b4c10433e4d6abb3f0855749

#上面的步骤就是确定范围的,接下来就是回答git的问题,他指定的提交点是好的还是坏的
git bisect good//如果是坏的,就bad,直到你找到哪个提交点导致出现问题
#查看维护日志
git bisect log

git branch
#完成操作后,要回切到工作分支上
git bisect reset
git branch

git reset --hard fb47ddb2db...

检查文件中每一行代码是谁提交的记录

1
git blame -L [起始行数],[文件名]

创建分支

1
2
3
4
5
6
#以当前节点作为分支的开始起点
git branch [分支名]
#以SHA1作为分支开始起点
git branch [分支名] [SHA1]
#创建并切换分支,sha1以哪个节点作为分支的起点
git checkout -b [分支名] [SHA1]

拉去远程仓库分支代码

1
2
3
4
git checkout 1.0
git pull
#或者
git checkout 1.0 origin/1.0

开发的过程中生成新分支

1
2
3
4
5
#因可能存在未被git监管和未提交的内容,需要将未提交的内容进行监管和暂存
git add .
git stash
#包含[SHA1]及之前的代码会被copy盗分支上
git branch [分支名] [SHA1]

重命名分支

在git中重命名远程分支,其实就是先删除远程分支,然后重命名本地分支,再重新提交一个远程分支。

1
2
3
4
5
6
7
8
//显示现在分支
git branch -av
//删除远程要删除的分支devel
git push --delete origin devel
//重命名本地分支devel为develop
git branch -m devel develop
//推送到远程
git push origin develop

这是由于在 github 中,devel 是项目的默认分支。要解决此问题,这样操作:

  1. 进入 github 中该项目的 Settings 页面;
  2. 设置 Default Branch 为其他的分支(例如 master);
  3. 重新执行删除远程分支命令。

查看远程仓库分支

1
git branch -a

根据远程仓库分支代码创建分支

1
git fetch origin [远程仓库分支名:远程仓库分支名] [本地仓库分支名:本地仓库分支名]

删除本地分支

1
git branch -d [分支名]

删除远程仓库分支

1
git remote remove [分支名]

查看分支

1
2
3
git show-branch
#
git branch

分支前面都存在*或者!

*表示当前分支

在–之后的是记录分支的提交信息

*+ [tmp] 远程2就表示该提交存在于两个分支中

显示某分支中某文件内容

1
git show [分支名]:[文件名]

显示某个节点某文件的内容

1
git show [SHA1] [文件名]

关于切换分支的逻辑

如果存在未被git追踪的文件,git是会将其忽略的

如果存在已追踪且被修改或删除,必须commit之后,才能切换

如果要不计后果的情况,强切,加-f

将当前的分支修改的内容同步到其他的分支上

假如你希望变更作用于另一个分支上,但由于当前分支如果不提交,是无法切换到另一个分支上的

1
git checkout -m [另一个分支名]

将一个区间的提交,移植到另一个分支

1
2
#当前分支,得到dev分支中dev~2之前的所有提交内容
git cherry-pick dev~2

cherry-pick会生成一条新的提交记录

代码行数统计

统计某人的代码提交量,包括增加,删除:

1
git log --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }' -

统计某人一个月内的代码提交量,包括增加,删除:

1
git log --since=1.month.ago --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }' -

仓库提交者排名前 5(如果看全部,去掉 head 管道即可):

1
git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5

仓库提交者(邮箱)排名前 5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字

1
git log --pretty=format:%ae | gawk -- '{ ++c[$0]; } END { for(cc in c) printf "%5d %s\n",c[cc],cc; }' | sort -u -n -r | head -n 5

贡献者统计:

1
git log --pretty='%aN' | sort -u | wc -l

提交数统计:

1
git log --oneline | wc -l

git log 参数说明:

–author 指定作者
–stat 显示每次更新的文件修改统计信息,会列出具体文件列表
–shortstat 统计每个commit 的文件修改行数,包括增加,删除,但不列出文件列表:
–numstat 统计每个commit 的文件修改行数,包括增加,删除,并列出文件列表:

-p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新
例如:git log -p -2
–name-only 仅在提交信息后显示已修改的文件清单
–name-status 显示新增、修改、删除的文件清单
–abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符
–relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)
–graph 显示 ASCII 图形表示的分支合并历史
–pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)
例如: git log –pretty=oneline ; git log –pretty=short ; git log –pretty=full ; git log –pretty=fuller
–pretty=tformat: 可以定制要显示的记录格式,这样的输出便于后期编程提取分析
例如:git log –pretty=format:””%h - %an, %ar : %s””
下面列出了常用的格式占位符写法及其代表的意义。
选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
–since 限制显示输出的范围,
例如: git log –since=2.weeks 显示最近两周的提交
选项 说明
-(n) 仅显示最近的 n 条提交
–since, –after 仅显示指定时间之后的提交。
–until, –before 仅显示指定时间之前的提交。
–author 仅显示指定作者相关的提交。
–committer 仅显示指定提交者相关的提交。

一些例子: git log --until=1.minute.ago // 一分钟之前的所有 log git log --since=1.day.ago //一天之内的log git log --since=1.hour.ago //一个小时之内的 log git log --since=`.month.ago --until=2.weeks.ago //一个月之前到半个月之前的log git

log –since ==2013-08.01 –until=2013-09-07 //某个时间段的 log git blame
看看某一个文件的相关历史记录
例如:git blame index.html –date short

子模块

保留子组件的现有目录结构的完整性,故而git创造了类似于maven中的module一样的功能,来实现子模块的管理

打个比方:现在我有一个父工程A,其工程路径下面有五个子工程BCDEF,按照往常是要git clone 6个工程才可以完全下载成功,而且工程与工程之间的路径关系也不知道。现在有了gitmodules就可以直接下载A工程就可以了,其他的5个子工程都会自动下载。

文件.gitmodules存放在父工程根目录下

1
2
3
4
5
6
7
8
9
10
[submodule "others/B"]
path = others/B
url = https://rep.XXX.com/crm/B.git
[submodule "sources/C"]
path = sources/C
url = https://rep.XXX.com/crm/C.git
[submodule "sources/D"]
path = sources/D
url = https://rep.XXX.com/crm/D.git
.....

添加子模块

1
git submodule add [远程仓库地址] [相对于父模块的相对路径]

注:

直接手动更改gitmodule文件是没有用的哦

远程仓库地址要先于子模块之前准备好

子模块的名称是可以与[相对于父模块的相对路径]不一致的

创建完成以后会生成.gitmodules.gitattributes这两个文件

.gitmodules.git/config保存着子模块的信息

从远程仓库获取所有模块数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#方式一
git clone --recursive [远程仓库地址]
#方式二
git clone [远程父仓库地址]
cd [父模块路径]
git submodule init
git submodule update
或者
git submodule update --init --recursive

cd [目标submodule]
git branch -a
git fetch origin [remote branch:remote branch] [new local branch:new local branch]
git checkout [new local branch]

删除子模块功能

先清空.gitmodules中的内容

再执行文件的删除

————

其他命令

1
2
# .gitmodules中子模块的内容更新到.git/config中
git submodule init

gitmodules参考

关闭issue

格式Fixes #45,45是issue的ID,在相应的issue的链接地址就有。

可以关闭issue的关键字:

  • close
  • closes
  • closed
  • fix
  • fixes
  • fixed
  • resolve
  • resolves
  • resolved

不同的仓库中关闭issue

格式close username/repository#issue_number

关闭多个issues

格式closes #34, closes #23, and closes example_user/example_repo#42

Fork&Pull Request

1
2
3
4
5
6
7
8
9
10
# 1、目标仓库A 页面 Fork
# 2、git clone 在自己仓库的Fork代码到本地
# 3、绑定数据源
git remote add upstream [fork源的远程仓库地址]
# 4、更新分支代码
git pull upstream master
# 5、commit&push到自己的仓库
git push origin master
# 6、pull request
# 7、等待对方同意

git停留在writing objects

1
git config --global http.postBuffer 524288000

http.postBuffer默认上限为1M,上面的命令是把git的配置里http.postBuffer的变量改大为500M

文件大,上传慢

1
git remote set-url origin [你的ssh地址]

把远程仓库的上传由原来的HTTPS改为SSH上传,github在国内本身就慢,还可能被dns污染 , 因此推荐使用SSH上传

迹_Jason wechat
分享快乐,感受科技的温度
坚持原创技术分享,您的支持将鼓励我继续创作!