Browse Source

增加commit对比,增加AI相关

fulian23 4 months ago
parent
commit
ce64251d15
11 changed files with 361 additions and 94 deletions
  1. 14 0
      .idea/CopilotChatHistory.xml
  2. 7 0
      .idea/GitNexus.iml
  3. 16 0
      aitest.py
  4. 58 18
      api/gitRouter.py
  5. 2 2
      demo.py
  6. 15 73
      gittest.py
  7. 0 0
      gittt.py
  8. 4 1
      models/gitModels.py
  9. 3 0
      test/addLines.py
  10. 121 0
      test/code.txt
  11. 121 0
      test/output.txt

File diff suppressed because it is too large
+ 14 - 0
.idea/CopilotChatHistory.xml


+ 7 - 0
.idea/GitNexus.iml

@@ -7,4 +7,11 @@
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>
+  <component name="PyNamespacePackagesService">
+    <option name="namespacePackageFolders">
+      <list>
+        <option value="$MODULE_DIR$/api" />
+      </list>
+    </option>
+  </component>
 </module>

+ 16 - 0
aitest.py

@@ -0,0 +1,16 @@
+import os
+from http import HTTPStatus
+from dashscope import Application
+response = Application.call(
+    # 若没有配置环境变量,可用百炼API Key将下行替换为:api_key="sk-xxx"。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。
+    api_key="sk-f49eecdb8e50462aa5cdc4f6f1c27446",
+    app_id='b2fc1c44f28647f3ab2fbe283d7e4102',# 替换为实际的应用 ID
+    prompt='你是谁?')
+
+if response.status_code != HTTPStatus.OK:
+    print(f'request_id={response.request_id}')
+    print(f'code={response.status_code}')
+    print(f'message={response.message}')
+    print(f'请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code')
+else:
+    print(response.output.text)

+ 58 - 18
api/gitRouter.py

@@ -1,15 +1,23 @@
-import os,json
-from fastapi import APIRouter,BackgroundTasks
-from models.gitModels import *
+import os, json,hashlib
+from fastapi import APIRouter, BackgroundTasks
+
 
 from git import Repo
 
 from pydantic import BaseModel
 
+from models.gitModels import Users
+
+
 class RequestBody(BaseModel):
     uuid: str
     repo_url: str
 
+class CommitHash(BaseModel):
+    uuid: str
+    repo_url: str
+    commit_hash: str
+
 
 def generate_repo_path(uuid, repo_url):
     repo_name = repo_url.split("/")[-1].replace(".git", "")
@@ -23,36 +31,47 @@ def get_repo(uuid, repo_url):
         return 0
     return Repo(path)
 
-gitrouter=APIRouter()
+
+gitrouter = APIRouter()
+
 
 @gitrouter.post("/clone")
 async def clone(request: RequestBody, background_tasks: BackgroundTasks):
     local_path, repo_name = generate_repo_path(request.uuid, request.repo_url)
     if os.path.exists(local_path):
-        return {"status": "400", "msg": "仓库已存在", "uuid": request.uuid, "repo_url": request.repo_url, "path": local_path}
+        return {"status": "400", "msg": "仓库已存在", "uuid": request.uuid, "repo_url": request.repo_url,
+                "path": local_path}
     else:
         background_tasks.add_task(Repo.clone_from, request.repo_url, local_path)
-        response= {"status":"200","msg": "成功创建克隆任务","uuid":request.uuid,"repoName":repo_name,"local_path":local_path}
+        response = {"status": "200", "msg": "成功创建克隆任务", "uuid": request.uuid, "repo_name": repo_name,
+                    "local_path": local_path}
         return response
 
+
 @gitrouter.post("/log")
 async def log(request: RequestBody):
+    user=await Users.get(uuid=request.uuid)
+    email = user.email
+    # email = "gshn666@qq.com"
+    email_md5 = hashlib.md5(email.encode(encoding='UTF-8')).hexdigest()
+    avatar = "https://cravatar.cn/avatar/"+email_md5+"?d=identicon"
     local_path, _ = generate_repo_path(request.uuid, request.repo_url)
-    repo=get_repo(request.uuid,request.repo_url)
+    repo = get_repo(request.uuid, request.repo_url)
     if not repo:
-        return {"status": "400", "msg": "仓库不存在", "uuid": request.uuid, "repo_url": request.repo_url,
-                "local_path": local_path}
-
-    log_=repo.git.log('--pretty={"commit":"%h","author":"%an","summary":"%s","date":"%cd"}', max_count=50,
-                          date='format:%Y-%m-%d %H:%M').split("\n")
-    log=list(map(json.loads, log_))
-    response= {"status":"200","msg": "成功获取日志","uuid":request.uuid,"repo_url":request.repo_url,
-               "local_path":local_path,"git_log":log}
+        return {"status": "404", "msg": "仓库不存在", "uuid": request.uuid, "repo_url": request.repo_url,
+                "local_path": local_path,"email":email,"avatar":avatar}
+
+    log_ = repo.git.log('--pretty={"commit":"%h","author":"%an","summary":"%s","date":"%cd"}', max_count=50,
+                        date='format:%Y-%m-%d %H:%M').split("\n")
+    log = list(map(json.loads, log_))
+    response = {"status": "200", "msg": "成功获取日志", "uuid": request.uuid, "repo_url": request.repo_url,
+                "local_path": local_path, "git_log": log,"email":email,"avatar":avatar}
     return response
 
+
 @gitrouter.post("/status")
 async def status(request: RequestBody):
-    repo=get_repo(request.uuid,request.repo_url)
+    repo = get_repo(request.uuid, request.repo_url)
     # 手动获取所有数据
     active_branch = repo.active_branch
     tracking_branch = active_branch.tracking_branch()
@@ -76,6 +95,27 @@ async def status(request: RequestBody):
     tracking = active_branch.tracking_branch().name
 
     status = {"ahead": ahead, "behind": behind, "conflicted": conflicted, "created": created_files,
-              "current": current,"deleted": deleted, "detached": detached, "files": all_files, "ignored": ignored_files,
-              "modified": modified_files,"not_added": untracked_files, "staged": staged, "tracking": tracking}
+              "current": current, "deleted": deleted, "detached": detached, "files": all_files,
+              "ignored": ignored_files,
+              "modified": modified_files, "not_added": untracked_files, "staged": staged, "tracking": tracking}
     return status
+@gitrouter.post("/change")
+async def change(request: CommitHash):
+    repo = get_repo(request.uuid, request.repo_url)
+    if not repo:
+        return {"status": "404", "msg": "仓库不存在", "uuid": request.uuid, "repo_url": request.repo_url}
+    commit = repo.commit(request.commit_hash)
+
+    if not commit.parents:  # 处理首次提交
+        print("首次提交,无父提交对比")
+        return
+
+    parent = commit.parents[0]
+
+    diffs = parent.diff(commit,create_patch=True, no_renames=True)
+
+    for diff in diffs:
+        print(f"文件 {diff.a_path} ({diff.change_type}):")
+        print(diff.diff.decode('utf-8'))
+
+

+ 2 - 2
demo.py

@@ -13,8 +13,8 @@ monkey_patch_for_docs_ui(app)
 register_tortoise(app=app, config=TORTOISE_ORM)
 
 @app.get("/user/{id}")
-async def test(id: int):
-    user= await Users.get(id=id)
+async def test(id: str):
+    user= await Users.get(uuid="9992cddb-b7d1-99ec-1bd2-35fdc177e623")
     print(type(user))
     return user
 app.include_router(gitrouter,prefix="/git")

+ 15 - 73
gittest.py

@@ -1,82 +1,24 @@
 from git import Repo
-import os
 
-# 配置参数
-repo_url_https = "https://github.com/fulian23/Maxtool"  # HTTPS克隆地址
-repo_url_ssh = "git@github.com:fulian23/Maxtool.git"  # SSH推送地址
-local_path = r"C:\Users\32965\Maxtool"  # 本地存储路径
-ssh_key_path = r"C:\Users\32965\.ssh\id_rsa"  # SSH私钥路径
 
-# 1. 克隆仓库(使用HTTPS)
-# Repo.clone_from(repo_url_https, local_path)
+def debug_commit_changes(repo_path, commit_hash, target_files=None):
+    repo = Repo(repo_path)
+    commit = repo.commit(commit_hash)
 
-# # 2. 修改远程仓库地址为SSH格式
-# repo = Repo(local_path)
-# origin = repo.remotes.origin
-# origin.set_url(repo_url_ssh)
-#
-# # 3. 创建测试文件
-# test_file = os.path.join(local_path, "test.txt")
-# with open(test_file, "w") as f:
-#     f.write("This is a test file added via GitPython")
-#
-# # 4. 提交更改
-# repo.git.add("test.txt")
-# repo.git.commit("-m", "Add test file")
-#
-# # 5. 配置SSH密钥并推送
-# ssh_cmd = f"ssh -i {ssh_key_path}"
-# with repo.git.custom_environment(GIT_SSH_COMMAND=ssh_cmd):
-#     origin.push()
+    if not commit.parents:
+        print("Initial commit, no parent to compare")
+        return
 
-# local_path = os.path.join('test', 't1')
-from git import Repo
-
-repo = Repo("../9992cddb-b7d1-99ec-1bd2-35fdc177e623/test")
-# branches = repo.remote().refs
-# for item in branches:
-#     print(item.remote_head)
-
-
-# 1. 基础状态
-
-
-# 2. 本地与远程差异
-active_branch = repo.active_branch
-tracking_branch = active_branch.tracking_branch()
-ahead = sum(1 for _ in repo.iter_commits(f"{active_branch}..{tracking_branch}"))
-behind = sum(1 for _ in repo.iter_commits(f"{tracking_branch}..{active_branch}"))
-
-conflicts = repo.index.unmerged_blobs()
-
-conflicted = [path for path, entries in conflicts.items()]
-
-created_files = repo.untracked_files
-current = repo.active_branch.name
-
-head_commit = repo.head.commit
-tree = head_commit.tree
-all_files = [item.path for item in tree.traverse() if item.type == 'blob']
-
-diffs = repo.index.diff(None)
-deleted = [d.a_path for d in diffs if d.change_type == 'D']
-
-detached = repo.head.is_detached
-
-ignored_files = repo.git.execute(["git", "ls-files", "--others", "--ignored", "--exclude-standard"]).split("\n")
-
-modified_files = [d.a_path for d in diffs]
+    parent = commit.parents[0]
+    # 强制生成文本差异,忽略重命名
+    diffs = parent.diff(commit, create_patch=True, no_renames=True)
 
-untracked_files = repo.untracked_files
+    for diff in diffs:
+        path = diff.a_path if diff.a_path else diff.b_path
+        print(f"文件: {path} ({diff.change_type})")
 
-staged_entries = repo.index.entries
-staged = [path[0] for path, _ in staged_entries.items()]
+        print(f"代码变更: {diff.diff.decode('utf-8')}")
 
-tracking = active_branch.tracking_branch().name
 
-status = {"ahead": ahead, "behind": behind, "condlicted": conflicted, "created": created_files,
-          "current": current,
-          "deleted": deleted, "detached": detached, "files": all_files, "ignored": ignored_files,
-          "modified": modified_files,
-          "not_added": untracked_files, "staged": staged, "tracking": tracking}
-print(status)
+# 调用示例
+debug_commit_changes(r"C:\Users\32965\repo\9992cddb-b7d1-99ec-1bd2-35fdc177e623\test", "a0126f5620377c4894394c918786c6e98d218b6c", target_files=["README.md"])

+ 0 - 0
gittt.py


+ 4 - 1
models/gitModels.py

@@ -8,4 +8,7 @@ class Users(Model):
     session = fields.CharField(max_length=50, unique=True)
     password = fields.CharField(max_length=50)
     permission = fields.JSONField()
-    git_info = fields.JSONField()
+    git_info = fields.JSONField()
+    avatar = fields.CharField(max_length=500, null=True)
+    email = fields.CharField(max_length=50, null=True)
+    registTime = fields.BigIntField()

+ 3 - 0
test/addLines.py

@@ -0,0 +1,3 @@
+with open('code.txt', 'r',encoding="utf8") as f_in, open('output.txt', 'w',encoding="utf8") as f_out:
+    for line_num, line in enumerate(f_in, start=1):
+        f_out.write(f"{line_num}\t{line}")  # 行号后添加制表符[1,3](@ref)

+ 121 - 0
test/code.txt

@@ -0,0 +1,121 @@
+import os, json,hashlib
+from fastapi import APIRouter, BackgroundTasks
+
+
+from git import Repo
+
+from pydantic import BaseModel
+
+from models.gitModels import Users
+
+
+class RequestBody(BaseModel):
+    uuid: str
+    repo_url: str
+
+class CommitHash(BaseModel):
+    uuid: str
+    repo_url: str
+    commit_hash: str
+
+
+def generate_repo_path(uuid, repo_url):
+    repo_name = repo_url.split("/")[-1].replace(".git", "")
+    base_path = os.path.join("C:/Users/32965/repo", uuid)
+    return os.path.join(base_path, repo_name), repo_name
+
+
+def get_repo(uuid, repo_url):
+    path, _ = generate_repo_path(uuid, repo_url)
+    if not os.path.exists(path):
+        return 0
+    return Repo(path)
+
+
+gitrouter = APIRouter()
+
+
+@gitrouter.post("/clone")
+async def clone(request: RequestBody, background_tasks: BackgroundTasks):
+    local_path, repo_name = generate_repo_path(request.uuid, request.repo_url)
+    if os.path.exists(local_path):
+        return {"status": "400", "msg": "仓库已存在", "uuid": request.uuid, "repo_url": request.repo_url,
+                "path": local_path}
+    else:
+        background_tasks.add_task(Repo.clone_from, request.repo_url, local_path)
+        response = {"status": "200", "msg": "成功创建克隆任务", "uuid": request.uuid, "repo_name": repo_name,
+                    "local_path": local_path}
+        return response
+
+
+@gitrouter.post("/log")
+async def log(request: RequestBody):
+    user=await Users.get(uuid=request.uuid)
+    email = user.email
+    # email = "gshn666@qq.com"
+    email_md5 = hashlib.md5(email.encode(encoding='UTF-8')).hexdigest()
+    avatar = "https://cravatar.cn/avatar/"+email_md5+"?d=identicon"
+    local_path, _ = generate_repo_path(request.uuid, request.repo_url)
+    repo = get_repo(request.uuid, request.repo_url)
+    if not repo:
+        return {"status": "404", "msg": "仓库不存在", "uuid": request.uuid, "repo_url": request.repo_url,
+                "local_path": local_path,"email":email,"avatar":avatar}
+
+    log_ = repo.git.log('--pretty={"commit":"%h","author":"%an","summary":"%s","date":"%cd"}', max_count=50,
+                        date='format:%Y-%m-%d %H:%M').split("\n")
+    log = list(map(json.loads, log_))
+    response = {"status": "200", "msg": "成功获取日志", "uuid": request.uuid, "repo_url": request.repo_url,
+                "local_path": local_path, "git_log": log,"email":email,"avatar":avatar}
+    return response
+
+
+@gitrouter.post("/status")
+async def status(request: RequestBody):
+    repo = get_repo(request.uuid, request.repo_url)
+    # 手动获取所有数据
+    active_branch = repo.active_branch
+    tracking_branch = active_branch.tracking_branch()
+    ahead = sum(1 for _ in repo.iter_commits(f"{active_branch}..{tracking_branch}"))
+    behind = sum(1 for _ in repo.iter_commits(f"{tracking_branch}..{active_branch}"))
+    conflicts = repo.index.unmerged_blobs()
+    conflicted = [path for path, entries in conflicts.items()]
+    created_files = repo.untracked_files
+    current = repo.active_branch.name
+    head_commit = repo.head.commit
+    tree = head_commit.tree
+    all_files = [item.path for item in tree.traverse() if item.type == 'blob']
+    diffs = repo.index.diff(None)
+    deleted = [d.a_path for d in diffs if d.change_type == 'D']
+    detached = repo.head.is_detached
+    ignored_files = repo.git.execute(["git", "ls-files", "--others", "--ignored", "--exclude-standard"]).split("\n")
+    modified_files = [d.a_path for d in diffs]
+    untracked_files = repo.untracked_files
+    staged_entries = repo.index.entries
+    staged = [path[0] for path, _ in staged_entries.items()]
+    tracking = active_branch.tracking_branch().name
+
+    status = {"ahead": ahead, "behind": behind, "conflicted": conflicted, "created": created_files,
+              "current": current, "deleted": deleted, "detached": detached, "files": all_files,
+              "ignored": ignored_files,
+              "modified": modified_files, "not_added": untracked_files, "staged": staged, "tracking": tracking}
+    return status
+@gitrouter.post("/change")
+async def change(request: CommitHash):
+    repo = get_repo(request.uuid, request.repo_url)
+    if not repo:
+        return {"status": "404", "msg": "仓库不存在", "uuid": request.uuid, "repo_url": request.repo_url}
+    commit = repo.commit(request.commit_hash)
+
+    if not commit.parents:  # 处理首次提交
+        print("首次提交,无父提交对比")
+        return
+
+    parent = commit.parents[0]
+
+    diffs = parent.diff(commit,create_patch=True, no_renames=True)
+
+    for diff in diffs:
+        print(f"文件 {diff.a_path} ({diff.change_type}):")
+        print(diff.diff.decode('utf-8'))
+
+

+ 121 - 0
test/output.txt

@@ -0,0 +1,121 @@
+1	import os, json,hashlib
+2	from fastapi import APIRouter, BackgroundTasks
+3	
+4	
+5	from git import Repo
+6	
+7	from pydantic import BaseModel
+8	
+9	from models.gitModels import Users
+10	
+11	
+12	class RequestBody(BaseModel):
+13	    uuid: str
+14	    repo_url: str
+15	
+16	class CommitHash(BaseModel):
+17	    uuid: str
+18	    repo_url: str
+19	    commit_hash: str
+20	
+21	
+22	def generate_repo_path(uuid, repo_url):
+23	    repo_name = repo_url.split("/")[-1].replace(".git", "")
+24	    base_path = os.path.join("C:/Users/32965/repo", uuid)
+25	    return os.path.join(base_path, repo_name), repo_name
+26	
+27	
+28	def get_repo(uuid, repo_url):
+29	    path, _ = generate_repo_path(uuid, repo_url)
+30	    if not os.path.exists(path):
+31	        return 0
+32	    return Repo(path)
+33	
+34	
+35	gitrouter = APIRouter()
+36	
+37	
+38	@gitrouter.post("/clone")
+39	async def clone(request: RequestBody, background_tasks: BackgroundTasks):
+40	    local_path, repo_name = generate_repo_path(request.uuid, request.repo_url)
+41	    if os.path.exists(local_path):
+42	        return {"status": "400", "msg": "仓库已存在", "uuid": request.uuid, "repo_url": request.repo_url,
+43	                "path": local_path}
+44	    else:
+45	        background_tasks.add_task(Repo.clone_from, request.repo_url, local_path)
+46	        response = {"status": "200", "msg": "成功创建克隆任务", "uuid": request.uuid, "repo_name": repo_name,
+47	                    "local_path": local_path}
+48	        return response
+49	
+50	
+51	@gitrouter.post("/log")
+52	async def log(request: RequestBody):
+53	    user=await Users.get(uuid=request.uuid)
+54	    email = user.email
+55	    # email = "gshn666@qq.com"
+56	    email_md5 = hashlib.md5(email.encode(encoding='UTF-8')).hexdigest()
+57	    avatar = "https://cravatar.cn/avatar/"+email_md5+"?d=identicon"
+58	    local_path, _ = generate_repo_path(request.uuid, request.repo_url)
+59	    repo = get_repo(request.uuid, request.repo_url)
+60	    if not repo:
+61	        return {"status": "404", "msg": "仓库不存在", "uuid": request.uuid, "repo_url": request.repo_url,
+62	                "local_path": local_path,"email":email,"avatar":avatar}
+63	
+64	    log_ = repo.git.log('--pretty={"commit":"%h","author":"%an","summary":"%s","date":"%cd"}', max_count=50,
+65	                        date='format:%Y-%m-%d %H:%M').split("\n")
+66	    log = list(map(json.loads, log_))
+67	    response = {"status": "200", "msg": "成功获取日志", "uuid": request.uuid, "repo_url": request.repo_url,
+68	                "local_path": local_path, "git_log": log,"email":email,"avatar":avatar}
+69	    return response
+70	
+71	
+72	@gitrouter.post("/status")
+73	async def status(request: RequestBody):
+74	    repo = get_repo(request.uuid, request.repo_url)
+75	    # 手动获取所有数据
+76	    active_branch = repo.active_branch
+77	    tracking_branch = active_branch.tracking_branch()
+78	    ahead = sum(1 for _ in repo.iter_commits(f"{active_branch}..{tracking_branch}"))
+79	    behind = sum(1 for _ in repo.iter_commits(f"{tracking_branch}..{active_branch}"))
+80	    conflicts = repo.index.unmerged_blobs()
+81	    conflicted = [path for path, entries in conflicts.items()]
+82	    created_files = repo.untracked_files
+83	    current = repo.active_branch.name
+84	    head_commit = repo.head.commit
+85	    tree = head_commit.tree
+86	    all_files = [item.path for item in tree.traverse() if item.type == 'blob']
+87	    diffs = repo.index.diff(None)
+88	    deleted = [d.a_path for d in diffs if d.change_type == 'D']
+89	    detached = repo.head.is_detached
+90	    ignored_files = repo.git.execute(["git", "ls-files", "--others", "--ignored", "--exclude-standard"]).split("\n")
+91	    modified_files = [d.a_path for d in diffs]
+92	    untracked_files = repo.untracked_files
+93	    staged_entries = repo.index.entries
+94	    staged = [path[0] for path, _ in staged_entries.items()]
+95	    tracking = active_branch.tracking_branch().name
+96	
+97	    status = {"ahead": ahead, "behind": behind, "conflicted": conflicted, "created": created_files,
+98	              "current": current, "deleted": deleted, "detached": detached, "files": all_files,
+99	              "ignored": ignored_files,
+100	              "modified": modified_files, "not_added": untracked_files, "staged": staged, "tracking": tracking}
+101	    return status
+102	@gitrouter.post("/change")
+103	async def change(request: CommitHash):
+104	    repo = get_repo(request.uuid, request.repo_url)
+105	    if not repo:
+106	        return {"status": "404", "msg": "仓库不存在", "uuid": request.uuid, "repo_url": request.repo_url}
+107	    commit = repo.commit(request.commit_hash)
+108	
+109	    if not commit.parents:  # 处理首次提交
+110	        print("首次提交,无父提交对比")
+111	        return
+112	
+113	    parent = commit.parents[0]
+114	
+115	    diffs = parent.diff(commit,create_patch=True, no_renames=True)
+116	
+117	    for diff in diffs:
+118	        print(f"文件 {diff.a_path} ({diff.change_type}):")
+119	        print(diff.diff.decode('utf-8'))
+120	
+121	

Some files were not shown because too many files changed in this diff