Git基础

版本控制器

版本控制是我们开发软件时的必备工作,将文件的版本进行系统的管理,不仅仅可以帮助我们灵活操作文件,也能简化我们开发的流程。

不使用版本管理器的情况下,如果想要对文件的版本进行管理,需要将不同版本的文件分别存储起来,并分别命名,这样我们的文件夹就会类似这样:

随着新版本的迭代,我们的文件则越来越多,也越来越难管理。在团队开发时,如果多人同时对一个文件进行操作,则需要各自保存不同的版本,然后集中审核内容。可想而知,在改动幅度大,人数多的情况下,使用传统方式来开发简直就是噩梦。

而版本控制器就是一个解决这些痛点的神奇软件,它不仅仅可以帮助我们记录每次文件的改动,还可以让同事协同编辑,不仅仅提升了我们个人的开发效率,更简化了团队开发的难度。

版本控制器示意图:

市面上曾经出现过很多版本控制器,比如SCCS, RCS, CVS, SVN, BitKeeperSCM,但是都不大好用。在2005年,大牛Linux Torvalds(Linux内核的最早作者)开发出了分布式版本控制器Git,因为它的灵活性和优越性,Git的用户成指数级上升,如今已经发展成了最受欢迎的版本控制器,奠定了版本控制领域的垄断地位。

补充说明:所有的版本控制器系统,只能跟踪文本文件的改动,比如txt文件、网页和所有的程序源代码等等。版本控制器可以追踪每次的改动,比如在第7行添加了新的单词”Git”,在第8行删除了”Max”之类的具体修改信息。但是图片、视频、微软Word这类二进制文件,虽然版本控制器也可以追踪管理,比如从50KB改成了40KB,但是具体改了什么,是没法知道的。

Git安装

Mac

使用MacOS的朋友有两种方法安装Git,第一种是先安装homebrew,然后通过homebrew安装Git,具体方法可以参观官网:https://brew.sh。第二种方法比较方便,直接下载官方安装包https://git-scm.com/download/mac

Windows

Windows的朋友可以直接下载安装包https://git-scm.com/download/win,根据提示逐步完成。安装完成后,会创建一个git的文件夹,里面有个程序叫git bash,打开git bash就能在一个Unix环境中使用Git了。

Linux

使用Linux的小伙伴们,只要打开Terminal,输入以下的指令即可:

# Ubuntu user
$ sudo apt-get install git-all
# Fedora user
$ sudo yum install git-all

创建版本库

版本库(repository)是一个Git会一直追踪的目录,这个目录中的所有文件的改动,包括各个文件的增加、删除和改动,都会被Git识别出来,以便Git可以追踪目录历史,并在将来某个时刻随时还原。

首先我们先把我们的个人信息告诉git,包括名字和邮箱,这样git就能知道我们的身份,更好地管理每个版本库的贡献者。

$ git config --global user.name "yourname"
$ git config --global user.name "youremail@gmail.com"

我们使用git config加上—global参数,来设置全局配置,这样所有的版本库都会认识我们的身份,当然也可以为特定版本库指定不同的用户名和邮箱,这里我们直接使用这种省事的方法。

创建一个版本库很简单,第一步先创建一个空目录,然后进入新创建的目录,使用git init命令:

$ mkdir new_repo
$ cd new_repo
$ git init

这样一个Git版本库就创建好了,此时当前目录中还多了一个 .git 目录,这个目录中的信息可以帮助Git来跟踪管理版本库,所以千万不能删除。(.git目录是隐藏目录,需要使用 ls -ah 命令才能看到)

要注意的是,我们不一定要使用空目录作为版本库,设定已有内容的目录为版本库也是可以的。

现在我们在new_repo目录中编写一个new_file.txt文件,内容如下:

Git is a distributed version-control system.
Git is awesome.

然后我们使用git add将新文件添加到仓库:

$ git add new_file.txt

然后我们再使用 git commit 将文件提交到仓库,并输入说明这次改动的内容:

$ git commit -m "Created a new file"

-m 之后的内容虽然可以是任意内容,但是最好是有意义的文字说明,这样才能从历史记录中方便地查找改动记录。

git commit 运行成功后,命令行中会提示1 file changed: 2 insertions,这样我们就知道有哪些具体的变动了。

成功创建了一个新文件后,我们可以继续工作,对新文件 new_file.txt 进行修改,将内容改成如下:

Git is popular.
Git is free.

然后我们运行 git status 查看结果:

$ git status
On branch master
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)

	modified:   new_file.txt

no changes added to commit (use "git add" and/or "git commit -a")

git status可以帮助我们了解版本库当前的状态,上面的结果告诉我们 new_file.txt 被修改过了,但是新的修改没有被提交。我们还可以使用 git diff 来查看具体被修改的内容:

$ git diff
diff --git a/new_file.txt b/new_file.txt
index 1647185..1c140a4 100644
--- a/new_file.txt
+++ b/new_file.txt
@@ -1,2 +1,2 @@
-Git is a distributed version-control system.
-Git is awesome.
+Git is popular.
+Git is free.

确定我们要修改的内容后,我们使用 git add new_file.txt 将修改过的文件添加至版本库。这个时候再使用 git status 查看,你会得到以下的信息:

$ git add new_file.txt 
$ git status
On branch master
Changes to be committed:
  (use "git restage HEAD <file>..." to unstage)

	modified:   new_file.txt

这告诉我们将要被提交的修改包括 new_file.txt,是时候提交了,我们还是直接使用 git commit 加上提交说明即可:

$ git commit -m "Modified the first two lines of new file"

然后我们再使用 git status 就会看到当前目录是干净的,没有需要提交的修改:

On branch master
nothing to commit, working tree clean

注意:任何没有使用git add将其加入staged状态的文件,都没法使用commit提交其修改。

版本的增删改查、版本回退

增删查改

但我们每次修改文件的时候,我们可以使用 git add 加上文件的名字,将文件加入版本库,然后再使用 git commit 提交修改。假设我们在版本库中创建了三个新的文件file1.txt,file2.txt,file3.txt,那我们我们可以使用以下的命令更新版本库:

$ git add file1.txt file2.txt file3.txt
$ git commit -m "Added three new files"

除了使用第一行的方式添加多个文件,我们可以使用git add .(英文句号)将当前所有有变化的文件加入版本库:

$ git add .

Git记录 (log)

在git中,每一次提交修改(commit),这条记录都会被保存起来。好比建大楼的时候,每个commit记录了盖楼需要添加和拿走的材料,这个施工过程都被记录下来了。而在git中,使用git log就能查看代码改动信息。

基于我们之前对版本库的改动,我们使用git log就能看的以下的信息,其中包含了commit ID、作者(修改者)、日期和修改备注:

commit 2443b8c95bd2f426447b76b77ad5385a3d79a4ea (HEAD -> master)
Author: Enoch <turingplanet@gmail.com>
Date:   Thu Oct 17 08:06:30 2019 -0700

    Added three new files

commit f76e81f3c8e7c54dd917e9616da64ff3fde4fab9
Author: Enoch <turingplanet@gmail.com>
Date:   Mon Oct 14 09:44:19 2019 -0700

    Modified the first two lines of new file

commit 2bbcd42eb3441375ae8f3c0b15d3f776302ad4fe
Author: Enoch <turingplanet@gmail.com>
Date:   Mon Oct 14 09:32:28 2019 -0700

    Added a new file

如果这个时候我们在file1.txt的内部添加以下内容:

new content

然后我们再使用git status就能看到当前版本库中被修改的内容:

On branch master
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)

	modified:   file1.txt

no changes added to commit (use "git add" and/or "git commit -a")

那我们就先将此改动添加(add)并提交(commit)到版本库:

$ git add file1.txt
$ git commit -m "Modified file1"
[master 0e84b3e] Modified file1
 1 file changed, 1 insertion(+)

这个时候我们再查看log,则会发现我们多了一条commit:

commit 0e84b3e405630f1fc6ca9beb82cf5ca9ed38ea5a (HEAD -> master)
Author: Enoch <walkerhaozheng@gmail.com>
Date:   Thu Oct 17 08:21:19 2019 -0700

    Modified file1

commit 2443b8c95bd2f426447b76b77ad5385a3d79a4ea
Author: Enoch <walkerhaozheng@gmail.com>
Date:   Thu Oct 17 08:06:30 2019 -0700

    Added three new files
...

Working directory, Staging area, Repository

为了帮助大家更好的理解工作区(working directory)、暂存区(staging directory)、和版本库(repository),我们可以对各个区域进行修改,然后通过git diff来查看他们之间的区别。

比如我们先给file2.txt添加以下的内容:

new content in file2

入行我们使用git diff来对比当前的工作区和上一次commit的版本库的区别:

$ git diff
diff --git a/file2.txt b/file2.txt
index e69de29..de59346 100644
--- a/file2.txt
+++ b/file2.txt
@@ -0,0 +1 @@
+new content in file2

如果这个时候我们使用add file2的修改,将当前的改动添加到暂存区,那么我们使用git diff后,会发现没有任何信息,这是因为我们的改动被转移到暂且区了。所以我们可以使用diff加上–cached来对比当前的暂存区和上一个commit的区别:

$ git diff --cached
diff --git a/file2.txt b/file2.txt
index e69de29..de59346 100644
--- a/file2.txt
+++ b/file2.txt
@@ -0,0 +1 @@
+new content in file2

我们可以再改动一下file3.txt,在其中添加以下内容:

new content in file3

我们可以使用git diff HEAD来对比当前暂且区和工作区的区别:

diff --git a/file2.txt b/file2.txt
index e69de29..de59346 100644
--- a/file2.txt
+++ b/file2.txt
@@ -0,0 +1 @@
+new content in file2
diff --git a/file3.txt b/file3.txt
index e69de29..944ef83 100644
--- a/file3.txt
+++ b/file3.txt
@@ -0,0 +1 @@
+new content in file3

这个时候我们将三个区间的内容进行对比,大家就能理解三个区间的关系了:

$ git diff
diff --git a/file3.txt b/file3.txt
index e69de29..944ef83 100644
--- a/file3.txt
+++ b/file3.txt
@@ -0,0 +1 @@
+new content in file3

$ git diff --cached
diff --git a/file2.txt b/file2.txt
index e69de29..de59346 100644
--- a/file2.txt
+++ b/file2.txt
@@ -0,0 +1 @@
+new content in file2

$ git diff HEAD
diff --git a/file2.txt b/file2.txt
index e69de29..de59346 100644
--- a/file2.txt
+++ b/file2.txt
@@ -0,0 +1 @@
+new content in file2
diff --git a/file3.txt b/file3.txt
index e69de29..944ef83 100644
--- a/file3.txt
+++ b/file3.txt
@@ -0,0 +1 @@
+new content in file3

但我们确定要修改的内容后,我们可以直接add并commit,将所有的修改加到暂存区后并提交到版本库:

$ git add .
$ git commit -m "Modified file2 and file3"
[master 0aef827] Modified file2 and file3
 2 files changed, 2 insertions(+)

时光穿越机 (checkout & reset)

有时候我们对一些文件做了改动,并加到暂存区,但是又后悔了,这些操作在 git 中也能实现。比如,我们继续改动file1.txt,在其中添加一行的内容:

new content
more content

然后我们把file1.txt加入暂存区,如果我们想要反悔,可以直接使用 git reset 把 file1.txt 从暂存区拉回到工作区,然后对文件做相应的修改后再add:

$ git add file1.txt
$ git status -s 
M  file1.txt # working directory
$ git reset file1.txt
Unstaged changes after reset:
M	file1.txt
$ git status -s 
 M file1.txt # staging area

然后我们把file1.txt中第一行new content删除,只剩下more content之后,直接add然后commit:

$ git add file1.txt
$ git commit -m "Added more content in file1"

接下来我们来了解git中的checkout,checkout可以帮助我们让当前的工作区回到特定的某个commit,比如我们使用git log查看过去的一些commit:

$ git log --oneline
5c10f81 (HEAD -> master) Added more content in file1
0aef827 Modified file2 and file3
0e84b3e Modified file1
2443b8c Added three new files
f76e81f Modified the first two lines of new file
2bbcd42 Added a new file

然后我们使用checkout回到 0aef827 这个commit,可以发现 file1.txt 的内容也变回了,因为在那个时候 file1 的内容并没有被改动,所以我们用了git log之后并没有 Added more content in file1 这个commit:

$ git checkout 0aef827
Note: checking out '0aef827'.
....
$ cat file1.txt
new content
$ git log --oneline
0aef827 (HEAD) Modified file2 and file3
0e84b3e Modified file1
2443b8c Added three new files
f76e81f Modified the first two lines of new file
2bbcd42 Added a new file

若想要在从过去回到现在,我们可以使用 git checkout master:

$ git checkout master
$ cat file1.txt
more content

我们可以通过下面的图片对checkout进行理解:

这里的HEAD其实是一个指针,可以让我们知道当前处于哪一个commit。如果我们想要让HEAD彻底回到某一个commit,我们可以使用在reset后面加上–hard参数:

$ git reset --hard HEAD # 当前add的工作全部消失,回到上一次commit 
HEAD is now at 5c10f81 Added more content in file1
$ git log --oneline
5c10f81 (HEAD -> master) Added more content in file1
0aef827 Modified file2 and file3
0e84b3e Modified file1
...
$ git reset --hard HEAD^ # 回到当前master的上一个commit:c0aef827
$ git reset --hard 0e84b3e # 回到具体的某一个commit
$ git log --oneline 
0e84b3e (HEAD -> master) Modified file1
2443b8c Added three new files
f76e81f Modified the first two lines of new file
2bbcd42 Added a new file

我们的 Modified file2 and file3 彻底消失了!如果我们想要重新挽回改动,可以使用 git reflog 查看最近做的所有的 HEAD 的改动,并使用 reset 重回到想要退回的 commit:

$ git reflog
0e84b3e (HEAD -> master) HEAD@{0}: reset: moving to 0e84b3e
0aef827 HEAD@{1}: reset: moving to HEAD^
5c10f81 HEAD@{2}: reset: moving to HEAD
5c10f81 HEAD@{3}: checkout: moving from 0aef8274ff89375120cbd38be7e4a494aa19f0e7 to master
0aef827 HEAD@{4}: checkout: moving from master to 0aef827
5c10f81 HEAD@{5}: commit: Added more content in file1
...
$ git reset --hard 5c10f81

通过查看 file1.txt 的内容,确定一下我们时空穿越成功:

$ git cat file1.txt
more content

分支、合并、标签管理

分支是什么?我们可以简单地把每个分支理解为一个平行宇宙,每个平行宇宙之间的变化不会影响到其他的宇宙,只有我们强行合并的时候,平行宇宙之间的改动才会互相影响。

那为什么我们需要分支呢?在开发软件的时候,我们经常需要同时开发不同的版本,比如版本1是稳定版本,而版本2需要开发新的功能A,版本3需要开发新的功能B,如果同时在一个分支中开发三个版本,那会乱套的。所以我们需要创建两个单独的分支给版本2和版本3进行开发,当各个版本的功能完善后,我们就可以将新的改变合并到稳定版本中,这样我们的开发流程即分工明确,也不会出错。

创建分支

在我们初始化一个版本库的时候,Git会默认创建一个master分支,提交也都默认提交到master中。我们有个HEAD指针,这个指针指明我们当前在哪个分支的commit中工作,如果我们切换到别的分支,我们的HEAD也会转移指向。

如果要创建分支,我们直接使用git branch <branch_name>即可,我们先创建一个分支dev,然后查看所有分支:

$ git branch dev 
$ git branch
  dev
* master

如果我们想要将 HEAD 切换到 dev 分支,我们可以使用 checkout:

$ git checkout dev 
Switched to branch 'dev'
$ git branch
* dev
  master

使用 git checkout -b <branch_name>,能直接创建并切换到新的分支:

$ git checkout -b dev
Switched to a new branch 'dev'

dev 分支中的文件和 master 的文件是一样的,当 HEAD 指向 dev 的时候,我们对文件夹中的修改是不会影响到 master 分支的。我们把 file1.txt 的内容改为 more content in dev branch,然后提交:

$ git commit -am "Modified file1 in dev branch" # -am参数: 添加所有改变,然后提交

合并分支

如果要把 dev 分支中的更新推送到 master 中,我们需要切换到 master,然后再将 dev 的更新合并过来:

$ git checkout master
Switched to branch 'master'

$ git merge dev
Updating 5c10f81..323b3eb
Fast-forward
 file1.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git log --oneline --graph
* 323b3eb (HEAD -> master, dev) Modified file1 in dev branch
* 5c10f81 Added more content in file1
* 0aef827 Modified file2 and file3
...

默认的 merge 是使用 Fast forward 方式,并不会保存分支图案,如果想要 log 保存分支的信息,我们在merge的时候需要添加 -no-ff 参数更新comment:

$ git reset --hard HEAD^ 
$ git merge --no-ff -m "Add merge info" dev
Merge made by the 'recursive' strategy.
 file1.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph
*   16572d5 (HEAD -> master) Add merge info
|\  
| * 323b3eb (dev) Modified file1 in dev branch 
|/  
* 5c10f81 Added more content in file1
...

分支冲突

在多人同时开发不同分支的时候,分支冲突是很常见的现象。比如程序员A在master中对file1进行修改并提交,而程序员B在dev分支中对file1也进行了修改并提交,那么在我们把dev合并到master的时候,我们就会发现冲突。

如果程序员A在file1中添加新的内容 new content in master,然后程序员B在file2中添加新的内容new content in dev,那么当我们查看各个分支log的时候,则会有以下的信息

# master 的 log
f844b75 (HEAD -> master) new content in master
dda9338 Add merge info
323b3eb Modified file1 in dev branch
...
# dev 的 log
aa1a832 (HEAD -> dev) new content in dev
323b3eb Modified file1 in dev branch
5c10f81 Added more content in file1
...

然后我们尝试把dev合并到master,就会输出冲突的信息:

$ git branch 
  dev
* master
$ git merge dev
Auto-merging file1.txt
CONFLICT (content): Merge conflict in file1.txt
Automatic merge failed; fix conflicts and then commit the result.

当git发现file1在master和dev上的版本不同时,就会提示冲突,我们只要打开file1,就会看到git给文件的标记:

# file1.txt
more content in dev branch
<<<<<<< HEAD
new content in master
=======
new content in dev
>>>>>>> dev        

我们手动合并两者的不同,然后提交就可以了

# file1.txt
more content in dev branch
merged content from master and dev
$ git commit -am "solve conflict""
[master f0687a0] solve confict

这时候我们再看看master的log:

$ git log --oneline --graph
*   f0687a0 (HEAD -> master) solve confict
|\  
| * aa1a832 (dev) new content in dev
* | f844b75 new content in master
* |   dda9338 Add merge info
|\ \  
| |/  
| * 323b3eb Modified file1 in dev branch
|/  
* 5c10f81 Added more content in file1
...

标签管理

标签是我们对commit设置的别名,可以帮助我们对commit进行更好的管理,如果你的老板叫你发布一个版本,然后告诉你commit id是f844b3…,这样的交流是容易出错的,因为id号又长又臭,很难管理。如果我们使用标签,比如每个版本都有专属的版本号,比如v2.0之类的,这样我们发布和管理commit就更方便了。

如果想要创建标签,只要切换到想要打标签的分支上,使用git tag <name>就好了:

$ git branch
  dev
* master
$ git tag v1.0 # 新的标签
$ git log --oneline
f0687a0 (HEAD -> master, tag: v1.0) solve confict
aa1a832 (dev) new content in dev
f844b75 new content in master
...

tag默认情况下是给当前分支的最新commit打标签,如果想要给旧的commit打上标签,使用git tag <tag_name> <commit_id>就好了:

$ git tag v0.9 f844b75
$ git log --oneline
f0687a0 (HEAD -> master, tag: v1.0) solve confict
aa1a832 (dev) new content in dev
f844b75 (tag: v0.9) new content in master
...

如果这个时候我们想要回到过去的版本,只需要使用标签名就可以了:

$ git checkout v0.9
$ git log --oneline
f844b75 (HEAD, tag: v0.9) new content in master
dda9338 Add merge info

注意:标签是和commit挂钩的,如果这个commit同时出现在多个分支上,那么这些分支都能查看此标签。

总结

$ git branch # 查看当前版本库所有分支
$ git branch <name> # 创建新的分支
$ git checkout <name> or git switch <name> # 切换分支
$ git checkout -b <name> or git switch -c <name> # 创建并切换至新的分支
$ git merge <name> # 将name分支合并至当前分支
$ git log --oneline --graph # 查看具体log信息
$ git branch -d <name> # 删除分支
$ git tag <tag_name> # 为当前的commit创建新的标签
$ git tag -d <tag_name> # 删除标签

远程仓库GitHub

Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。通常情况下,我们在一台服务器上部署一个主版本库,然后其他的机器就从此服务器上下载版本库,将版本库更新后,再将本地的版本库推送到主服务器,同时更新服务上的版本库,达到远程操作版本库的效果。

可是自己搭建服务器很麻烦,而且也不安全,多亏了GitHub这个免费的版本库托管网站,我们只要注册一个账户,就能创建属于自己的远程仓库。

添加远程库GitHub

github上注册一个账户,然后即可创建一个新的网上版本库:

远程数据库克隆

如果想要将别人的网上版本库下载到自己的电脑中,我们只需要找到版本库的地址,然后使用git clone命令克隆就可以了,非常方便。

这边作为演示,提供用Python编写的图像处理器供大家下载。首先登入gitHub版本库的地址https://github.com/turingplanet/digital-image-processor,点击绿色的框复制repo的地址。

然后在本地电脑中使用git clone复制版本库:

$ git clone https://github.com/turingplanet/digital-image-processor.git

最后进入版本库,直接运行程序即可:

$ cd digital-image-processor
$ python3 gui.py

然后就可以使用一个简易的图像处理器啦~

GitHub上有无数的开源程序供大家下载,都是使用相同的方法,现在大家可以去尽情探索了~

总结

$ git remote add <remote_name> <remote_address> # 添加远程仓库
$ git push -u <remote_name> master # 推送本地仓库至远程仓库
$ git clone <remote_address> # 克隆远程仓库

特别鸣谢

参考网址

https://haozhengio.com/git%E5%92%8Cgithub%E6%95%99%E7%A8%8B/

如何提交代码到Github项目

  1. 从源仓库OS-ABC/Kcoin-Golang 中把代码Fork 到自己的Github 账号里。

  2. 在自己Fork 的仓库中获取该仓库的Clone 地址。

  3. 使用git 命令将仓库Clone 到本地。

    命令:git clone <第二步中复制的仓库地址>

  4. 进入到Clone 下来的仓库目录中

  5. 把源仓库(OS-ABC/Kcoin-Golang)加入到远程仓库中,方便以后拉取更新

    ​ (1) 复制源仓库的地址

    ​ (2) 在git 中把源仓库地址加入到远程仓库中

    ​ (3) 查看一下当前远程仓库的信息

  6. 切换到dev 分支

    如果使用的是windows 下的git bash,可以看到最后蓝色部分指的是当前分支(master)。

    也可以使用git 命令查看当前的分支情况。

    命令:git branch -a

    红色的是远程仓库的分支信息,第一行前面有“*”的是当前分支。
    下面我们要在本地新建一个dev 分支,并让这个dev 分支和origin 仓库的dev 分支拥有
    相同历史。
    命令:git checkout -b dev origin/dev

    执行完这条命令后,可以看到当前分支已经变为dev 了:

  7. 拉取 upstream 上dev 分支的更新

    命令:git pull upstream dev

  8. 修改三连

    (1)git add .
    (2)git commit -m ‘this is a commit’
    (3)git push origin dev

  9. 在github 页面上提PR

    首先进入自己fork 下来的仓库(origin),点击new pull request。
    然后要特别注意下面的四个选择框

    按照中间的箭头方向,一定要从自己fork 的仓库(origin)的dev 分支到源仓库
    (upstream)的dev 分支,如果提到了upstream的master 分支上去,就会出现pr 中有
    一长串的commit 的情况。

    最后在提pr 的页面看一看文件改动对比,看看是不是都是自己想改的,然后填写好pr
    描述创建pr 就可以了。

公众号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值