git基本使用

splend21 Lv1

git有什么用

如果你使用Word写过长篇大论,那你一定有这样的经历:

想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的文档目录变得杂乱不堪。

过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。

看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删,真郁闷。

更要命的是,有些部分需要你的同事帮助填写,于是你把文件Copy到U盘里给她(也可能通过Email发送一份给她),然后,你继续修改Word文件。一天后,同事再把Word文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你作了哪些改动,得把你的改动和她的部分合并,真困难。

git就是用来解决以上的问题的!git作为当今世界上最先进的分布式版本控制系统,很方便的解决了以往手动管理版本的模式。

安装git

在Windows上使用Git,直接从Git官网直接下载安装程序,然后按默认选项安装即可。安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功。

git基本操作

image-20260214150933496

初始化仓库

git仓库,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

1
git init

通过git init命令把这个目录变成Git可以管理的仓库,无论这个目录是否为空

执行过后的直观变化就是当前目录下多了一个.git的目录

添加文件到Git仓库

分两步:

  1. 使用命令git add <file>,注意,可反复多次使用,添加多个文件,最后一次提交全部;
  2. 使用命令git commit -m <message>,完成提交。

例如:

1
2
git add a.txt
git commit -m "first commit"
查看仓库当前状态
  1. 要随时掌握工作区的状态,使用git status命令。
  2. 如果git status告诉你有文件被修改过,用git diff可以查看修改内容。
查看提交日志

git log命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是3,上一次是second commit,最早的一次是first commit

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git log
commit dccec770ccaefeed44db7be5f1187f5a865d018f (HEAD -> master)
Author: splend21 <281096458@qq.com>
Date: Sat Feb 14 14:35:44 2026 +0800
3
commit 92fbbf5862c42ec7f29b9f5d800350bdf4cd80e8
Author: splend21 <281096458@qq.com>
Date: Sat Feb 14 14:34:11 2026 +0800
second commit
commit ea1dd775a36171488b6fc748158f8dc5d0165f22
Author: splend21 <281096458@qq.com>
Date: Sat Feb 14 14:28:27 2026 +0800
first commit

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:

1
2
3
4
$ git log --pretty=oneline
dccec770ccaefeed44db7be5f1187f5a865d018f (HEAD -> master) 3
92fbbf5862c42ec7f29b9f5d800350bdf4cd80e8 second commit
ea1dd775a36171488b6fc748158f8dc5d0165f22 first commit

前面一大串是版本号,HEAD表示当前版本,master代表分支,再后面的部分就是提交时的说明

版本回退

假如我想回到当前版本的上一个版本该怎么办?

使用git reset命令:

1
$ git reset --hard HEAD^

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

--hard会回退到上个版本的已提交状态,而--soft会回退到上个版本的未提交状态,--mixed会回退到上个版本已添加但未提交的状态。

执行之后你再次执行git log,会发现:

1
2
3
$ git log --pretty=oneline
92fbbf5862c42ec7f29b9f5d800350bdf4cd80e8 (HEAD -> master) second commit
ea1dd775a36171488b6fc748158f8dc5d0165f22 first commit

最新的版本我已经看不到了,假如我又后悔了怎么办?

如果你还没有关闭命令行窗口,直接找到最新提交的版本号,执行以下命令就可以切回对应的版本:

1
git reset --hard dccec

版本号没必要写全,前几位就可以了,Git会自动去找。

如果我已经关了命令行窗口呢?

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

1
2
3
4
5
6
$ git reflog
dccec77 (HEAD -> master) HEAD@{0}: reset: moving to dccec
92fbbf5 HEAD@{1}: reset: moving to HEAD^
dccec77 (HEAD -> master) HEAD@{2}: commit: 3
92fbbf5 HEAD@{3}: commit: second commit
ea1dd77 HEAD@{4}: commit (initial): first commit

这样就可以找到之前的版本号进行回退了

撤销修改

在开始之前先说明几点以便于理解

  • 什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。
  • git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。
  • Git跟踪并管理的是修改,而非文件。

简单举一个例子:

第一次修改 -> git add -> 第二次修改 -> git commit

对同一个文件按照上述流程执行,会发现第一次的修改被提交了,第二次的修改不会被提交,这一点验证了上面的说法。

现在回到正题

试想这么一个场景:

现在是凌晨两点,你正在赶一份工作报告,你在readme.txt中添加了一行:

1
2
3
4
5
6
$ cat 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.
My stupid boss still prefers SVN.

在你准备提交前,一杯咖啡起了作用,你猛然发现了stupid boss可能会让你丢掉这个月的奖金!

既然错误发现得很及时,你就可以手动删掉最后一行,把文件恢复正确的状态。如果用git status查看一下:

1
2
3
4
5
6
7
8
9
$ 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>可以丢弃工作区的修改:

1
$ git checkout -- readme.txt

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

  • 一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
  • 一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

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

现在,看看readme.txt的文件内容:

1
2
3
4
5
$ cat 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.

文件内容果然复原了。

git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout命令。

现在假定是凌晨3点,你不但写了一些胡话,还git add到暂存区了:

1
2
3
4
5
6
7
$ cat 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.
My stupid boss still prefers SVN.
$ git add readme.txt

庆幸的是,在commit之前,你发现了这个问题。用git status查看一下,修改只是添加到了暂存区,还没有提交:

1
2
3
4
5
6
$ 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),重新放回工作区:

1
2
3
$ git reset HEAD readme.txt
Unstaged changes after reset:
readme.txt

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

再用git status查看一下,现在暂存区是干净的,工作区有修改:

1
2
3
4
5
6
7
$ 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

还记得如何撤销工作区的修改吗?

1
2
3
4
$ git checkout -- readme.txt
$ git status
On branch master
nothing to commit, working tree clean

整个世界终于清静了!

现在,假设你不但改错了东西,还从暂存区提交到了版本库,怎么办呢?还记得版本回退一节吗?可以回退到上一个版本。不过,这是有条件的,就是你还没有把自己的本地版本库推送到远程。还记得Git是分布式版本控制系统吗?我们后面会讲到远程版本库,一旦你把stupid boss提交推送到远程版本库,你就真的惨了……

删除文件

当你在工作区中删除了已提交的文件,这是git就会发现工作区与仓库不一致

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

1
2
3
4
5
6
7
$ 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

另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

1
$ git checkout -- test.txt

远程仓库

添加远程仓库

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

在github页面上新建一个空仓库,可以见到类似如下内容:

image-20260214171531026

跟着操作即可,注意将地址改为自己的github,同时远程库的名字就是origin,这是Git默认的叫法

删除远程库

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

1
git remote -v

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

1
git remote rm origin

此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到GitHub,在后台页面找到删除按钮再删除。

从远处仓库克隆

要克隆一个仓库,使用git clone命令克隆,同时还需要知道该仓库的地址。

例如:

1
git clone https://github.com/splend21/test.git

git分支

分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

在之前的版本回退中,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

1
2
3
4
5
6
7
8
9
                  HEAD


master


┌───┐ ┌───┐ ┌───┐
│ │───▶│ │──▶│ │
└───┘ └───┘ └───┘

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。

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

1
2
3
4
5
6
7
8
9
10
11
12
                 master


┌───┐ ┌───┐ ┌───┐
│ │───▶│ │──▶│ │
└───┘ └───┘ └───┘


dev


HEAD

你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!

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

1
2
3
4
5
6
7
8
9
10
11
12
                 master


┌───┐ ┌───┐ ┌───┐ ┌───┐
│ │──▶│ │───▶│ │───▶│ │
└───┘ └───┘ └───┘ └───┘


dev


HEAD

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

1
2
3
4
5
6
7
8
9
10
11
12
                           HEAD


master


┌───┐ ┌───┐ ┌───┐ ┌───┐
│ │──▶│ │───▶│ │───▶│ │
└───┘ └───┘ └───┘ └───┘


dev

所以Git合并分支也很快!就改改指针,工作区内容也不变!

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

1
2
3
4
5
6
7
8
9
                           HEAD


master


┌───┐ ┌───┐ ┌───┐ ┌───┐
│ │──▶│ │───▶│ │───▶│ │
└───┘ └───┘ └───┘ └───┘
创建与合并分支

每个仓库都自带主分支master,当前仓库仅有一个readme.txt

首先,我们创建dev分支,然后切换到dev分支:

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

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

1
2
3
$ git branch dev
$ git checkout dev
Switched to branch 'dev'

然后,用git branch命令查看当前分支:

1
2
3
$ git branch
* dev
master

git branch命令会列出所有分支,当前分支前面会标一个*号。

然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行:

1
Creating a new branch is quick.

然后提交:

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

现在,dev分支的工作完成,我们就可以切换回master分支:

1
2
$ git checkout master
Switched to branch 'master'

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

1
2
3
4
5
6
7
8
9
10
11
12
                  HEAD


master


┌───┐ ┌───┐ ┌───┐ ┌───┐
│ │──▶│ │───▶│ │───▶│ │
└───┘ └───┘ └───┘ └───┘


dev

现在,我们把dev分支的工作成果合并到master分支上:

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

git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。

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

当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

合并完成后,就可以放心地删除dev分支了:

1
2
$ git branch -d dev
Deleted branch dev (was b17d20e).

删除后,查看branch,就只剩下master分支了:

1
2
$ git branch
* master

因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

我们注意到切换分支使用git checkout <branch>,而前面讲过的撤销修改则是git checkout -- <file>,同一个命令,有两种作用,确实有点令人迷惑。

实际上,切换分支这个动作,用switch更科学。因此,最新版本的Git提供了新的git switch命令来切换分支:

创建并切换到新的dev分支,可以使用:

1
$ git switch -c dev

直接切换到已有的master分支,可以使用:

1
$ git switch master

使用新的git switch命令,比git checkout要更容易理解。

解决冲突

人生不如意之事十之八九,合并分支往往也不是一帆风顺的。

准备新的feature1分支,继续我们的新分支开发:

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

修改readme.txt最后一行,改为:

1
Creating a new branch is quick AND simple.

feature1分支上提交:

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

切换到master分支:

1
2
$ git switch master
Switched to branch 'master'

master分支上把readme.txt文件的最后一行改为:

1
Creating a new branch is quick & simple.

提交:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                            HEAD


master


┌───┐
┌─▶│ │
┌───┐ ┌───┐ ┌───┐ │ └───┘
│ │───▶│ │──▶│ │──┤
└───┘ └───┘ └───┘ │ ┌───┐
└─▶│ │
└───┘


feature1

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

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

果然冲突了!Git告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ 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 <file>..." to mark resolution)

both modified: readme.txt

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

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

1
2
3
4
5
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

Git用<<<<<<<=======>>>>>>>标记出不同分支的内容,我们修改如下后保存:

1
Creating a new branch is quick and simple.

再提交:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                                     HEAD


master


┌───┐ ┌───┐
┌─▶│ │──▶│ │
┌───┐ ┌───┐ ┌───┐ │ └───┘ └───┘
│ │──▶│ │───▶│ │──┤ ▲
└───┘ └───┘ └───┘ │ ┌───┐ │
└─▶│ │──────┘
└───┘


feature1

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ 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

最后,删除feature1分支:

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

工作完成。

Comments
On this page
git基本使用