概念
基础概念
-
本地电脑
-
代码区:工作区间,放代码的地方
-
暂存区:git所管理的暂存区域
-
本地仓库:git所管理的本机的硬盘区域
-
-
远程电脑
-
远程仓库:github、gitee
-
-
代码提交管理的过程
-
代码区------->暂存区------->本地仓库-------->远程仓库
-
代码区------->暂存区------->本地仓库 :这个过程是不需要联网的。
-
本地仓库-------->远程仓库:这个过程是需要联网的
-
git
-
Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目
-
git仓库的任何一个拷贝都可以独立作为一个服务器来使用
-
git中文件状态
-
未跟踪(untrack):文件为新增加的
-
已修改(modified):修改了文件,但还没保存到git仓库中。
-
已暂存(staged):对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
-
已提交(committed):表示文件已保存在git仓库中
-
git存在分支:
git 文件四种状态
-
Untracked:未跟踪。此文件在文件夹中,但并没有加入到git仓库,不参与版本控制.通过git add 状态变为Staged.
-
Unmodify:文件已经入库,未修改。即版本库中的文件快照内容与文件夹完全一致.这种类型的文件有两种去处,如果它被修改,而变为Modified.如果使用git rm 移出版本库,则成为Untracked文件.
-
Modified:文件已修改,仅仅是修改,并没有进行其他的操作。这个文件也有两个去处,通过git add可进入暂存staged状态,使用git checkout,则丢弃修改过,返回unmodify状态,这个git checkout即从库中取出文件,覆盖当前修改!
-
Staged:暂存状态。执行git commit则将修改同步到库中,这时库中的文件和本地文件又变为一致,文件为Unmodify状态.执行git reset HEAD filename取消暂存,文件状态为Modified
repo
repo是Google开发的用于管理Android版本库的一个工具,repo是用Python对git进行了封装,简化了对多个Git版本库的管理。对于repo管理的任何一个版本库,都需要使用git命令进行操作。
repo需要关注当前git库的数量、名称、路径等,对这些git库进行操作。通过集中维护所有git库的清单,repo可以方便的从清单中获取git库的信息。 这份清单会随着版本演进升级而产生变化,同时也有一些本地的修改定制需求,所以,repo是通过一个git库来管理项目的清单文件的,这个git库名字叫manifests。
repo是一系列脚本的集合,这些脚本也是通过git库来维护的,这个git库名字叫repo。
在客户端使用repo初始化一个项目时,就会从远程把manifests和repo这两个git库拷贝到本地。 repo将自动化的管理信息都隐藏根目录的.repo子目录中。
<manifest>
<remote name="origin" url="https://github.com/your-repo.git" fetch="refs/heads/*:refs/remotes/origin/*" />
<default revision="master" remote="origin" sync-j="4" />
<project name="project1" path="path/to/project1" remote="origin" revision="main" />
<project name="project2" path="path/to/project2" remote="origin" revision="develop" />
</manifest>
-
<remote>:描述了远程仓库的基本信息。
-
name描述的是一个远程仓库的名称;
-
fetch用作项目名称的前缘,在构造项目仓库远程地址时使用到;
-
review描述的是用作code review的server地址
-
-
<default>:default标签的定义的属性,将作为<project>标签的默认属性,在<project>标签中,也可以重写这些属性。
-
属性revision表示当前的版本,也就分支;
-
属性remote描述的是默认使用的远程仓库名称,即<remote>标签中name的属性值;
-
属性sync-j表示在同步远程代码时,并发的任务数量,配置高的机器可以将这个值调大
-
-
<project>:每一个repo管理的git库,就是对应到一个<project>标签,
-
path描述的是项目相对于远程仓库URL的路径,同时将作为对应的git库在本地代码的路径;
-
name用于定义项目名称,命名方式采用的是整个项目URL的相对地址。
-
Repo 使用
命令 | 可选项 | 解释 |
repo init -u <URL> [<OPTIONS>] | -u | 指定manifests这个远程git库的URL,manifests库是整个项目的清单 |
-m | –manifest-name:指定所需要的manifests库中的清单文件 | |
-b | –manifest-branch:指定manifest.xml文件中的一个版本,“分支” | |
repo sync [PROJECT_LIST] | -j | 开启多线程同步操作,这会加快sync命令的执行速度 |
-c | –current-branch:只同步指定的远程分支 | |
-d | –detach:脱离当前的本地分支,切换到manifest.xml中设定的分支(当第一次sync完代码后,往往会切换到dev分支进行开发。如果不带该参数使用sync, 则会触发本地的dev分支与manifest设定的远程分支进行合并,这会很可能会导致sync失败) | |
-f | –force-broken:当有git库sync失败了,不中断整个同步操作,继续同步其他的git库 | |
–no-clone-bundle | 在向服务器发起请求时,为了做到尽快的响应速度,会用到内容分发网络(CDN, Content Delivery Network)。同步操作也会通过CDN与就近的服务器建立连接, 使用HTTP/HTTPS的$URL/clone.bundle来初始化本地的git库,clone.bundle实际上是远程git库的镜像,通过HTTP直接下载,这会更好的利用网络带宽,加快下载速度。 当服务器不能正常响应下载$URL/clone.bundle,但git又能正常工作时,可以通过该参数,配置不下载$URL/clone.bundle,而是直接通过git下载远程git库。 | |
repo start <BRANCH_NAME> [<PROJECT_LIST>] | 指定的PROJECT的上,切换到<BRANCH_NAME>指定的分支。可以使用–all参数对所有的PROJECT都执行分支切换操作 | |
repo status [<PROJECT_LIST>] | 用于查看多个git库的状态 | |
repo upload [PROJECT_LIST] | 将本地的代码上传到远程服务器 | |
repo download <TARGET> <CHANGE> | download是从Gerrit下载改动 | |
repo forall [PROJECT_LIST] -c <COMMAND> | -c | 对指定的git库执行-c参数制定的命令序列。被执行的命令不限于git命令,而是任何被系统支持的命令,比如:ls 、 pwd 、cp |
repo prune [<PROJECT_LIST>] | 删除指定PROJECT中,已经合并的分支。 |
git使用
命令 | 可选项 | 解释 |
git init | 在当前目录新建一个仓库 | |
[project-name] | 在一个目录下新建本地仓库 | |
git clone [url] | 克隆一个远程仓库 | |
git diff | ||
HEAD -- . | 查看最新本地版本库和工作区所有文件的区别 | |
HEAD -- [file-name] | 查看最新本地版本库和工作区文件的区别 | |
HEAD^ -- [file-name] | 查看本地上一个版本和工作区文件的却别 | |
[local branch] origin/[remote branch] | 比较本地分支和远程分支的区别 | |
git status | 查看所有文件状态 | |
[file-name] | 查看指定文件状态 | |
git add | . | 将工作区的被修改的文件和新增的文件提交到暂存区,不包括被删除的文件 |
-u . | 将工作区的被修改的文件和被删除的文件提交到暂存区,不包括新增的文件 | |
-A . | 将工作区被修改、被删除、新增的文件都提交到暂存区 | |
Git commit | -m [massage] | 将暂存区所有文件添加到本地仓库 |
[file-name-1] [file-name-2] -m [massage] | 将暂存区指定文件添加到本地仓库 | |
-am [massage] | 将工作区的内容直接加入本地仓库 | |
--amend | 快速将当前文件修改合并到最新的commit,不会产生新的commit | |
--amend -m | 修改最新一次commit的信息 | |
git clean | 从工作目录中删除所有没有 tracked,没有被管理过的文件 | |
-n | 显示将要被删除的文件 | |
-d | 删除未被添加到 git 路径中的文件(将 .gitignore 文件标记的文件全部删除) | |
-f | 强制运行 | |
-x | 删除没有被 track 的文件 | |
git log | 显示所有commit日志 | |
--pretty=oneline | 将日志缩写为单行显示 | |
--graph --pretty=oneline --abbrev-commit | 查看分支合并情况 | |
| 查看分叉历史,包括:提交历史、各个分支的指向以及项目的分支分叉情况 | |
-3 | 查看最新3条commit日志数据 | |
git reflog | 显示操作本地版本库的命令,包括commit和reset等,在回退版本以后又后悔找不到commit id了可以使用此命令查看历史 | |
git push | 将文件添加到远程仓库 | |
-f | 强制提交,当我们本地reset到旧的版本时,然后普通push会被拦截,因为此时本地HEAD指向比远程库还要旧 | |
origin [branch-name] | 推送当前本地分支到指定远程分支 | |
git checkout | -- [file-name] | 用暂存区的文件覆盖掉工作区的文件,如果暂存区没有可更新的就会用commit的文件更新工作区的文件 |
[branch] | 切换分支 | |
-b [new-branch-name] | 创建并切换分支 | |
git branch | 查看当前分支 | |
[branch-name] | 创建分支 | |
-a | 查看本地和远程的所有分支 | |
-r | 查看远程所有分支 | |
-d [branch-name] | 删除一个分支 | |
| 强制删除一个没有合并的分支 | |
--set-upstream-to=origin/[branch-name] [branch-name] | 把本地分支和远程分支进行连接 | |
Git merge | 合并本地 | |
[branch-name] | 用于合并指定分支到当前分支 | |
--quit | 退出当前分支合并,当合并后冲突很多,要撤回合并分支就可以用这个命令 | |
--no-ff -m [massage] [branch-name] | 不使用Fast forward合并分支,这样会创建新的commit,所以需要massage。这样被合并的分支HEAD指向是会变的。 | |
git switch | [branch-name] | 切换到已有分支 |
-c [branch-name] | 创建新分支并切换到该分支 | |
git stash | 隐藏当前工作的修改 | |
| 执行存储时,添加备注,方便查找 | |
list | 查看隐藏的工作信息列表 | |
drop | 删除隐藏的工作信息 | |
pop | 恢复隐藏的工作信息,同时删除隐藏的工作信息 | |
apply [stash@{0}] | 恢复指定的隐藏工作信息,但是不会删除隐藏的工作信息 | |
git remote | 查看本地添加了哪些远程分支地址 | |
-v | 查看本地添加了哪些远程分支地址更详细信息 | |
add origin 远程地址 | 关联远程仓库 | |
git fetch | 拉取远程分支最新的commit到本地仓库的 | |
git pull | 从远程仓库拉取代码到工作空间
| |
git tag | 查看所有标签 | |
[version] | 给当前最新的commit打上标签 | |
[version] [commit-id] | 给指定的commit-id打上标签 | |
-a [version] -m [massage] [commit-id] | 给指定的commit-id打上标签并附上说明文字 | |
-d [version] | 删除标签 |
Git reset
原理:
git reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本
适用场景:
如果想恢复到之前某个提交的版本,且那个版本之后提交的版本都不要了,就可以用这种方法
head说明:
-
HEAD 表示当前版本
-
HEAD^ 上一个版本
-
HEAD^^ 上上一个版本
-
HEAD^^^ 上上上一个版本
-
HEAD~0 表示当前版本
-
HEAD~1 上一个版本
-
HEAD^2 上上一个版本
-
HEAD^3 上上上一个版本
git reset --soft HEAD~n/commit id :
-
版本库(repository):HEAD 指向指定<commit>
-
暂存区(index):不重置到指定<commit>,这次<commit>之后的所有变更都撤销到此
-
工作区(working directory):无变化
git reset --mixed HEAD~n/commit id :
-
版本库:HEAD 指向指定<commit>
-
暂存区(index):重置到指定<commit>
-
工作区(working directory):这次<commit>之后的所有变更都撤销到此
git reset --hard HEAD~n/commit id :
-
版本库:HEAD 指向指定<commit>
-
暂存区(index):重置到指定<commit>
-
工作区(working directory):重置到指定<commit>
-
注意:工作区未add的修改和暂存区未commit的修改,全部丢弃。(如果想回到最新提交可用 git reflog 查看commit号 然后再利用本命令恢复)
Git revert
原理:
git revert是用于“反做”某一个版本,以达到撤销该版本的修改的目的。比如,commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西
适用场景:
如果想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法
git revert -n 8b89621019c9adc6fc4d242cd41daeb13aeb9861(不需要的版本号)
重新commit
Git cherry-pick
指定的提交(commit)应用于其他分支
git cherry-pick <commit_id>
配置项:
-
-e:打开外部编辑器,编辑提交信息。
-
-n:只更新工作区和暂存区,不产生新的提交。
-
-x:在提交信息的末尾追加一行(cherry picked from commit …),方便以后查到这个提交是如何产生的。
-
-s:在提交信息的末尾追加一行操作者的签名,表示是谁进行了这个操作。
-
-m parent-number:如果原始提交是一个合并节点,来自于两个分支的合并,那么 Cherry pick 默认将失败,因为它不知道应该采用哪个分支的代码变动。-m配置项告诉 Git,应该采用哪个分支的变动。它的参数parent-number是一个从1开始的整数,代表原始提交的父分支编号。
git cherry-pick时遇到冲突:
-
通过git add .将文件标记为已解决,然后可以使用git cherry-pick --continue命令,继续进行cherry-pick操作。
-
要中断这次cherry-pick,使用git cherry-pick --quit,这种情况下当前分支中未冲突的内容状态将为modified,
-
要取消这次cherry-pick,则使用git cherry-pick --abort,这种情况下当前分支恢复到cherry-pick前的状态,没有改变。
Git rebase
-
合并本地的多条提交(commit)记录
每一个的git commit都会形成一个git节点,而如果把这些节点都push到远端,就会使项目的git日志很乱。
git rebase -i HEAD~1
-
分支合并
切换到feature分支上,执行rebase命令,相当于是想要把master分支合并到feature分支
# 这两条命令等价于
git rebase master feature
git checkout feature git rebase master
当在feature分支上执行git rebase master时,git会从master和featuer的共同祖先B开始提取feature分支上的修改,也就是C和D两个提交,先提取到。然后将feature分支指向master分支的最新提交上,也就是M。最后把提取的C和D接到M后面,实际是会依次拿M和C、D内容分别比较,处理冲突后生成新的C’和D’。一定注意,这里新C’、D’和之前的C、D已经不一样了,是我们处理冲突后的新内容,feature指针自然最后也是指向D’
使用技巧
branch 切换
在切换分支前要用git status查看确保分支处于work tree clean状态,否则A->B分支,此时在B分支下会同步A分支的修改,通过git status查看也会存在,容易造成分支混乱,显然这和git的独立管理各分支理念是违背的。如果想切换,可以采用git stash将当前修改存起来,然后切换分支,切换回来之后可以采用git stash pop进行恢复。
分支代码同步
在做项目时,路人甲基于develop拉了一个分支A,,路人乙基于develop拉了一个分支B,乙先把自己的修改同步到了develop,甲在A分支进行开发,本地A修改完之后,想要推到远端库,最好在自己的分支下同步下已的修改,防止后续冲突大。建议采用git fetch + git merge,而不是采用git pull ,git pull省略了很多细节。Merge过程中若有冲突,需要解决冲突。
分支恢复
开发时,不小心删除了分支,即使是master分支,也能正确恢复,
git branch 分支名 哈希值
修改log
常遇到的问题是在提交某一个版本时忘了添加log,比如A->B,此时版本号已经到B了发现A版本修改时,忘了添加log,此时回退到上一个版本:git reset --hard HEAD^。