Jade Dungeon

提交修改

建立git工程

新建立的工程

git init git-demo

初始化一个新的工程,会建立.git目录保存信息。add为当前项目建立快照(snapshot) 的索引(index file)。

提交时会在编辑器里要求输入备注。但是要注意第一行要少于50字,如果有第二行, 一定要空白。

拷贝过来的工程

拷贝过来的工程目录中要带上.git目录,然后执行git init就会重新恢复版本库 为可用状态了。

忽略指定文件

共享的gitignore

有很多文件是我们不想放到版本库中的,比如编辑过程中的临时文件或是程序构建中的临时 文件。可以在项目根目录下的.gitignore文件中指定排除规则:

*.a           # 忽略所有.a结尾的文件
!lib.a        # 但不包括 lib.a
/TODO         # 仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/        # 忽略 build/ 目录下的所有文件
doc/*.txt     # 忽略如 doc/aa.txt,但不包括 doc/subdir/aa.txt

独享的gitignore

.git/info/exclude里配置的不会共享

全局独享的gitignore

设置一个文件为全局独享的gitignore:

git config --global core.excludesfile=/home/bob/.gitignore

工程结构

工程目录

git rev-parse --git-dir        # .git目录的位置
git rev-parse --show-toplevel  # 工作区根目录
git rev-parse --show-prefix    # 根目录到当前目录的前缀
git rev-parse --show-cdup      # 移动到根目录

区域

对于git来说,可以理解为有三个区:

  • Working Tree:就是现在正在开发的项目。
  • Snapshot Index:快照/索引。就是一个缓冲区,让用户准备哪些东西要提交到版本库。
  • HEAD:就是版本库。

查看工作状态

在检查修改内容的过程中,还可以查看当前工作的状态:

git status

关于提交、回退与状态的概念图

提交、比较、回退

与提交相关的对象:

提交对象

撤消提交

找到需要回退的那次commit的 哈希值,

git reset --hard commit_id 

比较差异

设定比较工具:

[diff]
        tool = vimdiff
[merge]
        tool = vimdiff
[difftool]
        prompt = false

调用设定好的工具:

git difftool

快照

添加文件到快照

添加指定文件:

git add filename...

当前目录下所有文件:

git add .

把所有新增和改动的文件都加到暂存区:

git add -A

以交互命令方式增加文件:

git add -p

# 选好以后
git commit

取消对文件的修改

git checkout <file-name>

以交互方式选择要撤消的修改:

git checkout -p

提交

提交已经快照的内容:

git commit -m '<备注>'

提交所有已经在git管辖范畴中的文件:

git commit -a -m '<备注>'

重建基准

重建基准会改变历史记录,所以只能在个人专用的分支上做。

以交互方式重建基准:

git rebase -i <提交ID>

修复rebase造成的损坏

# 切到 本地与远程不一致的分支
git reset --soft <最后一次分叉前的提交>
git commit -m 'your message'
git push -f

移动与删除文件

删除文件

git rm filename...

不想一个一个输入文件名,可以直接在工作区删除文件,然后:

git add -u

把本地的修改与删除都快照。

恢复删除的文件

比如恢复前一版本删除的文件:

git checkout HEAD~1 -- hello.txt

移动文件

git mv file-from file-to

相当于下面三条命令:

mv file-from file-to
git rm file-from
git add file-to

修改最后一次提交

有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才 的提交操作,可以使用--amend选项重新提交:

git commit --amend

此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行 此命令的话,相当于有机会重新编辑提交说明,而所提交的文件快照和之前的一样。

启动文本编辑器后,会看到上次提交时的说明,编辑它确认没问题后保存退出,就会使用 新的提交说明覆盖刚才失误的提交。

如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行--amend提交:

git commit -m 'initial commit'
git add forgotten_file
git commit --amend 

暂存

把当前的工作区与快照封存起来,然后恢复为上一次提交。

  • 暂存:git stash。可以多暂存多次。
  • 取出最近一次:git stash pop
  • 每个暂存的标识:stash@{n}
  • 显示:git stash list。显示所有的暂存。
  • 比较暂存与暂存或其他提交:git diff stash@{1} stash@{2}
  • 恢复指定的暂存:git stash apply stash@{n}
  • 清空暂存列表:git stash clear

提交规范

避免在一次提交中有不相关的改动

你可能在一个已经做了其它改动的同一个文件里发现了一个错字或做了微小的代码重构, 但是一定要抵制诱惑与主要的改动一起记录,除非它们是直接相关的。

避免不相关改动间的推导

坚持基于行的编码风格,允许你添加,编辑或删除列表中的值而不用改动相邻行。一些例子:

var one = "foo"
    , two = "bar"
    , three = "baz"   // Comma-first style allows us to add or remove a
                      // new variable without touching other lines
 
  # Ruby:
  result = make_http_request(
    :method => 'POST',
    :url => api_url,
    :body => '...',   // Ruby allows us to leave a trailing comma, making it
  )                   // possible to add/remove params while not touching others

你为什么要使用这样的编码风格?好吧,总得想想将要git blame这个的人们。在 JavaScript的例子里,如果你要添加一个值「baz」然后提交,当有人blame添加「bar」的行时 你不想你的名字呈现把,因为这两个变量可能不相关。

在push之前总是清除你的历史。

如果提交还没有被共享,那么重新修订它们中不好的部分是安全的。下面的可能是Faraday 项目的永久历史,但是我最终把它压缩为仅2次提交,并编辑它们的信息以隐藏我首次设置 脚本遇到了麻烦这样一个事实。

备注规范

总是编写提交信息

总是编写提交信息,好像你在向正坐在你旁边且完全不知道整个事情缘由的同事解释此次 改动。每个Thoughtbot给出的提示都是为了更好的提交信息:

回答下面的问题:

  • 为什么此次改动是必需的?
  • 它是如何解决问题的?
  • 这次改动有什么副作用?
  • 考虑包含一个[to the discussion.] 的链接。

备注标题

第一行应该少于50个字。 随后是一个空行 第一行题目也可以写成:Fix issue #8976

喜欢用 vim 的哥们把下面这行代码加入 .vimrc 文件中,来检查拼写和自动折行

autocmd Filetype gitcommit setlocal spell textwidth=72

例如注释有这样的(转载)

一般情况下,提交 GIT 时的注释可以分成几类,可以用几个动词开始:

  • Added(新加入的需求)
  • Fixed(修复 bug)
  • Refacotry(重构)
  • Changed(成的任务)
  • Updated(完成的任务,或者由于第三方模块变化而做的变化)

尽量将注释缩减为一句话,不要包含详细的内容。 假如有 Issues 系统,其中可以包含 Issue 的 ID。比如:Issue #123456 包含作者的信息。比如 by Bruce

完整例子:

git commit -m 'Issue #[issue num] by [user]: [Short summary of the change].'

个人的习惯

  • 修 Issue 就写:fixed #XX
  • 小改直接就用一句话说清楚。
  • 大改的,自己建一个 Issue 说清楚情况、方案、变化。。。。,然后同 1

这里还有一个好处是,commit log 里面的#XXGitHub会显示成指向对应Issue的链接, 对应地Issue里面也会出现这条Issue被哪个commit引用的提示。

更屌炸天的是,类似fixed #XX这样的,GitHub 还会自动帮你把那条Issue给close掉。

其实最重要一点,commit log 是给人类看的,说清楚就好,不必太过拘谨,更不能写成 只给机器看的东西。

  • Rem: deprecate unused modules, 表示移除(Remove)
  • Ref: improved the implementation of module X, 表示重构(Refactory)

有同学要问了:如果一个commit里的内容无法用上述任意一种语句陈述,应该怎么办? 同学,那说明你的commit应该被拆分成多个小部分

当然我最喜欢的commit message还是第一个commit,内容是First Blood

压缩文件

压缩归档时忽略临时文件与配置文件:

# 归档最新
git cachive -o lastest.zip HEAD

# 指定 src 与 doc目录
git cachive -o partial.tar HEAD src doc

# 指定里程碑版本v1.0,并给归档文件添加目录1.0
git archive --format=tar --prefix=1.0/ v1.0 | gzip > foo-1.0.tar.gz

配置多个过程仓库

名字不同的多个远程库

修改.git/config,原来有一个叫作origin的远程:

[remote "origin"]
  url = git@git.dev.sh.ctripcorp.com:qwshan/jsp-myadmin.git
  fetch = +refs/heads/*:refs/remotes/origin/*

现在再加一个叫作github的远程:

[remote "github"]
  url = git@github.com:Jade-Shan/jsp-myadmin.git
  fetch = +refs/heads/*:refs/remotes/github/*

添加完以后,先和名为github的远程同步一下:

git pull github master

然后再推送上去:

git push github

名字相同的多个远程库

名字一样的话,只要push一个就可以都上去了:

[remote "origin"]
  url = git@git.dev.sh.ctripcorp.com:qwshan/jsp-myadmin.git
  url = git@github.com:Jade-Shan/jsp-myadmin.git
  fetch = +refs/heads/*:refs/remotes/origin/*
git push origin