# Git 常用命令 ## git配置 ```bash # 显示当前的Git配置 git config --list # 显示全局配置 git config --list --global # 编辑Git配置文件 git config -e [--global] # 设置提交代码时的用户信息, 加global为全局,不加为当前项目 git config [--global] user.name "[name]" git config [--global] user.email "[email address]" # (可选)设置代理 账号验证和上传可能会有问题 git config --global url."https://ghproxy.com/https://github.com/".insteadOf "https://github.com/" git config --global protocol.https.allow always # 查看 cat ~/.gitconfig # 找到insteadOf # 取消 git config --global --unset url.https://github.com/.insteadof ``` ### 推荐配置 ```sh # git config --global core.pager '' git config --global merge.ff false git config --global pull.rebase true # win系统 git config --global core.autocrlf false // true: 检出时转成crlf, 提交时转成lf git config --global core.eol lf # mac linu unix 系统 git config --global core.autocrlf input // 提交时把 CRLF 转换成 LF,签出时不转换 git config --global core.safecrlf true // 不允许混用 ``` ### 生成ssh密钥 除了使用账号密码,还可以用`ssh密钥`进行上传下载 以`jihulab`为例: 1. ssh密钥存放路径: macos: `~/.ssh/` 2. 生成: ```bash # 添加-f参数是为了指定名称,方便区分不同的服务商 ssh-keygen -t ed25519 -C 'jqtmviyu@gmail.com' -f ~/.ssh/jihulab_ed25519 # 生成id_ed25519和id_ed25519.pub文件 # 使用 ed25519 算法而不是rsa算法, 是因为rsa算法在新版本的OpenSSH被默认移除 # -C 只是备注信息 ``` 3. 配置 ```bash vim ~/.ssh/config # 添加以下配置 ==== # jihulab Host jihulab.com HostName jihulab.com User jqtmviyu@gmail.com PreferredAuthentications publickey IdentityFile ~/.ssh/jihulab_ed25519 StrictHostKeyChecking=no ==== ``` ```bash # 把.pub公钥复制到后台管理页面上 ``` 4. 测试连接 ```bash ssh -T git@jihulab.com ``` #### 错误解决 * 遇到报错, 权限过高, 忽略私钥 ```sh chmod -R 0722 ~/.ssh cd ~/.ssh chmod 0700 私钥 chmod 0722 公钥 # 测试是否连通 ssh -T github.com ``` * `StrictHostKeyChecking=no` * github具有指纹,是否继续(是/否) ```sh # 将远程服务器添加到您信任的主机列表中 ssh-keyscan github.com >> ~/.ssh/known_hosts ``` ### Git中全局忽略.DS_Store文件 ```sh vim ~/.gitconfig ==== [core] excludesfile = ~/.gitignore_global ==== vim ~/.gitignore_global ==== .DS_Store **/.DS_Store .DS_Store? ==== ``` ## 新建仓库 ### 创建一个新仓库 ```sh git clone cd <文件夹> git checkout -b main touch README.md git add README.md git commit -m "add README" git push -u origin main ``` * `git switch`和 `git restore`: 2.23新增的两个命令, 用来替代原来承担太多功能的`git checkout`, `switch`对应分支的操作,`restore`对应更改文件的操作 * `git checkout -b ` ==> `git switch -c`, `-c`是 --create * `git checkout <分支名>` ==> `git switch <分支名>` ### 推送已有的仓库 ```sh cd git init git remote add origin git add . git commit -m "Initial commit" git push -u origin main ``` * 设置 init 时的分支名为 `main`(原来是master) ```sh vim ~/.gitconfig === [init] defaultBranch = main === ``` ### 推送现有的 Git 仓库 ```sh cd git remote rename origin old-origin git remote add origin git push -u origin --all git push -u origin --tags ``` ### 初始化 ```bash # 在当前目录新建一个Git代码库 git init # 新建一个目录,将其初始化为Git代码库 git init ``` ### 下载 ```sh # 下载一个项目和它的整个代码历史 git clone # 下载指定分支 git clone -b # 只克隆一次提交,避免项目历史记录过大 git clone --depth=1 # 如果后续需要扩展 git fetch --depth=5 ``` ## 三个区域 ==Git 可以被分为三个主要区域: 工作目录 - 暂存区 - 仓库== 工作目录是你在本地项目中看到的实际文件和目录 暂存区是一个中间区域,它保存你即将提交到 Git 仓库的更改 Git 仓库是一个隐藏的目录(通常是 `.git`),它包含了项目的所有版本历史记录 ```sh 工作目录 (Working Directory) | | git add v 暂存区 (Staging Area) | | git commit v Git 仓库 (Repository) ``` ### 工作目录 ```sh # 查看工作目录中哪些文件有更改 git status # 查看工作目录中的具体更改内容 git diff ``` ### 暂存区 ```bash # 添加指定文件到暂存区 git add [file2...] # 添加指定目录到暂存区,包括子目录 git add # 添加当前目录的所有文件到暂存区 git add . # 添加每个变化前,都会要求确认 # 对于同一个文件的多处变化,可以实现分次提交 git add -p # 从暂存区中移除文件的更改(不删除工作目录中的更改) git reset # 查看暂存区中的具体更改内容 git diff --cached ``` ### git仓库 ```sh # 将暂存区中的更改提交到仓库 git commit -m "message" # 查看仓库中的提交历史记录 git log # 查看特定提交的详细信息 git show # 删除,并且将这次删除放入暂存区 git rm [file2...] # 删除文件但保留工作目录中的文件 git rm --cached # 改名文件,并且将这个改名放入暂存区 git mv ``` #### git仓库的原理 git仓库使用了一种称为对象数据库的方式, 主要由四种对象组成: 1. **Blob 对象**(Binary Large Object) 2. **Tree 对象** 3. **Commit 对象** 4. **Tag 对象** ##### 1. Blob 对象 Blob 对象存储文件的内容,但不存储文件名和文件的元数据(如权限)。每个文件的内容都通过 SHA-1 哈希生成一个唯一的标识,这个标识就是 Blob 对象的 ID。 示例:一个名为 `hello.txt` 文件,内容为 "Hello, Git!"。其 Blob ID 可能是: ``` 83baae61804e65cc73a7201a7252750c76066a30 ``` 每当文件被修改并再次添加到 Git 仓库时,Git 会为修改后的文件内容创建一个新的 Blob 对象,而不会修改原有的 Blob 对象。每个 Blob 对象都是文件内容的快照,以文件内容的 SHA-1 哈希值为唯一标识符 git使用 zlib 压缩, 在打包过程中,Git 会计算初始内容和修改内容之间的差异, 增量存储 Git 会在适当的时候自动进行垃圾回收和优化存储, 如果你删除了大量分支或文件,并希望立即释放磁盘空间,可以运行`git gc` ```sh du -sh .git git gc du -sh .git ``` ##### 2. Tree 对象 Tree 对象存储目录结构和文件名。一个 Tree 对象包含多个条目,每个条目指向一个 Blob 对象或另一个 Tree 对象(用于子目录)。Tree 对象还存储文件的元数据,如权限。 示例:一个包含 `hello.txt` 文件的目录,可能的 Tree 对象结构: ``` 100644 blob 83baae61804e65cc73a7201a7252750c76066a30 hello.txt ``` ##### 3. Commit 对象 Commit 对象存储每次提交的元数据,包括提交信息、提交时间、作者信息以及指向树对象的指针。每个提交对象还包含指向父提交的指针(除非它是第一次提交)。 示例:一个提交对象,可能的结构: ``` tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 parent 83baae61804e65cc73a7201a7252750c76066a30 author John Doe 1623078749 -0500 committer John Doe 1623078749 -0500 Initial commit ``` ##### 4. Tag 对象 Tag 对象用于为特定的提交打标签,它存储了标签的名字、创建标签的时间、创建标签的人的信息等。 ##### Git 的存储过程 1. **生成 Blob 对象**:每个文件的内容都会生成一个 Blob 对象,并存储在 Git 仓库中。 2. **生成 Tree 对象**:每个目录结构都会生成一个 Tree 对象,并存储在 Git 仓库中。 3. **生成 Commit 对象**:每次提交都会生成一个 Commit 对象,指向相应的 Tree 对象,并包含提交信息。 ##### 查看对象 ```sh # 查看提交对象 git log # 查看树对象 git cat-file -p # 查看 Blob 对象 git cat-file -p ``` ## 提交 ### 代码提交 ```bash # 提交暂存区到仓库区 git commit -m # 提交暂存区的指定文件到仓库区 git commit [file1] [file2...] -m # 提交工作区自上次commit之后的变化,直接到仓库区 git commit -a # 提交时显示所有diff信息 git commit -v ``` ### 修改提交信息 ```sh # 使用一次新的commit,替代上一次提交 # 如果代码没有任何新变化,则用来改写上一次commit的提交信息 git commit --amend # 或者 git commit --amend -m # 重做上一次commit,并包括指定文件的新变化 git commit --amend [file1] [file2...] ``` ### 多个提交作者 ```sh # 第一行"不要闭合,回车换行 # 然后回车,两个空行 git commit -m "提交信息 > > Co-authored-by: NAME Co-authored-by: AUTHOR-NAME " # 回车结束 ``` ## 分支 ### 列出分支 ```bash # 列出所有本地分支 git branch # 列出所有远程分支 git branch -r # 列出所有本地分支和远程分支 git branch -a # 查看本地分支和哪个远程分支对应 git branch -vv ``` ### 新建分支 ```sh # 新建一个分支,但依然停留在当前分支 git branch # 新建一个分支,并切换到该分支 git checkout -b # 示例 # git checkout -b dev origin/dev # 新建分支dev并切换到该分支, 链接到远程dev分支 # 新建一个分支,指向指定commit git branch # 新建一个分支,与指定的远程分支建立追踪关系 git branch --track ``` ### 切换分支 ```sh # 切换到指定分支,并更新工作区 git checkout # 切换到上一个分支 git checkout - ``` ### 改变指向 ```sh # 让分支指向另一个提交 git branch main HEAD~3 # 让main分支指向head的前第3个提交 git branch -f main HEAD~3 # -f表示强制 # 或者指向commit id git branch ``` ### 追踪远程分支 ```sh # 建立追踪关系,在现有分支与指定的远程分支之间 git branch -u # 示例 # git branch -u origin/master local-branch # -u是 --set-upstream的简写 # 这样 local-branch 就会跟踪 origin/master 了。如果当前就在 local-branch 分支上, 还可以省略 local-branch # git branch -u origin/master ``` ### 合并分支 ```sh # 合并指定分支到当前分支 # 合并前必须保证当前分支和远程仓库一样是最新的, 先fetch, pull, 再merge git merge # 不推荐 git merge --no-ff # 推荐, 不使用快速合并, 提交历史更加清晰, 不容易出错,可以全局设置 # git 分支合并到主分支时,去掉分支的冗余提交。即,将分支的多次提交一次性合并到主分支上。 git checkout master # 切换到主分支 git merge --squash dev # 一次性合并分支的多次提交 git commit -m 'xxx' # 将刚‘合并的提交’提交到主分支master # 拷贝一/多个commit,合并进当前分支, 比如把dev的c2和c4拷贝为c2', c4', 加到到main分支后面 # 合并时找到他们最近的公共节点进行比较 git cherry-pick [commit2...] ``` ### 删除分支 ```sh # 删除分支 git branch -d # 删除远程分支 git push origin --delete # 或者 git branch -dr # 清理本地仓库中已经不存在于远程仓库的远程分支引用 git remote prune origin ``` ### checkout的其他用法 ```sh # 从指定的提交中恢复某个文件 git checkout -- # git checkout abc123 -- a.txt # 丢弃工作目录中的更改 git checkout -- # git checkout -- a.txt ``` ## 标签 ### 查看标签 ```bash # 列出所有tag git tag # 查看tag信息 git show #列出符合条件的tag(筛选作用) git tag -l v1.* #查询远程tags的命令如下: git ls-remote --tags origin ``` ### 新建标签 ```sh # 新建一个tag在当前commit(轻量tag无-m标注信息) git tag # 详细编辑提交内容 git tag -a # 新建一个tag在指定commit git tag [commit] ``` ### 删除标签 ```sh # 删除本地tag git tag -d # 删除远程tag git push origin :refs/tags/ # 或者 git push origin --delete ``` ### 提交标签 ```sh # 提交指定tag git push # 提交所有tag git push --tags ``` ### 从标签新建分支 ```sh # 新建一个分支,指向某个tag git checkout -b ``` ### 拉取标签 ```sh # 拉取分支上现有的tags git fetch --tags # 拉取远程指定tag git fetch origin [远程tag名] ``` ## 信息 ### 文件状态 ```sh # 显示有变更的文件 git status # 显示指定文件是什么人在什么时间修改过 git blame ``` ### 提交历史 ```bash # 显示当前分支的版本历史 git log # 显示当前分支的最近几次提交和回退 git reflog # 就算是被硬重置了也能看到已丢失的记录 # 只显示提交 git log --oneline # 提交树 git log --oneline --graph git log --oneline --graph --decorate --all # 显示commit历史,以及每次commit发生变更的文件 git log --stat # 搜索提交历史,根据关键词 git log -S # 显示某个commit之后的所有变动,每个commit占据一行 git log [tagName] HEAD --pretty=format:%s # 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件 git log [tagName] HEAD --grep feature # 显示某个文件的版本历史,包括文件改名 git log --follow # 或者 git whatchanged # 显示指定文件相关的每一次diff git log -p # 显示过去5次提交 git log -5 --pretty --oneline # 显示所有提交过的用户,按提交次数排序 git shortlog -sn ``` ### 对比 ```sh # 显示暂存区和工作区的差异 git diff # 显示暂存区和上一个commit的差异 git diff --cached # 显示工作区与当前分支最新commit之间的差异 git diff HEAD # 显示两次提交之间的差异 git diff # 本地与远程的差集 :(显示远程有而本地没有的commit信息 git log # 统计文件的改动 git diff --stat # 显示今天你写了多少行代码 git diff --shortstat "@{0 day ago}" # 显示某次提交的元数据和内容变化 git show # 显示某次提交发生变化的文件 git show --name-only # 显示某次提交时,某个文件的内容 git show ``` ## 远程仓库 ### 显示信息 ```bash # 显示所有远程仓库 git remote -v # 显示某个远程仓库的信息 git remote show # git remote show origin # git remote show upstream ``` ### 添加/删除/重命名 ```sh # 增加一个新的远程仓库,并命名 git remote add # 删除一个远程仓库的所有配置 git remote remove # 重命名远程仓库 git remote rename ``` ### 更新url ```sh git remote set-url ``` ### 添加/删除 url列表 ```sh # 添加一个新的 URL 到指定远程仓库的 URL 列表 git remote set-url --add # 从一个远程仓库的 URL 列表中删除一个特定的 URL git remote set-url --delete ``` ### 从上游仓库的新建一个分支 code --> manin --> view all branches --> new branch --> 选择上游仓库, 选择上游分支 ```sh git fetch upstream git checkout -b 分支名 upstream/分支名 ``` ### 上传 ```sh # 上传本地指定分支到远程仓库 git push [-u] # -u是 --set-upstream的简写, 添加上游跟踪引用,以后可以直接使用 git push 和 git pull # shortname 比如说叫origin, 是代指远程仓库的别名, 只是在本地用的 # 示例 # git push origin master # git push -u origin master # 强行推送当前分支到远程仓库,即使有冲突 git push --force git push -f # 上传所有分支 git push -u --all # 推送所有分支到远程仓库 git push --all ``` ### 下载 ```sh # 下载远程仓库的所有变动 git fetch # 所有远程仓库 git fetch -a # 取回远程仓库的变化,并与本地分支合并 git pull [remote-branch] # pull = fetch + merge ``` ### 远程仓库改名 ```sh # 假设远程仓库改名 # 先查看远程仓库的url git remote -v # 更新url git remote set-url origin # 验证 git remote -v ``` ## 撤销 ### 文件 ```bash # 恢复暂存区的指定文件到工作区 git checkout # 恢复某个commit的指定文件到暂存区和工作区 git checkout # 恢复暂存区的所有文件到工作区 git checkout . # 将在工作空间但是不在暂存区的文件撤销更改 git restore # 将暂存区的文件从暂存区撤出,但不会更改文件 git restore --staged # 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变 git reset [file] # 重置暂存区与工作区,与上一次commit保持一致 --hard危险操作 git reset --hard ``` ### 分支 ```sh # 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变 git reset # 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致 git reset --hard # 未push时, 撤销上次commit git reset --soft 'HEAD^' # 不加单引号或者在^前加/ 报错 no matches found # : 因为 zsh 或其他 shell 中,当 ^ 符号被解释为一个特殊字符 --soft # 不删除工作空间的改动代码,撤销commit,撤销的commit会退到暂存区 --hard # 清空工作区和暂存区,撤销commit --mixed # 不删除工作空间的改动代码,撤销commit, 重置暂存区, 撤销的commit和暂存区会退到工作区 # 重置当前HEAD为指定commit,但保持暂存区和工作区不变 git reset --keep ``` ### 抵消撤销 ```sh # 新建一个commit,用来撤销指定commit # 后者的所有变化都将被前者抵消,并且应用到当前分支 git revert # 例如:假设当前提交是c2, 上一次提交是c1, 执行 git revert HEAD,会新增一个提交c2', 提交的状态和c1一样, 用处就是可以把这次变化推到远程分支上,其他开发者也能看到 # 而git reset是local的,退了就退了,其他人看不到, 还有想退到上次,要多加^, revert用 ``` ## 暂存 ```bash # 最常用 # 暂时将未提交的变化移除,稍后再移入 git stash # 命令恢复之前缓存的工作目录,将缓存堆栈中的对应stash删除, 默认为第一个stash,即stash@{0} git stash pop # 执行存储时,添加备注,方便查找,只有git stash 也要可以的,但查找时不方便识别 git stash save "save message" # 查看stash了哪些存储 git stash list # 显示做了哪些改动,默认show第一个存储,如果要显示其他存贮,后面加stash@{$num},比如第二个 git stash show stash@{1} git stash show # 显示第一个存储的改动,如果想显示其他存存储,命令:git stash show stash@{$num} -p ,比如第二个:git stash show stash@{1} -p git stash show -p # 应用某个存储,但不会把存储从存储列表中删除, 默认使用第一个存储,即stash@{0} git stash apply # 丢弃stash@{$num}存储,从列表中删除这个存储 git stash drop stash@{$num} # 删除所有缓存的stash git stash clear ``` ## 变基 ```sh # 将当前分支的更改应用到指定分支上 git rebase # git rebase main # 交互式 rebase git rebase -i # git rebase -i HEAD~3 # 如果 rebase 过程中出现冲突并解决后,继续进行 rebase。 git rebase --continue # 如果 rebase 过程中某个提交不需要应用,可以跳过 git rebase --skip # 如果 rebase 过程中出现问题并希望取消,可以中止 git rebase --abort # 尝试自动处理冲突 git rebase --strategy-option theirs # 将特定的提交范围重新定位到另一个分支 git rebase --onto # git rebase --onto main feature~1 feature # 在 rebase 过程中更改提交的作者信息 GIT_AUTHOR_NAME="New Author" GIT_AUTHOR_EMAIL="newauthor@example.com" git rebase ``` 示例: ```sh git rebase -i HEAD~4 # 弹出一个vim, 可以让你修改4次提交的前后顺序或者删除, 会把修改后的复制一份添加到修改的起始节点, 原来的就丢了 , 后面的4是表示距离HEAD最近的4次 ## 如果这里不用 HEAD~4, 而是用id, 那么就得用倒数第5次提交后生成的id, 因为是倒数第5次提交后的那个节点开始操作的 # pick:保留该commit(缩写:p) # reword:保留该commit,但我需要修改该commit的注释(缩写:r) # edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e) # squash:将该commit和前一个commit合并(缩写:s) # fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f) # exec:执行shell命令(缩写:x) # drop:我要丢弃该commit(缩写:d) # 比如有四次提交 pick xxxxxx 倒数第四次提交的注释 pick xxxxxx 倒数第三次提交的注释 pick xxxxxx 倒数第二次提交的注释 pick xxxxxx 最后提交的注释 # 现在四次提交要合并成一次 , 并且改注释 r xxxxxx 倒数第四次提交的注释 f xxxxxx 倒数第三次提交的注释 f xxxxxx 倒数第二次提交的注释 f xxxxxx 最后提交的注释 # ctrl + c 退出, Y, 回车 , 进入编辑注释界面 # 编辑要保存的注释 ``` ```sh # 在bigFix上找bug加了一堆调试代码, 提交了多次, 现在找到了, 解决了, 想合并到主分支上, 但不想合并那些加了调试代码的提交, 只想合并解决问题的那次提交 git rebase -i # 先变基 git cherry-pick bugfix ``` https://www.cnblogs.com/code-xu/p/14262963.html https://www.cnblogs.com/xueweihan/p/5743327.html