Subversion svn
概念
Revision相关概念
处于svn管理之下的每个目录下都有个.svn子目录,这个目录称为管理子目录(Administrative
Subdirectory)。目录中的每个文件都会在管理子目录中有一份拷贝,这份拷贝是更新work
copy时该文件在仓库中的最新版本。这个拷贝的版本称为pristine
copy或text-base version,svn术语中也称为BASE revision。
| Keyword | Meaning |
|-----------|--------------------------------------|
| HEAD | 仓库中的最新版本 |
| BASE | 在本地更新操作时对应的版本 |
| COMMITTED | BASE之前(包括BASE)的最后修改的版本 |
| PREV | COMMITTED - 1 |
本地更新到HEAD版本,此时BASE和HEAD是同一个版本。过了一会儿,有其他人做了提交,此时HEAD就与BASE不同了。由此,每个团队成员的BASE有可能是不同的,然而HEAD始终只有一个。BASE,COMMITTED,PREV三个术语都只针对本地某个文件而言。
svn update状态字符含义
| Character | Meaning |
|-----------|--------------------------------------------|
| U | Updated(received changes from the server.) |
| A | Added |
| D | Deleted |
| R | Replaced |
| G | merGed(changes did not inersect) |
| C | Conflict(changes overlapped) |
svn revert
svn revert ITEM 相当于:
- 删除ITEM
- svn update -r BASE ITEM
它无需去访问repository。
冲突
冲突发生后,subversion会做的三件事情
- svn update会打印字符“C”
- 往发生冲突的地方添加conflict markers,用以标记冲突的位置。如:
`` example
int open_camera(struct frame* pframe, float f)
=======
int open_camera(struct frame* pframe, int k)
>>>>>>> .r10
- 针对每个冲突的文件,subversion都会在working
copy中生成以下三个额外的文件
| filename | Meaning |
|------------------|------------------------------------------|
| filename.mine | before update, without conflict markers. |
| filename.rOLDREV | BASE revision before update. |
| filename.rNEWREV | HEAD revision of repository. |
<h3 id="解决冲突的三种办法">解决冲突的三种办法</h3>
- 手动合并冲突的文本
- 将某个临时文件覆盖掉冲突的文件
- 用svn revert filename丢弃本地更改
第1,2种办法,最后都需要运行svn resolved
filename来告诉subversion冲突已经解决,随后三个临时生成的文件即会删除,subversion不再认为还有冲突。第3种办法不需要运行svn
resolved。
<h2 id="树冲突(tree conflicts)参看:Dealing" target="_blank">http://svnbook.red-bean.com/nightly/en/svn.tour.treeconflicts.html">Dealing with Structural Conflicts">树冲突(tree conflicts)参看:Dealing" target="_blank">http://svnbook.red-bean.com/nightly/en/svn.tour.treeconflicts.html">Dealing with Structural Conflicts</h2>
树冲突出现的条件:本地检出文件后,仓库中该文件被删除,本地修改文件,本地运行svn
up。
svn的move操作在实现时会先copy再delete,然而这两者并不会自动关联起来,如果要删除某个本地文件,svn一般会给出警告,然而该delete是与copy关联的delete还是独立的delete,需要用户自己来确定。
还可参看:SVN树冲突和目录丢失问题" target="_blank">http://www.oschina.net/question/103087_12309">SVN树冲突和目录丢失问题
<h3 id="解决方法一:接受仓库的修改">解决方法一:接受仓库的修改</h3>
删除本地文件,再标记为resolved。如果想保留自己的修改,可以能过diff命令保存patch,再手动更改patch中相关文件名为更名后的文件名。更新到新的版本,再应用自己的更新。
<h3 id="解决方法二:使用本地的版本">解决方法二:使用本地的版本</h3>
example
$ svn delete --force code/baz.c 删除服务器上被人更改的文件
$ svn resolve --accept=working code/bar.c 用本地文件作为准。
<h2 id="删除中文帮助信息">删除中文帮助信息</h2>
svn update如果碰到冲突,默认情况下会等待用户输入,此时用lsof -p
xxxx查看svn
update打开的文件,可以查到打开了/usr/share/locale-langpack/zh<sub>CN</sub>/LC<sub>MESSAGES</sub>/subversion.mo。进一步查找发现Ubuntu中是由language-pack-zh-hans包提供的。删除此文件,svn所有命令帮助即变成英文。
<h2 id="保存帐号及密码">保存帐号及密码</h2>
- 简单认证的帐号信息在~/.subversion/auth/svn.simple下,删除相关文件即可。
- eclipse中使用JavaHL,方法与上面相同。
- eclipse中使用SVNKit,帐号信息用eclipse自身的服务中,保存帐号信息的文件的位置为eclipse*configuration/org.eclipse.core.runtime*.keyring。
<h2 id="配置注意事项">配置注意事项</h2>
在svnserve.conf的annon-read=read的情况下,如果在authz中有如下配置 \[/ \]
user=rw 在切换分支或与服务器上的版本进行对比时会出错"svn: Not authorized
to open root of edit
operation"。解决方案有两个,一是设置annon-read=none。二是在authz中添加\*=rw。
<h2 id="删除svn版本控制信息">删除svn版本控制信息</h2>
example
find . -type d -name '.svn' | xargs -n1 rm -fr
如果使用下面的命令删除.svn目录,会有很多警告信息:
example
find . -type d -name '.svn' -exec rm -fr {} \;
正确的写法是:
example
find . -depth -type d -name '.svn' -exec rm -fr {} \;
-depth表示深度优先,会先处理.svn目录下的内容,再删除目录,而不指定-depth,则在删除.svn目录后,再去处理.svn目录下的文件,就会导致大量错误警告信息。
<h2 id="取出单个文件">取出单个文件</h2>
example
svn co --depth=empty svn://host/images images_work_dir
svn up logo.jpg
<h2 id="Tools">Tools</h2>
- User" target="_blank">http://www.usvn.info/">User Friendly SVN Subversion仓库管理软件
- websvn <http://websvn.tigris.org/>
- subversion-tools <http://subversion.tigris.org/> miscellaneous tools
for use with Subversion clients and servers.
<h2 id="eclipse中无法显示Share Project的问题">eclipse中无法显示Share Project的问题</h2>
subclipse中如果没有正常disconnect,同时卸载了Subversion Team
Provider,那么就会无法显示"Share
Project"菜单项。唯一的解决办法是删除该工程(在硬盘上保留),然后再导入该工程。
<h1 id="Git <span class="tag" tag-name="git"><span class="smallcaps">git</span></span>">Git <span class="tag" tag-name="git"><span class="smallcaps">git</span></span></h1>
<h2 id="特点">特点</h2>
- CVS,Subversion,Perforce等都是针对每个文件保存更改变量,而git对每个文件保存一个快照,如果某个文件在新版本中没有更改,则会用一个链接指向未更改的文件。
- Git中大量的操作都是本地操作,所有的历史都保存在本地数据为中,这与其他几种VDS有很大不同。
- Git中的每个文件都会生成hash码(check-sum),用的算法是SHA-1。
- Git只添加数据,而不能删除数据。
<h2 id="配置">配置</h2>
<h3 id="https用户名和密码">https用户名和密码</h3>
- 使用.git-credentials
example
https://{username}:{password}@github.com
- 使用credential.helper
example
git config --global credential.helper store
- 使用.netrc
example
machine {git account name}.github.com
login your-usernmae
password your-password
<h3 id="Multiple SSH Keys settings for different github account">Multiple SSH Keys settings for different github account</h3>
- create different public key
example
$ ssh-add ~/.ssh/id_rsa_activehacker
$ ssh-add ~/.ssh/id_rsa_jexchan
you can delete all cached keys before
example
$ ssh-add -D
finally, you can check your saved keys
example
$ ssh-add -l
- Modify the ssh config
bash
#activehacker account
Host github.com-activehacker
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_activehacker
#jexchan account
Host github.com-jexchan
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_jexchan
<h3 id="use git through proxy">use git through proxy</h3>
<http://cms-sw.github.io/tutorial-proxy.html> How to open a SOCKS proxy
through an SSH tunnel The ssh command distributed with most Unix-like
systems can open a SOCKS proxy on the local machine and forward all
connections through the ssh tunnel. For example
example
ssh -f -N -D 1080 cmsusr.cms
will connect to the host cmsusr.cms, open a SOCKS proxy on the local
host on port 1080 (-D 1080), and put the process in background after a
successful connection (-f -N). How to connect to a git repository using
the SSH protocol
If the remote has a format like
git@github.com:cms-sw/cmssw.git <ssh://git@github.com/cms-sw/cmssw.git>
then you are connecting to the git server using the SSH protocol.
In this case, git relis on ssh to handle the connection; in order to
connect through a SOCKS proxy you have to configure ssh itself, setting
the ProxyCommand option in your ~/.ssh/config file:
Host github.com User git ProxyCommand nc -x localhost:1080 %h %p
For more information, see ssh<sub>config</sub>(5). How to connect to a
git repository using the HTTP or HTTPS protocols
If the remote has a format like
<http://github.com/cms-sw/cmssw.git>
<https://github.com/cms-sw/cmssw.git>
then you are connecting to the git server using the HTTP or HTTPS
protocols. In the old days, this used to be a dumb protocol, but since
git 1.6.6 it uses a smart protocol similar to that used by SSH or GIT.
In this case git uses libcurl to handle the connection; the version of
git bundled with CMSSW supports different kinds of proxies: SOCKS4,
SOCKS4a, SOCKS5, and HTTP/HTTPS. In order to connect through any proxy
supported by libcurl, you can set the http.proxy option:
git config –global http.proxy socks5://localhost:1080
For more information, see the –proxy option in curl(1) and the
http.proxy entry in git-config(1). How to connect to a git repository
using the GIT protocol
If the remote has a format like
<git://github.com/cms-sw/cmssw.git>
then you are connecting to the git server using the GIT protocol.
In this case, it is possible to use a helper command to connect through
any kind of proxy. A simple script is included with CMSSW, to connect
through a SOCKS5 proxy: git-proxy. You can configure git to use it with
git config –global core.gitproxy "git-proxy" git config –global
socks.proxy "localhost:1080"
For more information, see git-proxy –help.
<h3 id="Remove sensitive data">Remove sensitive data</h3>
<https://help.github.com/articles/remove-sensitive-data/>
<h3 id="当前路径对Git的影响">当前路径对Git的影响</h3>
Git的pull操作其实是fetch和merge的组合,git merge操作会去搜索working
tree,如果不指定参数的情况下,会在当前路径去搜索。当结合sudo来使用git时,git的这种行为会产生问题:
example
#> cd ~
#> sudo -u dmalikov git --git-dir=/home/dmalikov/path/to/repo/.git pull
/usr/libexec/git-core/git-sh-setup: line 142: cd: /root/.: Permission denied
Cannot chdir to /root/., the toplevel of the working tree
这里出现了切换路径的权限错误,究其原因是git
merge操作要从当前目录,也就是root用户所在的目录下去找,由于dmalikov用户没有切换到root用户home目录的权限,于是出现这个错误。解决办法是使用fetch:
example
sudo -u dmalikov git --git-dir=/home/dmalikov/path/to/repo/.git fetch
也可以先切换工作目录,然后再操作pull:
example
(cd /home/dmalikov/path/to/repo; sudo -u dmalikov git pull)
<h2 id="本地操作">本地操作</h2>
| Source Main Section | Destination Main Section | Action | Status |
|---------------------|--------------------------|----------------|-----------|
| Working Directory | Staging Area | Stage files | Staged |
| Staging Area | Git Directory | Commit files | Committed |
| Git Directory | Working Directory | Checkout files | Committed |
Git中的文件共有三种状态:committed,modified,staged。文件在修改以后,Stage之前的状态称为Modified。Staging
Area是git目录中的一个文件,存放了下次需要commit的信息,有时也称为index。
<h2 id="Remotes">Remotes</h2>
<h3 id="概念">概念</h3>
A remote is a reference, or handle, to another repository through a
filesystem or network path. You use a remote as a shorthand name for an
otherwise lengthy and complicated Git URL.
为了跟踪其它仓库中的数据,git使用了remote-tracking
branches。本地仓库的remote-tracking branch就是remote
repository的代理。你还可以设置local-tracking branch。 bare
repository没有检出的branch,不能在上面提交。bare
repository是协作开发的中心点,开发者通过clone和fetch操作从bare
repo中抽取数据,通过pull操作更新bare repo。bare
repo中也不会启用reflog。如果要让开发者往repo中push,那么这个repo就得是bare。
- git clone
原repo中refs/heads/下的branch会被复制到refs/remotes/下,而原repo中的refs/remotes并不会复制到新的repo。tag也会被clone复制过来,但像hooks,configuration
files,reflog,stash等就不会被复制过来了。
默认情况下,新clone的repo中会保留一个名为origin的链接,用来指向原repo。不过原repo可不会知道有谁连接了它。
<h3 id="常用操作">常用操作</h3>
| 命令 | 含义 |
|------------------------------------------|-------------------------------------|
| git clone URL | 将远程仓库克隆到本地目录,origin |
| git remote -v | 列出所有本地设定的remote repository |
| git remote add \[remote-name\] URL | 添加remote repository |
| git fetch \[remote-name\] | 从其他repository中抽取对象 |
| git push \[remote-name\] \[branch-name\] | 提交更改 |
| git remote show \[remote-name\] | 查看remote信息 |
<h3 id="git fetch">git fetch</h3>
从远程仓库下载数据,但不会自动合并,对本地的文件不会有任何影响,合并必须用git
merge。如果设定了本地branch track remote branch,则用git
pull命令会自动fetch并merge到当前branch。git clone会自动让本地master
branch去track remote master branch,因此git clone执行之后可用git
pull自动更新到远程repository。
<h3 id="git push">git push</h3>
如果push upstream之前已经有他人push
upstream,git会拒绝你的push,你必须再次pull
down,incorporate,然后再push upstream。
<h3 id="重置远程master到以前的提交">重置远程master到以前的提交</h3>
example
git checkout master
git reset --hard e3f1e37
git push --force origin master
example
git diff master..origin/master
<h2 id="Branch">Branch</h2>
<h3 id="概念">概念</h3>
example
git add README test.rb LICENSE2
git commit -m 'initial commit of my project'
提交以后git repository中会有如下五个对象: Single Commit Repository
Data
其中blob即为提交的文件。所谓branch,即是指向这些commit对象的轻量级的可移动的指针。说是轻量级,因为git中创建一个branch只需写入40个字节的SHA-1
checksum和1个回车符到repository的文件中。
Git默认的branch名为master,这是在commit后自动创建的。用git branch
\[branch-name\]可创建新的branch,但git
branch命令只创建,并不会切换到新的branch。Git通过一个特殊的指针HEAD来指向当前所在branch,这与Subversion的HEAD有着天壤之别。切换branch用git
checkout \[branch-name\]命令,此命令只是让HEAD指针指向新的branch。git
checkout -b \[branch-name\]是创建并切换的快捷方式,相当于git branch
\[branch-name\] ; git checkout \[branch-name\]。
切换branch是有条件的,如果index中有未解决的冲突,则无法checkout。必须在切换前解决这些冲突。
<h3 id="Merge">Merge</h3>
- Fast Forward
example
git checkout master
git merge hotfix
Git Merge Fast
Forward
- Simple Three-Way Merge
Three-Way: two branch, the common ancestor of the two.
Git自动找出两个分支的common ancestor,以common
ancestor为合并的base,合并以后自动commit,此commit称为merge
commit。merge commit对象与一般commit对象不同之处在于它有多个parent。
Three-way merge identify common
ancestor
Three-way merge after
commit
- Merge Conflicts
example
git merge iss53 #如果有冲突,则会提示CONFLICT,自动merge只完成了没有冲突的部分,并且不会提交。冲突的文件中,git会添加standard conflict-resolution marker。
git status #查看需要手动merge的文件。
编辑冲突文件或用git mergetool
example
git add [conflict-file] #标记冲突已经解决
git commit #此时会显示Merge branch信息
<h3 id="Remote Branch">Remote Branch</h3>
Remote branch是一种特殊的branch,你不能去控制它,只能通过网络更新。用git
clone命令复制repository后,本地的branch结构如下: Branches after git
clone
如果要共享本地branch,则需使用push操作(subversion中完全没有这个概念,只要commit即会提交到服务器,而git没有中央服务器一说,只谈共享某个branch)。
git push origin serverfix 将本地serverfix branch共享到origin remote git
push origin serverfix:awesomebranch 将本地serverfix branch共享到origin
remote的awesomebranch branch Tracking Branch是一种特殊的local
branch,Tracking Branch知道与自己与哪个remote
branch关联,不带任何参数使用git push和git pull就能自动push和pull。git
clone后,默认本地的master即与origin/master关联。有两种方法创建Tracking
Branch:
- git checkout -b \[branch\] \[remotename\]/\[branch\]
- git checkout –track \[remotename\]/\[branch\]
删除remote branch的语法比较古怪: git push \[remotename\] :\[branch\]
<h3 id="Rebasing">Rebasing</h3>
Rebase和Merge最终的效果是一样的,不同之处在于两者的历史不同。Rebase会把两个分支上所有的操作依序排列,看起来像是线性的,而merge的历史看起来是多条线并行的。使用Rebase的原则是:决不要对已经在公共repository上发布的commit进行rebase。Rebase只可用作在push之前,用来让自己的工作更干净。
<h2 id="fetch和pull的区别">fetch和pull的区别</h2>
fetch从远程仓库取出数据,但不会自动merge到当前work
directory。而pull则包含了merge操作。
<h2 id="Rebase和Merge">Rebase和Merge</h2>
将某分支的更改整合到另一分支上,Git中可用Rebase或Merge来完成。主要区别在于:Rebase会更改历史,而Merge会保留合并的历史。
<h3 id="Rebase">Rebase</h3>
Rebase可移动某个分支,不过commit并不能被移动,rebase实际做的事情是用原来的commits的changeset和metadata创建新的commit。Rebase步骤如下:
- Identify the commits to be moved (more accuratedly,replicated).
- Compute the corresponding changesets (patches).
- Move HEAD to the new branch location (base).
- Apply the changesets in order, making new commits preserving author
information. (replaying)
- Finally, update the branch ref to point to the new tip commit.
<h2 id="合并git仓库">合并git仓库</h2>
bash
git remote add repo2
git fetch repo2
git co -b newb repo2/master
git mv xxx xxx (把你想移动的文件移动到你喜欢的位置)
git co master
git merge newb
git remote rm repo2
<h2 id="Hooks">Hooks</h2>
共有两类Hook,一类是client
side,发生在像commit和merge操作的时候,另一类是server side,发生在git
server接收到pushed commit的时候。
<h2 id="Internals">Internals</h2>
Git底层是content-addressable filesystem,上面加了VCS接口。
Git中这些底层的命令称为“plumbing” commands或“porcelain”
commands,一般在脚本中使用得比较多。
<h3 id="blob对象">blob对象</h3>
Git就像一个简单的key-value data store。通过plumbing command
hash-object,就能往.git目录中添加数据,并获得key。如:
example
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
然后就能找到对象文件了:
example
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
用cat-file命令可以查看该object的内容:
example
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
保存文件话,无非多个参数:
example
$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
这些文件内容对象称为blob,可用下面的命令来看:
example
$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob
<h3 id="tree对象">tree对象</h3>
至于文件名是通过tree object来存放的。tree对象由一个或多个tree
entry组成,tree entry包含了指向blob或subtree的SHA-1指针,以及mode, type,
filename等。比如:
example
$ git cat-file -p master^{tree}
master<sup>tree</sup>表示上一个commit指向的tree对象。
<h3 id="commit对象">commit对象</h3>
blob对象存放文件内容,tree对象相当于blob和tree的容器,至于保存的时间、作者等信息就要通过commit对象来存放。
example
$ echo 'first commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
通过查看commit对象可以看到top tree对象及作者等信息:
example
$ git cat-file -p fdf4fc3
<h3 id="tag对象">tag对象</h3>
tag对象与commit对象很相似,唯一不同在于tag对象有指针指向commit对象,而不是commit对象一样指向tree对象。
tag有两种:annotated and lightweight。lightweight
tag只是一个引用,创建后就不会再有修改。
example
$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
annotated tag则会创建tag对象,在对象中指向commit对象。
example
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'
Git中任何对象都是可以tag的。
<h3 id="remote引用">remote引用</h3>
remote引用与branch引用唯一的区别在于remote引用是只读的。可以从remote引用中checkout,但HEAD并不会指向个引用,因此无法通过commit来更改。remote引用更像是书签,Git用来记录上一次使用时的服务器分支的状态。
<h3 id="refspec">refspec</h3>
example
$ git remote add origin https://github.com/schacon/simplegit-progit
执行上面的命令后,Git会添加如下配置:
[remote "origin"]
url = https://github.com/schacon/simplegit-progit
fetch = +refs/heads/*:refs/remotes/origin/*
refspec的格式为:+\<src\>:\<dst\>
其中,src为远程主机上的引用,dst为本地引用,+号表示即使不是fast-forward也可更新。
远程仓库添加成功后,在本地就可访问远程各个分支的日志了,下面三条命令的效果是相同的。
example
$ git log origin/master
$ git log remotes/origin/master
$ git log refs/remotes/origin/master
如果要限定只同步远程master分支,那么可作如下配置:
example
fetch = +refs/heads/master:refs/remotes/origin/master
也可以通过命令行指定:
example
$ git fetch origin master:refs/remotes/origin/mymaster
push指定分支到远程master:
example
$ git push origin master:refs/heads/qa/master
也可在配置文件中添加如下行:
example
push = refs/heads/master:refs/heads/qa/master
删除远程分支:
example
$ git push origin :topic
\<src\>:\<dst\>格式中让\<src\>为空,意味着远程分支会被删除。
<h2 id="日志">日志</h2>
<h3 id="在日志中找出指定文件的操作记录">在日志中找出指定文件的操作记录</h3>
工程根目录下的README.md被删除,时间已经过去很久,要找回来。
example
git log -- README.md
找出最后修改该文件之前的commit。
example
git checkout 65d9f1a43e5830618c7693d6fef4845ed9aade85 -- README.md
再将新的文件重新提交。
<h3 id="Git代码行统计命令集">Git代码行统计命令集</h3>
统计某人的代码提交量,包括增加,删除:
example
git log --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }' -
仓库提交者排名前 5(如果看全部,去掉 head 管道即可):
example
git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5
仓库提交者(邮箱)排名前
5:这个统计可能不会太准,因为很多人有不同的邮箱,但会使用相同的名字
example
git log --pretty=format:%ae | gawk -- '{ ++c[$0]; } END { for(cc in c) printf "%5d %s\n",c[cc],cc; }' | sort -u -n -r | head -n 5
贡献者统计:
example
git log --pretty='%aN' | sort -u | wc -l
提交数统计:
example
git log --oneline | wc -l
添加或修改的代码行数:
example
git log --stat|perl -ne 'END { print $c } $c += $1 if /(\d+) insertions/;'
git log --stat | perl -ne '$insertions += $1 if /(\d+) insertions/; $commits += 1 if /^commit /; END {print "commits: ${commits}, insertions: ${insertions}\n";}'
<h3 id="查看branch之间关系">查看branch之间关系</h3>
example
git log --graph --decorate --oneline --simplify-by-decoration --all
说明: –decorate 标记会让git log显示每个commit的引用(如:分支、tag等)
–oneline 一行显示 –simplify-by-decoration
只显示被branch或tag引用的commit –all
表示显示所有的branch,这里也可以选择,比如我指向显示分支ABC的关系,则将–all替换为branchA
branchB branchC
<h2 id="常用操作">常用操作</h2>
- 从其它分支中拷贝文件。
example
git checkout [branch] -- file name
<h2 id="查看某个文件的修改历史">查看某个文件的修改历史</h2>
- git log –pretty=oneline 文件名
- git show 356f6def9d3fb7f3b9032ff5aa4b9110d4cca87e
<h2 id="用githug学习git">用githug学习git</h2>
example
gem install githug
``