Git基础:使用指南

news2024/12/23 13:55:55

Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆,在本地机器上拷贝一个完整的Git仓库。

一、版本管理

1.1 创建版本库

版本库(repository)可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,Git能对每个文件的修改、删除进行跟踪,以便在将来某个时刻可以进行还原。

通过git init命令将目录变成Git仓库

$ git init
Initialized empty Git repository in C:/Users/yyz/Desktop/test/.git/ 

此时一个普通目录就变成了Git仓库,可以发现目录下多了一个.git文件夹,这个目录是Git来跟踪管理版本库的。

1.2 添加文件到版本库

现在向test目录或其子目录下创建一个 readme.txt 文件,内容如下:

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

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

$ git add readme.txt 

第二步,用命令git commit把文件提交到Git仓库(-m后面输入的是本次提交的说明)

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

执行git commit命令后会告诉你,1 file changed:1个文件被改动(新添加的readme.txt文件);2 insertions:插入了两行内容(readme.txt有两行内容)。

为什么Git添加文件需要addcommit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:

$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files." 

1.3 版本管理

现在已经成功添加并提交了一个 readme.txt 文件,接着继续修改 readme.txt,内容如下:

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

运行git status命令查看状态:

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

	modified:   readme.txt

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

git status命令可以查看仓库当前的状态,通过输出得知 readme.txt 被修改过了,但还没有准备提交的修改。

假如你休假两周之后继续上班,但已经记不清上次怎么修改的 readme.txt,这时可以用git diff这个命令看看:

$ git diff readme.txt 
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
 Git is free software. 

git diff即查看difference,显示的格式是Unix通用的diff格式,通过输出看到,我们在第一行添加了一个distributed单词。现在就知道了上一次对 readme.txt 做了什么修改,现在对文件进行提交。

$ git add readme.txt 

执行git commit之前,再查看一下当前仓库的状态

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

	modified:   readme.txt 

git status告诉我们,将要被提交的修改包括 readme.txt,下一步提交

$ git commit -m "add distributed"
[master e475afc] add distributed
 1 file changed, 1 insertion(+), 1 deletion(-) 

提交后,再用git status命令查看仓库的当前状态:

$ git status
On branch master
nothing to commit, working tree clean 

可以看到当前没有需要提交的修改,工作目录是干净的。

1.4 版本回退

可以把Git中的commit理解成“快照”,每当觉得文件修改到一定程度的时候,就可以保存一个快照,当你把文件改乱或者误删,还可以从最近的一个commit恢复。

在实际工作中,我们怎么知道 readme.txt 文件一共有几个版本被提交到Git仓库里了呢?这时就用到git log命令了。

$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:06:15 2018 +0800

    append GPL

commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:03:36 2018 +0800

    add distributed

commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 20:59:18 2018 +0800

    wrote a readme file 

git log命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是append GPL,上一次是add distributed,最早的一次是wrote a readme file

如果嫌输出信息太多,可以加上--pretty=oneline参数:

$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
e475afc93c209a690c39c13a46716e8fa000c366 add distributed
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file 

上面一大串类似1094adb...的是commit id(版本号),它是一个SHA1计算出来的一个非常大的数字,因为Git是分布式的版本控制系统,在工作中可能多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号就会发生冲突。

那么如何把 readme.txt 回退到上一个版本,即add distributed版本呢?

首先,Git必须知道当前版本是哪个版本,在Git中用HEAD表示当前版本,也就是最新的提交1094adb...,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

要把当前版本append GPL回退到上一个版本add distributed,就可以使用git reset命令:

$ git reset --hard HEAD^
HEAD is now at e475afc add distributed 

查看 readme.txt 的内容可以发现已经回到上一个版本了。

$ cat readme.txt
Git is a distributed version control system.
Git is free software. 

git log再看看现在版本库的状态:

$ git log
commit e475afc93c209a690c39c13a46716e8fa000c366 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:03:36 2018 +0800

    add distributed

commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 20:59:18 2018 +0800

    wrote a readme file 

会发现最新的那个版本append GPL不见了,好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,怎么办?其实只要上面的命令行窗口还没有被关掉,你就可以找到append GPLcommit id,然后回到未来的版本:

$ git reset --hard 1094a
HEAD is now at 83b0afe append GPL 

版本号写前几位就可以,Git会自动去找(不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个)

再次查看 readme.txt 的内容,发现果然回来了。

$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL. 

假如你回退到了某个版本,关掉了电脑,第二天想恢复到新版本但找不到新版本的commit id怎么办?

Git提供了一个命令git reflog用来记录你的每一次命令:

$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file 

可以看到append GPL的commit id是1094adb,现在又可以回到新版本了。

Git的版本回退速度之所以快,是因为Git在内部有个指向当前版本的HEAD指针,当回退版本的时候,Git仅仅是把HEAD从指向append GPL改为指向add distributed,顺便把工作区的文件更新了。

┌────┐
│HEAD│
└────┘
   │
   └──> ○ append GPL
        │
        ○ add distributed
        │
        ○ wrote a readme file
--------------------------------------
# 改为指向 add distributed 

┌────┐
│HEAD│
└────┘
   │
   │    ○ append GPL
   │    │
   └──>add distributed
        │
        ○ wrote a readme file 

二、基础知识

2.1 工作区和暂存区

Git和其他版本控制系统的不同之处就是有暂存区的概念。暂存区是Git非常重要的概念,弄明白了暂存区,就弄明白了Git的很多操作到底干了什么。

  • 工作区(Working Directory)
    工作区就是你在电脑里能看到的目录,比如我的 test 文件夹就是一个工作区。

  • 版本库(Repository)
    工作区有一个隐藏目录.git,这是Git的版本库,不算工作区。版本库里存了很多东西,其中最重要的就是称为stage的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

在这里插入图片描述

前面讲了向Git版本库里提交文件分两步执行:

  1. git add把文件添加进去,实际上就是把文件修改添加到暂存区;
  2. git commit提交更改,实际上就是把暂存区的内容提交到当前分支。

简单理解为,需要提交的文件修改全部放到暂存区,然后,一次性提交暂存区的所有修改。因为我们创建Git版本库时,Git自动为我们创建了一个master分支,所以git commit就是往master分支上提交更改。

下面开始实战,先对 readme.txt 加上一行内容,然后再新建一个LICENSE文本文件。

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage. 

先用git status查看一下状态:

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

	modified:   readme.txt

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

	LICENSE

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

Git告诉我们 readme.txt 被修改了,而 LICENSE 还从来没有被添加过,所以它的状态是Untracked

现在把readme.txtLICENSE都添加后,再查看一下状态:

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

	new file:   LICENSE
	modified:   readme.txt 

现在,暂存区的状态就变成这样了:

在这里插入图片描述

所以,git add命令就是把要提交的修改放到暂存区(Stage),然后执行git commit就可以一次性把暂存区的所有修改提交到分支。提交后如果没有对工作区做任何修改,那么工作区就是“干净”的。

$ git status
On branch master
nothing to commit, working tree clean 

现在版本库变成了这样,暂存区就没有任何内容了:

在这里插入图片描述

2.2 管理修改

在Git中是对修改进行跟踪并管理,而非文件。比如新增或删除了一行,更改了某些字符,甚至创建一个新文件,这都算是修改,都会被Git跟踪并管理。下面通过实验来说明。

第一步,为 readme.txt 加一行内容:

$ cat readme.txt
Git is a distributed version control system.
Git tracks changes. 

然后添加到暂存区:

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

然后再修改 readme.txt:

$ cat readme.txt 
Git is a distributed version control system.
Git tracks changes of files. 

提交:

$ git commit -m "git tracks changes"
[master 519219b] git tracks changes
 1 file changed, 1 insertion(+) 

提交后,再看看状态:

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

	modified:   readme.txt

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

会发现第一次修改被提交了,而第二次的修改没有被提交。这是因为Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是第二次修改并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了。

2.3 撤销修改

假如你在readme.txt中添加了一行内容,在提交前你又删掉这一行内容,手动把文件恢复到上一个版本的状态。用git status查看一下:

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

	modified:   readme.txt

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

可以发现Git会告诉你,git checkout -- file可以丢弃工作区的修改:

$ git checkout -- readme.txt 

命令git checkout -- readme.txt意思就是,把 readme.txt 文件在工作区的修改全部撤销,这里有两种情况:

  1. readme.txt 自修改后还没有被放到暂存区,现在撤销修改就回到和版本库一模一样的状态;

  2. readme.txt 已经添加到暂存区后,又作了修改,现在撤销修改就回到添加到暂存区后的状态。

总之就是让文件回到最近一次git commitgit add时的状态。

git checkout -- file命令中的 -- 很重要,没有--,就变成了 “切换分支” 的命令。

假如已经把错误代码git add到暂存区了,但在commit之前发现了这个问题。用git status查看一下,修改只是添加到了暂存区,还没有提交:

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

	modified:   readme.txt 

Git告诉我们,用命令git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区:

$ git reset HEAD readme.txt
Unstaged changes after reset:
M	readme.txt 

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

查看一下状态,现在暂存区是干净的,工作区有修改:

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

	modified:   readme.txt 

2.4 删除文件

在Git中删除也算修改操作,例如新建一个文件test.txt并提交:

$ git add test.txt

$ git commit -m "add test.txt"
[master b84166e] add test.txt
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt 

然后再把 test.txt 文件删除,这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,使用git status命令查看哪些文件被删除了:

$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	deleted:    test.txt

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

现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit

$ git rm test.txt
rm 'test.txt'

$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
 1 file changed, 1 deletion(-)
 delete mode 100644 test.txt 

现在,文件就从版本库中被删除了。

先手动删除文件,然后使用git rm <file>git add<file>效果是一样的。

另一种情况就是误删了,但是版本库里还存有该文件,可以用git checkout命令把误删的文件恢复到最新版本。

$ git checkout -- test.txt 

三、远程仓库

Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。这就需要一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。而这个服务器就是GitHub,这个网站就是提供Git仓库托管服务的,所以只需注册一个GitHub账号,就可以免费获得Git远程仓库。

3.1 设置ssh

由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以要先设置好ssh。

第1步: 创建SSH Key

在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsaid_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

$ ssh-keygen -t rsa -C "youremail@example.com" 

然后一路回车即可。完成后可以看到.ssh目录里有id_rsaid_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

第2步: 登陆GitHub,打开“Account settings”,“SSH Keys”页面,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:

点“Add Key”,可以看到Key已经添加:

GitHub通过SSH Key来识别是你推送的还是别人冒充的,GitHub还支持添加多个Key。假定你有两台电脑,分别在公司和家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。

3.2 添加远程库

现在在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作。

首先登陆GitHub,创建一个名为 test 的新仓库:

现在GitHub上的test仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后把本地仓库的内容推送到GitHub仓库。

现在根据GitHub的提示,在本地的test仓库下运行命令:

$ git remote add origin git@github.com:yangyezhuang/test.git 

添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的。

之后用git push命令把本地库的内容推送到远程,即把当前分支master推送到远程。

$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (20/20), 1.64 KiB | 560.00 KiB/s, done.
Total 20 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:michaelliao/learngit.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'. 

由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

推送成功后可以看到远程库的内容已经和本地一模一样。至此,只要本地作了提交,就可以把本地master分支的最新修改推送至GitHub

$ git push origin master 

删除远程库

如果添加的时候地址写错了,或者就是想删除远程库,可以用git remote rm <name>命令。使用前,建议先用git remote -v查看远程库信息:

$ git remote -v
origin  git@github.com:michaelliao/learn-git.git (fetch)
origin  git@github.com:michaelliao/learn-git.git (push) 

然后根据名字删除,比如删除origin

$ git remote rm origin 

此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。

3.3 克隆远程库

登陆GitHub,找到你想要克隆的仓库,可以看到GitHub给出了ssh地址,复制该地址。

使用git clone命令克隆一个本地库:

$ git clone git@github.com:yangyezhuang/Matplotlib.git
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3
Receiving objects: 100% (3/3), done. 

然后进入Matplotlib目录可以看到,项目已经克隆到本地了。

如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。

你也许还注意到,GitHub给出的地址不止一个,还可以用https://github.com/yangyezhuang/Matplotlib.git这样的地址。Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。使用https除了速度慢以外,每次推送都必须输入口令。

四、分支管理

4.1 创建与合并分支

什么是分支?有了分支,你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,既安全,又不影响别人工作。合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

Git会把每次提交串成一条时间线,这条时间线就是一个分支。git init时Git会自动创建一个master分支,即主分支。HEAD严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。

当创建新的dev分支时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

假如我们在dev上的工作完成了,就可以把dev合并到master上。其实就是直接把master指向dev的当前提交:

合并完分支后也可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后就剩下一条master分支:

下面开始实战:

首先创建一个 dev 分支,用git checkout -b命令创建并切换分支,相当于git branch dev + git checkout dev

$ git checkout -b dev
Switched to a new branch 'dev' 

然后用git branch命令查看当前分支,该命令会列出所有分支,当前分支前面会标一个*号。

$ git branch
* dev
  master 

现在就可以在dev分支上开发与提交了,对 readme.txt 加上一行,然后提交:

$ git cat readme.txt 
Creating a new branch is quick.

$ git add readme.txt 
$ git commit -m "branch test"
[dev b17d20e] branch test
 1 file changed, 1 insertion(+) 

dev分支的工作完成,现在切换回master分支:

$ git checkout master
Switched to branch 'master' 

切换回master分支后,查看readme.txt文件发现刚才添加的内容不见了,因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:

现在用git merge命令把dev分支的内容合并到master分支上:

$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+) 

合并后查看 readme.txt 发现和dev分支的最新提交是一样的。现在就可以删除dev分支了

注意:上面的Fast-forward信息,意思是,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

因为切换分支使用git checkout <branch>,撤销修改则是git checkout -- <file>,有点令人迷惑。因此,新版本的Git提供了新的git switch命令来切换分支。

# 创建并切换到新的分支:
$ git switch -c dev

# 直接切换到已有分支:
$ git switch master 

4.2 删除分支

开发中每添加一个新功能,最好新建一个分支开发,完成后,合并,最后删除该分支。

Git中使用git branch -d <file>命令删除分支:

如果commit提交了,但是还没有合并,这时删除分支的话Git会报错:

$ git branch -d test
error: The branch 'test' is not fully merged.
If you are sure you want to delete it, run 'git branch -D test'. 

提示test分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数。

$ git branch -D test
Deleted branch test (was 287773e). 

4.3 分支冲突

当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。

准备新的feature1分支:

$ git switch -c feature1
Switched to a new branch 'feature1' 

修改 readme.txt,在feature1分支上提交:

$ git cat readme.txt
Creating a new branch is quick AND simple.

$ git add readme.txt
$ git commit -m "AND simple"
[feature1 14096d0] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-) 

切换到master分支,在master分支上也对 readme.txt 进行修改,提交:

$ git cat readme.txt 
Creating a new branch is quick & simple.

$ git add readme.txt 
$ git commit -m "& simple"
[master 5dc6824] & simple
 1 file changed, 1 insertion(+), 1 deletion(-) 

现在,master分支和feature1分支各自都分别有新的提交,变成了这样:

这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:

$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result. 

通过输出可以看到,readme.txt 文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

	both modified:   readme.txt

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

可以直接查看 readme.txt的内容:

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1 

可以看到,Git用<<<<<<<=======>>>>>>>标记出不同分支的内容,我们需要手动修改后再进行提交:

Creating a new branch is quick and simple. 

再提交,最后删除feature1分支:

$ git add readme.txt 
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed

$ git branch -d feature1
Deleted branch feature1 (was 14096d0). 

现在,master分支和feature1分支变成了下图所示:

用带参数的git log也可以看到分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit
*   cf810e4 (HEAD -> master) conflict fixed
|\  
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/  
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file 

4.4 分支管理策略

通常合并分支时,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。下面实战一下--no-ff方式的git merge

首先创建并切换dev分支:

$ git switch -c dev
Switched to a new branch 'dev' 

修改readme.txt文件,并提交一个新的commit:

$ git add readme.txt 
$ git commit -m "add merge"
[dev f52c633] add merge
 1 file changed, 1 insertion(+) 

现在切换回master

$ git switch master
Switched to branch 'master' 

准备合并dev分支,请注意--no-ff参数,表示禁用Fast forward

$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+) 

因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。合并后用git log看看分支历史:

$ git log --graph --pretty=oneline --abbrev-commit
*   e1e9c68 (HEAD -> master) merge with no-ff
|\  
| * f52c633 (dev) add merge
|/  
*   cf810e4 conflict fixed
... 

可以看到,不使用Fast forward模式,merge后就像这样:

在实际开发中,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活,干活都在dev分支上。

4.5 Bug分支

开发中会经常遇到bug需要修复,修复bug时,我们可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

当你接到一个修复bug的任务时,当你准备创建一个分支issue-101来修复它时,突然想起来,当前正在dev上进行的工作还没有提交,Git提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge 

现在用git status查看工作区发现是干净的,因此可以放心地创建分支来修复bug。

首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git checkout -b issue-101
Switched to a new branch 'issue-101' 

现在在issue-101分支上将bug修复,然后commit提交,然后切换到master分支,并完成合并,最后删除issue-101分支:

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-) 

现在回到dev分支继续干活,

$ git switch dev
Switched to branch 'dev'

$ git status
On branch dev
nothing to commit, working tree clean 

工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看:

$ git stash list
stash@{0}: WIP on dev: f52c633 add merge 

工作现场还在,Git把stash内容存在某个地方了,有两个办法可以恢复:

  • git stash apply恢复,但是恢复后,stash内容并不删除,需要用git stash drop删除。

  • git stash pop,恢复的同时把stash内容也删了。

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

	new file:   hello.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   readme.txt

Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a) 

再用git stash list查看,就看不到任何stash内容了。你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:

$ git stash apply stash@{0} 

在master分支上修复了bug后,我们要想一想,dev分支是早期从master分支分出来的,所以,这个bug其实在当前dev分支上也存在。

那怎么在dev分支上修复同样的bug?重复操作一次?但是Git提供了更简单的方法,同样的bug,要在dev上修复,我们只需要把4c805e2 fix bug 101这个提交所做的修改“复制”到dev分支。注意:我们只想复制4c805e2 fix bug 101这个提交所做的修改,并不是把整个master分支merge过来。

Git提供了一个cherry-pick命令,让我们能复制一个特定的提交到当前分支:

$ git branch
* dev
  master
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-) 

Git自动给dev分支做了一次提交,注意这次提交的commit是1d4b803,它并不同于master的4c805e2,因为这两个commit只是改动相同,但确实是两个不同的commit。用git cherry-pick,我们就不需要在dev分支上手动再把修bug的过程重复一遍。当然也可以直接在dev分支上修复bug,然后在master分支上“重放”。

五、总结

  • 使用命令git init,初始化Git仓库

  • 使用命令git add <file>,可反复多次使用,添加多个文件

  • 使用命令git commit -m <message>完成提交

  • 使用命令git status,查看工作区状态

  • 如果git status告诉你有文件被修改过,用git diff可以查看修改内容

  • HEAD指向当前版本,使用命令git reset --hard commit_id可以切换版本

  • 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本

  • 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本

  • 每次修改,如果不用git add到暂存区,那就不会加入到commit中。

  • 当改乱了某个文件的内容,想丢弃工作区的修改时,用命令git checkout -- file

  • 当改乱了某个文件的内容并添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。

  • 假设把错的文件从暂存区提交到了版本库,想要撤销本次提交,只能进行版本回退。

  • 命令git rm用于删除版本库里的文件。如果不小心误删了,但是文件已经提交到版本库,那么就可以从版本库将文件进行还原,但是只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。

  • 要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git

  • 关联一个远程库时必须给远程库指定一个名字,origin是默认习惯命名;

  • 关联后,使用命令git push -u origin master第一次推送master分支的所有内容;此后,每次本地提交后,就可以使用命令git push origin master推送最新修改;

  • 使用git clone命令将远程仓库克隆到本地。

  • Git支持多种协议,包括https,但ssh协议速度最快。

  • 查看分支:git branch

  • 创建分支:git branch <name>

  • 切换分支:git checkout <name>git switch <name>

  • 创建+切换分支:git checkout -b <name>git switch -c <name>

  • 合并某分支到当前分支:git merge <name>

  • 删除分支:git branch -d <name>-D强行删除)

  • 查看分支合并图:git log --graph

  • 使用git stash可以将当前内容“储藏”起来,然后去修复bug,修复后,再git stash pop,回到工作现场。

  • 在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>把bug提交的修改“复制”到当前分支,避免重复劳动。

感谢大家的耐心阅读,如有建议请私信或评论留言

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1973057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型的架构参数是指定义模型基本结构和组成的各种参数,这些参数对模型的性能、训练效率和泛化能力具有重要影响。以下是对大模型架构参数的详细介绍

大模型架构参数 大模型的架构参数是指定义模型基本结构和组成的各种参数&#xff0c;这些参数对模型的性能、训练效率和泛化能力具有重要影响。以下是对大模型架构参数的详细介绍&#xff1a; 一、基本结构和组成 层数&#xff1a;模型的层数是指模型中全连接网络或特定结构…

vue3直播视频流easy-player

vue3直播视频流easy-player <script src"/easyPlayer/EasyPlayer-element.min.js"></script> easyPlayer文件下载地址 https://download.csdn.net/download/weixin_42120669/89605739 <template><div class"container"><div …

Vue进阶之Vue无代码可视化项目(九)

Vue无代码可视化项目—补充内容 背景介绍、方案设计Canvas Table创建一个新的vue项目普通表格的效果Canvas上手Canvas画表格-画基本表格CanvasTable处理事件系统CanvasTable表格滚动Vue组件封装思想拖拽组件 —smooth-dndDndDemo1.vueDndContainer.jsCanvasTable封装CanvasTabl…

LDR6020 iPad皮套一体式键盘充电方案解析

在移动办公与学习的浪潮中&#xff0c;iPad凭借其强大的性能与便携性&#xff0c;成为了越来越多人的首选设备。然而&#xff0c;随着工作与学习任务的日益复杂&#xff0c;单一的触控操作已难以满足高效、精准的需求。因此&#xff0c;搭配一款优秀的键盘成为了提升iPad使用体…

月木学途开发 3.1搭建CentOS虚拟机

安装CentOS 下载地址 &#xff1a;https://mirrors.aliyun.com/centos-vault/?spma2c6h.13651104.0.0.5f6612b2O7Cy9G 选择7.6.1810——isos——x86_64——CentOS-7-x86_64-DVD-1810.iso 安装 VMWare虚拟机 下载 下载地址&#xff1a;https://www.vmware.com/products/desktop…

分享c语言中一些实用的函数2

目录 一.头文件 1.sqrt()函数 2.sin&#xff0c;cos&#xff0c;tan函数 附加:宏定义π 3.exp函数 4.fabs函数 5.fmax函数 6.floor函数 7.log函数 附加&#xff1a;求一个数是几为数(运用floor函数和log函数) 8.pow函数 二.头文件 1.abs函数 附加: 一.头文件<…

详解HTTP协议版本(HTTP/1.0、1.1、2.0、3.0区别)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

数据采集器

目录 1. 采集Redis 2. 采集MySQL 3. 采集容器 1. 采集Redis 出口商和集成 |普罗 米修斯 (prometheus.io) 发布 奥利弗006/redis_exporter (github.com) 在目标机器上安装redis 上传redis采集器包redis_exporter-v1.53.0.linux-amd64.tar.gz [rootharbor opt]# tar -xf …

web3 solana

网址&#xff1a;HACKQUEST 学习初衷&#xff1a; 1.web3概念较为小众&#xff0c;相比于web2&#xff0c;机会较多 2.有机会remote work&#xff0c;带着笔记本到处浪&#xff0c;听着就不错 3.面对越来越卷的国内&#xff0c;有机会并有能力拥抱国外job&#xff0c;感觉是…

安卓车载多屏互动副屏底部有黑线条NavigationBar分析

背景&#xff1a; 在学习了马哥的wms和多屏互动课程后&#xff0c;大家普遍都可以跟着做出如下图效果的多屏互动&#xff1a; 其实初略来看这个成果已经完成一个多屏互动项目大部分功能&#xff0c;但是其实还是有一些bug的存在&#xff0c;今天我们就来分析一下多屏互动相关的…

FPGA实现SDI视频接收转USB3.0传输,GS2971+FT601方案,提供4套工程源码和QT上位机源码

目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本博已有的FPGA驱动USB通信方案FPGA基于GS2971的SDI视频解码方案FPGA基于FT601的USB3.0视频传输方案 3、详细设计方案设计原理框图SDI 相机GS2971-SDI解码芯片解读BT1120转RGB888图像缓存FT601-USB3.0芯…

计算机毕业设计选题推荐-校内跑腿业务系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

深入理解操作系统--绪论

文章目录 概述操作系统的基本概念多用户系统用户和组进程 小结 概述 最近在读操作系统&#xff0c;发现一些新的概念。写出来&#xff0c;让自己印象更深&#xff0c;希望也帮助一些不懂的朋友&#xff1b;但愿有所帮助吧。 操作系统的基本概念 计算机都包含操作系统的基本程…

自动化测试的回归策略!

在快速迭代的软件开发过程中&#xff0c;确保软件质量的稳定性和可靠性是每一个开发团队都面临的重要挑战。其中&#xff0c;回归测试作为验证软件变更后是否仍然满足原有需求的重要手段&#xff0c;其重要性不言而喻。而自动化测试的回归策略&#xff0c;正是为了应对这一挑战…

学习记录——day25 多线程编程 临界资源 临界区 竞态 线程的同步互斥机制(用于解决竟态)

目录 ​编辑 一、多进程与多线程对比 二、 临界资源 临界区 竞态 例1&#xff1a;临界资源 实现 输入输出 例2&#xff1a;对临界资源 进行 减减 例子3&#xff1a;临界资源抢占使用 三、线程的同步互斥机制&#xff08;用于解决竟态&#xff09; 3.1基本概念 3.2线…

C#实现数据采集系统-系统优化服务封装

系统优化-服务封装 现在我们调用modbustcp和mqtt都直接在Program,所有加载和功能都混合在一起,比较难以维护 类似asp.net core项目的Program.cs代码如下,构建服务配置和启动 要实现的效果,Main方法中就是一个服务启动,只需要几行代码 分析代码 这里分成两部分,一…

医疗健康类应用,适合采用哪些风格?本文归纳之,并附案例。

医疗健康类应用的设计风格应该注重用户友好性、专业性和清晰易懂。以下是一些适合医疗健康类应用采用的设计风格&#xff1a; 1. 清晰简洁的界面&#xff1a; 医疗健康类应用的界面设计应该简洁清晰&#xff0c;避免过多花哨的元素&#xff0c;让用户能够快速找到他们需要的信…

JAVA-案列练习-ATM项目

在JAVA入门学习后进行的项目练习——模拟ATM系统。 目录 1.设计内容和要求 2.代码实现 &#xff08;1&#xff09;ATM账号类的定义 &#xff08;2&#xff09;ATM类中的操作 &#xff08;3&#xff09;操作说明 3.总结 1.设计内容和要求 01 系统架构搭建&#xff0c;欢…

Spring Cloud全解析:注册中心之Eureka架构介绍

Eureka架构介绍 Eureka在设计时采用的是AP原则&#xff0c;是Netflix的一个子模块&#xff0c;用于微服务的服务注册与发现 P:Partition tolerance,网络分区容错。类似多机房部署&#xff0c;保证服务稳定性A: Availability&#xff0c;可用性C:Consistency &#xff0c;一致…

【时时三省】(C语言基础)函数递归

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ——csdn时时三省 什么是递归 程序调用自身的编程技巧称为递归。递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法&#xff0c;它通常把一个大型复杂…