36 KiB
Git简明教程
By 徐日
目录
1 安装Git
- 1.1 从源代码安装
- 1.2 Linux包管理工具安装
2 配置Git
- 2.1 配置用户名及邮箱
- 2.2 常用文本编辑器
- 2.3 差异分析工具
- 2.4 查看配置信息
- 2.5 help
3 Git基础
- 3.1 创建Git仓库
- 3.1.1 将当前目录转化为Git仓库
- 3.1.2 从远程仓库clone到本地仓库
- 3.2 文件操作
- 3.2.1 查看当前文件状态
- 3.2.2 跟踪文件
- 3.2.3 忽略文件
- 3.2.4 diff命令
- 3.2.5 提交文件
- 3.2.6 移除文件
- 3.2.7 文件改名
- 3.2.8 查看提交历史
- 3.2.9 取消变更
4 远程仓库
- 4.1 克隆远程仓库
- 4.2 添加远程仓库
- 4.3 推送数据到远程仓库
- 4.4 远程仓库的删除与重命名
5 标签
- 5.1 查看和新建标签
- 5.2 后期加注标签
6 分支
- 6.1 创建、切换和删除分支
- 6.2 合并分支
- 6.3 推送与获取
- 6.4 分支管理
- 6.4.1 长期分支
- 6.4.2 bug 分支
- 6.4.3 多人协作
1 安装Git
1.1 从源代码安装
首先安装依赖工具:
$ yum install curl-devel expat-devel gettext-devel \
openssl-devel zlib-devel
$ apt-get install curl-devel expat-devel gettext-devel \
openssl-devel zlib-devel
然后从Git官网下载最新版本源代码: Git 编译安装:
$ tar -zxf git-1.6.0.5.tar.gz
$ cd git-1.6.0.5
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install
1.2 Linux包管理工具安装
Fedora上用yum安装:
$ yum install git-core
Ubuntu等Debian体系的系统上,用apt-get安装:
$ apt-get instal git-core
2 配置Git
2.1 配置用户名及邮箱
配置您的名字和邮箱,这里用户名为 John Doe ,
邮箱为 johndoe@example.com 。
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
2.2 常用文本编辑器
选择Git默认的文本编辑器,方便输入信息,
这里编辑器设为emacs。
$ git config --global core.editor emacs
2.3 差异分析工具
在Git中常常会进行冲突合并,这时就要采用差异分析工具,
这里用到的是 vimdiff
$ git config --global merge.tool vimdiff
2.4 查看配置信息
在进行了上述操作,可以打印出配置信息查看是否配置成功,
使用 git config --list 命令。
$ git config --list
user.name=Scott Chacon
user.email=schacon@gmail.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...
2.5 help
如果不了解某个操作的具体命令,可以输入:
$ git help < verb >
$ git < verb > --help
$ man git-< verb >
进行命令的查询。
3 Git 基础
3.1 创建Git仓库
3.1.1 将当前目录转化为Git仓库
对现有的某个项目进行Git管理,那么Git的操作十分简单,只需
进入该项目的目录,执行:
$git init
该目录就转化为Git仓库,但现在仓库里还没开始跟踪任何一个文件
如果想将某个文件纳入版本控制,只需执行:
$git add *.c
$git add README
$git commit -m "初始化版本控制"
上面的命令就将当前目录下所有的C文件和README文件纳入版本控制
提交到了暂存区,并一起提交到了版本库。
3.1.2 从远程仓库clone到本地仓库
如果您在Github上已经有了项目的版本库,或者工作需要很多人一起进行
项目的版本控制,那么从远程库上直接克隆将是最快捷最正确的方式:
首先登录你的Github账号,找到需要克隆到本地的项目,点击 Clone or download
选择 Clone with SSH 获得SSH方式下载地址,如图: Github
$ git clone git@github.com:CrazyCatJack/gitskills.git
上面这条语句就是从我的Github账号上克隆gitskills仓库到本地,
你会发现在本地会多了一个名为gitskills的目录,里面有一个.git的目录和Github上
已克隆项目的全部文件,这时就可以工作了。当然,你也可以自定义库存在目录的名称:
$ git clone git@github.com:CrazyCatJack/gitskills.git yourname
3.2 文件操作
3.2.1 查看当前文件状态
通过 git status 命令查看当前项目文件的状态:
$ git status
On branch master
nothing to commit (working directory clean)
这表示该工作目录很干净,没有未提交的更改。如果进行了提交暂存区但没有commit,
或者没有提交暂存区但修改了文件(修改文件内容、新建或删除文件)。通过git status命令
我们就能知道项目中有哪些文件还未提交。
$ vim README
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
README
nothing added to commit but untracked files present (use "git add" to track)
3.2.2 跟踪文件
使用命令 git add 可以跟踪一个新文件
$git add README
上例将README文件跟踪,相当于提交到暂存区。
我们用 git status 可以查到当前项目的文件状态
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file:
README
可以看到git提示 README是一个新文件并提交到了暂存区,等待commit。
需要注意的是,如果新创建了文件,或者将已经提交的文件进行修改,那么
需要重新对有变更的文件进行git add 和 git commit。
3.2.3 忽略文件
在工程项目中,无论是编译内核还是编译根文件系统,编译bootloader
都会产生很多中间文件,比 如 *.o *.a等。因此为了避免不必要的麻烦,保持工作区的“干净”
有时我们会忽略这些中间文件,只提交最终生成的文件。
$ cat .gitignore
*.[oa]
*~
这里我们创建一个名为 .gitignore的文件,列出了要忽略的文件格式:所有.o和.a后缀的文件,所有
副本文件都将被git忽略。
文件 .gitignore 的格式规范如下:
• 所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
• 可以使用标准的 glob 模式匹配。
• 匹配模式最后跟反斜杠(/)说明要忽略的是目录。
• 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
这里解释一下glob模式:(*)匹配零个或多个任意字符;[ abc ] 匹配任意一个在括号里的字符;
(?)匹配任意一个字符;[0-9]表示匹配0到9范围内的任意一个字符。
这里给出了部分例子和注释:
此为注释 – #将被 Git 忽略
*.a # 忽略所有 .a 结尾的文件
!lib.a # 但 lib.a 除外
/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
3.2.4 diff命令
如果想查看已暂存但未提交的文件发生了哪些更改,可直接输入 git diff命令
diff --git a/addfile.c b/addfile.c
index 751cc4a..922af89 100644
--- a/addfile.c
+++ b/addfile.c
@@ -1 +1,5 @@
#include<iostream>
+int main()
+{
+return 0;
+}
该命令比较的是工作区当前文件和暂存区快照的差异,上述例子就是addfile.c文件已经add到暂存区后
再对其内容进行修改,增添的内容会用“+”表示出来。所以你应该能够猜到原来的文件是只有头文件,更改后
增加了主函数吧。
另一种情况是,你想查看这次暂存的文件和已经提交的文件的差别,那么需要键入git diff --cached 以查看情况:
xuri@Ubuntu-XuRi:~/gitskills$ git diff --cached
diff --git a/addfile.c b/addfile.c
index 922af89..3b5ad8c 100644
--- a/addfile.c
+++ b/addfile.c
@@ -1,4 +1,4 @@
-#include<iostream>
+#include<stdio.h>
int main()
{
return 0;
}
可以得知,我修改了头文件,在上一次提交中,我使用的是 #include< iostream > 头文件,在这次我提交到
缓存区的addfile.c文件中,我将头文件改为了 #include < stdio.h > 。
3.2.5 提交文件
当我们的更新后的文件已全部add到暂存库,一切准备就绪,那么我们就可以进行提交了。
输入git commit -m "对本次提交的注释" 即可进行提交
xuri@Ubuntu-XuRi:~/gitskills$ git commit -m "修改了文件addfile.c"
[master 3b33dd3] 修改了文件addfile.c
1 file changed, 3 insertions(+), 2 deletions(-)
可以看到,git提示,有一个文件发生了改变,修改了addfile.c文件
这时,我们再看一下工作区是否干净:
xuri@Ubuntu-XuRi:~/gitskills$ git status
位于分支 master
您的分支领先 'origin/master' 共 2 个提交。
(使用 "git push" 来发布您的本地提交)
无文件要提交,干净的工作区
这里我们看到系统提示是干净的工作区,又因为我的这个目录是连接到Github上的远程仓库,所以这里
git提示我的分支领先远程仓库两个提交。这部分内容我后面会详细介绍。
3.2.6 移除文件
首先,如果仅仅是简单地用rm指令移除已经跟踪的文件,则工作区是不干净的,也就是说,这属于一种文件更新
xuri@Ubuntu-XuRi:~/gitskills$ ls
addfile.c README.md xuri.c
xuri@Ubuntu-XuRi:~/gitskills$ rm README.md
xuri@Ubuntu-XuRi:~/gitskills$ ls
addfile.c xuri.c
xuri@Ubuntu-XuRi:~/gitskills$ git status
位于分支 master
您的分支领先 'origin/master' 共 2 个提交。
(使用 "git push" 来发布您的本地提交)
尚未暂存以备提交的变更:
(使用 "git add/rm <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
删除: README.md
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
可以看到Git提示我们这种文件更新需要提交,或者我们取消这种提交。
xuri@Ubuntu-XuRi:~/gitskills$ git commit -m "删除README.MD文件"
[master c4cea03] 删除README.MD文件
1 file changed, 4 deletions(-)
delete mode 100644 README.md
可以看到我们已经提交了这次文件更新。如果我们想要移除跟踪但不删除文件,可以用 git rm --cached 文件名
指令来移除跟踪。并在.gitignore文件中声明此文件,则git将忽略这一文件。
如果我们想递归的删除某个目录下的所有文件,可以用*git rm 目录名/*.c *这条指令意为递归的删除此目录下的所有
C文件。同时,也可以不指定目录,直接递归删除当前目录下所有的指定文件。eg: git rm *~ 这条命令会递归删除当
前目录下所有~结尾的文件。
3.2.7 文件改名
在git下,改名非常容易,只需用git mv name1 name2命令即可实现:
xuri@Ubuntu-XuRi:~/gitskills$ git mv addfile.c addfile2.c
xuri@Ubuntu-XuRi:~/gitskills$ git status
位于分支 master
您的分支领先 'origin/master' 共 3 个提交。
(使用 "git push" 来发布您的本地提交)
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
重命名: addfile.c -> addfile2.c
可以看到,git跟踪到了这次重命名,并且提示这次变更需要提交。
3.2.8 查看提交历史
在git中,我们可以不但可以查阅当前仓库的所有提交信息,还能够查到有关分支合并,处理冲突的信息。
这些都基于git log指令。
xuri@Ubuntu-XuRi:~/gitskills$ git log
commit c4cea037ed4cf2c15393362759e23864ddb7aad2
Author: xuri <xuri@allwinnertech.com>
Date: Mon Mar 13 09:08:05 2017 +0800
删除README.MD文件
commit 3b33dd3632ccfa7bc3395e33c1a6e4c37214f4a3
Author: xuri <xuri@allwinnertech.com>
Date: Fri Mar 10 17:35:56 2017 +0800
修改了文件addfile.c
commit afe63dbeace7dcbc89ea7a8a16d5d75bb0a9fd96
Author: xuri <xuri@allwinnertech.com>
Date: Fri Mar 10 16:32:42 2017 +0800
double yinhao
commit eb2ddb21a8dc23d2947d3e95a85443fbbcc96ef1
Merge: d19d73d 0a14898
Author: xuri <xuri@allwinnertech.com>
Date: Tue Mar 7 14:59:00 2017 +0800
上面是git log指令的执行结果,可以看到以往提交的历史,包含完整的哈希字串,合并分支,作者,提交日期以及
当初提交时填写的提交信息。但是这样看起来还是有些麻烦。而且合并分支的过程不是很明显。我们可以用 --pretty 来
让log指定格式显示,还可以用 graph显示合并过程。
xuri@Ubuntu-XuRi:~/gitskills$ git log --pretty=format:"%h %s" --graph
* c4cea03 删除README.MD文件
* 3b33dd3 修改了文件addfile.c
* afe63db double yinhao
* eb2ddb2 merged bug fix 101
|\
| * 0a14898 fix bug 101
|/
* d19d73d merge with no-ff
|\
| * 9f7ccc3 no-ff merge mode
|/
* 4445115 conflict fixed
|\
| * 2469543 AND simple
* | 4e89f0d & simple
|/
* ca44755 add branchtest words
* a8b200c add xuri.c
* 7bd3450 Initial commit
上面的 --pretty 指出了log的输出格式,这里对其他输出格式做出列表:
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
3.2.9 取消变更
如果修改了工作区被跟踪的文件,且已经提交到暂存区。但是想撤回这一提交怎么办呢?其实我们跟着命令走就好,
因为Git十分的智能。这里我们更改了addfile2.c文件,并且提交到暂存区。这时我们用git status命令去查看工作区
状态:
xuri@Ubuntu-XuRi:~/gitskills$ git status
位于分支 master
您的分支领先 'origin/master' 共 4 个提交。
(使用 "git push" 来发布您的本地提交)
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
修改: addfile2.c
可以看到git提示,如果对于addfile2.c的文件修改需要取消暂存,那么使用*got reset HEAD *命令
xuri@Ubuntu-XuRi:~/gitskills$ git reset HEAD addfile2.c
重置后取消暂存的变更:
M addfile2.c
xuri@Ubuntu-XuRi:~/gitskills$ git status
位于分支 master
您的分支领先 'origin/master' 共 4 个提交。
(使用 "git push" 来发布您的本地提交)
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: addfile2.c
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
可以看到在执行命令后,文件取消暂存,git提示需要对文件进行add和commit。
xuri@Ubuntu-XuRi:~/gitskills$ git checkout -- addfile2.c
xuri@Ubuntu-XuRi:~/gitskills$ git status
位于分支 master
您的分支领先 'origin/master' 共 4 个提交。
(使用 "git push" 来发布您的本地提交)
无文件要提交,干净的工作区
当我们执行*git checkout --*后,就取消了对文件的修改。文件回到修改前的状态。刺客查看工作区,发现是干净的。
4 远程仓库
4.1 克隆远程仓库
首先,如果在Github 或者 Gitlab上有自己的项目仓库,则可以同步到本地,这里我们用到的是git clone指令
xuri@Ubuntu-XuRi:~$ git clone git@github.com:CrazyCatJack/gitskills.git clonegit正克隆到 'clonegit'...
remote: Counting objects: 29, done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 29 (delta 8), reused 24 (delta 6), pack-reused 0
接收对象中: 100% (29/29), done.
处理 delta 中: 100% (8/8), done.
检查连接... 完成。
可以看到,这里从Github上克隆了一个仓库下来,并且可以指定本地仓库的目录,这里我采用了clonegit作为本地仓库
的存储目录。
4.2 添加远程仓库
我们也可以添加一个新的远程仓库,并指定新仓库的名称。执行git remote add [shortname] [url] 来创建新仓库。
xuri@Ubuntu-XuRi:~/clonegit$git remote add lg git@github.com:CrazyCatJack/learngit.git
xuri@Ubuntu-XuRi:~/clonegit$ git remote -v
lg git@github.com:CrazyCatJack/learngit.git (fetch)
lg git@github.com:CrazyCatJack/learngit.git (push)
origin git@github.com:CrazyCatJack/gitskills.git (fetch)
origin git@github.com:CrazyCatJack/gitskills.git (push)
这里我们创建了一个新仓库lg,它来源于Github上的另一个项目仓库:learngit。同样,我们也可以把learngit上的分支和
文件抓取下来,这就要用到git fetch <仓库名>
xuri@Ubuntu-XuRi:~/xuri_git$ git fetch origin2
warning: no common commits
remote: Counting objects: 29, done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 29 (delta 8), reused 24 (delta 6), pack-reused 0
Unpacking objects: 100% (29/29), done.
来自 github.com:CrazyCatJack/gitskills
* [新分支] dev -> origin2/dev
* [新分支] master -> origin2/master
* [新标签] v1.2 -> v1.2
* [新标签] v0.8 -> v0.8
这里我们是将仓库origin2上的分支和文件同步到本地仓库。现在我们就可以更改这些文件和分支,并进行提交了。
4.3 推送数据到远程仓库
当我们在本地仓库进行了文件的修改,并最终决定可以提交的时候。我们就可以通过push指令,将本地仓库的分支提交
到远程仓库。
xuri@Ubuntu-XuRi:~/xuri_git$ git checkout master
之前的 HEAD 位置是 8ec153d... boy2 commit boy2.c
切换到分支 'master'
您的分支与上游分支 'origin/master' 一致。
xuri@Ubuntu-XuRi:~/xuri_git$ ls
helloworld.c LICENSE readme.txt
xuri@Ubuntu-XuRi:~/xuri_git$ git push origin master
Everything up-to-date
可以看到,在这个例子中,我将master分支提交到了origin远程仓库。
查看远程仓库的信息也可以用*git remote show <仓库名>*来实现:
xuri@Ubuntu-XuRi:~/xuri_git$ git remote show origin
* 远程 origin
获取地址:git@github.com:CrazyCatJack/learngit.git
推送地址:git@github.com:CrazyCatJack/learngit.git
HEAD 分支:master
远程分支:
master 已跟踪
为 'git pull' 配置的本地分支:
master 与远程 master 合并
为 'git push' 配置的本地引用:
master 推送至 master (最新)
4.4 远程仓库的删除与重命名
我们可以用git remote rename命令修改远程仓库的名称。
xuri@Ubuntu-XuRi:~/xuri_git$ git remote
origin
origin2
xuri@Ubuntu-XuRi:~/xuri_git$ git remote rename origin2 or
xuri@Ubuntu-XuRi:~/xuri_git$ git remote
or
origin
如上,将origin2仓库重命名为or。同时,我们也可以移除远程仓库:
xuri@Ubuntu-XuRi:~/xuri_git$ git remote
or
origin
xuri@Ubuntu-XuRi:~/xuri_git$ git remote rm or
xuri@Ubuntu-XuRi:~/xuri_git$ git remote
origin
这里,我们将远程仓库or删除,只剩下origin仓库。
5 标签
5.1 查看和新建标签
首先我们来看一下如何查看已经打好的标签。通过git tag命令就可以轻松的查看了:
xuri@Ubuntu-XuRi:~/xuri_git$ git tag
v0.8
v1.2
这里能够看到我之前是给两次提交打了标签。如果你只对特定的标签感兴趣,可以这样执行:
xuri@Ubuntu-XuRi:~/xuri_git$ git tag -l v1.*
v1.2
就可以查到符合要求的标签了,这里是列出所有1.*的标签。下面我们来看两种新建标签的方法。
首先,可以创建一个含附注类型的标签,并用*-a*指定标签的名字:
xuri@Ubuntu-XuRi:~/xuri_git$ git tag -a v1.6 -m "my v1.6 version"
即使将当前最近的一次提交打标签为v1.6版本。同时我们也能查看每个标签的具体信息:
xuri@Ubuntu-XuRi:~/xuri_git$ git show v1.6
tag v1.6
Tagger: xuri <xuri@allwinnertech.com>
Date: Mon Mar 13 14:42:02 2017 +0800
my v1.6 version
commit 23fd13c166bf85285f81fd69863e32d98210a5df
Author: xuri <xuri@allwinnertech.com>
Date: Tue Mar 7 11:28:44 2017 +0800
add helloworld.c file
然后,我们也可以创建一个轻量级的标签,直接给出标签名即可。
xuri@Ubuntu-XuRi:~/xuri_git$ git tag v1.8
xuri@Ubuntu-XuRi:~/xuri_git$ git tag
v0.8
v1.2
v1.6
v1.8
5.2 后期加注标签
如果当初忘记给某次提交加标签,没有关系。我们可以查看提交历史,然后给那次提交打上标签。首先我们查看以往的
提交历史:
xuri@Ubuntu-XuRi:~/xuri_git$ git log --pretty=oneline
23fd13c166bf85285f81fd69863e32d98210a5df add helloworld.c file
cf65c31294c72523b51c82f1497ee6757241d6f7 rmove test.txt
63a23baaa51b405a974a8ba297bb6b46c432d286 add test.txt
b16e8a0d3155d8f7eef2ef4db4257acb68af46fc add 'of files'
ab72e32628dbbf0cfca4c3cf05c009e747723542 git tracks changes
1feddd387a01a1ea66f4697b4588554e94b445ef understand how stage works
7f8d525e642dc734856036622ca20b8b0447795e append GPL
7d3ef5c14ab8de9f7e9580e1680a99b00eb80f65 add distributed
9f43349582c2b4dc03f2519a880bd21893696b29 wrote a readme file
下面通过git tag命令直接把第一次提交打标签为v0.6 。
xuri@Ubuntu-XuRi:~/xuri_git$ git tag -a v0.6 9f4334
xuri@Ubuntu-XuRi:~/xuri_git$ git tag
v0.6
v0.8
v1.2
v1.6
v1.8
如果想要推送标签的话,可以用git push origin <标签号>来实现推送某一个标签,如果想要把更新后的所有标签推送到远程仓库的话,可以用git push origin --tags实现。 xuri@Ubuntu-XuRi:~/xuri_git$ git push origin v0.6
Counting objects: 1, done.
Writing objects: 100% (1/1), 187 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:CrazyCatJack/learngit.git
* [new tag] v0.6 -> v0.6
xuri@Ubuntu-XuRi:~/xuri_git$ git push origin --tags
Counting objects: 29, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (26/26), done.
Writing objects: 100% (28/28), 2.64 KiB | 0 bytes/s, done.
Total 28 (delta 7), reused 0 (delta 0)
remote: Resolving deltas: 100% (7/7), done.
To git@github.com:CrazyCatJack/learngit.git
* [new tag] v0.8 -> v0.8
* [new tag] v1.2 -> v1.2
* [new tag] v1.6 -> v1.6
* [new tag] v1.8 -> v1.8
6 分支
6.1 创建、切换和删除分支
首先,如果你想创建一个分支,可以使用*git branch *命令实现。举例说明:
xuri@Ubuntu-XuRi:~/xuri_git$ git branch xuri
xuri@Ubuntu-XuRi:~/xuri_git$ git branch
dev
* master
xuri
这就创建了名称为"徐日"的分支。而查看当前仓库的所有分支和当前所在分支则输入git branch即可。上例说明我们
目前在master分支。如果我们想切换到dev分支,那么就需要*git checkout *命令来实现:
xuri@Ubuntu-XuRi:~/xuri_git$ git checkout dev
切换到分支 'dev'。如果想创建分支并且直接切换到此分支,则我们可以使用*git checkout -b <branch name>*命令实
现:
xuri@Ubuntu-XuRi:~/xuri_git$ git checkout -b quick
切换到一个新分支 'quick'
xuri@Ubuntu-XuRi:~/xuri_git$ git branch
dev
master
* quick
xuri
上例实现了创建并同时切换到quick分支。那么,如果我们想创建一个全新的无log记录无文件的空白分支,我们应该怎么做呢?有两种方法能够实现:
- 使用symbolic-ref
git symbolic-ref HEAD refs/heads/<newbranchname>
rm .git/index
git clean -fdx
<do some change to commit>
git add <your change>
git commit -s
这里我们只有做出修改,并最终提交后才能在git branch命令下看到我们创建的全新分支。 2. 使用 --orphan
git checkout --orphan <newbranchname>
git rm -rf .
rm .gitignore
<do some change to commit>
git add <your change>
git commit -s
与第一种方法类似,在作出修改并最终提交后才能在git branch命令下看到新分支。
如果我们想删除quick分支,我们可以先切换回master分支,然后执行*git branch -d *就可完成对指定分支的删除。
xuri@Ubuntu-XuRi:~/xuri_git$ git branch -d quick
已删除分支 quick(曾为 3a9bad1)。
6.2 合并分支
首先,我们在分支quick上对"helloworld.c"文件进行修改,并对其进行提交。
xuri@Ubuntu-XuRi:~/xuri_git$ git status
位于分支 quick
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: helloworld.c
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
xuri@Ubuntu-XuRi:~/xuri_git$ git commit -a -m "modify helloworld.c file."
[quick 3a9bad1] modify helloworld.c file.
1 file changed, 1 insertion(+)
然后切换回主分支master,合并分支quick到master上。
xuri@Ubuntu-XuRi:~/xuri_git$ git checkout master
切换到分支 'master'
您的分支与上游分支 'origin/master' 一致。
xuri@Ubuntu-XuRi:~/xuri_git$ git merge quick
更新 23fd13c..3a9bad1
Fast-forward
helloworld.c | 1 +
1 file changed, 1 insertion(+)
在合并完分支后,如果用不到分支quick,可将其删除,删除分支见6.1节。
然而,并不是每次合并都如这次一般顺利,因为当你在当前分支对文件进行修改的同时,可能忽略了你在其他分支的修
改。当你在其他分支对某个文件修改并提交分支后,切换到当前分支,再次对这个文件进行修改并提交,如果此时合并
两个分支,那么结果就会出现冲突。不过聪明的Git将冲突的部分写到了冲突文件中,只有我们将冲突修复,我们才能够
提交文件。
xuri@Ubuntu-XuRi:~/xuri_git$ git merge xuri
自动合并 helloworld.c
冲突(内容):合并冲突于 helloworld.c
自动合并失败,修正冲突然后提交修正的结果。
xuri@Ubuntu-XuRi:~/xuri_git$ git status
位于分支 master
您的分支领先 'origin/master' 共 3 个提交。
(使用 "git push" 来发布您的本地提交)
您有尚未合并的路径。
(解决冲突并运行 "git commit")
未合并的路径:
(使用 "git add <文件>..." 标记解决方案)
双方修改: helloworld.c
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
上例就是当两个分支修改同一个文件并且提交的话,出现的冲突,git已经提示冲突来源于双方提交。不仅如此,git还帮
助我们把helloworld文件中有冲突的部分标记了出来,将别的小伙伴提交的内容一并显示在helloworld.c中。
xuri@Ubuntu-XuRi:~/xuri_git$ cat helloworld.c
#include<stdio.h>
int
main()
{
<<<<<<< HEAD
cout<<"hello linux ........."<<endl;
cout<<"suannihen"<<endl;
=======
cout<<"hello world!"<<endl;
cout<<"I like linux device."<<endl;
>>>>>>> xuri
return 0;
}
可以看到,git已经标出了冲突的分支来源。那么我们只需要将自己和别人的提交合并更改。再次提交helloworld.c这个
冲突文件。我们的冲突就解决了,合并成功。而且,我们还可以查看分支合并的图形界面。
用*git log --pretty="%h %s"*查看分支合并过程:
xuri@Ubuntu-XuRi:~/xuri_git$ git log --pretty="%h %s" --graph
* 17d6bbb conflict fixed
|\
| * 1a370d6 I like linux device.
* | 0639ee6 ....
* | 92bcd1f hello linux ^_^
* | 3a9bad1 modify helloworld.c file.
|/
* 23fd13c add helloworld.c file
当我们想要知道有哪些分支已经合并到了当前分支,那么我们需要执行git branch --merged
xuri@Ubuntu-XuRi:~/xuri_git$ git branch --merged
dev
* master
xuri
这里能够看到dev和xuri分支都被合并到了master分支上。或者git branch --no-merged来查看哪些分支没有被合并到
当前分支:
xuri@Ubuntu-XuRi:~/xuri_git$ git branch --no-merged
xuri@Ubuntu-XuRi:~/xuri_git$
所以这说明我们的所有分支都合并到了master分支上。而当我们使用完了临时分支,并且合并了分支后。可以选择删除
它: xuri@Ubuntu-XuRi:~/xuri_git$ git branch -d xuri
已删除分支 xuri(曾为 1a370d6)。
这里能够看到xuri分支已被删除,而如果我们的分支没有被合并,但是仍然想删除的话,
那么需要执行git branch -D < branchname > 。
6.3 推送与获取
首先,如果你完成了某个项目的开发,决定上传某个分支到服务器的时候。可以使用push指令来上传指定的分支到指定
的远程仓库。
xuri@Ubuntu-XuRi:~/xuri_git$ git push origin master
Counting objects: 12, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 671 bytes | 0 bytes/s, done.
Total 6 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 1 local objects.
To git@github.com:CrazyCatJack/learngit.git
22575e5..6ac5bb4 master -> master
上例就是将master分支上传到远程服务器origin的master分支上。
**这个操作有一个前提是本地的master分支和远程仓库的master必须建立对应关系。**当然,也有能够避免这种可能发
生错误的方法。采用git push origin master:master就可以避免这种不必要的错误。即将本地的master分支更新远程仓
库origin的master分支。
相对的,如果自己想获取此刻远程仓库和自己本地仓库不同的部分,可以执行git fetch origin命令实现同步。该命令
执行后,本地上的origin/master指针被更新。因此也就能够访问目前远程仓库的状况。
xuri@Ubuntu-XuRi:~/xuri_git$ git fetch origin
xuri@Ubuntu-XuRi:~/xuri_git$ git merge origin/master
Merge made by the 'recursive' strategy.
lllll | 1 +
1 file changed, 1 insertion(+)
create mode 100644 lllll
上例是在同步之后,进行远程分支与本地分支操作的演示。
6.4 分支管理
6.4.1 长期分支
首先需要明确一个概念:并不是所有分支都要提交。也就是说,只提交重要的分支,比如master分支,dev分支。
master分支可以用来发布稳定的版本。dev分支可以用来当做下一版本的存储库。在dev分支上还可以另开启其他分支,
其他分支用来维护dev这个分支,当dev到一定程度,可以满足下一版本的各种要求时,就把dev分支归并到master分支
。在这里要提到一点的是:通常情况下,分支合并用的是快速合并方式。但在这种模式下,删除分支后会丢掉分支
信息。所以这里建议采用git merge --no-ff -m "commit information" < branch name > 这样合并就会在分支历史上
留下分支信息。
6.4.2 bug 分支
当我们正进行某项工作,但是突然必须解决其他事情。比如你正在写一个项目文件,突然接到通知,马上修改一个bug
那么现在就需要我们保存工作空间,等修复完bug再恢复它,完成我们的工作。
这里我们用git stash命令存储当前工作区,等忙完了优先级高的事情再回来回复它:
xuri@Ubuntu-XuRi:~/xuri_git$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
新文件: newproject
xuri@Ubuntu-XuRi:~/xuri_git$ git stash
Saved working directory and index state WIP on master: 6ac5bb4 delete
HEAD 现在位于 6ac5bb4 delete
可以看到这里我们将现有的工作区保存了。那么当我们忙完了其他事情想要回来接着这个工作区工作的话,
用git stash list来查看有几个存储的工作区备份,用*git stash apply stash@{num}*来释放存储的工作区:
xuri@Ubuntu-XuRi:~/xuri_git$ git stash list
stash@{0}: WIP on master: 6ac5bb4 delete
xuri@Ubuntu-XuRi:~/xuri_git$ git stash apply stash@{0}
位于分支 master
您的分支与上游分支 'origin/master' 一致。
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
新文件: newproject
而这种释放方法不会删除掉保存的工作区,我们可以用git stash drop删除掉。当然,如果我们用git stash pop恢复
工作区的话则能够直接把存储的工作区自动删除掉免去drop操作。
6.4.3 多人协作
Git最主要的功能之一就是能够有多个人一起完成一项工作。但是这种方式也可能会出现冲突,因此这里给出解决方案
首先,如果远程仓库上的某个文件被其他人更新,而你又恰好也在修改这个文件并进行推送。那么这时就会出现推送
失败。这时就需要用git pull命令来抓取你提交的分支与远程仓库上对应分支的文件冲突。
xuri@Ubuntu-XuRi:~/xuri_git$ git push origin master
To git@github.com:CrazyCatJack/learngit.git
! [rejected] master -> master (non-fast-forward)
error: 无法推送一些引用到 'git@github.com:CrazyCatJack/learngit.git'
提示:更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支。
提示:再次推送前,先与远程变更合并(如 'git pull ...')。详见
提示:'git push --help' 中的 'Note about fast-forwards' 小节。
上例显示了无法推送的错误,因此用git pull抓取不同之处
xuri@Ubuntu-XuRi:~/xuri_git$ git pull
Merge made by the 'recursive' strategy.
anotherfile.c | 1 +
newfile.c | 6 ++++++
2 files changed, 7 insertions(+)
create mode 100644 anotherfile.c
create mode 100644 newfile.c
用git pull抓取该分支的不同之处到本地仓库,进行合并后再提交就可以了:
xuri@Ubuntu-XuRi:~/xuri_git$ git commit -m "merge"
[master 8055c20] merge
1 file changed, 1 deletion(-)
delete mode 100644 another
xuri@Ubuntu-XuRi:~/xuri_git$ git push origin master
Counting objects: 28, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (21/21), done.
Writing objects: 100% (22/22), 2.31 KiB | 0 bytes/s, done.
Total 22 (delta 7), reused 0 (delta 0)
remote: Resolving deltas: 100% (7/7), completed with 1 local objects.
To git@github.com:CrazyCatJack/learngit.git
95b66d1..8055c20 master -> master
可以看到我们这次推送成功了。