diff --git "a/\346\225\260\346\215\256\345\210\206\346\236\220\345\222\214\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267/main.js" "b/\346\225\260\346\215\256\345\210\206\346\236\220\345\222\214\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267/main.js"
new file mode 100644
index 0000000000000000000000000000000000000000..0bcdd439350a642f94de162ffa5f1846dee1ebb0
--- /dev/null
+++ "b/\346\225\260\346\215\256\345\210\206\346\236\220\345\222\214\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267/main.js"
@@ -0,0 +1,79 @@
+const Papa = require("papaparse");
+const Chart = require("chart.js/auto");
+const fs = require("fs");
+const path = require("path");
+
+// 读取 CSV 文件
+function readCSVFile(filePath) {
+ return new Promise((resolve, reject) => {
+ fs.readFile(filePath, "utf-8", (err, data) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(data);
+ }
+ });
+ });
+}
+
+// 解析 CSV 数据
+function parseCSVData(csvData) {
+ return new Promise((resolve, reject) => {
+ Papa.parse(csvData, {
+ header: true,
+ skipEmptyLines: true,
+ complete: function (results) {
+ resolve(results.data);
+ },
+ error: function (error) {
+ reject(error);
+ },
+ });
+ });
+}
+
+// 生成图表
+function generateChart(data) {
+ const ctx = document.getElementById("myChart").getContext("2d");
+ const labels = data.map((item) => item["Label"]);
+ const values = data.map((item) => parseFloat(item["Value"]));
+
+ new Chart(ctx, {
+ type: "bar",
+ data: {
+ labels: labels,
+ datasets: [
+ {
+ label: "Data Values",
+ data: values,
+ backgroundColor: "rgba(75, 192, 192, 0.2)",
+ borderColor: "rgba(75, 192, 192, 1)",
+ borderWidth: 1,
+ },
+ ],
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true,
+ },
+ },
+ },
+ });
+}
+
+// 主函数
+async function main() {
+ try {
+ const csvData = await readCSVFile("data.csv");
+ const parsedData = await parseCSVData(csvData);
+ generateChart(parsedData);
+ } catch (error) {
+ console.error("Error processing CSV data:", error.message);
+ }
+}
+
+// 确保 HTML 页面中有 canvas 元素
+document.write('');
+
+main();
diff --git "a/\346\225\260\346\215\256\345\210\206\346\236\220\345\222\214\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267/readme.md" "b/\346\225\260\346\215\256\345\210\206\346\236\220\345\222\214\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267/readme.md"
new file mode 100644
index 0000000000000000000000000000000000000000..6bdf2f21ada4bae0902786f7e0a998e9dacbfa90
--- /dev/null
+++ "b/\346\225\260\346\215\256\345\210\206\346\236\220\345\222\214\345\217\257\350\247\206\345\214\226\345\267\245\345\205\267/readme.md"
@@ -0,0 +1,48 @@
+# git history search 是一个跨分支历史搜索工具
+主要解决的就是git 不能跨分支搜索历史的问题。尤其是很多有定制开发需求的项目通过分支管理各个项目需求的时候,随着项目和分支个数增加,很难找到其他分支实现过的需求和功能逻辑,通过这个工具可以方便定位commitId 然后通过git cherry-pick 方便的合并代码。
+
+工具实现逻辑:
+1. 手动或自动将远端分支全部同步到本地
+2. 遍历全部分支,记录所有commit 的关键信息
+3. 将commit信息保存到lucene
+4. 通过lucene搜索项目分支信息
+
+
+
+目前支持查询的索引字段有: 项目project
,分支branch
,
+ 提交信息message
,提交时间time
作者author
, 邮箱email
,修改项change
+使用截图:
+
+项目地址:https://gitee.com/konanzheng/ghs
+下载地址: https://gitee.com/konanzheng/ghs/releases/
+程序运行说明:将ghs.exe 和application.properties 放到一个目录下,然后运行ghs.exe 即可。git仓库使用单独的clone方便分支切换。
+application.properties配置文件:
+```
+spring.application.name=ghs
+# 模板配置
+spring.thymeleaf.cache=false
+spring.thymeleaf.prefix=classpath:/templates/
+spring.thymeleaf.encoding=UTF-8
+spring.thymeleaf.suffix=.html
+spring.thymeleaf.mode=HTML
+# lucene 配置
+lucene_directory=D:/git_index
+# 索引前自动更新代码,默认 不开启
+pull_code_before_index=false
+# git-bash.exe 路径用于执行git命令进行代码更新
+git_bash_exe=C:\\Program Files\\Git\\git-bash.exe
+```
+git仓库代码更新配置说明:
+```
+ 1. 手动更新代码
+git branch -r | grep -v '\->' | while read remote; do git branch --track "${remote#origin/}" "$remote"; done
+# 验证本地分支情况
+git branch
+# 创建索引之前可以通过命令checkout每个分支并pull到最新
+git branch | grep -v '\->' | while read br; do git checkout "$br" && git pull --all; done
+2. 自动更新代码
+# 索引前自动更新代码,默认 不开启
+pull_code_before_index=false
+# git-bash.exe 路径用于执行git命令进行代码更新
+git_bash_exe=C:\\Program Files\\Git\\git-bash.exe
+```