程序人生 A log of my life

Git笔记

一图胜千言

获取

git clone git@github.com:liuzhongshu/liuzhongshu.github.com.git

如果网速慢,可以加代理 -c http.proxy="http://127.0.0.1:1080", 这个是针对http协议,如果clone的是git url

初始化远程仓库

这是很常见的操作,比如在github上已经创建了一个仓库,要将本地的目录上传上去,如果本地目录还不是库,先做以下操作

cd <localdir>
git init
编辑好.gitignore文件
git add .
git commit -m 'message'

然后将远端地址加上并同步,origin只是一个标签,可以任意取,-u会把这个信息记住,下次只需要git push就可以了。

git remote add origin <url>
git push -u origin master

url通常有两种,一种是https格式,一种是ssh格式,ssh格式需要事先在服务器端配置好ssh key,好处是以后都不需要输入账号密码了。

日常操作

可以参考几种工作模式,比如gitflow, github flow, gitlab flow,通常个人开发者使用github flow就可以了,复杂情况下需要使用gitlab flow,但总的来说基础是下面几个命令:

#创建一个分支:
git checkout -b fix-xx master
#修改结束后,合并到master分支:
git checkout master
git merge --no-ff fix-xx
git tag -a xx
# 需要的话删除分支,如果分支没有合并,可以用-D强制删除
git branch -d fixbug-xx

上面的-no-ff的作用,可以用下面一张图说明:

更多分支操作

#查看远端分支
git branch -r

本地操作

  • 添加所有修改,包括删除的文件git add . -A
  • 查看修改情况git status
  • 提交到本地git commit -m "comment"
  • 取消,下面两条都可以,暂存区域会取消,–hard会将修改的文件恢复到上次commit状态。
git reset
git reset --hard
  • 比较,下面三条依次用最新版本,上次版本,上上次版本和本地进行比较
git diff 
git diff HEAD~
git diff HEAD~2

更高级一点的用法,调用外部工具比较一个特定版本(用hash)的修改:

git difftool c1ac~ c1ac
#比较两个分支
git difftool branch1..branch2

可以加-d表示目录比较,更方便(而且可以把beyond compare设置为difftool)。

撤销

和svn不一样,git里本地的commit,如果没有push,是可以撤销的,方法很简单,比如撤销最后一次修改

git reset --soft HEAD~
git commit -m 'comment'

reset有三种模式 –soft,–mixed, –hard 其中–mixed是缺省模式,三者差异在于

  • –soft 不丢失本地修改,撤销的东西都合并在暂存区域,用来合并几次commit到一个commit最合适。
  • –hard 比较危险,本地没有提交的修改和撤销的修改全部丢弃。
  • –mixed 所有撤销的内容和本地修改合并到workcopy。

历史

git log可以很容易看其他分支的历史,只需要加上分支名称,比如git log feature1,如果只想看这个分支上的修改,不包括更早的历史,可以后面用..的方式,比如feature1是从master拉出来的,可以用

git log master..feature1

release notes

通过git log自动形成release notes是个不错的主意

git log -5 --pretty=format:"%s"

GUI工具

  • 很多人低估了内置的gui,使用git gui可以方便调出,在查看log和diff时,还是比较方便的,如果大部分文件是utf-8编码,可以设置下面这个全局设置,防止diff时乱码
git config --global gui.encoding utf-8

diff和merge工具

git可以配置调用外部diff工具,通常我习惯用beyond compare,可以在~/.gitconfig下增加以下配置

[diff]
    tool = bc3
[difftool]
    prompt = false
[difftool "bc3"]
    cmd = \"c:/program files (x86)/beyond compare 3/BCompare.exe\" \"$LOCAL\" \"$REMOTE\"
[merge]
    tool = bc3
[mergetool]
    prompt = false
    keepBackup = false
[mergetool "bc3"]
    cmd = \"c:/program files (x86)/beyond compare 3/BCompare.exe\" \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"
    trustExitCode = truecss

密码

在使用github时每次都输入密码是很麻烦的事,两个方法:

  • 可以设置git config credential.helper store之后就可以缓存账号密码了。
  • 如果服务器端支持ssh协议,可以把远端地址迁移到ssh协议上,这样:
git remote set-url origin git@xxx.git

或者clone的时候就使用ssh地址(非https地址)

换行处理

git缺省会自动做换行处理,我发现在某些情况下会导致问题,比如使用vue init一个github库时,bat文件没有正确转换回dos格式,所以我一般:

git config --global core.autocrlf false

log里的中文乱码

不记得从哪个版本的git开始,windows下看git log中文总是乱码,需要设置一个环境变量

LESSCHARSET=utf-8

hook

我最常用的一个hook,是自动push(会丢失git reset这个后悔药),在.git\hooks下放一个post-commit文件,内容为:

#!/bin/sh
git push origin master

可惜的是这个hook文件没有入库,导致每次clone后这个hook文件都要手工重建,替代方案是git有个全局配置,设置

git config --global init.templatedir "~/.git_template"

然后在~/.git_template/hooks下面放上脚本就可以让以后的新库也有这个文件,但还是解决不了新clone的问题,git2.9以后版本引入新的配置

git config --global core.hooksPath /path/to/my/centralized/hooks

勉强算是部分解决了这个问题(但很不灵活),挺遗憾,为什么hook不入库呢~

git库为什么这么大

可以用下面的命令找到历史上的大文件

git rev-list --all --objects | \
    sed -n $(git rev-list --objects --all | \
    cut -f1 -d' ' | \
    git cat-file --batch-check | \
    grep blob | \
    sort -n -k 3 | \
    tail -n40 | \
    while read hash type size; do 
         echo -n "-e s/$hash/$size/p ";
    done) | \
    sort -n -k1

删除历史文件

要从库中删除历史上的大文件,可以用bfg.jar,很容易

git clone --mirror git://example.com/some-big-repo.git
java -jar bfg.jar --strip-blobs-bigger-than 100M some-big-repo.git
cd some-big-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push