Bootstrap

【Git 学习笔记】第二章 Git 的配置(下)

2.5 几个配置示例

2.5.1 变基(rebase)与合并(merge)设置

默认情况下,在执行 git pull 时,如果本地分支的历史记录与远程分支的历史不同,则会进行合并提交。修改默认方式可使用:

# use rebase instead of merge: true / false
$ git config pull.rebase true

切换分支时,取消默认按合并(merge)的方式进行分支切换:

# never(default) lacal remote always
$ git config branch.autosetuprebase always

设置具体某分支在执行 git pull 操作时按 git rebase 进行:

# pattern: branch.<name>.rebase approach
$ git config branch.feature/2.rebase true

2.5.2 有效期设置

默认情况下,Git 将对未引用对象执行垃圾回收,并对为超过 90 天的 reflog 条目进行清理。一个 git 对象必须被其他对象引用,这个其他对象可以是一个 tree 树,一个 tag 标签,一个 branch 分支,或者其他 Git 内部簿记,如 stashreflog。有三个配置项可对有效期进行设置:

  • gc.reflogexpire
  • gc.reflogexpireunreachable
  • gc.pruneexpire
# To set a non-default expiry date on remote branches only
$ git config gc./refs/remote/*.reflogexpire never
# How long the reflog entries that are not a part of the current branch history should be available in the repository, 30d by default:
$ git config gc./refs/remote/*.reflogexpireunreachable "2 months"
# Set a date so git gc will prune objects sooner
$ git config gc.pruneexpire 3.days.ago

2.5.3 设置自动更正

出现笔误时 git 会自动提示:

笔误提示

可以设置自动更正的时间间隔,单位 厘秒(0.1秒):

$ git config help.autocorrect 5
$ git statis
WARNING: You called a Git command named 'statis', which does not exist.
Continuing under the assumption that you meant 'status'
in 0.5 seconds automatically...
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   another-file.txt
#

实测效果:(设为 3 秒后自动更正)

图 2-6 自动更正实测

2.6 设置 Git 别名

设置常见通用别名:

$ git config --global alias.co checkout 
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status

新增自定义别名:让某文件取消暂存

# pattern: git unstage <FILE>...
$ git config --global alias.unstage 'reset HEAD --'

# Test
$ echo "Something new" >> .\README.md
$ git unstage .\README.md
Unstaged changes after reset:
M       README.md
$ git unstage .\README.md
Unstaged changes after reset:
M       README.md
$ git st
On branch master
Your branch is behind 'origin/master' by 5729 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

实测截图:

在这里插入图片描述

更常见的是将自定义的 git 格式化后的日志形式存为一个别名:

$ git config --global alias.ll "log --pretty=format:'%C(yellow)%h%Cred%d %Creset%s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --numstat"

实测效果:

customized format log

git 也可以为自定义的外部脚本设置别名,此时别名内容前需要加感叹号 !。示例如下:

# add the line onto your ~/.gitconfig file
editconflicted = "!f() {git ls-files --unmerged | cut -f2 | sort -u ; }; $EDITOR 'f'"

这段脚本会打开预设的编辑器,其中包含由于合并或变基而处于冲突状态的所有文件。 该操作可以快速修复冲突并继续合并/变基。

测试如下:

$ git branch A 03f78fc
$ git branch B 9891497
$ git checkout A
Switched to branch 'A'
$ git merge B  
# conflicts found, and a list of conlicted files print out
# Resolve all these files and then run the script
$ git editconflicted
# Check the status
$ git st
# then commit to resolve merge
$ git ci

另一种创建 Git 别名的方法,是新建一个 shell 脚本,并使用名称 git-<your-alias-name> 保存。 设置文件权限为 可执行,并将其放在 $PATH 某一路径下,用命令行运行 git <your-alias-name> 看到相应结果。

查看当前定义的所有别名:

# Linux
$ git config --list | grep alias
# Powershell
> git config --list | where {$_ -match "alias"}

2.7 refspec 举例

设置 git 时,很难第一时间就想到 refspec,因为它通常是在执行 git 命令时被隐式调用。当执行仓库复制或添加远程分支时,.git/config 文件会有类似配置:

[remote "origin"]
  url = https://git.eclipse.org/r/jgit/jgit
  fetch = +refs/heads/*:refs/remotes/origin/*

其中 fetch 这行就包含了该仓库执行 fetch 时的 refspec

本例将在本地创建一个 bare 仓库(工作目录为空的仓库),以便示例代码执行 push 操作(否则会覆盖目标仓库的工作区(work area)和索引(index)。

# Create bare repo from Eclipse remote repo
$ git clone --bare https://git.eclipse.org/r/jgit/jgit jgit-bare.git
# Init local repo for demo
$ git init refspec-tests
Initialized empty Git repository in /Users/john.doe/refspec-tests/.git/
# Add remote origin for the local repo
$ cd refspec-tests
$ git remote add origin ../jgit-bare.git
# Enter the bare repo and rename "stable-*" branches into "stable/*"
$ cd ../jgit-bare.git && for br in $(git branch  -a | grep "stable-"); do new=$(echo $br| sed 's/-/\//'); git branch $new $br; done

这里发现两处笔误:

  1. Linux 环境下执行 sed 命令,替换为目标字符串的 / 需要转义,写作 \/,书中写为 /
  2. 远程分支的重命名,应该在 bare 仓库中进行,原书中少了一句 cd ../jgit-bare.git。反馈给作者核实后,两处笔误均已更正。
  3. 实测时由于与作者成书时的间隔已有三年多之久,这其间 jgit 库一直在更新;加上国内网络环境查,无法按照书中的命令在线同步 jgit 代码库。变通办法为:先同步 jgit 远程库到本地,然后重命名 stable 分支,再以此为基础创建 bare 仓库。如果本地库不预先拉取 jgit 的所有远程分支,拷贝 bare 库时将丢失所有远程分支。切记!

关于离线版的准备工作,操作如下:

# ... get local copy of jgit repo, named demo ... (by fortune)
# Rename branches: e.g. stable-1.2 --> stable/1.2
$ cd ./demo && for br in $(git branch  -a | grep "stable-"); do new=$(echo $br | sed 's/-/\//'); git branch $new $br; done
# clone bare repo based on local 'demo' repo
$ cd .. && git clone --bare ./demo jgit-bare.git
# ... (same as mentioned above)

再次强调

再次强调

再次强调

从本地仓库新建 bare 库时,数据源仓库的 所有本地分支 将作为 bare 库的远程跟踪分支(remote-tracking branch),被后续基于该 bare 库的其他衍生仓库查询出来。因此第 2 步中必须先将示例要用到的所有分支(即所有带 stable- 前缀的分支)签出。


示例一:只同步拉取 master 分支

# Edit .git/config file: heads/* --> heads/master; origin/* --> origin/master
[remote "origin"]
  url = ../jgit-bare.git
  fetch = +refs/heads/master:refs/remotes/origin/master

设置生效后,执行如下命令将只对 master 分支生效:

  • git fetch
  • git pull
  • git remote update origin

示例二:只同步带 stable 字样的分支

[remote "origin"]
  url = ../jgit-bare.git
  fetch = +refs/heads/master:refs/remotes/origin/master
  fetch = +refs/heads/stable/*:refs/remotes/origin/stable/*

示例三:设置推送到远程仓库的默认远程分支名称

添加默认推送名称:

[remote "origin"]
  url = ../jgit-bare.git
  fetch = +refs/heads/master:refs/remotes/origin/master
  fetch = +refs/heads/stable/*:refs/remotes/origin/stable/*
  push = refs/heads/develop:refs/remotes/origin/integration/master

创建本地分支并提交一个新版本:

# Create local branch 'develop', then add one commit
$ git checkout -b develop
Switched to a new branch 'develop'
$ echo "This is the developer setup, read carefully" > readme-dev.txt
$ git add readme-dev.txt
$ git commit -m "adds readme file for developers"

推送到远程分支(确认是否默认推送至 integration/master 分支):

$ git push

实测结果:

push to default branch test

几点说明:

  1. integration/master 分支仅在本地存在,远程仓库(bare 库)不存在。

  2. refspec 的格式为:<source>:<destination>

    1. fetch 操作:远程库为 source,本地库为 destination

    2. push 操作:远程库为 destination,本地库为 source

    3. refspec 中的 + 号,是指具体的引用写法可以被修改,即便不是快进式修改(即按时间顺序进行的修改,原文为:… to indicate that the ref pattern can be updated even though it isn’t a fast-forward update)

    4. refspec 不支持模糊匹配,因此不能写作:

      fetch = +refs/heads/stable*:refs/remotes/origin/stable*
      

      ref 的写法可以按命名空间设置,如:

      fetch = +refs/heads/stable/*:refs/remotes/origin/stable/*
      

      这也是为什么示例一开始就要重命名分支名称的根本原因;同时也从另一个角度说明,分支的命名最好按命名空间的方式进行设计。


关于上面提到的笔误,当时还联系了出版社求证,并有幸得到了作者的反馈,确实是写错了。原文如下:

Hi Anton,

Hope you are doing well.

This is what we have received from the author for the first query:

"I have walked through the section, and the missing \ in the command is correct.

Moreover, in order to archive what is described in the section, it is vital to mention that the renaming command (for br …) must be executed in the directory jgit-bare.git. If you follow the commands in the section strictly, you will be in the directory refspec-tests.

By changing the “for br …” command to “(cd …/jgit-bare.git && for br …)”, it will work. The parenthesis at the beginning and at the end are important."

(第二章完)

;