Git
Git 是分布式的代码管理工具。
查看状态
使用 status 命令来查看当前的状态
$ git status
暂存区操作
暂存区(Cache)相当于对我们代码的暂时保存,我们每次修改后,都可以添加至暂存区,来保存我们的修改。
添加
可以使用 add
命令来将文件添加到暂存区:
# 将文件添加到暂存区 $ git add <文件名>
放弃修改
添加到暂存区后,并不代表被正式提交。如果有意外情况,我们想要放弃暂存区的修改,可以使用 restore
命令:
# 将标记为 Modify 的文件回退到上一次的 Add 状态 $ git restore <文件名>
移出
如果想要将添加为暂存区的文件取消跟踪,可以使用 rm --cached
命令:
# 将 Add 状态的文件,取消追踪状态,变为 Update 状态(即新建状态) $ git rm --cached <文件名> # 将当前目录下的所有文件都取消追踪状态(必须全都是 Add 状态) $ git rm --cached -r . # 将当前目录下的所有文件(有 Modified 状态)强制取消追踪(不会丢失修改,但是不会追踪该文件了) $ git rm --cached -r -f .
总结
- 新建的文件,未被添加到暂存区,状态为 Update。
- 使用
add
命令,将 Update 状态的文件,添加进暂存区,状态变为 Add。 - 修改 Add 状态的文件,会引起 git 的检测,将状态标记为 Modified。
- 如果觉得改错了,可以使用
restore
命令,放弃所有修改,将文件回退至上一次 Add 状态。 - 如果想将 Add 状态的文件,变为 Update 状态,即移出暂存区,可以使用
rm --cached
命令,添加-r
参数可以递归的移出,添加-f
参数,可以强制移出 Modified 状态的文件,但不会导致 Modify 丢失。
本地库操作
本地库(Repository)是真正的版本管理,每次提交的代码,会作为一个版本记录下来,如果有意外,我们可以在版本间穿梭。
提交
使用 commit
命令来将所有暂存区的文件,提交到本地库中:
$ git commit -m '日志信息' [文件名]
提交到本地库后,文件会变为无状态(因为已经变成新版本了)。如果我们又修改了文件内容,那么文件会直接变为 Modified 状态。
查看记录
然后我们可以通过 log
和 reflog
命令来查看提交记录:
# 只输出版本 log $ git reflog # 输出全部信息 $ git log
每次提交会有一个哈希值,来标记这次提交。方便我们回退版本。
版本穿梭
如果想要回到其他版本的代码,可以使用 reset
命令来进行穿梭。
# 穿梭回版本号对应的版本(同时清空本地的暂存区和工作树,但是如果你有自己新建但是没有 add 的文件,并不会给你删掉的),版本号可以通过 reflog 或 log 命令查看 $ git reset --hard <版本号> # 其实 reset 命令有很多参数 $ git reset [--soft | --mixed | --hard] <版本号> # --soft 和 hard 一样,可以回退版本,但是 soft 会保存当前的修改到暂存区,适合哪些不想放弃当前的修改的情况。 $ git reset --soft HEAD^ # --mixed 是默认值,会保留工作树,清空暂存区。简单说就是 soft,但是将你的修改放到工作树,这时你再 add 一次,就和 soft 一样了 $ git reset HEAD^
reset
其实本质是移动指针,可能会丢失你的新版本,但 reflog
依旧会记录原来的版本,所以还是可以穿梭回去的。
重做提交
使用 revert
命令,可以重做一次提交(根据指定版本建立一次新的 commit 来回滚版本):
# 将本地版本回退到某个版本 $ git revert <版本号> # 正常会帮你 commit,但如果你不希望 commit,可以加上 -n 参数 $ git revert -n <版本号>
和 reset
命令的区别:
reset
是移动指针来回退版本,会导致指针后的版本全部丢失(但是可以从reflog
找到对应的版本号,再reset
过去)。revert
是用新的 commit 来回滚版本,不会导致前面的版本丢失。
在开发中,我们使用 reset
回退本地版本后,需要使用 push -f 来强制推送到远程。而 revert
是建立一个新的 commit,可以直接 push
到远程。
分支
为了能够更细粒度的控制版本,一般我们会创建多个分支,通过分支的不同,我们可以让线上版本保持大版本,让开发分支保持频繁迭代。
查看分支
使用 branch
命令可以查看分支:
# 查看本地所有分支 $ git branch # 查看本地所有分支(带版本号和提交信息) $ git branch -v # 查看所有分支(包括远程分支) $ git branch -a # 查看远程分支 $ git branch -r
创建分支
使用 branch
命令也可以创建分支:
$ git branch <分支名>
切换分支
使用 checkout
命令来切换分支:
# 切换到指定分支 $ git checkout <分支名> # 和上面的命令一样 $ git switch <分支名> # 创建,并切换到该分支(需要注意你是从哪个分支创建的) $ git checkout -b <分支名> <基于哪个分支> # 省略第二个分支名,就是从当前分支创建 # 上面这个命令比较常用,可以使用 git fetch 先将远程分支拉取,再通过以上命令新建分支 $ git fetch $ git checkout -b dev origin/dev # 基于远程库dev分支,来新建一个本地dev分支
删除分支
使用 branch [-d | -D]
命令删除分支:
# 删除一个没有新提交记录的分支 $ git branch -d <分支名> # 删除一个分支,即删除的分支存在没有合并到当前分支的提交 $ git branch -D <分支名>
合并分支
当我们开发好一个分支后,我们希望将这个分支合并到更高层次的分支去(比如 dev 分支合并到 main 分支):
# 首先我们需要切换到高层次分支(合并到的分支) $ git switch <合并到的分支> # 然后我们合并那个低层次分支(一般是我们开发的分支) $ git merge <开发好的分支> # 强制合并不相关的历史(一般是因为远程库创建了一个版本,本地库创建了一个版本,想要把他俩合并) $ git merge --allow-unrelated-histories <远程拉取到本地的分支>
如果合并时产生冲突,那我们需要解决冲突。冲突产生是因为有两个分支在同一位置产生了不同的修改,这时 Git 无法判断哪个需要保留,需要人工解决。一般是两个个人分支都进行了同一个位置的修改,第一个合并到高层次分支的不会冲突,但是第二个人想要合并的时候就会产生冲突。
一般来说,产生冲突后可以使用编译器在高层次分支中,直接选择保留哪个版本的代码,在解决冲突后,再 add
、commit
一次即可。
冲突合并完成后,双方都应该再拉取一次高层次分支的代码,来确保冲突合并的代码更新到双方的分支上。
建立追踪
如果想手动建立追踪关系,可以使用 branch --set-upstream
命令:
# 将本地的main分支,和远程的next分支建立追踪关系 $ git branch --set-upstream main origin/next
恢复分支
如果我们希望创建一个指向指定版本的分支,可以使用 branch
命令:
$ git branch <分支名> <版本号>
重命名分支
$ git branch -m <分支名> <新的分支名>
暂存修改
如果我们在开发时,在工作区和暂存区有一些新的修改,但是我们急需切换到其他分支时,还不希望提交本次代码时,可以使用 stash
命令,将我们的工作区和暂存区储存起来。
# 存储工作区和暂存区,保存后,我们的当前分支就会clean $ git stash
这样,由于工作区和暂存区被存储后干净了,我们可以直接切换到其他分支操作。等到想要将之前存储的修改取回时:
# 将存储的修改取回 $ git stash pop
远程仓库
在开发时,我们肯定需要一个远程库来在云端存储我们的代码。
创建远程库
在 GitHub 上创建即可。
添加到本地
我们有了远程库后,需要给本地库添加远程库,使用 remote
命令。
# 查看本地库记录的所有远程库 $ git remote -v # 添加远程库 $ git remote add <别名> <远程库地址>
推送
使用 push
命令将本地库推送到远程,这也是 git 中使用最多的命令之一。
# 推送本地库 $ git push <远程库别名> <本地分支名>:<远程分支名> # 例: $ git push origin main:main # 将本地的main分支推送到远程origin仓库的main分支 # 删除远程分支 $ git push <远程库别名> :<远程分支名> # 推送一个空的分支到远程,即删除远程分支 # 如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略 $ git push origin # 如果当前分支只有一个追踪分支,那么主机名也可以省略 $ git push # 如果当前分支与多个远程库存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数了 $ git push [ -u | --set-upstream ] origin main # 将main分支推送到origin,同时指定origin为默认主机 # 强制推送。如果本地库版本比远程库旧,那么Git会要求现在本地pull合并差异,如果想要强行推送,可以使用-f参数 $ git push [ -f | --force ] origin # 将本地所有分支,都推送到远程,远程没有的会创建 $ git push [ -a | --all ] origin
拉取
如果远程库的版本比本地更新,那么我们需要拉取远程库到本地:
# 拉取远程库 $ git pull <远程主机名> <远程分支名>:<本地分支名> # 例: $ git pull origin next:main # 将远程库的next分支拉回,与本地main分支合并 # 一般来说,远程分支名和我们本地分支名是一样的,我们可以省略冒号 $ git pull origin main # 等同于 $ git fetch origin $ git merge origin/next # 再一般来说,我们的分支是和远程分支建立追踪关系的(--set-upstream),这时我们可以省略分支名 $ git pull origin # 拉取远程库的当前分支,并合并 # 更一般来说,我们一个本地库可能只有一个远程库,这时我们还可以省略远程库名 $ git pull
pull
命令其实是 fetch
和 merge
的结合,而 fetch
命令:
# 使用fetch取回远程库更新 $ git fetch <远程主机名> <分支名> # 取回的更新,在本地主机上要用”远程主机名/分支名”的形式读取 # 例: $ git fetch origin main # 本地使用origin/main读取 # 再取回远程分支后,我们可以使用 checkout -b 来基于远程分支创建本地分支 $ git checkout -b newBranch origin/main # 或者使用merge合并到本地分支 $ git merge origin/master
克隆
如果我们希望完整的拉取一个项目到本地(不是更新我们的库),我们可以使用 clone
命令来直接把库拉到本地(而且 git 已经初始化)。
$ git clone <远程仓库地址>
如果我们希望从 0 拉取一个项目,我们最好使用 clone
,而不是直接下载 zip 文件,因为直接下载不会初始化 git。