全部
常见问题
产品动态
精选推荐

Git从入门到精通,Git命令大全

管理 管理 编辑 删除

Git说明:https://www.runoob.com/manual/git-guide/

腾讯Github:https://github.com/Tencent

阿里巴巴Github:https://github.com/alibaba

Git镜像:https://www.gitclone.com/https://ghproxy.com/

Git入门

资料来源:https://www.runoob.com/git/git-tutorial.htmlhttp://git-scm.com/docs

查看Git命令的帮助信息,git <command> --help

1.Git 工作区、暂存区和版本库(以本地举例)、远程仓库

  • 工作区:就是你在电脑里能看到的目录。
  • 暂存区:英文叫 stageindex。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。
  • 版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。
  • Git从入门到精通,Git命令大全
  • Git 工作区、暂存区和版本库
  • 图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage/index),标记为 "master" 的是 master 分支所代表的目录树。
  • 图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个"游标"。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。
  • 图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下,里面包含了创建的各种对象及内容。
  • 当对工作区修改(或新增)的文件执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。
  • 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。
  • 当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
  • 当执行 git rm --cached 命令时,会直接从暂存区删除文件,工作区则不做出改变。
  • 当执行 git checkout . 或者 git checkout -- 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区中的改动。
  • 当执行 git checkout HEAD . 或者 git checkout HEAD 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。

2.Git文件状态

在Git中文件大概分为四种状态:已修改(modified)、已暂存(staged)、已提交(committed)、未追踪(Untrack)

  • .gitignore内的文件,不会拥有任何一种状态,被git彻底无视。
  • 处于ignore列表的文件,无法被add添加;但是可以强制添加
  • 空目录、以及子目录全部是空目录的目录不会有Untrack状态,也无法通过add改变状态(无效)
  • 工作目录新增文件时,只要不处于ignore目录,都会变成Untrack状态;
  • 没有add过的文件或者被restore(不带--staged)的文件,处于Untrack状态;
  • 初次add和被add后产生修改的文件,会处于modifed状态。
  • 处于modified状态的文件,最开始可以进行add和restore两种操作,此时的add操作叫做 更新要提交的内容,add后变为staged状态,restore(不加staged标记)后变为Untrack;
  • add后变为staged状态的文件,可用restore --staged 变回modified状态;这个staged状态的内容可以用来恢复内容。没有被add的modified状态文件内容没有被记录(虽然有撤回,但是本质不一样);
  • 处于staged状态的文件,在没有commit之前再次产生修改时,会同时具有staged和modified两个状态(可以把statged状态的内容拉回来,覆盖。);但是commit时会使用内容最新的那个状态;
  • commit会提交所有staged状态的文件,所以commit可以理解有一个modified到staged状态的过程(实际可能不存在,因为暂存区本来就有变动的记录);所以暂存状态不能理解为处于暂存区,应当指的是被纳入下一次提交的文件;任何被追踪的产生修改的文件都会在暂存区被记录;成为下一次提交的一部分;
  • 未被追踪的文件被删除时,不会产生git状态。处于modofy未add时,会变成deleted状态;处于staged状态会保持暂存状态;
  • 已经被删除的(deleted状态)被追踪的文件,恢复后会变成modified状态;
  • 提示
  • add的作用是将文件添加到暂存区,只有被add的文件才会被追踪
  • 暂存区
  • (1)所谓的暂存区只是一个简单的索引文件而已。
    (2)暂存区这个索引文件里面包含的是文件的目录树,像一个虚拟的工作区,在这个虚拟工作区的目录树中,记录了文件名、文件的时间戳、文件长度、文件类型以及最重要的SHA-1值,文件的内容并没有存储在其中,所以说 它像一个虚拟的工作区。
    (3)索引指向的是.Git/objects下的文件。
    (4)暂存区的作用:除非是绕过暂存区直接提交,否则Git想把修改提交上去,就必须将修改存入暂存区最后才能commit。每次提交的是暂存区所对应的文件快照。

拓展:status提示信息


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)

  • 既然是Changes not staged for commit,就说明出现这个提示下的所有文件改动,都是存在于工作区的。stage是暂存区的意思,not stage说明都不在暂存区,那么说明在工作区。
  • (use “git add …” to update what will be committed)。执行这条命令就可以工作区里面的改变加入到暂存区。可以执行git add .把当前目录下所有改动加入暂存区。
  • (use “git checkout – …” to discard changes in working directory)。执行这条命令将丢弃在工作区的改动。可以执行git checkout *把当前目录下所有工作区的改动丢弃掉
Untracked files: 
      (use "git add <file>..." to include in what will be committed)

  • Untracked files,就说明出现这个提示下的所有文件都是当前HEAD没有被加入过的文件。这种改动也属于工作区。
  • (use “git add …” to include in what will be committed)。把Untracked files加入暂存区。
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

  • 当前分支比远程分支多了一次commit
Your branch and 'origin/master' have diverged, and have 1 and 1 different commits each, respectively

pull报错了,查看状态显示这个,先留着待解决吧

3.HEAD是什么

HEAD是Git中非常重要的一个概念,你可以称它为指针或者引用,它可以指向任意一个节点,并且指向的节点始终为当前工作目录,换句话说就是当前工作目录(也就是你所看到的代码)就是HEAD指向的节点。

4.git重命名检测

Git 采用了不同的方法:它没有选择去存储与文件移动操作相关的信息,而是采用了重命名检测算法。在该算法中,如果一个文件在某一次提交中消失了,它依然会存在于其前次提交,而如果某个拥有相同名字或相似内容的文件出现在了另一个位置,Git 就会自动检测到。如果是这种情况,Git 就会假定该文件被移动过了。

Git项目文件说明

Git init后主要有两个重要的文件和目录:.git目录和.gitignore

1. .gitignore

.gitignore文件存在于根目录(与.git同级的目录)用于在将文件提交到git暂存区时,指定将哪些文件排除;

有时候你想添加(git add)一个文件到Git,但发现添加不了,多半原因是这个文件被.gitignore忽略了

git add .不会添加被.gitignore忽视的文件,而git add -f . 强制添加所有文件,即使是.gitignore忽视的文件也添加。

当.gitignore文件不是你编写的,但是它编写的不符合实际需求,你可以使用git check-ignore命令进行检查,看是哪一个规则有问题了


#检测
 git check-ignore -v App.class
#结果
.gitignore:3:*.class	App.class

.gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。解决方法就是先把本地缓存删除(改变成未track状态),然后再提交。


git rm -r --cached .
git add .
git commit -m ‘update .gitignore’

也可以手动指定一个文件作为git忽略文件


git config core.excludesfile ***

对于全局Git配置,可以使用如下命令对全部仓库进行配置。


git config --global core.excludesfile **/.gitignore(文件相对或绝对位置)

忽略规则如下:

  1. 空格不匹配任意文件,可作为分隔符,可用反斜杠转义
  2. #开头的文件标识注释,可以使用反斜杠进行转义
  3. ! 开头的模式标识否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用 ! 也不会再次被包含。可以使用反斜杠进行转义
  4. / 结束的模式只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件
  5. / 开始的模式匹配项目跟目录
  6. 如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录
  7. ** 匹配多级目录,可在开始,中间,结束
  8. ? 通用匹配单个字符
  9. [] 通用匹配单个字符列表
  10. 各种项目的gitignore
  11. 参考地址:https://github.com/github/gitignore

2. .git目录

任意文件夹中,用 git init 命令初始化仓库,即可在此文件夹下创建 .git 文件夹(.打头为隐藏文件夹,所以平时可能看不到)。这个文件夹之外的部分叫做工作区(Working Directory),.git 文件夹我们称做 Git仓库 (Git Repository)。 通常会有7个文件5个目录,常见目录如下:


COMMIT_EDITMSG
HEAD
ORIG_HEAD
FETCH_HEAD
config
description
index
hooks/
info/
logs/
objects/
refs/

1. 文件 COMMIT_EDITMSG

此文件是一个临时文件,存储最后一次提交的信息内容,git commit 命令之后打开的编辑器就是在编辑此文件,而你退出编辑器后,git 会把此文件内容写入 commit 记录。

实际应用: git pull 远程仓库后,新增了很多提交,淹没了本地提交记录,直接 cat .git/COMMIT_EDITMSG 就可以弄清楚最后工作的位置了。

2. HEAD

此文件永远存储当前位置指针,就像 linux 中的 $PWD 变量和命令提示符的箭头一样,永远指向当前位置,表明当前的工作位置。在 git 中 HEAD 永远指向当前正在工作的那个 commit。(孤立HEAD?????)

HEAD 存储一个分支的 ref,Linux中运行:cat .git/HEAD 通常会显示:


ref: refs/heads/master

这说明你目前正在 master 分支工作。此时你的任何 commit,默认自动附加到 master 分支之上

git cat-file -p HEAD, 显示详细的提交信息:


tree 4cbb261560348e1727b5137f3ab6eceae8e1f34d
parent 22c457fe24f737505356edfb8696c7e50fd9d971
author Evan You <yyx990803@gmail.com> 1654857613 +0800
committer Evan You <yyx990803@gmail.com> 1654857613 +0800

chore: test pass


image.png
孤立head,不指向任何commit

3. ORIG_HEAD

正因为 HEAD 比较重要,此文件会在你进行危险操作时备份 HEAD,如以下操作时会触发备份


git reset
git merge
git rebase
git pull

此文件应用示例


# 回滚到上一次的状态(慎用!!!)
git reset --hard ORIG_HEAD

4. FETCH_HEAD

这个文件作用在于追踪远程分支的拉取与合并,与其相关的命令有 git pull/fetch/merge,而git pull 命令相当于执行以下两条命令:


$ git fetch
$ git merge FETCH_HEAD

# 显示如下>>>
From https://github.com/xxx/xxxx
* branch            master     -> FETCH_HEAD
Updating f785638..59db1b2

此时会默默备份 HEAD 到 ORIG_HEAD

5. config

此文件存储项目本地的 git 设置,典型内容如下:


[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
[remote "origin"]
        url = git@gitlab.xxxx.com/xxx.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
[branch "v2.6.0"]
        remote = origin
        merge = refs/heads/v2.6.0
[branch "v2.8.0"]
        remote = origin
        merge = refs/heads/v2.8.0

[core] 段的内容跟 git config 命令对应

执行以下命令:


git config user.name abc
git config user.email abc@abc.com

会在 config 文件中追加以下内容:


... ...
[user]
name = abc
email = abc@abc.com

git config --global 影响的则是全局配置文件 ~/.gitconfig。

[remote] 段表示远程仓库配置

[branch] 段表示分支同步设置

假设当前在 master 分支,执行 git pull 若出现以下提示:


There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

git pull 就说明 .git/config 文件缺少对应的 [branch "master"] 字段。

解决方案为:


git branch -u origin/master master
# 或者执行一次 push
git push -u origin master

会出现提示:


Branch 'master' set up to track remote branch 'master' from 'origin'.

其实就是生成以下内容在 .git/config中:


[branch "master"]
remote = origin
merge = refs/heads/master

手动编辑 .git/config,效果一样。这就是 upstream 的真正含义,即生成 config 中的这段配置。

6. description

说明这个文件主要用于 GitWeb 的描述,如果要启动 GitWeb 可用如下命令:


# 确保lighttpd已安装: brew install lighttpd
$ git instaweb --start

默认会启动 lighttpd 服务并打开浏览器 http://127.0.0.1:1234 (试着改成对外IP并分享给别人?)

以下显示当前的 git 仓库名称以及描述,默认的描述如下:

description

默认描述

上面这段话就是默认的 description 文件的内容,编辑这个文件来让你 GitWeb 描述更友好。

7. hooks/目录

存放 git hooks,用于在 git 命令前后做检查或做些自定义动作。运行 ls -F1 .git/hooks


prepare-commit-msg.sample  # git commit 之前,编辑器启动之前触发,传入 COMMIT_FILE,COMMIT_SOURCE,SHA1
commit-msg.sample          # git commit 之前,编辑器退出后触发,传入 COMMIT_EDITMSG 文件名
pre-commit.sample          # git commit 之前,commit-msg 通过后触发,譬如校验文件名是否含中文
pre-push.sample            # git push 之前触发

pre-receive.sample         # git push 之后,服务端更新 ref 前触发
update.sample              # git push 之后,服务端更新每一个 ref 时触发,用于针对每个 ref 作校验等
post-update.sample         # git push 之后,服务端更新 ref 后触发

pre-rebase.sample          # git rebase 之前触发,传入 rebase 分支作参数
applypatch-msg.sample      # 用于 git am 命令提交信息校验
pre-applypatch.sample      # 用于 git am 命令执行前动作
fsmonitor-watchman.sample  # 配合 core.fsmonitor 设置来更好监测文件变化



参考
https://git-scm.com/docs/githooks

如果要启用某个 hook,只需把 .sample 删除即可,然后编辑其内容来实现相应的逻辑。

比如要校验每个 commit message 至少要包含两个单词,否则就提示并拒绝提交,将 commit-msg.sample 改为 commit-msg 后,编辑如下:


#!/bin/sh
grep -q 'Ss+S' $1 || { echo '提交信息至少为两个单词' && exit 1; }

这样当提交一个 commit 时,会执行 bash 命令: .git/hooks/commit-msg .git/COMMIT_EDITMSG,退出值不为 0,就拒绝提交。

8. info/目录

此文件夹基本就有两个文件:

  1. 文件 info/exclude 用于排除规则,与 .gitignore 功能类似。
  2. 可能会包含文件 info/refs ,用于跟踪各分支的信息。此文件一般通过命令 git update-server-info 生成,内容通常如下:

9. logs/目录

记录了操作信息,git reflog 命令以及像 HEAD@{1} 形式的路径会用到。如果删除此文件夹(危险!),那么依赖于 reflog 的命令就会报错。

文件夹 objects/

此文件夹简单说,就是 git的数据库,运行 tree .git/objects,可以看到目录结构:


.git/objects/
|-- 0c
|   `-- d370696b581c38ee01e62b148a759f80facc2d
|-- 59
|   `-- 3d5b490556791212acd5a516a37bbfa05d44dd
|-- 61
|   `-- be44eedde61d723e5761577a2b420ba0fc2794
|-- 64
|   `-- c0aed8ddcbb546bdcec2848938fc82348db227
|-- d4
|   `-- 9904676ce8ddde276bdbfa9bbec313e90e0f50
|-- info
`-- pack
    |-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
    `-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack

这些文件分两种形式:pack压缩包 形式放在 pack/ 目录下,除此之外都是 hash文件 形式,被叫做 loost objects。

这个文件夹以及相应的算法,我没找到独立的名称,就叫它 hash-object 体系吧。因为确实有个 git hash-object 命令存在,是一个底层的负责生成这些 loost objects 文件,如果要看到这些文件各自的含义,执行以下命令:


git cat-file --batch-check --batch-all-objects

可以看到


04c87c65f142f33945f2f5951cf7801a32dfa240 commit 194
098217953a6ca169bed33d2be8a07d584fcdaf30 tree 31
0cd370696b581c38ee01e62b148a759f80facc2d commit 245
2a810017bfc85d7db2627f4aabdaa1583212bda3 blob 19
3920a07c1d5694df6b8658592b0939241d70e9e5 tree 93
593d5b490556791212acd5a516a37bbfa05d44dd tag 148
61be44eedde61d723e5761577a2b420ba0fc2794 tree 154
... ...

但你会发现这个列表里有些值在文件夹中并不存在,因为除了 loost objects 它还汇总了 pack 文件中的内容。hash文件

又称为 loose object,文件名称共由40字符的 SHA-1 hash 值组成,其中前两个字符为文件夹分桶,后38个字符为文件名称。

按文件内容可分为四种类型:commit, tree, blob, tag,若执行以下命令会生成所有四种类型:


echo -en 'xxn' > xx # 共 3 个字符
git add .
git commit -m 'update xx'
git tag -a 'v1.0' -m 'release: 1.0.0'

经过以上操作后,对比一下文件树,发现多了四个 hash文件:


|-- 0c
| `-- d370696b581c38ee01e62b148a759f80facc2d
|-- 18
| `-- 143661f96845f11e0b4ab7312bdc0f356834ce
|-- 30
| `-- 20feea86d222d83218eb3eb5aa9f58f73df04d
|-- 59
| `-- 3d5b490556791212acd5a516a37bbfa05d44dd
|-- 61
| `-- be44eedde61d723e5761577a2b420ba0fc2794
|-- 64
| `-- c0aed8ddcbb546bdcec2848938fc82348db227
|-- ad
| `-- f4c9afac7afae3ff3e95e6c4eefe009d547f00
|-- cc
| `-- c9bd67dc5c467859102d53d54c5ce851273bdd
|-- d4
| `-- 9904676ce8ddde276bdbfa9bbec313e90e0f50
|-- info
`-- pack
|-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
`-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack

这四个 hash文件 分别是:


cc/c9bd67dc5c467859102d53d54c5ce851273bdd # blob
30/20feea86d222d83218eb3eb5aa9f58f73df04d # commit
ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00 # tree
18/143661f96845f11e0b4ab7312bdc0f356834ce # tag 

其实这些文件都经过了压缩,压缩形式为 zlib。先安装一下解压工具 macOS 版 brew install pigz 或 windows 版 pigz,后执行:


$ pigz -d < .git/objects/cc/c9bd67dc5c467859102d53d54c5ce851273bdd

# BLOB类型,显示结果为>>>>(注意xx后有个n)
blob 3xx


$pigz -d < .git/objects/30/20feea86d222d83218eb3eb5aa9f58f73df04d

# COMMIT类型,显示结果为>>>>
commit 248tree adf4c9afac7afae3ff3e95e6c4eefe009d547f00
parent 0cd370696b581c38ee01e62b148a759f80facc2d
author jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562044880 +0800
committer jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562044880 +0800

update xx

$ pigz -d < .git/objects/ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00

# TREE类型,显示结果为>>>>
tree 154100644 abc*???]}?bJ?ڡX2??100644 asdf???CK?)?wZ???S?100644 iou???CK?)?wZ???S?100644 xx?ɽg?FxY-S?L?Q';?100644 yy???CK?)?wZ???S?


$ pigz -d < .git/objects/18/143661f96845f11e0b4ab7312bdc0f356834ce

# TAG类型,显示结果为>>>>
tag 155object 3020feea86d222d83218eb3eb5aa9f58f73df04d
type commit
tag v1.0
tagger jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562045942 +0800

release: 1.0.0

会发现,显示结果都是 type size+内容 形式,这就是 object 文件的存储格式:


[type] [size][NULL][content]

type 可选值:commit, tree, blob, tag,NULL 就是C语言里的字符结束符:�,size 就是 NULL后内容的字节长度。

type 的几种类型可以使用 git cat-file -t hash 看到,内容可以用 git cat-file -p hash 看到。


git cat-file -t ccc9bd67dc5c467859102d53d54c5ce851273bdd
# 显示结果为>>>>
blob

git cat-file -p ccc9bd67dc5c467859102d53d54c5ce851273bdd
# 显示结果为>>>>
xx

所以 blob 文件就是对原文件内容的全量拷贝,同时前面加了 blob size�,而文件名称的 hash 值计算是计算整体字符的 SHA-1 值:


echo -en 'blob 3�xxn' | shasum
# 显示结果为>>>>
ccc9bd67dc5c467859102d53d54c5ce851273bdd  -

知道原理后,其它类型格式请自行参考 斯坦福 Ben Lynn 所著的 GitMagic。

所以,当我们 git show 3020feea86d222d83218eb3eb5aa9f58f73df04d 时,会发生些什么?

找到 3020feea86d222d83218eb3eb5aa9f58f73df04d 这个 commit,显示出来
找到此 commit 关联的 tree object: adf4c9afac7afae3ff3e95e6c4eefe009d547f00,拉取相应的 blob 文件,并与当前工作区内的文件做 diff,然后显示出来

这就是 objects/ 文件夹作为 git数据库 被使用的真实例子。pack文件

为什么会有 .pack 文件?

由于每次 commit 都会生成许多 hash文件,并且由于 blob 文件都是全量存储的,导致 git 效率下降,于是有了 pack-format,优势:

对于大仓库存储效率高
利于网络传输,便于备份
增量存储,优化磁盘空间
将 .git/objects 下的部分文件打包成 pack格式


$ tree .git/objects/ | wc -l
311

$ git gc
Enumerating objects: 288, done.
Counting objects: 100% (288/288), done.
Delta compression using up to 4 threads
Compressing objects: 100% (287/287), done.
Writing objects: 100% (288/288), done.
Total 288 (delta 131), reused 90 (delta 0)

$ tree .git/objects/ | wc -l
12

可以看到文件数量减小了不少,其中大部分文件被打到一个 .pack 包中,并且是增量存储,有部分变更的文件只存储 基础hash + 变更内容,磁盘空间优化很明显。

git gc 其实运行了两条命令:git repack 用来打包 和 git prune-packed 用来移除已打包的 hash文件;

11.文件夹refs

refs 可以理解成文件系统中的 symbol link,看下结构:


$ tree .git/refs/

.git/refs
|-- heads
| `-- master
`-- tags
`-- v1.0

$ cat .git/refs/heads/master
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5

$ cat .git/refs/tags/v1.0
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5

$ git cat-file -t 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
commit

可以看到 master 和 v1.0 都指向 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 这个 commit。

refs/heads/ 文件夹内的 ref 一般通过 git branch 生成。git show-ref --heads 可以查看。

refs/tags/ 文件夹内的 ref 一般通过 git tag 生成。git show-ref --tags 可以查看。

如下:


$ git branch abc

$ tree .git/refs/

.git/refs/
|-- heads
| |-- abc
| `-- master
`-- tags
`-- v1.0

$ cat .git/refs/heads/abc
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5

说明新建分支其实就是生成了一个指向某个 commit 的 symbol link,当然在这里叫做 ref。

git tag 命令本质与 git branch 相同,只生成一个 ref 放在 tags 目录下,所以被称为 lightweight tag。

git tag -a xx 命令会首先生成一个类型为 tag 的 hash文件 放到 objects/ 目录,然后生成 ref 放到 tags 目录下指向那个文件。这就叫做 annotated tag,好处是可包含一些元信息如 tagger 和 message,被 git 的 hash-object 算法管理,可被 GPG 签名等,所以更稳定,更安全。

使用以下命令来拿到 refs 文件夹存储的信息:


$ git show-ref --head --dereference
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0
5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}

我们来看这些信息如何变化的:


$ touch new_file && git add . && git commit -m 'add new_file'
[master 44b0d05] add new_file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 new_file

$ git show-ref --head --dereference
44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 HEAD
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc
44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 refs/heads/master
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0
5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}

diff 一下可以看到:


5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master

这两行发生了变化。也就是每次 commit 时,HEAD 与 heads 都会自动更新。

12. index文件

文件保存成二进制对象以后,还需要通知 Git 哪些文件发生了变动。所有变动的文件,Git 都记录在一个区域,叫做"暂存区"(英文叫做 index 或者 stage)。等到变动告一段落,再统一把暂存区里面的文件写入正式的版本历史。

git update-index命令用于在暂存区记录一个发生变动的文件。


$ git update-index --add --cacheinfo 100644 
3b18e512dba79e4c8300dd08aeb37f8e728b8dad test.txt

上面命令向暂存区写入文件名test.txt、二进制对象名(哈希值)和文件权限。

git ls-files命令可以显示暂存区当前的内容。


$ git ls-files --stage
100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 test.txt

上面代码表示,暂存区现在只有一个文件test.txt,以及它的二进制对象名和权限。知道了二进制对象名,就可以在.git/objects子目录里面读出这个文件的内容。

git status命令会产生更可读的结果。


$ git status
要提交的变更:
    新文件: test.txt

上面代码表示,暂存区里面只有一个新文件test.txt,等待写入历史。

资料来源

参考:https://developer.aliyun.com/article/716483

Git远程仓库

Git 并不像 SVN 那样有个中心服务器。目前我们使用到的 Git 命令都是在本地执行,如果你想通过 Git 分享你的代码或者与其他开发人员合作。 你就需要将数据放到一台其他开发人员能够连接的服务器上。

1.添加远程仓库


git remote add [shortname] [url] #添加远程仓库
git remote rm name  # 删除远程仓库
git remote rename old_name new_name  # 修改仓库名

2.查看远端仓库


$ git remote
origin
$ git remote -v
origin    git@github.com:tianqixin/runoob-git-test.git (fetch)
origin    git@github.com:tianqixin/runoob-git-test.git (push)

3.获取远端仓库代码 git fetch


# 只能fetch到一个空白的分支,然后可以手动merge
$ git fetch <远程主机名> <远程分支名>:<本地分支名>

不填的话都是默认

4.拉取 git pull


git pull <远程主机名> <远程分支名>:<本地分支名>
# 允许合并不相关的分支 
$ git pull --allow-unrelated-histories

git pull操作其实是git fetch 与 git merge 两个命令的集合。 git fetch 和 git merge FETCH_HEAD 的简写。

相关文档:https://www.runoob.com/git/git-remote-repo.html

5.推送 git push


# 基本
$ git push <远程主机名> <本地分支名>:<远程分支名>
# 强制推送
$ git push --force origin master
# 删除远程分支
$ git push origin --delete master
# 允许合并不相关的分支
$ git push --allow-unrelated-histories


提示
如果另一个开发者在我们之前已经做过一次 push 操作,此次 push 命令就会被拒绝传送提交。这时候,我们必须要先做一次 pull 操作,将其他人新上载的更新取回,并本地合并。

如果本地分支名与远程分支名相同,则可以省略冒号,带上-u 参数相当于记录了push到远端分支的默认值,这样当下次我们还想要继续push的这个远端分支的时候推送命令就可以简写成git push即可。

Git 分支

1.创建分支命令 git branch


# 创建分支
$ git branch <branch>
# 创建分支并跟踪远程分支
$ git branch -u o/master foo

2.切换分支命令 git checkout

第一作用是切换分支,第二个是撤销修改。


# 切换指定分支
$ git checkout <branch>|<hash>|<tag>
# 创建一个的分支,它跟踪远程分支
$ git checkout -b 本地分支名x origin/远程分支名x
# 从暂存区恢复到工作区
$ git checkout .


提示
当你切换分支的时候,Git 会用该分支的最后提交的快照替换你的工作目录的内容, 所以多个分支不需要多个目录。

实际测试:

假设分支1 a文件未提交,分支2 a文件已提交。切换到到分支2时会替换 分支1的a文件。1切换到2时也会替换a文件;

两个分支都已经提交的 切换时会互相替换。一个提交一个没提交时,从a到b,b会保持a的暂存区和工作区

3.合并分支命令 git merge


# 合并指定分支到当前分支
$ git merge <branch>

4.删除分支 git branch -d


# 删除指定分支
$ git branch -d <branch>

5.分支列表 git branch


# 列出所有分支
$ git branch
# 查看远程所有分支
$ git branch -r
# 查看本地和远程所有分支
$ git branch -a

列出分支时,带*号的分支为当前活动分支

5.重命名分支 git branch -M


# 重命名指定分支
# 不填old默认重命名当前分支
$ git branch -m old new

# 强制重命名指定分支
$ git branch -M old new

git rebase 变基

1.介绍

Git rebase,通常被称作变基或衍合, 可以理解为另外一种合并的方式,与merge 会保留分支结构和原始提交记录不同,rebase 是在公共祖先的基础上,把新的提交链截取下来,在目标分支上进行重放,逐个应用选中的提交来完成合并。

不同公司,不同情况有不同使用场景,不过大部分情况推荐如下:

自己单机的时候,拉公共分支最新代码的时候使用rebase,也就是git pull -r或git pull --rebase。这样的好处很明显,提交记录会比较简洁。但有个缺点就是rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛,所以看个人需求了。

往公共分支上合代码的时候,使用merge。如果使用rebase,那么其他开发人员想看主分支的历史,就不是原来的历史了,历史已经被你篡改了。举个例子解释下,比如张三和李四从共同的节点拉出来开发,张三先开发完提交了两次然后merge上去了,李四后来开发完如果rebase上去(注意李四需要切换到自己本地的主分支,假设先pull了张三的最新改动下来,然后执行,然后再git push到远端),则李四的新提交变成了张三的新提交的新基底,本来李四的提交是最新的,结果最新的提交显示反而是张三的,就乱套了。

正因如此,大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序

2.原理

变基操作的工作原理很简单:Git 会让我们想要移动的提交序列在目标分支上按照相同的顺序重新再现一遍。这就相当于我们为各个原提交做了个副本,它们拥有相同的修改集、同一作者、日期以及注释信息。

3.命令


# 可以是commit 版本号、分支名称,合并多个提交到一个版本
$ git rebase -i  [startpoint]  [endpoint]  
# 变基发生冲突时,解决后继续变基
$ git rebase --continue
# 无视冲突,继续变基操作
$ git rebase --skip 
# 发生冲突时中断变基
$ git rebase --abort"

-i的意思是--interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit(注:该区间指定的是一个前开后闭的区间)。

Git 标签

Git 中的tag指向一次commit的id,通常用来给开发分支做一个标记,如标记一个版本号。

1.添加标签


git tag -a version -m "note"

注解:git tag 是打标签的命令,-a 是添加标签,其后要跟新标签号,-m 及后面的字符串是对该标签的注释。

2.提交标签到远程仓库


git push origin -tags




请登录后查看

集步致远 最后编辑于2024-05-21 16:08:24

快捷回复
回复({{post_count}}) {{!is_user ? '我的回复' :'全部回复'}}
回复从新到旧

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}}

作者 管理员 企业

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest==1? '取消推荐': '推荐'}}
{{item.floor}}#
{{item.user_info.title}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
{{item.like_count}}
{{item.showReply ? '取消回复' : '回复'}}
删除
回复
回复

{{itemc.user_info.nickname}}

{{itemc.user_name}}

作者 管理员 企业

回复 {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}   {{itemc.ip_address}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回复' : '回复'}}
删除
回复
回复
查看更多
回复
回复
716
{{like_count}}
{{collect_count}}
添加回复 ({{post_count}})

相关推荐

集步致远 企业
金牌技术服务商:专注CRMEB产品的深度定制与合规模式开发,点头像加微信,电话:13515970381

回答

345

发布

159

经验

20530

快速安全登录

使用微信扫码登录
{{item.label}} {{item.label}} {{item.label}} 板块推荐 常见问题 产品动态 精选推荐 首页头条 首页动态 首页推荐
加精
取 消 确 定
回复
回复
问题:
问题自动获取的帖子内容,不准确时需要手动修改. [获取答案]
答案:
提交
bug 需求 取 消 确 定

微信登录/注册

切换手机号登录

{{ bind_phone ? '绑定手机' : '手机登录'}}

{{codeText}}
切换微信登录/注册
暂不绑定
CRMEB客服

CRMEB咨询热线 咨询热线

400-8888-794

微信扫码咨询

CRMEB开源商城下载 开源下载 CRMEB官方论坛 帮助文档
返回顶部 返回顶部
CRMEB客服