Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。我在工作中使用的版本控制软件也是Git,因此分享一下记录的一些Git常用命令。
Git常用命令
查询git版本
sh
git -v
设置用户名
sh
git config --global user.name <用户名> // 全局配置,所有仓库生效
--system user.name <用户名> // 系统配置,对所有用户生效
(省略(Local))user.name <用户名> // 本地配置,只对本地仓库有效,即只对当前的git仓库有效
设置邮箱地址
sh
git config --global user.email <email address>
保存用户名和密码
sh
git config --global credential.helper store
查看配置(显示全部配置信息)
sh
git config -l
查看system配置
sh
git config --system --list
查看global配置
sh
git config --global --list
创建仓库
方法1: 初始化新的仓库
sh
git init
git init <目录名> # 表示在该目录下初始化仓库
方法2: 从远程克隆一个仓库
sh
git clone <仓库地址>
将添加到暂存区的文件取消暂存(即撤销git add操作)
sh
git rm --cached <file>
提交文件到本地仓库(只会提交暂存区重点文件)
sh
git commit -m '<提交的备注>' # -m: 用来添加提交信息,如果不加该参数,则会进入交互式状态,使用vim进行编辑
git commit -am # 用于提交跟踪过的文件,并附带一条提交信息,相当于"git add <file> + git commit -m <command>"
修改最后一次提交的记录
sh
git commit --amend # 可以添加、删除或修改最后一次提交的内容
需要先将要修改的文件添加到暂存区,然后再执行该命令,之后系统会打开一个编辑器让你修改提交信息,如果只是添加文件而不需要修改提交信息,可以使用git commit --amend --no-edit
。
查看仓库提交历史记录
sh
git log
git log --oneline # 查看简洁的提交记录
查看状态
sh
git status
git status -s
?? temp/ # 该行 是查询结果,第一个"?"表示暂存区的状态,第二个"?"表示工作区的状态
回退版本,退回到某个提交的状态
sh
git reset HEAD^ # 回退到上一个版本(对应 git reset --mixed HEAD^)
# git reset的三种模式
git reset --soft # 回退到某个版本并保留工作区和暂存区的所有修改内容
git reset --hard # 回退到某个版本并丢弃工作区和暂存区的所有修改内容
git reset --mixed # 回退到某个版本并只保留工作区的修改内容,丢弃暂存区的修改内容(默认)
误操作了git reset怎么办?
- 查看操作的历史记录:git reflog
- 找到误操作之前的版本号
- 再使用git reset 回退到这个版本
查看暂存区的文件
sh
git ls-files # 列出当前git仓库中跟踪的文件
对比差异
sh
git diff # 默认比较的是工作区和暂存区的差异内容
git diff HEAD # 比较工作区和版本库之间的差异
git diff --cached # 比较暂存区和版本库之间的差异
git diff <版本ID1> <版本ID2> # 比较两个版本之间的差异
git diff HEAD HEAD~ # 比较当前版本和上一个版本的差异,HEAD~ / HEAD^ 均表示上一个版本
git diff HEAD~3 HEAD xxx.txt # 查看具体某个文件的差异(提交前的三个版本和当前版本比较)
git diff <branch_name> <branch_name> # 比较两个分支之间的差异
说明:
git diff 后面不加任何参数时,显示发生更改的文件及更改的详细信息:
- 第一行会提示哪些文件发生了变更;
- 第二行:git会将文件的内容使用哈希算法生成一个40位的哈希值,第二行只显示哈希值的前七位,后面的6位数字表示文件的权限
- 再往下就是修改的内容,红色表示删除的内容,绿色表示添加的内容。
如下图所示:
删除文件
4种方法:
- 直接删除后提交
sh
rm <fileName> # 该命令是Linux命令,删除的是本地仓库(工作区)的文件
git add <fileName> # 删除暂存区中的该文件
- 将工作区和暂存区指定的文件都删除
sh
git rm <filename>
- 把文件从暂存区删除,但保留在当前工作区中
sh
git rm --cached <fileName>
- 递归删除某个目录下的所有子目录和文件
sh
git rm -r *
注意事项:删除后不要忘记提交,否则版本库中仍存在该文件
查看所有分支列表
sh
git branch
查看远程分支列表
sh
git branch -r
创建新分支
sh
git branch <分支名>
切换到不同的分支下
sh
git checkout <分支名>
说明:该命令除了用于切换分支外,还可以用来恢复文件或目录到之前的某一个状态。如果分支名和文件名相同时会出现歧义,git checkout命令会默认切换分支,而不是恢复文件。
为避免这种歧义,git官方在2.23版本开始提供一个新命令:
sh
git switch # 专门用来切换分支,推荐使用
恢复文件/目录到之前的某一个状态的用法
方法1: 使用git checkout
命令
- 恢复文件到最近一次的提交状态
sh
git checkout -- <文件名>
例如,要恢复名为demo.txt
的文件到最近一次提交的状态,可以使用:
sh
git checkout -- demo.txt
- 恢复目录到最近一次的提交状态
sh
git checkout -- <目录名>/*
例如,要恢复名为project
的目录到最近一次提交的状态,可以使用:
sh
git checkout -- project/*
方法2: 使用 git restore
命令(Git 2.23及以上版本)
从Git 2.23版本开始,git restore
命令被引入作为git checkout
的替代品。它提供了一种更明确的方式来恢复文件。
- 恢复文件:
sh
git restore <文件名>
例如,要恢复demo.txt
文件:
sh
git restore demo.txt
- 恢复指定目录中的所有文件
xml
git restore <目录名>/*
例如,要恢复project
目录中的所有文件:
git restore project/*
方法3: 使用 git reset
命令
如果要恢复到某个特定的提交状态(不仅仅是最近的提交),可以使用git reset
命令,这会将HEAD指向那个提交,并且可以有选择地修改工作目录和暂存区。
- 恢复到特定提交:
(1)找到想要回退到的提交的哈希值(commit hash)(可以使用git log
来查看提交历史)。
(2)使用以下命令:
sh
git reset --hard <commit-hash>
这会重置HEAD到指定的提交,并且工作目录和暂存区也会匹配那个提交的状态。注意,--hard
选项会丢失当前工作目录中的所有更改。
如果只是想查看更改而不实际重置,可以先试验不带--hard
的命令:
perl
git reset <commit-hash>
这会重置HEAD到指定的提交,但不会改变工作目录和暂存区的内容。可以先查看更改(使用git diff
),确认无误后再使用--hard
选项。
⚠️ 说明:
- 使用
--hard
选项时要小心,因为它会丢失当前的工作目录更改。确保自己已经备份了重要数据或者确认不需要这些的更改了。 - 如果不确定具体的提交哈希,可以先运行
git log
查看提交历史。 - 在执行这些操作之前,最好先运行
git status
和git diff
来了解当前的更改状态。
创建新分支并切换到该分支
sh
git checkout -b <分支名>
合并分支 & 变基分支
sh
git merge <分支名> # 合并分支,merge后面的分支名是要被合并的分支,当前所在的分支是合并后的目标分支
git rebase <分支名> # 变基分支,在执行rebase中,git会先找到当前分支和目标分支的共同祖先,再把当前分支上"从共同祖先到最新提交记录的所有提交"都移动到目标分支的最新提交后面,其中目标分支为rebase后面跟的分支名
说明:
- 两个分支没有修改同一个文件的同一处位置:git自动合并
- 两个分支修改了同一个文件的同一处位置:产生冲突
解决方法:
- 手动修改冲突文件,合并冲突内容
- 添加暂存区:
git add file
- 提交修改:
git commit -m 'message'
- 当不想继续执行合并操作时中止合并过程:
git merge --abort
# 中断合并
merge和rebase的异同:
markdown
- merge
优点:不破坏原分支的提交记录,所有提交记录和合并历史都会保留下来,方便后续的查看和回滚。
缺点:会产生额外的提交记录和两条分支线的合并,使提交记录变复杂。
- rebase
优点:不需要新增额外的提交记录到目标分支,形成一个线性的提交历史记录,直观且干净。
缺点:会改变提交历史,需要避免在一个共享分支上进行rebase操作。
查看分支图
sh
git log --graph --oneline --decorate --all
将以上命令定义一个别名使用:
sh
alias graph="git log --graph --oneline --decorate --all"
删除分支
sh
git branch -d <分支名> # -d: 删除已经完成合并的分支
git branch -D <分支名> # -D: 强制删除未合并的分支
git push origin --delete <分支名> # 删除远程分支
Git的一些其他知识点补充
git的工作区域
- 工作区(Working Directory):工作目录/本地工作目录(即.git所在的目录)
- 暂存区(Staging Area/Index):临时存储区域,保存即将提交到git仓库等修改内容(即.git/index)
- 本地仓库(Local Repository):git init的仓库目录(即.git/objects)
三者关系:开发完后,工作区的内容 git add
-> 暂存区 git commit
-> 本地仓库
git的文件状态
- 未跟踪(Untracked):新创建的还没有被git管理的文件(文件显示为红色),不参与版本控制。可以通过
git add
命令将该状态变为Staged - 未修改(Unmodified):文件已经入库,已经被git管理,但文件内容没有发生变化,即版本库中的文件快照内容和文件夹中完全一致。如果该类型文件被修改,则变成Modified,如果使用
git rm
从版本库移除,则变成Untacked文件 - 已修改(Modified):已经修改过的文件,但还没有添加到暂存区中。通过git add可进入Staged状态;使用
git checkout
切换分支则丢弃修改的内容,从而返回到Unmodified状态,其中git checkout命令从库中取出文件并覆盖当前修改 - 已暂存(Staged):已修改后添加到暂存区的文件(文件显示为绿色)。执行
git commit
则将修改的内容同步到库中,此时库中的内容和本地文件又变为一致,文件为Unmodified状态。可以执行git reset HEAD <filename>
取消暂存,文件状态变为Modified
以上文件状态的关系:Untrack -> git add
-> Unmodified -> 修改文件 -> Modified -> git add
-> Staged
GitHub的使用和远程仓库操作
1. 创建/推送一个仓库
- 在命令行上创建一个新仓库:
sh
echo "# remote-repo" >> READ.md
git init
git add READ.md
git commit -m "first commit"
git branch -M master
git remote add origin [email protected]:<仓库地址>/<仓库名.git>
git push -u origin master
- 从命令行推送一个已存在的仓库:
sh
git remote add <远程仓库别名> [email protected]:<仓库目录>/<仓库名.git>
git branch -M master # 指定分支的名称为master
git push -u origin master # 把本地的master分支和远程origin仓库的master分支关联起来,其中,-u是upstream的缩写,以上全写:git push -u origin master:master(本地仓库的分支和远程仓库的相同的话,可以略写本地仓库的分支名)
git remote -v # 查看当前仓库所对应的远程仓库的别名和地址
origin [email protected]:<仓库目录>/<仓库名.git> # origin:本地仓库对应的远程仓库别名
2. 使用ssh方式登陆需要配置ssh密钥
- 进入~/.ssh目录,执行ssh-key generate -t RSA -b 4096(生成SSH密钥, -t RSA: 指定协议为RSA; -b 4096: 指定生成的大小为4096)
- 回车后提示需要输入密钥的文件名称,eg.test(不指定的话,默认在.ssh目录下生成名为id_rsa的密钥文件)
- 输入密钥文件名称回车后,生成一个test(私钥文件)和一个test.pub(公钥文件,可上传至GitHub)
- 如果密钥文件是指定的名字,则需要再增加一个步骤:添加一个config文件,如下所示(表示在访问github.com时,指定使用test密钥文件)
sh
# github
Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/test
3. 本地仓库和远程仓库保持同步(pull 和 push命令)
sh
git push <remote> <branch>
git pull <远程仓库名> <远程分支名>:<本地分支名> # 作用:把远程仓库的指定分支拉取到本地分支再进行合并
git pull # 以上命令的简写版,表示拉取仓库别名为origin的master分支(高版本的是main)
默认分支名从master改为main的历史原因:由于master这个词带有种族歧视的含义,因此在后来的git高版本中更名为main。
注意点:执行完git pull之后,git会自动执行一次合并操作,如果远程仓库和本地仓库的修改内容没有冲突,则可以合并成功,否则由于冲突导致失败,需要手动解决冲突。
从远程仓库获取内容还可以使用fetch命令(只获取远程仓库的修改,不自动合并到本地仓库中,需要手动合并)
.gitignore文件的介绍
作用:可以忽略掉一些不应该加入到版本库中的文件。
生效的前提:文件不能是已经被添加到版本库中的文件。
应该忽略哪些文件:
- 系统/软件自动生成的文件
- 编译产生的中间文件和结果文件(eg.Java编译生成的.class文件)
- 运行时生成的日志文件、缓存文件、临时文件
- 涉及身份、密码、口令、密钥等敏感信息文件
.gitignore文件的匹配机制:
- 从上到下逐行匹配,每一行表示一个忽略模式
- 空行或者以
#
开头的行都会被git忽略(一般空行用于可读性的分隔,#
一般用作注释) - 使用标准的Blog模式匹配,例如:
text
* 通配任意个字符
? 匹配单个字符
[] 表示匹配列表中的单个字符,例如[abc]表示a/b/c
- ** 表示匹配任意的中间目录
-
\] 可以使用短中线连接,例如:\[0-9\]表示任意一位数字,\[a-z\]表示任意一位小写字母
举例:
text
*.a # 忽略所有.a文件
!lib.a # 在以上基础上,即使忽略了所有的.a文件,但跟踪所有的lib.a文件
/TODO # 只忽略当前目录下的TODO文件,不忽略subdir/TODO
build/ # 忽略任何目录下名为build的文件夹
doc/*.txt # 会忽略doc/note.txt,但不会忽略doc/server/note.txt
doc/**/*.pdf # 忽略doc/目录及其所有子目录下的.pdf文件
良好的习惯和规范
分支命名(推荐使用有意义的描述性名称命名分支)
- 版本发布分支/Tag示例:v1.0.0
- 功能分支示例:feature-login-page
- 修复分支示例:hotfix-#issueid-desc
分支管理:
- 定期合并已成功验证的分支,及时删除已经合并的分支
- 保持合适的分支数量
- 为分支设置合适的管理权限
Git和SVN最主要区别
- SVN是集中式版本控制系统(必须联网才能工作,对网络带宽要求较高),版本库集中放在中央服务器中,工作时使用自己的电脑,需要先从中央服务器得到最新版本再工作,完成工作后,需要把代码推送到中央服务器。
- Git是分布式版本控制系统,没有中央服务器,每个人的电脑就是一个完整的版本库,工作时不需要联网(因为版本都在自己电脑上)。协同的方法示例:自己在电脑上改了fileA,其他人也在电脑上改了fileA,此时只需要把各自的修改推送给对方,就可以互相看到对方的修改了。
Git是目前世界上最先进的分布式版本控制系统。
【扩展】ls -altr
sh
ls -altr
# -a: 展示目录中的所有文件,包括隐藏文件
# -l: 展示每个文件的详细信息(文件权限、链接数、所有者名称和组所有者、文件大小、最后修改的时间和文件/目录名称等)
# -t: 对最后修改日期和时间进行排序
# -r: (和-t连用)基于日期和时间逆向排序