Git(一)- 日常操作,包含基于Git Server端的一些设置

1. Git常见QA问题

1.1. 错误1: terminal prompts disabled

通过go get -u xx/xx更新包时候,遇到fatal: could not read Username for 'https://xx.com': terminal prompts disabled 问题

原因是默认情况下,git会使用https进行clone,会提示要输入用户名和密码,推荐使用ssh免密clone,参考 https://golang.org/doc/faq#git_https

解决方案,可以通过git命令配置通过 ssh替代https进行代码clone: git config --global --add url."git@github.com:".insteadOf "https://github.com/"

上述处理,等同于配置~/.gitconfig,增加类似下述代码,有的注意http和https都要配置

[url "git@git.xxx.com:"]
  insteadOf = https://git.xxx.com/
[url "git@git.xxx.com:"]
  insteadOf = http://git.xxx.com/

2. 1 Git Server服务配置

2.1. 1.1 git用户创建

// root创建gitman账号+5(可以忽略)
useradd git
passwd git

// 登入gitman账号 & 初始化基本ssh环境
ssh-kengen

// 将需要访问git srv服务器的用户的公钥加入到受信认证清单
$ cat /tmp/id_rsa.john.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.josie.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.jessica.pub >> ~/.ssh/authorized_keys

2.2. 1.2 git空仓库初始化

// 在/srv/git仓库下初始化一个空仓库
$ cd /srv/git
$ mkdir project.git
$ cd project.git
$ git init --bare
Initialized empty Git repository in /srv/git/project.git/

// 本地Mac操作,初始化一个本地仓库,加入remote源,并推送到remote源
$ cd myproject
$ git init
$ git add .
$ git commit -m 'Initial commit'
$ git remote add origin git@gitserver:/srv/git/project.git
$ git push origin master

// 可以支持受信的用户clone访问了
$ git clone git@gitserver:/srv/git/project.git
$ cd project
$ vim README
$ git commit -am 'Fix for README file'
$ git push origin master

2.3. 1.3 git-shell安全设定

由于我们是useradd加入的普通账户,当前所有这些用户也可以登录服务器并以git用户身份获得Shell,我们可以通过git-shell命令代替bash或csh该帐户的登录shell:

// 加入git-shell到 etc/shells 中
$ cat /etc/shells   # see if git-shell is already in there. If not...
$ which git-shell   # make sure git-shell is installed on your system.
$ sudo -e /etc/shells  # and add the path to git-shell from last command

// 修改过git账户的默认shell为git-shell
$ sudo chsh git -s $(which git-shell)

// 在MAC端测试
$ ssh git@gitserver
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to gitserver closed.

// 防止git的使用账户,转发端口等行为,需要进一步在`authorized_keys`进行限制,加上`no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty` 选项设定
$ cat ~/.ssh/authorized_keys
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4LojG6rs6h
PB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4kYjh6541N
YsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9EzSdfd8AcC
IicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myivO7TCUSBd
LQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPqdAv8JggJ
ICUvax2T9va5 gsg-keypair

// 再次在MAC端测试
$ ssh git@gitserver
PTY allocation request failed on channel 0
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to ioio.cool closed.

3. 2 Git常用Tips

3.1. 2.1 命令行PS提示

// 克隆仓库
git clone https://github.com/magicmonty/bash-git-prompt.git ~/.bash-git-prompt --depth=1


// 加入到 ~/.bashrc:
if [ -f "$HOME/.bash-git-prompt/gitprompt.sh" ]; then
    GIT_PROMPT_ONLY_IN_REPO=1
    source $HOME/.bash-git-prompt/gitprompt.sh
fi

// 重载bash: 
. ~/.bashrc

3.2. 2.2 Submodule操作

参考:https://git-scm.com/book/zh/v2/Git-工具-子模块

3.2.1. 2.2.1 submodule 初始化添加
// 在已有的git仓库中使用子模块,add后会在当前目录下,新增一个名为DbConnector的库
$ git submodule add https://github.com/chaconinc/DbConnector

// git status查看,新增文件
.gitmoudules
DbConnector

// cat .gitmodules,该文件随版本控制,同时modules中的url应该可以被访问
// 其中的配置,也可以通过git config submodule.DbConnector.url newUrl来覆盖
[submodule "DbConnector"]
    path = DbConnecor
    url = https://github.com/chaconinc/DbConnector

// 在主仓库提交,git commit后,看到160000模式,该模式为特殊模式,即将提交项纪录,而非纪录为文件或文件夹
$ git commit -am 'added DbConnector module'
[master fb9093c] added DbConnector module
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 DbConnector
3.2.2. 2.2.2 含submodule模块项目clone
// 克隆含子模块的项目
$ git clone https://github.com/chaconinc/MainProject
...
// 进入项目,并查看到包含.gitmodules文件,同时相关gitmodules对应仓库文件为空
// 初始化子模块配置,并从该项目中抓取所有数据,并检出府项目中的提交
$ git submodule init
$ git submoduel update

// init+update上述两步可以合成一步(在项目clone后,执行这个)
$ git submodule update --init
// 如果子模块还有嵌套的子模块,需要加上--recursive
$ git submodule update --init --recursive

// clone+init+update,可以合成一步,即在clone一个含submodules项目适合,直接加入`--recurse-submoduels`参数
$ git clone --recurse-submodules https://github.com/chaconinc/MainProject

Tips: 为安全起见,如果 MainProject 提交了你刚拉取的新子模块,那么应该在git submodule update后面添加--init选项,如果子模块有嵌套的子模块,则应使用--recursive选项

3.2.3. 2.2.3 进入submodule目录,进行拉取更新操作
// 从子模块远端拉取上游修改
cd DbConnector
git fetch
git merge origin/master

// 回到主项目,执行diff查看更新差异,其他类型诸如log和short
// 可以通过git config --global diff.submodule diff来配置,配置后,可以在全局的.git/config配置中看到
git diff --submodule=diff

// fetch+merge上述两步,可以合成一步,完成子模块更新并更新
git submodule update --remote
3.2.4. 2.2.4 submodules模块中的url有变更的情况
// 如果不想每次都指定--recurese-submodules选项,可以通过`git config submodule.recurse true`
$ git pull --recurse-submodules 

// 若直线pull或者submodule更新失败,需要同步修改git submodules的URL
将新的URL同步到本地中
$ git submodule sync --recursive
$ git submodule update --init --recursive
3.2.5. 2.2.5 submodules模块处于游离状态

在主项目中,运行了git submodule update,从子模块仓库更新了子项目后,子仓库会留在游离HEAD状态,意味着本地没有工作分支跟踪改动,这样即使后续再子项目文件中做了修改,提交后也可能会丢失,因为没有合并到主分支中。

为此,需要先checkout到指定分支,拉取最新的远程工作分支,在合并本地子项目中的内容(可以先做commit或者stash)

$ cd DbConnector
$ git checkout master
$ git stash
$ git submodule update --remote --merge
$ git pop (解决冲突)
$ git add .
$ git commit -m 'commit message'

// 回到主项目,同步submodule提交的更新内容(需要加上--merge或者--rebase),否则会重置成为一个游离态的HEAD
// 若忘记加--merge或者--rebase,子模块目录变为游离分支后,重新checkout会到master分支即可
git submodule update --remote --merge

// 若子模块有更新,又没有提交子模块内容,同时又不小心在主模块提交了子模块的更新,这样其他人无法更新到子模块内容,因此需要
$ git push --recurse-submodule=check
// 这里可以通过 `git config push.recurseSubmodules check`,来避免遗忘参数
// 默认参数是on-demand,是他的默认行为
3.2.6. 2.2.6 submodules的技巧

foreach,每个子模块中任意命令

// 子项目均暂存
$ git submodule foreach 'git stash'
// 子项目+分支快速创建
$ git submodule foreach 'git checkout -b featureA'
// 主项目+所有子项目的差异
$ git diff; git submodule foreach 'git diff'

// 设置别名
$ git config alias.supdate 'submodule update --remote -merge'

3.3. 2.3 测试git账号连通性

ssh -T git@gitlab.com

3.4. 2.4 仅下载Github上的指定目录内容(转svn)

  1. 从浏览器URI中找到要下载的内容,比如https://github.com/computer-system-education/os-syllabi/tree/master/material/tsinghua-os-2019-spring
  2. tree/master转成trunk
  3. 转成svn checkout(稍微会等一会): svn checkout https://github.com/computer-system-education/os-syllabi/trunk/material/tsinghua-os-2019-spring

完整的URL格式说明:

  • 如果您对master分支感兴趣,请trunk改用。所以完整的路径是trunk/foldername

  • 如果您对foo分支感兴趣,请branches/foo改用。完整的路径看起来像branches/foo/foldername

    Protip:如果愿意,您可以svn ls在下载之前查看可用的标签和分支

4. 3 Git使用

4.1. 3.1 图示操作

4.2. 3.2 分支管理

// 拉取并切换到远程指定分支
git fetch origin hotfix-20151118-v1
git checkout hotfix-20151118-v1

// 本地创建一个新分支
git checkout -b newbranch

4.3. 3.3 merge合并

// 1. 切换到合并分支(即最终代码组合的的那个分支,假定为develop)
git checkout develop

// 2. 合并,并形成新的提交点
git merge feature-x

// 3. 合并过程中可能出现冲突,配置命令行冲突解决工具vimdiff
git mergetool --tool=vimdiff

4.4. 3.4 rebase变基

// 1. 创建一个特性分支&做开发修改
git checkout -b feature-x

// 2. 将feature-x内容重放到develop
git checkout develop 
git rebase feature-x

// 3. 可能存在冲突,解决冲突(三方合并)
[fix origin code] && git add .

// 4. 继续rebase
git rebase --continue
git rebase --skip  (忽略当次patch,并继续)

5. 4 丢弃修改

// 丢掉本地所有更新
git reset --hard

6. 5 日志内容查看

// 上次修改的
git log -2 --pretty=short

// 上次修改的内容
git log -p -1

// 看文件
git log --name-only

git log -5 --pretty=medium --name-only