跳至内容

ADS中用W-Element模拟方形同轴线的RLGC建模方法

此文为记录ADS方形同轴线RLGC建模从报错到功能正常调用的过程,最终解决方案见文末。

我们知道ADS中只有圆柱同轴线符号,没有其他结构的同轴线,因此我们在ADS仿真时,为了达到同样的阻抗,需要使用LineCalc计算实际阻抗在ADS中的同轴尺寸。

那有没有办法不用圆柱同轴符号呢?我们可以尝试一下用RLGC电路来模拟符号的方法。

比如使用W_Element符号,该符号用于通过 RLGC 参数配置多导体输电线路。

为了使用W_Element,我们需要准备RLGC文件。

首先记住一定要打开一个workspace后再打开这个窗口,否则界面是这样的。

打开workspace后command line窗口就变成这样了。

点击Apply运行文件,点击保存按钮保存ael文件到指定位置。

由于我们准备的代码是静默生成文件,所以控制台无输出。

代码如下——


decl pi; pi = 3.14159265358979;
decl c; c = 299792458;
decl mu0; mu0 = 1.2566370614e-6;
decl eps0; eps0 = 8.854187817e-12;
decl a; a = 0.35;
decl b; b = 0.64;
decl eps_r; eps_r = 1.0006;
decl tand; tand = 0.002;
decl sigma; sigma = 5.96e7;
decl start_freq; start_freq = 1e6;
decl stop_freq; stop_freq = 150e6;
decl num_points; num_points = 150;
decl ratio; ratio = a / b;
decl Z0; Z0 = (47.086 / sqrt(eps_r)) \* (1.0 - ratio) / (0.279 + 0.721
\* ratio);
decl v; v = c / sqrt(eps_r);
decl L0; L0 = Z0 / v;
decl C0; C0 = 1.0 / (Z0 \* v);
decl df; df = (stop_freq - start_freq) / (num_points - 1);
decl fid; fid = fopen(\"square_coax.rlgc\", \"w\");
fprintf(fid, \"\* RLGC File\\n\");
fprintf(fid, \"\* Z0=%.2f\\n\", Z0);
fprintf(fid, \"\\n\");
decl Rdc; Rdc = 1.0/(sigma\*a\*a);
fprintf(fid, \"0.000000e+00 %.6e %.6e %.6e 0.000000e+00\\n\", Rdc, L0,
C0);
decl i; i = 1;
decl f;
decl Rs;
decl R;
decl G;
while(i \<= num_points)
{
f = start_freq + (i-1)\*df;
Rs = sqrt(pi \* f \* mu0 / sigma);
R = Rs \* (1.0/(2.0\*a) + 1.0/(2.0\*b));
G = 2.0 \* pi \* f \* C0 \* tand;
fprintf(fid, \"%.6e %.6e %.6e %.6e %.6e\\n\", f, R, L0, C0, G);
i = i + 1;
}
fclose(fid);

可以看到已经生成了RLGC文件。

但是在如下的电路中运行报错了。

W_Element1的设置如图所示。截图是错的,输入路径不能带双引号,否则会找不到文件。建议将rlgc文件放在项目文件夹,默认文件夹层级是“.\Megawave_wrk\data”。

为了验证RLGC文件是否正常,以及确认W_Element1是否设置错误,我们搭建这样一个电路,频率范围设置与RLGC代码中的一致。

  1. 新建一个 空白原理图

  2. 只放这 4 个元件:

    • PORT 两个

    • W-ELEMENT1 一个

    • SP1 (仿真控制器)一个

  3. 连线:
    PORT1 → W-ELEMENT1 → PORT2
    地线全部默认

确认一下设置有没有问题。

运行提示如图所示,依然无法读取RLGC文件。

查阅官方文档寻找原因,得出以下结论。

我现在用的是 Model_type=0(静态模型),但我的 RLGC文件是按频率分段的格式写的,这完全不匹配!

一、官方文档明确说明:两种模式的文件格式完全不同

1. Model_type=0(静态模型)

  • 只能用 RLGCfile 参数

  • 文件里没有频率点

  • 格式:[N] L11 L21 L22 C11 C21 C22 Rdc11 Rdc21 Rdc22 Gdc11 Gdc21
    Gdc22 Rs11 Rs21 Rs22 Gd11 Gd21 Gd22

  • 我的文件里写了频率、1MHz、150MHz 这些,Model_type=0 会直接当成无效数据,报读取错误。

2. Model_type=1(频率相关模型)

  • 不能用 RLGCfile 参数

  • 必须用 Lfile / Cfile / Rfile / Gfile 分别定义每个矩阵的文件

  • 每个文件的格式才是:[点数] 频率 L11 L21 L22 ...

  • 我现在的文件格式,只能给 Model_type=1 用,给 Model_type=0 用必然报错!

那我们试着把 W-Element 改成频率相关模式。因为 Model_type=1 要求每个矩阵单独一个文件,把我的RLGC 文件拆成 4 个独立文件试试。

编写一下生成四个独立文件的代码——

decl pi; pi = 3.14159265358979;
decl c; c = 299792458;
decl mu0; mu0 = 1.2566370614e-6;
decl a; a = 0.35;
decl b; b = 0.64;
decl eps_r; eps_r = 1.0006;
decl tand; tand = 0.002;
decl sigma; sigma = 5.96e7;
decl start_freq; start_freq = 1e6;
decl stop_freq; stop_freq = 150e6;
decl num_points; num_points = 150;
decl ratio; ratio = a / b;
decl Z0; Z0 = (47.086 / sqrt(eps_r)) \* (1.0 - ratio) / (0.279 + 0.721
\* ratio);
decl v; v = c / sqrt(eps_r);
decl L0; L0 = Z0 / v;
decl C0; C0 = 1.0 / (Z0 \* v);
decl df; df = (stop_freq - start_freq) / (num_points - 1);
decl fidL; fidL = fopen(\"L.rlgc\", \"w\");
decl fidC; fidC = fopen(\"C.rlgc\", \"w\");
decl fidR; fidR = fopen(\"R.rlgc\", \"w\");
decl fidG; fidG = fopen(\"G.rlgc\", \"w\");
// 写入点数(含DC点,共151个)
fprintf(fidL, \"%d\\n\", num_points + 1);
fprintf(fidC, \"%d\\n\", num_points + 1);
fprintf(fidR, \"%d\\n\", num_points + 1);
fprintf(fidG, \"%d\\n\", num_points + 1);
// DC点
decl Rdc; Rdc = 1.0/(sigma\*a\*a);
fprintf(fidL, \"%.6e %.6e\\n\", 0.0, L0);
fprintf(fidC, \"%.6e %.6e\\n\", 0.0, C0);
fprintf(fidR, \"%.6e %.6e\\n\", 0.0, Rdc);
fprintf(fidG, \"%.6e %.6e\\n\", 0.0, 0.0);
decl i; i = 1;
decl f;
decl Rs;
decl R;
decl G;
while(i \<= num_points)
{
f = start_freq + (i-1)\*df;
Rs = sqrt(pi \* f \* mu0 / sigma);
R = Rs \* (1.0/(2.0\*a) + 1.0/(2.0\*b));
G = 2.0 \* pi \* f \* C0 \* tand;
fprintf(fidL, \"%.6e %.6e\\n\", f, L0);
fprintf(fidC, \"%.6e %.6e\\n\", f, C0);
fprintf(fidR, \"%.6e %.6e\\n\", f, R);
fprintf(fidG, \"%.6e %.6e\\n\", f, G);
i = i + 1;
}
fclose(fidL);
fclose(fidC);
fclose(fidR);
fclose(fidG);

运行后,我的工程目录会生成这 4 个文件,然后 W-Element1这样设置(Model_type=1)。

然后我们发现报了这个奇怪的错误。

然后人工智能给了我提供奇怪的提示——

官方文档的隐藏要求:Model_type=1 的文件格式

我再仔细看了一遍我贴的帮助文档,里面写了关键一句:

For Model_type=1, the data consists of pairs, where each pair is formed
by a frequency value followed by the matrix entries at that frequency.

对 N=1 的传输线,L/C/R/G 都是 **1x1 矩阵**,所以每一行的数据格式是:

频率值 矩阵元素

但还有一个 **关键细节**,官方文档没写死,但 ADS 强制要求:

**频率值必须是整数,不能是科学计数法!**

ADS 的解析器对科学计数法(如
1.000000e+06)有兼容性问题,我文件里的频率用 1.000000e+06
写,它会解析失败,直接报 Error reading the W_Element data file。

我们现在将频率全部改成普通数字。

decl pi; pi = 3.14159265358979;
decl c; c = 299792458;
decl mu0; mu0 = 1.2566370614e-6;
decl a; a = 0.35;
decl b; b = 0.64;
decl eps_r; eps_r = 1.0006;
decl tand; tand = 0.002;
decl sigma; sigma = 5.96e7;
decl start_freq; start_freq = 1e6;
decl stop_freq; stop_freq = 150e6;
decl num_points; num_points = 150;
decl ratio; ratio = a / b;
decl Z0; Z0 = (47.086 / sqrt(eps_r)) \* (1.0 - ratio) / (0.279 + 0.721
\* ratio);
decl v; v = c / sqrt(eps_r);
decl L0; L0 = Z0 / v;
decl C0; C0 = 1.0 / (Z0 \* v);
decl df; df = (stop_freq - start_freq) / (num_points - 1);
decl fidL; fidL = fopen(\"L.rlgc\", \"w\");
decl fidC; fidC = fopen(\"C.rlgc\", \"w\");
decl fidR; fidR = fopen(\"R.rlgc\", \"w\");
decl fidG; fidG = fopen(\"G.rlgc\", \"w\");
fprintf(fidL, \"%d\\n\", num_points + 1);
fprintf(fidC, \"%d\\n\", num_points + 1);
fprintf(fidR, \"%d\\n\", num_points + 1);
fprintf(fidG, \"%d\\n\", num_points + 1);
decl Rdc; Rdc = 1.0/(sigma\*a\*a);
fprintf(fidL, \"0 %.6e\\n\", L0);
fprintf(fidC, \"0 %.6e\\n\", C0);
fprintf(fidR, \"0 %.6e\\n\", Rdc);
fprintf(fidG, \"0 %.6e\\n\", 0.0);
decl i; i = 1;
decl f;
decl Rs;
decl R;
decl G;
while(i \<= num_points)
{
f = start_freq + (i-1)\*df;
Rs = sqrt(pi \* f \* mu0 / sigma);
R = Rs \* (1.0/(2.0\*a) + 1.0/(2.0\*b));
G = 2.0 \* pi \* f \* C0 \* tand;
fprintf(fidL, \"%.0f %.6e\\n\", f, L0);
fprintf(fidC, \"%.0f %.6e\\n\", f, C0);
fprintf(fidR, \"%.0f %.6e\\n\", f, R);
fprintf(fidG, \"%.0f %.6e\\n\", f, G);
i = i + 1;
}
fclose(fidL);
fclose(fidC);
fclose(fidR);
fclose(fidG);

运行提示依然如上图所示。

我们再翻一下help文件。

For Model_type=1, the data consists of pairs, where each pair is formed
by a frequency value followed by the matrix entries at that frequency.

For N signal lines, the matrix entries are:

L11 L12 … L1N L21 L22 … LNN

Similarly for R, C, G files.

……

When N=1, the matrices are scalar (1×1).

So each line is:

frequency value

……

NOTE:

The tabular model (Model_type=1) is intended for coupled lines (N ≥ 2).

For a single line (N=1), use the static RLGC model (Model_type=0).

N=1 不能用 Model_type=1,只能用 Model_type=0。

放弃 Model_type=1,回到 Model_type=0(static),只用 1 个 RLGC 文件。

我们写这样一段.ael代码,生成文件名static的RLGC文件。

decl pi; pi = 3.14159265358979;
decl c; c = 299792458;
decl mu0; mu0 = 4 \* pi \* 1e-6;
decl a; a = 0.35;
decl b; b = 0.64;
decl eps_r; eps_r = 1.0006;
decl tand; tand = 0.002;
decl sigma; sigma = 5.96e7;
decl ratio; ratio = a / b;
decl Z0; Z0 = (47.086 / sqrt(eps_r)) \* (1.0 - ratio) / (0.279 + 0.721
\* ratio);
decl v; v = c / sqrt(eps_r);
decl L0; L0 = Z0 / v;
decl C0; C0 = 1.0 / (Z0 \* v);
decl Rdc; Rdc = 1.0/(sigma\*a\*a);
decl Gdc; Gdc = 0.0;
// 生成 STATIC 格式 RLGC 文件(N=1 专用)
decl fid; fid = fopen(\"static.rlgc\", \"w\");
fprintf(fid, \"1\\n\"); // N=1
fprintf(fid, \"%.6e \", L0); // L11
fprintf(fid, \"%.6e \", C0); // C11
fprintf(fid, \"%.6e \", Rdc); // Rdc11
fprintf(fid, \"%.6e \", Gdc); // Gdc11
fprintf(fid, \"0.0 \"); // Rs11
fprintf(fid, \"0.0\\n\"); // Gd11
fclose(fid);

然后我们试一下,完美。

结果一模一样。

现在我们回过头来说明一下代码的计算过程:

  1. 基础物理常数
  • pi → 圆周率

  • c = 299792458 → 真空中光速

  • mu0 = 4πe-7 → 真空磁导率

这些是传输线计算的基础常数。

  1. 传输线几何参数

我仿真的是 方形同轴线

  • a = 0.35:内导体半边长

  • b = 0.64:外导体内半边长

  1. 半径比 ratio

ratio = a / b

用于后面的阻抗经验公式。

  1. 核心公式:方形同轴线特性阻抗 Z0

Z0 = (47.086 / sqrt(eps_r)) * (1 - ratio) / (0.279 + 0.721 * ratio)

这是 ADS 官方方形同轴线阻抗经验公式

  • 47.086 → 固定系数

  • sqrt (eps_r) → 介质影响

  • (1-ratio) → 几何尺寸影响

  • 分母是拟合系数

输出:Z0 ≈ 31.68 Ω

  1. 相速度 v

v = c / sqrt(eps_r)

传输线中电磁波速度,永远比光速慢,由介质决定。

  1. 单位长度电感 L0

L0 = Z0 / v

传输线理论基础公式:

Z0 = sqrt(L0/C0)

v = 1/sqrt(L0*C0)

推导得到:

L0 = Z0 / v

  1. 单位长度电容 C0

C0 = 1 / (Z0 * v)

同样由传输线基础公式推导而来。

  1. 直流电阻 Rdc

Rdc = 1 / (sigma * a²)

  • sigma:导体电导率

  • a²:截面积

这是直流电阻模型

  1. 直流电导 Gdc

Gdc = 0

理想介质,无漏电流。

  1. 生成静态 RLGC 文件(ADS 专用格式)
1
L11 C11 Rdc11 Gdc11 Rs11 Gd11

格式严格对应:

1 → N=1

L0 → 单位长度电感

C0 → 单位长度电容

Rdc → 直流电阻

Gdc → 直流漏电导

0 → 集肤效应电阻(静态模型不用)

0 → 介质损耗电导(静态模型不用)

电子四极管参数计算方法——第一部分

已知电子管阳极电压Va=16kVV_{a} = 16kV,帘栅极电压Vg2=1200VV_{g_{2}} = 1200V,导通角70°。

查询TH781技术规格书,已知工作频率100MHz,帘栅压1500V时输出功率为280kW。

Eg2E_{g2}=1500V,PP_{\sim}=280kW。

由发射管的板流恒流特性曲线可知,每顺势都有一对栅压ege_{g}和板压eae_{a}互相对应,

当wt=0°瞬时,有eg=egmaxe_{g} = e_{gmax}ea=eamine_{a} = e_{amin}对应;

我们取此时UaEa\frac{Ua}{Ea}的经验值,UaEa=0.9\frac{Ua}{Ea} = 0.9得,eamin=EaUa=0.1Eae_{amin} = E_{a} - U_{a} = 0.1E_{a}Ua=0.9EaU_{a} = 0.9E_{a}=14.4kV;

当wt=90°瞬时,有eg=Ege_{g} = E_{g}ea=Eae_{a} = E_{a}对应;

确定静态工作点为eae_{a}=16kV,IaI_{a}=0A。由此可得eamine_{amin}=1.6kV。

根据发射管的输出功率P=12Ia1UaP_{\sim} = \frac{1}{2}I_{a_{1}}U_{a}

UaEa\frac{Ua}{Ea}取经验值时的基波分量Ia1=2P0.9EaI_{a1} = \frac{2P_{\sim}}{0.9E_{a}}=2*280/(0.9*16)=
38.8889 A

当wt=0瞬时,egmaxe_{gmax}时对应的Im,根据丙类放大器基波电流与分解系数α1\alpha_{1}的关系:Ia1=Imα1I_{a1} = I_{m}*\alpha_{1},解的Im=Ia1α1I_{m} = \frac{I_{a1}}{\alpha_{1}}

根据α1=θsinθcosθπ(1cosθ)\alpha_{1} = \frac{\theta - \sin\theta\cos\theta}{\pi(1 - \cos{\theta)}},求得α1\alpha_{1}=0.43555446。

ImI_{m}=38.8889/0.43555446=89.2859A;

当wt=0瞬时,取P点为(0.1$E_{a},,e_{amin}$)=(1.6kV,188V)

当wt=90°瞬时,取Q点为(EaE_{a},EgE_{g})=(16kV,-380V)

从坐标点获取参数:

栅偏压 Eg = Q.y = -380.00 V

栅极激励电压最大值 eg_max = A.y = 188.00 V

栅极激励电压振幅 Ug = eg_max + |Eg| = 188.00 + 380.00 = 568.00 V

十三点法计算原理如下——

原理:沿斜边AQ从A点(0°)到Q点(90°)按t=1cos(θ)t = 1 - cos(\theta)比例取点

请输入三个顶点坐标(格式:横坐标kV 纵坐标V):

直角顶点 O(x y): 1.6 -380

水平端点 Q(x y): 16 -380

垂直端点 A(x y): 1.6 188

三角形信息:

斜边端点:A(1.600 kV, 188.0 V) → Q(16.000 kV, -380.0 V)

斜边投影:Δx = 14.400 kV, Δy = -568.0 V

直角验证(OA·OQ): 0.000000 (应≈0)

经过十三点法计算可得,ABCDEF点的坐标分别为:

角度 比例 t 距A长度 横坐标(kV) 纵坐标(V)
0.0° 0 0 1.6 188 A
15.0° 0.034074 19.36 2.0907 168.65 B
30.0° 0.133975 76.122 3.5292 111.9 C
45.0° 0.292893 166.417 5.8177 21.64 D
60.0° 0.5 284.091 8.8 -96 E
75.0° 0.741181 421.126 12.273 -232.99 F
90.0° 1 568.183 16 -380 Q

通过观察恒流特性曲线得到,

ia(A)i_{a}(A)=
89.2859A,ia(B)i_{a}(B)=85.8A,ia(C)i_{a}(C)=72.02A,ia(D)i_{a}(D)=48.94A,ia(E)i_{a}(E)=21.81A,ia(F)i_{a}(F)=3.18A

ig(A)i_{g}(A)=7.81A,ig(B)i_{g}(B)=6.49A, ig(C)i_{g}(C)=3.10A, ig(D)i_{g}(D)=0.63A,
ig(E)i_{g}(E)=0A, ig(F)i_{g}(F)=0A

ig2(A)i_{g_{2}}(A)=5.43A,ig2(B)i_{g_{2}}(B)=3.38A,
ig2(C)i_{g_{2}}(C)=1.06A,ig2(D)i_{g_{2}}(D)=0A,
ig2(A)i_{g_{2}}(A)=0A,ig2(B)i_{g_{2}}(B)=0A

电流 A B C D E F
ia 89.2859 A 85.8 A 72.02 A 48.94 A 21.81 A 3.18 A
ig 7.81 A 6.49 A 3.10 A 0.63 A 0 0
i(g2) 5.43 A 3.38 A 1.06 A 0 0 0

根据上面得到的值计算Ia0I_{a_{0}}Ia1I_{a_{1}},得到Ia0I_{a_{0}}=23.032746A,Ia1I_{a_{1}}=39.328625A;

根据上面得到的值计算Ig0I_{g_{0}}Ig1I_{g_{1}},得到Ig0I_{g_{0}}=1.177083A,Ig1I_{g_{1}}=2.215583A;

根据上面得到的值计算Ig20I_{{g_{2}}^{0}},得到Ig20I_{{g_{2}}^{0}}=0.596250A;

板级负载电阻Roe=UaIa1=R_{oe} = \frac{U_{a}}{I_{a_{1}}} = 14400.00
V / 39.3286 A = 366.1455 Ω;

输入功率P=12UaIa1P_{\sim} = \frac{1}{2}{U_{a}I}_{a1}=0.5 * 14400.00 V *
39.3286 A = 283.1661 kW;

输出功率Po=EaIa0P_{o} = E_{a}I_{a_{0}}=16.00 kV * 23.0327 A = 368.5239 kW;

板级损耗Pa=PoPP_{a} = P_{o} - P_{\sim}=368.5239 kW - 283.1661 kW = 85.3578
kW;

帘栅极损耗Pg2=Eg2Ig20P_{g_{2}} = E_{g_{2}}I_{{g_{2}}^{0}}=1200.00 V * 0.596250 A
= 715.5000 W;

栅极损耗Pg=12UgIg1EgIg0P_{g} = \frac{1}{2}U_{g}I_{g_{1}} - E_{g}I_{g_{0}}=0.5*568.00*2.215583 -
(-380.00)*1.177083 = 1076.5173 W;

板级效率η=PPo\eta = \frac{P_{\sim}}{P_{o}}=283.1661 kW / 368.5239 kW =
0.768379 (76.84%);

激励功率Pg=12UgIg1P_{g_{\sim}} = \frac{1}{2}U_{g}I_{g_{1}}=0.5 * 568.00 V *
2.215583 A = 629.2257 W;

放大器的输入阻抗Rg=UgIg1+Ia1R_{g} = \frac{U_{g}}{I_{g_{1}} + I_{a_{1}}}=568.00 V /
(2.215583 A + 39.328625 A) = 13.6722 Ω。

符号 名称 数值 单位 符号 名称 数值 单位
Ia0 板极直流分量 23.0327 A Ia1 板极基波分量 39.3286 A
Ig0 栅极直流分量 1.1771 A Ig1 栅极基波分量 2.2156 A
Ig20 帘栅极直流分量 0.5963 A
Roe 板极负载电阻
Ua1/Ia1
366.15 Ω P~ 输出功率
½Ua1Ia1
283.17 kW
P0 直流输入功率
EaIa0
368.52 kW Rge 栅地输入阻抗
Ug/(Ig1+Ia1)
13.67 Ω
Pa 板极损耗
P0-P~
85.36 kW Pg2 帘栅极损耗
Eg2Ig20
715.50 W
Pg 栅极损耗
½UgIg1-EgIg0
1076.52 W Pg1 激励功率
½UgIg1
629.23 W
η 板级效率 P~/P0 76.84 %

C语言代码

#include <stdio.h>
#include <math.h>

#define PI 3.14159265358979323846
#define DEG_TO_RAD(x) ((x) * PI / 180.0)

// 全局变量,用于各阶段传递数据
double Ea, Eg, E_g2;           // 电压参数
double theta_deg, theta_rad;   // 导通角
int n;                         // 分解系数序号
double alpha_n;                // 分解系数
double Im;                     // 脉冲电流峰值
double Ia1_calculated;         // 阶段1计算的Ia1
double Ua;                     // 阳极电压振幅
double Ua_over_Ea;             // 板压利用系数
double P_tilde;                // 射频输出功率 P~ (kW)

// 13点法坐标点
typedef struct {
	double x;      // 横坐标 (kV)
	double y;      // 纵坐标 (V)
	char name;     // 点名称
} Point;

Point points[7];   // A, B, C, D, E, F, Q

// 电流采样值
double ia[6];      // Ia(A) ~ Ia(F)
double ig[6];      // Ig(A) ~ Ig(F)  
double ig2[6];     // Ig2(A) ~ Ig2(F)

// 计算结果
double Ia0, Ia1;   // 阳极电流直流分量和基波分量
double Ig0, Ig1;   // 栅极电流直流分量和基波分量
double Ig2_0;      // 帘栅极电流直流分量

// 最终计算结果
double R_oe;       // 板级负载电阻
double P_out_rf;   // 输出功率 (射频功率)
double P_in_dc;    // 输入功率 (直流功率)
double P_a;        // 板级损耗
double P_g2;       // 帘栅极损耗
double P_g;        // 栅极损耗
double eta;        // 板级效率
double P_g_drive;  // 激励功率
double R_g;        // 放大器输入阻抗
double U_g;        // 栅极激励电压振幅

// 函数声明
double calculate_alpha(double theta, int n);
void stage1_calculate_Im();
void stage2_thirteen_points();
void stage3_calculate_Ia();
void stage4_calculate_Ig();
void stage5_calculate_Ig2();
void stage6_final_calculations();

int main() {
	printf("=================================================\n");
	printf("    电子四极管技术参数计算系统\n");
	printf("=================================================\n\n");
	
	// 阶段1:计算Im
	stage1_calculate_Im();
	
	// 阶段2:13点法坐标计算
	stage2_thirteen_points();
	
	// 阶段3:计算Ia0, Ia1
	stage3_calculate_Ia();
	
	// 阶段4:计算Ig0, Ig1
	stage4_calculate_Ig();
	
	// 阶段5:计算Ig2_0
	stage5_calculate_Ig2();
	
	// 阶段6:最终计算
	stage6_final_calculations();
	
	printf("\n=================================================\n");
	printf("    所有计算完成!\n");
	printf("=================================================\n");
	
	return 0;
}

// 计算分解系数 α_n
double calculate_alpha(double theta, int n) {
	if (n == 0) {
		// α0 = (sinθ - θcosθ) / (π(1-cosθ))
		return (sin(theta) - theta * cos(theta)) / (PI * (1 - cos(theta)));
	} else if (n == 1) {
		// α1 = (θ - sinθcosθ) / (π(1-cosθ))
		return (theta - sin(theta) * cos(theta)) / (PI * (1 - cos(theta)));
	} else {
		// αn = 2[sin(nθ)cosθ - ncos(nθ)sinθ] / [π(1-cosθ)n(n²-1)]
		double numerator = 2 * (sin(n * theta) * cos(theta) - n * cos(n * theta) * sin(theta));
		double denominator = PI * (1 - cos(theta)) * n * (n * n - 1);
		return numerator / denominator;
	}
}

// 阶段1:输入基本参数,计算Im
void stage1_calculate_Im() {
	printf("【阶段1】计算脉冲电流峰值 Im\n");
	printf("-------------------------------------------------\n");
	
	// 输入Ea和Eg2
	printf("请输入阳极电压 Ea (kV): ");
	scanf("%lf", &Ea);
	
	printf("请输入帘栅极电压 Eg2 (V) [默认1500V]: ");
	scanf("%lf", &E_g2);
	if (E_g2 == 0) E_g2 = 1500.0;  // 默认值
	
	// 直接输入射频输出功率 P~
	printf("请输入射频输出功率 P~ (kW) [默认280.0kW]: ");
	scanf("%lf", &P_tilde);
	if (P_tilde == 0) P_tilde = 280.0;  // 默认值,与原计算后P~=252/0.9≈280一致
	
	double P_rf = P_tilde * 1000.0;  // 转换为W
	
	// 输入导通角
	printf("请输入导通角 θ (度): ");
	scanf("%lf", &theta_deg);
	theta_rad = DEG_TO_RAD(theta_deg);
	
	// 输入分解系数序号
	printf("请输入分解系数序号 n (0, 1, 2...): ");
	scanf("%d", &n);
	
	// 计算分解系数
	alpha_n = calculate_alpha(theta_rad, n);
	
	printf("\n计算结果:\n");
	printf("  角度 θ = %.2f° (%.6f 弧度)\n", theta_deg, theta_rad);
	printf("  分解系数 α_%d = %.8f\n", n, alpha_n);
	
	// 输入板压利用系数
	printf("请输入板压利用系数 Ua/Ea (范围0.85~0.95): ");
	scanf("%lf", &Ua_over_Ea);
	if (Ua_over_Ea < 0.85 || Ua_over_Ea > 0.95) {
		printf("警告:板压利用系数超出常规范围!\n");
	}
	
	// 计算Ua
	Ua = Ua_over_Ea * Ea * 1000;  // 转换为V
	
	printf("  板压利用系数 Ua/Ea = %.4f\n", Ua_over_Ea);
	printf("  阳极电压振幅 Ua = %.2f kV\n", Ua / 1000.0);
	
	// 计算基波分量 Ia1 = 2*P~ / Ua
	Ia1_calculated = (2.0 * P_rf) / Ua;
	printf("  基波电流分量 Ia1 = 2*P~/Ua = %.4f A\n", Ia1_calculated);
	
	// 计算Im = Ia1 / α1 (当n=1时)
	if (n == 1) {
		Im = Ia1_calculated / alpha_n;
	} else {
		// 如果n≠1,需要重新计算α1
		double alpha1 = calculate_alpha(theta_rad, 1);
		printf("  计算α1 = %.8f 用于求Im\n", alpha1);
		Im = Ia1_calculated / alpha1;
	}
	
	printf("\n>>> 脉冲电流峰值 Im = %.4f A\n", Im);
	printf("-------------------------------------------------\n\n");
}

// 阶段2:13点法坐标计算
void stage2_thirteen_points() {
	Point O, Q, A;  // 直角顶点、水平端点、垂直端点
	
	printf("【阶段2】13点法交点坐标计算\n");
	printf("-------------------------------------------------\n");
	printf("原理:沿斜边AQ从A点(0°)到Q点(90°)按 t = 1-cos(θ) 比例取点\n\n");
	
	// 输入三个顶点
	printf("请输入三个顶点坐标(格式:横坐标kV 纵坐标V):\n");
	printf("直角顶点 O(x y): ");
	scanf("%lf %lf", &O.x, &O.y);
	printf("水平端点 Q(x y): ");
	scanf("%lf %lf", &Q.x, &Q.y);
	printf("垂直端点 A(x y): ");
	scanf("%lf %lf", &A.x, &A.y);
	
	// 计算斜边向量
	double dx = Q.x - A.x;
	double dy = Q.y - A.y;
	double slope_length = sqrt(dx*dx + dy*dy);
	
	printf("\n三角形信息:\n");
	printf("  斜边端点:A(%.3f kV, %.1f V) → Q(%.3f kV, %.1f V)\n", A.x, A.y, Q.x, Q.y);
	printf("  斜边投影:Δx = %.3f kV, Δy = %.1f V\n", dx, dy);
	
	// 验证直角
	double oa_dx = A.x - O.x, oa_dy = A.y - O.y;
	double oq_dx = Q.x - O.x, oq_dy = Q.y - O.y;
	double dot = oa_dx*oq_dx + oa_dy*oq_dy;
	printf("  直角验证(OA·OQ): %.6f (应≈0)\n", dot);
	
	// 13点法角度定义
	double angles[] = {0, 15, 30, 45, 60, 75, 90};
	char point_names[] = {'A', 'B', 'C', 'D', 'E', 'F', 'Q'};
	
	printf("\n13点法交点坐标:\n");
	printf("角度  | 比例t   | 距A长度 | 横坐标(kV) | 纵坐标(V)   | 点\n");
	printf("------|---------|---------|------------|-------------|----\n");
	
	for (int i = 0; i < 7; i++) {
		double angle_rad = DEG_TO_RAD(angles[i]);
		double t = 1.0 - cos(angle_rad);  // 核心公式
		
		points[i].x = A.x + t * dx;
		points[i].y = A.y + t * dy;
		points[i].name = point_names[i];
		
		double dist_from_A = t * slope_length;
		
		printf("%5.1f°| %.6f| %8.3f| %11.4f| %12.2f| %c\n",
			   angles[i], t, dist_from_A, points[i].x, points[i].y, points[i].name);
	}
	
	// 将水平端点Q的y值赋给Eg,将垂直端点A的y值作为eg_max
	Eg = Q.y;
	double eg_max = A.y;  // 垂直端点A的y坐标就是eg_max
	
	// 计算Ug = eg_max + |Eg|
	U_g = eg_max + fabs(Eg);
	printf("\n>>> 从坐标点获取参数:\n");
	printf("  栅偏压 Eg = Q.y = %.2f V\n", Eg);
	printf("  栅极激励电压最大值 eg_max = A.y = %.2f V\n", eg_max);
	printf("  栅极激励电压振幅 Ug = eg_max + |Eg| = %.2f + %.2f = %.2f V\n", 
		   eg_max, fabs(Eg), U_g);
	
	printf("\n>>> 坐标计算完成,请根据恒流特性曲线读取各点电流值\n");
	printf("-------------------------------------------------\n\n");
}

// 阶段3:输入Ia(A)~Ia(F),计算Ia0, Ia1
void stage3_calculate_Ia() {
	printf("【阶段3】计算阳极电流分量 Ia0, Ia1\n");
	printf("-------------------------------------------------\n");
	printf("请输入从恒流特性曲线读取的阳极电流值 (A):\n");
	
	const char* labels[] = {"A", "B", "C", "D", "E", "F"};
	for (int i = 0; i < 6; i++) {
		printf("  i_a(%s) = ", labels[i]);
		scanf("%lf", &ia[i]);
	}
	
	// 使用13点法公式计算
	Ia0 = (1.0/12.0) * (0.5*ia[0] + ia[1] + ia[2] + ia[3] + ia[4] + ia[5]);
	Ia1 = (1.0/12.0) * (ia[0] + 1.93*ia[1] + 1.73*ia[2] + 1.41*ia[3] + ia[4] + 0.52*ia[5]);
	
	printf("\n输入值:\n");
	for (int i = 0; i < 6; i++) {
		printf("  i_a(%s) = %.4f A\n", labels[i], ia[i]);
	}
	
	printf("\n>>> 计算结果:\n");
	printf("  阳极电流直流分量 Ia0 = %.6f A\n", Ia0);
	printf("  阳极电流基波分量 Ia1 = %.6f A\n", Ia1);
	printf("-------------------------------------------------\n\n");
}

// 阶段4:输入Ig(A)~Ig(F),计算Ig0, Ig1
void stage4_calculate_Ig() {
	printf("【阶段4】计算栅极电流分量 Ig0, Ig1\n");
	printf("-------------------------------------------------\n");
	printf("请输入从恒流特性曲线读取的栅极电流值 (A):\n");
	
	const char* labels[] = {"A", "B", "C", "D", "E", "F"};
	for (int i = 0; i < 6; i++) {
		printf("  i_g(%s) = ", labels[i]);
		scanf("%lf", &ig[i]);
	}
	
	Ig0 = (1.0/12.0) * (0.5*ig[0] + ig[1] + ig[2] + ig[3] + ig[4] + ig[5]);
	Ig1 = (1.0/12.0) * (ig[0] + 1.93*ig[1] + 1.73*ig[2] + 1.41*ig[3] + ig[4] + 0.52*ig[5]);
	
	printf("\n输入值:\n");
	for (int i = 0; i < 6; i++) {
		printf("  i_g(%s) = %.4f A\n", labels[i], ig[i]);
	}
	
	printf("\n>>> 计算结果:\n");
	printf("  栅极电流直流分量 Ig0 = %.6f A\n", Ig0);
	printf("  栅极电流基波分量 Ig1 = %.6f A\n", Ig1);
	printf("-------------------------------------------------\n\n");
}

// 阶段5:输入Ig2(A)~Ig2(F),计算Ig2_0
void stage5_calculate_Ig2() {
	printf("【阶段5】计算帘栅极电流直流分量 Ig2_0\n");
	printf("-------------------------------------------------\n");
	printf("请输入从恒流特性曲线读取的帘栅极电流值 (A):\n");
	
	const char* labels[] = {"A", "B", "C", "D", "E", "F"};
	for (int i = 0; i < 6; i++) {
		printf("  i_g2(%s) = ", labels[i]);
		scanf("%lf", &ig2[i]);
	}
	
	Ig2_0 = (1.0/12.0) * (0.5*ig2[0] + ig2[1] + ig2[2] + ig2[3] + ig2[4] + ig2[5]);
	
	printf("\n输入值:\n");
	for (int i = 0; i < 6; i++) {
		printf("  i_g2(%s) = %.4f A\n", labels[i], ig2[i]);
	}
	
	printf("\n>>> 计算结果:\n");
	printf("  帘栅极电流直流分量 Ig2_0 = %.6f A\n", Ig2_0);
	printf("-------------------------------------------------\n\n");
}

// 阶段6:最终计算所有剩余参数
void stage6_final_calculations() {
	printf("【阶段6】最终参数计算\n");
	printf("-------------------------------------------------\n");
	
	// 重新计算eg_max和Ug,因为stage2中计算的是局部变量
	double eg_max = points[0].y;  // A点的y坐标
	double Eg_value = points[6].y; // Q点的y坐标
	double Ug_value = eg_max + fabs(Eg_value);
	
	// 1. 板级负载电阻 Roe = Ua / Ia1
	R_oe = Ua / Ia1;
	printf("1. 板级负载电阻:\n");
	printf("   Roe = Ua / Ia1 = %.2f V / %.4f A = %.4f Ω\n", Ua, Ia1, R_oe);
	
	// 2. 输入功率 (射频输出功率) P~ = 0.5 * Ua * Ia1
	P_out_rf = 0.5 * Ua * Ia1;
	printf("\n2. 射频输出功率:\n");
	printf("   P~ = 0.5 * Ua * Ia1 = 0.5 * %.2f V * %.4f A = %.4f kW\n", 
		   Ua, Ia1, P_out_rf/1000.0);
	
	// 3. 输出功率 (直流输入功率) Po = Ea * Ia0
	P_in_dc = (Ea * 1000) * Ia0;
	printf("\n3. 直流输入功率:\n");
	printf("   Po = Ea * Ia0 = %.2f kV * %.4f A = %.4f kW\n", 
		   Ea, Ia0, P_in_dc/1000.0);
	
	// 4. 板级损耗 Pa = Po - P~
	P_a = P_in_dc - P_out_rf;
	printf("\n4. 板级损耗:\n");
	printf("   Pa = Po - P~ = %.4f kW - %.4f kW = %.4f kW\n", 
		   P_in_dc/1000.0, P_out_rf/1000.0, P_a/1000.0);
	
	// 5. 帘栅极损耗 Pg2 = Eg2 * Ig2_0
	P_g2 = E_g2 * Ig2_0;
	printf("\n5. 帘栅极损耗:\n");
	printf("   Pg2 = Eg2 * Ig2_0 = %.2f V * %.6f A = %.4f W\n", 
		   E_g2, Ig2_0, P_g2);
	
	// 6. 栅极损耗 Pg = 0.5*Ug*Ig1 - Eg*Ig0
	P_g = 0.5 * Ug_value * Ig1 - Eg_value * Ig0;
	printf("\n6. 栅极损耗:\n");
	printf("   栅极激励电压最大值 eg_max = A.y = %.2f V\n", eg_max);
	printf("   栅偏压 Eg = Q.y = %.2f V\n", Eg_value);
	printf("   栅极激励电压振幅 Ug = eg_max + |Eg| = %.2f V\n", Ug_value);
	printf("   Pg = 0.5*Ug*Ig1 - Eg*Ig0 = 0.5*%.2f*%.6f - (%.2f)*%.6f = %.4f W\n", 
		   Ug_value, Ig1, Eg_value, Ig0, P_g);
	
	// 7. 板级效率 η = P~ / Po
	eta = P_out_rf / P_in_dc;
	printf("\n7. 板级效率:\n");
	printf("   η = P~ / Po = %.4f kW / %.4f kW = %.6f (%.2f%%)\n", 
		   P_out_rf/1000.0, P_in_dc/1000.0, eta, eta*100.0);
	
	// 8. 激励功率 Pg~ = 0.5 * Ug * Ig1
	P_g_drive = 0.5 * Ug_value * Ig1;
	printf("\n8. 激励功率:\n");
	printf("   Pg~ = 0.5 * Ug * Ig1 = 0.5 * %.2f V * %.6f A = %.4f W\n", 
		   Ug_value, Ig1, P_g_drive);
	
	// 9. 放大器输入阻抗 Rg = Ug / (Ig1 + Ia1)   // 修改为包含Ia1
	R_g = Ug_value / (Ig1 + Ia1);
	printf("\n9. 放大器输入阻抗:\n");
	printf("   Rg = Ug / (Ig1 + Ia1) = %.2f V / (%.6f A + %.6f A) = %.4f Ω\n", 
		   Ug_value, Ig1, Ia1, R_g);
	
	printf("\n-------------------------------------------------\n");
	printf("【计算结果汇总】\n");
	printf("-------------------------------------------------\n");
	printf("基本参数:\n");
	printf("  Ea = %.2f kV, Eg = %.2f V, Eg2 = %.2f V\n", Ea, Eg_value, E_g2);
	printf("  板压利用系数 Ua/Ea = %.4f\n", Ua_over_Ea);
	printf("  阳极电压振幅 Ua = %.2f kV\n", Ua/1000.0);
	printf("  θ = %.2f°, Im = %.4f A\n", theta_deg, Im);
	printf("  栅极激励电压最大值 eg_max = %.2f V\n", eg_max);
	printf("  栅极激励电压振幅 Ug = %.2f V\n", Ug_value);
	printf("  射频输出功率 P~ = %.2f kW\n", P_tilde);
	printf("\n电流分量:\n");
	printf("  Ia0 = %.6f A, Ia1 = %.6f A\n", Ia0, Ia1);
	printf("  Ig0 = %.6f A, Ig1 = %.6f A\n", Ig0, Ig1);
	printf("  Ig2_0 = %.6f A\n", Ig2_0);
	printf("\n功率与效率:\n");
	printf("  射频输出功率 P~ = %.4f kW\n", P_out_rf/1000.0);
	printf("  直流输入功率 Po = %.4f kW\n", P_in_dc/1000.0);
	printf("  板级损耗 Pa = %.4f kW\n", P_a/1000.0);
	printf("  帘栅极损耗 Pg2 = %.4f W\n", P_g2);
	printf("  栅极损耗 Pg = %.4f W\n", P_g);
	printf("  板级效率 η = %.2f%%\n", eta*100.0);
	printf("\n阻抗与激励:\n");
	printf("  板级负载电阻 Roe = %.4f Ω\n", R_oe);
	printf("  输入阻抗 Rg = %.4f Ω (基于Ig1+Ia1)\n", R_g);
	printf("  激励功率 Pg~ = %.4f W\n", P_g_drive);
	printf("-------------------------------------------------\n");
}

参考

[1]岑伟德.调频立体声广播发射机[M].北京:国防工业出版社,1990.

[2]罗勇杰.兆瓦级电子四极管电气参数设计与数值模拟[D].湛江:广东海洋大学,2016.

[3]张军.艾玛克电子管特性计算器计算法在维护工作中的应用[J].广播电视网络,2021(S01):29-34.

[4]曹丰岭.栅地电路与阴地电路[J].广播电视信息(下半月刊),2007(12):2.

文献管理工具:Zotero的使用教程

省流
Zotero文献管理软件使用指南 - 四川大学图书馆
五分钟学会文献管理神器—— Zotero_zotero文献管理-CSDN博客
Zotero使用分享(一)——导入文献、管理文献、引用文献_zotero导入文献-CSDN博客
Zotero使用分享(二)——添加新的引用样式(附中国标准GB/T 7714-2015)_zotero参考文献国标gb/t7714-CSDN博客
Zotero使用指南-下载、文献导入与阅读、文献引用与插件使用_zotero怎么引用文献-CSDN博客
最全zotero必备插件配置合集
Zotero 中文小组 | Zotero 中文小组

配置Jekyll博客本地开发环境(Windows 7)

本教程主要基于配置Jekyll博客本地开发环境(Windows)win7下搭建Jekyll写作环境,以我自己的方式尝试搭建,把过程和遇到的困难总结一下。
相信各位读者在阅读本文后,在Windows 7上能更快地配置好Jekyll博客本地开发环境。

一、安装 Ruby

Windows 上运行 Jekyll 需要先安装 Ruby。在Ruby 官网中我们可以看到官网推荐的版本。
使用 Windows 7 的读者 不要 安装 官网推荐的版本,点击图中的Achieve进入这个页面,选择 rubyinstaller-devkit-2.7.6-1 下载X64或X86版本。
如果能访问网站但下载不动文件,可以使用 wget 下载文件。这里提供我输入的命令——

wget https://github.com/oneclick/rubyinstaller2/releases/download/RubyInstaller-2.7.6-1/rubyinstaller-devkit-2.7.6-1-x64.exe

如果以上两个链接打不开,可以前往ruby-lang,点击文中的rubyinstaller.cn。同样,前往下载页面,点击进入文中的上海交通大学镜像服务-rubyinstaller2下载ruby。

下载后进行安装,在安装过程中不能更改软件安装路径否则会导致后续的 Jekyll 安装失败。(换句话说就是什么选项都不要动。)

安装完成后,会自动弹出 cmd.exe如下图所示, 提示安装 MSYS2,它是用来编译 Ruby 本地包的。

这里带你把坑一一踩完。
首先不要像图中这样输入,应该先输入1,再输入3,再出现提示就按回车键结束。至于2,输了也白输,就像这样——

安装结束后,分别输入 ruby -v 和 gem -v 查看版本,确认安装完成。

ruby -v
gem -v

二、安装 Jekyll

虽然目前 Jekyll 的版本已经到了 4 以上,但是还有很多问题(大坑),会导致很多包的版本不匹配,Windows 7 用户就按照知乎博主的建议安装3.8.5,3.8.5是比较可靠的,如果你已经安装了其他版本,建议卸载,命令如下:

gem uninstall jekyll -v 你的版本

再安装3.8.5:

gem install jekyll -v 3.8.5


注意这里不要使用下面的命令安装——

gem install jekyll bundler

单独安装bundler这一步是必要的,可以用上面的格式,但安装jekyll千万不要用上面这个命令。
读者朋友可以试一下现在运行jekyll serve,会报错。
安装完jekyll后我们可以用jekyll -v检查一下是否出现相似的错误。

注意这时我们还没安装 bundler

接下来输入以下命令安装bundler。

gem install bundler

接着确认安装完成。

jekyll -v
bundle -v

使用 bundle config 修改 Ruby 镜像源

Bundler 的 Gem 源代码镜像命令 (后面使用jekyll时会用到)

bundle config mirror.https://rubygems.org https://gems.ruby-china.org

这一步是“使用 bundle config 修改 Ruby 镜像源”。如果后面输入bundle install没报错,就不要输入上面的命令,https://gems.ruby-china.org很有可能连不上。

如果修改了Ruby镜像源后连不上,不要输入以下命令——

bundle config --delete 'mirror.https://rubygems.org https://gems.ruby-china.org'

应该输入——

bundle config --delete mirror.https://rubygems.org https://gems.ruby-china.org

Jeklly,启动!

基本静态页面生成。这一步大部分人都不会出错。

jekyll new myblog
cd myblog
jekyll serve

Server address: http://127.0.0.1:4000
Server running… press ctrl-c to stop.

出现…(Bundler::GemNotFound)问题解决方法

bundle install 一下。下载完以后 jekyll serve 就又能运行了。

出现 You have already activated i18n 1.14.1, but your Gemfile requires i18n 0.9.5. …(Gem::LoadError) 问题解决方法

Prepending bundle exec to your command may solve this.
输入bundle exec jekyll serve即可使用,通常第一次使用不会出现该提示。

Jekyll主题Ramme

我们在测试前应查看 Gemfile 文件内容,这次我们先jekyll -v探探路。

打开Gemfile文件。

source 'http://rubygems.org'

gem 'github-pages'
gem 'rouge'
gem 'jekyll'
gem 'jekyll-mentions'
gem 'jekyll-feed'
gem 'jekyll-sitemap'
gem 'jekyll-gist'

输入 gem install github-pages会安装github-pages、jekyll-sitemap和jekyll-gist。rouge、jekyll-mentions和jekyll-feed暂时安装不上。
以上步骤可做可不做,输入bundle install,接着输入bundle exec jekyll serve,浏览器打开http://127.0.0.1:4000,结束!

http://127.0.0.1:4000


在主题文件夹我们会发现新增了这几个文件/文件夹——

...\Ramme\.sass-cache"
...\Ramme\_site"
...\Ramme\Gemfile.lock"

总结

Jekyll安装和使用过程中,我们会遇到各种各样的问题,有些可以按照网上的教程解决,有些需要我们打开思路另辟蹊径。
这里举几个例子:

运行gem uninstall –all提示:ERROR: While executing gem … (Gem::DependencyRemovalException) Uninstallation aborted due to dependent gem(s)

运行 gem cleanup 后依然出现:ERROR: While executing gem … (Gem::DependencyRemovalException) Uninstallation aborted due to dependent gem(s)

你遇到的问题是尝试卸载一个或多个 gem 时,系统发现这些 gem 正在被其他 gem 依赖,因此无法卸载。这种情况通常在尝试卸载一个被其他 gem 直接或间接依赖的 gem 时出现。

这段话划掉……单个gem问题请对症下药,此路不通。

PSgem list --details 这将会列出所有的gem,并且会显示它们的版本、作者、安装路径等详细信息。

ERROR: Could not find a valid gem ‘jekyll’ (= 3.8.5) in any repository

出现该错误有可能是你删除了gem sources内容导致的,当然也可能不是。视具体情况而定。
1、检查 Gem 源:首先,检查你的 gem 源是否正确。你可以使用以下命令查看当前的 gem 源:

gem sources

默认情况下,你可能会看到一个名为 ‘https://rubygems.org/' 的源。这是 Ruby Gems 的主要源。如果需要添加其他的源,你可以使用以下命令:

gem source -a <新的源地址>

2、更新 Ruby 和 Gem:如果你的 Ruby 或 Gem 的版本过旧,可能会导致一些兼容性问题。你可以通过以下命令更新 Ruby 和 Gem:

# 更新 Ruby  
sudo apt-get install ruby-full  
  
# 更新 Gem  
gem update --system

3、尝试指定版本号:如果上述方法都不行,你可以尝试指定 ‘jekyll’ gem 的版本号进行安装。例如,如果你知道一个特定版本的 ‘jekyll’ 是可用的,你可以使用以下命令安装:

gem install jekyll --version "=<版本号>"

gem source -a https://rubygems.org提示Error fetching https://rubygems.org: timed out (https://rubygems.org/specs.4.8.gz)

1、清除 Gem 缓存:有时候,Gem 缓存可能会导致问题。你可以试着清除缓存然后再次尝试。在命令行中输入以下命令:

gem cleanup

2、更换 Gem 源:如果以上方法都不行,你还可以尝试更换 Gem 源。有许多其他的 Ruby Gem 源可供选择,比如 Ruby China 的源:

gem source -r https://rubygems.org  
gem source -a https://gems.ruby-china.com/

gem source -r https://rubygems.org 提示source https://rubygems.org not present in cache

当你尝试使用 gem source -r https://rubygems.org 命令来从缓存中移除一个源时,如果系统提示你 “source https://rubygems.org not present in cache”,那就意味着你的缓存中并没有这个源的数据。
如果你不一定要使用 https://rubygems.org 这个源,你可以尝试更换其他的源,比如使用 Ruby China 的源。

Jekyll提示使用了older的bundler版本

不用考虑——

gem uninstall bundler -v 你的版本
gem install bundler -v 要求的版本

gem update bundler 即可。

提示ERROR: While executing gem … (Gem::RemoteFetcher::UnknownHostError)

timed out (https://gems.ruby-china.com/quick/Marshal.4.8/bundler-2.4.20.gems pec.rz)

这是上一个问题安装特定版本bundler时出现的。不需要尝试更换源,update即可。
一个没试过的方法(不用试)——
手动下载并安装:如果以上方法都无法解决问题,你可以尝试手动下载Marshal gem的压缩包,然后解压并安装。在终端中执行以下命令:

# 下载Marshal gem的压缩包  
# 将URL替换为实际的下载地址  
wget https://gems.ruby-china.com/quick/Marshal.4.8/bundler-2.4.20.gemspec.rz  
  
# 解压压缩包  
unzip bundler-2.4.20.gemspec.rz  
  
# 安装Marshal gem  
gem install bundler-2.4.20/*.gemspec --no-document

bundle install显示Fetching source index from … Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from … due to underlying error <timed out …

通常跟网络连接和Rubygems 服务器没多大关系,请检查jekyll serve时是否报告jekyll版本旧了,比如原先该主题Jekyll 3.8.5 即可,这会提示要Jekyll 3.9.3。
如果一直不行请从Ruby开始重新操作。

三、卸载一切

卸载Jekyll

通常我们输入以下命令卸载Jekyll——

gem uninstall jekyll -v <jekyll version>

提示我们

ERROR:  While executing gem ... (Gem::DependencyRemovalException)
    Uninstallation aborted due to dependent gem(s)

这个错误表明至少有一个或多个 gem 依赖于 Jekyll,因此系统不允许你卸载它。
为了解决这个问题,你可以采取以下步骤:

  1. 查找依赖 Jekyll 的 gem

你可以使用以下命令来查找哪些 gem 依赖于 Jekyll:

gem dependency jekyll

这会列出所有直接依赖于 Jekyll 的 gem。
2. 考虑卸载依赖的 gem

如果你确定不再需要那些依赖于 Jekyll 的 gem,你可以尝试先卸载它们,然后再卸载 Jekyll。例如,如果 some_gem 依赖于 Jekyll,你可以这样操作:

gem uninstall some_gem
  1. 使用 –ignore-dependencies 选项

如果你确定要卸载 Jekyll,并且不关心其他 gem 是否还能正常工作,你可以使用 --ignore-dependencies 选项来强制卸载它:

gem uninstall jekyll --ignore-dependencies

卸载所有gem包

要卸载所有gem包,你可以使用Ruby的包管理器gem提供的命令来完成。以下是一些步骤来卸载所有gem包:

  1. 使用 gem list 命令来列出所有已安装的gem包。这将显示所有已安装的gem包及其版本号。像这样——
gem list
activesupport (7.0.8)
addressable (2.8.5)
base64 (0.1.1)
benchmark (default: 0.1.0)
bigdecimal (default: 2.0.0)
bundler (default: 2.1.4)
cgi (default: 0.1.0.1)
coffee-script (2.4.1)
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.23.10)
concurrent-ruby (1.2.2)
csv (default: 3.1.2)
date (default: 3.0.3)
dbm (default: 1.1.0)
delegate (default: 0.1.0)
did_you_mean (default: 1.4.0)
dnsruby (1.70.0)
em-websocket (0.5.3)
etc (default: 1.1.0)
ethon (0.16.0)
eventmachine (1.2.7 x64-mingw32)
execjs (2.9.1)
faraday (2.7.11)
faraday-net_http (3.0.2)
fcntl (default: 1.0.0)
ffi (1.16.2 x64-mingw32)
fiddle (default: 1.0.0)
fileutils (default: 1.4.1)
forwardable (default: 1.3.1)
forwardable-extended (2.6.0)
gdbm (default: 2.1.0)
gemoji (3.0.1)
getoptlong (default: 0.1.0)
github-pages (228)
github-pages-health-check (1.17.9)
google-protobuf (3.24.3 x64-mingw32)
html-pipeline (2.14.3)
http_parser.rb (0.8.0)
i18n (1.14.1)
io-console (default: 0.5.6)
ipaddr (default: 1.2.2)
irb (default: 1.2.6)
jekyll-avatar (0.7.0)
jekyll-coffeescript (1.1.1)
jekyll-commonmark (1.4.0)
jekyll-commonmark-ghpages (0.4.0)
jekyll-default-layout (0.1.4)
jekyll-feed (0.17.0, 0.15.1)
jekyll-gist (1.5.0)
jekyll-github-metadata (2.13.0)
jekyll-include-cache (0.2.1)
jekyll-mentions (1.6.0)
jekyll-optional-front-matter (0.3.2)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll-redirect-from (0.16.0)
jekyll-relative-links (0.6.1)
jekyll-remote-theme (0.4.3)
jekyll-sass-converter (1.5.2)
jekyll-seo-tag (2.8.0)
jekyll-sitemap (1.4.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.2.0)
jekyll-theme-cayman (0.2.0)
jekyll-theme-dinky (0.2.0)
jekyll-theme-hacker (0.2.0)
jekyll-theme-leap-day (0.2.0)
jekyll-theme-merlot (0.2.0)
jekyll-theme-midnight (0.2.0)
jekyll-theme-minimal (0.2.0)
jekyll-theme-modernist (0.2.0)
jekyll-theme-primer (0.6.0)
jekyll-theme-slate (0.2.0)
jekyll-theme-tactile (0.2.0)
jekyll-theme-time-machine (0.2.0)
jekyll-titles-from-headings (0.5.3)
jekyll-watch (2.2.1)
jemoji (0.12.0)
json (default: 2.3.0)
kramdown (2.4.0, 2.3.2)
kramdown-parser-gfm (1.1.0)
liquid (4.0.4)
listen (3.8.0)
logger (default: 1.4.2)
matrix (default: 0.2.0)
mercenary (0.3.6)
minima (2.5.1)
minitest (5.13.0)
mutex_m (default: 0.1.0)
net-pop (default: 0.1.0)
net-smtp (default: 0.1.0)
net-telnet (0.2.0)
nokogiri (1.15.4 x64-mingw32)
observer (default: 0.1.0)
octokit (4.25.1)
open3 (default: 0.1.0)
openssl (default: 2.1.3)
ostruct (default: 0.2.0)
pathutil (0.16.2)
power_assert (1.1.7)
prime (default: 0.1.1)
pstore (default: 0.1.0)
psych (default: 3.1.0)
public_suffix (5.0.3, 4.0.7)
racc (default: 1.4.16)
rake (13.0.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
rdoc (default: 6.2.1.1)
readline (default: 0.0.2)
reline (default: 0.1.5)
rexml (default: 3.2.3.1)
rouge (3.30.0, 3.26.0)
rss (default: 0.2.8)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (4.0.0)
sawyer (0.9.2)
sdbm (default: 1.0.0)
simpleidn (0.2.1)
singleton (default: 0.1.0)
stringio (default: 0.1.0)
strscan (default: 1.0.3)
terminal-table (3.0.2, 1.8.0)
test-unit (3.3.4)
thread_safe (0.3.6)
timeout (default: 0.1.0)
tracer (default: 0.1.0)
typhoeus (1.4.0)
tzinfo (2.0.6)
tzinfo-data (1.2023.3)
unf (0.1.4)
unf_ext (0.0.8.2 x64-mingw32)
unicode-display_width (2.5.0, 1.8.0)
uri (default: 0.10.0)
wdm (0.1.1)
webrick (1.8.1, default: 1.6.1)
xmlrpc (0.3.0)
yaml (default: 0.1.0)
zlib (default: 1.1.0)
  1. 要卸载所有gem包,你可以使用gem uninstall命令,并跟上每个gem包的名称和版本号。例如,如果你想要卸载名为gem-name的gem包,可以使用以下命令:
gem uninstall gem-name -v versionNumber
  1. 重复上述步骤,为每个gem包执行gem uninstall命令,直到你卸载了所有不需要的gem包。
  2. 在卸载所有gem包后,你还可以使用gem cleanup命令来清理无用的gem文件。这将删除所有不再需要的gem文件,以释放磁盘空间。
gem cleanup

我们换种思路卸载所有gem包。有两种方法可以实现这个目标。

第一种方法:

ruby -e "gem_list = `gem list --no-versions`.split($/).drop(1); gem_list.each { |gem| `gem uninstall -aIx #{gem}` }"

这段代码首先会获取所有已安装的gem包的列表,然后遍历这个列表,对每个gem包执行卸载操作。

第二种方法:
如果你的RubyGems版本大于等于2.1.0,你可以使用以下命令:

gem uninstall -aIx

这个命令会卸载所有版本的gem包,忽略依赖项,并包含可执行文件。
示例:

Administrator@AUTOBVT-Q90417J MINGW64 /e/Ramme (master)
$ gem uninstall -aIx
Successfully uninstalled activesupport-7.0.8
Successfully uninstalled addressable-2.8.5
Successfully uninstalled base64-0.1.1
Successfully uninstalled coffee-script-2.4.1
Successfully uninstalled coffee-script-source-1.11.1
Successfully uninstalled colorator-1.1.0
Removing commonmarker
Successfully uninstalled commonmarker-0.23.10
Successfully uninstalled concurrent-ruby-1.2.2
Successfully uninstalled dnsruby-1.70.0
Successfully uninstalled em-websocket-0.5.3
Successfully uninstalled ethon-0.16.0
Successfully uninstalled eventmachine-1.2.7-x64-mingw32
Successfully uninstalled execjs-2.9.1
Successfully uninstalled faraday-2.7.11
Successfully uninstalled faraday-net_http-3.0.2
Successfully uninstalled ffi-1.16.2-x64-mingw32
Successfully uninstalled forwardable-extended-2.6.0
Removing gemoji
Successfully uninstalled gemoji-3.0.1
Removing github-pages
Successfully uninstalled github-pages-228
Successfully uninstalled github-pages-health-check-1.17.9
Successfully uninstalled google-protobuf-3.24.3-x64-mingw32
Successfully uninstalled html-pipeline-2.14.3
Successfully uninstalled http_parser.rb-0.8.0
Successfully uninstalled i18n-1.14.1
Successfully uninstalled jekyll-avatar-0.7.0
Successfully uninstalled jekyll-coffeescript-1.1.1
Successfully uninstalled jekyll-commonmark-1.4.0
Successfully uninstalled jekyll-commonmark-ghpages-0.4.0
Successfully uninstalled jekyll-default-layout-0.1.4
Successfully uninstalled jekyll-feed-0.17.0
Successfully uninstalled jekyll-feed-0.15.1
Successfully uninstalled jekyll-gist-1.5.0
Successfully uninstalled jekyll-github-metadata-2.13.0
Successfully uninstalled jekyll-include-cache-0.2.1
Successfully uninstalled jekyll-mentions-1.6.0
Successfully uninstalled jekyll-optional-front-matter-0.3.2
Successfully uninstalled jekyll-paginate-1.1.0
Successfully uninstalled jekyll-readme-index-0.3.0
Successfully uninstalled jekyll-redirect-from-0.16.0
Successfully uninstalled jekyll-relative-links-0.6.1
Successfully uninstalled jekyll-remote-theme-0.4.3
Successfully uninstalled jekyll-sass-converter-1.5.2
Successfully uninstalled jekyll-seo-tag-2.8.0
Successfully uninstalled jekyll-sitemap-1.4.0
Successfully uninstalled jekyll-swiss-1.0.0
Successfully uninstalled jekyll-theme-architect-0.2.0
Successfully uninstalled jekyll-theme-cayman-0.2.0
Successfully uninstalled jekyll-theme-dinky-0.2.0
Successfully uninstalled jekyll-theme-hacker-0.2.0
Successfully uninstalled jekyll-theme-leap-day-0.2.0
Successfully uninstalled jekyll-theme-merlot-0.2.0
Successfully uninstalled jekyll-theme-midnight-0.2.0
Successfully uninstalled jekyll-theme-minimal-0.2.0
Successfully uninstalled jekyll-theme-modernist-0.2.0
Successfully uninstalled jekyll-theme-primer-0.6.0
Successfully uninstalled jekyll-theme-slate-0.2.0
Successfully uninstalled jekyll-theme-tactile-0.2.0
Successfully uninstalled jekyll-theme-time-machine-0.2.0
Successfully uninstalled jekyll-titles-from-headings-0.5.3
Successfully uninstalled jekyll-watch-2.2.1
Successfully uninstalled jemoji-0.12.0
Successfully uninstalled kramdown-2.4.0
Removing kramdown
Successfully uninstalled kramdown-2.3.2
Successfully uninstalled kramdown-parser-gfm-1.1.0
Successfully uninstalled liquid-4.0.4
Removing listen
Successfully uninstalled listen-3.8.0
Successfully uninstalled mercenary-0.3.6
Successfully uninstalled minima-2.5.1
Successfully uninstalled minitest-5.13.0
Successfully uninstalled net-telnet-0.2.0
Removing nokogiri
Successfully uninstalled nokogiri-1.15.4-x64-mingw32
Successfully uninstalled octokit-4.25.1
Successfully uninstalled pathutil-0.16.2
Successfully uninstalled power_assert-1.1.7
Successfully uninstalled public_suffix-5.0.3
Successfully uninstalled public_suffix-4.0.7
Removing rake
Successfully uninstalled rake-13.0.1
Successfully uninstalled rb-fsevent-0.11.2
Successfully uninstalled rb-inotify-0.10.1
Successfully uninstalled rouge-3.30.0
Removing rougify
Successfully uninstalled rouge-3.26.0
Successfully uninstalled ruby2_keywords-0.0.5
Successfully uninstalled rubyzip-2.3.2
Removing safe_yaml
Successfully uninstalled safe_yaml-1.0.5
Removing sass
Removing sass-convert
Removing scss
Successfully uninstalled sass-3.7.4
Successfully uninstalled sass-listen-4.0.0
Successfully uninstalled sawyer-0.9.2
Successfully uninstalled simpleidn-0.2.1
Successfully uninstalled terminal-table-3.0.2
Successfully uninstalled terminal-table-1.8.0
Successfully uninstalled test-unit-3.3.4
Successfully uninstalled thread_safe-0.3.6
Successfully uninstalled typhoeus-1.4.0
Successfully uninstalled tzinfo-2.0.6
Successfully uninstalled tzinfo-data-1.2023.3
Successfully uninstalled unf-0.1.4
Successfully uninstalled unf_ext-0.0.8.2-x64-mingw32
Successfully uninstalled unicode-display_width-2.5.0
Successfully uninstalled unicode-display_width-1.8.0
Successfully uninstalled wdm-0.1.1
Successfully uninstalled webrick-1.8.1
Successfully uninstalled xmlrpc-0.3.0
INFO:  Uninstalled all gems in C:/Ruby27-x64/lib/ruby/gems/2.7.0

如果你的RubyGems版本小于2.1.0,你需要使用类似下面的脚本:

for i in `gem list --no-versions`; do gem uninstall -aIx $i; done

这段代码同样会遍历所有已安装的gem包,并对每个gem包执行卸载操作。

请注意,这些操作会卸载你系统上安装的所有gem包,包括那些可能对你的系统或项目至关重要的包。在执行这些操作之前,请确保你了解卸载这些gem包可能带来的影响,并确保你有恢复这些gem包的方法(例如,通过备份或重新安装它们)。此外,这些命令只会卸载你通过gem安装的gem包,如果你使用其他方式(如RVM、rbenv或其他包管理器)安装的gem包,你可能需要使用相应的方法来卸载它们。

鉴于gem包如此之多,如果你需要快速卸载多个gem包,请阅读以下内容——

Sublime Text 如何删除所有括号内内容

Sublime Text 并没有直接提供一键删除所有括号内内容的功能。但你可以使用正则表达式(Regex)配合查找和替换(Find and Replace)功能来实现这个目标。

  1. 使用 Ctrl + H 打开查找和替换面板。
  2. 在 “Find” 框中,输入以下的正则表达式:\([^)]*\)。这个正则表达式会匹配任何在圆括号 () 中的内容。如果你想要匹配方括号 [] 或大括号 {} 中的内容,你可以相应地修改这个正则表达式。
\([^)]*\)
  1. 点击 “Replace All” 按钮,Sublime Text 就会删除所有匹配到的括号内的内容。

卸载gem和bundler

Ruby自带gem环境,因此当你卸载Ruby时,gem也会被一并卸载。这是因为gem是Ruby的一部分,它们共同构成了Ruby的运行环境。所以,如果你需要重新安装gem,通常需要在重新安装Ruby之后进行。

当你卸载Ruby时,Bundler通常也会被卸载,因为Bundler是一个Ruby gem,它依赖于Ruby的运行环境。Bundler用于管理Ruby项目的依赖关系,它是Ruby生态系统中的一个关键组件,但它是作为gem安装的,因此与Ruby本身紧密相关。

卸载Ruby后检查gem和bundler。

$ gem -v
bash: gem: command not found

$ bundler -v
bash: bundler: command not found

END!

参考

配置Jekyll博客本地开发环境(Windows)
win7下搭建Jekyll写作环境
jekyll s出现…(Bundler::GemNotFound)问题解决方法-2018-10-05
刘月林 | 使用 bundle config 修改 Ruby 镜像源
Ramme - Theme Info
Jekyll 安装、使用方法与卸载
文心一言

扩展阅读

如何卸载使用 bundle install`安装的所有gem-腾讯云开发者社区
Static Site Generators - Jamstack Themes

如何使用Sublime Text 4搭建 C/C++ 语言开发环境

刚刚安装的 Sublime Text 无法自行具备运行 C、C++ 代码的能力,需要我们手动对其进行设置。

配置GCC 编译环境

开始设置前,我们需要初始化好 GCC 编译环境。打开命令行窗口,输入gcc -v,如果输出GCC的具体版本等信息,表明当前系统以成功配置了 GCC 编译环境。(如图所示)

如果未配置GCC 编译环境,可阅读MinGW-w64安装教程——著名C/C++编译器GCC的Windows版本进行安装。

MinGW-W64 Online Installer:
sourceforge.net/project… 下载 mingw-get-setup.exe
MinGW Installation Manager → Basic Setup → 勾选 “mingw32-base” & “mingw32-gcc-g++” → Installation → Apply Changes

MinGW 离线安装:
sourceforge.net/project…
选择 MinGW-W64 GCC-8.1.0 的 x86_64-win32-seh
下载后文件: “x86_64-8.1.0-release-win32-seh-rt_v6-rev0.7z”
解压再配置环境变量


注意选择以下其中一个下载,不要下载MinGW-W64 Online Installer。

sjlj,seh的区别:

  • sjlj 全称是 SetJump / LongJump,前者设还原点,后者跳到还原点。可用于 32 位或者 64 位系统。
  • seh(Structured Exception Handling,结构化异常处理)是 Borland 公司的,微软买了其专利使用权,它利用了 FS 段寄存器,将还原点压入栈,收到异常时再弹出。相较而言,sjlj 是 C 标准库就有的东西,seh 在 2014 年前是有专利的,从性能上说 seh 比 sjlj 快。只用于64位系统。

【x86_64 64位】
1、seh 是新发明的,而 sjlj 则是古老的。只用于64位系统。
2、seh 性能比较好,但不支持 32位。 sjlj 稳定性好,支持 32位和64位。

因此,x86_64 系统架构的推荐使用 seh 的异常处理模型。

posix 和 win32 的区别是指编译器使用的线程模型。posix 是一种 UNIX API 标准,而 win32 是 Windows 的 API 标准。这两者之间有一些区别,例如在 mingw-w64 中,使用 posix 线程将启用 C++11/C11 多线程功能,并使 libgcc 依赖于 libwinpthreads。而使用 win32 线程则不会启用 C++11 多线程功能。

如果在 Windows 下开发 Linux 应用程序,则选择 posix;如果开发 Windows 平台下的应用程序,就需要选择 Win32。
这个你自己选择吧,你偏向于原生的C标准就选posix,面向Windows编程就选win32,如果你还是选择困难的话,毕竟我们最常用的还是Windows,选win32也没什么问题。

运行MinGW-W64-install.exe会安装失败。

下载 x86_64-win32-seh 的7z文件后解压。
添加环境变量,变量路径替换为解压路径。
环境变量内容如下:

- 变量名 变量值
- C_INCLUDEDE_PATH C:\MinGW\include
- LIBRARY_PATH C:\MinGW\lib
- Path C:\MinGW\bin

配置 Sublime Text 编辑器(GCC)

在已安装好 GCC 编译器的基础上,接下来开始正式配置 Sublime Text 编辑器。

在菜单栏中依次点击“Tools -> Build System -> New Build System”,由此即可在 Sublime Text 打开一个临时文件,如下所示:

删除其所有内容,并将如下内容完整地复制到该文件中:

{
    "cmd": ["gcc","${file}","-o", "${file_path}/${file_base_name}"],
    "file_regex":"^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
    "working_dir":"${file_path}",
    "selector": "source.c",
    "encoding":"cp936",
    "variants":
    [
        {
            "name": "C_Run",
            "cmd": ["cmd","/c", "gcc", "${file}", "-o", "${file_path}/${file_base_name}","&&", "cmd", "/c","${file_path}/${file_base_name}"]
        },
        {
            "name":"C_RunInCommand",
            "cmd": ["cmd","/c", "gcc", "${file}","-o","${file_path}/${file_base_name}", "&&","start", "cmd", "/c","${file_path}/${file_base_name} & pause"]
         }
    ]
}

按住Ctrl + S保存上述文件,并将文件取名为 gcc.sublime-build ,点击保存。

重新打开 Sublime Text,并依次在菜单栏中选择“Tools -> Build System”, 在该选项中就可以看到上一步创建好的 gcc_sublime-build 的文件名 gcc。

通过勾选 gcc 编译选项,我们就可以直接在 Sublime Text 运行写好的 C 语言程序。
编写一个 Hellow Word 程序, 选择“Tools -> Build With…”选项(Ctrl+Shift+B )编译运行。

这里有 gcc、gcc-C_Run 和 gcc-RunInCommand 3 个选项,其中 gcc 用于编译程序(读者可自行查看执行结果),gcc-C_Run 用于在 Sublime Text 内部调用 GCC 编译器并显示程序的执行结果,gcc_RunInCommand 用于在命令行窗口中借助 gcc 指令运行该程序并输出执行结果。

同样,如果想搭建 C++ 开发环境,只需再建立一个 g++.sublime-build 配置文件,并将如下内容拷贝到该文件中:

{
    "cmd": ["g++","-Wall", "${file}", "-o", "${file_path}/${file_base_name}"],
    "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
    "working_dir": "${file_path}",
    "selector": "source.c, source.c++",
    "encoding":"cp936",
    "variants":
    [
        {
            "name": "C++_Run",
            "cmd": ["cmd", "/c", "g++", "-Wall","${file}", "-o", "${file_path}/${file_base_name}", "&&", "cmd", "/c", "${file_path}/${file_base_name}"]
        },  
        {
            "name": "C++_RunInCommand",
            "cmd": ["cmd", "/c", "g++", "-Wall","${file}", "-o", "${file_path}/${file_base_name}", "&&", "start", "cmd", "/c", "${file_path}/${file_base_name} & echo.&pause"]
        }
    ]
}

配置TCC 编译环境

Tiny C Compiler(简称TCC, 或Tiny CC)是一个超小、超快的标准C语言编译器。
TCC Official Website Download Link
Windows 用户请下载 tcc-0.9.27-win64-bin.zip ,将下载好的文件,解压到某一文件夹即可。
在系统环境变量中双击Path,点击新建,添加tcc文件夹路径。
命令行窗口输入tcc检查是否配置完成。

C:\Users\Administrator>tcc
Tiny C Compiler 0.9.27 - Copyright (C) 2001-2006 Fabrice Bellard
Usage: tcc [options...] [-o outfile] [-c] infile(s)...
       tcc [options...] -run infile [arguments...]
General options:
  -c          compile only - generate an object file
  -o outfile  set output filename
  -run        run compiled source
  -fflag      set or reset (with 'no-' prefix) 'flag' (see tcc -hh)
  -Wwarning   set or reset (with 'no-' prefix) 'warning' (see tcc -hh)
  -w          disable all warnings
  -v -vv      show version, show search paths or loaded files
  -h -hh      show this, show more help
  -bench      show compilation statistics
  -           use stdin pipe as infile
  @listfile   read arguments from listfile
Preprocessor options:
  -Idir       add include path 'dir'
  -Dsym[=val] define 'sym' with value 'val'
  -Usym       undefine 'sym'
  -E          preprocess only
Linker options:
  -Ldir       add library path 'dir'
  -llib       link with dynamic or static library 'lib'
  -r          generate (relocatable) object file
  -shared     generate a shared library/dll
  -rdynamic   export all global symbols to dynamic linker
  -soname     set name for shared library to be used at runtime
  -Wl,-opt[=val]  set linker option (see tcc -hh)
Debugger options:
  -g          generate runtime debug info
  -b          compile with built-in memory and bounds checker (implies -g)
  -bt N       show N callers in stack traces
Misc. options:
  -x[c|a|n]   specify type of the next infile
  -nostdinc   do not use standard system include paths
  -nostdlib   do not link with standard crt and libraries
  -Bdir       set tcc's private include/library dir
  -MD         generate dependency file for make
  -MF file    specify dependency file name
  -m32/64     defer to i386/x86_64 cross compiler
Tools:
  create library  : tcc -ar [rcsv] lib.a files
  create def file : tcc -impdef lib.dll [-v] [-o lib.def]

使用方法

方法1. 打开命令行,转到源代码目录,输入: tcc 源代码文件名 即可。
此时,会在文件夹内生成.exe文件,双击即可运行。
方法2. 此方法为常用方法
打开命令行,转到源代码目录,输入: tcc -run 源代码文件名。

配置 Sublime Text 编辑器(TCC)

在已安装好 GCC 编译器的基础上,接下来开始正式配置 Sublime Text 编辑器。

在菜单栏中依次点击“Tools -> Build System -> New Build System”,由此即可在 Sublime Text 打开一个临时文件。
删除其所有内容,并将如下内容完整地复制到该文件中:

{
    "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
    "working_dir": "${file_path}",
    "selector": "source.c",
    "encoding": "gbk",

    "linux":    {"shell_cmd": "tcc -o ${file_path}/${file_base_name} ${file}"},
    "osx":      {"shell_cmd": "tcc -o ${file_path}/${file_base_name} ${file}"},
    "windows":  {"shell_cmd": "tcc -o ${file_path}/${file_base_name}.exe ${file}"},

    "variants": [
        {
            "name": "Run",
            "linux":    {"shell_cmd": "tcc -run ${file}"},
            "osx":      {"shell_cmd": "tcc -run ${file}"},
            "windows":  {"cmd": ["tcc", "-run", "${file}"]},
        },
        {
            "name": "Run(CMD)",
            "linux": {
                "shell_cmd": "gnome-terminal -e 'bash -c \"echo tcc -run ${file};echo;time tcc -run \\\"${file}\\\";echo ;echo Press any key to exit...;read -n 1;exit;\"'"
            },
            "windows": {
                // "shell_cmd": "git-bash -c \"echo tcc -run ${file_name};echo;time winpty tcc -run ${file_name};echo;echo Press any key to exit...;read -n 1;exit;\"",
                "cmd": [
                    "cmd", "/c",
                    "start", "cmd", "/c", "tcc -run ${file} &echo.&pause"
                ],
            },
            "osx": {}
        }
    ]
}

解决中文乱码

ConvertToUTF8: Reload With Encoding解决的是 Sublime Text 显示的编码问题,不能解决编译生成打开的cmd窗口文字乱码问题。
应该在保存程序文件前先FileSet File Encoding ToGBKBIG5

参考

MinGW-w64安装教程——著名C/C++编译器GCC的Windows版本
如何使用Sublime Text3搭建C语言开发环境_sublime c语言_LOVE_SCENARIO的博客
Sublime Text运行C和C++程序 - 楚千羽
【cpp 开发工具】MingGW 各版本区别及安装说明 - isanthree
Windows 下 MinGW 的选择与安装 - NEGOCES
MingW-W64-builds那么多版本,他们的区别是什么呢?_Ha-Ha-Interesting的博客
Sublime Text 配置C语言开发环境 - 简书
TCC(Tiny C Compiler)安装及使用方法-CSDN博客

推荐

【全网最新、最详细】如何使用 Sublime Text 4 优雅地写C++? - 知乎

Windows平台Sublime+LaTex配置教程

详细教程

下载LaTex

访问官网,点击Concise instructions, per platform: 下的 install on Windows

找到Easy install

When installing from the internet, we recommend downloading and running install-tl-windows.exe.

This installer first unpacks itself and then starts the installer proper, which is the same as for other platforms. An ‘Advanced’ button gives you many additional customization options.

When successful, the installer tries to do the post-install things that are considered appropriate on Windows:

  • Adds a TeX Live submenu of Windows’ Start menu. Entries include a GUI for TeX Live Manager and the TeXworks editor if was installed.
  • Optionally adds some filetypes and file associations.
  • Adds the directory of TeX Live Windows binaries to the search path.

The TeX Live Manager GUI mentioned above can be used to add or remove packages, and to keep the installation up to date.

Note. For Cygwin installations, see the Unix/Linux page.

下载上文的install-tl-windows.exe 到本地,双击运行。





安装完成,如下图:

Sublime Text 配置

Ctrl+Shift+P,输入LaTeXTools安装它。不过如今安装不上,只能手动安装。
把整个仓库克隆到本地,塞到 Sublime Text 的插件文件夹(C:\Users\Administrator\AppData\Roaming\Sublime Text\Packages)

git clone https://github.com/SublimeText/LaTeXTools

先把Sublime关闭掉,然后再重启软件,使Sublime能够在菜单中加载出 LaTeXTools。

Sublime中进行LaTexTools配置

在Sublime菜单栏依次选择Preferences-Package Settings-LaTexTools-Reset user settings to default。

然后重新在Sublime菜单栏依次选择Preferences-Package Settings-LaTexTools-Settings-User。

使用快捷键Ctrl+F,下方弹出搜索窗口,在其中输入windows为关键字,按Enter进行搜索。找到“windows”的path路径:

	"windows": {
		// Path used when invoking tex & friends; "" is fine for MiKTeX
		// For TeXlive 2011 (or other years) use
		// "texpath" : "C:\\texlive\\2011\\bin\\win32;$PATH",
		"texpath" : "",
		// TeX distro: "miktex" or "texlive"
		"distro" : "miktex",
		// Command to invoke Sumatra. If blank, "SumatraPDF.exe" is used (it has to be on your PATH)
		"sumatra": "",
		// Command to invoke Sublime Text. Used if the keep_focus toggle is true.
		// If blank, "subl.exe" or "sublime_text.exe" will be used.
		"sublime_executable": "",
		// how long (in seconds) to wait after the jump_to_pdf command completes
		// before switching focus back to Sublime Text. This may need to be
		// adjusted depending on your machine and configuration.
		"keep_focus_delay": 0.5
	},

将其修改成如下内容:

	"windows": {
		// Path used when invoking tex & friends; "" is fine for MiKTeX
		// For TeXlive 2011 (or other years) use
		// "texpath" : "C:\\texlive\\2011\\bin\\win32;$PATH",
		"texpath" : "E:\\texlive\\2023\\bin\\windows;$PATH",
		// TeX distro: "miktex" or "texlive"
		"distro" : "texlive",
		// Command to invoke Sumatra. If blank, "SumatraPDF.exe" is used (it has to be on your PATH)
		"sumatra": "",
		// Command to invoke Sublime Text. Used if the keep_focus toggle is true.
		// If blank, "subl.exe" or "sublime_text.exe" will be used.
		"sublime_executable": "C:\\Program Files\\Sublime Text\\subl.exe",
		// how long (in seconds) to wait after the jump_to_pdf command completes
		// before switching focus back to Sublime Text. This may need to be
		// adjusted depending on your machine and configuration.
		"keep_focus_delay": 60
	},

注意观察上面更改的部分, 分别是:

  • 第 5 行: 在 texpath 后面的双引号之间添加 texlive 的 win32 的文件路径.
  • 第 7 行: 在 distro 后面的双引号之间键入 texlive, 也就选择你安装的 TeX 的发行版本的,我的是 texlive, 所以键入 texlive , 如果你安装的是 miktex, 当然也应该改成 miktex.
  • 第 9 行: 在 sumatra 后面的双引号之间输入 SumatraPDF 的安装路径.
  • 第 12 行: sublime_executable 后面的双引号后面填入 subl.exe 的路径.
  • 第 16 行: 此行是设置从pdf 阅读器调整到 Sublime Text 的时间, 通常也设置长些, 默认的 0.5 秒的效果就是看到 SumatraPDF阅读器一闪而过, 这也许不是一个好的体验.

关闭 Sublime text 编译过程中开启新窗口

这里大家可以看到我们没有添加第 9 行的内容,而是修改了第 12 行的内容。
Windows平台 subl.exe 与 Sublime_text.exe 在同一个安装路径下,我们可以只填入 subl.exe 的路径。


至于一些教程提到的改"builder": "traditional","builder": "simple",……
默认是traditional请勿修改

使用LaTeXTools前请阅读该插件的 README.markdown,这里我摘取部分大家需要关注的内容——

Keybindings

Keybindings have been chosen to make them easier to remember, and also to minimize clashes with existing (and standard) ST bindings. I am taking advantage of the fact that ST supports key combinations, i.e. sequences of two (or more) keys. The basic principle is simple:

  • Most LaTeXTools facilities are triggered using Ctrl+l (Windows, Linux) or Cmd+l (OS X), followed by some other key or key combination

  • Compilation uses the standard ST “build” keybinding, i.e. Ctrl-b on Windows and Linux and Cmd-b on OS X. So does the “goto anything” facility (though this may change).

For example: to jump to the point in the PDF file corresponding to the current cursor position, use Ctrl-l, j: that is, hit Ctrl-l, then release both the Ctrl and the l keys, and quickly type the j key (OS X users: replace Ctrl with Cmd). To wrap the selected text in an \emph{} command, use Ctrl-l, Ctrl-e: that is, hit Ctrl-l, release both keys, then hit Ctrl-e (again, OS X users hit Cmd-l and then Cmd-e).

Ctrl-l (Cmd-l on OS X) is the standard ST keybinding for “expand selection to line”; this is remapped to Ctrl-l,Ctrl-l (Cmd-l,Cmd-l on OS X). This is the only standard ST keybinding that is affected by the plugin—an advantage of new-style keybindings.

Most plugin facilities are invoked using sequences of 2 keys or key combinations, as in the examples just given. A few use sequences of 3 keys or key combinations.

Henceforth, I will write C- to mean Ctrl- for Linux or Windows, and Cmd- for OS X. You know your platform, so you know what you should use. In a few places, to avoid ambiguities, I will spell out which key I mean.

Compiling LaTeX files

Keybinding: C-b (standard ST keybinding)

LaTeXTools offers a fully customizable build process. This section describes the default process, also called “traditional” because it is the same (with minor tweaks) as the one used in previous releases. However, see below for how to customize the build process.

The default ST Build command takes care of the following:

  • It saves the current file
  • It invokes the tex build command (texify for MikTeX; latexmk for TeXlive and MacTeX).
  • It parses the tex log file and lists all errors, warnings and, if enabled, bad boxes in an output panel at the bottom of the ST window: click on any error/warning/bad boxes to jump to the corresponding line in the text, or use the ST-standard Next Error/Previous Error commands.
  • It invokes the PDF viewer for your platform and performs a forward search: that is, it displays the PDF page where the text corresponding to the current cursor position is located.

Project files are fully supported! Some of the options related to building tex files are described here. However, you should consult the subsection on project-specific settings for further details.

Multi-file documents are supported as follows. If the first line in the current file consists of the text %!TEX root = <master file name>, then tex & friends are invoked on the specified master file, instead of the current one. Note: the only file that gets saved automatically is the current one. Also, the master file name must have a valid tex extension (i.e., one configured in the tex_file_exts settings), or it won’t be recognized.

As an alternative, to using the %!TEX root = <master file name> syntax, if you use a Sublime project, you can set the TEXroot option (under settings):

{
	... <folder-related settings> ...

	"settings": {
		"TEXroot": "yourfilename.tex"
	}
}

Note that if you specify a relative path as the TEXroot in the project file, the path is determined relative to the location of the project file itself. It may be less ambiguous to specify an absolute path to the TEXroot if possible.

TeX engine selection is supported. If the first line of the current file consists of the text %!TEX program = <program>, where program is pdflatex, lualatex or xelatex, the corresponding engine is selected. If no such directive is specified, pdflatex is the default. Multi-file documents are supported: the directive must be in the root (i.e. master) file. Also, for compatibility with TeXshop, you can use TS-program instead of program. Note: for this to work, you must not customize the command option in LaTeXTools.sublime-settings. If you do, you will not get this functionality. Finally, if you use project files, the program builder setting can also be customized there, under settings.

TeX options: you can pass TeX options to your engine in two ways (thanks Ian Bacher!). One is to use a %!TEX options = ... line at the top of your file. The other is to use the options builder setting in your settings file. This can be useful, for instance, if you need to allow shell escape. Finally, if you use project files, the options builder setting can also be customized there (again, under settings).

Customizing or replacing the compilation command (latexmk or texify) is also possible by setting the command option under Builder Settings. If you do, the TeX engine selection facility may no longer work because it relies on a specific compilation command. However, if you want to customize or replace latexmk/texify, you probably know how to select the right TeX engine, so this shouldn’t be a concern. Also note that if you are using latexmk and you set the $pdflatex variable, the TeX options facility will not function, as latexmk does not support this. See the Settings option below for details. Note: if you change the compilation command, you are responsible for making it work on your setup. Only customize the compilation command if you know what you’re doing.

使用 LaTeXTools 时我们要注意以下几点:

  1. 编译前,注意将编译系统改为LaTeX,按 Ctrl+Shift+B 编译;
  2. 默认使用的是 pdflatex ,现在想使用 xelatex 进行编译,使用如下代码:
%!TEX program = xelatex
\documentclass{article}
\begin{document}
this is a way
\end{document} 

加上 %!TEX program = xelatex 后,可以切换到 xelatex 进行编译。

开启 SumatraPDF 的反向搜索功能

方法 1

使用 Win+R 后,输入 cmd,执行以下命令

sumatrapdf.exe -inverse-search "\" C:\Program Files\Sublime Text\sublime_text.exe\" \"%f:%l\"

方法 2

通常我们在 设置-选项 中是找不到 设置反向搜索命令行 的,因为 SumatraPDF 默认不开启TeX 增强功能。
我们需要打开高级设置,settings->Advanced options,然后找到如下代码

EnableTeXEnhancements = false

将参数 false 改为 true。

EnableTeXEnhancements = true

重新启动 SumatraPDF ,设置反向搜索命令行 部分就出现了。

在 SumatraPDF 中点 设置-选项,在 设置反向搜索命令行 底下的输入框中输入

"C:\Program Files\Sublime Text\sublime_text.exe" "%f:%l"

这样在 SumatraPDF 中双击 PDF 显示的相应位置就可以跳转到 Sublime Text 编辑的 LaTeX 源代码处, 实现反向搜索。

SumatraPDF 配置

最后我们提一点SumatraPDF 高级设置。
SumatraPDF 菜单 - 设置 - 高级选项,替换内容为

// 无文档时,窗口的背景色,默认为黄色
MainWindowBackground = #fff200
// 是否允许用 Esc 键退出 SumatraPDF
EscToExit = false
// 是否用现有窗口打开文档
ReuseInstance = false

// 是否使用系统颜色显示 背景/文本色
UseSysColors = false

// 启动时是否恢复会话
RestoreSession = true

// 自定义 PDF, XPS, DjVu 和 PostScript 的 UI 界面
FixedPageUI [
// 文本色,默认为黑    
    TextColor = #000000

// 背景色,默认为白    
    BackgroundColor = #ffffff

// 选定文本色
    SelectionColor = #f5fc0c

// 文档在窗口中显示时的上,右,下,左边距
    WindowMargin = 2 4 2 4

// 书籍模式双页显示时,水平和垂直间距
    PageSpacing = 4 4

//梯度渐变色,只支持三种颜色. 实验性, 也许对继续阅读有帮助.

//通常建议为: #2828aa #28aa28 #aa2828
    GradientColors =
]

// 电子书(EPUB, Mobi, FictionBook)的 UI 界面定制选项.

// 若 UseFixedPageUI 为 True 时使用默认配置.
EbookUI [
//字体名称.重新打开文档后生效
    FontName = Georgia

//字体大小.重新打开文档后生效
    FontSize = 12.5

//文本色
    TextColor = #5f4b32

//页面背景色
    BackgroundColor = #fbf0d9

// 如果为 True, 电子书也将使用 PDF 文档的默认配置(开启打印和搜索,禁用自动 reflow)
    UseFixedPageUI = false
]

//漫画书和图片的 UI 界面定制选项
ComicBookUI [
// 文档在窗口中显示时的上,右,下,左边距
    WindowMargin = 0 0 0 0

// 书籍模式双页显示时,水平和垂直间距
    PageSpacing = 4 4

// 如果为 True, 默认显示漫画书文件为漫画模式 (一次性从右向左显示2页)
    CbxMangaMode = false
]

// CHM 文件定制选项.
ChmUI [
//若为真,界面将使用默认的 PDF 文档风格
    UseFixedPageUI = false
]

//各种文件类型的附加外部查看器列表(可以有多个条目格式)
ExternalViewers [
[
//命令行的调用外部查看器,可以用 %p 页号和 “%1” 文件名(在包含空格的路径中添加引号)
    CommandLine =

//菜单中显示外部查看器的名称
Name =打开对话框的过滤选项,要指定支持的文件类型,多个项目使用;分割,不要包含任何空格 (比如 *.pdf;*.xps)
Filter =
]
]

//是否显示菜单栏,可以使用 F9 或 ALT
ShowMenubar = true

//文件更改后是否自动重载 (目前还不支持工作在 ebook UI 模式) (introduced in version 2.5)
ReloadModifiedDocuments = true

//标题栏是否显示完整路径 (introduced in version 3.0)
FullPathInTitle = false

//缩放比例的间隔 介于 8.33 和 6400 之间
ZoomLevels = 8.33 12.5 18 25 33.33 50 66.67 75 100 125 150 200 300 400 600 800 1000

//缩放比率的步长,如果为0,则使用默认
ZoomIncrement = 0

//设置打印对话框的默认选项
PrinterDefaults [
默认值是 scaling (shrink, fit, none)
PrintScale = shrink
]

//自定义显示搜索结果 (used from LaTeX editors)
ForwardSearch [

//当设置为正数值时,将向前搜索高亮样式改为矩形.在页面的左边(从页边空白处注明)
    HighlightOffset = 0

//高亮选区的高度 (if HighlightOffset is > 0)
    HighlightWidth = 15

//高亮搜索的颜色
    HighlightColor = #6581ff

//为真时一直显示高亮
    HighlightPermanent = false
]

//一个空格分隔的密码尝试打开受密码保护的文件时(必须输入包含空格的密码时)

//(introduced in version 2.4)
DefaultPasswords =

//自定义主屏幕 DPI

//(如果这个值不是正数,将使用系统的UI设置  (introduced in version 2.5)
CustomScreenDPI = 0

//是否为每个文档保存显示配置
RememberStatePerDocument = true

// 当前 UI 的语言, 使用 ISO 码
UiLanguage =

//是否显示工具栏
ShowToolbar = true

//是否侧栏显示收藏夹
ShowFavorites = false

// SumatraPDF 关联的文件格式扩展名 (e.g. “.pdf .xps .epub”)
AssociatedExtensions =

//是否自动应用文件扩展名关联
AssociateSilently = false

// 是否每天检测一次更新
CheckForUpdates = true

//要忽略更新的版本
VersionToSkip =

//是否记住上次打开的文档
RememberOpenedFiles = true

//反向搜索时启动 LaTeX 编辑器
InverseSearchCmdLine =

//是否增强的 LaTex 反向搜索
EnableTeXEnhancements = false

//默认的页面布局.

//有效值: automatic, single page, facing, book view, 

//continuous, continuous facing, continuous book view
DefaultDisplayMode = automatic

//默认缩放模式 使用比例(in %) 或下述值: fit page, fit width, fit content
DefaultZoom = fit page

//默认窗口状态. 1 is normal, 2 is maximized, 

//3 is fullscreen, 4 is minimized
WindowState = 1

//默认窗口位置(x,y)和尺寸(宽,高)
WindowPos = 0 0 0 0

//对于支持解析的文档, 在侧栏显示目录标签(书签)
ShowToc = true

//侧栏宽度 favorites/bookmarks
SidebarDx = 0

//如果收藏夹和书签侧边栏部分可见,这就是书签的高度(目录表)部分
TocDy = 0

//是否显示启动页面
ShowStartPage = true

//是否使用标签模式
UseTabs = true

//打开文件的信息
FileStates [
[
//文档路径
    FilePath =

//为书签/收藏夹保留的值
    Favorites [
[
收藏夹在菜单中显示的名称
Name =

//书签页面数值
        PageNo = 0

//页面标签
        PageLabel =
]
]

//文档列表项目是否可以使用固定
    IsPinned = false

//文档列表项目是否可以隐藏
    IsMissing = false

//是否记录文档打开次数
    OpenCount = 0

//再次打开加密文档时,是否询问密码
    DecryptionKey =

//打开文件是否使用通用对话框
    UseDefaultState = false

//页面布局.

//有效值为: automatic, single page, facing, book view,

//continuous, continuous facing, continuous book view
    DisplayMode = automatic

//文档滚动距离 (in x and y direction)
    ScrollPos = 0 0

//上次阅读页面的编号
    PageNo = 1

//缩放比例或有效值: fit page, fit width, fit content
    Zoom = fit page

//页面旋转角度, 90 度递增
    Rotation = 0

//窗口状态. 1 is normal, 2 is maximized, 3 is fullscreen, 4 is minimized
    WindowState = 0

//默认位置 (可以任意显示器)
    WindowPos = 0 0 0 0

//是否显示书签
    ShowToc = true

//侧栏宽度
    SidebarDx = 0

//漫画模式,只限于漫画书
    DisplayR2L = false

//在电子书UI中恢复上次读取页所需的数据
    ReparseIdx = 0

//需要确定表的哪些部分已展开的数据.
    TocState =
]
]

//保存上次会话,用于崩溃时恢复会话 (introduced in version 3.1)
SessionData [
[
//data required for restoring the view state of a single tab
    TabStates [
[
//path of the document
        FilePath =

//same as FileStates -> DisplayMode
        DisplayMode = automatic

//number of the last read page
        PageNo = 1

//same as FileStates -> Zoom
        Zoom = fit page

//same as FileStates -> Rotation
        Rotation = 0

//how far this document has been scrolled (in x and y direction)
        ScrollPos = 0 0

//if true, the table of contents was shown when the document was closed
        ShowToc = true

//same as FileStates -> TocState
        TocState =
]
]

//当前选择的标签序列 ( 1 为基数)
    TabIndex = 1

//同步 FileState -> WindowState
    WindowState = 0

//默认位置
    WindowPos = 0 0 0 0

//侧栏关闭时的宽度
    SidebarDx = 0
]
]

//自动更新后重载文档的数据请求 (introduced in version 3.0)
ReopenOnce =

//上次更新时间
TimeOfLastUpdateCheck = 0 0

//在历史记录中保存打开次数
OpenCountWeek = 0

保存后重启 SumatraPDF ,再次打开高级设置。找到 ShortCuts 配置处,添加以下内容:

Shortcuts [
	[
		Cmd = CmdSaveAnnotations
		Key = s, S
	]
	[
		Cmd = CmdDeleteAnnotation
		Key = d, D
	]
	[
		Cmd = CmdEditAnnotations
		Key = e, E
	]
	[
		Cmd = CmdCreateAnnotUnderline
		Key = w, W
	]
	[
		Cmd = CmdCreateAnnotFreeText
		Key = t, T
	]
]

请根据需要修改高级设置内容。

参考

Sublime Text 3 插件 latextools 无法切换编译器? - 知乎
Sublime text 3 + Latex + SumatraPDF反向检索_反向查找 sumatra sublime-CSDN博客
如何优雅地使用 Sublime 编辑 LaTeX - 知乎
LaTex+Sublime+SumatraPDF安装详细教程 - 知乎
SumatraPDF 高级设置 - MTAz - 博客园
配置SumatraPDF快捷键实现Pdf阅读快速标注 - 知乎
Customizing SumatraPDF 3.5.1
调整SumatraPDF暗黑模式_sumatrapdf 黑色-CSDN博客

在Windows中使用FLAC无损音频编解码器

FLAC代表Free Lossless Audio Codec,是一种与MP3类似的音频格式,但它是无损的,意味着在FLAC中压缩音频时不会损失任何质量。这与Zip的工作原理类似,但使用FLAC时,您将获得更好的压缩效果,因为它是专门为音频设计的。您可以在您喜欢的播放器(或您的汽车或家庭立体声设备,请参阅支持的设备)中播放压缩的FLAC文件,就像播放MP3文件一样。

FLAC是最快且支持最广泛的无损音频编解码器,而且是唯一一个同时满足非专有、不受专利束缚、具有开源参考实现、格式和API文档齐全,以及其他多种独立实现条件的编解码器。

如果您不确定要下载什么,请参阅“使用FLAC”部分,了解有关播放FLAC文件、将CD翻录为FLAC等的说明和指南。本部分适用于官方FLAC工具。请参见下面的“额外内容”部分,了解支持或使用FLAC的第三方工具。

所有源代码和二进制文件均可在开源许可证下自由使用和分发。编解码器库在Xiph.org的BSD许可下分发,而插件和命令行工具 (flacmetaflac) 则在GPL下分发。(更多信息,请参阅许可页面。)

源代码:适用于稳定版和测试版的tarballs;还包括Windows(MSVC++)和*nix、*BSD、OS/2、OS X(autotools)的文档和构建系统。您也可以查看开发git存储库
Linux:大多数发行版都包含FLAC软件包,请使用包管理器获取FLAC。如果没有,请尝试rpmfind.netDebian的软件包
Windows:适用于Windows的FLAC(文件flac-X.Y.Z-win.zip包含32位和64位的命令行工具和库)。
Mac OS X:从Homebrew获取适用于OS X的FLAC工具
旧版本:旧版本

下载适用于Windows平台的FLAC

打开上文 Windows 平台的链接,或者打开 github 存储库 release 页面选择合适的版本下载。

如果仅作为命令行工具使用,仅需关注这两个文件。

使用FLAC

使用FLAC时应该多看看官方文档,入门只需关注FLAC - Using FLACFLAC - FAQ部分。
想了解用于转换FLAC文件和编辑元数据的命令行程序,在 Using FLAC 页面找到

Mac OS

If you want to convert audio files to/from FLAC:

Free Audio Converter, a free GUI audio file converter
XLD a nice GUI ripper/converter/burner
MacFLAC. Also includes our command-line programs for converting FLAC files and editing metadata.
MediaRage can edit FLAC tags.

跳转到这个界面——

入门我们仅需关注这部分内容,改一改能用就行。

## EXAMPLES

Some common encoding tasks using flac:

flac abc.wav
    Encode abc.wav to abc.flac using the default compression setting. abc.wav is not deleted.
flac --delete-input-file abc.wav
    Like above, except abc.wav is deleted if there were no errors.
flac --delete-input-file -w abc.wav
    Like above, except abc.wav is deleted if there were no errors or warnings.
flac --best abc.wav
    Encode abc.wav to abc.flac using the highest compression setting.
flac --verify abc.wav
    Encode abc.wav to abc.flac and internally decode abc.flac to make sure it matches abc.wav.
flac -o my.flac abc.wav
    Encode abc.wav to my.flac.
flac -T "TITLE=Bohemian Rhapsody" -T "ARTIST=Queen" abc.wav
    Encode abc.wav and add some tags at the same time to abc.flac.
flac *.wav
    Encode all .wav files in the current directory.
flac abc.aiff
    Encode abc.aiff to abc.flac.
flac abc.rf64
    Encode abc.rf64 to abc.flac.
flac abc.w64
    Encode abc.w64 to abc.flac.
flac abc.flac --force
    This one’s a little tricky: notice that flac is in encode mode by default (you have to specify -d to decode) so this command actually recompresses abc.flac back to abc.flac. –force is needed to make sure you really want to overwrite abc.flac with a new version. Why would you want to do this? It allows you to recompress an existing FLAC file with (usually) higher compression options or a newer version of FLAC and preserve all the metadata like tags too.

Some common decoding tasks using flac:

flac -d abc.flac
    Decode abc.flac to abc.wav. abc.flac is not deleted. NOTE: Without -d it means re-encode abc.flac to abc.flac (see above).
flac -d --force-aiff-format abc.flac
flac -d -o abc.aiff abc.flac
    Two different ways of decoding abc.flac to abc.aiff (AIFF format). abc.flac is not deleted.
flac -d --force-rf64-format abc.flac
flac -d -o abc.rf64 abc.flac
    Two different ways of decoding abc.flac to abc.rf64 (RF64 format). abc.flac is not deleted.
flac -d --force-wave64-format abc.flac
flac -d -o abc.w64 abc.flac
    Two different ways of decoding abc.flac to abc.w64 (Wave64 format). abc.flac is not deleted.
flac -d -F abc.flac
    Decode abc.flac to abc.wav and don’t abort if errors are found (useful for recovering as much as possible from corrupted files). 

将"flac-1.3.2-win\win64\flac.exe"的路径

C:\Users\Administrator\Downloads\Compressed\flac-1.3.2-win\win64

添加到系统环境变量即可作为命令行工具使用。
如果你安装了Foobar2000等支持FLAC的软件,也可以将对应的文件替换,体验新版本。

GPG入门教程

前言

Github设置有一项叫“SSH 与 GPG 公钥”,平常我们看一些教程只需要使用SSH 公钥,好像用不到GPG 公钥,直到我看到这篇有趣的帖子——👨‍💻 震惊!竟然有人在 GitHub 上冒充我的身份!

Git 的 commit 是可以任意修改的,你可以将某个坏 commit 嫁祸给别人,甚至将某个坏仓库的 commit 批量嫁祸给毫不知情的人。
使用一个只有我们自己手中拥有的 GPG 私钥对我们的 commit 进行签名,可以让 GitHub 确认我们本次 commit 是真实且是本人操作的。这样,别有用心的他人就无法以我们的身份创建「被签名」的 commit。在 GitHub 上使用的 GPG 密钥和我们的 SSH 密钥并不一样,后者 SSH key 唯一存在的原因是为了向 GitHub 证明身份,用于向我们拥有权限的仓库中进行 commit,而前者 GPG key 则是为了「证明我拥有本次 commit 的著作权」,也只有用 GPG 私钥签名的 commit 在 GitHub 上才会显示如下图的 Verified 绿色钦定小标标。

今天我们就来了解一下GPG是什么,以及怎么用。

什么是GPG

1991年,菲利普·齐墨尔曼(Philip R. Zimmermann)发明了PGP加密算法,第一个版本的 PGP 程式与其中使用的加密算法 BassOmatic 都是由齐默尔曼发展出来。但 PGP 是商业软件,不能自由使用。所以,自由软件基金会决定开发一款自由软件以替代 PGP。这就是GPG的由来。

PGP 与 GPG 的关系

PGP

  • Pretty Good Privacy,是一个被设计用来加密信息,保护隐私的软件。
  • OpenPGP 是与最初 PGP 工具兼容的 IETF 标准
  • 现在提到“PGP”, 基本上是说 OpenPGP 标准。

GPG

  • 即GnuPG。
  • GnuPG (“Gnu Privacy Guard”)是实现了 OpenPGP 标准的自由软件。
  • GnuPG 的命令行工具称为 “gpg”

GPG KEY 使用场景

  • 使用 GPG 密钥来签名你的 git commits
  • 使用公钥验证第三方软件的签名
  • 使用 gpg 公钥来加密你的邮件
  • 使用公钥认证来实现授权登陆 (Public Key Authentication)

Linux下使用GPG签名/加密

安装 GPG

Linux下GPG有两种安装方式。可以下载源码,自己编译安装。

./configure
make
make install

也可以安装编译好的二进制包

# Debian / Ubuntu 环境
sudo apt-get install gnupg

# Fedora 环境
yum install gnupg

# Arch 环境
sudo pacman -S gnupg

完成后键入以下命令

gpg --help

如果屏幕显示GPG的帮助,就表示安装成功。

生成密钥

gpg –-gen-key

回车以后会跳出

gpg (GnuPG) 2.2.41-unknown; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory '/c/Users/Administrator/.gnupg' created
gpg: keybox '/c/Users/Administrator/.gnupg/pubring.kbx' created
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.

GnuPG needs to construct a user ID to identify your key.

Real name:

使用“gpg –full-generate-key”生成GPG密钥,有详细的配置参数。

gpg (GnuPG) 2.2.41-unknown; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection?

默认选择第一个选项,表示加密和签名都使用RSA算法。
然后,系统就会问你密钥的长度。

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072)

接着,设定密钥的有效期。

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)

个人使用建议选择第一个选项,即永不过期,回车即可。接下来,系统让你确认。

Key does not expire at all
Is this correct? (y/N)

输入y,系统要求提供个人信息。

GnuPG needs to construct a user ID to identify your key.

Real name:
Email address:
Comment:

“Comment"这一栏可以空着。
然后你的用户ID就生成了。

You selected this USER-ID:
    "xiaxi626 <aijiang1220966821@hotmail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?

系统会让你最后确认一次。
输入O表示"确定”。
接着,系统会让你设定一个私钥的密码。

您需要一个密码来保护您的私钥:

二次输入确认密码后,系统就开始生成密钥了。

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

几分钟以后,系统提示密钥已经生成了。

gpg: /c/Users/Administrator/.gnupg/trustdb.gpg: trustdb created
gpg: directory '/c/Users/Administrator/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/c/Users/Administrator/.gnupg/openpgp-revocs.d/2DF491A4DB677422C13A5398102ED6A053101F78.rev'
public and secret key created and signed.

请注意上面的字符串"2DF491A4DB677422C13A5398102ED6A053101F78",这是"用户ID"的Hash字符串,可以用来替代"用户ID"。
这时,最好再生成一张"撤销证书",以备以后密钥作废时,可以请求外部的公钥服务器撤销你的公钥。

gpg --gen-revoke [用户ID]

上面的"用户ID"部分,可以填入你的邮件地址或者Hash字符串(以下同)。
系统提示。

Create a revocation certificate for this key? (y/N)

密钥管理

查看公钥

list-keys参数列出系统中已有的密钥。

gpg --list-key

网上教程的显示结果:

  /home/ruanyf/.gnupg/pubring.gpg
  -------------------------------
  pub 4096R/EDDD6D76 2013-07-11
  uid Ruan YiFeng <yifeng.ruan@gmail.com>
  sub 4096R/3FA69BE4 2013-07-11

第一行显示公钥文件名(pubring.gpg),第二行显示公钥特征(4096位,Hash字符串和生成时间),第三行显示"用户ID",第四行显示私钥特征。
本机输入gpg --list-key 显示:

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
/c/Users/Administrator/.gnupg/pubring.kbx
-----------------------------------------
pub   rsa3072 2023-08-25 [SC]
      2DF491A4DB677422C13A5398102ED6A053101F78
uid           [ultimate] xiaxi626 <aijiang1220966821@hotmail.com>
sub   rsa3072 2023-08-25 [E]

pubring.kbx是密钥块资源。
如果你要从密钥列表中删除某个密钥,可以使用delete-key参数。

gpg --delete-key [用户ID]

删除公钥前会提示

gpg: there is a secret key for public key "[公钥]"!
gpg: use option "--delete-secret-keys" to delete it first.

查看私钥

list-secret-keys参数列出系统中已有的私钥。

gpg --list-secret-keys

显示结果如下:

/c/Users/Administrator/.gnupg/pubring.kbx
-----------------------------------------
sec#  rsa3072 2023-08-25 [SC]
      2DF491A4DB677422C13A5398102ED6A053101F78
uid           [ultimate] xiaxi626 <aijiang1220966821@hotmail.com>
ssb   rsa3072 2023-08-25 [E]

输出密钥

公钥文件(.gnupg/pubring.gpg)以二进制形式储存,armor参数可以将其转换为ASCII码显示。

gpg --armor --output public-key.txt --export [用户ID]

or

gpg -a --export [用户ID] > public-key.asc

“用户ID"指定哪个用户的公钥,output参数指定输出文件名(public-key.txt)。

类似地,export-secret-keys参数可以转换私钥。

gpg --armor --output private-key.txt --export-secret-keys

or

gpg -a --export-secret-keys [用户ID] > private-key.asc

上传公钥

公钥服务器是网络上专门储存用户公钥的服务器。send-keys参数可以将公钥上传到服务器。

gpg --send-keys [用户ID] --keyserver hkp://subkeys.pgp.net

使用上面的命令,你的公钥就被传到了服务器subkeys.pgp.net,然后通过交换机制,所有的公钥服务器最终都会包含你的公钥。
由于公钥服务器没有检查机制,任何人都可以用你的名义上传公钥,所以没有办法保证服务器上的公钥的可靠性。通常,你可以在网站上公布一个公钥指纹,让其他人核对下载到的公钥是否为真。fingerprint参数生成公钥指纹。

gpg --fingerprint [用户ID]

输入密钥

除了生成自己的密钥,还需要将他人的公钥或者你的其他密钥输入系统。这时可以使用import参数。

gpg --import [密钥文件]

为了获得他人的公钥,可以让对方直接发给你,或者到公钥服务器上寻找。

gpg --keyserver hkp://subkeys.pgp.net --search-keys [用户ID]

正如前面提到的,我们无法保证服务器上的公钥是否可靠,下载后还需要用其他机制验证.

加密和解密

加密

假定有一个文本文件demo.txt,怎样对它加密呢?
encrypt参数用于加密。

gpg --recipient [用户ID] --output demo.en.txt --encrypt demo.txt

recipient参数指定接收者的公钥,output参数指定加密后的文件名,encrypt参数指定源文件。运行上面的命令后,demo.en.txt就是已加密的文件,可以把它发给对方。

gpg -ea -r [用户ID] filename

即会生成filename.asc的加密文件。

解密

对方收到加密文件以后,就用自己的私钥解密。

gpg --decrypt demo.en.txt --output demo.de.txt

decrypt参数指定需要解密的文件,output参数指定解密后生成的文件。运行上面的命令,demo.de.txt就是解密后的文件。

GPG允许省略decrypt参数。

gpg demo.en.txt

运行上面的命令以后,解密后的文件内容直接显示在标准输出。

gpg -o filename -d filename.asc

运行上面的命令以后,输入私钥密码。
即可把filename.asc的加密文件解密成filename文件。

签名

对文件签名

有时,我们不需要加密文件,只需要对文件签名,表示这个文件确实是我本人发出的。sign参数用来签名。

gpg --sign demo.txt

运行上面的命令后,当前目录下生成demo.txt.gpg文件,这就是签名后的文件。这个文件默认采用二进制储存,如果想生成ASCII码的签名文件,可以使用clearsign参数。

gpg --clearsign demo.txt

运行上面的命令后 ,当前目录下生成demo.txt.asc文件,后缀名asc表示该文件是ASCII码形式的。

如果想生成单独的签名文件,与文件内容分开存放,可以使用detach-sign参数。

gpg --detach-sign demo.txt

运行上面的命令后,当前目录下生成一个单独的签名文件demo.txt.sig。该文件是二进制形式的,如果想采用ASCII码形式,要加上armor参数。

gpg --armor --detach-sign demo.txt

签名+加密

上一节的参数,都是只签名不加密。如果想同时签名和加密,可以使用下面的命令。

gpg --local-user [发信者ID] --recipient [接收者ID] --armor --sign --encrypt demo.txt

local-user参数指定用发信者的私钥签名,recipient参数指定用接收者的公钥加密,armor参数表示采用ASCII码形式显示,sign参数表示需要签名,encrypt参数表示指定源文件。

验证签名

我们收到别人签名后的文件,需要用对方的公钥验证签名是否为真。verify参数用来验证。

gpg --verify demo.txt.asc demo.txt

举例来说,openvpn网站就提供每一个下载包的gpg签名文件。你可以根据它的说明,验证这些下载包是否为真。

在 Gitee 上使用 GPG key 来签名 commit

  1. Kleopatra创建OpenPGP密钥对,输入用户名和邮箱,注意邮箱必须与 Gitee 提交邮箱一致;
  2. 导出公钥和私钥文件;
  3. 打开公钥文件,复制内容到Gitee gpg_keys,复制页面GPG密钥的指纹字符串;
  4. 输入【查看公钥、私钥命令 | 输入密钥命令】,输入密钥到.gnupg文件夹;
  5. 输入查看公钥或查看私钥命令,得到"用户ID"的Hash字符串,和Gitee gpg_keys页面GPG公钥指纹字符串是一样的;
  6. 配置 Git
git config --global user.signingkey [Gitee网页GPG公钥指纹/本地"用户ID"的Hash字符串]
  1. 输入git config --global --list检查git config是否配置成功,输入git config --global --edit可修改配置;

  2. 添加到 Gitee 账户,查看GPG 公钥验证状态,GPG 邮箱为当前用户已激活邮箱验证才能通过;

    • 删除 仅移除 GPG 公钥,验证通过的 Commit 签名状态保持不变
    • 注销 移除 GPG 公钥并且将已验证的 Commit 签名状态修改为未验证
  3. 使用 GPG 签名进行提交

git commit -S -m "YOUR COMMIT MESSAGE"
git log --show-signature # 查看签名状态
  1. 提交的显示结果
git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 564 bytes | 564.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.4]
To gitee.com:xiaxi626/gpg-test.git
   8013057..53ca0f6  master -> master

  1. 签名状态,Commit出现Verified | This commit was signed with the committer's verified signature,成功。

    • Commit 验证通过的条件为:commit 提交邮箱与 commit GPG 签名所使用的公钥邮箱一致且 GPG 公钥验证通过。
  2. 查看 GPG 公钥

    • 输入 https://gitee.com/\<username>.gpg
    • 选择用户个人资料右上角的设置页面进入安全设置 - GPG 公钥
    • Gitee 平台 GPG 公钥:https://gitee.com/gitee.gpg

在 Github 上使用 GPG key 来签名 commit

Github 配置 GPG

配置 GPG 公钥到仓库

Github Setting -> SSH and GPG keys -> New GPG Key 导入即可

本地代码仓库启用GPG Sign

通过gpg --list-keys查看pub GPG key ID,后设置git签名时用的key

全局设置

# 配置已经生成的GPG Key ID
git config --global user.signingkey <pub GPG key ID>
# 配置启用GPG签名
git config --global commit.gpgsign true

指定仓库设置,需要进入代码目录:

# 配置已经生成的GPG Key ID
git config --local user.signingkey <pub GPG key ID>
# 配置启用GPG签名
git config --local commit.gpgsign true

重启 gpg-agent

第一次配置,必须重启,否则签名会失败,命令如下:

gpgconf –kill gpg-agent

上述步骤示例

Administrator@AUTOBVT-Q90417J MINGW64 ~/Desktop
$ git config --global user.signingkey 69A20512441F53BA4F13F93F74EA6A7E693AEF20

Administrator@AUTOBVT-Q90417J MINGW64 ~/Desktop
$ git config --global commit.gpgsign true

Administrator@AUTOBVT-Q90417J MINGW64 ~/Desktop
$ gpgconf –kill gpg-agent
gpg:OpenPGP:/usr/bin/gpg
gpgsm:S/MIME:/usr/bin/gpgsm
gpg-agent:Private Keys:/usr/bin/gpg-agent
scdaemon:Smartcards:/usr/lib/gnupg/scdaemon
dirmngr:Network:/usr/bin/dirmngr
pinentry:Passphrase Entry:/usr/bin/pinentry

Windows 上的 git-bash 上默认的 /usr/bin 目录在:C:\Program Files\Git\usr\bin\。

关闭GPG签名

所有仓库:

git config --global commit.gpgsign false
git config --global --unset commit.gpgsign

本地仓库:

git config --local commit.gpgsign false
git config --local --unset commit.gpgsign

git 使用

提交

git commit -am "feature: something"
git push origin develop

然后我们可以在 git 中看到 Verified 的标识。

如果不设置git config --global commit.gpgsign true,提交的时候加上一个 -S 参数就可以为提交签名:

git commit -S -m `your commit message`

提交tag时签名

git tag -s ...

查看日志

git log --show-signature -1

使用 Kleopatra 来签名/加密文件

Gpg4win(GNU Privacy Guard for Windows)是一个加密软件,用于对文件和电子邮件进行签名和加密。

它能够生成OpenPGP密钥对、签名/验证、加密/解密,还可以建立S/MIME认证请求。

OpenPGP 证书高级设置

1、密钥类型

  • RSA
  • DSA
  • ECDSA/EdDSA(默认)

2、证书用途

  • 签名
  • 证书 仅加密(默认)
  • 验证
  • 有效期结束于

新建密钥对后,界面会显示一条证书信息,包含名称、电子邮件、用户编号(认证的/已吊销)、有效期、密钥ID。
你可以双击证书来添加用户ID、认证用户IDs、吊销证书、吊销用户ID等。

签名/加密文件

  1. 新建OpenPGP秘钥对
  2. 输入名称和电子邮件
  3. 需要进行高级设置可以点击高级设置进行设置,默认密钥类型为ECDSA,可以根据需要设置过期时间
  4. 勾选“需要密码句保护生成的密钥”,输入密码句
  5. 密码复杂度低时会提示你重新输入密码,如果密码要求不是太高,直接点击Take this one anyway,如果需要返回重新设置,点击Enter new passphrase
  6. 秘钥对创建成功,右键“备份私钥”,生成密钥对的副本,输入密码,导出成功
  7. 导出公钥,右键该加密证书,导出公钥,选择公钥的导出目录,导出
  8. 将需要的加密的文件拖入到kleopatra中,点击签名/加密
  9. 选择加密证书以及加密文件存储位置,输入密码,加密完成

解密/验证文件

  1. 首先需要得到对方的加密公钥和私钥文件
  2. 先导入公钥文件(.asc),为本地创建一个证书
  3. 创建完成后,认证证书公钥,只认证自己,输入刚刚创建的本地证书密码
  4. 继续导入,选择私钥文件(.gpg)导入,查看证书详情,证书颜色已经变深,可以进行解密操作了
  5. 将对方发送的加密文件(.gpg)拖入到kleopatra中进行解密,点击 Save All 保存,得到解密后的文件

想要对方加密文件给你,你需要提供公钥给对方,对方用你的公钥进行加密,发送给你加密文件后,你需用你的私钥进行解密。

可以进行解密操作后,双击签名/加密输出的文件和点击解密/校验效果是一样的。

关于提交签名验证

使用 GPG、SSH、 或 S/MIME,可以在本地对标记和提交进行签名。 这些标记或提交在 GitHub 上标示为已验证,便于其他人信任更改来自可信的来源。

您可以在本地签署提交和标签,让其他人对您所做更改的源充满信心。 如果提交或标记具有可加密验证的 GPG、SSH、 或 S/MIME 签名,GitHub 会将提交或标记标示为“已验证”或“部分验证”。

存储库提交列表中提交的屏幕截图。 “已验证”以橙色轮廓突出显示。

对于大多数个人用户,GPG 或 SSH 会是对提交进行签名的最佳选择。 在较大型组织的环境中通常需要 S/MIME 签名。 SSH 签名是最容易生成的。 甚至可以将现有身份验证密钥上传到 GitHub 以用作签名密钥。 生成 GPG 签名密钥比生成 SSH 密钥更复杂,但 GPG 具有 SSH 所没有的功能。 GPG 密钥可以在不再使用时过期或撤销。 GitHub 将已使用此类密钥进行签名的提交显示为“已验证”,除非密钥标记为已泄露。 SSH 密钥没有此功能。

GPG 提交签名验证

GitHub 使用 OpenPGP 库来确认本地签名的提交和标记,是否根据你在 GitHub.com 上添加到帐户的公钥进行加密验证。

SSH 提交签名验证

可以使用 SSH 通过自己生成的 SSH 密钥对提交进行签名。 有关详细信息,请查看 user.SigningkeyGit 参考文档。 如果已使用 SSH 密钥向 GitHub 进行了身份验证,还可以再次上传该相同密钥以用作签名密钥。 可以添加到帐户的签名密钥数没有限制。

GitHub 使用 ssh_data(一种开放源代码 Ruby 库)来确认本地签名的提交和标记是否根据在 GitHub.com 上添加到帐户的公钥进行加密验证。

参考

👨‍💻 震惊!竟然有人在 GitHub 上冒充我的身份!
关于提交签名验证 - GitHub 文档
GPG入门教程 - 阮一峰的网络日志
Linux下GPG的使用
推荐一款好用的文件加密传输软件——Kleopatra(含详细使用文档)
Kleopatra文件的加密解密遇到部分问题_kleopatra解密_少猿的博客
使用 GPG Key 来构建签名、加密及认证体系 - 知乎
简明 GPG 概念 - 知乎
2021年,用更现代的方法使用PGP(上) - 知乎
PGP——密码技术的完美组合 - 简书
如何在 Gitee 上使用 GPG | Gitee 产品文档
使用 GPG 签名 Git Commit-谢先斌的博客
修改/重置 git 的全局配置 | 血衫非弧の一存

真白萌论坛推荐阅读

真白萌 | 真白萌论坛使用指南

真白萌 | 中日对照审核稿的简单处理

真白萌 | 日语入门&翻译入门

真白萌 | 日语入门指南1.35版本

真白萌 | 给想要学习日语的新人一点建议(1/8/2019更新)

真白萌 | [旧站搬运]常见翻译错误和误区汇总

真白萌 | 简单长难句的处理

真白萌 | 被动和使役的处理

真白萌 | 翻译警察记事本

学习Git 分支 (learn-git-branching)

摘自:https://oschina.gitee.io/learn-git-branching/
教程文本,不包含演示和题目。
Learn Git Branching 答案汇总

主要

基础篇


循序渐进地介绍 Git 主要命令

1: Git Commit

Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然后再粘贴一样,但比复制粘贴优雅许多!

Git 希望提交记录尽可能地轻量,因此在你每次进行提交时,它并不会盲目地复制整个目录。条件允许的情况下,它会将当前版本与仓库中的上一个版本进行对比,并把所有的差异打包到一起作为一个提交记录。

Git 还保存了提交的历史记录。这也是为什么大多数提交记录的上面都有父节点的原因 —— 我们会在图示中用箭头来表示这种关系。对于项目组的成员来说,维护提交历史对大家都有好处。

关于提交记录太深入的东西咱们就不再继续探讨了,现在你可以把提交记录看作是项目的快照。提交记录非常轻量,可以快速地在这些提交记录之间切换!

咱们来实际操作一下,看看提交记录是怎样的。右边展示了一个(小型)Git 代码库。当前有两个提交记录 —— 初始提交 C0 和其后可能包含某些有用修改的提交 C1。

点击下面的按钮创建一个新的提交记录

git commit

好了!非常棒!我们刚才修改了代码库,并把这些修改保存成了一个提交记录 C2。C2 的父节点是 C1,父节点是当前提交中变更的基础。

2: Git Branch

Git 的分支也非常轻量。它们只是简单地指向某个提交纪录 —— 仅此而已。所以许多 Git 爱好者传颂:

早建分支!多用分支!

这是因为即使创建再多分的支也不会造成储存或内存上的开销,并且按逻辑分解工作到不同的分支要比维护那些特别臃肿的分支简单多了。

在将分支和提交记录结合起来后,我们会看到两者如何协作。现在只要记住使用分支其实就相当于在说:“我想基于这个提交以及它所有的父提交进行新的工作。”

咱们通过实际操作来看看分支是什么样子的。

接下来,我们将要创建一个到名为 newImage 的分支。

git branch newImage

看到了吗,创建分支就是这么容易!新创建的分支 newImage 指向的是提交记录 C1。

现在咱们试着往新分支里提交一些东西。点击下面的按钮

git commit

哎呀!为什么 master 分支前进了,但 newImage 分支还待在原地呢?!这是因为我们没有“在”这个新分支上,看到 master 分支上的那个星号(*)了吗?这表示当前所在的分支是 master。

现在咱们告诉 Git 我们想要切换到新的分支上

git checkout <name>

下面的命令会让我们在提交修改之前先切换到新的分支上

git checkout newImage; git commit

这就对了!我们的修改已经保存到新的分支里了。

OK,你们都已经准备好使用分支了。当前窗口关闭后, 创建一个名为 bugFix 的新分支,然后切换过去。

对了,有个更简洁的方式:如果你想创建一个新的分支同时切换到新创建的分支的话,可以通过 git checkout -b <your-branch-name> 来实现。

3: Git Merge

太好了! 我们已经知道如何提交以及如何使用分支了。接下来咱们看看如何将两个分支合并到一起。就是说我们新建一个分支,在其上开发某个新功能,开发完成后再合并回主线。

咱们先来看一下第一种方法 —— git merge。在 Git 中合并两个分支时会产生一个特殊的提交记录,它有两个父节点。翻译成自然语言相当于:“我要把这两个父节点本身及它们所有的祖先都包含进来。”

通过图示更容易理解一些,咱们到下一页看一下。

我们准备了两个分支,每个分支上各有一个独有的提交。这意味着没有一个分支包含了我们修改的所有内容。咱们通过合并这两个分支来解决这个问题。

我们要把 bugFix 合并到 master 里

git merge bugFix

哇哦!看见了吗?首先,master 现在指向了一个拥有两个父节点的提交记录。假如从 master 开始沿着箭头向上看,在到达起点的路上会经过所有的提交记录。这意味着 master 包含了对代码库的所有修改。↓↓↓

咱们再把 master 分支合并到 bugFix:

git checkout bugFix; git merge master

因为 master 继承自 bugFix,Git 什么都不用做,只是简单地把 bugFix 移动到 master 所指向的那个提交记录。

现在所有提交记录的颜色都一样了,这表明每一个分支都包含了代码库的所有修改!大功告成!

要想通过这一关,需要以下几步:

创建新分支 bugFix
用 git checkout bugFix 命令切换到该分支
提交一次
用 git checkout master 切换回 master
再提交一次

用 git merge 把 bugFix 合并到 master

你随时都可以用“objective”命令来打开这个对话框!*

4: Git Rebase

第二种合并分支的方法是 git rebase。Rebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。

Rebase 的优势就是可以创造更线性的提交历史,这听上去有些难以理解。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。

咱们还是实际操作一下吧……

还是准备了两个分支;注意当前所在的分支是 bugFix(星号标识的是当前分支)

我们想要把 bugFix 分支里的工作直接移到 master 分支上。移动以后会使得两个分支的功能看起来像是按顺序开发,但实际上它们是并行开发的。

咱们这次用 git rebase 实现此目标

git rebase master

怎么样?!现在 bugFix 分支上的工作在 master 的最顶端,同时我们也得到了一个更线性的提交序列。

注意,提交记录 C3 依然存在(树上那个半透明的节点),而 C3’ 是我们 Rebase 到 master 分支上的 C3 的副本。

现在唯一的问题就是 master 还没有更新,下面咱们就来更新它吧……

现在我们切换到了 master 上。把它 rebase 到 bugFix 分支上……

git rebase bugFix

好了!由于 bugFix 继承自 master,所以 Git 只是简单的把 master 分支的引用向前移动了一下而已。

要完成此关,执行以下操作:

新建并切换到 bugFix 分支
提交一次
切换回 master 分支再提交一次
再次切换到 bugFix 分支,rebase 到 master 上

祝你好运!

高级篇


要开始介绍 Git 的超棒特性了,快来吧!

1: 分离 HEAD

在提交树上移动

在接触 Git 更高级功能之前,我们有必要先学习在你项目的提交树上前后移动的几种方法。

一旦熟悉了如何在 Git 提交树上移动,你驾驭其它命令的能力也将水涨船高!

我们首先看一下 “HEAD”。 HEAD 是一个对当前检出记录的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。

HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。

HEAD 通常情况下是指向分支名的(如 bugFix)。在你提交时,改变了 bugFix 的状态,这一变化通过 HEAD 变得可见。

下面咱们通过实际操作看一下。我们将会观察提交前后 HEAD 的位置。

git checkout C1; git checkout master; git commit; git checkout C2

看到了吗? HEAD 指向了 master,随着提交向前移动。

(译者注:实际这些命令并不是真的在查看 HEAD 指向,看下一屏就了解了。如果想看 HEAD 指向,可以通过 cat .git/HEAD 查看, 如果 HEAD 指向的是一个引用,还可以用 git symbolic-ref HEAD 查看它的指向。但是该程序不支持这两个命令)

分离的 HEAD

分离的 HEAD 就是让其指向了某个具体的提交记录而不是分支名。在命令执行之前的状态如下所示:

HEAD -> master -> C1

HEAD 指向 master, master 指向 C1

git checkout C1

现在变成了

HEAD -> C1

想完成此关,从 bugFix 分支中分离出 HEAD 并让其指向一个提交记录。

通过哈希值指定提交记录。每个提交记录的哈希值显示在代表提交记录的圆圈中。

2: 相对引用(^)

通过指定提交记录哈希值的方式在 Git 中移动不太方便。在实际应用时,并没有像本程序中这么漂亮的可视化提交树供你参考,所以你就不得不用 git log 来查查看提交记录的哈希值。

并且哈希值在真实的 Git 世界中也会更长(译者注:基于 SHA-1,共 40 位)。例如前一关的介绍中的提交记录的哈希值可能是 fed2da64c0efc5293610bdd892f82a58e8cbc5d8。舌头都快打结了吧…

比较令人欣慰的是,Git 对哈希的处理很智能。你只需要提供能够唯一标识提交记录的前几个字符即可。因此我可以仅输入fed2 而不是上面的一长串字符。

正如我前面所说,通过哈希值指定提交记录很不方便,所以 Git 引入了相对引用。这个就很厉害了!

使用相对引用的话,你就可以从一个易于记忆的地方(比如 bugFix 分支或 HEAD)开始计算。

相对引用非常给力,这里我介绍两个简单的用法:

使用 ^ 向上移动 1 个提交记录
使用 ~<num> 向上移动多个提交记录,如 ~3

首先看看操作符 (^)。把这个符号加在引用名称的后面,表示让 Git 寻找指定提交记录的父提交。

所以 master^ 相当于“master 的父节点”。

master^^ 是 master 的第二个父节点

现在咱们切换到 master 的父节点

git checkout master^

搞定。这种方式是不是比输入哈希值方便多了?!

你也可以将 HEAD 作为相对引用的参照。下面咱们就用 HEAD 在提交树中向上移动几次。

git checkout C3; git checkout HEAD^; git checkout HEAD^; git checkout HEAD^

很简单吧?!我们可以一直使用 HEAD^ 向上移动。

要完成此关,切换到 bugFix 的父节点。这会进入分离 HEAD 状态。

如果你愿意的话,使用哈希值也可以过关,但请尽量使用相对引用!

3: 相对引用2(~)

“~”操作符

如果你想在提交树中向上移动很多步的话,敲那么多 ^ 貌似也挺烦人的,Git 当然也考虑到了这一点,于是又引入了操作符 ~。

该操作符后面可以跟一个数字(可选,不跟数字时与 ^ 相同,向上移动一次),指定向上移动多少次。咱们还是通过实际操作看一下吧

咱们用 ~<num> 一次后退四步。

git checkout HEAD~4

多么的简洁 —— 相对引用就是方便啊!

强制修改分支位置

你现在是相对引用的专家了,现在用它来做点实际事情。

我使用相对引用最多的就是移动分支。可以直接使用 -f 选项让分支指向另一个提交。例如:

git branch -f master HEAD~3

上面的命令会将 master 分支强制指向 HEAD 的第 3 级父提交。

现在咱们来演示一下刚才的命令:

git branch -f master HEAD~3

这就对了! 相对引用为我们提供了一种简洁的引用提交记录 C1 的方式, 而 -f 则容许我们将分支强制移动到那个位置。

既然你已经看过相对引用与强制移动分支的演示了,那么赶快使用这些技巧来挑战这一关吧!

要完成此关,移动 HEAD,master 和 bugFix 到目标所示的位置。

4: 撤销变更

在 Git 里撤销变更的方法很多。和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。我们这个应用主要关注的是后者。

主要有两种方法用来撤销变更 —— 一是 git reset,还有就是 git revert。接下来咱们逐个进行讲解。

Git Reset

git reset 通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。

让我们来看看演示:

git reset HEAD~1

漂亮! Git 把 master 分支移回到 C1;现在我们的本地代码库根本就不知道有 C2 这个提交了。

Git Revert

虽然在你的本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的哦!

为了撤销更改并分享给别人,我们需要使用 git revert。来看演示:

git revert HEAD~1

奇怪!在我们要撤销的提交记录后面居然多了一个新提交!这是因为新提交记录 C2’ 引入了更改 —— 这些更改刚好是用来撤销 C2 这个提交的。也就是说 C2’ 的状态与 C1 是相同的。

revert 之后就可以把你的更改推送到远程仓库与别人分享啦。

要完成此关,分别撤销 local 分支和 pushed 分支上的最近一次提交。共需要撤销两个提交(每个分支一个)。

记住 pushed 是远程分支,local 是本地分支 —— 这么说你应该知道用分别哪种方法了吧?

移动提交记录


自由修改提交树

1: Git Cherry-pick

整理提交记录

到现在我们已经学习了 Git 的基础知识 —— 提交、分支以及在提交树上移动。 这些概念涵盖了 Git 90% 的功能,同样也足够满足开发者的日常需求

然而, 剩余的 10% 在处理复杂的工作流时(或者当你陷入困惑时)可能就显得尤为重要了。接下来要讨论的这个话题是“整理提交记录” —— 开发人员有时会说“我想要把这个提交放到这里, 那个提交放到刚才那个提交的后面”, 而接下来就讲的就是它的实现方式,非常清晰、灵活,还很生动。

看起来挺复杂, 其实是个很简单的概念。

Git Cherry-pick

本系列的第一个命令是 git cherry-pick, 命令形式为:

git cherry-pick <提交号>...

如果你想将一些提交复制到当前所在的位置(HEAD)下面的话, Cherry-pick 是最直接的方式了。我个人非常喜欢 cherry-pick,因为它特别简单。

咱们还是通过例子来看一下!

这里有一个仓库, 我们想将 side 分支上的工作复制到 master 分支,你立刻想到了之前学过的 rebase 了吧?但是咱们还是看看 cherry-pick 有什么本领吧。

git cherry-pick C2 C4

这就是了!我们只需要提交记录 C2 和 C4,所以 Git 就将被它们抓过来放到当前分支下了。 就是这么简单!

要通过此关, 只需要简单的将三个分支中的提交记录复制到 master 上就可以了。目标窗口展示了我们想要哪些提交记录,如果你不小心关掉了的话,通过 show goal 命令可以打开,左上角也有“显示目标按钮”

2: 交互式 rebase

交互式的 rebase

当你知道你所需要的提交记录(并且还知道这些提交记录的哈希值)时, 用 cherry-pick 再好不过了 —— 没有比这更简单的方式了。

但是如果你不清楚你想要的提交记录的哈希值呢? 幸好 Git 帮你想到了这一点, 我们可以利用交互式的 rebase —— 如果你想从一系列的提交记录中找到想要的记录, 这就是最好的方法了

咱们具体来看一下……

交互式 rebase 指的是使用带参数 –interactive 的 rebase 命令, 简写为 -i

如果你在命令后增加了这个选项, Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改。

在实际使用时,所谓的 UI 窗口一般会在文本编辑器 —— 如 Vim —— 中打开一个文件。 考虑到课程的初衷,我弄了一个对话框来模拟这些操作。

当 rebase UI界面打开时, 你能做3件事:

调整提交记录的顺序(通过鼠标拖放来完成)
删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
合并提交。 遗憾的是由于某种逻辑的原因,我们的课程不支持此功能,因此我不会详细介绍这个操作。简而言之,它允许你把多个提交记录合并成一个。

接下来咱们看个实例

当你点击下面的按钮时,会出现一个交互对话框。对提交记录做个排序(当然你也可以删除某些提交),点击确定看结果

git rebase -i HEAD~4

Git 严格按照你在对话框中指定的方式进行了复制。

要通过本关, 做一次交互式的 rebase,整理成目标窗口中的提交顺序。 记住,你随时都可以用 undo、reset 修正错误,这是不会记入步数的 :D

杂项


Git 技术、技巧与贴士大集合

1: 只取一个提交记录

本地栈式提交

来看一个在开发中经常会遇到的情况:我正在解决某个特别棘手的 Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。

这些调试和打印语句都在它们各自的提交记录里。最后我终于找到了造成这个 Bug 的根本原因,解决掉以后觉得沾沾自喜!

最后就差把 bugFix 分支里的工作合并回 master 分支了。你可以选择通过 fast-forward 快速合并到 master 分支上,但这样的话 master 分支就会包含我这些调试语句了。你肯定不想这样,应该还有更好的方式……

实际我们只要让 Git 复制解决问题的那一个提交记录就可以了。跟之前我们在“整理提交记录”中学到的一样,我们可以使用

git rebase -i
git cherry-pick

来达到目的。

由于我们刚刚闯过类似的关卡,所以要不要再尝试一次就看你自己了。但是如果你想试一把的话,确保 master 分支能得到 bugFix 分支上的相关提交。

2: 提交的技巧 #1

接下来这种情况也是很常见的:你之前在 newImage 分支上进行了一次提交,然后又基于它创建了 caption 分支,然后又提交了一次。

此时你想对的某个以前的提交记录进行一些小小的调整。比如设计师想修改一下 newImage 中图片的分辨率,尽管那个提交记录并不是最新的了。

我们可以通过下面的方法来克服困难:

先用 git rebase -i 将提交重新排序,然后把我们想要修改的提交记录挪到最前
然后用 commit --amend 来进行一些小修改
接着再用 git rebase -i 来将他们调回原来的顺序
最后我们把 master 移到修改的最前端(用你自己喜欢的方法),就大功告成啦!

当然完成这个任务的方法不止上面提到的一种(我知道你在看 cherry-pick 啦),之后我们会多点关注这些技巧啦,但现在暂时只专注上面这种方法。 最后有必要说明一下目标状态中的那几个’ —— 我们把这个提交移动了两次,每移动一次会产生一个 ‘;而 C2 上多出来的那个是我们在使用了 amend 参数提交时产生的,所以最终结果就是这样了。

也就是说,我在对比结果的时候只会对比提交树的结构,对于 ’ 的数量上的不同,并不纳入对比范围内。只要你的 master 分支结构与目标结构相同,我就算你通过。

3: 提交的技巧 #2

如果你还没有完成“提交的技巧 #1”(前一关)的话,请选通过以后再来!

正如你在上一关所见到的,我们可以使用 rebase -i 对提交记录进行重新排序。只要把我们想要的提交记录挪到最前端,我们就可以很轻松的用 –amend 修改它,然后把它们重新排成我们想要的顺序。

但这样做就唯一的问题就是要进行两次排序,而这有可能造成由 rebase 而导致的冲突。下面还是看看 git cherry-pick 是怎么做的吧。

要在心里牢记 cherry-pick 可以将提交树上任何地方的提交记录取过来追加到 HEAD 上(只要不是 HEAD 上游的提交就没问题)。

来看看这个例子:

git  cherry-pick C2

看到了吧?我们继续

这一关的目标和上一关一样,通过 –amend 改变提交记录 C2,但你不能用 rebase -i。自己想想要怎么解决吧! :D

对了,提交记录上面的’的数量并不重要,只是引用的不同而已。也就是说如果你的最终结果在某个提交记录上多了个’,我也会算你通过的。

4: Git Tags

相信通过前面课程的学习你已经发现了:分支很容易被人为移动,并且当有新的提交时,它也会移动。分支很容易被改变,大部分分支还只是临时的,并且还一直在变。

你可能会问了:有没有什么可以永远指向某个提交记录的标识呢,比如软件发布新的大版本,或者是修正一些重要的 Bug 或是增加了某些新特性,有没有比分支更好的可以永远指向这些提交的方法呢?

当然有了!Git 的 tag 就是干这个用的啊,它们可以(在某种程度上 —— 因为标签可以被删除后重新在另外一个位置创建同名的标签)永久地将某个特定的提交命名为里程碑,然后就可以像分支一样引用了。

更难得的是,它们并不会随着新的提交而移动。你也不能检出到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。

咱们来看看标签到底是什么样。

咱们先建立一个标签,指向提交记录 C1,表示这是我们 1.0 版本。

git tag v1 C1

很容易吧!我们将这个标签命名为 v1,并且明确地让它指向提交记录 C1,如果你不指定提交记录,Git 会用 HEAD 所指向的位置。

在这个关卡中,按照目标建立两个标签,然后检出到 v1 上面,要注意你会进到分离 HEAD 的状态 —— 这是因为不能直接在v1 上面做 commit。

在下个关卡中我们会介绍更多关于标签的有趣的应用。

5: Git Describe

由于标签在代码库中起着“锚点”的作用,Git 还为此专门设计了一个命令用来描述离你最近的锚点(也就是标签),它就是 git describe!

Git Describe 能帮你在提交历史中移动了多次以后找到方向;当你用 git bisect(一个查找产生 Bug 的提交记录的指令)找到某个提交记录时,或者是当你坐在你那刚刚度假回来的同事的电脑前时, 可能会用到这个命令。

git describe 的​​语法是:

git describe <ref>

<ref> 可以是任何能被 Git 识别成提交记录的引用,如果你没有指定的话,Git 会以你目前所检出的位置(HEAD)。

它输出的结果是这样的:

<tag>_<numCommits>_g<hash>

tag 表示的是离 ref 最近的标签, numCommits 是表示这个 ref 与 tag 相差有多少个提交记录, hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。

当 ref 提交记录上有某个标签时,则只输出标签名称

让我们来看一个例子,对于下面的提交树:

git tag v2 C3

git describe master 会输出:

v1_2_gC2

git describe side 会输出:

v2_1_gC4

git describe 就是这样了!试着在这个关卡指定几个位置来感受一下这个命令吧!

当你准备进行下一关时,只要提交一次就可以通过这个关卡。算是我们送你的一个小礼物吧 :P

高级话题


只为真正的勇士!

1: 多次 Rebase

多分支 rebase

哥们儿,我们准备了很多分支!咱们把这些分支 rebase 到 master 上吧。

但是你的领导给你提了点要求 —— 他们希望得到有序的提交历史,也就是我们最终的结果应该是 C6’ 在 C7’ 上面, C5’ 在 C6’ 上面,依此类推。

即使你搞砸了也没关系,用 reset 命令就可以重新开始了。记得看看我们提供的答案,看你能否使用更少的命令来完成任务!

2: 两个父节点

选择父提交记录

操作符 ^ 与 ~ 符一样,后面也可以跟一个数字。

但是该操作符后面的数字与 ~ 后面的不同,并不是用来指定向上返回几代,而是指定合并提交记录的某个父提交。还记得前面提到过的一个合并提交有两个父提交吧,所以遇到这样的节点时该选择哪条路径就不是很清晰了。

Git 默认选择合并提交的“第一个”父提交,在操作符 ^ 后跟一个数字可以改变这一默认行为。

废话不多说,举个例子。

这里有一个合并提交记录。如果不加数字修改符直接检出 master^,会回到第一个父提交记录。

(在我们的图示中,第一个父提交记录是指合并提交记录正上方的那个提交记录。)

git checkout master^

现在来试试选择另一个父提交……

git checkout master^2

看见了吧?我们回到了另外一个父提交上。
这正是我们都已经习惯的方法。

使用 ^ 和 ~ 可以自由地在提交树中移动,非常给力:

git checkout HEAD~; git checkout HEAD^2; git checkout HEAD~2

快若闪电!

更厉害的是,这些操作符还支持链式操作!试一下这个:

git checkout HEAD~^2~2

和前面的结果一样,但只用了一条命令。

课后小练习

要完成此关,在指定的目标位置创建一个新的分支。

很明显可以简单地直接使用提交记录的哈希值(比如 C6),但我要求你使用刚刚讲到的相对引用修饰符!

3: 纠缠不清的分支

纠缠不清的分支

哇塞大神!这关我们要来点不同的!

现在我们的 master 分支是比 one、two 和 three 要多几个提交。出于某种原因,我们需要把 master 分支上最近的几次提交做不同的调整后,分别添加到各个的分支上。

one 需要重新排序并删除 C5,two 仅需要重排排序,而 three 只需要提交一次。

慢慢来,你会找到答案的 —— 记得通关之后用 show solution 看看我们的答案哦。

远程

Push & Pull —— Git 远程仓库!


是时候分享你的代码了,让编码变得社交化吧

1: Git Clone

远程仓库

远程仓库并不复杂, 在如今的云计算盛行的世界很容易把远程仓库想象成一个富有魔力的东西, 但实际上它们只是你的仓库在另个一台计算机上的拷贝。你可以通过因特网与这台计算机通信 —— 也就是增加或是获取提交记录

话虽如此, 远程仓库却有一系列强大的特性

首先也是最重要的的点, 远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力, 但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据, 你仍可以通过远程仓库拿回你丢失的数据。

还有就是, 远程让代码社交化了! 既然你的项目被托管到别的地方了, 你的朋友可以更容易地为你的项目做贡献(或者拉取最新的变更)

现在用网站来对远程仓库进行可视化操作变得越发流行了(像 Github 或 Phabricator), 但远程仓库永远是这些工具的顶梁柱, 因此理解其概念非常的重要!

我们创建远程仓库的命令

直到现在, 教程都聚焦于本地仓库的操作(branch、merge、rebase 等等)。但我们现在需要学习远程仓库的操作 —— 我们需要一个配置这种环境的命令, 它就是 git clone。 从技术上来讲,git clone 命令在真实的环境下的作用是在本地创建一个远程仓库的拷贝(比如从 github.com)。 但在我们的教程中使用这个命令会有一些不同 —— 它会在远程创建一个你本地仓库的副本。显然这和真实命令的意思刚好相反,但是它帮咱们把本地仓库和远程仓库关联到了一起,在教程中就凑合着用吧。

咱们慢慢来,先看看远程仓库(在图示中)的样子。

git clone

就是它了! 现在我们有了一个自己项目的远程仓库。除了远程仓库使用虚线之外, 它们几乎没有什么差别 —— 在后面的关卡中, 你将会学习怎样在本地仓库和远程仓库间分享工作成果。

2: 远程分支

远程分支

既然你已经看过 git clone 命令了,咱们深入地看一下发生了什么。

你可能注意到的第一个事就是在我们的本地仓库多了一个名为 o/master 的分支, 这种类型的分支就叫远程分支。由于远程分支的特性导致其拥有一些特殊属性。

远程分支反映了远程仓库(在你上次和它通信时)的状态。这会有助于你理解本地的工作与公共工作的差别 —— 这是你与别人分享工作成果前至关重要的一步.

远程分支有一个特别的属性,在你检出时自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因, 你必须在别的地方完成你的工作, (更新了远程分支之后)再用远程分享你的工作成果。

为什么有 o/?

你可能想问这些远程分支的前面的 o/ 是什么意思呢?好吧, 远程分支有一个命名规范 —— 它们的格式是:

<remote name>/<branch name>

因此,如果你看到一个名为 o/master 的分支,那么这个分支就叫 master,远程仓库的名称就是 o。

大多数的开发人员会将它们主要的远程仓库命名为 origin,并不是 o。这是因为当你用 git clone 某个仓库时,Git 已经帮你把远程仓库的名称设置为 origin 了

不过 origin 对于我们的 UI 来说太长了,因此不得不使用简写 o :) 但是要记住, 当你使用真正的 Git 时, 你的远程仓库默认为 origin!

说了这么多,让我们看看实例。

如果检出远程分支会怎么样呢?

git checkout o/master; git commit

正如你所见,Git 变成了分离 HEAD 状态,当添加新的提交时 o/master 也不会更新。这是因为 o/master 只有在远程仓库中相应的分支更新了以后才会更新。

要通过本关,在 master 分支上做一次提交;然后检出 o/master,再做一提交。这有助于你理解远程分支的不同,他们的更新只是反映了远程的状态。

3: Git Fetch

Git Fetch

Git 远程仓库相当的操作实际可以归纳为两点:向远程仓库传输数据以及从远程仓库获取数据。既然我们能与远程仓库同步,那么就可以分享任何能被 Git 管理的更新(因此可以分享代码、文件、想法、情书等等)。

本节课我们将学习如何从远程仓库获取数据 —— 命令如其名,它就是 git fetch。

你会看到当我们从远程仓库获取数据时, 远程分支也会更新以反映最新的远程仓库。在上一了我们已经提及过这一点了。

在解释 git fetch 前,我们先看看实例。这里我们有一个远程仓库, 它有两个我们本地仓库中没有的提交。

git fetch

就是这样了! C2,C3 被下载到了本地仓库,同时远程分支 o/master 也被更新,反映到了这一变化

git fetch 做了些什么

git fetch 完成了仅有的但是很重要的两步:

从远程仓库下载本地仓库中缺失的提交记录
更新远程分支指针(如 o/master)

git fetch 实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。

如果你还记得上一节课程中我们说过的,远程分支反映了远程仓库在你最后一次与它通信时的状态,git fetch 就是你与远程仓库通信的方式了!希望我说的够明白了,你已经了解 git fetch 与远程分支之间的关系了吧。

git fetch 通常通过互联网(使用 http:// 或 git:// 协议) 与远程仓库通信。

git fetch 不会做的事

git fetch 并不会改变你本地仓库的状态。它不会更新你的 master 分支,也不会修改你磁盘上的文件。

理解这一点很重要,因为许多开发人员误以为执行了 git fetch 以后,他们本地仓库就与远程仓库同步了。它可能已经将进行这一操作所需的所有数据都下载了下来,但是并没有修改你本地的文件。我们在后面的课程中将会讲解能完成该操作的命令 :D

所以, 你可以将 git fetch 的理解为单纯的下载操作。

4: Git Pull

Git Pull

既然我们已经知道了如何用 git fetch 获取远程的数据, 现在我们学习如何将这些变化更新到我们的工作当中。

其实有很多方法的 —— 当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。也就是说就是你可以执行以下命令:

git cherry-pick o/master
git rebase o/master
git merge o/master
等等

实际上,由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是我们要讲的 git pull。

我们先来看看 fetch、merge 依次执行的效果

git fetch; git merge o/master

我们用 fetch 下载了 C3, 然后通过 git merge o/master 合并了这一提交记录。现在我们的 master 分支包含了远程仓库中的更新(在本例中远程仓库名为 origin)

如果使用 git pull 呢?

git pull

同样的结果!这清楚地说明了 git pull 就是 git fetch 和 git merge <just-fetched-branch> 的缩写!

5: 模拟团队合作

模拟团队合作

这里有一件棘手的事 —— 为了接下来的课程, 我们需要先教你如何制造远程仓库的变更。

这意味着,我们需要“假装”你的同事、朋友、合作伙伴更新了远程仓库,有可能是某个特定的分支,或是几个提交记录。

为了做到这点,我们引入一个自造命令 git fakeTeamwork!它的名称已经说明了一切,先看演示..

fakeTeamwork 默认操作就是在远程仓库的 master 分支上做一次提交。

git fakeTeamwork

完成了 —— 远程仓库增加了一个新提交,我们还没有下载它,因为我们还没有执行 git fetch。

你还可以指定提交的分支或是数量,只需要在命令后加上它们就可以了。

git fakeTeamwork foo 3

通过一个命令,我们就模拟队友推送了 3 个提交记录到远程仓库的 foo 分支。

接下来的关卡会相当的困难,所以在本关会让你做许多事情,先来热热身。

克隆一个远程仓库(用 git clone),再在刚创建的远程仓库中模拟一些修改,然后在你自己的本地分支上做一些提交,再拉取远程仓库的变更。这看起来像是包含了好几节的课程。

6: Git Push

Git Push

OK,我们已经学过了如何从远程仓库获取更新并合并到本地的分支当中。这非常棒……但是我如何与大家分享我的成果呢?

嗯,上传自己分享内容与下载他人的分享刚好相反,那与 git pull 相反的命令是什么呢?git push!

git push 负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。一旦 git push 完成, 你的朋友们就可以从这个远程仓库下载你分享的成果了!

你可以将 git push 想象成发布你成果的命令。它有许多应用技巧,稍后我们会了解到,但是咱们还是先从基础的开始吧……

注意 —— git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关。它的默认值取决于你正使用的 Git 的版本,但是在教程中我们使用的是 upstream。 这没什么太大的影响,但是在你的项目中进行推送之前,最好检查一下这个配置。

这里我们准备了一些远程仓库中没有的提交记录, 咱们开始先上传吧!

git push

过去了, 远程仓库接收了 C2,远程仓库中的 master 分支也被更新到指向 C2 了,我们的远程分支 (o/master) 也同样被更新了。所有的分支都同步了!

7: 偏离的提交历史

偏离的工作

现在我们已经知道了如何从其它地方 pull 提交记录,以及如何 push 我们自己的变更。看起来似乎没什么难度,但是为何还会让人们如此困惑呢?

困难来自于远程库提交历史的偏离。在讨论这个问题的细节前,我们先来看一个例子……

假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。

这种情况下, git push 就不知道该如何操作了。如果你执行 git push,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,异或由于你的提交已经过时而直接忽略你的提交?

因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push 变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作。

说了这么多,咱们还是看看实际案例吧!

git push

看见了吧?什么都没有变,因为命令失败了!git push 失败是因为你最新提交的 C3 基于远程分支中的 C1。而远程仓库中该分支已经更新到 C2 了,所以 Git 拒绝了你的推送请求。

那该如何解决这个问题呢?很简单,你需要做的就是使你的工作基于最新的远程分支。

有许多方法做到这一点呢,不过最直接的方法就是通过 rebase 调整你的工作。咱们继续,看看怎么 rebase!

如果我们在 push 之前做 rebase 呢?

git fetch; git rebase o/master;git push

我们用 git fetch 更新了本地仓库中的远程分支,然后用 rebase 将我们的工作移动到最新的提交记录下,最后再用 git push 推送到远程仓库。

还有其它的方法可以在远程仓库变更了以后更新我的工作吗? 当然有,我们还可以使用 merge

尽管 git merge 不会移动你的工作(它会创建新的合并提交),但是它会告诉 Git 你已经合并了远程仓库的所有变更。这是因为远程分支现在是你本地分支的祖先,也就是说你的提交已经包含了远程分支的所有变化。

看下演示…

咱们们用 merge 替换 rebase 来试一下……

git fetch; git merge o/master;git push

我们用 git fetch 更新了本地仓库中的远程分支,然后合并了新变更到我们的本地分支(为了包含远程仓库的变更),最后我们用 git push 把工作推送到远程仓库

很好!但是要敲那么多命令,有没有更简单一点的?

当然 —— 前面已经介绍过 git pull 就是 fetch 和 merge 的简写,类似的 git pull –rebase 就是 fetch 和 rebase 的简写!

让我们看看简写命令是如何工作的。

这次用 –rebase……

git pull --rebase; git push

跟之前结果一样,但是命令更短了。

换用常规的 pull

git pull; git push

还是跟以前一样!

由 fetch、rebase/merge 和 push 组成的工作流很普遍。后续课程我们会讲解更复杂的工作流,不过现在我们先解决这个关卡吧。

要完成本关,你需要完成以下几步:

克隆你的仓库
模拟一次远程提交(fakeTeamwork)
完成一次本地提交
用 rebase 发布你的工作

关于 origin 和它的周边 —— Git 远程仓库高级操作


做一名仁慈的独裁者一定会很有趣……

1: 推送主分支

合并特性分支

既然你应该很熟悉 fetch、pull、push 了,现在我们要通过一个新的工作流来测试你的这些技能。

在大型项目中开发人员通常会在(从 master 上分出来的)特性分支上工作,工作完成后只做一次集成。这跟前面课程的描述很相像(把 side 分支推送到远程仓库),不过本节我们会深入一些.

但是有些开发人员只在 master 上做 push、pull —— 这样的话 master 总是最新的,始终与远程分支 (o/master) 保持一致。

对于接下来这个工作流,我们集成了两个步骤:

将特性分支集成到 master 上
推送并更新远程分支

让我们看看如何快速的更新 master 分支并推送到远程。

git pull --rebase; git push

我们执行了两个命令:

将我们的工作 rebase 到远程分支的最新提交记录
向远程仓库推送我们的工作

这个关卡的 Boss 很厉害 —— 以下是通关提示:

这里共有三个特性分支 —— side1 side2 和 side3
我需要将这三分支按顺序推送到远程仓库
因为远程仓库已经被更新过了,所以我们还要把那些工作合并过来

:O 紧张了?祝你好运!完成了本关, 你就向目标又迈近了一大步啦!

2: 合并远程仓库

为什么不用 merge 呢?

为了 push 新变更到远程仓库,你要做的就是包含远程仓库中最新变更。意思就是只要你的本地分支包含了远程分支(如 o/master)中的最新变更就可以了,至于具体是用 rebase 还是 merge,并没有限制。

那么既然没有规定限制,为何前面几节都在着重于 rebase 呢?为什么在操作远程分支时不喜欢用 merge 呢?

在开发社区里,有许多关于 merge 与 rebase 的讨论。以下是关于 rebase 的优缺点:

优点:

Rebase 使你的提交树变得很干净, 所有的提交都在一条线上

缺点:

Rebase 修改了提交树的历史

比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。

一些开发人员喜欢保留提交历史,因此更偏爱 merge。而其他人(比如我自己)可能更喜欢干净的提交树,于是偏爱 rebase。仁者见仁,智者见智。 :D

本关,我们还是解决上一关卡中的问题,但是要用 merge 替换 rebase。这显然有点画蛇添足,但这只是为了更好的说明上面的观点。

3: 远程追踪

远程跟踪分支

在前几节课程中有件事儿挺神奇的,Git 好像知道 master 与 o/master 是相关的。当然这些分支的名字是相似的,可能会让你觉得是依此将远程分支 master 和本地的 master 分支进行了关联。这种关联在以下两种情况下可以清楚地得到展示:

pull 操作时, 提交记录会被先下载到 o/master 上,之后再合并到本地的 master 分支。隐含的合并目标由这个关联确定的。
push 操作时, 我们把工作从 master 推到远程仓库中的 master 分支(同时会更新远程分支 o/master) 。这个推送的目的地也是由这种关联确定的!

远程跟踪

直接了当地讲,master 和 o/master 的关联关系就是由分支的“remote tracking”属性决定的。master 被设定为跟踪 o/master —— 这意味着为 master 分支指定了推送的目的地以及拉取后合并的目标。

你可能想知道 master 分支上这个属性是怎么被设定的,你并没有用任何命令指定过这个属性呀!好吧, 当你克隆仓库的时候, Git 就自动帮你把这个属性设置好了。

当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 o/master)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 master。

克隆完成后,你会得到一个本地分支(如果没有这个本地分支的话,你的目录就是“空白”的),但是可以查看远程仓库中所有的分支(如果你好奇心很强的话)。这样做对于本地仓库和远程仓库来说,都是最佳选择。

这也解释了为什么会在克隆的时候会看到下面的输出:

local branch “master” set to track remote branch “o/master”

我能自己指定这个属性吗?

当然可以啦!你可以让任意分支跟踪 o/master, 然后该分支会像 master 分支一样得到隐含的 push 目的地以及 merge 的目标。 这意味着你可以在分支 totallyNotMaster 上执行 git push,将工作推送到远程仓库的 master 分支上。

有两种方法设置这个属性,第一种就是通过远程分支检出一个新的分支,执行:

git checkout -b totallyNotMaster o/master

就可以创建一个名为 totallyNotMaster 的分支,它跟踪远程分支 o/master。

闲话少说,咱们先看看演示!我们检出一个名叫 foo 的新分支,让其跟踪远程仓库中的 master

git checkout -b foo o/master; git pull

正如你所看到的, 我们使用了隐含的目标 o/master 来更新 foo 分支。需要注意的是 master 并未被更新!

git push 同样适用

git checkout -b foo o/master; git commit; git push

我们将一个并不叫 master 的分支上的工作推送到了远程仓库中的 master 分支上

第二种方法

另一种设置远程追踪分支的方法就是使用:git branch -u 命令,执行:

git branch -u o/master foo

这样 foo 就会跟踪 o/master 了。如果当前就在 foo 分支上, 还可以省略 foo:

git branch -u o/master

看看这种方式的实际的效果…

git branch -u o/master foo; git commit; git push

跟之前一样, 但这个命令更明确!

OK! 本节我们在不检出 master 分支的情况下将工作推送到的远程仓库中的 master 分支上。因为这是高级课程, 就不做过多的提示了! :P

4: Git push 的参数

Git Push 的参数

很好! 既然你知道了远程跟踪分支,我们可以开始揭开 git push、fetch 和 pull 的神秘面纱了。我们会逐个介绍这几个命令,它们在理念上是非常相似的。

首先来看 git push。在远程跟踪课程中,你已经学到了 Git 是通过当前检出分支的属性来确定远程仓库以及要 push 的目的地的。这是未指定参数时的行为,我们可以为 push 指定参数,语法是:

git push <remote> <place>

<place> 参数是什么意思呢?我们稍后会深入其中的细节, 先看看例子, 这个命令是:

git push origin master

把这个命令翻译过来就是:

切到本地仓库中的“master”分支,获取所有的提交,再到远程仓库“origin”中找到“master”分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。

我们通过“place”参数来告诉 Git 提交记录来自于 master, 要推送到远程仓库中的 master。它实际就是要同步的两个仓库的位置。

需要注意的是,因为我们通过指定参数告诉了 Git 所有它需要的信息, 所以它就忽略了我们所检出的分支的属性!

我们看看指定参数的例子。注意下我们当前检出的位置。

git checkout C0; git push origin master

好了! 通过指定参数, 远程仓库中的 master 分支得到了更新。

如果不指定参数会发生什么呢?

git checkout C0; git push

命令失败了(正如你看到的,什么也没有发生)! 因为我们所检出的 HEAD 没有跟踪任何分支

本关我们要更新远程仓库中的 foo 和 master, 但是 git checkout 被禁用了!

注意:远程分支使用 o/ 开头是因为 origin/ 对于 UI 来说太长了。不用太在意这个,直接用 origin 作为远程仓库的名称就可以了。

5: Git push 参数 2

<place> 参数详解

还记得之前课程说的吧,当为 git push 指定 place 参数为 master 时,我们同时指定了提交记录的来源和去向。

你可能想问 —— 如果来源和去向分支的名称不同呢?比如你想把本地的 foo 分支推送到远程仓库中的 bar 分支。

哎,很遗憾 Git 做不到…… 开个玩笑,别当真!当然是可以的啦 :) Git 拥有超强的灵活性(有点过于灵活了)

接下来咱们看看是怎么做的……

要同时为源和目的地指定 <place> 的话,只需要用冒号 : 将二者连起来就可以了:

git push origin <source>:<destination>

这个参数实际的值是个 refspec,“refspec” 是一个自造的词,意思是 Git 能识别的位置(比如分支 foo 或者 HEAD~1)

一旦你指定了独立的来源和目的地,就可以组织出言简意赅的远程操作命令了,让我们看看演示!

记住,source 可以是任何 Git 能识别的位置:

git push origin foo^:master

这是个另人困惑的命令,但是它确实是可以运行的 —— Git 将 foo^ 解析为一个位置,上传所有未被包含到远程仓库里 master 分支中的提交记录。

如果你要推送到的目的分支不存在会怎么样呢?没问题!Git 会在远程仓库中根据你提供的名称帮你创建这个分支!

git push origin master:newBranch

很赞吧!它是不是很聪明?! :D

在这个关卡中,试着完成目标窗口展示的提交树,记住参数格式哟:

<source>:<destination>

6: Git fetch 的参数

Git fetch 的参数

我们刚学习了 git push 的参数,很酷的 <place> 参数,还有用冒号分隔的 refspecs(<source>:<destination>)。 这些参数可以用于 git fetch 吗?

你猜中了!git fetch 的参数和 git push 极其相似。他们的概念是相同的,只是方向相反罢了(因为现在你是下载,而非上传)

让我们逐个讨论下这些概念……

<place> 参数

如果你像如下命令这样为 git fetch 设置 <place> 的话:

git fetch origin foo

Git 会到远程仓库的 foo 分支上,然后获取所有本地不存在的提交,放到本地的 o/foo 上。

来看个例子(还是前面的例子,只是命令不同了)

通过指定 place…

git fetch origin foo

你可能会好奇 —— 为何 Git 会将新提交放到 o/foo 而不是放到我本地的 foo 分支呢?之前不是说这样的 <place> 参数就是同时应用于本地和远程的位置吗?

好吧, 本例中 Git 做了一些特殊处理,因为你可能在 foo 分支上的工作还未完成,你也不想弄乱它。还记得在 git fetch 课程里我们讲到的吗 —— 它不会更新你的本地的非远程分支, 只是下载提交记录(这样, 你就可以对远程分支进行检查或者合并了)。

“如果我们指定 <source>:<destination> 会发生什么呢?”

如果你觉得直接更新本地分支很爽,那你就用冒号分隔的 refspec 吧。不过,你不能在当前检出的分支上干这个事,但是其它分支是可以的。

这里有一点是需要注意的 —— source 现在指的是远程仓库中的位置,而 <destination> 才是要放置提交的本地仓库的位置。它与 git push 刚好相反,这是可以讲的通的,因为我们在往相反的方向传送数据。

理论上虽然行的通,但开发人员很少这么做。我在这里介绍它主要是为了从概念上说明 fetch 和 push 的相似性,只是方向相反罢了。
我们只下载了远程仓库中 foo 分支中的最新提交记录,并更新了 o/foo

来看个疯狂的例子:

git fetch origin foo~1:bar

哇! 看见了吧, Git 将 foo~1 解析成一个 origin 仓库的位置,然后将那些提交记录下载到了本地的 bar 分支(一个本地分支)上。注意由于我们指定了目标分支,foo 和 o/foo 都没有被更新。

如果执行命令前目标分支不存在会怎样呢?我们看一下上个对话框中没有 bar 分支的情况。

git fetch origin foo~1:bar

看见了吧,跟 git push 一样,Git 会在 fetch 前自己创建立本地分支, 就像是 Git 在 push 时,如果远程仓库中不存在目标分支,会自己在建立一样。

没有参数呢?

如果 git fetch 没有参数,它会下载所有的提交记录到各个远程分支……

git fetch

相当简单,但是仅需更新一次,值得你去做!

好, 说得太多了!要完成本关,抓取目标窗口中指定的提交记录,使用这些魔幻的命令吧!

使用 fetch 时, 你必须指定 source 和 destination。 注意一下目标窗口, 因为提交对象的 ID 可能会变哦!

7: 没有 source 的 source

古怪的

Git 有两种关于 <source> 的用法是比较诡异的,即你可以在 git push 或 git fetch 时不指定任何 source,方法就是仅保留冒号和 destination 部分,source 部分留空。

git push origin :side
git fetch origin :bugFix

我们分别来看一下这两条命令的作用……

如果 push 空 <source> 到远程仓库会如何呢?它会删除远程仓库中的分支!

git push origin :foo

就是这样子, 我们通过给 push 传空值 source,成功删除了远程仓库中的 foo 分支, 这真有意思…

如果 fetch 空 <source> 到本地,会在本地创建一个新分支。

git push origin :bar

很神奇吧!但无论怎么说, 这就是 Git!

这个关卡很容易 —— 只要删除一个远程的分支, 再用 git fetch 在本地创建一个新分支就可以了!

8: Git pull 的参数

Git pull 参数

既然你已经掌握关于 git fetch 和 git push 参数的方方面面了,关于 git pull 几乎没有什么可以讲的了 :)

因为 git pull 到头来就是 fetch 后跟 merge 的缩写。你可以理解为用同样的参数执行 git fetch,然后再 merge 你所抓取到的提交记录。

还可以和其它更复杂的参数一起使用, 来看一些例子:

以下命令在 Git 中是等效的:

git pull origin foo 相当于:

git fetch origin foo; git merge o/foo

还有…

git pull origin bar~1:bugFix 相当于:

git fetch origin bar~1:bugFix; git merge bugFix

看到了? git pull 实际上就是 fetch + merge 的缩写, git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)

一起来看个例子吧:

如果我们指定要抓取的 place,所有的事情都会跟之前一样发生,只是增加了 merge 操作

git push origin master

看到了吧! 通过指定 master 我们更新了 o/master。然后将 o/master merge 到我们的检出位置,无论我们当前检出的位置是哪。

pull 也可以用 source:destination 吗? 当然喽, 看看吧:

git push origin master:foo

哇, 这个命令做的事情真多。它先在本地创建了一个叫 foo 的分支,从远程仓库中的 master 分支中下载提交记录,并合并到 foo,然后再 merge 到我们的当前检出的分支 bar 上。操作够多的吧?!

好啦, 该结束了!请按照目标窗口中的状态进行操作。你需要下载一些提交,然后创建一些新分支,再合并这些分支到其它分支, 但这用不了几个命令 :P

0%