前言
Git是一个分布式版本控制系统,它由Linux系统的创始者Linus Torvalds开发。与传统的集中式版本控制系统不同,Git使用分布式的方法,每个开发者都拥有完整的代码仓库副本,可以在本地进行提交、查看历史记录、切换分支等操作,而不需要连接到中央服务器。这种分布式的特性使得Git具有很强的灵活性和可靠性,即使在网络断开的情况下也能够继续工作。
Git的市场份额目前已经达到了89.23%
,所以学习Git是当代编程人员必不可少的一项技能,本文将从零基础开始带大家使用git管理我们的项目代码。
Technology | Domains | Market Share (Est.) |
---|---|---|
Git | 84,698 | 89.23% |
Microsoft Azure DevOps Server | 8,395 | 8.84% |
TortoiseSVN | 1,406 | 1.48% |
Serena PVCS Version Manager | 136 | 0.14% |
安装
debian系(debian、ubuntu、raspberrypi)
sudo apt update
sudo apt install git
macOS
brew install git
如果
brew
命令不存在,请先安装homebrew
windows
-
直接去
https://git-scm.com/download/win
下载安装包安装 -
如果你安装了scoop
scoop install git
-
如果你安装了chocolate
choco install git
-
如果你安装了winget
winget install --id Git.Git -e --source winget
git中三个重要的概念:工作区、版本库和暂存区
1. 工作区
在使用git init
创建git仓库后,默认就处于工作区中,我们在项目目录中进行任何修改(如新增删除修改文件)都与不会被git保存和跟踪,所做的修改也无法使用git进行恢复,此时我们使用git status
可以看到这些工作区中新增的文件会被标识为Untracked files
(未被跟踪的文件)
jagitch@4d7a018f5ea9:git-demo$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
main.js
node_modules/
package-lock.json
package.json
2. 版本库
当我们的项目开发完一个功能时,通常就会把所有的代码提交到git的版本库中,每一次提交,都会在版本库中生成一个Commit ID
,唯一标识此次提交的代码版本。只要提交到版本库中后,只要.git
目录不损坏,不管我们如何修改项目代码,我们都可以从版本库中将代码还原到对应版本的代码,我们还可以将本地的版本库提交到远程的git仓库,这样即使本地的’.git’目录损坏,我们也可以从远程仓库中恢复,如果远程仓库损坏了,只要本地有一个.git
目录是正常的,我们就不会丢失我们的代码,这也是git分布式版本控制相对于集中式版本控制系统的一大优势。
3. 暂存区(Index)
从工作区和版本库的介绍中,我们可以知道,工作区中的修改不会被git跟踪,也不能使用git进行恢复,如果在项目开发中,我们已经完成了一个功能的大部分工作,且运行都正常,但是我们在继续修改时,项目却运行失败了,于时我们想回到它上一次正常的地方重新修改,如果事先没有把上一次正常的文件内容提交到git版本库中,我们是无法回到上一次正常时的状态的。为了能够达到回到正常状态的目的,我们就需要将那时的代码提交到版本库中,但是我们的功能还没开发完成,如果这样做的话,那开发一个功能就得在版本库中提交多个版本,这会让版本库提交很多没用的内容,造成空间浪费,也会让版本库历史记录不清晰。
此时把未完成的功能提交到版本库不太好,不提交也不好。于是,git的作者Linus Torvalds就想到了一个好主意,他发明了一个叫暂存区(英文叫做Index)的东西,就是在工作区和版本库中间再加一个中转站,我们可以随时把对文件的修改使用git add
命令添加到暂存区,它不会影响我们git的版本库,我们可以随时把暂存区中的内容还原到工作区,这样我们就能不提交到版本库也能解决回到上次正常时的代码状态了。当我们整个功能都开发完成后,我们就可以将所有工作区中需要保存到git中的代码全部添加到暂存区,然后再使用git commit
命令将暂存区中的内容提交到版本库中。注意git commit
只能将暂存区中的内容提交到版本库中。
实例:使用git管理笔者开发的生日计算器
下面我们通过一个实例来介绍git的基本使用。笔者开发了一个Node.js开发的一个女朋友生日计算器,为了能保证代码安全和记录开发的历史版本,在传统的集中式版本控制系统svn
和现在流行的分布式版本控制系统git
中,我选择了后者,因为它更灵活、更开放、更流行。如果读者也想使用这个生日计算器的话,请安装Node.js,可以参考笔者的博文《十分钟带你入门Node.js 开发》
-
创建一个目录作为项目目录,管理项目所有文件(源代码文件、文档等)
jagitch@4d7a018f5ea9:~$ mkdir git-demo && cd git-demo
-
使用npm安装
date-fns
jagitch@4d7a018f5ea9:git-demo npm install date-fns jagitch@4d7a018f5ea9:git-demo ls node_modules package-lock.json package.json
执行
npm install date-fns
命令后,会在当前目录创建package.json和package-lock.json文件以及node_modules文件夹,我们安装的包都写入了配置文件package.json
中,date-fns包安装在node_modules
文件夹中。 -
创建main.js,输入以下代码
const { differenceInCalendarDays: diffDays, parse } = require("date-fns"); const readline = require("readline") const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) function calBirthday() { rl.question("请输入你女朋友的生日:", (birthday) => { year = new Date().getFullYear() var answer = "" const days = diffDays( parse(year + "-" + birthday, "yyyy-M-d", new Date()), new Date() ) if (days > 0) { answer = `你女朋友的生日还有${ days}天,千万要记住哦!` } else if (days == 0) { answer = `今天就是你女朋友的生日,赶紧去买束花吧!` } else if (days < 0) { const days2 = diffDays( parse((year + 1) + "-" + birthday, "yyyy-M-d", new Date()), new Date() ) answer = `糟糕,今年你错过了你女朋友的生日,已经错过${ (-days)}天了,下一个生日还有${ days2}天` } else { answer = "请输入正确的生日(如05-20)" console.log(answer) calBirthday() return } console.log(answer) rl.close() }); } calBirthday()
-
运行程序
jagitch@4d7a018f5ea9:git-demo node main.js 请输入你女朋友的生日:5-32 请输入正确的生日(如05-20) 请输入你女朋友的生日:15-23 请输入正确的生日(如05-20) 请输入你女朋友的生日:hello 请输入正确的生日(如05-20) 请输入你女朋友的生日:5-28 糟糕,今年你错过了你女朋友的生日,已经错过5天了,下一个生日还有360天 jagitch@4d7a018f5ea9:git-demo node main.js 请输入你女朋友的生日:6-2 今天就是你女朋友的生日,赶紧去买束花吧! jagitch@4d7a018f5ea9:git-demo node main.js 请输入你女朋友的生日:10-20 你女朋友的生日还有140天,千万要记住哦!
-
在项目根目录下执行下面命令,初始化一个git本地仓库,执行后项目根目录会生成一个
.git
目录,这个目录中就是一个完整的代码仓库。jagitch@4d7a018f5ea9:git-demo$ git init Initialized empty Git repository in /home/coder/workspace/git-demo/.git/
-
此时我们的文件都还在工作区中,使用
git status
命令可以发现他们的状态都是Untracked files
,文件的颜色在终端中显示的是红色jagitch@4d7a018f5ea9:git-demo$ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) main.js node_modules/ package-lock.json package.json nothing added to commit but untracked files present (use "git add" to track)
-
把工作区中的内容添加到暂存区
jagitch@4d7a018f5ea9:git-demo$ git add .
-
此时我们查看git的状态,由于node_modules目录内文件太多,此处只复制前10行,发现此时添加的文件都在
Changes to be committed
中,且文件颜色为绿色,意思是可以被提交到版本库的修改。jagitch@4d7a018f5ea9:git-demo$ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: main.js new file: node_modules/.package-lock.json new file: node_modules/date-fns/CHANGELOG.md new file: node_modules/date-fns/LICENSE.md
-
使用过Node.js的人都知道,node_modules是不需要提交到版本库中的,所以我们不应该把它添加到暂存区,我们可以使用
git rm --cached <file>
命令把它从暂存区移除,如果是移除目录的话,还要添加"-r"参数。jagitch@4d7a018f5ea9:git-demo$ git rm --cached node_modules fatal: not removing 'node_modules' recursively without -r jagitch@4d7a018f5ea9:git-demo$ git rm -r --cached node_modules rm 'node_modules/.package-lock.json' rm 'node_modules/date-fns/CHANGELOG.md' rm 'node_modules/date-fns/LICENSE.md' rm 'node_modules/date-fns/README.md'
-
此时,我们再查看git的状态
jagitch@4d7a018f5ea9:git-demo$ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: main.js new file: package-lock.json new file: package.json Untracked files: (use "git add <file>..." to include in what will be committed) node_modules/
发现node_modules目录已经已到
Untracked files
中了,此时它在工作区不在暂存区,其他在暂存区中的文件则显示在Changes to be committed
中 -
由于一个项目往往很多文件,每一次开发功能都要提交很多文件,所以我们必须要使用
git add .
来添加工作区中所有修改过的文件到暂存区,这样节约时间,不用一个文件一个文件的添加。这不可避免地又会把一些不该添加的文件或目录添加到暂存区,如node_module目录。这让我们很为难,天才Linus Torvalds又想到了好主意,使用在根目录中创建一个.gitignore
文件,可以把想让git忽略的文件或目录添加到.gitignore
中,这样我们执行git add .
时就不会把他们添加到git暂存区了。我们在项目根目录创建.gitignore文件jagitch@4d7a018f5ea9:git-demo$ echo node_modules >> .gitignore
-
此时我们再使用
git add .
命令jagitch@4d7a018f5ea9:git-demo$ git add . jagitch@4d7a018f5ea9:git-demo$ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: main.js new file: package-lock.json new file: package.json
可以发现node_modules目录直接不显示了,因为git已经忽略它了。此时
.gitignore
也被添加到了暂存区,这个文件是需要保存到版本库中的。 -
此时我们运行我们的项目
jagitch@4d7a018f5ea9:git-demo$ node main.js 请输入你女朋友的生日:5-20 糟糕,今年你错过了你女朋友的生日,已经错过16天了,下一个生日还有349天
-
现在我们演示一下把代码修改到项目出错,例如把
answer = `糟糕,今年你错过了你女朋友的生日,已经错过${ (-days)}天了,下一个生日还有${ days2}天`
改成
answer = `糟糕,今年你错过了你女朋友的生日,已经错过${ (days)}天了,下一个生日还有${ days2}天`
-
现在运行项目
jagitch@4d7a018f5ea9:git-demo$ node main.js 请输入你女朋友的生日:5-20 糟糕,今年你错过了你女朋友的生日,已经错过-16天了,下一个生日还有349天
-
此时查看git的状态
jagitch@4d7a018f5ea9:git-demo$ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: main.js new file: package-lock.json new file: package.json 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: main.js
可以发现main.js已经有一份保存在了暂存区中,后面我们对main.js进行了修改,修改后的main.js列在
Changes not staged for commit
中,此时我们可以使用git add main.js
将修改后的main.js提交到暂存区,由于暂存区只有一个,所以之前暂存区中的main.js就会被修改后的main.js覆盖;我们也可以使用git restore main.js
撤销工作区中对main.js的修改。 -
可以发现运行结果错过-16天变成了负数,这就是一个错误,在编程界通常叫做Bug,当然这是我们现在故意弄出的一个Bug,在真实的项目开发中,代码量比这里庞大多了,当导致出现的Bug很久也找不到原因的时候,如果当时我们把一份正常的代码添加到了暂存区,我们就可以恢复到那一次的代码重新进行开发,从而节省我们大量的开发时间。下面我们把我们的代码恢复,从而解决这个负数的问题。
jagitch@4d7a018f5ea9:git-demo$ git restore main.js jagitch@4d7a018f5ea9:git-demo$ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: main.js new file: package-lock.json new file: package.json
-
运行项目,发现项目又恢复到原来的状态了
jagitch@4d7a018f5ea9:git-demo$ node main.js 请输入你女朋友的生日:5-20 糟糕,今年你错过了你女朋友的生日,已经错过16天了,下一个生日还有349天
-
假如经过测试后,功能没有问题,代表这个功能已经开发完成,我们就可以将代码提交到版本库了
jagitch@4d7a018f5ea9:git-demo$ git add . jagitch@4d7a018f5ea9:git-demo$ git commit -m "完成女朋友生日计算器的开发" *** Please tell me who you are. Run git config --global user.email "[email protected]" git config --global user.name "Your Name" to set your account's default identity. Omit --global to set the identity only in this repository. fatal: unable to auto-detect email address (got 'jagitch@4d7a018f5ea9.(none)')
-
当我们执行
git commit
命令时,因为我们没有配置姓名和邮箱,git拒绝了我们的操作,因为git是一个分布式的、多人协作的版本控制系统,它需要记录这次提交的作者是谁,所以我们根据提示的命令设置用户名和邮箱,如果想把姓名和邮箱只用户此仓库,则把--global
省略jagitch@4d7a018f5ea9:git-demo$ git config user.email "[email protected]" jagitch@4d7a018f5ea9:git-demo$ git config user.name jagitch
-
再次提交代码到版本库
jagitch@4d7a018f5ea9:git-demo$ git commit -m "完成女朋友生日计算器的开发" [master (root-commit) fda2eb5] 完成女朋友生日计算器的开发 4 files changed, 65 insertions(+) create mode 100644 .gitignore create mode 100644 main.js create mode 100644 package-lock.json create mode 100644 package.json
-
从此,这个git仓库的版本库中就有了第一个版本,使用如下代码查看仓库的版本库
jagitch@4d7a018f5ea9:git-demo$ git log commit fda2eb5d4946fc97cf3ec44d2ee0fa577a594349 (HEAD -> master) Author: jagitch <[email protected]> Date: Wed Jun 5 18:26:27 2024 +0800 完成女朋友生日计算器的开发
总结
本文首先简要介绍了git是什么以及它的基本概念,它是由Linux的作者Linus Torvalds开发的一个版本控制系统;接着介绍了工作区、暂存区、版本库这三个重要的概念。最后采用一个生日计算器的实例演示了实际项目中的git的使用方法,通过这个实例,相信读者已经基本掌握了git的使用方法,git的功能还有很多很多,例如把本地仓库提交到远程仓库、分支、标签、将代码恢复到指定的版本等,如果还想继续学习git的高级用法,请关注我的专栏《Git从入门到精通》
每一次点赞,都是你独特的味道。每一次关注,都是我前行的动力。感谢有你,一起成长!