最近把版本管理系统换成git了,果然非常好用,难怪大家都在推荐。
首先不要有心理障碍,那些名词都是吓唬人的。所谓的“非线性开发”无非是指强大的branch和merge的能力,“分布式版本管理”就是说每人自己都有一套本地的repository,不存在一个集中的版本服务器。
给我带来的最直接的好处有:
1. 傻瓜都会的初始化,git init, git commit -a, 就完了。对于随便写两行代码就要放到SCM里的人来说,再合适不过。也可以拿git做备份系统,或者同步两台机器的文档,都很方便。
2. 绝大部分操作在本地完成,不用和集中SCM服务器交互,终于可以随时随地大胆地check in代码了。
3. branch管理容易多了,无论是建立新的branch,还是在branch之间切换都一条命令完成,不需要建立多余的目录。
4. branch之间merge时,不仅代码会merge在一起,check in历史也会保留,这点非常重要。
工具之所以好,除了方便好用,还在于它帮助并鼓励你做正确的事情。频繁check in是一件很好的事情,好处我不多说了,git就鼓励你频繁check in。branch也是一件好事情,我们大多很怕branch因为它太麻烦了,去掉这层心理包袱,branch可以让我们的开发工作很有条例。
还有一些实用的功能,比如bisect,用二分法来寻找regression,你以前手动做过这种事么?我做过。以后如果要做就不会怕了。还有stash,做hot fix非常方便。
如果正在用svn,劝服所有合作开发者使用git之前,可以先用git-svn,和svn整合得非常好。
分布式版本管理系统取代集中式版本管理系统,只是时间的问题了。
=============================
git 之五分钟教程
http://www.linuxsir.org/bbs/thread281294.html
yubao.liu at gmail.com
dieken at newsmth
我懒,估计读者也是懒的,所以写点小文供大家了解一下 git。
git 是一个分布式版本管理工具,关于它大家应该都有所耳闻了,
貌似 Linus 说是 2005 年 4 月 17 日公开的,如今已经 1.4.3.4
了,开发社区非常活跃,一如 Linux (Linus 英明神武,sigh),
git 现在的维护者是 Junio C Hamano,主页在 http://git.or.cz 。
关于分布式版本管理工具的定义,我土,感受最深的是每个人都有
自己的代码库,这个跟 SVN 不同,这个区别带来的好处是跟踪本地
的修改过程非常方便,SVN 里头不同代码库之间不能 switch,麻烦。
但是 SVN 的用户界面实在是比 git 友好得多,特别是 SVN 建立
在大家普遍比较熟悉的 CVS 模型上。
闲话少说,切入正题。
* git 的四种对象
在 git 中最基本的四种对象为 blob,tree,commit,tag。
blob - 即文件,注意只包含内容,没有名字,权限等属性(例外的是
包含大小)
tree - 所有文件名字以及其属性的列表,这些属性只是基本属性,比如
权限,是否符号链接。git 没有像 svn 那样丰富的 property 用。
commit - 表示修改历史,描述一个个 tree 之间如何联系起来的,每一个
commit 对应有一个 tree —— commit 的修改结果。commit 包含
作者、提交者、parent commit、tree、log、修改时间、提交时间,
注意 git 明确区分了 author 和 committer 这两个角色。
tag - 标签,它可以指向 blob, tree, commit 并包含签名,最常见的是
指向 commit 的 GPG 签名的标签。
blob,tree,commit 都是用其存储内容的 SHA-1 值命名的(不是简单的对
整个文件取 SHA-1 值),tag 自然使用的是普通名字。
git 的想法就是 blob 和 tree 中包含的只能是最公共的最基本的信息,
所以它不会在 blob 和 tree 里头存放 svn 那么多的自定义属性。
git 也只是定位在 content tracker,这个术语跟 VCS 是有微妙的差别的,
第一是体现在它对 blob 和 tree 的“纯数据”的要求上,第二是对分支
合并的处理上,如下所示(a, b, c 表示三个 commit):
-- a ----> master
\
b ----- c -> test
test 分支从 master 的 a 点上分出来,做了许多修改,然后当 master
分支和 test 分支合并会怎么样呢?按照通常的 CVS/SVN 的做法,是创建
一个新的 commit,继承自 a 和 c,但是 git 则是直接修改 master
使其指向 c,在 git 这称为 fast forward,整个版本图变成这样:
-- a -- b -- c --> master
\
`--> test
从图中看 master 分支和 test 分支分不出主次,这里头的含义很值得玩味。
* 选择对象
blob, tree, commit 都是用其 SHA-1 命名的,如果得到一个 SHA-1
值,但不知道是什么类型,可以用
git cat-file -t SHA-1
命令识别,它会输出 blob, tree, commit (如果给它一个 tag 名字,
它也能输出 tag) 这样的字符串,然后就可以用
git cat-file type SHA-1
查看其内容了,这里 type 是 blob, tree, commit, tag 中的一个。
注意对于 tree 对象它输出的是 tree 对象存储时的样子,看起来像是
乱码,所以对于 tree 要用
git ls-tree SHA-1
另外 git 也可以在需要 tree 对象的地方给它一个 commit 对象
或者一个 tag 对象,这时就是用 commit 或 tag 指向的 tree,
在 git 的手册中可以看到 tree-ish 这样的写法,意思就是可以
从 tree-ish 这个参数得到一个 tree,同样的,也有 commit-ish.
比如
git-ls-tree [-d] [-r] [-t] [-z] [--name-only]
[--name-status] [--full-name] [--abbrev=[<n>]]
<tree-ish> [paths…]
总结一下,可以用 SHA-1 值指定是哪一个 blob/tree/commit 对象(无需
指出类型),可以用 commit 或者 tag 指定一个 tree,可以用 tag 指定
一个 commit(一般 tag 都是用来指向 commit,虽然它可以指向其它类型
对象)。
在 git 中很频繁提到 ref 或者 reference,它就是一个名字,包含了
一个 commit。分支名字,tag 其实都是 reference,这可以从它们都
位于 .git/refs 下看出来,区别在于分支名字(就是指代各个分支的
HEAD)指向的目标可以改变,而 tag 不能。
指代一个 tree 中的某个文件可以用
tree-ish:path/to/file
的格式,注意 path 前面没有“/”。
* commit 记法
commit 因为有继承关系(ancestry),它的选择方式更特殊些,比如怎么
选择它的 parent commit,以及 parent commit 的 parent commit,
虽然这个信息可以逐步通过 git cat-file commit commit-ish 获得,
但显然需要一个简便的记法来指定,类似于 SVN 的 HEAD、PREV、COMMITTED。
这种 git 独有的记法在 git-rev-parse(1) 有详细描述,简要的说,
commit-ish^n 表示 commit 的第 n 个 直接 parent commit,n 从 1 算起。
只有在一个 commit 是合并的结果时这个 commit 才可能有
commit^2, commit^3。
当 n 等于 1 时可以省略,只写 ^。
当 n 等于 0 时就指 commit 本身,这可以用来将一个 tag
转成 commit。
这种 ^n 记法可以连续写,比如 HEAD^2^2 表示 HEAD 的第二个 parent commit
的第二个 parent commit。
commit-ish~n 这种记法是 commit-ish^^^...^ 总共 n 个 ^ 的简写,这样在
直线历史上能够方便的说倒数第 (n+1) 次提交(commit-ish^1
是倒数第二次)。
当 n 等于 1 时自然也是可以不写 1 的。
在接收 commit 集合的命令中,比如 git log,commit 的记法有特殊含义:
git log HEAD
表示按照 parent 关系从 HEAD 能够到达的所有 commit,
包含 HEAD。
git log ^HEAD~2 HEAD
表示从 HEAD 能够到达的所有 commit(包含 HEAD),排除
能从 HEAD~2 到达的所有 commit(包含 HEAD~2),这样计算
的结果就是 HEAD~1 和 HEAD.
这样的集合记法可以写多个,比如
git log commit-1 commit-2 ^commit-3 ^commit-4
特殊的,commitA..commitB 是 ^commitA commitB 的简写。
这种集合记法在比较两个分支的区别时很有效,比如想看 test 分支从 master
分支分出来后做了什么修改,可以用
git log ^master test
这里不需要从分支点排除,因为按照这种集合相减关系,得到的就是 test 自
master 分出来后所创建的所有 commit。
由于 git 的 fast forward 形式的合并,HEAD^1 并不总是合并前所在的
工作分支,这一点是很容易迷糊人的,如下图(a,b,c 代表 commit):
--- a ---> master
\
b ----- c ---> test
现在 master 从 test 上合并,按照 git 的做法,因为 a 是 c 祖先,而且
master 分支上自 a 之后没有新的修改,所以 git 直接把 master 指向 c,
而不创建新的 commit:
--- a --- b ----- c ---->master, test
现在 master 和 test 指向同一个 commit,master^1 是 b,而 b 并不在
*原先的* master 分支上。合并后,master 和 test 的历史是一致的,没有
区别,这个 CVS 和 SVN 的做法是不同的,按照 CVS 或者 SVN 的做法,
合并后 show log master 看到的应该是 a 和 d(d 的 parent commit 是 a 和 c),
但是 git log master 看到的是 a b c。
git 的这个 fast forward 形式的 merge 很有意思。
* .git 和 index
CVS/SVN 中,代码库和工作拷贝都是分开的,工作拷贝的版本信息放在每一层
目录中,这给在工作拷贝中 grep 代码不便。git 将代码库和工作拷贝的版本
信息统一放在工作拷贝的顶层目录下的 .git/ 中(可以配置使用其它目录)。
.git/
├─branches
├─hooks
├─info
├─objects
│ ├─info
│ └─pack
├─refs
│ ├─heads
│ └─tags
└─remotes
objects 下面是存放 blob, tree, commit 对象的地方,取 SHA-1的十六进制
前两个字符做目录名,余下的做文件名, 注意一个对象的 SHA-1 值不是直接
对这里面的文件计算 SHA-1 值得到的。
用 SHA-1 引用对象时,一般取其前六个字符,只要能够唯一识别一个对象即可,
.git 下可以存在 config 文件,这个是一个库的配置文件,用户可以在自己
的 HOME 目录下有 .gitconfig 文件,两者格式是一样的,参考 git-repo-config(1)。
.git 下可能还存在一个 logs 目录,由于 git 的 fast forward 形式的合并
做法,导致不能通过 git log 来获取一个分支的头曾经指向哪些 commit,这个
logs 目录下的文件就是用来记录这个信息的,要想使用它需要在 .git/config
或者 ~/.gitconfig 里头设置选项。这些 log 就称为 reflog,叫 ref 是因为
分支名和标签都是 commit 的引用,都放在 .git/refs 目录下。
.git 下还有 index 文件(对于刚创建的空库是没有的),这个文件就起到 CVS
的 .cvs 目录或者 SVN 的 .svn 目录的作用,它记录了工作拷贝在哪个 commit
上,以及工作拷贝中被 git 管理的文件的状态。在 SVN 中,.svn 里头保存了
一份代码,称为 BASE,index 里头跟它作用类似,不过它只是记录了文件名
和其 SHA-1 值、mtime、ctime、所在的设备号、权限位、uid、gid、size,
真正的文件内容都在 .git/objects 里头。
index 跟 tree 很类似,除了包含一些信息用于跟踪工作拷贝中的文件状态,
粗略的讲,使用 git 最常打交道的有三个 tree:HEAD 对应的 tree,index
对应的 tree,工作拷贝中的目录树。
git 和 svn 对于处理 index 和 BASE 版本的方式不大一样,SVN 是以工作拷贝为
中心,BASE 在提交前是不会变的,git 则以 index 为中心,在提交前 index 是
可以变的,这一点也很迷糊人,比如,如果你在工作拷贝中修改了文件,用 git
commit 却告诉你没有修改,因为 git commit 是把 index 代表的状态提交到库里
头,只有你用git-update-index your_file 将工作目录中的修改反映到 index 里,
git commit 才会跟直觉的提交行为一致,不过文件被提交到库里的时机是在调用
git-update-index 的时候,而非 git commit 的时候,git commit只是依照
index 的状态创建一个 tree 放入 .git/ojbects 里头。可以用 git commit -a
达到 svn commit 的直观效果。
index 也被称为 cache,这是早期的叫法。
一份 .gitconfig:
[core]
fileMode = false
logAllRefUpdates = true
compression = 9
[diff]
color = auto
[pack]
window = 64
[user]
email = your_email
name = your_name
[merge]
summary = true
* 入门操作
git 的命令分成两类,低级命令(称为“plumbing”)和高级命令(称为“porcelain”),
不算开头的 git 前缀,低级命令的名字 *一般* 是两个单词的,高级命令则 *一般* 是
一个单词的。
低级命令都是一些 C 写的小程序,直接操作 git 核心的对象如 tree,commit 以
及 index 等,这些是提供给编写 git 包装程序比如 cogito 的人使用的,高级命
令是面向最终用户的,这些命令要么是对 git 命令的包装,要么是提供一些方便的
功能比如统计 log。
一个 git 命令可以写成 "git-cmd" 或者 "git cmd",前者在 PATH 中寻找 "git-cmd"
这个命令并执行之,后者则是执行 git 命令,传入后面的 cmd 等参数,git 这个
程序在 exec-path 中寻找 git-cmd 并执行之,这个 exec-path 可以通过命令行
选项 --exec-path=XXX 或者 GIT_EXEC_PATH 环境变量设置,在安装 git 时它内部
也会保存一个缺省的 exec-path。这个路径列表的语法跟 PATH 一致。git 的这种
exec-path 机制能够让它很方便的加入更多的命令。
获取一个 git 命令的帮助可以用 man git-cmd 或者 git --help cmd 或者 git help cmd
或者 git cmd --help,这四种命令效果跟第一条一样。
(a)
git init-db
在当前目录下创建 git 库(.git)
(b)
echo hello > xxx
git add xxx
递归将 xxx 加入 git 控制中,执行完后文件已经被加入库中,index 也已更新,
这时 git diff 因为 index 跟 工作拷贝一致,所以没有差别。
// 其实就是用的 git-update-index,这个命令在更新 index 时也会立即把
// 文件写入库中,这点跟 SVN 的 add 不一样。
(c)
git commit
将 index 实例化为一个 tree,创建一个 commit,这个时候看.git/objects 下面
就有三个对象:blob xxx,一个 tree 和一个commit。
(d)
echo world >> xxx
git diff
比较 index 和 工作拷贝
git diff --cached
比较 index 和前一次提交对应的 tree,这就是 git commit 要提交的东西。
git diff HEAD
比较工作拷贝和前一次提交对应的 tree,这就是 git commit -a 要提交的东西。
(e)
git commit
因为 index 没有改变,而 commit 只是从 index 创建 tree,所以没有需要提交
的,提示用 git-update-index.
git update-index xxx
git commit
更新了 index,这下可以提交了, 如果是想提交 index 中所有修改过和删除了的
文件,这两步可以用 git commit -a(这个命令类似于在顶层目录执行 svn commit)。
(f)
git log -p
查看 log,-p 表示显示每一次修改的 diff。
(g)
git branch
显示所有分支,当前分支前面有一个 *,默认分支叫 master。
git branch test
建立一个分支 test
git checkout test
切换到 test 分支,这两步可以合并为 git checkout -b test。
(h)
在 test 分支做了有些修改后提交;
git checkout master
回到主分支
git log master..test
查看 test 分支上修改记录(参考 “commit 记法” 一节)。
git merge --no-commit "msg" HEAD test
这个命令的参数语法比较怪异。--no-commit 是让 git 不要马上
提交,这样可以检查一下合并结果是否正确,由于这段时间主干上
没有修改,所以这个合并其实就是 fast forward,直接将
master 指向 test 分支的 HEAD,不会创建新的 commit。
// git pull 的行为有点古怪,不推荐使用。
如果不是 fast forward,可以用 git diff 检查一下然后再
git commit;如果合并后有冲突,git 会把冲突标记写入文件,
这个时候打开这个文件编辑之,解决冲突后用 git-update-index
更新 index,然后 git commit。
(i)
gitk --all
查看版本图。
(j)
git branch -D test
删除 test 分支。
(k)
git clone git://git.kernel.org/pub/scm/git/git.git
把 git 的开发库镜像到本地的 git 目录中。
(l)
cd git && git fetch
与 git 的开发库同步。不建议用 git pull,可以用 git fetch
和 git merge 的组合代替 git pull。
(m)
git status
查看工作拷贝中的修改情况
(n)
git show commit-ish
查看一个 commit。
(o)
git reset commit-ish
将当前 HEAD 指向 commit-ish 代表的 commit。
这个命令有三个选项:
--mixed 默认选项,调整 HEAD 并重置 index,保留工作拷贝中的修改,
修改过的文件需要再次 git-update-index 以更新 index。
--soft 仅修改 HEAD。
--hard 修改 HEAD,并将 index 和 工作拷贝的状态对应到 commit-ish
相应的 tree 上,这会丢失工作拷贝中的修改!
在 git-reset(1) 中有一些这个命令的使用场合说明,可以看看,常用的可能
是第一条:
git reset --soft HEAD^
撤销最近一次提交。
// 类似于 svn revert 的是 git checkout。
(p)
git revert commit-ish
撤销库里的某次提交,注意跟 svn revert 撤销工作拷贝的修改不一样。
如果是撤销最近的提交,最好用 git reset。
(q)
有点点类似于 svn info 的命令是:
cat .git/remotes/origin
还有一个 git-push(1), 比较的复杂,以及其它的许多许多命令,
比如格式化 patch,将 patch 通过邮件发送,从 mbox 中提取
patch,等等,这篇小文就不罗嗦了(我也不大会-_-b),可以参考手册。
* 参考资料
http://www.kernel.org/pub/software/scm/git/docs/
这个上面的 tutorial 和 Everyday Git 指向两篇很好的教程,此页的
FURTHER DOCUMENTATION 中提到的 Discussion 以及 Core tutorial 对了解
git 的内部机制很有帮助。
http://git.or.cz/gitwiki/GitLinks
很多 git 相关资料的链接。
http://linux.yyz.us/git-howto.html
Kernel Hackers' Guide to git, 比较老的一篇教程了。
http://www.gelato.unsw.edu.au/archiv...512/13748.html
http://marc.theaimsgroup.com/?l=git&...2372012587&w=4
git for the confused, 虽然有点老了,仍然强烈推荐。
======================
Git Guide Pre-1.5.
Git Guide Pre-1.5.
http://wiki.sourcemage.org/Git_Guide_Pre-1.5
Intro
For given commands you can type either eg git commmit or git-commit; the man page is available via man git-commit or 'git commit --help'.
For more common everyday commands see the list in git help.
This guide assumes that the repository you work with was cloned by git 1.5, if it was cloned with an older version, some of the commands, especially related to branch handling don't work as described here. Either re-clone or use the commands from Git Guide Pre-1.5.
Setup
How do I get my copy of the repository?
Once you have your access setup:
$ git clone ssh://[<user@>]scm.sourcemage.org/smgl/grimoire.git
$ ls
grimoire
$ cd grimoire
Note: If you don't want to authenticate for every action, you can also use git:// instead of ssh:// for read/update operations. You will need ssh:// for any write operations. Do not use [WWW] http://, even if it seems to work. It is not a supported mechanism for developers getting timely updates.
Any other initial setup I need?
Tell git who you are:
$ git config user.name "FirstName LastName"
$ git config user.email "user@example.com"
If you have many git repositories under your current user, you can set this for all of them
$ git config --global user.name "FirstName LastName"
$ git config --global user.email "user@example.com"
If you want pretty colors, you can setup the following for branch, status, and diff commands:
$ git config --global color.branch "auto"
$ git config --global color.status "auto"
$ git config --global color.diff "auto"
Or, to turn all color options on (with git 1.5.5+), use:
$ git config --global color.ui "auto"
To enable aut-detection for number of threads to use (good for multi-CPU or multi-core computers) for packing repositories, use:
$ git config --global pack.threads "0"
Basics
For just dealing with the primary branch of the grimoire (test for the grimoire, aka "master" in git):
How do I keep my copy current?
Assuming you are on the master branch:
$ git pull
How do I update a spell, assuming I have no other changes I don't want to commit?
$ git pull
$ $EDITOR path/to/spell/files
$ git add path/to/new/files
Note: deletions will be noted automatically.
$ git commit -a
Note: fill the commit message with the spell name and the version number it is updated to.
$ git push origin master
Note: This commits *all* local changes, if you want to keep some not committed, see the Intermediate Usage section.
Oh no, a conflict! How do I resolve it?
$ $EDITOR path/to/conflicting/file
$ git update-index path/to/conflicting/file
$ git commit -F .msg
$ rm .msg
I screwed up, how do I reset my checkout?
$ git checkout -f
I want to fix my last commit.
$ $EDITOR path/to/spell/files
$ git commit --amend <some/path>
This will include the new changes you made and recommit with the previous commit message.
What other info commands are there?
$ git status
$ git log [<some/path>]
$ git show [<some commit id>]
$ git diff
Note:
*
git-config color.diff auto tells git you prefer colored output.
Intermediate Usage
I only want to commit certain files.
$ git commit path/to/some/files
Note:
*
git commit with no args may complain about needing to update the index to include some files in the commit; specifying explicit paths will avoid this.
*
Best practice is to always do a commit per atomic change so that a separate commit id is available for future cherry-picks, reverts, etc. In other words, if you have 5 spells to update, do a commit per spell directory and not one commit that includes all 5 updates.
I want to discard my uncommitted changes
$ git reset --hard
That will discard all the uncommitted changes you made locally. If you want to reset only a part of the repository, you'll have to use a trick:
$ git diff some/path | patch -p1 -R
How do I see all the available branches?
$ git branch
* master
The * denotes the current working branch. "master" is the branch each repository starts with, it is a local branch of the remote "master".
git branch -r shows remote branches, and git branch -a shows all:
$ git branch -r
origin/HEAD
origin/devel
origin/devel-iceweasel
origin/devel-shadow
origin/master
origin/stable-0.3
origin/stable-0.4
origin/stable-0.6
origin/stable-0.7
origin/stable-rc-0.4
origin/stable-rc-0.5
origin/stable-rc-0.6
origin/stable-rc-0.7
origin/stable-rc-0.8
I want to work on an upstream branch other than "master".
$ git checkout --track -b <local name> origin/<remote name>
Note: this creates a local branch <local name> based on the upstream branch and switches your working copy to that branch.
$ git pull
$ git push origin <local name>
Sometimes you may need to use the following instead of that last commit:
$ git push origin <local name>:<remote name without origin/>
I want to get back to the main branch.
$ git checkout master
I want to create a local WIP branch.
$ git branch devel-something <base branch>
$ git checkout devel-something
edit/add/commit/etc.
Or just:
$ git checkout -b devel-something <base branch>
edit/add/commit/etc.
If <base branch> is currently checked out, you don't have to specify it explicitly. You can use the "--track" argument to branch/checkout to make "git pull" work in this WIP branch without parameters.
I want to push my WIP branch changes back to the mainline.
$ git checkout master
$ git pull . devel-something
I want to delete a local WIP branch.
$ git branch -d devel-something
git will refuse to delete the branch if it contains changes that haven't been merged to master yet. To delete the branch anyway, use
$ git branch -D devel-something
I want to cherry-pick a change from one local branch to another local branch.
$ git log <first local branch> path/to/changed/file
$ git checkout <second local branch>
$ git cherry-pick <sha1 refspec (commit id) from the previous 'git log' output>
I want to cherry-pick something to a remote branch.
$ git checkout --track -b <tmp local branch> origin/<remote branch>
$ git cherry-pick <sha1 refspec of commit from other (local or remote) branch>
$ git push origin <tmp local branch>
$ git branch -D <tmp local branch>
I want to revert a commit.
$ git log path/to/file
$ git revert <sha1 refspec of the bad commit>
Note: revert and cherry-pick automatically do a commit with a message that preserves the original commit id. man git-cherry-pick for options to change this behaviour.
Advanced Usage
I want to publish my WIP branch on the central server.
$ git push origin devel-something:refs/heads/devel-something
Note:
*
Other people updating their clones will automatically get this branch in their remote branches list.
*
Branch names have to be unique, so "devel-something" must not yet exist as branch in the remote repository.
*
It is not possible to delete a remote branch, but you can remove the branch names from your remote branch listing by using "git branch -d -r origin/<remote branch name>".
I want to work in various remote branches often.
Just create a local branch for each remote branch you want to work on:
$ git branch --track devel origin/devel
$ git branch --track stable-rc-0.10 origin/stable-rc-0.10
I only want to push my commits up to a certain point.
$ git push origin <sha1 spec>:<remote branch dst>
I'm tired of typing my SSH key passphrase.
You may want to use ssh-agent to avoid having to type in your SSH password while working on the repositories (assuming you cloned with developer access). First we load the ssh-agent :
$ eval `ssh-agent`
Agent pid 10044
The above PID will be different for you. Now we load our key (assuming you only have one key or want to load them all):
$ ssh-add
If you have a specific key for the grimoire and want to only load that, use:
$ ssh-add ~/.ssh/grimoire_id_rsa
Assuming you named your grimoire SSH key ~/.ssh/grimoire_id_rsa.
You can also consider using a passphrase-less key, as long as the physical security of the private key file is reasonable (your development machine isn't share with other users, you have firewall protection in place, etc.).
Why do my commit emails have extra "Merge branch 'master'" messages?
When you do a git pull, it fetches any changes to the upstream branches, merges them into your checkout, and does an auto-commit back to your local repo. This auto-commit produces this message, and it gets sent along when you push. Note that this commit only happens if you have commits in your local repository that haven't been pushed yet.
If you want to avoid this, you can "rebase" your branch instead of using "pull". Rebasing works by rewinding your working copy to the state of the remote branch, then replays all your changes on top of that.
$ git fetch
$ git rebase remotes/origin/<remote branch>
Obviously this can lead to conflicts similar to "git pull", but unlike pull, rebase will stop on each conflict. After resolving this conflict (editing files and running "git update-index" on them), "git rebase --continue" will continue the rebase. If you want to skip the conflicting commit, use "git rebase --skip". The whole rebase can be stopped and your branch restored to how it was by using "git rebase --abort".
I have totally screwed up my repository with a broken merge
Most actions done on your git repository can be undone.
$ git reflog
78c80b7... HEAD@{0}: pull : Fast forward
c516234... HEAD@{1}: checkout: moving to master
c516234... HEAD@{2}: pull : Fast forward
01fdb2a... HEAD@{3}: rebase: mplayer: adapt configure options to latest svn
da8ed38... HEAD@{4}: rebase
...
This shows all actions that have been done in your repository. If you want to undo that latest pull, you can use "git reset":
$ git reset --hard HEAD@{1}
Good tutorial on the basics of git: [WWW] A tour of git: the basics
首先不要有心理障碍,那些名词都是吓唬人的。所谓的“非线性开发”无非是指强大的branch和merge的能力,“分布式版本管理”就是说每人自己都有一套本地的repository,不存在一个集中的版本服务器。
给我带来的最直接的好处有:
1. 傻瓜都会的初始化,git init, git commit -a, 就完了。对于随便写两行代码就要放到SCM里的人来说,再合适不过。也可以拿git做备份系统,或者同步两台机器的文档,都很方便。
2. 绝大部分操作在本地完成,不用和集中SCM服务器交互,终于可以随时随地大胆地check in代码了。
3. branch管理容易多了,无论是建立新的branch,还是在branch之间切换都一条命令完成,不需要建立多余的目录。
4. branch之间merge时,不仅代码会merge在一起,check in历史也会保留,这点非常重要。
工具之所以好,除了方便好用,还在于它帮助并鼓励你做正确的事情。频繁check in是一件很好的事情,好处我不多说了,git就鼓励你频繁check in。branch也是一件好事情,我们大多很怕branch因为它太麻烦了,去掉这层心理包袱,branch可以让我们的开发工作很有条例。
还有一些实用的功能,比如bisect,用二分法来寻找regression,你以前手动做过这种事么?我做过。以后如果要做就不会怕了。还有stash,做hot fix非常方便。
如果正在用svn,劝服所有合作开发者使用git之前,可以先用git-svn,和svn整合得非常好。
分布式版本管理系统取代集中式版本管理系统,只是时间的问题了。
=============================
git 之五分钟教程
http://www.linuxsir.org/bbs/thread281294.html
yubao.liu at gmail.com
dieken at newsmth
我懒,估计读者也是懒的,所以写点小文供大家了解一下 git。
git 是一个分布式版本管理工具,关于它大家应该都有所耳闻了,
貌似 Linus 说是 2005 年 4 月 17 日公开的,如今已经 1.4.3.4
了,开发社区非常活跃,一如 Linux (Linus 英明神武,sigh),
git 现在的维护者是 Junio C Hamano,主页在 http://git.or.cz 。
关于分布式版本管理工具的定义,我土,感受最深的是每个人都有
自己的代码库,这个跟 SVN 不同,这个区别带来的好处是跟踪本地
的修改过程非常方便,SVN 里头不同代码库之间不能 switch,麻烦。
但是 SVN 的用户界面实在是比 git 友好得多,特别是 SVN 建立
在大家普遍比较熟悉的 CVS 模型上。
闲话少说,切入正题。
* git 的四种对象
在 git 中最基本的四种对象为 blob,tree,commit,tag。
blob - 即文件,注意只包含内容,没有名字,权限等属性(例外的是
包含大小)
tree - 所有文件名字以及其属性的列表,这些属性只是基本属性,比如
权限,是否符号链接。git 没有像 svn 那样丰富的 property 用。
commit - 表示修改历史,描述一个个 tree 之间如何联系起来的,每一个
commit 对应有一个 tree —— commit 的修改结果。commit 包含
作者、提交者、parent commit、tree、log、修改时间、提交时间,
注意 git 明确区分了 author 和 committer 这两个角色。
tag - 标签,它可以指向 blob, tree, commit 并包含签名,最常见的是
指向 commit 的 GPG 签名的标签。
blob,tree,commit 都是用其存储内容的 SHA-1 值命名的(不是简单的对
整个文件取 SHA-1 值),tag 自然使用的是普通名字。
git 的想法就是 blob 和 tree 中包含的只能是最公共的最基本的信息,
所以它不会在 blob 和 tree 里头存放 svn 那么多的自定义属性。
git 也只是定位在 content tracker,这个术语跟 VCS 是有微妙的差别的,
第一是体现在它对 blob 和 tree 的“纯数据”的要求上,第二是对分支
合并的处理上,如下所示(a, b, c 表示三个 commit):
-- a ----> master
\
b ----- c -> test
test 分支从 master 的 a 点上分出来,做了许多修改,然后当 master
分支和 test 分支合并会怎么样呢?按照通常的 CVS/SVN 的做法,是创建
一个新的 commit,继承自 a 和 c,但是 git 则是直接修改 master
使其指向 c,在 git 这称为 fast forward,整个版本图变成这样:
-- a -- b -- c --> master
\
`--> test
从图中看 master 分支和 test 分支分不出主次,这里头的含义很值得玩味。
* 选择对象
blob, tree, commit 都是用其 SHA-1 命名的,如果得到一个 SHA-1
值,但不知道是什么类型,可以用
git cat-file -t SHA-1
命令识别,它会输出 blob, tree, commit (如果给它一个 tag 名字,
它也能输出 tag) 这样的字符串,然后就可以用
git cat-file type SHA-1
查看其内容了,这里 type 是 blob, tree, commit, tag 中的一个。
注意对于 tree 对象它输出的是 tree 对象存储时的样子,看起来像是
乱码,所以对于 tree 要用
git ls-tree SHA-1
另外 git 也可以在需要 tree 对象的地方给它一个 commit 对象
或者一个 tag 对象,这时就是用 commit 或 tag 指向的 tree,
在 git 的手册中可以看到 tree-ish 这样的写法,意思就是可以
从 tree-ish 这个参数得到一个 tree,同样的,也有 commit-ish.
比如
git-ls-tree [-d] [-r] [-t] [-z] [--name-only]
[--name-status] [--full-name] [--abbrev=[<n>]]
<tree-ish> [paths…]
总结一下,可以用 SHA-1 值指定是哪一个 blob/tree/commit 对象(无需
指出类型),可以用 commit 或者 tag 指定一个 tree,可以用 tag 指定
一个 commit(一般 tag 都是用来指向 commit,虽然它可以指向其它类型
对象)。
在 git 中很频繁提到 ref 或者 reference,它就是一个名字,包含了
一个 commit。分支名字,tag 其实都是 reference,这可以从它们都
位于 .git/refs 下看出来,区别在于分支名字(就是指代各个分支的
HEAD)指向的目标可以改变,而 tag 不能。
指代一个 tree 中的某个文件可以用
tree-ish:path/to/file
的格式,注意 path 前面没有“/”。
* commit 记法
commit 因为有继承关系(ancestry),它的选择方式更特殊些,比如怎么
选择它的 parent commit,以及 parent commit 的 parent commit,
虽然这个信息可以逐步通过 git cat-file commit commit-ish 获得,
但显然需要一个简便的记法来指定,类似于 SVN 的 HEAD、PREV、COMMITTED。
这种 git 独有的记法在 git-rev-parse(1) 有详细描述,简要的说,
commit-ish^n 表示 commit 的第 n 个 直接 parent commit,n 从 1 算起。
只有在一个 commit 是合并的结果时这个 commit 才可能有
commit^2, commit^3。
当 n 等于 1 时可以省略,只写 ^。
当 n 等于 0 时就指 commit 本身,这可以用来将一个 tag
转成 commit。
这种 ^n 记法可以连续写,比如 HEAD^2^2 表示 HEAD 的第二个 parent commit
的第二个 parent commit。
commit-ish~n 这种记法是 commit-ish^^^...^ 总共 n 个 ^ 的简写,这样在
直线历史上能够方便的说倒数第 (n+1) 次提交(commit-ish^1
是倒数第二次)。
当 n 等于 1 时自然也是可以不写 1 的。
在接收 commit 集合的命令中,比如 git log,commit 的记法有特殊含义:
git log HEAD
表示按照 parent 关系从 HEAD 能够到达的所有 commit,
包含 HEAD。
git log ^HEAD~2 HEAD
表示从 HEAD 能够到达的所有 commit(包含 HEAD),排除
能从 HEAD~2 到达的所有 commit(包含 HEAD~2),这样计算
的结果就是 HEAD~1 和 HEAD.
这样的集合记法可以写多个,比如
git log commit-1 commit-2 ^commit-3 ^commit-4
特殊的,commitA..commitB 是 ^commitA commitB 的简写。
这种集合记法在比较两个分支的区别时很有效,比如想看 test 分支从 master
分支分出来后做了什么修改,可以用
git log ^master test
这里不需要从分支点排除,因为按照这种集合相减关系,得到的就是 test 自
master 分出来后所创建的所有 commit。
由于 git 的 fast forward 形式的合并,HEAD^1 并不总是合并前所在的
工作分支,这一点是很容易迷糊人的,如下图(a,b,c 代表 commit):
--- a ---> master
\
b ----- c ---> test
现在 master 从 test 上合并,按照 git 的做法,因为 a 是 c 祖先,而且
master 分支上自 a 之后没有新的修改,所以 git 直接把 master 指向 c,
而不创建新的 commit:
--- a --- b ----- c ---->master, test
现在 master 和 test 指向同一个 commit,master^1 是 b,而 b 并不在
*原先的* master 分支上。合并后,master 和 test 的历史是一致的,没有
区别,这个 CVS 和 SVN 的做法是不同的,按照 CVS 或者 SVN 的做法,
合并后 show log master 看到的应该是 a 和 d(d 的 parent commit 是 a 和 c),
但是 git log master 看到的是 a b c。
git 的这个 fast forward 形式的 merge 很有意思。
* .git 和 index
CVS/SVN 中,代码库和工作拷贝都是分开的,工作拷贝的版本信息放在每一层
目录中,这给在工作拷贝中 grep 代码不便。git 将代码库和工作拷贝的版本
信息统一放在工作拷贝的顶层目录下的 .git/ 中(可以配置使用其它目录)。
.git/
├─branches
├─hooks
├─info
├─objects
│ ├─info
│ └─pack
├─refs
│ ├─heads
│ └─tags
└─remotes
objects 下面是存放 blob, tree, commit 对象的地方,取 SHA-1的十六进制
前两个字符做目录名,余下的做文件名, 注意一个对象的 SHA-1 值不是直接
对这里面的文件计算 SHA-1 值得到的。
用 SHA-1 引用对象时,一般取其前六个字符,只要能够唯一识别一个对象即可,
.git 下可以存在 config 文件,这个是一个库的配置文件,用户可以在自己
的 HOME 目录下有 .gitconfig 文件,两者格式是一样的,参考 git-repo-config(1)。
.git 下可能还存在一个 logs 目录,由于 git 的 fast forward 形式的合并
做法,导致不能通过 git log 来获取一个分支的头曾经指向哪些 commit,这个
logs 目录下的文件就是用来记录这个信息的,要想使用它需要在 .git/config
或者 ~/.gitconfig 里头设置选项。这些 log 就称为 reflog,叫 ref 是因为
分支名和标签都是 commit 的引用,都放在 .git/refs 目录下。
.git 下还有 index 文件(对于刚创建的空库是没有的),这个文件就起到 CVS
的 .cvs 目录或者 SVN 的 .svn 目录的作用,它记录了工作拷贝在哪个 commit
上,以及工作拷贝中被 git 管理的文件的状态。在 SVN 中,.svn 里头保存了
一份代码,称为 BASE,index 里头跟它作用类似,不过它只是记录了文件名
和其 SHA-1 值、mtime、ctime、所在的设备号、权限位、uid、gid、size,
真正的文件内容都在 .git/objects 里头。
index 跟 tree 很类似,除了包含一些信息用于跟踪工作拷贝中的文件状态,
粗略的讲,使用 git 最常打交道的有三个 tree:HEAD 对应的 tree,index
对应的 tree,工作拷贝中的目录树。
git 和 svn 对于处理 index 和 BASE 版本的方式不大一样,SVN 是以工作拷贝为
中心,BASE 在提交前是不会变的,git 则以 index 为中心,在提交前 index 是
可以变的,这一点也很迷糊人,比如,如果你在工作拷贝中修改了文件,用 git
commit 却告诉你没有修改,因为 git commit 是把 index 代表的状态提交到库里
头,只有你用git-update-index your_file 将工作目录中的修改反映到 index 里,
git commit 才会跟直觉的提交行为一致,不过文件被提交到库里的时机是在调用
git-update-index 的时候,而非 git commit 的时候,git commit只是依照
index 的状态创建一个 tree 放入 .git/ojbects 里头。可以用 git commit -a
达到 svn commit 的直观效果。
index 也被称为 cache,这是早期的叫法。
一份 .gitconfig:
[core]
fileMode = false
logAllRefUpdates = true
compression = 9
[diff]
color = auto
[pack]
window = 64
[user]
email = your_email
name = your_name
[merge]
summary = true
* 入门操作
git 的命令分成两类,低级命令(称为“plumbing”)和高级命令(称为“porcelain”),
不算开头的 git 前缀,低级命令的名字 *一般* 是两个单词的,高级命令则 *一般* 是
一个单词的。
低级命令都是一些 C 写的小程序,直接操作 git 核心的对象如 tree,commit 以
及 index 等,这些是提供给编写 git 包装程序比如 cogito 的人使用的,高级命
令是面向最终用户的,这些命令要么是对 git 命令的包装,要么是提供一些方便的
功能比如统计 log。
一个 git 命令可以写成 "git-cmd" 或者 "git cmd",前者在 PATH 中寻找 "git-cmd"
这个命令并执行之,后者则是执行 git 命令,传入后面的 cmd 等参数,git 这个
程序在 exec-path 中寻找 git-cmd 并执行之,这个 exec-path 可以通过命令行
选项 --exec-path=XXX 或者 GIT_EXEC_PATH 环境变量设置,在安装 git 时它内部
也会保存一个缺省的 exec-path。这个路径列表的语法跟 PATH 一致。git 的这种
exec-path 机制能够让它很方便的加入更多的命令。
获取一个 git 命令的帮助可以用 man git-cmd 或者 git --help cmd 或者 git help cmd
或者 git cmd --help,这四种命令效果跟第一条一样。
(a)
git init-db
在当前目录下创建 git 库(.git)
(b)
echo hello > xxx
git add xxx
递归将 xxx 加入 git 控制中,执行完后文件已经被加入库中,index 也已更新,
这时 git diff 因为 index 跟 工作拷贝一致,所以没有差别。
// 其实就是用的 git-update-index,这个命令在更新 index 时也会立即把
// 文件写入库中,这点跟 SVN 的 add 不一样。
(c)
git commit
将 index 实例化为一个 tree,创建一个 commit,这个时候看.git/objects 下面
就有三个对象:blob xxx,一个 tree 和一个commit。
(d)
echo world >> xxx
git diff
比较 index 和 工作拷贝
git diff --cached
比较 index 和前一次提交对应的 tree,这就是 git commit 要提交的东西。
git diff HEAD
比较工作拷贝和前一次提交对应的 tree,这就是 git commit -a 要提交的东西。
(e)
git commit
因为 index 没有改变,而 commit 只是从 index 创建 tree,所以没有需要提交
的,提示用 git-update-index.
git update-index xxx
git commit
更新了 index,这下可以提交了, 如果是想提交 index 中所有修改过和删除了的
文件,这两步可以用 git commit -a(这个命令类似于在顶层目录执行 svn commit)。
(f)
git log -p
查看 log,-p 表示显示每一次修改的 diff。
(g)
git branch
显示所有分支,当前分支前面有一个 *,默认分支叫 master。
git branch test
建立一个分支 test
git checkout test
切换到 test 分支,这两步可以合并为 git checkout -b test。
(h)
在 test 分支做了有些修改后提交;
git checkout master
回到主分支
git log master..test
查看 test 分支上修改记录(参考 “commit 记法” 一节)。
git merge --no-commit "msg" HEAD test
这个命令的参数语法比较怪异。--no-commit 是让 git 不要马上
提交,这样可以检查一下合并结果是否正确,由于这段时间主干上
没有修改,所以这个合并其实就是 fast forward,直接将
master 指向 test 分支的 HEAD,不会创建新的 commit。
// git pull 的行为有点古怪,不推荐使用。
如果不是 fast forward,可以用 git diff 检查一下然后再
git commit;如果合并后有冲突,git 会把冲突标记写入文件,
这个时候打开这个文件编辑之,解决冲突后用 git-update-index
更新 index,然后 git commit。
(i)
gitk --all
查看版本图。
(j)
git branch -D test
删除 test 分支。
(k)
git clone git://git.kernel.org/pub/scm/git/git.git
把 git 的开发库镜像到本地的 git 目录中。
(l)
cd git && git fetch
与 git 的开发库同步。不建议用 git pull,可以用 git fetch
和 git merge 的组合代替 git pull。
(m)
git status
查看工作拷贝中的修改情况
(n)
git show commit-ish
查看一个 commit。
(o)
git reset commit-ish
将当前 HEAD 指向 commit-ish 代表的 commit。
这个命令有三个选项:
--mixed 默认选项,调整 HEAD 并重置 index,保留工作拷贝中的修改,
修改过的文件需要再次 git-update-index 以更新 index。
--soft 仅修改 HEAD。
--hard 修改 HEAD,并将 index 和 工作拷贝的状态对应到 commit-ish
相应的 tree 上,这会丢失工作拷贝中的修改!
在 git-reset(1) 中有一些这个命令的使用场合说明,可以看看,常用的可能
是第一条:
git reset --soft HEAD^
撤销最近一次提交。
// 类似于 svn revert 的是 git checkout。
(p)
git revert commit-ish
撤销库里的某次提交,注意跟 svn revert 撤销工作拷贝的修改不一样。
如果是撤销最近的提交,最好用 git reset。
(q)
有点点类似于 svn info 的命令是:
cat .git/remotes/origin
还有一个 git-push(1), 比较的复杂,以及其它的许多许多命令,
比如格式化 patch,将 patch 通过邮件发送,从 mbox 中提取
patch,等等,这篇小文就不罗嗦了(我也不大会-_-b),可以参考手册。
* 参考资料
http://www.kernel.org/pub/software/scm/git/docs/
这个上面的 tutorial 和 Everyday Git 指向两篇很好的教程,此页的
FURTHER DOCUMENTATION 中提到的 Discussion 以及 Core tutorial 对了解
git 的内部机制很有帮助。
http://git.or.cz/gitwiki/GitLinks
很多 git 相关资料的链接。
http://linux.yyz.us/git-howto.html
Kernel Hackers' Guide to git, 比较老的一篇教程了。
http://www.gelato.unsw.edu.au/archiv...512/13748.html
http://marc.theaimsgroup.com/?l=git&...2372012587&w=4
git for the confused, 虽然有点老了,仍然强烈推荐。
======================
Git Guide Pre-1.5.
Git Guide Pre-1.5.
http://wiki.sourcemage.org/Git_Guide_Pre-1.5
Intro
For given commands you can type either eg git commmit or git-commit; the man page is available via man git-commit or 'git commit --help'.
For more common everyday commands see the list in git help.
This guide assumes that the repository you work with was cloned by git 1.5, if it was cloned with an older version, some of the commands, especially related to branch handling don't work as described here. Either re-clone or use the commands from Git Guide Pre-1.5.
Setup
How do I get my copy of the repository?
Once you have your access setup:
$ git clone ssh://[<user@>]scm.sourcemage.org/smgl/grimoire.git
$ ls
grimoire
$ cd grimoire
Note: If you don't want to authenticate for every action, you can also use git:// instead of ssh:// for read/update operations. You will need ssh:// for any write operations. Do not use [WWW] http://, even if it seems to work. It is not a supported mechanism for developers getting timely updates.
Any other initial setup I need?
Tell git who you are:
$ git config user.name "FirstName LastName"
$ git config user.email "user@example.com"
If you have many git repositories under your current user, you can set this for all of them
$ git config --global user.name "FirstName LastName"
$ git config --global user.email "user@example.com"
If you want pretty colors, you can setup the following for branch, status, and diff commands:
$ git config --global color.branch "auto"
$ git config --global color.status "auto"
$ git config --global color.diff "auto"
Or, to turn all color options on (with git 1.5.5+), use:
$ git config --global color.ui "auto"
To enable aut-detection for number of threads to use (good for multi-CPU or multi-core computers) for packing repositories, use:
$ git config --global pack.threads "0"
Basics
For just dealing with the primary branch of the grimoire (test for the grimoire, aka "master" in git):
How do I keep my copy current?
Assuming you are on the master branch:
$ git pull
How do I update a spell, assuming I have no other changes I don't want to commit?
$ git pull
$ $EDITOR path/to/spell/files
$ git add path/to/new/files
Note: deletions will be noted automatically.
$ git commit -a
Note: fill the commit message with the spell name and the version number it is updated to.
$ git push origin master
Note: This commits *all* local changes, if you want to keep some not committed, see the Intermediate Usage section.
Oh no, a conflict! How do I resolve it?
$ $EDITOR path/to/conflicting/file
$ git update-index path/to/conflicting/file
$ git commit -F .msg
$ rm .msg
I screwed up, how do I reset my checkout?
$ git checkout -f
I want to fix my last commit.
$ $EDITOR path/to/spell/files
$ git commit --amend <some/path>
This will include the new changes you made and recommit with the previous commit message.
What other info commands are there?
$ git status
$ git log [<some/path>]
$ git show [<some commit id>]
$ git diff
Note:
*
git-config color.diff auto tells git you prefer colored output.
Intermediate Usage
I only want to commit certain files.
$ git commit path/to/some/files
Note:
*
git commit with no args may complain about needing to update the index to include some files in the commit; specifying explicit paths will avoid this.
*
Best practice is to always do a commit per atomic change so that a separate commit id is available for future cherry-picks, reverts, etc. In other words, if you have 5 spells to update, do a commit per spell directory and not one commit that includes all 5 updates.
I want to discard my uncommitted changes
$ git reset --hard
That will discard all the uncommitted changes you made locally. If you want to reset only a part of the repository, you'll have to use a trick:
$ git diff some/path | patch -p1 -R
How do I see all the available branches?
$ git branch
* master
The * denotes the current working branch. "master" is the branch each repository starts with, it is a local branch of the remote "master".
git branch -r shows remote branches, and git branch -a shows all:
$ git branch -r
origin/HEAD
origin/devel
origin/devel-iceweasel
origin/devel-shadow
origin/master
origin/stable-0.3
origin/stable-0.4
origin/stable-0.6
origin/stable-0.7
origin/stable-rc-0.4
origin/stable-rc-0.5
origin/stable-rc-0.6
origin/stable-rc-0.7
origin/stable-rc-0.8
I want to work on an upstream branch other than "master".
$ git checkout --track -b <local name> origin/<remote name>
Note: this creates a local branch <local name> based on the upstream branch and switches your working copy to that branch.
$ git pull
$ git push origin <local name>
Sometimes you may need to use the following instead of that last commit:
$ git push origin <local name>:<remote name without origin/>
I want to get back to the main branch.
$ git checkout master
I want to create a local WIP branch.
$ git branch devel-something <base branch>
$ git checkout devel-something
edit/add/commit/etc.
Or just:
$ git checkout -b devel-something <base branch>
edit/add/commit/etc.
If <base branch> is currently checked out, you don't have to specify it explicitly. You can use the "--track" argument to branch/checkout to make "git pull" work in this WIP branch without parameters.
I want to push my WIP branch changes back to the mainline.
$ git checkout master
$ git pull . devel-something
I want to delete a local WIP branch.
$ git branch -d devel-something
git will refuse to delete the branch if it contains changes that haven't been merged to master yet. To delete the branch anyway, use
$ git branch -D devel-something
I want to cherry-pick a change from one local branch to another local branch.
$ git log <first local branch> path/to/changed/file
$ git checkout <second local branch>
$ git cherry-pick <sha1 refspec (commit id) from the previous 'git log' output>
I want to cherry-pick something to a remote branch.
$ git checkout --track -b <tmp local branch> origin/<remote branch>
$ git cherry-pick <sha1 refspec of commit from other (local or remote) branch>
$ git push origin <tmp local branch>
$ git branch -D <tmp local branch>
I want to revert a commit.
$ git log path/to/file
$ git revert <sha1 refspec of the bad commit>
Note: revert and cherry-pick automatically do a commit with a message that preserves the original commit id. man git-cherry-pick for options to change this behaviour.
Advanced Usage
I want to publish my WIP branch on the central server.
$ git push origin devel-something:refs/heads/devel-something
Note:
*
Other people updating their clones will automatically get this branch in their remote branches list.
*
Branch names have to be unique, so "devel-something" must not yet exist as branch in the remote repository.
*
It is not possible to delete a remote branch, but you can remove the branch names from your remote branch listing by using "git branch -d -r origin/<remote branch name>".
I want to work in various remote branches often.
Just create a local branch for each remote branch you want to work on:
$ git branch --track devel origin/devel
$ git branch --track stable-rc-0.10 origin/stable-rc-0.10
I only want to push my commits up to a certain point.
$ git push origin <sha1 spec>:<remote branch dst>
I'm tired of typing my SSH key passphrase.
You may want to use ssh-agent to avoid having to type in your SSH password while working on the repositories (assuming you cloned with developer access). First we load the ssh-agent :
$ eval `ssh-agent`
Agent pid 10044
The above PID will be different for you. Now we load our key (assuming you only have one key or want to load them all):
$ ssh-add
If you have a specific key for the grimoire and want to only load that, use:
$ ssh-add ~/.ssh/grimoire_id_rsa
Assuming you named your grimoire SSH key ~/.ssh/grimoire_id_rsa.
You can also consider using a passphrase-less key, as long as the physical security of the private key file is reasonable (your development machine isn't share with other users, you have firewall protection in place, etc.).
Why do my commit emails have extra "Merge branch 'master'" messages?
When you do a git pull, it fetches any changes to the upstream branches, merges them into your checkout, and does an auto-commit back to your local repo. This auto-commit produces this message, and it gets sent along when you push. Note that this commit only happens if you have commits in your local repository that haven't been pushed yet.
If you want to avoid this, you can "rebase" your branch instead of using "pull". Rebasing works by rewinding your working copy to the state of the remote branch, then replays all your changes on top of that.
$ git fetch
$ git rebase remotes/origin/<remote branch>
Obviously this can lead to conflicts similar to "git pull", but unlike pull, rebase will stop on each conflict. After resolving this conflict (editing files and running "git update-index" on them), "git rebase --continue" will continue the rebase. If you want to skip the conflicting commit, use "git rebase --skip". The whole rebase can be stopped and your branch restored to how it was by using "git rebase --abort".
I have totally screwed up my repository with a broken merge
Most actions done on your git repository can be undone.
$ git reflog
78c80b7... HEAD@{0}: pull : Fast forward
c516234... HEAD@{1}: checkout: moving to master
c516234... HEAD@{2}: pull : Fast forward
01fdb2a... HEAD@{3}: rebase: mplayer: adapt configure options to latest svn
da8ed38... HEAD@{4}: rebase
...
This shows all actions that have been done in your repository. If you want to undo that latest pull, you can use "git reset":
$ git reset --hard HEAD@{1}
Good tutorial on the basics of git: [WWW] A tour of git: the basics