最近在做项目整改,接触到一些git的高阶用法,让我不得不多方搜集资料,恶补了一下git。
一、git原理
git的官方文档有非常详细的介绍git的底层实现,在此膜拜一下linus大神。
但是我觉得这一张图会更加清晰:
图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage/index),标记为 "master" 的是 master 分支所代表的目录树。
图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个"游标"。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。
图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下,里面包含了创建的各种对象及内容。
当对工作区修改(或新增)的文件执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。
当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。
当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
当执行 git rm --cached
当执行 git checkout . 或者 git checkout --
当执行 git checkout HEAD . 或者 git checkout HEAD
二、暂存区
git ls-files 默认显示的是暂存区中全部文件的路径
[***]$ git ls-files
host/.gitignore
host/driver/Makefile
host/driver/hmcl_drm.c
host/driver/hmcl_drm.h
git ls-files -s 显示暂存区的文件路径。
[***]$ git ls-files -s
100644 b3b89ef9ab45fce8bf350d32d9eac0f130cb27bc 0 host/.gitignore
100644 7f1ea0b0713929449eb8df1dd21c55578bb1d223 0 host/driver/Makefile
100644 bb62310c5868f30a60d7be3b50a16a47bbac49ed 0 host/driver/hmcl_drm.c
100644 e467500dfea140f980aa3caf80fcf61f6d600b06 0 host/driver/hmcl_drm.h
git ls-tree 查看目录树
[***]$ git ls-tree 6b6391aee8beef6b5de8cd358502902859683e0e
040000 tree 13497446c6f35c77b4edebb5a24f1c06e8ba2cd6 host
三、git filter-branch
发现这个命令好像就是用于改写暂存区的,所以好多修改历史记录,或者清楚历史记录的命令均用该命令完成。
如永久删除一个目录,在清理大文件时会用到:
参考这篇博文:
Git如何永久删除文件(包括历史记录) - shines77 - 博客园 (cnblogs.com)
git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch relative_path_of_folder' --prune-empty --tag-name-filter cat -- --all
四、如何迁移开源仓库到gerrit或gitlab
当我们把开源仓库的commit全部迁移到gerrit或者gitlab时,经常会报commit超过了max而无法上传。
可以使用如下脚本分批上传:(该脚本可以实现按批上传commit)
#!/bin/bash
# Adjust the following variables as necessary
REMOTE=origin
BRANCH=$(git rev-parse --abbrev-ref HEAD)
BATCH_SIZE=100
range=HEAD
# count the number of commits to push
n=$(git log --first-parent --format=format:x $range | wc -l)
# push each batch
for i in $(seq $n -$BATCH_SIZE 1); do
# get the hash of the commit to push
h=$(git log --first-parent --reverse --format=format:%H --skip $i -n1)
echo "Pushing $h..."
git push $REMOTE ${h}:refs/heads/$BRANCH --force
done
# push the final partial batch
git push $REMOTE HEAD:refs/heads/$BRANCH --force
今天在研究gerrit启动脚本的时候发现,gerrit竟然是通过git命令去读配置文件,真是妙呀。
记录一下哈哈:
gerrit启动脚本中有个函数:
get_config() {
if test -f "$GERRIT_CONFIG" ; then
if test "x$1" = x--int ; then
# Git might not be able to expand "8g" properly. If it gives
# us 0 back retry for the raw string and expand ourselves.
#
n=`git config --file "$GERRIT_CONFIG" --int "$2"`
if test x0 = "x$n" ; then
n=`git config --file "$GERRIT_CONFIG" --get "$2"`
case "$n" in
*g) n=`expr ${n%%g} \* 1024`m ;;
*k) n=`expr ${n%%k} \* 1024` ;;
*) : ;;
esac
fi
echo "$n"
else
git config --file "$GERRIT_CONFIG" $1 "$2"
fi
fi
}
然后我们看下是这么用的,比如我的gerrit配置文件在/usr/local/gerrit/gerrit.config
配置文件长这样:
[gerrit]
basePath = git
canonicalWebUrl = http://***
serverId = b424a265-dc62-4897-a9ff-220c2002e3a8
[container]
javaOptions = "-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance"
javaOptions = "-Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance"
user = root
javaHome = /usr/local/java/jdk1.8.0_181/jre
[index]
type = lucene
[auth]
type = LDAP
[ldap]
server = ldap://ip:389
username = it-pub
accountBase = OU=a,DC=b,DC=c
groupBase = CN=gerrit,OU=groupname,OU=a,DC=b,DC=c
accountPattern = (&(objectClass=person)(sAMAccountName=${username}))
groupPattern = (cn=${groupname})
accountFullName = displayName
accountMemberField = memberOf
accountEmailAddress = mail
[receive]
enableSignedPush = false
[sshd]
listenAddress = *:29418
[httpd]
listenUrl = http://*:80/
[cache]
directory = cache
[plugins]
allowRemoteAdmin = true
命令: git config --file /usr/local/gerrit/etc/gerrit.config --get container.javaHome
[root@gerrit-slave gerrit]# git config --file /usr/local/gerrit/etc/gerrit.config --get container.javaHome
输出:
/usr/local/java/jdk1.8.0_181/jre
[root@gerrit-slave bin]# git config --file /usr/local/gerrit/etc/gerrit.config --get-all container.javaOptions
输出:
-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance
-Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance
[root@gerrit-slave bin]# git config --file /usr/local/gerrit/etc/gerrit.config --get-all container.javaOptions|tr '\n' ' '`
> ^C
[root@gerrit-slave bin]# git config --file /usr/local/gerrit/etc/gerrit.config --get-all container.javaOptions|tr '\n' ' '
输出:
-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance -Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance
这是一篇笔记,用于记录自己对git的理解和学习历程:
找到一个中文手册:git rev-parse - [ Git中文开发手册 ] - 在线原生手册 - php中文网
近期用Jenkins调度任务时发现Jenkins是这样用git下载代码的。
Jenkins流水线定义了clone_code函数:
def clone_code() {
checkout([$class: 'GitSCM',
branches: [[name: 'develop']],
extensions: [],
userRemoteConfigs: [[credentialsId: 'ci-account',
url: 'ssh://gerrit.company.com:29418/project/test']]])
}
下载代码的日志打印如下:
The recommended git tool is: NONE
using credential ci-account
Fetching changes from the remote Git repository
> git rev-parse --resolve-git-dir /home/jenkinsci/jenkins-slaves/workspace/test@2/.git # 检查是否是有效的存储库或指向有效存储库的gitfile,并打印存储库的位置。如果是一个gitfile,那么将打印解析的实际存储库的路径
> git config remote.origin.url ssh://gerrit.company.com:29418/project/test # 添加远端仓库
Fetching upstream changes from ssh://gerrit.company.com:29418/project/test
> git --version # timeout=10
> git --version # 'git version 2.25.1'
using GIT_SSH to set credentials Account jenkinsci used to clone
> git fetch --tags --force --progress -- ssh://gerrit.company.com:29418/project/test +refs/heads/*:refs/remotes/origin/* # Fetch代码及tag
Checking out Revision 2a12bf17eaaf79c1728047d359caae137701ea89 (origin/develop)
> git rev-parse origin/develop^{commit} # timeout=10
> git config core.sparsecheckout # timeout=10
> git checkout -f 2a12bf17eaaf79c1728047d359caae137701ea89 # checkout到最新的commit点
Commit message: "test commit"
> git rev-list --no-walk 89468aa688be19e5404bdcfe86facb519a65cc84 # timeout=10