归纳总结。
一。基本应用
1.安装
git的安装除了git core这个东西以外,我们还需要安装git gui和gitk,这些东西后面会方便我们的工作。
ubuntu下:直接采用新立德安装git。如果使用apt-get,可能会遗漏一些库文件,推荐将git的一些GUI工具也安装一下,这样可以更直观的了解我们的版本发展情况。
windows下:也有相应的安装文件,http://code.google.com/p/msysgit/downloads/list在这个目录下可以下载到,一般推荐安装这个list里面的第一。
git有一些全局的设置和变量,我们可以通过git config -l去查看这些参数,git在提交修改的时候会记录提交者的姓名和email,这样便于代码的整体管理,安装后我们需要设置一下我们的一些相关参数,具体设置的命令
git config --global user.name "XXXX" 设置全局的作者名
git config --global user.email "XXX" 设置全局的email地址。
这些变量根据选项的不同,放的位置也不同,如果使用--global选项,这些配置信息只是针对本用户使用;如果换成 --system ,此配置对整个系统的所有用户均适用;如果去掉此选项,只是针对某个项目有效,
保存在.git目录的配置文件。设置后,我们在查看每次的修改时,都会显示出这次修改掉作者是谁以及他的mail地址。
下面的命令基于home目录下的~/gittest,这个目录下面主要有三个文件:1.txt,2.txt,.gitignore这三个文件组成。想了解具体git命令的使用和说明,我们可以使用git XXX --help命令去显示相应命令的说明文档。
2.建立代码库
使用命令:git init
在当前目录中运行以完成初始化,随后可以使用git status去查看当前代码库的状态。这个时候,我们可以看到,我们在自己工程中的,1.txt和.gitignore这二个文件,提示我们他们是untrack的。 2.txt,文件由于我们在.gitignore中添加了它,所以这个文件将会被git ignore掉,这样方便我们的工作,因为我们在工作中,可能会有很多的*.tmp文件已经*.obj这些临时的我们不关心的文件,所以我们通过.gitignore这个文件去简化我们需要关注的文件 。 .gitignore这个文件里面的文件只要写相对路径就可以,每一行代表一个需要忽视的文件,文件类型或者是目录。
3.管理改动
a.查看当前状态
使用命令:git status
这个命令可以查看当前的状态:
untrack files是指尚未被git所管理的文件;changed but not updated是指文件被git管理,并且发生了改变,但改动还没被git管理;这两种状态,都可以看成是改动还没被git管理的状态,我们这里称非stage状态。
changes to be commited是指进入stage状态的文件,stage是commit和未管理之间的一个状态,也有别名叫index状态,也就是git已经管理了这些改动,但是还没完成提交。
.gitignore中的文件,不会出现在以上三个状态中。
b.向stage添加文件
使用命令:git add
git add [path]会把对应目录或文件,添加到stage状态
git add . 会把当前所有的untrack files和changed but not updated添加到stage状态
c.从stage移除文件
使用命令:git reset
这个命令与add相反,但它还有其他功能,后文介绍。
git reset [path]会改变path指定的文件或目录的stage状态,到非stage状
git reset 会将所有stage的文件状态,都改变成非stage状
d.删除文件
使用命令:git rm [path]
这个命令用来删除文件,可以用git status查看git rm与普通rm的差别。git rm后的文件,都在stage状态。
e.查看差异
使用命令:git diff
git diff 可以查看当前不在stage状态的改动,加参数--cached可以查看stage状态的差异,还可以指定文件名
如git diff --cached 1.txt 可以查看stage状态的1.txt的差异
注意stage状态的diff(即--cached)是stage与HEAD的差异,非stage状态的diff是与stage状态的差异。一个文件可以存在非stage和stage两个状态,可以通过git add和git reset合并。合并的结果都是非stage的状态,因为这个是在stage基础上修改的。
f.回退改动
使用命令:git checkout [path]
git checkout 有很多用处,这里用到的是checkout文件,一些版本管理软件提供有revert命令,可以revert某个文件上的改动。其实这个通过checkout这个文件的最新版本就可以完成。如果做的一些修改不需要了,用git checkout [path]就能把这个文件还原,同时也能从stage和非stage状态中去掉这个文件,就如同没有修改过一样。
g.暂存改动
使用命令:git stash
git stash可以把当前的改动(stage和unstage,但不包括untrack的文件)暂存。然后通过git stash list查看。并通过git stash apply重新取出来。但apply之前要保证worktree是干净的。
4.提交改动
使用命令:git commit
有了前面对stage状态的管理,提交简单了很多。这个命令会提交stage状态的文件。也有参数可以直接提交所有文件或者所有修改过的文件,不过一般建议都经过stage->commit的流程。
使用这个命令后,git会自动调用系统默认的文本编译器,去编辑commit的内容。这里,我的电脑默认的文本编辑器是vim。
或者使用git commit -m "XXXX"直接为这次修改提交注释。推荐使用。
需要强调的是,这里的提交,都是基于本地完成。这点跟以前中央式的版本管理软件有很大差别。而远程提交,会在多人协作里提到。
git commit -amend可以对最后一次提交做些修改,主要是改注释,也能改改已提交的东西。更强大一点的方法下面会介绍。
5.已提交改动管理
a.查看历史记录
使用命令:git log
使用这个命令,我们可以看到所有当前分支的commit。
使用这个命令后,出现的commit XXXXXXX,后面这个编码是每个commit的唯一编码,我们需要查看某一个commit,只要打出这个commit的前四位就可以了。
查看历史记录也可以使用gitk,更为方便。
b.查看版本差异
使用命令:git diff
git diff可以用来查看版本之间的差异,命令格式为git diff [version1]..[version2],如git diff HEAD^..HEAD~2
c.回退已提交改动
使用命令:git reset
git reset的另一作用是回退改动,记法是git reset [version],一般用HEAD表示最新的change log。那么,回退1个change的写法就是git reset HEAD^,2个为HEAD^^,3个为HEAD~3,以此类推。
git reset 有几个选项:
如果什么都不加,比如git reset HEAD^,会把改动回退到unstage状态,改动不会丢失。
如果加--soft,改动会回退到stage状态。
如果加--hard,改动会直接丢失。
d.有记录的回退
使用命令:git revert
git revert的回退方式与reset不一样,它会提交与回退改动相反的改动,来与之抵消。也就是和reset起到同样效果的同时,还留下痕迹。但一般只能用来处理最新一个change的回退。
一般调用方式为git revert HEAD。
6.patch的使用
a.制作patch
使用命令:git diff
前面提到的git diff的输出,其实就是标准的patch形式,使用git diff [param] > xxx.patch即可生成对应的patch。这种方式比较灵活,未提交的改动,提交的改动,分支差别等。都可以用这个方法生成出来。
使用命令:git format-patch
这是更为标准的制作patch方式,一般常用的格式为git format-patch [version],然后会从version开始直到最新的(不包括version),每一commit一个patch的众多patches,以0001开始命名,0001是最早的一个patch。也可以用git format-patch -n,n是最新的n个patch,效果与前面一样。另外还可以git format-patch [old_version]..[new_version]生成选择区间的patch。
b.应用patch
使用命令:git apply [path]
path指向patch的路径。patch如果遇到问题,可以手工合有问题的文件或代码块。剩余的再apply。
7.标签
tag是很有用的一项功能,简单来说,tag就是某个change的一个别名。要发布版本,标记里程碑等,都用tag来做。另外比如像android这样的项目,有多个git库,用同样的tag来统一标记每个库上的某个版本,也为版本的checkout带来了很大的方便。
基本命令:git tag
git tag [tag_name] [version],在对应版本上(一般用change的SHA1),创建tag
git tag -l 列出当前tag
git tag -d [tag_name] 删除tag
有了tag以后,可以使用git checkout [tag_name] -b [branch_name]来检出对应tag时刻的代码。也可以用tag name来实现diff等功能。
二。分支
使用分支的意义,如果你目前的代码需要做一些实验或者是一些不确定的修改,但是你又不想做过测试后,再把代码该回到原始状态或者是影响这个稳定的版本,这个时候我们就要使用branch。同时使用分支,也能方便同时维护很多不同模块的开发进程,保证他们的开发尽量不互相影响。这是很好的习惯。
1.分支的建立:
使用命令:git branch [branch_name]
这个命令会基于当前branch的状态创建另一个branch。git的branch是很轻量级的,新分支的创建就是一个版本的标记而已,无须复制文件。
使用命令:git checkout
git checkout 可以基于分支,tag,或change,创建出对应的分支。这种方式更加灵活。命令格式为git checkout [branch/tag/change] -b [branch_name]。
2.分支的查看:
使用命令:git branch
使用这个命令,可以看到目前存在的所有本地分支,并且可以看到目前工作在那个目录上。
3.分支的切换:
使用命令:git checkout[branch_name]
此命令可以切换分支,注意所有的stage和unstage状态都会被切一起切换,注意先用stash保存。
4.分支的合并:
这个操作是比较容易引起问题的,请尽量保证本地worktree比较干净再来做这一操作。有如下操作方式
a.整个分支合并
使用命令:git merge [branch_name]
使用这个命令来将branch_name指定的branch上的改动,合并到当前所在的branch。合并的轨迹可以在gitk中明显的看到。
合并两个分支,最容易产生的问题就是代码的冲突。git会提示冲突的文件,可以使用git status查看,提示unmerged。
用熟悉的编辑工具打开有冲突的文件,可以看到由<<<<<<<和======中间的,是目前所在branch的修改。========和>>>>>>>之间的,是要合并的修改。选择自己需要的部分,并移除<=>等符号,保存。
随后可以使用git add添加到stage状态,并提交即可。这样的提交因为有merge的动作在前,与普通提交是不同的,从gitk上可以看出有两条线的merge。
另外一点需要注意的是,即使不修改<=>中间的内容,也可以git add到stage,git会认为你已经merge好了。所以请一定记得首先做好所有conflict的处理!
另外一个冲突的解决方法是执行git mergetool,可以使用比如kdiff3等3路merge工具来完成merge,喜欢用图形工具的会方便一些。
b.合并单独的change
使用命令:git cherry-pick [change's SHA1]
git cherry-pick可以手工选择一些改动来集成到当前分支,是最灵活的方式。使用change的SHA1前4位即可。遇到冲突,和merge的解决方法一样。需要注意的是cherry-pick并不带来分支轨迹的变化。
5.分支的删除:
使用命令:git branch -d/-D
-d是删除已经合并过的branch,-D是直接删除
三。多人协作
git是分布式的版本控制工具,每个人都可以有自己的版本库,本地也可以存储完整的版本信息。多人协作的方式方法也有很多。这里主要介绍一些基本的操作,为了规范多人协作的模式,会有一些具体的协作流程。
1.clone版本库
使用命令:git clone [path]
git是分布式,所以如果不是新建库的话,就需要首先在本地将服务器上的版本库下载回来。git clone可以下载path指向的http://和git://协议的地址,也可以是本地文件夹。clone下来的库,包含了完整的版本和分支等信息。
git clone下来以后,使用git branch -r可以查看远程分支。一般是 远程名/分支名 的形式。远程分支一般只在更新时候用,或者作为merge的源,一般不要直接switch过去,更不要去更改。
2.配置个人信息
使用命令:git config
使用git config可以配置选项。个人信息也是在这里完成。加--global选项会对本用户名下所有的git库都采用全局配置。
姓名:git config --global user.name xxx
邮件:git config --global user.email xxx
3.更新代码
a.同步最新代码
使用命令:git fetch
git fetch的作用是更新远程分支的信息为最新,但并不会影响本地分支的状态,一般也不会带来什么问题。由于git是分布式的,所以fetch后,所有的远程库的内容,都拷贝到了本地。后面的集成代码过程中提到远程,其实都是
指本地的远程拷贝而已,并不是远程了。
b.集成最新代码
集成代码的工作,往往跟merge有关,所以也需要保证worktree尽量的干净。以免带来问题或增加复杂度。
1) 配置branch的tracking branch
tracking branch指定从哪里集成代码,后文也会提到这个配置也被用作提交代码的目的地
方法1: 在创建branch的时候指定,利用--track参数
比如git branch --tracking [local_branch_name] [full_remote_branch_name]
方法2: 手工指定或修改
git config branch.[branch_name].remote = origin (一般默认远程库代号都是origin)
git config branch.[branch_name].merge = refs/heads/[branch_name] (指定远程库的引用)
配置好tracking branch后,pull,push都不用加参数了。否则需要为他们加路径参数。
2) git pull
git pull等同于git fetch + git merge,merge的是当前所在branch所指定merge branch。如果出现冲突,用前面介绍过的方法解决。如果手工拆开使用git fetch, 之后再git merge,是需要为merge指定源branch的。
3)git rebase [branch_name]
git rebase的作用,是改变一个分支上的某些change的位置,一般是提升到新的基线版本,新版本由branch_name指定的branch的HEAD确定。比如基于linux2.6.26做了一些驱动,那么,当要把这些驱动的改动,
放到2.6.28版 本上去的时候,就可以用git rebase 2.6.28这样的命令来完成。 如有冲突,同样的方法去解决好后,执行git rebase --continue继续,或git rebase --abort退出,或git rebase --skip跳过当前冲突。
成功的话,就能把当前自己基于老版本的修改,变成基于最新版本的。 执行命令时,git rebase的参数,源branch name不由tracking branch配置决定,需要手工指定。
这里比较一下pull和rebase的差别。因为后面讲到push,也就是提交改动的时候,这些操作的结果会影响到远程库的分支状态。他们两者其实都能把最新的远程代码与本地代码合并。但前者主要的操作是merge,有可能提交
一个merge change log的,也会在历史中留下分支合并的信息(gitk可以看到分叉和合并)。而后者rebase,更大程度上能保证历史是线性的。所以,一般情况下推荐用rebase来做合并,这样也更符合传统版本控制软件的
流程。而merge更适合用在一些需要体现或者强调分支的地方,比如一个较复杂,较长期的功能开发分支,合入到主分支的时候。协调好这两种方式,对保证远程库分支的整洁是有重要意义的。
4.提交代码
使用命令:git push
配置好tracking branch之后,将本地分支的代码,提交到远程库,只要输入git push即可。但运行这个命令前,一定要首先保证已经merge或rebase过,否则是不会成功的。
默认push.default选项,是将本地所有和服务器上匹配的branch的改动,都push上去。一般我们需要设置git config push.default tracking,只推送当前branch到它所tracking的branch。
以上是一些比较精简的git操作流程。以供熟悉基本的git操作。git是大而复杂的系统,同样的事情有很多方法可以完成。每一种技巧,都可能有更好的实现方式,与此同时,本文中可能存在错误的地方。请大家及时指出。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·
git日常开发及基础流程
repo init -u git@xx.xx.xx.xx:android_r5/manifests.git
repo sync
repo forall -c gitcommand
repo forall -c git branch --track multimedia origin/r4_1
repo forall -c git checkout multimedia
git log
git checkout commit_name -b branch名
git stash 暂存
git stash pop 暂存后弹出
一。准备工作
1. 权限
如果使用linux,那么就在shell中;
ssh-keygen -r rsa
这会在用户的.ssh目录下生成默认的密钥对,把公钥id_rsa.pub改成你在shell中的全名,一般是:
username@hostname.pub
的形式,发给git库维护者。
2. 复制远程库
git clone git@服务器IP:项目.git
例:
git clone git@xx.xx.xx.xx:test.git
3. 配置git
用户信息
git config --global user.name yourmail
git config --global user.email yourmail
4. 选定基础分支
git branch -r查看当前远程分支
git branch查看本地分支
一般新clone的库本地只有master分支,这个分支我们不使用。首先需要根据所以开发的基础远程分支,创建一本地分支。
a. 查看所要开发分支的远程分支
b. 根据远程分支创建本地分支,tracking远程分支
例:
a. git branch -r 查看当前远程分支
origin/HEAD -> origin/master
origin/linux
origin/master
origin/windows
b. 选择你需要在其基础上开发的远程分支,并创建本地分支,假设这里是origin/linux
执行: git branch --tracking linux origin/linux
linux是本地分支名,origin/linux是远程分支名,本地名字一般跟远程名后面一样
ab两步,建立了origin/linux-->linux的分支关系。
二。开发
方式A:
5. 创建个人开发临时分支
因为需要用到git的rebase特性,所以一般都开一临时分支来做实际的开发工作。主要为了做一备份,以便rebase出现问题时方便恢复。
临时分支基于前面创建的本地分支,tracking此本地分支。
例:
git branch --tracking linux_working_driver linux
这样就建立好了origin/linux-->linux-->linux_working_driver的分支关系。
6. 临时分支上修改及提交
本地修改和提交,都在临时分支上完成。之后将其push回tracking的本地分支。
a. checkout临时分支
b. 本地的所有修改,以及提交,都在临时分支完成
例:
a. git checkout linux_working_audiodriver
b. change sth & git add . & git commit等
7. push临时分支到本地分支
push临时分支的改动到本地分支(临时分支的tracking分支). 因为本地分支上没有任何修改,这里的push是直接能完成的(fast-forward)。
例:
git push . linux_working_driver:linux
.表示本地库,后面的格式是 临时分支:本地分支
push前执行git branch确认当前确实在临时分支
方式B:
直接在本地分支上执行步骤6,也就是所有的改动和提交,都在本地分支上直接完成。
注意:
无论采用哪种方式,至此,一次开发任务完成。方式B完成开发后,也可以用步骤5的方法做一临时分支备份。
三。更新及合并
8. 更新远程代码
执行git fetch,远程代码会被更新至远程分支。
9. 合并远程代码
远程代码的合并,暂时都采用rebase所tracking的远程分支的方式。并根据实际情况,解决好冲突。
例:
git rebase origin/linux(执行前请git branch确认当前确实在本地分支)
10. 清除临时分支
如果合并远程代码到本地分支成功,临时分支作为备份就失去作用了。用以下命令清除。
git branch -D [branch_name]
注意-D是强制删除,因为本地分支rebase以后,和临时分支之间失去了fast-forward特性,所以必须-D强删。开始新的开发任务的话,新建临时分支是更容易的事情。
例:
git branch -D linux_working_audiodriver
注意:
如果步骤9没有合并好,可以git reset本地分支到步骤7之前的状态。重新执行步骤7。
四。提交改动
这里的提交是从本地向远程库提交。
11. 提交
本地分支合并等处理好了的话,提交是非常简单的。直接在本地分支上push对应分支即可。如果被远程库reject,多半是远程库又有了更新。需要重新执行步骤三。
例:
git push origin linux:refs/heads/linux
origin是远程库,后面的格式是 本地分支:远程服务器分支引用, push前执行git branch确认当前确实在本地分支.
千万注意这里用远程服务器分支引用,而不是origin/linux这样的远程分支名,否则会提交到错误的分支上去。