Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件,可以敏捷高效地处理任何或小或大的项目。Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不需要服务器端软件支持。
一、安装配置
1. 安装
$ sudo apt install git
2. 配置用户和邮箱
初次使用git,必须配置一下用户和邮箱
git config --global user.name "ymz316"
git config --global user.email "ymz316@email.com"
3.其他常见配置
git config --global core.editor vim # 设置git默认编辑器,不设置的话默认是nano,可选vim,pluma等
git config --global merge.tool vimdiff # 设置git默认差异分析工具
Git 可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合并工具的输出信息。
4. 其他常见指令
git config --list # 查看配置信息
git config user.name # 直接查询某个配置信息(如:user.name)
git help config # 获取帮助(如想知道config命令怎么用)
二、git基础使用
1. 创建git仓库
1) 直接在工作目录中创建新仓库
$ mkdir test
$ cd test
$ git init
$ git config init.defaultbranch=master #创建默认分支master
这时候会发现当前目录下生成了一个.git
目录,该目录下存放了 git 需要的数据和资源,但目前仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。
如果当前目录下有些文件需要纳入版本控制,需要先用git add
命令告诉git开始对这些文件进行跟踪,然后用git commit
提交。
$ touch a.txt b.txt #创建2个文件
$ git status #查看文件状态,提示此时位于master分支,且a.txt和b.txt都为未跟踪,需用git add进行跟踪
$ git a.txt b.txt #跟踪a.txt和b.txt
$ git status #查看文件状态,a.txt和b.txt已跟踪,但需要git commit提交,或者git rm取消跟踪
$ git commit -m "第一次提交" #提交
$ git log #查看提交记录
commit 0014fb6e33a533134db88c45ff7e5a5891a69c5b (HEAD -> master)
Author: ymz316 <ymz316@126.com>
Date: Fri Apr 29 08:54:17 2022 +0800
第一次提交
2) 从现有仓库克隆
切换到需要创建git仓库的上级目录,执行:git clone [url]
如:
git clone https://git.suckless.org/dwm
会在当前目录创建同名的dwm目录及目录内的文件,同时该目录下包含.git目录。如果希望新建的目录不是dwm,可以指定新的项目名称:
git clone https://git.suckless.org/dwm mydwm
此时新建的目录就是mydwm而不是dwm了。
2. 记录每次更新到仓库
对项目中的某些文件进行阶段性的修改之后,可以提交本次更新到仓库。
工作目录下的所有文件只有2种状态:已跟踪(tracked)和未跟踪(untracked)。
- 已跟踪文件又有3种状态:未修改(un modified)、已修改未暂存(modified/not staged)和暂存(staged)。
- 暂存状态的文件又分为2种:新加入跟踪的文件(new file) 和 已跟踪并修改暂存(modified)
更新提交流程:
未跟踪 —git add—> 暂存 —git commit—> 未修改
已修改 —git add—> 暂存 —git commit—> 未修改
每次都add和commit略显繁琐,可以跳过暂存直接提交,只要将 git commit
加上 -a
选项,可以将已经跟踪过的文件(只能对已跟踪过的文件使用,对于未跟踪过的文件无法使用,也就是新建的文件该语句会提示错误)暂存起来一并提交,从而跳过 git add
步骤,如:
git commit -a -m "修改说明"
检查文件状态:
git status # 只显示未跟踪状态(红色)、已修改状态(红色)和暂存状态(绿色)的文件。
git diff # 可以查看未暂存的文件更新了哪些部分
$ echo abc > a.txt #在a.txt文件写入abc
$ echo abc > b.txt #在b.txt文件写入abc
$ git add a.txt #追踪a.txt,并没有追踪b.txt
$ git status #检查文件状态
位于分支 master
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: a.txt
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: b.txt
$ git diff #查看未暂存文件b.txt更新情况
diff --git a/b.txt b/b.txt
index e69de29..8baef1b 100644
--- a/b.txt
+++ b/b.txt
@@ -0,0 +1 @@
+abc
$ git commit -a -m "第二次提交"
3.忽略某些文件:
有些文件我们不希望它们纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。如日志文件,或者编译过程中创建的临时文件等。
可以创建一个名为 .gitignore
的文件并列出要忽略的文件模式。编辑完 .gitignore
文件后同样需要更新提交,因为该文件也是属于跟踪文件。
文件
.gitignore
的格式规范如下:
- #开头的行为注释行。
- 可以使用标准的 glob 模式匹配。
- 以反斜杠(/)结束的表示要忽略的是目录。
- 以叹号(!)开头的表示要忽略指定模式以外的文件或目录。
说明:glob 模式是shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(a,b,c中的任意一个);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。
例子:
# 注释部分
# 忽略所有 .a 结尾的文件
*.a
# 但 lib.a 除外
!lib.a
# 仅仅忽略项目根目录下的 TODO 文件
/TODO
# 忽略 build/ 目录下的所有文件
build/
# 忽略 doc/*.txt
doc/*.txt
4.重命名文件
项目中的文件如果需要重命名,传统方法是:
$ mv a.txt c.txt
$ git rm a.txt
$ git add c.txt
$ git commit -m "第三次提交:重命名"
但git也提供了一条语句来实现上述3条重命名操作的效果:
$ git mv c.txt a.txt
$ git commit -m "第四次提交:重命名"
5.删除文件
项目中已经跟踪的文件删除后,需要用git rm
命令将其从跟踪清单中清除,并且使用git commit
提交,否则会提示该文件未暂存。
$ git rm b.txt
$ git commit -m "第五次提交:删除b.txt"
6.查看提交历史
git log --参数...
也可以gitk直接打开可视化图形查看界面(推荐)
7.修改上次提交
如果你提交之后发现还有些文件要再次修改,又不想增加一次修改记录,可以使用git commit --amend
这条命令。
三、关于git分支
首先明白 head
是什么。head
可以理解为当前指针位置,也可理解为指针指向当前分支,head
指向指向哪个分支,你的项目目录内只显示当前分支的文件内容。可通过git log --graph
(gitg 或 gitk 可以打开图形查看窗口)查看分支内容及 head
指向
$ git log --graph
* commit d4019afddd199af9cb56bc7fa7f5a7bab20efe8d (HEAD -> master)
| Author: ymz316 <ymz316@126.com>
| Date: Fri Apr 29 09:09:58 2022 +0800
|
| 第五次提交:删除b.txt
...省略输出...
这里head指向的当前分支为master
1.创建分支:$ git branch 分支名
$ git branch hollowman #创建分支hollowman
2.查看本地仓库分支:$ git branch
$ git branch # 带*的master,表示当前分支处于master
hollowman
* master
3.切换分支:$git checkout 分支名
切换分支,表示当前指针位置(head)指向了切换的分支,在此分支提交保存的数据不会影响其他分支的数据,此时若进行了修改提交,虽然数据变化了,但切换回原来的分支,数据仍将不变。
$ git checkout hollowman #切换到分支 'hollowman'
$ echo hhh >> a.txt #将123添加到文件1的末尾
$ git commit -a -m "分支修改" #提交保存
可以使用
git checkout -b 分支名
来创建并切换分支
4.查看两个分支的不同:$git diff 分支1 分支2
$ git diff master hollowman
diff --git a/a.txt b/a.txt
index 8baef1b..32fbbed 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1,2 @@
abc
+hhh
5.将指定分支的工作成果合并到当前分支:
语法:git merge 指定分支的名字
$ git checkout master #先切换到分支 'master'
$ git merge hollowman #将hollowman分支成果合并到当前分支master
更新 d4019af..c73606a
Fast-forward
a.txt | 1 +
1 file changed, 1 insertion(+)
合并分支后,当前分支和指定的分支内容相同,可以删除多余的分支(比如这里的hollowman)。
6.删除本地仓库指定分支:
$ git branch -d hollowman
git 鼓励使用者利用分支完成某个任务,然后再合并和删除分支,这和直接在master分支上工作效果虽然一样,但可以避免因操作失误而无法回到之前的可靠状态,过程更加安全。
7.分支合并冲突处理
上面的分支合并有个前提,也就是当前分支和合并的分支处于一条链中,这并不会产生冲突;但若两个分支都进行过commit操作,则不会处于一条链中,虽然在合并时会将2个分支中的修改部分均体现在新的文件中,但会提示自动合并失败,需要手动修改,重新commit提交才能算合并成功。
1) 在两条分支中分别修改同一个文件,并提交
$ git checkout -b hollowman #创建并切换到分支hollowman
$ echo ddd >> a.txt #将ddd追加到a.txt末尾
$ git commit -a -m "分支修改" #提交分支修改
$ git checkout master #切换到分支master
切换到分支 'master'
$ echo fff >> a.txt #将fff追加到a.txt末尾
$ git commit -a -m "分支修改" #提交分支修改
$ gitg #查看分支记录,可以发现master和还hollowman两个分支已经不处于同一条链路
2)将2个分支合并
$ git merge hollowman #合并分区,发现有冲突,需手动修复。
自动合并 a.txt
冲突(内容):合并冲突于 a.txt
自动合并失败,修正冲突然后提交修正的结果。
3)修复有冲突的文件
先看看自动合并且产生冲突的a.txt文件的内容信息:
$ cat a.txt
abc
hhh
<<<<<<< HEAD
fff
=======
ddd
>>>>>>> hollowman
会发现将master分支和hollowman分支所做的修改都写进了a.txt,其中<<<<<<< HEAD
表示当前分支master修改的内容,>>>>>>> hollowman
表示hollowman分支修改的内容,而=======
这是一条分界线。删掉这些标记,保存并提交
$ vim a.txt
abc
hhh
fff
ddd
$ git commit -a -m "合并"
此时分支合并成果,且master分支和hollowman分支处于1个节点上,可通过gitg查看。
4) 删除不需要的分支
$ git branch -d hollowman
已删除分支 hollowman(曾为 3cebb3b)。
四、git远程仓库实践
几个名词:远程(remote)、远程仓库(remote repository)、本地仓库(local repository)、分支(branch)、默认的远程分支别名(origin)、初始的本地分支名称(master)
1. 配置公钥
配置号公钥信息,才能在本地机器与远程仓库之间构成安全通道。首先在本地机器生成公钥信息
$ ssh-keygen -t rsa -C "ymz316@126.com" #在本地机器生成公钥信息
Generating public/private rsa key pair.
Enter file in which to save the key (/home/hollowman/.ssh/id_rsa): #回车即可
Enter passphrase (empty for no passphrase): #回车即可
Enter same passphrase again: #回车即可
Your identification has been saved in /home/hollowman/.ssh/id_rsa
Your public key has been saved in /home/hollowman/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:vxD+pX9wf0PQyJhi1TaFpzd72KEe1bVq/CLdmgRRtYY ymz316@126.com
The key's randomart image is:
+---[RSA 3072]----+
| ..+o |
| ..=..o|
| ..=E*o+|
| o o.=.B.|
| S ... *o+|
| . o o*ooo|
| o . =+=..|
| o =.+o+o|
| +.o+o o|
+----[SHA256]-----+
复制~/.ssh/id_rsa.pub文件内的数据,将其添加到gitee上。
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0Jng3qC1BsHhW7i0nqw0Mw03w0K9QOKrKHYOLHeRRaqCfuRBOxC
.........................................................................................
UPSqkoqXu+KM9K32bQsW18hGu0Yp2azzMKa9dubNt9nNIn0OfsXzoLdfnzWKczQVzIkRbQas= ymz316@126.com
2. 克隆远程仓库
$ git clone 远程仓库ssh地址 [本地仓库目录名] # 如果不输入‘本地仓库目录名’,则默认为远程仓库名gittest
下面语句将远程gittest仓库代码克隆到本地gittest文件夹
$ git clone https://gitee.com/ymz316/sig-documentation.git
正克隆到 'sig-documentation'...
remote: Enumerating objects: 123, done.
remote: Counting objects: 100% (123/123), done.
remote: Compressing objects: 100% (85/85), done.
remote: Total 123 (delta 34), reused 123 (delta 34), pack-reused 0
接收对象中: 100% (123/123), 4.10 MiB | 780.00 KiB/s, 完成.
处理 delta 中: 100% (34/34), 完成.
此时远程仓库数据被克隆到本地,并自动为远程仓库(remote repository)创建一个别名origin,同时,远程仓库默认分支名也会被克隆到本地裆裤,作为默认分支名(如master),指针指向远程仓库的默认分支(如master)。
可通过 git remote
或 git remote -v
查看远程仓库的简易或详细信息:
$ cd sig-documentation
$ git remote
origin
$ git remote -v
origin https://gitee.com/ymz316/sig-documentation.git (fetch)
origin https://gitee.com/ymz316/sig-documentation.git (push)
可以通过gitg查看本地仓库各分支详细信息。
3. 查看本地和远程仓库分支信息
$ git branch -r #查看远程仓库分支
origin/HEAD -> origin/master
origin/dev
origin/dev-moshengren
origin/master
$ git branch -a #查看本地仓库及远程仓库分支
* master
remotes/origin/HEAD -> origin/master
remotes/origin/dev
remotes/origin/dev-moshengren
remotes/origin/master
4. 创建分支,修改数据
$ git branch dev-ymz316
$ git checkout dev-ymz316
...修改数据...
$ git add .
$ git commit -a -m "提交数据说明"
5. 数据推送
本地仓库的分支信息修改后,可以将本地分支推送至远程仓库的指定远程分支名:git push 远程仓库名 本地分支名:远程分支名
,如果远程分支名不存在,则会新建一个远程分支名,如果省略远程分支名,则会默认为本地分支名。
推送数据到远程仓库
$ git push origin dev-ymz316:dev-ymz316
Username for 'https://gitee.com': ymz316
Password for 'https://ymz316@gitee.com':
枚举对象中: 6, 完成.
对象计数中: 100% (6/6), 完成.
使用 8 个线程进行压缩
压缩对象中: 100% (4/4), 完成.
写入对象中: 100% (4/4), 5.62 KiB | 5.63 MiB/s, 完成.
总共 4(差异 2),复用 0(差异 0),包复用 0
remote: Powered by GITEE.COM [GNK-6.3]
remote: Create a pull request for 'dev-ymz316' on Gitee by visiting:
remote: https://gitee.com/ymz316/sig-documentation/pull/new/ymz316:dev-ymz316...ymz316:master
To https://gitee.com/ymz316/sig-documentation.git
* [new branch] dev-ymz316 -> dev-ymz316
这里
git push origin dev-ymz316:dev-ymz316
也可以用缩写的方式 git push origin dev-ymz316` ,这两条语句作用相同。
6. 删除远程仓库指定分支,有2种方法:
$ git push origin :远程分支名
$ git push origin -d 远程分支名
7. 同步上游仓库
假如你的远程仓库是从上游仓库fork过来的,如果上游仓库进行了版本更新,是不会自动推送到你的远程仓库的,需要进行同步。
将上游仓库作为一个远程仓库添加到clone的本地git项目中,这里我的远程仓库 https://gitee.com/ymz316/sig-documentation.git
就是fork自 https://gitee.com/ukylin-os/sig-documentation.git
$ git remote add upstream https://gitee.com/ukylin-os/sig-documentation.git
查看一下远程仓库,可以看到新增上游仓库成功了
$ git remote -v
origin https://gitee.com/ymz316/sig-documentation.git (fetch)
origin https://gitee.com/ymz316/sig-documentation.git (push)
upstream https://gitee.com/ukylin-os/sig-documentation.git (fetch)
upstream https://gitee.com/ukylin-os/sig-documentation.git (push)
将上游仓库的版本信息fetch到本地
$ git fetch upstream
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 3), reused 0 (delta 0), pack-reused 0
展开对象中: 100% (7/7), 2.53 KiB | 78.00 KiB/s, 完成.
来自 https://gitee.com/ukylin-os/sig-documentation
* [新分支] dev -> upstream/dev
* [新分支] dev-moshengren -> upstream/dev-moshengren
* [新分支] master -> upstream/master
将上游仓库的dev分支合并到本地dev-ymz316(假如上游的dev分支进行了修改和提交)
$ git checkout dev-ymz316
$ git merge upstream/dev
此时dev-ymz316分支应与上游仓库dev分支保持一致,可以将本地dev-ymz316分支推送到自己的远程仓库dev分支
$ git push origin dev-ymz316:dev
至此,远程仓库的dev分支将与上游仓库的dev分支一致。
参考资料:
- https://gitee.com/progit/index.html
- https://www.runoob.com/git/git-tutorial.html
- https://www.cnblogs.com/matengfei123/p/8252128.html