我是灼灼,一只初学Java的大一金渐层。
向往余秀华和狄兰·托马斯的疯狂,时常沉溺于将情感以诗相寄;追逐过王尔德、王小波的文字,后陷于毛姆和斯蒂芬·金不可自拔;热爱文学的浪潮,白日梦到底却总在现实里清醒;艳羡平静又极度渴盼奔跑的力量。
欢迎与我交流鸭· QQ:1517526827;
个人博客:https://blog.csdn.net/weixin_52777510?spm=1001.2101.3001.5343
Java相关笔记正在连载中,欢迎来其他内容逛逛哟~
相关内容如下:
【连载1】Java笔记——基本结构与流程控制
【连载2】Java笔记——数组操作
【连载3】Java笔记——面向对象编程
【连载4】Java笔记——Java核心类
【连载5】Java笔记——异常处理
【连载6】Java笔记——反射和注解
【连载7】Java笔记——泛型
【连载8】Java笔记——集合
【连载9】MySQL学习笔记
【连载10】JDBC学习笔记
【连载11】Git和GitHub的使用笔记
笔记内容来源于:廖雪峰官方网站Git教程
没有安装的同学移步这里~
所有的版本控制系统,只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外;
版本控制系统可以告诉每次的改动,而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统没法知道;
Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的,如果要真正使用版本控制系统,就要以纯文本方式编写文件;
因为文本是有编码的,比如中文有常用的GBK编码,日文有Shift_JIS编码,如果没有历史遗留问题,建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持;
使用Windows的Git要特别注意:
不要使用Windows自带的记事本编辑任何文本文件。
原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符,会导致遇到很多不可思议的问题;建议下载Notepad++代替记事本,不但功能强大,而且免费!把Notepad++的默认编码设置为UTF-8 without BOM即可;
该notepad++下载地址不可用,更新为:https://notepad-plus-plus.org/downloads/v7.9.2/
或者可以按照自己的喜好换成vscode;
默认编码修改:
下载好后找到settings—>preferences—>new Document----->选中UTF-8,apply那个不勾选;
在Git中,每当觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit
。一旦把文件改乱了,或者误删了文件,可以从最近的一个commit
恢复,然后继续工作;
详细图解:
版本库(仓库)里面包含了暂存区stage和Git自建分支master、以及指向master的指针HEAD:
把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add
把文件添加进去,实际上就是把要提交的所有修改添加到暂存区;
第二步是用git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支(Git自建分支master)。
可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改;
一旦提交后,如果没有对工作区做任何修改,那么工作区就是“干净”的:
$ git status
On branch master
nothing to commit, working tree clean
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上,最早,只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
一台电脑上也是可以克隆多个版本库的,只要不在同一个目录下。不过,现实生活中不会有人在一台电脑上搞几个远程库玩,因为一台电脑上搞几个远程库完全没有意义,而且硬盘挂了会导致所有库都挂掉;
实际情况往往是,找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。
完全可以自己搭建一台运行Git的服务器,不过现阶段,为了学Git先搭个服务器绝对是小题大作。好在这个世界上有个叫GitHub的神奇的网站,从名字就可以看出,这个网站就是提供Git仓库托管服务的,所以,只要注册一个GitHub账号,就可以免费获得Git远程仓库。
由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
第1步:创建SSH Key。在用户主目录下(这个用户主目录怎么查看???咳,解决了,主目录就是C盘下面的用户(user)目录,进去看看有没有.ssh文件~),看看有没有.ssh目录,如果有,再看看这个目录下有没有
id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash,这里注意不需要身份就可以直接输),创建SSH Key:$ ssh-keygen -t rsa -C "youremail地址"
一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码;
如果一切顺利的话,可以在用户主目录里找到
.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件(具体应该不叫这个名字,只需要注意区分pub文件和非pub文件就可以了,用记事本打开它~),这两个就是SSH Key的秘钥对,id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。
关于如何查看自己有没有ssh?
https://docs.github.com/en/github/authenticating-to-github/checking-for-existing-ssh-keys
关于如何创建ssh的GitHub官方教程链接?
https://docs.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
创建ssh后的重要信息:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The key fingerprint is:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The key’s randomart image is:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
翻译:
输入保存密钥的文件xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
创建目录xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
输入密码(为空,无密码):
再次输入相同的密码:
您的标识已保存在xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx中。
您的公钥已保存在xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx中。
关键指纹是:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
密钥的randomart图片为:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
标识就是私钥文件;pub文件是公钥;
粘贴的是公钥内容!
关于公钥的复制报错:
Key is invalid. You must supply a key in OpenSSH public key format
解决:
https://blog.csdn.net/weixin_36396470/article/details/103660549
好像可以直接复制这里。。。那就跳过这条吧!
申请ssh公钥成功回复邮件:
The following SSH key was added to your account:
title名
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
为什么GitHub需要SSH Key?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
最后友情提示,在GitHub上免费托管的Git仓库,任何人都可以看到喔(但只有你自己才能改);
如果你不想让别人看到Git库,有两个办法,一个是交点保护费,让GitHub把公开的仓库变成私有的,这样别人就看不见了(不可读更不可写)。另一个办法是自己动手,搭一个Git服务器,因为是你自己的Git服务器,所以别人也是看不见的(公司内部开发必备)。
下面的学习需要确保已经拥有了一个GitHub账号~
这个远程仓库的网址就是最上面这个:HTTPS SSH后面的东西;
不过不重要,可以直接把下面的这个复制过来到git bash上~
…or create a new repository on the command line
echo “# learngit” >> README.md
git init
git add README.md
git commit -m “first commit”
git branch -M main
git remote add origin xxxxxxxxxxxxxxxxxxx仓库网址
git push -u origin main
但是输入命令之前要先进入本地的仓库—>
初始化命令:
git config --global user.name “yourName”
//注意,–和global之间没有空格
git config --global user.email “[email protected]”
如果报错:
fatal: not in a git directory
可能是因为多加了空格;
初始化之后要新建仓库,直接初始化仓库可能会变成我这样-_-还进入了master分支?!
xxxxxxxxxxxxxxxxxxx
$ git init
Initialized empty Git repository in xxxxxxxxxxxxxxxxxxx
按照教程的创建版本库一节重新创建本地仓库learngit;
关掉我的gitbash再来一次!
记下我的命令:
$ git config --global user.name ‘xxxxxxxxxxxxxxxxxxx’
$ git config --global user.email ‘xxxxxxxxxxxxxxxxxxx’
创建仓库及初始化:
xxxxxxxxxxxxxxxxxxx ~ (master)
$ mkdir learngitxxxxxxxxxxxxxxxxxxx ~ (master)
$ cd learngitxxxxxxxxxxxxxxxxxxxx (master)
$ pwd
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ~/learngit (master)
$ git init
Initialized empty Git repository in xxxxxxxxxxxxxxxxxxxx
摆脱不了master…
注意:
C盘:xxxxxxxxxxxxxxxxxxxx是原先创建的空仓库,弄错了;
新的在该目录的learngit下面,把文件放到这个目录下面才行;
xxxxxxxxxxxxxxxxxxxx ~/learngit (master)
$ git add README.md
fatal: pathspec ‘README.md’ did not match any filesxxxxxxxxxxxxxxxxxxxx ~/learngit (master)
$ git add readme.txtxxxxxxxxxxxxxxxxxxxx ~/learngit (master)
$ git commit -m “wrote a readme file”
[master (root-commit) 463d378] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
xxxxxxxxxxxxxxxxxxxx ~/learngit (master)
$ git branch -M mainxxxxxxxxxxxxxxxxxxxx ~/learngit (main)
$ git remote add origin xxxxxxxxxxxxxxxxxxxx仓库网址
fatal: remote origin already exists.
注意目前自动创建的分支叫main,不是master,master不存在!
创建分支推送仓库:
错误解决:
xxxxxxxxxxxxxxxxxxxx ~/learngit (main)
$ git remote -v
origin xxxxxxxxxxxxxxxxxxxx仓库网址(fetch)
origin xxxxxxxxxxxxxxxxxxxx仓库网址 (push)
已经推送好了!
push:
xxxxxxxxxxxxxxxxxxxx ~/learngit (main)
$ git push -u origin main
Logon failed, use ctrl+c to cancel basic credential prompt.
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 271 bytes | 271.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To xxxxxxxxxxxxxxxxxxxx仓库网址
- [new branch] main -> main
Branch ‘main’ set up to track remote branch ‘main’ from ‘origin’.
翻译:
xxxxxxxxxxxxxxxxxxxx〜/ learngit(主要)
$ git push -u origin主
登录失败,请使用ctrl + c取消基本凭据提示。
枚举对象:3,完成。
计数对象:已完成100%(3/3)。
增量压缩最多使用8个线程
压缩对象:100%(2/2),已完成。
写入对象:100%(3/3),271个字节|
271.00 KiB / s,已完成。总计3(增量0),重用0(增量0)
到xxxxxxxxxxxxxxxxxxxx仓库网址
- [新分支] main-> main
分支“主”已设置为从“来源”跟踪远程分支“主”。
邮箱回复:
You recently used a password to access the repository at xxxxxxxxxxxxxxxxxxxx仓库名 with git using git/2.22.0.windows.1.
Basic authentication using a password to Git is deprecated and will soon no longer work. Visit https://github.blog/2020-12-15-token-authxxxxxxxxxxxxxxxxxxxxxxxx-git-operations/ for more information around suggested workarounds and removal dates.
不用密码用什么???看不太懂…
这样,只要本地作了提交,就可以通过命令:
$ git push origin master
把本地master
分支的最新修改推送至GitHub;
当第一次使用Git的clone
或者push
命令连接GitHub时,会得到一个警告:
The authenticity of host 'github.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连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes
回车即可。
Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.
这个警告只会出现一次,后面的操作就不会有任何警告了。
如果你实在担心有人冒充GitHub服务器,输入yes
前可以对照GitHub的RSA Key的指纹信息是否与SSH连接给出的一致。
不知道,没有遇到…
步骤:
首先,登陆GitHub,创建一个新的仓库,名字叫
gitskills
;勾选
Initialize this repository with a README
,这样GitHub会自动创建一个README.md
文件。创建完毕后,可以看到README.md
文件;远程库已经准备好了,下一步是用命令
git clone
克隆一个本地库:$ git clone [email protected]:xxxxxxxx/gitskills.git Cloning into 'gitskills'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3 Receiving objects: 100% (3/3), done.
把Git库的地址换成自己的,然后进入
gitskills
目录看看,已经有README.md
文件了:$ cd gitskills $ ls README.md
如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。
GitHub给出的地址不止一个,还可以用
https://github.com/michaelliao/gitskills.git
这样的地址。实际上,Git支持多种协议,默认的git://
使用ssh,但也可以使用https
等其他协议。使用
https
除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh
协议而只能用https
。要克隆一个仓库,首先必须知道仓库的地址,然后使用
git clone
命令克隆。Git支持多种协议,包括
https
,但ssh
协议速度最快。
分支在实际中的作用:
创建一个属于自己的分支,别人看不到,还继续在原来的分支上正常工作,而自己在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作;
其他版本控制系统如SVN等都有分支管理,但是这些版本控制系统创建和切换分支比蜗牛还慢,导致分支功能成了摆设;
但Git无论创建、切换和删除分支,在1秒钟之内就能完成,无论版本库是1个文件还是1万个文件。
在版本回退中,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。在Git里,这个分支叫主分支,即master
分支(现在是main分支)。HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步。随着你不断提交,master
分支的线也越来越长。
当创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化。
从现在开始,对工作区的修改和提交就是针对dev
分支了,新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如在dev
上的工作完成了,就可以把dev
合并到master
上。可以直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变。
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,就剩下了一条master
分支:
我们是看不出来有些提交是通过分支完成的~
创建dev
分支,然后切换到dev
分支,命令:
$ git checkout -b dev
git checkout
命令加上-b
参数表示创建并切换(不加-b就只是切换,所以-b是把创建分支的那一步表示掉了),相当于这两条命令:
$ git branch dev
$ git checkout dev
用git branch
命令查看当前分支:
$ git branch //该命令会列出所有分支
* dev //标*号的是当前分支
master
这样就可以在dev
分支上作出修改并像之前一样正常提交;
$ git add readme.txt
$ git commit -m "branch test"
等待dev
分支的工作完成后,就可以切换回master
分支:
$ git checkout master //切换某一分支的命令
切换回master
分支后,查看修改是看不到的,因为那个提交是在dev
分支上,而master
分支此刻的提交点并没有变:
但是可以把dev
分支的工作成果合并到master
分支上!
$ git merge dev //合并dev分支到当前分支(注意当前)命令
Fast-forward //Git说明这次合并是快进模式:直接把master指向dev的当前提交,所以合并速度非常快
合并后,会发现当前分支和dev
分支的最新提交是完全一样的。
*不是每次合并都能Fast-forward
,存在其他方式的合并。
合并完成后,就可以删除dev
分支了:
$ git branch -d dev //-d代表删除
删除后,查看branch
,就只剩下master
分支了:
$ git branch
* master
因为创建、合并和删除分支非常快,所以Git鼓励使用分支完成某个任务,合并后再删掉分支,这和直接在master
分支上工作效果是一样的,但过程更安全;
切换分支使用git checkout
,前面讲过的撤销修改则是git checkout --
,同一个命令,有两种作用;
但是实际上,切换分支这个动作,用switch
更科学:最新版本的Git提供了新的git switch
命令来切换分支;
创建并切换到新的dev
分支:
$ git switch -c dev
直接切换到已有的master
分支:
$ git switch master
使用新的git switch
命令,比git checkout
要更容易理解。
当master
分支和新创建的feature1
分支各自都分别有新的修改并提交,就会变成这样:
Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,执行合并命令报错:
$ git merge feature1
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result. //必须手动解决冲突后再提交
git status
也可以告知冲突的文件;
也可以直接查看被修改文件的内容:
其中Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容;
按要求修改再提交后:
用带参数的git log
可以看到分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
* cf810e4 (HEAD -> master) conflict fixed
|\
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file
最后,删除feature1
分支;
通常合并分支时,Git会用Fast forward
模式,但这种模式下删除分支后,会丢掉分支信息。
如果强制禁用Fast forward
模式,即--no-ff
方式的git merge
合并分支,Git就会在merge时生成一个新的commit,这样从分支历史上就可以看出分支信息。
合并dev
分支时,加上--no-ff
参数,表示禁用Fast forward
:
$ git merge --no-ff -m "merge with no-ff" dev
因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去;
合并后用git log
查看分支历史:
$ git log --graph --pretty=oneline --abbrev-commit
* e1e9c68 (HEAD -> master) merge with no-ff
|\
| * f52c633 (dev) add merge
|/
* cf810e4 conflict fixed
...
不使用Fast forward
模式,merge合并分支后就像这样:
在实际开发中,按照几个基本原则进行分支管理:
首先,master
分支是非常稳定的,仅用来发布新版本,平时不能在上面干活;
干活都在dev
分支上,dev分支是不稳定的,到某个时候(比如新版本发布时),再把dev
分支合并到master
上,在master
分支发布新版本;
团队合作的分支看起来像这样:
Git分支十分强大,在团队开发中应该被充分应用。
在Git中,由于分支很强大,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
Git提供了一个stash
功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
储藏工作现场后用git status
查看工作区,就是干净的(除非有没有被Git管理的文件),因此就可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master
分支上修复,就从master
创建临时分支:
$ git checkout master
$ git checkout -b bug分支名
Switched to a new branch 'issue-101'
接下来修复bug,add并commit;
修复完成后,切换到master
分支,完成合并,再删除bug分支:
$ git switch master
$ git merge --no-ff -m "merged bug fix 101" bug分支
接着回到dev
分支干活:
$ git switch dev
$ git status
On branch dev
nothing to commit, working tree clean
发现工作区是干净的,使用git stash list
命令:
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge
工作现场还在,Git把stash内容存在某个地方了,恢复的办法有两个:
一是用git stash apply
恢复,但是恢复后,stash内容并不删除,需要用git stash drop
来删除;
二是用git stash pop
,恢复的同时把stash内容也删了;
$ git stash pop
再用git stash list
查看,就看不到任何stash内容了:
$ git stash list
可以多次stash,恢复的时候,先用git stash list
查看,然后恢复指定的stash,命令:
$ git stash apply stash@{0}
同样的bug要在master的分支dev上修复,需要把之前的提交所做的修改“复制”到dev分支;
注意:只复制之前这个提交所做的修改,并不是把整个master分支merge过来。
Git提供了一个cherry-pick
命令,复制一个特定的提交到当前分支:
$ git cherry-pick commit_id
两个commit只是改动相同,其实是两个不同的commit;
添加一个新功能时,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
切回主分支dev
,准备合并时:
$ git switch dev
突然要销毁这个分支!
$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.
销毁失败了…
分支还没有被合并,如果删除,将丢失掉修改;
如果要强行删除,需要使用大写的-D
参数。
强行删除命令:
$ git branch -D feature-vulcan
success!
从远程仓库克隆时,Git自动把本地的master
分支和远程的master
分支对应起来,远程仓库的默认名称是origin
。
查看远程库的信息,命令:
$ git remote
origin
显示更详细的信息,命令:
$ git remote -v
origin [email protected]:michaelliao/learngit.git (fetch)
origin [email protected]:michaelliao/learngit.git (push)
上面显示了可以抓取和推送的origin
的地址。如果没有推送权限,就看不到push的地址。
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上(这里默认现在是main):
$ git push origin master
如果要推送其他分支,比如dev
,就改成:
$ git push origin dev
并不一定要把本地分支往远程推送,哪些分支需要推送,哪些不需要?
master
分支是主分支,因此要时刻与远程同步;dev
分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;在Git中,分支完全可以在放在本地藏着玩,是否推送由自己决定!
多人协作时,大家都会往master
和dev
分支上推送各自的修改。
当小伙伴从远程库clone时,默认情况下,小伙伴只能看到本地的master
分支;
$ git branch
* master
如果小伙伴要在dev
分支上开发,必须创建远程origin
的dev
分支到本地,命令:
$ git checkout -b dev origin/dev
这样小伙伴就可以在dev
上继续修改,并时不时地把dev
分支push
到远程:
$ add
$ commit
$ git push origin dev
当你和你的小伙伴对同一个文件进行修改提交,并且都要向远程推送时,报错:
$ git push origin dev
To github.com:michaelliao/learngit.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to '[email protected]:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法:
先用git pull
把最新的提交从origin/dev
抓下来,然后,在本地合并,解决冲突,再推送:
$ git pull
git pull
也失败了,原因是没有指定本地dev
分支与远程origin/dev
分支的链接;
设置dev
和origin/dev
的链接:
$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
再pull:
$ git pull
手动解决冲突;
提交,再push:
$ git commit -m "fix env conflict"
$ git push origin dev
多人在同一个分支上协作时,后push的童鞋不得不先pull,在本地合并,然后才能push成功。
每次合并再push后,分支变成了这样:
$ git log --graph --pretty=oneline --abbrev-commit
* d1be385 (HEAD -> master, origin/master) init hello
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
| |/
* | 12a631b merged bug fix 101
|\ \
| * | 4c805e2 fix bug 101
|/ /
* | e1e9c68 merge with no-ff
|\ \
| |/
| * f52c633 add merge
|/
* cf810e4 conflict fixed
用(HEAD -> master)
和(origin/master)
标识出当前分支的HEAD和远程origin的位置;
命令git rebase
:
$ git rebase
First, rewinding head to replay your work on top of it...
Applying: add comment
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
Applying: add author
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
输出了一大堆操作,再使用git log
:
$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master) add author
* 3611cfe add comment
* f005ed4 (origin/master) set exit=1 //基于
* d1be385 init hello //非基于
...
原本分叉的提交现在变成一条直线了,原理很简单:Git把本地的提交“挪动”了位置,整个提交历史成了一条直线。rebase操作前后,最终的提交内容是一致的,但是本地的commit修改内容已经变化了,它们的修改不再基于d1be385 init hello
,而是基于f005ed4 (origin/master) set exit=1
,但最后的提交内容是一致的。
rebase操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观;
缺点是本地的分叉提交已经被修改过了。
最后通过push操作把本地分支推送到远程:
$ git push origin main
使用git log
查看效果:
$ git log --graph --pretty=oneline --abbrev-commit
发现远程分支的提交历史也是一条直线。
发布一个版本时,通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像,but分支可以移动,标签不能移动);
创建和删除标签都是瞬间完成的。
tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
在Git中打标签非常简单,首先切换到需要打标签的分支上:
$ git checkout master
命令git tag
,打一个新标签:
$ git tag v1.0
命令git tag
查看所有标签:
$ git tag
v1.0
默认标签是打在最新提交的commit上的;
也可以给历史提交的版本打标签,方法是找到它的commit id,然后打上就可以了:
$ git log --pretty=oneline --abbrev-commit //找到对应的commit id
$ git tag v0.9 对应的commit id
命令git tag
查看标签:
$ git tag
标签们....
标签不是按时间顺序列出,而是按字母排序的。
可以用git show
查看标签信息:
$ git show v0.9
可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字:
$ git tag -a tagname -m "说明文字" commit id
命令git show
查看说明文字:
$ git show v0.1
标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。
删除标签命令:
$ git tag -d tagname
创建的标签都只存储在本地,不会自动推送到远程,打错的标签可以在本地安全删除。
推送某个标签到远程的命令git push origin
:
$ git push origin v1.0
一次性推送全部尚未推送到远程的本地标签,命令:
$ git push origin --tags
标签已经推送到远程时,删除远程标签要先从本地删除:
$ git tag -d v0.9
然后从远程删除,删除命令push:
$ git push origin :refs/tags/v0.9
可登录GitHub查看是否从远程库删除了标签;
GitHub可以作为免费的远程仓库,个人的开源项目,放到GitHub上是完全没有问题的。
GitHub还是一个开源协作社区,通过GitHub,既可以让别人参与你的开源项目,也可以参与别人的开源项目。
在GitHub上,利用Git极其强大的克隆和分支功能,大家真正可以第一次自由参与各种开源项目;
如何参与一个开源项目?
比如人气极高的bootstrap项目,这是一个非常强大的CSS框架,可以访问它的项目主页https://github.com/twbs/bootstrap,点“Fork”就在自己的账号下克隆了一个bootstrap仓库,然后,从自己的账号下clone:
git clone [email protected]:xxxxxx/bootstrap.git
一定要从自己的账号下clone仓库,这样才能推送修改。
如果从bootstrap的作者的仓库地址
[email protected]:twbs/bootstrap.git
克隆,因为没有权限将不能推送修改。Bootstrap的官方仓库
twbs/bootstrap
、你在GitHub上克隆的仓库my/bootstrap
,以及你自己克隆到本地电脑的仓库,他们的关系就像下图显示的这样:┌─ GitHub ────────────────────────────────────┐ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ twbs/bootstrap │────>│ my/bootstrap │ │ │ └─────────────────┘ └─────────────────┘ │ │ ▲ │ └──────────────────────────────────┼──────────┘ ▼ ┌─────────────────┐ │ local/bootstrap │ └─────────────────┘
如果想修复bootstrap的一个bug,或者新增一个功能,立刻就可以开始干活,干完后,往自己的仓库推送。
如果希望bootstrap的官方库能接受你的修改,可以在GitHub上发起一个pull request。当然,对方是否接受你的pull request就不一定了。
使用GitHub时,国内的用户经常遇到的问题是访问速度太慢,有时候还会出现无法连接的情况。
可以使用国内的Git托管服务——Gitee(gitee.com)体验Git飞一般的速度。
和GitHub相比,Gitee也提供免费的Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,Gitee还提供了项目管理、代码托管、文档管理的服务,5人以下小团队免费。
Gitee的免费版本也提供私有库功能,只是有5人的成员上限。
使用Gitee和使用GitHub类似,在Gitee上注册账号并登录后,需要先上传自己的SSH公钥。选择右上角用户头像 -> 菜单“修改资料”,然后选择“SSH公钥”,填写一个便于识别的标题,然后把用户主目录下的
.ssh/id_rsa.pub
文件的内容粘贴进去:点击“确定”即可完成并看到刚才添加的Key:
如果已经有了一个本地的git仓库(例如,一个名为learngit的本地库),如何把它关联到Gitee的远程库上?
首先,在Gitee上创建一个新的项目,选择右上角用户头像 -> 菜单“控制面板”,然后点击“创建项目”:
项目名称最好与本地库保持一致:
然后,在本地库上使用命令
git remote add
把它和Gitee的远程库关联:git remote add origin [email protected]:liaoxuefeng/learngit.git
之后,就可以正常地用
git push
和git pull
推送了!如果在使用命令
git remote add
时报错:git remote add origin [email protected]:liaoxuefeng/learngit.git fatal: remote origin already exists.
这说明本地库已经关联了一个名叫
origin
的远程库,此时,可以先用git remote -v
查看远程库信息:git remote -v origin [email protected]:michaelliao/learngit.git (fetch) origin [email protected]:michaelliao/learngit.git (push)
可以看到,本地库已经关联了
origin
的远程库,并且,该远程库指向GitHub。我们可以删除已有的GitHub远程库:
git remote rm origin
再关联Gitee的远程库(注意路径中需要填写正确的用户名):
git remote add origin [email protected]:username/learngit.git
再查看远程库信息:
git remote -v origin [email protected]:liaoxuefeng/learngit.git (fetch) origin [email protected]:liaoxuefeng/learngit.git (push)
现在可以看到,origin已经被关联到Gitee的远程库了。通过
git push
命令就可以把本地库推送到Gitee上。git本身是分布式版本控制系统,可以同步到另外一个远程库,也可以同步到另外两个远程库。
使用多个远程库时要注意,git给远程库起的默认名称是
origin
;如果有多个远程库,需要用不同的名称来标识不同的远程库。仍然以
learngit
本地库为例,我们先删除已关联的名为origin
的远程库:git remote rm origin
然后,先关联GitHub的远程库:
git remote add github [email protected]:michaelliao/learngit.git
注意,远程库的名称叫
github
,不叫origin
了。接着,再关联Gitee的远程库:
git remote add gitee [email protected]:liaoxuefeng/learngit.git
同样注意,远程库的名称叫
gitee
,不叫origin
。用
git remote -v
查看远程库信息,可以看到两个远程库:git remote -v gitee [email protected]:liaoxuefeng/learngit.git (fetch) gitee [email protected]:liaoxuefeng/learngit.git (push) github [email protected]:michaelliao/learngit.git (fetch) github [email protected]:michaelliao/learngit.git (push)
如果要推送到GitHub,使用命令:
git push github master
如果要推送到Gitee,使用命令:
git push gitee master
这样一来,本地库就可以同时与多个远程库互相同步:
┌─────────┐ ┌─────────┐ │ GitHub │ │ Gitee │ └─────────┘ └─────────┘ ▲ ▲ └─────┬─────┘ │ ┌─────────────┐ │ Local Repo │ └─────────────┘
Gitee也同样提供了Pull request功能,可以让其他小伙伴参与到开源项目中来。
Git有很多可配置项,比如,让Git显示颜色,会让命令输出看起来更醒目:
$ git config --global color.ui true
这样Git会适当地显示不同的颜色;
使用git status
命令,可以给文件名标上颜色。
在Git工作区的根目录下创建一个特殊的.gitignore
文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
不需要从头写.gitignore
文件,GitHub已经准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
忽略文件的原则:
.class
文件;得到完整的.gitignore
文件再提交到Git,就完成了~
检验.gitignore
的标准是git status
命令是不是返回:working directory clean
。
使用Windows注意:
如果在资源管理器里新建一个.gitignore
文件,它会提示必须输入文件名,但是在文本编辑器里“保存”或者“另存为”就可以把文件保存为.gitignore
了。
如果想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore
忽略了:
$ git add 文件名
确实想添加该文件,命令参数 -f强制添加到Git:
$ git add -f 文件名
如果是.gitignore
写得有问题,需要找出来到底哪个规则写错了,命令git check-ignore检查:
$ git check-ignore -v App.class
.gitignore:3:*.class App.class
Git会告诉我们,.gitignore
的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。
还有些时候,当我们编写了规则排除了部分文件时,可以用git add -f
强制添加进去;
也可以添加两条例外规则(!.):把指定文件排除在.gitignore
规则外的写法就是!
+文件名,所以,只需把例外文件添加进去即可。
偷懒大法如下:
命令:st
表示status
:
$ git config --global alias.st status
用co
表示checkout
,ci
表示commit
,br
表示branch
,命令:
$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch
--global
参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用。
配置一个unstage
别名,命令git reset HEAD file
可以把暂存区的修改撤销掉(unstage),重新放回工作区:
$ git config --global alias.unstage 'reset HEAD' //把unstage换成reset HEAD
配置git last
来显示最后一次提交信息:
$ git config --global alias.last 'log -1'
把lg
配置成…:
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
配置文件
配置Git的时候,加上
--global
是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。每个仓库的Git配置文件都放在
.git/config
文件中:$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = [email protected]:michaelliao/learngit.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master [alias] last = log -1
别名就在
[alias]
后面,要删除别名,直接把对应的行删掉即可。而当前用户的Git配置文件放在用户主目录下的一个隐藏文件
.gitconfig
中:$ cat .gitconfig [alias] co = checkout ci = commit br = branch st = status [user] name = Your Name email = [email protected]
配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。
给Git配置好别名,就可以输入命令时偷个懒。
GitHub是一个免费托管开源代码的远程仓库。对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,就只能自己搭建一台Git服务器作为私有仓库使用。
搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的
apt
命令就可以完成安装。假设已经有
sudo
权限的用户账号,下面,正式开始安装。第一步,安装
git
:$ sudo apt-get install git
第二步,创建一个
git
用户,用来运行git
服务:$ sudo adduser git
第三步,创建证书登录:
收集所有需要登录的用户的公钥,就是他们自己的
id_rsa.pub
文件,把所有公钥导入到/home/git/.ssh/authorized_keys
文件里,一行一个。第四步,初始化Git仓库:
先选定一个目录作为Git仓库,假定是
/srv/sample.git
,在/srv
目录下输入命令:$ sudo git init --bare sample.git
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以
.git
结尾。然后,把owner改为git
:$ sudo chown -R git:git sample.git
第五步,禁用shell登录:
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑
/etc/passwd
文件完成。找到类似下面的一行:git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,
git
用户可以正常通过ssh使用git,但无法登录shell,因为我们为git
用户指定的git-shell
每次一登录就自动退出。第六步,克隆远程仓库:
现在,可以通过
git clone
命令克隆远程仓库了,在各自的电脑上运行:$ git clone git@server:/srv/sample.git Cloning into 'sample'... warning: You appear to have cloned an empty repository.
剩下的推送就简单了。
管理公钥
如果团队很小,把每个人的公钥收集起来放到服务器的
/home/git/.ssh/authorized_keys
文件里就是可行的。如果团队有几百号人,可以用Gitosis来管理公钥。这里我们不介绍怎么玩Gitosis了,几百号人的团队基本都在500强了,相信找个高水平的Linux管理员问题不大。
管理权限
有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具。
当对Git的提交、分支已经非常熟悉,可以熟练使用命令操作Git后,再使用GUI工具,就可以更高效。
Git有很多图形界面工具,推荐SourceTree,它是由Atlassian开发的免费Git图形界面工具,可以操作任何Git库。
首先从官网下载SourceTree并安装,然后直接运行SourceTree。
第一次运行SourceTree时,SourceTree并不知道我们的Git库在哪。如果本地已经有了Git库,直接从资源管理器把文件夹拖拽到SourceTree上,就添加了一个本地Git库:
也可以选择“New”-“Clone from URL”直接从远程克隆到本地。
提交
双击
learngit
这个本地库,SourceTree会打开另一个窗口,展示这个Git库的当前所有分支以及文件状态。选择左侧面板的“WORKSPACE”-“File status”,右侧会列出当前已修改的文件(Unstaged files):选中某个文件,该文件就自动添加到“Staged files”,实际上是执行了
git add README.md
命令:然后,在下方输入Commit描述,点击“Commit”,就完成了一个本地提交:
实际上是执行了
git commit -m "update README.md"
命令。使用SourceTree进行提交就是这么简单,它的优势在于可以可视化地观察文件的修改,并以红色和绿色高亮显示。
分支
在左侧面板的“BRANCHES”下,列出了当前本地库的所有分支。当前分支会加粗并用○标记。要切换分支,我们只需要选择该分支,例如
master
,然后点击右键,在弹出菜单中选择“Checkout master”,实际上是执行命令git checkout master
:要合并分支,同样选择待合并分支,例如
dev
,然后点击右键,在弹出菜单中选择“Merge dev into master”,实际上是执行命令git merge dev
:推送
在SourceTree的工具栏上,分别有
Pull
和Push
,分别对应命令git pull
和git push
,只需注意本地和远程分支的名称要对应起来,使用时十分简单。注意到使用SourceTree时,只是省下了敲命令的麻烦,SourceTree本身还是通过Git命令来执行任何操作。如果操作失败,SourceTree会自动显示执行的Git命令以及错误信息,我们可以通过Git返回的错误信息知道出错的原因:
Git极其强大,命令繁多,常用的命令只有十几个,掌握好这十几个常用命令,就可以得心应手地使用Git了。
国外网友制作的Git Cheat SheetGit Cheat Sheet
Git的官方网站:http://git-scm.com
放上思维导图,蓝色图例的都是命令噢~
笔记结束~引用均来自廖雪峰官方网站!!!
如果对你有帮助的话不要忘记一键三连噢~
谢谢鸭~
初次编写于2021//28日;