【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了

我是灼灼,一只初学Java的大一金渐层。
向往余秀华和狄兰·托马斯的疯狂,时常沉溺于将情感以诗相寄;追逐过王尔德、王小波的文字,后陷于毛姆和斯蒂芬·金不可自拔;热爱文学的浪潮,白日梦到底却总在现实里清醒;艳羡平静又极度渴盼奔跑的力量。
欢迎与我交流鸭· QQ:1517526827;
个人博客:https://blog.csdn.net/weixin_52777510?spm=1001.2101.3001.5343

Git和GitHub的使用笔记

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教程

文章目录

  • Git和GitHub的使用笔记
    • 安装Git
    • 创建版本库
    • 时光机穿梭
      • 版本回退
      • 工作区和暂存区
    • 远程仓库
      • 添加远程库
        • SSH警告
      • 从远程库克隆
    • 分支管理
      • 创建与合并分支
        • switch
      • 解决冲突
      • 分支管理策略
        • 分支策略
      • Bug分支
      • Feature分支
      • 多人协作
        • 推送分支
        • 抓取分支
      • Rebase
    • 标签管理
      • 创建标签
      • 操作标签
    • 使用GitHub
    • 使用Gitee
    • 自定义Git
      • 忽略特殊文件
      • 配置别名
        • 配置文件
      • 搭建Git服务器
        • 管理公钥
        • 管理权限
    • 使用SourceTree
      • 提交
      • 分支
      • 推送
    • 总结

安装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-repo

把文件往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_rsaid_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash,这里注意不需要身份就可以直接输),创建SSH Key:

$ ssh-keygen -t rsa -C "youremail地址"

一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码;

如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsaid_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 learngit

xxxxxxxxxxxxxxxxxxx ~ (master)
$ cd learngit

xxxxxxxxxxxxxxxxxxxx (master)
$ pwd
xxxxxxxxxxxxxxxxxxxx

xxxxxxxxxxxxxxxxxxxx ~/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 files

xxxxxxxxxxxxxxxxxxxx ~/learngit (master)
$ git add readme.txt

xxxxxxxxxxxxxxxxxxxx ~/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 main

xxxxxxxxxxxxxxxxxxxx ~/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;

SSH警告

当第一次使用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严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

git-br-initial

每次提交,master分支都会向前移动一步。随着你不断提交,master分支的线也越来越长。

当创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

git-br-create

Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化。

从现在开始,对工作区的修改和提交就是针对dev分支了,新提交一次后,dev指针往前移动一步,而master指针不变:

git-br-dev-fd

假如在dev上的工作完成了,就可以把dev合并到master上。可以直接把master指向dev的当前提交,就完成了合并:

git-br-ff-merge

所以Git合并分支也很快!就改改指针,工作区内容也不变。

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,就剩下了一条master分支:

git-br-rm

我们是看不出来有些提交是通过分支完成的~

创建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分支此刻的提交点并没有变:

git-br-on-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分支上工作效果是一样的,但过程更安全;

switch

切换分支使用git checkout ,前面讲过的撤销修改则是git checkout -- ,同一个命令,有两种作用;

但是实际上,切换分支这个动作,用switch更科学:最新版本的Git提供了新的git switch命令来切换分支;

创建并切换到新的dev分支:

$ git switch -c dev

直接切换到已有的master分支:

$ git switch master

使用新的git switch命令,比git checkout要更容易理解。

解决冲突

master分支和新创建的feature1分支各自都分别有新的修改并提交,就会变成这样:

git-br-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-br-conflict-merged

用带参数的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合并分支后就像这样:

git-no-ff-mode

分支策略

在实际开发中,按照几个基本原则进行分支管理:

首先,master分支是非常稳定的,仅用来发布新版本,平时不能在上面干活;

干活都在dev分支上,dev分支是不稳定的,到某个时候(比如新版本发布时),再把dev分支合并到master上,在master分支发布新版本;

团队合作的分支看起来像这样:

git-br-policy

Git分支十分强大,在团队开发中应该被充分应用。

Bug分支

在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分支,在上面开发,完成后,合并,最后,删除该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分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
  • bug分支只用于在本地修复bug,没必要推送到远程(除非老板要看看你每周到底修复了几个bug);
  • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

在Git中,分支完全可以在放在本地藏着玩,是否推送由自己决定!

抓取分支

多人协作时,大家都会往masterdev分支上推送各自的修改。

当小伙伴从远程库clone时,默认情况下,小伙伴只能看到本地的master分支;

$ git branch
* master

如果小伙伴要在dev分支上开发,必须创建远程origindev分支到本地,命令:

$ 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分支的链接;

设置devorigin/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

Rebase

多人在同一个分支上协作时,后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,既可以让别人参与你的开源项目,也可以参与别人的开源项目。

在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就不一定了。

使用Gitee

使用GitHub时,国内的用户经常遇到的问题是访问速度太慢,有时候还会出现无法连接的情况。

可以使用国内的Git托管服务——Gitee(gitee.com)体验Git飞一般的速度。

和GitHub相比,Gitee也提供免费的Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,Gitee还提供了项目管理、代码托管、文档管理的服务,5人以下小团队免费。

Gitee的免费版本也提供私有库功能,只是有5人的成员上限。

使用Gitee和使用GitHub类似,在Gitee上注册账号并登录后,需要先上传自己的SSH公钥。选择右上角用户头像 -> 菜单“修改资料”,然后选择“SSH公钥”,填写一个便于识别的标题,然后把用户主目录下的.ssh/id_rsa.pub文件的内容粘贴进去:

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第1张图片

点击“确定”即可完成并看到刚才添加的Key:

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第2张图片

如果已经有了一个本地的git仓库(例如,一个名为learngit的本地库),如何把它关联到Gitee的远程库上?

首先,在Gitee上创建一个新的项目,选择右上角用户头像 -> 菜单“控制面板”,然后点击“创建项目”:

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第3张图片

项目名称最好与本地库保持一致:

然后,在本地库上使用命令git remote add把它和Gitee的远程库关联:

git remote add origin [email protected]:liaoxuefeng/learngit.git

之后,就可以正常地用git pushgit 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显示颜色,会让命令输出看起来更醒目:

$ git config --global color.ui true

这样Git会适当地显示不同的颜色;

使用git status命令,可以给文件名标上颜色。

忽略特殊文件

在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。

不需要从头写.gitignore文件,GitHub已经准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore

忽略文件的原则:

  1. 忽略操作系统自动生成的文件,比如缩略图等;
  2. 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
  3. 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

得到完整的.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表示checkoutci表示commitbr表示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配置好别名,就可以输入命令时偷个懒。

搭建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就是这个工具。

使用SourceTree

当对Git的提交、分支已经非常熟悉,可以熟练使用命令操作Git后,再使用GUI工具,就可以更高效。

Git有很多图形界面工具,推荐SourceTree,它是由Atlassian开发的免费Git图形界面工具,可以操作任何Git库。

首先从官网下载SourceTree并安装,然后直接运行SourceTree。

第一次运行SourceTree时,SourceTree并不知道我们的Git库在哪。如果本地已经有了Git库,直接从资源管理器把文件夹拖拽到SourceTree上,就添加了一个本地Git库:

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第4张图片

也可以选择“New”-“Clone from URL”直接从远程克隆到本地。

提交

双击learngit这个本地库,SourceTree会打开另一个窗口,展示这个Git库的当前所有分支以及文件状态。选择左侧面板的“WORKSPACE”-“File status”,右侧会列出当前已修改的文件(Unstaged files):

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第5张图片

选中某个文件,该文件就自动添加到“Staged files”,实际上是执行了git add README.md命令:

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第6张图片

然后,在下方输入Commit描述,点击“Commit”,就完成了一个本地提交:

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第7张图片

实际上是执行了git commit -m "update README.md"命令。

使用SourceTree进行提交就是这么简单,它的优势在于可以可视化地观察文件的修改,并以红色和绿色高亮显示。

分支

在左侧面板的“BRANCHES”下,列出了当前本地库的所有分支。当前分支会加粗并用○标记。要切换分支,我们只需要选择该分支,例如master,然后点击右键,在弹出菜单中选择“Checkout master”,实际上是执行命令git checkout master

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第8张图片

要合并分支,同样选择待合并分支,例如dev,然后点击右键,在弹出菜单中选择“Merge dev into master”,实际上是执行命令git merge dev

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第9张图片

推送

在SourceTree的工具栏上,分别有PullPush,分别对应命令git pullgit push,只需注意本地和远程分支的名称要对应起来,使用时十分简单。

注意到使用SourceTree时,只是省下了敲命令的麻烦,SourceTree本身还是通过Git命令来执行任何操作。如果操作失败,SourceTree会自动显示执行的Git命令以及错误信息,我们可以通过Git返回的错误信息知道出错的原因:

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第10张图片

总结

Git极其强大,命令繁多,常用的命令只有十几个,掌握好这十几个常用命令,就可以得心应手地使用Git了。

国外网友制作的Git Cheat SheetGit Cheat Sheet

Git的官方网站:http://git-scm.com

放上思维导图,蓝色图例的都是命令噢~

【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第11张图片

笔记结束~引用均来自廖雪峰官方网站!!!
【连载】Git和GitHub的使用——有了远程仓库,妈妈再也不用担心我的硬盘了_第12张图片
如果对你有帮助的话不要忘记一键三连噢~
谢谢鸭~

初次编写于2021//28日;

你可能感兴趣的:(Java,学习笔记,基础知识,java,git,github)