[{"data":1,"prerenderedAt":1360},["ShallowReactive",2],{"post:\u002Fposts\u002Fgit\u002Fgit-install-and-basics":3,"posts:ordered":1063},{"id":4,"title":5,"body":6,"category":1047,"cover":1048,"date":1049,"description":1050,"extension":1051,"featured":1052,"meta":1053,"navigation":208,"path":1054,"seo":1055,"series":1056,"seriesOrder":87,"stem":1057,"tags":1058,"__hash__":1062},"posts\u002Fposts\u002Fgit\u002Fgit-install-and-basics.md","Git 安装与基本使用",{"type":7,"value":8,"toc":1025},"minimark",[9,16,20,24,41,44,57,64,67,72,75,112,123,127,130,145,148,161,165,168,252,255,268,275,278,281,316,323,342,349,365,372,375,378,422,437,440,443,446,458,461,476,479,485,497,500,503,530,533,580,590,593,612,615,659,662,665,698,714,717,729,732,743,749,752,822,825,828,911,917,920,927,956,966,984,987,1018,1021],[10,11],"img",{"title":12,"src":13,"alt":14,"width":15},"","https:\u002F\u002Fgit-scm.com\u002Fimages\u002Flogos\u002Fdownloads\u002FGit-Logo-2Color.png","Git 标志",215,[17,18,19],"p",{},"Git 是目前使用最广泛的分布式版本控制系统。这篇文章不打算把命令表抄一遍，而是先把\n它组织文件的方式讲清楚，再顺着这个模型理解日常要用的那几条命令。",[21,22,23],"h2",{"id":23},"为什么需要版本控制",[17,25,26,27,31,32,36,37,40],{},"把文件复制成 ",[28,29,30],"code",{},"报告-v2-最终版-真的最终版.docx"," 是很多人最早的\"版本管理\"。它的问题在于：\n你只知道文件是什么时候复制出来的，不知道",[33,34,35],"strong",{},"改了什么","、",[33,38,39],{},"为什么改","，更没法把两处改动合到一起。",[17,42,43],{},"版本控制系统解决的正是这些：",[45,46,47,51,54],"ul",{},[48,49,50],"li",{},"每次提交都记录改动的具体内容，以及一句说明",[48,52,53],{},"可以随时回到历史上任意一个节点",[48,55,56],{},"多个人可以同时改同一个项目，改完再合并",[17,58,59,60,63],{},"Git 和早期的集中式工具（比如 SVN）的关键区别是",[33,61,62],{},"分布式","：每个人本地都有一份完整的仓库，\n提交、查看历史、切换分支都不需要联网，只有和其他人同步时才需要。",[21,65,66],{"id":66},"安装",[68,69,71],"h3",{"id":70},"windows","Windows",[17,73,74],{},"最省事的是用 winget：",[76,77,81],"pre",{"className":78,"code":79,"language":80,"meta":12,"style":12},"language-bash shiki shiki-themes github-light github-dark","winget install --id Git.Git -e --source winget\n","bash",[28,82,83],{"__ignoreMap":12},[84,85,88,92,96,100,103,106,109],"span",{"class":86,"line":87},"line",1,[84,89,91],{"class":90},"sScJk","winget",[84,93,95],{"class":94},"sZZnC"," install",[84,97,99],{"class":98},"sj4cs"," --id",[84,101,102],{"class":94}," Git.Git",[84,104,105],{"class":98}," -e",[84,107,108],{"class":98}," --source",[84,110,111],{"class":94}," winget\n",[17,113,114,115,122],{},"也可以去 ",[116,117,121],"a",{"href":118,"rel":119},"https:\u002F\u002Fgit-scm.com\u002Fdownload\u002Fwin",[120],"nofollow","git-scm.com"," 下载安装包，一路默认即可。\n安装包里带了一个 Git Bash，后面在 Windows 上执行本文的命令用它就行。",[68,124,126],{"id":125},"macos","macOS",[17,128,129],{},"装了 Homebrew 的话：",[76,131,133],{"className":78,"code":132,"language":80,"meta":12,"style":12},"brew install git\n",[28,134,135],{"__ignoreMap":12},[84,136,137,140,142],{"class":86,"line":87},[84,138,139],{"class":90},"brew",[84,141,95],{"class":94},[84,143,144],{"class":94}," git\n",[17,146,147],{},"如果只是想快速拿到一个可用的 Git，系统自带的开发者工具里就有：",[76,149,151],{"className":78,"code":150,"language":80,"meta":12,"style":12},"xcode-select --install\n",[28,152,153],{"__ignoreMap":12},[84,154,155,158],{"class":86,"line":87},[84,156,157],{"class":90},"xcode-select",[84,159,160],{"class":98}," --install\n",[68,162,164],{"id":163},"linux","Linux",[17,166,167],{},"按你的发行版选一条：",[76,169,171],{"className":78,"code":170,"language":80,"meta":12,"style":12},"# Debian \u002F Ubuntu\nsudo apt update && sudo apt install git\n\n# Fedora \u002F RHEL\nsudo dnf install git\n\n# Arch\nsudo pacman -S git\n",[28,172,173,179,203,210,216,228,233,239],{"__ignoreMap":12},[84,174,175],{"class":86,"line":87},[84,176,178],{"class":177},"sJ8bj","# Debian \u002F Ubuntu\n",[84,180,182,185,188,191,195,197,199,201],{"class":86,"line":181},2,[84,183,184],{"class":90},"sudo",[84,186,187],{"class":94}," apt",[84,189,190],{"class":94}," update",[84,192,194],{"class":193},"sVt8B"," && ",[84,196,184],{"class":90},[84,198,187],{"class":94},[84,200,95],{"class":94},[84,202,144],{"class":94},[84,204,206],{"class":86,"line":205},3,[84,207,209],{"emptyLinePlaceholder":208},true,"\n",[84,211,213],{"class":86,"line":212},4,[84,214,215],{"class":177},"# Fedora \u002F RHEL\n",[84,217,219,221,224,226],{"class":86,"line":218},5,[84,220,184],{"class":90},[84,222,223],{"class":94}," dnf",[84,225,95],{"class":94},[84,227,144],{"class":94},[84,229,231],{"class":86,"line":230},6,[84,232,209],{"emptyLinePlaceholder":208},[84,234,236],{"class":86,"line":235},7,[84,237,238],{"class":177},"# Arch\n",[84,240,242,244,247,250],{"class":86,"line":241},8,[84,243,184],{"class":90},[84,245,246],{"class":94}," pacman",[84,248,249],{"class":98}," -S",[84,251,144],{"class":94},[68,253,254],{"id":254},"验证安装",[76,256,258],{"className":78,"code":257,"language":80,"meta":12,"style":12},"git --version\n",[28,259,260],{"__ignoreMap":12},[84,261,262,265],{"class":86,"line":87},[84,263,264],{"class":90},"git",[84,266,267],{"class":98}," --version\n",[17,269,270,271,274],{},"能打印出版本号（例如 ",[28,272,273],{},"git version 2.43.0","）就说明装好了。",[21,276,277],{"id":277},"首次配置",[17,279,280],{},"Git 会把每次提交的作者信息写进历史，所以装完先告诉它你是谁。这两条只需要执行一次：",[76,282,284],{"className":78,"code":283,"language":80,"meta":12,"style":12},"git config --global user.name \"你的名字\"\ngit config --global user.email \"你的邮箱@example.com\"\n",[28,285,286,302],{"__ignoreMap":12},[84,287,288,290,293,296,299],{"class":86,"line":87},[84,289,264],{"class":90},[84,291,292],{"class":94}," config",[84,294,295],{"class":98}," --global",[84,297,298],{"class":94}," user.name",[84,300,301],{"class":94}," \"你的名字\"\n",[84,303,304,306,308,310,313],{"class":86,"line":181},[84,305,264],{"class":90},[84,307,292],{"class":94},[84,309,295],{"class":98},[84,311,312],{"class":94}," user.email",[84,314,315],{"class":94}," \"你的邮箱@example.com\"\n",[17,317,318,319,322],{},"再顺手把默认分支名设成 ",[28,320,321],{},"main","，省得以后每次新建仓库都要改：",[76,324,326],{"className":78,"code":325,"language":80,"meta":12,"style":12},"git config --global init.defaultBranch main\n",[28,327,328],{"__ignoreMap":12},[84,329,330,332,334,336,339],{"class":86,"line":87},[84,331,264],{"class":90},[84,333,292],{"class":94},[84,335,295],{"class":98},[84,337,338],{"class":94}," init.defaultBranch",[84,340,341],{"class":94}," main\n",[17,343,344,345,348],{},"想确认配置结果，用 ",[28,346,347],{},"git config --list"," 查看，或者单独查某一项：",[76,350,352],{"className":78,"code":351,"language":80,"meta":12,"style":12},"git config --global user.email\n",[28,353,354],{"__ignoreMap":12},[84,355,356,358,360,362],{"class":86,"line":87},[84,357,264],{"class":90},[84,359,292],{"class":94},[84,361,295],{"class":98},[84,363,364],{"class":94}," user.email\n",[17,366,367,368,371],{},"配置存在 ",[28,369,370],{},"~\u002F.gitconfig"," 里，是个纯文本文件，可以直接编辑。",[21,373,374],{"id":374},"三个区域",[17,376,377],{},"理解 Git 的关键，是知道一个文件在你的本地会处于哪一层：",[379,380,381,394],"table",{},[382,383,384],"thead",{},[385,386,387,391],"tr",{},[388,389,390],"th",{},"区域",[388,392,393],{},"说明",[395,396,397,406,414],"tbody",{},[385,398,399,403],{},[400,401,402],"td",{},"工作区",[400,404,405],{},"你在编辑器里实际看到、正在修改的文件",[385,407,408,411],{},[400,409,410],{},"暂存区",[400,412,413],{},"已经挑好、准备放进下一次提交的改动",[385,415,416,419],{},[400,417,418],{},"版本库",[400,420,421],{},"已经提交、进入历史记录的快照",[17,423,424,425,428,429,432,433,436],{},"日常操作的实质，就是在把改动从工作区推进到暂存区，再推进到版本库。",[28,426,427],{},"git add"," 干的是\n第一步，",[28,430,431],{},"git commit"," 干的是第二步。多出来的暂存区一开始会觉得麻烦，但它让你可以\n",[33,434,435],{},"只挑一部分改动","提交——比如今天顺手改了两处不相干的东西，也能分成两次提交。",[21,438,439],{"id":439},"日常命令",[68,441,442],{"id":442},"建立仓库",[17,444,445],{},"把已有的目录变成 Git 仓库：",[76,447,449],{"className":78,"code":448,"language":80,"meta":12,"style":12},"git init\n",[28,450,451],{"__ignoreMap":12},[84,452,453,455],{"class":86,"line":87},[84,454,264],{"class":90},[84,456,457],{"class":94}," init\n",[17,459,460],{},"或者把别人已有的仓库复制到本地：",[76,462,464],{"className":78,"code":463,"language":80,"meta":12,"style":12},"git clone https:\u002F\u002Fgithub.com\u002F用户名\u002F仓库名.git\n",[28,465,466],{"__ignoreMap":12},[84,467,468,470,473],{"class":86,"line":87},[84,469,264],{"class":90},[84,471,472],{"class":94}," clone",[84,474,475],{"class":94}," https:\u002F\u002Fgithub.com\u002F用户名\u002F仓库名.git\n",[68,477,478],{"id":478},"查看状态",[17,480,481,484],{},[28,482,483],{},"git status"," 是使用频率最高的命令，任何时候不知道自己在哪一步，先敲它：",[76,486,488],{"className":78,"code":487,"language":80,"meta":12,"style":12},"git status\n",[28,489,490],{"__ignoreMap":12},[84,491,492,494],{"class":86,"line":87},[84,493,264],{"class":90},[84,495,496],{"class":94}," status\n",[17,498,499],{},"输出会告诉你哪些文件被改动了、哪些已经放进暂存区、当前在哪个分支。",[17,501,502],{},"想看具体改了哪几行，用：",[76,504,506],{"className":78,"code":505,"language":80,"meta":12,"style":12},"git diff          # 工作区 vs 暂存区\ngit diff --staged # 暂存区 vs 最近一次提交\n",[28,507,508,518],{"__ignoreMap":12},[84,509,510,512,515],{"class":86,"line":87},[84,511,264],{"class":90},[84,513,514],{"class":94}," diff",[84,516,517],{"class":177},"          # 工作区 vs 暂存区\n",[84,519,520,522,524,527],{"class":86,"line":181},[84,521,264],{"class":90},[84,523,514],{"class":94},[84,525,526],{"class":98}," --staged",[84,528,529],{"class":177}," # 暂存区 vs 最近一次提交\n",[68,531,532],{"id":532},"暂存与提交",[76,534,536],{"className":78,"code":535,"language":80,"meta":12,"style":12},"git add 文件名        # 暂存某个文件\ngit add .            # 暂存当前目录下所有改动\n\ngit commit -m \"说明这次改了什么\"\n",[28,537,538,551,563,567],{"__ignoreMap":12},[84,539,540,542,545,548],{"class":86,"line":87},[84,541,264],{"class":90},[84,543,544],{"class":94}," add",[84,546,547],{"class":94}," 文件名",[84,549,550],{"class":177},"        # 暂存某个文件\n",[84,552,553,555,557,560],{"class":86,"line":181},[84,554,264],{"class":90},[84,556,544],{"class":94},[84,558,559],{"class":94}," .",[84,561,562],{"class":177},"            # 暂存当前目录下所有改动\n",[84,564,565],{"class":86,"line":205},[84,566,209],{"emptyLinePlaceholder":208},[84,568,569,571,574,577],{"class":86,"line":212},[84,570,264],{"class":90},[84,572,573],{"class":94}," commit",[84,575,576],{"class":98}," -m",[84,578,579],{"class":94}," \"说明这次改了什么\"\n",[17,581,582,583,585,586,589],{},"提交说明尽量写清楚",[33,584,39],{},"，而不是重复文件名。",[28,587,588],{},"fix bug"," 这类信息三个月后\n对自己也毫无帮助。",[17,591,592],{},"如果提交完发现说明写错了，可以补上一次提交的说明：",[76,594,596],{"className":78,"code":595,"language":80,"meta":12,"style":12},"git commit --amend -m \"新的说明\"\n",[28,597,598],{"__ignoreMap":12},[84,599,600,602,604,607,609],{"class":86,"line":87},[84,601,264],{"class":90},[84,603,573],{"class":94},[84,605,606],{"class":98}," --amend",[84,608,576],{"class":98},[84,610,611],{"class":94}," \"新的说明\"\n",[68,613,614],{"id":614},"查看历史",[76,616,618],{"className":78,"code":617,"language":80,"meta":12,"style":12},"git log              # 完整历史\ngit log --oneline    # 每个提交压成一行\ngit log --oneline --graph --all   # 带分支图形，看起来最直观\n",[28,619,620,630,642],{"__ignoreMap":12},[84,621,622,624,627],{"class":86,"line":87},[84,623,264],{"class":90},[84,625,626],{"class":94}," log",[84,628,629],{"class":177},"              # 完整历史\n",[84,631,632,634,636,639],{"class":86,"line":181},[84,633,264],{"class":90},[84,635,626],{"class":94},[84,637,638],{"class":98}," --oneline",[84,640,641],{"class":177},"    # 每个提交压成一行\n",[84,643,644,646,648,650,653,656],{"class":86,"line":205},[84,645,264],{"class":90},[84,647,626],{"class":94},[84,649,638],{"class":98},[84,651,652],{"class":98}," --graph",[84,654,655],{"class":98}," --all",[84,657,658],{"class":177},"   # 带分支图形，看起来最直观\n",[68,660,661],{"id":661},"连接远程仓库",[17,663,664],{},"本地仓库建好后，把它和远端关联起来：",[76,666,668],{"className":78,"code":667,"language":80,"meta":12,"style":12},"git remote add origin https:\u002F\u002Fgithub.com\u002F用户名\u002F仓库名.git\ngit push -u origin main\n",[28,669,670,684],{"__ignoreMap":12},[84,671,672,674,677,679,682],{"class":86,"line":87},[84,673,264],{"class":90},[84,675,676],{"class":94}," remote",[84,678,544],{"class":94},[84,680,681],{"class":94}," origin",[84,683,475],{"class":94},[84,685,686,688,691,694,696],{"class":86,"line":181},[84,687,264],{"class":90},[84,689,690],{"class":94}," push",[84,692,693],{"class":98}," -u",[84,695,681],{"class":94},[84,697,341],{"class":94},[17,699,700,703,704,706,707,709,710,713],{},[28,701,702],{},"-u"," 会把本地 ",[28,705,321],{}," 和远端的 ",[28,708,321],{}," 绑定，之后直接 ",[28,711,712],{},"git push"," 就行。",[17,715,716],{},"从远端拉取别人的改动：",[76,718,720],{"className":78,"code":719,"language":80,"meta":12,"style":12},"git pull\n",[28,721,722],{"__ignoreMap":12},[84,723,724,726],{"class":86,"line":87},[84,725,264],{"class":90},[84,727,728],{"class":94}," pull\n",[68,730,731],{"id":731},"分支",[17,733,734,735,738,739,742],{},"分支让你可以在不影响主线的情况下试想法。下图里 ",[28,736,737],{},"master"," 和 ",[28,740,741],{},"testing"," 两个分支\n一开始指向同一串提交：",[17,744,745],{},[10,746],{"alt":747,"src":748},"两个分支指向同一串提交","\u002Fimages\u002Fgit-branches.png",[17,750,751],{},"日常用到的其实只有几条：",[76,753,755],{"className":78,"code":754,"language":80,"meta":12,"style":12},"git switch -c 新分支名   # 新建并切换过去\ngit switch main         # 切回 main\ngit branch              # 列出本地分支，当前分支前面有 *\ngit merge 分支名         # 把某个分支合并进当前分支\ngit branch -d 分支名     # 合并完删掉它\n",[28,756,757,773,785,795,808],{"__ignoreMap":12},[84,758,759,761,764,767,770],{"class":86,"line":87},[84,760,264],{"class":90},[84,762,763],{"class":94}," switch",[84,765,766],{"class":98}," -c",[84,768,769],{"class":94}," 新分支名",[84,771,772],{"class":177},"   # 新建并切换过去\n",[84,774,775,777,779,782],{"class":86,"line":181},[84,776,264],{"class":90},[84,778,763],{"class":94},[84,780,781],{"class":94}," main",[84,783,784],{"class":177},"         # 切回 main\n",[84,786,787,789,792],{"class":86,"line":205},[84,788,264],{"class":90},[84,790,791],{"class":94}," branch",[84,793,794],{"class":177},"              # 列出本地分支，当前分支前面有 *\n",[84,796,797,799,802,805],{"class":86,"line":212},[84,798,264],{"class":90},[84,800,801],{"class":94}," merge",[84,803,804],{"class":94}," 分支名",[84,806,807],{"class":177},"         # 把某个分支合并进当前分支\n",[84,809,810,812,814,817,819],{"class":86,"line":218},[84,811,264],{"class":90},[84,813,791],{"class":94},[84,815,816],{"class":98}," -d",[84,818,804],{"class":94},[84,820,821],{"class":177},"     # 合并完删掉它\n",[21,823,824],{"id":824},"一个完整的日常流程",[17,826,827],{},"把上面这些串起来，一次典型的工作大概是：",[76,829,831],{"className":78,"code":830,"language":80,"meta":12,"style":12},"git switch -c fix-login-error   # 1. 为新改动开一个分支\n# ... 改代码 ...\ngit status                      # 2. 看看动了哪些文件\ngit diff                        # 3. 确认改动内容\ngit add .                       # 4. 暂存\ngit commit -m \"修复登录页在空密码时的报错\"   # 5. 提交\ngit push -u origin fix-login-error          # 6. 推到远端\n",[28,832,833,847,852,862,871,882,896],{"__ignoreMap":12},[84,834,835,837,839,841,844],{"class":86,"line":87},[84,836,264],{"class":90},[84,838,763],{"class":94},[84,840,766],{"class":98},[84,842,843],{"class":94}," fix-login-error",[84,845,846],{"class":177},"   # 1. 为新改动开一个分支\n",[84,848,849],{"class":86,"line":181},[84,850,851],{"class":177},"# ... 改代码 ...\n",[84,853,854,856,859],{"class":86,"line":205},[84,855,264],{"class":90},[84,857,858],{"class":94}," status",[84,860,861],{"class":177},"                      # 2. 看看动了哪些文件\n",[84,863,864,866,868],{"class":86,"line":212},[84,865,264],{"class":90},[84,867,514],{"class":94},[84,869,870],{"class":177},"                        # 3. 确认改动内容\n",[84,872,873,875,877,879],{"class":86,"line":218},[84,874,264],{"class":90},[84,876,544],{"class":94},[84,878,559],{"class":94},[84,880,881],{"class":177},"                       # 4. 暂存\n",[84,883,884,886,888,890,893],{"class":86,"line":230},[84,885,264],{"class":90},[84,887,573],{"class":94},[84,889,576],{"class":98},[84,891,892],{"class":94}," \"修复登录页在空密码时的报错\"",[84,894,895],{"class":177},"   # 5. 提交\n",[84,897,898,900,902,904,906,908],{"class":86,"line":235},[84,899,264],{"class":90},[84,901,690],{"class":94},[84,903,693],{"class":98},[84,905,681],{"class":94},[84,907,843],{"class":94},[84,909,910],{"class":177},"          # 6. 推到远端\n",[17,912,913,914,916],{},"之后在代码托管平台上发起合并请求，走完评审再合并进 ",[28,915,321],{},"。",[21,918,919],{"id":919},"忽略不该提交的文件",[17,921,922,923,926],{},"编译产物、依赖目录、本地环境变量这些不该进版本库。在项目根目录建一个 ",[28,924,925],{},".gitignore","：",[76,928,930],{"className":78,"code":929,"language":80,"meta":12,"style":12},"node_modules\u002F\ndist\u002F\n.env\n*.log\n",[28,931,932,937,942,947],{"__ignoreMap":12},[84,933,934],{"class":86,"line":87},[84,935,936],{"class":90},"node_modules\u002F\n",[84,938,939],{"class":86,"line":181},[84,940,941],{"class":90},"dist\u002F\n",[84,943,944],{"class":86,"line":205},[84,945,946],{"class":90},".env\n",[84,948,949,953],{"class":86,"line":212},[84,950,952],{"class":951},"szBVR","*",[84,954,955],{"class":193},".log\n",[17,957,958,959,961,962,965],{},"已经写好的 ",[28,960,925],{}," 对尚未被跟踪的文件立即生效。如果某个文件",[33,963,964],{},"已经被提交过","，\n再加规则是不会让它消失的，需要先把它从跟踪列表里移除：",[76,967,969],{"className":78,"code":968,"language":80,"meta":12,"style":12},"git rm --cached 文件名\n",[28,970,971],{"__ignoreMap":12},[84,972,973,975,978,981],{"class":86,"line":87},[84,974,264],{"class":90},[84,976,977],{"class":94}," rm",[84,979,980],{"class":98}," --cached",[84,982,983],{"class":94}," 文件名\n",[21,985,986],{"id":986},"几点建议",[45,988,989,995,1003,1012],{},[48,990,991,994],{},[33,992,993],{},"提交要小而且要完整","。一个小改动配一条提交，出问题时容易定位，回滚也干净。",[48,996,997,1002],{},[33,998,999,1000],{},"提交前先 ",[28,1001,483],{},"。绝大多数\"我怎么把不该提交的文件传上去了\"，都是这一步省了。",[48,1004,1005,916,1008,1011],{},[33,1006,1007],{},"不要提交密钥",[28,1009,1010],{},".env","、私钥、访问令牌一旦进了历史，即使后面删掉，也仍然留在历史记录里。",[48,1013,1014,1017],{},[33,1015,1016],{},"不确定就先开分支","。分支在 Git 里几乎不花成本，试错了删掉即可。",[17,1019,1020],{},"掌握了这些，日常单人开发基本够用了。多人协作里的合并冲突、变基、挑选提交这些话题，\n等真正遇到时再单独展开。",[1022,1023,1024],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":12,"searchDepth":205,"depth":205,"links":1026},[1027,1028,1034,1035,1036,1044,1045,1046],{"id":23,"depth":181,"text":23},{"id":66,"depth":181,"text":66,"children":1029},[1030,1031,1032,1033],{"id":70,"depth":205,"text":71},{"id":125,"depth":205,"text":126},{"id":163,"depth":205,"text":164},{"id":254,"depth":205,"text":254},{"id":277,"depth":181,"text":277},{"id":374,"depth":181,"text":374},{"id":439,"depth":181,"text":439,"children":1037},[1038,1039,1040,1041,1042,1043],{"id":442,"depth":205,"text":442},{"id":478,"depth":205,"text":478},{"id":532,"depth":205,"text":532},{"id":614,"depth":205,"text":614},{"id":661,"depth":205,"text":661},{"id":731,"depth":205,"text":731},{"id":824,"depth":181,"text":824},{"id":919,"depth":181,"text":919},{"id":986,"depth":181,"text":986},"工具",null,"2026-10-04","从装好 Git 到跑通日常提交流程，把最常用的几条命令和它们背后的三个区域讲清楚。","md",false,{},"\u002Fposts\u002Fgit\u002Fgit-install-and-basics",{"title":5,"description":1050},"Git 入门","posts\u002Fgit\u002Fgit-install-and-basics",[1059,1060,1061],"Git","版本控制","入门","IQmWUWIp-bCiCmWWGAQwLy4HyRBkK1DwEF-tCpuGCNE",[1064,1071,1077,1083,1089,1095,1101,1107,1113,1119,1125,1131,1137,1143,1149,1155,1161,1167,1173,1179,1185,1190,1195,1200,1205,1210,1215,1220,1225,1231,1235,1239,1243,1247,1251,1255,1259,1263,1267,1271,1275,1281,1285,1291,1295,1299,1303,1307,1311,1315,1319,1324,1328,1332,1337,1342,1343,1347,1352,1356],{"path":1065,"title":1066,"description":1067,"date":1068,"series":1069,"seriesOrder":1070},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-28","软件分析与设计笔记 28：draw.io 作图手册：五张图的符号位置、落笔顺序与自检","不模拟软件界面，只讲清三件事：每个 UML 符号在 draw.io 里搜什么词、一张图该按什么顺序落笔、画完拿什么标准自查。覆盖用例图、活动图、顺序图、类图、界面草图五种图纸，末尾附「最容易画反的十二笔」总检表。","2026-11-19","软件分析与设计笔记",28,{"path":1072,"title":1073,"description":1074,"date":1075,"series":1069,"seriesOrder":1076},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-27","软件分析与设计笔记 27：Paper 1 应试：从读案例到交卷的一次完整演算","拿一份完整的案例，从头到尾走一遍：怎么读案例列角色、怎么在四个小问之间分配时间、每一张图从哪一句话推出来、两张表怎么填、交卷前拿什么清单自查。附 C25 真题与样卷的题型分值对照。","2026-11-18",27,{"path":1078,"title":1079,"description":1080,"date":1081,"series":1069,"seriesOrder":1082},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-26","软件分析与设计笔记 26：总结：把整条链路串起来","系列收尾：从需求到代码的完整链路回顾、每一环的产出与验收方式、六条贯穿全系列的原则，以及一份可以直接拿去做项目自查的清单。","2026-11-17",26,{"path":1084,"title":1085,"description":1086,"date":1087,"series":1069,"seriesOrder":1088},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-25","软件分析与设计笔记 25：代码重构：坏味道与手法","重构不改变外部行为、只改善内部结构：四种常见坏味道与对应手法、为什么动手之前必须先有测试、什么时候该重构，以及重构和前面那些设计模型是什么关系。","2026-11-16",25,{"path":1090,"title":1091,"description":1092,"date":1093,"series":1069,"seriesOrder":1094},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-24","软件分析与设计笔记 24：设计原则：内聚、耦合与多态","好设计怎么衡量：内聚与耦合分别在说什么、一次从「差设计」到「多态重设计」的完整对照、抽象类与接口的选择依据，以及复用为什么会从优势变成风险。","2026-11-15",24,{"path":1096,"title":1097,"description":1098,"date":1099,"series":1069,"seriesOrder":1100},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-23","软件分析与设计笔记 23：设计建模：从对象模型到能实现的方案","分析模型能用但还不能直接开工：对象模型怎么映射到关系模型、继承映射的三种方案怎么选、数据访问层与 Data Broker 解决什么问题，以及三层架构与 MVC 的分工。","2026-11-14",23,{"path":1102,"title":1103,"description":1104,"date":1105,"series":1069,"seriesOrder":1106},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-22","软件分析与设计笔记 22：分析建模：边界、实体与控制对象","不经过分析直接写代码会踩什么坑、三种分析对象各管一段、怎么用顺序图完成一次用例实现，以及从顺序图导出类的属性、操作与关系的方法。","2026-11-13",22,{"path":1108,"title":1109,"description":1110,"date":1111,"series":1069,"seriesOrder":1112},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-21","软件分析与设计笔记 21：UML：类图与顺序图","为什么需要一种共同的画法：类符号三格怎么画、五种关系的符号与设计含义、每种关系落到 Java 是什么写法，顺序图的三要素怎么摆；以及这三种最容易找不到的符号在 draw.io 里各搜什么词。","2026-11-12",21,{"path":1114,"title":1115,"description":1116,"date":1117,"series":1069,"seriesOrder":1118},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-20","软件分析与设计笔记 20：面向对象概念：对象、关系与多态","面向对象不是「用某个语言写代码」：对象与类的区别、封装真正在保护什么、五种关系（依赖、关联、聚合、组合、继承）各自何时用，以及多态为什么能减少条件判断。","2026-11-11",20,{"path":1120,"title":1121,"description":1122,"date":1123,"series":1069,"seriesOrder":1124},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-19","软件分析与设计笔记 19：规范化与逻辑数据模型","一次改地址引发的数据不一致，讲清规范化的真正动机；UNF 到 3NF 每一步在解决什么问题，派生字段为什么不能存，优化为什么和规范化方向相反，以及逻辑数据模型在 3NF 之上补了什么。","2026-11-10",19,{"path":1126,"title":1127,"description":1128,"date":1129,"series":1069,"seriesOrder":1130},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-18","软件分析与设计笔记 18：数据库分析：实体、关系与数据字典","为什么不能拿到需求就动手建表：实体与关系怎么判定、基数为什么必须由业务方确认、数据命名要遵守什么规则，以及数据字典四列里最容易被空着的那一列。","2026-11-09",18,{"path":1132,"title":1133,"description":1134,"date":1135,"series":1069,"seriesOrder":1136},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-17","软件分析与设计笔记 17：用生成式 AI 做设计与开发：能用的场景与要防的坑","生成式 AI 在工作流里真正站得住的三个场景 —— 造示例数据、解释陌生代码、起草初稿；它为什么会复现社会偏见、为什么会编得像真的，以及哪些内容绝不能直接采用。","2026-11-08",17,{"path":1138,"title":1139,"description":1140,"date":1141,"series":1069,"seriesOrder":1142},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-16","软件分析与设计笔记 16：从 persona 到 URS：一份交付物的完整骨架","把前面十几篇串成一份能交出去的东西：用户需求规格说明书的九个部分、每一章来自哪个环节、由谁确认，以及面向决策者的提案该讲清哪三件事。","2026-11-07",16,{"path":1144,"title":1145,"description":1146,"date":1147,"series":1069,"seriesOrder":1148},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-15","软件分析与设计笔记 15：非功能需求：把「快、稳、安全」变成数字","为什么「系统要快」不是一条需求：非功能需求怎么量化，决定性能的用户量、交易量、数据量三张表怎么估，安全需求要写清什么，以及可靠性的「不可用」该怎么定义。","2026-11-06",15,{"path":1150,"title":1151,"description":1152,"date":1153,"series":1069,"seriesOrder":1154},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-14","软件分析与设计笔记 14：软件测试：层级、方法与等价类划分","「我们测过了」这句话问题在哪：为什么静态评审和动态测试缺一不可，五个测试层级各自验证什么，白盒与黑盒的差别，以及用等价类划分和边界值把无限输入变成有限用例的方法。","2026-11-05",14,{"path":1156,"title":1157,"description":1158,"date":1159,"series":1069,"seriesOrder":1160},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-13","软件分析与设计笔记 13：需求获取：五种调查方法与一场访谈","需求从哪里来：五种事实调查方法各自适合回答什么、代价是什么，访谈从准备到记录的全过程，问卷设计里最容易犯的错，以及访谈记录必须写下来的那几项信息。","2026-11-04",13,{"path":1162,"title":1163,"description":1164,"date":1165,"series":1069,"seriesOrder":1166},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-12","软件分析与设计笔记 12：前端设计：把流程变成屏幕","界面设计不是「画得好看」：从活动图走到屏幕的三步，CRUD 任务流怎么当检查表，界面必须回答的三个问题，信息架构与五种导航类型，视觉一致性与留白，以及移动端与 Web 端的差别。","2026-11-03",12,{"path":1168,"title":1169,"description":1170,"date":1171,"series":1069,"seriesOrder":1172},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-11","软件分析与设计笔记 11：Agentic AI 与业务流程自动化","AI agent 到底是什么：五项能力与四个核心组件，什么场景该用定时任务、什么该用程序、什么才值得用 agent，两种实现路径的差别，必须提前防住的四类失败模式，以及设计方法从提示词到编排的演进。","2026-11-02",11,{"path":1174,"title":1175,"description":1176,"date":1177,"series":1069,"seriesOrder":1178},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-10","软件分析与设计笔记 10：精炼用例模型：include、extend 与泛化","三种精炼用例关系的判断标准：include 什么时候用、它和 extend 的区别到底在哪、参与者泛化与用例泛化各解决什么问题，以及精炼到什么程度就该停手。","2026-11-01",10,{"path":1180,"title":1181,"description":1182,"date":1183,"series":1069,"seriesOrder":1184},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-09","软件分析与设计笔记 09：功能需求（二）：从流程到业务实体","活动图画完之后，怎么从流程里筛出真正的业务实体：三条判断标准、哪些名词其实不该建实体、属性从哪里来，以及领域模型和后面的数据模型分别负责什么。","2026-10-31",9,{"path":1186,"title":1187,"description":1188,"date":1189,"series":1069,"seriesOrder":241},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-08","软件分析与设计笔记 08：功能需求（一）：用例模型与活动图","从用户故事到用例的翻译过程：什么才算「好」的参与者，系统边界怎么划，用例描述必须写清哪四件事，活动图怎么把分支、并行与泳道画清楚，以及这两个图在 draw.io 里各用哪个形状、最容易画错哪一笔。","2026-10-30",{"path":1191,"title":1192,"description":1193,"date":1194,"series":1069,"seriesOrder":235},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-07","软件分析与设计笔记 07：用户故事：把需求写成可验收的一句话","用户故事的三段结构，怎么从用户画像和数据地图里把它提取出来，一条故事多大算合适、时序为什么也是需求的一部分，以及验收标准怎么写才算真的可验收。","2026-10-29",{"path":1196,"title":1197,"description":1198,"date":1199,"series":1069,"seriesOrder":230},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-06","软件分析与设计笔记 06：范围界定（数据侧）：用数据校验你的判断","访谈结论和数字对不上时该怎么办：分析的四个层次分别在回答什么问题、为什么不能跳级、数据准备与搜集怎么做成一张应用地图，以及数据最容易在哪几个地方骗你。","2026-10-28",{"path":1201,"title":1202,"description":1203,"date":1204,"series":1069,"seriesOrder":218},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-05","软件分析与设计笔记 05：范围界定（用户侧）：用户画像与客户旅程图","为什么「所有用户」是一个陷阱，项目边界该怎么划，用户画像要写哪五个要素、最容易写成什么废纸，客户旅程图怎么把阶段、想法和情绪画在同一张纸上，以及怎么从这两样东西一步步推到可验证的需求。","2026-10-27",{"path":1206,"title":1207,"description":1208,"date":1209,"series":1069,"seriesOrder":212},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-04","软件分析与设计笔记 04：接手陌生案例的第一步：三问与一张表","拿到一个完全陌生的业务案例，怎么从零开始不跑偏：只问 Who、What、Why 三个问题，把「问题—事实—假设—理由」记成一张四列表，说清为什么假设必须写明理由，以及带着问题进访谈为什么比空手去有效得多。","2026-10-26",{"path":1211,"title":1212,"description":1213,"date":1214,"series":1069,"seriesOrder":205},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-03","软件分析与设计笔记 03：Agile 与 Scrum：迭代、角色与产出物","从「三周做完交付，客户说这不是我要的」讲起，说清瀑布式与迭代式的根本差别，Scrum 的三个角色和三种产出物，一个 sprint 里四次固定活动各自在解决什么问题，以及敏捷里最常见的几个误解。","2026-10-25",{"path":1216,"title":1217,"description":1218,"date":1219,"series":1069,"seriesOrder":181},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-02","软件分析与设计笔记 02：设计思维、用户驱动与数据驱动","从「客服要一个能一次看完所有排期的界面」这个翻车案例讲起，说清设计思维到底是什么、用户驱动与数据驱动各自能看见什么又看不见什么、为什么真正的落点是把两者合起来的 data-informed design，以及访谈中怎么问出有用的信息。","2026-10-24",{"path":1221,"title":1222,"description":1223,"date":1224,"series":1069,"seriesOrder":87},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-01","软件分析与设计笔记 01：课程地图与从需求到设计的主干","这一篇不写具体方法，先把整条链路铺开：软件分析与设计到底要解决什么问题，从需求到设计分成哪两段，中间有哪十几个环节，一份完整的交付物里应该有什么，以及后面 27 篇笔记怎么排、新人该按什么顺序读。","2026-10-23",{"path":1226,"title":1227,"description":1228,"date":1229,"series":1230,"seriesOrder":87},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-01","Spring 学习笔记 01：IoC、依赖注入与第一个 Spring Boot 应用","从「手写 new 出来的三层结构」的痛点讲起，说清 IoC 容器、Bean 与依赖注入是怎么回事，再用 Maven 与 Spring Initializr 跑起第一个能返回 JSON 的应用。","2026-10-15","Spring 学习笔记",{"path":1232,"title":1233,"description":1234,"date":1229,"series":1230,"seriesOrder":181},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-02","Spring 学习笔记 02：Spring Data JPA 基础——实体映射与 Repository","用 ORM 把类映射到表：@Entity、@Id、@GeneratedValue 怎么用，CrudRepository 三层接口怎么选，H2 与 MySQL 怎么配，以及 ddl-auto 到底该写哪个值。",{"path":1236,"title":1237,"description":1238,"date":1229,"series":1230,"seriesOrder":205},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-03","Spring 学习笔记 03：JPA 查询——派生方法、JPQL 与分页排序","Repository 自带的方法只够按主键查，这篇讲三种自己造查询的方式：方法名派生、@Query 写 JPQL、原生 SQL，以及 Pageable 分页排序和投影返回。",{"path":1240,"title":1241,"description":1242,"date":1229,"series":1230,"seriesOrder":212},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-04","Spring 学习笔记 04：JPA 关联映射与仓储测试","一对多、多对多怎么映射，谁负责维护外键，FetchType 与 CascadeType 该怎么选，顺带用 @DataJpaTest 与 AssertJ 把仓储层测起来。",{"path":1244,"title":1245,"description":1246,"date":1229,"series":1230,"seriesOrder":218},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-05","Spring 学习笔记 05：Spring MVC 与 Thymeleaf 入门","一次页面请求在 Spring MVC 里走了哪几步，@Controller 怎么把数据交给模板，@PathVariable 与 @RequestParam 怎么取参数，以及控制器为什么必须是无状态的。",{"path":1248,"title":1249,"description":1250,"date":1229,"series":1230,"seriesOrder":230},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-06","Spring 学习笔记 06：表单绑定与服务端校验","把表单字段绑到对象上，用 Bean Validation 注解做服务端校验，把错误信息显示回用户刚填的那一页，再补上自定义校验注解与全局异常处理。",{"path":1252,"title":1253,"description":1254,"date":1229,"series":1230,"seriesOrder":235},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-07","Spring 学习笔记 07：Thymeleaf 片段复用与国际化","用 th:fragment 与 th:insert\u002Fth:replace 把导航栏、页脚抽成片段，页面套用统一布局，再用 messages.properties 与 LocaleResolver 做中英文切换。",{"path":1256,"title":1257,"description":1258,"date":1229,"series":1230,"seriesOrder":241},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-08","Spring 学习笔记 08：HTTP Session 与会话状态管理","HTTP 无状态带来的问题，Cookie 与 Session 怎么分工，Spring 里读写会话的几种方式，以及会话固定、超时、多实例共享这些必须想清楚的事。",{"path":1260,"title":1261,"description":1262,"date":1229,"series":1230,"seriesOrder":1184},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-09","Spring 学习笔记 09：拦截器 HandlerInterceptor","登录检查、权限判断、请求耗时统计这类横切逻辑该放哪：HandlerInterceptor 的三个回调各在什么时机执行，怎么注册与排除路径，以及它和过滤器的分工。",{"path":1264,"title":1265,"description":1266,"date":1229,"series":1230,"seriesOrder":1178},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-10","Spring 学习笔记 10：Service 层与事务","一组数据库操作要么全成功要么全失败，靠 @Transactional 怎么做到；传播行为、隔离级别、回滚规则怎么选，以及自调用失效、乐观锁悲观锁这些实战里的坑。",{"path":1268,"title":1269,"description":1270,"date":1229,"series":1230,"seriesOrder":1172},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-11","Spring 学习笔记 11：REST API 基础——把 Service 暴露成接口","同一套 Service 怎么同时服务页面和客户端：@RestController 的路由与方法语义、状态码怎么给、DTO 怎么定，以及 MockMvc 端到端和切片测试的两种写法与各自的坑。",{"path":1272,"title":1273,"description":1274,"date":1229,"series":1230,"seriesOrder":1166},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-12","Spring 学习笔记 12：REST 进阶——版本、更新、跨域与文档化","接口能跑通之后要处理的几件事：URL 与响应结构怎么定、版本怎么演进、更新怎么做到可安全重试、CORS 怎么配、HATEOAS 与 Spring Data REST 该不该用。",{"path":1276,"title":1277,"description":1278,"date":1279,"series":1280,"seriesOrder":212},"\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes","Java 函数式编程笔记：Lambda、函数式接口与 Streams","学习 Java 函数式编程时的个人笔记：Lambda 表达式的五步简写与方法引用、常见函数式接口、Stream 流水线与惰性求值，以及创建流、中间操作、终止操作与收集器的用法。示例代码均为自写，运行结果来自本地实测。","2026-10-13","Java 学习笔记",{"path":1282,"title":1283,"description":1284,"date":1279,"series":1280,"seriesOrder":205},"\u002Fposts\u002Fjava\u002Fjava-generics-notes","Java 泛型笔记：泛型方法、有界类型与泛型类","学习 Java 泛型时的个人笔记：从重复的重载方法与多态尝试讲起，再到泛型方法与类型推断、有界泛型类型、Comparable 与 Number 边界，以及泛型类在集合中的用法。示例代码均为自写，运行结果来自本地实测。",{"path":1286,"title":1287,"description":1288,"date":1289,"series":1290,"seriesOrder":212},"\u002Fposts\u002Fmysql\u002Fmysql-learning-04","MySQL 学习笔记 04：数据操纵（增删改）","MySQL 学习笔记第四篇：INSERT 插入（含批量插入与 INSERT ... SELECT）、UPDATE 更新、DELETE 删除，以及各自的坑。","2026-10-10","SQL 学习笔记",{"path":1292,"title":1293,"description":1294,"date":1289,"series":1290,"seriesOrder":218},"\u002Fposts\u002Fmysql\u002Fmysql-learning-05","MySQL 学习笔记 05：DDL 概览","MySQL 学习笔记第五篇：数据定义语言概览——Schema、Table、View、Index、Constraints 这几类对象分别对应哪些 CREATE \u002F ALTER \u002F DROP 命令。",{"path":1296,"title":1297,"description":1298,"date":1289,"series":1290,"seriesOrder":230},"\u002Fposts\u002Fmysql\u002Fmysql-learning-06","MySQL 学习笔记 06：CREATE TABLE 与数据类型","MySQL 学习笔记第六篇：CREATE TABLE 的完整语法、常用数据类型的选型建议，以及外键的两种定义方式。",{"path":1300,"title":1301,"description":1302,"date":1289,"series":1290,"seriesOrder":235},"\u002Fposts\u002Fmysql\u002Fmysql-learning-07","MySQL 学习笔记 07：索引","MySQL 学习笔记第七篇：索引的作用与 B+ 树结构、单列与复合索引的创建、索引的代价与适用场景。",{"path":1304,"title":1305,"description":1306,"date":1289,"series":1290,"seriesOrder":241},"\u002Fposts\u002Fmysql\u002Fmysql-learning-08","MySQL 学习笔记 08：DROP 与 ALTER","MySQL 学习笔记第八篇：DROP 删除数据库对象、ALTER 修改表结构，以及常见的坑。",{"path":1308,"title":1309,"description":1310,"date":1289,"series":1290,"seriesOrder":1184},"\u002Fposts\u002Fmysql\u002Fmysql-learning-09","MySQL 学习笔记 09：约束与数据完整性","MySQL 学习笔记第九篇：SQL 约束——NOT NULL、CHECK、PRIMARY KEY、FOREIGN KEY 等如何保证数据完整性，以及它们的对比。",{"path":1312,"title":1313,"description":1314,"date":1289,"series":1290,"seriesOrder":1178},"\u002Fposts\u002Fmysql\u002Fmysql-learning-10","MySQL 学习笔记 10：视图","MySQL 学习笔记第十篇：视图（Views）的定义与特性、和表的对比、创建与使用，以及可更新视图的限制。",{"path":1316,"title":1317,"description":1318,"date":1289,"series":1290,"seriesOrder":1172},"\u002Fposts\u002Fmysql\u002Fmysql-learning-11","MySQL 学习笔记 11：存储过程","MySQL 学习笔记第十一篇：存储过程（Stored Procedures）的概念、创建与调用、带参数的写法，以及优缺点。",{"path":1320,"title":1321,"description":1322,"date":1323,"series":1290,"seriesOrder":87},"\u002Fposts\u002Fmysql\u002Fmysql-learning-01","MySQL 学习笔记 01：MySQL 基础知识","MySQL 学习笔记第一篇：MySQL Server 与 Workbench 的安装，数据库与 DBMS 的基本概念，关系模型、ACID 事务、常见数据类型，以及后续的学习路线。","2026-10-09",{"path":1325,"title":1326,"description":1327,"date":1323,"series":1290,"seriesOrder":181},"\u002Fposts\u002Fmysql\u002Fmysql-learning-02","MySQL 学习笔记 02：SELECT 查询与过滤","MySQL 学习笔记第二篇：SELECT 的基本语法与逻辑执行顺序，WHERE 过滤（比较、LIKE、IN、BETWEEN、NULL）、各种 JOIN、UNION 与子查询。",{"path":1329,"title":1330,"description":1331,"date":1323,"series":1290,"seriesOrder":205},"\u002Fposts\u002Fmysql\u002Fmysql-learning-03","MySQL 学习笔记 03：聚合、分组与输出控制","MySQL 学习笔记第三篇：COUNT\u002FSUM\u002FAVG 等聚合函数、GROUP BY 分组、HAVING 过滤、ORDER BY 与 LIMIT，以及一条 SELECT 的完整逻辑执行顺序。",{"path":1333,"title":1334,"description":1335,"date":1336,"series":1280,"seriesOrder":181},"\u002Fposts\u002Fjava\u002Fjava-oop-notes","Java 面向对象笔记：类、继承、多态与异常","学习 Java 面向对象编程时的个人笔记：类与对象、封装与访问修饰符、继承与多态、集合、抽象类与接口、异常处理。示例代码均为自写，运行结果来自本地实测。","2026-10-06",{"path":1338,"title":1339,"description":1340,"date":1341,"series":1280,"seriesOrder":87},"\u002Fposts\u002Fjava\u002Fjava-basics-notes","Java 基础语法笔记：从环境安装到方法与数组","学习 Java 基础语法时的个人笔记：JDK 安装、输入输出、变量与运算符、条件与循环、字符串、数组与方法。示例代码均为自写，运行结果来自本地实测。","2026-10-05",{"path":1054,"title":5,"description":1050,"date":1049,"series":1056,"seriesOrder":87},{"path":1344,"title":1345,"description":1346,"date":1049,"series":1056,"seriesOrder":181},"\u002Fposts\u002Fgit\u002Flocal-remote-repository","本地远程仓库的部署","用自己的机器当 Git 服务器：建一个裸仓库、走 SSH 连上去，把本地提交推上去。",{"path":1348,"title":1349,"description":1350,"date":1049,"series":1351,"seriesOrder":87},"\u002Fposts\u002Fguide\u002Fguide-file-layout","文件都放在哪","文章、图片、封面分别放在仓库的什么位置，哪些目录可以放手改、哪些碰了会出问题。","博客使用指南",{"path":1353,"title":1354,"description":1355,"date":1049,"series":1351,"seriesOrder":181},"\u002Fposts\u002Fguide\u002Fguide-frontmatter","frontmatter 每个字段是什么意思","文章开头那段被横线包住的字段，逐个说明作用、默认行为和写错的后果。",{"path":1357,"title":1358,"description":1359,"date":1049,"series":1351,"seriesOrder":205},"\u002Fposts\u002Fguide\u002Fguide-markdown","正文怎么写 —— Markdown 与排版效果","标题、链接、图片、代码、表格分别怎么写，以及本站对它们做了哪些自定义处理。",1793268151525]