全局配置

1
2
3
4
5
6
7
8
9
10
git config --global init.defaultBranch main
# 配置用户名和邮箱
git config --global user.name "fl"
git config --global user.email "fl_6145@163.com"
# 保存密码
git config --global credential.helper store
# 忽略ssl证书错误 禁用ssl校验
git config --global http.sslVerify false
# 查看设置值
git config --list

git别名

1
2
3
4
5
git config --global alias.lg1 "log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all"

git config --global alias.lg2 "log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''%C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all"

git config --global alias.lg '!"git lg1"'

下载问题

1
2
3
4
git config --global http.postBuffer 104857600   # 100MB
# Git缓存值设置为5G 5242880000
git config --global http.postBuffer 5242880000
git config --global https.postBuffer 5242880000

乱码问题

1
git config --global core.quotepath false

储藏分支

  • git stash命令:将当前未提交的修改(即,工作区的修改和暂存区的修改)先暂时储藏起来,这样工作区干净了后,就可以切换切换到master分支下拉一个fix分支。在完成线上bug的修复工作后,重新切换到dev分支下通过

  • git stash pop命令将之前储藏的修改取出来,继续进行新功能的开发工作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 查看储藏记录列表
    git stash list
    stash@{index}: WIP on [分支名]: [最近一次的commitID] [最近一次的提交信息]

    # 标识储藏记录
    git stash save [stashMessage]

    # 取出最近一次储藏的修改到工作区
    git stash pop

    # 取出指定index的储藏的修改到工作区中 git stash apply stash@{index}
    git stash apply 0

    # 将指定index的储藏从储藏记录列表中删除
    git stash drop stash@{index}

    # 清空你所有的内容
    git stash clear

pull 分支

  • pull 分支报错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    $ git pull origin
    提示:您有不同的分支,需要指定如何协调它们。
    hint: You have divergent branches and need to specify how to reconcile them.
    提示:您可以通过在之前某个时间运行以下命令之一来做到这一点
    hint: You can do so by running one of the following commands sometime before
    hint: your next pull:
    hint:
    hint: git config pull.rebase false # merge
    hint: git config pull.rebase true # rebase
    hint: git config pull.ff only # fast-forward only 仅快进
    hint:
    提示:可以将“git config”替换为“git config——global”来设置默认值
    hint: You can replace "git config" with "git config --global" to set a default
    提示:首选所有存储库。你也可以传递——rebase,——no-rebase,
    hint: preference for all repositories. You can also pass --rebase, --no-rebase,
    提示:或命令行上的——ff-only,以覆盖配置的默认per
    hint: or --ff-only on the command line to override the configured default per
    hint: invocation.
    fatal:需要指定如何协调不同的分支。
    fatal: Need to specify how to reconcile divergent branches.
  • 解决

    1
    2
    3
    4
    5
    6
    # 默认将pull下来的代码与现有改动的代码进行合并
    git config --global pull.rebase false

    # 回退到合并之前的代码,在进行pull拉取最新代码
    git reset --hard c129513
    git pull origin

–rebase

  1. 多人使用同一个远程分支合作开发,在 push 代码的时候很可能出现以下问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ git push origin master

    # 结果如下
    To github.com:hello/demo.git
    ! [rejected] master -> master (fetch first)
    error: failed to push some refs to 'git@github.com:hello/demo.git'
    hint: Updates were rejected because the remote contains work that you do
    hint: not have locally. This is usually caused by another repository pushing
    hint: to the same ref. You may want to first integrate the remote changes
    hint: (e.g., 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
  2. 从输出结果中可以明显看出此时远程分支有新的 commit 未同步到本地,无法推送。此时我们一般会执行以下操作:

    1
    2
    $ git pull origin master
    $ git push origin master
  3. 确实 push 成功了,但是此时用 git log 查看提交记录:多出了一条 merge commit,这个 commit 就是在执行 git pull origin master 的时候自动生成的。如果多人多次如此操作,那么提交记录就会出现很多条这种自动生成的 merge commit,非常难看。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 结果如下
    commit 1aefef1a2bedbd3ebd82db8dcf802011a35a9888 (HEAD -> master, origin/master, origin/HEAD)
    Merge: 24cfa5c f318a05
    Author: hello <hello@qq.com>
    Date: Tue Jul 23 09:53:47 2019 +0800

    Merge branch 'master' of github.com:hello/demo

    commit 24cfa5c3ad271e85ff0e64793bf2bcc9d700c233
    Author: hello <hello@qq.com>
    Date: Tue Jul 23 09:50:06 2019 +0800

    feat: 新功能提交

    commit f318a05b1a4cbc0a6cf8d7dc7d3fb99cbafb0363
    Author: world <world@qq.com>
    Date: Tue Jul 23 09:48:20 2019 +0800

    feat: 其他功能提交
  4. 解决方案:要解决以上问题,不再出现自动生成的 merge commit,那么只要在执行 git pull origin master 的时候带上 --rebase 即可:

    1
    2
    3
    # 等价于:git fetch --all && git rebase branch
    $
    $ git push origin master
  5. 注意事项: 执行 git pull --rebase 的时候必须保持本地目录干净。即:不能存在状态为 modified 的文件。(存在Untracked files是没关系的),如果出现冲突,可以选择手动解决冲突后继续 rebase,也可以放弃本次 rebase

  6. 如果 A、B 同学修改了同一个文件。那么很有可能会出现冲突,当 B 同学来执行命令的时候会出现如下状况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    $ git pull --rebase

    # 结果如下
    remote: Counting objects: 6, done.
    remote: Compressing objects: 100% (6/6), done.
    remote: Total 6 (delta 1), reused 0 (delta 0)
    Unpacking objects: 100% (6/6), done.
    From gitlab.lrts.me:fed/gitlab-merge
    93a1a93..960b5fc master -> origin/master
    First, rewinding head to replay your work on top of it...
    Applying: feat: 其他功能提交
    Using index info to reconstruct a base tree...
    M one.md
    Falling back to patching base and 3-way merge...
    Auto-merging one.md
    CONFLICT (content): Merge conflict in one.md
    error: Failed to merge in the changes.
    Patch failed at 0001 feat:其他功能提交
    hint: Use 'git am --show-current-patch' to see the failed patch

    Resolve all conflicts manually, mark them as resolved with
    "git add/rm <conflicted_files>", then run "git rebase --continue".
    You can instead skip this commit: run "git rebase --skip".
    To abort and get back to the state before "git rebase", run "git rebase --abort".

    # 这种情况下,可以手动打开 one.md 文件解决冲突,然后再执行:
    $ git add one.md
    $ git rebase --continue

    # 也可以用 git rebase --abort 放弃本次 rebase 操作。
  7. 总结:多人基于同一个远程分支开发的时候,如果想要顺利 push 又不自动生成 merge commit,建议在每次提交都按照如下顺序操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 把本地发生改动的文件贮藏一下
    $ git stash

    # 把远程最新的 commit 以变基的方式同步到本地
    $ git pull --rebase

    # 把本地的 commit 推送到远程
    $ git push

    # 把本地贮藏的文件弹出,继续修改
    $ git stash pop

rebase用法

不要通过rebase对任何已经提交到公共仓库中的commit进行修改,

不要在master分支上rebase,在master分支上rebase就会出现游离分支。

正常做法是比如在dev分支开发,然后需要的话在dev分支rebase,然后再merge到master

  • https://git-scm.com/docs/git-rebase

  • 初始化数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    # 我们初始化一个项目
    git init


    ## 制造一些提交
    touch base.txt
    git add .
    git commit -m "add base"

    touch 1.txt
    git add .
    git commit -m "add 1"

    touch 2.txt
    git add .
    git commit -m "add 2"

    touch 3.txt
    git add .
    git commit -m "add 3"

    touch 4.txt
    git add .
    git commit -m "add 4"

    touch 5.txt
    git add .
    git commit -m "add 5"

合并commit

  • 合并多个commit为一个完整commit,把如下分支B、C、D三个提交记录合并为一个完整的提交,然后再push到公共仓库。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    git rebase -i  [startpoint]  [endpoint]

    -i 的意思是--interactive,即弹出交互式的界面让用户编辑完成合并操作,
    [startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit(注:该区间指定的是一个前开后闭的区间)。

    # 在查看到了log日志后,我们运行以下命令:
    * 09b85cb - (17 minutes ago) add 5 - XXX (HEAD -> main)
    * 0f89d77 - (17 minutes ago) add 4 - XXX
    * 6c82fb4 - (17 minutes ago) add 3 - XXX
    * 4c4e075 - (17 minutes ago) add 2 - XXX
    * eda0aa7 - (17 minutes ago) add 1 - XXX
    * e90bc6c - (17 minutes ago) add base - XXX


    # 合并最近二次提交 需要选择二次提交的前一个提交
    git rebase -i 6c82fb4
    # 或者
    git rebase -i HEAD~2
  • 编辑说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    pick 0f89d77 add 4
    pick 09b85cb add 5

    # Rebase 6c82fb4..09b85cb onto 6c82fb4 (2 commands)
    #
    # Commands:
    每一个commit id 前面的pick表示指令类型,git 为我们提供了以下几个命令:
    pick:保留该commit(缩写:p)
    reword:保留该commit,但我需要修改该commit的注释(缩写:r)
    edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
    squash:将该commit和前一个commit合并(缩写:s)
    fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
    exec:执行shell命令(缩写:x)
    drop:我要丢弃该commit(缩写:d)

    ## 合并add 5到add 4
    pick 0f89d77 add 4
    s 09b85cb add 5

    ## 保存后是注释修改界面,修改保存后查看日志
    PS C:\Users\sfuli\Desktop\test> git lg
    * 7290f75 - (31 minutes ago) add 4 5 - XXX (HEAD -> main)
    * 6c82fb4 - (31 minutes ago) add 3 - XXX
    * 4c4e075 - (31 minutes ago) add 2 - XXX
    * eda0aa7 - (31 minutes ago) add 1 - XXX
    * e90bc6c - (31 minutes ago) add base - XXX

将某commit粘贴到另一个分支上

  • 当我们项目中存在多个分支,有时候我们需要将某一个分支中的一段提交同时应用到其他分支中,就像下图:

  • 我们希望将develop分支中的C~E部分复制到master分支中,这时我们就可以通过rebase命令来实现(如果只是复制某一两个提交到其他分支,建议使用更简单的命令:git cherry-pick)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    git rebase   [startpoint]   [endpoint]  --onto  [branchName]

    [startpoint] [endpoint]仍然和上一个命令一样指定了一个编辑区间(前开后闭),
    --onto 的意思是要将该指定的提交复制到哪个分支上。

    # 为了让这个区间包含C(90bc0045b)提交,我们将区间起始点向后退了一步。C(90bc0045b)和E(5de0da9f2)
    git rebase 90bc0045b^ 5de0da9f2 --onto master

    # 此时所有分支的状态虽然此时HEAD所指向的内容正是我们所需要的,但是master分支是没有任何变化的,git只是将C~E部分的提交内容复制一份粘贴到了master所指向的提交后面,我们需要做的就是将master所指向的提交id设置为当前HEAD所指向的提交id就可以了,即:应该是这样:
    git checkout master
    git reset --hard 0c72e64

Git推送

  • 新建仓库

    1
    2
    3
    4
    5
    6
    7
    echo "# README" >> README.md
    git init
    git add README.md
    git commit -m "first commit"
    git branch -M main
    git remote add origin git@github.com:xxx/xxx.git
    git push -u origin main
  • 已经存在的仓库

    1
    2
    3
    git remote add origin git@github.com:xxx/xxx.git
    git branch -M main
    git push -u origin main # git push --set-upstream origin main

指定远程分支

1
2
3
# 删除关联的origin的远程库
git remote rm origin
git remote add origin https://gitee.com/xxxxxx.git

ssh密钥

  • RSA 经典且可靠,但性能不够理想。 OpenSSH 版本大于 6.5(2014 年的古早版本),就可以利用 Ed25519 算法生成的密钥对,减少你的登录时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    # ssh-keygen -t ed25519
    ssh-keygen -t ed25519 -C "fl_6145@163.com"
    mkdir -p ~/.ssh && cd ~/.ssh
    # 我在 GitHub
    ssh-keygen -t ed25519 -f my_github_ed25519 -C "me@github"
    # 我在 Gitee
    ssh-keygen -t ed25519 -f my_gitee_ed25519 -C "me@gitee"
    # 我在 GitLab
    ssh-keygen -t ed25519 -f my_gitlab_ed25519 -C "me@gitlab"
    # 我在企业
    ssh-keygen -t ed25519 -f my_company_ed25519 -C "email@example.com"

    # RSA
    ssh-keygen -t rsa -b 4096 -f my_id -C "email@example.com"
    # [-t rsa] 表示使用 RSA 算法。
    # [-b 4096] 表示 RSA 密钥长度 4096 bits (默认 2048 bits)。Ed25519 算法不需要指定。
    # [-f my_id] 表示在【当前工作目录】下生成一个私钥文件 my_id (同时也会生成一个公钥文件 my_id.pub)。
    # [-C "email@example.com"] 表示在公钥文件中添加注释,即为这个公钥“起个别名”(不是 id,可以更改)。

    # github上添加ssh key
    cat ~/.ssh/id_rsa.pub

    # 验证ssh key是否设置成功
    ssh -T git@github.com
    ssh -T git@gitee.com
  • 添加到配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    # 关于别名
    # Host 是别名,HostName 是真正的域名。
    # 得益于别名,你可以直接以别名访问地址。例如:
    # 无别名: git clone git@github.com:torvalds/linux.git
    # 有别名: git clone github:torvalds/linux.git
    # 本例中使用与域名一致的别名,以免错误的配置导致登录不上。

    # 关于代理
    # SOCKS 代理格式: ProxyCommand connect -S localhost:1080 %h %p
    # HTTP 代理格式: ProxyCommand connect -H localhost:1080 %h %p
    ## SSH 代理依赖外部程序,这里使用了 Git for Windows 同捆的 connect.exe。
    ## Linux 下使用该代理方式需要额外安装 connect-proxy。

    # 我在 GitHub
    Host github.com
    Hostname github.com
    # ProxyCommand connect -H localhost:1080 %h %p
    User git
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/my_github_ed25519

    # 我在 GitLab
    Host gitlab.com
    Hostname gitlab.com
    # ProxyCommand connect -H localhost:1080 %h %p
    User git
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/my_gitlab_ed25519

    # 我在 Gitee
    Host gitee.com
    Hostname gitee.com
    User git
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/my_gitee_ed25519

    # 我在企业
    Host example.com
    Hostname example.com
    Port 22
    User git
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/my_company_ed25519

代码行数统计

1
2
3
4
5
6
7
# 个人提交的代码行数统计
git log --author="username" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -

# 查看项目每个人提交的代码行数统计
git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done
# 查询所有用户的提交总次数
git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r

大文件清理

斩草除根式

  • 把根目录下面的.git文件夹删除,然后重建,简单粗暴,但如果需要git log历史记录的,请不要这样做

    1
    2
    3
    4
    rm -rf .git
    # 删除之后,该目录就不是一个git仓库了,因此需要重建
    # 输入rm -rf + github仓库地址 在 github 的对应的库中到 setting 删除库
    rm -rf https://github.com/xxx/xxx.git

逐个攻破式

  • 对仓库进行gc操作

    1
    git gc
  • 查看空间使用,size-pack 是以千字节为单位表示的 packfiles 的

    1
    2
    git count-objects -v
    du -ah .git/objects
  • 运行底层命令 git verify-pack 以识别大对象,对输出的第三列信息即文件大小进行排序.

    1
    2
    3
    4
    5
    6
    7
    8
    # 占用空间最多的五个文件
    git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5
    # 787d86fa5890fa037cca876b7b67d581d424b357 blob 1259085 1168273 16059429
    # 第一行的字母其实相当于文件的id,用以下命令可以找出id 对应的文件名:
    git rev-list --objects --all | grep 787d86fa5890fa037cca876b7b67d581d424b357

    # 使用 rev-list 命令,传入 --objects选项,它会列出所有 commit SHA 值,blob SHA 值及相应的文件路径,这样查看 blob 的文件名。
    git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')"
  • 查找文件

    1
    2
    3
    4
    # 查出文件提交commit记录
    git log --pretty=oneline --branches -- ${FILE_PATH}
    # 想要知道这条commit id所在的分支,可以使用以下命令
    git branch -a --contains <COMMIT ID>
  • 删除文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 遍历所有提交: commit多了会比较慢
    git filter-branch -f --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch <file>' --tag-name-filter cat -- --all

    filter-branch命令可以用来重写Git仓库中的提交
    -f , --force 假如遇到冲突也让git强制执行
    --prune-empty 选项告诉git,如果因为重写导致某些commit变成了空(比如修改的文件全部被删除),那么忽略掉这个commit。
    --index-filter参数用来指定一条Bash命令,然后Git会检出(checkout)所有的提交, 执行该命令,然后重新提交。
    git rm --cached --ignore-unmatch file 让git删除掉缓存的文件,如果有匹配的话
    --tag-name-filter 表示对每一个tag如何重命名,重命名的命令紧跟在后面,当前的tag名会从标注输入送给后面的命令,用cat就表示保持tag名不变。
    -- 紧跟着的-- 表示分割符,
    --all 表示对所有的文件都考虑在内

    # 指定commit修改
    git filter-branch -f --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch <file>' -- <commit_id>
  • 回收内存

    1
    2
    3
    4
    5
    rm -rf .git/refs/original/
    git reflog expire --expire=now --all
    git fsck --full --unreachable
    git repack -A -d # 将所有未被包含在一个pack的松散对象连结成一个pack
    git gc --aggressive --prune=now
  • 提交到远程仓库

    1
    2
    3
    git push --force [remote] master
    # 让远程仓库变小
    git remote prune origin

大仓库clone

1
2
3
4
5
6
7
8
9
10
11
# 浅尝clone: depth的参数控制着clone最近多少次的提交。
git clone --depth=1 http://xxx.git

# 拉取完整当前分支
git fetch --unshallow

# 追踪所有远程分支
git remote set-branches origin '*'

# 拉取所有远程分支
git fetch -v

Github更新Fork仓库

  • 首先进入到仓库的目录,进入Pull requests 下面,点击 new pull request 创建一个合并请求

  • 根据箭头的方向,把要合并的仓库设置为自己的仓库( base repository 是你自己的仓库和分支 head repository 是你fork来源的仓库和分支 ):

  • 点击Create pull request,填写合并概要:

  • 点击确认合并:

一键更新同一目录下的多个仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# vim ~/git_pull_Batch.sh
#!/bin/bash

function showGreen(){ echo -e "\033[32m $1 \033[0m"}
function showBlue(){ echo -e "\033[36m $1 \033[0m"}
function showYellow(){ echo -e "\033[33m $1 \033[0m"}
function showWhite(){ echo -e "\033[37m $1 \033[0m"}

function traversal_dir(){
for sub_dir in `ls $1` #通过 ls root_dir 遍历出子目录,装入子目录 sub_dir 中
do
dir=$1"/"$sub_dir #将根目录 $1 与子目录 sub_dir 拼接成完整的目录
if [ -d $dir ] #判断:是目录的继续下一步
then
cd $dir
showBlue $dir
showGreen 'git pull '$sub_dir
git pull
echo #打印空行
else
showYellow $dir
echo #打印空行
fi
done
}

root_dir="N:\Desktop\qnit" #定义根目录,即项目 project 的上级目录。例如:root_dir/project/.git
traversal_dir $root_dir