7.分支管理

分支管理

创建和合并分支

版本回退中,提及每次提交,Git 都把它们串成一条时间线,这条时间线就是一个分支。默认情况下,只有一条时间线, 在 Git 里,这个分支叫主分支,即 master 分支。

HEAD 严格来说不是指向提交,而是指向 mastermaster 才是指向提交的,所以 HEAD 指向的就是当前分支。

一开始,master 分支就是一条线,Git 用 master 指向最新的提交,再用 HEAD 指向 master,就可以确定当前分支,以及当前分支的提交点:

HEAD_master.png

每次提交,master 分支都会向前移动一步,随着不断提交,master分支的线会越来越长。

当我们创建新的分支,例如 dev 时,Git,新建一个指针叫 dev,指向 master 相同的提交,再把 HEAD 指向 dev,就表示当前的分支在 dev 上:

HEAD_dev_master.png

Git 创建分支,除了增加一个 dev 指针,改 HEAD的指向,工作区的文件不会变化。

现在,对工作区的修改和提交就是针对 dev 分支,比如新提交一次后,dev 指针往前移动一步,而 master 指针不变:

HEAD_dev.png

假如,在 dev 上的工作完成了,就可把 dev 合并到 master上。最简单的方法就是直接把 master 指向 dev 的当前提交 ,就完成了合并:

HEAD_master_dev.png

所以 Git 合并分支的步骤是,改指针,工作区内容不变!

合并完分之后,甚至可以删除 dev 分支。删除 dev 分支就是把 dev 指针给删除掉,删除后,就剩下一条 master 分支:

DELETE_dev.png

下面开始代码代码示例:

首先,创建 dev 分支,然后切换到 dev 分支:

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

git checkout 命令加上 -b 参数表示创建并切换,相当于两条命令:

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

然后,用 git branch 命令查看当前分支:

$ git branch
* dev
  master

git branch 命令会列出所有的分支,当前分支前面会标记一个 * 号。

然后,可以尝试在 dev 分支上正提交,比如对 readme.txt 做个修改,加上一行:

Creating a new branch is quick.

然后提交:

$ git add readme.txt
$ git commit -m "branch test"
[dev c4fee22] branch test
 1 file changed, 1 insertion(+)

现在,dev 分支的工作完成,我们就可以切换回 master 分支:

$ git checkout master
Switched to branch 'master'

切换回 master 分支后,再查看一个 readme.txt 文件,刚才添加的内容不见了。

具体的原因是刚才提交的是在 dev 分支上,而 master 分支此刻的提交点并没有变:

CHANGE_dev.png

现在,需要把 dev 分支的工作结果合并到 master 分支上:

$ git merge dev
Updating 51e75c9..c4fee22
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

git merge 命令用于合并指定分支到当前分支。合并后,再查看 readme.txt 的内容,就可看到,和 dev 分支的最新提交是完全一样的。

注意上面有个 Fast-forward 信息,Git 告诉我们,这次合并是 “快速模式”,也就是直接把 master 指向 dev 的当前提交,所以合并速度非常快。

注意: 并不是每次合并都能 Fast-forward

合并完成后,就可以删除 dev 分支了:

$ git branch -d dev
Deleted branch dev (was c4fee22).

此时使用 git branch 名称查看分支,就剩下 master

git branch
* master
switch

我们注意到切换分支使用 git checkout <branch> ,前面讲过的撤销修改则是 git checout -- <file>,同一个命令,有两个作用。

实际上,切换分支这个动作,用 switch 更科学。

Git 提供新的 git switch 命令来切换分支。

创建并切换到新的 dev 分支,可以使用:

git switch -c dev

切换到已有的 master 分支,可以使用:

git switch master

使用 git switchgit checkout 更容易理解。

小结

Git 鼓励大量使用分支:
查看分支: git branch
创建分支: git branch <name>
切换分支: git checkout <name> 或者 git switch <name>
创建 + 切换分支: git checkout -b <name> 或者 git switch -c <name>
合并某分支到当前分支: git merge <name>
删除分支: git branch -d <name>


解决冲突

合并分支的时候会出现冲突的情况。下面举实例进行说明:

$ git switch -c featurel
Switched to a new branch 'featurel'

修改 readme.txt 最后一行,改为:

Creating a new branch is quick AND simple.

进行提交:

$ git add readme.txt
$ git commit -m "AND simple"
[featurel 0c8c6ad] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-)

切换到 master 分支:

$ git switch master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

master 分支上把 readme.txt 文件的最后一行改为:

Creating a new branch is quick & simple.

提交:

$ git add readme.txt
$ git commit -m "& simple"
[master 0808726] & simple
 1 file changed, 1 insertion(+), 1 deletion(-)

现在,master 分支和 featurel 分支各自都分别有 新的提交变成如下图所示:

conflict_branch.png

这种情况下,Git 无法执行 “快速合并”,只能试图把各自的修改合并起来,但是这种合并就可能会有冲突。

$ git merge featurel
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

Git 告诉我们,readme.txt 文件存在冲突,必须解决冲突后再提交。git status 也可以告诉我们冲突的文件:

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

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
    both modified:   readme.txt

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

我们可以直接查看 readme.txt 的内容

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes.
<<<<<<< HEAD
Creating  a new branch is quick & simple.
=======
Creating  a new branch is quick AND simple.
>>>>>>> featurel

Git用 <<<<<<<,=======,>>>>>>> 标记出不同分支的内容,我们修改如下后保存:

Creating a new branch is quick and simple.

再提交:

$ git add readme.txt
$ git commit -m "conflict fixed"
[master c9c0a09] conflict fixed

现在,master 分支和 featurel 分支变成了下图所示:
conflict_fixed_merge.jpg

用带参数的 git log 也可以看到分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit
*   31cbd3d (HEAD -> master) conflict fixed
|\  
| * 0c8c6ad (featurel) AND simple
* | 7bef0e9 & simple
|/  
* 88b8e8f (origin/master) learn how to create and merge branch
* c4fee22 branch test
* 51e75c9 add dir Image
* 6d9c499 add catalog and know how to clone from Github
* 0dbf81a add knowledge about git
* 6c33acb remove test.txt
* f5bda21 add test.txt
* d0c085d git tracks changes
* 8d4349c understand how stage works
* a58b4d1 append GPL
* f99120a add distributed
* ddc374f wrote a readme file

最后删除 featurel 分支:

$ git branch -d featurel
Deleted branch featurel (was 0c8c6ad).

小结

当 Git 无法自动合并分支时,需要先解决冲突。解决冲突后,再提交,合并。

解决冲突就是把 Git 合并失败的文件手动编辑成我们希望的内容,再提交。

git log --graph 命令可以看到分支合并图。


分支管理策略

通常,合并分支时,如果可能, Git 会用 Fast forward 模式,但这种模式下,删除分之后,会丢掉分支信息。

如果要强制禁用 Fast forward 模式,Git 就会在 merge 时,生成一个新的 commit,这样,从分支历史上就可以看出分支信息。

开始实战,--no-ff 方式下的 git merge:
首先,创建并切换 dev 分支:

$ git switch -c dev
Switched to a new branch 'dev'

修改 readme.txt 文件,并提交一个新的 commit:

$ git add readme.txt
$ git commit -m "add merge"
[dev 355581b] add merge
 1 file changed, 1 insertion(+)

现在切换会 master 分支:

$ git switch master
Switched to branch 'master'

准备合并 dev 分支,请注意 --no-ff 参数,表示禁用 Fast forward:

$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

因为本次合并要创建一个新的 commit,所以加上 -m 参数,把 commit 描述写进去。

合并后,用 git log 查看分支历史

$ git log --graph --pretty=oneline --abbrev-commit
*   53dd626 (HEAD -> master) merge with no-ff
|\  
| * 355581b (dev) add merge
|/  
......

不使用 Fast forward 模式,merge 后就像下图所示:

no-ff_merge.png

小结

合并分支时,加上 --no-ff 参数就可以用普通模式合并,合并后的历史有分支,能看出曾经做过合并,而 Fast forward 合并就看不出来做过合并。


Bug 分支

软件开发中, bug 是经常出现的。有了 bug 就需要修复。所以每个 bug 都可以通过一个新的临时分支来修复,合并分支,然后将临时分支删除。

举例:当接到一个代号为 007 的 bug 任务时,这些最开始的想法是创建一个分支 issue-007 来修改它。但是手中还有正在 dev 分支上进行的工作还没有提交:

$ git status
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   hello.py

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:   readme.txt

Git 提供了一个 stash 功能,可以把当前工作现场“储藏”起来,等以后恢复现场继续工作

$ git stash
Saved working directory and index state WIP on dev: 355581b add merge

现在,用 git status 查看工作区,就是干净的。

首先确定哪个分支修复 bug,假定需要在 master 分支上修复,就从 master 创建临时分支:

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commits.
  (use "git push" to publish your local commits)

$ git switch -c issue-007

修改,bug,再提交

$ git add readme.txt
$ git commit -m "fix issue 007"
[issue-007 2f20656] fix issue 007
 1 file changed, 1 insertion(+), 1 deletion(-)

修复完成后,切换到 master 分支,并完成合并,最后删除 issue-007 分支

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commits.
  (use "git push" to publish your local commits)

$ git merge --no-ff -m "merged bug fix 007" issue-007
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

现在回到 dev 分支干活

$ git switch dev
Switched to branch 'dev'

$ git status
On branch dev
nothing to commit, working tree clean

工作区是干净的,需要用 git stash list 命名查看刚才存储的工作现场

$ git stash list
stash@{0}: WIP on dev: 355581b add merge

Git 把 stash 内容存在某个地方了,但是需要恢复。有两个办法:

一是用 git stash apply 恢复,但是回复后,stash 内容并不删除,你需要用 git stash drop 来删除;

二是用 git stash pop,恢复的同时把 stash 内容也删了:

$ git stash pop
On branch dev
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:   readme.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    hello.py

no changes added to commit (use "git add" and/or "git commit -a")
Dropped stash@{0} (d53affca8101fb2906bc2f2f4c8245e11dd2b07d)

再用 git stash list 查看,就看不到任何 stash 内容了。

$ git stash list

可以多次 stash,恢复的时候,先用 git stash list 查看,然后恢复指定的 stash,用命令:

git stash apply stash@{0}

在 master 分支修复了 bug 后。假设 dev 分支是早期从 master 分支分出来的,所以,这个 bug 其实在当前的 dev 分支上也存在。

同样的 bug,要在 dev 上修复,只需要把 2f20656 fix issue 007 这个提交所做的修改“复制”到 dev 分支上。注意,只想复制 2f20656 fix issue 007 这个提交所做的修改,并不是想把整个 master 分支 merge 过来。

Git 提供了一个 cherry-pick 命令,让我们能够复制一个特定的提交到当前分支。

$ git branch
* dev
  master
$ git cherry-pick 2f20656
[dev 9231486] fix issue 007
 Date: Fri Oct 11 17:55:11 2019 +0800
 1 file changed, 1 insertion(+), 1 deletion(-)

Git 自动给 dev 分支做了一次提交,这次提交的 commit 是 9231486,不同于 master 的 2f20656,因为这两个 commit 只是改动相同,但确实是两个不同的 commit。用 git cherry-pick,就不需要在 dev 分支上手动再把修 bug 的过程重复一遍。

小结
修复 bug 时,会通过创建新的 bug 分支进行修复,然后合并,最后删除;

当手头工作没有完成时,先把工作现场 git stash 一下,然后修复 bug,修复后,在 git stash pop 或者 git stash apply stash@{0}(这里的 0 需要注意,可以用 git stash list 查看),回到工作现场;

在 master 分支上修复的 bug,想要合并到当前的 dev 分支,可以用 git cherry-pick <commit>命令,把 bug 提交的修改 "复制"到当前分支,避免重复劳动。


Feature 分支

软件开发中,总会有新的功能添加进来。

新功能添加进来,当然不希望因为一些实验性质的代码,把主分支搞乱了,所以每添加一个新功能,最好新建一个 feature 分支,在上面开发,完成后,合并,最后删除该 feature 分支。

假设,一个新任务:开发代码为 Vulcan 的新功能,该功能计划用于下一代星际飞船。

于是准备开发:

$ git checkout -b feature-vulcan
Switched to a new branch 'feature-vulcan'

假设开发完毕:

$ git add vulcan.py

$ git status
On branch feature-vulcan
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
  new file:   vulcan.py

$ git commit -m "add feature vulcan"
[feature-vulcan a698c9a] add feature vulcan
 1 file changed, 12 insertions(+)
 create mode 100644 vulcan.py

切回 dev,准备合并

git switch dev

但是,功能需取消,包含机密资料的分支必须销毁:

$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.

销毁失败。Git 提醒,feature-vulcan 分支还没有被合并,如果删除,将丢失修改。如果确定要删除,使用 -D 参数进行删除。

$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was e27e3b3).

小结
开发新 feature,最好新建一个分支;

如果要丢弃一个没有被合并过的分支,可以通过 git branch -D <name> 强行删除。


多人协作

当你从远程仓库克隆时,实际上 Git 自动把本地的 master 分支和远程的 master 分支对应起来,并且,远程仓库的默认名称是 origin

要查看远程库的信息,用 git remote:

$ git remote
origin

或者,用 git remote -v 显示更详细的信息

$ git remote -v
origin  git@github.com:username/git_learn.git (fetch)
origin  git@github.com:username/git_learn.git (push)

上面显示了可以抓取和推送的 origin 地址,如果没有推送权限,就看不到 push 的地址。

推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git 就会把该分支推送到远程库对应的远程分支上:

git push origin master

如果要推送其他分支,比如 dev

git push origin dev

但是,并不是一定要本地分支往远程推送,哪些分支需要,哪些分支不需要?

  • master 分支是主分支,因此要时刻与远程同步
  • dev 分支是开发分支,团队所有成员都需要在上工作,所以也需要与远程同步;
  • bug 分支只用于在本地修复 bug,不是一定要推送到远程。看实际需求。
  • feature 分支是否推送到远程,取决与团队合作是否在上面开发。
抓取分支

多人协作时,大家会往 masterdev 分支推送各自的修改。

假设团队伙伴,克隆远程库的项目:

$ git clone git@github.com:username/git_learn.git
Cloning into 'git_learn'...
remote: Counting objects: 40, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 40 (delta 14), reused 40 (delta 14), pack-reused 0
Receiving objects: 100% (40/40), done.
Resolving deltas: 100% (14/14), done.

默认情况下,只能看到本地的 master 分支。
现在,小伙伴要在 dev 分支上开发,就必须创建远程 origindev 分支到本地,于是用命令创建了本地 dev 分支:

git switch -c dev origin/dev

现在,他在 dev 上修改,然后时不时把 dev 分支 push 到远程“

git add env.txt
git commit -m "add env"
git push origin dev

小伙伴已经向 origin/dev 分支推送了他的提交,而这个时候,你自己也对同样的文件做出了修改,并试图推送:

$ git add evn.txt

$ git commit -m "add new env"

$ git push origin dev
To github.com:damengsanqianqiu/git_learn.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:damengsanqianqiu/git_learn.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

推送失败,因为小伙伴的最新提交和你试图推送的提交有冲突,解决办法:用 git pull 把最新的提交从 origin/dev 抓下来,然后在本地合并,解决冲突,再推送 :

$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> dev

git pull 也失败了,原因是没有指定本地 dev 分支与远程 origin/dev 分支的链接,根据提示,设置 devorigin/dev 的链接:

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再 pull:

$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.

这是,合并有冲突,手动解决,解决办法和分支管理中的解决冲突一样。解决后,提交,再 push:

git commit -m "fix env conflict"
git push origin dev

因此,多人协作的工作模式通常是这样的

  1. 首先,试图用 git push origin <branch-name> 推送自己的修改;
  2. 如果推送失败,则因为远程分支比本地的版本更新,需要先用 git pull 抓取试图合并;
  3. 如果合并有冲突,则解决冲突,并在本地提交;
  4. 没有冲突或解决冲突后,再用 git push origin <brance-name> 推送就可以成功。

如果 git pull 提示 no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令 git branch --set-upstream-to=origin/<branch> dev

小结

  • 查看远程库信息,使用命令 git remote -v
  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;
  • 从本地推送,使用 git push origin branch-name,如果推送失败,先用 git pull 抓取远程的新提交;
  • 在本地创建和远程分支对应的分支,使用 git switch -c branch-name origin/branch-name,本地和远程分支的名称最好一一致;
  • 建立本地分支和远程分支的关联,使用 git branch --set-upstream-to=origin/<branch> <branch>
  • 从远程抓取分支,使用 git pull,如果有冲突,先处理冲突。

Rebase

每次合并再 push 后,分支会变得混乱。

Git 有一种成为 rebase 的操作,能够让 Git 的提交历史变成直线。

和远程分支同步后,我们对 hello.py 这个文件做了两次提交,用 git log 查看:

* 24be579 (HEAD -> master) add author
* 94448fe add comment
* 6a7291a store Git Learn
* e36f4e9 (origin/master) add content about co-operative and update catalog
* 6cd4087 add knowledge about feature branch
* cb9328a stash content  of feature branch
* 9ceadc1 update content
* 04b8bda learn bug branch
* 129d030 stash master
*   5a4ac81 merged bug fix 007
|\  
| * 2f20656 fix issue 007
|/  
* f5ba50b know about stash
* 4fadb92 update catalog
*   d295e1e conflict fixed again
|\  
| * 81a6738 Update Git教程.md
* | 62dae67 add image and learn merge with no-ff
* |   53dd626 merge with no-ff
|\ \  
| |/  
|/|
| * 355581b add merge
|/  
* d046bdc add Image and learn how to fix conflict and merge
*   31cbd3d conflict fixed
|\  
| * 0c8c6ad AND simple
* | 7bef0e9 & simple
|/  
* 88b8e8f learn how to create and merge branch

注意到 Git 用 (Head -> master)(origin/master) 标识当前分支的 HEAD 和远程 origin 的位置分别是 24be579 add authore36f4e9 add content..,本地比远程分支快 3 个提交

现在我们尝试推送本地分支:

$ git push origin master
To github.com:username/git_learn.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:username/git_learn.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.

失败了,说明有人先往远程库推送了分支。先 pull 一下

$ git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:damengsanqianqiu/git_learn
   e36f4e9..538bd7c  master     -> origin/master
CONFLICT (add/add): Merge conflict in hello.py
Auto-merging hello.py
Automatic merge failed; fix conflicts and then commit the result.

这里自动合并失败,手动解决冲突(详情略)。

解决冲突,再提交。再用 git status 查看状态

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

加上刚才合并的提交,现在我们分支比远程分支超前 4 个提交。

git log 看看

$ git log  --graph --pretty=oneline --abbrev-commit
*   2027993 (HEAD -> master) fix conflict of hello.py
|\  
| * 538bd7c (origin/master) set exit = 1
* | 24be579 add author
* | 94448fe add comment
* | 6a7291a store Git Learn
|/  
* e36f4e9 add content about co-operative and update catalog

现在分支比较乱,这个时候,rebase 就派上用场了,用 git rebase 试试:

$ git rebase
First, rewinding head to replay your work on top of it...
Applying: add comment
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
Applying: add author
Using index info to reconstruct a base tree...
M hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py

再用 git log 看看,

$ git log --graph --pretty=oneline --abbrev-commit
* 2027993 (HEAD -> master) fix conflict of hello.py
* 24be579 add author
* 94448fe add comment
* 6a7291a store Git Learn
* 538bd7c (origin/master) set exit = 1

原来分叉的提交现在变成一条直线。我们注意到 Git 把我们本地的提交“挪动”了位置,放到了 538bd7c (origin/master) set exit = 1之后,这样整个提交历史就成了一条直线。rebase 操作前后,最终的提交内容是一直的,但是,本地的 commit 修改内容已经变化了,他们的修改不再基于 e36f4e9 add content...,而是基于 538bd7c (origin/master) set exit = 1,但最后的提交 2027993 内容是一致的。

这就是 rebase 操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观。缺点就是本地的分叉提交已经被修改过了。

小结

  • rebase 操作可以把本地未 push 的分叉提交历史整理成直线;
  • rebase 的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要第三方的对比。

具体详细的内容可参考廖雪峰的 Git 教程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值