git学习笔记

最近在做项目整改,接触到一些git的高阶用法,让我不得不多方搜集资料,恶补了一下git。

一、git原理

git的官方文档有非常详细的介绍git的底层实现,在此膜拜一下linus大神。

但是我觉得这一张图会更加清晰:

git学习笔记_第1张图片

  • 图中左侧为工作区,右侧为版本库。在版本库中标记为 "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  命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。

二、暂存区

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

你可能感兴趣的:(git,git,github)