• [技术干货] Git拉取代码的六种高效方式 【转载】
    1. git pull:拉取并自动合并(最常用)作用:从当前分支跟踪的远程分支拉取最新提交,并尝试自动合并。适用场景:日常开发中快速更新本地分支。注意:可能会导致冲突,需手动解决。1git pull等价于:12git fetchgit merge origin/main示例:12git checkout maingit pull2. git pull --rebase:拉取并变基(避免多余合并提交)作用:将本地提交“移到”远程最新提交之后,保持提交历史线性。适用场景:多人协作开发时希望保持清晰的提交历史。优点:避免产生多余的合并提交。注意:如果本地有冲突,需逐个解决并继续变基。1git pull --rebase示例:12git checkout feature/logingit pull --rebase origin main3. git fetch + git merge:分步拉取与合并(更安全)作用:先拉取远程仓库内容,再手动决定是否合并。适用场景:需要查看远程更新后再决定是否合并。优点:更灵活、可控性强。12git fetchgit merge origin/main示例:12git fetch origingit merge origin/feature/login4. git fetch + git rebase:分步拉取与变基(推荐协作使用)作用:先拉取远程内容,再将本地提交变基到远程最新提交之上。适用场景:多人协作开发,希望保持提交历史整洁。优点:避免合并提交,提交历史更清晰。12git fetchgit rebase origin/main示例:12git fetch origingit rebase origin/feature/login5. git pull origin <branch>:指定远程分支拉取作用:从指定远程分支拉取代码。适用场景:本地分支未设置追踪关系或想拉取其他分支。1git pull origin dev示例:12git checkout devgit pull origin dev6. git pull --ff-only:仅允许快进合并(防止合并提交)作用:只有当本地没有新提交时才允许合并(快进),否则报错。适用场景:确保本地分支是远程分支的直接延续,避免复杂合并。1git pull --ff-only总结表格拉取方式是否自动合并是否保留提交历史是否可能冲突推荐使用场景git pull✅否✅日常开发快速更新git pull --rebase❌✅✅多人协作,保持提交历史线性git fetch + git merge✅否✅需要检查后再合并git fetch + git rebase❌✅✅协作开发,保持提交干净git pull origin <branch>✅否✅指定分支拉取git pull --ff-only❌✅❌确保无冲突,强制快进合并使用建议个人开发分支:推荐使用 git pull --rebase 或 git fetch + git rebase,保持提交历史整洁。多人协作主分支(如 main/dev):使用 git pull 或 git pull --ff-only,确保合并策略符合团队规范。不确定远程变化:优先使用 git fetch 查看差异后再决定如何处理。如需进一步了解 git rebase 和 merge 的区别,可参考 Git 分支管理相关文档。
  • [问题求助] 使用go get 拉取华为git仓库中的代码报401
    有人遇到过这个问题吗?今天之前还好好的,突然就这样了
  • [案例共创] 【案例共创】基于华为开发者空间从0开始完成仓颉编程语言与Python的互操作
    本文基于当前仓颉通用版 (0.53.18) 进行开发演示,该版本对Linux平台提供Python FFI支持。文中的所有操作均在华为开发者空间的云主机中进行。关于Python FFI的更多说明,请参阅仓颉官方文档使用指南 -  ://docs.cangjie-lang.cn/docs/0.53.18/user_manual/source_zh_cn/FFI/cangjie-python.htmlAPI 文档  -  ://docs.cangjie-lang.cn/docs/0.53.18/libs/std/ffi_python/ffi_python_package_overview.html需要额外说明的是,截至2025年6月,仓颉尚未发布LTS版本,鸿蒙仓颉尚未提供Python FFI支持。。因此,Python FFI功能在0.53.x后续Canary版本中的变动不在本文讨论范围内。补充更新:2025年7月1日发布的仓颉(通用版)LTS1.0移除了Python FFI。0. 初始化开发者空间的云主机进入开发者空间 (cid:link_0),系统选择Ubuntu,这样可以确保预装了Python、Cangjie、以及对应的CodeArts IDE。华为云空间提供完整的开发环境。打开云主机,初始化后进入云桌面,可以通过终端命令行指令检查系统当前的 Cangjie 和 Python 版本。问🤔:什么是云主机?答🤓:云主机是开发者空间众多功能的其中之一,而云桌面是云主机默认的登录方式,此外还有无图形界面的云终端形式。1. 创建项目虽然云主机自身提供一定程度的数据持久化功能,但为了更好管理自己的代码,我们需要一个完善的流程来管理项目代码,避免意外情况造成的数据丢失。云桌面上的CodeArts IDE非常贴心地提供了完整的GitCode服务支持,此处我们可以通过点击云桌面上的GitCode快捷方式,在云主机的Firefox浏览器中访问GitCode网站。💡tips: 考虑到云主机的计算资源有限,不建议在复杂场景下高强度使用云主机内置的浏览器,同样的需求可以在外部浏览器中得到满足。GitCode的注册非常简单,此处过程省略。打开CodeArts IDE for Cangjie,其界面应如下所示:1.3 导入工程为方便后续操作,这里选择 导入GitCode 工程 。接下来我们需要获取登录令牌,在登录GitCode之后,可以根据链接指示进行操作。下图是手动创建登录令牌的界面参考,主要需要关注的是有效期和权限设置。注:熟悉云主机功能的开发者可能有注意到一个叫作同步项目的功能。这个功能的原理和上面的手动配置操作类似,区别在于同步项目需要自己手动关联华为云账号和GitCode账号;其次它目前有一个缺点,每次同步都会创建新的项目目录,此处手动配置token正是为了避免这个问题。1.1 创建代码仓库因为本文的预设前提是从0开始,故此处选择创建一个空项目,勾选初始化README。创建时需要留意 .gitignore文件 和 LICENSE 这两个文件的选择。初始化创建新项目后,切回 CodeArts IDE,选择刚创建的项目。1.1 初始化仓颉项目新建空仓库后可通过终端命令 cjpm new 完成项目初始化,通过 cjpm run 测试初始化项目运行结果。看到hello world即说明仓颉开发环境能够正常工作,在开始Python FFI代码编写前,我们通过Git提交代码完成项目的初始化。2. 在仓颉中调用Python函数2.2 指定libpython动态库位置仓颉的Python FFI默认只提供动态链接库的位置检测支持,动态库加载策略如下图所示:由于云主机预装的Python 3.12是不满足第二种查找策略的两点要求,若直接执行以下示例代码,则会报错退出:import std.ffi.python.*import std.log.*main(): Int64 { Python.load(loglevel: LogLevel.INFO) print("${Python.getVersion()}\n") Python.unload() return 0}为了解决这个问题,我们可以通过指定 PYTHON_DYNLIB 环境变量,强制仓颉运行时使用策略1进行动态库加载:export PYTHON_DYNLIB=$(find $(python -c 'import sysconfig as s;print(s.get_path("stdlib"))') -name 'libpython*' 2>/dev/null)也可以通过仓颉的子进程调用完成这个操作:// import std.io.* // import std.os.* // import std.os.process.* func pylib_path(config!: Bool = false): String { const PYEK = "PYTHON_DYNLIB" if (let Some(ev) <- getEnv(PYEK)) { return ev } let p = Process.start("bash", "-c", ##"find $( python3 -c \ 'import sysconfig as s; print(s.get_path("stdlib"))' ) \ -name 'libpython*' 2> /dev/null "##, stdOut: ProcessRedirect.Pipe) let rv = StringReader(p.stdOut).readToEnd() if (config) { setEnv(PYEK, rv) } return rv }2.1 调整仓颉运行时栈大小由于 Python 互操作使用到大量 Python 库的 native 代码,这部分代码在仓颉侧无法对其进行相应的栈保护。仓颉栈保护默认大小为 64KB,在对 Python C API 进行调用过程中,容易造成 native 代码超出默认栈大小,发生溢出,会触发不可预期的结果。建议用户在执行 Python 互操作相关代码前,配置仓颉默认栈大小至少为 1MB:export cjStackSize=1MB 3. 在仓颉中调用Python3.1 导入模块3.2 执行代码
  • [专题汇总] 【干货合集】上半年最后一次干货合集它来了,速来
    大家好,转眼间就过去半年了,这次带来上半年最后一次干货合集,希望可以帮助到大家。1.Python中自动化运维应用详细指南 【转载】https://bbs.huaweicloud.com/forum/thread-02112186033372780040-1-1.html2.Python中多进程调试技巧指南【转载】https://bbs.huaweicloud.com/forum/thread-0254186033461767036-1-1.html3.Python中分布式框架Ray扩展的详细指南【转载】https://bbs.huaweicloud.com/forum/thread-0254186033514531037-1-1.html4.Go语言代码格式化的技巧分享【转载】https://bbs.huaweicloud.com/forum/thread-0291186033591572046-1-1.html5.利用Python脚本实现批量将图片转换为WebP格式【转载】https://bbs.huaweicloud.com/forum/thread-02127186033644703043-1-1.html6.python如何调用java的jar包【转载】https://bbs.huaweicloud.com/forum/thread-0254186033702816038-1-1.html7.Python多进程开发之如何轻松突破GIL瓶颈【转载】https://bbs.huaweicloud.com/forum/thread-02112186033758338041-1-1.html8.Python嵌套循环的实现示例【转载】https://bbs.huaweicloud.com/forum/thread-0234186033811542039-1-1.html9.使用Python绘制动态方块热力图【转载】https://bbs.huaweicloud.com/forum/thread-02112186033897928042-1-1.html10.Python实现安全密码生成器的示例代码【转载】https://bbs.huaweicloud.com/forum/thread-02112186033937396043-1-1.html11.GitLab文件的上传与下载方式【转载】https://bbs.huaweicloud.com/forum/thread-02101186034000720043-1-1.html12.git stash命令基本用法详解【转载】https://bbs.huaweicloud.com/forum/thread-0206186034045364030-1-1.html13.通过git命令查看项目连接的仓库地址的几种方法【转载】https://bbs.huaweicloud.com/forum/thread-02127186034104222044-1-1.html14.S3 标签字符清洗的正则表达式实践记录【转载】https://bbs.huaweicloud.com/forum/thread-0255186034229565036-1-1.html15.Git配置SSH密钥与私钥的教程(跨平台完整指南)【转载】https://bbs.huaweicloud.com/forum/thread-0206186034272730031-1-1.html
  • [技术干货] Git配置SSH密钥与私钥的教程(跨平台完整指南)【转载】
    一、检查是否已有 SSH 密钥在开始之前,先检查本地是否已经存在 SSH 密钥对:1ls -al ~/.ssh如果你看到如下文件,则说明已经有密钥对了:id_rsa(私钥)id_rsa.pub(公钥)你可以选择继续使用现有密钥,或跳过此步骤并生成新的密钥对。二、生成新的 SSH 密钥对无论使用哪种操作系统,我们都可以使用以下命令生成新的 SSH 密钥对:1ssh-keygen -t rsa -b 4096 -C "your_email@example.com"macOS打开 终端(Terminal)。输入上述命令生成密钥对。按照提示选择保存路径(默认为 ~/.ssh/id_rsa)。设置一个可选的密码来保护你的私钥。添加到钥匙串(推荐)为了更方便地管理你的 SSH 私钥,在 macOS 上你可以运行以下命令将私钥添加到钥匙串中:1ssh-add --apple-use-keychain ~/.ssh/id_rsa这样可以避免每次使用时都要输入密码。Windows建议使用 Git Bash 或 PowerShell 来执行命令。1.打开 Git Bash 或 PowerShell。2.输入以下命令生成密钥对:1ssh-keygen -t rsa -b 4096 -C "your_email@example.com"3.按照提示选择保存路径,默认为 C:\Users\<YourUsername>\.ssh\id_rsa。4.可选:设置密码以增强安全性。使用 SSH Agent为了让 Windows 记住你的 SSH 密钥,你需要启动 SSH Agent 并添加你的私钥:12345# 启动 SSH Agenteval $(ssh-agent -s) # 添加私钥ssh-add ~/.ssh/id_rsa如果你使用的是 Windows 10/11,也可以启用 OpenSSH 客户端:控制面板 > 程序 > 启用或关闭 Windows 功能 > OpenSSH 客户端Linux1.打开 终端(Terminal)。2.输入以下命令生成密钥对:1ssh-keygen -t rsa -b 4096 -C "your_email@example.com"3.按照提示选择保存路径(默认为 ~/.ssh/id_rsa)。4.设置一个可选的密码来保护你的私钥。自动加载 SSH 密钥(可选)为了不每次都手动添加密钥,可以在 ~/.bashrc 或 ~/.zshrc 中添加以下内容:12eval "$(ssh-agent -s)"ssh-add ~/.ssh/id_rsa然后重新加载配置:1source ~/.bashrc   # 或 source ~/.zshrc三、将公钥添加到 Git 平台(如 GitHub、Gitee 等)1.查看公钥内容:1cat ~/.ssh/id_rsa.pub复制输出内容。2.登录你的 Git 平台账户(如 GitHub、Gitee、GitLab 等)。3.进入 Settings -> SSH and GPG keys(不同平台名称可能略有不同)。4.点击 New SSH key,粘贴刚刚复制的内容,填写标题后点击保存。四、测试 SSH 连接执行以下命令测试是否能成功连接到 Git 平台(以 GitHub 为例):1ssh -T git@github.com如果看到类似如下信息,说明配置成功:Hi username! You've successfully authenticated, but GitHub does not provide shell access.五、配置多个 SSH 账户(可选)如果你有多个 Git 账号(例如工作账号和个人账号),可以通过配置不同的 SSH 密钥实现多账户管理。1. 生成额外的密钥对例如生成用于工作的密钥:1ssh-keygen -t rsa -b 4096 -C "work_email@example.com" -f ~/.ssh/id_rsa_work2. 创建或编辑 SSH 配置文件1nano ~/.ssh/config添加如下内容(以 GitHub 为例):1234567891011# 默认账号Host github.com    HostName github.com    User git    IdentityFile ~/.ssh/id_rsa # 工作账号Host github-work    HostName github.com    User git    IdentityFile ~/.ssh/id_rsa_work3. 测试不同账户使用别名来测试:1ssh -T git@github-work六、常见问题与解决方法提示“Permission denied (publickey)”怎么办?检查 SSH 配置是否正确。确保公钥已添加到 Git 平台。使用 ssh -v git@github.com 查看详细错误日志。如何查看当前使用的 SSH 密钥?1ssh -i ~/.ssh/id_rsa.pub -v git@github.com如何切换默认密钥?修改 ~/.ssh/config 文件中的 IdentityFile 指向即可。
  • [技术干货] 通过git命令查看项目连接的仓库地址的几种方法【转载】
    要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法:1. 查看所有远程仓库地址使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL:1git remote -v输出示例:12origin  https://github.com/用户名/仓库名.git (fetch)origin  https://github.com/用户名/仓库名.git (push)这里的 origin 是远程仓库的默认名称,后面的 URL 就是仓库地址。2. 查看特定远程仓库的地址如果您想查看某个特定远程仓库的地址(例如 origin),可以使用:1git remote get-url origin输出示例:1https://github.com/用户名/仓库名.git3. 查看详细的配置信息使用 git config 命令查看完整的配置文件:1git config --list在输出中查找 remote.origin.url 字段,它对应仓库地址。4. 直接查看配置文件项目的 Git 配置文件位于 .git/config,您可以直接打开它:1cat .git/config文件内容类似:123[remote "origin"]  url = https://github.com/用户名/仓库名.git  fetch = +refs/heads/*:refs/remotes/origin/*5. 查看克隆时的原始 URL如果您是通过克隆(clone)创建的项目,可以查看:1git config --get remote.origin.url注意事项多个远程仓库:如果项目连接了多个远程仓库(例如 origin 和 upstream),git remote -v 会显示所有仓库的地址。SSH 与 HTTPS:URL 可能是 SSH 格式(git@github.com:用户名/仓库名.git)或 HTTPS 格式(https://github.com/用户名/仓库名.git)。本地项目:如果项目从未连接过远程仓库(纯本地项目),这些命令可能不会返回任何结果。通过以上命令,您可以快速找到项目当前连接的仓库地址!
  • [技术干货] git stash命令基本用法详解【转载】
    一、基本用法1. 保存当前修改(包括暂存区和工作区的内容)1git stash2. 查看保存了哪些 stash1git stash list示例输出:stash@{0}: WIP on main: 1234567 Fix bugstash@{1}: WIP on feature-x: abcdef0 Add feature x3. 恢复最近一次 stash 并删除它1git stash pop4. 恢复某一个 stash 并删除它1git stash pop stash@{1}5. 恢复某一个 stash 但保留它1git stash apply stash@{1}6. 删除某一个 stash1git stash drop stash@{0}7. 清除所有 stash1git stash clear二、常见增强用法1. 只 stash 暂存区和工作区中已修改的文件(不包括未追踪文件)1git stash -k   # 等价于 git stash --keep-index2. 包括未追踪的文件一起 stash1git stash -u   # 等价于 git stash --include-untracked3. 包括未追踪和忽略的文件一起 stash1git stash -a   # 等价于 git stash --all4. 带描述信息1git stash save "WIP: 修复登录页面问题"注意:Git 2.15 之后推荐使用 git stash push -m "message" 代替 save。三、举个例子假设你在 main 分支开发中临时要切换到 bugfix 分支修复紧急问题:12345git stash           # 保存当前未提交的代码git checkout bugfix # 切换分支...                 # 修复并提交git checkout main   # 回到原分支git stash pop       # 恢复之前保存的代码
  • [技术干货] GitLab文件的上传与下载方式【转载】
    GitLab 项目拉取到本地123456# 拉取git项目到本地  -b参数为选择拉取的特定分支git clone -b dev http://gitlab.yuan.cn/AI_PRODUCT/deplibs_v4.0/chat.git # git clone项目到本地之后可以通过下述命令更新本地代码git reset --hard HEAD  # 如果发生冲突,可以通过该命令返回到上一个版本解决冲突git pull origin dev  # 将git上dev分支代码更新到本地GitLab 项目上传方法##先在GitLab创建项目仓库1. 登录 GitLab:进入 GitLab 并选择目标组(或个人空间)。2. 创建新仓库:点击 New Project > 选择 Create Blank Project。填写项目名称并点击 Create Project。3. 获取仓库地址:在新建的仓库页面,复制 SSH 或 HTTPS 地址。4. 常见问题未配置用户名和邮箱:如果提示 Please tell me who you are,配置全局用户名和邮箱:12git config --global user.name "Your Name"git config --global user.email "your_email@example.com"5. 权限错误 (403):如果使用 HTTPS,请检查用户名和密码(或 Access Token)。如果使用 SSH,请确认公钥已正确添加到 GitLab。6. 远程分支冲突:如果推送时提示分支冲突,先拉取远程分支:12git pull origin main --rebasegit push方法 1:本地项目未初始化Git123456789101112131415161718192021## 初始化本地项目并上传进入本地项目目录:cd /path/to/your/project 初始化 Git 仓库:git init 连接远程仓库:git remote add origin <repository_url> 添加文件并提交:git add .git commit -m "Initial commit" 推送代码到远程仓库:git branch -M main  # 如果远程仓库主分支是 maingit push -u origin main如果需要推送到新分支,按下述步骤执行:git checkout -b 新分支名称  # 会创建一个新分支并切换到该分支下检查当前所在分支: git branchgit push -u origin 新分支名称方法 2:本地项目已初始化Git123456789检查是否已设置远程仓库:git remote -v 如果未设置,添加远程仓库:git remote add origin <repository_url> 将本地代码推送到远程仓库:git branch -M main  # 如果远程仓库主分支是 maingit push -u origin mainGitLab 上分支重命名方法:重命名远程分支(如 GitLab 上的分支)假设你要将分支 old-branch 重命名为 new-branch。步骤如下:# 1. 切换到要重命名的分支1git checkout old-branch# 2. 重命名为新分支名1git branch -m new-branch# 3. 推送新分支到远程1git push origin new-branch# 4. 删除远程旧分支1git push origin --delete old-branch可选:如果有其他人使用该分支提醒他们使用以下命令同步更新:123git fetch origingit branch -u origin/new-branch new-branchgit remote prune origin🚨 注意事项:GitLab 中的默认分支(如 main 或 master)不能直接删除,你必须先在项目设置中更改默认分支;重命名分支后,GitLab 上的 Merge Requests 不会自动迁移,你可能需要手动重新指定目标分支。
  • [案例共创] 【案例共创】基于仓颉编程语言+DeepSeek实现模拟课堂讨论助手
    【案例共创】基于仓颉编程语言+DeepSeek实现模拟课堂讨论助手项目概述本项目旨在通过华为开发者空间云主机,结合仓颉编程语言和DeepSeek API,开发一个简单的模拟课堂讨论助手。用户可以通过命令行输入相关知识点或问题,应用会返回该知识点的详细解释和记忆方法。这个助手可以用诙谐幽默的方式学习各种知识。适用对象喜欢用诙谐幽默的方式学习知识的爱好者。案例流程说明:① 使用CodeArts IDE for Cangjie编辑器开发仓颉程序调用;② 领取免费 DeepSeeK Tokens;③ 仓颉对接DeepSeek接口。开发步骤1. 环境准备注册并登录华为开发者空间:访问华为开发者空间官网,注册并登录账户。创建云主机实例:在华为开发者空间中创建一个新的云主机实例,选择默认的配置。使用开箱即用的云主机实例开发,点击 打开云主机 再点击打开远程桌面。图示即可看出是开箱即用的CodeArts IDE for Cangjie编辑器2. 项目初始化创建新项目:首先我们进入到开发者空间后,双击打开CodeArts IDE for Cangjie,在CodeArts IDE for Cangjie中创建一个新的项目,命名为“demo”(随便你怎么命名)。名称和位置可以自定义,产物类型选择executable。产物类型说明: executable,可执行文件;static,静态库,是一组预先编译好的目标文件的集合;dynamic,动态库,是一种在程序运行时才被加载到内存中的库文件,多个程序共享一个动态库副本,而不是像静态库那样每个程序都包含一份完整的副本。配置项目结构:设置项目的基本目录结构,包括源代码文件、配置文件等。创建项目后,打开src目录下main.cj文件,在预置代码中增加函数和函数调用代码(没有的话可以自己新建一份,代码内容下文我有附上)。然后点击右上的运行按钮运行项目,在终端中查看输出内容。如果遇到弹窗提示运行/调试配置,点击确定继续操作。修改后的main.cj文件代码:package demo func println_add(a: Int64, b:Int64): Int64 { let number: Int64 = a+b println(number) return number } main(): Int64 { println("hello world") println_add(3,4) println("hello Cangjie") return 0 } 调试项目:点击右上调试按钮,首次会提示配置调试,点击新增配置项,选择Cangjie(cjdb)Debug——launch。调试模式说明:Launch模式,启动调试器的同时加载被调程序;Attach,针对正在运行的程序,附加到已启动的程序。然后可以在main.cj代码中设置断点,再次点击调试按钮。可以在顶部看到调试按钮,在左侧看到调试过程中的变量、监视、调用堆栈、断点信息。2.1 版本管理 可选 (链接到gitcode)点完之后会要求你填入访问密钥先登录gitcode 然后点访问令牌 新建访问令牌 一直下一步即可 会得到一串令牌 然后将令牌输入即可。然后新建一个仓库 名字你自己取当前项目代码仓是空仓库你可以通过克隆仓库开始或使用以下方式为你的项目添加文件:或为你的项目添加以下文件: 添加 README.md添加 LICENSE添加 .gitignore命令行指引你还可以按照以下说明从你的电脑中上传现有文件或项目。Git 全局设置git config --global [user.name](http://user.name/) "weixin_41024010" git config --global user.email "[weixin_41024010@noreply.gitcode.com](mailto:weixin_41024010@noreply.gitcode.com)" 创建一个新仓库git clone [https://gitcode.com/weixin_41024010/test.git](https://gitcode.com/weixin_41024010/test.git) cd test echo "# test" >> [README.md](http://readme.md/) git add [README.md](http://readme.md/) git commit -m "add README" git branch -m main git push -u origin main推送现有的文件cd existing_folder git init git remote add origin [https://gitcode.com/weixin_41024010/test.git](https://gitcode.com/weixin_41024010/test.git) git add . git commit -m "Initial commit" git branch -m main git push -u origin main推送现有的 Git 仓库cd existing_repo git remote rename origin old-origin git remote add origin [https://gitcode.com/weixin_41024010/test.git](https://gitcode.com/weixin_41024010/test.git) git push -u origin --all git push -u origin --tags � 配置好git 就可以使用了 ,但是有个地方要先设置一下:提交文件限制改大一些 ,不然如果没有配置好GIT忽略文件的话 ,会推送不上去。改动可以从这里提交 ,这样就完成了代码的版本控制3. Cangjie对接DeepSeek接口华为云提供了单模型200万免费Tokens,包含DeepSeek-R1&V3满血版,我们可以登录华为云ModelArts Studio(MaaS)控制台领取免费额度,这里我们选择DeepSeek-R1满血版来搭建我们的工程。在云主机桌面底部菜单栏,点击打开火狐浏览器。用火狐浏览器访问ModelArts Studio首页:https://www.huaweicloud.com/product/modelarts/studio.html,点击ModelArts Studio控制台跳转到登录界面,按照登录界面提示登录,即可进入ModelArts Studio控制台。你也可以在本地浏览器打开进行相关操作。领取后点击调用说明,可以获取到对应的API地址、模型名称。当然API Key也要创建下,这里创建很简单,按提示跳转过去就成,我这里就不介绍了。记录对应的API地址、模型名称、API Key留作下面步骤使用。4. Cangjie对接DeepSeek编写代码进入云主机桌面,右键选择Open Terminal Here打开终端命令窗口,克隆仓颉示例代码仓库。git clone https://gitcode.com/CaseDeveloper/Cangjie-Examples.git 使用CodeArts IDE for Cangjie打开AIChat示例项目。左上点击文件—打开项目,选择前面克隆的示例代码目录Cangjie-Examples下的AIChat打开。修改配置config.json配置文件,配置DeepSeek的API信息。配置项:model、model、base_url{ "model": "DeepSeek-V3", "api_key": "Your KEY", "base_url": "[https://maas-cn-southwest-2.modelarts-maas.com/v1/infers/8a062fd4-7367-4ab4-a936-5eeb8c4/v1/chat/completions](https://maas-cn-southwest-2.modelarts-maas.com/v1/infers/8a062fd4-7367-4ab4-a936-5eeb8fb821c4/v1/chat/completions)", "system_prompt": "模拟课堂讨论 - Simulated Classroom Discussion,I need you to help me memorize the noun explanation, after I type a noun, you will simulate 5 students in the class to generate their speeches about the noun. The discussion must be humorous, and easy to understand. The entire conversation and instructions should be provided in Chinese. The first term is: 主题是" } 打开main.cj,运行项目,我们就可跟AI助手进行聊天了。(* 注意:打开main.cj可能提示Console标红,不影响程序运行,可以忽略。亦可以删除import std.console.Console重新编写该导入语句)至此,基于仓颉编程语言+DeepSeek实现模拟课堂讨论助手开发就完成了。接下来我们在聊天项目基础上,改编成“模拟课堂讨论助手”助手,有时候英文提示词会更有效其实超级简单,我们只需要再修改 “system_prompt” 即可。修改内容如下:"system_prompt": "模拟课堂讨论 - Simulated Classroom Discussion,I need you to help me memorize the noun explanation, after I type a noun, you will simulate 5 students in the class to generate their speeches about the noun. The discussion must be humorous, and easy to understand. The entire conversation and instructions should be provided in Chinese. The first term is: 主题是" 运行程序:最后我把代码完整的给大家呈现下(chat.cj):所有代码开源在git clone [https://gitcode.com/weixin_41024010/HUAWEIDEMO.git](https://gitcode.com/weixin_41024010/HUAWEIDEMO.git) 大家可以克隆我的代码使用。使用时要根目录是CHATDEEPSEEK (就是如图所示)不然编译会有问题。具体代码如下:package openai_chat import encoding.json.stream.* import net.http.ClientBuilder import net.http.HttpHeaders import net.http.HttpRequestBuilder import net.tls.TlsClientConfig import net.tls.CertificateVerifyMode import std.collection.ArrayList import std.io.ByteArrayStream import std.time.Duration public struct FunctionCall <: JsonDeserializable<FunctionCall> & JsonSerializable { public let name: String public let arguments: String public init(name: String, arguments: String) { this.name = name this.arguments = arguments } public static func fromJson(r: JsonReader): FunctionCall { var temp_name: String = "" var temp_arguments: String = "" while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "name" => temp_name = r.readValue<String>() case "arguments" => temp_arguments = r.readValue<String>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for FunctionCall") } } return FunctionCall(temp_name, temp_arguments) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("name").writeValue<String>(this.name) w.writeName("arguments").writeValue<String>(this.arguments) w.endObject() w.flush() } } public enum RoleType { User | Assistant | System | Function | NULL } public func role_type_to_str(role: RoleType): Option<String> { return match(role) { case RoleType.User => Some("user") case RoleType.Assistant => Some("assistant") case RoleType.System => Some("system") case RoleType.Function => Some("function") case RoleType.NULL => None } } public func str_to_role_type(role_option_str: Option<String>): RoleType { return match(role_option_str) { case Some(role_option) => match (role_option) { case "user" => RoleType.User case "assistant" => RoleType.Assistant case "system" => RoleType.System case "function" => RoleType.Function case x => throw Exception("unknow enum ${x} for RoleType") } case None => RoleType.NULL } } public struct Message<: JsonDeserializable<Message> & JsonSerializable { public let role: RoleType public var content: String public let function_call: Option<FunctionCall> public init(role: RoleType, content: String) { this.role = role this.content = content this.function_call = None } public init(role: RoleType, content: String, function_call: Option<FunctionCall>) { this.role = role this.content = content this.function_call = function_call } public static func fromJson(r: JsonReader): Message { var temp_role: Option<String> = None // role may null in stream chat var temp_content: String = "" var temp_functional_call: Option<FunctionCall> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "role" => temp_role = r.readValue<Option<String>>() case "content" => temp_content = r.readValue<String>() case "function_call" => temp_functional_call = r.readValue<Option<FunctionCall>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Message") } } let role_type: RoleType = str_to_role_type(temp_role) return Message(role_type, temp_content) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("role").writeValue<Option<String>>(role_type_to_str(this.role)) w.writeName("content").writeValue<String>(this.content) w.endObject() w.flush() } } public struct ChatRequest <: JsonSerializable { private let model: String private let messages: ArrayList<Message> private let max_tokens: Int64 private let temperature: Float64 private let top_p: Float64 private let n: Int32 private let stream: Bool public init( model: String, messages: ArrayList<Message>, max_tokens: Int64, temperature: Float64, top_p: Float64, n: Int32, stream: Bool ) { // construction function with messages this.model = model this.messages = messages this.max_tokens = max_tokens this.temperature = temperature this.top_p = top_p this.n = n this.stream = stream } public init( model: String, prompt: String, history: ArrayList<(String, String)>, system_prompt: String, max_tokens: Int64, temperature: Float64, top_p: Float64, n: Int32, stream: Bool ){ // construction function with prompt and system_prompt this.model = model this.messages = ArrayList<Message>([ Message(RoleType.System, system_prompt) ]) for ((use_msg, bot_msg) in history) { this.messages.append(Message(RoleType.User, use_msg)) this.messages.append(Message(RoleType.Assistant, bot_msg)) } this.messages.append(Message(RoleType.User, prompt)) this.max_tokens = max_tokens this.temperature = temperature this.top_p = top_p this.n = n this.stream = stream } public init( model: String, prompt: String, history: ArrayList<(String, String)>, system_prompt: String, stream: Bool ){ // construction function with prompt and default arguments this.model = model this.messages = ArrayList<Message>([ Message(RoleType.System, system_prompt) ]) for ((use_msg, bot_msg) in history) { this.messages.append(Message(RoleType.User, use_msg)) this.messages.append(Message(RoleType.Assistant, bot_msg)) } this.messages.append(Message(RoleType.User, prompt)) this.max_tokens = 2000 this.temperature = 0.7 this.top_p = 1.0 this.n = 1 this.stream = stream } public func toJson(w: JsonWriter) { w.startObject() w.writeName("model").writeValue<String>(this.model) w.writeName("messages").writeValue<ArrayList<Message>>(this.messages) w.writeName("max_tokens").writeValue<Int64>(this.max_tokens) w.writeName("temperature").writeValue<Float64>(this.temperature) w.writeName("top_p").writeValue<Float64>(this.top_p) w.writeName("n").writeValue<Int32>(this.n) w.writeName("stream").writeValue<Bool>(this.stream) w.endObject() w.flush() } } public struct Choice <: JsonDeserializable<Choice> & JsonSerializable { public let index: Int32 public let message: Option<Message> public let delta: Option<Message> public let finish_reason: Option<String> public let logprobs: Option<Float64> // dashscope for qwen need public init( index: Int32, message: Option<Message>, delta: Option<Message>, finish_reason: Option<String>, logprobs: Option<Float64> ) { this.index = index this.message = message this.delta = delta this.finish_reason = finish_reason this.logprobs = logprobs } public static func fromJson(r: JsonReader): Choice { var temp_index: Int32 = -1 var temp_message: Option<Message> = None var temp_delta: Option<Message> = None var temp_finish_reason: Option<String> = None var temp_logprobs: Option<Float64> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "index" => temp_index = r.readValue<Int32>() case "message" => temp_message = r.readValue<Option<Message>>() case "delta" => temp_delta = r.readValue<Option<Message>>() case "finish_reason" => temp_finish_reason = r.readValue<Option<String>>() case "logprobs" => temp_logprobs = r.readValue<Option<Float64>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Choice") } } return Choice(temp_index, temp_message, temp_delta, temp_finish_reason, temp_logprobs) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("index").writeValue<Int32>(this.index) w.writeName("message").writeValue<Option<Message>>(this.message) w.writeName("delta").writeValue<Option<Message>>(this.delta) w.writeName("finish_reason").writeValue<Option<String>>(this.finish_reason) w.writeName("logprobs").writeValue<Option<Float64>>(this.logprobs) w.endObject() w.flush() } } public struct Usage <: JsonDeserializable<Usage> & JsonSerializable { public let prompt_tokens: UInt64 public let completion_tokens: UInt64 public let total_tokens: UInt64 public init(prompt_tokens: UInt64, completion_tokens: UInt64, total_tokens: UInt64) { this.prompt_tokens = prompt_tokens this.completion_tokens = completion_tokens this.total_tokens = total_tokens } public static func fromJson(r: JsonReader): Usage { var temp_prompt_tokens: UInt64 = 0 var temp_completion_tokens: UInt64 = 0 var temp_total_tokens: UInt64 = 0 while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "prompt_tokens" => temp_prompt_tokens = r.readValue<UInt64>() case "completion_tokens" => temp_completion_tokens = r.readValue<UInt64>() case "total_tokens" => temp_total_tokens = r.readValue<UInt64>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Usage") } } return Usage(temp_prompt_tokens, temp_completion_tokens, temp_total_tokens) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("prompt_tokens").writeValue<UInt64>(this.prompt_tokens) w.writeName("completion_tokens").writeValue<UInt64>(this.completion_tokens) w.writeName("total_tokens").writeValue<UInt64>(this.total_tokens) w.endObject() w.flush() } } public struct ChatResponse <: JsonDeserializable<ChatResponse> { // some api names `id`, and some names `request_id` public let id: Option<String> public let request_id: Option<String> public let system_fingerprint: Option<String> public let model: String public let object: String public let created: UInt64 public let choices: ArrayList<Choice> public let usage: Option<Usage> public init( id: Option<String>, request_id: Option<String>, system_fingerprint: Option<String>, model: String, object: String, created: UInt64, choices: ArrayList<Choice>, usage: Option<Usage> ) { this.id = id this.request_id = request_id this.system_fingerprint = system_fingerprint this.model = model this.object = object this.created = created this.choices = choices this.usage = usage } public static func fromJson(r: JsonReader): ChatResponse { var temp_id: Option<String> = None var temp_request_id: Option<String> = None var temp_system_fingerprint: Option<String> = None var temp_model: String = "" var temp_object: String = "" var temp_created: UInt64 = 0 var temp_choices: ArrayList<Choice> = ArrayList<Choice>([]) var temp_usage: Option<Usage> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "id" => temp_id = r.readValue<Option<String>>() case "request_id" => temp_request_id = r.readValue<Option<String>>() case "system_fingerprint" => temp_system_fingerprint = r.readValue<Option<String>>() case "model" => temp_model = r.readValue<String>() case "object" => temp_object = r.readValue<String>() case "created" => temp_created = r.readValue<UInt64>() case "choices" => temp_choices = r.readValue<ArrayList<Choice>>() case "usage" => temp_usage = r.readValue<Option<Usage>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for ChatResponse") } } return ChatResponse( temp_id, temp_request_id, temp_system_fingerprint, temp_model, temp_object, temp_created, temp_choices, temp_usage ) } } public func get_domain( url: String ): String { var temp_url = url if (temp_url.startsWith("https://")) { temp_url = temp_url["https://".size..] } else if (temp_url.startsWith("http://")) { temp_url = temp_url["http://".size..] } let domain: String = temp_url.split("?")[0].split("/")[0] return domain } public func build_http_client( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)>, stream!: Bool ){ // prepare input data var array_stream = ByteArrayStream() let json_writer = JsonWriter(array_stream) let chat_res = ChatRequest( env_info.model, prompt, history, env_info.system_prompt, stream ) chat_res.toJson(json_writer) let post_data: Array<UInt8> = array_stream.readToEnd() var headers: HttpHeaders = HttpHeaders() headers.add("Authorization", "Bearer ${env_info.api_key}") headers.add("Content-Type", "application/json") if (stream) { headers.add("Accept", "text/event-stream") } let request = HttpRequestBuilder() .url(env_info.base_url) .method("POST") .body(post_data) .readTimeout(Duration.second * 120) .addHeaders(headers) .build() let client = if (env_info.base_url.startsWith("https")) { var tls_client_config = TlsClientConfig() tls_client_config.verifyMode = CertificateVerifyMode.TrustAll tls_client_config.domain = get_domain(env_info.base_url) ClientBuilder() .tlsConfig(tls_client_config) .build() } else { ClientBuilder().build() } return (request, client) } public func chat( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)> ): Option<String> { let (request, client) = build_http_client( prompt, env_info, history, stream: false ) var result_message: Option<String> = None var res_text = "" try { // call api let response = client.send( request ) // read result (support max revice 100k data) let buffer = Array<Byte>(102400, item: 0) let length = response.body.read(buffer) res_text = String.fromUtf8(buffer[..length]) // println("res_text: ${res_text}") var input_stream = ByteArrayStream() input_stream.write(res_text.toArray()) // convert text to ChatResponse object let json_reader = JsonReader(input_stream) let res_object = ChatResponse.fromJson(json_reader) let choices: ArrayList<Choice> = res_object.choices if (choices.size > 0) { let message = choices[0].message.getOrThrow() // println("message: ${message.content}") result_message = Some(message.content) } else { println("can't found any response") } } catch (e: Exception) { println("ERROR: ${e.message}, reviced text is ${res_text}") } client.close() return result_message } public func stream_chat( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)> ): Option<String> { let (request, client) = build_http_client( prompt, env_info, history, stream: true ) var result_response: String = "" var temp_text2 = "" try { // call api let response = client.send( request ) // read result let buffer = Array<Byte>(10240, item: 0) var finish_reason: Option<String> = None while(finish_reason.isNone() && temp_text2 != "[DONE]") { let length = response.body.read(buffer) let res_text = String.fromUtf8(buffer[..length]) for (temp_text in res_text.split("\n")) { temp_text2 = if (temp_text.startsWith("data: ")) { temp_text["data: ".size..] } else { temp_text } if (temp_text2.size == 0) { continue } if (temp_text2 == "[DONE]") { break } // println("========================") // println("temp_text: ${temp_text2}") // println("========================") var input_stream = ByteArrayStream() input_stream.write(temp_text2.toArray()) // convert text to ChatResponse object let json_reader = JsonReader(input_stream) let res_object = ChatResponse.fromJson(json_reader) let choices: ArrayList<Choice> = res_object.choices if (choices.size > 0) { finish_reason = choices[0].finish_reason if (finish_reason.isNone()) { let delta = choices[0].delta.getOrThrow() print("${delta.content}", flush: true) result_response += delta.content } } else { println("can't found any response") } } } } catch (e: Exception) { println("ERROR: ${e.message}, reviced text is ${temp_text2}") } client.close() if (result_response.size > 0) { return Some(result_response) } else { return None } } 5. 运行和测试最后我们测试下DeepSeek,如下图:到此整个项目案例创建完成,可见是非常简单且实用的,希望能给大家带来帮助。我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 cid:link_1参考链接https://gitcode.com/weixin_41024010/HUAWEIDEMOhttps://gitcode.com/Cangjie/Cangjie-Examples
  • [技术干货] git如何查看提交行数、删除行数【转】
    统计指定用户提交代码情况1git log --author="你的名字" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -统计每个人的代码提交情况1git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done在指定时间范围内查询新增字段修改时间since和until1git log  --author="xxxx" --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat:  --since ==2023-6-1 --until=2024-1-1 --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done参数说明12345678910111213141516171819202122232425262728293031323334353637383940414243git log 参数说明:--author   指定作者--stat   显示每次更新的文件修改统计信息,会列出具体文件列表--shortstat    统计每个commit 的文件修改行数,包括增加,删除,但不列出文件列表:  --numstat   统计每个commit 的文件修改行数,包括增加,删除,并列出文件列表:      -p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新       例如:git log -p  -2--name-only 仅在提交信息后显示已修改的文件清单--name-status 显示新增、修改、删除的文件清单--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)--graph 显示 ASCII 图形表示的分支合并历史--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)       例如: git log --pretty=oneline ; git log --pretty=short ; git log --pretty=full ; git log --pretty=fuller--pretty=tformat:   可以定制要显示的记录格式,这样的输出便于后期编程提取分析       例如:git log --pretty=format:""%h - %an, %ar : %s""       下面列出了常用的格式占位符写法及其代表的意义。                          选项       说明                         %H      提交对象(commit)的完整哈希字串                      %h      提交对象的简短哈希字串                      %T      树对象(tree)的完整哈希字串                          %t      树对象的简短哈希字串                           %P      父对象(parent)的完整哈希字串                      %p      父对象的简短哈希字串                          %an     作者(author)的名字                     %ae     作者的电子邮件地址                       %ad     作者修订日期(可以用 -date= 选项定制格式)                          %ar     作者修订日期,按多久以前的方式显示                           %cn     提交者(committer)的名字                       %ce     提交者的电子邮件地址                           %cd     提交日期                       %cr     提交日期,按多久以前的方式显示                     %s      提交说明  --since  限制显示输出的范围,       例如: git log --since=2.weeks    显示最近两周的提交       选项 说明                       -(n)    仅显示最近的 n 条提交                           --since, --after 仅显示指定时间之后的提交。                           --until, --before 仅显示指定时间之前的提交。                         --author 仅显示指定作者相关的提交。                       --committer 仅显示指定提交者相关的提交。
  • [技术干货] Git忽略文件.gitignore操作方法指南【转】
    Git忽略文件.gitignore在工程中,并不是所有文件都需要保存到版本库中的,例如“target”目录及目录下的文件就可以忽略。在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件或目录。gitignore的基础用法#    .gitignore配置文件的一些通用技巧 [参考:https://git-scm.com/docs/gitignore]#     1.空白行不匹配任何文件,所以可以作为可读性的分隔符,同时两端的空格将会被忽略.#     2.使用[#]开头,将会注释掉整行,使其不进行匹配操作,如果需要匹配#开头,可以使用转义字符[\].#     3.1匹配模式以[/]结尾,表示想要匹配一个目录及其子文件.(比如[foo/]会匹配foo目录及其下面的路径.)#     3.2匹配模式不包含[/],将会全局匹配该文件.#     4.通配符#       [*]:  匹配除[/]以外的任何内容,也就意味着[*]不能跨目录.#       [?]:  匹配除[/]和[[]以及[]]以外的任何一个字符.#       [**]: 匹配所有的内容,或者说匹配任意目录下的内容.#         示例:#           1.[**/foo/bar]  将会匹配所有直接在foo目录下的bar,无论foo处在何处.#           2.[foo/**]则表示匹配foo目录下的所有文件和目录.#           3.[a/**/b]则可以匹配a/b, a/c/b, a/c/d/b,即此处的[**]可以表示0个或多个.#           !!! 需要注意的是,除上面示例的用法外,剩余的[**]都是无效的..#     5.可以通过前缀[!]来表示不忽略某些文件,比如可以通过[!a]来确保文件a不会被忽略,即时前面已经声明了忽略其父目录,该模式优先级高于普通忽略模式.Git 三种方法忽略提交的文件在Git项目中定义 .gitignore 文件这种方式通过在项目的某个文件夹下定义 .gitignore 文件,在该文件中定义相应的忽略规则,来管理当前文件夹下的文件的Git提交行为。.gitignore 文件是可以提交到公有仓库中,这就为该项目下的所有开发者都共享一套定义好的忽略规则。在 .gitingore 文件中,遵循相应的语法,在每一行指定一个忽略规则。如:123*.log*.temp/vendor2\在Git项目的设置中指定排除文件这种方式只是临时指定该项目的行为,需要编辑当前项目下的 .git/info/exclude 文件,然后将需要忽略提交的文件写入其中。需要注意的是,这种方式指定的忽略文件的根目录是项目根目录。定义Git全局的 .gitignore 文件除了可以在项目中定义 .gitignore 文件外,还可以设置全局的 git .gitignore 文件来管理所有Git项目的行为。这种方式在不同的项目开发者之间是不共享的,是属于项目之上Git应用级别的行为。这种方式也需要创建相应的 .gitignore 文件,可以放在任意位置。然后在使用以下命令配置1Git: git config --global core.excludesfile \~/.gitignore.gitignore文件中的忽略规则在空格不匹配任意文件,可作为分隔符,可用反斜杠转义# 开头:标识注释,可以使用反斜杠进行转义! 开头:标识否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用 ! 也不会再次被包含。可以使用反斜杠进行转义/ 结束:只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件/ 开头:匹配文件如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录** 匹配多级目录,可在开始,中间,结束? 通用匹配单个字符[] 通用匹配单个字符列表常用匹配示例:123456789bin/ :忽略当前路径下的bin文件夹,该文件夹下的所有内容都会被忽略,不忽略 bin 文件/bin :忽略根目录下的bin文件/*.c :忽略 cat.c,不忽略 build/cat.cdebug/*.obj : 忽略 debug/io.obj,不忽略 debug/common/io.obj 和 tools/debug/io.obj**/foo : 忽略/foo, a/foo, a/b/foo等a/**/b : 忽略a/b, a/x/b, a/x/y/b等!/bin/run.sh : 不忽略 bin 目录下的 run.sh 文件\*.log : 忽略所有 .log 文件config.php : 忽略当前路径的 config.php 文件java忽略文件12345678910111213141516# 编译后的class文件,忽略所有以[.class]结尾的文件*.class# 日志文件,忽略所有以[.log]结尾的文件.*.log# BlueJ 文件,忽略所有以[.ctxt]结尾的文件.*.ctxt# Mobile Tools for Java (J2ME),忽略[.mtj.tmp/]目录及其子文件..mtj.tmp/# 打包文件,忽略所有以[.jar]或[.war]或[.nar]或[.ear]或[.zip]或[.tar.gz]或[rar]结尾的文件.*.jar*.war*.nar*.ear*.zip*.tar.gz*.rarIDE环境忽略文件12345.idea/*.idea/compiler.xml.idea/encodings.xml.idea/modules.xml*.imlmaven忽略文件1234567891011target/pom.xml.tagpom.xml.releaseBackuppom.xml.versionsBackuppom.xml.nextrelease.propertiesdependency-reduced-pom.xmlbuildNumber.properties.mvn/timing.properties# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)!/.mvn/wrapper/maven-wrapper.jarother环境忽略文件123456789101112131415161718*.sw?.#**#*~.classpath.project.settings/binbuildtargetdependency-reduced-pom.xml*.sublime-*/scratch.gradleGuardfileREADME.html*.iml.idea.gitignore规则不生效.gitignore只能忽略那些原来没有被 track 的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。解决方法就是先把本地缓存删除**(改变成未track状态)**,然后再提交:123git rm -r  cached .git add .git commit -m 'update .gitignore' 
  • [课程学习] 使用GitLab构建持续集成案例
    1. 部署GitLab(1)基础准备[root@master ~]# curl -O http://mirrors.douxuedu.com/competition/Gitlab-CI.tar.gz[root@master ~]# tar -zxvf Gitlab-CI.tar.gz[root@master ~]# ctr -n k8s.io image import gitlab-ci/images/images.tar[root@master ~]# docker load < gitlab-ci/images/images.tar(2)部署GitLab服务新建命名空间gitlab-ci:[root@master ~]# kubectl create ns gitlab-cinamespace/gitlab-ci created在gitlab-ci命名空间下部署GitLab,将80端口以NodePort方式对外暴露为30880,YAML资源文件如下:[root@master ~]# cd gitlab-ci[root@master gitlab-ci]# vi gitlab-deploy.yamlapiVersion: apps/v1kind: Deploymentmetadata:  name: gitlab  namespace: gitlab-ci  labels:    name: gitlabspec:  selector:    matchLabels:      name: gitlab  template:    metadata:      name: gitlab      labels:        name: gitlab    spec:      containers:      - name: gitlab        image: gitlab/gitlab-ce:latest        imagePullPolicy: IfNotPresent        env:        - name: GITLAB_ROOT_PASSWORD          value: Abc@1234        - name: GITLAB_ROOT_EMAIL          value: 123456@qq.com        ports:        - name: http          containerPort: 80        volumeMounts:        - name: gitlab-config          mountPath: /etc/gitlab        - name: gitlab-logs          mountPath: /var/log/gitlab        - name: gitlab-data          mountPath: /var/opt/gitlab      volumes:      - name: gitlab-config        hostPath:          path: /home/gitlab/conf      - name: gitlab-logs        hostPath:          path: /home/gitlab/logs      - name: gitlab-data        hostPath:          path: /home/gitlab/data创建service服务释放端口[root@master gitlab-ci]# vi gitlab-svc.yamlapiVersion: v1kind: Servicemetadata:  name: gitlab  namespace: gitlab-ci  labels:    name: gitlabspec:  type: NodePort  ports:    - name: http      port: 80      targetPort: http      nodePort: 30880  selector:    name: gitlab创建资源:[root@master gitlab-ci]# kubectl apply -f gitlab-deploy.yamldeployment.apps/gitlab created[root@master gitlab-ci]# kubectl apply -f gitlab-svc.yamlservice/gitlab created查看Pod:[root@master gitlab-ci]# kubectl -n gitlab-ci get podsNAME                      READY   STATUS    RESTARTS   AGEgitlab-7b54df755-6ljtp    1/1     Running   0          45s(3)自定义hosts查看GitLab Pod的IP地址:[root@master gitlab-ci]# kubectl -n gitlab-ci get pods -owideNAME                      READY   STATUS    RESTARTS   AGE    IP            NODE               NOMINATED NODE   READINESS GATESgitlab-7b54df755-6ljtp    1/1     Running   0          50s   10.244.1.43   k8s-worker-node1   <none>           <none>在集群中自定义hosts添加gitlab Pod的解析:[root@master gitlab-ci]# kubectl edit configmap coredns -n kube-system........apiVersion: v1data:  Corefile: |    .:53 {        errors        health {           lameduck 5s        }        ready        kubernetes cluster.local in-addr.arpa ip6.arpa {           pods insecure           fallthrough in-addr.arpa ip6.arpa           ttl 30        }## 添加以下字段        hosts {            10.244.1.43 gitlab-7b54df755-6ljtp            fallthrough        }        prometheus :9153##删除以下三行字段        forward . /etc/resolv.conf {           max_concurrent 1000        }                cache 30        loop        reload        loadbalance    }........[root@master gitlab-ci]# kubectl -n kube-system rollout restart deploy corednsdeployment.apps/coredns restarted进入gitlab pod中[root@master gitlab-ci]#  kubectl exec -ti -n gitlab-ci      gitlab-7b54df755-6ljtp   bashroot@gitlab-7b54df755-6ljtp:/# vi /etc/gitlab/gitlab.rb 在首行添加(ip为pod IP地址)external_url 'http://10.244.1.43:80'root@gitlab-7b54df755-6ljtp:/# rebootroot@gitlab-7b54df755-6ljtp:/# exit(4)访问GitLab查看Service:[root@master gitlab-ci]# kubectl -n gitlab-ci get svcNAME     TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGEgitlab   NodePort   10.96.108.3   <none>        80:30880/TCP   3m33s通过http://10.24.2.14:30880访问GitLab,用户名123456@qq.com,密码Abc@1234,如图1所示:(5)上传项目包点击“Create a project”按钮点击“Create blank project”创建项目demo-2048,可见等级选择“Public”点击“Create project”,进入项目将源代码推送到项目中:[root@master gitlab-ci]# cd /root/gitlab-ci/demo-2048[root@master demo-2048]# git config --global user.name "administrator"[root@master demo-2048]# git config --global user.email "admin@example.com"[root@master demo-2048]# git remote remove origin[root@master demo-2048]# git remote add origin http://10.24.2.14:30880/root/demo-2048.git[root@master demo-2048]# git add .[root@master demo-2048]# git commit -m "initial commit"[master (root-commit) 105c032] initial commit[root@master demo-2048]# git push -u origin droneUsername for 'http://10.24.2.14:30880': rootPassword for 'http://root@10.24.2.14:30880':              #输入密码Abc@1234Counting objects: 189, done.Delta compression using up to 8 threads.Compressing objects: 100% (137/137), done.Writing objects: 100% (189/189), 43.35 KiB | 0 bytes/s, done.Total 189 (delta 40), reused 0 (delta 0)remote: Resolving deltas: 100% (40/40), done.To http://10.24.2.14:30880/root/cloud-manager.git * [new branch]      master -> masterBranch master set up to track remote branch master from origin.2. 部署GitLab CI Runner(1)获取 GitLab CI Register Token登录GitLab管理界面(http://10.24.2.14:30880/admin),然后点击左侧菜单栏中的CI/CD下的Runners点击右侧按钮记录下参数Registration token的值,后续注册Runners时会用到该参数。(2)修改GitLab Runner配置清单首先创建一个名为gitlab-ci的serviceAccount:[root@master ~]# cd /root/gitlab-ci/[root@master gitlab-ci]# cat runner-sa.yaml apiVersion: v1kind: ServiceAccountmetadata:  name: gitlab-ci  namespace: gitlab-ci[root@master gitlab-ci]# cat runner-role.yaml kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: gitlab-ci  namespace: gitlab-cirules:  - apiGroups: [""]    resources: ["*"]    verbs: ["*"][root@master gitlab-ci]# cat runner-rb.yaml kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  name: gitlab-ci  namespace: gitlab-cisubjects:  - kind: ServiceAccount    name: gitlab-ci    namespace: gitlab-ciroleRef:  kind: Role  name: gitlab-ci  apiGroup: rbac.authorization.k8s.io[root@master gitlab-ci]# kubectl apply -f runner-sa.yaml serviceaccount/gitlab-ci created[root@master gitlab-ci]# kubectl apply -f runner-role.yaml role.rbac.authorization.k8s.io/gitlab-ci created[root@master gitlab-ci]# kubectl apply -f runner-rb.yaml rolebinding.rbac.authorization.k8s.io/gitlab-ci created[root@master gitlab-ci]# kubectl -n gitlab-ci get saNAME        SECRETS   AGEdefault     1         10mgitlab-ci   1         21s给default用户赋权:[root@master gitlab-ci]# vi default.yaml apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  name: default  labels:    k8s-app: gitlab-defaultroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: cluster-adminsubjects:- kind: ServiceAccount  name: default  namespace: gitlab-ci[root@master gitlab-ci]# kubectl apply -f default.yaml clusterrolebinding.rbac.authorization.k8s.io/default created
  • git的拉取、提交、合并、解决冲突详细教程—转载
     我们在开发中使用git,经常会遇到拉代码,切换分支,提交代码,新建分支,合并代码,解决冲突这些操作,下面我跟大家分享一个好用的git工具来进行这些操作。  首先,我们下载一个git工具 点击下载GitHub Desktop   1.拉取git代码  复制git地址   打开工具,点击右上角的File,点击Clone a repository,再点击URL,把git地址复制到第一个输入框,然后选择本地保存代码的目录,再点击Clone就拉取git代码到本地了   2.切换分支  点击中间这栏,再点击想要切换的分支,就会把本地的代码切换到目标分支了,如果本地代码有修改,并且没有提交,为了避免本地切换到目标分支代码冲突,建议先提交再切换。   3.提交代码  修改或者新增,删除代码都能在工具的左侧看到。  可以选择只提交一部分的修改,或者提交全部的修改,右侧绿色是新增,红色是删除,所有的代码修改操作,在工具都能看到。历史的修改记录,在History里面找到提交的提交记录,也能看到。   下面我们来将修改提交到git,提交分为三步,写修改的注释,然后按图点击两个按钮就提交成功了。   4.新建分支  我们先从main主分支,新建一个分支名为2024/1/4,再点击创建    5.合并代码,解决冲突  合并代码时会有冲突,或者不冲突两种情况,首先我们要知道为什么会有这两种情况,有冲突是  比如现在有两个分支,分别是主分支 main,新分支 2024/1/4。当我们从主分支建立新分支以后,同时修改了主分支和新分支的代码,这个时候就会出现代码冲突,否则就不会冲突。  第一种,不冲突的情况,如下图,点击按钮就可以合并代码了   第二种,冲突的情况,提交的时候会有三角形感叹号提示代码中有一个文件冲突   此时我们再点击提交,工具就会显示冲突的文件,并且你本地冲突的代码也会显示,   看我的开发工具中的代码   <<<<<<< HEAD 到 ======= 之间的是当前分支的代码   ======= 到 >>>>>>> main 之间的是主分支的代码,这个时候我们把多余的符号删除,再根据需要保留正确的代码就可解决冲突了。  解决了之后就正常的执行提交代码,再合并分支就可以啦。  如果还有问题的话,欢迎评论留言,来个点赞收藏吧~~~ ———————————————— 版权声明:本文为CSDN博主「a_靖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_35713752/article/details/135382052 
  • [技术干货] 一篇文章学会Git—转载
     一篇文章学会Git 简介 什么是Git Git是一个免费的开源分布式版本控制系统,也是目前为止世界上最先进的分布式版本控制系统。Git官方有一个视频介绍,可以点此观看  什么是版本控制系统? 一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。简单点理解就是一个可以帮助我们记录文件修改的系统。Git官方有一个视频介绍,可以点此观看  什么是分布式版本控制系统? 分布式版本控制系统时相对于集中式版本控制系统的。  集中式版本控制系统将仓库存放在中央服务器中集中管理,当你需要时从中央服务器中拉取最新的版本,修改完后将修改提交给中央服务器。这就会带来例如当中央服务器宕机时整个版本控制系统就会崩溃;推送或者拉取一个较大的文件时就会消耗很多时间等弊端。  分布式版本控制系统中,每个人电脑都是一个仓库,自己的文件可以在本地管理,当需要多人协同时只需要管理好本地仓库与协同仓库的版本即可  Git有什么作用 进行源代码管理  为什么要进行源代码管理 方便多人协同开发  方便代码版本控制  Git的特点 分布式版本控制系统,服务器和客户端都有版本控制能力,都能进行代码的提交、合并等操作。 在使用Git的时候会自动创建一个.git的隐藏文件夹作为本地仓库 Git操作流程  clone:第一次从Git服务器获取项目 add:将修改添加到本地仓库 commit:将修改提交到本地仓库 push:将本地仓库的修改提交到Git服务器 pull:将Git服务器中的项目获取到本地仓库 Git仓库 什么是仓库 仓库的英文名是repository,又被称为版本库。它是一个被Git管理的文件目录。  工作区,暂存区和仓库 工作区:对代码的新增,修改,删除等操作的区域。 暂存区:存储工作区的操作的区域。 仓库区:即本地仓库区域,会记录完成的操作与历史版本。  Git操作 安装 Mac 在mac上有多种方法可以安装Git,最简单的事通过Xcode命令行工具安装。  通过Xcode安装  1.下载并安装Xcode  2.在终端中运行git即可,如果尚未安装,它将提示您安装。  通过homebrew安装  1.安装homebrew  $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 1 2.安装git  $ brew install git 1 通过安装包安装  1.点击此下载最新版本  2.下载完成后打开安装包,一直下一步安装即可  Linux 在Linux发行版上安装GIt,可以通过附带的软件包管理工具来安装  Debian/Ubuntu  $ apt-get install git 1 其他发行版本见这个链接  Windows 下载安装包  根据操作系统位数选择,链接  安装默认选项安装即可  安装完成后通过可以通过git --version查看安装版本  配置Git 配置文件介绍 Git有一个git config的工具,可以设置和获取配置,用来控制Git外观及操作。这些变量可以存放在三个不同的位置,根据存放位置不同作用的范围也不同。  1./etc/gitconfig:包含系统上每个用户及其存储库的配置。  2.~/.gitconfig或~/.config/git/config:每个用户专属的配置  3.config.git/config:当前使用存储库的git目录,用于该存储库的配置  如果有相同配置项时,每个级别都会覆盖上一个级别中的值,即:3>2>1  可以使用一下命令查看所有设置以及设置的所属:  $ git config --list --show-origin 1 常见配置 下面介绍一些常见配置  身份配置 安装完Git做的第一件事应该是设置用户名和电子邮箱。  $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com 1 2 使用global后,该信息将会始终作用域Git操作上  Git编辑器配置 Git默认的编辑器是系统默认编辑器  如果要是用其他文本编辑器(例如vim),则可以执行以下操作:  $ git config --global core.editor vim 1 查看配置 如果要查看配置,可以使用git config --list命令列出Git可以找到的所有设置  你可能会看到很多配置项,找不到你想要的,这时候你可以通过git config <key>来查看特定的配置,例:  $ git config user.name silencehuliang 1 2 帮助 获取方式 Git获取帮助的方式有三种git help <verb>、git <verb> --help、man git-<verb>  例如,可以通过git help config获取config的相关帮助  如果只需要快速了解Git命令的可用选项也可以用-h来查看相关帮助  例如git add -h  创建项目 1.将本地不收版本控制的目录转换为Git仓库  ①进入本地目录  $ cd ~/Desktop/project 1 ②输入转化命令  $ git init 1 此时会在当前目录下创建一个.git目录,里面存放着Git仓库中所有的必须文件  $ ls -a .    ..    .git 1 2 2.从其他地方克隆现有的Git仓库  ①进入我们需要存放仓库的路径  $ cd ~/Desktop/ 1 ②将现有的仓库克隆下来  $ git clone https://github.com/Silencehuliang/project 1 查看状态 可以通过git status 查看仓库中稳健的状态 绿色表示文件在暂存区 红色表示文件在工作区 可以通过git add 将工作区文件添加到暂存区  添加项目中所有文件:git add .  添加指定文件:git add xxx.py  可以通过git commit将暂存区文件提交到仓库区  git commit -m "修改描述",其中-m参数后面跟的是对本次修改的描述 git commit -am "修改描述",可以通过-am来实现添加和提交合并操作 查看历史版本 通过git log或者git relog可以查看历史版本  回退版本 通过版本号会退版本    git reset --hard 版本号 1 通过HEAD回退版本  当工作区文件发生了意外需要回退到上一个版本时可以通过  `git reset --hard HEAD` 1 HEAD表示当前最新版本 HEAD^表示当前最新版本的前一个版本 HEAD^^表示当前最新版本的前两个版本,以此类推… HEAD~1表示当前最新版本的前一个版本 HEAD~10表示当前最新版本的前10个版本,以此类推… 撤销修改 只能撤销工作区、暂存区的代码,不能撤销仓库区的代码  撤销仓库区的代码就相当于回退版本操作  撤销工作区代码  新加代码num3 = 30,不add到暂存区,保留在工作区  git checkout 文件名 1 撤销暂存区代码  新加代码num3 = 30,并add到暂存区  # 第一步:将暂存区代码撤销到工作区 git reset HEAD  文件名 # 第二步:撤销工作区代码 git checkout 文件名 对比版本 对比版本库与工作区  新加代码num3 = 30,不add到暂存区,保留在工作区 git diff HEAD -- xxx.py 对比版本库  新加代码num3 = 30,并add到暂存区 git diff HEAD HEAD^ -- xxx.py 删除文件 删除文件分为确定删除和误删  在项目中新建test.py文件,并添加和提交到仓库  确定删除处理:    # 删除文件   rm 文件名   # git确定删除文件,对比添加文件git add    git rm 文件名   # 删除后记录删除操作版本   git commit -m '删除描述' 误删处理:撤销修改即可    # 删除文件   rm 文件名   # git撤销修改   git checkout -- 文件名 1 2 3 4 代码冲突 提示:多人协同开发时,避免不了会出现代码冲突的情况  原因:多人同时修改了同一个文件  危害:会影响正常的开发进度  注意:一旦出现代码冲突,必须先解决再做后续开发  解决冲突 原则:谁冲突谁解决,并且一定要协商解决 方案:保留所有代码 或者 保留某一人代码 解决完冲突代码后,依然需要add、commit、push,如果执行pull没有影响,就算真正解决了冲突代码 补充: 容易冲突的操作方式 多个人同时操作了同一个文件 一个人一直写不提交 修改之前不更新最新代码 提交之前不更新最新代码 擅自修改同事代码 减少冲突的操作方式 养成良好的操作习惯,先pull在修改,修改完立即commit和push 一定要确保自己正在修改的文件是最新版本的 各自开发各自的模块 如果要修改公共文件,一定要先确认有没有人正在修改 下班前一定要提交代码,上班第一件事拉取最新代码 一定不要擅自修改同事的代码 标签 当某一个大版本完成之后,需要打一个标签 作用: 记录大版本 备份大版本代码 在本地打标签 git tag -a 标签名 -m '标签描述' 1 推送标签到远程仓库 git push origin 标签名 1 删除本地和远程标签    # 删除本地标签   git tag -d 标签名   # 删除远程仓库标签   git push origin --delete tag 标签名 1 2 3 4 分支 作用: 区分生产环境代码以及开发环境代码 研究新的功能或者攻关难题 解决线上bug 特点: 项目开发中公用分支包括master、dev 分支master是默认分支,用于发布,当需要发布时将dev分支合并到master分支 分支dev是用于开发的分支,开发完阶段性的代码后,需要合并到master分支 查看当前分支   git branch 1 创建并切换到dev分支  git checkout -b dev 1 设置本地分支跟踪远程指定分支(将分支推送到远程)   git push -u origin dev 1 分支合并到master分支  先切换到master分支    git checkout master 1 分支合并到master分支    git merge dev ———————————————— 版权声明:本文为CSDN博主「geobuins」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/geobuins/article/details/135368731 
  • [其他] git分支使用规范
     Git分支使用规范主要包括以下几点:  1. 主分支(master):主分支用于存放稳定的、可发布的代码。在开发过程中,尽量避免直接修改主分支的代码,而是通过创建新的分支进行开发。  2. 开发分支(develop):开发分支用于存放正在开发的新功能或修复的bug。开发人员可以在开发分支上进行日常的开发工作。  3. 功能分支(feature):功能分支用于实现某个具体的功能或修复某个bug。当开始一个新的功能开发或bug修复时,从开发分支上创建一个新的特性分支,完成后将特性分支合并回开发分支。  4. 预发布分支(release):预发布分支用于准备发布新版本。当开发分支上的功能开发和bug修复都完成后,从开发分支上创建一个预发布分支,进行版本测试和发布准备工作。测试通过后,将预发布分支合并回主分支和开发分支。  5. 提交信息规范:在提交代码时,应遵循一定的提交信息规范,以便于其他开发人员理解每次提交的目的。常见的提交信息格式为:`<类型>: <简短描述>`,例如:`fix: 修复用户登录问题`。  6. 及时合并:在完成一个功能开发或bug修复后,应及时将特性分支合并回开发分支,避免长时间存在未合并的分支。同时,在发布新版本前,也应及时将预发布分支合并回主分支和开发分支。  7. 避免冲突:在合并分支时,可能会出现代码冲突的情况。这时需要开发者仔细检查冲突的代码,解决冲突后再提交合并请求。  8. 使用合适的合并工具:在合并分支时,可以使用合适的合并工具(如GitHub的合并按钮、GitLab的合并请求等)来简化合并过程,提高合并效率。 
总条数:361 到第 页
上滑加载中