这篇文章将涵盖在使用Git完成完成各种工作中将要使用的各种命令。看完之后,我们应该能配置并初始化一个仓库(repository),开始或停止跟踪(track)文件,暂存(stage)或提交(commit)更改。也将演示如何配置 Git 来忽略指定的文件和文件模式、如何迅速而简单地撤销错误操作、如何浏览你的项目的历史版本以及不同提交(commits)间的差异、如何向你的远程仓库推送(push)以及如何从你的远程仓库拉取(pull)文件。
获取Git仓库
将本地的项目初始化为仓库
首先进入项目的跟目录,例如,我新建了一个learnGit
的文件夹,作为我整个项目的根目录。
然后执行命令
git init
这样,我们就初始化好了一个仓库。然后会在这个文件夹里面新建一个.git
的文件夹。这个文件夹录含有你初始化的 Git 仓库中所有的必须文件,这些文件是Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。
接下来我们在根目录中新建一个describle.txt
的文件。然后我们执行命令
git add describle.txt # 这样,我们就将文件添加至了暂存区了,git也会开始监听这个文件的修改了
git commit # 暂存区的文件提交到仓库中去,这里会代开默认的编辑器(vim),让你输入此次提交的描述信息
我们就可以在这个编辑器里面输入对此次改动的一些描述,注意,这是vim编辑器,所以我们首先先按i
进入编写模式,然后我们在最后一行写入“initial projects”
这里输入的内容就是描述我们此次修改的信息,可以随便写。输入完成之后,然后按esc
键,推出编写模式,然后按shift + ;
键,就会在左下角显示一个冒号:
出来,然后输入wq
(保存并退出),然后按enter
键,就完成了此次的提交,并且添加了描述信息为"initial projects"
检查当前文件的状态
git status
这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。 此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件,否则 Git 会在这里列出来。 最后,该命令还显示了当前所在分支,并告诉你这个分支同远程服务器上对应的分支没有偏离。 现在,分支名是 “master”,这是默认的分支名。
现在,我们在项目中新建一个index.html
的文件。然后运行git status
的命令,会看到如下的信息
在状态报告中可以看到新建的 index.html文件出现在 Untracked files(未跟踪文件)
下面,未跟踪文件
意味着 Git 在之前的快照(提交)中没有这些文件;Git 不会自动将之纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文件”,即使用git add index.html
这个命令,这样,我们就成功的使这个文件被Git跟踪了。
然后我们在运行git status
后,index.html文件就在Changes to be committed 这行下面的,就说明是已暂存状态。
这个时候我们再运行git commit
命令之后,就会被添加至我们的本地仓库中了。
执行git commit
命令后会启动默认vim编辑器(如果我们没有配置默认的编辑器的话),我们就可以在里面输入一些信息,来描述此次提交版本的一些描述信息。或者我们直接使用-m
参数,就可以不用进入vim编辑器来编写提交的版本的描述信息。
git commit -m "描述信息"
接下来我们来修改一个已经被跟踪的文件,如上面的index.html
文件。然后再使用git status
查看状态信息
文件index.html 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。 要暂存这次更新,需要运行 git add 命令。 这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。 将这个命令理解为“添加内容到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。 现在让我们运行git add
将index.html放到暂存区,然后再看看 git status
的输出
可以看到这个文件也放入了暂存区去,下次执行commit
时就会提交到本地仓库去了
如果我们觉得git status
输出内容太繁琐了,我们可以使用-s
或--short
参数,我们将得到一种更为紧凑的格式输出。
git status -s
or
git status --short
的到的结果对应的参数表示的意思为:
M xxx1.txt
M xxx/xxx2.txt
MM xxx3.txt
A xxx4.txt
?? xxx5.txt
新添加的未跟踪文件前面有 ?? 标记,新添加到暂存区中的文件前面有A 标记,修改过的文件前面有M 标记。 你可能注意到了 M 有两个可以出现的位置,出现在右边的 M 表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。
上面的信息表示的意思就是:
- xxx1.txt文件修改了,但是没有放入暂存区。
- xxx/xxx2.txt文件表示该文件被修改了并且也放入的暂存区域内了。
- xxx3.txt文件表示在工作区被修改并提交到暂存区后又在工作区中被修改了,也就时如果我们执行
git status
命令的时候,就会看到xxx3.txt
在Untracked files
和Changes not staged for commit
区域下面都会显示出来。 - xxx4.txt文件已经被放到了暂存区里面去了。
- xxx5.txt文件表示这是新添加的文件,还没有被跟踪。
忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore的文件,列出要忽略的文件模式。 例如我再.gitignore
文件中写入以下信息:
# 忽略.a结尾的文件
*.a
# 但是一定要跟踪lib.a文件,即使你上面忽略了.a结尾的文件
!lib.a
# 只忽略当前目录中的TODO文件,而子目录下的/TODO文件将不被忽略
/TODO
# 忽略build/目录中的所有文件
build/
# 忽略doc/下的.txt文件,但不是doc/server/*.txt
doc/*.txt
# 忽略doc/目录中的所有.pdf文件
doc/**/*.pdf
文件.gitignore
文件的格式规范如下:
- 所有空行或者以#开头的行都会被Git忽略掉,即在这个文件中可以使用# 进行注释
- 可以使用标准的 glob 模式匹配。
- 匹配模式可以以(/)开头防止递归。
- 匹配模式可以以(/)结尾指定目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
glob模式
:指 shell 所使用的简化了的正则表达式。 星号(*) 匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 使用两个星号(*) 表示匹配任意中间目录,比如a/**/z
可以匹配 a/z, a/b/z 或 a/b/c/z
等。
GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表,你可以在
https://github.com/github/gitignore 找到它.
查看以暂存和未暂存的修改
如果git status
命令的输出对于你来说过于模糊,你想知道具体修改了什么地方,可以用 git diff
命令。稍后会详细介绍 git diff
,你可能通常会用它来回答这两个问题:当前做的哪些更新还没有暂存? 有哪些更新已经暂存起来准备好了下次提交? 尽管 git status
已经通过在相应栏下列出文件名的方式回答了这个问题,git diff
将通过文件补丁的格式显示具体哪些行发生了改变。
我们先修改index.html
文件里面的内容,然后f分别使用git diff
和git diff --cached
(Git 1.6.1 及更高版本还允许使用git diff --staged
,效果是相同的,但更好记些。)
上图中的绿色字体就是我刚刚在index.html
文件中添加的内容,我们可以看到,我们使用git diff
命令可以看到一些信息,但是使用--cached
就不能看到信息了,那是因为git diff
命令比较的是工作目录中当前文件
和暂存区域
快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。而git diff --cached(git diff --staged)
则是查看已暂存的将要添加到下次提交里的内容
。
我们先使用git add index.html
命令,然后我们在使用git diff --cached
命令就可以看到上面我们看到的信息了。
接下来,我们想要将此次的改动保存在本地仓库,那么我们就可以运行git commit
命令了,上面我们也已经说了一些他的用法了,这里就不在说了。
跳过使用暂存区域
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。 Git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:
移除文件
要从Git中移除某个文件,就必须要从已跟踪文件清单中移除(准确的说,是从暂存区域移除),然后提交。可以使用git rm
命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件的清单中了。
git rm <filename>
想要执行这条命令的话我们的文件必须已经提交至仓库了。然后这条命令才能被成功执行,否则git将找不到这个文件。
注意: 这条命令将会将本地的文件也会删除掉。
执行这条命令之后,这个文件下次就不在会被纳入版本管理了。如果删除之前修改并且已经放入暂存区域的话,则必须要用墙纸删除选项-f
(即force的首字母)。这个机制是为了防止误删还没有添加到快照的数据。因为这样的数据不能被Git
恢复的。
另一种情况是,我们想要把文件从Git
仓库中删除(亦即从暂存区域删除),但仍然希望保留当前的工作目录,也就是说,想要将此文件保留在磁盘,但是并不想让Git继续跟踪。当我们忘记添加.gitignore
文件,然后将一大堆不需要的文件添加至了暂存区了,这一命令就显得十分有用了。这条命令就是
git rm --cached <filename>
git rm
命令后面可以列出文件或者目录的名字,也可以使用glob模式。
例如:
git rm log/\*.log
*
之前的反斜杠\
,因为Git有他自己的文件模式扩展匹配方式,所以我们不用shell来帮忙展开。*
匹配所有字符
这条命令的意思是删除log文件夹下面所有扩展名为.log
的文件。
git rm \*~
删除以~结尾的所有文件
移动文件(更改文件名字)
修改文件的名字
git mv sourceFileName(源文件名字) targetFileName(修改文件的名字)
移动文件
git mv sourceFileName(源文件名字) targetFilePath(目标文件路径)
查看提交历史
如果我们想回顾一下提交历史,那么我们可以使用git log
命令
git log
使用这个命令之后我们需要按q
退出
常用选项
- -p 参数:用来显示每次提交内容的差异
git log -p
- -<n>:只显示最近两次提交
git log -p -2
- –stat:显示每次提交的简略信息
git log --stat
- –shortstat:只显示–stat中最后的行数修改添加移除统计
git log --shortstat
- –name-only:只显示提交文件的清单
git log --name-only
- –name-status: 显示新增,修改,删除的文件清单
git log --name-status
- –abbrev-commit: 仅显示SHA-1的前几个字符,而非所有的40个字符
git log --abbrev-commit
- –graph : 显示ASCII图形表示的分支合并历史
git log --graph
- –relative-date: 使用较短的相对时间显示(比如 “2weeks ago”)
git log --relative-date
- –since,–after: 仅显示最近2019年3月1日之后的提交内容
git log --since=2019-03-01
- –until,–before: 仅显示指定时间之前的提交
git log --until=2019-03-01
- –author: 仅显示指定作者的相关提交
git log --author=xxx
- –committer: 仅显示指定提交者的相关提交
git log --committer=xxx
- –grep: 仅显示含指定关键字的提交
git log --grep=add file
- -S:仅显示添加或移除了某个关键字的提交
git log -S<关键字>
- –pretty:指定使用不同于默认格式展示提交历史,这个选项有一些内置的子选项选择,比如
oneline
,将每个提交放在一行显示,还有一些选项为short
,full
,fuller
。这里就不一一展示了。git log --pretty=oneline
,还有一个不是内置的子选项,他可以允许我们自定义显示格式.
示例:git log --pretty=format:"%h - %an, %ar : %s"
fromat的常用选项:
选项 | 说明 |
---|---|
%H | 提交对象(commit)的完整哈希 |
%h | 提交对象的简短哈希 |
%T | 树对象(tree)的完整哈希 |
%t | 树对象的简短哈希 |
%P | 父对象(parent)的完整哈希 |
%p | 父对象的简短哈希 |
%an | 作者(author)的名字 |
%ae | 作者的电子邮件 |
%ad | 作者的修订日期(可以使用--date= 选项制定格式) |
%ar | 作者的修订日期,按多久以前的方式显示 |
%cn | 提交者(committer)的名字 |
%ce | 提交者的电子邮件 |
%cd | 提交日期 |
%cr | 提交日期,按多久以前的方式显示 |
%s | 提交说明 |
注意:
- 提交者和作者不一定是一个人,作者是修改项目的人,提交者是将修改之后的项目提交到Git仓库的人。
- 如果得到的同时满足两个选项搜索条件的提交,我们就得用
--all-match
选项,否则,满足任意一个提交的条件都会被匹配出来。
撤销操作
在有些时候我们想要撤销某些操作,例如当我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了,此时,我们可以运行--amend
选项的提交命令尝试重新提交。
git commit --amend
这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是对此次提交的描述。
如果忘记暂存了某些需要的修改,然后就进行了提交,那么这些忘记暂存了的修改不会被一同提交。
这个使我们我们有两种解决方案:
- 将忘记的暂存的修改重新,暂存,然后再次提交。这样的话我们忘记了的修改会被提交至仓库,但是并不会和上次的提交在同一次提交中。如果我们想让他们在同一次提交中,我们就得使用下面一种的方法。
- 首先执行
git add forgotten_file
将忘记了暂存的文件提交至暂存区,然后执行git commit --amend
就会将这些文件一同放入了上一次的提交中了。
取消暂存的文件
你已经修改了两个文件并且想要将它们作为两次独立的修改提交,但是却意外地输入了 git add *
(添加当前目录下的所有文件至暂存区) 暂存了它们两个。 如何只取消暂存两个中的一个呢?这个时候我们就可以执行git reset HEAD <filename>
来取消暂存了。
撤销对文件的修改
如果我们不想保留对某一个文件的修改,还原为上次提交时的样子,那么我们可以使用git checkout -- <filename>
来实现这个功能
注意: 这个命令只能还原修改过后没有放在暂存区的文件,如果放入了暂存区,那么这个命令将不会有任何作用。
远程仓库的使用
为了能在任意 Git 项目上协作,你需要知道如何管理自己的远程仓库。 远程仓库是指托管在因特网或其他网络中的你的项目的版本库。 你可以有好几个远程仓库,通常有些仓库对你只读,有些则可以读写。 与他人协作涉及管理远程仓库以及根据需要推送或拉取数据。 管理远程仓库包括了解如何添加远程仓库、移除无效的远程仓库、管理不同的远程分支并定义它们是否被跟踪等等。
查看远程仓库列表
如果你想查看已经配置了的远程仓库,可以运行git remote
命令。他会列出你指定的每一个远程服务器的简写。
如果你已经克隆了远程服务器上的一个仓库,那么至少应该能看到 origin
- 这是 Git 给你克隆的仓库服务器的默认名字。
可以指定参数-v
,显示需要读写远程仓库使用的Git保存的简写与其对应的URL。
git remote -v
如果你的远程仓库不止一个,该命令会将它们全部列出。
添加远程仓库
添加一个新的远程Git仓库 ,同时我们可以指定一个轻松引用的简写。
git remote add pb(简写) https://github.com/paulboone/ticgit
我们可以使用git remote -v
查看到此次添加的远程服务器
这个时候我们要对这个远程服务器进行拉取和推送数据时,我们就可以使用pb
来代替这个url了
从远程仓库中抓取和拉取
从远程仓库中获得数据
git fetch pb(remote-name)
这个命令会访问远程仓库,从中拉取所有你还没有的数据。执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
如果你使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。 所以,git fetch origin
会抓取克隆(或上一次抓取)后新推送的所有工作。 必须注意 git fetch
命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并
入你的工作。
如果我们并不想自己手动来合并抓取的数据,那么我们可以使用git pull pb[remote-name]
命令来达到我们的需求。这个命令就会抓取远程服务器中的仓库中的数据,并且尝试自动合并到我们当前的分支。
推送到远程仓库
当我们想要将我们的项目时,必须将其推送到上游,这个命令很简单:git push [remote-name] [branch-name]
。例如想要将master 分支推送到origin服务器时,我们可以使用下面这个命令。
git push origin master
只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。 当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先将他们的工作拉取下来并将其合并进你的工作后才能推送。
查看某一个远程仓库的信息
如果想要查看某一个远程仓库的更多信息,可以使用 git remote show [remote-name] 命令。例如,查看origin仓库的信息。
git remote show origin
它会列出远程仓库的 URL 与跟踪分支的信息。 这些信息非常有用,它告诉你正处于 master 分支,并且如果运行 git pull,就会抓取所有的远程引用,然后将远程 master 分支合并到本地 master 分支。 它也会列出拉取到的所有远程引用。
远程仓库的移除与重命名
如果想要重命名引用的名字可以运行git remote rename
去修改一个远程仓库的简写名。 例如,想要将 pb重命名为 paul:
git remote rename pb paul
注意: 这同样也会修改你的远程分支名字,那些过去引用pb/master的现在会引用paul/master
如果因为一些原因想要移除一个远程仓库 - 你已经从服务器上搬走了或不再想使用某一个特定的镜像了,又或者某一个贡献者不再贡献了 - 可以使用 git remote rm
,例如,移除paul仓库:
git remote rm paul
打标签
列出标签
git tag
上面命令能列出仓库中已有的标签,但是现在我们什么也看不到,因为我们还没有创建任何一个标签。
创建标签
Git主要使用两种标签类型:轻量标签(lightweight)与附注标签(annotated)。
-
一个轻量标签很像一个不会改变的分支-它只是一个特定提交的引用。
-
附注标签是存储在Git数据库中的一个完整对象。它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用GNU Privacy Guard(GPU)签名与验证。通常建议创建附注标签,这样你可以拥有以上所有的信息,但是如果我们想要一个临时的标签,或者因为某些原因不想要保存那些信息,我们就可以使用轻量级标签。
附注标签
Git中创建一个附注标签很简单的,最简单的方式是当你在运行tag
命令时指定-a
选项:
git tag -a v1.0 -m "my version 1.0"
这样,我们就创建好了一个附注标签,-m参数指定了一条将会存储在标签中的备注信息,如果没有这个-m参数,Git就会打开默认的编辑器要求我们输入信息。
使用git show
命令可以看到标签信息与其对应的提交信息:
git show v1.0
输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息。我们按q
键就能退出 了。
轻量标签
轻量标签本质上是将提交校验和存储到一个文件中 - 没有保存任何其他信息。 创建轻量标签,不需要使用 -a、-s 或 -m 选项,只需要提供标签名字:
git tag v1.0-lw
我们习惯在轻量级标签后面添加一个后缀-lw
,当然,不填加也是可以的。
后期打标签
我们也可以对过去的提交打标签,例如我的提交历史是这样的:
现在我们需要给描述信息为first commit
这次提交打标签,那么我们可以这样做:
git tag -a v0.5 41e4d6a(指定的校验和,可以为部分指定)
这样我们就给那次提交打了一个标签了。
共享标签
默认情况下,git push
命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样 - 我们可以运行 git push origin [tagname]
。
git push origin v0.5
如果想要一次性推送很多标签,也可以使用带有 --tags 选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里。
git push origin --tags
现在,别人从仓库中克隆或拉取,他们也能得到你的那些标签。
删除标签
要删除掉你本地仓库上的标签,可以使用命令 git tag -d <tagname>
。例如,可以使用下面的命令删除掉一个轻量级标签:
git tag -d v0.5
注意: 上述命令并不会从任何远程仓库移除这个标签,你必须使用 git push <remote> :refs/tags/<tagname>
来更新你的仓库:
git push origin :refs/tags/v0.5
检出标签
如果想查看某个标签所指向的文件版本,可以使用git checkout
命令,虽然说这会使你的仓库处于“分离头指针(detacthed HEAD)”状态——这个状态有些不好的副作用:
git checkout v1.0
在"分离头指针"状态下,如果我们做了某些更改然后提交他们,标签不会发生变化,但你的新提交将不会属于任何分支,并且将无法访问,除非确切的提交哈希。因此,如果我们需要进行更改–比如说你正在修复旧版本的错误–者通常需要新建一个新的分支。
git checkout -b version2 v2.0.0
当然,如果在这之后又进行了一次提交,version2分支会因为这个改动向前移动,version2分支就会和v2.0.0标签稍微有点不同,这是就可以放心了。
Git别名
Git不会在我们输入部分命令时自动推出你想要的命令。如果不想每次都输入完整的Git命令,可以通过git config
文件来轻松的为每一个命令设置一个别名。例如:
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
这样,当我们要输入git commit
时,只需要输入git ci
.
总结
现在,我们可以完成所有基本的 Git 本地操作-创建或者克隆一个仓库、做更改、暂存并提交这些更改、浏览你的仓库从创建到现在的所有更改的历史。