Git Note
Git Note
git init: 将当前文件夹作为git根目录
git add:将某个文件加入到staged中
git restore --staged:将某个文件从staged中去除,但是不改变原有文件
git reset:上一条指令可能因为没有还任何commit而失败,这时用git reset.但是git reset有很多种语义,务必当心使用
git rm:从磁盘删除文件,并将删除动作放入staging area。若你后悔了,不想在下次操作中包含删除,执行restore --staged <file>(但是并不能恢复磁盘文件)
git restore:从staged area中删除,并且恢复文件
git commit -m ""
git仓库下的文件夹里如果还有git仓库,它不会递归进入,两者隔离
git log:查看commit history
setup github repo
git remote add <any_name_you_want> (preferred origin) <repo>`
git remote -v: 查看是否添加成功
git remote rename origin upstream:把origin仓库重命名为upstream
git push -u origin main:将当前的main commit push到origin的main(默认两个一样) -u表示当前的main对应到origin/main(一劳永逸,不用以后继续写了)
git push origin local_branch:remote_branch:从local_
branch推送到origin/remote_branch
how to write gitignore
log.txt: ignore a file
*: matches any number of characters excluding /
abc/*.tmp # ignore all .tmp files in abc directory
**: matches any number of characters including /
**/foo.tmp # ignore all foo.tmp files in any directory
?: matches a single character
config?.yml # ignores config1.yml, configA.yml,...
[abc] matches a single character
file[123].txt # ignores file1.txt, file2.txt, file3.txt
directories:
logs/ # ignores the logs directory
*.bak # ignores all .bak files anywhere
/secret.txt # only ignores secret.txt in the root directory
negate use:
*.log # ignores all .log files
!important.log # except important.log
what should be omitted from version control
binary files: *.class, *.jar, *.exe
temporary files
local files: .idea/
sensitive content
fork
从别人仓库fork到自己仓库,然后clone(自己仓库或别人仓库)
一般会remote关联自己仓库为origin,关联别人仓库为upstream
从仓库同步到自己的git:fetch + merge = pull
详情参考gitmastery
examining commit
查看一个commit修改了什么
一个commit可以用任何指向它的ref称呼,<ref>~<n>指的是ref指向的commit前第n个commit
git log --oneline --decorate显示commit message
git show 显示最后一条commit更改的东西
git show HEAD~1
git show main~1
git show e60deae # first few characters of the SHA
git show e60deae..... # run git log to find the full SHA and specify the full SHA
tag commits
Git lets you tag commits with names, making them easy to reference later.
A tag stays fixed to the commit.
- A lightweight tag is just a ref that points directly to a commit, like a branch that doesn’t move.
- An annotated tag is a full Git object that stores a reference to a commit along with metadata such as the tagger’s name, date, and a message.
Annotated tags are generally preferred for versioning and public releases, while lightweight tags are often used for less formal purposes, such as marking a commit for your own reference.
如果想更换tag,必须delete再重新创建
git tag v1.0 # lightweight tag
git tag -a v0.9 HEAD~2 -m "First beta release" # annotated tag
git tag -d v1.0
git tag v1.0 HEAD~1 #delete and recreate tag
将tag上传到github
git push origin v1.0
# 不需要登陆github直接可以看到是否push成功
git ls-remote --tags origin
# 从remote删除tag
git push --delete origin v1.0
# 把所有tag全部push到remote
git push origin --tags
比对两个历史版本
git diff v0.9 HEAD # 从v0.9到HEAD经历了什么修改
git diff HEAD v0.9 # 从HEAD到v0.9经历了什么修改
git diff # 打印当前的unstaged changes
git diff --staged # 打印当前的staged changes
git diff -a # 有时候因为编码问题,有些文件会被识别为二进制,于是diff不会打印出来细节,用-a强制打印细节
git diff -- list.txt # --将比较缩小为一个文件
git diff --staged -- list.txt
git diff HEAD~2 HEAD -- list.txt
去到一个指定commit
checkout指令
git checkout v1.0: checks out the commit taggedv1.0git checkout 0023cdd: checks out the commit with the hash0023cddgit checkout HEAD~2: checks out the commit 2 commits behind the most recent commit.
git log --oneline --decorate # 只输出到HEAD
git log --oneline --decorate --all # 输出所有
每一次commit的track包含上一次commit未修改的文件和新修改并且加入track的文件。
如果我目前有unstaged或者新add的文件但是要跳转到其他commit,如果untrack文件在另一个commit中不存在,会将这个文件原封不动带过去;如果存在,那么会阻止你跳转
这时候我们可以进行git stash来暂存staged 和unstaged
git stash
git stash pop:把stash changes给apply回来,同时从stash中删除
To proceed with a checkout when there are conflicting uncommitted changes in the working directory, there are several options:
- Commit the changes.
- Stash the changes.
- Discard the changes.
覆盖历史commit
如果有一个commit是由很大问题的,我们可以用git reset删除掉这个commit
三种reset:soft, mix, hard
-
模式 Staged (暂存区) 里的文件 Unstaged (工作区) 里的文件 适用场景 --soft保留(原封不动) 保留(原封不动) 只是想撤销上一次 commit,重新修改 commit message。 --mixed(默认模式)清空(退回到工作区) 保留(原封不动) 撤销了 git add和git commit。文件还在,但需要重新 add。--hard全部删掉 全部删掉 最危险! 彻底放弃所有还没提交的修改,回到某个干净的状态。
reset可能会导致和remote的diverge,我们通过force push可以强制remote也删掉历史commit,或者我们先pull别人的,merge好再push上去
git reset默认回到HEAD,因此git reset --hard相当于扔掉所有staged
和unstaged changes
如果我reset扔掉的commit有tag,那么在打印log的时候,那几个commit仍然存在,只有所有ref都没了这几个commit才真正被删除掉。这时是还可以checkout回去的
revert一个commit
通过revert指令,git会计算那个指令修改了什么,然后将把那些修改reverse,加入到新的commit中。
revert可能会引入conflict,这时候要手动resolve
如何维护一个好的commit history
每一个commit应该代表一个self-contained修改,不要将很多修改堆在一起commit
Git 支持对文件内容的“精细化管理”,允许你只提交文件中的部分修改(Hunks/Lines),而非整个文件。
这种操作被称为交互式暂存(Interactive Staging),其主要价值和应用场景如下:
核心价值
- 逻辑分离:即使在同一个文件里做了多次修改,也可以将它们拆分成多个逻辑独立的提交(Commits)。
- 保持历史整洁:避免将无关的实验性代码或临时调试代码混入正式提交中。
典型应用场景
- 顺手修复:在开发 A 功能时,顺便修复了一个无关的 B 错误。
- 实验清理:在同一个文件中尝试了多种方案,最终只想提交确定的那部分代码。
- 关注点分离:确保每一个 Commit 只负责一件事情,使版本历史更易于审查和回溯。
使用git add -p实现操作
Git 提交信息(Commit Message)不仅仅是记录“改了什么”,更重要的是解释“为什么改”。
优秀的提交信息是项目历史的重要文档,能显著提升团队协作、代码审查和后期维护的效率。以下是其要点概述:
核心结构
一个完整的提交信息通常由两部分组成(中间用空行隔开):
- 标题(Subject Line):简洁明了地概括本次更改。
- 正文(Body):详细解释背景、意图、技术决策、影响范围或副作用。
关键原则
- 解释意图而非代码:代码差异(Diff)已经展示了代码层面的变化,提交信息应侧重于说明背后的动机和逻辑。
- 易于回溯:当项目历史变长时,清晰的信息能让开发者无需深入阅读每一行代码,就能快速理解历史决策。
- 专业格式:使用短标题 + 详细正文的结构,并利用空行分隔,这符合 Git 的标准规范和阅读习惯。
some conventions:
- Keep the subject line (the first line) under 50–72 characters.
- Write the subject in the imperative mood (e.g., Fix typo in README rather than Fixed typo or Fixes typo).
- Leave a blank line between the subject and the body, if you include a body.
- Wrap the body at around 72 characters per line for readability.
当commit太杂乱的时候,可以通过rebase一次性整理
git rebase -i HEAD~4
第一次提示是指定每个commit应该pick/reword/edit(拆分)/squash(合并)/.../drop
第二次提示是确认修改后的commit message
更新上一次的message:
To change the commit message subject only, use the git commit --amend -m "<new commit message>" command.
To change the entire commit message (not just the subject), run the git commit --amend command, which will open the text editor for you to edit the commit message. The commit will be updated when you close the text editor.
branch
新建branch
git checkout -b <new_branch_name> # 新建branch并将HEAD指向它
branch <branch_name> # 进入这个branch
=
switch -c <new_branch_name>
switch <branch_name>
merge branch
将两个branch的change combine到一起,会出现一个merge commit
如果一个一个分叉其实没有diverge,fast-forward会直接把这个分叉移到另一个分叉顶点。
但是我们也可以让它不fast-forward,这时候就会出现一个merge commit
git merge -no-ff <branch_name>
可以用reset在交汇点undo一个merge commit
branch diff
1->2(Branch A) 1->3(Branch B)
git diff A..B # 节点2 vs 节点3
git diff A...B # 节点2 vs 节点1
squash merge
普通merge:
Main : A --- B ------------------- G (Merge Commit)
\ /
Feature : C --- D --- E --- F
squash merge:
Main : A --- B --- S (Squash Commit)
\
Feature : C --- D --- E --- F (这些依然存在于 feature 分支,但不会进入 main)
merge conflict
如果两个commit修改了同一处地方无法automatically merge,会提示merge conflict需要人工merge
rename branch
git branch -m <orginal_name> <new_name>
common branch naming convention:
<category>/<name>
delete branch
git branch -d <branch> # 将已经merge的branch删除,但是不一定删除节点,因为之后的commit需要这一个commit,只有完全没有任何东西指向它才会删除(git会保留30天,可以用sha恢复)
git branch -D <branch> # 没有merge的branch删除
sync branches
-
merge
-
rebase,也会出现conflict,而且大多更加麻烦,可能会发生多次conflict
-
cherry-pick移动一段连续的commit到某个点,可能会有一次conflict
git cherry-pick A..B # 包含A不包含B git cherry-pick A^..B # 包含A和B
pulling branches from a remote
从remote clone一个repo之后,只有main的数据被复制到本地,其他的branch都是references to remote repo,如果要修改它们需要先把它们fetch到本地。
git branch -a # 查看当前有哪些branch
git switch -c <new_local_branch_name> <remote_branch_name> # 将remote拉取到本地建立新branch
如果clone之后remote branch变了
如果local和remote都有互相没有的变动,那么在从remote pull之后会将local的新commitdiverge出去,然后自动进行merge(可能出现merge conflict)
delete branches from a remote
git push origin --delete fantasy # 可以先新建一个本地branch也可以不建
renaming branches in a remote
git switch -c fantasy origin/fantasy # create a local copy, tracking the remote branch
git branch -m fantasy fantasy-books # rename local branch
git push -u origin fantasy-books # push the new branch to remote, and set it to track
git push origin --delete fantasy # delete the old branch
新建一个内容相同名字不同的branch,然后把当前branch删掉。
pull request
自己的remote更新后,可以request别人把自己的code pull到upstream。upstream repo的人会review你的代码,可能会把你的代码merge进去,也有可能close PR(reject)。他们也可能会在你的PR上加入comment让你继续修改。
pull request不是一个快照,而是分支的连接,所以如果你pull request过一个branch,下一次更新这个branch会自动被推送到upstream。
一般我们会开一个不同的branch然后进行推送。
同一个repo也是可以进行pull request的,这个是为了让其他developer审核你的代码。
如果你的PR提交后,main分支有了新的提交并且存在冲突,那么PR就会显示“存在冲突”。这时我们要手动对PR进行修改来使当前PR适应新的main。
git checkout main
git pull upstream main # 先把新的main pull下来
git checkout pr-branch # assuming pr-branch is the name of branch in the PR
git merge main # 在本地更新main
最后push上去会在upstream自动更新PR,让其他开发者更好merge
创建 Pull Request
定义:PR 是一种请求远程仓库“拉取”你所提议的代码更改的机制。
- 基本流程:
- Fork 目标仓库到个人账户。
- Clone 到本地并进行修改、提交(Commit)。
- Push 到自己的 Fork 仓库。
- 在 GitHub 上发起 New pull request,指定目标分支(base)和对比分支(head)。
- 确认差异(Diff)后提交。
- 关键点:
- Draft PR:允许创建“草稿”状态的 PR,表示工作尚在进行中,暂不接受合并。
- 自动更新:只要向发起 PR 的同名分支推送新代码,PR 会自动同步更新。
- 冲突解决:如果 PR 出现冲突,标准做法是在本地拉取 upstream 的最新代码,合并到 PR 分支解决冲突后再重新推送。
评审 Pull Request
- 定义:这是一个协作过程,项目成员对提交的代码进行检查并提供反馈。
- 操作步骤:
- 查看 Files changed 选项卡中的代码差异。
- 添加行内注释(针对单行或多行代码提供意见)。
- 使用 "Start a review" 功能将多条评论暂存,最后统一提交。
- 常用术语:
- LGTM (Looks Good To Me):表示代码没问题,可以合并。
- nit (nit-picking):指微小的改进建议(如拼写、格式),不影响代码功能。
合并 Pull Request
- 权限:只有拥有仓库写权限的成员才能执行合并。
- 合并状态:
- 如果分支落后(Out-of-date),可以点击 GitHub 提供的 "Update branch" 按钮。
- 如果有冲突(Merge conflicts),通常需要 PR 作者在本地解决后才能合并。
- 合并选项:支持多种模式,初学者推荐使用 Create merge commit。
- 后续同步:合并后,作者及其他成员需从上游仓库拉取最新代码,以同步本地和自己的 Fork 仓库。
其他项目管理功能
- Issue Tracker (议题追踪): 用于任务跟踪和 Bug 报告。
- Projects (项目看板): 类似 Kanban 的任务管理板。
- Milestones (里程碑): 归纳相关 Issue 和 PR,追踪发布进度。
- Releases (发布): 管理软件版本、版本说明及二进制附件。
- GitHub Actions (自动化工作流): 实现持续集成(CI),如自动测试、构建和部署。
- GitHub Pages (项目网站): 直接从仓库托管静态网站。
Comments
No comments yet.