Git tips

8.2

Three ways to exclude files

  • .gitignore applies to every clone of this repository

  • .git/info/exclude only applies to your copy (local)

  • ~/.gitignore applies to all repositories (local)

    git config --global core.excludesfile `~/.gitignore`
    

Remove untracked file

# Dry run
git clean -n

git clean
git clean -f

Add empty folder

.gitignore

# Ignore everything in this directory
*
# Except this file
!.gitignore

checkout accept theirs or accept ours

git checkout --ours -- <filename>
git checkout --theirs -- <filename>

Pathspec xxx did not match any files known to git

确保提交完代码或做好备份

# Paths are removed from the index
git rm -r --cached .
# Reset
git reset .

Git proxy

Enable and disable proxy for all or a specific URL

git config --global http.proxy 'socks5://127.0.0.1:1000'

git config --global --unset http.proxy

git config --global http.https://github.com.proxy 'socks5://127.0.0.1:1000'

git config --global --unset http.https://github.com.proxy

It's saved in ~/.gitconfig.

Git gui clients

图形化的 GUI 相对 CMD 更适合展示复杂的相互关联的信息。

Recover from add+reset file

cd .git/lost-found/other

grep -r 'content' .

git merge / git rebase

git pull 相当于 git fetch + git merge。那么git mergegit rebase有什么区别呢?两者都是对代码进行合并,具体的方式有所不同,最终合并后代码的提交记录也会不同。

fetch后的代码

- o - o - o - H - A - B - C (master)
               \
                P - Q - R (origin/master)

merge后的代码

- o - o - o - H - A - B - C - X (master)
               \             /
                P - Q - R -- (origin/master)

rebaes后的代码

- o - o - o - H - P - Q - R - A' - B' - C' (master)
                          |
                          (origin/master)

使用git pull可以启用rebase合并方式

# config
git config branch.<name>.rebase true

# once
git pull --rebase

git pull VS git fetch Vs git rebase

blame

查看文件每行或某几行最近改动的记录,显示 commit id

git blame [head] README.md
git blame -L 1,5 README.md

查看某几行的提交记录,显示 commit message

git log -u -L 1,5:README.md

log

查看包含某个改动的提交详情

git log -S "some change"

查看用户的提交,部分匹配

git log --author="Name"

# 当前用户
git log --author="$(git config user.name)"

查看某个文件相关的提交

git log -p filepath

Error: Permission denied (publickey)

Error: Permission denied (publickey)

git diff

查看 pull 后的更改

git diff @{1}

查看某个文件的更改

git diff commit-id file

查看特定提交的更改

git diff commit-id~ commit-id

git show commit-id

查看最近提交的更改

最近一次

git diff HEAD~..HEAD

默认和 HEAD 比较,可以省略。@是 HEAD 的缩写。

git diff HEAD~
git diff @^
git diff commit-id

最近两次更改的文件名和状态

git diff --name-status HEAD~2..HEAD

使用配置的图形化工具查看

git difftool HEAD~..HEAD

HEAD 后的符号 windows: ~, Linux: ^ or ~

查看未提交时的更改

git diff

git pull branch

git pull origin <branch>

Merge commits into the branch

git checkout <branch>
git cherry-pick <commit-id>

git clone branch

git clone --single-branch --branch <branch> <remote-repo>

Add empty folder

在该目录中添加 .gitignore文件。参考

# Ignore everything in this directory
*
# Except this file
!.gitignore

Reduce .git folder?

repack

重新打包内部文件

git repack -a -d --depth=250 --width=250

可以舍弃 commit history 的情况

确认不需要以往历史记录的情况,可以重新生成 master 分支。所有本地仓库都需要重新 clone。

参考

# 新建分支并提交
git checkout --orphan lastest_branch
git add -A
git commit -m "message"

# 删除并替换原 master 分支
git branch -D master
git branch -m master

# 上传,再从远程仓库下载,.git 文件夹会小很多
git push -f origin master

How to write git commit message

How to Write a Git Commit Message

Style, Content, Metadata 三个方面统一规范

  • Separate subject from body with a blank line
  • Limit the subject line to 50* characters
  • Capitalize the subject line
  • Do not end the subject line with a period
  • Use the imperative mood in the subject line
  • Wrap the body at 72 characters
  • Use the body to explain what and why vs. how

For example:

  • Refactor subsystem X for readability
  • Update getting started documentation
  • Remove deprecated methods
  • Release version 1.0.0

同步其它分支的文件

git checkout master

git checkout gh-pages README.md

回滚到某次提交

# 保留修改
git reset --soft <commit_id>

# 放弃修改
git reset --hard <commit_id>

# 更新远程仓库
git push -f

查看某个提交记录并返回

# 查看记录
git checkout <commit_id>

# 返回分支头部
git checkout <branch_name>

# 返回最近的分支
git checkout -

缓存修改

在一个分支上修改时,如果中途需要临时切换到另一个分支且还不能提交代码,可以使用缓存命令保留修改

# 缓存修改
git stash

# 添加注释 save
git stash save <message>

# 缓存特定文件
git stash push -m <message> file1 file2

# git checkout other_branch
# ...
# git checkout origin_branch

# 恢复修改
git stash pop # 生效后移除缓存
git stash apply # 生效后仍保留缓存

# 查看修改
git stash show -p stash@{0} --name-only

# 列出所有
git stash list

# 移除不要的 stash
git stash drop <id>
git stash drop 0
# 移除所有 stash
git stash clear

当前修改提交到另一个分支

改动在提交前和分支没有关系,

  • 未提交,可以直接切换分支后再提交。如果文件有冲突,需要stash

  • 已经提交,

    git log # commit-id
    git checkout branch
    git cherry-pick <commit-id>
    git checkout master
    git reset HEAD~1
    

删除文件

错误上传文件后,需要删除仓库中的文件

同时删除本地文件和仓库索引

git rm <file>

等价于

rm <file>
git add <file>

不再追踪已经提交的文件(加入.gitignore)

仅删除仓库索引,保留本地文件

# Modify .gitignore

git rm --cached <file>
git add <file>
git commit -m "Message"

根据 commit hash checkout 恢复相应的文件

Stackoverflow Answer

git checkout c5f567~ -- file/to/restore

未提交

git checkout -- file/to/restore

查看删除的文件

git log --diff-filter=D --summary

未提交

git ls-files --deleted

撤销已经 push 的提交

通过新增一个相反的提交,来抵消某次提交的效果

git revert <commit_id>
git push

syncing a fork

Github fork 过来的仓库如何更新呢?upstream 是原始远程仓库地址。

git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git

git fetch upstream

git checkout master

# 更新自己的本地仓库
git merge upstream/master

# 更新自己的远程仓库
git push origin master

merge, rebase, cherry-pick

假设需要合并 branch 到 master

merge,提交历史保持并行,适合远程和本地的合并

git checkout master
git merge branch

rebase,提交历史变成串行,适合本地合并

git checkout branch
git rebase master

cherry-pick,合并分支中某个指定的提交

git checkout master
git cherry-pick <commit-id-from-branch>

切换分支时,修改过本地文件会被覆盖的错误处理

参考链接

error: Your local changes to the following files would be overwritten by checkout

在切换回来时,能保留本地文件的修改,可以使用git stash

# on master
git stash save file-name

# do sth with branch
git checkout branch

git checkout master
git stash pop

查看某次提交修改的文件

git diff-tree --no-commit-id --name-only -r <commit-id>

查看某个文件或分支对应的提交

git rev-parse master
git rev-parse master:README.md

分支新建、合并、上传、删除

新建

git checkout -b new_branch

将 new_branch 合并到 master

git checkout master
git merge new_branch

上传

git push origin new_branch:new_branch

删除本地分支

git branch -d new_branch

删除远程分支

git push --delete origin new_branch

查看分支

查看本地

git branch

查看远程

git branch -r

# or
git ls-remote

查看所有

git branch -a

fatal: refusing to merge unrelated histories

GitHub 新建了仓库,并加入证书。同时本地仓库也单独作了提交。合并时会报错

git pull

fatal: refusing to merge unrelated histories

需要添加参数

git pull --allow-unrelated-histories

查找 Github 上 stars 最多的仓库

https://github.com/search?q=stars:>1&s=stars&type=Repositories

不跟踪加到代码库中的文件变化

添加file

git update-index --assume-unchanged file

撤销file

git update-index --no-assume-unchanged file

不同仓库间同步文件修改时间

参考 git-cache-meta

原理

先记录所有文件的修改时间到文件中,在目标目录中读取该文件来同步时间。

git_cache-meta.sh

#!/bin/sh -e

#git-cache-meta -- simple file meta data caching and applying.
#Simpler than etckeeper, metastore, setgitperms, etc.
#from http://www.kerneltrap.org/mailarchive/git/2009/1/9/4654694
#modified by n1k
# - save all files metadata not only from other users
# - save numeric uid and gid

# 2012-03-05 - added filetime, andris9

# Modify date
# \( -printf 'touch -c -d "%TY-%Tm-%Td %TH:%TM:%TS" %p\n' \) \

# Access date
# \( -printf 'touch -c -d "%AY-%Am-%Ad %AH:%AM:%AS" %p\n' \) \

: ${GIT_CACHE_META_FILE=.git_cache_meta}
case $@ in
    --store|--stdout)
    case $1 in --store) exec > $GIT_CACHE_META_FILE; esac
    find $(git ls-files)\
        \( -printf 'chown %U %p\n' \) \
        \( -printf 'chgrp %G %p\n' \) \
        \( -printf 'touch -c -d "%TY-%Tm-%Td %TH:%TM:%TS" %p\n' \) \
        \( -printf 'chmod %#m %p\n' \) ;;
    --apply) sh -e $GIT_CACHE_META_FILE;;
    *) 1>&2 echo "Usage: $0 --store|--stdout|--apply"; exit 1;;
esac

用法

源目录,记录文件修改时间

sh git-cache-meta --store

目标目录,应用文件修改时间

sh git-cache-meta --apply
📖