Git原理介绍,优雅的使用Git

git是软件开发版本控制系统,类似linux文件系统的实现(当然比linux文件系统简单)。

Git对象

Git是如何将文件进行存储?如何将相同文件不同提交进行分割?都依赖与Git对象

  • repository(Tree)
  • Tree对象(多commit)
  • Commit对象(Blob)
  • Blob对象(存储)

举例说明

初始化Git

1
2
3
mkdir gittest
cd gittest
git init

查看目录结构tree .git

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
├── heads
└── tags

  • branches - 新版本不在使用
  • config - Git项目特有的配置
  • description - GitWeb 程序使用
  • HEAD - 文件指向当前分支
  • hooks - 客户端或服务端钩子脚本
  • info - .gitignore 文件中管理的忽略模式 (ignored patterns) 的全局可执行文件
  • objects - Git对象存储目录
  • refs - 目录存储指向数据 (分支) 的提交对象的指针

添加文件到Git

添加: echo 'version 1' > test.txt git add test.txt

查看: find .git/objects -type f 结果如下:

1
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30

查看对象类型: git cat-file -t 83baae61804e65cc73a7201a7252750c76066a30 结果如下:

1
blob

查看对象内容: git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 结果如下:

1
version 1

提交文件到Git

提交: git commit -a -m "firt commit"

查看: find .git/objects -type f 结果如下:

1
2
3
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects/6d/c9727a5974bb610f81c22914081d7e6373ae77

objects目录新生成了两个对象,这两个对象类型内容是什么呢?我们来看下

查看类型:git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579

1
tree

查看内容:git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579

1
100644 blob 83baae61804e65cc73a7201a7252750c76066a30	test.txt

查看类型:git cat-file -t 6dc9727a5974bb610f81c22914081d7e6373ae77

1
commit

查看内容:git cat-file -p 6dc9727a5974bb610f81c22914081d7e6373ae77

1
2
3
4
5
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author jerrylou <gunsluo@gmail.com> 1473652081 +0800
committer jerrylou <gunsluo@gmail.com> 1473652081 +0800

firt commit

综上git存储都是通过blob,commit,tree对象。blob存储文件提交内容,commit存储操作提交信息,tree存储指向blob的指针。

结构

Git repository是很多不同commit的集合,是有向无环图。如下:

1
2
3
A---B---C---D---E---F--- master
\ / \
G------H I---J--- feature

merge和rebase

新的特性分支feature上有F,G两个commit。我们项将feature上的改动同步到master,可以feature分支merge到master。将得到如下:

1
2
3
A---B---C---D--- master
\ /
F------G----- feature

我们会发现D节点有两个父节点C和G,经常做merge操作会导致无法得到正确的修改历史。

使用rebase,得到如下:

1
2
3
A---B---C---D------ master
\
F---G---feature

不用担心修改历史

命令

Alt text

  • git clone - 下载git源码
  • git pull –rebase origin master - 同步远程master分支到本地
  • git push origin master - 提交当前分支到远程master分支
  • git stash [pop] - 换成修改commit
  • git reset –hard {commit} - 恢复到commit提交点
  • git reflog - 查看修改log,可用于恢复数据
  • git commit –amend - 撤销最后一次提交
  • git checkout {文件名} - 撤销对文件的修改

解决冲突

当做了新的功能准备提交代码时,第一件事同步要合入分支代码(如dev)

  1. git pull –rebase origin dev
  2. 提示冲突先修改冲突文件保证代码正确
  3. git add 冲突文件
  4. git rebase –contunie
  5. 如还有冲突,跳到第二步,直到无冲突

长时间不rebase主分支容易发生冲突。

我的.gitconfig配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
[alias]
st = status -sb
br = branch -vv
ds = diff --staged
standup = log --since '1 day ago' --oneline --author sebastian@kusnier.net
lastweek = log --since '1 week ago' --oneline
ci = commit
amend = commit --amend -C HEAD
undo = reset --soft HEAD^
co = checkout
df = diff
dc = diff --cached
lg = log -p
lol = log --graph --decorate --pretty=oneline --abbrev-commit
lola = log --graph --decorate --pretty=oneline --abbrev-commit --all
l = log --pretty=oneline -n 20 --graph
graph = log --graph --pretty=format':%C(yellow)%h%Cblue%d%Creset %s %C(white) %an, %ar%Creset'
ls = ls-files
g = grep -I
vd = difftool -y -t gvimdiff
p = !"git pull; git submodule foreach git pull origin master"
undopush = push -f origin HEAD^:master
# Credit an author on the latest commit
credit = "!f() { git commit --amend --author \"$1 <$2>\" -C HEAD; }; f"

# Show files ignored by git:
ign = ls-files -o -i --exclude-standard
[user]
name = jerrylou
email = gunsluo@gmail.com
[core]
editor = /usr/local/bin/vim
excludesfile = /Users/jerrylou/.gitignore_global
pager = cat
autocrlf = input
[difftool "Kaleidoscope"]
cmd = ksdiff --partial-changeset --relative-path \"$MERGED\" -- \"$LOCAL\" \"$REMOTE\"
[diff]
tool = Kaleidoscope
[difftool]
prompt = false
[mergetool "Kaleidoscope"]
cmd = ksdiff --merge --output \"$MERGED\" --base \"$BASE\" -- \"$LOCAL\" --snapshot \"$REMOTE\" --snapshot
trustExitCode = true
[mergetool]
prompt = false
keepBackup = true
[merge]
tool = Kaleidoscope
log = true
summary = true
[difftool "sourcetree"]
cmd = opendiff \"$LOCAL\" \"$REMOTE\"
path =
[mergetool "sourcetree"]
cmd = /Applications/SourceTree.app/Contents/Resources/opendiff-w.sh \"$LOCAL\" \"$REMOTE\" -ancestor \"$BASE\" -merge \"$MERGED\"
trustExitCode = true

参考