Git_learn学习
目录
- 1.基础设置
- 2.关联仓库
- 设置姓名和邮箱地址
- 关联本地仓库到gitee
- 3.git基本知识
- git init——初始化仓库
- git status——查看仓库的状态
- git add——向暂存区中添加文件
- git commit——保存仓库的历史记录
- git push——推送至远程仓库
- git log——查看提交日志
- git diff——查看更改前后的差别
- 忽略某些文件
- 4.分支的操作
- 什么是分支的操作?
- git branch——显示分支一览表
- git checkout - b——创建、切换分支
- git checkout branchname
- 分支的初步运用
- git merge——合并分支
- git log – graph——以图表形式查看分支
- 5.更改提交的操作
- git reset——回溯历史版本
参考:
菜鸟教程——git:https://www.runoob.com/git/git-workspace-index-repo.html
Pro Git(中文版):
《GitHub入门与实践》
本博客用到的代码在:https://gitee.com/lazyone/git_learn.git,供大家参考。
1.基础设置
为了学习git,初始化了一个仓库,仓库地址:https://gitee.com/lazyone/git_learn.git
此外用一些基础的c++语言来做相关的实验,界面使用vscode,里面插件那些也挺好用的。
src里面就一个main.cpp,使用cmake进行编译。
2.关联仓库
设置姓名和邮箱地址
git config --global user.name "Firstname Lastname"
git config --global user.email "your_email@example.com"
通过上述两行命令可以在“~/.gitconfig”中以如下形式输出设置文件
[user]
name = Firstname Lastname
email = your_email@example.com
想更改这些信息时,可以直接编辑这个设置文件。这里设置的姓名和邮箱地址会用在 Git 的提交日志中。
ps:使用
git config --global color.ui auto
代码可以让命令的输出拥有更高的可读性。
参考:《GitHub入门与实践》
关联本地仓库到gitee
确认设置好姓名和邮箱后,拉取仓库的地址
git remote add origin https://你的地址
# 之后输入
git push -u origin "master"
输入上述指令后,输入你的账号密码即可成功关联到gitee仓库了。
当然你也可以先在gitee上创建仓库,然后再通过git clone克隆到本地,这里就不多介绍。
3.git基本知识
git init——初始化仓库
执行了 git init命令的目录下就会生成 .git 目录。这个 .git 目录里存储着管理当前目录内容所需的仓库数据。 “在 Git 中,我们将这个目录的内容称为“附属于该仓库的工作树”。文件的编辑等操作在工作树中进行,然后记录到仓库中,以此管理文件的历史快照。如果想将文件恢复到原先的状态,可以从仓库中调取之前的快照,在工作树中打开。开发者可以通过这种方式获取以往的文件。”——《GitHub入门与实践》
git status——查看仓库的状态
git status命令用于显示 Git 仓库的状态。这是一个十分常用的命令。
执行玩git push指令后,使用git status后会显示
如果你在本地新增或者改变了文件后,在使用git status则会显示
这里我修改了main.cpp文件,以及新增了一个头文件与cpp文件,因此显示上图。
#ifndef __POINT_H__
#define __POINT_H__
#include <iostream>
class Point {
private:
/* data */
int x, y;
public:
Point(int xx, int yy);
~Point();
void showPoint();
};
#endif // __POINT_H__
#include "Point.h"
Point::Point(int xx, int yy) {
this->x = xx;
this->y = yy;
std::cout << "Point's Constructor." << std::endl;
}
Point::~Point() { std::cout << "Point's Destructor." << std::endl; }
void Point::showPoint() {
std::cout << "X:" << this->x << "|" << "Y:" << this->y << std::endl;
}
#include <iostream>
#include "Point.h"
int main(int, char**) {
Point p1(1,1);
p1.showPoint();
std::cout << "Hello, world!\n";
}
vscode还能显示修改的地方,下图是修改前的版本和修改后的版本。
这里需要明了git的工作区、暂存区和版本库的概念
参考:https://www.runoob.com/git/git-workspace-index-repo.html
- 工作区(工作树): 就是你在电脑里能看到的目录。
- 暂存区: 英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。
- 版本库: 工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。
下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:
git add——向暂存区中添加文件
如果只是在工作树中创建了文件,那么该文件并不会被记入 Git 仓库的版本管理对象当中。因此我们用 git status命令查看创建文件时,它会显示在 Untracked files 里。要想让文件成为 Git 仓库的管理对象,就需要用 git add命令将其加入暂存区(Stage 或者 Index)中。暂存区是提交之前的一个临时区域。
git add .
# 或者想上传某个指定文件
# git add 指定文件
执行完git add . 再执行git status后可以看到,编辑和新增的文件都有了显示。
git commit——保存仓库的历史记录
git commit命令可以将当前暂存区中的文件实际保存到仓库的历史记录中。通过这些记录,我们就可以在工作树中复原文件。
git commit -m "First commit"
# -m 参数后的 "First commit"称作提交信息,是对这个提交的
概述。
执行后终端会显示如左图结果所示。执行完 git commit命令后再来查看当前状态则会显示右图状态。
当前工作树处于刚刚完成提交的最新状态,所以结果显示没有更改。
git push——推送至远程仓库
git push是一个Git命令,用于将本地Git仓库中的代码推送到远程存储库。在使用git push命令时,需要指定以下两个参数:
- <remote>:远程存储库的别名,Git存储库可以与多个远程存储库进行通信,每个远程存储库都需要有一个唯一的别名。默认情况下,Git会将远程存储库的别名设置为“origin”,它表示默认的远程存储库。
- <branch>:要推送的分支名,此参数指定了本地哪个分支应该被推送到目标存储库中。默认情况下,Git会将当前分支推送到目标存储库。
git push -u origin master
执行上述命令后
推送后,之前提交的就被推送到远程仓库中去了
git log——查看提交日志
git log命令可以查看以往仓库中提交的日志。
git log
# git log --pretty=short:只显示提交信息的第一行
# git log 文件名:只显示指定目录、文件的日志
# git log -p:显示文件的改动
# git log -p 2:用 -2 则仅显示最近的两次更新
# git log --stat:在做代码审查,或者要快速浏览其他协作者提交的更新都作了哪些改动时,就可以用这个选项
如上图所示,屏幕显示了刚刚的提交操作。commit 栏旁边显示的“ddc……”是指向这个提交的哈希值。Git 的其他命令中,在指向提交时会用到这个哈希值。Author 栏中显示我们给 Git 设置的用户名和邮箱地址。Date 栏中显示提交执行的日期和时间。再往下就是该提交的提交信息。
git diff——查看更改前后的差别
git diff命令可以查看工作树、暂存区、最新提交之间的差别。这里先在main.cpp中新增一行代码std::cout << "git diff" << std::endl;,执行 git diff命令,查看当前工作树与暂存区的差别。
git diff
# git diff HEAD # 要查看与最新提交的差别,请执行该命令
# git diff --cached # 已经暂存起来的文件和上次提交时的快照之间的差异
# git diff --staged # 同上,1.6.1才有
由于我们尚未用 git add命令向暂存区添加任何东西,所以程序只会显示工作树与最新提交状态之间的差别。这里解释一下显示的内容。“+”号标出的是新添加的行,被删除的行则用“-”号标出。我们可以看到,这次只添加了一行。
执行玩git diff后在执行git add .后,执行git diff就不会显示任何消息了,可以用git diff HEAD显示最近一次的提交差别。
“不妨养成这样一个好习惯:在执行 git commit命令之前先执行git diff HEAD命令,查看本次提交与上次提交之间有什么差别,等确认完毕后再进行提交。这里的 HEAD 是指向当前分支中最新一次提交的指针。”——《GitHub入门与实践》
修改过后,执行git commit命令。并查看下log成功显示第二个提交。
忽略某些文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。——《Pro Git(中文版)》
文件 .gitignore 的格式规范如下:
- 所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配。
- 匹配模式最后跟反斜杠(/)说明要忽略的是目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
# gitignore文件中内容
build/*
.vscode/*
我的.gitignore中就忽略了vs的文件夹设置,cmake生成的build文件夹。
4.分支的操作
什么是分支的操作?
分支是Git版本控制中的一个非常重要的概念和操作。在Git中,分支是指指向版本库中特定提交记录的指针。在一个Git项目中,主要的分支是master分支,它通常用来表示项目的稳定状态。而其他的分支则被用于开发新功能、修复问题等任务。
使用Git分支,你可以基于一个已经存在的分支创建一个新的分支,这与创建一个完全独立的版本类似。在不影响其他团队成员工作的情况下,你可以在新分支上编写新代码、修改已有代码、提交变更等等。之后,你可以将此新分支合并到原始分支中,这将导致原始分支的代码与新分支代码的结合。
Git分支操作是实现并发多版本控制的基础。在Git中,分支的创建、切换、合并、删除等操作都是非常容易的,提供了非常灵活的分支管理能力。
git branch:列出所有的本地分支,包括当前所在分支。
ps:为了导出文档方便,将图片替换成代码块的方式展示
G:\workspace\git\git_learn>git branch
* master
可以看到 master 分支左侧标有“ *”(星号),表示这是我们当前所在的分支。
git branch -b branchname:创建一个新的分支,并切换到这个分支。branchname是新建分支的名称。
G:\workspace\git\git_learn>git checkout -b feature-A
Switched to a new branch 'feature-A'
这里创建了feature-A的分支,并切换过去了,这是如果再次运行git branch,则会出现:
G:\workspace\git\git_learn>git branch
* feature-A
master
当前就在feature-A分支中
git checkout branchname
git checkout branchname:切换到一个已经存在的分支。branchname是需要切换的分支名称。
G:\workspace\git\git_learn>git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
G:\workspace\git\git_learn>git branch
feature-A
* master
当前切换到master分支中
分支的初步运用
首先切换到feature-A分支中
在feature-A分支中修改Point.cpp文件与Point.h文件
#ifndef __POINT_H__
#define __POINT_H__
#include <iostream>
class Point {
private:
/* data */
int x, y, z;
public:
Point(int xx, int yy, int zz);
~Point();
void showPoint();
};
#endif // __POINT_H__
#include "Point.h"
Point::Point(int xx, int yy, int zz) {
this->x = xx;
this->y = yy;
this->z = zz;
std::cout << "Point's Constructor." << std::endl;
}
Point::~Point() { std::cout << "Point's Destructor." << std::endl; }
void Point::showPoint() {
std::cout << "X:" << this->x << "|" << "Y:" << this->y << "|" << "Z:"<< this->z<< std::endl;
}
这里将点的维度增加到三维,并按照之前的方式进行推送
G:\workspace\git\git_learn>git add .
G:\workspace\git\git_learn>git commit -m "feature"
[feature-A 8c14abb] feature
3 files changed, 6 insertions(+), 5 deletions(-)
G:\workspace\git\git_learn>git push -u origin feature-A
Enter passphrase for key '/c/Users/pzq/.ssh/id_rsa':
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 6 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 526 bytes | 526.00 KiB/s, done.
Total 6 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.4]
remote: Create a pull request for 'feature-A' on Gitee by visiting:
remote: https://gitee.com/lazyone/git_learn/pull/new/lazyone:feature-A...lazyone:master
To gitee.com:lazyone/git_learn.git
* [new branch] feature-A -> feature-A
branch 'feature-A' set up to track 'origin/feature-A'.
此时我们的远程仓库中多了一个feature-A分支
切换到master分支,发现.cpp与.h文件中Point类还是二维的,说明我们对feature-A分支中的代码进行改动,不会影响到master分支中。
Git分支的优点在于它可以让开发者在新功能开发的同时不影响已经发布并稳定的版本,从而真正实现了迭代式开发。
git merge——合并分支
接下来,我们假设 feature-A 已经实现完毕,想要将它合并到主干分支 master 中。首先切换到 master 分支。然后合并 feature-A 分支。为了在历史记录中明确记录下本次分支合并,我们需要创建合并提交。因此,在合并时加上 --no-ff参数。
G:\workspace\git\git_learn>git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.
G:\workspace\git\git_learn>git merge --no-ff feature-A
Merge made by the 'ort' strategy.
src/Point.cpp | 5 +++--
src/Point.h | 4 ++--
src/main.cpp | 2 +-
3 files changed, 6 insertions(+), 5 deletions(-)
这样一来,feature-A 分支的内容就合并到 master 分支中了。
git log – graph——以图表形式查看分支
用 git log --graph命令进行查看的话,能很清楚地看到特性分支(feature-A)提交的内容已被合并。除此以外,特性分支的创建以及合并也都清楚明了
G:\workspace\git\git_learn>git log --graph
* commit 47a17a4b46b9af720fb0f26535b067b1c8a22b85 (HEAD -> master)
|\ Merge: 07b4a48 8c14abb
| | Author: lazyone <emal>
| | Date: Tue Apr 18 13:07:43 2023 +0800
| |
| | Merge branch 'feature-A'
| |
| * commit 8c14abbbcf2d343774c200e2cb28a080740c3d94 (origin/feature-A, feature-A)
| | Author: lazyone <emal>
| | Date: Tue Apr 18 12:58:12 2023 +0800
| |
| | feature
| |
* | commit 07b4a483109f4627c320aed57629db524da6fd8f (origin/master, origin/HEAD)
|/ Author: lazyone <emal>
| Date: Tue Apr 18 11:39:45 2023 +0800
| Date: Tue Apr 18 11:39:45 2023 +0800
|
| readme
5.更改提交的操作
git reset——回溯历史版本
根据git log --graph的输出,我们可以简单画出版本的迭代情况,如图所示
那如果想要回溯版本,回到二维点的时候,可以运行git reset —hard加上目标时间点的哈希值就可以
G:\workspace\git\git_learn>git reset --hard 07b4a483109f4627c320aed57629db524da6fd8f
HEAD is now at 07b4a48 readme
这里通过指令回到了二维点的时候,通过指令创建了分支feature-B
G:\workspace\git\git_learn>git checkout -b feature-B
Switched to a new branch 'feature-B'
在feature-B中,向main.cpp中添加一行指令
#include <iostream>
#include "Point.h"
int main(int, char**) {
Point p1(1,1);
p1.showPoint();
std::cout << "Hello, world!\n";
std::cout << "git diff" << std::endl;
std::cout << "add feature-B" << std::endl;
}
然后执行add和commit指令。
目前本地仓库的状态就变成了下图所示的状态
如果我的期望状态是对feature-A分支进行合并后,又对feature-B进行合并,即下图所示
则相关步骤为:
- 切换到master分支
- git reflog查看merge操作后的时间哈希值
G:\workspace\git\git_learn>git reflog
07b4a48 (HEAD -> master, origin/master, origin/HEAD) HEAD@{0}: checkout: moving from feature-B to master
07bf69a (feature-B) HEAD@{1}: commit: feature-B
07b4a48 (HEAD -> master, origin/master, origin/HEAD) HEAD@{2}: checkout: moving from master to feature-B
07b4a48 (HEAD -> master, origin/master, origin/HEAD) HEAD@{3}: checkout: moving from feature-B to master
07b4a48 (HEAD -> master, origin/master, origin/HEAD) HEAD@{4}: checkout: moving from master to feature-B
07b4a48 (HEAD -> master, origin/master, origin/HEAD) HEAD@{5}: reset: moving to 07b4a483109f4627c320aed57629db524da6fd8f
47a17a4 HEAD@{6}: checkout: moving from feature-A to master
8c14abb (origin/feature-A, feature-A) HEAD@{7}: checkout: moving from master to feature-A
47a17a4 HEAD@{8}: checkout: moving from feature-A to master
8c14abb (origin/feature-A, feature-A) HEAD@{9}: checkout: moving from master to feature-A
47a17a4 HEAD@{10}: merge feature-A: Merge made by the 'ort' strategy.
07b4a48 (HEAD -> master, origin/master, origin/HEAD) HEAD@{11}: checkout: moving from master to master
07b4a48 (HEAD -> master, origin/master, origin/HEAD) HEAD@{12}: checkout: moving from feature-A to master
8c14abb (origin/feature-A, feature-A) HEAD@{13}: checkout: moving from master to feature-A
- 进行版本回溯
G:\workspace\git\git_learn>git reset --hard 47a17a4
HEAD is now at 47a17a4 Merge branch 'feature-A'
- 合并feature-B分支
G:\workspace\git\git_learn>git merge --no-ff feature-B
Auto-merging src/main.cpp
Merge made by the 'ort' strategy.
src/main.cpp | 1 +
1 file changed, 1 insertion(+)
至此,分支合并完成,运行git log —graph 可以查看相关操作
* commit d5c04fedd5ea32152426a433a0ae0e2848790330 (HEAD -> master)
|\ Merge: 47a17a4 07bf69a
| | Author: lazyone <emal>
| | Date: Tue Apr 18 13:48:13 2023 +0800
| |
| | Merge branch 'feature-B'
| |
| * commit 07bf69a170c2e4964f937cf8bf5d2effcd7db654 (feature-B)
| | Author: lazyone <emal>
| | Date: Tue Apr 18 13:42:23 2023 +0800
| |
| | feature-B
| |
* | commit 47a17a4b46b9af720fb0f26535b067b1c8a22b85
|\ \ Merge: 07b4a48 8c14abb
| |/ Author: lazyone <emal>
|/| Date: Tue Apr 18 13:07:43 2023 +0800
| |
| | Merge branch 'feature-A'
| |
| * commit 8c14abbbcf2d343774c200e2cb28a080740c3d94 (origin/feature-A, feature-A)
| | Author: lazyone <emal>
| | Date: Tue Apr 18 12:58:12 2023 +0800
| |
| | feature
从上述案例中可以查看git的强大,这还是git的基本功能。
此时feature-B已经没用了,想要删除运行git branch -d feature-B 即可
G:\workspace\git\git_learn>git branch -d feature-B
Deleted branch feature-B (was 07bf69a).