Git 入门笔记

一、什么是 Git??

  • git

    一个分布式版本控制系统

  • gitkraken

    一个可视化的 git 管理工具

  • github

    一个 git 的远端仓库(同类的还有 gitlab、gitee 等)

    所以,不要把 git 和实验室的 gitlab 混为一谈。git 是管理版本的工具,是对于本地代码的管理;而 gitlab 是一个远端仓库,用于存放 git 管理的代码。

二、了解 Git

所以我们现在先抛开远端仓库,用 git 管理好本地代码先吧!

  1. git 的三个区域(空间维度):

    • 工作区域(Working Directory):也就是平时存放代码的地方
    • 暂存区域(Stage):用于临时存放你的改动,事实上它只是一个文件,保存即将提交的文件列表信息
    • Git 仓库(Repository):是安全存放数据的位置,这里边有你提交的所有版本的数据。

    在本地提交代码基本上就是这三个状态的转换

  2. git 文件的四个状态:

    • 未跟踪(untracked):新文件,没被添加进暂存区过
    • 已修改(modified):对应了在工作区内修改了文件
    • 已暂存(staged):对应了已经把修改的文件提交到暂存区域
    • 已提交(committed):将暂存区域提交到一个新的版本,工作区回归未修改状态
  3. git 的版本(时间维度):

    截取自http://bramus.github.io/ws2-sws-course-materials/xx.git.html
    截取自 http://bramus.github.io/ws2-sws-course-materials/xx.git.html

    每一次 commit 都会根据提交的内容和人计算出哈希值作为这个 commit 的 id。

    HEAD 是一个指针,指向当前的状态的 commit;HEAD~ 则是指向上一个 commit 的指针(如图为 1d7a184),HEAD~X 则是指向上 X 个 commit 的指针。

三、上手 Git!

  • 来了老弟

    首先,git 为了辨别是谁提交的代码,你必须设置你的 username 和 emial

    1
    2
    git config --global user.name "your name"
    git config --global user.email "your@email.com"

    这仅仅是给 git 辨别提交代码的身份用,你喜欢设置假的或者用我的名字上传我也一点办法也没有。

    以上代码是设置全局的,如果你只想这个用户名和邮箱只对此工程有效,去掉 --global 即可。

    然后就可以初始化仓库

    1
    git init

    至此我们的工程已经进入了由 git 支配的世界

  • 工作区 -> 暂存区

    我们可以用以下命令查看目前版本库的状态:

    1
    git status

    顺便插播 status 的部分选项:

    选项 说明
    -s--short 以短格式输出
    -b--branch 显示分支和跟踪信息
    ... ...

    如果你刚刚 init 了一个 git 仓库,那么你会发现你的文件处于 untrack 的状态;

    如果你刚刚修改了某个文件,那么你会发现被修改的文件处于 unstage 状态;

    此时我们需要用以下命令将这些文件添加到暂存区中:

    1
    git add yourfile

    特殊用法:

    命令 说明
    git add -A 提交所有变化
    git add -u 提交被修改 (modified) 和被删除 (deleted) 文件,不包括新文件 (new)
    git add . 提交新文件 (new) 和被修改 (modified) 文件,不包括被删除 (deleted) 文件
  • 暂存区 -> Git 仓库

    接下来就是提交代码环节了!

    1
    git commit -m "your commit message"

    但是往往事情没有那么简单,有的人提交了代码后发现,好像落了点什么,不要着急不要回退。将新更改好的版本 add 到暂存区后,使用以下命令将这次改变合并到之前的 commit 中:

    1
    git commit  --amend

    如果不需要修改 commit message 的话,还可以加上 --no-edit!

  • 查看状态

    如果说上文的 status 能查看空间维度的状态的话,以下命令就能查看时间维度的状态:

    1
    git log

    log 提供了不少的选项,以下列出少许常用的,更多的建议自行搜索:

    选项 说明
    --graph 显示 ASCII 图形表示的分支合并历史。
    --oneline 只显示提交 ID 和提交信息的第一行
    ... ...

    接下来的命令用于找不同:

    1
    git diff

    以下表格很重要:

    比较工作区与上次 commit 比较暂存区和上一次 commit 比较工作区和暂存区
    git diff git diff --cached git diff HEAD

    工作区下列命令是对 git 文件进行删除、重命名:

    1
    2
    git rm fileA.cpp              # 从git中删除fileA.cpp文件
    git mv fileB.cpp fileC.cpp #将fileB.cpp重命名为fileC.cpp
  • 暂存修改

    除了 commit,我们还可以用如下命令安全的存放你的现在的状态哦

    1
    git stash

    用如下命令就可以恢复暂存了

    1
    git stash pop

四、穿越时间

欲想穿越,就要搞懂分清两个命令 resetcheckout

  • reset

    使用如下命令可以将 HEAD 分支移到前一个 commit

    1
    git reset HEAD~

    当然也可以把 HEAD~ 替换成你想到达的分支名、commit id!

    注意以下区别:

    命令 说明
    git reset --mixed (默认) 修改版本库、暂存区
    git reset --soft 修改版本库
    git reset --hard 修改版本库、暂存区、工作区

    git log 一看,为什么我回到前一个 commit 之后原来的就消失了!??

    不着急,我们可以使用以下命令找回你的 commit id 和 HEAD 指针!

    1
    git reflog

    而且 reset 还可以单独修改个别文件

    1
    git reset [版本] [文件名或路径]
  • checkout

    使用如下命令可以把 HEAD 指针移到某个分支

    1
    git checkout [branchname]

    当然也可以把分支名替换成 HEAD 指针、commit id!

    那么,checkoutreset 又有什么区别!!?

    图片描述
    图片描述

    最重要的区别在于 reset 会移动 HEAD 所指向分支的指向,而 checkout 只移动 HEAD 指针本身!

    这样带来了个问题,使用 checkout 你可以指向一个并没有任何分支指向的 commit!此时 git 便会帮你创建一个匿名分支!在匿名分支上的所有操作在你切换到别的分支时会被全部丢弃!

五、分支

  • 你的第一个分支

    使用以下命令查看分支状况:

    1
    git branch

    使用以下命令创建名为 develop 的分支,并切换到新分支下:

    1
    2
    git branch develop      # 创建分支
    git checkout develop # 切换到develop分支下

    或者使用下面命令偷懒!

    1
    git checkout -b hotfix  # 创建并切换到hotfix分支下
  • merge 合并!

    以下命令将 develop 分支合并到 master 中:

    1
    2
    git checkout master    # 首先要切换到master分支!
    git merge develop # 将develop分支merge到master中

    要注意的是,使用以上命令,git 会采用默认的 Fast forward 格式进行 merge(因为此时 develop 分支比 master 分支新),这样 merge 的这次操作不会有 commit 信息,log 中也不会有分支的图案。我们可以采取 --no-ff 这种方式保留 mergecommit 信息:

    1
    git merge --no-ff -m "commit message" develop

    燃鹅很多时候时期并没有那么简单,更多时候在 merge 之后便会报告合并出现了冲突!git 会在有冲突的文件下作形如以下的标记:

    1
    2
    3
    4
    5
    <<<<<<< HEAD
    # edited in master
    =======
    # edited in develop
    >>>>>>> develop

    这时,# edited in develop# edited in master 发生了冲突,当我们手动处理好冲突之后就可以 commit 代码啦!

    此时,删除 develop 分支也不会有什么影响啊(仅删除了 develop 分支的指针):

    1
    git branch -d develop
  • rebase

    文字功底匮乏,请看图!

    重新划分分支冲突
    重新划分分支冲突
    重新划分分支冲突
    重新划分分支冲突
    重新划分分支冲突
    重新划分分支冲突
    重新划分分支冲突
    重新划分分支冲突

    故事是这样的,原本在 branch-A 的 C3 基于了 C1 来开发。突然程序出现了个惊天大 bug,情急之下新建了分支 branch-B 修复了惊天大 bug。但是 C3 当然也想在无惊天大 bug 的代码下进行开发呀!于是便把分支 branch-Brebase 到了 branch-A 中,此时 C3*(这时的 C3 已经不是往年的 C3,是没有惊天大 bug 的 C3,故加 *)已经变成了基于 C4 开发了!

    1
    git rebase develop

    在处理冲突之后,使用以下命令就可以成功的 rebase 啦

    1
    git rebase --continue

    注意!rebase 会无情修改别人的 commit!如图 C2、C4

六、远端仓库

查看远端仓库:

1
git remote

加上 -v 可显示需要读写远端仓库使用的 Git 保存的简写与其对应的 URL。

查看某远端仓库更多信息:

1
git remote show [remote-name]

添加远端仓库:

1
git remote add [shortname] [url]

克隆远端仓库:

1
git clone [url]

从远端仓库中获得数据:

1
git fetch [remote-name]

如果你有一个分支设置为跟踪一个远端分支,可以使用 git pull 命令来自动的抓取然后合并远端分支到当前分支:

1
git pull [remote-name]

查看本地分支和远端分支的跟踪关系:

1
git branch -vv

推送到远端仓库:

1
2
3
4
git push [remote-name] [branch-name]

# 同时创建远端分支方法
git push [remote-name] [branch-name]:[remote-branch-name]

跟踪一个远端分支:

1
2
3
4
5
6
7
# 设置一个已有的本地分支跟踪一个刚刚拉取下来的远端分支
git branch -u [remote-name]/[branch-name]

# 远端有本地无
git checkout --track [remote-name]/[branch-name]
# 或者
git checkout -b [branch-name] [remote-name]/[branch-name]

远端仓库重命名:

1
git remote rename

删除远端仓库:

1
git remote rm