- 前言
- git clone
- git add
- git commit
- git remote
- git push
- git pull
- git fetch
- git rebase
- git cherry-pick
- git branch
- git checkout
- git submodule
- git status
- git log
- git reset
- git revert
- 相关链接
本文包括了 git 常用命令(个人)的使用和解释
下文中有类似
分支1 * 分支2 [1111111]提交1 [2222222]提交2 [0000000]提交0 [0000000]提交0
的内容,是为了便于理解不同命令的实现效果
实际上,git 中的分支只是他们指向了不同的提交:
[0000000]提交0 ____ [1111111]提交1(HEAD -> 分支1) |_________ [2222222]提交2(分支2)
名词解释:
远程服务器:远程服务器(运行 git 服务的服务器)
远程仓库:远程服务器存放 项目代码 的地方(remote)
远程分支:远程仓库对应的某个 git 分支(branch)
commit ish: commit 标识,前几位就可以帮助 git 找到对应的 commit
下文中 [7777777] 等代表 commit ish 的前⑦位
标准的 ish 形如 f95ee18789ac1f6aa0af59d3cbadf8c81be0e641
本文假设你已经拥有 git 账号,拥有可用的 git 服务器
git clone克隆远程仓库时使用,克隆的内容放到指令执行时所处的目录
git clone url|ssh
最常用的是 url(https),这是一种安全的、方便的克隆仓库的方式
ssh 则需要你提供给服务器你的 public ssh key(公钥)以确保服务器能正确的识别你然后通过 ssh 向你提供代码
其他的 git 服务还可能提供其他克隆的方式,例如 github 的 github-cli
告知 git 暂存已经修改过的内容
git add file|floder|... git add * # 常用命令,暂存所有修改项
git add 将匹配到的修改过的文件存入暂存区,git commit 时提交的更改就是它们,git 默认不暂存 .gitignore 文件下匹配到的内容,例如
# myProject/.gitignore 我的吐槽.txt # add 时忽略 # myProject 目录结构 - src + index.js -Modified # 假设修改了部分内容 - 我的吐槽.txt -Modified # 假设修改了部分内容
此时,执行 git add * 将只有 index.js 存入暂存区
如果后续想要撤销暂存的内容,既让某个文件回到修改之前的状态
git checkout --git commit
告知 git 将暂存区的内容放到一个提交中去,这个提交需要你写一些提交信息(commit message)
git add * git commit -m '-m代表将后面的引号里的内容当做提交信息' git commit # 执行命令后进入默认的编辑器编辑提交信息 # !保存后才认为提交信息有效
在成熟的项目中,往往对 commit message 有格式要求,比如
[bugfix-bugid] bugtitle
…content…
这时,每次打这些文字是不可取的,项目中如果有模板文件则使用
git config commit.template <模板路径>
此后使用 git commit 后会直接在模板上编辑
补充:如果 commit 之后发现该 commit 不合心意,比如有新的文件应该存在这次提交中、提交信息不满意等,可以重新编辑 commit 信息,这会改变 commit hash,也就是说改完的 commit 不是之前的 commit
git add * git commit git add * # 假设你有新的更改应该放到这次提交上 git commit --amend # 修改 commit-message
注意:由于前后不是同一个commit,如果你已经将 commit 推到远程仓库的某个分支,使用 git status 会发现,有一个待提交的 commit(改完的),和一个要拉取的提交,这是因为本地提交抹除了原来的提交,替换为新的了,但是远程没有
# 远程 example 分支提交记录 [1234567]旧的提交 [6543210]初始化时的提交 # 本地 example 分支提交记录 [2345678]改过的提交 [6543210]初始化时的提交
git 经过对比后才有了这种结果,如果有权限的话使用
git push -f origin example # 强制推到远程仓库
备注:origin 指默认的 remote 名
git remotegit remote 将展示 git 远程仓库的信息
git remote -v # 列出全部 remote 详细信息 origin git@github.com/xxx (fetch) origin git@github.com/xxx (push)
假设你自己写了一个项目,想把它放到 github、gitlab 和 gitee上去,便可以添加远程仓库(remote),一般直接按默认选项设置即可
假设我们要添加一个名为 gitlabRemote 的 gitlab 仓库
git remote add gitlabRemote git@gitlab.com/xxx(仓库地址) # 可以通过 remove 删除或者 rename 重命名 git remote remove gitlabRemote git remote rename gitlabRemote 新名称
添加好仓库后
git pull|fetch|push gitlabRemote
即可拉取、抓取、推送提交到gitlab仓库的指定分支了
补充:使用 git fetch,相当于 git fetch origin,git 会从 origin 对应的仓库地址抓取(fetch)有关该仓库所有的分支、提交等信息
git pull 将会把 commits 推给 origin 对应的仓库地址
同理,推、拉不同的远程分支时使用的 origin/branch 转为了 …github.com/xxx/branch
告知 git 向远程分支推送 commits
这里不是推送全部代码,而是 commits,这样可以让 git 占用更小的带宽,取得更高的灵活性和速度,了解git运行机制
将本地的所有提交(commits)推送到远程分支
git push<本地分支名>:<远程分支名> eg: git push origin <本地分支名> # 相当于 git push origin 本地分支名:本地分支名 git push # 相当于 git push origin 当前分支:upstream分支
详解:
git push origin localBranch:remoteBranch # git 将本地分支 localBranch 暂存的 commits 推送到 # 指定的仓库 origin 的指定分支 remoteBranch 上
同时,上面提到的 upstream 是指 push 在没有提供明确仓库、分支名时 git 默认使用的内容
默认的 master 分支的 upstream 是 origin/master
所以下面的命令完成同一效果
git push git push origin master git push origin master:master
可以使用下面的命令为一个分支(branch)指定 upstream
git branch --set-upstream-to=/
push 也支持删除远程分支
git push origin :<远程分支名> # 相当于推给远程分支空内容
关于代码冲突:
如果你和你的团队成员负责两个不同的部分,修改的代码互不相关则不会出现代码冲突,如果你和你的团队成员同时修改了一份文件,例如:
// 另一个团队成员
// src/index.js 第一行
console.log('成员1改过的字符')
// 你
// src/index.js 第一行
console.log('你改过的字符')
git 不知道应该应用他的还是你的,这就需要你手动的解决冲突,打代码改成最终正确的样子
git pull告知 git 拉取本地和远程分支之间差下的 commits
# 和 push 类似 git pull远程分支名:本地分支名 eg: git pull origin remoteBranch:localBranch # 将远程仓库 origin 的分支 remoteBranch 中的 commits 获取(git fetch) # 然后将对比过后将两分支差出来的 commit 写入本地分支 远程: 本地:* [1111111]新的commit =写入> [1111111]新的commit [1222222]本地也有的commit [1222222]本地也有的commit
如果你此时本地有些提交还没有推送,git会使用 merge 命令来处理它们,我没有经常使用它,而是结合 rebase 使用
git pull --rebase<远程分支名>:<本地分支名>
这样可以把拉倒的 commits 直接塞到没推送的 commits 下面
远程: 本地:*
[1000001]未推送的commit
[1111111]远程的commit=写入>[1111111]远程的commit
[1222222]本地也有的commit [1222222]本地也有的commit
其他简写规则与 git push 类似
git fetch告知 git 抓取远程仓库的信息,这些信息包括 分支、commits 等
git pull 基于 git fetch 实现,但个人推荐先使用 git fetch
git fetch 比较安全,他只会抓取信息,不会改变 commit
git fetch
如果抓取到新信息则会显式地打印到终端
git rebase告知 git 变基
git rebase <分支名>
简单理解就是,尝试将其他分支的代码、commits覆盖到你手头分支的基底上去,commit history 中相同的部分可以理解为基底
分支1 分支2 * [1111113]公用内容修改 [1111114]分支2修改内容 [1111111]commit1111111 [1111111]commit1111111 -----基 [1111112]commit1111112 [1111112]commit1111112 -----底
假设我们现在在分支2上工作,需要分支1上的公共内容修改的提交
# 在 分支2 上 git rebase 分支1
这个命令将会把两个分支不一样的 commits 从分支1 摘出来,尝试覆盖到分支2上,如果[111113]和[111114]修改了同一块代码,那就是喜闻乐见的代码冲突了,需要手动修改冲突之后
git add * git rebase --continue # 或者放弃 rebase 不推荐 skip git rebase --abort
rebase之后分支1就作为我们分支2的新基底了
分支1 分支2 *
[1111114]分支2修改内容
[1111113]公用内容修改 [1111113]公用内容修改
[1111111]commit1111111 [1111111]commit1111111
[1111112]commit1111112 [1111112]commit1111112
上文中提到的 git pull --rebase 也是同理
相当于把指定远程分支上的提交当做本地分支的基底
将指定的某些提交添加到基底上
git cherry-pick
分支1 分支2 * [1111113]公用内容修改 [1111114]分支2修改内容 [1111115]分支1提交项1 [1111111]分支2提交项1 [1111116]分支1提交项2 [1111112]分支2提交项2
现在假设你在分支2上工作,你需要修改部分内容,但这些内容分支1上已经有人做过了,你当然不会去复制粘贴一份,使用 cherry-pick
# 在 分支2 上
git cherry-pick 1111113
分支1 分支2 *
[1111114]分支2修改内容
[1111113]公用内容修改 [1111113]公用内容修改
[1111115]分支1提交项1 [1111111]分支2提交项1
[1111116]分支1提交项2 [1111112]分支2提交项2
如果 [111114] 和 [111113] 有冲突可以使用
git cherry-pick --quit|--continue(解决完冲突之后)|--abortgit branch
查看、管理 git 本地仓库的分支们
git branch # 查看本地分支 -r 查看远程的 -a 查看全部的 * master # 你当前在 master 分支 bug-0001 # 用于处理 0001号 bug 的分支 git branch -d bug-0001 # 删除 bug-0001 分支 git branch -D bug-0001 # 强制删除 bug-0001 分支 git branch bug-0002 # 新建 bug-0002 分支 # 创建分支将会基于当前分支内容创建git checkout
git 检出(切换)分支命令
git checkout <要切的分支名> git checkout -b <分支名> # 新建该分支并检出到该分支 git checkout -b <分支名>
用例:假设你的项目有两个版本1.0.0和1.1.0
你现在在1.1.0版本的分支上工作,需要改一个 bug,可以用:
git checkout -b bug-xxxx
这时出现了一个1.0.0上的 bug 要你处理,就可以直接在这个分支上用:
git checkout -b bug-yyyy origin/version1.0.0_branch
随后继续你的工作
git checkout --
管理项目中的 git 子模块
git submodule addgit submodule init git submodule update
假设你的项目依赖于你自己的一个组件库且你决定通过子模块引入它
# 你的组件库 假设名称叫 time
# time/src/getTime.js
- time
- src
+ getTime.js
假设该组件的地址是 github.com/xxx/time
你在你的项目中可以使用以下命令来添加这个组件库
git submodule add https://github.com/xxx/time submdTime # 可以通过 -b来指定依赖那个分支下的代码
这时,项目中会多出一个 submdTime 文件夹和一个 .gitmodules 的文件,形如
[submodule "time"] path = submdTime url = https://github.com/xxx/time
但是 submdTime 文件夹里没有内容,需要通过
git submodule init git submodule update # 或者 git submodule update --init --recursive # 自动递归所有子模块并初始化
如果需要推送或者拉取子模块的变更,只需要 cd 进入子模块目录正常操作即可,随后返回项目目录执行git add *
删除子模块
rm -rf 子模块目录 # 随后手动删除 # .gitmodules # .git/config # .git/module/ 中的相关内容后即可 # 如果再添加新的同名子模块时报错则 git rm --cached 子模块名称
补充:在添加子模块后,打开 github 会看到 submdTime 文件夹后会多出点东西,形如: submdTime @abc1234
这个 abc1234 是用于标识子模块版本的东西
假设你在添加完子模块后,开始了你的工作,在 master 分支的基础上新建了 bug-0001 分支。
git branch * master git checkout -b bug-0001 git branch master * bug-0001
你在 bug-0001 上修完了 bug,并且在子模块里改了点东西
这时 bug-0001 上子模块的版本变为了 abc1245
提交完毕后,切回 master 分支会发现,原本 up-to-date(最新)的 master 分支多了一个修改项,这是将 master 子模块的 abc1234 改为本地(bug-0001改过后的)子模块的 abc1245
diff --git a/submdTime b/submdTime --- a/submdTime +++ b/submdTime @@ -1 +1 @@ -Subproject commit abc1234...... +Subproject commit abc1245......-dirty # 版本(commit ish)变了
你需要根据要求来决定是否将这次子模块更新推到 master 上
这说明,每个 branch 上的 submodule 都是独立的版本,但本地代码只有一个在使用中的版本,因此会出现上述现象
git status查看本地 git 的状态
git status On branch bug-0001 Changes to be committed: (use "git reset HEADgit log..." to unstage) modified: 已经暂存的更改过的文件 Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: 还没暂存的更改过的文件 还会提示你当前分支与 upstream 设置的分支差了几个 commit 和还有几个 commit 没提交
打印 git 的本地提交记录,有助于你查看和远程分支差了那些提交,在 cherry-pick 时也很有用,能帮你快速找到要 cherry-pick 的那个提交
git log commit(HEAD -> notation) Author: 作者 Date: 提交日期 commit msg ------ 多个commit ------ commit (HEAD -> notation) Author: 作者 Date: 提交日期 commit msg
但是这样查看方式虽然内容很全,但是不够明了,找起来怪麻烦
可以使用 --oneline 简化输出
git log --oneline 1000004 [bugfix-0004] 还没推送的提交 1000003(origin/master, origin/HEAD, master) [bugfix-0003] master分支最新的提交 1000002 [bugfix-0002] xxx 1000001 commit msggit reset
这个命令可以让 git 本地的 HEAD(通常指向最新提交) 指到指定的提交上去
git reset
常用于撤销 commits 的内容
比如你处于某些因素改了一些东西,后来你又得改回去
你可以找到这次提交的 commit ish,例如 [1111111]
git reset 1111111^ # ^代表向上移动一次 HEAD 指针
这样我们的代码就处于 1111111 号提交之前的状态了,不用担心,后续的改动都还在,只是不在暂存区了,根据需要改好后git add就好了
如果你还能记得需要重置的提交离 HEAD 多远,可以使用 HEAD~n
例如:你刚刚提交了一次,但是需要撤销它
git reset HEAD~1 # HEAD~1 等同于 HEAD^
注意:git reset 只重置了本地的提交,但是远程的并不会改变。如果你撤销了一些远程也存在的提交,会导致远程、本地提交不一致。如果有权限的话需要git push -f
上面的 git reset 重置了本地的提交,但是远程的并不会改变。这个就是来改远程的喽
git revert
注意:reset 是让 HEAD 指向指定的提交,revert 是让 HEAD 指向指定的提交之前的那个提交然后作为新的提交提交到远程
最新的提交 * --- revert后 ---远程的提交
[1111111]需要revert的提交 [2222222]撤销111号提交的提交
[0000000]提交000号 [1111111]需要revert的提交
[0000000]提交000号
上面的提交是使用git revert 1111111的结果
相关链接git官方推荐电子书
learnGit



