Git
本文最后更新于 2024-01-09,文章内容可能已经过时。
Git
一个学习git的优秀网站:https://learngitbranching.js.org/?locale=zh_CN
1.介绍:
git是一个版本控制软件,是可以在你电脑不联网的情况下,只在本地使用的一个版本管理工具,其作用就是可以让你更好的管理你的程序,比如你原来提交过的内容,以后虽然修改了,但是通过git这个工具,可以把你原来提交的内容重现出来,这样对于你后来才意识到的一些错误的更改,可以进行还原。
1.1 版本控制软件类型:
1.1.1 本地版本:
单机运行,使维护文件版本的操作工具化.
1.1.2 集中式:
集中化的版本控制系统:联网运行,支持多人协作开发;性能差、用户体验不好.
主工程师搭好项目框架
在公司服务器创建一个远程仓库,并提交代码
其他人拉取代码,并行开发
每个人独立负责一个功能,开发完成提交代码
其他人随时拉取代码,保持同步
1.1.3 分布式:
联网运行,支持多人协作开发;性能优秀、用户体验好.
分布式与中央式的区别主要在于,分布式除了远程仓库之外团队中每一个成员的机器上都有一份本地仓库,每个人在自己的机器上就可以进行提交代码,查看版本,切换分支等操作而不需要完全依赖网络环境。
这里的分布式是指本机和服务器互相备份,与一般传统意义上的分布式有所区别.
主工程师搭好项目框架 ,并提交代码到本地仓库
在公司服务器创建一个远程仓库,并将1的提交推送到远程仓库
其他人把远程仓库所有内容克隆到本地,拥有了各自的本地仓库,开始并行开发
每个人独立负责一个功能,可以把每一个小改动提交到本地(由于本地提交无需立即上传到远程仓库,所以每一步提交不必是一个完整功能,而可以是功能中的一个步骤或块)
功能开发完毕,将和这个功能相关的所有提交从本地推送到远程仓库
每次当有人把新的提交推送到远程仓库的时候,其他人就可以选择把这些提交同步到自己的机器上,并把它们和自己的本地代码合并
1.2 Git特性:
1.2.1 保存方式:
svn是保存一份原始的本地文件和改动.
而git是保存每一次的完整的文件快照.
就像使用了数据库索引一样,增加了读性能而略微牺牲了写性能.
1.3 Git与Github:
GitHub 与 Git 是完全不同的两个东西。 在 Git 中,开发者将源代码存入名叫“Git 仓库”的资料库中并加以使用。而 GitHub 则是在网络上提供 Git 仓库的一项服务。
Git 仓库管理功能是 GitHub 的核心。因此,使用 GitHub 之前必须先掌握 Git 的相关知识,同时本地的设备还要安装 Git 的环境。
GitHub 上公开的软件源代码全都由 Git 进行管理且只支持用 Git 进行管理。 先有 Git,再有 Github,是 Github 傍 Git 大腿发展起来的,而不是 Git 傍 GitHub 大腿。
Gitlab也是同样的道理.
2.使用:
要正确的使用git,首先要了解git的相关名词.
2.1 分区:
git一共有四个分区,分别是:
Workspace:工作区
Index / Stage:暂存区
Repository:仓库区(或本地仓库)
Remote:远程仓库
2.1.1 工作区:
程序员进行开发改动的地方,简单来说,就是你平常开发代码的地方.
比如我的项目a放在x这个文件加里面,那么x这个文件夹就是工作区.
在工作区文件夹下,通过git status命令可以查看工作区的状态
任何对象都是在工作区中诞生和被修改.
2.1.2 版本库:
版本库又名仓库,英文名repository,你可以简单的理解一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改,删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻还可以将文件”还原”。
版本库一般保存在工作区的.git文件夹下(此文件一般被隐藏,被隐藏的文件就会看到他).
2.1.2 暂存区:
英文叫stage, 或index.一般保存在版本库中.
用来记录提交文件的地方,所有在暂存区的文件都会被git进行管理,也就是说git会记录被保存在暂存区的文件的改动.
为什么会有暂存区,可能在你的工作区目录下,有一些东西是不需要被git管理的(比如一些测试用的图片,测试插件等),这个时候就需要暂存区来进行区分.
如果查看工作区状态,如图:
Untracked表明这个文件(或者文件夹)对git来说是不存在的,git并没有管理这些文件.
而使用git add 文件名把文件添加进入暂存区之后就可以看到文件状态从"untracked"(未跟踪)变成了 "staged"(已暂存).
任何修改都是从进入index区才开始被版本控制;
2.1.4 本地仓库:
本地仓库就是git的分布式版本管理系统的体现.他保存了保存了对象被提交过的各个版本,比起工作区和暂存区的内容,它要更旧一些。
使用git commit 把暂存区文件提交到本地仓库.
只有把修改提交到本地仓库,该修改才能在仓库中留下痕迹;
2.1.5 远程仓库:
英文Remote Repository.
远程仓库是存储在网络上的代码仓库,它允许多个开发者协作编辑和共享代码。远程仓库通常托管在云端服务或者内部服务器上,开发者可以通过网络连接到远程仓库来进行代码的上传、下载和协作工作。
远程仓库通常被视为代码的中央存储库,团队成员可以将其代码推送到远程仓库,以便其他人可以获取更新。本地仓库是一个开发者的工作副本,而远程仓库是整个团队的协作平台。
远程仓库的内容可能被分布在多个地点的处于协作关系的本地仓库修改,因此它可能与本地仓库同步,也可能不同步,但是它的内容是最旧的。(相对于特定的本地开发这来说)
使用git push把本地仓库推送到远程仓库.
下面这张图展示了四个区域的关系.
2.2 HEAD与分支:
HEAD 是 Git 中一个独特的引用,它是唯一的。HEAD:当前commit的引用当前 commit 在哪里,HEAD 就在哪里,这是一个永远自动指向当前 commit 的引用,所以你永远可以用 HEAD 来操作当前 commit。
分支:英文branch.
Git中的分支是指对代码仓库中的不同开发路径或版本状态的引用。分支使开发者能够在不影响主要代码线的情况下,同时处理多个开发任务或功能。 无论是在本地仓库中还是在远程仓库中,都可以存在不同的分支,他们之间可以建立联系.(虽然一般情况下没太有必要,因为就算不建立联系也可以完成所有的功能)
每一个分支不是一个单独的仓库,而是一个仓库中的不同代码状态的表示。在Git中,分支是指向特定提交(commit)的指针,这些提交包含了项目的代码快照。每个分支都是在仓库中的相同代码基础上创建的,它们实际上共享同一个仓库。
一个Git仓库通常包含多个分支,每个分支代表了项目的不同开发路径或不同的功能、修复等。分支的创建可以基于现有分支(通常是主分支或其他分支)来进行,然后在分支上进行开发工作,最终将更改合并回主分支或其他目标分支。
分支的创建和合并使得开发者能够同时在不同的开发任务或功能上工作,而不会相互干扰。当分支完成其任务后,可以将其合并回主分支或其他合适的分支中。
2.2.1 分支合并:
分支合并是将不同的分支上的代码更改整合到一个分支中的过程。通常,开发者在不同的分支上进行工作,每个分支可能代表不同的开发任务、功能、修复等,当这些工作完成时,需要将这些更改合并回主分支或其他目标分支,以保持代码库的整洁和功能完整。]
需要注意的是,本地仓库与远程仓库之间的同步也是一种分支之间的合并,与远程仓库各个分支之间的合并逻辑相同。
2.2.2 合并冲突:
合并冲突是指在将一个分支的更改合并到另一个分支时,Git 无法自动合并这些更改的情况。这通常发生在以下情况下:
并行编辑:两个不同的分支上的开发者同时编辑了相同文件的相同部分。当你尝试合并这两个分支时,Git 无法确定应该保留哪个更改。
删除冲突:一个分支删除了文件,而另一个分支修改了该文件。Git 不知道应该删除文件还是保留修改。
重命名冲突:一个分支重命名了文件或目录,而另一个分支对同一文件或目录进行了其他更改。Git 不知道如何合并这两种操作。
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,其中<<<HEAD是指当前主分支(HEAD指向的分支)修改的内容,>>>>>dev 是指dev分支上修改的内容。
举个例子:
比如你昨天从master分支上拉取代码,发现里面文件a的某一行为:
int a = 1;
然后你今天开始工作,修改这句话为:
int a = 2;
然后你再本地执行了一系列操作,并成功将这个修改提交到远程仓库属于你自己的分支上,但是再尝试合并时出现了分支冲突,于是你查看master分支上的代码,发现它已经变成了:
int a = 3;
这样在你合并的时候,git无法知道要保存哪一个,所以就会报错。
而这样的原理是基于commit的提交,当你昨天master上拉下来时,远程有可能有四个commit,而你拉下来的历史提交记录,所以本地也应该有四个commit,而你在本地提交之后,本地就相当于有了五个commit,而远程仓库也是同样的有五个commit。而当你试图合并的时候,git会对比这两个commit,发现他俩的内容不一样,所以会报错。
而如果再合并的时候,你这里有五个commit,而远程仓库只有四个commit,那么它就会按照更多的commit进行修改,并同步commit记录。
2.3 基本工作流程:
2.3.1 开始:
首先我们需要一个版本库,获取版本库有两种方式:
2.3.1.1 git init:
通过命令 git init 把新建的文件目录,变成git可以管理的仓库
这时候你当前目录下会多了一个.git的目录,这个目录是Git来跟踪管理版本的
2.3.1.2 git clone:
使用git clone url 克隆远程的版本库到本地,适用于一般的开发情况.
2.3.2 创建本地分支:
在本地工作需要一个自己的分支
通过git branch 新的分支名 创建一个新的本地分支
2.3.3 开发代码:
进行所需的代码开发工作.
2.3.4 提交与合并:
git add --all提交到暂存区
git commit -m "日志信息"暂存区文件提交到本地仓库
git push origin(远程仓库url) 分支名 本地仓库的提交到远程仓库.
一般的规范是在远程仓库上也建立每个人的操作分支,然后每个人提交的时候提交到自己的分支上,然后在登录git的管理端,进行手动和代码合并(merge).
2.3.5 后续开发:
为了尽可能少的出现冲突问题,每次进行代码开发前,都需要git pull 一下master代码,(这样分支就会从此时开始。只有在你本地开发的这段时间内,有人向master上进行了合并,而你开发完成之后,正好跟他修改了同一个文件的同一个部分,才会发生冲突。通过这种规范把产生冲突的可能性降到了最低。)然后进行开发,完成后重复上面提交与合并的操作。
2.3.6 其他常用命令:
一下是一些git的常用命令,作为参考。
一、新建代码库
在当前目录新建一个Git代码库
$ git init
# 新建一个目录,将其初始化为Git代码库 $ git init [project-name] # 下载一个项目和它的整个代码历史 $ git clone [url]
二、配置
# 显示当前的Git配置 $ git config --list
# 编辑Git配置文件 $ git config -e [--global] # 设置提交代码时的用户信息 $ git config [--global] user.name "[name]" $ git config [--global] user.email "[email address]"
三、增加/删除文件
# 添加指定文件到暂存区 $ git add [file1] [file2] ... # 添加指定目录到暂存区,包括子目录 $ git add [dir] # 添加当前目录的所有文件到暂存区 $ git add . # 添加每个变化前,都会要求确认 # 对于同一个文件的多处变化,可以实现分次提交 $ git add -p
# 删除工作区文件,并且将这次删除放入暂存区 $ git rm [file1] [file2] ... # 停止追踪指定文件,但该文件会保留在工作区 $ git rm --cached [file] # 改名文件,并且将这个改名放入暂存区 $ git mv [file-original] [file-renamed]
四、代码提交
# 提交暂存区到仓库区 $ git commit -m [message] # 提交暂存区的指定文件到仓库区 $ git commit [file1] [file2] ... -m [message] # 提交工作区自上次commit之后的变化,直接到仓库区 $ git commit -a
# 提交时显示所有diff信息 $ git commit -v
# 使用一次新的commit,替代上一次提交 # 如果代码没有任何新变化,则用来改写上一次commit的提交信息 $ git commit --amend -m [message] # 重做上一次commit,并包括指定文件的新变化 $ git commit --amend [file1] [file2] ...
五、分支
# 列出所有本地分支 $ git branch
# 列出所有远程分支 $ git branch -r
# 列出所有本地分支和远程分支 $ git branch -a
# 新建一个分支,但依然停留在当前分支 $ git branch [branch-name] # 新建一个分支,并切换到该分支 $ git checkout -b [branch] # 新建一个分支,指向指定commit $ git branch [branch] [commit] # 新建一个分支,与指定的远程分支建立追踪关系 $ git branch --track [branch] [remote-branch] # 切换到指定分支,并更新工作区 $ git checkout [branch-name] # 切换到上一个分支 $ git checkout - # 建立追踪关系,在现有分支与指定的远程分支之间 $ git branch --set-upstream [branch] [remote-branch] # 合并指定分支到当前分支 $ git merge [branch] # 选择一个commit,合并进当前分支 $ git cherry-pick [commit] # 删除分支 $ git branch -d [branch-name] # 删除远程分支 $ git push origin --delete [branch-name] $ git branch -dr [remote/branch]
六、标签
# 列出所有tag $ git tag
# 新建一个tag在当前commit $ git tag [tag] # 新建一个tag在指定commit $ git tag [tag] [commit] # 删除本地tag $ git tag -d [tag] # 删除远程tag $ git push origin :refs/tags/[tagName] # 查看tag信息 $ git show [tag] # 提交指定tag $ git push [remote] [tag] # 提交所有tag $ git push [remote] --tags
# 新建一个分支,指向某个tag $ git checkout -b [branch] [tag]
七、查看信息
# 显示有变更的文件 $ git status
# 显示当前分支的版本历史 $ git log
# 显示commit历史,以及每次commit发生变更的文件 $ git log --stat
# 搜索提交历史,根据关键词 $ git log -S [keyword] # 显示某个commit之后的所有变动,每个commit占据一行 $ git log [tag] HEAD --pretty=format:%s
# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件 $ git log [tag] HEAD --grep feature
# 显示某个文件的版本历史,包括文件改名 $ git log --follow [file] $ git whatchanged [file] # 显示指定文件相关的每一次diff $ git log -p [file] # 显示过去5次提交 $ git log -5 --pretty --oneline
# 显示所有提交过的用户,按提交次数排序 $ git shortlog -sn
# 显示指定文件是什么人在什么时间修改过 $ git blame [file] # 显示暂存区和工作区的差异 $ git diff
# 显示暂存区和上一个commit的差异 $ git diff --cached [file] # 显示工作区与当前分支最新commit之间的差异 $ git diff HEAD
# 显示两次提交之间的差异 $ git diff [first-branch]...[second-branch] # 显示今天你写了多少行代码 $ git diff --shortstat "@{0 day ago}" # 显示某次提交的元数据和内容变化 $ git show [commit] # 显示某次提交发生变化的文件 $ git show --name-only [commit] # 显示某次提交时,某个文件的内容 $ git show [commit]:[filename] # 显示当前分支的最近几次提交 $ git reflog
八、远程同步
# 下载远程仓库的所有变动 $ git fetch [remote] # 显示所有远程仓库 $ git remote -v
# 显示某个远程仓库的信息 $ git remote show [remote] # 增加一个新的远程仓库,并命名 $ git remote add [shortname] [url] # 取回远程仓库的变化,并与本地分支合并 $ git pull [remote] [branch] # 上传本地指定分支到远程仓库 $ git push [remote] [branch] # 强行推送当前分支到远程仓库,即使有冲突 $ git push [remote] --force
# 推送所有分支到远程仓库 $ git push [remote] --all
九、撤销
# 恢复暂存区的指定文件到工作区 $ git checkout [file] # 恢复某个commit的指定文件到暂存区和工作区 $ git checkout [commit] [file] # 恢复暂存区的所有文件到工作区 $ git checkout . # 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变 $ git reset [file] # 重置暂存区与工作区,与上一次commit保持一致 $ git reset --hard
# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变 $ git reset [commit] # 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致 $ git reset --hard [commit] # 重置当前HEAD为指定commit,但保持暂存区和工作区不变 $ git reset --keep [commit] # 新建一个commit,用来撤销指定commit # 后者的所有变化都将被前者抵消,并且应用到当前分支 $ git revert [commit] # 暂时将未提交的变化移除,稍后再移入 $ git stash $ git stash pop
2.4 Feature Branching:新式工作流程:
核心: (1)任何新的功能(feature)或 bug 修复全都新建一个 branch 来写; (2)branch 写完后,合并到 master,然后删掉这个 branch(可使用git origin -d 分支名
删除远程仓库的分支)。
优势: (1)代码分享:写完之后可以在开发分支review之后再merge到master分支 (2)一人多任务:当正在开发接到更重要的新任务时,你只要稍微把目前未提交的代码简单收尾一下,然后做一个带有「未完成」标记的提交(例如,在提交信息里标上「TODO」),然后回到 master 去创建一个新的 branch 进行开发就好了。
3.在IDEA中使用:
3.1 基本命令:
当一个文件夹包含.git文件,也就是版本库之后,使用idea打开就可以在左上角看到git命令:
可以使用这些来进行更加简单的各种git操作,而不需要输入命令。
pull就是直接点击就行.
commit包含了add和commit的操作,可以把文件提交到本地仓库,
需要在下方输入提交日志。
push可以直接把本地仓库提交到远程仓库。
3.2 解决冲突:
出现冲突时们会给出具体文件的区别,自己选择就行。
选择需要保留的版本。