创建别名

使用 git log 查看提交历史,但是输出冗杂。通常使用

git log --oneline --abbrev-commit --all --graph --decoreate --color

来获得更美观易读的输出。但是每次输入这么多肯定很烦人,使用

git config --global alias.graph "log --graph --oneline --decorate=short"

增加一个全局别名,这个别名对于任何地方的 git 都适用。如此一来,键入 git graph 会等效于

git log --graph --oneline --decorate=short

样例输出:

$ git graph
 
* 36f2d65 (HEAD -> master, origin/master, origin/HEAD) Forget it
* 9b4a6d7 Update ref list
* 3931d4d Using relative path for image
* ba18821 Upload pics
* ceca69a fixed reference
* be15df2 fixed picture address
* 97a36f3 Initial commit

今天发现一个还不错的alias,效果如下

$ git log --pretty=format:'%Cgreen %ad %Cred%h%Creset -%C(yellow)%d%Creset %s %C(bold blue)<%an>%Creset' --abbrev-commit --date=format:'%F %T'
 
 2025-07-15 15:03:14 910a0c5 - (HEAD -> master, origin/master) add getSHSZBarRTSnapshotRobo <bing.hu>
 2025-07-11 11:23:49 d95d7c8 - [RTMDD-1461] add news contraint for specific users <bing.hu>
 2025-07-08 17:13:40 df0681b - fix json empty double issue <Shang.Wu>
 2025-07-08 10:35:29 8dd8a98 - refine redis merge <Shang.Wu>
 2025-07-02 13:23:20 7ec1569 - [MDL-613] fix empty exchangeCD and currencyCD <bing.hu>
 2025-06-04 16:02:11 17392f0 - [DATAIFS-15083] getSHSZBarRTSnapshot2 switch source to 13.115 <bing.hu>
 2025-05-08 14:09:00 f884afb - improve s3 query <Shang.Wu>
 
# 可以增加一个别名
$ git config --global alias.plog "log --pretty=format:'%Cgreen %ad %Cred%h%Creset -%C(yellow)%d%Creset %s %C(bold blue)<%an>%Creset' --abbrev-commit --date=format:'%F %T'"

忽略已经添加的文件

git rm --cached <somefiles>

推送本地分支到远程

# 远程分支如果不存在,则自动创建。
git push origin <local_brach>:<remote_branch>

拉取远程分支到本地

# 从远程分支切换(并创建,如果不存在)本地分支
git checkout -b <local_branch> origin/<remote_branch>
 
# 另:取回远程分支并创建对应的本地分支,不换自动切换到该分支
git fetch origin <remote_brach>:<local_branch>

删除commit历史

如果不小心将隐私信息推送至远程仓库(如github),那么仅仅删除再更新再推送到远程仓库覆盖是不够的,别人还是可以通过你的commit历史查到你所做的更改,所以这种情况下必须删除之前所有的commit history. 大致思路是创建一个孤立分支,然后重新添加文件,再删除master分支,将新建的分支重命名为master,再推送到远程强制覆盖1

# Check out to a temporary branch:
git checkout --orphan TEMP_BRANCH
 
# Add all the files:
git add -A
 
# Commit the changes:
git commit -am "Initial commit"
 
# Delete the old branch:
git branch -D master
 
# Rename the temporary branch to master:
git branch -m master
 
# Finally, force update to our repository:
git push -f origin master

在commit之前撤销add操作

要在 commit(提交)之前撤销 git add,运行  git reset <file>  或  git reset 取消所有更改即可。

合并某个文件到当前分支

例如当前在master分支,希望合并某个分支dev的某个或多个文件到当前分支:

git checkout dev file1 file2 ...

但是上述做法会强行覆盖当前分支的文件,没有冲突处理,更安全的做法是先从当前分支新建分支master_temp,然后在master_temp中checkout,最后再将master_temp分支merge到master分支:

# Create a branch based on master
git checkout -b master_temp
 
# Chechkout file1 from dev to master_temp
git checkout dev file1
git commit -m "checkout file1 from dev"
 
# Switch to master and merge, then delete
git checkout master
git merge master_temp
git branch -d master_temp

Ref: https://segmentfault.com/a/1190000008360855

Git merge

当你觉得很多时候对于一个命令的很多子命令或者选项不是很清晰,而且查了忘,忘了查,那多半是你不理解它的工作机制。或者说它对你来说不是那么自然易懂,这个时候就需要深入以下,了解以下它的基本原理,帮助自己理解,以便记忆。

git merge就是如此,你要知道merge的含义是什么?它其实就是在被merge的分支上重现要merge的commits. 比如说:

a---b---c---d---e (master)
    \
     `--A---B---C (dev)

你当前在master分支的e节点,你要merge dev分支。其实就是将A、B、C三个commit在master分支上重现,仿佛master分支上曾经也做过这些改动。那么冲突的来源就是你在两个分支中,对同一个文件作了不同的改动,如何解决不言而喻。

小朋友,你是否有很多?

Q: 我想只重现B节点怎么办?
A: git checkout master && git cherry-pick 62ecb3,这里62ecb3是节点B的commit标识。

Q: 我想重现A-B,但不要C怎么办?
A: git checkout -b newbranch 62ecb3 && git rebase --onto master 76cada^,这里76cada是A节点的commit标识。先基于B创建一个分支,这个分支包含了A节点的改动,然后rebase到master上去,结果就是A和B重现在master分支上。

Ref:

  1. https://stackoverflow.com/questions/161813/how-to-resolve-merge-conflicts-in-git
  2. Cherry-Picking specific commits from another branch

Fork之后如何同步fork源的更新

# see remote status
git remote -v
 
# add upstream if not exist one
git remote add upstream https://github.com/<origin_owner>/<origin_repo>.git
git remote -v

从上游仓库 fetch 分支和提交点,提交给本地 master,并会被存储在一个本地分支 upstream/master

git fetch upstream

切换到任意分支,merge已经fetch的分支即可:

git checkout somebrach
git merge upstream/master

see: https://www.zhihu.com/question/28676261

Ref:

  1. Configureing a remote for a fork
  2. Syncing a fork

从另一个分支检出某个文件并重命名

有时候开了一个孤立分支,但是想参考其他分支的代码,而当前分支又有同名文件,此时就需要从其他分支检出文件并重命名。

# show the content of a.cpp in specific commit HEAD^
git show HEAD^:a.cpp
 
# that's done
git show HEAD^:a.cpp > b.cpp

Ref: search “git-checkout older revision of a file under a new name” in stack overflow

查看已被跟踪的文件

git ls-files

Ref: search “how can i make git show a list of the files that are being tracked” in stack overflow

Submodule

git submodule本质上是指向一个其他仓库的链接,默认clone不会将submodule对应的仓库克隆下来。

# help
git submodule --help
 
# 添加submodule
#   1. 进入目标子文件夹
git submodule add https://github.com/imtianx/liba.git
 
# 更新submodule
cd xxx
git pull
git submodule update --recursive
 
# 在主目录下更新submodule liba
git submodule update --remote liba
 
# 删除submodule
vim .gitmodules # 删除相应条目
vim .git/config # 删除相应条目
rm -rf .git/modules/liba # 删除对应的git文件夹
 
# 在克隆时连同submodule一并克隆
git clone https://github.com/imtianx/MainProject.git --recursive
# is equivalent to
git clone https://github.com/imtianx/MainProject.git
git submodule init
git submodule update

Ref:

  1. Git-工具-子模块
  2. Git 子模块:git submodule

如何撤销本地commit

有时候本地add了一写diff,随手commit了,接着又有些diff可以共用这个commit,就想撤销刚刚的commit,把所有的diff合并在一起作为一次commit。

# for more info, type git reset -h
git reset --soft <commit_id>

修改已提交的commit message

# commit_id至少比要修改的那个commit早一个版本
git rebase -i <commit_id>
 
# 列出 rebase 的 commit 列表,不包含 <commit id>
$ git rebase -i <commit id>
# 最近 3 条
$ git rebase -i HEAD~3
# 本地仓库没 push 到远程仓库的 commit 信息
$ git rebase -i
 
# vi 下,找到需要修改的 commit 记录,`pick` 修改为 `edit` 或 `e`,`:wq` 保存退出
# 重复执行如下命令直到完成
$ git commit --amend --message="modify message by daodaotest" --author="jiangliheng <jiang_liheng@163.com>"
$ git rebase --continue
 
# 中间也可跳过或退出 rebase 模式
$ git rebase --skip
$ git rebase --abort
 
# 如果只是更改last commit
git commit --amend

Git rebase

Cf. https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase

rebasemerge都是将另一分支的提交(commit)集成到当前分支的方法。而merge会保留两条分支的所有commit,然后解决冲突,然后形成一个merge commit,从git log上来看,原本线性的提交历史分了叉,然后又合了并。而rebase则是基于当前分支的某次提交去重现另一个分支,rebase之后依然能够保留提交历史的线性状态。

a---b---c---d---e (master)
    \
     `--A---B---C (dev)

From a content perspective, rebasing is changing the base of your branch from one commit to another making it appear as if you’d created your branch from a different commit. Internally, Git accomplishes this by creating new commits and applying them to the specified base. It’s very important to understand that even though the branch looks the same, it’s composed of entirely new commits.

The primary reason for rebasing is to maintain a linear project history. For example, consi der a situation where the main branch has progressed since you started working on a feature branch. You want to get the latest updates to the main branch in your feature branch, but you want to keep your branch’s history clean so it appears as if you’ve been working off the latest main branch.

You have two options for integrating your feature into the main branch: merging directly or rebasing and then merging. The former option results in a 3-way merge and a merge commit, while the latter results in a fast-forward merge and a perfectly linear history. The following diagram demonstrates how rebasing onto the main branch facilitates a fast-forward merge.

Rebasing is a common way to integrate upstream changes into your local repository. Pulling in upstream changes with Git merge results in a superfluous merge commit every time you want to see how the project has progressed. On the other hand, rebasing is like saying, “I want to base my changes on what everybody has already done.”

注:写这个的时候,我自己对rebase的理解也很模糊。

任何时候不清楚的时候请终止rebase:

git rebase --abort

反复操练几次,git有友好的提示信息。

查看提交记录

# 查看最近一次提交了哪些文件
git log --status -n1

git-mm使用简介

git mm init -u ssh://git@lfg-y.codehub.huawei.com:2222/DCP_Industry_SIG/sig_back_main.git -b master -g C  # 初始化仓库,C表示只下载C语言的代码
 
git mm sync # 同步代码
git mm start br_dev # 创建分支
git mm upload -y # 上传代码,注意这个操作应该在每个git仓库使用常规方式提交之后,统一上传

git reset/restore/revert? 傻傻分不清楚

补丁的创建和应用

参考:http://note.alvinhtml.com/developer/git.html

Git 提供了两种补丁方案,一是用 git diff 生成的 UNIX 标准补丁 .diff 文件,二是 git format-patch 生成的 Git 专用 .patch 文件。

.diff 文件只是记录文件改变的内容,不带有 commit 记录信息,多个 commit 可以合并成一个 diff 文件。

.patch 文件带有记录文件改变的内容,也带有 commit 记录信息,每个 commit 对应一个 patch 文件。

使用 git 的 format-patch 和 am 命令进行生成 patch 和打 patch

  1. 对于 git 这种以 project 为单位的修改,尤其是涉及到多个文件夹下的多个文件的改动时,非常方便,能够记录所有的改动(添加,修改,删除文件等)
  2. 可以保存 commit 信息。
  3. 能够灵活的获取 patch。可以获取任意两个 commit 之间的 patch 集。

从本地修改生成diff:https://mazhuang.org/wiki/git/

将未添加到暂存区的更改生成 diff 文件:

git diff > demo.diff

将已添加到暂存区的更改生成 diff 文件:

git diff --cached > demo.diff

合并上面两条命令生成的 diff 文件包含的更改:

git apply demo.diff

Footnotes

  1. https://gist.github.com/heiswayi/350e2afda8cece810c0f6116dadbe651