创建版本库
初始化一个Git仓库,使用git init
命令;
添加文件到Git仓库,分两步:
- 使用使用命令
git add <file>
,注意,可反复多次使用,添加多个文件; - 第二步,使用命令
git commit
,完成。
example:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
时光机穿梭
- 查看当前仓库下工作区状态,使用
git status
命令。 - 如果
git status
告诉你有文件被修改过,用git diff <file>
可以查看修改内容。
版本回退
HEAD
指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id
。回退到上一个版本可以使用git reset --hard HEAD^
,上上一个就是HEAD^^
,再多了就不方便,通常使用commit_id
来指定回退到哪个版本。- 穿梭前,用
git log
可以查看提交历史,以便确定要回退到哪个版本。 - 要重返未来,用
git reflog
查看命令历史,以便确定要回到未来的哪个版本
工作区和暂存区
工作区就是我们本地的文件,使用git add
命令,每次将工作区里的修改添加到暂存区。每次git的机制是记录每次的修改,而非每个版本的内容,大大减少了存储量。然后git commit
命令每次将暂存区里所有内容提交到当前分支里。
管理修改
如果不add
到暂存区,没法commit
到branch
里去。
注意:
- 第一次修改->
git add
->第二次修改->git commit
-> branch里保存的是第一次修改内容 - 第一次修改->
git add
->第二次修改->`git add
->git commit
-> branch里保存的是第二次修改内容;
上面的对比,形象的表达出,每做一次修改,都需要git add
一下,也就是需要加到暂存区里,否则无法commit到分支里。
撤销修改
- 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
git checkout -- <file>
。 - 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令
git reset HEAD <file>
,就回到了场景1,第二步按场景1操作。 - 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
example:
$ git checkout -- readme.txt #场景1
$ git reset HEAD readme.txt #场景2
$ git checkout -- readme.txt
删除文件
使用命令 git rm <file>
,然后还要git commit
一下,告诉分支这文件被删除了。
远程仓库
使用命令$ ssh-keygen -t rsa -C "[email protected]"
生成自己的私钥id_rsa
和公钥id_rsa.pub
,将公钥里的内容拷贝到Github里“Account settings”,“SSH Keys”页面,即可。
Github可以提交需要SSH key,这样你可以在家里提交,也可以在公司提交,每一个终端对应一个公钥(也就是SSH key)。
添加远程库
在Github上新建一个repo,命名为learngit,在本地的learngit仓库下运行
$ git remote add origin [email protected]:Ocxs/learngit.git
$ git remote add <主机名> <网址>
该命令是添加远程主机,其中origin
是远程库的名字,Git默认的叫法。其中网址可以使用https
协议,但是使用SSH支持的原生Git协议很快,且每次推送都不需要输入密码,首次推送有个警告,确定即可。
接下来将本地库的内容推送到远程库,使用命令
$ git push -u origin master
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。以后直接使用git push origin master
即可。
从远程库克隆
上面讲的是从本地推送repo到github上(本地到远程),下面用命令git clone
克隆一个本地库(远程到本地)
example:
$ git clone [email protected]:Ocxs/gitskills.git
分支管理
一个repo,有多个分支,然后多人协作,git的优点之一。
创建与合并分支
在git中,HEAD
是一个指针,指向不同分支。
创建分支dev
并且切换到dev
分支:
$ git checkout -b dev
git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
$ git branch dev # 创建分支
$ git checkout dev #切换到dev分支
然后使用git branch
命令查看当前分支:
$ git branch
* dev
master
当前分支前面会标一个*
号。
然后在readme.txt里面加一些内容,然后使用git add readme.txt
和git commit -m "branch test"
,然后使用git checkout master
命令切换到master
分支下面,这时候查看readme文件,是无法看到修改的,但是使用命令
$ git merge dev #将dev分支合并到当前分支
后,就会发现readme.txt文件发生变化,有了之前的添加内容。 删除分支的命令是:
$ git branch -d dev
小结:
- 查看分支:
git branch
- 创建分支:
git branch <name>
- 切换分支:
git checkout <name>
- 创建+切换分支:
git checkout -b <name>
- 合并某分支到当前分支:
git merge <name>
- 删除分支:
git branch -d <name>
解决冲突
当你在一个分支feature1
里修改了内容,在master
分支里也修改了内容,当两个内容不一样时,使用
git merge
命令就会有冲突,这时候只能手动修改,改好之后再git add
,git commit
即可。如果没有冲突的话,git merge
后不需要git add
和git commit
。可以使用
$ git log --graph --pretty=oneline --abbrev-commit
查看合并的情况。--pretty=oneline --abbrev-commit
可以不加。
分支管理策略
通常,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
$ git merge --no-ff -m "merge with no-ff" dev
其中--no-ff
参数,表示禁用Fast forward
,因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去。合并后,我们用git log
看看分支历史就能看到之前的dev
分支。
总结:合并分支时,加上--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward
合并就看不出来曾经做过合并。
Bug分支
适用场景:当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101
来修复它,但是,等等,当前正在dev
上进行的工作还没有提交,并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
幸好,Git还提供了一个stash
功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
$ git stash
Saved working directory and index state WIP on dev: 6224937 add merge
HEAD is now at 6224937 add merge
用git status
查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。因为如果工作区不干净,在创建并转到其他分支的时候,会提示你让你先commit当前的内容,在转到其他分支。
当你创建bug分支,修复好bug,在切换回来后,如何恢复刚才的工作现场呢?
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
恢复有两个方法:
- 用
git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除; - 用
git stash pop
,恢复的同时把stash内容也删了。
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
Feature分支
这一节主要讲如何强制删除一个分支,使用
$ git branch -D <name>
多人写作
查看远程库信息:
$ git remote
origin
用git remote -v
显示更详细的信息:
$ git remote -v
origin [email protected]:michaelliao/learngit.git (fetch)
origin [email protected]:michaelliao/learngit.git (push)
上面显示了可以抓取和推送的origin
的地址。如果没有推送权限,就看不到push的地址。
推送分支:
$ git push origin dev # dev是分支的名字,可以是master等其他名字
抓取分支:
$ git clone [email protected]:michaelliao/learngit.git
# 该命令抓取的是master分支,如过需要抓取dev分支,在dev分支上开发,使用下面命令:
$ git checkout -b dev origin/dev
如果此时遇到之前说的合并冲突,则需要手动解决冲突,先用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.
然后在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
。 这就是多人协作的工作模式,一旦熟悉了,就非常简单。
总结:
- 查看远程库信息,使用
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
,如果有冲突,要先处理冲突。
标签管理
每个commit都会有个号,比如6a5819e...,这种很难记,所以需要搞个tag,按照tag就能找到与之匹配的commit,会方便许多。
创建标签
$ git tag v1.0
就给当前分支的最新commit打了一个tag为v1.0。由于默认标签是打在最新提交的commit上的,如果想给之前的commit打tag,则先找到历史提交的commit id,然后在打tag:
$ git log --pretty=oneline --abbrev-commit
6a5819e merged bug fix 101
cc17032 fix bug 101
7825a50 merge with no-ff
6224937 add merge
59bc1cb conflict fixed
400b400 & simple
75a857c AND simple
fec145a branch test
d17efd8 remove test.txt
...
# 然后给commit id为6224937的commit打tag
$ git tag v0.9 6224937
这时候使用命令git tag
查看标签:
$ git tag
v0.9
v1.0
注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show <tagname>
查看标签信息:
$ git show v0.9
commit 622493706ab447b6bb37e4e2a2f276a20fed2ab4
Author: Michael Liao <[email protected]>
Date: Thu Aug 22 11:22:08 2013 +0800
add merge
...
还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:
$ git tag -a v0.1 -m "version 0.1 released" 3628164
小结:
- 命令
git tag <name>
用于新建一个标签,默认为HEAD,也可以指定一个commit id; git tag -a <tagname> -m "blablabla..."
可以指定标签信息;git tag -s <tagname> -m "blablabla..."
可以用PGP签名标签;(这个我感觉我暂时用不到, 就没介绍)- 命令
git tag
可以查看所有标签。操作标签
如果标签打错了,也可以删除:
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。$ git tag -d v0.1 Deleted tag 'v0.1' (was e078af9)
如果要推送某个标签到远程,使用命令git push origin <tagname>
:
$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To [email protected]:michaelliao/learngit.git
* [new tag] v1.0 -> v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 554 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To [email protected]:michaelliao/learngit.git
* [new tag] v0.2 -> v0.2
* [new tag] v0.9 -> v0.9
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
$ git tag -d v0.9
Deleted tag 'v0.9' (was 6224937)
然后,从远程删除。删除命令也是push,但是格式如下:
$ git push origin :refs/tags/v0.9
To [email protected]:michaelliao/learngit.git
- [deleted] v0.9
小结:
- 命令
git push origin <tagname>
可以推送一个本地标签; - 命令
git push origin --tags
可以推送全部未推送过的本地标签; - 命令
git tag -d <tagname>
可以删除一个本地标签; - 命令
git push origin :refs/tags/<tagname>
可以删除一个远程标签。
使用Github
先Fork别人项目,然后在从自己的帐号上clone,用git clone
,然后在git push
,通过发pull request
给别人,看别人是否接受。
自定义Git
配置颜色:
$ git config --global color.ui true
忽略特殊文件,配置.gitignore
有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次git status
都会显示Untracked files ...
,会很烦。
解决办法:在Git工作区的根目录下创建一个特殊的.gitignore
文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
example:
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build
# My configurations:
db.ini
deploy_key_rsa
然后把.gitignore
也提交到Git,就完成了!
如果想添加一个文件到git,但是由于该文件被.gitignore
忽略了,两种解决方法:
- 强制添加,使用命令
git add -f App.class
; - 找出
.gitignore
哪个地方规则限制了该问题,然后修改,
Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。$ git check-ignore -v App.class .gitignore:3:*.class App.class
配置别名
example:
$ git config --global alias.co checkout # git co == git checkout
$ git config --global alias.ci commit # git ci == git commit
$ git config --global alias.br branch # git br == git branch
配置文件:
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = [email protected]:Ocxs/learngit.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[alias]
last = log -1
co = checkout
ci = commit
br = branch
st = status
别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。配置Git的时候,加上--global
是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。每个仓库的Git配置文件都放在.git/config
文件中。
搭建Git服务器
暂时用不到,不赘述。