在本地创建仓库,并覆盖旧的仓库

rm -rf .git
git init
git add .
git commit -m .
git branch -M main
git remote add origin git@github.com:MisakeMikoto/blog.git(地址)
git push -u origin main -f

Git 常用命令

git init
git clone
git add
git config # 配置信息
git status # 查看文件状态
git diff # 查看更新的详细信息命令
git commit # 提交命令
git reset HEAD # 取消缓存命令
git rm # 删除
git mv # 移动或者重命名

git config

git config --global user.name '你的用户名'
git config --global user.email '你的邮箱'

git status

git add <文件名> # 暂存文件
git checkout <文件名> # 放弃未暂存文件的修改
git commit -a # 将被修改的文件暂存并提交

git diff

查看更新的详细信息

git diff # 尚未缓存的改动
git diff --cached # 已缓存的改动
git diff HEAD # 已缓存的和未缓存的所有改动
git diff --stat # 展示摘要,而非整个

git commit

git commit -m "第一次版本提交"
git commit -am "第一次版本提交" # 跳过add这一步,使用 -a

git reset HEAD

git reset HEAD test.txt # 取消缓存的 test.txt 使用commit 提交的时候,该文件不会被提交,相当于取消了git add test.txt

git rm

Git

Git 有三种状态,你的文件可能处于其中之一: 已提交(committed)已修改(modified)已暂存(staged)

  • 已修改表示修改了文件,但还没保存到数据库中。
  • 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
  • 已提交表示数据已经安全地保存在本地数据库中。

工作区、暂存区以及 Git 目录。

工作区是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。

暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。

Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。

基本的 Git 工作流程如下:

  1. 在工作区中修改文件。
  2. 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
  3. 提交更新,找到暂存区的文件,将快照永久性存储到 Git 目录。

Git 配置

  • /etc/gitconfig 用于存储对于系统上每一个用户以及他们仓库的配置,在执行git config后加上–system的选项,就会读写该文件中的配置
  • /.gitconfig或/.config/git/config文件: 只针对当前用户。可以传递–global让git读写此文件,这会对系统上的所有的仓库生效
  • 当前使用仓库的 Git 目录中的 config 文件(即 .git/config):针对该仓库。 你可以传递 --local 选项让 Git 强制读写此文件,虽然默认情况下用的就是它

每一个级别会覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。

在 Windows 系统中,Git 会查找 $HOME 目录下(一般情况下是 C:\Users\$USER )的 .gitconfig 文件。

可以通过以下命令查看所有的配置以及它们所在的文件:

git config --list --show-origin

用户信息

安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:

git config --global user.name "John Doe"
git config --global user.email johndoe@example.com

文本编辑器

如果你想使用不同的文本编辑器,例如 Emacs,可以这样做:

git config --global core.editor emacs

对于 Notepad++,一个流行的代码编辑器来说,你可能想要使用 32 位的版本, 因为在本书编写时 64 位的版本尚不支持所有的插件。 如果你在使用 32 位的 Windows 系统,或在 64 位系统上使用 64 位的编辑器,那么你需要输入如下命令:

git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin

检查配置信息

如果想要检查你的配置,可以使用 git config --list 命令来列出所有 Git 当时能找到的配置。

你可能会看到重复的变量名,因为 Git 会从不同的文件中读取同一个配置(例如:/etc/gitconfig~/.gitconfig)。 这种情况下,Git 会使用它找到的每一个变量的最后一个配置。

你可以通过输入 git config <key>: 来检查 Git 的某一项配置

git config user.email

获取帮助

若你使用 Git 时需要获取帮助,有三种等价的方法可以找到 Git 命令的综合手册(manpage)

git help <verb>
git <verb> --help
man git-<verb>
git add -h

Git 总结

状态

Git 下文件生命周期图。

  • Tracked (git add)
    • Unmodified (git commit)
    • Modified (modify or delete file )
    • Staged (git add)
  • Untracked(git rm )
    • git rm will delete the file from disk, If we want to delete file just from staged use –cached

git log

check the commit history

  • -p 按补丁格式显示每个提交引入的差异。
    --stat 显示每次提交的文件修改统计信息。
    --shortstat 只显示 –stat 中最后的行数修改添加移除统计。
    --name-only 仅在提交信息后显示已修改的文件清单。
    --name-status 显示新增、修改、删除的文件清单。
    --abbrev-commit 仅显示 SHA-1 校验和所有 40 个字符中的前几个字符。
    --relative-date 使用较短的相对时间而不是完整格式显示日期(比如“2 weeks ago”)。
    --graph 在日志旁以 ASCII 图形显示分支与合并历史。
    --pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline、short、full、fuller 和 format(用来定义自己的格式)。
    --oneline --pretty=oneline --abbrev-commit 合用的简写。

    format “format”

    • 选项 说明
      %H 提交的完整哈希值
      %h 提交的简写哈希值
      %T 树的完整哈希值
      %t 树的简写哈希值
      %P 父提交的完整哈希值
      %p 父提交的简写哈希值
      %an 作者名字
      %ae 作者的电子邮件地址
      %ad 作者修订日期(可以用 –date=选项 来定制格式)
      %ar 作者修订日期,按多久以前的方式显示
      %cn 提交者的名字
      %ce 提交者的电子邮件地址
      %cd 提交日期
      %cr 提交日期(距今多长时间)
      %s 提交说明

限制 git log 输出的选项

选项 说明
-<n> 仅显示最近的 n 条提交。
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示作者匹配指定字符串的提交。
--committer 仅显示提交者匹配指定字符串的提交。
--grep 仅显示提交说明中包含指定字符串的提交。
-S 仅显示添加或删除内容匹配指定字符串的提交。

git commit

git commit -amend 可以从新提交之前的提交,而不产生新的提交,只会在原提交时做出修改

git reset HEAD “file path” 可以取消该文件的暂存

git checkout – “file path” 可以把该文件还原为本地的最新快照,相当于撤销了对该文件的修改

git status -s 状态记录

新添加的未跟踪文件前面有 ?? 标记

新添加到暂存区中的文件前面有 A 标记

修改过的文件前面有 M 标记

左栏指明了暂存区的状态,右栏指明了工作区的状态

git diff

git diff 查看暂存前后的变化

git diff cached/staged 查看暂存起来的变化

git rm

git rm –cached 删除git对于文件的追踪,但是保留本地的文件

git rm -f 强制删除git对于文件的追踪,无论该文件是否修改,是否暂存

git rm log/\*.log 删除git 目录下所有以log为后缀的文件,git有自己的文件模式扩展方式,可以不用shell来帮忙展开。

git mv

修改文件名

git checkout

撤销对于文件的修改,将文件还原为上一次暂存时候的样子

git remote 远程仓库

查看远程仓库

-v 显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL

git remote add <shortname> <url> 添加远程仓库,并使用别名

git remote show <remote> 查看某一个远程仓库的更多信息

git remote rename <oldname> <newname> 远程仓库本地简写重命名

git remote rm/remove <remote> 删除一个远程仓库,一旦你使用这种方式删除了一个远程仓库,那么所有和这个远程仓库相关的远程跟踪分支以及配置信息也会一起被删除。

git fetch

从远程仓库中抓取与拉取

git fetch <remote> 这个命令会访问远程仓库,从中拉取所有你还没有的数据,执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。

git pull

从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支

git push

git push <remote> <branch>

推送到远程的仓库具体的分支

git tag

打标签

-l, -list 列出所有标签 以字母顺序列出标签

git tag -l/-list <wildcard> 按照特定的模式查找标签

标签分类:Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)。

附注标签:git tag -a <Label> -m <description of label>

轻量标签:git tag <label>

后期打标签:git tag -a <label> <commit>

共享标签:git push <remote> <tagname> 默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 git push <remote> –tags 如果想要一次性推送很多标签,也可以使用带有 --tags 选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里。现在,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。

删除标签:git tag -d <tagname>

删除远程标签:git push <remote> :refs/tags/<tagname> 面这种操作的含义是,将冒号前面的空值推送到远程标签名,从而高效地删除它

删除远程标签2:git push <remote> –delete <tagname>

检出标签:**git checkout <tagname> ** 这会使你的仓库处于“分离头指针(detached HEAD)”的状态——这个状态有些不好的副作用:

在“分离头指针”状态下,如果你做了某些更改然后提交它们,标签不会发生变化, 但你的新提交将不属于任何分支,并且将无法访问,除非通过确切的提交哈希才能访问。 因此,如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支:

git checkout -b <newbrance> <tagname> 如果在这之后又进行了一次提交,version2 分支就会因为这个改动向前移动, 此时它就会和 v2.0.0 标签稍微有些不同,这时就要当心了。

git show

查看标签信息和与之对应的提交信息

git 别名

设置别名: git config –global alias.co checkout : co == checkout

Git 只是简单地将别名替换为对应的命令。 然而,你可能想要执行外部命令,而不是一个 Git 子命令。 如果是那样的话,可以在命令前面加入 ! 符号

git config --global alias.visual '!gitk'

分支

$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'

当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和, 然后在 Git 仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象, 它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。 如此一来,Git 就可以在需要的时候重现此次保存的快照。

现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个 对象 (记录着目录结构和 blob 对象索引)以及一个 提交 对象(包含着指向前述树对象的指针和所有提交信息)。

首次提交对象及其树结构。

首次提交对象及其树结构

做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。

提交对象及其父对象。

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。 master 分支会在每次提交时自动向前移动。

分支及其提交历史。

创建

git branch <branchName> 这会在当前所在的提交对象上创建一个指针。

两个指向相同提交历史的分支。

Git 又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为 HEAD 的特殊指针,在 Git 中,它是一个指针,指向当前所在的本地分支

你可以简单地使用 git log 命令查看各个分支当前所指的对象。 提供这一功能的参数是 --decorate

分支切换

git checkout testing

这样 HEAD 就指向 testing 分支了。

HEAD 指向当前所在的分支。

那么,这样的实现方式会给我们带来什么好处呢? 现在不妨再提交一次

HEAD 分支随着提交操作自动向前移动。

HEAD 分支随着提交操作自动向前移动

如图所示,你的 testing 分支向前移动了,但是 master 分支却没有,它仍然指向运行 git checkout 时所指的对象。 这就有意思了,现在我们切换回 master 分支看看:

git checkout master

检出时 HEAD 随之移动。

这条命令做了两件事。 一是使 HEAD 指回 master 分支,二是将工作目录恢复成 master 分支所指向的快照内容。 也就是说,你现在做修改的话,项目将始于一个较旧的版本。 本质上来讲,这就是忽略 testing 分支所做的修改,以便于向另一个方向进行开发。

我们不妨再稍微做些修改并提交:

vim test.rb
git commit -a -m 'made other changes'

现在,这个项目的提交历史已经产生了分叉(参见 项目分叉历史)。 因为刚才你创建了一个新分支,并切换过去进行了一些工作,随后又切换回 master 分支进行了另外一些工作。 上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。 而所有这些工作,你需要的命令只有 branchcheckoutcommit

项目分叉历史。

项目分叉历史

git log –oneline –decorate –graph –all

$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。 创建一个新分支就相当于往一个文件中写入 41 个字节(40 个字符和 1 个换行符)

分支的新建和合并

git checkout -b <branchname> 新建一个分支并同时切换到那个分支上

git merge <branchname> 合并当前分支与branchname指向分支

git branch -d <branchname> 删除分支

遇到冲突时的分支合并

使用git status显示冲突

使用vscode打开冲突的文件,解决冲突后 git commit

图形化工具解决冲突 git mergetool

分支管理

查看每一个分支的最后一次提交:git branch -v

查看已经合并或者未合并的分支:git branch –merged/–no-merged

强制删除不需要合并的分支:git branch -D

远程分支

git ls-remote <remote> 显式地获得远程引用的完整列表

git remote show <remote> 获取远程分支的更多信息

克隆之后的服务器与本地仓库。

本地与远程的工作可以分叉

如果你在本地的 master 分支做了一些工作,在同一段时间内有其他人推送提交到 git.ourcompany.com 并且更新了它的 master 分支,这就是说你们的提交历史已走向不同的方向。 即便这样,只要你保持不与 origin 服务器连接(并拉取数据),你的 origin/master 指针就不会移动。

更新远程跟踪分支

git fetch <remote>

在本地创建基于远程分支的本地分支

git branch -b <branch> <remote>/<branch>

简化版:git branch –track <remote>/<branch>

进一步:如果尝试检出的分支 (a) 不存在且 (b) 刚好只有一个名字与之匹配的远程分支;git checkout <branch> 那么这个炒作会创建一个跟踪分支

设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支 : git branch -u/–set-upstream-to <remote>/<branch>

查看跟踪的所有分支:git branch -vv

删除远程分支

git push <remote> –delete <branch>

变基

通过合并操作来整合分叉了的历史。

git checkout experiment
git rebase master

将 `C4` 中的修改变基到 `C3` 上。

现在回到 master 分支,进行一次快进合并。

git checkout master
git merge experiment

两种整合方法的最终结果没有任何区别,但是变基使得提交历史更加整洁。 你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的, 但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。

一般我们这样做的目的是为了确保在向远程分支推送时能保持提交历史的整洁——例如向某个其他人维护的项目贡献代码时。 在这种情况下,你首先在自己的分支里进行开发,当开发完成时你需要先将你的代码变基到 origin/master 上,然后再向主项目提交修改。 这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可。

请注意,无论是通过变基,还是通过三方合并,整合的最终结果所指向的快照始终是一样的,只不过提交历史不同罢了。 变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。

从一个主题分支里再分出一个主题分支的提交历史

从一个主题分支里再分出一个主题分支的提交历史。

git rebase --onto master server client

以上命令的意思是:“取出 client 分支,找出它从 server 分支分歧之后的补丁, 然后把这些补丁在 master 分支上重放一遍,让 client 看起来像直接基于 master 修改一样”。这理解起来有一点复杂,不过效果非常酷。

截取主题分支上的另一个主题分支,然后变基到其他分支。

变基的风险

如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。

如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

解决方法

在一个被变基然后强制推送的分支上再次执行变基,不一定有效,只有git 可以识别出对方提交的变基,与本地已有的快照的关系(git 推断出本地的快照如果变基为对方提交的快照)

git pull –rebase / git fetch <remote> , git rebase <remote>/<branch>