- distributed version control system (DVCS)
- VCS: 简单的管理,例如RCS
- CVCS(centralized VCS)
- DVCS: 每个clone都是一个backup,能处理不同的remote
- 操作模式
- 本地操作、集中下载和上传
- The Platonic ideal is that each commit should compile and should move steadily towards more and more tests passing.
- ugly interface and beautiful design -> bottom-up地理解git
This XKCD comic captures Git’s reputation:
- Snapshots: 文件是blob,文件夹是tree,snapshot是top-level tree
- Modeling history: relating snapshots
- a history is a directed acyclic graph (DAG) of snapshots
- 一个snapshot可能有多个parent,比如merge
- snapshot被称作commit,是Git的精髓所在,不像其它VCS针对每个文件储存changes
- Data model, as pseudocode
- Objects and content-addressing
- id由SHA-1 hash生成,40个十六进制字符
git cat-file -p显示对象信息
- References:照顾可读性,和对象不同,它是mutable的
master表示主分支的最近commitHEAD表示“where we currently are”
- repositories = objects + references
// a file is a bunch of bytes
type blob = array<byte>
// a directory contains named files and directories
type tree = map<string, tree | blob>
// a commit has parents, metadata, and the top-level tree
type commit = struct {
parent: array<commit>
author: string
message: string
snapshot: tree
}
type object = blob | tree | commit
objects = map<string, object>
def store(object):
id = sha1(object)
objects[id] = object
def load(id):
return objects[id]
references = map<string, string>
def update_reference(name, id):
references[name] = id
def read_reference(name):
return references[name]
def load_reference(name_or_id):
if name_or_id in references:
return load(references[name_or_id])
else:
return load(name_or_id)- staging area也称作index
- 和
git add相关- Git tracks changes to a developer’s codebase, but it’s necessary to stage and take a snapshot of the changes to include them in the project’s history.
git addperforms staging, the first part of that two-step process. Any changes that are staged will become a part of the next snapshot and a part of the project’s history. Staging and committing separately gives developers complete control over the history of their project without changing how they code and work.
- Git tracks changes to a developer’s codebase, but it’s necessary to stage and take a snapshot of the changes to include them in the project’s history.
- 意义在于摆脱snapshot和当前状态的绝对联系,使commit操作更灵活
- checkout在working directory之间切换;reset从staging area回复到working directory
git help <command>: get help for a git commandgit command -h: concise help- freenode 的
#git和#github频道寻求帮助
git init: creates a new git repo, with data stored in the.gitdirectorygit status: tells you what's going ongit status -s: 左列是staging area,右列是working tree
git add <filename>: adds files to staging area- 对于同一文件,add之后有新改动要重新add
-p添加每个变化前,都会要求确认。对于同一个文件的多处变化,可以实现分次提交
git commit: creates a new commit- Write good commit messages and Even more reasons to write good commit messages: 大写开头,祈使句,简短
- saves the snapshot to the project history and completes the change-tracking process. In short, a commit functions like taking a photo. Anything that’s been staged with
git addwill become a part of the snapshot withgit commit. git commit -am "m"可以先add再commit,commit的文件都是tracked状态-v显示diff信息
git log: shows a flattened log of historygit log --pretty=format:"%h %s" --all --graph --decorate --no-merges: visualizes history as a DAGgit shortlog/ git log --oneline: 只显示标题-p显示全部信息,-3显示三条--stat显示统计信息-S function_name --since=2.weeks --before="2008-11-01" --grep --author --committer --no-merges--all显示所有branch信息, 或者指定特定的branch
git diff: 比较working directory和staging area- `git diff --staged':比较staging area和last commit
git diff <filename>: show differences since the last commitgit diff <revision> <filename>: shows differences in a file between snapshotsgit difftool,图形界面- Diff without dots / with two dots / with three dots: https://matthew-brett.github.io/pydagogue/git_diff_dots.html
git diff > tmp.diff编辑器可显示syntax
git checkout <revision>: updates HEAD and current branchgco <revision> $file_path用于回退特定文件
git revert-m 1或2对于有多个上游commit的commit,用来指定具体revert到哪一个commit
git rm filegit rm --cached,只删除index,不删除working treegit rm log/\*.log,通配符,注意要加\,Git有自己的文件名拓展
git tag -l "v1.8.5*":-l是为了通配符匹配- Annotated Tags:
git tag -a v1.4 (<commit>) -m "my version 1.4" - Lightweight Tags:
git tag v1.4-lw git push origin <tag>/--tags,需要单独push,--follow-tags只push annotated tags
- Annotated Tags:
git tag -d v1.4-lw
git push origin :refs/tags/v1.4-lw
git push origin --delete <tagname>
git show <commit>/<tag>
git mv file_from file_to
mv README.md README
git rm README.md
git add README思考git的原理,commit的组成
branch的使用方式:1.Long-Running Branches 2.Topic Branches
branch的成本极低,instantaneous
-
git branch: shows branches-v -rverbose,显示远程分支,-a显示所有本地和远程--merged和--no-merged
-
git branch <name>: creates a branchgit branch <name> <commit>git branch -d删除branch
-
git checkout -b <name>: creates a branch and switches to it- same as
git branch <name>; git checkout <name> git checkout --切换到上一个分支- checkout需要所有changes已经commit,或者参考Stashing and Cleaning
git checkout <tag>会进入detached HEAD状态,做的commit只属于这一个commit
- same as
-
git checkout large-mr-branch -- path/to/your/file.ext- 拆MR
-
git checkout --track origin/serverfix: 创建本地分支并track远程分支 -
git merge <revision>: merges into current branch, merge的不同策略如下:- Fast-forward,直接在当前commit上遍历即可
- recursive strategy: three-way merge
- 如果发生冲突,手动修改unmerged files然后add ,或者用mergetool
-
git mergetool: use a fancy tool to help resolve merge conflicts -
git rebase: rebase set of patches onto a new basegit rebase <basebranch> <topicbranch>rebase命令找到共同的祖先节点,然后按顺序replay commits
git checkout experiment
git rebase master # <=> git rebase master experiment
git checkout master
git merge experimentgit rebase --onto master server client
This basically says, “Take the client branch, figure out the patches since it diverged from the server branch, and replay these patches in the client branch as if it was based directly off the master branch instead.” It’s a bit complex, but the result is pretty cool.
Do not rebase commits that exist outside your repository and that people may have based work on. 会发生混淆,尤其是别人能重新把已经被rebase消除掉的commits push上来的时候,可能会fetch到存在冲突的experiment分支
- 如果遇到了这种问题,可以
git fetch & git rebase experiment或直接git pull --rebase
总结:rebase和filter-branch本质上是在tell storys,呈现给读者可读性更强的历史记录
tracking branch的缩写:@{u}或@{upstream}
-
git remote: list remotes-v
-
git remote add <name> <url>: add a remote- name本质上是reference
-
git remote show/rename/rm <remote>git ls-remote <remote>
-
如何更新fork仓库的代码
-
git remote -v git remote add upstream git@github.com:xxx/yyy.git git fetch upstream git merge upstream/master
-
-
git push <remote> <local branch>:<remote branch>: send objects to remote, and update remote referencegit push origin lab1:lab1git push --set-upstream origin my-branch,本地关联远程分支,用来省略上面一行的分支标注git push origin --delete my-branch删除远程分支
-
git branch --set-upstream-to=<remote>/<remote branch>: set up correspondence between local and remote branch- 相当于
-u
- 相当于
-
git fetch <remote>: retrieve objects/references from a remotegit fetch origin
git fetch origin master:tmp
git diff tmp
git merge tmp
git branch -d tmpgit fetch --all
git branch -vv
git push origin --delete serverfix-
git pull: same asgit fetch; git mergegit pull --rebaseandgit rebase --continue实用的冲突处理策略 -
git clone: download repository from remote- 在最后可加文件夹名参数
-o修改origin name
fetch只能得到远程新分支的引用,如果想得到实体:
git merge origin/serverfixgit checkout -b serverfix origin/serverfix
git commit --amend: edit a commit's contents/message, 可把staging area加到上一次commit里- 可指定文件
git reset HEAD <file>: unstage a filegit reset HEAD~或git reset HEAD~1撤销上次的commit(会回复到modified状态)git reset --hard- 回到上次commit的版本,配合
git pull/push(如果file是working directory内的,会很危险)
- 回到上次commit的版本,配合
- Github如何回退敏感信息
# 切到被别人push --force过的分支版本
git fetch --all
git reset --hard origin/dev
# 回退remote敏感信息
git log
git reset --hard XXXXXXXX
git push origin HEAD --force git checkout -- <file>: discard changes (很危险的指令)git clean -fd删掉unstaged文件
git diff > patch && git apply patch
-
git cat-file -p: 显示对象信息- 40位Hash值,前2位是文件夹,后38位是文件名
- 存在
.git/objects/中 - 理解git常用命令原理
-
git config: Git is highly customizable/etc/gitconfig对应--system~/.gitconfig或~/.config/git/config对应--globalpath/.git/config对应--localgit config --list (--show-origin)显示所有config- 设置Identity,见下面Github部分
-
git clone --depth=1: shallow clone, without entire version history -
git add -p: interactive staging -
git rebase -i: interactive rebasing -
git blame: show who last edited which linegit blame -L :collection _config.yml
-
git stash: temporarily remove modifications to working directory-
git stash pop [--index][stash@{id}]git stash pop恢复最新的进度到工作区git stash pop --index恢复最新的进度到工作区和暂存区git stash pop stash@{1}恢复指定的进度到工作区。stash_id是通过git stash list命令得到的。通过git stash pop命令恢复进度后,会删除当前进度
-
git stash save --include-untracked -
git stash apply stash@{n} -
git stash list -
git stash show -p | git apply -R
-
-
git cherry-pick <commit>: 将指定的提交(的变更)应用到当前分支。- 利用它只pull request一个特定的commit
git cherry-pick commit1..commit2:应用从 commit1 (不含) 到 commit2 (含) 的所有提交。- 处理合并提交 (Merge Commit): 直接
cherry-pick一个合并提交会失败,因为 Git 不知道应该采用哪个父分支的变更。- 解决方案: 使用
-m(mainline) 选项指定“主线”。
- 查看父提交:
git show <merge-commit-hash>,输出中会包含Merge: <parent-1-hash> <parent-2-hash>。 - 确定主线: 父提交1通常是被合并的目标分支 (如
master),父提交2是来源分支。我们通常想应用来源分支的变更。 - 执行命令:
git cherry-pick -m 1 <merge-commit-hash>。此命令意为“将该合并提交与它的第一个父提交进行比较,然后将差异应用到当前分支”。
- 解决方案: 使用
- 注意: 操作前最好确保不在“分离头指针”(
detached HEAD)状态,可以先git checkout -b <new-branch-name>创建一个新分支,否则cherry-pick产生的新提交可能会丢失。
-
git bisect: binary search history (e.g. for regressions) -
git submodule add <url> /path: 添加子模块。 -
git submodule update: 更新子模块,使其与主项目的记录匹配。--init: 初始化尚未克隆的子模块。--recursive: 递归更新所有嵌套的子模块。- 常用组合:
git submodule update --init --recursive是克隆一个带子模块的仓库后,最常用的初始化命令。 --remote: 拉取最新代码。此选项会忽略主项目记录的 commit SHA-1,转而检出子模块远程跟踪分支的最新 commit。这对于始终希望使用子模块最新版本的场景非常有用。--force: 强制覆盖。此选项会强制检出子模块,丢弃所有本地的修改。这在需要将子模块重置为干净状态时非常有用,但操作具有破坏性,需谨慎使用。例如:git submodule update --force --recursive。
-
git submodule deinit -f .: 反初始化子模块。 -
其他技巧与注意事项:
- 移除submodule:https://stackoverflow.com/questions/1260748/how-do-i-remove-a-submodule/36593218#36593218
- 如果报错
already exists in the index,用git rm -r --cached /path解决此问题。 submodule很适合和dotfiles搭配,但如果用在项目里可能出现问题,尤其是需要 commit 模块代码的时候。- 使用时可能遇到的坑的集合
- commit 时有坑,需要先 commit 子模块,再 commit 主体,参考:https://stackoverflow.com/questions/8488887/git-error-changes-not-staged-for-commit
-
.gitignore: specify intentionally untracked files to ignore.gitignore_global,我的设定
# ignore all .a files
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
# /TODO
# ignore all files in any directory named build
# build/
# ignore doc/notes.txt, but not doc/server/arch.txt
# doc/*.txt
# ignore all .pdf files in the doc/ directory and any of its subdirectories
# doc/**/*.pdf
- Git and GPG: my security notes
- GUIs: there are many GUI clients out there for Git. We personally don't use them and use the command-line interface instead.
- Shell integration: it's super handy to have a Git status as part of your shell prompt (zsh, bash). Often included in frameworks like Oh My Zsh.
- Editor integration: similarly to the above, handy integrations with many
features. fugitive.vim is the standard
one for Vim.
:Gblame,:Gbrowse很方便,需要装rhubarb.vim:Git直接打开hub
- Workflows: we taught you the data model, plus some basic commands; we didn't tell you what practices to follow when working on big projects (and there are many different approaches).
- GitHub: Git is not GitHub. GitHub has a specific way of contributing code to other projects, called pull requests.
- Other Git providers: GitHub is not special: there are many Git repository hosts, like GitLab and BitBucket.
- GitHub is a Git hosting repository that provides developers with tools to ship better code through command line features, issues (threaded discussions), pull requests, code review, or the use of a collection of free and for-purchase apps in the GitHub Marketplace.
- 用SSH连GitHub
git config --global user.name "huangrt01"
git config --global user.email huangrt01@163.com
# MacOs
ssh-keygen -t rsa -b 4096 -C "huangrt01@163.com" # 如果rsa已占用,可用ssh-keygen -t ed25519 -C "huangruiteng@xxx.com"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa --apple-use-keychain # MacOS带
pbcopy < ~/.ssh/id_rsa.pub # MacOS , Linux用xclip
上github添加SSH Key
ssh -T git@github.com
# ssh-keygen -y -f ~/.ssh/id_rsa
# Linux
Github被墙
sudo vim /etc/hosts
# GitHub Start
192.30.253.112 github.com
192.30.253.119 gist.github.com
151.101.184.133 assets-cdn.github.com
151.101.184.133 raw.githubusercontent.com
151.101.184.133 gist.githubusercontent.com
151.101.184.133 cloud.githubusercontent.com
151.101.184.133 camo.githubusercontent.com
151.101.184.133 avatars0.githubusercontent.com
151.101.184.133 avatars1.githubusercontent.com
151.101.184.133 avatars2.githubusercontent.com
151.101.184.133 avatars3.githubusercontent.com
151.101.184.133 avatars4.githubusercontent.com
151.101.184.133 avatars5.githubusercontent.com
151.101.184.133 avatars6.githubusercontent.com
151.101.184.133 avatars7.githubusercontent.com
151.101.184.133 avatars8.githubusercontent.com
# GitHub End
- 改电信DNS:
101.226.4.6,218.30.118.6
-
如果设置ssh key后,git push仍然要求输入邮箱密码
git remote -v查看origin使用的是https还是ssh- 如果是https,替换成ssh即可
git remote set-url origin git@github.com:huangrt01/XXX.git
-
Failed to connect to github 443这一问题的解决方案git remote set-url origin git@github.com:huangrt01/XXX.git, 先把连接方式由https改成ssh- 再在
~/.ssh/config中把ssh的端口22改成https端口443
Host github.com
User huangrt01@163.com
Hostname ssh.github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa
Port 443- 建立仓库
git init
git remote add origin git@github.com:huangrt01/dotfiles.git
git pull --rebase origin master
git push --set-upstream origin master
Git Worktree 允许在同一仓库的多个分支上同时工作,无需多次克隆仓库。
-
核心概念:
- 一个
.git目录可以关联多个工作目录(worktree) - 每个 worktree 对应一个不同的分支或 commit
- 避免频繁切换分支的麻烦
- 一个
-
常用命令:
# 创建新的 worktree,关联到特定分支 git worktree add ../feature-branch feature # 查看所有 worktree git worktree list # 删除 worktree git worktree remove ../feature-branch git worktree prune # 清理已删除的 worktree
-
适用场景:
- 同时开发多个功能分支
- 紧急修复 bug 不打断当前开发
- 对比不同分支的代码
- Oh Shit, Git!?! is a short guide on how to recover from some common Git mistakes.
- Git for Computer Scientists is a short explanation of Git's data model, with less pseudocode and more fancy diagrams than these lecture notes.
- Git from the Bottom Up is a detailed explanation of Git's implementation details beyond just the data model, for the curious.
- How to explain git in simple words
- git handbook,里面有一些资源
- 完整doc文档
- resources to learn Git
- 如何fork一个私库
- tig:图形化git历史

