目录
- 前言
- 1.安装
-
- [1.1. windows](#1.1. windows)
-
- 1.1.1.git客户端
- 1.1.2.配置git客户端
- 1.1.3.安装TortoiseGit图形客户端
- [1.1.4 关于文件换行问题](#1.1.4 关于文件换行问题)
- 1.2.ubuntu
-
- 1.2.1.ubuntu终端Git中文乱码
- [1.2.2 git log中文乱码解决](#1.2.2 git log中文乱码解决)
- 2.建立版本库
- 3.基本操作
-
- 3.1.忽略某些文件
- 3.2.保留一些空目录
- 3.3.提交到本地
- 3.4.查看差异和日志
- 3.5.远程仓库
- 3.6.远程分支
- 3.7.本地分支操作
- 3.8.检出以前的版本
-
- [3.8.1. 根据sha值建立分支](#3.8.1. 根据sha值建立分支)
- 3.8.2.临时检出版本
- 3.9.打标签和推送标签
- 3.10.分支管理
- 4.子模块
- 附录A.托管服务器推荐
- 附录B.学习资料
前言
以前一直用subversion,使用图形界面,用了好几年,后来公司要求转git,别人一月就转变过来了,我一年后才转换过来,寻找各种教程,尝试各种工具.最终发现引领我入门的是官网的Pro git,而最终选择的工具是直接使用命令行.
这是我常用的命令行使用手册,当时迷茫的是命令行太多,哪些是我需要的呢?怎样使用git命令代替我使用subversion通过界面进行操作.基于这两点考虑,我总结了一些使用场景我常用的命令,这些命令我也记不住,在使用的过程中打开我这篇手册,忘记的操作立马查看,只要记得使用场景就可以了,然后慢慢简单使用的命令代替场景下的命令.慢慢优化形成了这本手册,其实子模块使用很少,没有进行命令优化,可以不学习子模块,代替子模块比较好的方案是使用repo.
1.安装
1.1. windows
1.1.1.git客户端
在网站https://git-scm.com/ 下载最新版的git客户端进行安装。
安装时,一直next就可以了。
1.1.2.配置git客户端
配置如下:
bash
git config --global user.name "fedorayang"
git config --global user.email "fedorayang@163.com"
git config --global --bool http.sslVerify true
git config --global branch.autosetuprebase always
git config --global credential.helper store
git config --global core.quotepath false
git config --global core.filemode false
查看当前的配置
bash
git config --list
user.name,user.email每次提交时,在log中都会体现,用于标识提交者和联系方式。
这几个配置选项一定要配置。
1.1.3.安装TortoiseGit图形客户端
Git使用建议使用命令行,其实图形客户端使用起来也非常方便。
https://tortoisegit.org/ 下载最新的客户端,
安装时,一直next就可以了。
查看是否安装成功。
1.1.4 关于文件换行问题
Dos和Windows平台: 使用回车(CR)和换行(LF)两个字符来结束一行,回车+换行(CR+LF),即"\r\n";
Mac 和 Linux平台:只使用换行(LF)一个字符来结束一行,即"\n";
最早Mac每行结尾是回车CR 即'\r',后mac os x 也投奔了 unix。
bash
#提交时转换为LF,检出时转换为CRLF
git config --global core.autocrlf true
#提交时转换为LF,检出时不转换
git config --global core.autocrlf input
#提交检出均不转换
git config --global core.autocrlf false
你也可以在文件提交时进行safecrlf检查
bash
#拒绝提交包含混合换行符的文件
git config --global core.safecrlf true
#允许提交包含混合换行符的文件
git config --global core.safecrlf false
#提交包含混合换行符的文件时给出警告
git config --global core.safecrlf warn
1.2.ubuntu
bash
sudo apt install git-all
安装git所有相关
1.2.1.ubuntu终端Git中文乱码
ubuntu终端Git中文乱码:200\273\347\273\223
使用git add添加要提交的文件的时候,显示形如2200\273\347\273\223乱码。
解决方案:
bash
git config --global core.quotepath false
1.2.2 git log中文乱码解决
1.运行Git Bash窗口,在该窗口导航条(即最上面)右键,选择Options−>Text,找到下面两处:
Locale: zh_CN
Charector set: UTF-8
2.到Git Bash命令窗口输入如下设置命令语句
bash
#该命令表示提交命令的时候使用utf-8编码集提交
git config --global i18n.commitencoding utf-8
#该命令表示日志输出时使用utf-8编码集显示
git config --global i18n.logoutputencoding utf-8
#设置LESS字符集为utf-8
export LESSCHARSET=utf-8
设置完成后,发现使用git log后,之前提交代码的中文注释正确显示出来.
2.建立版本库
建立版本库根据使用场景不同有几种方式:下载网上开源版本库,自己创建本地版本库,在服务器上创建版本库。
2.1.下载网上开源版本库
使用浏览器开源项目,例如https://source.denx.de/u-boot/u-boot
2.1.1.复制下载地址
2.1.2.使用命令行下载库
打开git命令行窗口
输入 git clone https://source.denx.de/u-boot/u-boot.git 。 会自动下载,等待下载完成。
2.1.3.使用图形界面下载库
复制git仓库地址后,可以使用torroiseGit下载仓库
2.2.在当前目录初始化版本库
2.2.1.命令行方式创建
bash
#初始化版本
git init
#把当前工作区内容添加到版本库
git add
#提交到仓库
git commit
2.2.2.GUI方式创建
3.基本操作
3.1.忽略某些文件
.gitignore
3.2.保留一些空目录
touch .gitkeep
3.3.提交到本地
git status
git add
git commit
3.4.查看差异和日志
git log -p -2
git diff
3.5.远程仓库
3.5.1查看当前的远程库
git remote
git remote -v
3.5.2.添加远程仓库
git remote add [shortname] [url]
3.5.3.从远程仓库抓取数据
bash
git fetch [remote-name]
3.5.4.推送数据到远程仓库
bash
git push [remote-name] [branch-name]
#创建远程分支并推送
git push -u [remote-name] [branch-name]
3.5.5.查看远程仓库信息
git remote show [remote-name]
3.5.6.远程仓库的删除和重命名
git remote rename pb paul
git remote rm paul
3.5.7.更改远程仓库服务器地址
git remote origin set-url [url]
3.5.8.远程仓库版本回退
git push <仓库名> <分支名> -f
3.5.9.远程仓库删除后更新到本地
git remote prune origin
git branch -a
3.6.远程分支
3.6.1.推送
git push origin serverfix
git push origin refs/heads/serverfix:refs/heads/serverfix
[local] [remote]
git push origin serverfix:serverfix
git push origin serverfix:awesomebranch
3.6.2.查看远程分支
git fetch origin
或者
git fetch
git branch -a
3.6.3.合并到当前分支
git merge origin/serverfix
3.6.4.建立新的本地分支
git checkout -b serverfix origin/serverfix
或者
git checkout --track origin/serverfix
3.6.5.获取数据
git pull
git fetch 和 git merge origin/serverfix
3.6.6.推送数据
git push
3.6.7.删除远程分支
git push origin :serverfix
3.7.本地分支操作
3.7.1.建立分支
git branch onebranch 建立新的分支
git checkout --b secondbrach 建立新的分支名跳转到新分支
3.7.2.查看所有分支
git branch --a
3.7.3.切换分支
git checkout somebranch
3.7.4.删除分支
git branch --d somebranch
3.8.检出以前的版本
3.8.1. 根据sha值建立分支
git log 获取历史版本的sha值
git branch oldver 05984e59d8 根据sha值建立分支
使用过后,删除临时分支
3.8.2.临时检出版本
git log 获取历史版本的sha值
git checkout 05984e59d8 -- . 检出历史版本
使用过后恢复最新版本版本
git reset HEAD .
git checkout -- .
git clean --df
3.9.打标签和推送标签
对于发布的软件版本需要打上标签,方便管理
git tag -a v1.0.0
git push origin v1.0.0
3.10.分支管理
Git管理流程有许多种,这里推荐流程如下:
参考文档 http://roclinux.cn/?p=2129#more-2129
开源实现可以参考这个
https://github.com/petervanderdoes/gitflow-avh
4.子模块
4.1.添加子模块
bash
#在主仓库执行
git submodule add https://gitee.com/fedorayang/sumodule_sub1.git
#默认情况下,子模块会将子项目放到一个与仓库同名的目录中,本例中是"sumodule_sub1".
tree
sumodule_main/
├── README.md
└── sumodule_sub1
└── README.md
bash
#如果你想要放到其他地方,那么可以在命令结尾添加一个不同的路径.
git submodule add https://gitee.com/fedorayang/sumodule_sub2.git extern
tree
sumodule_main/
├── extern
│ └── README.md
├── README.md
└── sumodule_sub1
└── README.md
ini
git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitmodules
new file: extern
new file: sumodule_sub1
.gitmodules
文件内容:
ini
[submodule "sumodule_sub1"]
path = sumodule_sub1
url = https://gitee.com/fedorayang/sumodule_sub1.git
[submodule "extern"]
path = extern
url = https://gitee.com/fedorayang/sumodule_sub2.git
该配置文件保存了项目 URL 与已经拉取的本地目录之间的映射.
bash
#通过在本地执行
git config submodule.extern.url <私有URL>
#来覆盖这个选项的值
查看差异.
bash
git diff --cached
git diff --cached --submodule
bash
git commit #提交
git push origin master #推送
4.2.克隆含有子模块的项目
分步进行
bash
git clone https://gitee.com/fedorayang/sumodule_main.git
#当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件.
tree sumodule_main/
sumodule_main/
├── extern
├── README.md
└── sumodule_sub1
初始化本地配置文件
bash
git submodule init
Submodule 'extern' (https://gitee.com/fedorayang/sumodule_sub2.git) registered for path 'extern'
Submodule 'sumodule_sub1' (https://gitee.com/fedorayang/sumodule_sub1.git) registered for path 'sumodule_sub1'
从该项目中抓取所有数据 并检出父项目中列出的合适的提交
bash
git submodule update
Cloning into '/home/git_test/sumodule_main/extern'...
Cloning into '/home/git_test/sumodule_main/sumodule_sub1'...
Submodule path 'extern': checked out '855083d4a426763ab36c0e0dde3218591e332b08'
Submodule path 'sumodule_sub1': checked out 'c6982e7d3a6b18e016d589ee234a04941e776b5a'
现在子目录是处在和之前提交时相同的状态了。
一步完成
git clone --recurse-submodules https://gitee.com/fedorayang/sumodule_main.git single_step
如果你已经克隆了项目但忘记了 --recurse-submodules
,那么可以运行 git submodule update --init
将 git submodule init
和 git submodule update
合并成一步。
如果还要初始化、抓取并检出任何嵌套的子模块, 请使用简明的 git submodule update --init --recursive
。
4.3.在包含子模块的项目上工作
4.3.1.从子模块的远端拉取上游修改
分步实现
bash
#1.进入到子模块目录
#2.拉取
git fetch
#3.合并
git merge origin/master
如果你现在返回到主项目并运行 git diff --submodule
,就会看到子模块被更新的同时获得了一个包含新添加提交的列表。
bash
git diff --submodule
Submodule sumodule_sub1 c6982e7..a43342c:
> hello
如果你不想每次运行 git diff
时都输入 --submodle
,那么可以将 diff.submodule
设置为 "log" 来将其作为默认行为。
bash
git config --global diff.submodule log
git diff
Submodule sumodule_sub1 c6982e7..a43342c:
> hello
如果在此时提交,那么你会将子模块锁定为其他人更新时的新代码。
单步实现
如果你不想在子目录中手动抓取与合并,那么还有种更容易的方式。 运行 git submodule update --remote
,Git 将会进入子模块然后抓取并更新。
bash
git submodule update --remote
此命令默认会假定你想要更新并检出子模块仓库的 master
分支。
子模块其他分支
不过你也可以设置为想要的其他分支。既可以在 .gitmodules
文件中设置 (这样其他人也可以跟踪它),也可以只在本地的 .git/config
文件中设置。
bash
git config -f .gitmodules submodule.sumodule_sub1.branch develop
git submodule update --remote
如果不用 -f .gitmodules
选项,那么它只会为你做修改。但是在仓库中保留跟踪信息更有意义一些,因为其他人也可以得到同样的效果。
查看信息
这时我们运行 git status
,Git 会显示子模块中有"新提交"。
bash
git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: extern
modified: sumodule_sub1
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .gitmodules
modified: sumodule_sub1 (new commits)
如果你设置了配置选项 status.submodulesummary
,Git 也会显示你的子模块的更改摘要:
bash
git config status.submodulesummary 1
git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: extern
modified: sumodule_sub1
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .gitmodules
modified: sumodule_sub1 (new commits)
Submodule changes to be committed:
* extern 855083d...31954ce (1):
> init
* sumodule_sub1 c6982e7...a43342c (1):
> hello
Submodules changed but not updated:
* sumodule_sub1 a43342c...35c50c4 (1):
> develop
这时如果运行 git diff
,可以看到我们修改了 .gitmodules 文件,同时还有几个已拉取的提交需要提交到我们自己的子模块项目中。
bash
git diff
这非常有趣,因为我们可以直接看到将要提交到子模块中的提交日志。 提交之后,你也可以运行 git log -p
查看这个信息。
bash
git log -p --submodule
更新单个子模块
当运行 git submodule update --remote
时,Git 默认会尝试更新 所有 子模块, 所以如果有很多子模块的话,你可以传递想要更新的子模块的名字。
4.3.2.从项目远端拉取上游更改
只是执行 git pull
获取你新提交的更改还不够,应该使用
bash
git submodule update --init --recursive
#或者
git pull --recurse-submodules
可以将配置选项 submodule.recurse
设置为 true
,此选项会让 Git 为所有支持 --recurse-submodules
的命令使用该选项(除 clone
以外)。
子模块的 URL 发生了改变
bash
# 将新的 URL 复制到本地配置中
git submodule sync --recursive
# 从新 URL 更新子模块
git submodule update --init --recursive
4.4.在子模块上工作
首先,让我们进入子模块目录然后检出一个分支。
bash
cd DbConnector/
git checkout stable
Switched to branch 'stable'
然后尝试用 "merge" 选项来更新子模块。 为了手动指定它,我们只需给 update
添加 --merge
选项即可。 这时我们将会看到服务器上的这个子模块有一个改动并且它被合并了进来。
bash
cd ..
git submodule update --remote --merge
现在让我们看看当我们对库做一些本地的改动而同时其他人推送另外一个修改到上游时会发生什么。
如果我们现在更新子模块,就会看到当我们在本地做了更改时上游也有一个改动,我们需要将它并入本地。
bash
git submodule update --remote --rebase
如果你忘记 --rebase
或 --merge
,Git 会将子模块更新为服务器上的状态。并且会将项目重置为一个游离的 HEAD 状态。
即便这真的发生了也不要紧,你只需回到目录中再次检出你的分支(即还包含着你的工作的分支)然后手动地合并或变基 origin/stable
(或任何一个你想要的远程分支)就行了。
4.4.1发布子模块改动
如果我们在主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到麻烦, 因为他们无法得到依赖的子模块改动。那些改动只存在于我们本地的拷贝中。
为了确保这不会发生,你可以让 Git 在推送到主项目前检查所有子模块是否已推送。 git push
命令接受可以设置为 "check" 或 "on-demand" 的 --recurse-submodules
参数。 如果任何提交的子模块改动没有推送那么 "check" 选项会直接使 push
操作失败。
bash
git push --recurse-submodules=check
The following submodule paths contain changes that can
not be found on any remote:
DbConnector
Please try
git push --recurse-submodules=on-demand
or cd to the path and use
git push
to push them to a remote.
如果你想要对所有推送都执行检查,那么可以通过设置 git config push.recurseSubmodules check
让它成为默认行为。
另一个选项是使用 "on-demand" 值,它会尝试为你这样做。
bash
git push --recurse-submodules=on-demand
如你所见,Git 进入到 DbConnector 模块中然后在推送主项目前推送了它。 如果那个子模块因为某些原因推送失败,主项目也会推送失败。 你也可以通过设置 git config push.recurseSubmodules on-demand
让它成为默认行为。
4.4.2.合并子模块改动
如果你和其他人同时改动了一个子模块引用,那么可能会遇到一些问题。 也就是说,如果子模块的历史已经分叉并且在父项目中分别提交到了分叉的分支上,那么你需要做一些工作来修复它。
如果一个提交是另一个的直接祖先(一个快进式合并),那么 Git 会简单地选择之后的提交来合并,这样没什么问题。
不过,Git 甚至不会尝试去进行一次简单的合并。 如果子模块提交已经分叉且需要合并,那你会得到类似下面的信息:
bash
git pull
remote: Counting objects: 2, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 2 (delta 1), reused 2 (delta 1)
Unpacking objects: 100% (2/2), done.
From https://github.com/chaconinc/MainProject
9a377d1..eb974f8 master -> origin/master
Fetching submodule DbConnector
warning: Failed to merge submodule DbConnector (merge following commits not found)
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.
所以本质上 Git 在这里指出了子模块历史中的两个分支记录点已经分叉并且需要合并。 它将其解释为 "merge following commits not found" (未找到接下来需要合并的提交),虽然这有点令人困惑,不过之后我们会解释为什么是这样。
如果你运行 git diff
,就会得到试图合并的两个分支中记录的提交的 SHA-1 值。
bash
git diff
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
所以,在本例中,eb41d76
是我们的子模块中大家共有 的提交,而 c771610
是上游拥有的提交。 如果我们进入子模块目录中,它应该已经在 eb41d76
上了,因为合并没有动过它。 如果不是的话,无论什么原因,你都可以简单地创建并检出一个指向它的分支。
来自另一边的提交的 SHA-1 值比较重要。 它是需要你来合并解决的。 你可以尝试直接通过 SHA-1 合并,也可以为它创建一个分支然后尝试合并。 我们建议后者,哪怕只是为了一个更漂亮的合并提交信息。
所以,我们将会进入子模块目录,基于 git diff
的第二个 SHA-1 创建一个分支然后手动合并。
bash
$ cd DbConnector
$ git rev-parse HEAD
eb41d764bccf88be77aced643c13a7fa86714135
$ git branch try-merge c771610
(DbConnector) $ git merge try-merge
Auto-merging src/main.c
CONFLICT (content): Merge conflict in src/main.c
Recorded preimage for 'src/main.c'
Automatic merge failed; fix conflicts and then commit the result.
我们在这儿得到了一个真正的合并冲突,所以如果想要解决并提交它,那么只需简单地通过结果来更新主项目。
bash
$ vim src/main.c (1)
$ git add src/main.c
$ git commit -am 'merged our changes'
Recorded resolution for 'src/main.c'.
[master 9fd905e] merged our changes
$ cd .. (2)
$ git diff (3)
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit eb41d764bccf88be77aced643c13a7fa86714135
-Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d
++Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a
$ git add DbConnector (4)
$ git commit -m "Merge Tom's Changes" (5)
[master 10d2c60] Merge Tom's Changes
- 首先解决冲突
- 然后返回到主项目目录中
- 再次检查 SHA-1 值
- 解决冲突的子模块记录
- 提交我们的合并
这可能会让你有点儿困惑,但它确实不难。
有趣的是,Git 还能处理另一种情况。 如果子模块目录中存在着这样一个合并提交,它的历史中包含了的两边的提交,那么 Git 会建议你将它作为一个可行的解决方案。 它看到有人在子模块项目的某一点上合并了包含这两次提交的分支,所以你可能想要那个。
4.5.子模块的技巧
4.5.1.子模块遍历
4.5.2.有用的别名
4.6.子模块的问题
4.6.1.切换分支
4.6.2.从子目录切换到子模块
4.7.子模块命令
bash
absorbgitdirs
add
deinit
foreach
init
set-branch
set-url
status
summary
sync
update
附录A.托管服务器推荐
全球大多数开源软件在这里安家,资源丰富,国外网站,速度有点慢。中国对这个网站很犹豫有时屏蔽,有时开放,需要科学上网才行。
只对中国国内,速度快,资源不太丰富。
附录B.学习资料
git官网《Pro Git book》是一本很好的开源书籍。
Windows本地手册
[Ubuntut本地手册](file:///usr/share/doc/git-doc)
本地手册使用浏览器打开,这里有大量网页帮助文档,太详细而显冗余。