核心: 直接记录快照 而非差异比较
Git 是一个免费、开源的分布式版本控制系统。版本控制(Vension Control)是一种在开发的过程中用于管理/记录相关文件、目录或工程等内容变化/修订情况,以便于团队/开发者查看变更记录、备份/恢复版本内容的软件工程技术。版本控制常用于管理多人协同开发项目,能够方便的帮我们实现跨区域多人协同并行开发、追踪项目生命周期及开发过程、控制项目代码的一致性、提高开发效率等,同时也是进行CI/CD的基础。常见的版本控制系统主要分为集中式版本控制系统(如SVN)和分布式版本控制系统(如Git),二者的主要区别如下:
Git在版本控制过程中,将整个流程范围划分为工作区 Workspace、暂存区 Index/Stage、版本库/本地库 Repository、远程库Remote 四个主要部分。它们之间的关系及作用说明如下:
git init
指令在当前项目目录下初始化git管理仓库(将当前项目目录纳入git管理),并产生 .git
隐藏文件目录(该隐藏目录中存放了git仓库的管理/配置信息及数据),简单来说.git
隐藏文件目录所在的项目目录即为工作区。原子性提交,即每一次提交都是由多个文件的修改组成的整体,而且这些修改要么全部成功,要么全部失败。
但原子性提交的特性使得对于某些文件的选择性提交变得麻烦。通过暂存区,可以使用多个相关暂存指令精确的选择和维护我们需要提交的某些修改,降低Commit的粒度,保证每一次Commit都是干净的,然后再一次性的(原子性的)将暂存区的组织内容提交到版本库,问题就完美的解决了。 工作目录下的每一个文件都不外乎这两种状态:已跟踪 或 未跟踪。已跟踪的文件是指那些被纳入了Git管理即版本控制的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是已提交,已修改(包括编辑、删除等)或者已暂存;简而言之,已跟踪的文件就是 Git 已经知道的文件。
未跟踪的文件是指工作目录中除已跟踪文件外的其它所有文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有被放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为 Git 刚刚检出了它们,而你尚未编辑过它们。
三者都是基于Web的Git远程代码托管仓库平台,为共享开源项目提供了平台,为开发团队提供了用于存储、共享、发布和联合开发项目的中心云存储位置。 从代码的私密性和安全性来看,自行搭建GitLab服务是更好的选择。 但是对于开源项目来说,GitHub/Gitee仍然是代码托管的首选。简单来说,Git是一个开发者的本地版本管理库工具,为了方便用户个人使用;而Github、Gitee、Gitlab都是远程版本管理库平台,为了多人协作/共享而建立。
(1)git init 初始化仓库
git init
命令用于在当前项目目录下初始化 Git 仓库来管理当前目录下的所有项目文件,生成 .git 隐藏文件目录(所有有关此项目的快照、底层文件信息等数据都存放在这里),将当前目录纳入Git管理并作为工作区 WorkSpace。当然,你可以有多个项目目录,对应的可以生成多个git仓库来分别管理,互不影响。
## 注意:尚未进行版本控制的项目目录,想要用 Git 来控制它,那么首先需要进入该项目目录中
git init #初始化git仓库
注意: 初始化git仓库后,项目工作目录下的当前所有文件都是未被追踪/未被管理的状态,需要手动添加暂存区。
(2)git config 配置信息
Git 的配置文件决定了 Git 在各个环节的具体工作方式和行为,Git提供了git config
系列指令用来配置或读取相应的工作环境变量,并根据其存放位置和读取顺序主要划分为三种配置级别,每一个级别会覆盖低一级别的相同变量配置生效。在Windows系统下的配置文件位置及作用域如下:
## 1.查看配置信息
git config --list #查看git相关的所有配置信息
git config --local --list #查看git的仓库级别配置信息
git config --global --list #查看git的用户级别配置信息
git config --system --list #查看git的系统级别配置信息
## 2.设置git配置信息 -- 配置用户签名信息(用于提交版本时标识用户,必须设置)
git config user.name "John Doe" #配置用户名(默认local级别配置)
git config user.email "[email protected]" #配置用户邮箱(默认local级别配置)
git config --global user.name "John Doe" #配置用户名(global级别配置)
git config --global user.email [email protected] #配置用户邮箱(global级别配置)
## 3.清除git配置信息
git config --local --unset user.name
git config --global --unset user.name
(1)git status 查看状态
git status
命令用于查看工作目录和暂存区所有项目文件的变更状态,但git status
不显示已经commit
到项目历史中去的信息。git status
的工作原理是比较暂存区索引文件和当前HEAD提交之间的差异,以及比较工作树目录和暂存区索引文件获得工作树中没有被Git跟踪的差异路径。第一个是通过运行git commit
来提交的; 第二个和第三个是你可以通过在运行git commit
之前运行git add
来提交的。比如工作区新增了文件则为untacked,工作区修改了文件则为modified,工作区删除了文件则为deleted等。
git status #展示当前文件状态,分为三部分
$ git status
On branch main
#1.已经更新同步到暂存区stage, 等待commit提交到本地库的文件
Changes to be committed:
(use "git restore --staged ..." to unstage)
modified: README.md
#2.被追踪有修改/删除, 但是没有被更新同步到暂存区stage的文件
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: test.txt
#3.还未被追踪的文件,即从没有add过的文件
Untracked files:
(use "git add ..." to include in what will be committed)
status.txt
git status -s
或 git status --short
可用于获得简短的状态输出结果
$ git status -s
M README.md
M test.txt
?? status.txt
(2)git diff 比较差异
git status
命令的输出比较简单,仅仅是列出了文件的变更状态。而git diff
命令可以对比变更文件之间的具体差异信息,包括具体修改了什么地方,git diff
能通过文件补丁的格式更加具体地显示哪些行发生了改变。
##1.git diff: 默认查看 workspace 工作区与 index 暂存区之间的差异,此命令比较的是工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。
$ git diff
diff --git a/README.md b/README.md #README.md文件的差异
index ebb2ebd..7a2e6a7 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-This is a README.md v2
\ No newline at end of file
+This is a README.md v3
+
+this is test v3
\ No newline at end of file
diff --git a/test.txt b/test.txt #test.txt文件的差异
index d6b04ea..4bc5315 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
-func53 complate 100% cvcv
\ No newline at end of file
+func53 complate 100% cvcv
+test version 2
\ No newline at end of file
##2.git diff --cached/--staged: 查看 index 暂存区与 local repositorty 本地库的差异,比对已当前暂存文件与最后一次提交版本之间的文件差异(已add还未commit)
$ git diff --cached
diff --git a/README.md b/README.md
index ebb2ebd..7a2e6a7 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-This is a README.md v2
\ No newline at end of file
+This is a README.md v3
+
+this is test v3
\ No newline at end of file
diff --git a/lisience.txt b/lisience.txt
new file mode 100644
index 0000000..1845bed
--- /dev/null
+++ b/lisience.txt
@@ -0,0 +1 @@
+许可证版本 v1.1
\ No newline at end of file
diff --git a/test.txt b/test.txt
index d6b04ea..4bc5315 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
-func53 complate 100% cvcv
\ No newline at end of file
+func53 complate 100% cvcv
+test version 2
\ No newline at end of file
##3.git diff [commitHash1] [commitHash2]: 比较不同提交版本之间的内容差异
$ git diff HEAD #比较当前工作区与本地库之间的差异
$ git diff HEAD^ #比较当前工作区与上次提交版本之间的差异,等价于HEAD~1
$ git diff HEAD~2 #比较当前工作区与第前两次提交版本之间的差异(HASH的缩写)
$ git diff HEAD^ HEAD~3 #比较上次提交版本与第上上次提交版本之间的差异
(3)git log 查看提交历史
git log
命令用于查看所有的版本提交历史记录,不传入任何参数的默认情况下,git log
会按时间先后顺序列出所有的提交,最近的更新排在最上面。 每个记录会列出每个版本提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明等信息。除此之外,git log
命令还可以配合强大的参数,筛选时间、作者、条数等信息。
$ git log #查看所有版本提交(commit对象)的详细历史记录
$ git log --oneline #查看所有版本提交(commit对象)的简单历史记录
(4)git reflog 查看操作历史
git reflog
命令用于查看所有分支的版本操作记录,包括commit、reset、revert等命令的操作。每一 次你提交或改变分支时,Git 都会默默地记录每一次你改变 HEAD 时它的值与操作情况,并更新引用日志reflog。reflog和log的区别在于,git log
不包含回退或删除/撤销的提交版本信息,而git reflog
则可以显示所有的操作记录,包括提交,回退/撤销等操作历史,一般用于版本回退/错误恢复。
$ git reflog
bb3fa89 (HEAD -> master) HEAD@{0}: commit: test.txt error commit #当前HEAD分支指向 HASH-1 = bb3fa89..
74cc4be HEAD@{1}: commit (merge): Merge branch 'func53'
a8f2ab1 HEAD@{2}: checkout: moving from func53 to master
0fb1536 (func53) HEAD@{3}: commit: func53 second commit 100%
4d9784c HEAD@{4}: checkout: moving from master to func53
a8f2ab1 HEAD@{5}: merge hotbug: Fast-forward
23125fc HEAD@{6}: checkout: moving from hotbug to master
a8f2ab1 HEAD@{7}: checkout: moving from master to hotbug
23125fc HEAD@{8}: checkout: moving from hotbug to master
a8f2ab1 HEAD@{9}: commit: hotdog first commit
23125fc HEAD@{10}: checkout: moving from master to hotbug
23125fc HEAD@{11}: checkout: moving from func53 to master
(1)git add 提交暂存区
git add
命令用于将当前工作目录下的目标文件快照同步添加到暂存区 index,以便为下一次提交commit准备暂存的内容。如果是新增的文件,则暂存区将同步新增文件暂存,并对文件进行追踪;如果是修改的文件,则暂存区将同步更新/覆盖文件暂存;如果是删除的文件,则暂存区将同步删除文件暂存;如果是没变动的文件,则暂存区将同步保持文件暂存,即暂存区的作用是保持与工作目录信息同步。
git add [file1] [file2] ... #指定暂存多个文件
git add [dir] #指定暂存某目录
git add . #指定暂存当前目录下的所有文件
git add
指令的执行原理是维护 index 暂存区文件的内容,对新增的文件则新增索引记录,修改的文件则覆盖索引记录,删除的文件则删除索引记录,没变动的文件则保持索引记录。其核心原理包括两个底层命令的组合执行:
(2)git commit 提交本地库
git commit
命令用于对当前的暂存区进行版本快照,将暂存区内容添加到本地仓库中,生成一次项目提交版本。
git commit -m [message] #生成当前所有暂存区快照,附加提交说明信息 message
git commit [fileName1] [fileName2] ... -m [message] #提交暂存区的指定文件到本地库
git commit -a -m [message] #提交已被跟踪的文件(modified、deleted)直接更新到本地库,自动执行暂存命令(帮你省去了add步骤),但是新添加的文件(untacked)不受影响。
##注意:使用git commit提交前需要先设置提交的用户信息,包括用户名和邮箱。
git commit
提交的多个历史提交版本之间依次形成一条版本链,由HEAD指针控制当前所处版本快照的位置。git commit
指令的核心原理包括几个底层命令的执行:
(3)git rm 删除文件
git rm
命令用于将指定文件从暂存区和当前工作目录下中删除,删除后文件状态为deleted并自动更新到暂存区,然后等待commit提交到本地库。该指令相当于以下几个指令的简化组合执行:
git rm <file> #将指定文件从暂存区和当前工作目录下中删除
git rm -f <file> #强制删除
git rm --cached <file> #把文件从暂存区域移除记录,但仍保留在当前工作目录中(Untracked状态)
git rm –r <dir> #递归删除目录
(4)git mv 移动或重命名
git mv
命令用于将指定文件移动或重命名,修改后文件状态为renamed并自动更新到暂存区,然后等待commit提交到本地库。该指令相当于以下几个指令的简化组合执行:
git mv [file] [newfile] #将文件file重命名为newfile
git mv [file] [new dir] #将文件移动到新目录下
(5)git 撤销修改
以下指令本质是从暂存区中拉取还原,重新覆盖工作区文件(拉取对应暂存区文件,并将其替换成工作区文件)。前提就是工作区文件修改了/新增了,但是还没提交到暂存区。简单的说 就是当我们把工作区弄乱了/后悔了,可以帮我们拉取上次最新的暂存区对应文件,恢复工作区。
##git checkout 恢复工作区文件
git checkout [filename] #撤回工作区对应的文件操作(包括删除也会复原)
git checkout -- [filename] #同上
git checkout [commitHash] -- [file_name] #将指定commit提交版本的内容同步还原到当前工作区和暂存区
##git restore 恢复工作区文件(最新)
git restore [filename] #撤回工作区对应的文件操作(包括删除也会复原)
注意:git checkout
这个命令相对比较底层,承担了太多的职责,它既被用来切换分支,又被用来恢复工作区文件,对用户造成了很大的认知负担。Git在2.23版本中,引入了两个新命令 git switch
和 git restore
,用以替代现在 git checkout
的分支切换和工作区恢复的职责。
##git reset HEAD 恢复暂存区文件
git reset HEAD [filename] #将暂存区对应文件的修改撤销,回退到上一个提交版本
##git restore 恢复暂存区文件(最新)
git restore --staged [filename] #将暂存区对应文件的修改撤销,回退到上一个提交版本
**注意:**暂存区撤回指令的本质是使用上次提交版本的内容覆盖当前暂存区,将暂存区中的暂存记录回退到上一次提交。但是这不会同步更改工作区的当前文件内容。所以回退后,工作区文件内容不变(git status
会提示modified状态),可以手动在工作区执行 git restore
重新覆盖恢复工作区文件。
该命令会修改上次提交对象的描述信息,重新提交当前暂存区快照并覆盖之前的提交记录。注意commitHash也改变了(本质就是生成一个新的commit对象来覆盖上次的提交对象),被覆盖的提交版本不会在log中出现,但在reflog可以找到。
git commit --amend #重新覆盖提交本次版本
(6)git reset 版本回退/版本穿梭
git reset
命令用于重置/移动HEAD版本指针,回退当前版本,并选择性的覆盖暂存区/工作区。需要注意的是git reset
命令执行之后,回退目标版本之后的所有版本记录都不会在log中出现,但在reflog中可以找到。
git reset [--soft | --mixed | --hard] [commitHash | HEAD] #重置/回退提交版本
git reset --soft [commitHash | HEAD] #软回退
注意: 当回退后继续修改文件再次提交时,会在V3版本基础之上再创建一个新的commit提交版本,并移动HEAD指针指向的分支来使其指向该commit提交,这样依次提交下去。如果我们使用git log
命令查看本地版本库的历史提交信息的时候,就不会出现V4版本提交的信息。会是V1
、V2
、V3
、V5
是一条分支线。但是V4版本是不会在Git中删除的,会永远的存储在Git的本地版本库中,我们可以使用git reflog
命令查看该V4版本的提交信息,关于这点,下面都同理不再赘述。
git reset --mixed [commitHash | HEAD] #中回退
git reset --hard [commitHash | HEAD] #硬回退
注意:–hard 参数是 reset 命令唯一的危险用法,它也是 Git 会 真正地销毁数据的仅有的几个操作之一。 其他任何形式的 reset 调用都可以轻松撤消,但是 --hard 选项不能,因为它强制覆盖了工作目录中的文件,任何未提交/未跟踪的修改都会被直接删除。
(1)分支的概念
几乎每一种版本控制系统都以某种形式支持分支,一个分支代表了一条独立的开发线。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。分支是Git的一个核心点,Git分支实际上是一个指向最新提交对象的活动指针HEAD,每当我们在当前分支下更新版本时,指针都会后移始终指向该分支链上的最新commit对象。从底层原理来看:
(2)分支的意义
Git默认的主分支为master开发线,也就是我们的主commit链。每一个分支都是从当前主分支中复制出去的一条独立开发线/链。分支的好处就是主分支可以一直保持稳定的开发版本路线(master主版本),对于一些功能/Bug的更新或者开发都可以在不影响主分支的情况下,由新的分支来独立开发,等开发完成稳定后再合并回主分支。并且Git最终是用于多人合作开发维护的,分支的存在使得每个人之间互不影响而独立开发,又能统一代码版本,便于后期合并。
(1)git 创建与查看分支
当新建分支时,会在refs目录下生成一个对应branch-name的分支索引文件,该文件的内容初始与当前分支的最新索引文件内容相同(即可视为拷贝副本),该索引文件保存并指向了当前分支下的最新提交版本。
git branch #列出所有本地分支(带*为主分支)
git branch [branch-name] #新建一个分支,但依然停留在当前分支
git branch -v #查看当前分支的最新提交信息。包含 commit对象哈希,提交提示信息message
(2)git 切换分支
当使用切换分支命令时,会将HEAD指针指向refs的当前分支索引文件,而当前分支索引文件又保存了该分支的最新提交快照对象,并使用该对象信息去更新当前工作目录(工作目录会随着分支切换而覆盖更新)。
##git checkout 切换分支
git checkout [branch-name] #切换到指定分支(最新commit),并用该分支的最后提交的快照替换你的当前工作目录的内容
git checkout -b [branchname] #创建新分支并立即切换到该分支下
##git switch 切换分支
git switch [branch-name] #切换分支
git switch -c [branchname] #创建新分支并立即切换到该分支下
注意: 在分支切换时,务必保证当前分支的工作树是干净的!如果当前分支上有 未暂存的修改/未提交的暂存,则切换分支时会产生以下问题:
(3)git 删除分支
使用删除分支命令之前必须切换出该分支(最好切换至主分支master)。分支删除操作的本质即:分支删除并不真正删除底层objects存储数据,只是删除refs下的分支索引文件,相当于清除了该分支从拷贝节点出来之后的所有分支开发记录。
git branch -D [branch-name] #不管是否合并了分支,强制删除指定分支。--delete --force 的简化版本
git branch -d [branchname] #删除指定分支,如果该分支有提交且未进行合并,则会删除失败。
(4)git 指定分支
git branch [name] [commitHash] #指定从commitHash处创建新分支
该指令用于新建一个名为name的分支,并使该分支指向对应的commitHash提交对象(从指定提交对象处引出新分支,该新分支的索引信息也是指向该对象的指针,拷贝副本),注意这里的新建分支也不切换。
(5)git 分支合并与冲突
git merge [branch_name] #使用当前分支合并目标分支
git add
命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决)。 新增的文件会同时合并新增进来,删除的文件会同步在当前分支删除掉,内容修改的文件会比对每一行,不同修改的部分保留,相同部分的修改会提示冲突,并在其中插入修改信息,需要手动修改。(6)分支实战
背景:目前正在参与一个公司项目Git-Demo,主分支为master稳定版本。当前你正在开发一个新功能(代号#53),开发分支为 func53。在该分支上开发到50%时,突然老板打电话说主版本上存在一个紧急Bug需要赶快修复,于是你临时暂停当前功能开发任务,新开一个分支hotbug,用于在当前稳定版上修复bug。修复后经测试没有问题,合并回主分支,并更新版本,然后删除该hotbug分支;然后你继续开发你的新功能工作,完成剩下的50%,然后经测试无误后合并回主分支,并发布新版本。
步骤一: 功能开发。在当前master分支,使用git branch -b func53 新建并切换到该分支,在该分支上开发新的业务代码(新建txt文件,输入 func53 complate 50%表示当前工作完成50%),然后add+commit暂停保存当前分支任务。
步骤二: bug修改。git checkout master切回主分支,使用git branch -b hotbug 新建并切换到该分支,在该分支上修复bug。修复完成后,然后add+commit提交该分支任务。
步骤三: 合并bug修复分支。切换回主分支master,然后使用git merge hotbug合并分支。因为此时hotbug再master的直接下游,此时的合并属于快进合并,不会产生冲突,master指针直接移动到hotbug。但此时func53分支存在两个问题:一是存在master原分支上的hotbug问题,二是相比此时的master处于过期状态。
步骤四: 删除无用的hotbug分支。git branch -d hotbug
步骤五: 切回分支func53,继续完成未完成的任务工作。假设将原txt文件修改为 “func53 complate 100%”,然后将原master的a.txt文件内容加上一句"a.txt for 53 v3"。以上两步表示工作完成。然后通过add+commit提交分支任务。
步骤六: 切回主分支master,合并func53分支,合并新功能。
此时合并由于修改了同一文件(a.txt)的同一行部分,在合并时会产生冲突,需要手动修改冲突部分并add+commit即可完成合并。
步骤七: 删除无用的func53分支。git branch -d func53
git clone
命令用于拷贝一个 Git 远程仓库到本地,该命令执行时无需登录验证。其中,Git 远程仓库地址支持 SSH 免密拉取和 HTTP 拉取。其相关克隆命令参数如下:
git clone [url] #克隆远程仓库到本地,指向其默认分支。默认情况下生成的本地项目目录与URL所指向的项目的名称一致,通常就是该URL最后一个/之后的项目名称
git clone [url] [project_name] #克隆远程仓库到本地,并重命名本地项目目录为project_name
git clone -b [指定分支名] [远程仓库url] #克隆远程仓库到本地,并指向指定分支(和git clone [url]效果一样,都是克隆全部分支和数据,只不过默认指向的分支不同)
git clone -b [指定分支名] --single-branch [远程仓库url] #只克隆远程仓库的指定分支数据到本地,而不提取其他分支。注意使用了--single-branch将无法git checkout 到其他远程分支,因为没有追踪其他远程分支(git branch -r/-a只有本地分支数据)
git checkout -b [branch_name] [remote_branch] #克隆后,本地切换远程分支(在本地新建分支branch_name并与远程仓库的分支remote_branch进行关联)
git checkout -t [remote_branch] #克隆后,本地切换远程分支(默认会在本地建立一个和远程分支名字一样的分支)
git clone
命令执行时,git默认会将远端所有的仓库代码和分支数据(包括所有log记录)拷贝到开发者本地。但是此时使用git branch
命令在本地只看到一个分支信息(一般默认为master),其他远程branch只能使用branch -r
或branch -a
命令来看到。原因是,git clone
命令执行后所有此时的仓库数据/记录都会被下载克隆到本地的objects中保存,但默认只在本地refs分支索引目录中生成一个master(默认)分支索引,所以我们只能看到一个本地分支。但此时在remote/origin repository区中也保存了一份远端所有分支的快照,而且该区域是只读的不能修改(其他分支都是隐藏的),因此我们可以使用branch -r
命令来查看远端分支。换句话说,远程分支(remote branch)是对远程仓库中的分支的索引。它们是一些无法移动的本地分支;只有在 Git 进行网络交互时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。
如果想要修改/操作其他分支,可以使用 git checkout -b [branch_name] [origin/master]
指令在本地新建分支并与远程仓库的分支进行关联。其本质为在本地新建一个refs分支索引,指向本地objects中的已保存的远端分支数据,并与远程分支建立关联。
git remote
命令用于操作远程仓库相关配置在本地的信息。
git remote -v #显示所有本地库关联的远程仓库信息(别名-url-operation)
git remote show [本地别名/远端url] #显示远程仓库详细信息
git remote add [localname] [url] #添加远程仓库(别名+url),配置信息在本地config文件中
git remote rm name #删除本地远程仓库信息
git remote rename old_name new_name #修改本地仓库别名
(1)Git Push基本使用
git push
命令用于从将本地的分支版本上传推送到远程仓库并合并。需要注意的是 push 指令推送时,必须要求用户登录且具有相关仓库的开发权限,用户的Git登陆方式包括HTTP账号校验和SSH免密登录两种:
ssh-keygen -t rsa -C "[email protected]" #基于你的邮箱,使用rsa加密算法生成SSH Key(两次默认回车,生成SSH Key 文件)
cat /.ssh/id_rsa.pub #在本地用户文件下找到SSH Key公钥,并将其中的key复制到远程库中的SSH配置中,添加SSH key
#后面再进行push/pull/clone时,我们可以使用SSH链接,就会自动免密操作
git push
命令的基本参数和使用方法说明如下(推送时本地项目目录名称和远程仓库名称可以不同,只推送更新内容):
git push <远程主机名> <本地分支名>:<远程分支名> #将本地的分支版本上传到远程分支并合并。
git push <远程主机名> <本地分支名> #如果本地分支名与远程分支名相同,则可以省略冒号
git push <远程主机名> <本地分支名> -f #强制使用本地覆盖远程仓库(若本地推送时出现了,如果本地版本与远程版本有差异,但又要强制推送)
(2)Git Push 冲突解决
git push
命令的本质是推送本地最新的commit对象版本到远程库延顺更新版本,这就要求本地库的基础版本与远程库目前版本一致。但在多人协作开发时,经常出现远程分支上存在本地分支中不存在的提交记录(其他人先一步推送了它的版本,导致本地版本与目前的远程版本有差异),此时就出现了PUSH冲突而导致报错。
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
解决方法包括以下三种情况:
git push -f <远程主机名> <本地分支名>
强制覆盖远程仓库 git pull
命令用于从远程获取代码并合并到本地的版本。需要注意的是 pull 指令拉取时,必须要求用户登录且具有相关仓库的开发权限同上。
git pull <远程主机名> <远程分支名>:<本地分支名> #将远程主机的远程分支拉取过来,与指定本地分支合并
git pull <远程主机名> <远程分支名> #将远程分支与当前本地分支合并
git pull #当前分支只有一个追踪分支,可以省略远程主机名和分支名
git pull 本质可以看作是 git fetch 和 git merge ** 指令的组合执行。git pull
命令首先会执行git fetch
命令,这一操作用于下载远程仓库的内容同步到本地。之后执行git merge
命令来合并(合并算法)远程内容(包括仓库数据+版本记录),合并的结果会在本地创建一个新的合并commit版本。**下面我们将来说一下如何处理pull冲突问题。
git fetch
命令用于从远程库中抓取代码库更新到本地,但不会强制合并分支数据。这个命令从远程库服务器中抓取本地没有的数据,并且更新本地数据库,移动远程跟踪分支 origin/branch
指针到更新之后的位置。
# 将某个远程主机的更新,全部取回本地(包括所有的commits和文件)
$ git fetch <远程主机名>
# 取回特定分支的更新(取回的更新,在本地主机上要用"远程主机名/分支名"的形式读取)
$ git fetch <远程主机名> <分支名>
注意: 远程跟踪分支只能读,不能写。如果需要在这个分支上进行开发,可以在这个分支上新建一个本地分支,然后进行开发。
远程分支(remote branch)是对远程仓库中的分支的索引。它们是一些无法移动的本地分支;只有在 Git 进行网络交互时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。
我们用 远程仓库名/分支名
这样的形式表示远程分支。比如我们想看看上次和 origin 仓库进行通讯时 master 分支的样子,就应该查看 origin/master
分支。如果你和同伴一起修复某个问题,但他们先推送了一个 iss53 分支到远程仓库,虽然你可能也有一个本地的 iss53 分支,但指向服务器上最新更新的却应该是 origin/iss53
分支。
可能有点乱,我们不妨举例说明。假设你们团队有个地址为 git.ourcompany.com 的 Git 服务器。如果你从这里克隆,Git 会自动为你将此远程仓库命名为 origin,并下载其中所有的数据,建立一个指向它的 master 分支的指针,在本地命名为 origin/master
,但你无法在本地更改其数据(远程跟踪分支指针,只读)。接着,Git 建立一个属于你自己的本地 master 分支,始于 origin 上 master 分支相同的位置,你可以就此开始工作。
如果你在本地 master 分支做了些改动,与此同时,其他人向 git.ourcompany.com 推送了他们的更新,那么服务器上的 master 分支就会向前推进,而于此同时,你在本地的提交历史正朝向不同方向发展。不过只要你不和服务器通讯,你的 origin/master
指针仍然保持原位不会移动。
如果要与给定的远程仓库同步数据,运行 git fetch
命令(在本例中为 git fetch origin
)。 这个命令查找 “origin” 是哪一个服务器(在本例中,它是 git.ourcompany.com
), 从中抓取本地没有的数据,并且更新本地数据库,移动 origin/master
指针到更新之后的位置。
要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。 换一句话说,这种情况下,不会有一个新的 serverfix
分支——只有一个不可以修改的 origin/serverfix
指针。可以运行 git merge origin/serverfix
将这些工作合并到当前所在的分支。 如果想要在自己的 serverfix
分支上工作,可以将其建立在远程跟踪分支之上(这会给你一个用于工作的本地分支,并且分支起点位于 origin/serverfix
):
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
注意: 可以通过 git branch -vv
来查看那些分支是跟踪分支。其中第一列是本地分支名,第二列是该分支对应的 SHA-1 值,第三列[ ]中蓝色的就是跟踪的远程分支,之后跟着的是最后一次提交信息。
假设你已经通过远程分支做完所有的工作了——也就是说你和你的协作者已经完成了一个特性, 并且将其合并到了远程仓库的 master
分支(或任何其他稳定代码分支)。 可以运行带有 --delete
选项的 git push
命令来删除一个远程分支。 如果想要从服务器上删除 serverfix
分支,运行下面的命令:
$ git push origin --delete serverfix #删除远程分支,该指令也会删除对应的远程追踪分支
To https://github.com/schacon/simplegit
- [deleted] serverfix
登录Gitee远程托管中心Web平台,由项目组管理人员新建远程库(一个远程库对应一个本地.git项目)。然后在仓库设置中,配置开发协作者信息与权限:
(1)项目组组长在远程仓库中上传项目相关的初始资源文件与代码(假设仅有一个master主分支),然后由多个项目开发协作人员从远端库中克隆代码到本地(Clone),此时每个开发人员的本地均有一个与远端同步的本地master分支;
git clone [url]
(2)开发人员A在该本地分支上进行相关业务功能开发,假设迭代开发了两个版本;此时另一位开发人员B也进行了相关的任务开发,并提前一步开发完成且推送/更新到了远程库。
(3)开发人员A此时将开发完成的代码推送至远程仓库完成更新/合并,但此时由于版本不一致导致Push会报错提示推送失败,开发人员A需要先进行Pull来拉取最新版本。
$ git push https://gitee.com/zju-wx/Git-resources.git master
To https://gitee.com/zju-wx/Git-resources.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'https://gitee.com/zju-wx/Git-resources.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
(4)开发人员A继续使用Pull命令拉取远程分支并合并远程仓库,来完成本地与远端的同步。在此期间需要解决Pull合并冲突。注意合并操作会基于最近公共祖先节点,对比文件的操作差异,来执行三路合并算法,合并规则包括: