盒子
盒子
文章目录
  1. 前言
  2. Git 基本概念
    1. Git 配置
      1. 命令自动补全
      2. 修改默认编辑器
      3. HTTP 访问 Git Server 免输入密码
    2. Git 的基本组成部分
    3. 文件的状态
    4. Git 提交历史
  3. Git 命令
    1. 基本命令
    2. 版本撤销或回退
    3. 分支
    4. patch相关命令
    5. 远程仓库相关
  4. Github 相关
    1. 1. Github 基本概念
    2. 2. 给开源项目贡献代码
    3. 3. Github Flow
    4. 4. Github 支持 Git LFS
    5. Gist
    6. Gitter
    7. Tig
  5. 参考文献
  6. 附:视频教程

Git基本使用指南

前言

本人接触 Git 也有两年了,但是之前只是草草学习,没有很好的掌握他,主要原因是没有工作需要(在学校里面哪用的到Git…)。我觉得 Git 这个工具在学校里很难充分了解到他,只有在公司里十几个人一起开发项目时才会感觉到他的优越性。

作为一个程序员,学习 Git 的原因有两个:

  1. 在工作中一个项目肯定要多人完成,多人协作开发再正常不过了。
  2. Github 越来越火,要用 Github 就必须要会 Git。

因此 Git 目前已经是一个软件开发人员默认的必会技能。

在工作中一般首选是用 Git 的 GUI 工具来操作 Git(这里推荐 Tower,用起来特别顺手;Github Desktop 也不错, Sourcetree 用的人也非常多,而且跨平台),因为在工作中一个项目一天的提交量超过200是很正常的,如果你要看今天哪些人提交了、提交了哪些内容,用命令行 git log 就很费劲,图形化界面的展示就看起来舒服很多。

很多人说:用命令行看起来多帅啊。但是解决问题是第一位,你只要了解了 Git 的操作原理,用图形化界面能够更清晰地完成你的工作。

我一般为了让自己不忘记 git 命令,都用命令行。

本文会按照以下顺序介绍:

  • Git 基本概念。
  • Git 基本命令、分支命令、撤销命令、合并命令、patch命令、远程仓库命令。
  • Github, Github Flow, Gist, Tig, Gitter。

本文所用的软件或平台包括:

  1. MAC
  2. Git + Github
  3. Tower 2.1.0
  4. Beyond Compare 4

Git 是 Global Information Tracker 的缩写,即对全局信息进行跟踪。

等到阅读完本文,可以通过 Learn Git Branching 验证下自己是不是真的学会了 Git。

Git 基本概念

Linus Torvalds 在 Google Talk 上做的关于 Git 的演讲:这里,笔记见这里

Git 配置

在使用 Git 之前需要做2件事:

  1. 配置 UserName(git config --global user.name "<name>"), Email(git config --global user.email <email>)。
  2. 如果要连接远程代码托管网站(如 Github),则需要生成 SSH 协议的 RSA 密钥和公钥。

命令自动补全

为了让 Git 在 bash 中拥有类似 bash 的提示功能,比如输入 git co,再按两下 tab,则会提示可用命令有 commit, config,我们可以如下配置:

  • 下载 git-completion.bash 文件放到用户主目录。
  • .bash_profile 中添加 source ~/git-completion.bash

修改默认编辑器

默认 git 使用的编辑器是vim,如果想改成 sublime text,则步骤如下:

  • 在用户的工作目录创建 bin 目录。
  • 命令行执行 ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" ~/bin/subl
  • ~/bin/subl 添加到环境变量 PATH 中(.zshrc 或 .bashrc 中)。
  • 命令行执行 git config --global core.editor "subl -n -w"

但是个人使用下来还是用 vim 作为默认编辑器体验最好,因为一直在命令行里面操作比较流畅。

HTTP 访问 Git Server 免输入密码

本小节参考:

  • 安装 wget: 从 “http://ftp.gnu.org/gnu/wget/“ 下载解压文件,并依次执行:./configure --with-ssl=openssl, make, sudo make install
  • 下载 git-credential-netrc 脚本:wget https://raw.githubusercontent.com/git/git/master/contrib/credential/netrc/git-credential-netrc
  • git-credential-netrc 脚本拥有执行权限:chmod +x git-credential-netrc
  • 将该脚本拷贝到 bin 目录(即放入 PATH 环境变量中):cp git-credential-netrc ~/bin/
  • 在用户目录下创建 .netrc 文件,内容为:

模板如下:

machine <git-server-host>  # 比如 github.com
login <account>
password <your-password>
protocol http
  • 对 .netrc 文件控制权限,因为里面有密码:sudo chmod 0600 .netrc
  • git 配置:git config --global credential.helper "~/bin/git-credential-netrc"
  • 安装 GPG 命令:sudo port install gnupg gnupg2
  • 生成 GPG key:gpg --gen-key
  • 查看你的 GPG key: gpg --list-keys
  • 用 GPG 密钥加密 .netrc 文件:gpg -e -r <你的gpg key> .netrc
    http://www.sunzhongwei.com/http-git-server-without-password.html

Git 的基本组成部分

  • 工作目录(working directory): 你在文件管理器中看得到的目录就是工作目录。
  • 暂存区(Stage Area or Index)。
  • 本地仓库(Local Repository): Git 是分布式的版本控制系统,本地仓库就是本地的一个备份仓库。
  • 远程仓库(Remote Repository): Github 中托管的(不是在本地的)仓库。

目前有很多 Git 仓库的托管网站,国外有 Github, Bitbucket,国内有 Gitcafe, Coding.net,功能或许有些许区别,但是建议使用 Github,因为 Github 的知名度最高,而且著名的开源项目都会放在这托管,况且目前 Github 还没被封,即使被封了,最好也不要用国内的,原因1是指不定哪一天公司倒闭了,还要转移项目,多麻烦;原因2是 Github 生态圈已经构成,就像微信和来往功能可能差不多,但是你朋友全在用微信,你会用来往的概率大吗?当然为了熟悉 Git 的基本使用,用 Gitcafe, Coding.net 也是不错的选择,因为毕竟速度更快。

Github 提供学生特权,包括免费 .me 的域名(小伙伴再也不用担心没有个人域名了)、让你的 Github 账号拥有5个私有仓库等。

文件的状态

工作目录中的文件共有几种常见状态:

  • Untracked: 未跟踪的,表示该文件没有被 Git 管理。
  • Unmodified: 被 Git 跟踪但工作目录中该文件没有被修改过。
  • Modified: 在工作目录中,该文件被改过了,但还没被加入暂存区。
  • Staged: 工作目录中文件被修改,并且已被加入暂存区。
  • Deleted: 工作目录中某个文件被删除,但是把这个删除操作还没提交到本地仓库。
  • Unmerged: 当文件处于冲突时的状态。

Git 提交历史

Git 提交的历史构成了一个有向无环图,每个节点是一个提交对象(Commit Object,其中包含SHA-1码、提交人的名字、提交人的邮箱、日期等),每条边表示提交的顺序,比如 u-->v,表示 v 是父节点,u 是子节点,即 uv 的后一次提交,如下图:

上图中可以看出:

  • 红色的如 master, testing
  • 分支名,我们可以在 .git/refs/heads 下看到有 mastertesting 两个文件名(这是分支的实现方式,下文还会讲到)。
  • HEAD 是一个指针,一般指向一个分支名(从图中也可以看出),表示的是最后一次提交,实现方法是在 .git/ 目录下有个 HEAD 文件,里面包含了他指向的分支名,比如 ref: refs/heads/master,但是 HEAD 指针也可以指向一个提交(.git/HEAD 文件的内容是一次提交的 HASH 值),称为”detached HEAD”(分离式 HEAD)。

Git Big Picture 是拓展命令,可以以图片格式输出整个 Git 树。

Git 中 author 与 commitor 的区别:对于一个开源项目,如果你要贡献一段代码,你就是 author,帮你提交进项目的人就是 commitor。

在公司中,如果要提交到公司内的 gitlab,需要使用公司邮箱,如果在提交中带有私人邮箱的提交,则会 push 不上去。经常遇到的问题是你在开发公司的项目,但是用的是私人邮箱提交,等到发现的时候已经来不及了。那怎么对过去的提交批量修改邮箱配置呢?在命令行执行下面命令即可:

git filter-branch -f --env-filter "GIT_AUTHOR_NAME='<new_name>'; GIT_AUTHOR_EMAIL='<new_email>';" HEAD

Git 命令

下面介绍的命令都是在 Git 中很常用的,这些命令的含义也是必须要搞清楚的,当然这些命令在 Tower 中也能实现,所以平时工作可以使用 Tower 完成,没必要再用命令行。

基本命令

  • git status: 查看当前状态,包括当前处于哪个分支、有哪些修改的文件没加入暂存区、哪些修改的文件加入了暂存区但没提交。
  • git add <file>: 将 <file> 加入跟踪或将修改添加到暂存区。
  • git add -A <file>: 如果 <file> 被删除了,想把这个删除的改变提交上去,那么用上面的命令添加会失败,因此加 -A,表示将工作区对 <file> 的所有修改都提交到暂存区。因此删除文件建议使用 git rm,而不是从文件管理器直接删除。
  • git add -p <file>: 交互式地对 <file> 文件的部分内容进行暂存,而不是一起暂存。具体参见 这里
  • git commit -m <message>: 将暂存区的内容提交到本地仓库。
  • git commit --amend: 当你对上次提交不满意(比如漏了提交2个文件,或者多提交了文件,或者对提交输入的 message 不满意),想重新提交,则使用 --amend 参数,效果是将暂存区的内容提交,并覆盖上次的提交。
  • git commit -am <message>: 多了 -a 参数,跳过 git add 命令直接提交。
  • git commit --fixup <SHA-1>: 创建一个新的提交,专门修复较早期 <SHA-1> 提交(比如有个文件忘提交了),新提交的 commit message 和 <SHA-1> 的一样。
  • git commit --squash <SHA-1>: 和 git commit --fixup <SHA-1> 作用一样,只是新提交的 commit message 不是 <SHA-1> 的 commit message,而是自定义 commit message,默认是 !squash <eariler commit message>
  • git mv <old> <new>: 将文件重命名(这里注意,不能手动直接改文件名哦!),但是在本地仓库中还没生效,直到 git commit 之后才生效。
  • git diff <file>: 查看工作目录和暂存区中 <file> 的区别。
  • git diff --cached <file>: 查看暂存区和本地仓库中 <file> 的区别。
  • git diff <SHA1> <SHA2>: 比较两次提交(<SHA1><SHA2>)的区别。
  • git diff <branch> <file>: 查看分支 <branch> 中的 <file> 和工作目录的 <file> 的区别。
  • git log: 查看提交的历史,包括提交的 SHA-1 码、谁提交了、什么时候提交的。
  • git log --pretty=oneline: 更简洁地显示提交历史,每个提交都只有一行,只显示 SHA-1 和 commit message。
  • git blame <file>: 查看 <file> 文件的每行是谁修改的,blame是责怪的意思,因此这个命令用来找罪魁祸首用。
  • git show <commit-id>: 显示 <commit-id> 的具体修改。

.git/ORIG_HEAD 文件的内容是一次提交的HASH值,他也是一个指针,指针名叫 “ORIG_HEAD”,当git进行一些危险操作比如merge,reset等时,git就会把进行危险操作之前的HEAD指针指向的提交保存到ORIG_HEAD中,以便用户后悔了,想恢复。

一般在公司开发项目,tag 肯定会使用,比如在2015年8月12日要发 1.0 版本,那么到了那个时候,就会在某个提交处打上 v1.0 的 tag。

tag 分为 lightweight tag(轻量级标签,只是指向一个提交,并不带任何 message)和annotated tag(带注释标签,标签带有注释)。所有标签都存放在 .git/refs/tags 目录中,每个标签是一个文件,如果是轻量级标签,则内容是提交的 HASH 值;如果是带注释标签,则内容是 Object 值。

  • git tag: 显示所有标签。
  • git tag <name>: 创建一个轻量级标签 <name>,并指向 HEAD。
  • git tag <name> <commit-id>: 创建一个轻量级标签 <name>,并指向 <commit-id>
  • git tag -a <name> -m <message>: 创建一个带注释标签 <name>,他有注释 <message>
  • git show <tag-name>: 查看 <tag-name> 标签的详细信息。
  • git push --tags: 将所有 tag 提交到远程仓库,因为默认的 git push 并不会提交 tag。
  • git tag -d <name>: 删除本地 tag。
  • git push origin :refs/tags/<name>: 将本地删除的名为 <name> 的 tag 推送到远程仓库。

接下来介绍一个不太常用的命令:git bisect 相关命令,该命令主要用来排查某个未知时段出现的问题,假设你现在的代码是编译不过的(提交树为 c1->c2->c3->c4->c5->c6,当前提交是 c6),你又不知道什么时候引入错误的,你唯一确定的是在 c1 提交的时候代码还是编译通过的,此时可以使用 git bisect,该命令的主要思想是通过二分查找的方法在提交树上确定问题发生在哪个提交上,并联系提交者。以前面的例子为例,我们执行下列命令:

git bisect start   # 开始二分查找问题
git bisect good c1 # c1 提交的时候代码还是好的
git bisect bad c6  # c6 提交的时候代码有问题

运行完上面命令后,git 会进入 c1 到 c6 的中间提交状态,这里是 c4,当你发现 c4 提交代码还是好的,则执行 git bisect good,那么可以确定问题一定发生在 c5 到 c6 之间;如果 c4 提交代码是坏的,则执行 git bisect bad,那么可以确定问题一定发生在 c2 到 c4 之间;以此类推,通过二分查找的方法,能够更快的定位问题。

定位完问题后,执行 git bisect reset 可以恢复执行 git bisect start 之前的状态。

查找问题的利器 - Git Bisect

版本撤销或回退

  • git checkout <file>: 丢弃工作目录对 <file> 的修改,并用暂存区的 <file> 替换。
  • git checkout HEAD <file>: 将工作目录和暂存区的 <file> 用 HEAD 指针指向的 <file> 替换。
  • git checkout <branch> <file>: 用分支 <branch> 中的 <file> 替换工作目录和暂存区的 <file>
  • git rm <file>: 删除工作目录和暂存区的 <file>,但是本地仓库没有影响,只有 git commit 才会从本地仓库中删除 <file>。因此如果你想删除一个文件,不要在文件管理器里删除,而要用 git rm
  • git rm --cached <file>: 取消对 <file> 的跟踪,但是对于工作目录、本地仓库中和远程仓库中 <file> 的状态不会变化,只是在暂存区中删除 <file>
  • git reset HEAD <file>: 将暂存区中的 <file> 用本地仓库的 HEAD 指针中 <file> 的状态替换,并将原本在暂存区中 <file> 的修改变为未暂存状态。
  • git reset --hard <SHA-1>: 将工作目录和暂存区的状态回到 <SHA-1>
  • git reset --soft <SHA-1>: 工作目录和暂存区内容不变,仅仅将 HEAD 指向 <SHA-1>。这个命令的使用场景:我在公司里面使用内部的 Gitlab,你如果要 push,则必须 git config 的邮箱配成公司邮箱才行,但是我一开始用我的私人邮箱进行了一次提交,就后悔了,因为一旦包含这次提交,我就不能把代码 push 到公司 Gitlab 上了。这时候想要丢弃这次提交,但是又想要工作目录仍然保存我最新修改的代码,这时候可以用 git reset --soft
  • git revert HEAD: 生成新的提交,新的提交中撤销 HEAD 指向提交的修改内容。revert 表示”恢复”的意思,但是 “git revert” 和 “git reset” 的区别是:”git reset” 只是修改 HEAD 指针,而 “git revert” 是生成一个新的提交,比如 HEAD 指向的提交中添加了一行,那么在 “git revert” 生成的新的提交中删除该行,即 revert 某个提交实际上就是将原本添加的变成删除,将原本删除的变成添加。
  • git revert <commit-id>: 生成新的提交,新的提交中撤销 <commit-id> 提交的内容,撤销方法和上面一样,即 <commit-id> 提交中添加的,在新的提交中就删除。
  • git reflog: 显示 HEAD 指针曾经指向的所有提交,在.git/logs/HEAD文件中保存了HEAD这个列表。使用场景:找回丢失的提交(比如 git reset --hard 后悔了,但你又通过 git log 找不到原来的提交了,那么就可以使用 git reflog)。

如果理解不了 git checkoutgit reset 命令,可以看图解Git,里面讲的非常清楚,讲清楚了很多教程中忽略的问题。

git checkout 适用于文件级别的撤销,git reset --hard 适用于仓库级别的撤销。

分支

Git 的分支非常轻量级,实现方法是每个分支对应 .git/refs/heads/ 目录下的一个文件,每个文件只包含一个 SHA-1 码,表示提交ID,比如文件testing 的内容是 6c35ebf175daff89f32b9123529fe22d93d1d8d9,因此一个分支名指向一个提交对象。

因此鼓励 早点建分支,经常建分支

分支的合并主要采用三方合并:比如当前分支是 master,执行 git merge testing,表示将 mastertesting 以及这两个分支的公共祖先进行三方合并。

特性分支(Topic Branch): 用于实现某个单一功能而存在的分支。比如有一个项目,他有3个功能,我们就开3个特性分支。

分支的合并主要有两种方法:merge 和 rebase,各有特色,rebase 能够形成更加线性的提交历史。

使用 rebase 要遵循一个规则:永远不要衍合那些已经推送到远程仓库的提交,即比如你现在在本地完成了一个功能,最新的提交为01234,你把它推送到远程仓库之后,那么你就不能再衍合01234这个提交了,因为这会导致其他人的混乱。

  • git branch <name>: 保留本地目录、暂存区的修改,并创建名为 <name> 的分支(即创建一个指针,指向 HEAD 指针指向的分支对应的提交)。
  • git checkout <branch>: 将 HEAD 指针指向 <branch> 分支名,即当前分支为 <branch>
  • git checkout <commit-id>: 将 HEAD 指针指向 <commit-id> 提交(一般 HEAD 指针都是指向分支名的),形成 detached HEAD。
  • git checkout -b <name>: 创建分支 <name>,并将 HEAD 指针指向 <name> 分支,等价于(1)git branch <name>(2)git checkout <name>
  • git branch -d <name>: 删除分支 <name>(其实只是删除分支文件而已)。
  • git branch -D <name>: 强力删除分支 <name>(Git 会自己判断是不是可以删除分支,和 -d 的区别是如果 Git 判断不能删除分支,则用 -D 可以强制删除)。
  • git branch -a: 列出全部的分支。
  • git branch -r: 列出远程分支。
  • git branch: 列出本地分支。
  • git branch --merged: 列出已经与当前分支合并的分支,通常列出的那些分支除了当前分支之外都可以删掉,因为已经没有任何作用了。
  • git branch --no-merged: 列出尚未和当前分支合并的分支。
  • git stash: 贮藏(stash)当前的状态,git专门有一个”stash栈”用来存放贮藏的多个状态。这个命令的使用场景是:你正在基于 dev 分支写代码,突然Boss说在 master 分支上出了个bug,快去修复。那么你写代码写到一半,怎么办?你想要像游戏一样先将当前状态存档,修复完了bug之后,再读档恢复到先前的状态。
  • git stash pop: 从”stash栈”弹出一个状态,并恢复该状态。具体的 stash 实现原理请看这里
  • git stash apply: 和 git stash pop 不同,这个命令并不会从”stash栈”弹出状态,只是纯粹将栈顶的状态恢复。
  • git merge <name>: 将分支 <name> 合并入当前分支。
  • git cherry-pick <commit-1> <commit-2> ... <commit-n>: 将提交 <commit-1> ... <commit-n> 的提交内容在 HEAD 指针上按顺序重演一遍。比如有一系列提交:b1->b2->b3,HEAD 指向 b3,执行 git cherry-pick b1 b2 后,变成了 b1->b2->b3->b1->b2,HEAD 指向后一个 b2。这个命令的使用场景:当在所有分支上都出现了一个共同的小bug,此时就可以通过在一个分支上修改该bug,并将该bug的提交应用在其他的分支上。
  • git cherry-pick <start-commit>..<end-commit>: 和上一个命令一样,只是将 <start-commit><end-commit> 的所有提交(不包括 <start-commit>) 在 HEAD 指针上按顺序重演一遍。
  • git cherry-pick <start-commit>^..<end-commit>: 和上一个命令一样,只是将 <start-commit><end-commit> 的所有提交(包括 <start-commit>) 在 HEAD 指针上按顺序重演一遍。
  • git rebase <name>: 将当前分支衍合到 <name> 分支。将当前分支与 <name> 分支的公共祖先开始到当前分支的改变在 <name> 分支重演一遍,然后将当前分支移到最新处,<name> 分支不动。rebase 命令可以说是自动一系列的 cherry-pick 命令。比如现在分支 b1b2 的公共祖先是 bbb1 之间有4次提交(c1,c2,c3,c4),如果当前分支是 b1,且运行 git rebase b2,则会将这四次提交(c1,c2,c3,c4)重新在 b2 分支上重演一遍(形成的Git树为:b2->c1->c2->c3->c4),并且将 b1 分支指向最新的那个提交,而 b2 分支不动。
  • git rebase --onto <SHA-1> <name>: 和上一条命令的区别在于,这条命令是从 <SHA-1> 提交开始在 <name> 分支上衍合,比如上面的例子,假设 git rebase --onto c2 b2,则将 c3,c4 衍合到 b2,即形成 b2->c3->c4,b1 分支指向 c4 提交。
  • git rebase <name> <fname>: 将 <fname> 分支衍合到 <name> 分支。
  • git rebase -i <start-commit>: 重新提交从 <start-commit> (不包括该提交)开始的所有提交。-i 表示进入交互模式,即打开一个编辑器,你可以选择删除某些提交,或修改某些提交的commit message,或合并某些提交为一个提交。因为这个命令比较复杂,这里举了例子:

我们创建了7次提交,如下:

git log --pretty=oneline # 第一步
cc21fc335bc61ee49bc325ee59250fe7fd84496a g
63fb59217d78100405867774a377bae2974718ef f
b61cece6c683809f0bfe4ddc7dc4a6672af714ac e
511e6959690aa76fbfa055c1154af925b7516571 d
a7388b246adfe7c82d402b4280b18b825528d9e4 c
eab9c3451745efdbadbcd78c49feb56ffa834ddc b
8950be78e1cc5f8d1693c418d0a23a9681bb8042 a
git rebase -i 8950be7 # 第二步
# 打开编辑器后显示如下:
pick eab9c34 b
pick a7388b2 c
pick 511e695 d
pick b61cece e
pick 63fb592 f
pick cc21fc3 g

你可以对该内容进行修改。

  • 删除某行表示取消该提交。
  • pick: 使用该提交。
  • reword: 和 pick 含义一样,但是可以修改 commit message(但是并不是直接在每行最右边直接修改,而是后来保存退出编辑器后再修改)。
  • fixup: 将该提交和上一个提交合并为一个”新”提交,新提交的 commit message 只会使用上一个提交的 commit message。
  • squash: 和 fixup 一样合并提交,只是可以自定义新提交的 commit message。
  • 如果只是想删除全部的提交,那就别用 git rebase -i 了,直接用 git reset --hard

我们可以修改如下:

reword eab9c34 b # 修改 eab9c34 提交的 commit message,等到保存退出后git会自动弹出编辑器让你自定义 commit message
#pick a7388b2 c # 删除 a7388b2 提交
pick 511e695 d # 将 511e695, b61cece, 63fb592 合并为一个提交,并使用 511e695 的 commit message
fixup b61cece e
fixup 63fb592 f
pick cc21fc3 g

当保存并退出编辑器后,git会从上到下执行每行的命令。

合并分支时会出现冲突,对于代码量很大的文件,相比手工一行一行解决冲突,使用 Beyond Compare 更加方便。

patch相关命令

“git format-patch” 命令用于生成补丁文件(.patch),将这些补丁文件给其他人可以通过 “git am \“ 应用这些补丁。在 Git 中,一次提交对应一个 .patch 文件,补丁文件的命名方式为:<id>-<message>.patch,\从 1 开始计数,\ 是这次提交的第一行 commit message。因此如果要对多个提交生成补丁,则会生成多个 .patch 文件。

  • git format-patch -n: 生成 HEAD 前 n 个提交的补丁(包含 HEAD 的提交)。
  • git format-patch -n <commit-id>: 生成 <commit-id> 的前 n 次提交的补丁(包含 <commit-id>)。
  • git format-patch -1 <commit-id>: 仅生成 <commit-id> 一次提交的补丁。
  • git format-patch <start-commit-id> <end-commit-id>: 生成 <start-commit-id> 开始到 <end-cimmit-id> 提交的补丁(包含 <start-commit-id>, end-commit-id)。
  • git format-patch <commit-id>: 生成 <commit-id> 之后的所有提交的补丁(不包含 <commit-id>)。
  • git format-patch --root <commit-id>: 生成从树根到 <commit-id> 的所有提交的补丁(包含 <commit-id>)。
  • git am bugfix.patch: 应用 bugfix.patch 补丁文件。

生成多个补丁文件给他人会让别人应用补丁比较麻烦,因为不得不一个一个应用,因此可以通过 git format-patch -3 --stdout > bugfix.patch 将所有生成的补丁合成一个 bugfix.patch 文件。

远程仓库相关

远程仓库相关有两个概念:

  1. 远程分支 :对远程仓库状态的索引。引入远程仓库之后,分支就分为了本地分支和远程分支,本地分支名字以 <分支名> 形式存在,远程分支名字以 <远程主机名>/<分支名> 形式存在。比如在克隆远程仓库后,就会出现 origin 的远程主机名,对应的URL是 https://..../xx.git,而远程仓库的 master 分支是 origin/master
  2. 追踪分支 :在引入远程仓库之后,本地分支和远程分支会有一种追踪(相关联)的关系,比如本地分支 master 和远程分支 origin/master 拥有追踪关系,这种追踪关系可以自定义,也可以由 Git 自动赋予。

对远程分支的进一步解释:

他只是对远程仓库状态的索引。比如别人在远程仓库新建了一个 test 分支,而这个分支在你本地是没有的,则当你 git fetch 之后,本地只会有远程分支 origin/test,而不会有本地分支 test,你需要通过 git checkout -b test origin/test 创建内容和远程分支 origin/test 一样的本地分支 test

  • git clone <url>: 克隆远程仓库 <url>,并赋予远程主机名 origin,这是克隆所有分支。
  • git clone -b <branch_name> <url>: 克隆远程仓库 <url><branch_name> 分支到本地。
  • git clone -o <remote> <url>: 克隆远程仓库 <url>,并赋予远程主机名 <remote>
  • git push <remote-name> <local-branch>:<remote-branch>: 将本地 <local-branch> 分支提交到远程主机名为 <remote-name><remote-branch> 分支。
  • git push origin master:master dev_01:dev_01 dev_02:dev_02: git push 命令支持同时做几个推送命令,这条命令是同时推送 master, dev_01, dev_02 分支到远程仓库。
  • git push <remote-name> <local-branch>: 将本地分支 <local-branch> 提交到远程主机名为 <remote-name> 的与本地分支有追踪关系的远程分支。
  • git push <remote-name> --force <local-branch>:<remote-branch>: 假设你改错了个东西,并且提交到了远程仓库,你想回到旧版本,并更新到远程仓库,那么你在本地 git reset 到旧的 commit 后,使用上面的 git push 命令是提交不上去的(报错:”Updates were rejected because your current branch is behind its remote counterpart”),这时可以使用这条命令,即加 --force
  • git push <remote-name> :<remote-branch>: 删除远程主机名为 <remote-name><remote-branch> 分支。
  • git fetch <remote-name>: 从远程主机 <remote-name> 中取回所有更新,远程主机的分支在本地会用 <remote-name>/<branch> 表示,其实这也是个分支名而已(这种形式的分支称为“远程分支”)。如果你想合并入本地的 <branch> 分支,则可以用 git checkout <branch>git merge <remote-name>/<branch> 合并;也可以用 git pull <remote-name> <branch> 直接完成。
  • git fetch <remote-name> <branch>: 取回远程主机 <remote-name> 中的分支 <branch> 的更新。
  • git remote add <name> <url>: 添加远程主机名 <name>,并用他代表 <url>
  • git remote rm <name>: 删除远程主机名 <name>,并不是真的删除远程仓库内容。
  • git remote -v: 列出可用的远程主机名。
  • git remote set-url <name> <url>: 重新将远程主机名 <name> 对应的地址是 <url>
  • git remote rename <old> <new>: 重命名远程主机 <old><new>
  • git pull <remote-name> <remote-branch>:<local-branch>: 取回远程主机 <remote-name> 的分支 <remote-branch> 更新,并将他与本地分支 <local-branch> 合并。
  • git pull <remote-name> <remote-branch>: 取回远程主机 <remote-name> 的分支 <remote-branch> 更新,并将他与当前分支合并。

Github 相关

Github 是 Git 仓库的托管平台(又被称为”全球最大的同性交友网站”),但是发展到现在,他其实已经变成了一个团队协作的平台(Github 的口号是”Build Software better, together”),Google Code 也因为他关闭了,Google 将他的仓库都搬到 Github 上了。

Github 又多了 Social 的概念,比如 Follow People, Watch Repositories,你也可以通过 Explore 找到更多的 Projects 或 People。

Github for Mac 是 Github 官方推出的客户端,非常好用,他弱化了 Git 的概念,比如 push,pull 等,而用 Sync 代替了 pull+push。在2015年8月12日又推出了新版,新增功能有:

  • 提出了”Start contributing, natively”,即在本地客户端就可以完成 Pull Request,而不需要到 Github 网站上。
  • 按行提交,而不是将整个文件的改变一起提交。
  • 仅仅通过单击分支的方式就切换分支。

1. Github 基本概念

  • Star: 收藏项目,类似于浏览器当中的加入书签功能。
  • Watch: 除了收藏,当该项目收到 Pull Request 时,都会通知你。
  • Fork: 只有你想贡献代码给该项目,才用 Fork,一般都用 Star,因此流传着一句话:不贡献代码的 Fork 就是耍流氓!

有一个很普遍的问题,就是 Fork 之后,如果原作者的代码更新了,在你自己 Fork 的项目中是看不出更新的。最简单的解决方法是重新 Fork;复杂一点的是 merge。

  • Issues: Github 每个项目都有一个 Issues 模块,官方定义:”Issues are used to track todos, bugs, feature requests, and more.”,简单地说就是项目的小型论坛,你可以在里面提任何的问题,可以回复别的Issue,但是 Issues 的最终命运是要被关闭的,即问题解决了就关闭。

2. 给开源项目贡献代码

作为一个有上进心的程序员,为开源世界做贡献是很常见的愿景。

假设这次要贡献的是 JQuery,地址是:https://github.com/jquery/jquery。方法如下:

  1. Fork 别人的项目成为自己的项目,地址是 https://github.com/xiazdong/jquery
  2. https://github.com/xiazdong/jquery Clone 到本地。
  3. 修改代码。
  4. 提交到远程仓库 https://github.com/xiazdong/jquery
  5. New Pull Request 等待原作者的审批。

3. Github Flow

Github Flow 是由 Github 人员总结的一套使用 Git 的工作流程如下图:

上图中的编号解释:

  1. 将队友加入项目的 Collaborator。
  2. 每个功能都新建一个特性分支。
  3. 在特性分支上做开发。
  4. 开发完之后提交 Pull Request。
  5. 提交完后可以在所在的 Pull Request 讨论留言,作为 Code Review。
  6. 当达成一致时,Merge Pull Request 到 master 分支,其中 master 分支的代码都是可以直接部署运行的,即最稳定的,不能直接在 master 上改代码。

团队合作流程 有对 Github Flow 进行详细介绍。

一般在公司里的开发习惯是:如果2015年8月12日要发一个版本,那么新建一个 dev_20150812 分支,但是不要再该分支上修改,而是每次都新建一个分支(比如 dev_20150812_feature1),修改完了之后再发一个 merge request 到 dev_20150812。

4. Github 支持 Git LFS

原来,根据 官方说明,Github 对于每个仓库的大小限制在 1G,仓库中每个文件的大小限制 100M,如果文件大小大于 50M,那么 push 时会出现 warning。

但是在2015年4月8日,Github官方宣布支持 Git LFS 扩展,每个仓库默认有 1G 的大文件存储空间,每个月带宽也是 1G。Git LFS(Large File Storage) 的官方定义是”command line extension for managing large files in Git”,即 Git LFS 是为了让 Git 更好地支持大文件管理而实现的一个命令行的扩展,即你只要安装好了之后,就能够使用类似 git lfs 使用 LFS,即将 LFS 集成到 Git 中。

Git LFS 的实现思路可以从上面的图中看出:有一个 Git Large File Storage Server(位于右上角)专门存储大文件,而 Git 仓库只是使用一个”text pointer”(仅仅是一段文本,作为指针)来记录大文件的位置。原来 Git 对大文件管理支持的不好是因为加入大文件之后,Git 仓库就会变得非常大,管理起来就速度很慢,而现在有了这种方法之后,Git 仓库不会因为大文件的加入而体积变大。

在 Github 中,Github 既充当 Git 仓库的角色,也充当 LFS Server 的角色(地址是:”https://media.githubusercontent.com/media/xiazdong/...")。

Git LFS 的安装教程见这里,官方文档见这里

目前我已经收到 Github 的邮件,说已经正式公开支持 LFS 特性。

这里列举几个常用的命令:

  • git lfs --h: 查看帮助。
  • git lfs track <file>: LFS 开始跟踪 <file>
  • git lfs track: 列出 LFS 跟踪的文件。
  • git lfs untrack <file>: LFS 取消跟踪 <file>
  • git lfs push: 将 LFS 跟踪的文件 push 到 LFS Server。
  • git lfs status: 查看 LFS 的状态。

Gist

  • 英文定义:Gist is a simple way to share snippets and pastes with others. All gists are Git repositories, so they are automatically versioned, forkable and usable from Git.
  • 中文定义:Gist是一种分享小段代码给别人的简单的方式。每个 Gist 都是 Git 仓库,他被自动进行版本控制,他也能被 fork。

Gitter

Gitter 是一款专门为 Github 打造的聊天应用(标语为”Chat, for GitHub.”),你可以为某个你的项目新建一个聊天室,并且邀请一些人在里面讨论。目前支持 Web, Mac, IOS, Android。

用下来感觉是不错,而且还跨平台,逼格特别高,但是其实直接建个QQ群也挺好使的。

Tig

Tig 是一款字符模式的 Git 操作界面。不过不容易上手。

  • 输入 tig 进入 Main View。可以看到一系列提交 log。可以通过”上下键”上下选择具体某个提交。
  • 选中某个提交后,输入 “Enter” 查看提交的信息。输入 “s” 进入这次提交的 Status View,即查看这次提交修改了哪些文件。输入 “t” 查看这次提交的 Tree View。
  • 输入 “q” 回上一级。
  • 输入 “h” 进入帮助界面。

我目前还没有适应这种使用 Git 的方式,还是比较推荐 Tower。

参考文献

附:视频教程

快过年了,附上点福利吧~

支持一下
扫一扫,支持xiazdong