KaiSpace
tech

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.

  1. A lightweight tag is just a ref that points directly to a commit, like a branch that doesn’t move.
  2. 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 tagged v1.0
  • git checkout 0023cdd: checks out the commit with the hash 0023cdd
  • git 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 addgit 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)。
  • 保持历史整洁:避免将无关的实验性代码或临时调试代码混入正式提交中。

典型应用场景

  1. 顺手修复:在开发 A 功能时,顺便修复了一个无关的 B 错误。
  2. 实验清理:在同一个文件中尝试了多种方案,最终只想提交确定的那部分代码。
  3. 关注点分离:确保每一个 Commit 只负责一件事情,使版本历史更易于审查和回溯。

使用git add -p实现操作

Git 提交信息(Commit Message)不仅仅是记录“改了什么”,更重要的是解释“为什么改”。

优秀的提交信息是项目历史的重要文档,能显著提升团队协作、代码审查和后期维护的效率。以下是其要点概述:

核心结构

一个完整的提交信息通常由两部分组成(中间用空行隔开):

  • 标题(Subject Line):简洁明了地概括本次更改。
  • 正文(Body):详细解释背景、意图、技术决策、影响范围或副作用。

关键原则

  • 解释意图而非代码:代码差异(Diff)已经展示了代码层面的变化,提交信息应侧重于说明背后的动机和逻辑
  • 易于回溯:当项目历史变长时,清晰的信息能让开发者无需深入阅读每一行代码,就能快速理解历史决策。
  • 专业格式:使用短标题 + 详细正文的结构,并利用空行分隔,这符合 Git 的标准规范和阅读习惯。

some conventions:

  1. Keep the subject line (the first line) under 50–72 characters.
  2. Write the subject in the imperative mood (e.g., Fix typo in README rather than Fixed typo or Fixes typo).
  3. Leave a blank line between the subject and the body, if you include a body.
  4. 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

  1. merge

  2. rebase,也会出现conflict,而且大多更加麻烦,可能会发生多次conflict

  3. 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 是一种请求远程仓库“拉取”你所提议的代码更改的机制。

  • 基本流程
    1. Fork 目标仓库到个人账户。
    2. Clone 到本地并进行修改、提交(Commit)。
    3. Push 到自己的 Fork 仓库。
    4. 在 GitHub 上发起 New pull request,指定目标分支(base)和对比分支(head)。
    5. 确认差异(Diff)后提交。
  • 关键点
    • Draft PR:允许创建“草稿”状态的 PR,表示工作尚在进行中,暂不接受合并。
    • 自动更新:只要向发起 PR 的同名分支推送新代码,PR 会自动同步更新。
    • 冲突解决:如果 PR 出现冲突,标准做法是在本地拉取 upstream 的最新代码,合并到 PR 分支解决冲突后再重新推送。

评审 Pull Request

  • 定义:这是一个协作过程,项目成员对提交的代码进行检查并提供反馈。
  • 操作步骤
    1. 查看 Files changed 选项卡中的代码差异。
    2. 添加行内注释(针对单行或多行代码提供意见)。
    3. 使用 "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.