Git学习笔记

255 阅读7分钟

小记:由于长时间对 sourcetree 的依赖,深感自己对 git 以及 git 的操作越来越陌生。并且在目睹了目前所在团队 git 工作流程管理的混乱之后,愈发感到 git 以及 git 工作流程相关知识的重要性。遂决定对 gitGit flow 做一次认真学习以提高自己在这方面的认知和能力。以下即是本次学习的一点心得。

一些有意思的 Git 命令

这里列出了一些 git 命令,其中一些是在之前的学习中没有覆盖到但功能相当攒劲的(刘姥姥进大观园即视感),另外一些则有点像流水账,记下来方便自己日后查找。自己比较熟悉的一些命令就没列了。

目前日常的 git 操作以使用命令行为主,vscode 的源代码管理工具和图形化 git 工具(sourcetree)为辅,实际效果不错。

  • git commit --amend [file1] [file2] -m [message]
    功能:使用一次新的 commit,替代上一次提交;如果代码没有任何新变化,则用来改写上一次 commit 的提交信息。如果不想修改提交信息,加上 --no-edit 即可。简单来说就是既可以覆盖上一次提交,也可以只覆盖上次提交的 commit message,非常好用。

  • git add -p [filename]
    该命令等价于 git add --patch [filename]
    功能:挑选要 commit 的内容,添加每个变化前,都会要求确认,对于同一个文件的多处变化,可以实现分次提交;运行该命令后,会有一系列的操作选项(以及对应的操作说明)供选择,个人觉得比较常用的选项有:

    1. s —— split current hunks into smaller hunks(将内容分成小块);
    2. y —— stage this hunk for next commit(将该小块加入暂存区);
    3. n —— do not stage this hunk for next commit(该小块不加入暂存区);
    4. q —— quit, do not stage this hunk or any of the remaining  hunks(退出,并且该小块以及之后的小块都不加入暂存区);
  • git push <remote> [local_branch]:[remote_branch]
    功能:将本地分支推送至远端。一般如果不需要给远程分支起个新名字的话直接 git push <remote> [branch_name] 就完事。

  • git push <remote> -d [branch_name]
    功能:删除远程分支。学习时遇到一条同样功能的命令:git branch -dr origin/[branchname],经实践发现,虽然运行该命令后会有一条反馈:“Deleted remote-tracking branch [remote/branchname] (was [commitID]).”,然而远端上该分支仍然存在,暂且认为该命令失效吧,在这里提一下。

  • git merge [--ff|--ff-only|—no-ff|--squash]
    功能:合并分支,后面这三个参数很有意思,值得仔细了解并使用:

    1. --ff: 当条件允许时,也就是满足 fast-forward merge 条件时,即:当要合并进来的分支是当前分支的后代(when the merged-in history is a descendant of the current history)时,git 直接把 HEAD 指针指向合并分支的头,完成合并,同时不会创建 merge commit,此即 fast-forward merge;反之,当不满足条件时,那就是普通 merge,会创建一个 merge commit

      plus: 当不满足 fast-forward merge 条件而 git pull(相当于 git fetch + git merge )时会触发普通 merge 而产生一条 merge commit 记录(比如我们项目里到处都是这种原因而产生的 merge commit ...)。
      个人推荐的解决方法是:
      1. 创建自己的 feature 开发分支,并且:“多用rebase” —— 尤雨溪
      2. git config pull.ff only: 相当于在 git pullgit merge 阶段使用 --ff-only 模式,下一条细嗦。

    2. --ff-only: 满足 fast-forward merge 条件时,采用 fast-forward merge;否则,则拒绝合并且用一个非零的状态退出(exit with a non-zero status)。

    3. --no-ff: 在所有情况下都会创建一个 merge commit,就算满足 fast-forward merge 的条件也会创建。

    4. --squash: 使用 squash 方式合并,把多次分支 commit 历史压缩为一次,并创建一次 merge commit

  • git rebase [branchname] | [remote/branchname]
    绝对的神器,各种文章已经不少了,这里不再赘述。只是强调一点:一定要仅在自己的分支上使用,并且确保没有其他分支依赖你这条分支。遇到解决不了的冲突及时 git rebase --abort,和队友沟通解决,不要硬来。

  • git rebase -i HEAD~[number](倒数几次提交) | git rebase -i <hashA>..<hashB>(左开右闭) | git rebase -i <hashA>^..<hashB>(闭合)
    功能:对 commit 进行梳理合并,对于消除提交历史上那些乱七八糟无意义的提交非常有帮助,会让提交历史变得整洁,可读性大大提高。需要注意的是,如果过度整合会不方便后面可能出现的版本回退以及其他需要精细选择基点的操作。

  • git pull --rebase
    功能:类似 git rebase,将自己本地的提交调整至提交历史的最前面,主要是可以避免产生 merge commit

  • git log & git log [branchname]
    功能:查看本地分支提交记录

  • git log [remote/branchname]
    功能:查看远程分支提交记录
    --stat :显示提交历史,以及每次 commit 发生变更的文件
    --author="authorname" :使用作者名作为过滤条件;
    -[number] :截取前几次的提交记录;
    --oneline :将信息浓缩成一行;
    --date=[iso | local] :时间格式,推荐 iso;
    --no-merges :过滤掉 merge 的信息;
    --merges :只统计 merge 的信息; | :竖杠,将左边的值传给右边;
    wc -l :统计 commit 次数;
    grep [string] :pattern 匹配;
    examplegit log --author=张三 --merges | grep 'Merge branch'| wc -l -> 统计某人的 merge 总数;

  • git log --all --format='%aN <%cE>' | sort -u
    功能:列出 Git 仓库的所有贡献者及邮箱;
    plus:里面那些带 % 的奇怪字符叫做 format placeholders,有一大堆,点击前面链接,在页面中搜索 format placeholders 可查。

  • git tag [tag] & git tag -a [tag] -m [description]
    功能:版本发布时标记版本号。第二个命令可以为标记添加描述。

  • git branch & git branch -r
    功能:列出所有本地分支。列出所有远程分支。

  • git branch --show-current
    功能:打印当前分支的名称。

  • git reset [file]
    功能:恢复暂存区的文件修改到工作区;
    恢复暂存区中的所有文件修改到工作区 —— git reset .

  • git checkout [file]
    功能:恢复工作区中文件的修改;
    撤销工作区中所有文件的修改 —— git checkout .

  • git clean -df [file]
    功能:删除 untracked 文件和目录。
    -d :删除包含 untracked 文件的 untracked 目录;
    -f :强制执行 git clean 的操作,git clean 默认需要强制执行;
    -n :不实际的进行移除操作,仅仅列出需要 clean 的文件;
    清除所有 untracked 文件和目录:git clean -df .

  • git reflog --date=iso | grep [branchname]
    功能:查看分支由哪里切出。
    grep :pattern 匹配;
    --date :时间格式,可选 iso 或者 local

  • git fetch [remotename] [branchname]
    功能:将远程仓库某分支的数据下载至本地;
    git fetch [remotename] :将远程仓库所有分支的数据都下载至本地,功能与 git remote update [remotename] 相同;

  • git stash
    功能:储存代码;
    git stash save [stash_description] :储存代码并添加自定义描述;
    -u :同 --include-untracked ,在 git stashgit stash save 时使用,存储未跟踪的文件,如果不带这个参数,新创建的文件是不能被 stash 的;
    -p :挑选部分内容 stash,操作同前面所列 git add -p
    -k :所有对暂存区的改变会维持不变;
    git stash list :查看现在所有的 stash;
    git stash pop [stash_id] :恢复 stash 的存储(同时清除对应 stash),如果没有 stash_id 则默认按照栈的原则恢复最新插入的一条;
    git stash apply [stash_id] :应用 stash 的存储(对应的 stash 不会被清除);
    git stash drop [stash_id] :清理对应的 stash;
    git stash clear :清理所有存储;
    git stash show [stash_id] :展示 stash 中的修改,加上 -p 参数可展示更详细的更改信息;

关于 git 工作流程

我是通过以下两篇文章对 git 工作流程进行学习的:
《A successful Git branching model》 by Vincent Driessen
《Git 工作流程》 by 阮一峰

一点想法:对于互联网的 web 应用来说,git flow 其实不一定严格适用,尤其是发布部分,但其开发部分的 git 流程控制是非常不错的,同时也可以参考 gitlab flow 进行一定程度的变通以符合项目的实际情况。