1.Git 简史
Git有三种状态,分别是已提交(commited)、 已修改(modified)和已暂存(staged)。
- 已提交:表示数据已经安全的保存在本地数据库中
- 已修改:表示修改了文件,但还没保存到数据库中
- 已暂存:表示数据对一个已修改的文件的当前版本做了标记,使之包含在下次提交的快照中
由此引入Git项目的三个工作区域的概念:Git仓库,工作目录以及暂存区域。
Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作`‘索引’’,不过一般说法还是叫暂存区域。
基本的 Git 工作流程如下:
- 在工作目录中修改文件。
- 暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
2.Git信息配置
用户信息的配置有System级别 ,global(用户)级别和local(当前仓库)三个设置,底层配置会覆盖顶层配置。用户信息包括用户名称和邮件地址。
git config --global user.name "HustDsy"
git config --global user.email "379644606@qq.com"
#查看配置信息
git config --global --list
3.Git基础
-
克隆现有的仓库, git clone git@github.com:HustDsy/progit.git
-
工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。 工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。
、
-
为了方便下面的测试,在github上新建了一个git仓库progit,用来接下来的测试。(测完就删了)
3.1检查当前文件状态
使用git status
来查看文件的状态。现在最开始仓库里只有readme这样的一个文件。
可以看出来,所有已跟踪的文件在上次提交后都未被修改过。此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件,否则Git会列出来。现在分支名是main。
接下来再项目下创建一个新的txt文件。如果之前并不存在这个文件,使用 git status
命令,会看到一个未跟踪的文件。
$ echo "dsy is a cool boy" > description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
description.txt
在状态报告中可以看到新建的description.txt文件出现在Untracked files下面。未跟踪的文件意味着Git之前的快照(提交)中没有这些文件;
3.2跟踪新文件
使用命令git add
开始跟踪一个文件。所以要跟踪新文件,运行:
$ git add description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: description.txt
只要在 Changes to be committed
这行下面的,就说明是已暂存状态。
3.4暂存已修改文件
现在来修改一个已被跟踪的文件。就修改description.txt,可以
$ echo "today is friday" > description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: description.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: description.txt
可以看到文件出现在Changes not staged for commit这一行,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。接下来使用git add
,这个命令理解成添加内容到下一次提交中,现在运行git status
来看看结果。
$ git add description.txt
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: description.txt
3.5状态简览
使用git status -s
或者git status --short
将会得到一种更为紧凑的格式输出。
$ git status -s
MM 2.txt
M README.md
A description.txt
M test.txt
?? 1.txt
其中A代表新增的文件,??代表未跟踪的文件,M出现在右边代表修改了,但是没有添加到暂存区,M出现在左边代表修改了并且添加到了暂存区。出现两个MM代表添加到暂存区之后又被修改了。
3.5忽略文件
一般我们总有些文件无需纳入Git管理,也不希望它们总出现在未跟踪文件列表。通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等,在这种情况下,我们可以创建一个名为gitinore
的文件,列出要忽略的文件模式。来看一个实际的例子:
*.[oa]
*~
第一行告诉Git忽略.o或.a结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。
GitHub有一个十分详细的针对数十种项目及语言的.gitnore文件列表,网址为https://github.com/github/gitignore 。
3.6查看已暂存和为暂存的修改
直接使用git diff
用来比较工作目录中当前文件和暂存区域快照之间的差异.
使用git diff --cached
或者git diff --staged
用来表示已暂存的将要添加到下次提交里的内容。
前者可以理解成当前版本和修改后的已经暂存的版本的比较,后者可以理解成初始版本和修改后的已经暂存的版本的比较。
3.7提交更新
现在的暂存区域已经准备妥当已经可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有git add
过,否则提交的时候不会记录这些还没暂存起来的变化。
git commit
命令后添加-m选项,将提交信息与命令放在同一行,如下所示:
$ git commit -m "add some test"
[main 77b3d30] add some test
4 files changed, 3 insertions(+)
create mode 100644 1.txt
create mode 100644 description.txt
请记住,提交时记录的是放在暂存区域的快照。 任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。
3.8跳过使用暂存区域
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git提供了一个跳过使用暂存区域。git commit -a
$ git status
On branch main
Your branch is ahead of 'origin/main' by 3 commits.
(use "git push" to publish your local commits)
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: 1.txt
modified: 2.txt
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -m "skip stage" -a
[main e8b2879] skip stage
3 files changed, 3 insertions(+), 2 deletions(-)
3.9移除文件
首先假设第一种情况我们需要删除某个文件,正常的操作一般是三步,执行rm命令,再执行git add命令,最后在git commit。使用git rm的话相当于直接执行了前两步。
$ git rm 2.txt
$ git add 2.txt
$ git rm description.txt
$ git status
On branch main
Your branch is ahead of 'origin/main' by 5 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: 2.txt
deleted: description.txt
第二种情况就是我们将某些文件加入到了暂存区,但是我们想将它从暂存区移除这时候使用git rm --cached
$ git status
On branch main
Your branch is ahead of 'origin/main' by 7 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: test.txt
#现在我们将test.txt从暂存区移除
$ git rm --cached test.txt
#查看状态,已经移除了
$ git status
On branch main
Your branch is ahead of 'origin/main' by 7 commits.
(use "git push" to publish your local commits)
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
nothing added to commit but untracked files present (use "git add" to track)
3.10重命名文件
$ git mv file_from file_to
$ git mv README.md README
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
#相当于接下来的三条命令
$ mv README.md README
$ git rm README.md
$ git add README
3.11查看提交历史
在提交了若干更新之后,或者克隆了某个项目之后,我们回顾历史。完成这个任务的命令是git log
。默认不使用任何参数的话,显示的是如下信息。
$ git log
commit 8802df96599434fd5ee82a3704c6bf88187eeb5a (HEAD -> main)
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:41:30 2021 +0800
dada
commit 21ebd4bb70e592e75f79be67252eb9df26d2e8cd
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:31:22 2021 +0800
dada
commit 1106d9bcce983340778d4bdfdc2d0ba449e8e902
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:27:19 2021 +0800
delete
commit f14b52c530ba8dd266bc4b2fb7746ad93dd38eea
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:16:53 2021 +0800
git rm
加上-p
是用来显示每次提交的内容差异,加上-2来仅显示最近两次提交。该选项除了显示基本信息之外,还附带了每次commit的变化。使用git --stat
显示每次提交的简略信息。
$ git log -p -2
commit 8802df96599434fd5ee82a3704c6bf88187eeb5a (HEAD -> main)
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:41:30 2021 +0800
dada
diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..be40189
--- /dev/null
+++ b/test.txt
@@ -0,0 +1,2 @@
+2222
+2222
commit 21ebd4bb70e592e75f79be67252eb9df26d2e8cd
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:31:22 2021 +0800
dada
diff --git a/test.txt b/test.txt
deleted file mode 100644
index 49ca6c4..0000000
$git log --stat -2
commit 8802df96599434fd5ee82a3704c6bf88187eeb5a (HEAD -> main)
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:41:30 2021 +0800
dada
test.txt | 2 ++
1 file changed, 2 insertions(+)
commit 21ebd4bb70e592e75f79be67252eb9df26d2e8cd
Author: HustDsy <379644606@qq.com>
Date: Mon Nov 15 15:31:22 2021 +0800
dada
test.txt | 2 --
1 file changed, 2 deletions(-)
另外一个常用的选项是--pretty
。这个选项可以指定使用不同于默认格式的方式展示提交历史,这个选项有一些内建的子选项供你使用。git log --pretty=format"%h%s" --graph
$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
* 11d191e Merge branch 'defunkt' into local
git log
的常用选项如下所示:
还可以限制git log
的输出范围。
3.12撤销操作
撤销操作以下几种情
-
假设上一次的commit操作,你忘记添加文件或者commit信息写错了,使用–amend选项的提交命令尝试重新提交.
git commit -amend
-
假设你把某个文件放到了暂存区,但是想把它拿出来
$ git status On branch main Your branch is ahead of 'origin/main' by 10 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: dsy.txt modified: test.tst $ git reset HEAD dsy.txt #或者 git restore --staged dsy.txt #这样子dsy.txt就从暂存区里拿了出来 $ git status On branch main Your branch is ahead of 'origin/main' by 10 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: test.tst Untracked files: (use "git add <file>..." to include in what will be committed) dsy.txt
-
撤销对文件的修改,加入你不保留对某个文件的修改,如何方便地撤销修改。有两种方式。将它还原成上次提交的样子或者刚放入工作目录时的样子。
$ git checkout -- dsy.txt
$ git restore dsy.txt
3.13远程仓库
-
查看已配置完毕的远程仓库服务器可以使用
git remote -v
命令$ git remote -v dsy https://github.com/HustDsy/CCEH.git (fetch) dsy https://github.com/HustDsy/CCEH.git (push) dsyy git@github.com:HustDsy/CCEH.git (fetch) dsyy git@github.com:HustDsy/CCEH.git (push) origin git@github.com:HustDsy/progit.git (fetch) origin git@github.com:HustDsy/progit.git (push) pb https://github.com/paulboone/ticgit (fetch) pb https://github.com/paulboone/ticgit (push) #使用git fetch可以拉下来相关的仓库的代码
-
添加远程仓库
git remote add (仓库名) (仓库地址)
-
从远程仓库中抓取与拉去数据
git fetch [remote-name]
-
推送到远程仓库
git push [remote-name] [branch name]
#推送到origin的master分支 $git push origin master
-
远程仓库的移除
git remote rm [name]
与命名git remote rename
$git remote rename pb paul $git remote rename paul
3.14打标签
- 列出标签
git tag
- 创建标签,主要有轻量标签和附注标签,一个轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用。然而,附注标签是存储在 Git 数据库中的一个完整对象。 它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。 通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。
- 附注标签的创建运行
tag
命令式指定-a
选项。git tag -a v1.1 -m "test tag----"
- 轻量标签,轻量标签的本质上是将提交检验和存储到一个文件中,创建轻量级标签,不需要使用
-a -s -m
选项,只需要提供标签名字。git tag v1.1
- 后期打标签
git tag -a v1.2 [校验和]
- 共享标签/将标签显式的推送到共享服务器上.
git push [remote-name] [tag-name]
- 检出标签
git checkout -b [branchname] [tagname]
4.Git分支
Git保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照,在进行提交操作时,Git会保存一个提交对象。每次提交会包含一个指向暂存内容快照的指针。但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱,以及指向它的父对象的指针。普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象。
$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'
当使用 git commit
进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在 Git 仓库中这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。
做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针
Git的分支,其实本质上仅仅是指向提交对象的可变指针。Git的默认分支名字是master。在多次提交操作之后。其实已经有一个指向最后那个提交对象的master
分支。它会在每次的提交操作中自动向前移动。
4.1分支创建
git创建分支实际上是创建了一个可以移动的新的指针。创建一个testing分支,可以使用git branch
命令:
$ git branch testing
这会在当前所在的提交对象上创建一个指针。
在 Git 中,Head是一个指针,指向当前所在的本地分支(译注:将 HEAD
想象为当前分支的别名)。
我们可以简单的使用git log
命令查看各个分支当前所指的对象。提供这一功能的参数是--decorate
。
git log --oneline --decorate
4.2分支切换
要切换到一个已存在的分支,我们可以使用git checkout
命令。我们现在切换到新创建的testing
分支去:
git checkout testing
在两个分支上分别提交之后分支的变化如下:
4.3分支的新建与合并
git merge [branchname]
将branchname 合并到当前分支
git checkout -b [branchname]
创建对应的分支
git branch -d [branchname]
删除某个分支
git branch -r -d [remotename/branch]
在不删除远程分支的情况下停止跟踪远程分支
git checkout -t [remote-branch] -b [branchname]
创建一个本地分支跟踪远端分支
4.4分支开发工作流
git push [服务器名字] [本地分支]:[远程分支]
git pull [服务器名字] [远程分支]:[本地分支]
git fetch [服务器名] [分支名]
git push origin --delete [branchname] 或者 git push origin :[branchname]
4.5变基
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
- 可以这样理解,手先git会将experiment这个分支上的每个commit都取消掉。
- 将上面的操作临时保存成patch文件,存在.git/rebase目录下;
- 然后把experiment分支更新到最新的master分支上
- 最最后把上面保存的patch文件应用到experiment分支上