介绍

本文将对git的基本原理和常用命令进行介绍。

Git是什么

Git(the stupid content tracker) 是一款免费、开源的分布式版本控制系统

Git的特点

  • 直接记录快照,而非差异比较
  • 几乎所有操作都是本地执行
  • 时刻保持数据完整性(SHA-1 算法)

Git对象模型

在Git系统中有四中类型的对象,所有的操作都是基于这四种类型的对象:

  • blob: 这种对象用来保存文件的内容。

  • tree: 可以理解成一个对象关系树,它管理一些tree和blob对象。

  • commit: 指向一个tree,它用来标记项目某一个特定时间点的状态。包括关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交

  • tag: 给某个提交增添一个标记。

Git工作流

本地仓库由 git 维护的三棵“树”组成:

  • 第一个是 工作目录,它持有实际文件

  • 第二个是 缓存区(Index),它像个缓存区域,临时保存你的改动

  • 第三个是 HEAD,指向你最近一次提交后的结果

Git存储

[renyl@localhost .git]$ tree -L 1
.
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── FETCH_HEAD
├── HEAD
├── hooks
├── index
├── info
├── logs
├── objects
├── ORIG_HEAD
├── packed-refs
└── refs

6 directories, 8 files
[renyl@localhost .git]$ tree -L 1

说明:

  • config: 这个是git仓库的配置文件

  • logs: 保存所有更新的引用记录

  • hooks: 可以设置特定的git命令后触发相应的脚本

  • HEAD: 这个文件指向了一个分支的引用

  • objects: 所有的Git对象都会存放在这个目录中,对象的SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名

  • refs: 这个目录一般包括三个子文件夹,heads、remotes和tags (git show-ref –heads)

  • index: 这个文件就是我们前面提到的暂存区,是一个二进制文件 (git ls-files –stage)

Git常用命令

init

  • Client:
mkdir my_project	# 创建仓库
cd my_project	 
git init	 
  • Server:
mkdir my_project2	# 创建裸仓库,裸仓库没有工作区
cd my_project2	 
git init –bare sample.git	 
chown –R git:git sample.git	 

clone

git clone https://github.com/kulong0105/mutt.git
git clone git@github.com:kulong0105/mutt.git

注:

  • https协议,默认情况下,只能在本地看到master分支,可以使用git branch命令查看
  • ssh支持的原生git协议,会自动在本地分支与远程分支之间,建立一种追踪关系(tracking), 所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的master分支自动追踪rigin/master分支。

checkout

git checkout -b dev              #创建新的dev分支
git checkout -b dev  origin/dev	 #创建远程origin的dev分支到本地。
git checkout  -- hello.c         #放弃工作区的修改,让这个文件回到最近一次git commit或git add时的状态。
git checkout $commit -- hello.c	 #回撤到$commit版本下hello.c文件的内容

branch

git branch                      #查看本地分支
git branch -r                   #查看远程分支
git branch -vv                  #产看本地和远程分支的track
git branch -d  my_branch        #删除分支
git branch -D  my_branch        #强行删除还未合并的分支
git branch -u origin/dev dev    #设置本地dev分支track远程dev分支(远程dev分支必须要存在)
git branch -d -r origin/test    #删除本地的远程分支

创建一个分支并在远程track:

git checkout -b br-2.0
git push origin br-2.0
git branch -u origin/br-2.0 br-2.0

创建远程upstream的分支并在origin进行track:

git checkout -b br-3.0 upstream/br-3.0
git push -u origin  br-3.0

config

git config --global user.name "kulong0105"             #global是全局参数,即该命令在这台电脑的所有Git仓库下都有用。
git config --global user.email kulong0105@gmail.com
git config --global color.ur true                      #显示颜色,会让命令输出看起来更醒目
git config --global alias.st status                    #配置别名
git config format.subjectPrefix "PATCH MY_REPO"
git config --list

add

git add hello.c     #添加文件到缓存区
git add -p          #允许你交互地选择你想要提交的内容
git add -u
git add --all

rm

git rm test.txt  	#删除文件

diff

git diff readme.c            #查看工作区和缓存区中的区别
git diff HEAD -- hello.c     #查看工作区和版本库里面最新版本的区别
git diff 0cbba12 -- hello.c  #查看工作区和版本库里"ocabba12"的区别
git diff $branch $filename   #查看branch分支与当前分支$filename文件的区别
Git diff $commit1~ $commit1
Git diff $commit1~ $commit2

show

git show v1.0                                     #查看标签信息
git show $commit:tests/xfstest                    #查看某个commit时的某个文件内容
git show --format=fuller --stat --patch -w -M     #-w表示忽略所有空格引起的变化

blame

show last modified each line of a file

git blame $filename

status

git status	 

log

git log --pretty=oneline --abbrev-commit
git log -n1 --pretty=format:'%cn %ce' $commit
git log -n1 --format=%s $commit
git log --author="Alex Kras" --after="1 week ago" --oneline
git log -p $filename       #不仅显示提交说明、提交者以及提交日期,还会显示这每次提交实际修改的内容
git log -L 10,15:$filename #允许指定文件中的某些行,有点像带焦点的"git log -p"

commit

git commit -a -s -m "add file"
git commit --amend
  • -a:表示所有文件。
  • -s:表示在patch的头部增加Signed-off-by。

remote

git remote add origin https://github.com/kulong0105/Test.git
git remote add origin git://github.com/kulong0105/Test.git
git remote -v
git remote prune origin	 #删除stale remote-tracking branches
git remote set-url origin $repo_url

说明:

  • 使用https协议:每次push都要输入用户名和密码。可以让git记录密码:使用命令git config --global credential.helper wincred
  • 使用git协议: 每次push不需要每次输入密码,因为是通过ssh key的方式来认证的

更新upstraem仓库到个人仓库:

$ git remote add upstream https://github.com/yeasy/docker_practice
$ git fetch upstream
$ git rebase upstream/master
$ git push -f origin master

ls-remote

$ git ls-remote
From ssh://git@192.168.20.14:24/yilong.ren/git-exercise.git
422fbe9a62c196cc06b22a5911ed14c72b6631d1HEAD
422fbe9a62c196cc06b22a5911ed14c72b6631d1HEADrefs/heads/dev
422fbe9a62c196cc06b22a5911ed14c72b6631d1HEADrefsrefs/heads/master
$

fetch

git fetch origin         #获取远程主机origin的所有分支更新(不进行合并)
git fetch origin master  #只获取远程主机origin的master分支更新

所取回的更新,在本地主机上要用”远程主机名/分支名”的形式读取, 比如origin主机的master,就要用origin/master读取,可以通过git branch命令查看

merge

git merge my_branch
git merge --no-ff -m "merge with no-ff" my_branch

合并my_branch分支到当前分支,终端会显示如”Fast-forward”这样的信息,表示直接把master指向dev的当前提交, 所以合并速度非常快, 但这种模式下,删除分支后,会丢掉分支信息

加上–no-ff参数后,表示禁用Fast forward模式,git会在merge时生成一个新的commit, 合并后的历史有分支,能看出曾经做过合并,而fast forward模式合并就看不出来曾经做过合并。

pull

git pull [远程主机名] [远程分支名]:[本地分支名]

git pull origin next	    #获取远程主机next分支的更新,再当前分支合并
git pull origin next:master #获取远程主机origin的next分支,并合并到本地master分支
git pull --rebase

rebase

git rebase origin/master
git rease -i

push

git push [远程主机名] [ 本地分支名]:[远程分支名]

git push origin :dev          #删除远程dev分支
git push -u origin master     #首次push时需要加参数-u,表示建立“追踪关系"
git push origin br-2.0        #推送本地br-2.0分支到远程
git push
git push -f                   #强制push到远程分支
git push origin v1.0          #默认情况下,不会主动推送标签到远程。该功能完成推送v1.0标签到远程
git push origin --tags        #一次性推送全部尚未推送到远程的本地标签

reset

git reset HEAD hello.c        #撤销缓存区的提交。
git reset --hard $commit_id   #根据commit_id恢复到某个版本库
git reset --hard HEAD~$NUM    #恢复到某个版本库,HEAD表示恢复到最新的版本库,NUM从1开始,若为1表示最新版本库的前一个版本。

revert

git revert $commit

reflog

git reflog	#查看版本库变更的历史

rev-list

git rev-list HEAD -- $filename
git rev-list -n3 $commit

stash

git stash                         #把分支当前工作现场"储藏"起来
git stash -p                      #允许你交互地选择你想要存藏的内容
git stash list                    #查看“储藏”的工作现场
git stash pop                     #恢复工作现场,且删除“储藏”的工作现场
git stash apply && git stash drop #分两步做达到同样的效果
git stash apply stash@{0}

tag

git tag v1.0 $commit_id	                            #给某个版本打标签
git tag	                                            #查看标签标签不是按时间顺序列出,而是按字母排序的
git tag -d v1.0	                                    #删除标签
git tag -d v2.0 && git push origin :refs/tags/v2.0  #删除远程标签

format-patch

git format-patch -1
git format-patch -s $commit_id       #-s表示从当前commit到head之前所有的commit(不包括当前commit)
git format-patch -n2 -s $commit_id   #表示从之前的某个commit到当前的commit(包括当前的commit)
  • 遵守: one thing one commit
  • 规范化msg:
    • 50-character subject line
    • 72-character wrapped longer description. This should answer:
      • Why was this change necessary?
      • How does it address the problem?
      • Are there any side effects?
  • Include a link to the ticket, if any

send-email

git send-email xxx.patch

am

Apply a series of patches from a mailbox

git am -xxx.patch

bisect

$ git bisect start
$ git bisect bad            # Current version is bad
$ git bisect good v2.3.0    # v2.3.0 was the last version tested that was good

filter-branch

如果想在git repo中完全删除一个意外提交的敏感文件或大文件,可以使用如下步骤: (https://help.github.com/articles/removing-sensitive-data-from-a-repository/)

$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' --prune-empty --tag-name-filter cat -- --all
$ git push origin --force --all
$ git push origin --force --tags   # In order to remove the sensitive file from your tagged releases
$ git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
$ git reflog expire --expire=now --all
$ git gc --prune=now

.gitignore文件

存放要忽略的文件, 当使用show/diff/clean等命令时, git就会自动忽略这些文件。

Git配置

[user]
    name =  Yilong Ren
    email = yilong.ren@sky-data.cn
[alias]
    lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
    lg2 = log --color --graph --pretty=format:'%h %ad | %s%d [%an]' --abbrev-commit --date=short
    sw = show --pretty=raw
    sw2 = show --format=fuller --stat --patch -w -M
    sw3 = show --format=fuller --stat --patch -w -M --word-diff-regex=.
    st = status
    co = checkout
    ci = commit
    br = branch
    unstage = reset HEAD
    pf = pull --ff-only
    type = cat-file -t
    dump = cat-file -p
[color]
    ui = true
    diff= true
[format]
    numbered = auto
    signoff = true
[push]
    default = simple
[diff]
    renames = true
[sendemail]
    smtpserver = smtp.sky-data.cn
    smtpencryption = tls
    smtpuser = yilong.ren@sky-data.cn
    smtppass = xxx

#   smtpserver = smtp.sky-data.cn
#   from = Yilong Ren <yilong.ren@sky-data.cn>
#   chairnreplyto = false
#   smtpserverport = 25
#   envelope-sender = yilong.ren@sky-data.cn

    to = yilong.ren@sky-data.cn
    cc = team_ci@sky-data.cn

学习链接