-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
executable file
·92 lines (92 loc) · 46.5 KB
/
search.xml
File metadata and controls
executable file
·92 lines (92 loc) · 46.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Git Merge Commits]]></title>
<url>%2F2020%2F12%2Fgit-merge-commits%2F</url>
<content type="text"><![CDATA[平时在 Git 的使用过程中,遇到了好些场景需要把多个 Commit 合并成一个 Commit。经过在网上一通搜索,大家也是仁者见仁,智者见智。在本文中总结了几种常见场景,如果大家还遇到过其他的场景,也欢迎留言讨论。 场景一:把最新的改动合并本地还未提交到远端的 commit 上先来热一下身,看一个最简单的场景。C1、C2 已经被提交到远端仓库,C3 已提交到本地仓库,现在想要把还未提交到本地的 C4 和 C3 合并。 步骤: 1234git add .# 按照提示修改 Commit message,如果不用修改,可以直接加上 --no-editgit commit --amend 场景二:合并本地还未提交到远端的 commitC1、C2 已经被提交到远端仓库,C3、C4 已提交到本地仓库,现在想要把 C3 和 C4 合并。 方法一:使用 rebase -i,基于 SHA 值步骤: 12# f944f36e 为 C2 的 SHA 值git rebase -i f944f36e 执行 rebase -i 操作后会看到类似如下的界面(注意这里 Commit 的顺序): 123456789101112131415161718 1 pick 7d1cc37 C3 2 pick 34f88df C4 3 4 # Rebase f944f36..34f88df onto f944f36 (2 commands) 5 # 6 # Commands: 7 # p, pick <commit> = use commit 8 # r, reword <commit> = use commit, but edit the commit message 9 # e, edit <commit> = use commit, but stop for amending 10 # s, squash <commit> = use commit, but meld into previous commit 11 # f, fixup <commit> = like "squash", but discard this commit's log message 12 # x, exec <command> = run command (the rest of the line) using shell 13 # b, break = stop here (continue rebase later with 'git rebase --continue') 14 # d, drop <commit> = remove commit 15 # l, label <label> = label current HEAD with a name 16 # t, reset <label> = reset HEAD to a label 17 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]... 然后根据自己的需求对每个 Commit 选择对应的操作,这里我们对 C3 选择 pick,对 C4 以及后面的 Commit(如果有的话)选择 squash 或者 fixup 都可以,然后保存退出 vi 模式即可,C3 后面的 Commit 都会被合并到 C3 中。这里通过灵活运用不同的操作,可以改变提交顺序,提交信息,合并提交,放弃提交等操作。 方法二:使用 rebase -i,基于 HEAD 的相对位置该方法是基于 HEAD 的相对位置,适合合并少量的 Commit。如果想要看到2个 Commit,就写 HEAD~2。 步骤: 1git rebase -i HEAD~2 其他的步骤和方法一相同。方法一和方法二的详细解释可以参考这个回答。 方法三:使用 Soft Reset 和 Commit步骤: 12git reset --soft HEAD~2git commit -m 'your message' 如果想要复用之前的提交信息,可以用这个命令 git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"。更详细的解释,可以参考这个回答。 方法四:使用 Hard Reset 和 Merge Squash 注意: 在采用该方式之前,先确保所有的改动都已经提交,因为 Hard Reset 会直接丢掉 Staged 和 Unstaged 的改动。 步骤: 1234567891011# Reset the current branch to the commit just before the last 2:git reset --hard HEAD~2# HEAD@{1} is where the branch was just before the previous command.# This command sets the state of the index to be as it would just# after a merge from that commit:git merge --squash HEAD@{1}# Commit those squashed changes. The commit message will be helpfully# prepopulated with the commit messages of all the squashed commits:git commit 在执行 Merge 的时候可能会遇到一个错误:fatal: refusing to merge unrelated histories。这是因为默认情况下,Git 是不允许在不同的项目之间执行 Merge 动作的。但是默认不允许不代表不能做,可以通过加上参数 --allow-unrelated-histories 强制执行。具体的解释可以参考这个回答。 这种方式的好处是,默认会自动带出之前的详细提交信息。如果最后需要重写提交信息,也可以直接 git commit -m 'your message'。更详细的解释,可以参考这个回答。 场景三:合并本地提交和已经 Push 到远端的提交其实该场景和场景二并没有太大的区别,唯一需要特别注意的是 Force Push(git push -f),如果 Force Push 不成功,可以检查一下远端仓库是否设置的有分支保护策略。有可能覆盖掉其他人的提交的潜在风险,在 Push 之前需要再三确认远端没有比本地更新的提交记录。操作步骤和场景二中的类似,这里就不再赘述。 场景四:将所有的提交记录合并为一个 Commit 先介绍一下这个场景产生的背景。有一个已经开发了一段时间的项目,从某个时间点开始,需要不定期把之前一段时间的所有提交合并成一个提交并且规整提交信息为特定的格式,然后 Push 到远端的另一个仓库中,而且这个操作在将来还需要增量进行。比如:1月1号需要把 Repo A 的 C1 和 C2 合并成 CC1 并 Push 到 Repo B 中,1月10号需要把 Repo A 的 C3 和 C4 合并成 CC2 并 Push 到 Repo B 中,依次类推。 第一次合并操作,即把所有的提交合并成一个提交。 1git rebase -i <第一个提交的 SHA 值> 因为这里必须要提供一个 SHA 值,所以最终结果一定会大于或等于2个提交记录,并不能达到我们的目的。 那么,聪明的程序员马上想到基于相对位置的方式,假设本地总共4个提交记录,我是不是可以通过HEAD~4来定位呢? 1git rebase -i HEAD~4 这是你发现事与愿违,并不能成功执行,而是得到一个错误:fatal: invalid upstream 'HEAD~4'。但是功夫不负有心人,经过一番摸索,找到一个参数 --root 可以帮我们完美解决问题。 1git rebase -i --root 第一次合并操作之后,后续的操作可以通过分支的方式操作更加容易,就如 Pull Request 中的 Squash and Merge 一般丝滑。 具体操作步骤如下: 123456789# 假设第一次将 C1 和 C2 合并成了 CC1,这时我们从合并完成的这个点创建出一个分支(以分支名 `repo-b` 为例)git checkout -b repo-b <CC1 的 SHA 值># 假设 Repo A 的 C3 和 C4 在 master 分支上,通过 Merge Squash 的方式合并 C3 和 C4 成为 CC2# 如果此时 master 上除了 C3 和 C4 还有其他的提交记录,那么我们可以从 C4 的地方创建出新分支(以 `repo-a` 为例),然后在下面的命令中将 `master` 替换为 `repo-a` 即可git merge --squash master --allow-unrelated-histories# 此时如果有冲突就解决冲突,解决完冲突后,执行 `git commit`# 最后 Push 到 Repo B 的远端仓库即可。 至此,我遇到过的几种场景就列举完了,欢迎大家留言补充,或者探讨更好的方式。]]></content>
<categories>
<category>Productivity</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[开发人员常用的国内镜像仓库]]></title>
<url>%2F2020%2F09%2Fawesome-mirrors%2F</url>
<content type="text"><![CDATA[本文如果对你有帮助请帮忙Star一下,以帮助更多的人。 由于众所周知的原因,中国的开发者在下载各种依赖的时候速度都比较慢。因此部分有实力的高校、公司在国内搭建了方便开发者的各种镜像仓库,并且几乎都是和国外的源定时同步的。本仓库主要收集方便中国开发者提速的源,以及配置的方式。 下面的操作方式在不同的系统可能会有细微的差别,欢迎大家补充和纠正。 目录 Docker Nodejs Python Java Goproxy Ruby Alpine apk Centos yum Debian apt Ubuntu apt Homebrew iOS Git Docker首先从网上搜索国内的docker registry源,然后修改docker的配置并重启docker。在这里我比较推荐使用免费的阿里云镜像加速器(至少截至2020年09月03日还是免费的),目前使用一直比较平稳。 获取镜像加速url 注册一个阿里云账号并登录,在产品与服务中搜索容器镜像服务,跟随引导完成必要的一些步骤,然后来到这个页面,就可以看到自己专有的加速器地址了。 给docker客户端配置镜像加速器 这里以MacOS为例,其他系统类似。如果没有/etc/docker/daemon.json文件,则可以直接通过下面的命令完成配置;如果该文件已经存在了,则选择自己熟悉的文本编辑器编辑该文件,添加加速器地址的配置。如果你使用的带界面的客户端,也可以在Preferences... -> Docker Engine显示的编辑器中添加相应的配置。 123456sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-'EOF'{ "registry-mirrors": ["https://xxx.mirror.aliyuncs.com"]}EOF 注意:将https://xxx.mirror.aliyuncs.com替换成你自己的镜像加速器地址。再次提醒,配置完成后,重启docker之后才会生效。 除了阿里云的源,还有一些其他的源可以参考: https://registry.docker-cn.com http://hub-mirror.c.163.com http://docker.mirrors.ustc.edu.cn http://mirror.azure.cn/help/docker-registry-proxy-cache.html Nodejs下面是NPM的配置方式,以淘宝的源为例。 临时使用 12345678# 为单用户安装npm install --registry https://registry.npm.taobao.org <package-name>#为所有用户安装npm install --global --registry https://registry.npm.taobao.org <package-name># yarnyarn save <package-name> --registry https://registry.npm.taobao.org 默认使用 12345# npmnpm set registry https://registry.npm.taobao.org# yarnyarn config set registry https://registry.npm.taobao.org 除了淘宝的源,还有一些其他的源可以参考: http://crproxy.trafficmanager.net:4873 另外,可以通过nrm管理多个NPM的源,相应的yarn也有yrm。 PythonPyPI (Python Package Index) 是 Python 编程语言的软件存储库。开发者可以通过 PyPI 查找和安装由 Python 社区开发和共享的软件,也可以将自己开发的库上传至 PyPI 。这里以阿里云的源为例。 临时使用 1pip install -i https://mirrors.aliyun.com/pypi/simple <package-name> 注意,simple 不能少, 是 https 而不是 http 默认使用 升级 pip 到最新的版本 (>=10.0.0) 后进行配置: 12pip install pip -Upip config set global.index-url https://mirrors.aliyun.com/pypi/simple 除了阿里云的源,还有一些其他的源可以参考: https://pypi.tuna.tsinghua.edu.cn/simple https://pypi.mirrors.ustc.edu.cn/simple https://pypi.douban.com/simple http://mirror.azure.cn/pypi/simple Anaconda的配置方式可以参考清华大学的Anaconda镜像使用帮助 Java Maven 配置 打开 Maven 的配置文件(windows机器一般在maven安装目录的conf/settings.xml),在<mirrors></mirrors>标签中添加 mirror 子节点: 123456<mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url></mirror> 如果想使用其它代理仓库,可在<repositories></repositories>节点中加入对应的仓库使用地址。以使用spring代理仓为例: 12345678910<repository> <id>spring</id> <url>https://maven.aliyun.com/repository/spring</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots></repository> gradle 配置 在 build.gradle 文件中加入以下代码: 1234567allprojects { repositories { maven { url 'https://maven.aliyun.com/repository/public/' } mavenLocal() mavenCentral() }} 如果想使用 maven.aliyun.com 提供的其它代理仓,以使用 spring 仓为例,代码如下: 12345678allprojects { repositories { maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/spring/'} mavenLocal() mavenCentral() }} 除了Spring的仓库之外,阿里云还提供一些其他的源: central https://maven.aliyun.com/repository/central jcenter https://maven.aliyun.com/repository/public public https://maven.aliyun.com/repository/public google https://maven.aliyun.com/repository/google gradle-plugin https://maven.aliyun.com/repository/gradle-plugin spring https://maven.aliyun.com/repository/spring spring-plugin https://maven.aliyun.com/repository/spring-plugin grails-core https://maven.aliyun.com/repository/grails-core apache snapshots https://maven.aliyun.com/repository/apache-snapshots Goproxy使用go1.11以上版本并开启go module机制,下面以阿里云的源为例: 12export GO111MODULE=onexport GOPROXY=https://mirrors.aliyun.com/goproxy 除了阿里云的源,还有一些其他的源可以参考: https://goproxy.cn https://goproxy.io Ruby这里以配置Ruby China的源为例: 1gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/ Bundler用户参考如下配置: 1bundle config mirror.https://rubygems.org https://gems.ruby-china.com 这样你不用改你的Gemfile的source。 123source 'https://rubygems.org/'gem 'rails', '4.2.5'... 除了Ruby China的源,还有一些其他的源可以参考: https://mirrors.aliyun.com/rubygems https://mirrors.tuna.tsinghua.edu.cn/rubygems https://mirrors.ustc.edu.cn/rubygems http://mirror.azure.cn/rubygems Alpine apkAlpine Linux 是一个面向安全,轻量级的基于musl libc与busybox项目的Linux发行版。 下面以配置阿里云的源作为例子: 1sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories 除了阿里云的源,还有一些其他的源可以参考: dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn Centos yumCentOS,是基于 Red Hat Linux 提供的可自由使用源代码的企业级 Linux 发行版本;是一个稳定,可预测,可管理和可复制的免费企业级计算平台。 更换源之前,建议先备份一下,下面以阿里云的源为例: 1mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 下载阿里云的源: 123456# Centos 6curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-6.repo# Centos 7curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo# Centos 8curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo 生成缓存: 1yum makecache 注意: 非阿里云ECS用户会出现 Couldn't resolve host 'mirrors.cloud.aliyuncs.com'信息,不影响使用。用户也可自行修改相关配置: 1sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo 除了阿里云的源,还有一些其他的源可以参考: https://mirrors.tuna.tsinghua.edu.cn/help/centos http://mirrors.ustc.edu.cn/help/centos.html http://mirror.azure.cn/help/centos.html Debian aptDebian GNU/Linux ,是一个操作系统及自由软件的发行版,由一群自愿付出时间和精力的用户来维护并更新。它附带了超过 59000 个软件包,这些预先编译好的软件被打包成一种良好的格式以便于用户安装和使用。 下面以阿里云的源为例: debian 7.x (wheezy) 编辑/etc/apt/sources.list文件(需要使用sudo), 在文件最前面添加以下条目(操作前请做好相应备份) 1234deb http://mirrors.aliyun.com/debian/ wheezy main non-free contribdeb http://mirrors.aliyun.com/debian/ wheezy-proposed-updates main non-free contribdeb-src http://mirrors.aliyun.com/debian/ wheezy main non-free contribdeb-src http://mirrors.aliyun.com/debian/ wheezy-proposed-updates main non-free contrib debian 8.x (jessie) 编辑/etc/apt/sources.list文件(需要使用sudo), 在文件最前面添加以下条目(操作前请做好相应备份) 1234deb http://mirrors.aliyun.com/debian/ jessie main non-free contribdeb http://mirrors.aliyun.com/debian/ jessie-proposed-updates main non-free contribdeb-src http://mirrors.aliyun.com/debian/ jessie main non-free contribdeb-src http://mirrors.aliyun.com/debian/ jessie-proposed-updates main non-free contrib debian 9.x (stretch) 编辑/etc/apt/sources.list文件(需要使用sudo), 在文件最前面添加以下条目(操作前请做好相应备份) 12345678deb http://mirrors.aliyun.com/debian/ stretch main non-free contribdeb-src http://mirrors.aliyun.com/debian/ stretch main non-free contribdeb http://mirrors.aliyun.com/debian-security stretch/updates maindeb-src http://mirrors.aliyun.com/debian-security stretch/updates maindeb http://mirrors.aliyun.com/debian/ stretch-updates main non-free contribdeb-src http://mirrors.aliyun.com/debian/ stretch-updates main non-free contribdeb http://mirrors.aliyun.com/debian/ stretch-backports main non-free contribdeb-src http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib 除了阿里云的源,还有一些其他的源可以参考: https://mirrors.tuna.tsinghua.edu.cn/help/debian http://mirrors.ustc.edu.cn/help/debian.html Ubuntu aptUbuntu,是一款基于 Debian Linux 的以桌面应用为主的操作系统,内容涵盖文字处理、电子邮件、软件开发工具和 Web 服务等,可供用户免费下载、使用和分享。 下面以阿里云的源为例: 用你熟悉的编辑器打开/etc/apt/sources.list,替换默认的http://archive.ubuntu.com/为mirrors.aliyun.com。 ubuntu 16.04 配置如下123456789101112131415deb http://mirrors.aliyun.com/ubuntu/ xenial maindeb-src http://mirrors.aliyun.com/ubuntu/ xenial maindeb http://mirrors.aliyun.com/ubuntu/ xenial-updates maindeb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates maindeb http://mirrors.aliyun.com/ubuntu/ xenial universedeb-src http://mirrors.aliyun.com/ubuntu/ xenial universedeb http://mirrors.aliyun.com/ubuntu/ xenial-updates universedeb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates universedeb http://mirrors.aliyun.com/ubuntu/ xenial-security maindeb-src http://mirrors.aliyun.com/ubuntu/ xenial-security maindeb http://mirrors.aliyun.com/ubuntu/ xenial-security universedeb-src http://mirrors.aliyun.com/ubuntu/ xenial-security universe ubuntu 18.04(bionic) 配置如下1234567891011121314deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse ubuntu 20.04(focal) 配置如下1234567891011121314deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse 除了阿里云的源,还有一些其他的源可以参考: https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu http://mirrors.ustc.edu.cn/help/ubuntu.html http://mirror.azure.cn/help/ubuntu.html HomebrewHomebrew 是一款自由及开放源代码的软件包管理系统,用以简化 macOS 系统上的软件安装过程。它拥有安装、卸载、更新、查看、搜索等很多实用的功能,通过简单的一条指令,就可以实现包管理,十分方便快捷。 下面以阿里云的源为例: 名称 说明 brew Homebrew 源代码仓库 homebrew-core Homebrew 核心源 homebrew-cask 提供 macOS 应用和大型二进制文件的安装 homebrew-bottles 预编译二进制软件包 Bash 终端配置 1234567891011# 替换brew.git:cd "$(brew --repo)"git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git# 替换homebrew-core.git:cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git# 应用生效brew update# 替换homebrew-bottles:echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.bash_profilesource ~/.bash_profile 恢复默认配置 出于某些场景, 可能需要回退到默认配置, 你可以通过下述方式回退到默认配置。 首先执行下述命令: 123456# 重置brew.git:cd "$(brew --repo)"git remote set-url origin https://github.com/Homebrew/brew.git# 重置homebrew-core.git:cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"git remote set-url origin https://github.com/Homebrew/homebrew-core.git 然后删掉 HOMEBREW_BOTTLE_DOMAIN 环境变量,将你终端文件~/.bash_profile中HOMEBREW_BOTTLE_DOMAIN行删掉, 并执行source ~/.bash_profile。 除了阿里云的源,还有一些其他的源可以参考: https://mirrors.tuna.tsinghua.edu.cn/help/homebrew http://mirrors.ustc.edu.cn/help/brew.git.html iOSCocoaPods 是一个 Cocoa 和 Cocoa Touch 框架的依赖管理器,具体原理和 Homebrew 有点类似,都是从 GitHub 下载索引,然后根据索引下载依赖的源代码。 下面以清华大学的源为例: 对于旧版的 CocoaPods 可以使用如下方法使用 tuna 的镜像: 123$ pod repo remove master$ pod repo add master https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git$ pod repo update 新版的 CocoaPods 不允许用pod repo add直接添加master库了,但是依然可以: 123$ cd ~/.cocoapods/repos $ pod repo remove master$ git clone https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git master 最后进入自己的工程,在自己工程的podFile第一行加上: 1source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git' 注意: 从1.7.2开始,已经完全切到CDN上了。1.8以上甚至把CDN作为默认源使用,在Podfile最上面添加即可。 1source 'https://cdn.cocoapods.org/' 除了清华大学的源,还有一些其他的源可以参考: https://gitclub.cn/CocoaPods/Specs.git http://git.oschina.net/akuandev/Specs.git Git 以下内容摘自原仓库的说明文档,推荐优先参考原文档。 fgit是一个可以无缝替换git命令行的工具,使用优化线路为使用github.com加速。 官方介绍的特点: 目前实测git clone速度超过15MB/S 支持github.com私有库,也支持push 两种工作模式:镜像(反向代理)模式和HTTP代理模式,都是实时连接github.com,不是缓存 支持包括clone/push/pull/fetch在内的各种git命令,兼容git命令行参数,可以用来无缝替换git命令行 只针对github.com加速,不干扰对非github.com库的使用 使用go语言开发,不是shell脚本或.bat,跨平台。Windows 10、Linux (Unbuntu)、Mac (x86)都实测通过 安装 Mac 12sudo curl -L https://github.com/fastgh/fgit/releases/download/v1.0.0/fgit.darwin -o /usr/local/bin/fgitsudo chmod +x /usr/local/bin/fgit Linux 12sudo curl -L https://github.com/fastgh/fgit/releases/download/v1.0.0/fgit.linux -o /usr/local/bin/fgitsudo chmod +x /usr/local/bin/fgit 使用 和常规的git命令行几乎相同,支持各种命令行参数 对于公共库,clone/pull/fetch时默认使用镜像模式,基于安全考虑,镜像模式不支持push以及私有库 镜像模式例如:fgit clone https://github.com/spring-projects/spring-boot.git --depth=1 两种情况会判定为需要push或者私有库,此时会使用HTTP代理模式,代理模式比镜像模式安全 push URL中包含用户名,那么会被判定为需要push或者私有库。 对于clone命令,URL是从clone的URL中解析得到,对于其它git命令,则使用git remote -v得到 代理模式例如: fgit push origin master fgit clone https://fastgh@github.com/fastgh/fgit.git 也可以通过--use-proxy选项强制走HTTP代理模式,例如: fgit --use-proxy clone https://github.com/fastgh/fgit.git 代理服务器的优化线路成本高,所以大家尽量使用镜像模式,以节省服务器带宽资源 其它功能: 可以打开调试开关,看一看fgit的工作过程:下载服务器列表 –> 设置镜像或代理 –> 执行git –> 回复镜像或代理 fgit --debug clone https://github.com/fastgh/fgit.git fgit首次运行时,会在用户主目录下生成一个配置文件.fgit.json,包含服务器地址等信息,必要时可以通过设置这个文件选择接入其它服务方,或指定镜像服务器或代理服务器]]></content>
<categories>
<category>Productivity</category>
</categories>
<tags>
<tag>Untagged</tag>
</tags>
</entry>
<entry>
<title><![CDATA[加速 Hyperledger Fabric 的 Docker 镜像构建过程]]></title>
<url>%2F2020%2F08%2Fspeed-up-hyperledger-fabric-build-process%2F</url>
<content type="text"><![CDATA[Hyperledger Fabric 从v2.0开始,全面将docker基础镜像替换成了体积更小、潜在安全风险更少、更加轻量的Alpine Linux,从而使得make docker出来的各种镜像的体积几乎都缩小为原来的一半,确实能够节省更多的硬盘空间。但是,由于众所周知的原因,对于生在红旗下,长在新中国的程序员们,第一次在Fabric项目下构建docker镜像时,依然是奇慢无比,屡次超时。 那么这个问题怎么解决呢? 分析速度的瓶颈首先通过分析速度慢的原因,找出可以优化的点。通过分析make docker命令,大概过程是这样的:首先是docker会从docker registry pull Alpine作为基础镜像,然后使用apk add --no-cache xxx安装一些软件,最后通过make命令build出Peer、Order以及其他的tools的二进制包。到这里相信国内的各种奇人义士已经磨刀霍霍,迫不及待的开始替换各种国内的mirror了。 下面的各种资源都来源于网上各位好心人的分享。 加速docker pull过程首先从网上搜索国内的docker registry源,然后修改docker的配置并重启docker。在这里我比较推荐使用自己专有的免费的阿里云镜像加速器,目前使用一直比较平稳。 获取镜像加速url 注册一个阿里云账号并登录,在产品与服务中搜索容器镜像服务,跟随引导完成必要的一些步骤,然后来到这个页面:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors,就可以看到自己专有的加速器地址了。 给docker客户端配置镜像加速器 如果没有/etc/docker/daemon.json文件,则可以直接通过下面的命令完成配置;如果该文件已经存在了,则选择自己熟悉的文本编辑器编辑该文件,添加加速器地址的配置。如果你使用的带界面的客户端,也可以在Preferences... -> Docker Engine显示的编辑器中添加相应的配置。 12345678sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-'EOF'{ "registry-mirrors": ["https://xxx.mirror.aliyuncs.com"]}EOFsudo systemctl daemon-reloadsudo systemctl restart docker 注意:将https://xxx.mirror.aliyuncs.com替换成你自己的镜像加速器地址。再次提醒,配置完成后,重启docker之后才会生效。 至此我们完成了docker pull镜像阶段的加速,接下来我们加速在Alpine中安装软件的过程。 加速Alpine安装软件的过程在docker pull下来的Alpine镜像中,使用apk安装软件时默认使用的是国外的源,速度比较慢。这里我们把源替换为国内的源,可以大大节省时间。下面以Hyperledger fabric peer的Dockerfile为例,替换为阿里的源: 12345678910111213141516171819...FROM alpine:${ALPINE_VER} as peer-base# 使用下面这行命令完成源的替换RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositoriesRUN apk add --no-cache tzdataFROM golang:${GO_VER}-alpine${ALPINE_VER} as golang# 使用下面这行命令完成源的替换RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositoriesRUN apk add --no-cache \ bash \ gcc \ git \ make \ musl-devADD . $GOPATH/src/github.com/hyperledger/fabric... 另外也可以替换为中科大的源:dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn,或者清华的源:dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn,或者其他的源。 注意:这里对于使用multi-stage的Dockerfile,如果不同的stage使用的是不同的基础镜像,则都需要替换源。 到这里,当docker使用Alpine作为基础镜像时,安装依赖软件的过程就会快很多。]]></content>
<categories>
<category>Productivity</category>
</categories>
<tags>
<tag>Blockchain</tag>
<tag>Docker</tag>
<tag>Hyperledger-Fabric</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Mac上的JDK多版本管理]]></title>
<url>%2F2019%2F09%2Fmanage-multiple-java-versions-on-mac%2F</url>
<content type="text"><![CDATA[我的Mac上已经有一个JDK8的版本了,这不JDK13刚发布(2019-09-17),想快速的尝一尝鲜,就得安装多个版本的JDK了。这个对Node、Ruby、Python的使用者来说,已经不是个什么新鲜话题了,但是对于Java的使用者来说,似乎没有那么多的人受到过多版本的折磨(我是通过GitHub上nvm、rbenv、pyenv、jenv的Star数量臆测出这个结论的 :P)。 nvm rbenv pyenv jenv 安装JDK 13通过Homebrew 安装JDK 13,可以先通过brew cask info java查看目前Java的版本: 12345678java: 13,33:5b8a42f3905b406298b72d750b6919f6https://openjdk.java.net/Not installedFrom: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb==> NameOpenJDK Java Development Kit==> Artifactsjdk-13.jdk -> /Library/Java/JavaVirtualMachines/openjdk-13.jdk (Generic Artifact) 这里显示的是JDK13,正好是我想要安装的JDK版本,如果不是你想要的版本可以自己搜索相应的 Homebrew Tap。接下来直接安装: 123456$ brew cask install java$ java -versionopenjdk version "13" 2019-09-17OpenJDK Runtime Environment (build 13+33)OpenJDK 64-Bit Server VM (build 13+33, mixed mode, sharing) 这就说明JDK13已经安装好了。 但是另一个问题来了,我电脑上原来安装的JDK8去哪呢?我如何在不同的版本中随意切换呢?比如像Node的nvm,Ruby的rvm,Python的pyenv等。答案是我们可以通过jenv来实现相同的效果。 安装 jEnv 安装 jEnv 12$ brew install jenv$ exec $SHELL -l 安装完成之后,然后检查是否安装成功。 1234567$ jenv doctor[OK] No JAVA_HOME set[ERROR] Java binary in path is not in the jenv shims.[ERROR] Please check your path, or try using /path/to/java/home is not a valid path to java installation. PATH : /usr/local/Cellar/jenv/0.5.2/libexec/libexec:/Users/xxx/.cargo/bin:/Users/xxx/.pyenv/shims:/Users/username/.pyenv:/Users/xxx/.nvm/versions/node/v8.11.4/bin:/Users/xxx/bin:/usr/local/bin:/Users/xxx/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/xxx/Documents/Projects/golang/bin[ERROR] Jenv is not loaded in your zsh[ERROR] To fix : cat eval "$(jenv init -)" >> /Users/xxx/.zshrc 在这里如果按照提示执行cat eval "$(jenv init -)" >> /Users/xxx/.zshrc:可能会得到如下错误: 12345678910111213141516171819202122cat eval "$(jenv init -)" >> /Users/xxx/.zshrccat: eval: No such file or directorycat: export PATH="/Users/xxx/.jenv/shims:${PATH}"export JENV_SHELL=zshexport JENV_LOADED=1unset JAVA_HOMEsource '/usr/local/Cellar/jenv/0.5.2/libexec/libexec/../completions/jenv.zsh'jenv rehash 2>/dev/nulljenv() { typeset command command="$1" if [ "$#" -gt 0 ]; then shift fi case "$command" in enable-plugin|rehash|shell|shell-options) eval `jenv "sh-$command" "$@"`;; *) command jenv "$command" "$@";; esac}: No such file or directory 经过一番搜索,得到如下的解决办法,主要就是将cat替换为echo,这里我已经给jEnv提了个PR,以消除这个干扰。 Bash用户 123$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile$ echo 'eval "$(jenv init -)"' >> ~/.bash_profile$ exec $SHELL -l Zsh用户 123$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc$ echo 'eval "$(jenv init -)"' >> ~/.zshrc$ exec $SHELL -l 然后再次执行jenv doctor,得到如下信息: 12345[OK] No JAVA_HOME set[ERROR] Java binary in path is not in the jenv shims.[ERROR] Please check your path, or try using /path/to/java/home is not a valid path to java installation. PATH : /usr/local/Cellar/jenv/0.5.2/libexec/libexec:/Users/xxx/.jenv/shims:/Users/xxx/.cargo/bin:/Users/xxx/.pyenv/shims:/Users/username/.pyenv:/Users/xxx/.cargo/bin:/Users/xxx/.pyenv/shims:/Users/username/.pyenv:/Users/xxx/.nvm/versions/node/v8.11.4/bin:/Users/xxx/bin:/usr/local/bin:/Users/xxx/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/xxx/Documents/Projects/golang/bin:/Users/xxx/Documents/Projects/golang/bin[OK] Jenv is correctly loaded 为了能够正确的设置JAVA_HOME,最好开启export插件: 12$ jenv enable-plugin export$ exec $SHELL -l 如果你是Maven用户,建议开启Maven插件,使得Maven能够使用正确的JDK版本: 12$ jenv enable-plugin maven$ exec $SHELL -l 管理不同版本的JDK添加JDK添加最新安装的JDK: 1$ jenv add $(/usr/libexec/java_home) 如果/usr/libexec/java_home所指的位置不是你想要的,也可以手动指定目录: 1$ jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/ 查看JDK版本执行jenv versions: 123456 system* 1.8 (set by JENV_VERSION environment variable) 1.8.0.191 13 openjdk64-13 oracle64-1.8.0.191 默认情况下,system指的是系统中安装的最新版本的JDK。 切换JDK版本 Global设置全局模式下的JDK版本: 123$ jenv global 13$ exec $SHELL -l $ java -version Local在某个工作目录下设置JDK版本,会在当前目录下创建一个.java-version的文件: 123$ jenv local 1.8$ exec $SHELL -l $ java -version Shell设置当前Shell session中的JDK版本: 12$ jenv shell 1.8$ java -version 参考链接 http://www.jenv.be/ https://github.com/jenv/jenv https://emcorrales.com/blog/install-oracle-jdk-macos-homebrew]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>JDK</tag>
<tag>jEnv</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JVM 执行 Java 程序时的内存区域划分]]></title>
<url>%2F2019%2F09%2Fjava-virtual-machine-runtime-memory-layout%2F</url>
<content type="text"><![CDATA[在学习 Java 虚拟机(后面简称:JVM)中的垃圾回收机制(GC)之前,先需要了解 在 JVM 中的 Java 程序(class 文件)加载到内存之后到底是怎么存的。在阅读了 JVM规范和周志明的《深入理解Java虚拟机(第2版)》之后,总结一下JVM中的内存划分以及各个区域的作用。 在JVM规范中定义了5种运行时的数据区域:程序计数器(Program Counter Register)、Java虚拟机栈(JVM Stacks)、堆(Heap)、方法区(Method Area)、运行时常量池(Runtime Constant Pool)、本地方法栈(Native Method Stack)。在周志明的书中还提到了直接内存(Direct Memory),它并不是JVM运行时数据区域的一部分,在JVM的规范中也没有相关的定义。下面分别来说明各自的用途。 程序计数器程序计数器,也叫PC Register。它的用途很单一,但是却是很多功能的基础。如果线程当前执行的是Native方法,那么寄存器里的值就是Undefined;如果线程当前执行的是非Native方法,那么寄存器里的值就是当前执行的JVM字节码指令的地址。像我们常用的分支、循环、跳转、异常处理、线程恢复等都依赖于它。 由于JVM支持多个线程同时执行,所以每个线程都有一个独立的程序计数器,各个线程互不影响,这类内存区域也称之为线程私有的。 Java虚拟机栈虚拟机栈也是线程私有的,随着一个线程的创建而创建,主要用来存储栈帧(Stack Frame)。什么是栈帧呢?在Java中,每个方法在执行时就会先创建一个栈帧并放入虚拟机栈中,在方法执行完毕时再从虚拟机栈中移除该栈帧。它主要用来存储局部变量表、操作数栈、动态链接、方法出口等信息。我们常说的堆(Heap)和栈(Stack)中的栈,指的就是虚拟机栈。 在JVM规范中并没有对虚拟机栈空间的大小做限制,可以设置为固定大小的,也可以设置为可扩展的。但是在规范中定义了两种异常情况: 如果计算时请求的栈空间大于虚拟机栈的最大值,则会抛出StackOverflowError异常; 如果虚拟机栈设置为可扩展的并且无法再获取更多内存时,则会抛出OutOfMemoryError异常。 堆相比而言,堆在JVM管理的内存区域中属于最大的一块,随着虚拟机的启动而创建,用来存储所有的class实例和数组,所有线程共享这一区域,该区域也是垃圾回收的主要区域。虽然JVM规范中说所有的对象实例都在该区域分配空间,但是随着JIT技术的逐步发展,这一说法也不严谨了。 堆空间的大小也可以设置为固定大小,或者可扩展的。但不管是何种方式,规范中还是定义了一种异常场景: 如果计算需要更多的堆空间而无法满足时,则会抛出OutOfMemoryError异常。 方法区方法区和堆一样,也是随着虚拟机启动而创建,所有线程共享,主要用来存储被JVM加载的类信息、常量、静态变量等信息。 JVM规范中并未严格要求要对该区域进行垃圾回收,但是HotSpot虚拟机在垃圾回收的时候还是会考虑该区域,在分代垃圾回收中所说的“永久代”指的就是方法区。方法区的大小也可以设置为固定大小,或者可扩展的。但不管是何种方式,规范中还是定义了一种异常场景: 如果计算需要更多的方法区空间而无法满足时,则会抛出OutOfMemoryError异常。 运行时常量池运行时常量池是方法区的一部分,用于存储编译期生成的各种字面量和符号引用。在Java中并不要求常量一定只有编译期才能产生,运行期间也可能将新的常量放入池中,例如String类的intern()方法。 每个运行时常量池都是随着一个类或者接口的创建而创建的。在规范中定义了一种异常场景: 在创建一个类或者接口时,如果运行时的常量池无法分配到足够的空间时,则会抛出OutOfMemoryError异常。 本地方法栈本地方法栈和虚拟机栈类似,也是线程私有的,随着一个线程的创建而创建,只不过虚拟机栈是用来服务Java方法调用,而本地方法栈是用来服务本地方法调用的。 在JVM规范中并没有对本地方法栈空间的大小做限制,可以设置为固定大小的,也可以设置为可扩展的。在规范中也定义了两种异常情况: 如果计算时请求的栈空间大于本地方法栈的最大值,则会抛出StackOverflowError异常; 如果本地方法栈设置为可扩展的并且无法再获取更多内存时,则会抛出OutOfMemoryError异常。 直接内存*直接内存不受虚拟机参数的控制,在NIO中有一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以通过Native方法在堆外分配内存,然后通过DirectByteBuffer对象来引用这块内存。因为避免了在Java堆和Native堆之间来回复制数据,从而在某些场景中能够得到性能的提升。一旦使用的直接内存超过了物理内存的总和,则会抛出OutOfMemoryError异常。 参考链接 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5 https://book.douban.com/subject/24722612/]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>JVM</tag>
</tags>
</entry>
<entry>
<title><![CDATA[在MySQL中使用LAST_INSERT_ID获取唯一自增序列]]></title>
<url>%2F2019%2F07%2Fimplement-sequence-with-mysql-last-insert-id%2F</url>
<content type="text"><![CDATA[一般如果遇到生成全局唯一的自增ID的需求时,往往第一反应都是直接利用数据的Sequence对象,简单,直接了当。但是MySQL偏偏不支持Sequence对象,那我们该如何是好呢? 什么是SequenceSequence也叫做序列,一般用做表的主键,或者一些项目的编号等。一般具有以下几个特点: 全表唯一 自增 不一定严格连续(中间由于事务的回滚可能会出现洞,比如1,2,3,5,6) 在常见的几种数据库中,Oracle、SQL Server都内置有Sequence对象,具体用法就不在此赘述了。在本文中我们来讨论一下如何在原生不支持Sequence的MySQL(目前最新的大版本为8.0)中模拟出Sequence的效果。 如何在MySQL中模拟SequenceMySQL中的auto_increment一般是用来生成表的主键,本身能够生成自增的唯一ID,但是一张表只能有一个列带有auto_increment属性。在实际项目中,我们可能需要不止一种序列号,比如项目编号(PROJ-001,PROJ-002…)、发票编号(INV-0001,INV-0002…),订单编号(ORD-0001,ORD-0002…)等等,下面将通过auto_increment和LAST_INSERT_ID相结合实现该功能。 LAST_INSERT_ID函数该函数有两种形式:LAST_INSERT_ID(), LAST_INSERT_ID(expr)。无参的形式会返回最近一次执行INSERT语句时auto_increment的值;带expr的形式会返回表达式的值,并且该值会被记住,在下一次调用LAST_INSERT_ID()时也返回该值。下面我们来看一个例子。 首先创建一张表: 1234CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL); 然后插入两条数据: 1234INSERT INTO user(name) VALUES('张三');INSERT INTO user(name) VALUES('李四');SELECT LAST_INSERT_ID(); 此时得到的结果是2。 注意:如果是一条语句插入多条值,则返回的是插入第一条时自动生成的ID,而不是最后一条的。 比如我们再插入三条数据,不过换个写法: 123456INSERT INTO user(name) VALUES('王五'), ('赵六'), ('郑七');SELECT LAST_INSERT_ID(); 此时得到的结果是3,而不是5。 获取自增序列在实际的项目中我们完全可以换个方式,避免上面👆的情况,作为一个程序员,何必没有困难,制造困难为难自己呢?接着看下一个更加通用的例子。 创建另一张表并初始化数据: 12345678CREATE TABLE sequence ( id INT AUTO_INCREMENT PRIMARY KEY, seq_type VARCHAR(50) NOT NULL, year INT NOT NULL, current_val BIGINT NOT NULL);INSERT INTO sequence(seq_type, year, current_val) VALUES('INVOICE', 2019, 0); 每次在获取current_val之前,先通过LAST_INSERT_ID(current_val + 1)更新: 12345UPDATE sequence SET current_val = LAST_INSERT_ID(current_val + 1)WHERE seq_type = 'INVOICE' AND year = 2019;SELECT LAST_INSERT_ID(); 这样每次都能获取自增之后的值了,但是也有例外的情况。比如两个人同时在获取新的值,A先做了update操作,然后B也做了update操作,然后A的操作由于某种原因回滚了,B的操作成功了,此时序列中间就会出现一个洞。虽然不是严格连续的,但是在大多数业务场景中,已经满足要求了。 还需要注意的是,如果seq_type或者year条件不满足,那么这里的SELECT LAST_INSERT_ID();就会始终返回上一次的值,可能会导致意想不到的的错误。 LAST_INSERT_ID() vs. MAX()LAST_INSERT_ID()是以数据库连接为基础的,即使有多个人同时通过多个连接获取Sequence也不会有问题,每个客户端会获取到属于他自己的序列号,不用担心会受到其他客户端的影响,或者影响其他客户端。在这种情况下,MAX()恐怕就不能正常工作了。 参考链接 https://www.percona.com/community-blog/2018/10/12/generating-identifiers-auto_increment-sequence/ https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id http://www.mysqltutorial.org/mysql-last_insert_id.aspx]]></content>
<categories>
<category>Database</category>
</categories>
<tags>
<tag>MySQL</tag>
</tags>
</entry>
<entry>
<title></title>
<url>%2F404%2F404.html</url>
<content type="text"><![CDATA[]]></content>
</entry>
<entry>
<title><![CDATA[关于我]]></title>
<url>%2Fabout%2Findex.html</url>
<content type="text"><![CDATA[简历持续更新中… Hongwei Peng(彭洪伟),pengisgood(at)gmail(dot)comFull-stack developer | Open source contributor | Lifelong learner 简历 LinkedIn: https://www.linkedin.com/in/max-hongwei-peng Stackoverflow: https://stackoverflow.com/story/maxpeng 出版的书籍AngularJS深度剖析与最佳实践 开源贡献下面是我贡献过代码或文档的开源项目列表: 2020https://github.com/ipfs/go-ipfs-api https://github.com/ipfs-shipyard/ipfs-primer https://github.com/hyperledger/fabric https://github.com/hyperledger/fabric-docs-i18n 2019https://github.com/justauth/JustAuth https://github.com/jenv/jenv 2018https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster https://github.com/hyperledger/cello 2017https://github.com/OpenLMIS/openlmis-stockmanagement https://github.com/OpenLMIS/openlmis-referencedata https://github.com/OpenLMIS/openlmis-ui-layout https://github.com/OpenLMIS/openlmis-stockmanagement-ui https://github.com/OpenLMIS/openlmis-ui-components https://github.com/OpenLMIS/openlmis-requisition-refUI https://github.com/OpenLMIS/dev-ui 2014https://github.com/jingyanjiaoliu/angular-guide-zh https://github.com/sbt/website]]></content>
</entry>
<entry>
<title><![CDATA[Categories]]></title>
<url>%2Fcategories%2Findex.html</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[Tags]]></title>
<url>%2Ftags%2Findex.html</url>
<content type="text"></content>
</entry>
</search>