远程仓库
本文以Gitee为例作为远程仓库
SSH设置
注册好gitee账号后,可以通过设置SSH来避免每次远程操作都输入账号密码。首先应该在本地生成sshkey,在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
ssh-keygen -t ed25519 -C "xxxxx@xxxxx.com"
注意:这里的
xxxxx@xxxxx.com
只是生成的 sshkey 的名称,并不约束或要求具体命名为某个邮箱。
现网的大部分教程均讲解的使用邮箱生成,其一开始的初衷仅仅是为了便于辨识所以使用了邮箱。
复制生成后的 ssh key,通过仓库主页 「个人」->「安全设置」->「ssh公钥」 ,添加生成的 public key 添加到仓库中。
添加后,在终端(Terminal)中输入
ssh -T git@gitee.com
首次使用需要确认并添加主机到本机SSH可信列表。若返回 Hi XXX! You've successfully authenticated, but Gitee.com does not provide shell access.
内容,则证明添加成功。
gitee用户邮箱
在gitee中要配置提交的邮箱,这个邮箱用于提交时账号的认证,如果提交时的本地邮箱和gitee设置的邮箱一致,则这次提交统计到该账号的一次提交。
如果本地的邮箱和gitee的不一致,那么在提交时,会以另一个用户的身份来统计。
也可以设置只有指定的邮箱次可以推送
一个电脑连多个远程仓库
一个SSH
如果同时需要配置多个仓库的连接,比如同时配置github和gitee。其实可以使用同一份公钥,将这个公钥同时配到github和gitee中去,然后如果需要指定的username和email时,可以在相应的目录中可以设置username和email。比如在最初的配置时,使用了
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
这个假设用于gitee,为默认值。那么如果想要在github中推代码,可以在目录中重新设置username和email,这次设置时不夹global,只对当前的目录生效。
git config --global user.name "Your GithubName"
git config --global user.email "githubEmail@example.com"
多个SSH
- 生成一个github用的SSH-Key
$ ssh-keygen -t rsa -C 'xxxxx@company.com' -f ~/.ssh/gitee_id_rsa
- 生成一个gitee用的SSH-Key
$ ssh-keygen -t rsa -C 'xxxxx@qq.com' -f ~/.ssh/github_id_rsa
- 在 ~/.ssh 目录下新建一个config文件,添加如下内容(其中Host和HostName填写git服务器的域名,IdentityFile指定私钥的路径)
# gitee
Host gitee.com
HostName gitee.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/gitee_id_rsa
# github
Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/github_id_rsa
4.用ssh命令分别测试
$ ssh -T git@gitee.com
$ ssh -T git@github.com
建立Gitee仓库
在gitee中可以建立自己的远程仓库
1、在新建仓库页面填写仓库信息。仓库相关概念说明如下:
- 仓库名称: 仓库的名称,用于仓库命名
- 归属:仓库归属账户,可以是个人账号/组织/企业中的一种,创建成功后该账户默认为仓库的拥有者(管理员)
- 路径:仓库的git访问路径,由
用户个性地址+仓库路径名称
组成。创建仓库后用户将通过该路径访问仓库。 - 仓库介绍:仓库的简单介绍
- 是否开源:设置仓库是否为公开仓库,公开仓库对所有人可见,私有仓库仅限仓库成员可见。
- 选择语言:仓库主要开发用的编程语言
- 添加
.gitignore
:系统默认提供的git忽略提交的文件模板,设置.gitignore
后将默认忽略指定目录/文件到仓库 - 添加开源许可证:如果仓库为公开仓库,可以添加设置仓库的开源协议,作为对当前项目仓库和衍生项目仓库许可约束,开源许可证决定了该开源项目是否对商业友好。
- Readme:项目仓库自述文档,通常包含有软件的描述或使用的注意事项。
- 使用
\**\*模板文件
初始化仓库:使用Issue
或Pull Request
文件模板初始化仓库
点击创建后就可以创建自己的远程gitee仓库。然后就可以将本地仓库的文件推至远程仓库。
方法一、先将仓库clone到本地,然后再push到gitee仓库
$ git clone https://gitee.com/用户个性地址/HelloGitee.git #将远程仓库克隆到本地
在克隆过程中,如果仓库是一个私有仓库,将会要求用户输入 Gitee 的账号和密码。按照提示输入即可。
当然,用户也可以通过配置本地的git配置信息,执行git config
命令预先配置好相关的用户信息,配置执行如下:
$ git config --global user.name "你的名字或昵称"
$ git config --global user.email "你的邮箱"
在 Gitee 平台中,强烈建议您在【设置-多邮箱管理】中的“提交邮箱”与上面配置信息中的邮箱地址保持一致,这样平台就能及时地统计您在平台中提交代码的贡献了。
修改代码后,在仓库目录下执行下面命令
$ git add . #将当前目录所有文件添加到git暂存区
$ git commit -m "my first commit" #提交并备注提交信息
$ git push origin master #将本地提交推送到远程仓库
方法二、本地初始化一个仓库,设置远程仓库地址后,再push
在本地的learngit
仓库下运行命令:
$ git remote add origin git@gitee.com:xxx/learngit.git
添加后,远程库的名字就是origin
,这是Git默认的叫法,也可以改成别的,但是origin
这个名字一看就知道是远程库。然后push
$ git push -u origin master
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在起,只要本地作了提交,就可以通过命令:
$ git push origin master
在新建仓库时,如果在 Gitee 平台仓库上已经存在 readme 或其他文件,在提交时可能会存在冲突,这时用户需要选择的是保留线上的文件或者舍弃线上的文件,如果您舍弃线上的文件,则在推送时选择强制推送,强制推送需要执行下面的命令(默认不推荐该行为):
$ git push origin master -f
如果您选择保留线上的 readme 文件,则需要先执行:
$ git pull origin master
SSH警告
你第一次使用Git的clone
或者push
命令连接Gitee时,会得到一个警告:
The authenticity of host 'gitee.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?
这是因为Git使用SSH连接,而SSH连接在第一次验证Gitee服务器的Key时,需要你确认Gitee的Key的指纹信息是否真的来自GitHub的服务器,输入yes
回车即可。
Git会输出一个警告,告诉你已经把Gitee的Key添加到本机的一个信任列表里了:
Warning: Permanently added 'gitee.com' (RSA) to the list of known hosts.
这个警告只会出现一次,后面的操作就不会有任何警告了。
删除远程库
如果添加的地址写错了,或者想删除远程库,可以用git remote rm <name>
命令,使用前,最好先使用git remote -v
查看远程库信息:
$ git remote -v
origin git@gitee.com:gitnpr/notes-and-knowledge.git (fetch)
origin git@gitee.com:gitnpr/notes-and-knowledge.git (push)
然后,根据名字删除,比如删除origin
:
$ git remote rm origin
此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到Gitee,在后台页面找到删除按钮再删除。
分支管理
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
创建与合并分支
在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master
分支。HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步。当创建分支的时候,比如创建一个dev分支,这时git新建了一个dev指针,指向的是master的提交,再把HEAD指向dev,就表示当前分支在dev上:<img src="git使用.assets/image-
之后对工作区的修改和提交就是针对分支dev了,比如一次新的提交后,dev指针往前移动了一步,而master指针不变。
如果在dev上的工作完成了,需要进行合并,把dev合并到master上,git直接把master指向当前dev的提交就完成了合并:
合并完分之后,也可以删除dev分支,删除dev分支就是把dev的指针给删掉,删掉后,就只剩下master一条分支:
实际操作:
$ git checkout -b dev
Switched to a new branch 'dev'
git checkout
命令加上-b
参数表示创建并切换,相当于以下两条命令:
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
然后,用git branch
命令查看当前分支,该命令会展示出所有分支,
$ git branch
* dev
master
然后在test.txt中新加一行create a new branch
,然后提交。再切换回主分支,查看该文件中并没有新加的一行,这是因为刚才的改动知识在dev分支上改的,而master分支的提交点并没有变化。
然后可以将dev分支合并到master上,这时再查看文件就有了新增的那一行。
$ git merge dev
Updating 8fb43d3..f78d888
Fast-forward
test.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
注意到上面的Fast-forward
信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。
当然,也不是每次合并都能Fast-forward
。合并完成后,如果不再需要dev分支,就可以删除dev分支了
$ git branch -d dev
Deleted branch dev (was f78d888).
再查看现有的分支,就只有master了
$ git branch
* master
分支命令总结
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
或者git switch <name>
创建+切换分支:git checkout -b <name>
或者git switch -c <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
冲突
当两个分支同时对一个文件进行修改时,就可能会产生冲突。
新建一个分支dev1,然后将文件test.txt加上一行branch dev1 add
,然后commit。
$ git checkout -b dev1
Switched to a new branch 'dev1'
然后切回到master分支,这时文件test.txt中并没有dev中新加的最后一行,在文件最后一行中加入master add
,然后commit。
现在master和dev1分支中都有新的提交
这种情况下对分支进行合并就无法快速合并,产生了冲突
$ git merge dev1
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.
git告诉我们,test.txt文件存在冲突,必须手动解决冲突后再提交,通过git status
也可以告诉我们冲突的文件:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
这时打开test.txt查看其内容
git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,我们修改后保存
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git tracks changes of files.
create a new branch
master add
branch dev1 add
然后再进行提交就可以了,现在分支的情况就变为了
通过git log --graph
命令可以查看到分支合并图
分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
$ git merge --no-ff -m "merge with no-ff" dev
合并dev
分支,--no-ff
参数,表示禁用Fast forward
。因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去。
不使用Fast forward
模式时,merge后为:
–no–ff用途
Git 合并两个分支时,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,叫做“快进”(fast-forward),比如下图:
A---B---C feature
/
D---E---F master
要把feature合并到master中,执行以下命令
$ git checkout master
$ git merge feature
结果变为
A---B---C feature
/ master
D---E---F
因为 feature 就在 master 的下游,所以直接移动了 master 的指针,master 和 feature 都指向了 C。而如果执行了 git merge --no-ff feature
的话,是下面的结果:
A---B---C feature
/ \
D---E---F-----------G master
由于 --no-ff
禁止了快进,所以会生成一个新的提交,master 指向 G。
从合并后的代码来看,结果其实是一样的,区别就在于 --no-ff
会让 Git 生成一个新的提交对象.快进式合并会把 feature 的提交历史混入到 master 中,搅乱 master 的提交历史。所以如果你根本不在意提交历史,那么 --no-ff
其实没什么用。不过,如果某一次 master 出现了问题,你需要回退到上个版本的时候,比如上例,你就会发现退一个版本到了 B,而不是想要的 F,因为 feature 的历史合并进了 master 里。
分支策略
开发时,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
stash
git提供了git stash来进行暂存功能,主要用于以下情形:
- 发现有一个类是多余的,想删掉它又担心以后需要查看它的代码,想保存它但又不想增加一个脏的提交。这时就可以考虑
git stash
。 - 使用git的时候,我们往往使用分支(branch)解决任务切换问题,例如,我们往往会建一个自己的分支去修改和调试代码, 如果别人或者自己发现原有的分支上有个不得不修改的bug,我们往往会把完成一半的代码
commit
提交到本地仓库,然后切换分支去修改bug,改好之后再切换回来。这样的话往往log上会有大量不必要的记录。其实如果我们不想提交完成一半或者不完善的代码,但是却不得不去修改一个紧急Bug,那么使用git stash
就可以将你当前未提交到本地(和服务器)的代码推入到Git的栈中,这时候你的工作区间和上一次提交的内容是完全一样的,所以你可以放心的修Bug,等到修完Bug,提交到服务器上后,再使用git stash apply
将以前一半的工作应用回来。 - 经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。解决这个问题的办法就是
git stash
命令。储藏(stash)可以获取你工作目录的中间状态——也就是你修改过的被追踪的文件和暂存的变更——并将它保存到一个未完结变更的堆栈中,随时可以重新应用。
stash当前修改
git stash
会把所有未提交的修改(包括暂存的和非暂存的)都保存起来,可以用于后续恢复。
在文件test.txt
中加一行stash test
,此时查看状态为:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
这时使用git stash
将修改暂存下来,然后在查看状态
$ git stash
Saved working directory and index state WIP on master: ee11e7a conflict
$ git status
On branch master
nothing to commit, working tree clean
注意stash是本地的,不会通过git push
命令上传到git server上。实际应用中推荐给每个stash加一个message,用于记录版本,使用git stash save
取代git stash
命令。示例如下:
$ git stash save "test-cmd-stash"
进行stash后,改动全部被暂存下来。并且文件变为修改之前的状态。
查看现有stash
通过git stash list
可以查看已有的stash
$ git stash list
stash@{0}: WIP on master: ee11e7a conflict
恢复stash
一是用git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除,后面可以跟着stash名字
另一种方式是用git stash pop
,恢复的同时把stash内容也删了,这个指令将缓存堆栈中的第一个stash删除,并将对应修改应用到当前的工作目录下。
可以多次stash,恢复的时候,先用git stash list
查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
查看指定stash的diff
可以使用git stash show
命令,后面可以跟着stash名字。示例如下:
$ git stash show
test.txt | 1 +
1 file changed, 1 insertion(+)
$ git stash show stash@{0}
test.txt | 1 +
1 file changed, 1 insertion(+)
在该命令后面添加-p
或--patch
可以查看特定stash的全部diff,如下:
$ git stash show stash@{0} -p
diff --git a/test.txt b/test.txt
index 05286a6..a91a6a9 100644
--- a/test.txt
+++ b/test.txt
@@ -4,4 +4,5 @@ Git tracks changes of files.
create a new branch
master add
branch dev1 add
+stash test
从stash创建分支
如果你储藏了一些工作,暂时不去理会,然后继续在你储藏工作的分支上工作,你在重新应用工作时可能会碰到一些问题。如果尝试应用的变更是针对一个你那之后修改过的文件,你会碰到一个归并冲突并且必须去化解它。如果你想用更方便的方法来重新检验你储藏的变更,你可以运行 git stash branch,这会创建一个新的分支,检出你储藏工作时的所处的提交,重新应用你的工作,如果成功,将会丢弃储藏。
$ git stash branch testchanges
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
#
# modified: lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)
暂存未跟踪或忽略的文件
默认情况下,git stash
会缓存下列文件:
- 添加到暂存区的修改(staged changes)
- Git跟踪的但并未添加到暂存区的修改(unstaged changes)
但不会缓存一下文件:
- 在工作目录中新的文件(untracked files)
- 被忽略的文件(ignored files)
git stash
命令提供了参数用于缓存上面两种类型的文件。使用-u
或者--include-untracked
可以stash untracked文件。使用-a
或者--all
命令可以stash当前目录下的所有修改。
cherry-pick
git cherry-pick
命令的作用,就是将指定的提交(commit)应用于其他分支。
git cherry-pick <commitHash>
举例来说,代码仓库有master和feature两个分支。
a - b - c - d Master
\
e - f - g Feature
现在将提交f应用到master分支。
切换到 master 分支
$ git checkout master
Cherry pick 操作
$ git cherry-pick f
上面的操作完成以后,代码库就变成了下面的样子。
a - b - c - d - f Master
\
e - f - g Feature
从上面可以看到,master分支的末尾增加了一个提交f。
git cherry-pick命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。
$ git cherry-pick feature
上面代码表示将feature分支的最近一次提交,转移到当前分支。Cherry pick 支持一次转移多个提交。
$ git cherry-pick <HashA> <HashB>
上面的命令将 A 和 B 两个提交应用到当前分支。这会在当前分支生成两个对应的新提交。
如果想要转移一系列的连续提交,可以使用下面的简便语法。
// 不包含A,包含B
$ git cherry-pick A..B
上面的命令可以转移从 A 到 B 的所有提交。它们必须按照正确的顺序放置:提交 A 必须早于提交 B,否则命令将失败,但不会报错。
注意,使用上面的命令,提交 A 将不会包含在 Cherry pick 中,即git cherry-pick (commitidA…commitidB]。如果要包含提交 A,可以使用下面的语法。
// 如果想搞成[]区间,使用 git cherry-pick A^..B 相当于[A B]包含A
$ git cherry-pick A^..B
多人协作
工作模式
多人协作的工作模式通常是这样:
- 首先,可以试图用
git push origin <branch-name>
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并; - 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用
git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
常用命令
- 查看远程库信息,使用
git remote -v
; - 本地新建的分支如果不推送到远程,对其他人就是不可见的;
- 从本地推送分支,使用
git push origin branch-name
,如果推送失败,先用git pull
抓取远程的新提交; - 在本地创建和远程分支对应的分支,使用
git checkout -b branch-name origin/branch-name
,本地和远程分支的名称最好一致; - 建立本地分支和远程分支的关联,使用
git branch --set-upstream branch-name origin/branch-name
; - 从远程抓取分支,使用
git pull
,如果有冲突,要先处理冲突。
关于 git push和git origin的默认行为:https://segmentfault.com/a/1190000002783245
参考:
-
liaoxuefeng.com
-
https://www.liaoxuefeng.com/wiki/896043488029600
-
https://gitee.com/help/articles/4120
-
https://www.cnblogs.com/tocy/p/git-stash-reference.html
-
https://blog.csdn.net/muzidigbig/article/details/122321393