[知乎转载]git和github使用详解(发现有很多已经忘了,复习的同时顺便练练在论坛上转贴)

git简介

git 是一个分布式版本控制系统(Distributed Version Control System,简称 DVCS)在这类系统中,像(Git、Mercurial、Bazaar 以及 Darcs 等)客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复,因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份

诞生背景

同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代

Linux 内核开源项目有着为数众多的参与者,绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码

到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统,他们对新的系统制订了若干目标:

  • 速度
  • 简单的设计
  • 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
  • 完全分布式
  • 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标,它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统

安装git

win:在 git 的官网下载对应安装包,安装即可,官网下载地址 https://git-scm.com/download/win

linux:安装文档 https://git-scm.com/download/linux

# CentOS/Fedora
yum install git
或者
dnf install git

# Debian/Ubuntu
apt-get install git

使用git

参考文档:https://git-scm.com/book/zh/v2/

设置签名

安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址(否则会报错)因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改,其目的是为了区分不同开发人员的身份,很多 GUI 工具都会在第一次运行时帮助你配置这些信息

注意:这里的用户名和邮件地址和远程仓库github的账号是没有任何关系的

# 设置命令 git config :
1、仓库级别:仅在当前本地库范围内有效
git config user.name stache
git config user.email stache@admin.com
# 设置后信息保存于:项目路径/.git/config

2、系统用户级别(--global):登陆当前操作系统的用户范围
git config --global user.name stache
git config --global user.email stache@admin.com
# 设置后信息保存于:~/.gitconfig

# 级别优先级:就近原则,仓库级别优于系统用户级别

常用命令

# 配置默认文本编辑器,当 Git 需要你输入信息时会调用它,如果未配置 Git 会使用操作系统默认的文本编辑器
git config --global core.editor vim

# 在 Windows 系统上,如果你想要使用指定的文本编辑器,那么必须指定可执行文件的完整路径
git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"

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

# 查看 Git 的某一项配置
git config user.name

# 初始化本地库
cd /home/user/my_project
git init

git status #查看工作区、暂存区状态

# 格式更为紧凑的输出
git status -s
git status --short

git add file1 #将工作区的“新建/修改”添加到暂存区

git restore --staged file1 #将文件从暂存区撤离

git commit -m "this is first commit" file1.txt #将文件提交到本地仓库

初始化本地库

如果你有一个尚未进行版本控制的项目目录,想要用 Git 来控制它,那么首先需要进入该项目目录中,然后执行 git init 命令即可初始化一个本地仓库

cd /home/code/my_project
git init

该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪

可以通过 git add 命令来对指定的文件进行追踪,然后执行 git commit 进行提交

$ git add README #添加文件追踪
$ git commit -m "first commit" README #将文件提交到本地仓库

克隆现有仓库

如果你想获得一份已经存在了的 Git 仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就可以使用 git clone 命令

# 克隆仓库的命令格式 git clone <url> 
# 比如,要克隆 Git 的链接库 libgit2
git clone https://github.com/libgit2/libgit2

# 如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以通过额外的参数指定新的目录名
git clone https://github.com/libgit2/libgit2 mylibgit2

# 克隆主要包含三个部分:
# 1、克隆会把远程库下载到本地
# 2、然后初始化本地仓库
# 3、设置 origin 远程仓库地址别名

克隆会把远程库下载到本地,然后初始化本地仓库,并设置 origin 远程仓库地址别名

查看仓库状态

git status 可以查看工作区和暂存区的状态 git status 命令的输出十分详细,但其用语有些繁琐,使用 git status -s 命令或 git status --short 命令,你将得到一种格式更为紧凑的输出

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   ccc.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:   ccc.txt
        modified:   test01.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .gitignore
        simple-java-maven-app/

$ git status -s
MM ccc.txt
A  ddd.txt
 M test01.txt
?? .gitignore
?? simple-java-maven-app/

输出中左侧部分通过两个大写字符显示状态标识,两个标识字符我们暂且叫为左栏 和 右栏,左栏指明了暂存区的状态,右栏指明了工作区的状态,新添加的未跟踪文件前面有 ?? 标记,新添加到暂存区中的文件前面有 A 标记,修改过的文件前面有 M 标记

提交历史记录

在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史,可使用 git log 命令,在不传入任何参数的默认情况下,git log 会按时间先后顺序列出所有的提交,最近的更新排在最上面,这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明

$ git log
commit b31c616d2039451f183ecbbcabdfa95bc3ca9494 (HEAD -> master)
Author: stachefix <stache@foxmail.com>
Date:   Tue Jun 7 10:42:02 2022 +0800

    first commit

git log 有许多选项可以帮助你搜寻你所要找的提交,如下:

# 选项是 -p 或 --patch ,它会显示每次提交所引入的差异(按补丁的格式输出)
# 你也可以限制显示的日志条目数量,例如使用 -2 选项来只显示最近的两次提交
git log -p -2

git log --stat #查看到每次提交的简略统计信息
git log --oneline #简洁显示,缩短了hash值 
git reflog #显示要移到目的版本的步数

# --pretty 这个选项可以使用不同于默认格式的方式展示提交历史
# 这个选项有一些内建的子选项供你使用,比如 oneline 会将每个提交放在一行显示,在浏览大量的提交时非常有用
# 另外还有 short,full 和 fuller 选项,它们展示信息的格式基本一致,但是详尽程度不一
git log --pretty=oneline

# format 参数可以定制记录的显示格式
# 这样的输出对后期提取分析格外有用,因为这样输出的格式不会随着 Git 的更新而发生改变
git log --pretty=format:"%h - %an, %ar : %s"

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

# 当 oneline 或 format 与另一个选项 --graph 结合使用时尤其有用
# 这个选项添加了一些 ASCII 字符串来形象地展示你的分支、合并历史
git log --pretty=format:"%h %s" --graph

# git log 其它一些选项:
--shortstat 只显示 --stat 中最后的行数修改添加移除统计
--name-only 仅在提交信息后显示已修改的文件清单
--name-status 显示新增、修改、删除的文件清单
--abbrev-commit 仅显示 SHA-1 校验和所有 40 个字符中的前几个字符
--relative-date 使用较短的相对时间而不是完整格式显示日期(比如“2 weeks ago”)
--oneline --pretty=oneline --abbrev-commit 合用的简写

# 限制输出长度

# git log 有许多非常实用的限制输出长度的选项,也就是只输出一部分的提交
# 如 -2 选项了,它只会显示最近的两条提交, 实际上 2 可以修改为任何整数,表示仅显示最近的 n 条提交

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

# --since 和 --until 这种按照时间作限制的选项很有用,例如,下面的命令会列出最近两周的所有提交:
git log --since=2.weeks

# -S 是一个非常有用的过滤器是(俗称“pickaxe”选项)它接受一个字符串参数
# 并且只会显示那些添加或删除了该字符串的提交
# 假设你想找出添加或删除了对某一个特定函数的引用的提交,可以调用:
git log -S function_name

# 综合例子:在 Git 源码库中查看 Junio Hamano 在 2008 年 10 月其间
# 除了合并提交之外的哪一个提交修改了测试文件,可以使用下面的命令
git log --pretty="%h - %s" --author='Junio C Hamano' --since="2020-04-01" --before="2020-05-01" --no-merges -- t/

撤销操作

在任何一个阶段,你都有可能想要撤消某些操作,这里注意,有些撤消操作是不可逆的(操作失误可能会导致数据丢失)操作时需要非常细心

1、修正最后提交

有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了,此时,可以运行带有 --amend 选项的提交命令来重新提交,重新提交将代替最后一次提交的结果,从效果上来说,就像是上一次的提交从未存在过一样,它并不会出现在仓库的历史中。修补提交最明显的价值是可以稍微改进你最后的提交,而不会让“啊,忘了添加一个文件”或者 “小修补,修正笔误”这种提交信息弄乱你的仓库历史

$ git commit -m 'first commit' #第一次提交
$ git add file01 #提交完后才发现file01忘记提交了,这时我们添加 file01到暂存区
$ git commit -m 'this first commit' --amend #使用--amend选项,使本次的提交覆盖掉上一次的提交

2、取消暂存文件

接下来的两个小节演示如何操作暂存区和工作目录中已修改的文件。 这些命令在修改文件状态的同时,也会提示如何撤消操作。 例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交, 但是却意外地输入 git add * 暂存了它们两个,如何只取消暂存两个中的一个呢? git status 命令提示了你:

# 如下
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   ccc.txt #有两个文件加入了暂存区
        modified:   ddd.txt

# 将 ddd.txt 文件撤出暂存区
# 注意因为git版本的不同,撤销命令有可能也会不一样,我们要以git提示的撤销命令为准
$ git restore --staged ddd.txt

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   ccc.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:   ddd.txt #ddd.txt文件已经恢复到已修改但未暂存的状态

3、撤销工作区修改

这是一个危险的命令,你对那个文件在本地的任何修改都会消失,Git 会用最近提交的版本覆盖掉它,除非你确实清楚不想要对那个文件的本地修改了,否则请不要使用这个命令

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   ccc.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:   ddd.txt #已修改为暂存的状态

# 撤销工作区的修改
$ git restore ddd.txt

# 再次查看工作区中已修改未暂存状态中已经没有ddd.txt
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   ccc.txt

# 查看文件内容也恢复到修改前状态
$ cat ddd.txt
dfsdfsdfsdfsdfsdfbbbbbccc

# 如果你想保留对那个文件做出的修改,但是现在仍然需要撤消
# 那么可以使用 Git 分支来保存进度,使用分支通常是比较好的做法

记住,在 Git 中任何 已提交 的东西几乎总是可以恢复的,甚至那些被删除的分支中的提交或使用 --amend 选项覆盖的提交也可以恢复,然而,任何你未提交的东西丢失后很可能再也找不到了

回退版本

git reset --hard 45bce24 #根据索引值回到指定历史版本

# 一个^表示回退一步,n个表示回退n步(使用^只能后退版本)
git reset --hard HEAD^ #往前回退一个版本
git reset --hard HEAD^^ #往前回退二个版本
git reset --hard HEAD^^ #往前回退三个版本

# 使用~符号表示后退n步(使用~也是只能后退版本)
git reset --hard HEAD~n

# git reset(reset的三个参数)
    --soft:仅仅在本地库移动HEAD指针
    --mixed:在本地库移动HEAD指针、并重置暂存区
    --hard:在本地库移动HEAD指针、重置暂存区、且重置工作区(日常使用多)

标签管理

像其他版本控制系统(VCS)一样,Git 可以给仓库历史中的某一个提交打上标签,以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点( v1.0 、 v2.0 等等)。 在本节中,你将会学习如何列出已有的标签、如何创建和删除新的标签、以及不同类型的标签分别是什么

查看标签

# 查看标签
$ git tag
v1.0
v1.0-lw

# 通过使用 git show 命令可以看到具体的标签信息和与之对应的提交信息
$ git show v1.0
tag v1.0
Tagger: stachefix <stache@foxmail.com>
Date:   Fri Jun 17 13:48:22 2022 +0800

first tag version 1.0

commit b8bd6251bea270ce33fa133bf06de7aca77a6402 (HEAD -> master, tag: v1.0, origin/master)
Merge: 30e0874 2c8b05d
Author: stachefix <stache@foxmail.com>
Date:   Wed Jun 1 10:26:38 2022 +0800

    sfa aaaab bbb

diff --cc test.txt
index d75e2ce,2d75e03..1e9d2f5
--- a/test.txt
+++ b/test.txt
@@@ -1,3 -1,4 +1,6 @@@
  this is test
 +nbts change test aaaaaaaaa
 +naibao change test bbbbbbb
+ nbts change test bbbb
+ nbts change test aaaa
+ naibao change test

创建便签

Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)轻量标签很像一个不会改变的分支它只是某个特定提交的引用

而附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证

通常会建议创建附注标签,这样你可以拥有以上所有信息。但是如果你只是想用一个临时的标签, 或者因为某些原因不想要保存这些信息,那么也可以用轻量标签。

1、添加附注标签

# -a 选项添加附注标签
# -m 选项指定了一条将会存储在标签中的信息,如果没有为附注标签指定一条信息,Git 会启动编辑器要求你输入信息
$ git tag -a v1.0 -m "first tag version 1.0"

$ git tag
v1.0

2、添加轻量标签

轻量标签本质上是将提交校验和存储到一个文件中,没有保存任何其他信息。 创建轻量标签,不需要使用 -a、-s 或 -m 选项,只需要提供标签名字

$ git tag v1.0-lw

$ git tag
v1.0
v1.0-lw

3、后期打标签

# 查看提交历史记录
$ git log --oneline
b8bd625 (HEAD -> master, tag: v1.0-lw, tag: v1.0, origin/master) sfa aaaab bbb
30e0874 this is test
2c8b05d j j c t
348fa80 c t t s
0f2b7e8 c t y s
0ec8cbf naiba change to test.txt
45a03af nbts change test.txt
c800c2a create test.txt file

# 将历史提交 45a03af 打上 v0.1 的标签
$ git tag -a v0.1 -m "version v0.1" 45a03af

$ git tag
v0.1
v1.0
v1.0-lw

$ git show v0.1
tag v0.1
Tagger: stachefix <stache@foxmail.com>
Date:   Fri Jun 17 14:00:13 2022 +0800

version v0.1

commit 45a03afc4286aed049886c6513a27bc938484900 (tag: v0.1)
Author: stachefix <stache@foxmail.com>
Date:   Tue May 31 16:08:03 2022 +0800

    nbts change test.txt

diff --git a/test.txt b/test.txt
index d014168..b4597c0 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
 this is test
+nbts change test

推送标签

默认情况下,git push 命令并不会传送标签到远程仓库上,在创建完标签后你必须显式地推送标签到共享服务器上,你可以运行 git push origin

如果想要一次性推送很多标签,也可以使用 --tags 选项,这将会把所有不在远程仓库服务器上的标签全部传送到过去

# 将 v0.1 标签传送到远程仓库
$ git push origin v0.1
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 158 bytes | 158.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:stachefix/naiba.git
 * [new tag]         v0.1 -> v0.1

# 将多个标签传送到远程仓库
$ git push origin --tags
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 162 bytes | 162.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:stachefix/naiba.git
 * [new tag]         v1.0 -> v1.0
 * [new tag]         v1.0-lw -> v1.0-lw

# 当其他人从仓库中克隆或拉取,他们也能得到你的那些标签了

删除标签

$ git tag
v0.1
v1.0
v1.0-lw

# 本地仓库删除标签
$ git tag -d v1.0-lw
Deleted tag 'v1.0-lw' (was b8bd625)

$ git tag
v0.1
v1.0

# 将删除标签同步到远程仓库
# 注意上述 -d 选项命令并不会从任何远程仓库中移除这个标签
# 你必须用 git push origin --delete tagname 来更新你的远程仓库
$ git push origin --delete v1.0-lw
To github.com:stachefix/naiba.git
 - [deleted]         v1.0-lw

# 另一种变体 git push <remote> :refs/tags/<tagname>
# 这种操作的含义是,将冒号前面的空值推送到远程标签名,从而高效地删除它
$ git push origin :refs/tags/v0.1
To github.com:stachefix/naiba.git
 - [deleted]         v0.1

检出标签

如果你想查看某个标签所指向的文件版本,可以使用 git checkout 命令, 虽然这会使你的仓库处于“分离头指针(detached HEAD)”的状态,这个状态有些不好的副作用:

$ git checkout v0.1
Note: switching to 'v0.1'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at c800c2a create test.txt file

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

$ git checkout -b naiba-fz v0.1
Switched to a new branch 'naiba-fz'

如果在这之后又进行了一次提交,naiba-fz 分支就会因为这个改动向前移动,此时它就会和 v0.1 标签稍微有些不同,这时就要当心了

分支管理

分支管理的特性:

  • 同时并行推进多个功能开发,提高开发销量
  • 各分支开发过程中,如果某个分支开发失败,不会对其它分支有任何影响,失败分支删除重新开始即可

增删改查

# 查看所有分支
git branch
或
git branch -v 

# 查看哪些分支已经合并到当前分支 
git branch --merged

# 查看所有包含未合并工作的分支
git branch --no-merged

# 查看尚未合并到 master 分支的有哪些
git branch --no-merged master

git branch hot_fix #创建一个hot_fix分支

git checkout hot_fix #切换到hot_fix分支

# 创建新分支的同时切换过去
git checkout -b <new_branch_name>

# 删除分支
# 如果分支还未合并,尝试使用 git branch -d 命令删除它时会失败
# 如果真的想要删除分支并丢掉那些工作,如同帮助信息里所指出的,可以使用 -D 选项强制删除它
git branch -d hot_fix

合并分支

# 1、切换到接受修改的分支上:
git checkout master

# 2、执行merge命令将hot_fix合并到master分支上
git merge hot_fix

解决分支冲突

冲突表现:当前分支和要被合并的分支修改了同一个文件的内容时,git 难以选择是需要保留当前分支的内容还是保留被合并分支的内容,因此就产生了冲突,需要我们手动处理冲突

# 例如:在master和hot_fix中都对ccc.txt文件进行了修改
# 在进行合并操作时会将冲突部分使用特殊符号标记起来
$ git merge hot_fix
stache@DESKTOP-339K653 MINGW64 /d/wdd/code/project01 (master)

# 将 hot_fix 合并到 master 中
$ git merge hot_fix
Auto-merging ccc.txt
CONFLICT (content): Merge conflict in ccc.txt #提示ccc.txt文件存在冲突
Automatic merge failed; fix conflicts and then commit the result.

# 合并冲突,提示符也变成了 (master|MERGING)
stache@DESKTOP-339K653 MINGW64 /d/wdd/code/project01 (master|MERGING)

# 查看冲突文件,冲突内容会使用特殊标记符号 <<<<<<< HEAD 和 >>>>>>> hot_fix 标记起来
$ cat ccc.txt
fen zhi gua li
<<<<<<< HEAD
sdfsd
sdfsdf
sdfsdf
=======
sdfsdf
sfsdfsfghffgjfghfghrfghfghf
>>>>>>> hot_fix

解决冲突步骤:
1、编辑冲突文件,删除特殊标记符号,并把文件内容需改到满意状态并保存
vi ccc.txt

# 2、添加冲突文件到暂存区
git add ccc.txt

# 3、提交,注意此时 commit 时不能带文件名,否则会报错,至此冲突就解决合并成功
git commit -m "merge hot_fix"

查看远程分支

通过 git ls-remote 来显式地获得远程引用的完整列表, 或者通过 git remote show 获得远程分支的更多信息

$ git ls-remote origin
b8bd6251bea270ce33fa133bf06de7aca77a6402        HEAD
b8bd6251bea270ce33fa133bf06de7aca77a6402        refs/heads/master
c800c2a6136a53da46da5fc543b3fc31102f9dfc        refs/heads/test_branch
539083be8f33005153ef59e71620edbe6e33c17e        refs/tags/v1.0
b8bd6251bea270ce33fa133bf06de7aca77a6402        refs/tags/v1.0^{}

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:stachefix/naiba.git
  Push  URL: git@github.com:stachefix/naiba.git
  HEAD branch: master
  Remote branches:
    master      tracked
    test_branch tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local refs configured for 'git push':
    master      pushes to master      (up to date)
    test_branch pushes to test_branch (up to date)

推送分支

当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上。 本地的分支并不会自动与远程仓库同步,你必须显式地推送想要分享的分支。 这样,你就可以把不愿意分享的内容放到私人分支上,而将需要和别人协作的内容推送到公开分支

# 将本地 test_branch 分支推送到远程仓库的 test_tranch
$ git push origin test_branch
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: Create a pull request for 'test_branch' on GitHub by visiting:
remote:      https://github.com/stachefix/naiba/pull/new/test_branch
remote:
To github.com:stachefix/naiba.git
 * [new branch]      test_branch -> test_branch

# 将本地 test_branch 分支推送到远程仓库的 demo_tranch
$  git push origin test_branch:demo_branch
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: Create a pull request for 'demo_branch' on GitHub by visiting:
remote:      https://github.com/stachefix/naiba/pull/new/demo_branch
remote:
To github.com:stachefix/naiba.git
 * [new branch]      test_branch -> demo_branch

这里有些工作被简化了,Git 自动将 test_branch 分支名字展开为 refs/heads/stest_branch:refs/heads/test_branch, 那意味着“推送本地的 test_branch 分支来更新远程仓库上的 test_branch 分支”你也可以运行 git push origin test_branch:test_branch 它会做同样的事——也就是说“推送本地的 test_branch 分支,将其作为远程仓库的 test_branch 分支”可以通过这种格式来推送本地分支到一个命名不相同的远程分支,如果并不想让远程仓库上的分支叫做 test_branch,可以运行 git push origin test_branch:demo_branch 来将本地的 test_branch 分支推送到远程仓库上的 demo_branch 分支。

当下一次其他协作者从服务器上抓取数据时,他们会在本地生成一个远程分支 origin/test_branch 指向服务器的 test_branch 分支的引用:

$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From GitHub - schacon/simplegit: example repo used for testing and such
 * [new branch]      test_branch    -> origin/test_branch

要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝) 换一句话说,这种情况下,不会有一个新的 test_branch 分支——只有一个不可以修改的 origin/test_branch 指针

可以运行 git merge origin/test_branch 将这些工作合并到当前所在的分支。如果想要在自己的 test_branch 分支上工作,可以通过远程分支创建本地分支,这会给你一个用于工作的本地分支,并且起点位于 origin/test_branch

$ git checkout -b test_branch origin/test_branch
Branch serverfix set up to track remote branch test_branch from origin.
Switched to a new branch 'test_branch'

跟踪远程分支

从一个远程跟踪分支检出一个本地分支会自动创建所谓的“跟踪分支”(它跟踪的分支叫做“上游分支”)跟踪分支是与远程分支有直接关系的本地分支。 如果在一个跟踪分支上输入 git pull,Git 能自动地识别去哪个服务器上抓取、合并到哪个分支

当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支。 然而,如果你愿意的话可以设置其他的跟踪分支,或是一个在其它远程仓库上的跟踪分支,又或者不跟踪 master 分支。最简单的实例就是像之前看到的那样,运行 git checkout -b / 这是一个十分常用的操作所以 Git 提供了 --track 快捷方式:

$ git checkout --track origin/test_branch
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'test_branch'

由于这个操作太常用了,该捷径本身还有一个捷径。如果你尝试检出的分支不存在,且刚好只有一个名字与之匹配的远程分支,那么 Git 就会为你创建一个跟踪分支:

$ git checkout test_branch
Branch test_branch set up to track remote branch test_branch from origin.
Switched to a new branch 'test_branch'

如果想要将本地分支与远程分支设置为不同的名字,你可以轻松地使用上一个命令增加一个不同名字的本地分支:

$ git checkout -b demo_branch origin/test_branch
Branch sf set up to track remote branch test_branch from origin.
Switched to a new branch 'demo_branch'

现在,本地分支 demo_branch 会自动从 origin/test_branch 拉取

设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用 -u 或 --set-upstream-to 选项运行 git branch 来显式地设置

$ git branch -u origin/test_branch
Branch test_branch set up to track remote branch test_branch from origin.

如果想要查看设置的所有跟踪分支,可以使用 git branch 的 -vv 选项。 这会将所有的本地分支列出来并且包含更多的信息,如每一个分支正在跟踪哪个远程分支与本地分支是否是领先、落后或是都有。

$ git branch -vv
  master    1ae2a45 [origin/master] deploying index fix
* test_branch cb67mw9 [origin/test_branch: ahead 2] forgot the brackets
* nihao_branch k867bd9 [dev/nihao_branch: ahead 3, behind 1] this should do it
testing   2na46sl trying something new

这里可以看到 test_branch 分支正在跟踪 origin/test_branch 并且 “ahead” 是 2,意味着本地有两个提交还没有推送到服务器上。也能看到 master 分支正在跟踪 origin/master 分支并且是最新的。 接下来可以看到 nihao_branch 分支正在跟踪 dev 服务器上的 nihao_branch 分支并且领先 3 落后 1,意味着服务器上有一次提交还没有合并入同时本地有三次提交还没有推送。最后看到 testing 分支并没有跟踪任何远程分支

需要重点注意的一点是这些数字的值来自于你从每个服务器上最后一次抓取的数据。这个命令并没有连接服务器,它只会告诉你关于本地缓存的服务器数据。 如果想要统计最新的领先与落后数字,需要在运行此命令前抓取所有的远程仓库。 可以像这样做:

$ git fetch --all
$ git branch -vv

删除远程分支

假设你已经通过远程分支做完所有的工作了,也就是说你和你的协作者已经完成了一个特性,并且将其合并到了远程仓库的 master 分支(或任何其他稳定代码分支)可以运行带有 --delete 选项的 git push 命令来删除一个远程分支,如果想要从服务器上删除 serverfix 分支,运行下面的命令:

$ git push origin --delete test_branch
To https://github.com/schacon/simplegit
 - [deleted]         test_branch

基本上这个命令做的只是从服务器上移除这个指针,Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的

操作远程仓库

为了能在任意 Git 项目上协作,你需要知道如何管理自己的远程仓库。 远程仓库是指托管在因特网或其它网络中的你的项目的版本库。 你可以有好几个远程仓库,通常有些仓库对你只读,有些则可以读写。 与他人协作涉及管理远程仓库以及根据需要推送或拉取数据。 管理远程仓库包括了解如何添加远程仓库、移除无效的远程仓库、管理不同的远程分支并定义它们是否被跟踪等等

查看远程仓库

$ git clone git@github.com:stachefix/naiba.git nbtest
Cloning into 'nbtest'...
remote: Enumerating objects: 25, done.
remote: Counting objects: 100% (25/25), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 25 (delta 5), reused 21 (delta 1), pack-reused 0
Receiving objects: 100% (25/25), done.
Resolving deltas: 100% (5/5), done.

$ cd nbtest/

# 如果想查看你已经配置的远程仓库服务器,可以运行 git remote 命令
# 它会列出你指定的每一个远程服务器的简写,如果你已经克隆了自己的仓库,那么至少应该能看到 origin 
# 这是 Git 给你克隆的仓库服务器的默认名字
$ git remote
origin

# 指定选项 -v,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
$ git remote -v
origin  git@github.com:stachefix/naiba.git (fetch)
origin  git@github.com:stachefix/naiba.git (push)

# 查看某个远程库的详细信息,会列出远程仓库的 URL 与跟踪分支的信息
$ git remote show origin
* remote origin
  Fetch URL: git@github.com:stachefix/naiba.git
  Push  URL: git@github.com:stachefix/naiba.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

添加远程仓库

运行 git remote add 添加一个新的远程 Git 仓库,同时指定一个方便使用的简写

$ git remote add ticgit https://github.com/schacon/ticgit

$ git remote -v
origin  git@github.com:stachefix/naiba.git (fetch)
origin  git@github.com:stachefix/naiba.git (push)
ticgit  https://github.com/schacon/ticgit (fetch)
ticgit  https://github.com/schacon/ticgit (push)

编辑远程仓库

# 修改一个远程仓库的简写名
$ git remote rename ticgit tic

$ git remote
origin
tic

# 值得注意的是这同样也会修改你所有远程跟踪的分支名字。 那些过去引用 ticgit/master 的现在会引用 tic/master

# 修改远程仓库的 url 地址
$ git remote set-url tic https://www.baidu.com

$ git remote -v
origin  git@github.com:stachefix/naiba.git (fetch)
origin  git@github.com:stachefix/naiba.git (push)
tic     https://www.baidu.com (fetch)
tic     https://www.baidu.com (push)

删除远程仓库

$ git remote remove tic

$ git remote
origin

推送至github

# 将本地库推送到github远程库(push)操作如下:
1、git init #初始化本地库
2、echo 'this is test' > vi test.txt #创建新文件
3、git add test.txt #添加文件到暂存区
4、git commit -m "create test.txt file" test.txt #提交文件
5、git remote add origin git@github.com:stachefix/naiba.git #设置远程仓库
6、将本地库推送至github是需要验证账号信息的,这里我们通过设置ssh密钥来设置鉴权信息
  设置ssh密钥:官方文档https://docs.github.com/cn/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account
  注意:在2021年8月13日之前还可以使用账号密码的方式验证,但从这之后密码验证方式被移除,只允许通过这种密钥的方式
  设置步骤:
    1、首先查看一下本机是否有密钥存在:ls -la ~/.ssh/ 如果存在则可以直接用,不存在则需要生成
    2、生成新的ssh密钥:ssh-keygen -t ed25519 -C "your_email@example.com"
      注意:如果你使用的是不支持 Ed25519 算法的旧系统,则使用:ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
      这将使用提供的电子邮件作为标签创建一个新的 SSH 密钥
    3、查看生成的密钥并复制:cat ~/.ssh/id_ed25519.pub
    4、将密钥填写到github账户中,在github网站中操作路径:Settings -> SSH and GPG keys -> New SSH key 将复制的密钥添加并保存
7、git push -u origin master #将本地仓库master分支推送到 origin 远程库
  注意:因为ssh密钥只支持ssh地址,而不支持https地址

拉取远程仓库

# 将远程文件拉取到本地(pull)有两种方式:
# 1、git pull [远程库地址别名] [远程库分支名]
# 此种方式比较简单直接,会将远程代码拉取后直接与本地代码合并
git pull origin master


# 2、分步执行先fetch,然后在merge
1、git fetch [远程库地址别名] [远程库分支名]
  git fetch origin master #这个命令会访问远程仓库,从中拉取所有你还没有的数据

2、git merge [远程库地址别名/远程分支名]
  # 必须注意 git fetch 命令只会将数据下载到你的本地仓库
  # 它并不会自动合并或修改你当前的工作区,当准备好时你必须手动将其合并入你的工作区
  git merge origin/master

# 此种方式分两步执行,虽然麻烦,但是比较安全
# 首先通过 fetch 将远程代码拉取下来,但是不和本地仓库进行合并
# 我们可以通过 git checkout [远程库地址别名/远程分支名] 切换到刚刚拉取下来的代码分支
# 通过查看代码内容确保没有问题后,在通过 git merge 将拉取的代码和本地代码进行合并

# 注:pull = fetch + merge

处理推送冲突

冲突解决要点:

  1. 如果不是基于 github 远程库的最新版做所的修改,则会产生冲突,不能直接推送,必须先拉取
  2. 拉取下来后如果进入冲突状态,则按照“分支冲突解决”操作解决即可
# 与远程库操作时冲突处理示例

$ git push origin master
To Welcome to nginx!:stachefix/naiba.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'Welcome to nginx!:stachefix/naiba.git' #提示冲突
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

# 首先要拉取远程最新代码
$ git pull origin master
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 287 bytes | 28.00 KiB/s, done.
From Welcome to nginx!:stachefix/naiba
 * branch            master     -> FETCH_HEAD
   0ec8cbf..0f2b7e8  master     -> origin/master
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

stache@DESKTOP-339K653 MINGW64 /d/wdd/code/nbts (master|MERGING) #拉取最新代码后,自动进入冲突模式
$ vi test.txt #修改对应的冲突文件

stache@DESKTOP-339K653 MINGW64 /d/wdd/code/nbts (master|MERGING)
$ git add test.txt #将修改后的冲突文件加入暂存区

stache@DESKTOP-339K653 MINGW64 /d/wdd/code/nbts (master|MERGING)
$ git commit -m "j j c t" #提交,注意解决冲突时的提交不能带文件名

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

# 再次推出即可成功推送
$ git push origin master
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 16 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 610 bytes | 610.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
To Welcome to nginx!:stachefix/naiba.git
   0f2b7e8..2c8b05d  master -> master

日常使用技巧

删除文件找回

删除文件能找回的前提是,删除前,文件存在时的状态已经提交到了本地库

# 操作命令:git reset --hard [指针位置]
1、如果删除操作已经提交到本地库,则操作指针位置指向历史记录即可恢复文件
  git reset --hard 45bce24

2、删除操作尚未提交到本地库,则指针位置使用 HEAD
  git reset --hard HEAD

比较文件差异

# 将工作区中的文件和暂存区进行比较
git diff [文件名]
    
# 将工作区中的文件和本地库历史记录比较
git diff [本地库中的历史版本] [文件名]
git diff HEAD^ test01.txt

# 不带文件名则比较多个文件
git diff
2 个赞

内容很不错,可以再深入一点,了解一下git的游离头指针状态和一些常见场景的文件恢复