Part I: Git 工作结构
A. 结果区
最终的 commit 结果
B. 暂存区/stage
C. 工作区
本地工作环境
Part II: 常用命令
A. 工作区
-
在本地建立 git 管理的仓库
git init git_project
cd git_project
-
工作区向暂存区添加文件
git add file_name // 添加编辑或者删除的文件,不添加新文件 git add -u // 添加新文件和编辑过的文件不包括删除的文件 git add . // 添加所有内容 git add -A .
-
现有状态查询
git status
-
更改文件名,删除文件
-
删除
git rm file_name
-
更改文件名
git mv old_name new_name
- 这行命令等价于
// 本地改名 mv old_name new_name // 添加改名后文件 git add new_name // 删除旧文件 git rm old_name
-
-
分支操作
// 查看分支数 git branch -v // 查看所有分支数,包括本地和远端 git branch -av // 创建新分支并且切换到这个新分支 git checkout -b new_branch_name // 切换分支 git checkout branch_name // 查看分支位置 cat .git/HEAD $ ref: refs/heads/branch_name // 删除分支 git branch -d branch_name git branch -D branch_name //完全删除
-
比较命令 git diff
// 比较工作区和暂存区的区别;会比较所有文件的区别; git diff // 比较两次 Commit 的区别 git diff Commit_id1 Commit_id2 // 比较分支的区别 git diff branch1 branch2 // 可选参数 --filename ,只比较指定文件的区别 git diff branch1 branch2 --filename /* * HEAD^ = HEAD~1; HEAD^^ = HEAD~2; 以此类推 * HEAD^ 表示 HEAD 上一层的 Commit_id; */ git diff HEAD HEAD^ /* * 比较暂存区和 HEAD 文件的区别 * 我们修改了某个文件,并且 git add 到暂存区,就可以用这个命令 * 来查看我们的修改的地方; */ git diff --cached //
-
Scroll Back
git add
和git checkout --file
是逆操作(工作区和暂存区)// 将 file1 的内容恢复成暂存区的内容;相当于放弃上一次 git add 之后所有的修改 git checkout --file1
B. 暂存区
-
将暂存区的修改向远端 git commit
// -m: commit 原因,是必选参数 git commit -m'commit reason' // 修改最近一次的Commit的 git commit reason git commit --amend
-
查看版本历史
// 以 oneline 方式查看历史 git log --oneline // 以 graph 方式查看; 比 oneline 更加直观 git log --graph // 查看最近3次的历史,次数可变 git log -n3 --oneline // 查看所有分支历史 git log --all git log --all --graph
-
reset 指令
git reset
和git commit
起到相反的作用;(暂存区和结果区)// 将暂存区恢复成 HEAD 指针位置,上一次 Commit 位置; // 相当于放弃这之后的所有修改;HEAD 也可以换成其他 Commit_id; git reset HEAD // 部分恢复; 如只将 file1 恢复成 HEAD 状态; git reset HEAD --file1 // 在工作区误删了文件,需要从结果区重新恢复; // HEAD 可以换成其他的 Commit_id git reset --hard HEAD
注意:git reset --hard Commit_id
会回退到 Commit_id 这个位置,即 HEAD=Commit_id ;
C. 结果区
Part III. .git 文件夹结构
-
.git/refs/heads
- 下面有以分支名命名的文件,如 master 等。保存的内容为此分支下最后一次 commit 的 hash 值;
-
.git/objects
-
下面是每次 commit 产生的 hash 值的前两位命名的文件夹,每个文件夹下有以剩下的 hash 值命名的文件;每个文件代表了一种连接关系。
-
E.g. Git/doc/readme 这样一个文件结果,如果在 objects 中存储的话,要有 4 个 hash 值文件;
- commit 是当前整个存储结构的快照(snapshot),文件名如 2a ;存储type = tree;
- 2a 指向 Git/doc 这层关系;type = tree; 文件名如 2b;
- 2b 指向 doc/readme 这层关系;type = tree; 文件名如 2c;
- 2c 指向 readme 这个文件;文件类型为 blob ;这个文件也会有单独的 hash 值,如 2d;
-
按照上面的过程,这样就形成了 2a->2b->2c->2d 的链式关系;
-
Part IV: 分离头指针 Detached Header
A: 不在任何分支下工作
需要保存的变更必须要在和某些分支挂钩的情况下操作,不然如果切换到其他分支,所有操作都不会被保存;
但是如果只是实验性操作,分离头指针状态则是很可取的;
B: 常用命令
// 切换到任一个 Commit_id,相当于在此处有了一个分叉
git checkout Commit_id
// 修复;在 Commit_id 处新建分支
git branch New_branch Commit_id
C: 变基 git rebase
变基常用作修改 Commit, 比如合并,删除等操作
e.g.
如果我们用 git log 信息来查看 commit 历史,得到如下结果;
Commit_id1
Commit_id2
Commit_id3
Commit_id4
-
这样四个 commit 顺序上是由新到旧,如果用
git rebase -i Commit_id3
,那么我们可以对于Commit_id1
,Commit_id2
进行操作。当然,最后会产生一个新的Commit_id
. -
操作方法和 vim 很相似;默认是
pick Commit_id
, 这是第一种策略,我们如果想做其他操作,只需要改变pick
即可;然后:wq
退出即可 -
如果我们对 1, 2 进行合并操作;在之后的修改策略中选择
s
即可。 -
如果我们对 1, 4 进行合并;
git rebase -i Commit_id4
得到返回的结果是:
pick id3
pick id2
pick id1
(Fix: 2019.6.30)
之前的顺序是 id1 -- id3;但是真正顺序和 git log
命令是相反的,也就是:git log
命令得到的顺序是 新-->旧 ;但是 git rebase
命令中,我们的顺序是 旧-->新。
(Add: 2019.6.30)
注意:
- 在做变基操作时,要时刻主要现在是不是在分离头指针的状态!在分离头指针状态下所有的
commit
都不会被保存。 - 标志就是在 git bash 下,没有变基的时候是显示
(master)
,但是处于分离头指针状态时则是(master|REBASE-i)
。这个时候要使用git rebase --abort
命令放弃分离头指针状态。
我们需要自己添加:
pick id3
S id4
pick id2
pick id1
有的修改策略需要增加信息,根据信息增加即可;
Part V: 其他常用Git操作指令
A: 临时切换,需要保存当前工作区
- 根据之前的叙述,可以先
git add
,然后将再git chekout
- 更好的方法:
git stash
// 将当前工作区存入一个临时工作区
git stash
// 查看
git stash list
// 回退保存的工作区,但是 stash 并没有被清空;
git stash apply
// 覆写当前工作区(当前工作区所有内容丢失),stash 中的内容也清空
git stash pop
Part VI: 和远端Github的操作
A: Github --> 本地
- 有两种协议,一种是 “哑协议” ,一种是智能协议;
- 智能协议有进度条显示,现在大多 https:// 都是智能协议;
// 哑协议;参数 --bare 代表裸仓库,
git clone --bare ~~~/.git
// 智能协议
git clone --bare https://~~~/.git
B: 本地 ---> Github
// 查看远端仓库
git remote -v
/* 添加远端仓库,比如向 Github 中添加仓库名为 origin 的仓库,需要本地在这个仓库下
git remote add repo_name url
git remote add origin https://github.com/user/repo.git
C: 同步
-
若远端和本地不同,
git push
时会失败;-
推荐用
git pull
-
可以直接
git push remote-repo-name --all
-
也可以用
merge
将远端和本地的信息进行同步;git fetch remote-repo-name branch-name git merge remote-repo-name/branch-name
-
如果
git merge
报错,可以自行查看报错信息,添加相关参数。一般而言,要加上git merge --allow-unrelated-histories ~
这个参数; -
最后,
git push remote-repo-name branch-name
-
-
-
同步原则
-
允许
ahead
, 不允许behind
.所以每次都要同步检查 -
注意,如果我们在本地执行了变基操作,修改了 commit 信息;和 Github 进行同步时,应该用
git push --force
命令,不然无法同步!
(Add: 2019.6.30)
注意:-
我们所说的允许
ahead
, 不允许behind
,有这样一种情况:我们修改了最新的commit_ID1
之前的 commit ,比如说commit_ID3
,我们在使用git push
的时候肯定会报behind
错误。- 有两个文件 A & B,如果最近的一次 commit 只是修改了 B 文件,而且删除的
commit_ID3
正好是和 文件A 有关的修改。那么,如果强行git push
,那么 文件B 的修改会被同步的云端,但是文件A会被恢复到没有 commit 之前的状态,丢失所有修改。
- 有两个文件 A & B,如果最近的一次 commit 只是修改了 B 文件,而且删除的
-