阅读 106

git系列---基础使用

注: 该系列文章整理自《Pro Git》,非原创。

在开始学习 Git 的时候,请努力分清你对其它版本管理系统的已有认识,如 Subversion 和 Perforce 等;这么做能帮助你使用工具时避免发生混淆。 Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑

git系列:

git系列--基础使用

git系列--分支

一、git思想和基本工作原理

1.1 直接记录快照,而非差异比较

Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。其它大部分系统以文件变更列表的方式存储信息, 这类系统(CVS、Subversion、Perforce、Bazaar 等等)将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。

Git 更像是把数据看作是对小型文件系统的一组快照。每次你提交更新,或在Git中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个快照流。

这种对待数据的方式,使得git分支管理特别强大。

1.2 近乎所有操作都是本地执行

在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息,等有网络时,再更新到服务器上。因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。

1.3 三种状态和三个工作区域

Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。已提交表示数据已经安全的保存在本地数据库中。已修改表示修改了文件,但还没保存到数据库中。已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。

由此引入 Git 项目的三个工作区域的概念:Git仓库工作目录以及暂存区域

Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。

工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。

暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作“索引”,不过一般说法还是叫暂存区域。

基本的 Git 工作流程如下:

  1. 在工作目录中修改文件。
  2. 暂存文件,将文件的快照放入暂存区域。
  3. 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

如果 Git 目录中保存着特定版本的文件,就属于已提交状态。如果作了修改并已放入暂存区域,就属于已暂存状态。如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。

1.4 用户信息

当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。 这样做很重要,因为每一个Git的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改:

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
复制代码

如果想要检查你的配置,可以使用 git config --list 命令来列出所有 Git 当时能找到的配置。

$ git config --list
user.name=John Doe
user.email=johndoe@example.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...
复制代码

你可以通过输入 git config : 来检查 Git 的某一项配置

$ git config user.name
John Doe
复制代码

二、git基础使用

本章目标:配置并初始化一个仓库(repository)、开始或停止跟踪(track)文件、暂存(stage)或提交(commit)更改。 本章也将向你演示如何配置Git来忽略指定的文件和文件模式、如何迅速而简单地撤销错误操作、如何浏览你的项目的历史版本以及不同提交(commits)间的差异、如何向你的远程仓库推送(push)以及如何从你的远程仓库拉取(pull)文件

2.1 获取Git仓库

有两种取得 Git 项目仓库的方法。

  1. 第一种是在现有项目或目录下导入所有文件到 Git 中;
  2. 第二种是从一个服务器克隆一个现有的 Git 仓库。

2.1.1 在现有目录中初始化仓库

若想对现有的项目进行管理,只需要进入该项目目录并输入:

$ git init

该命令将会创建初始仓库。会创建一个名为 .git(隐藏文件) 的子目录,这个子目录含有你初始化的Git仓库中所有的必须文件,这些文件是Git仓库的骨干。

但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪维护。如果该文件夹已经存在文件,你可以开始跟踪这些文件并提交。

$ git add *.c  //添加后缀为.c的文件
$ git add LICENSE //添加LICENSE
$ git add . //添加所有文件
$ git commit -m 'initial project version' //提交
复制代码

现在,你已经得到了一个实际维护(或者说是跟踪)着若干个文件的 Git 仓库。

2.1.2 克隆现有仓库

如果你想获得一份已经存在了的Git仓库的拷贝,比如github上某个开源项目。这时就要用到git clone命令。Git克隆的是该Git仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件。 当你执行 git clone命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库。

克隆仓库的命令格式是 git clone [url]。 比如,要克隆 Git 的可链接库 ,可以用下面的命令:

$ git clone https://github.com/XWayne/TapeView.git
复制代码

这会在当前目录下创建一个名为 “TapeView” 的目录,并包含项目中的文件。 如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以使用如下命令:

$ git clonehttps://github.com/XWayne/TapeView.git myTape

复制代码

这将执行与上一个命令相同的操作,不过在本地创建的仓库名字变为 myTape。

2.2 记录每次更新到仓库

工作目录下的所有文件,对于git来说,只有两种状态:已跟踪或未跟踪 ;已跟踪文件指那些被纳入版本控制的文件,上次快照中有他们的记录,工作一段时间后,可能处于未修改,已修改或已放入暂存区;除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。对于初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。

编辑已跟踪文件后,Git将它们标记为已修改文件,我们将这些修改文件放入暂存区后就处于已放入暂存区,提交后,就又恢复成未修改状态。

2.2.1 检查当前文件状态

要查看哪些文件处于什么状态,可以用git status命令。如在克隆仓库后立即使用此命令,会看到类似的输出:

$ git status
On branch master
nothing to commit, working directory clean
复制代码

这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过,也没有出现任何处于未跟踪状态的新文件

现在,创建一个新的README文件,如果之前不存在,再次运行git status命令,会看到新的未跟踪文件:

$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    README

nothing added to commit but untracked files present (use "git add" to track)
复制代码

在状态报告中可以看到新建的 README 文件出现在 Untracked files 下面。

2.2.2 跟踪新文件

使用命令 git add 开始跟踪一个文件。 所以,要跟踪 README文件,运行:

$ git add README
复制代码

此时再运行git status命令,会看到README文件已被跟踪,并处于暂存状态:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
复制代码

在 Changes to be committed 这行下面的,就说明是已暂存状态。

2.2.3 暂存已修改文件

现在修改一个已被跟踪的,名为 CONTRIBUTING.md的文件然后运行 git status 命令,会看到下面内容:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README

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:   CONTRIBUTING.md
复制代码

文件 CONTRIBUTING.md 出现在 Changes not staged for commit这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。

要暂存这次更新,需要运行git add命令。这是个多功能命令:

  1. 可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区
  2. 还能用于合并时把有冲突的文件标记为已解决状态等。

将这个命令理解为 “添加内容到下一次提交中” 而不是“将一个文件添加到项目中”要更加合适。

我们运行 git add 将"CONTRIBUTING.md"放到暂存区,然后再看看 git status 的输出:

$ git add CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md
复制代码

现在两个文件都已暂存,下次提交时就会一并记录到仓库。

2.2.4 提交更新

提交命令:

$ git commit -m "你要写的本次提交的备注信息"
复制代码

请记住,提交时记录的是放在暂存区域的快照。任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。

2.2.5 跳过使用暂存区域

Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给git commit 加上 -a 选项,Git就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:

2.2.6 忽略文件

一般我们总会有些文件无需纳入Git的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。

参考:

  1. 忽略特殊文件
  2. 官方准备的各种配置文件

2.2.7 移除文件

要从 Git 中移除某个文件,即要从已跟踪文件清单中移除,并连带从工作目录中删除指定的文件,可以使用命令git rm <file>

$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    PROJECTS.md
复制代码

提交之后即完成此次操作。

若只想从Git仓库中删除,但仍希望保留在工作目录即磁盘中(当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆.a这样的编译生成文件添加到暂存区时,这一做法尤其有用),使用 --cached 选项:

$ git rm --cached README.txt
rm 'README.txt'

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    README.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        README.txt

复制代码

2.2.8 查看提交历史

查看提交历史,是git log命令

$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit
复制代码

默认不用任何参数的话,git log会按提交时间列出所有的更新,最近的更新排在最上面。正如你所看到的,这个命令会列出每个提交的SHA-1校验和、作者的名字和电子邮件地址、提交时间以及提交说明。

更多git log 使用,参考:查看提交历史:git log

2.3 撤消操作

在任何一个阶段,你都有可能想要撤消某些操作。

注意:有些撤消操作是不可逆的。这是在使用 Git 的过程中,会因为操作失误而导致之前的工作丢失的少有的几个地方之一。

2.3.1 重新提交

有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。此时,可以运行带有 --amend 选项的提交命令尝试重新提交:

$ git commit --amend -m '新的提交信息'
复制代码

这个命令会将暂存区中的文件提交。如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。将代替上一次提交的结果。

2.3.2 取消暂存的文件

命令:git reset HEAD <file>...来取消暂存

你已经修改了两个文件并且想要将它们作为两次独立的修改提交,但是却意外地输入了 git add * 暂存了它们两个。如何只取消暂存两个中的一个呢? git status 命令提示了你:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md
复制代码

在 “Changes to be committed” 文字正下方,提示使用 git reset HEAD <file>...来取消暂存。 所以,我们可以这样来取消暂存 CONTRIBUTING.md 文件:

$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M	CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

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:   CONTRIBUTING.md
复制代码

2.3.3 撤销对文件的修改

使用git checkout -- <file>命令来撤销文件的修改

若不想保留对文件的修改,即想恢复到上一次提交的样子,可以通过该命令取消(git status亦有提示)

$ git checkout -- CONTRIBUTING.md
复制代码

注意: 在Git中任何已提交的东西几乎总是可以恢复的。甚至那些被删除的分支中的提交或使用--amend选项覆盖的提交也可以恢复。然而,任何你未提交的东西丢失后很可能再也找不到

2.4 远程仓库的使用

为了能在任意Git项目上协作,你需要知道如何管理自己的远程仓库。远程仓库是指托管在因特网或其他网络中的你的项目的版本库。你可以有好几个远程仓库,通常有些仓库对你只读,有些则可以读写。与他人协作涉及管理远程仓库以及根据需要推送或拉取数据。管理远程仓库包括了解如何添加远程仓库、移除无效的远程仓库、管理不同的远程分支并定义它们是否被跟踪等等。

2.4.1 查看远程仓库

如果想查看已经配置好的远程仓库服务器,可以运行git remote命令,会罗列出你配好的每一个远程服务器的简写。(如果你已经克隆了自己的远程仓库,那么至少应该能看到origin,这是Git给你克隆的仓库服务器的默认名字)

$ git remote
origin
复制代码

你也可以指定选项 -v,会显示需要读写远程仓库使用的Git保存的简写与其对应的 URL。

$ git remote -v
origin  https://github.com/XWayne/TapeView.git (fetch)
origin  https://github.com/XWayne/TapeView.git (push)
复制代码

2.4.2 添加远程仓库

运行 git remote add <shortname> <url>添加一个新的远程Git仓库,同时指定一个你可以轻松引用的简写:

$ git remote add pb  https://github.com/XWayne/TapeView.git
$ git remote -v
origin  https://github.com/XWayne/TapeView.git (fetch)
origin  https://github.com/XWayne/TapeView.git (push)
pb      https://github.com/XWayne/TapeView.git (fetch)
pb      https://github.com/XWayne/TapeView.git (push)

复制代码

现在你可以在命令行中使用字符串 pb 来代替整个 URL。

2.4.3 从远程仓库中抓取与拉取

从远程仓库中获得数据,可以执行: $ git fetch [remote-name]

这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。

注意: git fetch命令会将数据拉取到你的本地仓库——它并不会自动合并或修改你当前的工作。当准备好时你必须手动将其合并入你的工作。

如果你有一个分支设置为跟踪一个远程分支,可以使用git pull命令来自动的抓取然后合并远程分支到当前分支。

TODO:更多细节,后期分支再讲

2.4.4 推送到远程仓库

当你想分享你的项目时,必须将其推送到上游。 这个命令很简单:git push [remote-name] [branch-name]。 当你想要将 master 分支推送到 origin 服务器时(再次说明,克隆时通常会自动帮你设置好那两个名字),那么运行这个命令就可以将你所做的备份到服务器:

TODO:更多细节,后期分支再讲

2.4.5 查看某个远程仓库

如果想要查看某一个远程仓库的更多信息,可以使用 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)
复制代码

它同样会列出远程仓库的 URL 与跟踪分支的信息。 这些信息非常有用,它告诉你正处于 master 分支,并且如果运行 git pull,就会抓取所有的远程引用,然后将远程 master 分支合并到本地 master 分支。 它也会列出拉取到的所有远程引用。

2.4.6 远程仓库的移除与重命名

如果想要重命名引用的名字可以运行git remote rename 去修改一个远程仓库的简写名。 例如,想要将 pb 重命名为 paul,可以用 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
复制代码

注: 更多远程仓库的使用,参考:远程仓库的使用

2.5 打标签

Git 可以给历史中的某一个提交打上标签,以示重要。比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等)。

2.5.1 列出标签

在 Git 中列出已有的标签是非常简单直观的。 只需要输入 git tag以字母顺序列出标签):

$ git tag
v0.1
v1.3
复制代码

你也可以使用特定的模式查找标签。 例如,Git 自身的源代码仓库包含标签的数量超过 500 个。 如果只对 1.8.5 系列感兴趣,可以运行:

$ git tag -l 'v1.8.5*'
v1.8.5
v1.8.5-rc0
v1.8.5-rc1
v1.8.5-rc2
v1.8.5-rc3
v1.8.5.1
v1.8.5.2
v1.8.5.3
v1.8.5.4
v1.8.5.5
复制代码

2.5.2 创建标签

Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。

一个轻量标签很像一个不会改变的分支——它只是一个特定提交的引用。

然而,附注标签是存储在 Git 数据库中的一个完整对象。 它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。 通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。

(1)附注标签

在 Git 中创建一个附注标签是很简单的。 最简单的方式是当你在运行 tag 命令时指定 -a 选项:

$ git tag -a v1.4 -m "my version 1.4"
$ git tag
v0.1
v1.3
v1.4
复制代码

通过使用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
复制代码
(2)轻量标签

轻量标签本质上是将提交校验和存储到一个文件中——没有保存任何其他信息。只需要提供标签名字:

$ git tag v1.99
$ git tag
v0.1
v1.3
v1.4
v1.5
v1.99
复制代码

这时,如果在标签上运行 git show,你不会看到额外的标签信息。 命令只会显示出提交信息:

$ git show v1.99
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number
复制代码

2.5.3 后期打标签

你也可以对过去的提交打标签。 假设提交历史是这样的:

$ git log --pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment'
a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support
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
964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
复制代码

现在,假设在 v1.2 时你忘记给项目打标签,也就是在 “updated rakefile” 提交。 你可以在之后补上标签。 要在那个提交上打标签,你需要在命令的末尾指定提交的校验和(或部分校验和):

$ git tag -a v1.2 9fceb02 -m "version 1.2"
复制代码

2.5.4 共享标签

默认情况下,git push命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样——你可以运行git push origin [tagname]

$ git push origin v1.0
复制代码

可在github项目的“release”中看到:

如果想要一次性推送很多标签,也可以使用带有--tags选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里。

$ git push origin --tags
复制代码

现在,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。

2.5.5 删除标签

要删除掉你本地仓库上的标签,可以使用命令 git tag -d <tagname>。例如,可以使用下面的命令删除掉一个轻量级标签:

$ git tag -d v1.0
Deleted tag 'v1.0' (was 4e28e8f)
复制代码

应该注意的是上述命令并不会从任何远程仓库中移除这个标签,你必须使用 git push <remote> :refs/tags/<tagname>来更新你的远程仓库:

$ git push origin :refs/tags/v1.0
To https://github.com/XWayne/TapeView.git
 - [deleted]         v1.0

复制代码

2.6 Git别名

Git 并不会在你输入部分命令时自动推断出你想要的命令。 如果不想每次都输入完整的 Git 命令,可以通过 git config 文件来轻松地为每一个命令设置一个别名。

$ git config --global alias.ci commit
复制代码

这意味着,当要输入 git commit 时,只需要输入 git ci

注: 更多别名的使用,参考:Git别名

备注

该系列文章笔记整理自《Pro Git》,非原创。

关注下面的标签,发现更多相似文章
评论