6150 字
31 分钟Add commentMore actions
Git使用指南

Git是什么#

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

那什么是版本控制系统?#

想象一下你正在写一篇重要的论文或一份复杂的代码:

你想保存一个“稳定版本”,然后去尝试一些新的、可能失败的想法。
你不小心删了一大段内容,想恢复到几小时前的状态。
你和同学/同事一起协作,需要合并你们各自修改的部分,又怕互相覆盖。

如果你用Microsoft Word写过长篇大论,那你一定有这样的经历:

想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的Word文档变成了这样:

lots of word

TIP

doc文件是二进制文件,并不能够使用git这类工具协同,这里借用了 廖雪峰 - Git教程 生动形象的说明 补充说明:docx格式的文件本质上是一个XML文件,将 .docx 格式的文件后缀改为ZIP后解压, 可以看到解压出来有word文件夹,它包含了Word文档的大部分内容。而其中的document.xml文件则包含了文档的主要文本内容,这是可供协同的部分

过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。

看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删。

更要命的是,有些部分需要你的同事帮助填写,于是你把文件Copy到U盘里给她(也可能通过Email发送一份给她),然后,你继续修改Word文件。一天后,同事再把Word文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你做了哪些改动,得把你的改动和她的部分合并,真困难。

于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?

这个软件用起来就应该像这个样子,能记录每次文件的改动:

版本文件名用户说明日期
1service.doc张三删除了软件服务条款7/12 10:38
2service.doc张三增加了License人数限制7/12 18:09
3service.doc李四财务部门调整了合同金额7/13 9:51
4service.doc张三延长了免费升级周期7/14 15:17

记录每一次文件变更:你可以随时回到历史上的任何一个版本。
支持多人协作:轻松合并多个人的工作,并解决冲突。
创建“平行宇宙”(分支):在不影响主线的情况下进行新功能开发或Bug修复。

这样,你就结束了手动管理多个“版本”的史前时代,进入到版本控制的20世纪。

安装Git#

在Linux上安装Git#

首先,你可以试着输入git,看看系统有没有安装Git:

Terminal window
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

像上面的命令,有很多Linux会友好地告诉你Git没有安装,还会告诉你如何安装Git。

Linux 软件包管理工具#

Debian及其衍生版(如Ubuntu):#

APT (Advanced Package Tool)

APT是Debian及其衍生版的主要软件包管理工具。

常用的命令有:

  • sudo apt update:更新软件包列表。
  • sudo apt upgrade:升级所有已安装的软件包。
  • sudo apt install <package>:安装软件包。
  • sudo apt remove <package>:移除软件包。

Fedora、RHEL、CentOS(现在CentOS已经转为使用RPM):#

DNF (The Next Generation of YUM)

DNF是Fedora和RHEL 8及更高版本中的新一代软件包管理工具,替代了旧的YUM工具。

常用的命令有:

  • sudo dnf update:更新所有软件包。
  • sudo dnf install <package>:安装软件包。
  • sudo dnf remove <package>:移除软件包。

对于CentOS 7及之前版本,通常使用YUM:

  • sudo yum update:更新所有软件包。
  • sudo yum install <package>:安装软件包。
  • sudo yum remove <package>:移除软件包。

Arch Linux:#

pacman

pacman是Arch Linux的包管理器。

常用的命令有:

  • sudo pacman -Syu:更新并升级所有软件包。
  • sudo pacman -S <package>:安装软件包。
  • sudo pacman -R <package>:移除软件包。

Debian系仅需通过一条sudo apt install git就可以直接完成Git的安装,非常简单。

如果是其他Linux版本,请参考发行版说明,例如,RedHat Linux可以通过命令sudo yum install git安装。没有包管理器的发行版可以自行下载源码编译安装,仅适合老鸟。

在Windows上安装Git#

在Windows上使用Git,有两种安装方法。

直接安装#

第一种是直接从Git官网直接下载安装程序,然后按默认选项安装即可。安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功。

包管理器#

第二种是先安装一个包管理器(winget、scoop、choco),推荐Scoop,然后在PowerShell中通过scoop install git安装Git。

配置Git#

安装好Git后,还需要最后一步: 设置你的身份信息。这个信息会附加到你的每一次提交上,告诉别人这是谁干的。在命令行输入:

Terminal window
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
TIP

—global 表示这是全局配置,这台电脑上所有的 Git 仓库都会使用这个配置。

因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。

注意git config命令的--global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。

身份标识 和 权限验证#

你可以把这两件事想象成寄一封信:

git config 的用户名和邮箱:这就像你在信封上写的 “寄件人”信息。

它的作用是标识。当这封信(你的代码提交 commit)被收到后,别人一看就知道这封信是谁写的。
理论上,你可以在“寄件人”那里写任何名字和地址,比如写成“超人”。这封信本身依然会被寄出去。

为什么需要它:在团队协作中,当大家看到 git log 时,能够清晰地知道每一次修改是谁、在什么时候完成的。它是一个纯粹的元数据(Metadata)。

GitHub 账号和凭证:这就像你用来打开邮筒投递信件的 “钥匙”。

它的作用是验证权限。你必须拥有正确的钥匙,才能打开属于你自己的那个邮筒(你的 GitHub 仓库),把信投进去。 如果你没有钥匙,或者钥匙不对,就算信封上写的是你的名字,邮局(GitHub 服务器)也不会让你投递。

能决定你是否可以修改远程仓库的,是你推送时使用的凭证(PAT 或 SSH 密钥),而不是你 git config 里设置的用户名和邮箱。

常见问题#

Q1: 我的 git config 需要和我的 GitHub 账号一样吗?
答案:不强制要求,但强烈推荐 user.email 保持一致。

user.name:可以是你 GitHub 的用户名,也可以是你的真实姓名。建议保持一致,方便辨认。
user.email:强烈建议设置为你 GitHub 账号绑定的邮箱地址。
为什么?
因为 GitHub 会使用你提交记录中的 email 地址来匹配 GitHub 用户。

如果匹配成功:在 GitHub 网站上,这次提交旁边会显示你的头像,并且这次提交会被计入你的贡献图(小绿格)。
如果匹配不成功:提交依然会成功(只要你有推送权限),但在 GitHub 网站上,这次提交的作者旁边会是一个灰色的默认头像,并且不会链接到你的个人主页,也不会计入贡献图。

隐私提示:如果你不想暴露你的真实邮箱,可以在 GitHub 的 邮箱设置 页面勾选 "Keep my email addresses private",然后 GitHub 会提供一个 ID+username@users.noreply.github.com 格式的私密邮箱。你可以将这个私密邮箱设置到你的 git config 中。

Q2: 如果不一样,如何控制我的仓库不被随意修改?
答案:通过管理你仓库的访问权限和保护你的个人凭证。

保护你的凭证:你的 Personal Access Token (PAT) 或 SSH 私钥就是你仓库的钥匙。绝对不要泄露它们! 只要别人没有你的钥匙,他们就无法向你的仓库 push 代码,无论他们的 git config 设置成什么。
管理协作者 (Collaborators):在你的 GitHub 仓库设置中,有一个 “Collaborators” 选项。只有被你添加为协作者的人,才有权限向你的仓库推送代码。陌生人是无法推送的。
使用组织和团队 (For larger projects):在组织中,你可以更精细地设置每个团队或每个成员对不同仓库的读、写、管理权限。
分支保护规则 (Branch Protection Rules):你可以设置规则,例如,不允许任何人直接向 main 分支 push 代码,所有修改必须通过 Pull Request 的方式进行,并且需要至少一个其他成员审查通过后才能合并。这是团队协作的最佳实践。

创建版本库#

什么是版本库呢?版本库又名仓库(Repository),你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

  1. 首先,选择一个合适的地方,创建一个空目录:
Terminal window
$ mkdir demo
$ cd demo
$ pwd
TIP

pwd用于显示我当前的目录

CAUTION

如果你使用Windows系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文。

  1. 第二步,通过git init命令把这个目录变成Git可以管理的仓库:
Terminal window
$ git init
Initialized empty Git repository in /home/p1ume/demo/.git/

通过以上操作Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。

如果你没有看到.git目录,那是因为这个目录默认是隐藏的,用ls -ah命令就可以看见

也不一定必须在空目录下创建Git仓库,选择一个已经有东西的目录也是可以的。不过,如果使用自己正在开发的公司项目来学习Git…


如果想要从远程服务器克隆一个现有仓库又该怎么做呢?

  1. 首先,选择一个合适的地方,创建一个空目录:
Terminal window
$ mkdir demo
$ cd demo
$ pwd
  1. 把整个项目(包括所有历史记录)下载 (clone) 到你的本地。
Terminal window
$ git clone https://github.com/git/git.git
or
$ git clone --depth 1 https://github.com/git/git.git
TIP

遇到大项目clone很慢时,可以使用 --depth 1 来加速,指定了 depth 1 的时候,就是只保留了最新的入口,历史入口就没下载了,即无法随意的使用git reset回滚到历史commits
我一般在 clone 类原生仓库时使用该参数,这极大地降低了我的下载时间,且需要用到其他commits和branch的场景并不多

这样非常轻松地完成了项目的复刻。

把文件添加到版本库#

首先这里再明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。

不幸的是,Microsoft的doc格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的,前面我们举的例子只是为了演示,如果要真正使用版本控制系统,就要以纯文本方式编写文件。

因为文本是有编码的,比如中文有常用的GBK编码,日文有Shift_JIS编码,如果没有历史遗留问题,强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。

CAUTION

千万不要使用Windows自带的记事本编辑任何文本文件。原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符,你会遇到很多不可思议的问题,比如,网页第一行可能会显示一个“?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。

具体操作#

初始化一个Git仓库,使用git init命令。

添加文件到Git仓库,分两步:

使用命令git add <file>,注意,可反复多次使用,如git add 1.txt 2.txt,添加多个文件; 使用命令git commit -m <message>,完成。

疑难解答

Q:输入git add readme.txt,得到错误:fatal: not a git repository (or any of the parent directories)。

A:Git命令必须在Git仓库目录内执行(git init除外),在仓库目录外执行是没有意义的。

Q:输入git add readme.txt,得到错误fatal: pathspec ‘readme.txt’ did not match any files。

A:添加某个文件时,该文件必须在当前目录下存在,用ls或者dir命令查看当前目录的文件,看看文件是否存在,或者是否写错了文件名。

协作#

远程仓库基本概念:

  • origin: Git 默认给远程仓库起的名字。当你 git clone 一个项目时,Git 会自动将克隆的地址设为 origin。
  • push: 将本地的提交“推送”到远程仓库。
  • pull: 从远程仓库“拉取”最新的变更并与本地分支合并。
  • fetch: 仅从远程仓库“获取”最新的变更,但不自动合并。

常用协作流程#

场景:你已经有了一个本地仓库,想把它推送到 GitHub 上。

在 GitHub 上创建一个新的空仓库(不要勾选 “Initialize this repository with a README”)。

复制仓库地址,例如 https://github.com/your-username/your-repo.git。

在你的本地仓库中,关联这个远程仓库:

Terminal window
git remote add origin https://github.com/your-username/your-repo.git

将本地的 main 分支推送到 origin:

Terminal window
# -u 参数会把本地的 main 分支和远程的 main 分支关联起来,以后推送可以直接用 git push
git push -u origin main

日常协作流程:#

开始工作前,先拉取最新代码,确保你的本地版本是最新的:

Terminal window
git pull origin main

创建自己的分支进行开发:

Terminal window
git switch -c my-feature

在 my-feature 分支上进行修改、添加、提交。 推送你的功能分支到远程仓库:

Terminal window
git push origin my-feature

在 GitHub 或 GitLab 网站上,发起一个 Pull Request (或 Merge Request),请求团队成员审查你的代码并将其合并到主分支。

实用技巧#

.gitignore 文件#

项目里总有一些文件你不想让 Git 管理,比如编译产物、日志文件、密码配置等。在项目根目录下创建一个名为 .gitignore 的文件,把这些文件或文件夹的名字写进去即可。

示例 .gitignore:

# 忽略所有 .log 文件
*.log
# 忽略 node_modules 目录
/node_modules/
# 忽略密码文件
credentials.json

撤销操作#

情况一:修改了工作区的文件,但还没 add,想撤销。

Terminal window
# 撤销对单个文件的修改
git restore <file_name>
# 或者旧命令: git checkout -- <file_name>

情况二:已经 add 到了暂存区,想撤销 add,放回工作区。

Terminal window
# 将文件从暂存区撤出
git restore --staged <file_name>
# 或者旧命令: git reset HEAD <file_name>

情况三:已经 commit 了,想修改最后一次提交。 (比如提交信息写错了,或者漏掉了一个文件)

Terminal window
# 1. 如果有漏掉的文件,先 git add <漏掉的文件>
# 2. 然后执行
git commit --amend
# 这会打开一个编辑器让你修改提交信息,保存退出即可。

情况四:彻底撤销最近的提交(危险操作!)

Terminal window
# 回退到上一个版本,并丢弃之后的所有修改(工作区和暂存区都会被重置)
git reset --hard HEAD^ # HEAD^ 表示上一个版本
# 回退到指定的 commit ID
git reset --hard <commit_hash>
WARNING

--hard 参数会彻底删除你的工作,请谨慎使用,尤其不要对已经推送到远程的提交使用。

临时保存现场#

当你正在一个分支上开发,突然需要去另一个分支修复一个紧急 Bug,但当前的工作还没完成,不想提交。这时 git stash 就派上用场了。

Terminal window
# 1. 将当前工作区和暂存区的修改保存起来
git stash
# 2. 现在你的工作区是干净的,可以切换到其他分支干活了
git switch hotfix-branch
# ...修复bug,提交...
git switch main
git merge hotfix-branch
# 3. 活干完了,切回原来的分支
git switch my-feature
# 4. 恢复之前保存的工作现场
git stash pop

TODO#

Git 常用命令#

  • git init
  • git clone
  • git remote add origin ***.git
  • git push -u origin master
  • 推送到远程仓库的dev分支:git push origin dev
  • git log
  • git log --graph --pretty=oneline --abbrev-commit
  • git status
  • git diff
  • git add *
  • git commit -m "message"
  • commit之后又改了一个小bug,但是又不想增加一个commit,可以用:git commit --amend --no-edit,直接将改动添加到上一次的commit中
  • git push
  • git pull
  • touch .gitignore

Git 标签管理#

  • 首先切换到需要打标签的分支上,然后使用git tag v1.0就可以在当前commit打上v1.0的标签
  • git tag v1.0 commitID 对特定commit打标签
  • 打标签时加上message:git tag -a <tagname> -m "message"
  • git tag 查看所有标签
  • git show [tagname] 查看标签详细信息
  • git push origin <tagname>可以推送一个本地标签到远程仓库
  • git push origin --tags可以推送全部未推送过的本地标签
  • git tag -d <tagname>可以删除一个本地标签
  • git push origin :refs/tags/<tagname>可以删除一个远程标签(先从本地删除)

Git 撤销与回滚#

  • 暂存区git add之后commit之前存在的区域;工作区git commit之后存在的区域;远程仓库git push之后;
  • 作了修改,但还没git add,撤销到上一次提交:git checkout -f -- filenamegit checkout -f -- .
  • 作了修改,并且已经git add,但还没git commit
    • 先将暂存区的修改撤销:git reset HEAD filename/git reset HEAD;此时修改只存在于工作区,变为了 “unstaged changes”;
    • 再利用上面的checkout命令从工作区撤销修改
  • git add之后,作了修改,想丢弃这次修改:git checkout -f --filename会回到最近一次git add
  • 作了修改,并且已经git commit了,想撤销这次的修改:
    • git revert commitID. 其实,git revert可以用来撤销任意一次的修改,不一定要是最近一次
    • git reset --hard commitID/git reset --hard HEAD^(HEAD表示当前版本,几个^表示倒数第几个版本,倒数第100个版本可以用HEAD~100);参数--hard:强制将暂存区和工作区都同步到指定的版本
    • git resetgit revert的区别是:reset是用来回滚的,将HEAD的指针指向了想要回滚的版本,作为最新的版本,而后面的版本也都没有了;而revert只是用来撤销某一次更改,对之后的更改并没有影响
    • 然后再用git push -f提交到远程仓库

Git 分支管理#

  • 创建分支: git branch test
  • 切换分支: git checkout test
  • 创建并切换分支:git checkout -b test
  • 将test分支的更改合并到master分支:先在test分支上commit、push,再:git checkout master; git merge test
  • 如果合并时产生冲突:先手动解决冲突,再合并
  • 删除分支:git branch -d test
  • git stash
    • 如果当前分支还有任务没有做完,也不想提交,但此时需要切换或者创建其它分支,就可以使用stash将当前分支的所有修改(包括暂存区)先储藏起来;然后就可以切换到其它分支
    • 在其它分支工作完成之后,首先切换回原来的分支,然后使用git stash list命令查看
    • 可以使用git stash apply <stash number>恢复之前储藏的工作现场,再使用git stash drop <stash number>删除掉储藏的内容
    • 也可以直接用git stash pop恢复并删除内容
  • 如果在其它分支上做了一个修改(比如修复了一个bug,这次修改有一个commitID),想要将这次修改应用到当前分支上,可以使用:git cherry-pick commitID,可以复制一个特定的提交到当前分支

RESTful API#

REST指Representational State Transfer,可以翻译为“表现层状态转化”

主要思想#

  • 对网络上的所有资源,都有一个统一资源标识符 URI(Uniform Resource Identifier);
  • 这些资源可以有多种表现形式,即REST中的“表现层”Representation,比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现。URI只代表资源的实体,不代表它的形式;
  • “无状态(Stateless)”思想:服务端不应该保存客户端状态,只需要处理当前的请求,不需了解请求的历史,客户端每一次请求中包含处理该请求所需的一切信息;
  • 客户端使用HTTP协议中的 GET/POST/PUT/DELETE 方法对服务器的资源进行操作,即REST中的”状态转化“

设计原则#

  • URL设计
    • 最好只使用名词,而使用 GET/POST/PUT/DELETE 方法的不同表示不同的操作;比如使用POST /user代替/user/create
    • GET:获取资源;POST:新建/更新资源;PUT:更新资源;DELETE:删除资源;
    • 对于只支持GET/POST的客户端,使用X-HTTP-Method-Override属性,覆盖POST方法;
    • 避免多级URL,比如使用GET /authors/12?categories=2代替GET /authors/12/categories/2
    • 避免在URI中带上版本号。不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI,版本号可以在HTTP请求头信息的Accept字段中进行区分
  • 状态码:服务器应该返回尽可能精确的状态码,客户端只需查看状态码,就可以判断出发生了什么情况。
  • 服务器回应:在响应中放上其它API的链接,方便用户寻找

HTTP请求有哪些常见状态码?#

2xx状态码:操作成功。200 OK
3xx状态码:重定向。301 永久重定向;302临时重定向
4xx状态码:客户端错误。400错误请求;401未经授权;403禁止;404未找到;
5xx状态码:服务端错误。500服务器内部错误;501服务不可用

参考#

Git使用指南
https://p1ume.vercel.app/posts/git/
作者
p1ume
发布于
2024-04-01
许可协议
CC BY-NC-SA 4.0