Git 简明教程 - 基础篇

发布于 2024-06-05 10:06:29 字数 13648 浏览 20 评论 0

很早就想些一篇关于 git 的文章了,这玩意儿实在好用,但是内容又比较多,这里我讲解最基本使用技巧,这个足以应对 99%以上的场景,剩下那些真的要用到就去看官网手册。

Git 是目前世界上最先进的分布式版本控制系统(没有之一),它的诞生也是个很有趣的故事。大家都知道 Git 是 Linus 大神写的,据说刚开始的时 候,linux 内核源码使用 BitKeeper 这个商业版本控制系统,BitKeeper 授权 Linux 社区免费使用,但是某一天开发 Samba 的 Andrew 这个家伙试图破解 BitKeeper 协议,东窗事发。于是 BitKeeper 公司一怒之下收回了免费使用权。Linus 大神是不可能去道歉 的,于是他就花了 2 个星期用 C 语言写了 Git,一个月内,Linux 源码就由 Git 管理了,无敌是多么寂寞。

相比较像 svn 这样的集中式版本管理,分布式版本管理优势在哪里呢?这里先说两个,后面再说另外几个杀手级优点。

首先,分布式所有客户机都有一个完整拷贝,所以不用担心服务器挂点。另外分布式不需要联网就可以工作,没有中央服务器。

安装 Git

在 linux 上面基本就是一条命令:

yum install git

如果在 windows 上面,就去官网下载安装文件,点击安装即可。

安装完成,还要简单配置下全局设置:

git config --global user.name "Your Name"
git config --global user.email "email@example.com"
# 彩色的 git 输出:
git config --global color.ui true
# 显示历史记录时,每个提交的信息只显示一行:
git config --global format.pretty oneline

初始化

初始化仓库:

mkdir gitdemo && cd gitdemo
git init
# Initialized empty Git repository in /root/gitdemo/.git/

当前目录下多了一个.git 的目录,这个目录是 Git 来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把 Git 仓库给破坏了。

.gitignore 文件

如果你有一些文件不需要版本跟踪就写到这里面,比如:

build/
*.pyc

接受通配符,具体规则请参考 gitignore 说明

添加文件到版本库

先编写一个 readme.txt 文件,内容如下:

hello git
I like git very much

第一步,使用 git add 命令添加 read.txt 到 git:

git add readme.txt

执行上面的命令,没有任何显示,这就对了

第二步,使用 git commit 命令提交到 git 仓库:

git commit -m "readme file"

输出:

[master (root-commit) 50f5fdc] readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt

回退和撤销

刚刚提交完后,继续编辑 readme.txt,内容如下:

hello git
I like git very much
add something

现在,运行 git status 命令看看结果:

[root@controller161 gitdemo]# 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 被修改过了,但还没有准备提交的修改。

虽然 Git 告诉我们 readme.txt 被修改了,但如果能看看具体修改了什么内容,自然是很好的,这时候使用 git diff 命令:

[root@controller161 gitdemo]# git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 2236b09..4114c7f 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,3 @@
hello git
I like git very much
+add something

知道了对 readme.txt 作了什么修改后,再把它提交到仓库就放心多了,步骤还是先 add,再 commit:

git add readme.txt

执行 add 之后,我们先不提交,看看状态:

[root@controller161 gitdemo]# git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: readme.txt

git status 告诉我们,将要被提交的修改包括 readme.txt,下一步,就可以放心地提交了:

[root@controller161 gitdemo]# git commit -m "modify readme"
[master 1d57c05] modify readme
1 file changed, 1 insertion(+)

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

[root@controller161 gitdemo]# git status
On branch master
nothing to commit, working directory clean

没有需要提交的修改,而且,工作目录是干净(working directory clean)的

版本回退

现在再修改 readme.txt 文件如下:

hello git
I like git very much
add something
我又加了点东西在后面

然后再提交:

[root@controller161 gitdemo]# git add readme.txt
[root@controller161 gitdemo]# git commit -m "添加中文行"
[master 90507c6] 添加中文行
1 file changed, 1 insertion(+)

像这样,你不断对文件进行修改,然后不断提交修改到版本库里,实际上这个 commit 操作就相对于一个快照。以后你误改误删了文件是可以回退的。

我们可以通过 git log 命令查看提交历史:

[root@controller161 gitdemo]# git log --pretty=oneline
90507c6859f8af73a651f033de8ea901811cb4e8 添加中文行
1d57c05ee44d590f279050276884551e77d2ffb1 modify readme
50f5fdcea453fed2eee690b5e686994040ffe210 readme file

第一列是 commit 的一个 id 号(版本号),是 SHA1 计算出来的一个非常大的数字,用十六进制表示。

或者你想通过 ASCII 艺术的树形结构来展示所有的分支, 每个分支都标示了他的名字和标签:

git log --graph --oneline --decorate --all

看看哪些文件改变了:

git log --name-status

假如你想回退到 modify readme 那个版本,可以通过 git reset 命令:

[root@controller161 gitdemo]# git reset --hard 1d57c05ee44
Unstaged changes after reset:
M readme.txt

reset 后面指定版本号,你可以只取前面几位,只要能区分就行。我们再来看 readme.txt:

[root@controller161 gitdemo]# cat readme.txt
hello git
I like git very much
add something

确实回退到那个提交版本了。

现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的 commit id 怎么办?

在 Git 中,总是有后悔药可以吃的。回退必须指定 commit id,而 Git 提供了一个命令 git reflog 用来记录你的每一次命令:

[root@controller161 gitdemo]# git reflog
1d57c05 HEAD@{0}: reset: moving to 1d57c05ee44
90507c6 HEAD@{1}: commit: 添加中文行
1d57c05 HEAD@{2}: commit: modify readme
50f5fdc HEAD@{3}: commit (initial): readme file

利用 rebase 合并多个 commit

在使用 Git 作为版本控制的时候,我们可能会由于各种各样的原因提交了许多临时的 commit,而这些 commit 拼接起来才是完整的任务。那么我们为了避免太多的 commit 而造成版本控制的混乱,通常我们推荐将这些 commit 合并成一个

首先假设我们有如下几个 commit:

[root@controller161 gitdemo]# git log --pretty=oneline --abbrev-commit
ad6fa66 ok , I resolve confict
3c18f63 modify README again
10c9f30 modify readme by xiao ming
796e584 dev branch modify readme by self
dbf4ee5 Initial commit

我想将最近 3 个(ad6fa66、3c18f63、10c9f30)合并为 1 个提交历史,怎样做呢?

git rebase -i 796e584

注意这个 796e584 是指的合并提交的前一个,不参与合并。出现下面的编辑界面:

pick 3c18f63 modify README again
pick 10c9f30 modify readme by xiao ming

# Rebase 796e584..ad6fa66 onto 796e584 (2 command(s))
#
# 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
# d, drop = remove commit

很奇怪的是第一条 commit 老不显示,应该是解决冲突的 commit 根本没办法合并原因。

可以看到其中分为两个部分,上方未注释的部分是填写要执行的指令,而下方注释的部分则是指令的提示说明。指令部分中由前方的命令名称、commit hash 和 commit message 组成。

当前我们只要知道 pick 和 squash 这两个命令即可。

  1. pick 的意思是要会执行这个 commit
  2. squash 的意思是这个 commit 会被合并到前一个 commit

我们将 10c9f30 这个 commit 前方的命令改成 squash 或 s,然后输入:wq 以保存并退出。

Git 会压缩提交历史,如果有冲突,需要修改,修改的时候要注意,保留最新的历史,不然我们的修改就丢弃了。修改以后要记得敲下面的命令:

git add .
git rebase --continue

如果你想放弃这次压缩的话,执行以下命令:

git rebase --abort

如果没有冲突,或者冲突已经解决,则会出现如下的编辑窗口,出现下面的界面:

# This is a combination of 2 commits.
# The first commit's message is:

modify README again

# This is the 2nd commit message:

modify readme by xiao ming

然后修改成下面 commit 说明的:

modify README again , modify readme by xiao ming

输入 wq 保存并退出, 再次输入 git log 查看 commit 历史信息,你会发现这 3 个 commit 已经合并了。

[root@controller161 gitdemo]# git log --pretty=oneline --abbrev-commit
4d07b18 modify README again , modify readme by xiao ming
796e584 dev branch modify readme by self
dbf4ee5 Initial commit

很奇怪,之前这个 ad6fa66 ok , I resolve confict 也被合并了,好神秘哦。有明白为什么的同学告我下。

两个分支 rebase

rebase 还有一种用法,就是当两个分支产生分叉,比如 master 和 dev,最后你想让 dev 分支历史看起来像没有经过任何合并一样,你也许可以用 git rebase:

$ git checkout dev
$ git rebase master

这些命令会把你的 dev 分支里的每个提交(commit) 取消掉,并且把它们临时保存为补丁(patch)(这些补丁放到”.git/rebase”目录中),然后把 dev 分支更新到最新的 master 分支,最后把保存的这些补丁应用到 master 分支上。

当 dev 分支更新之后,它会指向这些新创建的提交(commit),而那些老的提交会被丢弃。如果运行垃圾收集命令(pruning garbage collection), 这些被丢弃的提交就会删除

同样和合并提交一样,在 rebase 的过程中,也许会出现冲突(conflict),在这种情况,Git 会停止 rebase 并会让你去解决冲突;在解决完冲突后,用 git add 命令去更新这些内容的索引(index), 然后,你无需执行 git commit ,只要执行:

$ git rebase --continue

这样 git 会继续应用(apply) 余下的补丁,同样,在任何时候,你可以用–abort 参数来终止 rebase 的行动,并且 dev 分支会回到 rebase 开始前的状态:

$ git rebase --abort

工作区、暂存区、提交历史

在 git 里面有三个很重要的概念:工作区、暂存区、提交历史。

工作区(Working Directory)

就是你在电脑里能看到的目录,比如我的 gitdemo 文件夹就是一个工作区

暂存区(Stage)

一般存放在 “.git 目录下” 下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。实际上指向暂存区的指针名就是 index。

提交历史(Commit History)

隐藏目录 .git 其实是 Git 的版本库,也叫分支的提交历史。Git 为我们自动创建的第一个分支 master,以及指向 master 的一个指针叫 HEAD。请注意暂存区也是放在这个隐藏目录里面的。

img

把文件往 Git 版本库里添加的时候,是分两步执行的:

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

再次修改 readme.txt:

hello git
I like git very much
add something
这次加一行啦啦啦

另外再添加一个文件 LICENSE,内容如下:

MIT

先用 git status 查看一下状态:

[root@controller161 gitdemo]# 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

现在,使用两次命令 git add 或者 git add --all ,把 readme.txt 和 LICENSE 都添加后,用 git status 再查看一下:

[root@controller161 gitdemo]# git add --all
[root@controller161 gitdemo]# git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: LICENSE
modified: readme.txt

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

img

所以, git add 命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行 git commit 就可以一次性把暂存区的所有修改提交到分支:

[root@controller161 gitdemo]# git commit -m "show stage work"
[master 32db843] show stage work
2 files changed, 2 insertions(+)
create mode 100644 contact.txt

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

[root@controller161 gitdemo]# git status
On branch master
nothing to commit, working directory clean

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

img

关于 diff

很多时候需要用 diff 命令来比较文件差异,总结一下:

  • git diff readme.txt -> 工作区 和 暂存区比较
  • git diff --cache readme.txt -> 暂存区 和 版本库比较
  • git diff HEAD -- readme.txt -> 版本库 和 工作区比较

如果 git diff 后面不加文件名 readme.txt,表示要显示所有文件的差异。

撤销修改

有时候你也会犯傻修改了不该改的东西,这样时候可以通过 git checkout 命令撤销修改。

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

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

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

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

还有一种情况是,你想将暂存区的修改撤销掉:

git reset HEAD readme.txt

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

注意:这里 git reset 并没有不会对工作区产生任何影响,只是撤销了暂存区的修改。

还记得如何丢弃工作区的修改吗?

git checkout -- readme.txt

整个世界终于清静了

总结一下撤销修改场景:

  1. 场景 1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout -- file
  2. 场景 2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令 git reset HEAD file ,就回到了场景 1,第二步按场景 1 操作。
  3. 场景 3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
  4. 场景 4:删除版本库里面的文件,但是工作区间内容保留,一般是先提交了想忽略的文件。 git reset --mixed 版本库 ID
  5. 对于已提交至远程服务器的错误提交,参考下面的命令:
git reset --soft/mixed/hard <commit_id>
git push origin HEAD --force

HEAD 最新提交
HEAD~ 上 1 个提交
HEAD~2 上 2 个提交

重要的事说三遍,最后有必要再次总结几个重要的指令:

  • 当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
  • 当执行 git rm --cached <file> 命令时,会直接从暂存区删除文件,工作区则不做出改变。
  • 当执行 git rm -f <file> 命令时,会直接把暂存区和工作区全部删了。
  • 当执行 git checkout . 或者 git checkout -- <file> 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。
  • 当执行 git checkout HEAD . 或者 git checkout HEAD <file> 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。

git reset 有三个选项, --hard、--mixed、--soft

注意这三个选项对文件层面的 git reset 毫无作用,因为缓存区中的文件一定会变化,而工作目录中的文件一定不变。

//仅仅只是撤销已提交的版本库,不会修改暂存区和工作区
git reset --soft 版本库 ID
``
//仅仅只是撤销已提交的版本库和暂存区,不会修改工作区
git reset --mixed 版本库 ID

//彻底将工作区、暂存区和版本库记录恢复到指定的版本库
git reset --hard 版本库 ID

注意这个版本库 ID 应该不是你刚刚提交的版本库 ID,而是刚刚提交版本库的上一个版本库。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

拍不死你

暂无简介

0 文章
0 评论
599 人气
更多

推荐作者

玍銹的英雄夢

文章 0 评论 0

我不会写诗

文章 0 评论 0

十六岁半

文章 0 评论 0

浸婚纱

文章 0 评论 0

qq_kJ6XkX

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文