Git 手册

基本操作 创建版本库: git init 克隆版本库 git clone /path/to/repo git clone user@host:/path/to/repo 多版本库: git remote add <repo-name> <repo> 多版本库列表: git remote -v 更新版本库 git pull [repo-name] repo-name 默认为 origin git pull --rebase 可以避免无意义的 pull-merge, 使版本树尽可能是一条直线 git fetch [repo-name] - git pull does a git fetch followed by a git merge repo-name 默认为 origin 会把 repo-name 的更新都 fetch 到本地, 包括新分枝和新标签, 但不会建立本地分枝 提交到本地 stage: git add <file/dir> git commit -m "<comment>" 对于已经处于版本控制中的文件, 可以使用 git commit -am "<comment>" 略过 stage 有时需要构造空提交: git commit --allow-empty -m "EMPTY" 提交到远程 git push [repo-name] [branch] repo-name 默认为 origin, branch 默认为当前分枝 git push -u <repo-name> <branch> 设置 push/pull 时默认使用的 repo-name git push <repo-name> <branch>:<remote-branch> - 如果本地和远程分枝名字不同 反悔 revert stage: git reset HEAD <file> reset working: git checkout -- <file> restore(1+2): git checkout HEAD <file> checkout 上一个版本, 放在 stage/working 中: git checkout HEAD~ <file> 如果改动, add, 再改动, 使用 1. 不会恢复到第一次改动的结果; 使用 2. 才行! git reset <rev> [file] 没有 file 把 HEAD/branch 指向 rev 有 file 把 rev 版本的 file checkout 出来 如果 --hard, 那么 stage 和 working 都更新为 rev; 如果 --soft, 那么 stage 和 working 都不变(但 git status 会发现有文件 stage 了, 这是相对运动的结果); 如果 --mixed (默认), 那么只更新 stage (这时 git status 会发现 working 中有改动, 原因同上) rev 默认为 HEAD: 如果 --hard, 那么 stage 和 working 都更新为 HEAD (和 3. 效果一样); --soft 没意义; 如果 --mixed, 那么只更新 stage (就是 1. 咯~) 没有 file 时 --hard 类似于 checkout, 区别在于 reset <rev> 会移动 branch 指针, checkout <branch> 回不去, 而 checkout <rev> 再 checkout <branch> 可以回去 放弃所有本地改动(含本地提交): git reset --hard <repo-name>/<remote-branch> git checkout <rev> [file] 没有 file 会建立一个 detached HEAD (匿名分枝) - 最好别用! 有 file 把 rev 版本的 file checkout 出来 rev 默认为 -- 即 stage, 从 stage checkout 到 working 其它 rev 从 rev checkout 到 stage 再到 working git revert <rev>: 类似 svn 中的倒着 merge, 生成一个新版本 V+, 和 V-完全一样 - 仅用于 revert 一个很久以前的错误改动! 修改已经 push 到远程的提交 使用下面的方法修改已经 push 到远程的提交 git push <repo-name> +<branch>:<remote-branch> 修改上一个提交: git commit --amend 修改多个提交(交互式) git rebase -i <rev>, 编辑 rev 之后的提交, rev 默认为 repo-name/remote-branch 对于想合并到前一个的提交, 选择 fixup 对于想删除的提交, 直接删除对应的行 将上 N 个提交合并成一个 git reset --soft HEAD^(N-1) git commit --amend 分枝 建立分枝: git checkout -b <branch> 在当前 HEAD 上创建分枝并切换到新分枝 git checkout -b <branch> <repo-name>/<remote-branch> 跟踪远程分枝 切换分枝: git checkout <branch> 删除分枝: git branch -d <branch> 对于没有合并或提交到远程的分枝, 删除时会提示用 -D 强制删除 将分枝提交到远程(在远程建立新分枝): git push <repo-name> <branch> 提交到远程之后, 如果希望能够 pull 该分枝, 需要 git push -u 删除远程分枝: git push <repo-name> :<remote-branch> 删除已删除远程分枝的本地缓存: git remote prune origin 查看所有分枝: git branch -av git branch 不显示远程分枝 diff 分枝 diff: git diff <src-branch> <tgt-branch> (tgt 默认为当前分枝) 如果是和 master 比较, src-branch 应该是master 版本 diff: git diff <src-rev> <tgt-rev> (tgt 默认为当前版本) src-rev 应该是比 tgt-rev 更老的版本(祖先版本) git merge-base <src-branch> <tgt-branch> 可能可以得到你需要的 src-rev 其它 diff working 和 stage 的 diff: git diff stage 和 HEAD 的 diff: git diff --cached working 和 HEAD 的 diff: git diff HEAD 注意 上述 3 种方式都不会 diff 新文件(未处于版本控制中的文件) 合并 合并 branch 到当前分枝: git merge <branch> fetch 后用 git merge <repo-name>/<remote-branch> merge 如果 merge <branch>, 修改 branch, 再 merge, 只 merge 修改的部分 合并冲突时, 不 merge 而是指定某个版本: git checkout --ours/--theirs <file> “复制” 一次提交(比如某个分枝只有一次提交需要保留): git cherry-pick <rev> 停止 “复制”: git cherry-pick --abort 将当前分枝 rebase 到 tgt-branch: git rebase <tgt-branch> merge 与 rebase 的区别 merge 建立了一个新版本, 而 rebase 是分枝上有多少新版本, 就会建立多少新版本 rebase 后需要再做一次快速 merge, 才能使 tgt-branch 的指针正确(两者指针移动方式不同) 查看状态信息 git status git log git reflog: 查看 HEAD 指针的移动历史 git branch --merge: 查看哪些分枝已被合并到当前分枝 git branch --no-merged: 查看尚未合并的分枝 git remote show <repo-name>: 查看远程信息 暂存 git stash git stash pop git stash list git stash clear git stash apply stash@{x} git stash drop stash@{x} git stash branch <branch> 标签 git tag # 标签列表 git tag <tag> # 在当前版本上打标签 git show <tag> # 查看标签 git tag -d <tag> # 删除标签 git push <repo-name> <tag> # 提交标签 git push <repo-name> :<tag> # 删除远程标签 submodule @2015.04.13 git submodule add <remote-path> [<local-path>] # 添加 remote-path 为当前项目的 submodule git rm -r <local-path> # 如果有问题, 试试 git rm --cached <local-path> vim .gitmodules # 通常会在 git rm 时自动执行 git commit rm -rf .git/modules/<local-path> # 可能不必要 vim .git/config

December 3, 2013 · 预计阅读时间 4 min · Uraka Lee

How to code review

引 版本控制工具:Git 代码 review 工具:ReviewBoard 代码 review 需要积极评估代码的设计和清晰程度,项目 owner 还要确保被review 代码的逻辑正确性,而不仅限于命名和格式 准备工作 用 ldap 账号登录 确保参与 review 的人都需要登录过,否则会找不到人 确保你要 review 的代码的 git 已经加入 reviewboard 确保你要 review 的代码基点已经 push 到 git 服务器 代码基点: git diff <src-rev> <tgt-rev> 中的 <src-rev> 提交一个 review 生成diff git diff <src-rev> <tgt-rev> > d1.diff 一般来说src是tgt的父节点 如何得到 <src-rev> 对于开分枝 / 开发 / merge 的工作模式而言 <src-rev> 就是你开分枝时分枝点的版本 可以用 git merge-base <src-branch> <tgt-branch> 得到 <src-rev> 比如你要从 work 分枝 merge 回 master,则执行 git merge-base master work 得到 <src-rev>,前提是你没有 merge 过 但如果你已经 merge 过,这样得到的 <src-rev> 是从最后一次 merge 的版本 对于在一个分枝上持续开发的工作模式 查看git log 找到需要的 <src-rev> 应该尽量在代码稳定时再 merge:勤开分枝,慎重 merge 应该尽量使用 amend,减少 commit,git log 才好找 访问 reviewboard,点击 New Review Request 选择 Repository,选择 diff 文件,点击 Create Review Request 点击 View Diff,查看上传结果是否和预期一致 填写 Summary / People / Description,注意 people 是 ldap 账号名,用 tab 补全,别按回车 确认提交,系统会发邮件给 people,通知他们来 review 更新一个 review 修改代码提交之后生成新 diff git diff <src-rev> <tgt-rev2> > d2.diff 注意 确保在一次 review 中,所有 diff 的 <src-rev> 都是同一个 访问 reviewboard,打开要更新的 review 只能更新自己提交的 review 点击 Update 下拉菜单中的 Update Diff,选择 diff 文件,点击 Upload 点击 View Diff,查看上传结果是否和预期一致 填写本次更新的内容,确认提交 review 别人的代码 点击邮件通知中的链接,登录 reviewboard 点击 View Diff,查看被修改的代码 点击有问题的行,填写有什么问题 继续 review,直到所有代码都 review 完毕 点击页面上方悬浮条中的 Edit Review,预览所有你填写的内容 如果没有问题,点击 Publish Review 如果需要修改,可以直接在预览页面上修改 如果需要增加,点击 Cancel,增加,再 Edit Review 如果代码较多,可以 review 一页提交一页 根据别人的 review 修改代码 别人提出的每个问题都会生成一个 issue 对照这些 issue 修改代码;如果 fix 了,就点 fixed;如果有异议,可以回复讨论;如果确认不需要改,就点 drop 修改完成后,提交代码,生成新的 diff 更新 review,继续,直到所有 issue 都 fixed 或 drop

December 1, 2013 · 预计阅读时间 1 min · Uraka Lee

我的 Git 配置

vim ~/.gitconfig [color] ui = true diff = true status = true branch = true interactive = true [alias] st = status ss = status -s ci = commit ca = commit --amend co = checkout ll = log --graph --color --format=format:'%C(bold)%h%C(reset) -%C(bold)%d%C(reset) %C(white)%s%C(reset) %C(bold red)- %an' --abbrev-commit ft = fetch pl = pull --rebase br = branch cp = cherry-pick mg = merge rb = rebase dci = dcommit sbi = submodule init sbu = submodule update sbp = submodule foreach git pull sbc = submodule foreach git co master

November 28, 2013 · 预计阅读时间 1 min · Uraka Lee

我的 Vim 配置

vim ~/.vimrc # 行号 set nu # tab 转空格 set ts=4 set expandtab set autoindent # 语法高亮 syntax enable set background=dark colorscheme solarized solarized 是一套很棒的配色方案,请参考 vpsee 中的方法进行配置。

November 28, 2013 · 预计阅读时间 1 min · Uraka Lee

Mac 图标缓存的二三事

信息焦虑的第三个症状:随时需要“清理”自己的电脑,虽然完全不知道究竟“清理”了啥。 CleanMyMac 满足了我的这个愿望,但也给我带来了一些麻烦。首当其冲的就是图标缓存问题——每次清理完之后,缓存就会瘫痪,不仅 Launchpad 中有的图标显示不出来,Alfred 也难以幸免,最让人无法接受的是 Finder 会崩溃(Path Finder 貌似无碍)。好在重启之后一切如常。 今天终于不淡定了,因为重启也不能解决问题了!按照 Apple 社区上给出的解决办法,打开终端,运行命令,图标找回,分组消失。。。 rm ~/Library/Application\ Support/Dock/*.db killall Dock 好吧,本来分组这种东西也是我蛋疼搞的,消失就消失吧,有 Alfred 呢。。。 Update@2013.11.26: 最新版的 CleanMyMac 2.2.0 貌似不会有清理完缓存瘫痪的问题了 Update@2015.01.21: 最新版的 MacOS 10.10 已经不能用上述命令行清理 Launchpad 了 Update@2016.03.10: 可以使用如下命令重置 Launchpad 图标 defaults write com.apple.dock ResetLaunchPad -bool true; killall Dock

November 25, 2013 · 预计阅读时间 1 min · Uraka Lee

纠结的 Alfred 与 Spotlight

既然已经不用 Spotlight 了,就打算把它的索引彻底关掉。Google 到若干相关文章,操作也都很简单,Apple 官网也有,于是就照着做了。 然后 Alfred 就挂了。新安装的 app 在 Alfred 中找不到,清了 cache 之后更是啥都没有了。 继续搞!没有找到确切的答案,但也能根据现有知识猜个差不离。无非是 Alfred 要调用 Spotlight 的结果,所以 Spotlight 关得不对 Alfred 也就不 work了。 于是还得打开 Spotlight,但是怎么打开还要考虑一下。目标是:Application 索引,而其它不索引。所以先把 HD 加到 privacy 中,再把 HD 下面 Application 之外的都加到 privacy 中;重启;查看一下索引大小,大概 12K;最后把 HD 从 privacy 中移除,DONE! PS1: Spotlight 索引文件的位置(from Apple 社区)在磁盘根目录 / PS2: 查看其大小需要 root 权限: sudo du -sh .Spotlight-V100 PS3: Path Finder 居然也要调用 Spotlight 的结果 PS4: Spotlight 真应该搞白名单机制,黑名单太纠结了

November 25, 2013 · 预计阅读时间 1 min · Uraka Lee

Octopress 新手小记

信息焦虑的第二个症状:换过好几个博客,但都因为配置复杂、格式不可控、备份麻烦等原因放弃了。 作为一个程序员,深深地为自己不能迅速地把博客搞定而惭愧。在构思许久之后,终于还是决定先偷懒,把习惯陪养起来再说。Octopress 还是非常符合我审美的,没有太多可配置的东西,所有格式都能自己掌控,更不用说备份——噢!又犯病了。。。 唐巧的博客很容易搜到,但我还是建议第一次搞的同学看一下官网。如果你像我一样在使用最新版的 MacOS,那么 ruby 部分就会简化许多: git clone git://github.com/imathis/octopress.git octopress cd octopress sudo gem install bundler bundle install rake install rake new_post['Article Name'] 写作就是 MarkDown 了, 虽然和 wiki 的写法有些差异, 容易记混. 通常我都会使用 Sublime Text + MarkDown 插件, 同时运行 rake preview 进行调试(-___-b). Preview 模式下, published:false 的文章会是可见的; 可能由于 bug, 如果你开着 preview 运行 deploy, published:false 的文章也会是可见的… rake deploy 前务必退出 preview, 并运行 rake generate. rake deploy 之前,要 git add 和 git push origin source, git 新手(像我)往往都习惯于顺手来个 git pull。唔,然后就悲剧了。因为这个时候 git 已经很傻地把你的 source 分枝和远程的 master 分枝关联在一起了,所以 pull 的时候会没头没脑地要 merge 这两个分枝。已经 merge 的怎么回退我就不说了,还没 pull 的,先修改一下 config 文件。 ...

November 24, 2013 · 预计阅读时间 2 min · Uraka Lee