参考网址:https://git-scm.com/book/zh/v2
初次远行Git前的配置:
Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位置:
/etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置。 如果使用带有 --system 选项的 git config 时,它会从此文件读写配置变量。
~/.gitconfig 或 ~/.config/git/config 文件:只针对当前用户。 可以传递 --global 选项让 Git 读写此文件。
当前使用仓库的 Git 目录中的 config 文件(就是 .git/config):针对该仓库。
每一个级别覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。
用户信息
当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。 这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
再次强调,如果使用了 --global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 --global 选项的命令来配置。
很多 GUI 工具都会在第一次运行时帮助你配置这些信息。
文本编辑器
既然用户信息已经设置完毕,你可以配置默认文本编辑器了,当 Git 需要你输入信息时会调用它。 如果未配置,Git 会使用操作系统默认的文本编辑器,通常是 Vim。 如果你想使用不同的文本编辑器,例如 Emacs,可以这样做:
$ git config --global core.editor emacs
如果想要检查你的配置,可以使用 git config --list 命令来列出所有 Git 当时能找到的配置。
你可以通过输入 git config <key>: 来检查 Git 的某一项配置。
获取帮助
若你使用 Git 时需要获取帮助,有三种方法可以找到 Git 命令的使用手册:
$ git help <verb>
$ git <verb> --help
$ man git-<verb>
例如,要想获得 config 命令的手册,执行
$ git help config
在现有目录中初始化仓库
如果你打算使用 Git 来对现有的项目进行管理,你只需要进入该项目目录并输入:
$ git init
该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。
如果你是在一个已经存在文件的文件夹(而不是空文件夹)中初始化 Git 仓库来进行版本控制的话,你应该开始跟踪这些文件并提交。 你可通过 git add 命令来实现对指定文件的跟踪,然后执行 git commit 提交:
$ git add *.c
$ git add LICENSE
$ git commit -m 'initial project version'
克隆现有的仓库
如果你想获得一份已经存在了的 Git 仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone 命令。 如果你对其它的 VCS 系统(比如说Subversion)很熟悉,请留心一下你所使用的命令是"clone"而不是"checkout"。 这是 Git 区别于其它版本控制系统的一个重要特性,Git 克隆的是该 Git 仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件。 当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。 事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库(虽然可能会丢失某些服务器端的挂钩设置,但是所有版本的数据仍在)。
$ git clone https://github.com/libgit2/libgit2
使用 Git 时文件的生命周期如下:
要查看哪些文件处于什么状态,可以用 git status 命令
例如:
新建一个文件test.txt
然后git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
nothing added to commit but untracked files present (use "git add" to track)
说明该文件还没有被跟踪,执行$ git add test.txt
,再次git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: test.txt
说明该文件已经是暂存状态,使用$ git rm --cached <file>
可以清除暂存
$ git commit -m "新建test.txt"
提交到本地仓库
修改一个已被跟踪的文件
编辑test.txt的内容,然后git status
On branch 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: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
使用git add加入到暂存区,或者使用$ git checkout -- <file>
恢复
git add 命令是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。
git status查看
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: test.txt
说明已经处于暂存状态等待提交,或者$ git reset HEAD <file>
来清除暂存。
忽略文件
新建一个.gitignore 的文件,列出要忽略的文件模式。
文件 .gitignore 的格式规范如下:
所有空行或者以 # 开头的行都会被 Git 忽略。
可以使用标准的 glob 模式匹配。
匹配模式可以以(/)开头防止递归。
匹配模式可以以(/)结尾指定目录。
要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号()匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 使用两个星号() 表示匹配任意中间目录,比如`a/**/z` 可以匹配 a/z, a/b/z 或 `a/b/c/z`等。
我们看一个 .gitignore 文件的例子:
\# no .a files 忽略所有.a结尾的文件
*.a
\# but do track lib.a, even though you're ignoring .a files above,被忽略的.a文件中不包括lib.a文件
!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
GitHub 有一个十分详细的针对数十种项目及语言的 `.gitignore` 文件列表,你可以在 https://github.com/github/gitignore 找到它.
查看已暂存和未暂存的修改git diff
git diff
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。
git diff --cached
或者git diff --staged
查看已暂存的将要添加到下次提交里的内容。
请注意,git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件后,运行 git diff 后却什么也没有,就是这个原因。
提交
git commit
后在vim编辑器中输入提交内容
git commit -m "..."
直接输入提交内容
git commit -a -m "..."
可以跳过暂存区直接提交,Git 会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤。
移除文件
手动删除
手动删除后,git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
可以看到,可以通过git add 或者git rm两种方式添加到暂存区记录此次操作。如果文件在删除之前已经被修改且放到了暂存区的话,需要使用-f强制删除,这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。
命令删除
可以直接使用git rm <file>
,这个命令会删除文件,并暂存操作。
删除git仓库中的某个文件(亦即从暂存区域移除),但是本地保留,比如一些误提交的大文件。
$ git rm --cached <file>
或者使用glob模式$ git rm --cached \*~
移动文件、重命名
粗暴的方法
$ mv test.txt test2.txt
$ git rm test.txt
$ git add test2.txt
简单的方法
$ git mv test.txt test2.txt
查看提交历史
git log,默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。
git log 有许多选项可以帮助你搜寻你所要找的提交, 接下来我们介绍些最常用的。
一个常用的选项是 -p,用来显示每次提交的内容差异。 你也可以加上 -2 来仅显示最近两次提交。
--stat 选项在每次提交的下面列出所有被修改过的文件、有多少文件被修改了以及被修改过的文件的哪些行被移除或是添加了。 在每次提交的最后还有一个总结。
另外一个常用的选项是 --pretty。 这个选项可以指定使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如用 oneline 将每个提交放在一行显示,查看的提交数很大时非常有用。 另外还有 short,full 和 fuller 可以用,展示的信息或多或少有些不同,请自己动手实践一下看看效果如何。
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
但最有意思的是 format,可以定制要显示的记录格式。 这样的输出对后期提取分析格外有用 — 因为你知道输出的格式不会随着 Git 的更新而发生改变:
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : changed the version number
085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
a11bef0 - Scott Chacon, 6 years ago : first commit
当 oneline 或 format 与另一个 log 选项 --graph 结合使用时尤其有用。 这个选项添加了一些ASCII字符串来形象地展示你的分支、合并历史:
$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
* 11d191e Merge branch 'defunkt' into local
其他筛选条件
条数限制:上面的-2其实是-<n>的写法,n可以是任何整数。
时间限制:比如 --since 和 --until,$ git log --since=2.weeks
,这个命令可以在多种格式下工作,比如说具体的某一天 "2008-01-15",或者是相对地多久以前 "2 years 1 day 3 minutes ago"。
还可以给出若干搜索条件,列出符合的提交。 用 --author 选项显示指定作者的提交,用 --grep 选项搜索提交说明中的关键字。 (请注意,如果要得到同时满足这两个选项搜索条件的提交,就必须用 --all-match 选项。否则,满足任意一个条件的提交都会被匹配出来)。
另一个非常有用的筛选选项是 -S,可以列出那些添加或移除了某些字符串的提交。 比如说,你想找出添加或移除了某一个特定函数的引用的提交,你可以这样使用:$ git log -Sfunction_name
。
撤消操作
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令尝试重新提交
$ git commit --amend
这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。文本编辑器启动后,可以看到之前的提交信息。 编辑后保存会覆盖原来的提交信息。也可以直接使用-m添加提交信息
$ git commit --amend -m "..."
最终你只会有一个提交 - 第二次提交将代替第一次提交的结果。
取消暂存的文件
git reset HEAD <file>
撤销对文件的修改(文件是未暂存状态)
git checkout -- <file>
注意:在 Git 中任何 已提交的 东西几乎总是可以恢复的,然而,任何你未提交的东西丢失后很可能再也找不到了。
远程仓库的使用
查看远程仓库
git remote -v
,它会列出你指定的每一个远程服务器的简写,-v会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。
git remote show [remote-name]
,可以查看某一个远程仓库的更多信息。
$ git remote show origin
* remote origin
Fetch URL: https://github.com/schacon/ticgit
Push URL: https://github.com/schacon/ticgit
HEAD branch: master
Remote branches:
master tracked
dev-branch tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)
添加远程仓库
运行 git remote add <shortname> <url> 添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写。
从远程仓库中抓取与拉取
git fetch [remote-name]
,这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
如果你使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。 所以,git fetch origin 会抓取克隆(或上一次抓取)后新推送的所有工作。
必须注意 git fetch 命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。
git pull
会抓取然后合并远程分支到当前分支。
推送到远程仓库
git push [remote-name] [branch-name]
,只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。 当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先将他们的工作拉取下来并将其合并进你的工作后才能推送。
远程仓库的移除与重命名
如果想要重命名引用的名字可以运行 git remote rename
去修改一个远程仓库的简写名.
$ git remote rename pb paul
$ git remote
origin
paul
值得注意的是这同样也会修改你的远程分支名字。 那些过去引用 pb/master 的现在会引用 paul/master。
如果因为一些原因想要移除一个远程仓库 - 你已经从服务器上搬走了或不再想使用某一个特定的镜像了,又或者某一个贡献者不再贡献了 - 可以使用 git remote rm :
$ git remote rm paul
$ git remote
origin
打标签
列出标签
$ git tag
v0.1
v1.3
也可以添加筛选条件(glob):
$ git tag -l 'v1.8.5*'
v1.8.5
v1.8.5-rc0
v1.8.5-rc1
创建标签
附注标签
$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
-m 选项指定了一条将会存储在标签中的信息。 如果没有为附注标签指定一条信息,Git 会运行编辑器要求你输入信息。
通过使用 git show 命令可以看到标签信息与对应的提交信息:
$ git show v1.4
tag v1.4
Tagger: Ben Straub <ben@straub.cc>
Date: Sat May 3 20:19:12 2014 -0700
my version 1.4
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
changed the version number
输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息。
轻量标签
轻量标签本质上是将提交校验和存储到一个文件中 - 没有保存任何其他信息。
git tag ...
补签
可以对过去的提交打标签,假如提交历史是这样的:
$ git log --pretty=oneline
0d52aaab4479697da7686c15f77a3d64d9165190 one more thing
6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment'
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function
4682c3261057305bdd616e23b64b0857d832627b added a todo file
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
如果想对其中“updated rakefile”补签,要在那个提交上打标签,你需要在命令的末尾指定提交的校验和(或部分校验和):
$ git tag -a v1.2 9fceb02
$ git show v1.2
tag v1.2
Tagger: Scott Chacon <schacon@gee-mail.com>
Date: Mon Feb 9 15:32:16 2009 -0800
version 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8
Author: Magnus Chacon <mchacon@gee-mail.com>
Date: Sun Apr 27 20:43:35 2008 -0700
updated rakefile
共享标签(推送标签到远程仓库)
默认情况下,git push不会传送标签,需要显示的推送标签git push origin [tagname]
。
也可以使用git push origin --tags将所有不在远程仓库的标签全部传送过去。
检出标签
在 Git 中你并不能真的检出一个标签,因为它们并不能像分支一样来回移动。 如果你想要工作目录与仓库中特定的标签版本完全一样,可以使用 git checkout -b [branchname] [tagname] 在特定的标签上创建一个新分支:
$ git checkout -b version2 v2.0.0
Switched to a new branch 'version2'
分支
先简单了解一下Git的处理方式:
首先是暂存操作,暂存操作会为每一个文件计算校验和,然后会把当前版本的文件快照保存到 Git 仓库中,最终将校验和加入到暂存区域等待提交;
然后是提交操作,当使用 git commit 进行提交操作时,Git 会先计算每一个子目录的校验和,然后在 Git 仓库中这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,该提交对象会包含指向这个树对象(项目根目录)的指针,一个指向暂存内容快照的指针,还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。
Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。 它会在每次的提交操作中自动向前移动。Git 的 “master” 分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人都懒得去改动它。
分支创建
创建一个testing分支
$ git branch testing
上图中HEAD是一个特殊指针,它指向了当前所在的分支,git branch
只是创建了一个分支,并不会自动切换到新分支上去。如果要在创建的同时切换到新分支,可以用这个命令$ git checkout -b testing
,加一个-b。
分支切换
$ git checkout testing
这样HEAD就指向了testing分支。
此时如果commit一次之后,testing分支的指针便会向前移动到一个新的版本,而master分支然后停留在之前的位置,git checkout master
,回到master分支,这条命令做了两件事。 一是使 HEAD 指回 master 分支,二是将工作目录恢复成 master 分支所指向的快照内容。 也就是说,你现在做修改的话,项目将始于一个较旧的版本。 本质上来讲,这就是忽略 testing 分支所做的修改,以便于向另一个方向进行开发。这时候commit的话,便会产生分叉,可以通过git log --oneline --decorate --graph --all
来查看项目分叉历史,当然也可以在合适的时候将分支进行合并。
分支的新建与合并
让我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流。 你将经历如下步骤:
1.开发某个网站。
2.为实现某个新的需求,创建一个分支。$ git checkout -b myJob
3.在这个分支上开展工作。$ git commit -a -m 'do something'
正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:
4.切换到你的线上分支(production branch)。$ git checkout master
5.为这个紧急任务新建一个分支,并在其中修复它。
$ git checkout -b hotfix
$ git commit -a -m 'fixed bug'
6.在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
$ git checkout master
$ git merge hotfix
合并的时候可能会看到"快进(fast-forward)"这个词,由于当前 master 分支所指向的提交是你当前提交(有关 hotfix 的提交)的直接上游,所以 Git 只是简单的将指针向前移动。 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。
问题已经修复完成,可以删除hotfix分支了$ git branch -d hotfix
7.切换回你最初工作的分支上,继续工作。$ git checkout myJob
如果在myJob分支提交了新的东西,然后按照上面的步骤合并到master,这时候Git的工作方式不再是fast-forward,合并时会将现在HEAD所指向的master和myJob以及这两个分支的某个共同的祖先(Common Ancestor)三者做一次合并,如果合并成功,Git会将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。如果合并过程中产生冲突,Git 会暂停下来,等待你去解决合并产生的冲突。 你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件,任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。此时需要先解决冲突后,再次暂存并提交。
git branch的其他命令:
直接输入git branch会得到当前所有分支的一个列表,其中master前面的*表示当前HEAD指针指向该分支:
$ git branch
myJob
* master
testing
如果需要查看每一个分支的最后一次提交,可以运行 git branch -v 命令:
$ git branch -v
myJob 93b412c fix javascript issue
* master 7a98805 Merge branch 'myJob'
testing 782fd34 add scott to the author list in the readmes
如果要查看哪些分支已经合并到当前分支,可以运行 git branch --merged。已经合并的分支,可以通过git branch -d 删除掉;
查看所有包含未合并工作的分支,可以运行 git branch --no-merged,没有合并的分支使用git branch -d 命令删除它时会失败,但是可以使用git branch -D强制删除。
持续更新ing~