贡献者: addis
一个简单美观的 GUI 客户端是 GitHub Desktop(未必需要使用 github.com),然而许多 GUI 的功能比起命令行来还是要少得多,例如 GitHub Desktop 不支持 stage
,不支持把仓库或指定文件恢复到以前某个版本等。要安装 Git 命令行程序,Windows 用户可以下载 Git for Windows,Debian 或 Ubuntu 可以直接用 sudo apt install git
安装。Git for Windows 的 Git Bash 相当于 linux 的命令行,linux 常用的命令很多都有.
Git for Windows 的 Git bash 是不支持 symlink 的,symlink clone 下来以后都会变成文本文件(文本就是 symlink 指向的路径),所以还是建议在 WSL 中安装 Git 使用。但就算使用 wsl,checkout 出的 symlink 也可能只是一个文本文件,内容是目标的路径。此时可以手动使用 ln -sf $(cat 文件) 文件
去修复,也可以写一个脚本(见文末)。
但新的问题是,WSL 用 git 经常会有权限不足问题。所以 git bash 和 WSL 可以结合起来用。
如果 repo 非常大(十几 G 到几百 G),可以考虑用 Scalar
git config --global user.name "用户名"
.
git config --global user.email "邮箱"
邮箱决定 commit 的作者。
clone, pull
等。要让 Git 记住 token,用 git config --global credential.helper store
如果 token 变了,再用一次该命令即可,要测试是否成功可用 git pull
,如果成功则不需要 token。
git config --global credential.helper wincred
,然后可以用弹出来的 github 登录界面用账号密码登录,或者取消任何 GUI,然后在命令行用用户名和 token 登录。
git config --global --unset credential.helper
. 如果还不行的话只好把下文的所有 config 文件删除并重装 git。
git config --global core.autocrlf true
, 这样 commit 的时候会把所有 CRLF 换行变为 LF(子节 3 ), 而 checkout 的时候会把所有 LF 变为 CRLF. Linux 下设置 git config --global core.autocrlf input
, 这样 commit 的时候会把所有 CRLF 变为 LF, 而 chekout 的时候不做任何操作。如果这么设置了,GitHub Desktop 自动产生的 .gitattribute
文件就可以删掉了(因为解决的是同一个问题).
git config --global core.quotepath off
git config --global core.longpaths true
git config core.protectNTFS false
。但这么做可能仍会有一些问题(例如 windows 的文件夹中最后的。会自动删除)
git config --global core.editor "vim"
可以把用于 commit 的编辑器设为 vim
git config --global --add safe.directory "*"
git config --global http.postBuffer 2000000000
(字节)
git config --global core.safecrlf false
(不会改变功能)
git config --global http.sslverify false
忽略
git config --global pull.rebase false; git config --global merge.ff only
(注意这并不会影响 git merge
命令)
git init
可以把某个文件夹变为仓库,也可以现在 GitHub 上新建仓库再 git clone
到本地。注意多次使用并不会丢失什么东西(如果 .git 文件夹里面已经有数据)。
git status
用于查看仓库的当前状态,例如有哪些新增、改变、移动和删除的文件。注意 renamed: xxx -> xxx
不一定只是改了文件名,也可能有内容改动。
git diff --cached --name-status
可以显示已经缓存的状态,而不是重新扫描一次文件
git diff [文件或路径]
用于显示上次 commit
之后的改变,如果有文件被 add
, 则显示其 add
之后的改变。
git add [文件或路径]
用于把改变的文件添加到 staging area, commit 的时候只会 commit 这些文件。如果文件又发生了改动,需要再次 git add
.
git commit -am "标题"
git status
和 git diff
太常用,可以用 ctrl+r stat
或者 ctrl+r dif
等。如果不对就再按一次 ctrl+r
即可。这是命令行的功能不是 Git 功能。
git init
即可。该命令会在当前目录创建 .git 文件夹,里面包含所有历史信息。
git clone
时在后面加上 --depth N
选项可以只下载最后 N
次的 commit
git clone <url> newname
会把本地 repo 文件夹命名成 newname
.
开头的),git status
也不显示
git status
只会显示 worktree 和 index 之间的区别,而不一定是最新 commit 之间的(对于服务器上被 push 的 repo 而言)
git status 目录
可以只显示某个目录的 status
diff --staged --name-status
.git/config
. 用户的 .gitconfig
文件可以在 home 目录(~)找到(对当前用户生效), git config --global
的设置全部存在这里(只对使用的用户生效!)。系统的设置文件可以在 linux 的 /etc/gitconfig
或 Windows 的 /C/ProgramData/Git/config
中找到(对所有用户生效). repo 中的设置覆盖用户设置覆盖系统设置。
git config --list
可以列出当前目录的所有 config, 注意如果有设置多次出现,后出现的为准。git config --show-origin <xxx.xxx>
可以显示某个 config 所在的文件。
.gitattributes
文件也有用户(而不是 repo)的版本,可以用 ~/.config/git/attributes
文件设置。
git clone https://...
即可。git 会 clone 到当前目录下。
git status
发现所有的文件都改变了,用 git config --global core.fileMode false
即可。如果还是不行就去掉 --global
。
git
只会追踪 x
权限的变化(不确定是三个 x
还是一个 x
)。
git clone <folder1> <folder2>
把 folder1 中的 repo 复制到 folder2 中。但注意路径要用 /
而不是 \
. 如 /C/Users/addis/Documents/GitHub/cn2D/
git clone --no-checkout ...
选项可以只复制 .git 文件夹。
git:https://github.com
.
git status
.
git add <file>
把变化添加到 staging area(也就是 index). 用 git add <dir>/
添加目录,用 git add 目录
添加某个目录的变化,用 git add -A
添加所有变化。
git add -u
可以只 add 修改的和删除的文件,新文件不 add
.gitignore
的例子:folder/
忽略整个文件夹,folder/*.txt
忽略文件夹中的所有 txt 文件,folder/**/*.pdf
忽略 folder 文件夹和所有子文件夹中的 pdf 文件。
git config --global core.excludesfile ~/.gitignore_global
.gitignore
,用 git add -f ...
。
git update-index --assume-unchanged 文件
才能生效。
git diff
用于显示文件夹和 stage area 中的区别。git diff --staged
用于显示 stage area 和上一次 commit 的区别。git diff --name-only
可以只显示文件名。
git diff
的 --word-diff
选项可以显示行内的细微差别,当行比较长的时候使用(如 latex 段落)。github 中有时候也不会显示具体位置。如果还想更精确,用 --word-diff-regex=.
git commit
用于自动打开文本编辑器信息并 commit. git commit -m "message"
直接把 message 作为 commit 信息。git commit -a
可以自动 git add 所有变化的文件,也可以在后面再加 -m "message"
.
git rm <file>
. 这样做相当于手动删除文件,再 stage 该变化。如果文件已经 stage 了,用 git rm -f <file>
.
git mv <file_from> <file_to>
, 相当于 git rm <file_from>
再 git add <file>
.(在 Windows 中,要改变文件名的大小写必须这么做而不能直接给文件重命名)
git log
. 要显示每次 commit 的 diff, 用 git log -p
, 要只显示前两次,用 -2
. 用 --stat
可以显示基本统计(几个+几个-).
git log <hash>
查看某次 commit 以及更早。
git log ... --name-only [COMMIT_HASH]
只显示改变的文件名
git log --name-status [COMMIT_HASH]
显示改变的文件名每个文件是改变了还是新增还是删除等。
git log -- 文件或文件夹
可以只显示一个文件的历史,-p
和 --word-diff-regex
同样有效。
git log --grep=<pattern>
git log -S'字符串' -- 文件
可以在一个文件所有历史版本中搜索 字符串
,且该字符串在改 commit 中发生了改变(变成别的或者由别的变成?)。-G
也类似,但支持 regex 搜索。
git log --reverse --oneline -n 20
git log -- 文件
,
git fetch origin
,然后 git log origin/main
,后者只读取本地的 cache,不联网,所以要先 fetch
。
git commit --amend
可以覆盖上一次 commit (如果上一次 commit 忘了做什么事情).
git reset
可以 unstage 所有文件,git reset HEAD <file>
可以 unstage 一个文件。
git checkout -- <file>
可以撤销一个文件从上次 commit 后的变化(危险操作!). git checkout .
可以撤销所有文件从上次 commit 后的变化(危险操作!)但是他们不会影响 untracked files
git checkout folder
checkout 的是 index,也就是 git add 保存的地方。而对于服务器上被 push 的 repo,index 和 HEAD 的版本很可能是不同的。这时用 git checkout master -- ...
的才是最新版本。
git checkout <hash> -- <file1> <file2>...
可以将某个文件还原到某次 commit 的状态。同样也适用于文件夹。
git ls-files --deleted | xargs git checkout --
可以只把 git status
显示删除的东西恢复,假设没有东西被 git add
。
GIT_WORK_TREE=/some/path/ git checkout ...
可以把任何 git checkout
的内容输出到指定文件夹,注意这里的 GIT_WORK_TREE=/some/path/
只有本次有效
git checkout-index -a -f --prefix=/some/path/
可以把所有最新版本的所有内容从 .git
文件夹 checkout 到指定的文件夹,注意最后的 /
是必须的,-a
表示全部文件,-f
表示覆盖已有文件。如果要 checkout 某个文件,在后面用 -- 文件名
即可,要把 -a
去掉。这种方法不能 checkout 某个文件夹
git mv <old_name> <new_name2>
可以把某个文件重命名,包括只改变大小写(windows 中也可以,直接在文件夹中改变大小写是无效的)
git grep
可以在仓库中快速搜索关键词,grep --exclude-dir='.git'
则往往较慢。注意 git grep
不能搜到没有 add
的文件,除非加了 --no-index
。git grep -l
可以仅列出文件名(有些文件一行很长)。如果要只列出文件名,用 --git-grep
。
git remote
显示当前 repository 的 remote server shortname. 要显示网址,用 -v
.
git remote add <shortname> <url>
添加一个 remote server (一个 repo 可以有多个 server).
git remote rename <shortname> <shortname1>
可以重命名 remote。
git remote set-url <shortname> <new-url>
可以修改某个 remote 的 url。
git fetch <shortname>
从服务器上下载本地没有的数据。
git pull
的功能是 fetch 然后 merge.
git remote show <shortname>
显示 remote 的信息。
git remote rename <oldname> <newname>
可以修改服务器的名字。
git remote remove <shortname>
删除和服务器的连接(并不会删除服务器上的 repo)
git push
可以把当前 branch 的变化上传到服务器。
git clone
的是某个空 project, 第一次 commit 后上传用 git push -u origin master
git checkout <id>
可以把工作区间的所有文件修改到某次 commit 的状态,且不会影响当前的任何 commit. 这时会进入 'detached HEAD' 状态。
--amend
,建议不要),然后 checkout master
,git 就会提示是否用 git branch
创建一个分支。如果此时强行 checkout,就会导致新的 commit 变成一个 dangling commit,很不好。推荐的方法是 git checkout -b 新branch名
,就可以把刚刚的 commit 创建新分支,基于 detached HEAD。
git push <remote> <branch>
可以把分支推送到服务器
git checkout master
可以从 git checkout <id>
中恢复
git reset --hard
用于把 working directory 和 staging area 还原到最近一次 commmit 的状态。
git reset HEAD~1
等效于 git reset --mixed HEAD~1
, 用于撤销最近一次 commit,文件夹中的文件不改变,改变的文件变为 changed not staged 的状态
git reset --soft HEAD~1
用于撤销最近一次 commit,改变的文件变为 staged 的状态
git reset --hard HEAD~1
用于撤销最近一次 commit (本地). 如果已经推送到了服务器,继续用 git push origin HEAD --force
即可。
git stash
可以临时保存修改(不包括 untracked 的文件)git stash -u
包括 untracked 的文件。git stash pop
可以恢复这些修改(相当于 merge)。注意 pop
以后的文件并不一定和 stash 的时候一模一样,例如 stash 之后 pop 之前 pull 了一些变化,那么 pop 的时候相当于在这些变化的基础上 merge。
git stash list
可以里列出文件,git stash drop
可以丢弃这些文件。
git filter-branch -f --tree-filter 'rm -rf <path/file>' HEAD
注意这样做本身并不一定会使 repo 变小。如果要清空一个文件夹,就用 path/*
就好。特别注意这会把几乎所有的 commit 的 hash 改变。
git gc --aggressive --prune=now
会可以将 repo 缩小,PhysWiki 的 .git 大概能缩小到一半。鼓励经常使用 git gc
,但 --aggressive
选项会比较慢但也是安全的。gc 表示 garbage collection。该命令还可以让 .git
中的文件数量大大减小(copy 文件夹会快很多)
git gc
命令只对本地 repo 有效,其实最有效的貌似还是直接把本地 repo 删了再 clone 一次。但是注意检查是否有不小心被 .gitignore 屏蔽的文件没有在服务器上
git gc
用的内存可能会非常多(尤其是 repo 很大的时候),可以用(按需要调节,容量单位有 k,m,g
)。实测中,100GB 的数据库可以只用 4G 服务器内存和 4G 缓存,限制单线程,以及 50k window 完成。
git config --global pack.windowMemory 512m # 限制内存大小
git config --global pack.packSizeLimit 1g # 限制 pack 文件大小(默认单个文件)
git config --global pack.threads 2 # 使用多少线程压缩 pack
git repack --max-pack-size=1g -a
拆分成 1GB 的小块。这和先设置 pack.packSizeLimit
效果是一样的。
windowMemory
越大,就能做越多 delta。
git clean -f
可以清除 untracked file, -d
选项可以清除 untracked directory, git clean -n
显示将要清除的文件。git clean -fdX
会连被 ignore 的文件和文件夹一起删掉。
export LESSCHARSET=utf-8
git config --global core.quotepath false
显示 status 编码
git config --global gui.encoding utf-8
图形界面编码
git config --global i18n.commit.encoding utf-8
提交说明编码
git config --global i18n.logoutputencoding utf-8
输出 log 编码
在 linux 系统中,只需要一条设置,就是
git config --global core.quotepath false
gitk
可以弹出 GUI 显示每一次 commit 的文件目录,不需要 checkout 就可以看到。
git push
到一个 nonbare repo 一般是不允许的,除非这个 repo 设置了 git config receive.denyCurrentBranch warn
或者 git config receive.denyCurrentBranch ignore.
这时如果 push, 并不会改变 remote 的 working directory. (其实这正是我增量备份 .git 需要的!)
git config --local --bool core.bare false
,然后把所有文件数据库文件放到新建的 .git
里面,就可以把一个 bare repo 变成正常 repo。
git status
有时候会显示 not a git repo
,改成非 bare 即可。
.git
文件夹),那么后者就会被前者自动忽略。
git rebase -i HEAD~数字
把之前 数字
次 commit 显示到默认编辑器中(最新的 commit 在最下面),把连续几个 commit 前面的 pick
改成 squash
,然后保存退出。修改的这几次 commit 将消失不见,融入前一次的 commit 作为一次 commit。commit 信息也会自动融合,但会跳出另一个编辑器手动修改。修改之后,从改成 squash
的 commit 的上一次 commit 开始,后面所有 commit 的 hash 都会改变。
git fsck
可以检查数据库(.git
文件夹)是否出现错误(硬盘坏点等),但无法恢复,只能通过备份。如果检查完后输出了很多 dangling blob
是没有关系的,说明 git 保存了一些没有出现在任何 commit 中的多余文件。
git status
一般只是检查 tree 中的文件的修改时间是否有更新,而不会做一次完整的 checksum。如果需要检查是否有硬件 bit rot,可以先 touch 所有文件:find . -type f -exec touch {} +
,然后再 git status
。
git --git-dir=路径/.git --work-tree=tree的路径 命令
可以指定 .git
所在的文件夹以及 tree 的路径,然后执行 git 命令
,例如 git status
甚至 init
。注意两个路径选项都不能省略,无论当前在什么路径。
git ls-tree -d --name-only master
当前目录下的文件(git 数据库中的版本,默认是该 branch 的最后一次 commit)
git branch -D master
),然后把新 branch 重命名为 master。最后做一下 git gc
。
.git
数据库中每一次备份的文件目录。
git fsck
报告某个 pack 文件毁坏了,首先把它做一个备份。以下步骤笔者没试过。
git unpack-objects < path/to/packfile.pack
把 packfile 加压成 object
git repack -a
然后 repack,不会包含坏的 object
git index-pack -v path/to/packfile.pack
git tag
可以显示所有 tag。
git tag -a tag名
可以创建一个 annotated tag(推荐的 tag 类型)
git tag -d tag名
删除一个 tag
git push origin --tags
可以把 tag 的信息更新到上游
release名.tag.gz
和 release名.zip
在支持 symlink 的系统中(包括 NTFS 文件系统使用 WSL1;exFat 不支持)若 symlink 出现问题,可以用以下 bash 代码修复 repo 中的 symlink。该代码从 git 数据库中获取所有 symlink,如果检查到对应的文件实际上是一个文本,那就手动创建 symlink。
# must be called at the same directory of `.git` folder
# loop through all symlinks in repo
for file in \
$(git ls-files -s | egrep '^120000' | awk '$NF != "" {print $NF}');
do
if [[ ! -L "$file" ]]; then
# current file is not a symlink and not empty
printf "[fixing] $file -> $(cat $file)\n"
if [[ -s "$file" ]]; then
# file is not empty
ln -sf "$(cat $file)" "$file"
if [[ ! -e "$file" ]]; then # dest exist
echo "warning: destination doesn't exist."
fi
else
echo "file is empty!"
fi
fi
done
git branch <branch>
创造一个 new branch.
git checkout -b <branch>
创造一个 new branch 并切换到该 branch
git branch <branch> <hash>
用于从指定的 commit 创建 branch.
git branch -m 新名字
重命名当前的 branch。
git branch -d <branch>
用于删除某个 branch.
git branch -r
显示 remote 的所有 branch。
git branch --set-upstream-to=<remote>/<branch>
可以将某个 remote 作为默认,用于设置 pull 和 push 的默认 remote 和 branch
git branch -vv
可以显示所有本地的 branch 的默认远程 branch
git branch
显示本地 checkout 过的 branch。-vv
显示更多信息,包括 hash,remote,最后 commit 标题。前面有星号的是当前的 branch(git status
也会显示)。
git clone
下载一个完整的 repo 以后,是包含所有 branch 的,可以 git checkout <branch>
。但开始 git branch [-vv]
并不会显示,只会显示 checkout 过的。
git clone ... --depth = 1
后不能 checkout 其他 branch。因为它包含一个默认的 --single-branch
选项。默认下载的是 master
,如果要指定,用 git clone --depth 1 --branch <branch> url
git checkout <branch> -- <file1> <file2>...
可以把某个 branch 的指定文件还原到当前 branch。
git fetch <remote> <branch>
可以下载一个指定的 branch。注意不要用 pull
,会试图和当前 branch 合并。
git checkout <branch>
就在本地创建刚 fetch 的 branch。
git pull
只是从服务器下载当前 branch 的变化。
git diff branchA branchB -- 文件
用于比较两个 branch 中同一个文件。
git checkout <branch> -- 文件
可以从别的 branch 里面 checkout 一个文件到当前 branch。
git show-branch
可以查看已有的 branch
git checkout <branch>
把文件夹中的文件切换成某个 branch 中的文件。但是新的修改和 staging area 中的修改也会移植到该 branch 中。如果不想移植,就先 commit.
git switch <branch>
也可以用于切换 branch。
git checkout <name>
也可以用于从服务器上下载本地不存在的 branch.
git push <remote> --delete <branch>
用于删除 server 上的 branch.
git push --set-upstream <server> <branch>
用于把某个 branch 推送到 server 上并 track.
git clone -b <branch> --single-branch <url>
用于从 url 克隆一个指定的 branch.
git checkout -b <branch> <remote>/<branch>
新增一个 branch 并切换过去,并跟随指定的远程 branch。然后 git pull origin <branch>
可以同步。
git push
后面可以加某次 commit 的 hash 和 branch 如 abcd:master
,或者如 HEAD~1:master
.
git cherry-pick <hash> --no-commit
可以把任意 branch 的某次 commit 的改变应用到当前 branch,但是不 commit。也可以用 <hash1>...<hash2>
指定一个范围。
git blame -L 开始行号,结束行号 文件
可以查看某个文件的某个范围被哪次 commit 修改。
git merge <branch>
把某个 branch 并入到当前 branch 中并 commit。用 --no-commit
不 commit。
<branch>
有多个新的 commit,则 merge 后也会产生多个。若要合并为一个,用 git merge --squash <branch>
,默认不 commit(相当于把 <branch>
里面的文件直接替换到当前 branch)。
git merge --abort
可以恢复到 merge 以前。
master
和 draft
,在分叉后都有自己的若干 commit。那么在 git checkout draft
以后,如果用 git rebase master
,那么就相当于把这个分支产生后的所有修改都重新基于 master
的最新 commit,并把应用后的结果在该分支修改。如果发生冲突,git status
会提示一些文件 both modified
。这时如果要取消 rebase 就用 git rebase --abort
,如果要手动修改就打开这些文件编辑,会看到类似于一些这样的格式
<<<<<<< HEAD
modified by master
=======
modified by draft
>>>>>>> latest commit title of draft
同时显示了不同版本的内容,需要手动把这部分解决,例如只保留其中之一。注意这个显示的不同版本是当前分支第一个与 master 最新版的冲突的 commit,只要把这个解决,之后 commit 中对该文件的修改都会自动应用。解决所有冲突后对这些文件用 git add
标记冲突已经解决,再用 git rebase --continue
即可。
master
的最新 commit 产生的一样!查看该分支的 git log
会先给出 master 分支从头到尾的 commit,然后再列出该分支的所有 commit,但是这些 commit 修改的内容是基于 master 的最新 commit 的。