Tutorials of Git

作者:廖雪峰

史上最浅显易懂的Git教程!

为什么要编写这个教程?因为我在学习Git的过程中,买过书,也在网上Google了一堆Git相关的文章和教程,但令人失望的是,这些教程不是难得令人发指,就是简单得一笔带过,或者,只支离破碎地介绍Git的某几个命令,还有直接从Git手册粘贴帮助文档的,总之,初学者很难找到一个由浅入深,学完后能立刻上手的Git教程。

既然号称史上最浅显易懂的Git教程,那这个教程有什么让你怦然心动的特点呢?

首先,本教程绝对面向初学者,没有接触过版本控制概念的读者也可以轻松入门,不必担心起步难度;

其次,本教程实用性超强,边学边练,一点也不觉得枯燥。而且,你所学的Git命令是“充分且必要”的,掌握了这些东西,你就可以通过Git轻松地完成你的工作。

文字+图片还看不明白?有视频!!!

本教程只会让你成为Git用户,不会让你成为Git专家。很多Git命令只有那些专家才明白(事实上我也不明白,因为我不是Git专家),但我保证这些命令可能你一辈子都不会用到。既然Git是一个工具,就没必要把时间浪费在那些“高级”但几乎永远不会用到的命令上。一旦你真的非用不可了,到时候再自行Google或者请教专家也未迟。

如果你是一个开发人员,想用上这个世界上目前最先进的分布式版本控制系统,那么,赶快开始学习吧!

关于作者

廖雪峰,十年软件开发经验,业余产品经理,精通Java/Python/Ruby/Visual Basic/Objective C等,对开源框架有深入研究,著有《Spring 2.0核心技术与最佳实践》一书,多个业余开源项目托管在GitHub,欢迎微博交流:

读书笔记

集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。

分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

通过git init命令把这个目录变成Git可以管理的仓库:

$ git init

当前目录下多了一个.git的隐藏目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。

所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。

使用Windows的童鞋要特别注意:

千万不要使用Windows自带的记事本编辑任何文本文件。原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符,你会遇到很多不可思议的问题,比如,网页第一行可能会显示一个“?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。建议你下载Notepad++代替记事本,不但功能强大,而且免费!记得把Notepad++的默认编码设置为UTF-8 without BOM即可:

用命令git add告诉Git,把文件添加到仓库:

$ git add readme.txt

执行上面的命令,没有任何显示,这就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。

用命令git commit告诉Git,把文件提交到仓库:

$ git commit -m “wrote a readme file”

为什么Git添加文件需要add,commit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:

$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m “add 3 files.”

git status命令可以让我们时刻掌握仓库当前的状态
git diff顾名思义就是查看difference

$ git diff readme.txt

每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作

版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log命令查看:如果嫌输出信息太多,看得眼花缭乱的,可以试试加上–pretty=oneline参数

$ git log –pretty=oneline

为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。

在Git中,用HEAD表示当前版本
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL:

改为指向add distributed:

然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。
Git提供了一个命令git reflog用来记录你的每一次命令:

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。

我们把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。


git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。

Git会告诉你,git checkout – file可以丢弃工作区的修改
就是让这个文件回到最近一次git commit或git add时的状态。
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

$ git checkout – readme.txt

git checkout – file命令中的–很重要,没有–,就变成了“切换到另一个分支”的命令,git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

用命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区:

$ git reset HEAD readme.txt

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit:

$ git rm test.txt
$ git commit -m “remove test.txt”

在本地的learngit仓库下运行命令:

$ git remote add origin git@github.com:michaelliao/learngit.git

请千万注意,把上面的michaelliao替换成你自己的GitHub账户名,否则,你在本地关联的就是我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的SSH Key公钥不在我的账户列表中。

添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。

下一步,就可以把本地库的所有内容推送到远程库上:

$ git push -u origin master

把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

从现在起,只要本地作了提交,就可以通过命令:

$ git push origin master

登陆GitHub,创建一个新的仓库,名字叫gitskills:我们勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个README.md文件。创建完毕后,可以看到README.md文件:现在,远程库已经准备好了,下一步是用命令git clone克隆一个本地库:

$ git clone git@github.com:michaelliao/gitskills.git

GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。

使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。

每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长:

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!

不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

所以Git合并分支也很快!就改改指针,工作区内容也不变!

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

创建dev分支,然后切换到dev分支:

$ git checkout -b dev

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

$ git branch dev
$ git checkout dev

用git branch命令查看当前分支:git branch命令会列出所有分支,当前分支前面会标一个*号。

$ git branch

dev分支的工作完成,我们就可以切换回master分支:

$ git checkout master

切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:

我们把dev分支的工作成果合并到master分支上:git merge命令用于合并指定分支到当前分支。

$ git merge dev

合并完成后,就可以放心地删除dev分支了:

$ git branch -d dev

因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

master分支和feature1分支各自都分别有新的提交,变成了这样:

Git告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容

修改后保存,再提交:

$ git add readme.txt
$ git commit -m “conflict fixed”
[master 59bc1cb] conflict fixed
现在,master分支和feature1分支变成了下图所示:

用带参数的git log也可以看到分支的合并情况:
$ git log –graph –pretty=oneline –abbrev-commit

合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

准备合并dev分支,请注意–no-ff参数,表示禁用Fast forward:

$ git merge –no-ff -m “merge with no-ff” dev

不使用Fast forward模式,merge后就像这样:

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

所以,团队合作的分支看起来就像这样:

Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

$ git stash

刚才的工作现场存到哪去了?用git stash list命令看看:

$ git stash list

一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
另一种方式是用git stash pop,恢复的同时把stash内容也删了:

$ git stash pop

你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:

$ git stash apply stash@{0}

软件开发中,总有无穷无尽的新的功能要不断添加进来。

添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。

当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。

要查看远程库的信息,用git remote:或者,用git remote -v显示更详细的信息:

$ git remote
$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)

上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

$ git push origin master
如果要推送其他分支,比如dev,就改成:

$ git push origin dev

master分支是主分支,因此要时刻与远程同步;
dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:

$ git checkout -b dev origin/dev
现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:

你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:

$ git branch –set-upstream dev origin/dev
Branch dev set up to track remote branch dev from origin.

再pull:$ git pull

多人协作的工作模式通常是这样:

首先,可以试图用git push origin branch-name推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!
如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch –set-upstream branch-name origin/branch-name。

tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。

在Git中打标签非常简单,首先,切换到需要打标签的分支上:

$ git branch

然后,敲命令git tag 就可以打一个新标签:

$ git tag v1.0
可以用命令git tag查看所有标签:标签不是按时间顺序列出,而是按字母排序的。可以用git show 查看标签信息, 还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:还可以通过-s用私钥签名一个标签:签名采用PGP签名,因此,必须首先安装gpg(GnuPG),如果没有找到gpg,或者没有gpg密钥对,就会报错, 如果报错,请参考GnuPG帮助文档配置Key。用命令git show 可以看到PGP签名信息,用PGP签名的标签是不可伪造的,因为可以验证PGP签名。

$ git tag -a v0.1 -m “version 0.1 released” 3628164

$ git tag

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?

方法是找到历史提交的commit id,然后打上就可以了:

$ git log –pretty=oneline –abbrev-commit

比方说要对add merge这次提交打标签,它对应的commit id是6224937,敲入命令:

$ git tag v0.9 6224937

如果标签打错了,也可以删除:

$ git tag -d v0.1

如果要推送某个标签到远程,使用命令git push origin

$ git push origin v1.0

一次性推送全部尚未推送到远程的本地标签:

$ git push origin –tags

如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:

$ git tag -d v0.9

然后,从远程删除。删除命令也是push,但是格式如下:

$ git push origin :refs/tags/v0.9

如何参与一个开源项目呢?比如人气极高的bootstrap项目,这是一个非常强大的CSS框架,你可以访问它的项目主页https://github.com/twbs/bootstrap,点“Fork”就在自己的账号下克隆了一个bootstrap仓库,然后,从自己的账号下clone:

git clone git@github.com:michaelliao/bootstrap.git
一定要从自己的账号下clone仓库,这样你才能推送修改。如果从bootstrap的作者的仓库地址git@github.com:twbs/bootstrap.git克隆,因为没有权限,你将不能推送修改。

Bootstrap的官方仓库twbs/bootstrap、你在GitHub上克隆的仓库my/bootstrap,以及你自己克隆到本地电脑的仓库,他们的关系就像下图显示的那样:

如果你想修复bootstrap的一个bug,或者新增一个功能,立刻就可以开始干活,干完后,往自己的仓库推送。

如果你希望bootstrap的官方库能接受你的修改,你就可以在GitHub上发起一个pull request。当然,对方是否接受你的pull request就不一定了。

在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。

忽略文件的原则是:

忽略操作系统自动生成的文件,比如缩略图等;
忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

检验.gitignore的标准是git status命令是不是说working directory clean。
如果你在资源管理器里新建一个.gitignore文件,它会非常弱智地提示你必须输入文件名,但是在文本编辑器里“保存”或者“另存为”就可以把文件保存为.gitignore了。

你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了:

$ git add App.class
The following paths are ignored by one of your .gitignore files:
App.class
Use -f if you really want to add them.
如果你确实想添加该文件,可以用-f强制添加到Git:

$ git add -f App.class
或者你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore命令检查:

$ git check-ignore -v App.class
.gitignore:3:.class App.class

Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。

给Git配置好别名,就可以输入命令时偷个懒。我们鼓励偷懒。

小结

初始化一个Git仓库,使用git init命令。
添加文件到Git仓库,分两步:
第一步,使用命令git add ,注意,可反复多次使用,添加多个文件;
第二步,使用命令git commit,完成。

要随时掌握工作区的状态,使用git status命令。
如果git status告诉你有文件被修改过,用git diff可以查看修改内容。

HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset –hard commit_id。
穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。

每次修改,如果不add到暂存区,那就不会加入到commit中。

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout – file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。

要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git;

关联后,使用命令git push -u origin master第一次推送master分支的所有内容;

此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;

分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!

要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。

Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。

Git鼓励大量使用分支:

  • 查看分支:git branch
  • 创建分支:git branch
  • 切换分支:git checkout
  • 创建+切换分支:git checkout -b
  • 合并某分支到当前分支:git merge
  • 删除分支:git branch -d

当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
用git log –graph命令可以看到分支合并图。

Git分支十分强大,在团队开发中应该充分应用。

合并分支时,加上–no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。

开发一个新feature,最好新建一个分支;

如果要丢弃一个没有被合并过的分支,可以通过git branch -D 强行删除。

查看远程库信息,使用git remote -v;

本地新建的分支如果不推送到远程,对其他人就是不可见的;

从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;

在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;

建立本地分支和远程分支的关联,使用git branch –set-upstream branch-name origin/branch-name;

从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

命令git tag 用于新建一个标签,默认为HEAD,也可以指定一个commit id;

git tag -a -m “blablabla…”可以指定标签信息;

git tag -s -m “blablabla…”可以用PGP签名标签;

命令git tag可以查看所有标签。

命令git push origin 可以推送一个本地标签;
命令git push origin –tags可以推送全部未推送过的本地标签;
命令git tag -d 可以删除一个本地标签;
命令git push origin :refs/tags/可以删除一个远程标签。

在GitHub上,可以任意Fork开源仓库;
自己拥有Fork后的仓库的读写权限;
可以推送pull request给官方仓库来贡献代码。

忽略某些文件时,需要编写.gitignore;
.gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理!

配置别名

如果敲git st就表示git status那就简单多了,当然这种偷懒的办法我们是极力赞成的。我们只需要敲一行命令,告诉Git,以后st就表示status:

$ git config –global alias.st status

当然还有别的命令可以简写,很多人都用co表示checkout,ci表示commit,br表示branch:

$ git config –global alias.co checkout
$ git config –global alias.ci commit
$ git config –global alias.br branch

–global参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用。命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区。既然是一个unstage操作,就可以配置一个unstage别名:

$ git config –global alias.unstage ‘reset HEAD’

配置一个git last,让其显示最后一次提交信息:

$ git config –global alias.last ‘log -1’
这样,用git last就能显示最近一次的提交:

$ git last

甚至还有人丧心病狂地把lg配置成了:

git config –global alias.lg “log –color –graph –pretty=format:’%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset’ –abbrev-commit”

配置Git的时候,加上–global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。

配置文件放哪了?每个仓库的Git配置文件都放在.git/config文件中:
而当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:

搭建Git服务器非常简单,通常10分钟即可完成;
要方便管理公钥,用Gitosis;
要像SVN那样变态地控制权限,用Gitolite。

Git虽然极其强大,命令繁多,但常用的就那么十来个,掌握好这十几个常用命令,你已经可以得心应手地使用Git了。

友情附赠国外网友制作的Git Cheat Sheet,建议打印出来备用:

Git Cheat Sheet

Practice in Action

确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit

4.3日
Require: Remove private file on Git Server
Condition:

  1. After Delete the server private file manually
  2. Local private file was modified

Ops:
Backup local private file and add it to the gitignore configuration

  1. Git pull to local
  2. git rm local private file
  3. git commit rm op
  4. git merge
  5. git push
  6. restore bak private file to original path
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: gradle.properties
no changes added to commit (use "git add" and/or "git commit -a")
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git add .
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git commit -m "git ignore file test"
[master 253b740] git ignore file test
1 file changed, 1 insertion(+)
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git push
To git.coding.net:ipcreator/ui.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'git@git.coding.net:ipcreator/ui.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git pull
remote: Counting objects: 2, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 2 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
From git.coding.net:ipcreator/ui
bf74696..d2a6223 master -> origin/master
CONFLICT (modify/delete): gradle.properties deleted in d2a62231247dc9ed112e155cb60b7395790a42a1 and modified in HEAD.
Version HEAD of gradle.properties left in tree.Automatic merge failed; fix conflicts and then commit the result.
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master|MERGING)
$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add/rm <file>..." as appropriate to mark resolution)
deleted by them: gradle.properties
no changes added to commit (use "git add" and/or "git commit -a")
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master|MERGING)
$ git rm gradle.properties
gradle.properties: needs merge
rm 'gradle.properties'
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master|MERGING)
$ git merge
fatal: You have not concluded your merge (MERGE_HEAD exists).
Please, commit your changes before you merge.
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master|MERGING)
$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
deleted: gradle.properties
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master|MERGING)
$ git commit -m "merge delete op from server"
[master eb01575] merge delete op from server
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git push
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 506 bytes | 0 bytes/s, done.
Total 4 (delta 2), reused 0 (delta 0)
To git.coding.net:ipcreator/ui.git
d2a6223..eb01575 master -> master
user@LAPTOP-082RQVIH MINGW64 /d/AndroidSDK/Dev/ui (master)
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

$ git init
Reinitialized existing Git repository in D:/IPCreatorBlog/themes/yilia/.git/

$ git remote add yilia git@github.com:ipcreator/hexo-theme-yilia.git

$ git push -u yilia master
Branch master set up to track remote branch master from yilia.
Everything up-to-date

$ git status
On branch master
Your branch is up-to-date with ‘yilia/master’.
Changes not staged for commit:
(use “git add …” to update what will be committed)
(use “git checkout – …” to discard changes in working directory)

modified:   _config.yml
modified:   layout/_partial/after-footer.ejs
modified:   layout/_partial/footer.ejs
modified:   layout/_partial/left-col.ejs
modified:   source-src/css/article-main.scss
modified:   source-src/css/fonts/iconfont.svg
modified:   source-src/js/mobile.js
modified:   source/fonts/iconfont.dba24b.svg

Untracked files:
(use “git add …” to include in what will be committed)

source/main.0d8e3c.css
source/main.0d8e3c.js
source/mobile.0b1d99.js
source/slider.fa3fc2.js

no changes added to commit (use “git add” and/or “git commit -a”)

$ git branch

  • master

$ git add _config.yml

$ git status
On branch master
Your branch is up-to-date with ‘yilia/master’.
Changes to be committed:
(use “git reset HEAD …” to unstage)

modified:   _config.yml

Changes not staged for commit:
(use “git add …” to update what will be committed)
(use “git checkout – …” to discard changes in working directory)

modified:   layout/_partial/after-footer.ejs
modified:   layout/_partial/footer.ejs
modified:   layout/_partial/left-col.ejs
modified:   source-src/css/article-main.scss
modified:   source-src/css/fonts/iconfont.svg
modified:   source-src/js/mobile.js
modified:   source/fonts/iconfont.dba24b.svg

Untracked files:
(use “git add …” to include in what will be committed)

source/main.0d8e3c.css
source/main.0d8e3c.js
source/mobile.0b1d99.js
source/slider.fa3fc2.js

$ git add layout/_partial/after-footer.ejs
$ git add layout/_partial/footer.ejs
$ git add layout/_partial/left-col.ejs
$ git add source-src/css/article-main.scss
$ git add source-src/css/fonts/iconfont.svg
warning: LF will be replaced by CRLF in source-src/css/fonts/iconfont.svg.
The file will have its original line endings in your working directory.

$ git add source-src/js/mobile.js
$ git add source/fonts/iconfont.dba24b.svg
warning: LF will be replaced by CRLF in source/fonts/iconfont.dba24b.svg.
The file will have its original line endings in your working directory.

$ git add source/main.0d8e3c.css
$ git add source/main.0d8e3c.js
warning: LF will be replaced by CRLF in source/main.0d8e3c.js.
The file will have its original line endings in your working directory.

$ git add source/mobile.0b1d99.js
$ git add source/slider.fa3fc2.js
warning: LF will be replaced by CRLF in source/slider.fa3fc2.js.
The file will have its original line endings in your working directory.

$ git status
On branch master
Your branch is up-to-date with ‘yilia/master’.
Changes to be committed:
(use “git reset HEAD …” to unstage)

modified:   _config.yml
modified:   layout/_partial/after-footer.ejs
modified:   layout/_partial/footer.ejs
modified:   layout/_partial/left-col.ejs
modified:   source-src/css/article-main.scss
modified:   source-src/js/mobile.js
new file:   source/main.0d8e3c.css
new file:   source/main.0d8e3c.js
new file:   source/mobile.0b1d99.js
new file:   source/slider.fa3fc2.js

$ git commit -m “first bak”
[master 8251377] first bak
10 files changed, 111 insertions(+), 28 deletions(-)
create mode 100644 source/main.0d8e3c.css
create mode 100644 source/main.0d8e3c.js
create mode 100644 source/mobile.0b1d99.js
create mode 100644 source/slider.fa3fc2.js

$ git push -u yilia master
Counting objects: 18, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (18/18), done.
Writing objects: 100% (18/18), 63.54 KiB | 0 bytes/s, done.
Total 18 (delta 10), reused 3 (delta 0)
remote: Resolving deltas: 100% (10/10), completed with 10 local objects.
Branch master set up to track remote branch master from yilia.
To github.com:ipcreator/hexo-theme-yilia.git
8ff6b52..8251377 master -> master

$ git status
On branch master
Your branch is up-to-date with ‘yilia/master’.
Changes not staged for commit:
(use “git add …” to update what will be committed)
(use “git checkout – …” to discard changes in working directory)

modified:   _config.yml

no changes added to commit (use “git add” and/or “git commit -a”)

$ git diff HEAD – _config.yml
diff –git a/_config.yml b/_config.yml
index 4f8698a..e00540b 100644
— a/_config.yml
+++ b/_config.yml
@@ -79,11 +79,11 @@ duoshuo-key: ipcreator
style:

  • #header: ‘#4d4d4d’
  • header: ‘#47A2E4’
  • header: ‘#4d4d4d’
  • #header: ‘#47A2E4’
  • #slider: ‘linear-gradient(200deg,#a0cfe4,#e8c37e)’
  • slider: ‘linear-gradient(200deg,#47A2E4,#47A2E4)’
  • slider: ‘linear-gradient(200deg,#a0cfe4,#e8c37e)’
  • #slider: ‘linear-gradient(200deg,#47A2E4,#47A2E4)’

$ git add _config.yml

$ git commit -m “restore color”
[master 1acf4a2] restore color
1 file changed, 4 insertions(+), 4 deletions(-)

$ git push -u yilia master
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 329 bytes | 0 bytes/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
Branch master set up to track remote branch master from yilia.
To github.com:ipcreator/hexo-theme-yilia.git
2547415..1acf4a2 master -> master

Coding.net实践

一、先在coding.net新建一个仓库
二、使用gitclone方法新建一个本地库
三、将要上传的文件拷贝到本地库
四、先在本地add和commit
五、提交到远程库

1
2
3
4
5
$ git clone
$ git status
$ git add .
$ git commit -m "First commit"
$ git push