Git – 快速指南
Git – 快速指南
Git – 基本概念
版本控制系统
版本控制系统 (VCS)是一种软件,可帮助软件开发人员协同工作并维护其工作的完整历史记录。
下面列出的是 VCS 的功能 –
- 允许开发人员同时工作。
- 不允许覆盖彼此的更改。
- 维护每个版本的历史记录。
以下是 VCS 的类型 –
- 集中版本控制系统 (CVCS)。
- 分布式/去中心化版本控制系统 (DVCS)。
在本章中,我们将只关注分布式版本控制系统,尤其是 Git。Git 属于分布式版本控制系统。
分布式版本控制系统
集中式版本控制系统 (CVCS) 使用中央服务器来存储所有文件并支持团队协作。但是 CVCS 的主要缺点是它的单点故障,即中央服务器的故障。不幸的是,如果中央服务器停机一小时,那么在那一小时内,根本没有人可以进行协作。即使在最坏的情况下,如果中央服务器的磁盘损坏并且没有进行适当的备份,那么您将丢失项目的整个历史记录。在这里,分布式版本控制系统 (DVCS) 应运而生。
DVCS 客户端不仅检查目录的最新快照,而且还完全镜像存储库。如果服务器出现故障,则可以将来自任何客户端的存储库复制回服务器以恢复它。每次结帐都是存储库的完整备份。Git 不依赖中央服务器,这就是为什么您可以在离线时执行许多操作。您可以在离线时提交更改、创建分支、查看日志以及执行其他操作。您只需要网络连接来发布您的更改并进行最新的更改。
Git的优势
免费和开源
Git 是在 GPL 的开源许可下发布的。它可以通过互联网免费获得。您可以使用 Git 管理房地产项目,而无需支付一分钱。由于它是开源的,您可以下载其源代码并根据您的要求进行更改。
又快又小
由于大多数操作都是在本地执行的,因此在速度方面具有巨大的优势。Git 不依赖中央服务器;这就是为什么每次操作都不需要与远程服务器交互的原因。Git 的核心部分是用 C 编写的,这避免了与其他高级语言相关的运行时开销。虽然 Git 镜像了整个存储库,但客户端的数据量很小。这说明了 Git 在客户端压缩和存储数据的效率。
隐式备份
当数据有多个副本时,丢失数据的机会非常少。任何客户端上存在的数据都会镜像存储库,因此可以在发生崩溃或磁盘损坏的情况下使用它。
安全
Git 使用一种称为安全散列函数 (SHA1) 的通用加密散列函数来命名和识别其数据库中的对象。每个文件和提交都会在检出时通过其校验和进行校验和检索。这意味着,在不了解 Git 的情况下,不可能更改 Git 数据库中的文件、日期和提交消息以及任何其他数据。
不需要强大的硬件
在 CVCS 的情况下,中央服务器需要足够强大以服务整个团队的请求。对于较小的团队,这不是问题,但随着团队规模的增长,服务器的硬件限制可能成为性能瓶颈。在 DVCS 的情况下,开发人员不与服务器交互,除非他们需要推送或拉取更改。所有繁重的工作都发生在客户端,因此服务器硬件确实可以非常简单。
更容易分支
CVCS 使用廉价的复制机制,如果我们创建一个新分支,它会将所有代码复制到新分支,因此既耗时又效率低下。此外,在 CVCS 中删除和合并分支既复杂又耗时。但是使用 Git 进行分支管理非常简单。创建、删除和合并分支只需几秒钟。
DVCS 术语
本地存储库
每个 VCS 工具都提供一个私人工作场所作为工作副本。开发人员在他们的私人工作场所进行更改,提交后,这些更改成为存储库的一部分。Git 更进一步,为他们提供整个存储库的私有副本。用户可以使用此存储库执行许多操作,例如添加文件、删除文件、重命名文件、移动文件、提交更改等等。
工作目录和暂存区或索引
工作目录是签出文件的地方。在其他 CVCS 中,开发人员通常会进行修改并将其更改直接提交到存储库。但是 Git 使用了不同的策略。Git 不会跟踪每个修改过的文件。每当您提交操作时,Git 都会查找暂存区中存在的文件。只有那些出现在暂存区的文件才会被考虑提交,而不是所有修改过的文件。
让我们看看 Git 的基本工作流程。
步骤 1 – 您从工作目录修改文件。
步骤 2 – 您将这些文件添加到暂存区。
第 3 步– 您执行从暂存区移动文件的提交操作。推送操作后,它将更改永久存储到 Git 存储库中。
假设您修改了两个文件,即“sort.c”和“search.c”,并且您希望每个操作有两个不同的提交。您可以在暂存区添加一个文件并提交。第一次提交后,对另一个文件重复相同的过程。
# First commit [bash]$ git add sort.c # adds file to the staging area [bash]$ git commit –m “Added sort operation” # Second commit [bash]$ git add search.c # adds file to the staging area [bash]$ git commit –m “Added search operation”
斑点
斑点代表乙inary大号ARGE鄂JECT。文件的每个版本都由 blob 表示。blob 包含文件数据,但不包含有关该文件的任何元数据。它是一个二进制文件,在 Git 数据库中,它被命名为该文件的 SHA1 哈希值。在 Git 中,文件不按名称寻址。一切都是内容寻址的。
树木
树是一个对象,它代表一个目录。它包含 blob 以及其他子目录。树是一个二进制文件,它存储对 blob 和树的引用,它们也被命名为树对象的SHA1哈希。
提交
提交保存存储库的当前状态。提交也由SHA1哈希命名。您可以将提交对象视为链表的节点。每个提交对象都有一个指向父提交对象的指针。从给定的提交,您可以通过查看父指针来回溯以查看提交的历史记录。如果一个提交有多个父提交,那么该特定提交是通过合并两个分支创建的。
分行
分支用于创建另一条开发线。默认情况下,Git 有一个 master 分支,它与 Subversion 中的主干相同。通常,会创建一个分支来处理新功能。功能完成后,它会与主分支合并,然后我们删除该分支。每个分支都由 HEAD 引用,它指向分支中的最新提交。每当您进行提交时,HEAD 都会更新为最新的提交。
标签
标签为存储库中的特定版本分配一个有意义的名称。标签与分支非常相似,但不同的是标签是不可变的。这意味着,标签是一个分支,没有人打算修改。为特定提交创建标签后,即使您创建新提交,也不会更新。通常,开发人员会为产品发布创建标签。
克隆
克隆操作会创建存储库的实例。克隆操作不仅检查工作副本,而且还镜像完整的存储库。用户可以使用这个本地存储库执行许多操作。唯一涉及网络的时间是同步存储库实例时。
拉
拉操作将更改从远程存储库实例复制到本地存储库实例。拉操作用于两个存储库实例之间的同步。这与 Subversion 中的更新操作相同。
推
推送操作将更改从本地存储库实例复制到远程存储库实例。这用于将更改永久存储到 Git 存储库中。这与 Subversion 中的提交操作相同。
头
HEAD 是一个指针,它总是指向分支中的最新提交。每当您进行提交时,HEAD 都会更新为最新的提交。分支的负责人存储在.git/refs/heads/目录中。
[CentOS]$ ls -1 .git/refs/heads/ master [CentOS]$ cat .git/refs/heads/master 570837e7d58fa4bccd86cb575d884502188b0c49
修订
Revision 表示源代码的版本。Git 中的修订由提交表示。这些提交由SHA1安全哈希标识。
网址
URL 表示 Git 存储库的位置。Git URL 存储在配置文件中。
[tom@CentOS tom_repo]$ pwd /home/tom/tom_repo [tom@CentOS tom_repo]$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] url = [email protected]:project.git fetch = +refs/heads/*:refs/remotes/origin/*
Git – 环境设置
在您可以使用 Git 之前,您必须安装并进行一些基本的配置更改。以下是在 Ubuntu 和 Centos Linux 上安装 Git 客户端的步骤。
安装 Git 客户端
如果您使用基于 Debian 的 GNU/Linux 发行版,那么apt-get命令将完成所需的工作。
[ubuntu ~]$ sudo apt-get install git-core [sudo] password for ubuntu: [ubuntu ~]$ git --version git version 1.8.1.2
如果您使用的是基于 RPM 的 GNU/Linux 发行版,请使用给定的yum命令。
[CentOS ~]$ su - Password: [CentOS ~]# yum -y install git-core [CentOS ~]# git --version git version 1.7.1
自定义 Git 环境
Git 提供了 git config 工具,它允许您设置配置变量。Git 将所有全局配置存储在.gitconfig文件中,该文件位于您的主目录中。要将这些配置值设置为全局,请添加–global选项,如果省略–global选项,则您的配置特定于当前 Git 存储库。
您还可以设置系统范围的配置。Git 将这些值存储在/etc/gitconfig文件中,该文件包含系统上每个用户和存储库的配置。要设置这些值,您必须具有 root 权限并使用–system选项。
当上面的代码被编译和执行时,它会产生以下结果 –
设置用户名
Git 每次提交都会使用此信息。
[jerry@CentOS project]$ git config --global user.name "Jerry Mouse"
设置电子邮件 ID
Git 每次提交都会使用此信息。
[jerry@CentOS project]$ git config --global user.email "[email protected]"
避免合并提交以进行拉取
您从远程存储库中提取最新更改,如果这些更改不同,则默认情况下 Git 创建合并提交。我们可以通过以下设置来避免这种情况。
jerry@CentOS project]$ git config --global branch.autosetuprebase always
颜色高亮
以下命令在控制台中为 Git 启用颜色突出显示。
[jerry@CentOS project]$ git config --global color.ui true [jerry@CentOS project]$ git config --global color.status auto [jerry@CentOS project]$ git config --global color.branch auto
设置默认编辑器
默认情况下,Git 使用系统默认编辑器,该编辑器取自 VISUAL 或 EDITOR 环境变量。我们可以使用 git config 配置一个不同的。
[jerry@CentOS project]$ git config --global core.editor vim
设置默认合并工具
Git 不提供用于将冲突更改集成到工作树中的默认合并工具。我们可以通过启用以下设置来设置默认合并工具。
[jerry@CentOS project]$ git config --global merge.tool vimdiff
列出 Git 设置
要验证本地存储库的 Git 设置,请使用git config –list命令,如下所示。
[jerry@CentOS ~]$ git config --list
上述命令将产生以下结果。
user.name=Jerry Mouse [email protected] push.default=nothing branch.autosetuprebase=always color.ui=true color.status=auto color.branch=auto core.editor=vim merge.tool=vimdiff
Git – 生命周期
在本章中,我们将讨论 Git 的生命周期。在后面的章节中,我们将介绍每个操作的 Git 命令。
一般工作流程如下 –
-
您将 Git 存储库克隆为工作副本。
-
您可以通过添加/编辑文件来修改工作副本。
-
如有必要,您还可以通过其他开发人员的更改来更新工作副本。
-
您在提交之前查看更改。
-
您提交更改。如果一切正常,那么您将更改推送到存储库。
-
提交后,如果您发现有问题,则更正上次提交并将更改推送到存储库。
下面显示的是工作流程的图示。
Git – 创建操作
在本章中,我们将看到如何创建远程 Git 存储库;从现在开始,我们将把它称为 Git 服务器。我们需要一个 Git 服务器来允许团队协作。
创建新用户
# add new group [root@CentOS ~]# groupadd dev # add new user [root@CentOS ~]# useradd -G devs -d /home/gituser -m -s /bin/bash gituser # change password [root@CentOS ~]# passwd gituser
上述命令将产生以下结果。
Changing password for user gituser. New password: Retype new password: passwd: all authentication token updated successfully.
创建裸仓库
让我们使用init命令后跟–bare选项来初始化一个新的存储库。它在没有工作目录的情况下初始化存储库。按照惯例,裸仓库必须命名为.git。
[gituser@CentOS ~]$ pwd /home/gituser [gituser@CentOS ~]$ mkdir project.git [gituser@CentOS ~]$ cd project.git/ [gituser@CentOS project.git]$ ls [gituser@CentOS project.git]$ git --bare init Initialized empty Git repository in /home/gituser-m/project.git/ [gituser@CentOS project.git]$ ls branches config description HEAD hooks info objects refs
生成公共/私人 RSA 密钥对
让我们来看看配置 Git 服务器的过程,ssh-keygen实用程序会生成公钥/私钥 RSA 密钥对,我们将使用它来进行用户身份验证。
打开终端并输入以下命令,然后为每个输入按回车键。成功完成后,它将在主目录中创建一个.ssh目录。
tom@CentOS ~]$ pwd /home/tom [tom@CentOS ~]$ ssh-keygen
上述命令将产生以下结果。
Generating public/private rsa key pair. Enter file in which to save the key (/home/tom/.ssh/id_rsa): Press Enter Only Created directory '/home/tom/.ssh'. Enter passphrase (empty for no passphrase): ---------------> Press Enter Only Enter same passphrase again: ------------------------------> Press Enter Only Your identification has been saved in /home/tom/.ssh/id_rsa. Your public key has been saved in /home/tom/.ssh/id_rsa.pub. The key fingerprint is: df:93:8c:a1:b8:b7:67:69:3a:1f:65:e8:0e:e9:25:a1 tom@CentOS The key's randomart image is: +--[ RSA 2048]----+ | | | | | | | . | | Soo | | o*B. | | E = *.= | | oo==. . | | ..+Oo | +-----------------+
ssh-keygen生成了两个密钥,第一个是私有的(即 id_rsa),第二个是公共的(即 id_rsa.pub)。
注意:切勿与他人共享您的私钥。
将密钥添加到 authorized_keys
假设有两个开发人员在从事一个项目,即 Tom 和 Jerry。两个用户都生成了公钥。让我们看看如何使用这些密钥进行身份验证。
Tom 使用ssh-copy-id命令将他的公钥添加到服务器,如下所示 –
[tom@CentOS ~]$ pwd /home/tom [tom@CentOS ~]$ ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
上述命令将产生以下结果。
[email protected]'s password: Now try logging into the machine, with "ssh '[email protected]'", and check in: .ssh/authorized_keys to make sure we haven't added extra keys that you weren't expecting.
同样,Jerry 使用 ssh-copy-id 命令将他的公钥添加到服务器。
[jerry@CentOS ~]$ pwd /home/jerry [jerry@CentOS ~]$ ssh-copy-id -i ~/.ssh/id_rsa [email protected]
上述命令将产生以下结果。
[email protected]'s password: Now try logging into the machine, with "ssh '[email protected]'", and check in: .ssh/authorized_keys to make sure we haven't added extra keys that you weren't expecting.
将更改推送到存储库
我们在服务器上创建了一个裸存储库,并允许两个用户访问。从现在开始,Tom 和 Jerry 可以通过将其添加为远程存储库来将其更改推送到存储库。
每次从.git/config文件读取配置时,Git init 命令都会创建.git目录来存储有关存储库的元数据。
Tom 创建一个新目录,添加 README 文件,并将他的更改作为初始提交提交。提交后,他通过运行git log命令验证提交消息。
[tom@CentOS ~]$ pwd /home/tom [tom@CentOS ~]$ mkdir tom_repo [tom@CentOS ~]$ cd tom_repo/ [tom@CentOS tom_repo]$ git init Initialized empty Git repository in /home/tom/tom_repo/.git/ [tom@CentOS tom_repo]$ echo 'TODO: Add contents for README' > README [tom@CentOS tom_repo]$ git status -s ?? README [tom@CentOS tom_repo]$ git add . [tom@CentOS tom_repo]$ git status -s A README [tom@CentOS tom_repo]$ git commit -m 'Initial commit'
上述命令将产生以下结果。
[master (root-commit) 19ae206] Initial commit 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 README
Tom 通过执行 git log 命令检查日志消息。
[tom@CentOS tom_repo]$ git log
上述命令将产生以下结果。
commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
Tom 将他的更改提交到本地存储库。现在,是时候将更改推送到远程存储库了。但在此之前,我们必须将存储库添加为远程,这是一次性操作。在此之后,他可以安全地将更改推送到远程存储库。
注意– 默认情况下,Git 仅推送到匹配的分支:对于本地端存在的每个分支,如果远程端已存在同名分支,则会更新远程端。在我们的教程中,每次我们将更改推送到原始主分支时,请根据您的要求使用适当的分支名称。
[tom@CentOS tom_repo]$ git remote add origin [email protected]:project.git [tom@CentOS tom_repo]$ git push origin master
上述命令将产生以下结果。
Counting objects: 3, done. Writing objects: 100% (3/3), 242 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To [email protected]:project.git * [new branch] master −> master
现在,更改已成功提交到远程存储库。
Git – 克隆操作
我们在 Git 服务器上有一个裸仓库,Tom 也推送了他的第一个版本。现在,Jerry 可以查看他的更改。克隆操作创建远程存储库的实例。
Jerry 在他的主目录中创建一个新目录并执行克隆操作。
[jerry@CentOS ~]$ mkdir jerry_repo [jerry@CentOS ~]$ cd jerry_repo/ [jerry@CentOS jerry_repo]$ git clone [email protected]:project.git
上述命令将产生以下结果。
Initialized empty Git repository in /home/jerry/jerry_repo/project/.git/ remote: Counting objects: 3, done. Receiving objects: 100% (3/3), 241 bytes, done. remote: Total 3 (delta 0), reused 0 (delta 0)
Jerry 将目录更改为新的本地存储库并列出其目录内容。
[jerry@CentOS jerry_repo]$ cd project/ [jerry@CentOS jerry_repo]$ ls README
Git – 执行更改
Jerry 克隆了存储库并决定实现基本的字符串操作。所以他创建了 string.c 文件。添加内容后,string.c 将如下所示 –
#include <stdio.h> int my_strlen(char *s) { char *p = s; while (*p) ++p; return (p - s); } int main(void) { int i; char *s[] = { "Git tutorials", "Tutorials Point" }; for (i = 0; i < 2; ++i) printf("string lenght of %s = %d\n", s[i], my_strlen(s[i])); return 0; }
他编译并测试了他的代码,一切正常。现在,他可以安全地将这些更改添加到存储库中。
git add 操作将文件添加到暂存区。
[jerry@CentOS project]$ git status -s ?? string ?? string.c [jerry@CentOS project]$ git add string.c
Git 在文件名前显示一个问号。显然,这些文件不是 Git 的一部分,这就是为什么 Git 不知道如何处理这些文件。这就是为什么 Git 在文件名前显示一个问号。
Jerry 已将文件添加到存储区,git status 命令将显示暂存区中存在的文件。
[jerry@CentOS project]$ git status -s A string.c ?? string
为了提交更改,他使用了 git commit 命令后跟 -m 选项。如果我们省略 -m 选项。Git 将打开一个文本编辑器,我们可以在其中编写多行提交消息。
[jerry@CentOS project]$ git commit -m 'Implemented my_strlen function'
上述命令将产生以下结果 –
[master cbe1249] Implemented my_strlen function 1 files changed, 24 insertions(+), 0 deletions(-) create mode 100644 string.c
commit查看日志详情后,运行git log命令。它将显示所有提交的信息及其提交 ID、提交作者、提交日期和提交的SHA-1哈希值。
[jerry@CentOS project]$ git log
上述命令将产生以下结果 –
commit cbe1249b140dad24b2c35b15cc7e26a6f02d2277 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Implemented my_strlen function commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
Git – 查看更改
查看提交细节后,Jerry 意识到字符串长度不能为负数,这就是他决定更改 my_strlen 函数的返回类型的原因。
Jerry 使用git log命令查看日志详细信息。
[jerry@CentOS project]$ git log
上述命令将产生以下结果。
commit cbe1249b140dad24b2c35b15cc7e26a6f02d2277 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Implemented my_strlen function
Jerry 使用git show命令查看提交详细信息。git show 命令将SHA-1提交 ID 作为参数。
[jerry@CentOS project]$ git show cbe1249b140dad24b2c35b15cc7e26a6f02d2277
上述命令将产生以下结果 –
commit cbe1249b140dad24b2c35b15cc7e26a6f02d2277 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Implemented my_strlen function diff --git a/string.c b/string.c new file mode 100644 index 0000000..187afb9 --- /dev/null +++ b/string.c @@ -0,0 +1,24 @@ +#include <stdio.h> + +int my_strlen(char *s) +{ + char *p = s; + + while (*p) + ++p; + return (p -s ); + } +
他将函数的返回类型从 int 更改为 size_t。测试代码后,他通过运行git diff命令来检查他的更改。
[jerry@CentOS project]$ git diff
上述命令将产生以下结果 –
diff --git a/string.c b/string.c index 187afb9..7da2992 100644 --- a/string.c +++ b/string.c @@ -1,6 +1,6 @@ #include <stdio.h> -int my_strlen(char *s) +size_t my_strlen(char *s) { char *p = s; @@ -18,7 +18,7 @@ int main(void) }; for (i = 0; i < 2; ++i) { - printf("string lenght of %s = %d\n", s[i], my_strlen(s[i])); + printf("string lenght of %s = %lu\n", s[i], my_strlen(s[i])); return 0; }
Git diff在新添加的行前显示‘+’号,删除的行显示‘-‘ 号。
Git – 提交更改
Jerry 已经提交了更改,他想更正他的最后一次提交。在这种情况下,git amend操作会有所帮助。修改操作更改最后一次提交,包括您的提交消息;它会创建一个新的提交 ID。
在修改操作之前,他会检查提交日志。
[jerry@CentOS project]$ git log
上述命令将产生以下结果。
commit cbe1249b140dad24b2c35b15cc7e26a6f02d2277 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Implemented my_strlen function commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
Jerry 使用 –amend 操作提交新更改并查看提交日志。
[jerry@CentOS project]$ git status -s M string.c ?? string [jerry@CentOS project]$ git add string.c [jerry@CentOS project]$ git status -s M string.c ?? string [jerry@CentOS project]$ git commit --amend -m 'Changed return type of my_strlen to size_t' [master d1e19d3] Changed return type of my_strlen to size_t 1 files changed, 24 insertions(+), 0 deletions(-) create mode 100644 string.c
现在,git log 将显示带有新提交 ID 的新提交消息 –
[jerry@CentOS project]$ git log
上述命令将产生以下结果。
commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Changed return type of my_strlen to size_t commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
Git – 推送操作
Jerry 使用修改操作修改了他的最后一次提交,他准备推送更改。Push 操作将数据永久存储到 Git 存储库。成功推送操作后,其他开发者可以看到 Jerry 的变化。
他执行 git log 命令以查看提交详细信息。
[jerry@CentOS project]$ git log
上述命令将产生以下结果:
commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Changed return type of my_strlen to size_t
在push操作之前,他想查看他的更改,所以他使用git show命令查看他的更改。
[jerry@CentOS project]$ git show d1e19d316224cddc437e3ed34ec3c931ad803958
上述命令将产生以下结果:
commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Changed return type of my_strlen to size_t diff --git a/string.c b/string.c new file mode 100644 index 0000000..7da2992 --- /dev/null +++ b/string.c @@ -0,0 +1,24 @@ +#include <stdio.h> + +size_t my_strlen(char *s) + { + char *p = s; + + while (*p) + ++p; + return (p -s ); + } + +int main(void) + { + int i; + char *s[] = { + "Git tutorials", + "Tutorials Point" + }; + + + for (i = 0; i < 2; ++i) printf("string lenght of %s = %lu\n", s[i], my_strlen(s[i])); + + return 0; + }
Jerry 对他的改变很满意,他已经准备好推动他的改变。
[jerry@CentOS project]$ git push origin master
上述命令将产生以下结果:
Counting objects: 4, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 517 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To [email protected]:project.git 19ae206..d1e19d3 master −> master
Jerry 的修改已经成功推送到仓库;现在其他开发人员可以通过执行克隆或更新操作来查看他的更改。
Git – 更新操作
修改现有功能
Tom 执行克隆操作并找到一个新文件 string.c。他想知道谁将此文件添加到存储库中以及出于什么目的,因此,他执行了git log命令。
[tom@CentOS ~]$ git clone [email protected]:project.git
上述命令将产生以下结果 –
Initialized empty Git repository in /home/tom/project/.git/ remote: Counting objects: 6, done. remote: Compressing objects: 100% (4/4), done. Receiving objects: 100% (6/6), 726 bytes, done. remote: Total 6 (delta 0), reused 0 (delta 0)
克隆操作将在当前工作目录中创建一个新目录。他将目录更改为新创建的目录并执行git log命令。
[tom@CentOS ~]$ cd project/ [tom@CentOS project]$ git log
上述命令将产生以下结果 –
commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Changed return type of my_strlen to size_t commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
观察日志后,他意识到文件string.c是Jerry添加的,用于实现基本的字符串操作。他对 Jerry 的代码很好奇。于是他在文本编辑器中打开 string.c 并立即发现了一个错误。在 my_strlen 函数中,Jerry 没有使用常量指针。因此,他决定修改 Jerry 的代码。修改后,代码如下 –
[tom@CentOS project]$ git diff
上述命令将产生以下结果 –
diff --git a/string.c b/string.c index 7da2992..32489eb 100644 --- a/string.c +++ b/string.c @@ -1,8 +1,8 @@ #include <stdio.h> -size_t my_strlen(char *s) +size_t my_strlen(const char *s) { - char *p = s; + const char *p = s; while (*p) ++p; }
测试后,他提交更改。
[tom@CentOS project]$ git status -s M string.c ?? string [tom@CentOS project]$ git add string.c [tom@CentOS project]$ git commit -m 'Changed char pointer to const char pointer' [master cea2c00] Changed char pointer to const char pointer 1 files changed, 2 insertions(+), 2 deletions(-) [tom@CentOS project]$ git log
上述命令将产生以下结果 –
commit cea2c000f53ba99508c5959e3e12fff493b Author: Tom Cat <[email protected]> Date: Wed Sep 11 08:32:07 2013 +0530 Changed char pointer to const char pointer commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Changed return type of my_strlen to size_t commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
Tom 使用 git push 命令推送他的更改。
[tom@CentOS project]$ git push origin master
上述命令将产生以下结果 –
Counting objects: 5, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 336 bytes, done. Total 3 (delta 1), reused 0 (delta 0) To [email protected]:project.git d1e19d3..cea2c00 master −> master
添加新功能
同时,Jerry 决定实现字符串比较功能。于是他修改了string.c。修改后,文件如下所示 –
[jerry@CentOS project]$ git diff
上述命令将产生以下结果 –
index 7da2992..bc864ed 100644 --- a/string.c +++ b/string.c 30Git Tutorials @@ -9,9 +9,20 @@ size_t my_strlen(char *s) return (p -s ); } +char *my_strcpy(char *t, char *s) + { + char *p = t; + + while (*t++ = *s++) + ; + + return p; + } + int main(void) { int i; + char p1[32]; char *s[] = { "Git tutorials", "Tutorials Point" @@ -20,5 +31,7 @@ int main(void) for (i = 0; i < 2; ++i) printf("string lenght of %s = %lu\n", s[i], my_strlen(s[i])); + printf("%s\n", my_strcpy(p1, "Hello, World !!!")); + return 0; } }
在测试之后,他准备好推动他的改变。
[jerry@CentOS project]$ git status -s M string.c ?? string [jerry@CentOS project]$ git add string.c [jerry@CentOS project]$ git commit -m "Added my_strcpy function" [master e944e5a] Added my_strcpy function 1 files changed, 13 insertions(+), 0 deletions(-)
在推送操作之前,他通过查看日志消息来验证提交。
[jerry@CentOS project]$ git log
上述命令将产生以下结果 –
commit e944e5aab74b26e7447d3281b225309e4e59efcd Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:41:42 2013 +0530 Added my_strcpy function commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Changed return type of my_strlen to size_t commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
Jerry 对这些变化很满意,他想推动他的变化。
[jerry@CentOS project]$ git push origin master
上述命令将产生以下结果 –
To [email protected]:project.git ! [rejected] master −> master (non-fast-forward) error: failed to push some refs to '[email protected]:project.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
但是 Git 不允许 Jerry 推送他的更改。因为 Git 发现远程仓库和 Jerry 的本地仓库不同步。因此,他可能会丢失项目的历史记录。为了避免这种混乱,Git 失败了这个操作。现在,Jerry 必须先更新本地存储库,然后才能推送自己的更改。
获取最新更改
Jerry 执行 git pull 命令以将他的本地存储库与远程存储库同步。
[jerry@CentOS project]$ git pull
上述命令将产生以下结果 –
remote: Counting objects: 5, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From git.server.com:project d1e19d3..cea2c00 master −> origin/master First, rewinding head to replay your work on top of it... Applying: Added my_strcpy function
拉操作后,Jerry 检查日志消息并找到 Tom 提交 ID 为cea2c000f53ba99508c5959e3e12fff493ba6f69的提交的详细信息
[jerry@CentOS project]$ git log
上述命令将产生以下结果 –
commit e86f0621c2a3f68190bba633a9fe6c57c94f8e4f Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:41:42 2013 +0530 Added my_strcpy function commit cea2c000f53ba99508c5959e3e12fff493ba6f69 Author: Tom Cat <[email protected]> Date: Wed Sep 11 08:32:07 2013 +0530 Changed char pointer to const char pointer commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 08:05:26 2013 +0530 Changed return type of my_strlen to size_t commit 19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <[email protected]> Date: Wed Sep 11 07:32:56 2013 +0530 Initial commit
现在,Jerry 的本地存储库与远程存储库完全同步。所以他可以安全地推送他的更改。
[jerry@CentOS project]$ git push origin master
上述命令将产生以下结果 –
Counting objects: 5, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 455 bytes, done. Total 3 (delta 1), reused 0 (delta 0) To [email protected]:project.git cea2c00..e86f062 master −> master
Git – 存储操作
假设您正在为您的产品实现一个新功能。您的代码正在进行中,突然出现客户升级。因此,您必须将新功能的工作搁置几个小时。您不能提交部分代码,也不能丢弃您的更改。因此,您需要一些临时空间,您可以在其中存储部分更改,然后再提交。
在 Git 中,存储操作会获取您修改过的跟踪文件、暂存更改,并将它们保存在一堆未完成的更改中,您可以随时重新应用这些更改。
[jerry@CentOS project]$ git status -s M string.c ?? string
现在,您想为客户升级切换分支机构,但您不想提交您一直在做的事情;所以你会隐藏更改。要将新的存储推送到您的堆栈中,请运行git stash命令。
[jerry@CentOS project]$ git stash Saved working directory and index state WIP on master: e86f062 Added my_strcpy function HEAD is now at e86f062 Added my_strcpy function
现在,您的工作目录是干净的,所有更改都保存在堆栈中。让我们用git status命令验证一下。
[jerry@CentOS project]$ git status -s ?? string
现在您可以安全地切换分支并在其他地方工作。我们可以使用git stash list命令查看隐藏的更改列表。
[jerry@CentOS project]$ git stash list stash@{0}: WIP on master: e86f062 Added my_strcpy function
假设您已经解决了客户升级问题并且您又回到新功能上寻找您的半成品代码,只需执行git stash pop命令,从堆栈中删除更改并将它们放在当前工作目录中。
[jerry@CentOS project]$ git status -s ?? string [jerry@CentOS project]$ git stash pop
上述命令将产生以下结果:
# On branch master # Changed but not updated: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: string.c # # Untracked files: # (use "git add ..." to include in what will be committed) # # string no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (36f79dfedae4ac20e2e8558830154bd6315e72d4) [jerry@CentOS project]$ git status -s M string.c ?? string
Git – 移动操作
顾名思义,移动操作将目录或文件从一个位置移动到另一个位置。Tom 决定将源代码移动到src目录中。修改后的目录结构将如下所示 –
[tom@CentOS project]$ pwd /home/tom/project [tom@CentOS project]$ ls README string string.c [tom@CentOS project]$ mkdir src [tom@CentOS project]$ git mv string.c src/ [tom@CentOS project]$ git status -s R string.c −> src/string.c ?? string
为了使这些更改永久化,我们必须将修改后的目录结构推送到远程存储库,以便其他开发人员可以看到这一点。
[tom@CentOS project]$ git commit -m "Modified directory structure" [master 7d9ea97] Modified directory structure 1 files changed, 0 insertions(+), 0 deletions(-) rename string.c => src/string.c (100%) [tom@CentOS project]$ git push origin master Counting objects: 4, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 320 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To [email protected]:project.git e86f062..7d9ea97 master −> master
在 Jerry 的本地仓库中,在 pull 操作之前,会显示旧的目录结构。
[jerry@CentOS project]$ pwd /home/jerry/jerry_repo/project [jerry@CentOS project]$ ls README string string.c
但是在pull操作之后,目录结构会得到更新。现在,Jerry 可以看到src目录和该目录中的文件。
[jerry@CentOS project]$ git pull remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From git.server.com:project e86f062..7d9ea97 master −> origin/master First, rewinding head to replay your work on top of it... Fast-forwarded master to 7d9ea97683da90bcdb87c28ec9b4f64160673c8a. [jerry@CentOS project]$ ls README src string [jerry@CentOS project]$ ls src/ string.c
Git – 重命名操作
到目前为止,Tom 和 Jerry 都在使用手动命令来编译他们的项目。现在,Jerry 决定为他们的项目创建 Makefile,并为文件“string.c”命名。
[jerry@CentOS project]$ pwd /home/jerry/jerry_repo/project [jerry@CentOS project]$ ls README src [jerry@CentOS project]$ cd src/ [jerry@CentOS src]$ git add Makefile [jerry@CentOS src]$ git mv string.c string_operations.c [jerry@CentOS src]$ git status -s A Makefile R string.c −> string_operations.c
Git在文件名前显示R表示文件已重命名。
对于提交操作,Jerry 使用了 -a 标志,这使得 git commit 自动检测修改过的文件。
[jerry@CentOS src]$ git commit -a -m 'Added Makefile and renamed strings.c to string_operations.c ' [master 94f7b26] Added Makefile and renamed strings.c to string_operations.c 1 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/Makefile rename src/{string.c => string_operations.c} (100%)
提交后,他将更改推送到存储库。
[jerry@CentOS src]$ git push origin master
上述命令将产生以下结果 –
Counting objects: 6, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 396 bytes, done. Total 4 (delta 0), reused 0 (delta 0) To [email protected]:project.git 7d9ea97..94f7b26 master −> master
现在,其他开发人员可以通过更新他们的本地存储库来查看这些修改。
Git – 删除操作
Tom 更新他的本地存储库并在src目录中找到编译的二进制文件。查看提交消息后,他意识到编译的二进制文件是由 Jerry 添加的。
[tom@CentOS src]$ pwd /home/tom/project/src [tom@CentOS src]$ ls Makefile string_operations string_operations.c [tom@CentOS src]$ file string_operations string_operations: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped [tom@CentOS src]$ git log commit 29af9d45947dc044e33d69b9141d8d2dad37cc62 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 10:16:25 2013 +0530 Added compiled binary
VCS 仅用于存储源代码而不是可执行的二进制文件。因此,Tom 决定从存储库中删除此文件。为了进一步操作,他使用了git rm命令。
[tom@CentOS src]$ ls Makefile string_operations string_operations.c [tom@CentOS src]$ git rm string_operations rm 'src/string_operations' [tom@CentOS src]$ git commit -a -m "Removed executable binary" [master 5776472] Removed executable binary 1 files changed, 0 insertions(+), 0 deletions(-) delete mode 100755 src/string_operations
提交后,他将更改推送到存储库。
[tom@CentOS src]$ git push origin master
上述命令将产生以下结果。
Counting objects: 5, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 310 bytes, done. Total 3 (delta 1), reused 0 (delta 0) To [email protected]:project.git 29af9d4..5776472 master −> master
Git – 修复错误
人非圣贤孰能。所以每个 VCS 都提供了一个功能来修复错误,直到某个点。Git 提供了一个功能,我们可以使用它来撤消对本地存储库所做的修改。
假设用户不小心对其本地存储库进行了一些更改,然后想要撤消这些更改。在这种情况下,还原操作起着重要的作用。
还原未提交的更改
让我们假设 Jerry 不小心修改了他本地存储库中的文件。但他想撤消他的修改。为了处理这种情况,我们可以使用git checkout命令。我们可以使用这个命令来恢复文件的内容。
[jerry@CentOS src]$ pwd /home/jerry/jerry_repo/project/src [jerry@CentOS src]$ git status -s M string_operations.c [jerry@CentOS src]$ git checkout string_operations.c [jerry@CentOS src]$ git status –s
此外,我们可以使用git checkout命令从本地存储库中获取已删除的文件。让我们假设 Tom 从本地存储库中删除了一个文件,并且我们想要恢复这个文件。我们可以通过使用相同的命令来实现这一点。
[tom@CentOS src]$ pwd /home/tom/top_repo/project/src [tom@CentOS src]$ ls -1 Makefile string_operations.c [tom@CentOS src]$ rm string_operations.c [tom@CentOS src]$ ls -1 Makefile [tom@CentOS src]$ git status -s D string_operations.c
Git在文件名前显示字母D。这表明该文件已从本地存储库中删除。
[tom@CentOS src]$ git checkout string_operations.c [tom@CentOS src]$ ls -1 Makefile string_operations.c [tom@CentOS src]$ git status -s
注意– 我们可以在提交之前执行所有这些操作。
从暂存区删除更改
我们已经看到,当我们执行添加操作时,文件从本地存储库移动到声明区域。如果用户不小心修改了一个文件并将其添加到暂存区,他可以使用git checkout命令恢复他的更改。
在 Git 中,有一个 HEAD 指针始终指向最新的提交。如果要撤消暂存区的更改,则可以使用 git checkout 命令,但是使用 checkout 命令,您必须提供一个附加参数,即 HEAD 指针。附加提交指针参数指示 git checkout 命令重置工作树并删除暂存更改。
让我们假设 Tom 修改了他本地存储库中的一个文件。如果我们查看该文件的状态,会显示该文件已被修改但未添加到暂存区。
tom@CentOS src]$ pwd /home/tom/top_repo/project/src # Unmodified file [tom@CentOS src]$ git status -s # Modify file and view it’s status. [tom@CentOS src]$ git status -s M string_operations.c [tom@CentOS src]$ git add string_operations.c
Git 状态显示该文件存在于暂存区,现在使用 git checkout 命令将其还原并查看还原文件的状态。
[tom@CentOS src]$ git checkout HEAD -- string_operations.c [tom@CentOS src]$ git status -s
使用 Git 重置移动 HEAD 指针
在进行少量更改后,您可以决定删除这些更改。Git reset 命令用于重置或恢复更改。我们可以执行三种不同类型的重置操作。
下图显示了 Git reset 命令的图示。
柔软的
每个分支都有一个 HEAD 指针,指向最新的提交。如果我们使用带有 –soft 选项后跟提交 ID 的 Git reset 命令,那么它将仅重置 HEAD 指针而不会破坏任何内容。
.git/refs/heads/master文件存储 HEAD 指针的提交 ID。我们可以使用git log -1命令来验证它。
[jerry@CentOS project]$ cat .git/refs/heads/master 577647211ed44fe2ae479427a0668a4f12ed71a1
现在,查看最新的提交 ID,它将与上面的提交 ID 匹配。
[jerry@CentOS project]$ git log -2
上述命令将产生以下结果。
commit 577647211ed44fe2ae479427a0668a4f12ed71a1 Author: Tom Cat <[email protected]> Date: Wed Sep 11 10:21:20 2013 +0530 Removed executable binary commit 29af9d45947dc044e33d69b9141d8d2dad37cc62 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 10:16:25 2013 +0530 Added compiled binary
让我们重置 HEAD 指针。
[jerry@CentOS project]$ git reset --soft HEAD~
现在,我们只需将 HEAD 指针复位一个位置。让我们检查.git/refs/heads/master 文件的内容。
[jerry@CentOS project]$ cat .git/refs/heads/master 29af9d45947dc044e33d69b9141d8d2dad37cc62
文件中的提交 ID 已更改,现在通过查看提交消息进行验证。
jerry@CentOS project]$ git log -2
上述命令将产生以下结果。
commit 29af9d45947dc044e33d69b9141d8d2dad37cc62 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 10:16:25 2013 +0530 Added compiled binary commit 94f7b26005f856f1a1b733ad438e97a0cd509c1a Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 10:08:01 2013 +0530 Added Makefile and renamed strings.c to string_operations.c
混合的
带有 –mixed 选项的 Git 重置会从暂存区恢复那些尚未提交的更改。它仅从暂存区恢复更改。对文件工作副本所做的实际更改不受影响。默认的 Git reset 相当于 git reset –mixed。
难的
如果在 Git reset 命令中使用 –hard 选项,它将清除暂存区;它将 HEAD 指针重置为特定提交 ID 的最新提交,并删除本地文件更改。
让我们检查提交 ID。
[jerry@CentOS src]$ pwd /home/jerry/jerry_repo/project/src [jerry@CentOS src]$ git log -1
上述命令将产生以下结果。
commit 577647211ed44fe2ae479427a0668a4f12ed71a1 Author: Tom Cat <[email protected]> Date: Wed Sep 11 10:21:20 2013 +0530 Removed executable binary
Jerry 通过在文件开头添加单行注释来修改文件。
[jerry@CentOS src]$ head -2 string_operations.c /* This line be removed by git reset operation */ #include <stdio.h>
他通过使用 git status 命令验证了它。
[jerry@CentOS src]$ git status -s M string_operations.c
Jerry 将修改后的文件添加到暂存区并使用 git status 命令进行验证。
[jerry@CentOS src]$ git add string_operations.c [jerry@CentOS src]$ git status
上述命令将产生以下结果。
# On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: string_operations.c #
Git 状态显示该文件存在于暂存区。现在,使用 –hard 选项重置 HEAD。
[jerry@CentOS src]$ git reset --hard 577647211ed44fe2ae479427a0668a4f12ed71a1 HEAD is now at 5776472 Removed executable binary
Git reset 命令成功,这将从暂存区恢复文件并删除对文件所做的任何本地更改。
[jerry@CentOS src]$ git status -s
Git 状态显示文件已从暂存区恢复。
[jerry@CentOS src]$ head -2 string_operations.c #include <stdio.h>
head 命令还显示重置操作也删除了本地更改。
Git – 标签操作
标签操作允许为存储库中的特定版本提供有意义的名称。假设 Tom 和 Jerry 决定标记他们的项目代码,以便他们以后可以轻松访问它。
创建标签
让我们使用git tag命令标记当前的 HEAD 。Tom 使用 -a 选项提供标签名称,并使用 -m 选项提供标签消息。
tom@CentOS project]$ pwd /home/tom/top_repo/project [tom@CentOS project]$ git tag -a 'Release_1_0' -m 'Tagged basic string operation code' HEAD
如果要标记特定提交,请使用适当的 COMMIT ID 而不是 HEAD 指针。Tom 使用以下命令将标签推送到远程存储库。
[tom@CentOS project]$ git push origin tag Release_1_0
上述命令将产生以下结果 –
Counting objects: 1, done. Writing objects: 100% (1/1), 183 bytes, done. Total 1 (delta 0), reused 0 (delta 0) To [email protected]:project.git * [new tag] Release_1_0 −> Release_1_0
查看标签
汤姆创建了标签。现在,Jerry 可以使用带有 –l 选项的 Git tag 命令查看所有可用标签。
[jerry@CentOS src]$ pwd /home/jerry/jerry_repo/project/src [jerry@CentOS src]$ git pull remote: Counting objects: 1, done. remote: Total 1 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (1/1), done. From git.server.com:project * [new tag] Release_1_0 −> Release_1_0 Current branch master is up to date. [jerry@CentOS src]$ git tag -l Release_1_0
Jerry 使用 Git show 命令后跟其标签名称来查看有关标签的更多详细信息。
[jerry@CentOS src]$ git show Release_1_0
上述命令将产生以下结果 –
tag Release_1_0 Tagger: Tom Cat <[email protected]> Date: Wed Sep 11 13:45:54 2013 +0530 Tagged basic string operation code commit 577647211ed44fe2ae479427a0668a4f12ed71a1 Author: Tom Cat <[email protected]> Date: Wed Sep 11 10:21:20 2013 +0530 Removed executable binary diff --git a/src/string_operations b/src/string_operations deleted file mode 100755 index 654004b..0000000 Binary files a/src/string_operations and /dev/null differ
删除标签
Tom 使用以下命令从本地和远程存储库中删除标签。
[tom@CentOS project]$ git tag Release_1_0 [tom@CentOS project]$ git tag -d Release_1_0 Deleted tag 'Release_1_0' (was 0f81ff4) # Remove tag from remote repository. [tom@CentOS project]$ git push origin :Release_1_0 To [email protected]:project.git - [deleted] Release_1_0
Git – 补丁操作
Patch 是一个文本文件,其内容类似于 Git diff,但除了代码之外,它还包含有关提交的元数据;例如,提交 ID、日期、提交消息等。我们可以从提交中创建补丁,其他人可以将它们应用到他们的存储库中。
Jerry 为他的项目实现了 strcat 函数。Jerry 可以创建他的代码路径并将其发送给 Tom。然后,他可以将收到的补丁应用到他的代码中。
Jerry 使用 Git format-patch命令为最新提交创建补丁。如果要为特定提交创建补丁,请使用COMMIT_ID和 format-patch 命令。
[jerry@CentOS project]$ pwd /home/jerry/jerry_repo/project/src [jerry@CentOS src]$ git status -s M string_operations.c ?? string_operations [jerry@CentOS src]$ git add string_operations.c [jerry@CentOS src]$ git commit -m "Added my_strcat function" [master b4c7f09] Added my_strcat function 1 files changed, 13 insertions(+), 0 deletions(-) [jerry@CentOS src]$ git format-patch -1 0001-Added-my_strcat-function.patch
上面的命令在当前工作目录中创建.patch文件。Tom 可以使用这个补丁来修改他的文件。Git 提供了两个命令来分别应用补丁git am和git apply。git apply修改本地文件而不创建提交,而git am修改文件并创建提交。
要应用补丁并创建提交,请使用以下命令 –
[tom@CentOS src]$ pwd /home/tom/top_repo/project/src [tom@CentOS src]$ git diff [tom@CentOS src]$ git status –s [tom@CentOS src]$ git apply 0001-Added-my_strcat-function.patch [tom@CentOS src]$ git status -s M string_operations.c ?? 0001-Added-my_strcat-function.patch
补丁应用成功,现在我们可以使用git diff命令查看修改。
[tom@CentOS src]$ git diff
上述命令将产生以下结果 –
diff --git a/src/string_operations.c b/src/string_operations.c index 8ab7f42..f282fcf 100644 --- a/src/string_operations.c +++ b/src/string_operations.c @@ -1,5 +1,16 @@ #include <stdio.h> +char *my_strcat(char *t, char *s) diff --git a/src/string_operations.c b/src/string_operations.c index 8ab7f42..f282fcf 100644 --- a/src/string_operations.c +++ b/src/string_operations.c @@ -1,5 +1,16 @@ #include <stdio.h> +char *my_strcat(char *t, char *s) + { + char *p = t; + + + while (*p) ++p; + while (*p++ = *s++) + ; + return t; + } + size_t my_strlen(const char *s) { const char *p = s; @@ -23,6 +34,7 @@ int main(void) {
Git – 管理分支
分支操作允许创建另一条开发线。我们可以使用此操作将开发过程分为两个不同的方向。例如,我们发布了一个 6.0 版本的产品,我们可能想要创建一个分支,以便 7.0 功能的开发可以与 6.0 错误修复分开。
创建分支
Tom 使用 git branch <branch name> 命令创建一个新分支。我们可以从现有分支创建一个新分支。我们可以使用特定的提交或标签作为起点。如果未提供任何特定的提交 ID,则将以 HEAD 为起点创建分支。
[jerry@CentOS src]$ git branch new_branch [jerry@CentOS src]$ git branch * master new_branch
创建了一个新分支;Tom 使用 git branch 命令列出可用的分支。Git 在当前签出的分支之前显示一个星号。
创建分支操作的图示如下所示 –
在分支之间切换
Jerry 使用 git checkout 命令在分支之间切换。
[jerry@CentOS src]$ git checkout new_branch Switched to branch 'new_branch' [jerry@CentOS src]$ git branch master * new_branch
创建和切换分支的快捷方式
在上面的例子中,我们分别使用了两个命令来创建和切换分支。Git 为checkout 命令提供了-b选项;此操作创建一个新分支并立即切换到新分支。
[jerry@CentOS src]$ git checkout -b test_branch Switched to a new branch 'test_branch' [jerry@CentOS src]$ git branch master new_branch * test_branch
删除一个分支
可以通过在 git branch 命令中提供 –D 选项来删除分支。但是在删除现有分支之前,切换到另一个分支。
Jerry 目前在test_branch 上,他想删除该分支。所以他切换分支并删除分支,如下所示。
[jerry@CentOS src]$ git branch master new_branch * test_branch [jerry@CentOS src]$ git checkout master Switched to branch 'master' [jerry@CentOS src]$ git branch -D test_branch Deleted branch test_branch (was 5776472).
现在,Git 将只显示两个分支。
[jerry@CentOS src]$ git branch * master new_branch
重命名分支
Jerry 决定在他的字符串操作项目中添加对宽字符的支持。他已经创建了一个新分支,但是分支名称不合适。因此,他通过使用-m选项后跟旧分支名称和新分支名称来更改分支名称。
[jerry@CentOS src]$ git branch * master new_branch [jerry@CentOS src]$ git branch -m new_branch wchar_support
现在, git branch 命令将显示新的分支名称。
[jerry@CentOS src]$ git branch * master wchar_support
合并两个分支
Jerry 实现了一个函数来返回宽字符串的字符串长度。新代码将显示如下 –
[jerry@CentOS src]$ git branch master * wchar_support [jerry@CentOS src]$ pwd /home/jerry/jerry_repo/project/src [jerry@CentOS src]$ git diff
上面的命令产生以下结果 –
t a/src/string_operations.c b/src/string_operations.c index 8ab7f42..8fb4b00 100644 --- a/src/string_operations.c +++ b/src/string_operations.c @@ -1,4 +1,14 @@ #include <stdio.h> +#include <wchar.h> + +size_t w_strlen(const wchar_t *s) + { + const wchar_t *p = s; + + while (*p) + ++p; + return (p - s); + }
测试后,他提交并将他的更改推送到新分支。
[jerry@CentOS src]$ git status -s M string_operations.c ?? string_operations [jerry@CentOS src]$ git add string_operations.c [jerry@CentOS src]$ git commit -m 'Added w_strlen function to return string lenght of wchar_t string' [wchar_support 64192f9] Added w_strlen function to return string lenght of wchar_t string 1 files changed, 10 insertions(+), 0 deletions(-)
请注意,Jerry 正在将这些更改推送到新分支,这就是他使用分支名称wchar_support而不是master分支的原因。
[jerry@CentOS src]$ git push origin wchar_support <−−− Observer branch_name
上述命令将产生以下结果。
Counting objects: 7, done. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 507 bytes, done. Total 4 (delta 1), reused 0 (delta 0) To [email protected]:project.git * [new branch] wchar_support -> wchar_support
提交更改后,新分支将显示如下 –
Tom 很好奇 Jerry 在他的私有分支里做了什么,他检查了wchar_support分支的日志。
[tom@CentOS src]$ pwd /home/tom/top_repo/project/src [tom@CentOS src]$ git log origin/wchar_support -2
上述命令将产生以下结果。
commit 64192f91d7cc2bcdf3bf946dd33ece63b74184a3 Author: Jerry Mouse <[email protected]> Date: Wed Sep 11 16:10:06 2013 +0530 Added w_strlen function to return string lenght of wchar_t string commit 577647211ed44fe2ae479427a0668a4f12ed71a1 Author: Tom Cat <[email protected]> Date: Wed Sep 11 10:21:20 2013 +0530 Removed executable binary
通过查看提交消息,Tom 意识到 Jerry 为宽字符实现了 strlen 函数,并且他希望在 master 分支中具有相同的功能。他没有重新实现,而是决定通过将他的分支与主分支合并来获取 Jerry 的代码。
[tom@CentOS project]$ git branch * master [tom@CentOS project]$ pwd /home/tom/top_repo/project [tom@CentOS project]$ git merge origin/wchar_support Updating 5776472..64192f9 Fast-forward src/string_operations.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
合并操作后,主分支将出现如下 –
现在,分支wchar_support已与主分支合并。我们可以通过查看提交消息或查看对 string_operation.c 文件所做的修改来验证它。
[tom@CentOS project]$ cd src/ [tom@CentOS src]$ git log -1 commit 64192f91d7cc2bcdf3bf946dd33ece63b74184a3 Author: Jerry MouseDate: Wed Sep 11 16:10:06 2013 +0530 Added w_strlen function to return string lenght of wchar_t string [tom@CentOS src]$ head -12 string_operations.c
上述命令将产生以下结果。
#include <stdio.h> #include <wchar.h> size_t w_strlen(const wchar_t *s) { const wchar_t *p = s; while (*p) ++p; return (p - s); }
测试后,他将他的代码更改推送到主分支。
[tom@CentOS src]$ git push origin master Total 0 (delta 0), reused 0 (delta 0) To [email protected]:project.git 5776472..64192f9 master −> master
变基分支
Git rebase 命令是一个分支合并命令,但不同的是它修改了提交的顺序。
Git merge 命令尝试将来自其他分支的提交放在当前本地分支的 HEAD 之上。例如,你的本地分支提交了 A->B->C->D 并且合并分支提交了 A->B->X->Y,那么 git merge 会将当前本地分支转换为类似 A-> B->C->D->X->Y
Git rebase 命令试图找出当前本地分支和合并分支之间的共同祖先。然后通过修改当前本地分支中的提交顺序将提交推送到本地分支。例如,如果您的本地分支提交了 A->B->C->D 并且合并分支提交了 A->B->X->Y,那么 Git rebase 会将当前本地分支转换为类似 A- >B->X->Y->C->D。
当多个开发人员在单个远程存储库上工作时,您无法修改远程存储库中的提交顺序。在这种情况下,您可以使用 rebase 操作将本地提交置于远程存储库提交之上,并且您可以推送这些更改。
Git – 处理冲突
在 wchar_support 分支中执行更改
Jerry 正在wchar_support分支上工作。他更改了函数的名称,并在测试之后提交了他的更改。
[jerry@CentOS src]$ git branch master * wchar_support [jerry@CentOS src]$ git diff
上面的命令产生以下结果 –
diff --git a/src/string_operations.c b/src/string_operations.c index 8fb4b00..01ff4e0 100644 --- a/src/string_operations.c +++ b/src/string_operations.c @@ -1,7 +1,7 @@ #include <stdio.h> #include <wchar.h> -size_t w_strlen(const wchar_t *s) +size_t my_wstrlen(const wchar_t *s) { const wchar_t *p = s;
在验证代码后,他提交了他的更改。
[jerry@CentOS src]$ git status -s M string_operations.c [jerry@CentOS src]$ git add string_operations.c [jerry@CentOS src]$ git commit -m 'Changed function name' [wchar_support 3789fe8] Changed function name 1 files changed, 1 insertions(+), 1 deletions(-) [jerry@CentOS src]$ git push origin wchar_support
上述命令将产生以下结果 –
Counting objects: 7, done. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 409 bytes, done. Total 4 (delta 1), reused 0 (delta 0) To [email protected]:project.git 64192f9..3789fe8 wchar_support -> wchar_support
在主分支中执行更改
同时在 master 分支中,Tom 也更改了相同函数的名称,并将他的更改推送到 master 分支。
[tom@CentOS src]$ git branch * master [tom@CentOS src]$ git diff
上面的命令产生以下结果 –
diff --git a/src/string_operations.c b/src/string_operations.c index 8fb4b00..52bec84 100644 --- a/src/string_operations.c +++ b/src/string_operations.c @@ -1,7 +1,8 @@ #include <stdio.h> #include <wchar.h> -size_t w_strlen(const wchar_t *s) +/* wide character strlen fucntion */ +size_t my_wc_strlen(const wchar_t *s) { const wchar_t *p = s;
在验证 diff 之后,他提交了他的更改。
[tom@CentOS src]$ git status -s M string_operations.c [tom@CentOS src]$ git add string_operations.c [tom@CentOS src]$ git commit -m 'Changed function name from w_strlen to my_wc_strlen' [master ad4b530] Changed function name from w_strlen to my_wc_strlen 1 files changed, 2 insertions(+), 1 deletions(-) [tom@CentOS src]$ git push origin master
上述命令将产生以下结果 –
Counting objects: 7, done. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 470 bytes, done. Total 4 (delta 1), reused 0 (delta 0) To [email protected]:project.git 64192f9..ad4b530 master -> master
在wchar_support分支上,Jerry 为宽字符串实现了 strchr 函数。测试后,他提交并将他的更改推送到wchar_support分支。
[jerry@CentOS src]$ git branch master * wchar_support [jerry@CentOS src]$ git diff
上面的命令产生以下结果 –
diff --git a/src/string_operations.c b/src/string_operations.c index 01ff4e0..163a779 100644 --- a/src/string_operations.c +++ b/src/string_operations.c @@ -1,6 +1,16 @@ #include <stdio.h> #include <wchar.h> +wchar_t *my_wstrchr(wchar_t *ws, wchar_t wc) + { + while (*ws) { + if (*ws == wc) + return ws; + ++ws; + } + return NULL; + } + size_t my_wstrlen(const wchar_t *s) { const wchar_t *p = s;
在验证之后,他提交了他的更改。
[jerry@CentOS src]$ git status -s M string_operations.c [jerry@CentOS src]$ git add string_operations.c [jerry@CentOS src]$ git commit -m 'Addded strchr function for wide character string' [wchar_support 9d201a9] Addded strchr function for wide character string 1 files changed, 10 insertions(+), 0 deletions(-) [jerry@CentOS src]$ git push origin wchar_support
上述命令将产生以下结果 –
Counting objects: 7, done. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 516 bytes, done. Total 4 (delta 1), reused 0 (delta 0) To [email protected]:project.git 3789fe8..9d201a9 wchar_support -> wchar_support
处理冲突
Tom 想看看 Jerry 在他的私有分支上做了什么,所以他试图从wchar_support分支中提取最新的更改,但 Git 中止了操作并显示以下错误消息。
[tom@CentOS src]$ git pull origin wchar_support
上面的命令产生以下结果 –
remote: Counting objects: 11, done. 63Git Tutorials remote: Compressing objects: 100% (8/8), done. remote: Total 8 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (8/8), done. From git.server.com:project * branch wchar_support -> FETCH_HEAD Auto-merging src/string_operations.c CONFLICT (content): Merge conflict in src/string_operations.c Automatic merge failed; fix conflicts and then commit the result.
解决冲突
从错误信息来看,很明显 src/string_operations.c 存在冲突。他运行 git diff 命令以查看更多详细信息。
[tom@CentOS src]$ git diff
上面的命令产生以下结果 –
diff --cc src/string_operations.c index 52bec84,163a779..0000000 --- a/src/string_operations.c +++ b/src/string_operations.c @@@ -1,8 -1,17 +1,22 @@@ #include <stdio.h> #include <wchar.h> ++<<<<<<< HEAD +/* wide character strlen fucntion */ +size_t my_wc_strlen(const wchar_t *s) ++======= + wchar_t *my_wstrchr(wchar_t *ws, wchar_t wc) + { + + while (*ws) { if (*ws == wc) + return ws; + ++ws; + } + return NULL; + } + + size_t my_wstrlen(const wchar_t *s) ++>>>>>>>9d201a9c61bc4713f4095175f8954b642dae8f86 { const wchar_t *p = s;
由于 Tom 和 Jerry 更改了同一个函数的名称,Git 处于混乱状态,它要求用户手动解决冲突。
Tom 决定保留 Jerry 建议的函数名称,但他保留了他添加的注释,原样。删除冲突标记后, git diff 将如下所示。
[tom@CentOS src]$ git diff
上述命令产生以下结果。
diff --cc src/string_operations.c diff --cc src/string_operations.c index 52bec84,163a779..0000000 --- a/src/string_operations.c +++ b/src/string_operations.c @@@ -1,8 -1,17 +1,18 @@@ #include <stdio.h> #include <wchar.h> + wchar_t *my_wstrchr(wchar_t *ws, wchar_t wc) + { + while (*ws) { + if (*ws == wc) + return ws; + ++ws; + } + return NULL; + } + +/* wide character strlen fucntion */ - size_t my_wc_strlen(const wchar_t *s) + size_t my_wstrlen(const wchar_t *s) { const wchar_t *p = s;
由于 Tom 修改了文件,他必须先提交这些更改,然后才能提取更改。
[tom@CentOS src]$ git commit -a -m 'Resolved conflict' [master 6b1ac36] Resolved conflict [tom@CentOS src]$ git pull origin wchar_support.
Tom 已经解决了冲突,现在 pull 操作将成功。
Git – 不同的平台
GNU/Linux 和 Mac OS 使用换行符 (LF)或换行符作为行结束符,而 Windows 使用换行符和回车符 (LFCR)组合来表示行结束符。
为了避免由于这些行尾差异而导致不必要的提交,我们必须配置 Git 客户端以将相同的行尾写入 Git 存储库。
对于Windows系统,我们可以配置Git客户端在checkout时将行尾转换为CRLF格式,在commit操作时将其转换回LF格式。以下设置将完成必要的工作。
[tom@CentOS project]$ git config --global core.autocrlf true
对于 GNU/Linux 或 Mac OS,我们可以配置 Git 客户端在执行结帐操作时将行尾从CRLF转换为LF。
[tom@CentOS project]$ git config --global core.autocrlf input
Git – 在线存储库
GitHub是一种基于 Web 的托管服务,用于使用 Git 版本控制系统的软件开发项目。它还具有可直接从服务网站下载的标准 GUI 应用程序(Windows、Mac、GNU/Linux)。但是在这个会话中,我们只会看到 CLI 部分。
创建 GitHub 存储库
去github.com。如果您已经拥有GitHub帐户,请使用该帐户登录或创建一个新帐户。按照github.com网站上的步骤创建一个新的存储库。
推送操作
Tom 决定使用GitHub服务器。为了开始一个新项目,他创建了一个新目录和一个文件。
[tom@CentOS]$ mkdir github_repo [tom@CentOS]$ cd github_repo/ [tom@CentOS]$ vi hello.c [tom@CentOS]$ make hello cc hello.c -o hello [tom@CentOS]$ ./hello
上述命令将产生以下结果:
Hello, World !!!
在验证他的代码后,他使用 git init 命令初始化目录并在本地提交他的更改。
[tom@CentOS]$ git init Initialized empty Git repository in /home/tom/github_repo/.git/ [tom@CentOS]$ git status -s ?? hello ?? hello.c [tom@CentOS]$ git add hello.c [tom@CentOS]$ git status -s A hello.c ?? hello [tom@CentOS]$ git commit -m 'Initial commit'
之后,他将GitHub存储库 URL添加为远程源并将其更改推送到远程存储库。
[tom@CentOS]$ git remote add origin https://github.com/kangralkar/testing_repo.git [tom@CentOS]$ git push -u origin master
Push 操作会询问GitHub用户名和密码。认证成功后,操作将成功。
上述命令将产生以下结果:
Username for 'https://github.com': kangralkar Password for 'https://[email protected]': Counting objects: 3, done. Writing objects: 100% (3/3), 214 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/kangralkar/test_repo.git * [new branch] master −> master Branch master set up to track remote branch master from origin.
从现在开始,Tom 可以将任何更改推送到GitHub存储库。他可以在GitHub存储库中使用本章中讨论的所有命令。
拉动操作
Tom 成功地将他的所有更改推送到GitHub存储库。现在,其他开发人员可以通过执行克隆操作或更新其本地存储库来查看这些更改。
Jerry 在他的主目录中创建一个新目录并使用 git clone 命令克隆GitHub存储库。
[jerry@CentOS]$ pwd /home/jerry [jerry@CentOS]$ mkdir jerry_repo [jerry@CentOS]$ git clone https://github.com/kangralkar/test_repo.git
上述命令产生以下结果:
Cloning into 'test_repo'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 3 (delta 0) Unpacking objects: 100% (3/3), done.
他通过执行 ls 命令来验证目录内容。
[jerry@CentOS]$ ls test_repo [jerry@CentOS]$ ls test_repo/ hello.c