【git】使用教程、经验、手册
如果觉没有目录得看得难受,可以在这看:Pro git 2
速查
-
commit前一定要先 pull
-
如果commit后,push时发现有内容没pull,那么要进行merge,校验完改动后生成一次merge commit。不想进行merge则用下列办法取消本地的commit,pull以后再提交
git reset --soft <index>
-
如果准备用
commit --amend
修改上一次commit,那先别push,如果已经push,则用 push --force。 最好是别用commit --amend
,而是另起commit -
如果pull时,发现有人用过
commit --amend
,那么只能强制覆盖本地。
git reset --hard origin/master
一、git介绍
从个人角度上来说,git可以干嘛?
-
管理github等代码托管平台上的仓库,如果要做开源项目,必须要用到git,公司项目代码保存也是git或svn
-
下载github代码,还是觉得下zip方便...
-
版本控制,这才是git的正确用途,也是其初衷。比如你写代码,写着写着觉得写错了,想回到以前的某个状态,你再ctrl+Z得多麻烦,git就能帮你解决此问题
二、git安装&获取仓库
- 安装好git第一步,设置自己的身份信息。Win10右键文件资源管理器即可调出git bash,使用的linux shell习惯
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
# 换行问题:
git config --global core.autocrlf input
# 密码永久保存(明文):
git config --global credential.helper store
# 查看信息
git config --list
# 将git commit默认的文本编辑器从nano改为vim
git config --global core.editor vim
- 第二步,有代理就设置代理
# 设置
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy https://127.0.0.1:7890
# 查看
git config --global http.proxy
git config --global https.proxy
# 取消
git config --unset http.proxy
git config --unset https.proxy
- 获取git仓库
# 本地建立仓库: 进入一个文件夹
git init # 此文件夹就会被存放仓库
# 克隆(下载)远程仓库,会新建目录,后面可以再追加一个目录名用于重命名
git clone https://github.com/*/* # https
git clone git@github.com:*/* # ssh
git clone ssh://git@{ip|domain_name}:port/.../repositories.git
linux建议使用SSH,方便 push,而Windows使用https比较方便,后面<a href="#五、远程仓库">远程仓库时</a>会说
三、记录每次更新到仓库
3.1 追踪文件
git的文件有三种状态(位置): 已修改、暂存区、仓库
-
一个新文件或一个仓库中的文件被修改,那它就处于已修改状态;
-
使用
git add
命令可以追踪新文件、添加已修改文件到暂存区; -
最后使用
git commit -m “”
命令提交暂存区的文件到仓库,他默认需要一个注释,可以帮助你回忆你这次修改的内容,github也会显示此注释
3.2 查看文件状态
-
git status
status 能显示未追踪的文件、新追踪还没commit的文件、已修改的文件等, -s 选项能查看简况
-
git diff
diff 只能查看仓库中 被修改后 还未add的文件,它能查看文件内容做了哪些修改
3.3 提交文件到仓库
常规入库流程: add ——> commit -m
优点是能分批,把不同的文件打上不同的注释。shell中可以使用反斜杠 \ 来提交多行注释
如果不想多打一行 add *
命令,又没有新追踪文件,可以使用 commit -a -m ""
能提交所有被修改过的被追踪(仓库中)的文件,但是不推荐使用 -a,因为当你想要撤销操作的时候很麻烦
3.4 移除文件、取消跟踪、重命名和移动
删除文件的两种办法:
-
git rm
,再 commit -
如果直接在文件管理器删除了文件,又修改了被追踪的文件,最后 add ——> commit -m
你会发现,用 status 查看,删除文件的这一步还是没被提交,解决方法是:
文件资源管理器删除文件,再
commit -a -m ""
取消跟踪,即从仓库中删除记录,但仍将文件保留在硬盘上,后续记得加入.gitignore
git rm --cached
重命名和移动:
git mv
,不要使用文件资源管理器移动或重命名,git无法跟踪。git mv 其实是 rm + add
3.5 忽略追踪某些文件
如果不希望某些文件被追踪,可以新建 .gitignore
文件,用于存放忽略规则,当你将此文件追踪、提交后,规则就生效
当规则生效后,你不应当使用 add *
,它无法追踪任何文件。而应该使用 add .
忽略追踪规则的格式如下
-
所有空行或者以#开头的行都会被 Git 忽略。
-
可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
-
匹配模式可以以(/)开头防止递归。
-
匹配模式可以以(/)结尾指定目录。
-
要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。
glob即简化的正则表达式,让我们看看一些例子以方便理解:
# 忽略所有的 .a 文件
*.a
# 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO
# 忽略任何目录下名为 build 的文件夹
build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf
3.6 不再更新已追踪的文件
如果文件已经被commit,那么.gitignore
不会生效
应该使用
# 不再追踪某个文件
git update-index --skip-worktree <file>
# 恢复追踪某个文件
git update-index --no-skip-worktree <file>
这个只在本地生效
四、撤销操作、日志与版本回退
除非要回退某个文件到之前的某个版本,否则无需撤销操作,继续修改继续提交即可
4.1 回滚已修改文件
-
当你的某个文件处于工作区时,如果你修改后觉得不妥,想撤销操作,回到最近一次提交的版本
git checkout -- <file>
-
当你的文件被add到暂存区,想要将其退回到工作区时
git reset HEAD <file>
git restore --staged <file>
文档里说的是reset,但是status提示的是restore 。参考没必要将文件退回到工作区,不commit,继续修改和add即可。除非要checkout
4.2 commit后的回滚、追加、修改
-
查看git日志、提交历史
git log
commit后面一段很长的字符串为索引,(HEAD -> master)意思是HEAD是一个指针,他总指向最近一次提交用 -p 参数可以查看更仔细的日志,包含每次提交的改动细节
用 -3 等数字可以限制只输出最近几次提交的日志,q 退出
-
当你commit提交文件时遗漏了某些文件,或者注释有误
下列命令会把暂存区的都提交进上一次commit,而且还可以用i来修改注释,:wq保存退出,git log查看日志
git commit --amend
# 简写
git commit --am
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend -m "" # 会将暂存区文件提交,且两次提交只有一次记录,采用后者的注释
-
当你commit、push提交的文件有误,你想撤销commit操作时
-
简单安全的做法是修改后再次提交
-
如果要回到上一次提交的版本,可以撤销commit操作,但这可能存在风险
-
git revert HEAD
# HEAD 指向最近一次提交,你也可以通过log获取索引来撤销之前的某次commit
# 会生成一次 commit 日志
# :q 返回终端
git push origin master
-
git reset
撤销编辑、撤销提交首先我们要了解Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。如下图
此时本地仓库对应的是commit4,git reset 可以让本地仓库对应的指针变为commit3或是commit1等之前的版本,当然,也可以变为commit4之后的某个commit,如commit5
Git 的分支,其实本质上仅仅是指向提交对象的可变指针。参考
-
git reset --hard HEAD
将暂存区、工作区中的内容恢复成上一次提交的版本(快照),换句话说你没commit的内容都会被擦除。如果你刚commit,想撤销这次commit,那么可以把HEAD换成其它索引(危险!会丢失工作内容,建议采取下面的--soft) -
git reset --soft <index>
将HEAD指向指定的索引,即回退到某个版本,假设从4回到3,那么3往后的操作会被放到暂存区。参考
reset和分支有关,如果操作后无法push,可以使用 -f 强制push
-
五、远程仓库
创建仓库后,使用clone即可将仓库克隆到本地,后续还能使用pull
但是push需要身份验证
Windows会直接跳转网页要求认证,后续无需认证。使用https比较方便,而且Windows挂梯子扶墙也比较容易
Linux建议使用SSH链接,使用https链接每次都需要输入账号密码,或需要配置文件,而且https会被墙,相比之下ssh就能流畅访问
linux使用ssh连接github步骤
- 生成ssh密钥
ssh-keygen # 生成ssh key,默认即可,一直回车,最后会生成一对公钥和私钥文件
# -t 指定密钥类型,默认是 rsa ,可以省略。
# -C 设置注释文字,比如邮箱。
cat /root/.ssh/id_rsa.pub # 将公钥内容复制
-
github添加公钥
登录 GitHub,打开 Personal settings 页面,选择 SSH and GPG keys 选项添加
-
测试
ssh -T git@github.com
如果之前 clone 了 https 的链接,导致origin是https的,更改origin的方法是:
# 查看远程仓库地址
git remote -v
# 删除
git remote rm origin
# 添加
git remote add origin [url]
六、分支 & 远程分支
# 查看本地分支
git branch # -a 查看所有分支,包括远程
# 切换分支
git checkout branch_name
当前分支有修改未commit时无法切换分支,要么commit,要么checkout文件到之前提交的状态
# 新建分支,带有主分支(当前分支)的commit历史
git checkout -b new_branch_name
# 从远程仓库下拉新分支
git checkout -b new_branch_name origin/branch_name
# 新建空白分支
git checkout --orphan emptybranch
# 此时分支中有主分支的所有文件,且都在暂存区
# 此时分支还看不到,需要commit后才能看到新分支
git rm -rf .
echo 1 > readme.md
git add .
git commit -m 'new branch'
# 提交新分支到远程仓库,正常提交
git push origin new_branch_name
# 删除本地分支
git branch -d branch_name
# 删除远程分支
git push --delete origin branch_name
# 重命名本地分支
git branch -m branch_name new_branch_name
重命名远程分支:先删除,再推送一个新的上去
七、子模块-嵌套git项目
如果想在主项目中使用某个子项目,应该使用git submodule
如果你直接在主项目中克隆子项目,不删除子项目的.git文件夹的话,你没法把这个改动commit
如果你删除了子项目的.git文件夹,你没法再从上游更新子项目,当这个子项目不是你创建、维护的话,这很尴尬,子项目没法做版本控制
7.1 在项目中新建submodule
从远程拉取一个子项目到主项目
git submodule add <url>
git commit -m 'add submodule'
# git push origin master
对本地来说,运行git submodule add
,会自动把子项目的代码克隆到本地,并且在主项目创建.gitmodules
文件
在GitHub中,如果子模块来自于GitHub,那么点击可以跳转到相应项目,否则无法点击
7.2 更新submodule到最新版本
# 更新子模块
cd <submodule>
git pull
# 或者
git submodule update --remote <submodule>
7.3 clone一个含submodule的项目
git clone --recurse-submodules <url>
git clone <url>
# 这时submodule文件夹内为空
git submodule update --init
# 这时更新子模块
7.4 嵌套的设计理念
有时可能会觉得submodule的设计略有不便,比如我就想直接嵌套一个git项目,其中的代码就要被我copy在主项目中,即使是主项目的远程仓库也要有copy
我想更新子项目就更新,对方删库了也不影响我
但是抱歉,git并不能直接实现上述目的
目前能做到的是,fork一份要用到的目标子项目,再引用你fork的项目,这样即使对方删库,也不影响你,想更新就同步你的fork仓库
git嵌套的设计理念我认为就是单独管理主项目和子项目,主项目中引用子模块就只能引入和从远程更新本地代码,即使你修改了子项目的代码,也是推到子项目的远程仓库中
也就是说,主项目不关心子模块内部的改动,.gitmodules
中就记录一个名字和url,
而对于下列需求十分不友好,因为它就不是这样设计的:
在修改子模块代码并让主项目记录这一修改
7.5 嵌套与远程仓库
前面说过:当你push主项目时,子项目的代码不会被push,只会 push .gitmodules 文件,以及空的子模块文件夹
虽然 .gitmodules 文件中只有名字path和url,但实际上,git会记住你使用的submodule的版本
只要你未执行git submodule update --remote
,就未更新你submodule的版本,即使被引用的项目有更新版本
当你重新 clone 你的主项目时(submodule也被clone),其中的submodule还会停留在你之前设定的版本
八、搭建git服务器,创建私人远程仓库
如果觉得GitHub访问慢,或者过于私人代码不想放海外,不想放别人的仓库中,即使是GitHub私人仓库(Copilot能忽略License用所有公有代码来训练,谁知道会不会窃取私有仓库,虽然大多数人的代码也没太大价值😏)
那么想在家里和公司同步一些个人的项目,可以选择使用公网服务器搭建私人仓库
其实所谓的git服务器,并不需要像web服务器那样运行一个后台软件,它的构成是git客户端+ssh服务,本地通过ssh连接后,通过git命令来推拉远程仓库
如果想像GitHub那样有web可视化管理,可以选择安装gitlib、gogs、gitea,但是需要安装数据库之类的,而且web和ssh+git的设计理念不同,web是代码托管,你无需考虑服务器内部的样子,事实上你的代码在服务器内部并不是按原本的结构存储。而ssh+git服务器上的目录、文件和你本地是一模一样的。
这里示范前者,git客户端+ssh服务,无web,通过ssh连接,而不能像GitHub那样支持https
本人的服务器是Rocky Linux 8,首先服务器确保已经安装了git,使用git --version
检查
接着是配置git,参考官方文档
8.1 创建用户
groupadd git
useradd git -g git -d /home/git -s /usr/bin/git-shell
创建用户组git,创建用户git,指定用户组git,home目录为/home/git,指定shell为git-shell,这是一个受限的shell工具,方便将用户 git 的活动限制在与 Git 相关的范围内,提高安全性,但是就无法使用git用户ssh登录,以及su git
了。
8.2 配置ssh,开启RSA认证
vi /etc/ssh/sshd_config
确保其中有这几项
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
意思是允许通过密钥登录,本地电脑中必须生成密钥对,想要登录的远程主机的目标用户的home目录下的.ssh/authorized_keys文件中必须保存着本地ssh密钥对的公钥,每行保存一个,可供多个机器远程登录
8.3 本地生成ssh密钥,上传远程服务器
在本地机器上,如是Windows,呼出gitbash
cd ~
# 检查,如果没有id_rsa id_rsa.pub
ls -la .ssh
# 生成
ssh-keygen -t rsa -C "xxx@xxxmail.com"
# 回车回车回车
# 检查
ls -la .ssh
cat .ssh/id_rsa.pub
拷贝公钥内容,粘贴到远程服务器的/home/git/.ssh/authorized_keys
中去
8.4 创建远程仓库
目前不知道怎么在没有远程仓库的情况下,直接推本地仓库到远程服务器并进行创建仓库操作
目前只能先在远程中创建,然后再把本地的推上去
新远程仓库:
# 因为不能su git,所以得分几步
cd /home/git
# 创建名为xxx的仓库,Linux下目录名为xxx.git,clone到Windows文件夹名为xxx
git init --bare xxx.git
chown -R git:git !$
# 也可以用下面的,一句话,用git用户创建
su -s /bin/bash -c "git init --bare /home/git/xxx.git" git
可以整理成脚本链接到环境变量中
#!/bin/bash
su -s /bin/bash -c "git init --bare /home/git/$1" git
chmod +x gitinit && ln -s /root/gitinit /usr/bin/gitinit
本地克隆:
git clone git@192.168.1.66:java-mvel-ctf.git
# 如果不是22端口
git clone ssh://git@{ip|domain_name}:port/home/git/repositories.git
如果本地已有仓库,设置远程地址:git remote add origin git@192.168.3.60:git-test.git
九、标签 tag
一个版本可以有多个标签,GitHub上每个标签都能发布一个release
本地标签操作
# 在当前分支版本打标签
git tag <tagName>
# 列出所有标签
git tag -l
# 删除标签
git tag -d <tagName>
远程标签操作
pull默认不拉取标签
# 拉取标签
git pull --tags
# 推送标签
git pull <origin> <master> --tags <tagName>
# 删除远程标签
git push origin :v1.0
删除远程标签也可以用git push --delete origin <tagName>
,但是不推荐,因为会误删分支
十、换行符
Linux下换行符为LF,即\n
,Windows下为CRLC,即\r\n
推荐git仓库中都为LF,从使用上来说,Windows中\n不会报错和影响显示,但是Linux中\r\n会影响显示以及可能报错
可以配置git
git config --global core.autocrlf input
input提交时转换为LF,检出时不转换
还有选项true,提交时转换为LF,检出时转换为CRLF;false提交检出均不转换
也可以通过在仓库中用.gitattributes
文件配置
十一、取消缓存用户密码
当我们切换GitHub用户时,遇到这个问题
管理员身份运行
git config --global --unset credential.helper
即可,后续只需更改~/.gitconfig中的credential.helper即可