最近边学边练JAVA,写了几个小代码,想把这些都保存起来,以后有机会了,还可以进一步的维护和升级。于是想到了GitHub。前面发现,使用整理文章的方式让我更好的掌握Markdown文本编辑器的使用以及Snipaste工具的使用,乘着这个机会,把Git和GitHub也好好的整理一下。
版本控制系统
版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理。 版本控制主要包括:检入检出控制、分支和合并、历史记录。 版本控制系统(VCS),就是完成以上功能的系统。
本地版本控制系统
在本地硬盘中记录文件变更信息。比较流行的如:RCS。其工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。 缺点显而易见,只能一个人使用,不方便团队开发;换一台电脑进行开发也不方便。如果电脑损坏,所有文件变更信息全会丢失,不安全。
集中化管理的版本控制系统
Centralized Version Control Systems,简称 CVCS,在一个单一的集中管理的服务器中保存所有文件变更信息。协同工作人员都通过客户端连接到这台服务器。诸如:CVS、Subversion 以及 Perforce 等。 缺点是单点故障问题,如果服务器宕机,那都无法提交更新,无法协同工作。如果服务器硬盘损坏,那整个文件变更信息会全部丢失。
分布式版本控制系统
Distributed Version Control System,简称 DVCS,如:Git、Mercurial、Bazaar 以及 Darcs 等。 客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。 更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。比如Git,中国的用户访问Github可能较慢,可以与Github远程仓库同步的同时,还使用Gitee的远程仓库。 同籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。 还可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。
在了解版本控制系统的基本功能和分类后,我们着重来了解一下Git。
Git 简介
Git 是一个开源的分布式版本控制系统。
Git是什么?
直接记录快照,而非差异比较
Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方式。 从概念上来说,其它大部分系统以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce、Bazaar 等等) 将它们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异 (它们通常称作 基于差异(delta-based) 的版本控制)。 Git 更像是把数据看作是对小型文件系统的一系列快照。 在 Git 中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。 为了效率,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。 这是 Git 与几乎所有其它版本控制系统的重要区别。 因此 Git 重新考虑了以前每一代版本控制系统延续下来的诸多方面。 Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。 比如Git 分支管理时,这种方式对待数据能获得很大益处。
近乎所有操作都是本地执行
在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。 而相比集中式版本控制系统,Git 在这方面速度会有极大的提升感觉,因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。 这意味着你在离线或者没有 VPN 时,几乎可以进行任何操作。
保证完整性
Git 中所有的数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。
Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
Git 一般只添加数据
执行的 Git 操作,几乎只往 Git 数据库中 添加 数据。 你很难使用 Git 从数据库中删除数据,也就是说 Git 几乎不会执行任何可能导致文件不可恢复的操作。
未提交更新时有可能丢失或弄乱修改的内容。但是一旦你提交快照到 Git 中, 就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
三种状态
文件可能处于其中之一: 已提交(committed)、已修改(modified) 和 已暂存(staged)。
- 已修改表示修改了文件,但还没保存到数据库中。
- 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
- 已提交表示数据已经安全地保存在本地数据库中。
三个阶段
Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。
-
工作区,又叫工作目录,是对项目的某个版本或分支独立提取出来的内容。简单理解,就是你要编写或修改的代码所处的目录。这个目录存放的是项目的某个版本,或某个分支,你在切换版本或分支时,这个目录就存放从 Git 仓库的压缩数据库中提取出来某个版本或分支的文件,放在这个目录中供你使用或修改。使用的命令是Git checkout -
暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。添加要提交的文件列表的命令是Git add <file> -
Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。创建这个本地仓库是初如化命令Git init 。会在要创建仓库的目录下自动创建一个隐藏的.git目录。 要将暂存区中的文件提交到Git仓库中会使用提交命令Git commit 。
所以,基本的Git工作流程就是:
- 在工作区中修改文件。
- 将你想要下次提交的更改选择性地暂存(
Git add ),这样只会将更改的部分添加到暂存区。 - 提交更新(
Git commit ),找到暂存区的文件,将快照永久性存储到 Git 目录。
如果 Git 目录中保存着特定版本的文件,就属于已提交 状态。 如果文件已修改并放入暂存区,就属于已暂存状态。 如果自上次检出后,作了修改但还没有放到暂存区域,就是已修改状态。
远程仓库
除了上面提到的三个阶段之外,其实还有一个远程仓库(Remote Repository),例如著名的Github,就可以提供远程仓库,实现代码的托管。除此之外,国内还有Gitee提供远程仓库服务,当然,你也可以同时同步这两个远程仓库。
本地仓库可以选择与远程仓库同步,但也可以脱离远程仓库几乎完整使用所有Git相关功能。
这四者之间的关系如下图。
Git 下载
Git Windows平台上安装
完成安装之后,就可以使用命令行的 git 工具(已经自带了 ssh 客户端)了, 另外还有一个图形界面的 Git 项目管理工具。 在开始菜单里找到"Git"->“Git Bash”,会弹出 Git 命令窗口,你可以在该窗口进行 Git 操作。
也可以在工作目录下在右键菜单中找到Git Bash来打开。 除此这外,Git还有一个GUI的工具。但命令行模式可以执行Git的所有命令,但GUI只实现了所有功能的一个子集,以降低操作难度。 不同的人常会安装使用不同的GUI工具,但所有人,一定会用命令行工具。
GitHub Desktop
另一个简单的方法是安装 GitHub Desktop。 该安装程序包含图形化和命令行版本的 Git。 它也能支持 Powershell,提供了稳定的凭证缓存和健全的换行设置。
可以在 GitHub for Windows 网站下载。
初次运行Git前,需要配置Git的环境。每台计算机上只需要配置一次,程序升级时会保留配置信息。 你可以在任何时候再次通过运行命令来修改它们。
Git 配置
Git 提供了一个叫做 git config 的工具,专门用来配置或读取相应的工作环境变量。 这些环境变量,决定了 Git 在各个环节的具体工作方式和行为,还有外观等。这些变量可以存放在以下三个不同的地方:
- /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。 (由于它是系统配置文件,因此你需要管理员或超级用户权限来修改它。)
- ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。
- 当前项目的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。你可以传递 --local 选项让 Git 强制读写此文件,默认情况下用的就是它。
每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。
在 Windows 系统中,Git 会查找 $HOME 目录下(一般情况下是 C:\Users\$USER )的 .gitconfig 文件。 此外,Git 还会尝试找寻 /etc/gitconfig 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。
可以通过以下命令查看所有的配置以及它们所在的文件:
git config --list --show-origin
配置用户信息
安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改 配置个人的用户名称和电子邮件地址:
$ git config --global user.name “java old man” $ git config --global user.email javaoldman@163.com
如果用了 --global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。
如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 --global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。
查看配置信息
$ git config --list
有时候会看到重复的变量名,那就说明它们来自不同的配置文件(比如 /etc/gitconfig 和 ~/.gitconfig),不过最终 Git 实际采用的是最后一个。
也可以直接查阅某个环境变量的设定,只要把特定的名字跟在后面即可
$ git config user.name
文本编辑器
可以配置默认文本编辑器了,当 Git 需要你输入信息时会调用它。 如果未配置,Git 会使用操作系统默认的文本编辑器。 在 Windows 系统上,如果你想要使用别的文本编辑器,那么必须指定可执行文件的完整路径。 它可能随你的编辑器的打包方式而不同。
对于 Notepad++,一个流行的代码编辑器来说,你可能想要使用 32 位的版本, 因为在本书编写时 64 位的版本尚不支持所有的插件。 如果你在使用 32 位的 Windows 系统,或在 64 位系统上使用 64 位的编辑器,那么你需要输入如下命令:
git config --global core.editor “‘C:/Program Files/Notepad++/notepad++.exe’ -multiInst -notabbar -nosession -noPlugin”
获得帮助
若你使用 Git 时需要获取帮助,有三种等价的方法可以找到 Git 命令的综合手册(manpage):
$ git help <verb> $ git <verb> --help $ man git-<verb>
例如:要想获得 git config 命令的手册,执行
$ git help config
如果你不需要全面的手册,只需要可用选项的快速参考,那么可以用 -h 选项获得更简明的 “help” 输出:
git add -h
得到运行结果如下:
usage: git add [<options>] [--] <pathspec>...
-n, --dry-run dry run
-v, --verbose be verbose
-i, --interactive interactive picking
-p, --patch select hunks interactively
-e, --edit edit current diff and apply
-f, --force allow adding otherwise ignored files
-u, --update update tracked files
--renormalize renormalize EOL of tracked files (implies -u)
-N, --intent-to-add record only the fact that the path will be added later
-A, --all add changes from all tracked and untracked files
--ignore-removal ignore paths removed in the working tree (same as --no-all)
--refresh don't add, only refresh the index
--ignore-errors just skip files which cannot be added because of errors
--ignore-missing check if - even missing - files are ignored in dry run
--sparse allow updating entries outside of the sparse-checkout cone
--chmod (+|-)x override the executable bit of the listed files
--pathspec-from-file <file>
read pathspec from file
--pathspec-file-nul with --pathspec-from-file, pathspec elements are separated with NUL character
Git 工作流程
一般工作流程如下:
- 克隆 Git 资源作为工作目录(或将尚未进行版本控制的本地目录转换为 Git 仓库)。
- 在克隆的资源上添加或修改文件。
- 如果其他人修改了,你可以更新资源。
- 在提交前查看修改。
- 提交修改。
- 在修改完成后,如果发现错误,可以撤回提交并再次修改并提交。
所以,工作流程的第一步是获取Git仓库
获取Git仓库
通常有两种获取 Git 项目仓库的方式:
- 将尚未进行版本控制的本地目录转换为 Git 仓库;
- 从其它服务器 克隆 一个已存在的 Git 仓库。
两种方式都会在你的本地机器上得到一个工作就绪的 Git 仓库。
在已存在目录中初始化仓库
如果你有一个尚未进行版本控制的项目目录,想要用 Git 来控制它,那么首先需要进入该项目目录中。 例如在 Windows 上:
$ cd /c/user/my_project
也可以直接在项目目录下,在鼠标右键上打开Git Bash,也可以直接进入该项目目录。 之后执行:
git init
该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。
如果在一个已存在文件的文件夹(而非空文件夹)中进行版本控制,你应该开始追踪这些文件并进行初始提交。 可以通过 git add 命令来指定所需的文件来进行追踪,然后执行 git commit :
git add *.c git add LICENSE git commit -m ‘initial project version’
现在,就已经得到了一个存在被追踪文件与初始提交的 Git 仓库。
克隆现有的仓库
如果你想获得一份已经存在了的 Git 仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone 命令。
Git 克隆的是该 Git 仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件。 当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。 事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库 (虽然可能会丢失某些服务器端的钩子(hook)设置,但是所有版本的数据仍在。
克隆仓库的命令是 git clone <url> 。 比如,要克隆 Git 的链接库 libgit2,可以用下面的命令:
git clone https://github.com/libgit2/libgit2
url中,第一个libgit2是github的用户名,第二个libgit2是这个用户名下的一个远程仓库名称。 如果是使用Gitee做为远程仓库,也同样的道理。
Git 支持多种数据传输协议。 上面的例子使用的是 https:// 协议,不过也可以使用 git:// 协议或者使用 SSH 传输协议,比如 user@server:path/to/repo.git 。如上例也可以是:
git clone git@github.com:libgit2/libgit2.git
这会在当前目录下创建一个名为 “libgit2” 的目录,并在这个目录下初始化一个 .git 文件夹, 从远程仓库拉取下所有数据放入 .git 文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的 libgit2 文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。 如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以通过额外的参数指定新的目录名:
git clone https://github.com/libgit2/libgit2 mylibgit
这会执行与上一条命令相同的操作,但目标目录名变为了 mylibgit。
注意:使用clone获取Git仓库时,不需要使用Git init命令来初始化仓库。因为clone命令会自动初始化这个仓库。
记录每次的更新到仓库
现在电脑中有了一个项目的Git仓库,并从这个仓库中检出(checkout)了所有文件的工作副本在工作目录中。 我们通常会对这些文件进行修改,当完成一个阶段的目标,想要记录下来,就可以将它提交到仓库中。
注意工作目录下的每一个文件都不外乎这两种状态:已跟踪 或 未跟踪。
已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后, 它们的状态可能是未修改,已修改或已放入暂存区。简而言之,已跟踪的文件就是 Git 已经知道的文件。
工作目录中除已跟踪文件外的其它所有文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有被放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为 Git 刚刚检出了它们, 而你尚未编辑过它们。
编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复。
检查当前文件状态
可以用 git status 命令查看哪些文件处于什么状态。 如果在克隆仓库后立即使用此命令,会看到类似这样的输出:
git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。 此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件,否则 Git 会在这里列出来。 最后,该命令还显示了当前所在分支,并告诉你这个分支同远程服务器上对应的分支没有偏离(文件是同步的)。 现在,分支名是“main”,这是默认的分支名。 注意由于github将默认分支名称由master改为main,所以,这里是main,而不是有些文章中提的master。
现在,我们可以在项目目录下创建一个README文件(可以在windows下创建,也可以直接使用命令行创建,这里使用命令行创建)
echo “My Project” > README
这是一条命令行命令,意思是输出字符串"My Project",但通过">"这个管道符号,将输出的字符串送到README这个文件中。
再次使用git status ,可以看到一个新的未跟踪文件:
git status
On branch main
Your branch is up to date with 'origin/main'.
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 下面。 未跟踪的文件意味着 Git 在之前的快照(提交)中没有这些文件; Git 不会自动将之纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文件”。 这样的处理让你不必担心将生成的二进制文件或其它不想被跟踪的文件包含进来。 不过现在的例子中,我们确实想要跟踪管理 README 这个文件。
跟踪新文件
使用命令 git add 开始跟踪一个文件。 所以,要跟踪 README 文件,运行:
git add README
此时再运行 git status 命令,会看到 README 文件已被跟踪,并处于暂存状态:
git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
只要在 Changes to be committed 这行下面的,就说明是已暂存状态。 如果此时提交,那么该文件在你运行 git add 时的版本将被留存在后续的历史记录中。
之前我们使用 git init 后就运行了 git add \<files\> 命令,开始跟踪当前目录下的文件。 git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件。
暂存已修改的文件
现在修改一个已被跟踪的文件(clone下来的默认全是被跟踪的,自己新建的,可以使用git add命令跟踪并自动暂存)。比如修改一个名为README.md 的clone下来的,已被跟踪的文件。然后使用git status 来查看
git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
文件 README.md 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。 要暂存这次更新,需要运行 git add 命令。 这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。 将这个命令理解为“精确地将内容添加到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。
现在运行 git add 将README.md 放到暂存区,然后再看看 git status 的输出:
git add README.md git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
modified: README.md
现在两个文件都已暂存,下次提交时就会一并记录到仓库。 假设此时,你想要在 README.md 里再加条注释。 重新编辑存盘后,准备好提交。 不过且慢,再运行 git status 看看:
git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
modified: README.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
现在 README.md 文件同时出现在暂存区和非暂存区。 实际上 Git 只不过暂存了你运行 git add 命令时的版本。 如果你现在提交,README.md 的版本是你最后一次运行 git add 命令时的那个版本,而不是你运行 git commit 时,在工作目录中的当前版本。 所以,运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来:
git add README.md git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
modified: README.md
状态简览
git status 命令的输出十分详细,但其用语有些繁琐。 Git 有一个选项可以帮你缩短状态命令的输出,这样可以以简洁的方式查看更改。 如果你使用 git status -s 命令或 git status --short 命令,你将得到一种格式更为紧凑的输出。
git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
新添加的未跟踪文件前面有 ?? 标记,新添加到暂存区中的文件前面有 A 标记,修改过的文件前面有 M 标记。 输出中有两栏,左栏指明了暂存区的状态,右栏指明了工作区的状态。 例如,上面的状态报告显示: README 文件在工作区已修改但尚未暂存,而 lib/simplegit.rb 文件已修改且已暂存。 Rakefile 文件已修,暂存后又作了修改,因此该文件的修改中既有已暂存的部分,又有未暂存的部分。
查看已暂存和未暂存的修改
想知道具体修改了什么地方,可以用 git diff 命令。 通常可能会用它来回答这两个问题:当前做的哪些更新尚未暂存? 有哪些更新已暂存并准备好下次提交? 虽然 git status 已经通过在相应栏下列出文件名的方式回答了这个问题,但 git diff 能通过文件补丁的格式更加具体地显示哪些行发生了改变。
假如再次修改README.md 文件,但不缓存,运行git status命令可以看到。
git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
modified: README.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff:
git diff
可以看到,比较后的结果是添加了一行“add this line"这行信息。
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。
若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff --staged 命令。 这条命令将比对已暂存文件与最后一次提交的文件差异:
git diff --staged
请注意,git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件,运行 git diff 后却什么也没有,就是这个原因。
提交更新
现在的暂存区已经准备就绪,可以提交了。 在此之前,请务必确认还有什么已修改或新建的文件还没有 git add 过, 否则提交的时候不会记录这些尚未暂存的变化。 这些已修改但未暂存的文件只会保留在本地磁盘。 所以,每次准备提交前,先用 git status 看下,你所需要的文件是不是都已暂存起来了, 然后再运行提交命令 git commit:
git commit
这样会启动你选择的文本编辑器来输入提交说明。 可以看到,默认的提交消息包含最后一次运行 git status 的输出,放在注释行里,另外开头还有一个空行,供你输入提交说明。 你完全可以去掉这些注释行,不过留着也没关系,多少能帮你回想起这次更新的内容有哪些。 退出编辑器时,Git 会丢弃注释行,用你输入的提交说明生成一次提交。
另外,你也可以在 commit 命令后添加 -m 选项,将提交信息与命令放在同一行,如下所示:
git commit -m “the first commit”
[main e65b856b3] the first commit
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 README
现在你已经创建了第一个提交! 可以看到,提交后它会告诉你,当前是在哪个分支(main)提交的,本次提交的完整 SHA-1 校验和是什么(e65b856b3),以及在本次提交中,有多少文件修订过,多少行添加和删改过。 请记住,提交时记录的是放在暂存区域的快照。 任何还未暂存文件的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。
跳过使用暂存区域
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。 Git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:
$ 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: CONTRIBUTING.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m ‘added new benchmarks’
[master 83e38c7] added new benchmarks
1 file changed, 5 insertions(+), 0 deletions(-)
看到了吗?提交之前不再需要 git add 文件“CONTRIBUTING.md”了。 这是因为 -a 选项使本次提交包含了所有修改过的文件。 这很方便,但是要小心,有时这个选项会将不需要的文件添加到提交中。
移除文件
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。 可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
如果只是简单地从工作目录中手工删除文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是 未暂存清单)看到:
$ rm PROJECTS.md $ git status
On branch master
Your branch is up-to-date with 'origin/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: PROJECTS.md
no changes added to commit (use "git add" and/or "git commit -a")
然后再运行 git rm 记录此次移除文件的操作:
git rm PROJECTS.md
rm 'PROJECTS.md'
git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: PROJECTS.md
下一次提交时,该文件就不再纳入版本管理了。 如果要删除之前修改过或已经放到暂存区的文件,则必须使用强制删除选项 -f(译注:即 force 的首字母)。 这是一种安全特性,用于防止误删尚未添加到快照的数据,这样的数据不能被 Git 恢复。
git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式。比如:
git rm log/\*.log
注意到星号 * 之前的反斜杠 \, 因为 Git 有它自己的文件模式扩展匹配方式,所以我们不用 shell 来帮忙展开。 此命令删除 log/ 目录下扩展名为 .log 的所有文件。 类似的比如:
git rm \*~
该命令会删除所有名字以 ~ 结尾的文件。
删除跟踪
想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆 .a 这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 --cached 选项:
git rm --cached README
移动文件
Git 并不显式跟踪文件移动操作。 如果在 Git 中重命名了某个文件,仓库中存储的元数据并不会体现出这是一次改名操作。 不过 Git 非常聪明,它会推断出究竟发生了什么 既然如此,当你看到 Git 的 mv 命令时一定会困惑不已。 要在 Git 中对文件改名,可以这么做:
git mv file_from file_to
它会恰如预期般正常工作。 实际上,即便此时查看状态信息,也会明白无误地看到关于重命名操作的说明:
git mv README.md README git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
其实,运行 git mv 就相当于运行了下面三条命令:
mv README.md README git rm README.md git add README
如此分开操作,Git 也会意识到这是一次重命名,所以不管何种方式结果都一样。 两者唯一的区别在于,git mv 是一条命令而非三条命令,直接使用 git mv 方便得多。 不过在使用其他工具重命名文件时,记得在提交前 git rm 删除旧文件名,再 git add 添加新文件名。
|