跳到主要内容

如何记录文件变化

· 阅读需 8 分钟

diff 算法是用于比较两个文本文件的差异,并生成它们之间的差异输出的算法。它的核心思想是找出两个文本之间的最小差异,并将这些差异表示为一系列的 增量操作,通常是 插入、删除、修改。这个过程通常会用在 文本比较版本控制(如 Git)、文件同步(如 rsync)等场景中。

diff 算法的基本思路:

  1. 将文本分解为行:将两个文本文件按行分割,通常是按行、单词或字符进行比较。在传统的 diff 中,通常是按行比较。

  2. 找到公共部分和不同部分

    • 最长公共子序列(LCS):核心思想是找到两个文本之间的最长公共子序列(LCS)。LCS 是两个文本中没有改动的部分。diff 算法会通过计算 最长公共子序列 来确定哪些行是相同的,哪些行发生了变化。

    • 差异生成:根据 LCS,可以通过比较每个文本中的不同部分,找出需要的差异操作(如:插入、删除)。

具体实现

实现 diff 算法的经典方法是 动态规划(Dynamic Programming, DP),它的基本思想是通过递归关系和记忆化(缓存)来逐步解决问题。这里是一个基于 动态规划diff 算法的简单实现过程。

1. 定义问题

假设有两个文本(文本 A 和文本 B),分别为:

文本 A:
A1
A2
A3
A4

文本 B:
B1
A2
A3
B4

我们要比较文本 A 和文本 B 之间的差异。

2. 构建 DP 表

我们可以通过一个二维数组 dp[i][j] 来表示文本 A 的前 i 行与文本 B 的前 j 行之间的 最长公共子序列(LCS)的长度。dp[i][j] 的值表示在比较文本 A 的前 i 行和文本 B 的前 j 行时,它们的 LCS 长度。

初始化:

  • dp[0][0] = 0,表示空文本之间的 LCS 长度为 0。
  • 如果文本 A 或文本 B 中某一方为空,则 dp[i][0] = 0dp[0][j] = 0

状态转移:

  • 如果 A[i-1] == B[j-1],则 dp[i][j] = dp[i-1][j-1] + 1
  • 否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1]),即选择删除或插入的操作。

3. 构建差异

一旦我们构建了 dp 表,我们可以回溯来确定文本 A 和文本 B 之间的差异:

  • 如果 A[i-1] == B[j-1],则这两行是相同的,不需要操作。
  • 如果 A[i-1] != B[j-1],则表示有差异,我们需要插入或删除对应的行。
  • 通过回溯,我们可以生成差异的详细列表。

4. 生成差异输出

diff 输出通常包括三种操作:

  • 删除:如果 A 中有一行,而 B 中没有,表示该行被删除。
  • 插入:如果 B 中有一行,而 A 中没有,表示该行被插入。
  • 修改:如果 A 和 B 中的同一行内容不同,表示该行被修改。

动态规划实现 diff 算法的伪代码:

def diff(A, B):
m, n = len(A), len(B)
dp = [[0] * (n + 1) for _ in range(m + 1)]

# 构建 dp 表
for i in range(1, m + 1):
for j in range(1, n + 1):
if A[i - 1] == B[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

# 回溯生成差异
diff_output = []
i, j = m, n
while i > 0 and j > 0:
if A[i - 1] == B[j - 1]:
i -= 1
j -= 1
elif dp[i - 1][j] >= dp[i][j - 1]:
diff_output.append(f"- {A[i - 1]}") # 删除操作
i -= 1
else:
diff_output.append(f"+ {B[j - 1]}") # 插入操作
j -= 1

# 处理剩余的行
while i > 0:
diff_output.append(f"- {A[i - 1]}") # 删除剩余的行
i -= 1
while j > 0:
diff_output.append(f"+ {B[j - 1]}") # 插入剩余的行
j -= 1

return diff_output[::-1] # 逆序返回差异

# 示例
A = ["A1", "A2", "A3", "A4"]
B = ["B1", "A2", "A3", "B4"]
diff_output = diff(A, B)
for line in diff_output:
print(line)

输出:

- A1
+ B1
A2
A3
- A4
+ B4

5. 优化

  • 时间复杂度:这个动态规划算法的时间复杂度是 O(m * n),其中 mn 分别是两个文本的行数。由于我们需要构建一个 m x n 的二维表。

  • 空间复杂度:空间复杂度也是 O(m * n),因为我们需要存储一个 m x n 的 DP 表。

为了优化空间,有些 diff 算法使用 线性空间 优化方法,例如只保存当前行和上一行的 DP 状态,从而将空间复杂度降到 O(min(m, n))

总结

diff 算法的实现大体依赖于 动态规划,通过计算 最长公共子序列(LCS) 来找出文本之间的差异。然后,通过回溯 DP 表生成详细的增量操作(插入、删除、修改)。这个过程广泛应用于文件比较、版本控制等场景。

nodejs中常用的diff库

在 Node.js 中,有几个非常流行的库可以用来实现 diff 算法,用于比较文本或文件的差异。以下是几个常用的库:

1. diff

diff 是一个常用的库,用于比较两个文本(字符串或文件)的差异,并生成一个类似 Git 的差异输出。

安装:

npm install diff

使用示例:

const Diff = require('diff');

const text1 = "A1\nA2\nA3\nA4";
const text2 = "B1\nA2\nA3\nB4";

const diffResult = Diff.diffLines(text1, text2);

diffResult.forEach((part) => {
// Green for additions, red for deletions
const color = part.added ? 'green' : part.removed ? 'red' : 'grey';
console.log(part.value[color]);
});

解释:

  • diffLines 比较两个字符串的行差异。
  • part.addedpart.removedpart.value 表示差异的类型(增加、删除、没有变化)及其值。
  • 该库支持多种比较模式,如 diffWords(按单词比较)、diffChars(按字符比较)等。

2. jsdiff

jsdiff 是另一个非常常用的库,它提供了比 diff 更强大的功能,可以进行单词、字符、行级别的差异比较。这个库广泛用于处理文本差异,并生成差异报告。

安装:

npm install diff

使用示例:

const jsdiff = require('diff');

const text1 = "A1\nA2\nA3\nA4";
const text2 = "B1\nA2\nA3\nB4";

// 比较行级差异
const diff = jsdiff.diffLines(text1, text2);

diff.forEach((part) => {
// Green for additions, red for deletions
const color = part.added ? 'green' : part.removed ? 'red' : 'grey';
console.log(part.value[color]);
});

3. deep-diff

deep-diff 主要用于深度比较两个对象的差异,但它也可以用于比较较复杂的数据结构,包括 JSON 对象和数组。它能返回关于添加、删除或修改的详细信息。

安装:

npm install deep-diff

使用示例:

const diff = require('deep-diff').diff;

const object1 = { a: 1, b: 2, c: 3 };
const object2 = { a: 1, b: 3, c: 3 };

const differences = diff(object1, object2);

differences.forEach((difference) => {
console.log(difference);
});

输出示例:

{
kind: 'E', // E表示修改,N表示新增,D表示删除
path: ['b'], // 修改路径
lhs: 2, // 左边的值(旧值)
rhs: 3 // 右边的值(新值)
}

4. diff-match-patch

diff-match-patch 是一个 Google 提供的库,它可以高效地生成文本差异和合并差异。它的特点是能够处理非常大的文本文件,并且能够生成高效的差异输出。

安装:

npm install diff-match-patch

使用示例:

const diff_match_patch = require('diff-match-patch');
const dmp = new diff_match_patch();

const text1 = "A1\nA2\nA3\nA4";
const text2 = "B1\nA2\nA3\nB4";

// 生成差异
const diff = dmp.diff_main(text1, text2);
dmp.diff_cleanupSemantic(diff);

console.log(diff);

解释:

  • diff_main 用于计算两个文本之间的差异。
  • diff_cleanupSemantic 会对生成的差异进行优化,以便忽略某些无关的变化。

5. diff2html

如果你想把 diff 输出渲染为 HTML 格式,diff2html 是一个很好的选择,它将 diff 结果转换为易于展示的 HTML 格式。

安装:

npm install diff2html

使用示例:

const Diff = require('diff');
const Diff2Html = require('diff2html').Diff2Html;

const text1 = "A1\nA2\nA3\nA4";
const text2 = "B1\nA2\nA3\nB4";

const diff = Diff.diffLines(text1, text2);
const html = Diff2Html.getPrettyHtml(diff, { inputFormat: 'json', showFiles: false });

console.log(html);

总结:

这些库提供了不同的功能和灵活性,适用于各种场景:

  • diffjsdiff:用于简单的文本差异比较。
  • deep-diff:适合复杂的对象或数组结构的比较。
  • diff-match-patch:适合处理大文件或高效的文本差异处理。
  • diff2html:适合将 diff 结果转换为 HTML 展示。

你可以根据你的需求选择最合适的库。如果你的目标是实现文本或文件级别的差异比较,diffjsdiff 是最常用和高效的选择。

胜任力模型

· 阅读需 2 分钟

人力资源领域中有个专业名词叫“胜任力模型”。它的具体含义为,对组织或企业中的某一个职位,依据提出的职责要求,为完成本职责而需要的能力要素的集中表示。简而言之,胜任力模型会告诉你,如果要胜任某个职位,需要哪些知识、哪些技能,只有具备了这些知识和能力,你才有可能胜任这个职位。这个模型在企业中常被用来当作人才招聘的评估工具和培训辅导工具。对于个人来说,胜任力模型也可以是我们自身做职业生涯规划时的一个很好的工具。如果你5年后,想成为一名HRD(人力资源总监),那么利用胜任力模型,你可以评估自己现在与HRD的差距。明确差距后,你的所有学习行动都是为了缩小这个差距。一般企业的职位胜任力模型由企业的专家、资深人士确定, 而对于刚毕业的学生或者一名中基层的职场人士来说,往往对于自己要从事的岗位需要哪些知识技能是模糊不清的。这时,建议你利用招聘网站上已经发布的岗位招聘需求,来做学习内容萃取,这种方式我们简称为“岗位描述萃取法”。

2017/多编程语言的软件通讯

· 阅读需 1 分钟

首选 socket,本机用socket其实和那些管道之类的性能差距不是特别大, 最关键是 socket 可以从单机多进程扩展到多机多进程,也就是分布式的环境。 当然了,如果确实是单机多进程,也可以看下常见的进程间通信方式 1 匿名管道通信 匿名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动 ,而且只能在具有亲缘关系的进程间使用。 2 高级管道通信 3. 有名管道通信 4. 消息队列通信 5. 信号量通信 6. 共享内存等

2025-07/Git

· 阅读需 8 分钟

git 分远程仓库 ,本地仓库,本地暂存区

git add ->暂存区 git commit -> 本地仓库 git push -->远程仓库

git init

当前文件夹内创建git仓库


git add .
-- 或 git add 文件名
-- 或 git add 文件夹

添加所有文件进版本控制(暂存区)


git commit -m ' 提交的信息'


git push origin main

提交到 origin 的main分支 可以缩写 git push

git pull

远程同步到本地仓库和 暂存区

git fetch
远程同步到仓库 ,但不和暂存区比对

git merge


git status

本地文件状态

远程相关命令

添加远程仓库

git remote add origin 仓库 或 git remote add github 仓库名

origin 可作为默认

修改远程仓库

git remote set-url origin 地址

删除远程仓库

git remote rm origin 或git remote rm github


提交到远程 git push origin main 缩写 git push

git 文件重命名/移动

git mv 原始 新名

Git分支管理

git branch -M main 切换主分支

Git账户相关

配置 用户名 和邮箱

git  config  --global user.name  "用户名"
git config --global user.email "你的邮箱"


查看配置列表

git config --list

Git相关问题

每次都要输入密码

git config credential.helper store

Git回滚到某个版本或提交

git commit后,如何撤销commit

修改了本地的代码,然后使用:

git add file
git commit -m '修改原因'

执行commit后,还没执行push时,想要撤销这次的commit,该怎么办?

解决方案:
使用命令:

git reset --soft HEAD^

这样就成功撤销了commit,如果想要连着add也撤销的话,--soft改为--hard(删除工作空间的改动代码)。

命令详解:

HEAD^ 表示上一个版本,即上一次的commit,也可以写成HEAD1
如果进行两次的commit,想要都撤回,可以使用HEAD
2

--soft
不删除工作空间的改动代码 ,撤销commit,不撤销git add file

--hard
删除工作空间的改动代码,撤销commit且撤销add

另外一点,如果commit注释写错了,先要改一下注释,有其他方法也能实现,如:

git commit --amend
这时候会进入vim编辑器,修改完成你要的注释后保存即可

git reset 命令

本地代码回滚主要围绕着 git reset 命令,该命令会把版本库和工作目录改变为已知状态。具体来讲,git reset 调整 HEAD 引用指向指定的提交,默认情况下还会更新索引以匹配该提交。根据需要,git reset 命令也可以修改工作目录以呈现指定提交代表的项目修订版本。

git reset 命令有三个主要选项:--soft--mixed--hard

soft 提交

--soft 会将 HEAD 引用指向指定提交。索引和工作目录的内容保持不变。这个版本的命令有“最小”影响,只改变一个符号引用的状态使其指向一个新提交。

mixed 提交

--mixed 会将 HEAD 指向指定提交。索引内容也跟着改变以符合指定提交的树结构,但是工作目录中的内容保持不变。这个版本的命令将索引变成你刚刚暂存该提交全部变化时的状态,它会显示工作目录中还有什么修改。--mixed 是 git reset 的默认模式。

hard 提交

这条命令将 HEAD 引用指向给定提交。索引的内容也跟着改变以符合给定提交的树结构。此外,工作目录的内容也随之改变以反映给定提交表示的树的状态。当改变工作目录的时候,整个目录结构都改成给定提交对应的样子。做的修改都将丢失,新文件将被删除。在给定提交中但不在工作目录中的文件将恢复回来。

git reset 选项影响

选项 HEAD 索引 工作目录 --soft 是 否 否 --mixed 是 是 否 --hard 是 是 是

本地代码回滚

首先通过 git log 查找要回退到的提交标记(commit id),该命令显示从最近到最远的提交日志;

$ git log

或者只显示提交的 commit id 和对应的注释的选项,如下:

$ git log --pretty=oneline
36915978c5b7e6cf4364b0b778409ba375e14289 all jar change to release
32989905ae07a92741cfedb0391544666df45885 del jacoco
......

其次,通过 git reset 回滚到指定 commit id

$ git reset --hard <commit_id>

hard 选项,表示彻底将工作区、暂存区和版本库记录恢复到指定的版本库。

远程仓库代码回滚

将本地回滚的代码推送到远程仓库,这里需要加强制的选项 -f 或 --force

$ git push -f origin <branch_name>

在强制推送本地回滚的代码到远程仓库时,如针对 master 分支操作很有可能提示未有强制推送的权限,如下提示:

git@MacBook-Pro xxx-xx (master) $ git ps origin head --force
Total 0 (delta 0), reused 0 (delta 0)
remote: GitLab: You are not allowed to force push code to a protected branch on this project.
To http://gitlab.xxx.com/xxxx/xxx-xx.git
! [remote rejected] head -> master (pre-receive hook declined)
error: failed to push some refs to 'http://gitlab.xxx.com/xxxx/xxx-xx.git'

以 gitlab 为例,一般 master 分支都会设置成保护分支,不允许 push --force,需要取消设置

Github示例



echo "# snow-framework-java" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/snow-projects/snow-framework-java.git
git push -u origin main

或者



git remote add origin https://github.com/snow-projects/snow-framework-java.git
git branch -M main
git push -u origin main


git其他信息,

1.下载路径

https://git-scm.com/downloads

2.使用git之前需要做的最小配置

配置user.name和user.email,在仓库中,如果local和golable配置不同,local优先起作用

git config --global user.name 'dcc'

git config --global user.email '123@qq.com'

git config --global user.password "123456(新的密码)"


其中global代表当前用户。

3.config的三个作用域

缺省等同于local

git config --local # local只对某个仓库有效

git config --global #global对当前用户所有仓库有效

git config --system #system对系统所有登录的用户有效

显示config的配置,加 --list

git config --list --local #显示当前仓库的配置

git config --list --global # 显示当前用户的配置

git config --list --system #当前系统的所有用户的配置

建git仓库

两种场景:

1.把已有的项目代码纳入git管理

cd 项目代码所在文件夹

git init

2.新建的项目直接用git管理

cd 某个文件夹

git init your_project # 会在当前路径下创建和项目名称同名的文件夹

cd you_project

创建一个文件,然演示提交

clear 清理命令行

git 往仓库里添加文件

工作目录-git add-》暂存区-git commit-》版本历史

git add files 提交文件到暂存区,可以加多个文件,空格分开

git add . 表示提交所有

git add -u 表示提交更新过的,update

git commit -m '提交信息描述' # 提交文件到本地版本历史

git status

查看文件状态

git log 查看提交记录

查看最近n次提交的记录

git log -n 

查看提交的内容:git log --oneline

$ git log --oneline
656ab27 (HEAD -> master) image 改名
d8635c8 update
0a9fdf6 修改
140b101 css
9e2b38c js
d373bb1 add index.html
011164e add image
dfa30a4 add me

查看所有分支记录

git log --all

图形化查看

git log --all --graph -n5

按q推出git log

通过图形界面查看提交历史

gitk

给文件重命名

方式一: 相当于移动文件

mv  旧文件 新文件名
git add 新文件
git rm 旧文件

方式二: 只能修改git管理的文件

git mv 旧文件名  新文件名

git reset --hard

暂存区和工作目录都会被清理掉,最好不要执行;

git 分支命令

查看分支

 git branch

创建分支

创建dev分支,会自动切换到dev

git checkout -b dev

切换分支

git checkout master

.git目录

cd .git

ls -al

HEAD文件

cat HEAD # 说明当前所在分支

config存放配置信息

refs目录

引用分支信息

heads目录

存放分支信息

比如master

ccDuan@DESKTOP-F9J8CSE MINGW64 /e/gittest/git_learning/.git (GIT_DIR!)
$ cd refs/

ccDuan@DESKTOP-F9J8CSE MINGW64 /e/gittest/git_learning/.git/refs (GIT_DIR!)
$ ls
heads/ tags/

ccDuan@DESKTOP-F9J8CSE MINGW64 /e/gittest/git_learning/.git/refs (GIT_DIR!)
$ cd heads/

ccDuan@DESKTOP-F9J8CSE MINGW64 /e/gittest/git_learning/.git/refs/heads (GIT_DIR!)
$ ls
dev master

ccDuan@DESKTOP-F9J8CSE MINGW64 /e/gittest/git_learning/.git/refs/heads (GIT_DIR!)
$ cat master
656ab27ee9799dca5d5fbbd2e64d54fb6f5e07fd

git cat-file -t 查看文件类型

git cat-file -t   656ab27ee

commit类型

git cat-file -p 查看内容

tag目录

存放tag类型

objects目录

存放tree类型

pack存放打包文件

commit,tree,bolob对象的关系

commit指每次的变更

一个commit对应一个tree,tree代表文件夹

blob代表文件

Git源码

git/git at v1.0.0 (github.com)

2025-07/PNPM

· 阅读需 1 分钟
  • 包管理工具

npm install -g pnpm

创建vue

注意带上sudo

pnpm create vite <project-name> -- --template vue
cd <project-name>
pnpm install
pnpm dev

cd universe-dust-web pnpm install pnpm run dev