介绍
$ git-clone [url]http://www.bitsun.com/git/gittutorcn.git[/url]
创建一个版本库:git-init-db
$ mkdir gittutorcn $ cd gittutorcn $ git-init-db
defaulting to local storage area
-
一个叫 HEAD 的文件,我们现在来查看一下它的内容:
$ cat .git/HEAD
现在 HEAD 的内容应该是这样:ref: refs/heads/master
我们可以看到,HEAD 文件中的内容其实只是包含了一个索引信息,并且,这个索引将总是指向你的项目中的当前开发分支。 -
一个叫 objects 的子目录,它包含了你的项目中的所有对象,我们不必直接地了解到这些对象内容,我们应该关心是存放在这些对象中的项目的数据。Note
关于 git 对象的分类,以及 git 对象数据库的说明,请参看 [Discussion] -
一个叫 refs 的子目录,它用来保存指向对象的索引。
植入内容跟踪信息:git-add
$ echo "Hello world" > hello $ echo "Silly example" > example
$ git-add hello example
$ git-update-index --add hello example
$ git-update-index --force-remove foo.c
Note |
git-add 可以将某个目录下的所有内容全都纳入内容跟踪之下,例如: git-add ./path/to/your/wanted 。但是在这样做之前,应该注意先将一些我们不希望跟踪的文件清理掉,例如,gcc 编译出来的 *.o 文件,vim 的交换文件 .*.swp 之类。 |
提交内容到版本库:git-commit
$ git-status
# # Initial commit # # # Updated but not checked in: # (will commit) # # new file: example # new file: hello #
$ git-commit -m "Initial commit of gittutor reposistory"
查看当前的工作:git-diff
$ echo "It's a new day for git" >> hello
$ git-diff
diff --git a/hello b/hello index a5c1966..bd9212c 100644 --- a/hello +++ b/hello @@ -1 +1,2 @@ Hello, world +It's a new day for git
$ git-update-index hello $ git-commit -m "new day for git"
$ git-commit -a -m "new day for git"
管理分支:git-branch
-
创建一个属于自己的个人工作分支,以避免对主分支 master 造成太多的干扰,也方便与他人交流协作。
-
当进行高风险的工作时,创建一个试验性的分支,扔掉一个烂摊子总比收拾一个烂摊子好得多。
-
合并别人的工作的时候,最好是创建一个临时的分支,关于如何用临时分支合并别人的工作的技巧,将会在后面讲述。
创建分支
$ git-branch robin $ git-checkout robin
删除分支
$ git-branch -D branch-name
查看分支
$ git-branch
$ cat .git/HEAD
查看项目的发展变化和比较差异
git-show-branch
git-diff
git-whatchanged
$ git-checkout robin $ echo "Work, work, workd" >> hello $ git-commit -m "Some workd" -i hello
$ git-checkout master $ echo "Play, play, play" >> hello $ echo "Lots of fun" >> example $ git-commit -m "Some fun" -i hello example
$ git-show-branch
* [master] Some fun ! [robin] some work -- * [master] Some fun + [robin] some work *+ [master^] a new day for git
$ git-diff master^ robin
diff --git a/hello b/hello index 263414f..cc44c73 100644 --- a/hello +++ b/hello @@ -1,2 +1,3 @@ Hello World It's a new day for git +Work, work, work
Note |
关于 GIT 版本世系编号的定义,请参看 git-rev-parse 。 |
$ git-checkout master $ git-whatchanged
diff-tree 1d2fa05... (from 3ecebc0...) Author: Vortune.Robin <[email]robin@BITSUN.COM[/email]> Date: Tue Mar 21 02:24:31 2006 +0800 Some fun :100644 100644 f24c74a... 7f8b141... M example :100644 100644 263414f... 06fa6a2... M hello diff-tree 3ecebc0... (from 895f09a...) Author: Vortune.Robin <[email]robin@BITSUN.COM[/email]> Date: Tue Mar 21 02:17:23 2006 +0800 a new day for git :100644 100644 557db03... 263414f... M hello
合并两个分支:git-merge
$ git-checkout master $ git-merge "Merge work in robin" HEAD robin
$ git-checkout master $ git-pull . robin
Trying really trivial in-index merge... fatal: Merge requires file-level merging Nope. Merging HEAD with d2659fcf690ec693c04c82b03202fc5530d50960 Merging: 1d2fa05b13b63e39f621d8ee911817df0662d9b7 Some fun d2659fcf690ec693c04c82b03202fc5530d50960 some work found 1 common ancestor(s): 3ecebc0cb4894a33208dfa7c7c6fc8b5f9da0eda a new day for git Auto-merging hello CONFLICT (content): Merge conflict in hello Automatic merge failed; fix up by hand
$ cat hello
Hello World It's a new day for git <<<<<<< HEAD/hello Play, play, play ======= Work, work, work >>>>>>> d2659fcf690ec693c04c82b03202fc5530d50960/hello
Hello World It's a new day for git Play, play, play Work, work, work
$ git-commit -i hello
逆转与恢复:git-reset
命令形式:
命令的选项:
- --mixed
- 仅是重置索引的位置,而不改变你的工作树中的任何东西(即,文件中的所有变化都会被保留,也不标记他们为待提交状态),并且提示什么内容还没有被更新了。这个是默认的选项。
- --soft
- 既不触动索引的位置,也不改变工作树中的任何内容,我们只是要求这些内容成为一份好的内容(之后才成为真正的提交内容)。这个选项使你可以将已经提交的东西重新逆转至“已更新但未提交(Updated but not Check in)”的状态。就像已经执行过 git-update-index 命令,但是还没有执行 git-commit 命令一样。
- --hard
- 将工作树中的内容和头索引都切换至指定的版本位置中,也就是说自 <commit-ish> 之后的所有的跟踪内容和工作树中的内容都会全部丢失。因此,这个选项要慎用,除非你已经非常确定你的确不想再看到那些东西了。
一个重要技巧--逆转提交与恢复
$ git-checkout master $ git-checkout -b softreset $ git-show-branch
! [master] Merge branch 'robin' ! [robin] some work * [softreset] Merge branch 'robin' --- - - [master] Merge branch 'robin' + * [master^] Some fun ++* [robin] some work
$ echo "Botch, botch, botch" >> hello $ git-commit -a -m "some botch" $ git-show-branch
! [master] Merge branch 'robin' ! [robin] some work * [softreset] some botch --- * [softreset] some botch - - [master] Merge branch 'robin' + * [master^] Some fun ++* [robin] some work
$ cat .git/refs/heads/softreset .git/ORIG_HEAD
5e7cf906233e052bdca8c598cad2cb5478f9540a 7bbd1370e2c667d955b6f6652bf8274efdc1fbd3
git-reset --soft HEAD^
$ cat .git/ORIG_HEAD
5e7cf906233e052bdca8c598cad2cb5478f9540a
Hello World It's a new day for git Play, play, play Work, work, work Nice, nice, nice
$ git-diff ORIG_HEAD
diff --git a/hello b/hello index f978676..dd02c32 100644 --- a/hello +++ b/hello @@ -2,4 +2,4 @@ Hello World It's a new day for git Play, play, play Work, work, work -Botch, botch, botch +Nice, nice, nice
$ git-commit -a -c ORIG_HEAD
提取版本库中的数据
$ git-checkout -f foo.c
标定版本
$ git-tag my-first-tag
$ git-tag mytag f0af6283824688f9d23426031734657661b54388
$ git-tag -s <tag-name>
合并外部工作
$ git-fetch <remote-repository>
- Rsync
-
rsync://remote.machine/patch/to/repo.git/
- SSH
-
remote.machine:/path/to/repo.git
or
ssh://remote.machine/patch/to/repo.git/这是可以上传和下载的双向传输协议,当然,你要有通过 ssh 协议登录远程机器的权限。它可以找出两端的机器提交过的对象集之中相互缺少了那些对象,从而得到需要传输的最小对象集。这是最高效地交换两个版本库之间的对象的方式(在 git 兼容的所有传输协议当中)。下面是个取得 SSH 远程版本库的命令例子:$ git-fetch robin@192.168.1.168:/path/to/gittutorcn.git (1) (1) 这里 robin 是登录的用户名,192.168.1.168 是保存着主版本库的机器的 IP 地址。
- Local directory
-
/path/to/repo.git/本地目录的情况和 SSH 情况是一样的。
- git Native
-
git://remote.machine/path/to/repo.git/git 自然协议是设计来用于匿名下载的,它的工作方式类似于 SSH 协议的交换方式。
- HTTP(S)
-
[url]http://remote.machine/path/to/repo.git/[/url]
通过电子邮件交换工作
$ git-clone [url]http://www.bitsun.com/git/gittutorcn.git[/url]
Note |
如果你的确修改过 origin 分支的内容,那么在生成 patch 文件之前,请用 git-reset --hard 命令将它逆转到最原始的,没经过任何修改的状态。 |
* [master] your buddy's contribution ! [origin] degining of git-format-patch example -- * [master] your buddy's contribution *+ [origin] degining of git-format-patch example
$ git-fetch origin (1) $ git-rebase origin (2) $ git-format-patch origin (3) (1)更新 origin 分支,防止 origin 分支不是最新的公共版本,产生错误的补丁文件; (2)将你在 master 上提交的工作迁移到新的源版本库的状态的基础上; (3)生成补丁文件;
$ git-checkout -b buddy-incomming $ git-am /path/to/0001-your-buddy-s-contribution.txt
用 Git 协同工作
$ git-clone /home/alice/project myrepo
(编辑一些文件) $ git-commit -a (如果需要的话再重复这个步骤)
$ cd /home/alice/project $ git pull /home/bob/myrepo
$ git fetch /home/bob/myrepo master:bob-incoming
$ git whatchanged -p master..bob-incoming
$ git-checkout master $git-pull . bob-incoming
$ git-pull
$ git branch * master origin
$ git-clone alice.org:/home/alice/project/ myrepo
为版本库打包
$ git repack
Note |
你将会看到两个文件,pack-*.pack and pack-*.idx 在 .git/objects/pack 目录。他们的关系是很密切的,如果你手动将他们拷贝到别的版本库中的话,你要决定将他们一起拷贝。前者是保存着所有被打包的数据的文件,后者是随机访问的索引。 |
$ git prune-packed
Note |
git pull git-pull 对于 HTTP 传输来说,一个打包过的版本库会将一定数量的相关联的对象放进一个有关联性的打包中。如果你设想多次从 HTTP 公共版本库中导入数据,你也许要频繁地 reapck & prune,要么就干脆从不这样做。 |
发布你的工作
Note |
公共版本库是可以被映像的,kernel.org上的git公共版本库也是这样管理的。 |
Note |
git push使用一对命令,git-send-pack在本地机上运行,git-receive-pack在远程机上运行。这两个命令通过SSH连接来进行通讯。 |
$ mkdir my-git.git
$ GIT_DIR=my-git.git git-init-db
Note |
当你直接运行程序的时候,很多sshd的安装版并没有将你的shell作为登陆的shell;这就是说,如果你登陆的shell是bash 的话,被读到的是.bashrc而不是.bash_profile。确认.bashrc设置好了$PATH路径,这样你才可以运行git-receive-pack命令。 |
Note |
如果你打算通过HTTP来发布这个版本库,这是你就应该运行命令chmod +x my-git.git/hooks/post-update。这确认了每次你导入数据到这个版本库中,git-update-server-info能够被执行。 |
$ git push:/path/to/my-git.git master
$ git push master.kernel.org:/pub/scm/git/git.git/
将工作捆绑到一起
$ git show-branch ! [commit-fix] Fix commit message normalization. ! [diff-fix] Fix rename detection. * [master] Release candidate #1 --- + [diff-fix] Fix rename detection. + [diff-fix~1] Better common substring algorithm. + [commit-fix] Fix commit message normalization. * [master] Release candidate #1 ++* [diff-fix~2] Pretty-print messages.
$ git merge 'Merge fix in diff-fix' master diff-fix $ git merge 'Merge fix in commit-fix' master commit-fix
$ git show-branch ! [commit-fix] Fix commit message normalization. ! [diff-fix] Fix rename detection. * [master] Merge fix in commit-fix --- - [master] Merge fix in commit-fix + * [commit-fix] Fix commit message normalization. - [master~1] Merge fix in diff-fix +* [diff-fix] Fix rename detection. +* [diff-fix~1] Better common substring algorithm. * [master~2] Release candidate #1 ++* [master~3] Pretty-print messages.
$ git reset --hard master~2
$ git pull . commit-fix diff-fix $ git show-branch ! [commit-fix] Fix commit message normalization. ! [diff-fix] Fix rename detection. * [master] Octopus merge of branches 'diff-fix' and 'commit-fix' --- - [master] Octopus merge of branches 'diff-fix' and 'commit-fix' + * [commit-fix] Fix commit message normalization. +* [diff-fix] Fix rename detection. +* [diff-fix~1] Better common substring algorithm. * [master~1] Release candidate #1 ++* [master~2] Pretty-print messages.
管理版本库
-
git-daemon(1) 容许匿名下载版本库。
-
git-shell(1) 面向中心版本库模式的用户的类似 受限的 shell 的命令。
例子
- 在 /pub/scm 上运行 git 守护进程
-
$ grep git /etc/inet.conf git stream tcp nowait nobody \ /usr/bin/git-daemon git-daemon --inetd --syslog --export-all /pub/scm
这个配置行应该在配置文件中用一行来写完。 - 仅给开发者 push/pull 的访问权限。
-
$ grep git /etc/passwd (1) alice:x:1000:1000::/home/alice:/usr/bin/git-shell bob:x:1001:1001::/home/bob:/usr/bin/git-shell cindy:x:1002:1002::/home/cindy:/usr/bin/git-shell david:x:1003:1003::/home/david:/usr/bin/git-shell $ grep git /etc/shells (2) /usr/bin/git-shell (1) 将用户的登录 shell 设定为 /usr/bin/git-shell, 它除了运行 "git-push" 和 "git-pull" 不能做任何事。 这样用户就可以通过 ssh 来访问机器。 (2) 许多的发行版需要在 /etc/shells 配置文件中列明要用什么 shell 来作为登录 shell。
- CVS - 模式的公共库。
-
$ grep git /etc/group (1) git:x:9418:alice,bob,cindy,david $ cd /home/devo.git $ ls -l (2) lrwxrwxrwx 1 david git 17 Dec 4 22:40 HEAD -> refs/heads/master drwxrwsr-x 2 david git 4096 Dec 4 22:40 branches -rw-rw-r-- 1 david git 84 Dec 4 22:40 config -rw-rw-r-- 1 david git 58 Dec 4 22:40 description drwxrwsr-x 2 david git 4096 Dec 4 22:40 hooks -rw-rw-r-- 1 david git 37504 Dec 4 22:40 index drwxrwsr-x 2 david git 4096 Dec 4 22:40 info drwxrwsr-x 4 david git 4096 Dec 4 22:40 objects drwxrwsr-x 4 david git 4096 Nov 7 14:58 refs drwxrwsr-x 2 david git 4096 Dec 4 22:40 remotes $ ls -l hooks/update (3) -r-xr-xr-x 1 david git 3536 Dec 4 22:40 update $ cat info/allowed-users (4) refs/heads/master alice\|cindy refs/heads/doc-update bob refs/tags/v[0-9]* david (1) 将所有的开发人员都作为 git 组的成员。 (2) 并且给予他们公共版本库的写权限。 (3) 用一个在 Documentation/howto/ 中的 Carl 写的例子来实现版本库的分支控制策略。 (4) Alice 和 Cindy 可以提交入 master 分支,只有 Bob 能提交入 doc-update 分支, David 则是发行经理只有他能创建并且 push 版本标签。
- 支持默协议传输的 HTTP 服务器。
-
dev$ git update-server-info (1) dev$ ftp [email]user@isp.example.com[/email] (2) ftp> cp -r .git /home/user/myproject.git (1) 保证 info/refs 和 object/info/packs 是最新的。 (2) 上传到你的 HTTP 服务器主机。
项目开发的模式推介
-
在你自己的本地机器上准备好主版本库。你的所有工作都在这里完成。
-
准备一个能让大家访问的公共版本库。如果其他人是通过默协议的方式(http)来导入版本库的,那么你有必要保持这个 默协议的友好性。 git-init-db 之后,复制自标准模板库的 $GIT_DIR/hooks/post-update 将包含一个对 git-update-server-info 的调用,但是 post-update 默认是不能唤起它自身的。通过 chmod +x post-update 命令使能它。这样让 git-update-server-info 保证那些必要的文件是最新的。
-
将你的主版本库推入公共版本库。
-
git-repack 公共版本库。这将建立一个包含初始化提交对象集的打包作为项目的起始线,可能的话,执行一下 git-prune,要是你的公共库是通过 pull 操作来从你打包过的版本库中导入的。
-
在你的主版本库中开展工作,这些工作可能是你自己的最项目的编辑,可能是你由 email 收到的一个补丁,也可能是你从这个项目的“子系统负责人” 的公共库中导入的工作等等。你可以在任何你喜欢的时候重新打包你的这个私人的版本库。
-
将项目的进度推入公共库中,并给大家公布一下。
-
尽管一段时间以后,"git-repack" 公共库。并回到第5步继续工作。
-
准被一个你自己的工作目录,它通过 git-clone 克隆自项目领导人的公共库。原始的克隆地址(URL)将被保存在 .git/remotes/origin 中。
-
准备一个可以给大家访问的公共库,就像项目领导人所做的那样。
-
复制项目领导人的公共库中的打包文件到你的公共库中,除非你的公共库和项目领导人的公共库是在同一部主机上。以后你就可以通过 objects/info/alternates 文件的指向来浏览它所指向的版本库了。
- 将你的主版本库推入你的公共版本库,并运行 git-repack,如果你的公共库是通过的公共库是通过 pull 来导入的数据的话,再执行一下 git-prune 。
-
在你的主版本库中开展工作。这些工作可能包括你自己的编辑,来自 email 的补丁,从项目领导人,“下一级子项目负责人”的公共库哪里导入的工作等等。你可以在任何时候重新打包你的私人版本库。
-
将你的变更推入公共库中,并且请“项目领导人”和“下级子系统负责人”导入这些变更。
-
每隔一段时间之后,git-repack 公共库。回到第 5 步继续工作。
-
准备你的工作库,它应该用 git-clone 克隆自“项目领导人”的公共库(如果你只是开发子项目,那么就克隆“子项目负责人”的)。克隆的源地址(URL)会被保存到 .git/remotes/origin 中。
-
在你的个人版本库中的 master 分支中开展工作。
-
每隔一段时间,向上游的版本库运行一下 git-fetch origin 。这样只会做 git-pull 一半的操作,即只克隆不合并。公共版本库的新的头就会被保存到 .git/refs/heads/origins 。
-
用 git-cherry origin 命令,看一下你有什么补丁被接纳了。并用 git-rebase origin 命令将你以往的变更迁移到最新的上游版本库的状态中。(关于 git-rebase 命令,请参考 git-rebase)
- 用 git-format-patch origin 生成 email 形式的补丁并发给上游的维护者。回到第二步接着工作。
上一篇:textmate操作手册