Git Tips¶
教程¶
Q&A¶
- don’t commit generated files, like pdf from LaTeX sources, see (pdf is not proper) and (images are OK in git) for more discussions. (But I think if pdf is only for several commits, it would be totally OK.)
GitHub¶
- 添加和设置项目徽章: shields.io
- 开通 Sponsor
- relase public from private repo
- issue 中如果在第一个评论里面加 task,则会在 issue 列表中显现处理,如 Issue 57
- ignore whitespace when comparing commits: add
?w=1
https://stackoverflow.com/questions/37007300/how-to-ignore-whitespace-in-github-when-comparing
2023-03-23: GitHub updated RSA SSH HOST KEY
Recently, when running git push
, it throws that
Warning: the ECDSA host key for 'github.com' differs from the key for the IP address '140.82.114.3'
Actions¶
- use
setup-python
instead ofcontainer: python
: fast and flexible - In YAML, if needed, use single quotes (instead of double quotes) for strings. see for more details
Searching¶
SETUP & INIT: config
¶
Installation¶
sudo apt-get install git
Setup User Name and Email¶
git config --global user.name "test"
git config --global user.email "test@163.com"
除了设置全局的 name
和 email
,也可以指定 --local
(因为是默认行为,因此也可以省略)对不同 project 指定不同的 name
及 email
,
git config [--local] user.name "foo"
git config [--local] user.email "foo@gmail.com"
另外还有更高级的配置 --system
。显然,local 覆盖 global, 而 global 覆盖 system.
如果想要知道当前 name
和 email
,可以通过下面命令查看
git config user.name
git config user.email
参考 Is it possible to have different Git configuration for different projects?
push with ssh key¶
ssh-keygen -t rsa -C "test@163.com"
复制 ~/.ssh/id_rsa.pub
到 github 上。
sign with GPG key¶
Official documentation on Github: Managing commit signature verification
Two nice Chinese tutorials on setting GPG keys
The steps are:
# generate GPG key, 4096bits
gpg --full-generate-key
# get secret key id
gpg --list-secret-keys --keyid-format LONG
# configure git
git config --global user.signingkey <SECRET_KEY_ID>
git config --global commit.gpgsign true
# tell github public GPG key
gpg --armor --export <SECRET_KEY_ID>
CLEAN: clean
¶
remove untracked files¶
git clean -n
git clean -f
refer to How to remove local (untracked) files from the current Git working tree
CLONE: clone
¶
clone from pull request
git clone .....
git fetch origin pull/2/head
git checkout -b pullrequest FETCH_HEAD
- 提高
git clone
速度
git config --global http.postBuffer 524288000
- clone 单个分支
git clone -b BRANCH_NAME ...
- clone 多个分支
git clone ...
# list the remote-tracking branches
git branch -r
# OR: list both remote-tracking branches and local branches
git branch -a
git checkout -b BRANCH_NAME REMOTE_BRANCH
# OR: the branch name is exactly in the remote branches
git checkout BRANCH_NAME
BRANCH: branch
¶
# list branch
$ git branch
# create a new branch
$ git branch [branch_name]
# switch branch
$ git checkout [branch_name]
delete branches¶
## Delete a remote branch
$ git push origin --delete <branch> # Git version 1.7.0 or newer
$ git push origin :<branch> # Git versions older than 1.7.0
## Delete a local branch
$ git branch --delete <branch>
$ git branch -d <branch> # Shorter version
$ git branch -D <branch> # Force delete un-merged branches
## Delete a local remote-tracking branch
$ git branch --delete --remotes <remote>/<branch>
$ git branch -dr <remote>/<branch> # Shorter
$ git fetch <remote> --prune # Delete multiple obsolete tracking branches
$ git fetch <remote> -p # Shorter
adapted from cmatskas/GitDeleteCommands.ps1
see also: 删除github的master分支
LOG: log
¶
- extract the last revision time of a file,
git log --date=short --format=%ad file
, it has been used in the mkdocs-git-revision-date-plugin.
PUSH: push
¶
default push¶
采用低版本的 git, 如 v1.8.3.1
当 push 时会报出以下提醒信息,
warning: push.default is unset; its implicit value is changing in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the current behavior after the default changes, use:
git config --global push.default matching
To squelch this message and adopt the new behavior now, use:
git config --global push.default simple
See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)
而自己本机电脑一般采用 v2.0+
, 比如当前 v2.17.1
的 git,所以直接采用 simple
模式便好了。
See also: Git push与pull的默认行为
require username and password¶
possible reason: use the default HTTPS instead of SSH
correct this by
git remote set-url origin git@github.com:username/repo.git
参考 Git push requires username and password
REMOTE: remote
¶
change remote repo name¶
举个例子,如将一个名为 epi 的仓库改名为 depi,再次在本地提交虽然也能成功,但是会提示你原始的仓库已经移动,请修改为新的仓库地址,于是我们可以利用下面的命令进行修改
git remote set-url origin git@github.com:szcf-weiya/depi.git
modify origin¶
本地通过 git clone
得到仓库 CellProfiler/CellProfiler,后来需要在此基础上做些更改用到自己的项目中去,于是对原仓库进行了 fork,不过忘记了本地的仓库其实是从原仓库 clone 下来的,而非来自 fork 后的仓库,所以当在 git push 会试图像原仓库进行 push 时,当然是会被拒绝的。
简单的方法便是直接更改本地仓库的 origin,首先可以通过
git remote show origin
来查看当前 origin,前几行显示如下
$ git remote show origin
* remote origin
Fetch URL: git@github.com:CellProfiler/CellProfiler.git
Push URL: git@github.com:CellProfiler/CellProfiler.git
更改可以使用
git remote rm origin
git remote add origin git@github.com:szcf-weiya/CellProfiler.git
或者直接一步走
git remote set-url origin git@github.com:szcf-weiya/CellProfiler.git
注意到这里 url 都是直接取的 git@github.com
,倘若用了 https://github.com
,则 push 时会要求手动输入密码。
参考 How to change the fork that a repository is linked to
sync forked repo¶
- 添加原仓库,比如
git remote add upstream git@github.com:LCTT/TranslateProject.git
查看当前远程仓库
git remote -v
- pull
git pull upstream master
参考Quick Tip: Sync a GitHub Fork via the Command Line
Rewrite History: rebase, reset
¶
rebase after merge¶
Suppose I have done
git add .
git commit -m "add local changes"
git pull
then a merge will be invoked.
But actually these two commits do not have conflicts, so we can use
$ git rebase
First, rewinding head to replay your work on top of it...
to remove the merge commit.
discard local changes¶
Here are instructions when running git status
.
- not added: just
git checkout .
- last added:
git reset HEAD
, orgit restore --staged <file>
- last committed:
- 丢弃修改的内容:
git reset --hard HEAD^
- 保留修改的内容:
git reset --soft HEAD^
- 还有一种
mixed
,也是默认不带参数的
- 丢弃修改的内容:
- jump to historical commit:
git reset --hard <commit>
如果也要更新远程仓库,如 github,则需要使用 git push -f
强制更新。
参考
discard reset¶
type
git reflog
to find the log of all ref updates, and then use
git reset 'HEAD@{1}'
to restore the corresponding state of HEAD@{1}
, where 1
implies that last state, and it can be set as other refs.
refer to How to undo ‘git reset’?
case study¶
某次写博客时,有一篇仍处于草稿箱的文件 A.md 暂时不想上传,但是更改了已经发表的文章 B.md 中的 typo,然后提交时一不留神直接用
$ g "fix typo"
提交了,其中 g
是在 .bashrc
中自定义的一个函数,
# file .bashrc
# alias for git
func_g(){
rm -f core
git add .
git commit -a -m "$1"
git push
}
alias g=func_g
本来会直接 push 上去的,但是发现及时,Ctrl-C 取消掉了,不过 commit 需要回退。注意到这时并不想放弃修改的文档,所以 --hard
选项不可取,
$ git reset --soft HEAD^
这只是取消了 commit,但是仍然被 add 了,查看 git status,会有一行提示语,
(use "git reset HEAD <file>..." to unstage)
所以下一步自然为
$ git reset HEAD A.md
有时可能删掉已经 add (甚至 commit,但还没 push) 的文件(比如下文中的 Peek 2020-08-20 10-06.mp4
),首先如果 committed,则采用
$ git reset --soft HEAD^
取消 commit,然后 git add
就好了,但是继续撤销 add 的状态,并没有作用,而是提示
Unstaged changes after reset:
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: docs/Linux/Peek 2020-08-20 10-06.mp4
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: docs/Linux/Peek 2020-08-20 10-06.mp4
$ git reset HEAD docs/Linux/Peek 2020-08-20 10-06.mp4
Unstaged changes after reset:
D docs/Linux/Peek 2020-08-20 10-06.mp4
$ git add .
$ git status
On branch master
Your branch is up to date with 'origin/master'.
amend commit message¶
If the latest commit, type
git commit --amend
to edit the commit message, and then
git push --force-with-lease
where --force-with-lease
is a safer version than --force
, in which the former will not overwrite any work on the remote branch while the latter would overwrites a remote branch with the local branch. More detailed comparisons refer to git push –force-with-lease vs. –force
Refer to Changing git commit message after push (given that no one pulled from remote).
discard un-pushed merges¶
现有本地、服务器仓库各一个,通过 GitHub 进行共享,原则上只通过本地编辑代码,然后在服务器中借助 git pull
进行更新代码。但是有一行代码由于文件路径原因,不得已在服务器端更改,在 git pull
之前进行了 git add; git commit
操作,所以 pull
的时候会产生 merge,而且这样下去每一次进行 pull
都会进行 merge。这样就很难看出服务器端真正更改的内容了,这时发现了 git rebase
,这张图很好地展示了它要干的事情,
这也正是我想要的,简单说就是把基于以前的更改转换成基于最新的更改。
然而我这情况还没这么直接,因为有过很多 merges,这些 merges 都想放弃掉。
$ git log --graph --format=%s
后来发现了 –rebase-merges 选项,似乎是在处理我的问题(最后还是理解反了),但是没高兴多久,服务器端的 git 版本只有 1.8.3.1
,而这个选项至少要求 2.x
。不过有注意到了 --preserve-merges
选项,新版本中已经 deprecated,而且文档中有这么一句,
[DEPRECATED: use --rebase-merges instead]
似乎也可以用,但是这个名字怎么感觉跟我的目标是相反的呢!?(此时还未意识到),后来简单试了一下
$ git rebase --preserve-merges
发现没啥变化。这才意识到自己理解错了这两个命令。
后来又注意到了 --root
选项,发现这个跟自己的目标挺像的,而且抱着搞坏了大不了重新 git clone
一下,所以果断去试了,history 确实都变成线性了
但是似乎还没成功,commits 都变成不一样了,
后来又瞎运行了下
$ git rebase
发现竟然成功了,
现在我甚至怀疑是不是一开始直接 git rebase
就好了,也不需要 --root
,不过 root
是说可以 rebase 所有 reachable commits,没有什么 upstream 限制,不知道 merges 算不算 upstream 限制(以后再玩玩). 如果需要更新的话,仍采用 git pull
,但是 git pull
不是相当于 git fetch & git merge
么?!不过需要注意加上 --rebase
选项
change commit time¶
TL;DR
GIT_AUTHOR_DATE=$(date -d "-10 days - 8 hours" -Iseconds) GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git commit -m ""
因为习惯将当天的工作通过 commit 贡献图反映出来,但有时候提交时超了一点点时间就变成第二天了,有时会强迫性地想把时间改过来。此前一直在用一种比较笨但很有效的方法,即修改系统时间——往回拨几分钟,再进行 commit。但是昨晚这样操作时出现了失误,只调整了时间,日期并没有回退一天,push 之后才意识到,就这样多了一条未来的 commit。
于是就需要像修改 commit message 修改时间重新 commit。首先尝试的是,
git commit --amend --date="13/04/2021 @ 23:59"
这样 git log
确实显示时间更改了,
$ git log
commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Author: XXX
Date: Tue Apr 13 23:59:49 2021 +0800
但是 force push 到 github 上时仍然是当前的时间。这时才意识到有所谓的 GIT_AUTHOR_DATE 和 GIT_COMMITTER_DATE 之分,通过加上 --pretty=fuller
可以看到 committer 及其 date (refer to How to configure ‘git log’ to show ‘commit date’)
$ git log --pretty=fuller
commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Author: XXX
AuthorDate: Tue Apr 13 23:59:49 2021 +0800
Commit: XXX
CommitDate: Tue Apr 13 23:59:08 2021 +0800
所以修改完 author date 之后,仍然采用笨办法回拨系统时间再提交,这样就能修改 commit date,于是便出现了上面 commit date 竟然比 author date 早的结果。当然更直接的做法是直接在命令行 commit 时同时指定这两个时间,
$ GIT_AUTHOR_DATE=$(date -d "-10 days - 8 hours") GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git commit -m "XXXX"
在本机上, date
默认输出格式为
$ date -d "-10 days - 8 hours"
Mon May 24 12:17:17 CST 2021
其中 CST
为 China Standard Time (UTC+0800),然而,Central Standard Time (UTC-06:00) 缩写也是 CST
,另外 Cuba Standard Time (UTC-05:00) 也为同一缩写,详见 。
大概基于此,上述时间被识别为了 UTC-0600,
$ git log --pretty=fuller
Author: szcf-weiya <your@email>
AuthorDate: Mon May 24 12:18:06 2021 -0600
Commit: szcf-weiya <your@email>
CommitDate: Mon May 24 12:18:06 2021 -0600
为了避免歧义,加入选项 -Iseconds
指定为 ISO-8601 格式,并精确至 seconds
.
$ GIT_AUTHOR_DATE=$(date -d "- 8 hours" -Iseconds) GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git commit -m "XXX"
$ git log --pretty=fuller
Author: szcf-weiya <your@email>
AuthorDate: Mon May 24 12:27:06 2021 +0800
Commit: szcf-weiya <your@email>
CommitDate: Mon May 24 12:27:06 2021 +0800
另外,GIT_**_DATE
支持的日期格式详见 man git-commit
.
change date of an older commit¶
TL; DR
Avoid to use, let it go.
Warning
Try to test
$ git filter-branch --env-filter 'if [ $GIT_COMMIT == 4fae3c78eac6230ee53082654aaf58ab79fbddc5 ]; then echo $GIT_AUTHOR_DATE; fi'
Rewrite 4f479f96d4672aaf11f800306b10bb95e83071fd (1/21) (0 seconds passed, remaining 0 predicted) /usr/lib/git-core/git-filter-branch: 1: [: 4f479f96d4672aaf11f800306b10bb95e83071fd: unexpected operator
...
I want to update the commit date of an historical commit, where there are 3 more recent commits. Following How can one change the timestamp of an old commit in Git?,
$ git rebase -i e5c9f
the mark the commit to be edited, not that e5c9f
is the one before the commit to be edited,
after saving,
$ git rebase -i e5c9f
Stopped at 4fae3c7... XXXXXXXXXXXXXXXXXXXXXXXXXXX
You can amend the commit now, with
git commit --amend '-S'
Once you are satisfied with your changes, run
git rebase --continue
then change the date via
$ GIT_AUTHOR_DATE=$(date -d "2021-07-11T23:58:48+0800" -Iseconds) GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git commit --amend --no-edit --reset-author
[detached HEAD e1e1cec] XXXXXXXXXXXXXXXXXXXXXXXXXXX
note that the hash has been updated, then run
$ git rebase --continue
Successfully rebased and updated refs/heads/master.
Now the status is
$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 4 and 4 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Then just type git pull
, but I immediately realized that do not use git pull
since it will introduce 4 more commits, together with the merge one. Note that
$ git reflog
1f4ff92 (HEAD -> master) HEAD@{0}: pull: Merge made by the 'recursive' strategy.
f8ea37c HEAD@{1}: rebase -i (finish): returning to refs/heads/master
f8ea37c HEAD@{2}: rebase -i (pick): XXXXXXXX
59b6935 HEAD@{3}: rebase -i (pick): XXXXXXXX
b849915 HEAD@{4}: rebase -i (pick): XXXXXXXX
then go back to
$ git reset HEAD@{1}
$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 4 and 4 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
then run
$ git push --force-with-lease
However, although the author date indeed has updated, the commit date points to the current date.
TEMPORARY COMMITS: stash
¶
Temporarily store modified, tracked files in order to change branches.
git stash # Save modified and staged changes
git stash list # list stack-order of stashed file changes
git stash pop # write working from top of stash stack
git stash drop # discard the changes from top of stash stack
git stash
git pull
git stash apply
MERGE: merge
¶
manually resolve merge conflicts¶
- open the file and edit
- git add
- git commit
refer to How do I finish the merge after resolving my merge conflicts?
.gitignore
¶
忽略已 track 的文件¶
.gitignore
只能忽略那些原来没有被 track 的文件,如果某些文件已经被纳入了版本管理中,则修改 .gitignore
是无效的。那么解决方法就是先把本地缓存删除(改变成未track状态),然后再提交:
git rm -r --cached foo.txt
git add .
git commit -m 'update .gitignore'
see also: apply .gitignore
to committed files
PULL: pull
¶
in rstudio¶
在使用rstudio的git功能时,某次commit的时候,勾选了amend previous commit,然后push的时候就出错了
后来直接运行git pull
后,重新push,便解决了问题。
附上git pull的某篇博客git pull命令
Webhook配置¶
需要在腾讯云服务器上自动更新github pages的内容,于是采用webhook来实现。
npm install -g forever
forever start server.js
webhooks 响应特定分支的 push¶
参考Web Hooks - execute only for specified branches #1176@rtripault
提取 json 中的 ref
,在 do_POST()
中进行设置响应动作。
CHECKOUT: checkout
¶
- 撤销未被 add 的文件
git checkout -- file
- 撤销所有更改
git checkout -- .
- 从其他分支更新文件
git checkout master -- SOMEFILE
参考 Quick tip: git-checkout specific files from another branch
rewrite 时百分比的含义¶
表示相似性。
参考What does the message “rewrite … (90%)” after a git commit mean? [duplicate]
Travis CI¶
中文折腾为什么 Travis CI 中用 ‘*’ 号不会上传文件,最后还是指定了文件名。
刚刚找到解决方案 How to deploy to github with file pattern on travis?
简单说要加上
file_glob: true
列出另一分支的目录¶
git ls-tree master:dirname
cannot lock ref¶
参考 git pull时遇到error: cannot lock ref ‘xxx’: ref xxx is at (一个commitID) but expected的解决办法
另外试试 git gc
参考
CRLF
& LF
¶
双系统切换时,原先在 Win 处理,后来换到 Linux 处理,报出
CRLF will be replaced by LF.
设置 autocrlf
配置项:
- false 表示取消自动转换功能。适合纯 Windows
- true 表示提交代码时把 CRLF 转换成 LF,签出时 LF 转换成 CRLF。适合多平台协作
- input 表示提交时把 CRLF 转换成 LF,检出时不转换。适合纯 Linux 或 Mac
alias¶
本来想同时在一行命令中运行 git add .
和 git commit -m
,但是如果采用 ;
或者 &&
连接时都报错,
error: unknown switch `m’
顺带发现了 ;
和 &&
以及 ||
的区别,详见 Run multiple commands in one line with ;
, &&
and ||
- Linux Tips
;
: 无论第一个命令成功与否,都会运行第二个命令&&
: 只有当第一个命令成功运行,才会运行第二个命令||
: 只有当第一个命令失败后,才会运行第二个命令
后来发现这个,Git add and commit in one command,可以通过 git config
来配置 alias,
git config –global alias.add-commit ‘!git add -A && git commit’
则以后只需要调用
git add-commit -m ‘My commit message’
这跟在 .bashrc
中配置有异曲同工之妙!
标签¶
- 打标签
# annotated
git tag -a v1.0 -m "version 1"
# lightweight
git tag v1.0
# push to github
git push origin v1.0
# or
git push origin --tags
- 删除标签
# local
git tag -d v1.0
# github
git push origin :refs/tags/v1.0
详见 mobilemind/git-tag-delete-local-and-remote.sh
DIFF: diff
¶
color in git diff¶
在服务器上使用 git diff
,没有像在本地 Ubuntu 那样用颜色高亮出不同的地方,这个可以通过在 ~/.gitconfig
中进行配置,
[color]
diff = auto
status = auto
branch = auto
interactive = auto
ui = true
pager = true
参考 How to colorize output of git?
顺带发现,如果在 markdown 中复制 git diff 的 output,可以使用 diff
的代码块,会将更改的部分高亮出来。
color different words¶
by default, the differences are shown per sentences, add option --color-words
would only highlight the modified words
compare two files not indexed by git¶
use option --no-index
show line ending changes¶
git diff --color --word-diff-regex=.
which highlights all whitespace changes everywhere in lines.
公开私有仓库的部分代码¶
可能的解决方案:
- 新建一个 branch,以备公开
- public branch on private repo? a discussion
- 手动移动
- 如果只有小部分的代码需要公开,则这样在新 branch 上面需要删掉过多无关的文件夹。
- 以 release 形式发布,并不创建新仓库
- 通过脚本打包需要公开的代码
Submodule¶
~/github/techNotes/docs$ git submodule add git@github.com:szcf-weiya/TFnotes.git