前言说明
1.git的工具有多种,官方的 windows版本是 msysgit,现在叫 git for windows了。其它的也不太好用,如 tortoisegit就没必要使用了。github的
git for windows 主要包括 git bash和git gui
2.git提供代码的话,需要确认身份,如 github,每次提交代码需要用户名、密码,也可以使用ssh,这样就不用每次输入了。
使用 ssh(和linux上使用没什么区别):(1)产生密钥:ssh-keygen -t rsa -t:加密类型 -b 密钥长度 -C:说明,一般是email
(2)将密钥上传到 github就可以了
(在github教程中提到的 ssh-agent和ssh-add,是多个程序共用 ssh key时才有可能用到,一般没必要用)
github准备
产生key 时,用 github注册邮箱来说明这个key
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
上传 ssh public key 到 github:
$ clip < ~/.ssh/id_rsa.pub
设置姓名和email
git config --global user.name "YOUR NAME"
git config --global user.email "YOUR EMAIL ADDRESS"
测试
ssh -T git@github.com
结果显示如下表示成功:Hi username! You've successfully authenticated, but GitHub does not provide shell access.
说明:
1.github对不同的连接方式的url不一样,分 https, ssh, subversion 选择使用ssh,在项目右侧中有对该项目的 url
github操作
创建库(repository)
方式一:在创建时不创建 README 文件,这样其实库并没有真正的创建,需要在本地建一个库,然后上传到 github。操作如下:(在创建完库后就会提示的)
echo "# simple" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:colorlifeli/simple.git
git push -u origin master
方式二:勾选创建 README,则会初始化库,然后 clone 到本地:
git clone git@github.com:colorlifeli/web.git
http://www.git-scm.com/book/en/v2/Getting-Started-About-Version-Control
git知识
基本概念
git是分布式的,也即每个点的版本信息是一样的。git serve和git 客户端仅从版本内容来看是一样的,没有git2区别。
git server的作用是有一个统一的地方来收集所有点的版本信息、合并等。
git server需要起一个 ssh 服务器端,另外一般有一个 web 服务器以方便大家通过网页查看。
所以git的操作大部分都是 local 的,提交代码、查看版本信息、比较等都是在本地进行。与server的交互在于获取完整的其他点的版本信息以及将自己的版本信息放上去。
git的原理:git保存的不是增量的变化,每一个版本都是所有文件的一个列表,对于没有更改的文件,则指向原文件,而不会拷贝。
git对每个文件进行 hash,使用 SHA-1 算法,得到40位的code,将此作为文件的标识,有利于防止文件被非法修改或不经 git 系统而进入库
git 只增加数据到库,极少删除文件,所以一旦commit,文件就不会丢失。
git库的文件有3个状态:
modified, stage, committed 分别对应库的3个区间:
working diretory: 相当于某个 version 的一份 checkout
stage area: 仅是一个文件列表,表示下一次commit将会包含哪些文件。(因为有些文件你修改了,但可能想在下下个版本才提交)
git repository(local database):保存了所有的元数据和版本
git配置
有3个级别:
1. 系统级:/etc/gitconfig
2.用户级:~/.gitconfig or ~/.config/git/config
3.项目级:当前项目的 .git/config
相同配置后一级别优先,如项目级是最优先的。
第一个要配置的是姓名和email:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
也可以为项目单独配置,去掉 --global即可
查看所有配置:$ git config --list
查看单个配置:$ git config user.name
查看帮助:
git help
git help <command>
git <command> --help
man git-<command> //这个windows上不能用
git repository 操作:
1.对某个目录、项目生成 git 库:
进入该目录,执行: git init
git add <file or directory>,将文件或目录下所有文件(包括子目录)加入到库
git commit
2.克隆某个已存在的库:
git clone <url> [diretory-name]:将会在本地生成这个项目名称的目录,包含库内容。如果指定了目录名称,则会在此目录下生成库
文件状态
在工作目录下,如果新增了文件,则这个文件初始时并不属于git管理的,需要使用 git add命令来加入git管理(实际上是加入stage area,对于 modified的文件,也可以用 git add)。
这个状态称为 untracked。也就是当一个文件不属于最后一个版本时,它的状态是 untracked。文件状态变化如下图:
查看文件状态:$ git status 简单显示:$ git status -s
由 stage 变为 unstage(modified): git reset HEAD <file>
由unstage(modified) 变为unmodified: git checkout -- <file> (相当于不要本次修改了)
.gitignore
# no .a files
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
/TODO
# ignore all files in the build/ directory
build/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf files in the doc/ directory
doc/**/*.pdf
查看修改内容
git diff:只显示所有unstage的修改。如果一个文件stage之后,再进行了修改,则只显示新修改的内容。(可见stage了的话,会产生一个新文件)
git diff --staged/--cached:显示所有stage的修改,即准备提交的修改。
提交
$ git commit -m "<comment>"
如果想git 自动 stage 所有修改的文件,则:
$ git commit -a -m '<comment>'
删除和移动文件
使用 git rm 和 git mv 命令
git rm <file or direcotry or 正则表达式>:会将文件变为 untracked 状态,并从磁盘上删除
如果在文件系统删除了文件,则会显示此文件的状态为 unstaged,需要执行 git rm 才会 commit
如果仅是从git管理删除而不从磁盘删除,则:
$ git rm --cached README
git mv 命令相当于3个命令的组合: mv、git rm、git add
如果在磁盘上移动了文件,在提交上需补回 git rm,git add
在同一目录下 mv 相当于重命名。
查看提交日志
$ git log
最新的提交显示在前面。
$ git log -p -2 :对最后2个提交显示差异
$ git log --stat :每个提交多了一句总结,包括修改了多少文件、删除插入多少
$ git log --pretty :改变显示的格式,如:git log --pretty=format:"%h - %an, %ar : %s"
$ git log --since=2.weeks :最后2个星期
$ git log -S<string> : 出现内容为string的提交
远程协作
这是针对某个项目的。
增加远程协作点,即增加 server:
git remote add [shortname] [url]
查看:$ git remote -v
git clone 默认增加了一个 remote,名称是 origin,默认在本地建一个branch,叫 master
$ git fetch [remote-name] 从远端获得所有本地没有的数据,只是获取而不会 merg。一般不使用
$ git pull 如果本地有一个 branch与远程对应,则会获取数据并尝试merge
git push [remote-name] [branch-name]: 将本地 branch的修改上传到远端,如果远端已经有新的修改,则需要先pull down,合并后才能上传
git remote show [remote-name] :查看远端信息
git tag
打标签,暂不使用。
aliase
为命令起别名,暂不理会。
branch 分支
git在commit时,主要做的事有:
1.生成每个新增或修改的文件,存到库里。
2.生成一个树文件,包括对每个子目录进行 checksum 计算,生成一棵目录树的信息,还包括指向每个修改文件的指针
3.生成 commit metadata对象文件,包括指向树文件的指针,commit的用户、email、comment,还有一个指向上一次commit的指针(可以看作是父指针)
branch分支实际上就是一个指向某个 commit metadata的指针。
由于 commit metadata有父指针,因此合并分支时,git可以知道进行分支的那个commit在哪里,从而基于那个commit进行合并。
HEAD既指向当前分支,也指向最新提交。回退提交的状态用的就是HEAD
当前分支: HEAD 是当前分支的一个指针,通过
git log --oneline --decorate
可以看到head指向哪个分支,也就是当前分支了。或者直接 git branch 命令,带星号的就是当前分支
创建分支:
$ git branch <branch-name>
创建branch,将创建一个指向当前最新 commit的指针。(实际上是一个 sha-1的文件)
切换branch
$ git checkout <branch-name>
切换分支会改变当前 working copy的文件至目标分支的最后一个 commit的状态(也就是会撤消更改)。当前的文件会删除。因此在切换分支前,git会要求将当前的改动都commit了才能切换,否则改动就会没了。
$ git checkout -b <branch-name> 创建并切换分支,即上面2条命令的简化
查看分支情况:
$ git log --oneline --decorate --graph --all
merge
首先,切换回 master,再执行要merge哪个分支:
$ git checkout master
$ git merge <branch-name>
因此merge的意思是把别人合并到自己,即合并他人,而不是合并到他人
如果合并前master没有另外的更新,则只是简单移动指针。
如果合并前master已经有了另外的更新,则合并产生的 commit 有2个父 commit
一般合回master之后,就将 branch删掉:
$ git branch -d <branch-name>
merge 冲突
如果 git 不能 auto merge,则它会对不能 merge的地方进行标记,
1. 通过 git status 可以查到哪些文件不能 merge
2. 打开文件,查询标记,上面的是 master的,下面的是分支,手动合并。标记类似“<<<<<<<HEAD:index.html”
3. git add 文件即可。
也可以使用工具:
$ git mergetool
分支管理总结:
可以看到,git主要是小分支的理念,也就是分支一般会比较小,不断地创建分支、删除分支,所以 git的设计是创建分支的开销足够小。
git 的小分支称为 topic branch。
topic branch的优点:
1. 将不同关注点的修改作为一个 branch,因为关注点不同,合并时不会产生太多的手动合并。
2. 各自管理生命周期,在需要的时候才与 master合并
如果分支比较大,另一种做法是:对不同的分支在开发,将 master下到不同的目录,再建分支,开发完再合回master,这样就不会相互影响。这相当于一个人模拟了多个人在开发的情况,问题变成了多个克隆之间如何合并了,也就是和远程库合并的问题。
对于一个大项目来说,既要有长期、稳定分支,也要有用于开发、修bug的分支,如下已比较能满足大部分的需求:
远程分支概念
远程交互的例子:
1. 对远程 server起一个名字:
git remote add <name> <url>
如:git remote add origin git@github.com:colorlifeli/simple.git
2. 获取远程分支:
$ git fetch origin
3 将在本地建立代表远程的所有分支,名字是 <romote-name>/<branch>,如 origin/master
这个分支本地不可对其commit的,只是代表server的信息
4. 在本地创建一个分支与其关联:
$ git checkout -b master origin/master
本地这个分支称为 tracking branch,也称为 upstream。
5. 这时,本地有了2个分支,需要时,就获取 server 最新的信息,然后合并到 tracking branch,然后作为 upstream 合并到 server 相应的分支
6. 如果有多个server,分支情况也是类似的,如上图,多了一个 teamone/master 远程分支,它其实是 origin/master的一个子分支,则会在 origin/master上进行分支标记。
git clone 自动完成前4步,并将远程名字定为 origin,分支名字和远程一样(实际可自定)
git回退本地版本
git reset --mixed:此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commit和index信息
git reset --soft:回退到某个版本,只回退了commit的信息,不会恢复到index file一级。如果还要提交,直接commit即可
git reset --hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容
reset回退的方式是删除提交的历史,如果已push到远程仓库的话,这样可能会引起问题。(如果没有push,则关系不大)
这时最好用 revert ,revert 操作是根据已做的提交自动生成一个新的提交来进行覆盖,达到恢复的目的。