git的基本功工作方式包括以下几个:

  • 主分支: master
  • 开发分支: develop
  • 提测分支: relase 如:relase/V2.0.0

新功能开发,develop 分支检出一个 feature 分支开发(合并后可删除) bug 修复,develop 分支检出一个 hotfix 分支开发(合并后可删除)

提测从 develop 检出一个 relase 分支提测,发布后 relase 分支合并到 master

若前端项目只有一个人负责,可以直接在 develop 开发,使用 Git 工作流规范,可以推进持续集成的统一建设,不会影响产品的持续发布。

下面的内容包括了 Git 常用的命令,以及各命令或详或简但有必要的解释说明,其中也包括了很多相关的引用链接用于扩展阅读。


推送和拉取

推送:

# 推送远程,并设置当前分支的 upstream 为 main,设置之后每次推送当前分支只需要 git push 即可
git push -u origin main

# 推送远程至 upstream,如果没有设置,在 git 2.0 之前,所有同名分支被推送,git 2.0 之后,报错
git push

# 强制推送
git push -f

# 推送至远程,但是设置远程分支名称
git push origin hehe:haha

拉取:

git checkout -b <local-branch> origin/<remote-branch> # 拉取远程指定分支并检出

​git push​​​的默认行为:默认行为根据 git 配置里的​​push.default​​​的值决定,在 git 2.0 之前的默认值是​​matching​​​,之后的是​​simple​​,所以在 2.0 之后如果没有设置上游分支 upstream 就会报错。

​push.default​​的可选值:

  • nothing,禁止​​git push​​​,必须显式指定推送分支,比如​​git push origin master​​;
  • current,把本地分支推送至远程,远程分支名和本地分支名相同;
  • upstream,推送到 upstream 上;
  • tracking,同上一个​​upstream​​,不推荐使用;
  • simple,推送到 upstream 上,但是要保证 upstream 的分支名和本地分支名相同;
  • matching,推送所有的本地和远程相同名称的分支。

设置​​local​​(仓库)级别的默认推送行为:

git config push.default nothing

分支

查看分支:

# 查看所有分支
git branch -a

# 查看本地分支
git branch

# 查看远程分支
git branch -r

删除分支:

# 删除本地分支(删除不了未合并过的分支)
git branch -d feature-album

# 删除未合并过的本地分支
git branch -D feature-album # 大写的 D

# 删除远程分支
git push origin --delete feature-album # 第一种方法
git push origin :refs/branch/feature-album # 第二种方法,这种方法适用在,分支名和标签名相同时,执行第一种方法会冲突报错,则使用这个方法,因为第一种方法也可以用来删除标签

检出远程分支:

git checkout -b <local-branch> origin/<remote-branch> # 拉取远程指定分支并检出

重命名分支(本地):

# 重命名当前分支
git branch -m <new_name> # -m 是 --move 短格式

# 重命名指定分支
git branch -m <old_name> <new_name>

# 在大小写无感的文件系统中重命名分支
git branch -M <New_Name> # 如果不是用 -M,会报错 fatal: 一个分支名 'new_name' 已经存在

重命名分支(本地):

# 重命名当前分支
git branch -m <new_name> # -m 是 --move 短格式

# 重命名指定分支
git branch -m <old_name> <new_name>

# 在大小写无感的文件系统中重命名分支
git branch -M <New_Name> # 如果不是用 -M,会报错 fatal: 一个分支名 'new_name' 已经存在

克隆

git clone git@github.com:wswmsword/git-learning.git

克隆所有分支,并切换到指定分支:

git clone -b main git@github.com:wswmsword/git-learning.git # 克隆所有分支,并切换到 main

克隆指定分支:

git clone -b main --single-branch git@github.com:wswmsword/git-learning.git # 克隆 main

克隆指定文件夹(git 2.25+ (Q1 2020)):

mkdir git-sparse-checkout && cd git-sparse-checkout # 创建并进入文件夹
git init -q # 静默初始化
git remote add origin git@github.com:wswmsword/git-learning.git
git sparse-checkout set assets # 设置指定文件夹,文件夹路径为 assets
git pull origin main
ls # assets
git sparse-checkout disable # 关闭 sparse-checkout

旧版本的 git 克隆指定文件夹:

mkdir git-sparse-checkout-old && cd git-sparse-checkout-old
git init -q
git remote add origin git@github.com:wswmsword/git-learning.git
git config --global core.sparseCheckout true
echo "assets" >> .git/info/sparse-checkout
git pull origin main

提交

暂存:

git add . # 暂存所有改动文件
git add -e <filename> # 把一个文件里的部分行移至暂存,暂存一部分
git add -p <filename> # 交互式,执行后将输出选项供选择,选择 e 可以暂存一部分
git add -u # 除未跟踪的文件外,把所有文件暂存
git add --ignore-removal . # 除已删除的文件外,暂存所有的文件

提交:

# 提交,添加信息
git commit -m "junior commit"

# 提交指定文件(这里的文件是 file-abc)
git commit file-abc -m "add single file"

# 提交,打开默认编辑器编辑提交信息
git commit

# 暂存已跟踪的文件,同时提交(省略了 git add 命令,但是这里只能暂存已跟踪文件)
git commit -am "junior commit"

# 提交,打开默认编辑器编辑提交信息,包含 diff 信息
git commit -v

# 提交,允许空提交信息
git commit --allow-empty-message

# 修改上一次提交
git commit --amend -m "better message was committed"

# 打开默认编辑器,修改上次提交信息
git commit --amend

# 修改上次提交的邮箱
git commit --amend --author "New Authorname <authoremail@mydomain.com>"

检出某一条提交记录:

git checkout <commit-hash>

合并

# 指定分支合并到当前分支
git merge feature-album

# 安全合并
git merge --no-ff --no-commit my-branch # --no-commit 不自动提交,--no-ff 不使用 fast-forward(快进)

# 合并的时候把一个分支合并成一条记录
git merge --squash feature-album

# 合并分支不提交记录
git merge feature-album --no-commit

# 终止合并(解决冲突的阶段)
git merge --abort

合并其它分支的一个文件:

git checkout --patch testing-merge-file features/album/audio-formats # 合并 testing-merge-file 分支的 features/album/audio-formats 文件

执行完上面的命令后进入优选项的交互界面,选择 e(manually edit the current hunk),进行文件的冲突编辑,最后保存,文件会在暂存区内。

如果命令中不加​​--patch​​,执行完命令的行为是当前分支的文件被指定分支的文件覆盖:

git checkout testing-merge-file features/album/audio-formats # 当前分支的文件 features/album/audio-formats 被 testing-merge-file 分支的覆盖

关于​​git merge <branch>​​​在​​fast-forward​​​情况下和​​git rebase <branch>​​的区别:

目标分支超过主分支,并且主分支没有新的提交记录,

*E-*F-*G-*H-*I    BRANCH
/
*A-*B-*C-*D MAIN

,​​fast-forward​​​和​​git rebase​​合并的效果一样,

*A-*B-*C-*D-*E-*F-*G-*H-*I MAIN | BRANCH

,当主分支有了新的提交(J、K),

*E-*F-*G-*H-*I    BRANCH
/
*A-*B-*C-*D-*J-*K MAIN

,就无法使用​​fast-forward​​​,合并之后必须有一个单独的合并节点,但是​​git rebase​​仍然可以合并,并且能保持提交线的整洁,

*A-*B-*C-*D-*J-*K-*E'-*F'-*G'-*H'-*I'             MAIN | BRANCH

,有冲突的节点因为解决冲突会生成新的​​commit-id​​​,有相同提交的记录会被目标分支的节点覆盖,​​rebase​​的节点也不一定是连续的,可能会穿插到主分支已经提交的节点中(按提交先后)。

关于三方合并:

master
|
*C0-*C1-*C2-*C4
\
*C3-*C5
|
iss53

在这样的分支结构中,要把​​iss53​​​合并到​​master​​​,要使用​​C4​​​、​​C5​​​和公共祖先​​C2​​,这三个节点做一个三方合并。

三方合并有利于自动合并:例如,节点 C2 有文件 F 包含内容


,节点 C4 的文件 F 包含内容

,节点 C5 的文件 F 包含内容



,现在把​​iss53​​​合并至​​master​​,因为三方合并,文件 F 的内容变成了


,因为 C4 在 C2 的基础上清空了第一行,C5 在 C2 的基础上填补了第三行,C2、C4、C5 都有第二行,所以合并的结果是清空第一行、保留第二行和填补第三行,这其中需要公共祖先 C2 的参与。

提示:当合并解决完冲突后,修改提交信息,“添加一些细节给未来检视这个合并的读者一些帮助,告诉他们你是如何解决合并冲突的,以及理由是什么”。