[英]Git rebase does not deletes files in feature branch
I created two branches b1 and b2 based on dev branch on a same day.我在同一天基于dev分支创建了两个分支b1 和 b2 。 Both branch have file named f1.txt .
两个分支都有名为f1.txt的文件。
After 1 day, I did a commit ( c1 ) deleting file f1.txt in branch b1 and pushed the changes to remote branch b1 and merged it to dev . 1 天后,我在分支 b1 中进行了提交( c1 )删除文件 f1.txt 并将更改推送到远程分支 b1并将其合并到dev 。
After 1 month I worked on a branch b2 , made a commit (c2) and did ( git pull origin/dev --rebase ). 1 个月后,我在分支 b2上工作,提交(c2)并做了 ( git pull origin/dev --rebase )。 I mostly do ( git pull --rebase )
我主要做( git pull --rebase )
The file f1.txt is still there in branch b2 .文件f1.txt仍然存在于分支 b2中。
How can we update local branch in case of file deletion in branch b2 ?如果分支 b2中的文件删除,我们如何更新本地分支?
Rebase doesn't work with files . Rebase 不适用于文件。 Rebase works with commits .
变基适用于提交。 Specifically,
git rebase
means:具体来说,
git rebase
意味着:
That is, we take some existing series of commits:也就是说,我们采用一些现有的提交系列:
K--L <-- my-feature (HEAD)
/
...--G--H--I--J <-- main
and, because we don't like the fact that the parent commit of K
is H
—these two commits are in the wrong place in the graph, in other words—we run:并且,因为我们不喜欢
K
的父提交是H
的事实——这两个提交在图中的错误位置,换句话说——我们运行:
git rebase main
and Git will take commits K
and L
and copy them to new commits. Git 会接受提交
K
和L
并将它们复制到新的提交中。 The new commits will:新的提交将:
K
and L
, butK
和L
相同的事情,但是J
J
so that we get:这样我们得到:
K--L <-- my-feature
/
...--G--H--I--J <-- main
\
K'-L' <-- new-and-improved-my-feature
Note how both branches co-exist at this point.注意此时两个分支是如何共存的。
Now we make use of the fact that humans (though not Git) find commits by using a branch name .现在我们利用人类(尽管不是 Git)通过使用分支名称来查找提交的事实。 (Git uses hash IDs.) Commits
K'
and L'
look a lot like K
and L
, and a human looking at L'
will probably mistakenly think they are looking at L
. (Git 使用哈希 ID。)提交
K'
和L'
看起来很像K
和L
,一个人看着L'
可能会错误地认为他们正在看L
。 ( Git won't, because L'
will have a totally different random-looking hash ID.) ( Git不会,因为
L'
会有一个完全不同的随机哈希 ID。)
So now, we have Git swap the names around a bit!所以现在,我们让 Git 稍微交换一下名称! Then we have Git check out the new branch it built:
然后我们让 Git 检查它构建的新分支:
K--L <-- old-and-lousy-my-feature
/
...--G--H--I--J <-- main
\
K'-L' <-- my-feature (HEAD)
and then delete the name on the old branch:然后删除旧分支上的名称:
K--L [abandoned
/
...--G--H--I--J <-- main
\
K'-L' <-- my-feature (HEAD)
We won't be able to find commits KL
any more, because we use names.我们将无法再找到提交
KL
,因为我们使用名称。 (Git can still find them, as long as Git can find the hash IDs. Git keeps those hash IDs around for a while, or on GitHub, forever.) (Git 仍然可以找到它们,只要 Git 可以找到哈希 ID。Git 将这些哈希 ID 保存一段时间,或者永远保存在 GitHub 上。)
That's rebase in a nutshell, though we can get fancy using git rebase -i
for instance.简而言之,这就是变基,尽管我们可以使用
git rebase -i
来获得幻想。 Here Git will put up an instruction sheet with pick
commands, which we can modify to read edit
or reword
or whatever.在这里,Git 将放置一个带有
pick
命令的指令表,我们可以修改它以读取edit
或reword
或其他任何内容。 This changes how Git does the copying-of-commits.这改变了 Git 复制提交的方式。 Before we go any further, we should mention that the actual copying happens through
git cherry-pick
.在我们继续之前,我们应该提到实际的复制是通过
git cherry-pick
发生的。 1 The way cherry-pick "copies a commit" is, roughly speaking, to compare the commit's snapshot against the commit's parent's snapshot. 1cherry -pick“复制提交”的方式,粗略地说,是将提交的快照与提交的父级快照进行比较。 Whatever changes this commit makes, those are the changes the copy will make, too.
无论这个提交做出什么改变,副本也会做出这些改变。
Thus, in our example above, the change (the git diff
output) you'll see if you show commit K
—if you do this before rebasing, or if you save its hash ID so that you can pick it out after rebasing—will match the change that you'll see if you show commit K'
after rebasing.因此,在我们上面的示例中,如果您显示提交
K
,您将看到的更改( git diff
输出)——如果您在变基之前执行此操作,或者如果您保存其哈希 ID 以便您可以在变基后将其挑选出来——将如果您在变基后显示提交K'
,则匹配您将看到的更改。
1 This is true for all interactive git rebase
operations, and for most git rebases
in modern Git, but in Git before version 2.26, a lot of rebase operations used a different back-end. 1这适用于所有交互式
git rebase
操作,并且对于现代Git 中的大多数git rebases
,但在 2.26 版本之前的 Git 中,许多 rebase 操作使用不同的后端。 The result is usually the same, and you should generally think of rebase as "automated cherry-pick" anyway.结果通常是相同的,无论如何,您通常应该将 rebase 视为“自动挑选”。
git pull
completelygit pull
The pull
command is basically a convenience command: it just runs two other Git commands for you. pull
命令基本上是一个方便的命令:它只是为您运行另外两个Git 命令。 This makes it relatively easy to understand, except that to really understand it, you must understand both of the other two commands!这使得它相对容易理解,除了要真正理解它,你必须理解其他两个命令! So let's see what they are:
那么让我们看看它们是什么:
First, git pull
runs git fetch
.首先,
git pull
运行git fetch
。
The git fetch
command means reach out to some other Git repository and collect some commit(s) from it if and as needed . git fetch
命令意味着在需要时访问其他 Git 存储库并从中收集一些提交。 This can get complicated, so let's leave it at this for now.这可能会变得复杂,所以让我们暂时搁置它。 The new commits that
git fetch
picks up, if any, get recorded in a special file in the hidden .git
folder: .git/FETCH_HEAD
. git fetch
拾取的新提交(如果有)会记录在隐藏的.git
文件夹中的一个特殊文件中: .git/FETCH_HEAD
。 Depending on your Git version and how you invoke git pull
, this git fetch
can also update things like origin/dev
.根据您的 Git 版本以及您调用
git pull
的方式,此git fetch
还可以更新诸如origin/dev
类的内容。 But in all cases, this does not affect any of your current work .但在所有情况下,这都不会影响您当前的任何工作。
New commits, stuffed into your Git repository, are simply new commits.填充到您的 Git 存储库中的新提交只是新提交。 You aren't actually using any of them yet.
您实际上还没有使用它们中的任何一个。 Usually, we fetch new commits because we want to use them .
通常,我们获取新的提交是因为我们想使用它们。 So...
所以...
Having run git fetch
, git pull
runs a second Git command.运行
git fetch
后, git pull
运行第二个 Git 命令。
This second command is up to you.第二个命令由您决定。 You can choose to have Git run
git merge
—that's the default, if you haven't set anything else up—or you can choose to have Git run git rebase
.你可以选择让 Git 运行
git merge
——这是默认设置,如果你没有设置任何其他东西的话——或者你可以选择让 Git 运行git rebase
。
The purpose of this second command is, of course, to make use of the new commits you fetched in step 1. This is where everything gets pretty tricky though.当然,第二个命令的目的是利用您在步骤 1 中获取的新提交。不过,这就是一切变得非常棘手的地方。
We'll get back to git fetch
in a moment as it is about to matter a lot.我们稍后会回到
git fetch
,因为它很重要。 Let's take a look now at the information you have provided.现在让我们看一下您提供的信息。
I created two branches b1 and b2 based on dev branch on a same day.
我在同一天基于dev分支创建了两个分支b1 和 b2 。 Both branch have file named f1.txt .
两个分支都有名为f1.txt的文件。
This isn't quite the right description.这不是完全正确的描述。 Let's correct it.
让我们纠正它。
Since Git is really about commits —not files, not branches, but commits —let's draw the situation you've described so far.既然 Git 真的是关于提交——不是文件,不是分支,而是提交——让我们画出你到目前为止所描述的情况。 You had a repository, which has some set of commits.
您有一个存储库,其中包含一些提交。 Each commit holds a full snapshot of every file, plus some metadata (things like who made the commit and when, and the raw hash ID of the commit's parent commit).
每个提交都包含每个文件的完整快照,以及一些元数据(例如提交的人员和时间,以及提交的父提交的原始哈希 ID)。 We'll draw this set of commits and the branch names, like this:
我们将绘制这组提交和分支名称,如下所示:
...--G--H <-- dev, b1, b2
That is, all three branch names select some commit.也就是说,所有三个分支名称都选择了一些提交。 Each commit has a unique hash ID—some big ugly string of letters and digits, unique to that one particular commit—and a branch name gives a way for a human to tell Git: Just get me the latest commit, whatever its hash ID is.
每个提交都有一个唯一的哈希 ID——一些大而丑陋的字母和数字字符串,对那个特定的提交来说是唯一的——并且一个分支名称让人们可以告诉 Git:只要给我最新的提交,不管它的哈希 ID 是什么. Git does this by storing the latest commit's hash ID in the branch name.
Git 通过将最新提交的哈希 ID 存储在分支名称中来做到这一点。 So the three branch names here,
dev
, b1
, and b2
all store the hash ID of commit H
.所以这里的三个分支名称
dev
、 b1
和b2
都存储了提交H
的哈希 ID。 Commit H
stores, in its snapshot, the full set of files including file f1.txt
;提交
H
在其快照中存储包括文件f1.txt
在内的完整文件集; commit H
stores, in its metadata, the hash ID of earlier (parent) commit G
.提交
H
在其元数据中存储早期(父)提交G
的哈希 ID。
Since all three branch names hold the same hash ID , all three branches select commit H
.由于所有三个分支名称都具有相同的哈希 ID ,因此所有三个分支都选择提交
H
That one commit holds file f1.txt
, and it will do so forever (no part of any commit can ever change).那个提交包含文件
f1.txt
,并且它将永远这样做(任何提交的任何部分都不能改变)。
After 1 day, I did a commit ( c1 ) deleting file f1.txt in branch b1
1天后,我做了一个提交( c1 )删除分支b1中的文件f1.txt
Let's draw commit C1
(I'll even call it C1
here):让我们绘制提交
C1
(我什至在这里称它为C1
):
C1 <-- b1
/
...--G--H <-- dev, b2
Commit C1
has a new snapshot: in C1
's snapshot, file f1.txt
doesn't exist.提交
C1
有一个新快照:在C1
的快照中,文件f1.txt
不存在。 That's a full snapshot of every file, so when we compare the snapshot in H
to the snapshot in C1
, one of the changes we see is "delete file f1.txt
".这是每个文件的完整快照,因此当我们将
H
中的快照与C1
中的快照进行比较时,我们看到的更改之一是“删除文件f1.txt
”。
Note that C1
does not store changes .请注意,
C1
不存储更改。 It just stores a snapshot plus metadata.它只存储快照和元数据。 The "change" we see is a result of comparing
H
vs C1
, as obtained by following branch name dev
or b2
to commit H
, and following branch name b2
to C1
.我们看到的“变化”是比较
H
与C1
的结果,这是通过遵循分支名称dev
或b2
提交H
以及遵循分支名称b2
到C1
获得的。
and pushed the changes to remote branch b1 and merged it to dev .
并将更改推送到远程分支b1并将其合并到dev 。
I presume this means: I ran git push origin b1
or I ran git push -u origin b1
.我想这意味着:我跑了
git push origin b1
或者我跑了git push -u origin b1
。 So far so good, but now, we have a bit of a problem.到目前为止一切顺利,但现在,我们遇到了一些问题。 When you use
git push
, you are doing the closest thing there is to the opposite of git fetch
.当您使用
git push
时,您正在做与git fetch
最接近的事情。 You have your Git call up some other Git software, that is working with a different but related Git repository—what Git calls a clone .你让你的 Git 调用其他一些 Git 软件,该软件正在使用不同但相关的 Git 存储库——Git 称之为克隆。 You probably cloned your repository from theirs, but "clone-ness" is symmetric: if Fred2 is a clone of Fred1, then Fred1 is a clone of Fred2.
您可能从他们的存储库中克隆了您的存储库,但“克隆性”是对称的:如果 Fred2 是 Fred1 的克隆,那么 Fred1 是 Fred2 的克隆。
The thing here is that each clone has its own branches .这里的问题是每个克隆都有自己的分支。 That's why you said "remote branch b1 ": that's a branch named
b1
over on origin
.这就是您说“远程分支b1 ”的原因:这是一个名为
b1
的分支,位于origin
上。 So now they have a b1
.所以现在他们有一个
b1
。 Then you say and merged it to dev
, but what is it here?然后你说并将它合并到
dev
,但它在这里是什么? How did you do this merge?你是怎么做这个合并的?
Given that you have your own dev and your own
b1`, you might have run:鉴于您有自己的
dev and your own
b1`,您可能已经运行:
git checkout dev; git merge b1
Or, given that you tagged this whole thing with或者,假设你用
github , maybe you mean that after:
github ,也许你的意思是:
git push -u origin b1
you used a green clicky button on GitHub to cause the merge to happen in the GitHub repository over on GitHub .你在GitHub 上使用了一个绿色的点击按钮,导致合并在 GitHub 存储库中发生在 GitHub 上。 That is, you did the merging in their repository, without affecting your repository.
也就是说,您在他们的存储库中进行了合并,而不会影响您的存储库。
I don't know which of these might be right, but if we assume you used the button on GitHub for now, and did nothing locally, your local repository now has this:我不知道其中哪一个可能是正确的,但是如果我们假设您现在使用 GitHub 上的按钮,并且在本地没有做任何事情,那么您的本地存储库现在有这个:
C1 <-- b1, origin/b1
/
...--G--H <-- dev, b2, origin/dev
Note that I've added the origin/
names.请注意,我已经添加了
origin/
名称。 These are your Git's way of remembering what your Git thinks is set up in the GitHub repository over on GitHub.这些是你的 Git 记住你的 Git认为是在 GitHub 存储库中设置在 GitHub 上的方式的方式。 Your Git can only update these in certain cases.
你的 Git 只能在某些情况下更新这些。 The most common case is when using
git fetch
, but even here it gets a bit complicated.最常见的情况是使用
git fetch
,但即使在这里它也会变得有点复杂。
If you did the merge in your repository , though, here's what you'll have, assuming you allow git merge
to do a fast-forward (which is its default):但是,如果您在存储库中进行了合并,假设您允许
git merge
进行快进(这是默认设置),这就是您将拥有的:
C1 <-- b1, dev, origin/b1
/
...--G--H <-- b2, origin/dev
Let's return to your own commands, as you've described them:让我们回到您自己的命令,正如您所描述的那样:
After 1 month I worked on a branch b2 ,
1 个月后,我在分支 b2工作,
If we start with the drawing above, without any updates to it—which is all we can do given your description at this point—then b2
still points to commit H
, which still has file f1.txt
in it.如果我们从上面的图开始,没有对其进行任何更新——根据你的描述,我们现在能做的就是——然后
b2
仍然指向提交H
,其中仍然有文件f1.txt
。
made a commit ( c2 )
提交 ( c2 )
Presumably you did not touch f1.txt
in this commit, so it has the same f1.txt
in the new commit:大概你没有在这个提交中触及
f1.txt
,所以它在新提交中具有相同的f1.txt
:
C1 <-- b1, [dev?], origin/b1
/
...--G--H <-- [dev?], origin/dev
\
C2 <-- b2
Note that I don't know which commit your dev
selects, nor whether their (origin's) dev
still selects commit H
or not.请注意,我不知道您的
dev
人员选择了哪个提交,也不知道他们的(来源) dev
人员是否仍然选择提交H
and did ( git pull origin/dev --rebase ).
并做到了( git pull origin/dev --rebase )。
We now have to get into some of the gory details of git fetch
.我们现在必须深入了解
git fetch
的一些血腥细节。 When you run git pull
, with or without the --rebase
option, git pull
passes most of your arguments on to git fetch
.当您运行
git pull
时,无论是否带有--rebase
选项, git pull
都会将您的大部分参数传递给git fetch
。 The --rebase
option, however, is an option to git pull
to tell it which second command to run , so it does not pass that one on to git fetch
.然而,
--rebase
选项是git pull
的一个选项,告诉它要运行第二个命令,因此它不会将该命令传递给git fetch
。
Let's add this to the mix too:让我们也将其添加到混合中:
I mostly do ( git pull --rebase )
我主要做( git pull --rebase )
So one of these two will run:所以这两个之一将运行:
git fetch origin/dev
and one of these will run:其中之一将运行:
git fetch
git fetch
git fetch
的血腥细节When you run git fetch
with no additional arguments , as in:当您运行
git fetch
时没有其他参数,如:
git fetch
(as you'd get with git pull --rebase
for instance), your Git will: (例如,你会得到
git pull --rebase
),你的 Git 将:
origin
, using the URL stored under the name origin
;origin
下的 URL 在origin
调用另一个 Git;origin/*
names based on what got brought over in step 3.origin/*
名称。 Any branch names they deleted won't show up in step 2, so won't be updated in step 3, so your Git won't delete the corresponding origin/
name in your own repository.他们删除的任何分支名称都不会显示在步骤 2 中,因此不会在步骤 3 中更新,因此您的 Git 不会删除您自己的存储库中相应的
origin/
名称。 That is, supposed they had a branch temporary
, and you ran git fetch
.也就是说,假设他们有一个
temporary
分支,而您运行了git fetch
。 You'd get an origin/temporary
at this time.此时您将获得
origin/temporary
。 Later, they delete their temporary
.后来,他们删除了他们的
temporary
. You run git fetch
, and your Git sees no temporary
.你运行
git fetch
,你的 Git 看不到temporary
的。 But this just means your Git doesn't update your origin/temporary
.但这只是意味着您的 Git 不会更新您的
origin/temporary
。 So it sticks around from earlier, even though there's no point to its existence any more.所以它从早些时候就一直存在,即使它的存在已经没有意义了。 Usually this doesn't matter much, it just means that "stale" names accumulate.
通常这并不重要,它只是意味着“陈旧”的名称会累积。 2
2
So git fetch
by itself means get everything and update all my remote-tracking names, then stop there .所以
git fetch
本身意味着获取所有内容并更新我所有的远程跟踪名称,然后停在那里。 That's pretty often exactly what I want , so it's what I normally use—I almost never use git pull
at all myself.这通常正是我想要的,所以这是我通常使用的——我自己几乎从不使用
git pull
。 I like to see what got updated, see the new branch names if there are any, and maybe take a look at new commits that came in.我想看看更新了什么,如果有的话,看看新的分支名称,也许看看新的提交。
But, if you like git pull
, remember that git pull origin dev
means run git fetch origin dev
.但是,如果你喜欢
git pull
,请记住git pull origin dev
的意思是运行git fetch origin dev
。 Remember that git pull origin/dev
means *run git fetch origin/dev
.请记住,
git pull origin/dev
意味着 *run git fetch origin/dev
。
The latter doesn't work!后者不行! You said that's what you ran, and if that's what you ran, that would explain why it didn't work.
你说那是你跑的,如果那是你跑的,那就可以解释为什么它不起作用。 The first non-option word after
git fetch
is the name of the remote . git fetch
之后的第一个非选项词是远程的名称。 The only remote you have is (probably 3 ) origin
.您拥有的唯一遥控器是(可能是3 个)
origin
。 There's no origin/dev
remote, and git fetch origin/dev
will thus fail.没有
origin/dev
远程,因此git fetch origin/dev
将失败。
This might have been a typo and maybe you ran git pull origin dev
.这可能是一个错字,也许你跑了
git pull origin dev
。 That would run git fetch origin dev
.那将运行
git fetch origin dev
。 Running this tells your Git: call up the other Git over at origin
, and list out some or all of their branch names, but I'm only really interested in the one spelled dev
.运行它会告诉你的 Git:在
origin
调用另一个 Git,并列出它们的部分或全部分支名称,但我只对拼写为dev
的那个真正感兴趣。 They'll list their names and your Git will see if they have a dev
.他们会列出他们的名字,你的 Git 会查看他们是否有
dev
。 If so, they'll find out which commit their dev
has as its most recent commit.如果是这样,他们将找出他们的
dev
人员将哪个提交作为其最近的提交。
If that commit is new to you—ie, if they have a dev
and it has new-to-your commits—your Git will bring those commits over.如果那个提交对你来说是新的——也就是说,如果他们有一个
dev
者并且它对你的提交来说是新的——你的 Git 将把这些提交带过来。 In any case, whether or not they have new-to-you commits, your Git will write their most-recent- dev
-commit hash ID into .git/FETCH_HEAD
.在任何情况下,无论他们是否有新提交,您的 Git 都会将他们最近的
dev
-commit 哈希 ID 写入.git/FETCH_HEAD
。 If your Git version is at least 1.8.2, your Git will also update your origin/dev
now, but if your Git is older than that, your Git will fail to update your origin/dev
.如果您的 Git 版本至少为 1.8.2,您的 Git 现在也会更新您的
origin/dev
,但如果您的 Git 比这更旧,您的 Git 将无法更新您的origin/dev
。
Once your git fetch
finishes, it will return "success" if:一旦你的
git fetch
完成,它将返回“成功”,如果:
Otherwise your git fetch
tells your git pull
that it failed, and your pull stops at this point.否则,您的
git fetch
会告诉您的git pull
它失败了,并且您的 pull 会在此时停止。 So git fetch origin/dev
would fail and would stop your rebase at this point.所以
git fetch origin/dev
会失败,此时会停止你的变基。
If that was a typo and you actually ran git pull origin dev --rebase
(not git pull origin/dev --rebase
), then your git fetch
probably worked (GitHub's computers are almost always up and working) and you probably got a .git/FETCH_HEAD
with the right hash ID in it.如果这是一个错字,并且您实际上运行了
git pull origin dev --rebase
(不是git pull origin/dev --rebase
),那么您的git fetch
可能有效(GitHub 的计算机几乎总是正常工作)并且您可能得到了一个.git/FETCH_HEAD
有正确的哈希 ID。
In that case , your Git will run git rebase hash-ID
, using the hash ID found in .git/FETCH_HEAD
.在这种情况下,您的 Git 将使用
.git/FETCH_HEAD
中的哈希 ID 运行git rebase hash-ID
。 This will conduct a git rebase
as shown above, using the hash ID of their dev
commit.这将使用他们的
dev
提交的哈希 ID 执行如上所示的git rebase
。
2 If you want to get rid of these "stale" remote-tracking names, use git fetch --prune
or git remote prune origin
. 2如果您想摆脱这些“陈旧”的远程跟踪名称,请使用
git fetch --prune
或git remote prune origin
。 I usually want my Git software to do this automatically every time;我通常希望我的 Git 软件每次都自动执行此操作; to make your Git do that, use
git config --global fetch.prune true
.要让你的 Git 做到这一点,请使用
git config --global fetch.prune true
。 Just be aware that this makes your Git remove remote-tracking names as soon as it realizes that they are gone!请注意,这会使您的 Git 在意识到远程跟踪名称消失后立即删除它们!
3 You will set up one remote per other-Git-repository that you need to talk to regularly. 3您将为每个需要定期交谈的 other-Git-repository 设置一个遥控器。 When you use
git clone
to make an initial clone, that sets up one remote ("the other Git repository that I cloned") and calls it origin
.当您使用
git clone
进行初始克隆时,它会设置一个远程(“我克隆的另一个 Git 存储库”)并将其称为origin
。 That's the one remote that almost everyone has in almost every repository.这是几乎每个人在几乎每个存储库中都有的一个遥控器。
The file f1.txt is still there in branch b2 .
文件f1.txt仍然存在于分支 b2中。
What we need to know, now, is:现在我们需要知道的是:
git rebase
you showed correct, in which case it failed, or did you show us a typo, and the one you ran was correct and the one you showed was wrong?git rebase
是否正确,在这种情况下它失败了,或者您是否向我们显示了一个错字,而您运行的那个是正确的,而您显示的那个是错误的?dev
?dev
中存储了什么哈希 ID? What was in the commit(s) there? You can run:你可以运行:
git fetch origin
git log --all --decorate --oneline --graph
to get a Git-drawn diagram of the commit graph along with both your and their names for the various commits, and that will tell us more.获取提交图的 Git 绘制图表以及您和他们的各种提交的名称,这将告诉我们更多信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.