【Git】基础 Git 命令

Tip: 查看 git 命令手册
$ git help <command>

Git 简介

基本逻辑

Git 工作区、暂存区和版本库的概念,可看 相关介绍

基本操作

本地仓库

1
2
3
4
5
git clone --shallow     # 浅克隆(shallow clone),不包括完整的版本历史信息
# add and commit
git add -p <file name>  # -p 分块交互式暂存
git add . && git commit -m "initial commit"
git commit --amend

远程仓库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
### 提交代码
git push origin main[:main]  # git push <remote> <local branch>:<remote branch>
git --set-upstream-to=origin/main # bind remote branch with local
git push

### 拉取代码
# fetch 将获取远程仓库中的新提交,并将其保存在本地的远程跟踪分支中,不改变工作区
# merge 将远程跟踪分支合并到当前分支
git fetch; git merge

# pull 先执行 git fetch,然后自动执行 git merge(默认)
git pull origin main [--merge]

使用 git fetch 可以查看远程仓库的更新情况,在合适的时候决定是否将这些更新合并到本地分支。 这样可以避免意外的合并冲突或不必要的更改,同时也提供了更大的灵活性。

image.png|502

上述拉取代码合并的方式会创建一个新的合并提交,而使用 rebase 会看起来是在目标分支的最新提交上进行更改。

  • 希望保留分支的历史记录并明确显示合并点,可以选择 merge
  • 希望提交历史保持线性,清晰地展示你的更改,可以选择 rebase
1
2
3
4
# git fetch 后执行 git rebase
git pull origin main --rebase
# 冲突解决并 add 修改后
git rebase --continue

rebase 的历史改写特性也可能导致冲突或其他问题,在对公共分支(如 main)进行 rebase 操作时,应该特别小心,以避免破坏其他人的工作或引入冲突。

Local 常用操作

查看变更/历史

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
git status

# 工作区与暂存区 diff
git diff
# 暂存区与最新本地版本库(本地库中最近一次commit的内容)
git diff --cached  [<path>...]
# 工作区与指定commit-id的差异
git diff commit-id  [<path>...]
# 两个 commit 的差异
git diff commit-id-a commit-id-b [<path>...]
# 查看本地分支和远程分支
git diff remotes/origin/main main

# commit history
git log        # 显示历史日志
git log -p -2  # 近2次,显示 patch(diff)
git log --oneline --stat  # 简略文件更改信息统计
git reflog     # 更详细,每次 amend 的内容有记录
git log --all --graph --decorate --oneline # 可视化历史记录(有向无环图

# 内容历史
git blame <file name> | grep 'code' # 查看最后修改某行的人
git show <commit-id>        # 查看 commit 内容
git show <commit-id> --stat # 查看 commit 变更的文件列表

分支 branch

1
2
3
4
5
6
7
8
# 查看
git branch -vv  # local 显示commit信息(very verbose)
git branch -r   # 查看远程分支
git branch -a   # 查看所有分支

# 新建
git branch <new_branch>; git checkout <new_branch>  # create and switch
git checkout -b <new_branch>

暂存、撤销、回退

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 暂存当前进度
git status
git stash save 'message...'  # 添加一些注释
git stash list  # 显示保存进度的列表
git stash pop

# 撤销 add,不更改文件(暂存区 -> 工作区)
git reset <filename>

# 文件恢复(暂存区 替换 工作区)
git checkout -- <filename>
# 若已经 add 需要先
git reset HEAD -- <filename>

# 回退
git checkout <commit-id>   # 更改 HEAD 指向,更新工作区,查看历史 commit 代码
git checkout <tag-id>
# 返回最新分支
git checkout master
git checkout -             # 之前处于分离头状态(detached HEAD),切换回上一个分支或提交
                           ## HEAD (no branch)
                           
git checkout HEAD .        # HEAD 指向内容 替换 现在的工作区和暂存区

# 回退上一版本(撤销 commit)
git reset --hard HEAD^     # 改变本地工作区(代码丢弃)
git reset --soft HEAD^

取消 track 文件

1
2
3
4
5
6
7
# 列出 master 分支下跟踪的文件 
git ls-tree -r master --name-only

# 删除已经 add 的内容(本地保留)
git rm -r --cached output/*
# 删除文件(包含本地!!!)
git rm -r *

别名 alias

1
2
3
4
5
6
vim ~/.gitconfig

[alias]
	graph = log --all --graph --decorate --oneline

-> git graph

子模块 submodule

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 添加 submodule
git submodule add <url> <submodule_path>

# 初始 git clone 的工程中带有 submodule 时并不会自动下载,此时下载可以
git submodule init
git submodule update
# 一个命令完成
git submodule update --init --recursive 

# 去除 submodule
rm -rf submodule_path     # 子模块目录及源码
vi .gitmodules            # 项目目录下 .gitmodules 中相关条目
vi .git/config            # 配置项中子模块相关条目 git config --remove-section submodule.<submodule_path>
rm .git/module/submodule_name   # 模块下的子模块目录

忽略文件 gitignore

项目中不是所有的文件都需要放进仓库(如 data/*),设置 ignoring files 指定不需要 track 的文件类型。 具体内容可参考 .gitignore 文件样例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 单个仓库设置
cd project_folder
echo 'output/*' >> .gitignore

# 全局设置(所有仓库)
vim ~/.gitignore_global
git config --global core.excludesfile ~/.gitignore_global

# .gitignore只能忽略那些原来没有被track的文件
# 如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的
# 需要先把本地缓存删除(改变成未track状态),
git rm --cached <file_name>

# 更新 track
git rm -r --cached .
vim .gitignore
git add . && git commit --amend

tag 标签

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 创建标签
git tag -a <tagname> [commit] [-m <msg>]
# git tag -a v1.0 abcd1234 -m "Version 1.0 release"

# 查看
git show tagname
git tag                 # 查看所有标签

# 推送标签到远程仓库
git push origin tagname
git push origin --tags  # 所有标签

# 删除标签
git tag -d tagname      # 本地
git push origin :refs/tags/tagname # 远程

commit 时间修改

1
git commit --amend --no-edit --date="2022-01-01 10:30:00"

常用场景

撤销 commit amend

1
2
3
4
5
6
7
$ git reflog
c135a6c (HEAD -> master) HEAD@{0}: commit (amend): xxx
371ba40 (origin/master, origin/HEAD) HEAD@{1}: xxx

$ git reset HEAD@{1}
$ git add .
$ git commit -m 'new commit'

分支合并

1
2
3
4
5
6
7
8
9
git merge <branch>  # merge branch and HEAD
git merge --abord

# 若有冲突,编辑冲突文件
vim <conflict_file>

# 解决冲突后
git add <conflict_file>
git merge --continue

fork 同步更新原项目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
git remote add upstream https://github.com/original/repo.git

# 确认添加状态,多出两个upstream
git remote -v

# 从上游仓库 fetch 分支和提交点到本地,存储于本地分支 upstream/master
git fetch upstream
git branch -a

# 切换到本地主分支,merge
git checkout master
git merge upstream/master

git push origin master

更改某次提交内容

如果某个功能点相关的改动只生成一个 commit,将极大的简化功能点在分支间的移动工作,现在发现之前的 commit 功能不完整,如何更改这次提交内容呢

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# git rebase -i 交互式变基
# 找到需要更改的 commit-id(rebase 其父提交^), 将行首的 pick 改成 edit 后保存
git rebase <commit-id>^ --interactive

vim file # change file

git add .
git commit --amend
git rebase --continue

# 若有冲突,编辑冲突文件
vim <conflict_file>

# 解决冲突后
git add <conflict_file>
git commit --amend
git rebase --continue

如何把当前改动追加到这次 commit 上呢

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
git stash # 保存改动

git rebase <commit-id> --interactive # git rebase -i 交互式变基

git stash pop # change file

git add .
git commit --amend
git rebase --continue

# 若有冲突,解决冲突同上

查找提交内容

  • blame
  • 一起显示更改内容和 commit 信息的方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
git log -S <string> # 字符串匹配
git log -G <regex>  # 正则匹配

# 查看具体用法
git help log

# 得到含有相关修改内容的 commit 后,查看提交内容
git checkout <commit-id>
git checkout master

git show <commit-id>

Remote 设置

初始设置 & 新建仓库

参考 官方文档 完成初始化操作,当然也可不适用命令行,直接在网页上新建仓库。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# github CLI login
gh auth login

# create a repo
git init -b main

# 使用 github CLI 创建远程仓库
gh repo create --source=. --private

# 手动创建
git remote add orgin https://github.com/aaa/xxx.git

# config
git config --global  --list
git config --global user.name "alfly"
git config --global user.email alfly0023@gmail.com

ssh/https 切换

选择不同的远程连接方式,完成相关 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# add ssh key
cat ~/.ssh/id_rsa.pub

# validate
ssh -T git@github.com

# check
git remote -v

# change to ssh
git remote set-url origin git@github.com:Alfly/Tai-e-assignments.git

SSH 连接设置

mac 配置 (已备份至 dotfiles-local)

1
2
3
# 22 端口默认关闭
sudo systemsetup -getremotelogin
sudo systemsetup -setremotelogin on

在 HTTPS 端口使用 SSH

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
vi ~/.ssh/config
# switch to port 443 -> [ssh.github.com]:443
# ssh -T [-p 22] git@github.com
# ->
# ssh -T -p 443 git@ssh.github.com
Host github.com
  Hostname ssh.github.com
  Port 443
  User git
  IdentityFile /Users/lfe/.ssh/id_rsa
  TCPKeepAlive yes
  PreferredAuthentications publickey
  # socks5 proxy here is not working
  # ProxyCommand nc -x localhost:7891 -X 5 %h %p

Linux 服务器 github SSH 配置代理 (todo: 备份至 dotfiles-local)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ vi ~/.ssh/config
--------------------
ProxyCommand connect -S xxx.xxx.xx.xx:7890  -a none %h %p

Host github.com
  User git
  Port 22
  Hostname github.com
  IdentityFile "/home3/lfe/.ssh/id_rsa"
  TCPKeepAlive yes

Host ssh.github.com
  User git
  Port 443
  Hostname ssh.github.com
  IdentityFile "/home3/lfe/.ssh/id_rsa"
  TCPKeepAlive yes

Todo

  • git mergetool:使用工具来处理合并冲突
  • git bisect:通过二分查找搜索历史记录
  • git cherry-pick:选择性地将一个或多个提交应用到当前分支

Reference

0%