2010年4月27日星期二

Git详解(二)

Git中级用法:


Ignoring files
项目里总会出现你不想跟踪的文件。当然这些文件你不对它们进行git add操作就行了,但是这样也很麻烦, 如果使用git add .命令和git commit -a命令呢?你能告诉git去忽略一些文件,我们只需要在我们工作目录顶级创建一个.gitignore文件就可以了。就像这样:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ vi .gitignore
 
 
# Lines starting with '#' are considered comments.
# Ignore any file named foo.txt.
foo.txt
# Ignore (generated) html files,
*.html
# except foo.html which is maintained by hand.
!foo.html
# Ignore objects and archives.
*.[oa]
# Ignore log direction and .DS_Store files
/log
.DS_Store


你可以到http://www.kernel.org/pub/software/scm/git/docs/gitignore.html看gitignore的详细解释。你也可以把.ignore文件当到你工作目录里的任意tree目录里,然后你只需要把这个.ignore文件加到你的git 控制下,使用git add命令。


Rebasing
假设你在远程的分支origin上创建了一个分支mywork。



1
$ git checkout -b mywork origin


创建一个新分支并选中它。
然后你做了一些改变:



1
2
3
4
$ vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit


与此同时,另外一些人在origin branch 上也做了一些工作,创建了两个提交。这就意味着origin和mywork会有冲突。在这一点上,你可以用pull来merge你的改变,结果是创建了一个新的merge commit。 但是你只想在mywork分支上保留你的commit历史,而不需要任何merge,那么你可以选择使用git rebase:



1
2
$ git checkout mywork
$ git rebase origin


这个命令会把你每次从mywork的提交都会以补丁的形式暂时保存在一个叫.git/rebase的目录下,当更新到origin的最新版本的时候,就会把这个补丁给新的mywork打回去,这样,就不会有任何merge commit在历史记录里。


一旦你的‘mywork’引用指向的是新被创建的那个commit对象,那么老的commit对象会被遗弃,如果你运行垃圾回收器(git gc),它们很可能会被移除。


如果rebase的过程发现冲突,那么在你解决冲突以后,使用git add命令把它们再次更新到git index里,然后你要使用:



1
$ git rebase --continue


接着中断前的rebase来,但是如果你想返回到你rebase前的状态,那只能让这个过程流产了:



1
$ git rebase --abort


Interactive Rebasing
你可以使用互动的rebase,使用这个模式,你可以在把它们提交到某地之前重写你自己的commit对象。 它可以让你容易分离merge和re-order commit, 你也可以清除已经pull到本地的commits。


你可以在git rebase后面加参数 -i,和 --interactive来给commit应用interactive模式。$ git rebase -i origin/master


一旦你执行了git rebase  -i,你就会被扔到编辑模式,我本地的代码库已经乱了,所以偷个例子吧,呵呵:
pick fc62e55 added file_size
pick 9824bf4 fixed little thing
pick 21d80a5 added number to log
pick 76b9da6 added the apply command
pick c264051 Revert “added file_size” - not implemented correctly


# Rebase f408319..b04dc3d onto f408319
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#


这是说,你有5个提交。每个提交遵循下面的格式:
(action) (partial-sha) (short commit message)


现在你可以改变那个action(默认是pick),可以改成,edit或squash,或者只是保留pick。你也可以reorder commit,移除你不想要的那行就行了。当你退出编辑模式,git就会尝试应用新的commit。


Interactive Adding
这是个好方法,你可以用它来微操你的每次git add 文件到git index。 启动这个模式的命令就是:



1
git add -i


然后输出:



1
2
3
4
5
6
7
8
9
10
11
12
$>git add -i
staged     unstaged path
1:    unchanged        +4/-0 assets/stylesheets/style.css
2:    unchanged      +23/-11 layout/book_index_template.html
3:    unchanged        +7/-7 layout/chapter_template.html
4:    unchanged        +3/-3 script/pdf.rb
5:    unchanged      +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown
 
*** Commands ***
1: status   2: update   3: revert   4: add untracked
5: patch    6: diff     7: quit     8: help
What now>


看到那些命令了吧。你可以选择,如果不懂,可以输入8,然后回车,就会输出帮助:



1
2
3
4
5
6
7
8
9
10
status        - show paths with changes
update        - add working tree state to the staged set of changes
revert        - revert staged set of changes back to the HEAD version
patch         - pick hunks and update selectively
diff          - view diff between HEAD and index
add untracked - add contents of untracked files to the staged set of changes
*** Commands ***
1: [s]tatus      2: [u]pdate      3: [r]evert      4: [a]dd untracked
5: [p]atch      6: [d]iff      7: [q]uit      8: [h]elp
What now>


当然你不输入数字,直接输入help也是一样的效果。


这种模式下,你有多种选择。当你选择完毕以后,那么输入7或q退出,然后git commit你的改变,记住这里不要用git commit -a,否则你前面都做无用功了。


Stashing
当你在工作中发现一个不相关的但是却显而易见微不足道的bug的时候,你可能想在继续你的工作之前修复它。你可以用git stash来保存当前的工作状态,在修复完那个bug之后,再回来继续。



1
$ git stash "work in progress for foo feature"


这个命令会把你当前的改变保存到stash,并且会重新设置你的工作tree和匹配index到你修改bug的状态,这样在你提交修改bug代码的时候,就不会和你之前做的工作相冲突了:
… edit and test …



1
$ git commit -a -m "fix:bugs"


然后,你可以回到你之前的工作状态:



1
$ git stash apply


很好很强大。 svn好像没这功能吧?


Stash Queue


你可以stashing了多个状态(老是修bug)


这可以看到这个stash list:



1
2
3
$>git stash list
stash@{0}: WIP on book: 51bea1d... fixed images
stash@{1}: WIP on master: 9705ae6... changed the browse code to the official repo


你可以使用命令来回到你想去的那个stash:



1
git stash apply stash@{1}


清除stash可以用命令:



1
git stash clear


Git Treeishes


除了吃力的写出那40位的sha乱码来引用一个commit对象或是tree对象之外,还有很多方法。在Git里这些被称为 treeish。

Partial Sha

如果你的commit sha是980e3ccdaac54a0d4de358f3fe5d718027d96aae,git可以通过下列标识来识别它:
980e3ccdaac54a0d4de358f3fe5d718027d96aae
980e3ccdaac54a0d4
980e3cc


我自己试了试前5位,ms也能识别。


Branch, Remote or Tag Name
你也可以用branch,remote,tag name代替一个sha。
如果你的master 分支是在980e3 commit上,而且你也把它push到了origin,并且它也有一个tag name叫v1.0,那么下面这些是等价的:
980e3ccdaac54a0d4de358f3fe5d718027d96aae
origin/master
refs/remotes/origin/master
master
refs/heads/master
v1.0
refs/tags/v1.0
都代表同一个commit object



1
2
3
$ git log master
 
$ git log refs/tags/v1.0


输出是一样的。


Date Spec



1
2
git log master@{yesterday}
git log master@{1 month ago}


注意这种格式的,得到的sha会是不同的。


Ordinal Spec
git log [email protected]{5}, 同上。


Carrot Parent
这会给你一个特别commit的第n次父类。这个格式仅对merge commits有用 - commit对象有大于一个的父类。
master^2


Tilde Spec
给你第n次commit对象的外祖父类。上上级。
master~2
这会返回这个master指向的commit对象的第一个parent的第一个parent。等价于:
master^^
你也可以这么做:
master^^^^^^
master~3^~2
master~6


Tree Pointer
master^{tree}


Blob Spec
master:/path/to/file


Range
你可能指定一个commit的范围,那么:
7b593b5..51bea1
它会包含从7b593b5开始的每次提交:
7b593b5..


Tracking Branches
一个tracking branche是指链接到远程分支的一个本地分支。当你往这个分支push和pull的时候,会自动的的push或pull到远程的那个分支。


git clone命令会自动的设置一个master分支,它是origin/master的tracking branch。
你也可以通过–track参数手动创建一个tracking branch:



1
git branch --track experimental origin/experimental


然后,当你运行:
$ git pull experimental
它会自动的从origin取代码,并且merge origin/experimental到你本地的experimental分支。
同样,当你push到origin,它会push你experimental分支到origin的experimental。


Finding with Git Grep
使用git grep(http://www.kernel.org/pub/software/scm/git/docs/git-grep.html)可以很方便的查找文件。这和通常的unix命令grep功能类似。
例如,我想查看在git版本库里用到def的每个地方,我可以运行:



1
2
3
4
5
6
7
8
9
10
11
$ git grep def
config/boot.rb:RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
config/boot.rb:    def boot!
config/boot.rb:    def booted?
config/boot.rb:      defined? Rails::Initializer
config/boot.rb:    def pick_boot
config/boot.rb:    def vendor_rails?
config/boot.rb:    def preinitialize
config/boot.rb:    def preinitializer_path
config/boot.rb:    def run
...


如果我想看行数,我也可以加-n参数:



1
2
3
4
5
6
7
8
9
10
11
$ git grep -n def
config/boot.rb:4:RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
config/boot.rb:8:    def boot!
config/boot.rb:15:    def booted?
config/boot.rb:16:      defined? Rails::Initializer
config/boot.rb:19:    def pick_boot
config/boot.rb:23:    def vendor_rails?
config/boot.rb:27:    def preinitialize
config/boot.rb:31:    def preinitializer_path
config/boot.rb:37:    def run
...


如果是仅仅感兴趣它在哪个文件,我们可以用–name-only参数:



1
2
3
4
5
6
7
$ git grep --name-only def
config/boot.rb
config/environment.rb
config/initializers/inflections.rb
config/initializers/new_rails_defaults.rb
config/routes.rb
...


我们也可以看有多少行匹配def:



1
2
3
4
5
6
$ git grep -c def
config/boot.rb:18
config/environment.rb:9
config/initializers/inflections.rb:1
config/initializers/new_rails_defaults.rb:2
config/routes.rb:2


更多可以看git grep的文档。


Undoing in Git - Reset, Checkout and Revert


Fixing un-committed mistakes
如果你已经混乱了你的working tree,但是还没有提交,那么可以使用这个命令来恢复到最近提交时候的状态:



1
$ git reset --hard HEAD


这个命令会扔掉你所有的改变,包括已经加到git index里的改变。


如果你只是想restore一个文件,那么使用checkout:



1
2
$ git checkout -- hello.rb
$ git checkout HEAD hello.rb


Fixing committed mistakes
对于已经提交的错误,有两个方法:
你可以创建一个新的commit,去undo你旧的提交做的那些事情。这是个正确的做法。


你也可以返回来修改旧的commit。一般不要这样做。


Fixing a mistake with a new commit
使用git revert命令:



1
$ git revert HEAD


你也可以revert更早的commit,例如:



1
$ git revert HEAD^


Fixing a mistake by modifying a commit


可以使用git rebase -i 修改你的commit,或用–amend参数。


Maintaining Git
Ensuring good performance
对于一个大的git仓库,可能会有一些历史信息占用了太多的空间或内存。那么可以用命令:



1
2
3
4
5
6
7
8
9
10
git gc
输出:
git gc
Counting objects: 810, done.
Compressing objects: 100% (723/723), done.
Writing objects: 100% (810/810), done.
Total 810 (delta 95), reused 0 (delta 0)
Removing duplicate objects: 100% (256/256), done.
 
Ensuring reliability


git fsck命令会在代码仓里执行一些检查并返回一些问题报告,最常见的警告是关于dangling对象:



1
2
git fsck
dangling blob 1b2b2a5e3f9518e2aa243337683391451b281d7c


Setting Up A Public Repository
假设你的私人repos是在~/.proj下面。我们首先要创建一个repos的新的clone,并且告诉git-daemon 它是public的:



1
2
$ git clone --bare ~/proj proj.git
$ touch proj.git/git-daemon-export-ok


这个结果是导出一个‘赤裸的’git repos。


下一步copy proj.git到你计划作为一个public repos的服务器。你可以用scp,rsync,或者其他。


Exporting a git repository via the git protocol
这是首选的方法。
可以启动git daemon服务,详细的看:http://www.kernel.org/pub/software/scm/git/docs/git-daemon.html


Exporting a git repository via http



1
2
3
4
$ mv proj.git /home/you/public_html/proj.git
$ cd proj.git
$ git --bare update-server-info
$ chmod a+x hooks/post-update


最后两行代码可以参看:
http://www.kernel.org/pub/software/scm/git/docs/git-update-server-info.html
http://www.kernel.org/pub/software/scm/git/docs/githooks.html
这样就可以了:



1
$ git clone http://yourserver.com/~you/proj.git



Setting Up a Private Repository


Repo Access over SSH


最简单的方式是通过ssh来使用git。



1
2
$ git clone --bare /home/user/myrepo/.git /tmp/myrepo.git
$ scp -r /tmp/myrepo.git myserver.com:/opt/git/myrepo.git


Multiple User Access using Gitosis
如果不想为每个用户都开一个单独的帐号,你需要用一个叫gitosis的工具。
http://www.urbanpuddle.com/articles/2008/07/11/installing-git-on-a-server-ubuntu-or-debian




Related posts:

  1. Git一分钟教程 流程:取代码 → 每次工作前更新代码到最新版本 → 修改代码 → 提交代码到服务器 取代码及修改全局设置 设置用户名与邮箱 git...
  2. Git服务器安装 Git on Ubuntu Server 安装Git-Core: sudo apt-get update # 可选步骤...
  3. Git详解(一) 我用git最开始是在github,每次提交代码以后,右上角的部分就会显示这些信息,类似于下面的: commit  fa6f27b7de063c2f301b0e7148b5bd5e813faa98 tree       5e7a19c158b89fbc52a078771a833ee839727404 parent   76f31606376180ca88efa12be341dbb14fb06fdf 咋一看,这40位的乱码挺吓人的,但是你了解它的作用就不会被吓到了。 这是object name,是作为你每次提交的信息标识。这是用SHA1加密hash函数根据你的对象的内容算出来的。Git的一些优点:...
  4. Satellite: a self-syncing distributed wiki 介绍 satellite is a self-syncing distributed wiki with file uploads...


Related posts brought to you by Yet Another Related Posts Plugin.

没有评论:

发表评论