2023.7.10 入职距今已经两个月零 3 天了,培训课程十分紧张也没来得及做一些技术的思考和整理。主要是下班回家后只想躺着玩手机,周末持续性出去撒欢。 但只学习不思考和整理是程序员的忌讳。培训课程结束后,会对这两个月的培训时间进行思考,同时对未来该怎么更好的工作也进行一个思考,甚至包括如何更好的休息锻炼来保持充沛的精力。
回到正文,git
是程序员写框架和交流代码时的必要工具,而过于贫瘠的实操经验导致我真的不会这玩意。尤其是多人协作 pull, merge
或者 reset
时,时常把代码搞的乱七八糟。所以在这里记录 git
的踩坑记录。
git
操作时很大程度受限于实际的情景,本地基于什么分支进行了什么修改,是否暂存,是否提交,是否有冲突等等等等。出问题后去网上搜索时,网上的例子和本地的例子不一定很符合,或者说只有一半符合。往往不知道该执行哪些命令,是否会把文件弄的很乱无法撤回。
这个时候建议把实际情景描述一下,去问问 GPT
,以我的使用经验,得到的回答 99.9% 都是可用的。
git
开发时,A 分支的代码泄漏到了 B 分支 ?
问题背景
当时想实现 master
分支只有 README.md, .gitignore, 3rdparty
等公共文件。
- 对于任务一,新建
dev1
分支,并在dev1
文件夹里面写代码 - 对于任务二,新建
dev2
分支,并在dev2
文件夹里面写代码
这样 dev1
和 dev2
分支的代码位于不同文件夹,互不干扰。最后全部合并到 master
分支的时候,也不会产生冲突。
错误操作
在实现期间出现了一个漏洞,当完成 dev1
任务的代码后,直接在 dev1
分支下 git checkout -b dev2
,这样就会发生:dev2
分支下有 dev1
的代码,不是很优雅。
当时培训课程的进度比较紧张,也没有刻意去关注这个问题。只是在 dev2
分支下手动删除了 dev1
文件夹的代码,这样在 git status
的时候会看到很多 delete
信息,且会随着 dev2
分支的提交而提交到 gitlab
中,merge
时会看到很多无用的删除文件信息。
正确做法
随着课程的陆续学习,框架规模越来越大,代码文件也越来越复杂。由于自己的 git
实操很少,担心 git
误操作后导致分支或文件过于混乱。又回过头来重新看这一问题,在本地进行一些简单的实验后发现了正确做法。
在完成 dev1
分支的代码并提交后,应该 git checkout master
,在 master
分支下新建 dev2
分支,这样才能实现 dev2
分支不含 dev1
的代码,保证提交代码时的信息足够干净。
记一次代码污染
背景
起因:需求是将本地 local
分支提交到 develop
分支。我理解成了将本地的 local
分支提交到 develop
分支,并向 master
提交 PR
。于是执行了:
1 | git push -u origin local:develop |
这样就导致了代码污染。因为可能有其他人基于 develop
分支开发代码,而我的 local
代码直接覆盖了远程的 develop
代码。
- 其他人提交代码的时候,会导致代码冲突;
- 其他人获取
develop
代码时,会获取到我的local
代码,但是我的local
代码没有经过检查和测试,负责模块整合的人也没有处理我这个模块可能存在的异常。所以很可能在运行期间存在错误。
正确做法
1 | git push -u origin local |
这样远程仓库中就会有一个 local
分支,提交 PR
时将 local
分支提交到 develop
分支即可。为什么要添加 -u
参数?
如果你在本地仓库中使用 git clone
命令克隆了一个远程仓库,并在本地仓库中使用 git checkout -b A
命令创建了一个名为 A
的新分支,并使用 git push A
命令将该分支推送到远程仓库,那么远程仓库将会有一个名为 A
的分支。
但是在使用 git push
命令时,你需要指定要推送的分支和远程仓库的名称。如果你使用 git push A
命令,git
将会尝试将本地仓库中名为 A
的分支推送到远程仓库中名为 A
的分支,但是如果远程仓库中不存在名为 A
的分支,git
将会报错。
因此,如果你想要将本地仓库中的 A
分支推送到远程仓库,并且希望在远程仓库中创建一个名为 A
的分支,应该使用以下命令:
1 | git push -u origin A |
这将会将本地仓库中的 A
分支推送到名为 origin
的远程仓库,并在远程仓库中创建一个名为 A
的分支。
使用代码回撤来解决代码污染
但是现在已经做错了,需要使用代码回撤来修复污染。可以使用 git reflog
命令查看本地仓库的提交历史,找到 develop
分支的提交记录。使用git reset
命令将代码重置到 develop
。
1 | $ git reflog |
找到最后一个 develop
分支的提交记录,记下该提交的哈希值。运行 git reset
命令将本地仓库的 develop
分支重置到该提交记录。例如,如果最后一个 develop
分支的提交记录的哈希值为 abc123
,则可以运行以下命令:
1 | $ git reset --hard abc123 |
运行 git push --force
命令将本地仓库的 develop
分支强制推送到远程仓库。请注意,这将覆盖远程仓库中的 develop
分支,因此请确保已经找到了正确的提交记录。这样就能恢复 develop
分支之前的代码。
1 | $ git push --force origin develop |
git 实用命令
一般的开发流程
首先克隆仓库
1 | git clone git@xxx.git |
创建本地分支,并对应远程分支
1 | git branch -a // 查看分支 |
获取新分支
clone
仓库的 1 天后,有新分支提交到了远程仓库,所以本地没有这个分支。为了查看新分支的代码,需要更新分支:
1 | git remote update origin -p |
暂存修改
在新分支开发代码时,遇到紧急任务需要切换到其他分支修复漏洞。但是新分支的代码才写了一点点还没有 commit
,如果直接 git checkout
会报错,因为新分支的修改没有被存下来或提交。此时可以暂存修改:
1 | git stash save "your_label" // 暂存当前未提交的更改 |
save
用于给 stash
的内容添加一个标签,后期可以根据加入的 label 恢复 stash
时容易找到。
需要注意的是,stash
命令不会暂存新添加的文件,只会暂存修改的文件。如果要暂存新添加的文件:
1 | git stash save "your_label" --include-untracked // 会暂存新添加的文件 |
当你完成其他工作并切换回原分支时,先 git pull
拉取分支的远程更新,如果有冲突就处理冲突,之后可以使用以下命令还原暂存的更改:
1 | git stash pop |
不建议以下的操作,因为这会直接放弃当前分支的修改:
1 | git checkout -f <branch_name> // 切换到另一个分支并丢弃未提交的更改 |
git 丢弃本地的修改
代码改的乱七八糟不想要了:
1 | git reset --hard HEAD |
撤销对单个文件的修改(文件依然在工作区):git checkout scripts/run_android.sh
临时代码推送
临时创建了一个文件夹复现了某个问题,需要把这份代码提交到某个仓库。在 git init
之后增加远程仓库:
1 | git remote add origin git@xxx:xxx.git |
因为是临时新建的仓库,所以目前处于 master
分支。执行下面命令,将本地的 master
分支推送到远程的 test
分支(远程没有的话会自动创建):
1 | git push origin master:test // 不加 master: 会报错,因为本地没有 test 分支 |
修改错别字,不值得重新 commit
首先修改小错误,然后:
1 | git add . |
如果此时直接 push
会报错,因为 git status
显示并没有新的内容。如果是提交到自己的分支,在不影响他人的开发的情况下可以直接:
1 | git push origin master:test -f |
这样仓库上只显示一次 commit
记录。如果不是强制推送,那么会遇到下面的问题:
1 | To git@github.xxxx.git |
起因是在这次 push
之前有一次 git commit --amend
修改错别字的操作,当时这个修改是没有提交的。所以再次修改代码并提交时,就遇到了冲突。因为同一文件同样的位置有不同的内容,无法自动合并,所以 push
的时候报错。
此时需要手动 git pull
一下,由用户自己手动 merge
处理冲突。如果是 vscode
的话,看一下哪里修改,如果保留当前版本,点击 accept current change
即可。再次 git add commit push
就没问题了。
代码写到一半,需要同步同事的代码
此时只需要在当前分支下 pull
代码。把自己的代码完成后,再次提交到分支。假设远程分支叫 B
,基于 B
分支 checkout -b
出 A
分支,在 A
分支写代码。远程分支有更新, merge
了一些修改,让 A
获取到 B
的更新:
1 | git pull origin B |
git 查看本地分支对应的远程分支名
1 | git branch -vv |
git 查看分支差异
git
基于 B
分支创建了分支 A
,并在 A
分支进行了修改和提交,提交后,vscode
等编辑器内无法看到修改内容。可以通过下述命令查看 A
分支和 B
分支的差异,也就是看 A
分支都改动了哪里。
1 | git diff A B file_path |
多次 commit
记录合并
为了保证提交信息的整洁,可以使用 git rebase
命令来将多个 commit
合并成一个,并保留代码的修改。以下是具体步骤:
使用
git log
命令查看你想要合并的commit
记录的哈希值,例如将以下 3 个commit
记录合并成一个:1
2
3
4$ git log --oneline
3a2b1c3 Add feature A
2b3c4d5 Fix bug B
1c2d3e4 Implement feature C使用
git rebase -i HEAD~3
命令来打开交互式rebase
编辑器,其中HEAD~3
表示要合并的commit
记录数量。在编辑器中,将第二个和第三个commit
记录的操作改为squash
,表示将它们合并到第一个commit
记录中。例如:1
2
3pick 3a2b1c3 Add feature A
squash 2b3c4d5 Fix bug B
squash 1c2d3e4 Implement feature C保存并关闭编辑器。
pick
操作会将一个提交应用到当前分支,而 squash
操作会将一个提交合并到前一个提交中,从而将多个提交合并成一个。
git
会自动打开另一个编辑器,让你编辑合并后的commit
信息。你可以保留第一个commit
记录的信息,或者修改为新的commit
信息。保存并关闭编辑器。使用
git push --force
命令将修改后的commit
记录推送到远程仓库。注意,由于使用了--force
参数,这会覆盖远程仓库中的历史记录,因此请确保你的操作不会影响其他人的工作。
git reset 用法
git reset
命令用于将当前分支的 HEAD
指针移动到指定的提交,同时可以选择是否修改暂存区和工作目录。--hard
和 --soft
是 git reset
命令的两个选项,它们的区别在于是否修改暂存区和工作目录。
--hard
选项会将HEAD
指针、暂存区和工作目录都重置为指定的提交。这意味着所有未提交的更改都会被丢弃,工作目录中的文件会被覆盖为指定提交中的文件。--soft
选项只会将HEAD
指针移动到指定的提交,而不会修改暂存区和工作目录。这意味着所有未提交的更改都会保留在工作目录中,可以通过git status
命令查看它们的状态。
一般来说,如果你想完全撤销所有未提交的更改并回到指定的提交,可以使用 --hard
选项。如果你只是想将 HEAD
指针移动到指定的提交,但保留未提交的更改,可以使用 --soft
选项。
这个命令我用的不多,实际场景用到时在补充。