Git使用方法

Git的工作原理:
在这里插入图片描述
Git 常用命令集
如何修改本地仓库地址:
1.打开git bash
2.cd 你想创建仓库的位置例如 cd F:\mygit_repo (名字中间有空格的如Program file写为Program” ”file)
3.使用 git init 命令
如何修改远程仓库地址:(待补充)

右键菜单中, Git Init Here直接在当前目录下创建一个代码仓库, Git Gui打开Gui的图形操作页面

配置属性:
git config --global user.name “balabala” 配置用户名
git config --global user.email “balabala@aa.com” 配置邮箱
git config --global user.name 后面没接东西,显示用户名
用户自己的全局配置保存在~/.gitconfig中,参考链接
生成SSH Key: ssh-keygen -t rsa -C "youremail@xmail.com" ,会得到id_rsa和id_rsa.pub,一般是保存在家目录的.ssh目录下。复制id_rsa.pub中的内容到github的settings→SSH and GPG keys里,就完成了密钥的配置.
密钥里面包含了用户名和密码的信息,所以不同设备下的同名用户可以使用同一个密钥,同一个设备下的不同用户不能使用同一个密钥。

Git使用本地仓库之基本操作
创建本地仓库:cd到仓库的目录下,然后输入 git init,即可创建一个本地代码仓库。我们想删除代码仓库只需使用rm -rf 仓库名 命令,把这个文件夹删掉即可。
将文件添加到本地暂存区(原理图中的index):add命令
git add 文件名 不需双引号,文件需已存在,文件名应带上后缀
git add -u 一次性添加所有被删除和修改的文件到暂存区,方便快捷
git add -A 一次性添加全部文件,包括untracked文件
git add -p filename 对指定文件的部分修改:有时候一口气写了很多东西,但是这些东西应当分开提交,用这个命令进行交互式处理,选择需要的部分进行提交,详见参考链接。其实 VSCode自带了这个功能,只是有时候它显示不出来,就只能手动敲git命令来完成了。
git ls-files 列出已被add的文件(已被commit进的也算)
将某个文件剔除出ls-files列表,但不删除该文件本身:
git rm --cached xx.py
git -C path command可以在指定的目录path下运行git命令,相当于cd path && git command,做自动化脚本的时候比较方便

添加与提交修改

提交修改:commit命令,将已add的东西全部commit进本地代码库
基本格式:git commit
提交时可以给本次提交加上一些声明:git commit -m “声明内容”
声明内容可以在以后的某一天想看的时候用git log命令调出来看,git log返回的内容包括版本号(那个长长的字符串),提交人的姓名和邮箱,提交时间,修改的内容,也即声明内容
git commit --amend可以修改最近一次commit的描述,参考资料其他参考资料

做了一段时间工作的时候,想查看我们做了些什么改动:git diff命令,查看现有文件与暂存区中文件的差别(而不是与commit了的文件做比较),命令返回的文本中,红色-对应的是被删除了部分,绿色+对应的是新添加的部分。
git diff branch1 branch2 --stat //显示出所有有差异的文件列表
git diff branch1 branch2 文件名(带路径) //显示指定文件的详细差异
git diff branch1 branch2 //显示出所有有差异的文件的详细差异
撤销(文档写错一不小心按了Ctrl+S):
文档尚未add的情况:可以先用git diff看一下改了哪些东西,可以手动回去改回来,也可以直接git checkout 文件名,就能一键把文档恢复成前一个add的版本啦!
如果一不小心add了:checkout无效,要先取消添加才能撤回提交:
git reset HEAD 文件名
git checkout 文件名
再一不小心commit了:只能回退版本了:
git reset --hard HEAD
--hard 参数表示放弃所有的改动并恢复文件至某个版本
git reset详解
在Git中,用HEAD代表当前版本,上一个版本就是HEAD^,再上一个版本就是HEAD^^,依此类推。
HEAD也可以替换成版本号,版本号通过git log获取。实际使用时,一般版本号只输入前7位就不会出现重复的情况了,可以不输入那么多位。
回退后,你突然后悔了,想回退回新的那个版本,可是遗憾的是,你键入git log却发现没有了最新的那个版本号:还有办法,git reflog命令可以看到之前输入的每一条指令,以及执行指令时对应的版本号(前7位)
还原某个特定的文件到之前的版本:git checkout 版本号 文件名
git checkout master 回到主分支上
git revert用于逆转之前的提交,比如A-B,现在想要在A的基础上改,但是B里面又有一些有用的东西,不能git reset,这时可以考虑用git revert HEAD,它会追加一个commit,将代码还原到A的状态,git revert HEAD~2就是还原到A的上一个提交的状态,而逆转之前排在前面的B等依然存留在commit中,不会被移除。如果是要把一个很久之前或者由于某种原因曾经出现在commit tree但现在不在的提交还原出来,用revert就很费劲,它好像一次只能回退一步,总之没有办法一步到位,对于这种需求还是自己git diff然后手动更改代码然后提交好一点,或者是git checkout回去把整个文件的内容拷回来。参考链接
新命令switch和restore

有一些目录或者文件我们不想添加到仓库中,比如lib,gen,bin以及我们自己的data目录等等,我们可以在代码仓库的根目录下创建一个名为.gitignore的文件,然后把要忽略的文件名或文件格式写上去就可以了!.gitignore详解

有时候刚提交完一个版本,回头就发现代码有问题,这个版本是个不可用的版本,我们想要把它删掉,要怎么做呢:要使用rebase命令
git rebase -i命令除了可以删掉版本,也可以合并两个版本,比如合并commit中的内容,其他功能还没有探索过
其实rebase原本的用法是在合并分支上,跟merge类似都能合并两个不同分支的修改,但具体行为不同,详见参考链接1参考链接2

获取git log的缩略信息,保存在同步日志文件中(参考链接1参考链接2参考链接3):

# commit id,branch和log信息,-1表示最近的1次提交,也就是刚拉下来的提交,
# --oneline表示把所有信息包括原本多行的日志都折到一行里显示
git log -1 --oneline
# author信息,sed -n获取某些行,2p表示获取第2行
git log -1 | sed -n '2p'
分支

创建分支:git branch 分支名
分支间的切换:git checkout 分支名,切换后文件的内容即被改变
git checkout -b dev 创建分支dev并切换到dev,此语句相当于 git branch devgit checkout dev
查看所有分支:git branch 当前分支的前面有*标示
合并分支:git merge 分支名 将某一分支A合并到当前分支上,实际上是当前分支点移到了A点(注:如果在不同的分支中都修改了同一个文件的同一部分,Git 就无法干净地把两者合到一起)。
出现冲突:两个分支由于工作内容不同,或者是两个开发者后来改了什么东西,文件内容出现冲突是很正常的事情。在出现冲突时,冲突的文件将会被写入类似下面的信息:
在这里插入图片描述
该信息指出,当前分支(HEAD)在与dev-object-jw2005的分支合并的过程中出现了冲突,箭头往左表示冲突开始,也即“<<<HEAD”上面的部分没有冲突;箭头往右表示冲突结束,也即“>>>dev…”下面的部分没有冲突;中间就是冲突的部分了,从<<到====的部分是上分支的内容,从>>到====的部分是下分支的内容。可以看到,这次冲突中,HEAD的内容为空(或者说是两个空行),dev分支则是多了四行语句,那这次只需要将四行代码放进来,即可解决这个冲突。

删除分支:git branch -d 分支名
删除本地分支后需要同步到远程,删除远程的分支:git push origin --delete 分支名参考链接

分支合并时出现merge conflict等可以看这个:Git-分支-分支的新建与合并

远程仓库的搭建与同步

新建立一个远程仓库之后,首先要把本地与这个仓库联系起来:
git remote add origin address, address指远程仓库的ssh地址,这样就把该仓库与我们本地的origin标号联系了起来,可以认为origin就是那个远程仓库的引用,以后push和pull就不需要输入一长串的ssh地址了,用origin替代就可以了。
git remote -v命令显示当前已添加的远程仓库符号以及对应的地址

本地仓库与远程仓库同步
从远程仓库下载:git clone url_repository aaa,注意到clone下来的东西是在当前目录下新建了一个文件夹,名字为aaa(一般不需要加aaa,此时文件夹名称将为github项目的名称)。新版本Git中clone只会下载一个分支,其他分支要自己手动下载:git fetch origin dev(dev为远程仓库的分支名)。git clone -b 远程仓库分支名git clone commit版本号 [本地文件夹名],相当于clone下来之后又执行了一次checkout到我们所指定的分支或版本。clone是指本地仓库没有这个东西时,“无中生有”,复制下来一套完整的文件。之后就不再进行clone了(也无法再进行clone了,不能clone到非空的目录),后面要用pull来更新文件的版本。
从远程仓库同步到本地:git pull origin master
从本地仓库同步到远程仓库:git push origin master
从远程仓库clone下来之后,git branch -a会看到remote/开头的分支,对应于远程仓库上的分支。我们可以checkout到那上面,它只是远程仓库在本地的一个副本,修改它并不会修改远程仓库上的内容。
当存在多个分支的时候,我们就需要指定从哪个分支同步到哪个分支了:
git push origin 本地分支名:远程分支名,不需要两个名字相同,想推到哪推到哪
pull也是一样,git pull origin 远程分支名:本地分支名,需要注意的是如果此前本地没有这个分支,那么第一次拉取远程分支应该使用git checkout -b 本地分支名 origin/远程分支名参考链接),否则可能会出现与当前分支的merge conflict,原因尚且未知。
分离(detached)头:由于远程分支毕竟不是本地的分支,逻辑上我们只能修改本地的分支,因此如果checkout到一个远程分支上,这个HEAD将被认为是“分离的”,它可以进行跟普通HEAD同样的操作,也能push和pull,但只要checkout到了别的分支,之前在分离头上所做的工作就会全部丢失。那如果一不小心,没有先git stash,就在分离头上把做了一段时间的工作给提交了该怎么办呢?只需在当前分离头位置建立一个分支:git branch tmp,该操作会得到一个正常的分支,内容与分离头一致;然后切回原本的分支:git checkout master,然后汇合:git merge tmp,此时工作就汇入原来的分支啦!最后把临时分支删除:git branch -d tmp,大家就当无事发生过~(参考链接
fetch的功能比pull稍弱,只更新远程仓库在本地上的副本,不对本地分支的内容进行更改,而pull则是把本地分支的内容也做了更新。pull相当于fetch+merge

有时候远程仓库有小更新,而本地分支已经更新了很多个提交,此时两个仓库差别比较大,直接merge可能不太好,这时可以考虑先fetch下来,然后一个一个文件检查:git checkout origin/main xx.py,该命令会用远程分支中的文件覆盖当前的文件并add,我们只需要取消add,就能在VSCode里看到文件的变化,然后修改,改完再自己commit一次,最后再merge的时候出现的conflict或者modify就会少掉很多,不容易漏掉啥被覆盖了都不知道的文件。参考链接

给远程仓库建立一个新分支:先在本地git branch dev生成一个新分支,把东西commit进这个分支的仓库之后,再git push origin dev就可以了。
在进行pull和push的时候需要指定本地分支与远程分支的映射关系,否则会报no tracking information错误或者has no upstream branch错误,详情请看:https://blog.csdn.net/tterminator/article/details/78108550
git branch -u aaa将当前本地分支与远程分支aaa建立映射关系(此时无需指定URL)
git branch --unset-upstream 解除当前本地分支与远程分支的映射关系
Git之使用GitHub搭建远程仓库
git checkout -b 本地分支名 origin/远程分支名从远程拉取一个本地没有的分支

标签

commit id可读性比较差,我们可以通过给某个commit打标签的方式,来快捷回到之前的某一个提交,参考链接
添加标签:

# 添加本地tag
git tag -a <tag_name> -m "comment"
# 推送到远程
git push --tags

删除标签:

# 删除本地tag
git tag -d <tag_name>
# 推送到远程
git push origin :refs/tags/<tag_name>

与他人一起在github上做项目:如何加入别人的Git项目——Git Fork指南

Github忘记密码怎么办

进阶: 在自己的服务器上搭建一个git仓库, 起到github的功能

本地上的.git/目录我们称为项目仓库.
git init --bare aa.git 生成一个./aa.git/目录. 如果直接git init aa.git, 则会生成./aa.git/.git目录, 不满足我们的要求.
后续操作详见: 如何在服务器上搭建Git版本仓库(干货)
gitea可以用来自建git服务器:安装方法

进阶:清理git仓库中无用的大文件

参考链接,好文
该说的都在这篇博客里头了,没啥要说的了,哈哈。
我自己使用的时候,clone本地仓库之后.git仓库依旧没见小,但push上一个全新的空仓库时明显变小了,但是push -f到原来的仓库反而从1.0M变成了1.2M,很奇怪,最后采用新建仓库再推的方式,在保留commit log的情况下把1.0M删减到300+K。
上面的博客需要git push -f,这篇也是,但是下面这篇介绍的方法不需要git push -f,我自己还没有试过,仅做收录:BFG Repo-Cleaner

在Git仓库中添加其他Git仓库

git submodule add <url> <path> 其中,url为子模块的路径,path为该子模块存储的目录路径,参考链接
git submodule的使用
git submodule查看子模块信息:

 352d9xxx image_capture (heads/master)
+d486cxxx socket_python (heads/master)

编号表示父模块当前使用的子模块版本,这个版本与我们cd到子模块目录下git log最上一条版本id是一致的。要更改使用的子模块版本,就跟无submodule情形下一样,用checkout就可以了。
如果编号前面有个+,表示父项目使用的子模块版本相比之前发生了变动;如果编号前面有个-,说明子模块的代码还没有拉下来。
把子模块的代码拉下来:先git submodule init,再git submodule update就可以了,参考链接
git submodule 相关操作解析
修改子模块之后只对子模块的版本库产生影响,对父项目的版本库不会产生任何影响,所以子模块可以安心开发提交做啥都行,只要父项目没有更改使用的子模块commit id,对于父项目而言子模块就是一直没有变化的,这就很好地隔离了上下层模块,个人觉得非常好用。如果父项目需要用到最新的子模块代码,我们需要更新父项目中submodule commit id,默认的我们使用git status就可以看到父项目中submodule commit id已经改变了,我们只需要再次提交就可以了。
在子模块目录下git pull跟在父级目录git submodule update是等价的,都是把子模块更新到最新版本。如果子模块里还有子模块,这个操作只会更新子模块,因此还需要手动在子模块目录下git submodule update才能更新子模块的子模块。在子模块太多的时候,一个一个手动更新令人抓狂,因此我们要用更便捷的方法来完成这件事情:git submodule update --remote 子模块名,不写子模块名则会更新所有子模块,将它们更新到远程分支对应的版本(注意:如果远程分支更旧,也依然会被checkout到远程分支的版本,也就是版本回退,此时子模块会位于detach head上,但输出信息没有什么不同,所以一不留神就挖了个坑不知道什么时候会跳进去,要多留意)。这里说的远程分支默认是master,如果想要使用别的分支,则需要手动设定一下:git config -f .gitmodules submodule.子模块名.branch 分支名。此时如果子模块还有子模块,它还是不会更新,需要增加一个--recursive参数。所以最后的完整子模块更新命令应该是:git submodule update --remote --recursive参考链接
(后记:上面的命令都有各种不适合我的问题,最后选择了git submodule foreach git pull origin --recurse-submodules
foreach这个命令挺好用的,比如git submodule foreach git branch可以直接查看所有子模块的分支,避免误操作导致在detach head上提交还浑然不知。

通常我们提交带子模块的仓库时,都是有满足父模块和子模块的版本对应关系的,在回退版本的时候,单纯git checkout 版本id只会回退父模块,而不会回退子模块,这使得版本不再匹配。使用git checkout --recurse-submodules 版本id来让子模块也同时checkout回与当时的父模块版本相匹配的版本。git reset --hard也是类似的道理,需要加上--recurse-submodules

删除子模块(参考链接)

git submodule deinit {MOD_NAME}
# 删除.gitmodules中记录的模块信息(--cached选项清除.git/modules中的缓存)
git rm --cached {MOD_NAME}

修改子模块的url并更新到远程(参考链接):
.git/config文件直接管控url等信息,但这个文件是支持用户自定义的,它并不参与版本控制,因此我们对这个文件的更改,是没有办法推送到远程仓库的。
参与版本控制的子模块相关的配置存放在.gitmodules分支,对这个文件的修改可以推送到远程仓库;但修改这个文件并不会改变当前git的配置,因此需要以某种方式让这个更改应用到.git/config中。

# 修改子模块的url
vim .gitmodules
修改 .gitmodules 文件中对应模块的url属性;
使用 git submodule sync 命令,将新的URL更新到文件.git/config;
再使用命令初始化子模块:git submodule init
最后使用命令更新子模块:git submodule update

有时候在手工核对子模块的时候,文件里头只有一个commit id,没有分支,找不到这个提交,此时可以通过git branch -r --contains commit_id找到包含这个id的所有分支(参考链接)。

多人合作时带子模块的仓库有时候会被人无意中错改了子模块的版本,为了找出子模块的变化历程,需要使用这条命令:git log --patch master -- path/to/submodule参考链接

Git的妙用

git grep someword folder_name 在某个目录下的所有文件内容中搜索某个关键字,-n可以返回所在行数(VsCode的Ctrl+Shift+F搜索包含了这个功能)
git stash 可以保存当前的工作状态。在debug的时候,如果写好一段代码,测试完之后想做比较大的修改,但当前的代码又没有完整到可以单独作为一个版本提交,在之前我是branch之后在debug分支提交一份,后面如果要回到大改前的版本可以用git reset --hard。而现在有了git stash,我有了另外一种更方便的选择:在第一次修改结束后,运行git stash push(或者直接git stash,二者等价),当前基于上一个版本有改动的部分会被保存起来,同时将这部分改动抹去,当前代码恢复到HEAD版本。恢复到上一个的版本的好处之一是这时我们可以checkout了,不会被提示有未保存的改动。保存之后,git stash list可以看到我们目前的存档点的概览,git stash diff可以查看保存的改动具体是什么,git stash apply可以把当前代码恢复到存档点。如果有多个存档,具体恢复哪一个存档通过git stash apply stash@{n}中的n来决定,n=0就是最近一个存档点,n=1的倒数第二近,以此类推,跟HEAD~2是一样的道理。git stash pop会在回档之后顺手把存档点给删掉,而apply则不会,apply可以搭配git stash drop手动删档。git stash clear是删除所有存档。createrestore应该是用不到了,略过。
git stash save "xx" -u 把untracked的文件也一并推进堆栈。
git stash list --date=local显示stash的时间点,方便我们回溯。
参考文档
写到这里突然想到,其实还可以直接commit第一个版本的代码,在大改结束之后commit一个稳定的版本,然后git rebase HEAD~2,也同样可以实现我们的要求。

git blame可以查看单个文件的修改历史,可以清晰地看到一个文件在n多个版本里面是什么时候做了改动,例如我发现版本A跟版本B的xx.py有一个地方不一样,但是他们中间隔着几十个版本,我不想一个一个去翻他们的git diff,那用blame就能只看这一个文件在那个不一样的地方,是在哪个版本被修改过的,非常舒服。gitea里有这个功能,github不知道有没有。(VsCode安装gitlen插件后可以看blame,而且在file history里可以看得更清楚)
git blame用法
有时候我们的提交里头有很多黑历史,不想给别人看,只想把最好的一面展现出来,把最新的几个提交放到远程仓库,这时候可以曲线救国:首先git checkout到要放远程仓库的提交的最早一个提交,把代码拷到新目录A,删掉.git/,重新git init, git add, git commit,然后主仓库checkout到下一个提交,整个目录拷到新目录B,用A的.git/覆盖掉B的.git/,然后在B里头git add, git commit,接着主仓库checkout到下一个提交,整个目录拷到新目录A,用B的.git/覆盖掉A的.git/,然后在A里头git add, git commit,如此往复,直到更新完毕。

提升使用体验

git操作免密码
gitea目前由于某种原因,我一直没有办法用上ssh-key,只能http,http每次都要输入账号密码,很烦人,有没有什么办法让它自己搞定?
方法一:修改.git/config里头的url:

# 这里仅节选需要修改的键值
[remote "origin"]
    # 原始url
    # url = http://120.24.55.96:3000/repository_path
    # 添加了账号密码的url
    url = http://username:password@120.24.55.96:3000/repository_path
    fetch = +refs/heads/*:refs/remotes/origin/*

直接把账号密码写进来,一劳永逸。缺点是每个仓库都要这么搞一遍,如果仓库比较多,也还是很烦人,而且密码不能带@

方法二:git config --global credential.helper store,启动credential helper的记录模式,只需输入一次账号密码,后续就都是自动输入了,就像chrome的自动填充一样,方便快捷,参考链接
与方法一相比的局限性是,如果是在dockerfile里对仓库进行处理,方法一可以配置好账号密码,方法二中间需要人工干预输入账号密码。

git config --global --unset credential.helper  ## 取消自动记住密码
git config --global credential.helper 'cache --timeout=600'  ## 缓存10分钟

不同操作系统间的换行符冲突
Windows是\r\n,Linux是\n,两者混合提交会导致git频繁对换行符的变更对大量文件做变更提交,这显然是我们不愿看到的,因此我们应当做一些设置(参考链接):

# 禁止提交混合\n和\r\n的文件
git config --global core.safecrlf true
# 自动把\r\n换为\n
git config --global core.autocrlf input

同时要在VSCode里头把files:eol的值设为\n参考链接)。

其他

http方式和ssh方式的区别

疑难解答

Git 使用中的常见问题及解决方案

  • Q:git pull时出现错误refusing to merge unrelated histories,无法pull
    A:这是因为远程仓库origin上的分支master和本地分支master被Git认为是不同的仓库,所以不能直接合并。要合并两个不同的项目,git需要添加一句代码,在git pull,这句代码是在git 2.9.2版本发生的,最新的版本需要添加–allow-unrelated-histories。假如我们的源是origin,分支是master,那么我们 需要这样写git pull origin master --allow-unrelated-histories需要知道,我们的源可以是本地的路径
  • Q:git clone时报错server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
    A:解决方案
  • Q: git diff中文乱码
    A: export LESSCHARSET=utf-8参考链接
  • Q: git pull时出现Failed to connect to 127.0.0.1 port 7890: Connection refused报错
    A: 不知道什么时候被加了代理,git config --global http.proxy && git config --global https.proxy看看是不是真有代理,如果有就git config --global --unset http.proxy && git config --global --unset https.proxy取消掉,参考链接
  • Q: git clone没问题,要push回去的时候报错"fatal: unable to access ‘xxx’: The requested URL returned error: 403"
    A: 可能是修改过Gitea的密码,本地还用的旧的,就通不过,
    git config --local --unset credential.helper && git config --global --unset credential.helper && git config --system --unset credential.helper清除记住的账号密码,重新push,提示输入账号密码,问题解决(参考链接)。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值