序
记录一些开发中常用的git命令操作,持续更新。。
Tips:开发中可能存在误操作,因此需要了解一些撤销等命令的用法,但还是推荐按标准的PR流程更新代码:
- 开发分支迭代时间过长、修改内容过多可能会导致大面积冲突,解冲突会比较耗时间;
- 团队合作时,按统一格式命名分支、标记版本号(tag),按标准流程提交、评审、PR、Merge,可以很大程度上避免代码冲突和误操作。
文章目录
一、代码推送/获取
1.拉取项目
进入存放代码的目录,打开终端执行下列命令,下载完成后可以在当前目标的项目同名文件下看见代码:
git clone -b ssh://https://github.com/alibaba/canal.git(git项目地址)
GitHub查看项目地址示例:
2.代码管理
提交代码
# 查看本次修改文件
git status
# 查看本次修改内容
git diff
# 按文件名添加代码
git add ./User/failPath
# 添加本次变更的所有代码
git add .
# 添加了多余的文件,执行命令
git rm -r --cached src/test/file_name
# 提交修改到开发分支中
# 注意:必须要有注释,注释要有意义;
git commit -m "Fix sst dirorder bug caused by manual compaction"
# 添加所有变更代码并提交修改
git commit -a -m "Fix sst dirorder bug caused by manual compaction"
# 推送到远端当前开发分支
# 如果push失败,需要加-f参数强制提交
git push -u origin feature/a_new_funcation
撤销修改
- 撤销上次修改的某个文件
这些命令只能找回最近一次提交中的文件版本,如果修改文件后有过新的提交,那么这些命令无法找回预期的文件版本。
# 已经使用git rm命令将其从Git跟踪中移除,使用以下命令来撤销删除
git checkout HEAD -- 文件名
# 只是在工作区删除了文件,还没有使用git rm命令将其从Git跟踪中移除,使用以下命令来撤销删除
git checkout -- 文件名
- 撤销上次commit
# 撤销最近一次提交,撤销git add,下面两个命令效果相同
git reset --mixed HEAD~1
git reset --mixed HEAD^
# 撤销最近一次提交,但不撤销git add
git reset --soft HEAD^
# 如果是已经push到远程,上述操作后,push到远程要加-f参数
git push -u -f origin 分支名
参考文档:如何撤销上次commit
拉取代码
# 拉取远端代码到本地
# 也会将最新的tag拉取到本地
git pull
撤销拉取
# 查看master分支的历史变动记录,找到需要恢复的上一个commit(pull之前的commit)
git reflog master
# 指定commit哈希值恢复
git reset --hard <COMMIT_ID>
# 也可以通过参考以下命令
git reset --hard master@{1}
2.提交管理
合并提交
为使提交记录更加简洁,去除多余的提交记录,可以参考以下步骤合并commit:
step1.查看当前提交日志,选择需要合并的commit
# 查看提交日志,执行q推出日志查看
git log
# 合并最新的num个commit
git rebase -i HEAD~num
以下示例查看了feature分支对应的commit记录:
可以观察到,注释为“add2”的两个提交没有意义,假如需要把这两次提交合并到第一次提交,则执行git rebase -i HEAD~3。
step2.修改commit设置
执行命令后,会看到下图所示内容,使用vim编辑器打开的文档。
页面输入i进入编辑,ESC退出编辑,:q退出,:q!强制退出,:wq!强制退出并保存修改。
pick对应当前分支设置,59c7a1486对应分支的序号,后面则是commit的注释(comment)。
修改pick部分的设置,即修改了当前提交的留存状态,Commands部分解释了每种设置对commit进行的操作,可以根据需要修改。
pick 59c7a1486 support openApi
pick f03b7a2b6 add2
pick eb085e378 add2
# Rebase 39f673fdc..eb085e378 onto 39f673fdc (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
例如,合并两个注释为add2的提交到59c7a1486,执行rebase命令之后,按如下操作:
- 按键【i】,进入编辑;
- 修改pick为s(s:squash,使用,但合并到之前的提交);
pick 59c7a1486 support rds query openApi
s f03b7a2b6 add2
s eb085e378 add2
- 按键【ESC】,退出编辑;
- 按键【wq!】,保存修改并退出
- 修改合并后commit的注释(comment):
按键【i】,进入编辑,不需要的注释行在行首添加#;按键【ESC】、【wq!】保存修改并退出:
# This is a combination of 3 commits.
# This is the 1st commit message:
# 已有注释也可以修改
support openApi
# This is the commit message #2:
# add2(例如这里不需要,可以注释掉,如果需要可以保留)
# This is the commit message #3:
add2
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Wed Jan 10 18:13:35 2024 +0800
#
# interactive rebase in progress; onto 39f673fdc
# Last commands done (3 commands done):
# squash f03b7a2b6 add2
# squash eb085e378 add2
# No commands remaining.
- git log 查看提交记录,观察rebase结果是否符合预期;
查看命令
# 以下为查看命令
#查看对某个功能、bugfix所做的修改
git show som_commit
#查看某个文件或者目录被哪些commit、功能改动过
git log some_file_or_some_dir
# 找到包含本地最近提交(commit)的分支(列出所有包含当前HEAD指向的提交的本地分支)
git branch --contains HEAD
# 找到所有分支(包括远程分支)中包含最近一次提交的分支,-a选项会让Git显示所有分支
git branch -a --contains HEAD
# 找到包含特定提交(需要已知该提交的哈希值)的分支
git branch --contains <commit-hash>
# 找到包含最近几个提交的本地分支
git log -4 --format="%H"
# 找到最近一次提交在哪个分支上(通过查看引用日志(reflog)来确定HEAD最近是从哪个分支移动的)
git reflog | grep -o 'moving from [^ ]\+' | grep -o '[^ ]*$' | head -n 1
二、分支管理
1.分支切换
# 切换开发分支
git checkout feature/your_dev_branch
# 新建分支
git checkout -b feature/your_dev_branch
# 删除分支
git checkout -D feature/your_dev_branch
2.跟踪远程分支
# 获取远程仓库的最新信息
git fetch origin
# 创建一个新的本地分支,来跟踪远程分支,执行命令之后会切换到本地新建分支
git checkout -b local_branch origin/remote_branch
# 创建一个新的本地分支,来跟踪远程分支,仅新建分支,不切换
git branch local_branch origin/remote_branch
# 拉取代码
git pull
2.合并远端代码
# 合并远端提交到本地分支
git rebase origin/remote_branch
3.解决分支冲突
执行rebase命令时可能会遇到分支冲突,此时可以按如下步骤解决:
- 执行git rebase如果遇到冲突,git会停止rebase并提示需解决冲突。此时,首先使用git status来查看哪些文件存在冲突;
- 出现冲突的文件会在冲突部分提示不同分支的内容(一般以======隔开),可以根据需要保留需要的代码行;此外,也可以使用如下命令选择保留本地或者远程修改:
# 保留本地的修改,--ours选项告诉git在冲突解决时选择本地版本
git checkout --ours 文件名
# 保留远程的修改,--theirs选项告诉git在冲突解决时选择远程版本
git checkout --theirs 文件名
- 解决完所有冲突后,使用git add .命令来把解决冲突后的所有文件标记为已解决状态;
- 使用git rebase --continue命令来继续rebase操作。
三、版本/tag管理
说明:tag号可以用于标记分支的版本
1.切换tag
# 拉取远程仓库的标签(tag)到本地,远程仓库(默认名为origin),如果远程仓库的名字不是origin,需修改为仓库名
git fetch origin --tags
# 拉取远程仓库某一个特定的标签到本地
git fetch origin tag <tagname>
# 查看当前所有tag号
git tag
# 匹配通配符查看当前tag
git tag -l 2.3.*
# 切换到指定tag
git checkout <tag_name>
# 当切换到一个标签时,处于一个"detached HEAD"状态,表示开发者不在任何分支上,此状态下任何提交都不会被记录到任何分支
# 这个标签的基础上创建一个新的分支可以使用以下命令:
git checkout -b <branch_name> <tag_name>
2.创建/删除tag
# 创建tag
git tag tag_name
# 另一种创建tag的方式
git tag -a tag_name -m "description info"
#将tag推送到stash远程
git push origin tag_name
# 删除一个tag
git tag -d tag_name
# 删除远程(stash)上的tag
git push origin :tag_name
四、其他命令
1.缓存修改
说明:开发错了分支,但修改还没跟踪:
# 存到暂存区
git stash
# 切换到正确的开发分支
git checkout local_branch
# 取出
git pop
2.统计代码行
git 统计代码行数包括了空行和注释,相关命令:
- git ls-files:列出Git仓库中当前分支跟踪的所有文件;
- xargs:将git ls-files的输出作为wc -l命令的参数;
- wc -l:计算文件的行数。
# 统计当前分支所有代码行数,输出每个文件的行数以及最后的总行数
git ls-files | xargs wc -l
# 统计特定类型的文件(例如,只统计.java文件)
git ls-files '*.java' | xargs wc -l
# 统计某个特定目录下的文件行数
git ls-files 'some/directory/*' | xargs wc -l
# 统计某个特定作者的代码行数
git ls-files | xargs -I{} git blame --line-porcelain {} | grep "^author " | sort | uniq -c
备注:想要更精确的统计(例如,只统计实际的代码行数),需要使用更复杂的脚本或专门的代码统计工具,如cloc(Count Lines of Code)。cloc是一个开源的统计代码行数的工具,它可以更准确地区分代码、注释和空行,安装使用命令如下:
# For Ubuntu/Debian systems
sudo apt-get install cloc
# For Red Hat/CentOS systems
sudo yum install cloc
# For macOS with Homebrew
brew install cloc
# 输出各种类型文件的详细统计信息,包括文件数、空行数、注释行数和代码行数
cloc $(git ls-files)
五、标准PR流程
- 执行命令git branch显示所有分支,确定当前在test分支中(test是长期的基础分支,用于往master分支合并,其他开发分支往test分支合并);
- 根据本次开发的功能取一个feature/xxx的分支名,并切换到新分支:git checkout -b feature/a_new_funcation
- 在编译器中进行开发、测试;
- 查看当前修改状态,确定修改的文件:git status;
- [可选]确认修改内容:git diff;
- 提交代码到本地分支,执行命令:git add、git commit;
- [可选]修改开发分支的commit,将所有commit合并为一个:git rebase -i HEAD~n(按需去掉一些无效的commit);
- rebas远程分支,将自己的开发分支rebase到test分支上(rebase后开发分支会包含test的最新代码),保证提交历史是一条线:git rebase origin/test;
- 创建一个PR,源分支填写自己的开发分支,目标分支填写test分支,等待审核;
- 合并PR。