Github 学习笔记——从零开始学习
一.Git简介
1.什么是Github
Git是目前世界上最先进的分布式版本控制系统,所谓的版本控制系统简单来说就是一个开发软件他可能经过多次的修改,版本控制系统会记录软件版本修改的操作,以便达到对软件版本进行管理的目的
2.Github 的开发语言
Git是Linus(Linux创始人)用C语言开发的。
3.集中式和分布式的区别
集中式版本控制系统,版本库是存放在中央服务器的,需要进行任务工作,必须要从中央服务器取得最新的版本,然后才能进行任务操作,任务完成之后,在将完成好的工作推送给中央服务器。特点就是集中管理存储,需要联网,一旦中央服务器坏的,工作任务就丢失了。
分布式版本控制系统,没有所谓的中央服务器,每个人的电脑上都是一个完整的版本库,个人电脑的损坏并不影响整个任务,依旧可以从其他人那里拿到该任务。分布式版本的多人协作就是不同的人对任务进行修改了之后,互相将修改的任务推送给对方,就可以合并修改。分布式版本控制系统的服务器只起到一个中转站的作用,即使服务器损坏,也只是不能通讯,并不会造成丢失。
4.Github的安装
这里只说Linux系统的使用,在Ubuntu Linux系统,使用git
命令就能查看系统是否安装Git:
如果没有安装Git,使用sudo apt-get install git
命令进行安装。
二.Github 的使用(常用命令)
1.创建版本库
使用一个合适的文件夹,可以自己创建一个空目录:
$ mkdir learngit
$ cd learngit
通过git init
命令把目录变成Git管理的仓库。
这个.git里面的文件不要随意修改。请谨慎的在现有文件加上创建Git仓库,以免造成不必要的麻烦。
编写一个文件:vim readme.txt
,内容如下:
Git is a version control system.
Git is free software.
第一步使用git add
命令将文件添加到仓库
$ git add readme.txt
第二步使用git commit
将文件提交到仓库
$ git commit -m "wrote a readme file"
-m 后面的双引号内容为解释信息,可以随意写,但是最好是有意义的文字,可以解释该次提交内容。这里为什么提交内容需要两步呢?解释就是第一步的add命令只是将需要提交的文件放入暂存区,并没有真正的提交,可以随时撤回,真正需要提交时,才使用git commit命令进行提交。
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
2.Git状态查看和修改
使用git status
命令可以查看当前状态
修改文件后,使用git diff
命令可以查看对比文件修改前后的差别:
$ git diff readme.txt
然后再使用add和commit命令可对修改后的内容进行提交
3.版本的回退
使用git log
命令可以查看修改的内容和时间节点:
$ git log
在命令后面加上--pretty=oneline
参数可以减少信息量:
$ git log --pretty=oneline
在Git中使用HEAD
表示当前版本,上一个版本是HEAD^
,上上一个版本是HEAD^^
,当版本数目过多时,比如前100个版本就是HEAD~100
。
使用git reset
命令进行版本回退
$ git reset --hard HEAD^
查看readme.txt内容,版本确实会被还原。
此时查看版本历史
$ git log
之前的最新版本已经没有了。
也可以使用版本号回到指定的版本git reset --hard commit_id
,版本号不必写全,Git会自动联想,只要写开头一部分即可。比如再回到之前的最新版本:
$ git reset --hard 8ad2d
查看readme.txt内容,版本确实又回到了最新
最后使用git reflog
可以记录每一次的命令,即使电脑关电,也能查看之前的版本号
$ git reflog
然后根据版本号进行指定的版本回退。
4.细说工作区和暂存区
工作区:电脑里面能看到的目录,比如前面的learngit文件夹就是一个工作区
版本库:工作区的隐藏目录.git
,这个文件夹是Git的版本库。暂存区就是在版本库里面,称为stage(index),本测试用系统Git暂存区实际是index。还有一个自动创建的第一个分支master
,还有指向master
的指针叫做HEAD
。
Git中将文件像Git版本库添加时,分两步执行:第一步使用git add
将文件修改添加到暂存区;第二步使用git commit
提交更改,实际就是把暂存区的所以内容提交到当前分支。总的来说,就是把需要提交的文件修改通通放大暂存区,然后一次性提交暂存区的所有修改。
修改readme.txt和添加LICENSE文件后,使用git status
查看状态:
使用git add
添加到暂存区:
$ git add readme.txt LICENSE
使用vim 编辑器查看index文件vim index
可以看到暂存区内容:
HEAD内容为:
执行git commit
命令可以把暂存区所有修改提交到分支master:
$ git commit -m "understand how stage works"
此时再查看工作区状态,工作区变得很干净:
git status
再查看暂存区index里面的内容,发现没有stage相关的任何内容:
5.修改的管理
Git跟踪管理的不是文件,而是修改。所谓的修改,就是对某个文件的增删改查,Git就是对这个修改进行记录。
进行实验,继续对readme.txt进行修改,增加一行内容Git tracks changes.
:
进行添加:
$ git add readme.txt
$ gut status
继续修改readme.txt,增加·of files.
:
直接提交:
$ git commit -m "git tracks changes"
查看状态:
可以看到,第二次的修改其实是没有被提交的,这是因为第二次的修改并没有使用git add
命令,只有第一次的修改使用了该命令,修改被放入暂存区,准备提交,第二次修改没有放入暂存区,没有被提交,git commit
只负责把暂存区的修改进行提交。
6.撤销修改
对readme.txt进行修改,加入一个错误行,内容为My stupid boss still prefers SVN
查看使用git status
使用git checkout -- file
可以丢弃工作区的修改,即把file文件在工作区的修改全部撤销。注意,该撤销只能撤销回到最近一次git commit
或者git add
时的状态
git checkout -- readme.txt
注意,这里的命令里面的--
不能省略,否则命令含义会发生变化。
除了使用git checkout -- file
之外,还可以使用命令git reset HEAD <file>
来把暂存区的修改撤掉,重新放回工作区,其中HEAD
表示最新的版本。
如果已经将版本提交到版本库,那么就可以使用版本回退功能回退到上一个版本。
7.文件的删除
文件删除也是一种修改,通常有两种选择从版本库删除该文件。
一种就是使用命令git rm
并且git commit
$ git rm test.txt
$ git commit -m "remove test.txt"
另一种是删除错了,需要对误删除的文件进行恢复到最新版本
git checkout -- test.txt
git checkout
的实际操作就是用版本库里面的版本替换工作区里面的版本,无论工作区是删除或者修改,都可以一键还原。
三.Github远程仓库
1.添加远程仓库
之前的操作是基于本地的Git仓库,需要在Github创建一个远程仓库,并且让两个仓库进行远程同步,远程仓库的可以当做备份也能够进行共享,进行协同开发。
想要创建远程仓库,首先需要登录Github,使用Creat a new repository,创建一个新仓库。
创建好的新仓库后,会提示使用这个仓库克隆新的仓库,或者把已有的本地仓库和它相关联,之后把本地仓库内容推送到Github。
使用git remote add origin git@github.com:nofishisme/learngit.git
添加远程仓库,将本地仓库和远程仓库关联,远程仓库名字默认是origin
,可以进行修改,但是一般不建议。
然后使用git push -u origin master
将本地库的内容推送到远程库上面去。最新2024年3/29进行操作,出现了错误,存在以下提示:
可以使用https的方式解决,将.git/config里面url后的内容改成https的形式。
改成
改完之后直接使用git push -u origin master
会提示输入账号密码,但是最后又报错说该方式已经无法使用,需要继续更换方式。
解决办法使用生成令牌token,具体教程参考【突发】解决remote: Support for password authentication was removed on August 13, 2021. Please use a perso-优快云博客,其核心就是将生成的令牌号与之前url 后的链接相结合,url后的链接变为https://<your_token>@github.com/<USERNAME>/<REPO>.git
,其中<your_token>用令牌码替换,使用用户名替换,用项目名字替换,最终结果为如下:
又出现问题,截图如下:
解决办法是查看git是否使用代理。后发现是因为挂了代理出现了问题,关闭掉代理后可正常提交。
终于,艰难操作之后,将本地库的内容推送到了远程,将本地库内容推送到远程,使用git push命令,实际上是把当前分支master推送到远程。
总结,关联一个远程库,有效的做法是使用命令以下命令:
git remote add origin https://<your_token>@github.com/<USERNAME>/<REPO>.git
其中的token需要自己在Git上申请。
远程库是空的,第一次推送master
分支时,加上-u
参数,Git会同时把本地的master
分支内容推送远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,以后推送和拉取就能简化命令。
后续,本地的提交,都可以通过git push origin master
将本地master
分支的最新修改推送到Github。
2.删除远程库
使用git remote rm <name>
命令可以删除远程库,删除前,使用git remote -v
查看远程库信息。
该删除只是解除远程和本地的绑定关系,并不是物理上删除了远程库,远程库本身没有任何改动。真正的删除远程库,需要登录到Github进行删除。
补充(2024/4/4):语句命令find . -name ".git" | xargs rm -Rf
可以解除本地文件夹的仓库身份
3.远程库克隆
前面是先创建本地库,再关联远程库,但是最好的方式就是先创建远程库,然后从远程库进行克隆。
新建一个仓库,取名为gitskills,勾选Initialize this repository with a README
,Github就会自动创建一个README.md文件。
使用命名git clone
克隆一个本地库
$ git clone https://github.com/nofishisme/gitskills.git
在这里使用命令git clone git@github.com:nofishisme/gitskills.git
还是会报错,提示无法识别远程仓库,后续提供解决使用SSH的解决办法。
4.解决SSH无法使用的办法
从一开始,使用SSH相关的命令,都会提示出错:
显示无法读取到远程仓库,这是因为没有使用SSH秘钥,需要对Github配置SSH秘钥连接。
1.打开终端;
2.输入命令生成密钥ssh-keygen -t rsa -C "Your_email@example.com"
3.输入您要保存密钥文件的路径和名称,或者直接敲击回车键选择默认值,例如:/home/YourName/.ssh/id_rsa;
4.在弹出的窗口中输入密码,或者直接敲击回车键选择不设置密码;
5.成功生成SSH Key 会提示The key's randomart image is:
,并显示一个图形,然后得到一个公共密钥(id_rsa.pub)和一个私有密钥(id_rsa)
这时已经成功地生成了 SSH 密钥对。公钥是要提供给远程服务器的,私钥则要保留在本地,并加以保护。另外,如果您想要让 SSH 代理管理您的密钥,您还需要在本地配置 SSH 代理。
Github添加SSH秘钥,找到settings,SSH and GPG keys
四.Git分支管理
1.分支管理
分支管理就是在原来分支上提取一个分支,该分支不对原分支造成影响,再对提取的分支进行操作后,再与原来的分支进行合并,这样的分支功能既安全又不会影响别人的正常工作。
2.创建和合并分支
每次提交,Git就把它们串成一条时间线,这条时间线就是一个分支。最开始,只有一条时间线,被称之为主分支,也就是master
分支。HEAD
并不是指向提交而是指向master
,而master
是指向提交的,也就是说,HEAD
指向当前分支。
master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点。
每一次提交,master
分支都会向前移动一步,随着不断的提交,master
分支会越来越长。
可以创建一个新的分支,取名叫dev
,Git会新建一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上。
新增了dev
分支后,后续的工作区的修改就是针对dev
分支了,每产生一次新的提交,dev
指针就往前移动一步,但是master
指针不变。
合并分支最简单的方法就是直接把master
指向dev
的当前提交,就能完成合并。
合并完成之后,也可以直接删除dev
分支,删掉后就剩下master
一条分支。
首先,创建dev
分支并切换到dev
分支,其中-b
参数表示创建并切换:
$ git checkout -b dev
或者$ git switch -c dev
或者
$ git branch dev
$ git checkout dev
或者$ git switch dev
可以换使用git branch
命令查看当前分支
$ git branch
可以看见,当前分支就是dev
,前面会加上*
号。
在dev
分支上对readme.txt
进行修改,新增一行Creating a new branch is quick.
,并进行提交。
完成dev
分支工作后,使用命令git checkout master
切回主分支。
切换为主分支后发现readme.txt
里面并没有新添加的内容,这是因为那个提交在分支dev
上,但是master
分支没有变。
使用命令git merge dev
可以将dev
分支的工作成果合并到当前的master
分支上。
$ git merge dev
注意提示中的Fast-forward
,其表示这次合并是“快进模式”,直接把master
指针指向dev
的当前提交,所以合并速度非常快。
合并完成之后,就可以删除分支dev
$ git branch -d dev
使用branch很快,也很安全,所以提倡使用分支来完成某个任务。
注意,如果使用命令git switch -c <name>
提示不是一个git命令,那么可能是本地git的版本有问题,使用命令git --version
可以查看当前的git版本。
git --version
可以使用命令将git版本升级,无论是否安装git,都可以使用以下命令安装最新版本的git。
$ sudo add-apt-repository ppa:git-core/ppa
$ sudo apt update
$ sudo apt install git
3.分支冲突解决
新建一个分支,进行冲冲突测试,创建一个分支feature1
,并切换到分支,修改readme.txt
内容,最后一行添加Creating a new branch is quick AND simple.
,并在分支feature1
上进行提交,再切换会master
分支,会提示当前的master
分支比远程的master
分支要超前一个提交。
同时修改master
分子里面的readme.txt
文件,最后一行添加Creating a new branch is quick & simple.
。可以看到,master
分支里面添加的内容和feature1
分支里面添加内容不一致,也将master
分支内容提交。
于是master
分支和feature1
分支都有了自己新的提交:
在这种情况下,Git无法执行快速合并,只能尝试将各自修改合并,但是可能产生冲突。
合并产生冲突,必须要解决冲突之后才能提交。
可以直接查看待修改文件的内容进行修正,这里是readme.txt
文件:
<<<<<<<
,=======
,>>>>>>>
表示不同分支的内容。可以直接手动修改内容,然后提交,这里修改为
Creating a new branch is quick and simple
,合并的情况如下:
使用带参数的命令git log
可以查看分支合并情况。
$ git log --graph --pretty=oneline --abbrev-commit
最后,再删除feature1
分支。
$ git branch -d feature1
4.分支管理策略
Git通常会倾向于使用Fast forward
的合并模式,但是这种模式下删除分支会丢掉分支信息。强行禁用Fast forward
模式,Git会在merge时生成一个新的commit,这样就可以从分支历史上看出分支信息。
以下为--no-ff
方式的git merge
创建新的分支dev
并且切换到分支dev
,在分支dev
修改readme.txt
的内容然后进行提交,在切换会主master
分支。
这里提示分支领先‘origin/master’共四个提交时因为本地仓库没有提交更新到远程仓库。
使用--no-ff
参数来进行分支dev
的合并,表示禁用Fast forward
:
$ git merge --no-ff -m "merge with no-ff" dev
本次合并要创建一个新的commit
,所以要加上-m
参数,把commit
的描述写进去。
查看分支历史:
$ git log --graph --pretty=oneline --abbrev-commit
fast forward
模式的合并看不出曾经有合并,而加上--no-ff
参数后就可以用普通模式合并,能看到历史分支。
5.用于BUG的分支
当软件开发中遇到bug时,可以创建一个临时分支来进行修复,修复后在合并分支,然后删除临时分支。‘
当遇到bug需要处理,但是当前工作区任务却并没有完成,则可以使用git stash
命令可以将当前工作现场”储藏“起来,等后面恢复现场后继续工作,然后再创建分支进行bug处理。需要在哪个分支进行bug处理,就要在哪个分支创建上创建临时分支。
1.创建并切换到分支issue-101
2.修复需要修改的文件,修改完成后添加提交到git
3.切换到主分支master(main)
4.使用非forward的方式进行分支合并
5.删除分支issue-101
使用命令git stash list
可以查看被保存的工作现场,可以使用两种方法进行恢复。一是使用命令git stash apply
进行恢复,恢复后stash内容并不会删除,需要使用git stash drop
来删除。二是使用命令git stash pop
,恢复的同时把stash内容也删除了。
如果除了主分支master
外,在dev
分支上也有同样的bug,怎么解决呢?解决办法有两种。
操作一是在dev
分支上进行之前同样的操作。
更简单的操作二是把b196443 fix bug 101
这个提交所做的修改“复制”到dev
分支,使用命令git cherry-pick
可以复制一个特定的提交到当前分支。
可以看到这里的提交就是分支dev 2e26da0
,虽然两个提交改动相同,但是实际是和master不同的分支。
6.新功能Feature分支
每当要添加一个新功能时,为了防止实验代码把主分支代码打乱,会新建一个feature分支,在上面开发完成后再进行合并,然后再删除feature分支。
假设要开发一个代号为FishOS的功能。
1.新建分支
$git switch -c feature-FishOS
2.开发完成后添加提交
$ git add FishOS.cpp
$ git commit -m "add feature FishOS"
3.切换回dev
,准备合并
$git switch dev
正常情况下,feature可以正常合并,删除。但是假设项目出了问题,新功能必须取消,而且还要删除这个新的分支,
$ git branch -d feature-FishOS
提示分支未合并,不能删除,但是可以强制删除
$ git branch -D feature-FishOS
7.多人合作
远程分支的克隆,就是本地的master分支和远程的master分支对应起来,远程默认仓库名称是origin。
使用命令git remote
或者git remote -v
可以显示远程库信息
使用命令$ git push origin master
就可以把本地master分支上的所有本地提交推送到远程库对应的远程分支上,如果要推送其他分支,更改最后的远程分支名字:$git push origin dev
进行推送即可
分支推送的必要性:
1.master分支是主分支,要时刻与远程同步
2.dev分支是开发分支,团队所有成员需要在上面工作,所以也需要与远程同步
3.其余的的bug和feature分支依据情况而定
分支的抓取(fork)
使用另一个电脑(添加SSH key到github,教程前面有:Ctrl + 左键点击)或者同一个电脑的另一个目录下进行克隆:
从别人的仓库克隆或者克隆别人仓库时,默认情况下只能看到本地的master分支
如果要进行远程开发,就必须要新建远程origin的dev分支到本地
$ git checkout -b dev origin/dev
注:这里出现了一个错误,在克隆时远程仓库并没有dev分支导致无法在新克隆的仓库中使用上述命令创建分支dev
这里的解决方式是在新克隆的仓库(nofish@ubuntu:~/git_study/learngit$ )里创建dev
分支git switch -c dev
,然后在dev分支里面做修改并且提交推送到远程仓库,这个时候远程仓库机会存在dev分支。然后再回到原始的仓库(nofish@ubuntu:~/learngit$ ),用git pull
命令拉取远程仓库,拉取完成后使用git branch
查看本地分支会发现也只有master分支,不存在dev分支,但是命令git checkout -b dev origin/dev
已经可以使用。
解决分支问题后,就是要在分支上进行修改创建,
其他人对远程仓库的dev分支进行推送之后,如果自己又对同样的文件做了修改,并且尝试推送
发现自己修改无法进行推送,这是因为别人的最新提交和自己的推送提交有冲突,解决办法就是,先用git pull
把最新的提交从origin/dev
抓下来,然后,在本地合并,解决冲突,再进行推送
$ git pull
发现git pull
失败,原因是没有指定本地dev
分支与远程origin/dev
分支的链接,根据提示,设置dev
和origin/dev
的链接
$ git branch --set-upstream-to=origin/dev dev
再进行git pull
拉取成功但是合并有冲突,需要手动解决合并问题,如果没有设置合并方式,会先提示上图的冲突解决策略,这里选择的是git config pull.rebase false
。然后再使用git pull
命令进行拉取
$ git config pull.rebase false
$ git pull
这回git pull
成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的分支冲突解决:Ctrl + 左键点击完全一样。解决后,提交,再push:
多人工作模式总结:
1.先试用命令git push origin <branch-name>
推送自己的修改;
2.如果推送失败,就是远程分支比本地更新,先试图用git pull
合并;
3.合并产生冲突,需要解决冲突,再本地提交;
4.没有冲突或者解决掉冲突后,再用git push origin <branch-name>
推送就能成功!
注:如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
五.常用命令集合
命令 | 作用 | 备注 |
---|---|---|
git | 在linux系统上查看有没有安装Git | sudo apt-get install git使用以上命令可以安装 |
git init | 通过git init命令把这个目录变成Git可以管理的仓库 | |
git add <file> | 用命令git add告诉Git,把文件添加到仓库 | 第一步使用git add添加 |
git commit -m <message> | 用命令git commit告诉Git,把文件提交到仓库 | 第二步使用git commit提交 |
git push -u origin master | 将本地仓库提交推送到远程仓库 | 加-u 表示第一次提交时创建远程仓库 |
git status | 运行git status命令查看当前仓库状态 | |
git diff <file> | 用git diff这个命令查看file文件的修改对比 | 查看difference,显示的格式正是Unix通用的diff格式 |
git log | git log命令显示从最近到最远的提交日志 | 如果嫌输出信息太多,看得眼花缭乱的,可以试试加上–pretty=oneline参数 |
git reset --hard HEAD^ | 版本回退到上一个版本 | |
git reset --hard commit_id | 版本回退到指定版本 | 版本号不必写全 |
git reflog | 记录每一次的命令 | 即使电脑关电,也能查看之前的版本号 |
git checkout -- file | 撤销工作区的修改 | 该撤销只能撤销回到最近一次git commit 或者git add 时的状态 |
git reset HEAD <file> | 把暂存区的修改撤掉,重新放回工作区 | 其中HEAD 表示最新的版本 |
git rm <file> | 从版本库删除该文件 | 需要配合git commit -m <message> 提交修改 |
git checkout -- <file> | 对误删除的文件进行恢复到最新版本 | |
git remote add origin <rep> | 添加远程仓库 | 需要远程仓库已创建 |
git remote rm <name> | 删除远程库 | 该删除只是解除远程和本地的绑定关系,并不是物理上删除了远程库 |
git clone <rep> | 克隆一个仓库 | |
git checkout -b | 创建一个分支,并切换到该分支 | |
git switch -c <name> | 创建一个分支,并切换到该分支 | |
git merge <name> | 将分支和主分支合并 | 默认是Fast-forward的合并模式 |
git merge --no-ff -m | 将分支和主分支合并 | 不使用Fast-forward的合并模式 |
git branch | 查看当前分支 | 加上-a 查看当前所有分支,包括远程仓库 |
git log --graph --pretty=oneline --abbrev-commit | 查看分支合并情况 | 图形化界面 |
git branch -d <name> | 删除一个分支 | |