git学习笔记

系列学习笔记首次连载于公众号:“AInewworld”,关注了解更多~

相关教程:

很好很全的官方教程第二版(力推)

廖雪峰的官方网站

莫烦python的视频git教程

1 git基础教程

1.1 git基础

(1)git思想:直接记录快照,而非差异比较;

Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这是 Git 同其他系统的重要区别。

(2)近乎所有操作都是本地执行;

在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网。

(3)文件的三种状态

对于任何一个文件,在 Git 内都只有三种状态:

  • 已提交(committed)

    已提交表示该文件已经被安全地保存在本地数据库中了;

  • 已修改(modified)

    已修改表示修改了某个文件,但还没有提交保存;

  • 已暂存(staged)

    已暂存表示把已修改的文件放在下次提交时要保存的清单中。

三者之间的关系转换如图所示:
git学习笔记_第1张图片

每个项目都有一个隐藏的 Git 目录(注:如果 git clone 出来的话,就是其中 .git 的目录;如果 git clone –bare 的话,新建的目录本身就是 Git 目录。),它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据,一般情况下绝对不要人为修改。

基本的 Git 工作流程如下:

在工作目录中修改某些文件。
对修改后的文件进行快照,然后保存到暂存区域。
提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。

1.2 git安装

更多详细参考: 安装 Git

抽出来记录下常见的安装:

(1)在 Linux 上安装
在 Ubuntu 这类 Debian 体系的系统上,可以用 apt-get 安装:

$ apt-get install git

(2)在 Mac 上安装

在 Mac 上安装 Git 有两种方式。最容易的当属使用图形化的 Git 安装工具,下载地址在:
http://sourceforge.net/projects/git-osx-installer/

(3)在 Windows 上安装

在GitHub 的页面上下载 exe 安装文件并运行:

http://msysgit.github.com/

完成安装之后,就可以使用命令行的 git 工具(已经自带了 ssh 客户端)了,另外还有一个图形界面的 Git 项目管理工具。

注:windows命令行切换盘需要/d参数 :cd /d D:

1.3 初次运行git前的配置(很重要

初试使用时,主要需要配置一些用户信息;一般在新的系统上,我们都需要先配置下自己的 Git 工作环境。配置工作只需一次,以后升级时还会沿用现在的配置。当然,如果需要,你随时可以用相同的命令修改已有的配置。

(1)用户信息

第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录:

$ git config --global user.name "John Doe"
$ git config --global user.email [email protected]

这里要修改的也就是自己的名字与邮箱;其他不变

如果用了 –global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 –global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。

其他配置

(2)文本编辑器配置

接下来要设置的是默认使用的文本编辑器。Git 需要你输入一些额外消息的时候,会自动调用一个外部文本编辑器给你用。默认会使用操作系统指定的默认编辑器,一般可能会是 Vi 或者 Vim。如果你有其他偏好,比如 Emacs 的话,可以重新设置:

$ git config --global core.editor emacs

(3)查看配置信息

要检查已有的配置信息,可以使用 git config –list 命令:

$ git config --list
user.name=Scott Chacon
[email protected]
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...

也可以直接查阅某个环境变量的设定,只要把特定的名字跟在后面即可,像这样:

$ git config user.name
Scott Chacon

1.4 通过帮助文档获取使用方法

对于一个指令可以是呀git自带帮助查看使用方法。
比如,要学习 config 命令可以怎么用,运行:

$ git help config

2. git的基本使用

2.1 创建一个版本库

创建一个版本库有两种方式,一种自己新建,一种克隆已有的。

  • 在工作目录中初始化新仓库

创建一个仓库用于git管理,那么里面的文件都可以被git管理记录上,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
创建一个版本库非常简单,

(1)首先,选择一个合适的地方,创建一个空目录:

$ mkdir learngit
$ cd learngit
$ pwd
/Users/michael/learngit

(2)通过 git init 命令把这个目录变成Git可以管理的仓库:

$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/

建立好后,通过ls命令可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。如果你没有看到.git目录,那是因为这个目录默认是隐藏的,用 ls -ah 命令就可以看见

(3)把文件添加到版本库

我们可以新建现在我们编写一个readme.txt文件,内容如下:

Git is a version control system.
Git is free software.

一定要放到learngit目录下(子目录也行),因为这是一个Git仓库,放到其他地方Git再厉害也找不到这个文件。

在这之前确保完成前面说的git前的配置,比如如果你没有注册自己的姓名与邮箱,就会出现提醒“tell me who you are”的错误.之后,

第一步,用命令git add告诉Git,把文件添加到仓库:

$ git add readme.txt

执行上面的命令,没有任何显示,这就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。

第二步,用命令git commit告诉Git,把文件提交到仓库:

$ git commit -m "wrote a readme file"
[master (root-commit) cb926e7] wrote a readme file
 1 file changed, 2 insertions(+)
 create mode 100644 readme.txt

其中双冒号后面表示注释,这个注释还是很重要的,当你想看你的历史版本的时候,这个注释就会显示出来,所以commit添加一个明确的注释对于版本查找来说非常重要。

  • 从现有仓库克隆 (非常重要的应用)

如果复制一个开源项目,可以先把该项目的 Git 仓库复制一份出来,这就需要用到 git clone 命令。

克隆仓库的命令格式为 git clone [url]。比如,要克隆 Ruby 语言的 Git 代码仓库 Grit,可以用下面的命令:

$ git clone git://github.com/schacon/grit.git

这会在当前目录下创建一个名为grit的目录,其中包含一个 .git 的目录,用于保存下载下来的所有版本记录,然后从中取出最新版本的文件拷贝。如果进入这个新建的 grit 目录,你会看到项目中的所有文件已经在里边了,准备好后续的开发和使用。

如果希望在克隆的时候,自己定义要新建的项目目录名称,可以在上面的命令末尾指定新的名字:

$ git clone git://github.com/schacon/grit.git mygrit

唯一的差别就是,现在新建的目录成了 mygrit,其他的都和上边的一样。

2.2 更新仓库

  • 工作目录下面的所有文件都不外乎这两种状态:已跟踪或未跟踪。(已跟踪的文件又包括好几种状态)
  • 初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。
  • 新建的其他项目属于未跟踪文件,既没有上次更新时的快照,也不在当前的暂存区域。

(1)检测当前文件状态
:要确定哪些文件当前处于什么状态,可以用 git status 命令。比如新建了一个test.txt,然后直接git status的结果:

D:\soft\git\learngit>git status
On branch master

No commits yet

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

        test.txt

nothing added to commit but untracked files present (use "git add" to track)

可以注意两点:

  • 首先,自己建立了一个主分支 branch master;

  • 其次出现了一个未跟踪的文件 Untracked files,提醒需要跟踪。

(2)跟踪新文件

使用命令 git add 开始跟踪一个新文件。

其实 git add 的潜台词就是把目标文件快照放入暂存区域,也就是 add file into staged area,同时未曾跟踪过的文件标记为需要跟踪。这样就好理解后续 add 操作的实际意义了。

注意:运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来:

添加一个文件:

git add test.txt

假如一下有好多个文件需要添加:

git add .

(4)提交更新 重要

现在的暂存区域已经准备妥当可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有 git add 过,否则提交的时候不会记录这些还没暂存起来的变化。所以,每次准备提交前,先用 git status 看下,是不是都已暂存起来了,然后再运行提交命令 git commit

>> git commit -m "change1"
[master (root-commit) 0e77290] change1
 3 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1.txt
 create mode 100644 2.txt
 create mode 100644 test.txt

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

(5)查看已暂存和未暂存的更新(查看前后之间的变化)

实际上 git status 的显示比较简单,仅仅是列出了修改过的文件,如果要查看具体修改了什么地方,可以用 git diff 命令。用 git diff –cached 查看已经暂存起来的变化:

(6)移动文件(mv)

要在 Git 中对文件改名,可以这么做:

$ git mv file_from file_to

其实,运行 git mv 就相当于运行了下面三条命令:

$ mv README.txt README
$ git rm README.txt
$ git add README

如此分开操作,Git 也会意识到这是一次改名,所以不管何种方式都一样。当然,直接用 git mv 轻便得多,不过有时候用其他工具批处理改名的话,要记得在提交前删除老的文件名,再添加新的文件名。

2.3 查看历史与状态(log与diff)

在 git 中, 每一次提交(commit)的修改, 都会被单独的保存起来. 也可以说 git 的中的所有文件都是一次次修改累积起来的. 文件好比楼房, 每个 commit 记录 了盖楼需添加或者拿走的材料. 整个施工过程也被记录了下来.

2.3.1: git log用于查看历史版本commit的信息的

比如这里历史上我进行了两次commit,那么在一个项目下进行git log:

>> D:\soft\git\learngit>git log
commit 1b6d23eaf9ad62ea9e468211c15682a6b4b242c6 (HEAD -> master)
Author: Jun <1924153192@qq.com>
Date:   Sat Nov 25 19:43:02 2017 +0800

    change in 1

commit 0e772909ce9abd01f4fa9b93642ac40db4a44cd1
Author: Jun <1924153192@qq.com>
Date:   Thu Nov 23 17:41:19 2017 +0800

    change1
  • 默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。
  • 每次信息会显示:Author是谁,Date是什么时候,以及那个时候的HEAD指向了哪里。
  • 注意这个命令只会显示commit的历时信息,比如现在你又对一个文件进行了修改,但是没有add或者没有commit,此时再执行git log不会有什么更新的。

另外git log还有许多参数可以带。详细参考链接

2.3.2: git diff命令:查看这次还没 add (unstaged) 的修改部分 和上个已经 commit 的文件有何不同

现在在上一次commit后,我修改了一下其中一个text.txt文件,添加了一行:

change in test

那么此时我还没有add,更没有commit,此时我执行git diff:

>> D:\soft\git\learngit>git diff
diff --git a/test.txt b/test.txt
index e69de29..9377178 100644
--- a/test.txt
+++ b/test.txt
@@ -0,0 +1 @@
+change in test
  • 显示我添加了一行change in test。 +为添加,-为删除。

可以通过git status来观看文件是否add了,那么假设我add的文件,但是没有commit文件,同样我想看一下差别怎么办呢?

现在我们先add一下上面那个文件:

>> D:\soft\git\learngit>git status
On branch master
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

        modified:   test.txt

no changes added to commit (use "git add" and/or "git commit -a")

>> D:\soft\git\learngit>git add test.txt

>> D:\soft\git\learngit>git status
On branch master
Changes to be committed:
  (use "git reset HEAD ..." to unstage)

        modified:   test.txt

那么已经 add 了这次修改, 文件变成了 “可提交状态” (staged), 我们可以在 diff 中添加参数 –cached 来查看修改:

>> D:\soft\git\learngit>git diff

>> D:\soft\git\learngit>git diff --cached
diff --git a/test.txt b/test.txt
index e69de29..9377178 100644
--- a/test.txt
+++ b/test.txt
@@ -0,0 +1 @@
+change in test
  • 可以看到,add后但是没有commit的时候,直接git diff是没有反应的,必须加上–cached才能看到差别。

2.4 回到从前的相关操作

2.4.1:修改已经commit的版本

有时候我们总会忘了什么, 比如已经提交了 commit 却发现在这个 commit 中忘了附上另一个文件.

现在假设我们刚才commit后,修改了1.txt,然后我们又去commit了一下,之后突然想起来1.txt被修改了,还没有add,那么肯定就没有被commit,这个时候我们想add后再commit,同时上一次的commit想忽略掉,可以进行下面这样:

$ git commit -m 'change in 1'
$ git add 1.txt
$ git commit --amend --no-edit   # "--no-edit": 不编辑, 直接合并到上一个 commit
$ git log --oneline # "--oneline": 每个 commit 内容显示在一行
  • “–oneline”: 每个 commit 内容显示在一行,也就是作者以及邮箱不显示。
>> D:\soft\git\learngit>git log --oneline
9c2c0fb (HEAD -> master) change in test
bbc6d6b change in 2
1b6d23e change in 1
0e77290 change1

2.4.2: reset 将一个add后的文件回到add之前

比如现在我们修改一下1.txt,然后add一下,之后在回去add之前:

>> D:\soft\git\learngit>git add 1.txt

>> D:\soft\git\learngit>git status -s
M  1.txt  #:staged  注意此时的M为绿色,代表已经modeified了

>> D:\soft\git\learngit>git reset 1.txt
Unstaged changes after reset:
M       1.txt

>> D:\soft\git\learngit>git status -s
 M 1.txt #:unstaged 注意此时的M为红色,代表还没有modeified

2.4.3: reset 回到 commit 之前(穿梭各个历时版本之中)

在穿梭到过去的 commit 之前, 我们必须了解 git 是如何一步一步累加更改的.
git学习笔记_第2张图片

每个 commit 都有自己的 id 数字号, HEAD 是一个指针, 指引当前的状态是在哪个 commit. 最近的一次 commit 在最右边, 我们如果要回到过去, 就是让 HEAD 回到过去并 reset 此时的 HEAD 到过去的位置.

现在我们把所以的文件全部add后再commit,查看一下现在有几个版本的commit了:

>> D:\soft\git\learngit>git log --oneline
42735bb (HEAD -> master) change in 1.1
9c2c0fb change in test
bbc6d6b change in 2
1b6d23e change in 1
0e77290 change1
  • 可以看到这里有5个历史的commit
  • 最近3次我们都在改1.txt里面的文件,看一下1.txt文件的内容:

    change in 1
    change 
    test reset

    现在我们想回到上一个版本怎么办呢?两种方式:一种直接

 "HEAD^"

另外一种使用它的id号,比如前面的 bbc6d6b 这些。

>> D:\soft\git\learngit>git reset --hard 9c2c0fb
HEAD is now at 9c2c0fb change in test

>> D:\soft\git\learngit>git log --oneline
9c2c0fb (HEAD -> master) change in test
bbc6d6b change in 2
1b6d23e change in 1
0e77290 change1
  • 可以看到head已经变化了位置;
  • 打开1.txt文件可以看到:

    change in 1
    change 

    发现里面的内容是不是也变化了,没错,回到历史版本,里面的内容是会一起变化的。

但是这样我们也发现上面的 change in 1.1也不见了,如果又想回去怎么办呢?使用 git reflog 可以查看所有的head

>> D:\soft\git\learngit>git reflog
9c2c0fb (HEAD -> master) HEAD@{0}: reset: moving to 9c2c0fb
42735bb HEAD@{1}: reset: moving to HEAD
42735bb HEAD@{2}: commit: change in 1.1
9c2c0fb (HEAD -> master) HEAD@{3}: commit (amend): change in test
9ee4d05 HEAD@{4}: commit: change in test
bbc6d6b HEAD@{5}: commit: change in 2
1b6d23e HEAD@{6}: commit: change in 1
0e77290 HEAD@{7}: commit (initial): change1

这样如果我们还想回到change in 1.1,那么直接索引他的编号即可:

>> D:\soft\git\learngit>git reset --hard 42735bb
HEAD is now at 42735bb change in 1.1

>> D:\soft\git\learngit>git log --oneline
42735bb (HEAD -> master) change in 1.1
9c2c0fb change in test
bbc6d6b change in 2
1b6d23e change in 1
0e77290 change1

我们又再次奇迹般的回到了change in 1.1。 有了编号,我们可以穿梭到任意一个版本的文件。此时去看1.txt,它的文件内容也改变了。

2.4.4: 使得单个文件回到从前 (checkout)

之前我们使用 reset 的时候是针对整个版本库, 回到版本库的某个过去. 不过如果我们只想回到某个文件的过去, 又该怎么办呢?

其实 checkout 最主要的用途并不是让单个文件回到过去, 我们之后会继续讲 checkout 在分支 branch 中的应用, 这一节主要讲 checkout 让文件回到过去.

我们现在的版本库中有三个文件:

1.txt
2.txt
test.txt

先看一下历史状态:

>> D:\soft\git\learngit>git log --oneline
42735bb (HEAD -> master) change in 1.1
9c2c0fb change in test
bbc6d6b change in 2
1b6d23e change in 1
0e77290 change1

我们仅仅要对 1.txt 进行回到过去操作, 回到 9c2c0fb: change in test 这一个 commit. 使用 checkout + id 9c2c0fb + – + 文件目录 1.txt, 我们就能将 1.txt 的指针 HEAD 放在这个时刻 9c2c0fb:

>> D:\soft\git\learngit>git checkout 9c2c0fb -- 1.txt

ok来看一下1.txt的内容:

change in 1
change 

发现回到了从前。看一下整个commit,发现依然在1.1的版本,唯独1.txt回到了test的版本。

D:\soft\git\learngit>git log --oneline
42735bb (HEAD -> master) change in 1.1
9c2c0fb change in test
bbc6d6b change in 2
1b6d23e change in 1
0e77290 change1

我们在 1.txt 加上一行内容 # I went back to change test 然后 add 并 commit 1.txt:

>> D:\soft\git\learngit>git add 1.txt

>> D:\soft\git\learngit>git commit -m "1.txt went to test and add comment for 1.txt "
[master ad3b7dc] 1.txt went to test and add comment for 1.txt
 1 file changed, 1 insertion(+), 1 deletion(-)

>> D:\soft\git\learngit>git log --oneline
ad3b7dc (HEAD -> master) 1.txt went to test and add comment for 1.txt
42735bb change in 1.1
9c2c0fb change in test
bbc6d6b change in 2
1b6d23e change in 1
0e77290 change1

可以看出, 不像 reset 时那样, 我们的 change 1.1 并没有消失, 但是 1.txt 却已经回去了过去, 并改写了未来.

2.5 git的一些技巧和窍门

自动补全

下载 Git 的源代码,进入 contrib/completion 目录,会看到一个 git-completion.bash 文件。将此文件复制到你自己的用户主目录中。,并把下面一行内容添加到你的 .bashrc 文件中:

source ~/.git-completion.bash

Linux 上则复制到 /etc/bash_completion.d/ 目录中。这两处目录中的脚本,都会在 Bash 启动时自动加载。

如果在 Windows 上安装了 msysGit,默认使用的 Git Bash 就已经配好了这个自动补全脚本,可以直接使用。

在输入 Git 命令的时候可以敲两次跳格键(Tab),就会看到列出所有匹配的可用命令建议:

$ git co
commit config

Git 命令别名

Git 并不会推断你输入的几个字符将会是哪条命令,不过如果想偷懒,少敲几个命令的字符,可以用 git config 为命令设置别名。来看看下面的例子:

$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status

现在,如果要输入git commit 只需键入 git ci 即可。而随着 Git 使用的深入,会有很多经常要用到的命令,遇到这种情况,不妨建个别名提高效率。

用这种技术还可以创造出新的命令,比方说取消暂存文件时的输入比较繁琐,可以自己设置一下:

$ git config --global alias.unstage 'reset HEAD --'

这样一来,下面的两条命令完全等同:

$ git unstage fileA
$ git reset HEAD fileA

3. 分支

3.1 :何谓分支

很多时候我们需要给自己或者客户用一个稳定的版本库, 然后同时还在开发另外一个升级版. 自然而然, 我们会想到把这两者分开处理, 用户使用稳定版, 我们开发我们的开发版. 不过 git 的做法却不一样, 它把这两者融合成了一个文件, 使用不同的分支来管理.

分支就是在当前的项目上独立一个head指针出来,有多少个分支就有多少个独立的head,不同的分支之间可以进行不同独立的操作。相当于可以多并行化的对用一个文件进行不同的操作而不会干扰,最后再进行合并即可。

在 Git 中提交时,会保存一个提交(commit)对象,该对象包含一个指向暂存内容快照的指针,包含本次提交的作者等相关附属信息,包含零个或多个指向该提交对象的父对象指针:首次提交是没有直接祖先的,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。

通常情况下,我们不建立分支的时候,就默认在初始化的主分支master上进行的,通常我们会把 master 当作最终的版本。开发新版本或者新属性的时候, 在另外一个分支上进行, 这样就能使开发和使用互不干扰了。

3.2 :新建分支

现在有一个项目,下面有两个文件:1.txt和2.txt,其中我commit了两次,没有分支,看一下状态就是:

>> D:\soft\git\test>git log --oneline --graph
* 3c9fcdc (HEAD -> master) change two
* 164e1f8 change one
  • 注意: –graph就可以把分支图都画出来。

下面我建立一个分支,使用的命令式 git branch

>> D:\soft\git\test>git branch testing

D:\soft\git\test>git log --oneline --graph
* 3c9fcdc (HEAD -> master, testing) change two
* 164e1f8 change one
  • 可以看到,在目前第二个commit下,就有两个HEAD了,目前的HEAD是指向master的。

假如我想把HEAD切换到testing怎么办呢?此时需要使用 git checkout 命令了。

>> D:\soft\git\test>git checkout testing
Switched to branch 'testing'

>> D:\soft\git\test>git log --oneline --graph
* 3c9fcdc (HEAD -> testing, master) change two
* 164e1f8 change one
  • 可以看到切换后会有提示,同时HEAD也变化了。

现在来说说分支有什么用,说之前我们先看看我的两个文件中的内容:

1.txt:
change 1-1
change 1-2

2.txt:
change 2-1
change 2-2

好,现在我们相在testing分支上修改内容,同时master住分支上先不变,相当于master是一个1.0版本在运行着,而我们正在研发2.0版本,待研发好了再合并到master上去。

简单修改下1.txt的内容如下:

change 1-1
change 1-2
change in testing branch 

然后看一下状态:

>> D:\soft\git\test>git status
On branch testing
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

        modified:   1.txt

no changes added to commit (use "git add" and/or "git commit -a")

>> D:\soft\git\test>git add .

>> D:\soft\git\test>git commit -m "change in tesing"
[testing c01b5b8] change in tesing
 1 file changed, 2 insertions(+), 1 deletion(-)

>> D:\soft\git\test>git log --oneline --graph
* c01b5b8 (HEAD -> testing) change in tesing
* 3c9fcdc (master) change two
* 164e1f8 change one
  • 很简单,首先我们在分支tesing下面,提醒我修改了文件没add,add完了之后看一下状态还是在分支tesing,没问题。

下面关键来了 ,我们说我们在testing分支下操作的文件,理论上如果我们切换到主分支master下的时候,里面的1.txt的文件没有变化,下面来看一下,先切换分支:

>> D:\soft\git\test>git checkout master
Switched to branch 'master'

之后我们打开了 1.txt 发现如下:

change 1-1
change 1-2

神奇吧,加的 change in testing branch 内容并没有显示出来。

好了假设我们在testing分支上开发完了,想合并到branch上去,可以如下:

>> D:\soft\git\test>git checkout master
Already on 'master'

>> D:\soft\git\test>git merge testing
Updating 3c9fcdc..c01b5b8
Fast-forward
 1.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
  • 首先我们需要切换到master上(当然前面已经做这件事了);
  • 其次使用合并命令(merge)
  • 合并完了我们可以看到说1.txt添加了内容。这个时候我们再去打开看一下 1.txt 发现:

    change 1-1
    change 1-2
    change in testing branch 

    内容更改了吧。

为了把分支合并过程显示出来,可以如下添加 –no-ff 参数:

>> D:\soft\git\test>git merge --no-ff -m "merge testing to master" testing
Merge made by the 'recursive' strategy.
 1.txt | 1 +
 1 file changed, 1 insertion(+)

>> D:\soft\git\test>git log --oneline --graph
*   51ebeb2 (HEAD -> master) merge testing to master
|\
| * 5ff866d (testing) change three
|/
* c01b5b8 change in tesing
* 3c9fcdc change two
* 164e1f8 change one
  • 可以看到,这个分支的过程都显示出来了,怎么分支的,在哪里合并到,都有。

当该testing分支没有使用价值的时候可以删除它,使用 git branch -d 参数,如下:

>> D:\soft\git\test>git branch -d testing
Deleted branch testing (was 5ff866d).

再来看一下状态:

>> D:\soft\git\test>git log --oneline --graph
*   51ebeb2 (HEAD -> master) merge testing to master
|\
| * 5ff866d change three
|/
* c01b5b8 change in tesing
* 3c9fcdc change two
* 164e1f8 change one
  • 和上面的相比可以发现,testing分支的head已经没有了。

3.3 :分支存在冲突怎么办

有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一个文件的同一部分,Git 就无法干净地把两者合到一起。比如上面这个例子,我在新建了testing分支后,在testing分支中我去修改了1.txt,但是有个人在这个master分支中也跑去修改了1.txt,那么再去合并就会出现问题,下面模拟一下,实现我们建个分支testing,然后在testing修改1.txt,在master分支中也修改1.txt,之后合并看看:

先切换到testing分支,然后人为修改下1.txt
>> D:\soft\git\test>git checkout testing
Switched to branch 'testing'
>> D:\soft\git\test>git add .
>> D:\soft\git\test>git commit -m "change in testing 1.1"

再切换到master分支,然后人为修改下1.txt
>> D:\soft\git\test>git checkout master
Switched to branch 'master'

>> D:\soft\git\test>git add .

>> D:\soft\git\test>git commit -m "change in master"
[master 8cc6917] change in master
 1 file changed, 2 insertions(+), 1 deletion(-)

>> D:\soft\git\test>git merge --no-ff -m "merge testing to master" testing
Auto-merging 1.txt
CONFLICT (content): Merge conflict in 1.txt
Automatic merge failed; fix conflicts and then commit the result.
  • Git 作了合并,但没有提交,它会停下来等你解决冲突。要看看哪些文件在合并时发生冲突,可以用 git status 查阅:

    >> D:\soft\git\test>git status
    On branch master
    You have unmerged paths.
      (fix conflicts and run "git commit")
      (use "git merge --abort" to abort the merge)
    
    Unmerged paths:
      (use "git add ..." to mark resolution)
    
            both modified:   1.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")

    任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出 。Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。这个时候我们去打开1.txt,可以看到此文件包含类似下面这样的部分:

change 1-1
change 1-2
change in testing branch 
change in testing branch 
<<<<<<< HEAD
change in master branch 
=======
change in testing branch 
>>>>>>> testing

这个时候你需要手动去调整了。比如如果这里我把两者都保留,也就是testing分支里面的修改和master里面的修改都保留,这个时候我们只需要下面这三行全部手动删除即可。

<<<<<<< HEAD
=======
>>>>>>> testing

在解决了所有文件里的所有冲突后,运行 git add 将把它们标记为已解决状态。

>> D:\soft\git\test>git add .

>> D:\soft\git\test>git status
On branch master
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

        modified:   1.txt

可以看到冲突基本解决。我们再来看下状态:

>> D:\soft\git\test>git commit -m "change all"
[master 49f7c12] change all

>> D:\soft\git\test>git log --oneline --graph
*   49f7c12 (HEAD -> master) change all
|\
| * 965c38c (testing) change in tesing 1
* | 8cc6917 change in master
|/
*   51ebeb2 merge testing to master
|\
| * 5ff866d change three
|/
* c01b5b8 change in tesing
* 3c9fcdc change two
* 164e1f8 change one
  • 注意几个变化:可以看到在master和testing分支上,各有一个*, 也就是输出的第三行和第四行,这就表示的是你在master和testing上都修改过文件,最后再合并到一起的。

3.4 :分支的管理

对于分支,还有一些其他方面的管理方法,下面简单列举一下:

  • 直接使用git branch 命令,不加任何参数的话,它会给出当前所有分支的清单;

    $ git branch
      iss53
    * master
      testing

    其中带*号的为当前所在分支;

  • 加上 -v 参数,可以查看各个分支最后一个提交对象的详细信息。

  • 想要查看那些分支已经被合并了,可以用 –merged ; 查看哪些没有被合并 –no-merged ;这样做的好处可以把那些已经合并的子分支给删除掉,避免冗余。删除命令为: git branch -d 。有的分支没有合并,那么如果用 -d 删除时会报错,这个时候想强制删除,可以用大写D, -D 参数。

3.5 :利用分支进行开发工作的一般流程

本节内容的官方教程讲的很好,很通用,值得了解。这里摘取一下。

现在我们已经学会了新建分支和合并分支,可以(或应该)用它来做点什么呢?在本节,我们会介绍一些利用分支进行开发的工作流程。而正是由于分支管理的便捷,才衍生出了这类典型的工作模式,你可以根据项目的实际情况选择一种用用看。

长期分支

由于 Git 使用简单的三方合并,所以就算在较长一段时间内,反复多次把某个分支合并到另一分支,也不是什么难事。也就是说,你可以同时拥有多个开放的分支,每个分支用于完成特定的任务,随着开发的推进,你可以随时把某个特性分支的成果并到其他分支中。

许多使用 Git 的开发者都喜欢用这种方式来开展工作,比如仅在 master 分支中保留完全稳定的代码,即已经发布或即将发布的代码。与此同时,他们还有一个名为 develop 或 next 的平行分支,专门用于后续的开发,或仅用于稳定性测试 — 当然并不是说一定要绝对稳定,不过一旦进入某种稳定状态,便可以把它合并到 master 里。这样,在确保这些已完成的特性分支(短期分支,比如之前的 iss53 分支)能够通过所有测试,并且不会引入更多错误之后,就可以并到主干分支中,等待下一次的发布。

本质上我们刚才谈论的,是随着提交对象不断右移的指针。稳定分支的指针总是在提交历史中落后一大截,而前沿分支总是比较靠前(见图)。

这里写图片描述

或者把它们想象成工作流水线,或许更好理解一些,经过测试的提交对象集合被遴选到更稳定的流水线:
git学习笔记_第3张图片

你可以用这招维护不同层次的稳定性。某些大项目还会有个 proposed(建议)或 pu(proposed updates,建议更新)分支,它包含着那些可能还没有成熟到进入 next 或 master 的内容。这么做的目的是拥有不同层次的稳定性:当这些分支进入到更稳定的水平时,再把它们合并到更高层分支中去。再次说明下,使用多个长期分支的做法并非必需,不过一般来说,对于特大型项目或特复杂的项目,这么做确实更容易管理。

特性分支

在任何规模的项目中都可以使用特性(Topic)分支。一个特性分支是指一个短期的,用来实现单一特性或与其相关工作的分支。可能你在以前的版本控制系统里从未做过类似这样的事情,因为通常创建与合并分支消耗太大。然而在 Git 中,一天之内建立、使用、合并再删除多个分支是常见的事。

我们在上节的例子里已经见过这种用法了。我们创建了 iss53 和 hotfix 这两个特性分支,在提交了若干更新后,把它们合并到主干分支,然后删除。该技术允许你迅速且完全的进行语境切换 — 因为你的工作分散在不同的流水线里,每个分支里的改变都和它的目标特性相关,浏览代码之类的事情因而变得更简单了。你可以把作出的改变保持在特性分支中几分钟,几天甚至几个月,等它们成熟以后再合并,而不用在乎它们建立的顺序或者进度。

现在我们来看一个实际的例子。请看图下,由下往上,起先我们在 master 工作到 C1,然后开始一个新分支 iss91 尝试修复 91 号缺陷,提交到 C6 的时候,又冒出一个解决该问题的新办法,于是从之前 C4 的地方又分出一个分支 iss91v2,干到 C8 的时候,又回到主干 master 中提交了 C9 和 C10,再回到 iss91v2 继续工作,提交 C11,接着,又冒出个不太确定的想法,从 master 的最新提交 C10 处开了个新的分支 dumbidea 做些试验。

git学习笔记_第4张图片

现在,假定两件事情:我们最终决定使用第二个解决方案,即 iss91v2 中的办法;另外,我们把 dumbidea 分支拿给同事们看了以后,发现它竟然是个天才之作。所以接下来,我们准备抛弃原来的 iss91 分支(实际上会丢弃 C5 和 C6),直接在主干中并入另外两个分支。最终的提交历史将变成图下这样:
git学习笔记_第5张图片

请务必牢记这些分支全部都是本地分支,这一点很重要。当你在使用分支及合并的时候,一切都是在你自己的 Git 仓库中进行的 — 完全不涉及与服务器的交互。

4. github的简单使用

github是一个在线项目托管平台,里面有众多别人开源的项目,非常好。

有时候我们想把我们的项目不光存在本地,还想存在网上,或者共享出来和大家一起使用或者修改之类的,就可以使用github了。国内也有一个在线托管平台 码云,也很强大,提供很多服务。

下面简单介绍如何把自己的项目上传到github上去。

传上去首先得拥有一个账户,这个就不说了,注册一个即可。接着我们需要在github主页新建一个库:

git学习笔记_第6张图片

比如这里新建一个后就是这样,这里根据提醒,在自己的本地客服端输入下面的两行,前提是客户端上当前目录要索引到你的项目目录下

  • 第一行就是表面你要上传的网络地址;
  • 第二行就是把你的项目推到github上去;

这里还是以前几节的那个例子为例,首先我们来看看这个例子:

git学习笔记_第7张图片

运行这两句以后,你的项目就推到了github上了,在github上刷新一下看看:

git学习笔记_第8张图片

可以看到有了。因为我们推送的是整个项目,而且可以看到推送的是主分支master(注意项目中的次分支,比如这里有testing分支是没有没推上去的),而且项目里面的隐藏文件夹 .git 也会被推上去。需要注意的是,电脑如果是第一次推的话,可能会弹出让你输入github的账号密码信息,输入进去就好了(以windows系统为例的)。

git学习笔记_第9张图片

推送分支上去

假设我们想把testing分支也推上去怎么办呢,直接再推送一遍。

>> D:\soft\git\test>git push -u origin testing
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/jungrea/helloworld.git
 * [new branch]      testing -> testing
Branch 'testing' set up to track remote branch 'testing' from 'origin'.

git学习笔记_第10张图片

这个时候我们去看下面的内容,可以看到,分支不同,内容是不一样的,完全和本地的是一模一样。

查看本地编辑的历时信息

同时我们可以在网页上观看我们commit的过的历史信息,像下面这样:

git学习笔记_第11张图片

然后可以看到:
git学习笔记_第12张图片

删除一个项目

如果我们想删除一个已经建立的项目,可以如下:
git学习笔记_第13张图片

之后拉倒最下面,删除即可,点击后输入库的名字,就删除了。再去查看,就发现没有这个项目了。
git学习笔记_第14张图片

你可能感兴趣的:(c++教程)