【Git/GitHub】学习笔记二 Git的常用操作
发布日期:2021-07-01 02:51:11 浏览次数:2 分类:技术文章

本文共 24996 字,大约阅读时间需要 83 分钟。

文章目录

这里将介绍Git相关的基本知识与操作方法,介绍一些必不可少的Git操作。


1. 基本操作

git init 初始化仓库

要用Git进行版本管理,必须先初始化Git仓库。我们需要实际建立一个新文件夹,之后用 git init 命令初始化以创建新的Git仓库:

$ mkdir git-tutorial$ cd git-tutorial$ git initInitialized empty Git repository in D:/MyDownloads/git-tutorial/.git/

初始化成功的标识是执行了 git init 命令的目录下会生成 .git 目录。其中存储着管理当前目录内容所需的仓库数据。在Git中,这个目录的内容被称作:附属于该仓库的工作树。文件的编辑等操作在工作树中进行,然后记录到仓库中,以此管理文件的历史快照。如果要恢复文件到之前的状态,可以从仓库中调取之前的快照,在工作树中打开。

我们的本地仓库由Git维护的三棵树组成:

  • 第一个是我们的 工作目录 ,它持有实际文件;
  • 第二个是 暂存区(Index) ,它像一个缓存区域,临时保存个人的改动;
  • 第三个是 HEAD ,它指向我们最后一次提交的结果。
    在这里插入图片描述

git status 查看仓库中的状态

git status 命令用于显示Git仓库的状态。工作树和仓库的状态,会在操作过程中不断变化。在Git操作中就常常用 git status 查看当前状态。下面实际运行一下:

$ git statusOn branch masterNo commits yetnothing to commit (create/copy files and use "git add" to track)

结果显示,我们当前正处于 master 分支下,还显示:没有可提交的内容。所谓提交 Commit ,指的是:记录工作树中所有文件的当前状态。没有可提交的内容说明,当前我们建立的仓库中没有记录任何文件的任何状态。

我们建立 README.md 文件作为管理对象,对第一次提交做准备:

$ touch README.md$ git statusOn branch masterNo commits yetUntracked files:  (use "git add 
..." to include in what will be committed) README.mdnothing added to commit but untracked files present (use "git add" to track)

可以看到,Untracked files 中显示了 README.md 文件。可见,只要对Git的工作树或者仓库进行操作,git status 命令的显示结果就会发生变化


git add 向暂存区中添加文件

如果仅使用Git仓库的工作树创建文件,那么该文件不会被记入Git仓库的版本管理对象中,用 git status 能发现它们被显示在 Untracked files 之中。要想让文件变成Git仓库的管理对象,需要用 git add <filename> 或者 git add * 命令提出更改,将它们添加到暂存区 (Stage/Index提交之前的一个临时区域) 当中:

$ git add README.md$ git statusOn branch masterNo commits yetChanges to be committed:  (use "git rm --cached 
..." to unstage) new file: README.md

README.md 加入暂存区后,git status 命令的显示结果发生变化,README.md 文件显示在 Changes to be committed 当中。这是Git基本工作流程的第一步


git commit 保存仓库的历史记录

git commit -m "代码提交信息" 命令实际提交改动,将当前暂存区中的文件实际保存到仓库的历史记录中,通过这些记录可以在工作树中回滚和恢复文件。另外,使用这一命令,可以记述提交信息,-m 参数后的字符串就是提交信息,用来对这个提交进行概述:

$ git commit -m "First commit"[master (root-commit) d938084] First commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 README.md

现在,我们的改动已经提交到了 HEAD ,但是还没有到我们的远端仓库。如果要记录得更加详细,就不加 -m ,直接执行 git commit 命令。执行后编辑器自动启动,显示如下:

# Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit# On branch master# # Initial commit## Changes to be committed:#  (use "git rm --cached 
..." to unstage)# # new file: README.md#

# 标为注释的 Changes to be committed 要提交的更改一栏中,可以看到本次提交中包含的文件。需要注意的是:如果在编辑器启动后,将提交信息留空并直接关闭编辑器,提交就会被中止

在编辑器中记录提交信息的格式如下:

  • 第一行:用一行文字简述提交的更改内容;
  • 第二行:空行
  • 第三行以后:记录更改的原因和详细内容

将提交信息按照格式记录完毕后,保存并关闭编辑器,以 # 为注释的行不用删除,之后,这些记录的提交信息就会被提交。只要按照这样的格式输入和提交,以后就可以通过确认日志的命令看到这些记录。

查看提交后的状态,发现当前工作树处于刚刚完成提交的最新状态,没有内容进行提交:

$ git statusOn branch masternothing to commit, working tree clean

git log 查看提交日志

使用 git log 可以查看仓库以往的提交日志:

$ git logcommit d938084c76d2a56a672b75cd91797fcdb3a8a078 (HEAD -> master)Author: memcpy0 <2183927003@qq.com>Date:   Sun Sep 6 22:27:59 2020 +0800    First commit

可以发现,这是刚刚的提交操作,commit 一栏后跟着的是指向这个提交的哈希值。在Git的其他命令中,指向提交时将用到这个哈希值。Author 栏则显示给Git设置的用户名和邮箱地址。Date 栏中显示提交执行的日期和时间。接着就是这个提交的提交信息。

git log 命令后加上 --pretty=short ,就可以让程序显示第一行简述信息,从而方便查看多个提交的日志:

$ git log --pretty=shortcommit d938084c76d2a56a672b75cd91797fcdb3a8a078 (HEAD -> master)Author: memcpy0 <2183927003@qq.com>    First commit

如果在 git log 之后加上目录名,就只会显示该目录下的日志;如果添加文件名,就只会显示和该文件相关的日志:

$ git log README.mdcommit d938084c76d2a56a672b75cd91797fcdb3a8a078 (HEAD -> master)Author: memcpy0 <2183927003@qq.com>Date:   Sun Sep 6 22:27:59 2020 +0800    First commit

更进一步,如果想查看提交带来的改动,可以加上 -p 参数,文件的前后差别就会显示在提交信息之后。执行如下命令,就可以只查看 README.md 文件的提交日志、提交前后的差别:

$ git log -p README.mdcommit d938084c76d2a56a672b75cd91797fcdb3a8a078 (HEAD -> master)Author: memcpy0 <2183927003@qq.com>Date:   Sun Sep 6 22:27:59 2020 +0800    First commitdiff --git a/README.md b/README.mdnew file mode 100644index 0000000..e69de29

从这里可以看出,git log 可以组合多种参数,帮助开发者查看以往提交的内容。我们也不必一次记下所有的参数,需要时查看就可以了。

git diff 查看更改前后的差别

git diff 可以查看工作树暂存区最新提交之间的差别。为了理解,以之前提交的 README.md 为例,写一点内容进去:

# Git学习笔记

接着执行 git diff 命令,查看当前工作树和暂存区的区别——由于我们还没有 git add 向暂存区中添加文件,所以程序只会显示工作树和最新提交状态之间的差别"+" 号显示的是新添加的行,被删除的行用 "-" 号标出,这里只添加了一行:

$ git diffdiff --git a/README.md b/README.mdindex e69de29..70c68ac 100644--- a/README.md+++ b/README.md@@ -0,0 +1 @@+# Git学习笔记\ No newline at end of file

git add README.md 命令将 README.md 加入暂存区。如果之后立刻执行 git diff ,由于此时工作树和暂存区的状态没有区别,结果什么都不会显示。要查看与最新提交的差别,执行以下命令:

$ git diff HEADdiff --git a/README.md b/README.mdindex e69de29..70c68ac 100644--- a/README.md+++ b/README.md@@ -0,0 +1 @@+# Git学习笔记\ No newline at end of file

在执行 git commit 命令之前,先执行 git diff HEAD 命令查看本次提交和上次提交之间有什么区别,确认后再提交,是一个好习惯。注意:这里的 HEAD 是指向当前分支中最新一次提交的指针。由于确认了两个提交之间的差别,直接运行 git commit 即可:

$ git commit README.md[master a23d4ca] Add index                           1 file changed, 1 insertion(+)

之后查看提交日志,确认提交是否成功。发现能够查到第二个提交:

$ git logcommit a23d4ca4adf7c50ded2011debe3965e9d6343dc7 (HEAD -> master)Author: memcpy0 <2183927003@qq.com>Date:   Sun Sep 6 23:37:59 2020 +0800    Add indexcommit d938084c76d2a56a672b75cd91797fcdb3a8a078Author: memcpy0 <2183927003@qq.com>Date:   Sun Sep 6 22:27:59 2020 +0800    First commit

2. 分支操作

在多人并行开发的过程中,往往同时存在多个最新的代码状态。master 分支是Git默认创建的分支,所有开发都是以这个分支为中心进行的;每个分支中都拥有自己的最新代码。

不同分支之间,可以同时进行完全不同的任务。某个分支的任务完成后,再将它合并到 master 分支上。通过灵活运用分支和合并,能够进行大规模的并行开发。

在这里插入图片描述

git branch 显示分支表和当前分支、创建、删除和移动分支

git branch 命令可以将分支名列表显示,同时确认当前所在的分支:

$ git branch* master

结果中没有显示其他分支名,表示本地仓库只有 master 一个分支;master 分支左侧标有 "*" ,表示这是当前分支,我们正在 master 分支下进行开发。

git checkout -b 创建、切换分支

要以当前的 master 分支为基础创建新的分支,需要用到 git checkout -b 命令。执行下述命令,会创建名为 feature-A 的分支,并切换过去:

$ git checkout -b feature-ASwitched to a new branch 'feature-A'

注意,这条命令等价于下面的连续两条命令:

$ git branch feature-A   //创建feature-A分支$ git checkout feature-A //切换当前分支到feature-A分支

注意,除非我们把分支推送到远端仓库,不然该分支就是不为他人所见的:git push origin <branch> 。之后查看分支列表,显示处于 feature-A 分支下,因为 feature-A 分支左侧标有 "*"

$ git branch* feature-A  master

在这一状态下,可以正常开发、修改代码、执行 git add 命令并进行提交,不过代码会被提交到 feature-A 分支。不断对一个分支进行提交的操作,被称为培育分支

此时如果在 README.md 中实际添加一行:

# Git学习笔记- this is feature-A

添加到暂存区,进行提交,之后这一行就添加到 feature-A 分支中:

$ git add README.md$ git diff HEADdiff --git a/README.md b/README.mdindex 70c68ac..f9a2349 100644--- a/README.md+++ b/README.md@@ -1 +1,2 @@-# Git学习笔记\ No newline at end of file+# Git学习笔记+- this is feature-A\ No newline at end of file$ git commit -m "Add feature-A"[feature-A 8bdbe0d] Add feature-A 1 file changed, 2 insertions(+), 1 deletion(-)

现在回头看一下 master 分支有没有受到影响。切换到 master 分支:

$ git checkout masterSwitched to branch 'master'

然后查看 README.md 文件,发现它仍然保持之前的状态,没有被添加文字:

$ cat README.md# Git学习笔记

可见,feature-A 分支的更改,不会影响到 master 分支,这正是在开发中创建分支的优点。只要创建多个分支,就可以在彼此隔离、互不影响的情况下,同时进行多个功能的开发。

如果想要把新建的分支删除掉,使用如下命令,这里不实际执行:

$ git branch -d feature-A //删除feature-A分支

现在切换回 feature-A 分支:

$ git checkout -Switched to branch 'feature-A'

这样用 "-" 连字符代表分支名,就可以切换到上一个分支。或者将 "-" 替换成 feature-A 也可以。


③ 特性分支与主干分支

特性分支,是集中实现单一特性或者主题 Topic ,除此之外不进行任何作业的分支。日常开发中,往往创建多个特性分支,并保留一个随时可以发布软件的稳定分支,稳定分支通常由 master 分支来担当。

  • 之前创建的 feature-A 主要实现 feature-A ,除此之外不进行任何作业。即使开发中发现了BUG,也需要创建新的分支,在新分支中进行修正。
  • 基于特定主题的作业,在特定分支中进行,完成后再与 master 分支合并。只要基于这一开发流程,就能确保 masterr 分支随时可以供人查看,其他开发者也可以放心地从 master 分支创建新的特性分支。

主干分支是特性分支的原点,也是合并的终点。通常 master 分支是主干分支,主干分支中没有开发中途的代码,可随时供他人查看。有时候需要管理多个版本发布,则需要有多个主干分支。


git merge 合并分支

假设 feature-A 已经实现完成,将它合并到主干分支 master 中。首先切换到 master 分支使其成为当前分支:

$ git checkout masterSwitched to branch 'master'

接着进行合并,为了在历史记录中明确记录下本次分支合并,需要创建合并提交。需要在合并时加上 --no-ff 参数:

$ git merge --no-ff feature-AMerge made by the 'recursive' strategy. README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)

git log 查看,发现 feature-A 分支的内容已经被合并到 master 分支中了:

$ git logcommit f5521b1559418e0f784e067a340356bd2d21e640 (HEAD -> master)Merge: a23d4ca 8bdbe0dAuthor: memcpy0 <2183927003@qq.com>Date:   Mon Sep 7 10:11:32 2020 +0800    Merge branch 'feature-A' into mastercommit 8bdbe0de8eb9b2f2d40cc0fcbb04e630dbd1d254 (feature-A)Author: memcpy0 <2183927003@qq.com>Date:   Mon Sep 7 01:32:55 2020 +0800    Add feature-Acommit a23d4ca4adf7c50ded2011debe3965e9d6343dc7Author: memcpy0 <2183927003@qq.com>Date:   Sun Sep 6 23:37:59 2020 +0800    Add indexcommit d938084c76d2a56a672b75cd91797fcdb3a8a078Author: memcpy0 <2183927003@qq.com>Date:   Sun Sep 6 22:27:59 2020 +0800    First commit

git log --graph 图表形式查看分支

如果用 git log --graph 查看,就能够用图表形式输出提交日志,非常直观。可以看到特性分支 feature-A 提交的内容已被合并:

$ git log --graph*   commit f5521b1559418e0f784e067a340356bd2d21e640 (HEAD -> master)|\  Merge: a23d4ca 8bdbe0d| | Author: memcpy0 <2183927003@qq.com>| | Date:   Mon Sep 7 10:11:32 2020 +0800| || |     Merge branch 'feature-A' into master| || * commit 8bdbe0de8eb9b2f2d40cc0fcbb04e630dbd1d254 (feature-A)|/  Author: memcpy0 <2183927003@qq.com>|   Date:   Mon Sep 7 01:32:55 2020 +0800||       Add feature-A|* commit a23d4ca4adf7c50ded2011debe3965e9d6343dc7| Author: memcpy0 <2183927003@qq.com>| Date:   Sun Sep 6 23:37:59 2020 +0800||     Add index|* commit d938084c76d2a56a672b75cd91797fcdb3a8a078  Author: memcpy0 <2183927003@qq.com>  Date:   Sun Sep 6 22:27:59 2020 +0800      First commit

3. 更改提交的操作

git reset 回溯历史版本

前面学会了如何在实现功能后进行合并提交、累积提交日志作为历史记录。但是这仅仅是Git功能的冰山一角。Git的真正强大之处,在于可以灵活操作历史版本——分散型仓库的优势可以让Git在不影响其他仓库的前提下,对历史版本进行操作。

为此,我们先回溯历史版本,创建一个名为 fix-B 的特性分支:

master		 / |		/  |	   /   |	  /	   |feature-A  |    fix-B	  \    |    /	   \   |   /	  	\  |  / 	  	 \ | /	  	  node	  	   |	  	   |	  	   |	  	  node

a. 回溯到创建 feature-A 分支前

我们回溯到上一次 feature-A 分支创建之前,创建一个名为 fix-B 的特性分支。要让仓库的 HEAD 、暂存区、当前工作树,回到指定状态,需要用 git reset --hard 命令。只要提供目标时间点的哈希值,就可以完全恢复到该时间点的状态。 执行下面的命令:

$ git reset --hard a23d4ca4adf7c50ded2011debe3965e9d6343dc7HEAD is now at a23d4ca Add index

b. 创建 fix-B 分支

我们成功回到了特性分支 feature-A 创建之前的状态。因为所有文件都回溯到了指定哈希值对应的时间点上README.md 文件的内容也恢复到了当时的状态。接着创建特性分支 fix-B

$ git checkout -b fix-BSwitched to a new branch 'fix-B'

在这个主题中,我们在 README.md 中添加一行文字:

# Git学习笔记- this is fix-B

然后直接添加并提交 README.md 文件:

$ git add README.md$ git commit -m "Fix B"[fix-B d4914ee] Fix B 1 file changed, 2 insertions(+), 1 deletion(-)

现在的状态,或者说当前 fix-B 分支的状态,如下所示:

$ git log --graph* commit d4914ee6e016a759393be87e517804d2061ee5ad (HEAD -> fix-B)| Author: memcpy0 <2183927003@qq.com>| Date:   Mon Sep 7 11:17:40 2020 +0800||     Fix B|* commit a23d4ca4adf7c50ded2011debe3965e9d6343dc7 (master)| Author: memcpy0 <2183927003@qq.com>| Date:   Sun Sep 6 23:37:59 2020 +0800||     Add index|* commit d938084c76d2a56a672b75cd91797fcdb3a8a078  Author: memcpy0 <2183927003@qq.com>  Date:   Sun Sep 6 22:27:59 2020 +0800      First commit

接着,fix-B 分支的目标是如下所示的状态,即主干分支合并 feature-A 分支的修改后,又合并 fix-B 的修改:

master		   | \		   |  \           |   \           |    \           |     \		  node   | 		 / |     |		/  |     |	   /   |     |	  /	   |     |feature-A  |   fix-B	  \    |    /	   \   |   /	  	\  |  / 	  	 \ | /	  	  node	  	   |	  	   |	  	   |	  	  node

c. 推进至 feature-A 分支合并后的状态

因此,我们需要恢复到 feature-A 分支被合并后的状态,这一操作被称为推进历史git log 只能够查看以当前状态为终点的历史日志,这里需要使用 git reflog 查看当前仓库的操作日志,在其中找到回溯历史之前的哈希值,通过 git -reset --hard 恢复到回溯历史前的状态:

$ git reflogd4914ee (HEAD -> fix-B) HEAD@{
0}: commit: Fix Ba23d4ca (master) HEAD@{
1}: checkout: moving from master to fix-Ba23d4ca (master) HEAD@{
2}: checkout: moving from fix-B to mastera23d4ca (master) HEAD@{
3}: checkout: moving from master to fix-Ba23d4ca (master) HEAD@{
4}: reset: moving to a23d4ca4adf7c50ded2011debe3965e9d6343dc7f5521b1 HEAD@{
5}: merge feature-A: Merge made by the 'recursive' strategy.a23d4ca (master) HEAD@{
6}: checkout: moving from feature-A to master8bdbe0d (feature-A) HEAD@{
7}: checkout: moving from master to feature-Aa23d4ca (master) HEAD@{
8}: checkout: moving from feature-A to master8bdbe0d (feature-A) HEAD@{
9}: checkout: moving from master to feature-Aa23d4ca (master) HEAD@{
10}: checkout: moving from feature-A to master8bdbe0d (feature-A) HEAD@{
11}: checkout: moving from master to feature-Aa23d4ca (master) HEAD@{
12}: checkout: moving from feature-A to master8bdbe0d (feature-A) HEAD@{
13}: commit: Add feature-Aa23d4ca (master) HEAD@{
14}: checkout: moving from master to feature-Aa23d4ca (master) HEAD@{
15}: commit: Add indexd938084 HEAD@{
16}: commit (initial): First commit

我们在日志中看到 commit, checkout, merge, reset 等Git命令的执行记录。只要不对Git进行GC,就可以通过日志随意调取近期的历史状态。即使我们错误执行了Git操作,也基本都可以通过 git reflog 命令恢复到原先的状态。

feature-A 特性分支合并后的状态,对应的哈希值是 f5521b1 ,我们将 HEAD 、暂存区、工作树恢复到这个时间点的状态:

$ git checkout masterSwitched to branch 'master'$ git reset --hard f5521b1HEAD is now at f5521b1 Merge branch 'feature-A' into master

现在回到了回溯前的历史状态,此时的状态如下:

master		 / |		/  |	   /   |	  /	   |feature-A  |    fix-B	  \    |    /	   \   |   /	  	\  |  / 	  	 \ | /	  	  node	  	   |	  	   |	  	   |	  	  node

② 消除冲突

现在只要合并 fix-B 分支,就可以得到我们的目标状态:

$ git merge --no-ff fix-BAuto-merging README.mdCONFLICT (content): Merge conflict in README.mdAutomatic merge failed; fix conflicts and then commit the result.

发生什么状况了?原来是 README.md 文件发生了冲突 Conflict ,系统在合并 README.md 时,feature-A 分支更改的部分和本次想要合并的 fix-B 分支更改的部分发生了冲突。不解决冲突,就无法合并。

在编辑器中打开 README.md ,会发现其内容变成下述状态:

# Git学习笔记<<<<<<< HEAD- this is feature-A=======- this is fix-B>>>>>>> fix-B

======= 以上的部分是当前 HEAD 的内容,以下的部分是要合并的 fix-B 分支中的内容,在编辑器中修改成想要的样子:

# Git学习笔记- this is feature-A- this is fix-B

这里的修正,将 feature-Afix-B 的内容并存于文件中。但是实际的开发中,往往需要删除其中一部分,所以发生冲突时需要仔细分析冲突部分的内容,再进行修改。

冲突解决后,执行添加和提交命令,由于本次解决了冲突,所以记录的提交信息为 "Fix conflict"

$ git add README.md$ git commit -m "Fix conflict"[master 81d143c] Fix conflict

查看日志图表,这次合并已经被记录下来:

$ git log --graph*   commit 81d143c79a1a67d6a477115cd8c2dff48245bc6a (HEAD -> master)|\  Merge: f5521b1 d4914ee| | Author: memcpy0 <2183927003@qq.com>| | Date:   Mon Sep 7 12:49:18 2020 +0800| || |     Fix conflict| || * commit d4914ee6e016a759393be87e517804d2061ee5ad (fix-B)| | Author: memcpy0 <2183927003@qq.com>| | Date:   Mon Sep 7 11:17:40 2020 +0800| || |     Fix B| |* |   commit f5521b1559418e0f784e067a340356bd2d21e640|\ \  Merge: a23d4ca 8bdbe0d| |/  Author: memcpy0 <2183927003@qq.com>|/|   Date:   Mon Sep 7 10:11:32 2020 +0800| || |       Merge branch 'feature-A' into master| || * commit 8bdbe0de8eb9b2f2d40cc0fcbb04e630dbd1d254 (feature-A)|/  Author: memcpy0 <2183927003@qq.com>|   Date:   Mon Sep 7 01:32:55 2020 +0800||       Add feature-A|* commit a23d4ca4adf7c50ded2011debe3965e9d6343dc7| Author: memcpy0 <2183927003@qq.com>| Date:   Sun Sep 6 23:37:59 2020 +0800||     Add index|* commit d938084c76d2a56a672b75cd91797fcdb3a8a078  Author: memcpy0 <2183927003@qq.com>  Date:   Sun Sep 6 22:27:59 2020 +0800      First commit

git commit --amend 修改提交信息

使用 git commit --amend 可以修改上一条提交信息。这里把上一条提交信息记为 "Fix conflict" ,但是这实际上是 fix-B 分支的合并,解决合并时发生的冲突只是过程之一,这样标记不太恰当。所以进行修改:

$ git commit --amendFix conflict# Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit.## Date:      Mon Sep 7 12:49:18 2020 +0800## On branch master# Changes to be committed:#       modified:   README.md#

执行命令后,编辑器启动,显示的内容包含之前的提交信息,把它修改为 Merge branch 'fix-B' ,然后保存文件,关闭编辑器:

[master 9faf393] Merge branch 'fix-B'                                      Date: Mon Sep 7 12:49:18 2020 +0800

之后显示上面的结果。然后,执行 git log --graph 命令,可以看到提交日志中的内容也被修改:

$ git log --graph*   commit 9faf393fe36699141f0727f09149716f6ca1b630 (HEAD -> master)|\  Merge: f5521b1 d4914ee| | Author: memcpy0 <2183927003@qq.com>| | Date:   Mon Sep 7 12:49:18 2020 +0800| || |     Merge branch 'fix-B'| || * commit d4914ee6e016a759393be87e517804d2061ee5ad (fix-B)| | Author: memcpy0 <2183927003@qq.com>| | Date:   Mon Sep 7 11:17:40 2020 +0800| || |     Fix B| |* |   commit f5521b1559418e0f784e067a340356bd2d21e640|\ \  Merge: a23d4ca 8bdbe0d| |/  Author: memcpy0 <2183927003@qq.com>|/|   Date:   Mon Sep 7 10:11:32 2020 +0800| || |       Merge branch 'feature-A' into master| || * commit 8bdbe0de8eb9b2f2d40cc0fcbb04e630dbd1d254 (feature-A)|/  Author: memcpy0 <2183927003@qq.com>|   Date:   Mon Sep 7 01:32:55 2020 +0800||       Add feature-A|* commit a23d4ca4adf7c50ded2011debe3965e9d6343dc7| Author: memcpy0 <2183927003@qq.com>| Date:   Sun Sep 6 23:37:59 2020 +0800||     Add index|* commit d938084c76d2a56a672b75cd91797fcdb3a8a078  Author: memcpy0 <2183927003@qq.com>  Date:   Sun Sep 6 22:27:59 2020 +0800      First commit

git rebase -i 压缩历史

在合并特性分支前,如果发现已提交的内容中有一些拼写错误,可以提交一个修改,然后把这个修改包含在前一个提交中,压缩成一个历史记录。这是一个很有用的技巧。

先创建一个 feature-C 特性分支:

$ git checkout -b feature-CSwitched to a new branch 'feature-C'

feature-C 的实现 README.md 中,添加一行含有拼写错误的文字,以便之后修正:

# Git学习笔记- this is feature-A- this is fix-B- this is faeture-C

这是个小变更,提交时没必要先执行 git add 再执行 git commit ,用 git commit -am 一次完成这两步操作:

$ git commit -am "Add feature-C"[feature-C 3814a73] Add feature-C 1 file changed, 2 insertions(+), 1 deletion(-)

现在自行修正刚才提交内容中的错误,修改后的差别如下:

$ git diffdiff --git a/README.md b/README.mdindex b07bb4a..76679b1 100644--- a/README.md+++ b/README.md@@ -1,4 +1,4 @@ # Git学习笔记 - this is feature-A - this is fix-B-- this is faeture-C\ No newline at end of file+- this is feature-C\ No newline at end of file

进行提交,修正错字漏字 typo

$ git commit -am "Fix typo"[feature-C fb5281a] Fix typo 1 file changed, 1 insertion(+), 1 deletion(-)

事实上不应该在历史记录中看到这些提交,因为健全的历史记录不需要它们。我们来更改历史,把 "Fix typo" 修改的内容和前一次提交合并,在历史记录中合并为一次完美的提交:

$ git rebase -i HEAD~2

这样执行 git rebase 命令,将选定当前分支中包含 HEAD (最新提交) 在内的两个最新历史记录为对象,并在编辑器中打开:

pick 3814a73 Add feature-Cpick fb5281a Fix typo# Rebase 9faf393..fb5281a onto 9faf393 (2 commands)## Commands:# p, pick 
= use commit# r, reword
= use commit, but edit the commit message# e, edit
= use commit, but stop for amending# s, squash
= use commit, but meld into previous commit# f, fixup
= like "squash", but discard this commit's log message# x, exec
= run command (the rest of the line) using shell# b, break = stop here (continue rebase later with 'git rebase --continue')# d, drop
= remove commit# l, label

我们将 fb5281aFix typo 的历史记录,压缩到 3814a73Add feature-C 中。如下所示,将 fb5281a 左侧的 pick 部分删除,修改为 fixup

pick 3814a73 Add feature-Cfixup fb5281a Fix typo

随后,保存编辑器中的内容,关闭编辑器。显示 rebase 成功,也就是将上面的两个提交作为对象,将 "Fix typo" 的内容合并到上一个提交 "Add feature-C" 中,改写成一个新的提交:

Successfully rebased and updated refs/heads/feature-C.

现在查看提交日志,发现 "Add feature-C" 的哈希值已经变化,说明提交被修改:

$ git log --graph* commit b472ba0a73b25fb4691381171a4b2f8fc9c7008f (HEAD -> feature-C)| Author: memcpy0 <2183927003@qq.com>| Date:   Mon Sep 7 15:20:27 2020 +0800||     Add feature-C|*   commit 9faf393fe36699141f0727f09149716f6ca1b630 (master)|\  Merge: f5521b1 d4914ee| | Author: memcpy0 <2183927003@qq.com>| | Date:   Mon Sep 7 12:49:18 2020 +0800| || |     Merge branch 'fix-B'| || * commit d4914ee6e016a759393be87e517804d2061ee5ad (fix-B)| | Author: memcpy0 <2183927003@qq.com>| | Date:   Mon Sep 7 11:17:40 2020 +0800| || |     Fix B| |省略

这样 "Fix typo" 就被抹去,相当于 "Add feature-C" 中从来没有出现过拼写错误,是一种良性的历史改写。feature-C 分支的使命结束,将它和 master 分支合并:

$ git checkout masterSwitched to branch 'master'$ git merge --no-ff feature-CMerge made by the 'recursive' strategy. README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)

git checkout --<filename> 替换本地改动

假如操作失误(当然,这最好永远不要发生),可以使用如下命令替换掉本地改动:

$ git checkout -- 

此命令会使用 HEAD 中的最新内容替换掉我们的工作目录中的文件。已添加到暂存区的改动以及新文件都不会受到影响

假如想丢弃在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将本地主分支指向它:

$ git fetch origin$ git reset --hard origin/master

4. 推送到远程仓库

我们了解了对本地单一仓库的操作,是时候接触远程仓库——和本地仓库相对独立的另一个仓库。先在GitHub上创建一个仓库,并且设置为本地仓库的远程仓库。

参考之前的步骤在GitHub上新建一个仓库,仓库名要和本地仓库保持一致,即 git-tutorial 。创建时不勾选 add a README file 选项(一旦勾选,GitHub一侧的远程仓库会自动生成 README 文件,从创建之初就和本地仓库失去了整合性,虽然也可以后期强制覆盖,但是麻烦),直接 Create repository 即可。

git remote add 添加远程仓库

在GitHub上创建的仓库路径为 https://github.com/memcpy0/git-tutorial.git ,现在用 git remote add 命令将其添加为本地仓库的远程仓库:

$ git remote add origin https://github.com/memcpy0/git-tutorial.git

这一命令执行后,Git会自动将 https://github.com/memcpy0/git-tutorial.git 远程仓库的名称设置为 origin (标识符)。

git push 推送至远程仓库

我们的改动现在已经在本地仓库的 HEAD 中了。如果要将当前分支下本地仓库的内容推送到远程仓库,需要用到 git push origin master 命令。可以把 master 换成想要推送的任何分支。假定我们就是在 master 分支下操作:

$ git push -u origin masterEnumerating objects: 20, done.Counting objects: 100% (20/20), done.Writing objects: 100% (20/20), 1.81 KiB | 205.00 KiB/s, done.Total 20 (delta 0), reused 0 (delta 0), pack-reused 0To https://github.com/memcpy0/git-tutorial.git * [new branch]      master -> masterBranch 'master' set up to track remote branch 'master' from 'origin'.

这样执行 git push 命令,当前本地仓库 master 分支的内容就会被推送到GitHub的远程仓库 originmaster 分支。-u 参数可以在推送的同时,将 origin 仓库的 master 分支,设置为本地仓库当前分支的 upstream 上游。此后,运行 git pull 从远程仓库获取内容时,本地仓库的分支就能直接从 originmaster 分支获取内容,省去另外添加参数的麻烦。

在GitHub上,可以确认远程仓库 master 分支的内容,和本地 master 分支相同:

在这里插入图片描述

除了 master 分支以外,远程仓库还可以创建其他分支。如果要推送到 master 以外的分支,比如说,在本地仓库创建 feature-D 分支,并将它以同名形式 push 到远程仓库:

$ git checkout -b feature-DSwitched to a new branch 'feature-D'$ git push -u origin feature-DTotal 0 (delta 0), reused 0 (delta 0), pack-reused 0remote:remote: Create a pull request for 'feature-D' on GitHub by visiting:remote:      https://github.com/memcpy0/git-tutorial/pull/new/feature-Dremote:To https://github.com/memcpy0/git-tutorial.git * [new branch]      feature-D -> feature-DBranch 'feature-D' set up to track remote branch 'feature-D' from 'origin'.

现在,在GitHub中可以看到 feature-D 分支:

在这里插入图片描述

如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,可以使用命令添加:git remote add origin <server> 。如此就能够将你的改动推送到所添加的服务器上去了。


5. 从远程仓库中获取

上面我们将GitHub上新建的仓库设置为远程仓库,并向其 pushfeature-D 分支。现在,所有能够访问这个远程仓库的人,都可以获取 feature-D 分支并加以修改。

现在,我们装作又有一个开发者来共同开发目标仓库——在另一个目录下新建一个本地仓库,学习从远程仓库获取内容的操作。

git clone 获取远程仓库

首先到其他目录下,将GitHub上的仓库 clone 到本地,不要和之前操作的仓库处于同一目录下。我创建了一个 my-git 目录,然后复制到这一目录下:

$ git clone https://github.com/memcpy0/git-tutorial.git my-gitCloning into 'my-git'...remote: Enumerating objects: 20, done.remote: Counting objects: 100% (20/20), done.remote: Compressing objects: 100% (10/10), done.remote: Total 20 (delta 0), reused 20 (delta 0), pack-reused 0Unpacking objects: 100% (20/20), 1.79 KiB | 10.00 KiB/s, done.

执行 git clone 命令后,我们默认处于 master 分支下,同时系统自动将 origin 设置成被 clone 的远程仓库的标识符。即,当前本地仓库的 master 分支和GitHub远程仓库 originmaster 分支在内容上完全相同。

git branch -a 命令查看本地仓库和远程仓库的分支信息,结果中显示了 remotes/origin/feature-D ,说明远程仓库中已经有了 feature-D 分支:

$ git branch -a* master  remotes/origin/HEAD -> origin/master  remotes/origin/feature-D  remotes/origin/master

git checkout -b feature-D origin/feature-D 将远程的 feature-D 分支获取到本地仓库,-b 后面是本地仓库中新建分支的名称,命名为 feature-D ,和远程仓库中的对应分支保持同名;新建分支名称后,是获取来源的分支名称,这里是名为 origin 的(GitHub端的远程)仓库的 feature-D 分支作为来源,然后在本地仓库中创建 feature-D 分支:

$ git checkout -b feature-D origin/feature-DSwitched to a new branch 'feature-D'Branch 'feature-D' set up to track remote branch 'feature-D' from 'origin'.

然后向本地的 feature-D 分支提交更改。在 README.md 文件中添加一行文字 - this is feature-D ,按照之前的方式提交:

$ git commit -am "Add feature-D"[feature-D dcb426b] Add feature-D 1 file changed, 2 insertions(+), 1 deletion(-)

现在推送 feature-D 分支:

$ git pushEnumerating objects: 3, done.Counting objects: 100% (3/3), done.Writing objects: 100% (3/3), 289 bytes | 96.00 KiB/s, done.Total 3 (delta 0), reused 0 (delta 0), pack-reused 0To https://github.com/memcpy0/git-tutorial.git   2fd5cb9..dcb426b  feature-D -> feature-D

git pull 获取最新的远程仓库分支

回到原来的目录下,这边的本地仓库只是创建了 feature-D 分支,没有在其中进行任何提交。然而远程仓库的 feature-D 分支已经有了我们刚刚推送的提交。

此时,可以执行 git pull 命令,以在工作目录中获取 (fetch) 并合并 (merge) 远端的改动。将本地的 feature-D 分支更新到最新状态。当前分支为 feature-D 分支:

$ git pull origin feature-Dremote: Enumerating objects: 5, done.remote: Counting objects: 100% (5/5), done.remote: Compressing objects: 100% (2/2), done.remote: Total 3 (delta 1), reused 2 (delta 0), pack-reused 0Unpacking objects: 100% (3/3), 248 bytes | 9.00 KiB/s, done.From https://github.com/memcpy0/git-tutorial * branch            feature-D  -> FETCH_HEAD   2fd5cb9..dcb426b  feature-D  -> origin/feature-DUpdating 2fd5cb9..dcb426bFast-forward README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)

GitHub远程仓库中的 feature-D 分支是最新状态,所以本地的 feature-D 分支就得到了更新。以后只要像平常一样在本地进行开发,再 push 到远程仓库,就可以和其他开发者一起在同一个分支进行作业。当然,如果两个人同时修改了同一部分的代码,push 时容易发生冲突。为减少冲突的发生,建议更频繁地进行 push, pull 操作。

总的来说,git pullgit push 两种情况下,Git都会尝试去自动合并改动。遗憾的是并非每次都成功,可能出现冲突 (conflicts) ,这时候就需要修改这些文件,手动合并这些冲突 conflicts 。改完之后,还需要执行如下命令以将它们标记为合并成功:

$ git add 

在合并改动之前,可以使用如下命令预览差异:

$ git diff 

6. 标签操作

为软件发布创建标签是推荐的做法。这个概念早已存在,在SVN中也有。可以执行如下命令创建一个叫做 1.0.0 的标签:

$ git tag 1.0.0 1b2e1d63ff

1b2e1d63ff 是你想要标记的提交记录的哈希值前 10 10 10 位字符。可以使用下列命令获取哈希值:

$ git log

也可以使用少一点的前几位哈希值,只要它的指向具有唯一性。


7. 学习Git的其他资料

助你入门Git的简明指南。

用图示学习Git的使用。
:记录常用的一些命令。
:记录了Git的完整命令,更方便查看。
Git的学习网站。
GitHub网站的使用文档。
可在Web上一边操作一边学习Git的基本功能,不过只有英文版。随便输入一些Git命令:

学习Git基本操作的网站,它的特点在于可以直观看到分支的树形结构,还可以切换各种语言进行学习。上面的知识,我会陆续整理到本文中。

就职于GitHub的Scott Chacon写的,零基础学习Git的资料,有各种语言版本。

转载地址:https://memcpy0.blog.csdn.net/article/details/108456149 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【Git/GitHub】学习笔记三 熟悉GitHub功能
下一篇:【Git/GitHub】学习笔记一 GitHub和Git的初步使用

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月24日 03时03分27秒