Git项目协同开发学习笔记1:项目库开发基础git命令

这年头git基本都是项目开发的标配,以前恰好碰到了就花了两天时间系统学习了下。本文内容基原本自如下tutorial:Learn Git(建议直接去看原文,由于这个网站是有更新的)。这个是我看过对git进行版本控制和项目协做原理讲解最清楚的文档,就记下比较加深理解和记忆。css

1.git是啥

Git是一种分布式版本控制系统(Distributed Version Control System),这是与以前流行的CVS,SVN之类的存在中央库的系统明显差别,即在git中每一个开发者的库都是完整的。git相比以前的VCS具备如下性能更强也更灵活,更安全(SHA1哈希算法)。在以后的why git for your organization模块里面大力安利了一波git给包括开发者,市场,管理,设计师,客户支持,人力资源一大波吃瓜群众。这里就不细说了,能够本身去看。html

2.安装git

git在windows,linux,mac os平台上均可用,所以无论你在哪一个平台上进行开发,只要使用git能对源码进行很好的管理。以ubuntu系统为例:前端

$ sudo apt-get update
$ sudo apt-get install git

至于windows系统的话直接下载安装包安装便可。
通常在安装完git以后会顺便把本身的git帐号和邮箱设为默认值,简化之后的操做。linux

$ git config --global user.name "Emma Paris"    
$ git config --global user.email "eparis@atlassian.com"

3.1新建代码库--git init

git init命令用于创建新的库。这既能够将已经存在可是并未版本化的项目转化为git库,也能够新建一个新的空库。所以这个命令是开始新项目的时候的第一个git命令,而且一个项目这个命令只须要一次(以后会用git clone将现存的库复制到本机的库中)。执行这个命令之后在项目的根目录下会产生一个.git的子目录,这里包含了一切关于这个库元数据(metadata)。好比:git

$ git init <directory>

这个命令将会在指定的目下下新建一个git库,这个文件夹下面除了.git没有别的内容。github

$ git init --bare <directory>

而指定--bare参数则会新建一个空的git库可是省略了工做目录(没有.git文件夹,本来git中的内容直接在新产生的文件夹中,所以通常文件名中会自带.git后缀,好比my-project.git)。共享库通常都是用带--bare标签新建的。
为何共享库须要用--bare创建
对于须要共享的中央库,若是库是non-bare的,那么在push branch时可能会覆盖库中在本机上作的一些修改;而若是时bare的,由于没有工做目录,因此不可能在库中编辑文件以及commit(以后会讲这个git的核心命令),也就不存在这个问题。因此通常中央库时bare的,而各开发者的本地库时non-bare的。
正则表达式

3.2复制代码库--git clone

以前也提到了,这个命令用于复制现存的库。复制后的库的操做将彻底独立于原来的库,而在复制的同时会自动产生一个名为origin的远程连接指向原来的库,方便之后两个库之间的一些交互操做。于git init相似,这个命令在一个项目中也通常只须要执行一次。算法

$ git clone <repo> # 指定将库复制到当前文件夹
$ git clone <repo> <directory> # 将指定库复制到指定文件夹
$ git clone ssh://john@example.com/path/to/my-project.git # john是当前开发者的用户名

原始库能够在本地也能够在远程机器上经过http或者ssh连接获取。(ssh的端口可能会被某些防火墙block,因此通常会用http;可是用ssh来链接github时不用输入密码,更方便一些)。
git的项目协做模式:每一个开发者的库都是彻底的库,不须要像SVN那样经过中间库而互相之间能够push和pull commit。
shell

3.3配置代码库--git config

这个命令可让你直接经过命令行配置当前库的一些参数。ubuntu

$ git config user.name <name> # 定义当前库全部commit所用的用户名
$ git config --global user.name <name> # 定义当前用户全部commit使用的用户名
$ git config --global user.email <email> # 定义当前用户全部commit使用的email
$ git config --global alias.<alias-name> <git-command> # 建立git命令别称(懒无止境)
$ git config --system core.editor <editor> # 定义文本编辑器来给当前机器用户调用git commit之类的命令时使用
$ git config --global --edit # 直接用文本编辑器打开全局配置文件

PS:这些参数也能够在一下文件中找到:

  • (repo)/.git/config文件储存当前库的设置
  • ~/.gitconfig 保存当前用户的设置。这些设置须要用--global标签才能更改。
  • $(prefix)/etc/gitconfig储存当前系统的设置(没找到,不过感受通常也用不到)
    而当各级配置出现冲突的时候,库本地设置会覆盖用户设置,用户设置能覆盖系统设置。

4.保存修改--git add & git commit

项目开发必然包含是编辑-阶段缓存-提交这一流程。git add命令将当前工做目录中的一些改动保存到缓存区域,在正式提交(git commit)以前并不会影响库中的是实质内容(至关因而工做目录和项目历史之间的缓存区)。所以这一命令的使用很是常态化,每编辑修改一个或几个互相相关文件就能够git add,而后用git commit生成一个高度相关的快照。

$ git add <file> # 缓存全部指定文件中的变更
$ git add <directory> # 缓存指定文件夹中的变更
$ git add -p # 开启交互式缓存环节容许选择文件中具体那些变更存入缓存区

最后一个命令的操做方式在开启交互环境的时候会展现:y:缓存当前变更块;n:忽略当前变更块;s:将当前变更块分的更小;e:手动编辑变更块;q:退出交互环境。

而git commit命令则是提交缓存区中的快照到项目历史中,每个提交的快照均可以认为是项目的一个安全版本。除了显式命令以外,git是不会主动修改任何一个提交的快照。

$ git commit # 打开文本编辑器让你输入本次提交的快照的描述信息
$ git commit -m <message> # 直接输入的字符串做为提交的描述信息
$ git commit -a # 提交工做目录下全部改动的快照。注意:这里只包括以前用git add追踪过的文件

在git中commit的快照是保存在本地库中的,于以前的SVN保存在中央库这一点是明显的差别。与git add的缓存区相似,能够将本地库视为要编辑的代码和中央库之间的缓冲区。这种方式带来的优点有不少:好比能够将一次功能更新分解成多个commit,让相关的commit聚在一块儿,方便提交中央库以前对本地的commit历史进行整理。
git的一大核心特征就是commit的是快照而非SVN中的差别。git每次commit都将文件的全部内容都存快照中;而SVN每次保存的是文件中改动的地方。所以git对各commit快照的操做不须要整合之类的步骤,直接对目标commit进行操做便可。

具体操做实例:

$ git add hello.py
$ git commit
# 在编辑器中输入如下信息
#Change the message displayed by hello.py
#
#- Update the sayHello() function to output the user's name
#- Change the sayGoodbye() function to a friendlier message

通常输入信息的格式为第一行是不超过50各字母的总结,空一行,而后是具体改动的说明。

5.1 隐藏改动--git stash

若是当你一个功能开发到一半忽然有一个紧急任务要立刻作(码农平常),那么这个时候就须要用git stash这个命令把上次commit到目前的改动先隐藏起来使其不影响别的开发任务,等到你完成以后调出这个隐藏部分而后继续开发直至下次commit为止。

$ git status    # 显示上次commit与当前状态下的有差别文件路径(有改动的,新添加的,以及未被追踪的)
On branch master        # 有改动的分支
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

$ git stash
Saved working directory and index state WIP on master: 5002d47 our new homepage #保存stash到本地,将HEAD指向最近一次commit
HEAD is now at 5002d47 our new homepage

$ git status # 这种状态下就能够进行随意操做-好比新建commit,切换branch等,以后有须要再切回到stash的分支继续操做便可
On branch master
nothing to commit, working tree clean

$ git stash pop # 弹出以前最近的stash
On branch master
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Dropped refs/stash@{0} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

$git stash apply # 与pop相似,只是这边弹出的stash只是一份拷贝。所以能够将这个stash用于多个分支。
On branch master
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

在默认状况下,git stash只会stash已经在缓存区域的变更(staged changes)和正在被git追踪的的文件中的变更(Unstaged changes),并不会stash工做副本下unstaged文件和被git定义忽略的文件(以后会在gitigore部分有介绍)。因此若是在以前的栗子中添加一个新文件可是没有用git add命令stage,那么这个就是untracked文件不会被stash。可是若是在stash命令中加入-u(--include-untracked)标签,那么未被追踪的文件也会被stash;而用-a(--all)标签则会把被git忽略的文件也stash。

$ script.js

$ git status
On branch master
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Untracked files:

    script.js
# ————————————————————————————————————————
$ git stash
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch master
Untracked files:

    script.js
# -------------------------------------------------------------------------------
$ git stash -u
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch master
nothing to commit, working tree clean

对多个stash进行操做
stash的使用次数并无限制,所以能够有创建多个stash。默认状态下stash被认为是创建stash时分支和commit顶部的WIP(work in process),经过git stash list能够查看全部的stash。若是stash个数比较多,能够用git stash save在stash时添加注释信息以便后续调用时查看。须要回到哪一个stash状态的时候用git stash pop + stash id便可。

$ git stash list
stash@{0}: WIP on master: 5002d47 our new homepage # 5002d47是创建stash时最近的commmit id,master是stash时的分支名,stash@{0}就是stash的id
stash@{1}: WIP on master: 5002d47 our new homepage
stash@{2}: WIP on master: 5002d47 our new homepage

$ git stash save "add style to our site"
Saved working directory and index state On master: add style to our site
HEAD is now at 5002d47 our new homepage

$ git stash list
stash@{0}: On master: add style to our site
stash@{1}: WIP on master: 5002d47 our new homepage
stash@{2}: WIP on master: 5002d47 our new homepage

$ git stash pop stash@{2}

在不肯定stash到底作了哪些改动的时候能够用git stash show来查看具体改动信息。

$ git stash show # 差别汇总
 index.html | 1 +
 style.css | 3 +++
 2 files changed, 4 insertions(+)
$ git stash -p # -p/--patch标签用于显示具体差别
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+  text-decoration: blink;
+}
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>

部分stash
用git stash -p/--patch命令将会迭代文件中的每一处改动块来决定是否要stash这个部分。

$ git stash -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+  text-decoration: blink;
+}
Stash this hunk [y,n,q,a,d,/,e,?]? y
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>
Stash this hunk [y,n,q,a,d,/,e,?]? n

操做模式与以前的git add相同。

从stash处建立新分支
若是当前分支的变更偏离了stash中的变更,那么在调用stash时代时候就极可能产生冲突。这个时候就须要用git stash branch建立新的分支来加载stash中的变更。

$ git stash branch add-style stash@{1} # 从创建stash时的commit处创建并切换到新的分支add-style,而后载入stash中的变更。
Switched to a new branch 'add-stylesheet'
On branch add-stylesheet
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Dropped refs/stash@{1} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

删除stash
若是以前储存的stash不要了能够用git stash drop/clear来清楚。

git stash drop stash@{1} # 删除stash@{1}分支
Dropped stash@{1} (17e2697fd8251df6163117cb3d58c1f62a5e7cdb)
git stash clear # 删除全部stash

5.2 git stash工做原理

stash实际上是特殊的commit。.git/refs/stash中储存了一个特殊的标签指向最近新建的stash,而以前创建的stash则是经过当前stash标签的引用日志(reflog)进行指向。因此当指向stash@{n}的时候实际上是指向了当前stash标签的第n条引用日志。而当stash命令执行的时候,会根据状况新建2或3条commit,同时.git/refs/stash文件中会更新一个指针用于指向新创建的commit。此外因为是commit,因此能够经过git log(后面会讲)来查看stash的历史记录。

$ git log --oneline --graph stash@{0} 
*-. 953ddde WIP on master: 5002d47 our new homepage # 新的commit用于存储当前工做目录下被追踪的文件
|\ \ 
| | * 24b35a1 untracked files on master: 5002d47 our new homepage # 新的commit用于存储当前工做目录下未追踪的文件(这个commit只有在工做目录下存在未追踪的文件,同时--include-untracked或--all标签也同时使用的时候才会被创建)
| * 7023dd4 index on master: 5002d47 our new homepage # 新的commit用于存储缓存区(git add)
|/ * 5002d47 our new homepage # HEAD指针指向的以前存在的最近的一次commit

在stash以前工做目录的状态通常以下所示:

而当stash命令执后的状态以下:

带--include-untracked标签

带--all标签

6. 忽略文件配置--.gitignore文件

git把工做目录下的文件分为3类:tracked(追踪的)-以前有stage(git add)或者commit记录的文件;untracked(未追踪的)-还没有stage和commit的文件;ignored(忽略的)-被git特地忽略的文件。通常被忽略的文件包括第三方文件(各类包),源文件可以产生的文件或文件夹,编译后的文件(.pyc),运行过程当中产生的日志文件(.log),隐藏的系统文件(.DB_store),我的的IDE配置文件等。而这些被忽略的文件的模式则是存储在.gitignore文件中。.gitignore文件能够存在多个,发生冲突的时候本地文件夹的.gitignore的配置会覆盖库根目录下的,根目录下的则会覆盖系统层面的,所以为了方便期间通常都是只在库的根目录下放一个。忽略文件的模式采用正则表达式规则,具体能够看这里,下面举几个经常使用的栗子:

**/logs # 当前库下全部名为logs目录下的文件(双*用于匹配当前库下面的任何文件夹),好比logs/debug.log,/build/logs/foo.log
*.log # 当前库下全部log后缀的文件,好比logs/debug.log, foo.log
!important.log # 除此之外的全部文件,与前一命令一块儿使用时则是表示除了important.log文件之外的全部log文件都会被忽略

须要注意的时.gitignore文件位于根目录下,所以能够被stage和commit到版本迭代中。若是只是想定义本身所需的忽略文件并不想commit到代码库中,能够将忽略规则添加到.git/info/exclude文件中,这个文件不会被stage,所以避免了可能被commit到公共库的状况。
若是须要将本机下全部库都设置相同的忽略规则,那么就能够用$ git config --global core.excludesFile .gitignore命令,而这同时也就不须要考虑把.gitignore 文件放在哪的问题了。
若是须要忽视以前commit的文件,须要用git rm命令将文件重库中删除而后再添加相应规则到.gitignore文件中。

$ echo debug.log >> .gitignore
$ git rm --cached debug.log # --cached表示只删除库里的文件而保留工做目录下的。若是不用则两个地方都会被删掉
rm 'debug.log'
$ git commit -m "Start ignoring debug.log"

若是须要commit被忽略的文件,能够在git add的时候加上-f/--force标签;也能够用以前提到的!来指定进行例外文件。

$ cat .gitignore
*.log
$ git add -f debug.log
$ echo !debug.log >> .gitignore
#-----------------------------
$ cat .gitignore
*.log
!debug.log
$ git add debug.log

若是须要stash被忽略的文件,如以前所说,须要在git stash时添加--all标签。
而当你不肯定某个文件因为哪一个规则被忽略时,能够用git check-ignore命令查看忽略文件的规则。

$ git check-ignore -v debug.log
.gitignore:3:*.log debug.log # 格式未忽略文件的规则:规则所在行;规则;忽略的文件

7.1 查看工做目录和缓存区状态--git status

工做目录和缓存区的状态包括staged变更(存到缓存区),还未staged的变更以及还没有被追踪的文件,而那些被忽略的文件信息并不会有所显示。因此git status的输出通常以下:

# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
#modified: hello.py
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
#modified: main.py
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
#hello.pyc

在commit以前及时git status能够防止一些偶然操做致使commit一些想要的变更。

7.2 产看commit历史记录--git log


git log只能用来查看commit的历史记录,而后选择须要炒做的commit快照。

$ git log #以默认格式输出commit历史
$ git log -n <limit> # 限制输出最近<limit>个commit
$ git log --oneline # 将每个commit的信息压缩到一行中显示,很方便查看大量commit
$ git log --stat # 增长显示具体变更的文件和每一个文件增减的行数
$ git log -p # -p/--patch具体显示每一个commit的改动信息
$ git log --author="<pattern>" # 显示某位开发者的commit,匹配信息能够是字符串也能够是正则表达式
$ git log --grep="<pattern>" # 显示某种模式的commit,匹配信息能够是字符串也能够是正则表达式。
$ git log <since>..<until> # 显示从since到until之间的commit,能够是commit ID,分支名称(master..some-brance),HEAD指针(HEAD~2:当前commit的往前数第二个commit)
$ git log <file> # 显示某个文件相关的commit
$ git log --graph # 用文字图的形式显示commit
$ git log --decorate # 增长显示commit的分支名或者标签名

注意commit ID为40为的SHA1检验和(好比3157ee3718e180a9476bf2e5cab8e3f1e78a73b7),这样用于确保ID的惟一性和检验commit的完整性。

8.消除变更--git checkout/git revert/git reset/git clean

这些命令都能用来对历史commit进行处理,可是原理和效果都不太相同。
git checkout
这个命令的对象能够有3种:文件,commit,分支(checkout分支主要能够是用来切换分支用,在以后有具体介绍在这里就先跳过)。checkout commit时使整个工做目录都回归到选择的commit的状态且并不对目前的状态有任何影响;而checkout文件则是将具体文件回到特定commit时的状态而不影响工做目录下的其余文件。要注意的时checkout commit命令只是看一个只读操做,在查看旧版本或者以前的commit的时候不会对当前库有任何不良影响,所以是一个十分安全的操做。

$ git checkout <commit> <file>

commit能够用commit哈希码或者标签(tag)做为指定变量,但此时HEAD指针将会处于分离的状态(在正常状态时HEAD指针是指向某一个分支的最前端,而当checkout命令调用时,HEAD指针将会指向给定的commit)。

$ git log --oneline # 假设当前工做分支为master
b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.py
435b61d Create hello.py
9773e52 Initial import
$ git checkout a1e8fb5 # 切换到a1e8fb5的commit状态,此时查看编译甚至修改文件都不会对以前分支的状态有任何影响
$ git checkout master # 切回以前的分支继续开发

而当checkout的对象为文件时,则会确实影响当前库的状态。当你从新commit到文件以前的版本时,也就将文件的状态退回到了特定版本。

$ git checkout a1e8fb5 hello.py # 将hello.py文件的状态切换到a1e8fb5 commit状态,文件确实发生改动
$ git checkout HEAD hello.py # 将hello.py文件状态切回到最近的状态

git revert
git revert回到以前的状态的工做方式是新建一个commit而后把须要回归的commit的状态快照复制到这个commit。这样作的好处就是以前commit的历史记录仍旧完整,使协做时不至于commit历史记录改变出现冲突。

# Edit some tracked files
# Commit a snapshot
$ git commit -m "Make some changes that will be undone"
# Revert the commit we just created
$ git revert HEAD # 回到了commit以前的状态

git reset
相比于git revert能保存commit历史的这种相对安全的方法,git set的工做方式是指向要回归的commit,并将如今的状态到须要回归的commit之间全部的commit快照都删除,所以调用git reset时候是直接改动了commit的历史记录,使用的时候(尤为是御别人协做的时候)须要格外当心。通常状况下只在本地库中使用git reset命令,而避免在共享的公共库上面进行这项操做。

$ git reset <file> # 移除缓存区中的特定文件,但并不会改变工做目录的状态。
$ git reset # 将缓存区状态回归到最近一次的commit状态,但不会改变工做目录的状态(也就是有机会git add回去以前的状态)
$ git reset --hard # 将缓存区和工做目录都回归到以前commit的状态(消去上次commit以后的全部变更)
$ git reset <commit> # 将缓存区状态回归到指定的commit状态,但并不改变工做目录的状态
$ git reset --hard <commit> # 将缓存区和工做目录都回归到特定commit的状态(消去指定commit以后发生的全部变更)

基于git reset 和git revert的操做方式,通常前者用于本地的撤销操做,然后者用于共享的commit的撤销操做。

若是在公共库中进行reset操做就会出现如下情况,git会认为你新建了分支,须要用merge操做进行整合。

下面两个栗子讲如下git reset最多见的用途:
1.删除缓冲区的特定文件(git add的时候不当心加错了)

# Edit both hello.py and main.py

# Stage everything in the current directory
$ git add .

# Realize that the changes in hello.py and main.py
# should be committed in different snapshots

# Unstage main.py
$ git reset main.py

# Commit only hello.py
$ git commit -m "Make some changes to hello.py"

# Commit main.py in a separate snapshot
$ git add main.py
$ git commit -m "Edit main.py"

2.删除本地commit历史(掩盖本身曾经作过的一些蠢到没朋友的一些测试)

# Create a new file called `foo.py` and add some code to it

# Commit it to the project history
$ git add foo.py
$ git commit -m "Start developing a crazy feature"

# Edit `foo.py` again and change some other tracked files, too

# Commit another snapshot
$ git commit -a -m "Continue my crazy feature"

# Decide to scrap the feature and remove the associated commits
$ git reset --hard HEAD~2 # 回归开发crazy feature以前的commit状态,并销毁痕迹

git clean
git clean会移除工做目录下未被追踪的文件(避免了用git status查看状态来一一确认须要删除的未追踪文件,很是方便)。须要主要的是这个命令跟rm命令同样是不可撤销的,使用的时候须要考虑清楚。而git clean命令也常常御git reset --hard命令一块儿使用,由于后者能只能对追踪的文件进行操做,二者一块儿使用可以确保搞那个钱工做目录回到特定的版本状态,这在项目编译后须要把编译文件去掉进行打包操做时特别有用。

$ git clean -n # 显示会被清除的文件,但并无实际运行。(方便确认)
$ git clean -f # 删除为追踪的文件(除非把用git config --global 命令把clean.requireForce设置为false,-f是必须加的),但并不会删除未追踪的文件夹以及忽略的文件
$ git clean -f <path> # 删除指定文件夹下的未追踪文件
$ git clean -df # 删除未追踪的文件和文件夹
$ git clean -xf # 删除当前目录下未追踪和忽略的文件

9.重写历史--git commit --amend/git rebase/git rebase -i /git reflog

与以前的删除操做相似,git也提供了一些修改commit历史的命令,虽然这可能会致使一些内容的损失。
git commit --amned
这个命令直接用新的commit快照来替换以前的commit(注意时替换而非修改),这让咱们可以很方便的修正一些有问题的commit快照。

正如git reset同样,这个命令也不要用来修正已经共享的commit,由于这至关于以前的commit被删掉了,若是别人的新功能依赖以前共享的commit那就很容易产生冲突。

# Edit hello.py and main.py
$ git add hello.py
$ git commit

# Realize you forgot to add the changes from main.py
$ git add main.py
$ git commit --amend --no-edit # 不改变以前的commit的信息,在忘了添加文件到缓存区的时候特别有用。

git rebase
这个命令是把某个分支以新的commit的形式整个移到另外一个分支的最前端。因为有新建commit的操做,也就对分支的历史有所改动。效果以下:

git rebase <base> # 对象能够是分支ID,标签(tag),HEAD指针

这个命令主要用于保持一个线性的项目开发历史。当你须要把一个分支的新功能merge到master分支上,用rebase命令产生的历史就会如上图所示保持线性,之后查看的时候很是清晰。rebase也是把上游公共库的变更整合到本地库的常见作法(直接用merge可能会产生打乱原有commit的顺序,当你查看的时候可能会不明因此),可是与以前git commit --amend 和git reset命令同样,git rebase命令也须要避免在已经共享的commit上面。

# Start a new feature
$ git checkout -b new-feature master # 从master分支新建并切换到该分支
# Edit files
$ git commit -a -m "Start developing a feature"
# Create a hotfix branch based off of master
$ git checkout -b hotfix master # 发现有个问题须要hotfix下,就从master新建了hotfix分支
# Edit files
$ git commit -a -m "Fix security hole"
# Merge back into master
$ git checkout master 
$ git merge hotfix # 修复完成后把hotfix分支整合到master分支
$ git branch -d hotfix # 删除hotfix分支
$ git checkout new-feature # 切换到new-feature分支
$ git rebase master # 把new-feature分支移动到master分支的最前端保持线性历史(由于以前插入了hotfix的commit,若是这届合并的话new-featrue的历史就会在hotfi以前了)
$ git checkout master
$ git merge new-feature

git rebase -i
-i标签是指进行交互式的rebase操做,能够对commit历史进行分割,改动,删除等操做,而不是一股脑把全部commit都移动到一个分支最前端。这就给了开发者很大的自由,在开发的时候在本机上能够随便commit,只要在提交公共库以前用这个命令把commit调整好、去掉过期的commit、保持每一个commit都是有意义的(装逼神器)。

# Start a new feature
$ git checkout -b new-feature master
# Edit files
$ git commit -a -m "Start developing a feature"
# Edit more files
$ git commit -a -m "Fix something from the previous commit"

# Add a commit directly to master
$ git checkout master
# Edit files
$ git commit -a -m "Fix security hole"

# Begin an interactive rebasing session
$ git checkout new-feature
$ git rebase -i master
# 在new-feature分支上有两个commit
pick 32618c4 Start developing a feature # pick是命令,若是须要执行别的操做用别的命令便可
pick 62eed47 Fix something from the previous commit

pick 32618c4 Start developing a feature
squash 62eed47 Fix something from the previous commit # squash命令即表示这个commit在rebase的时候会被去掉(在git log的时候能够查看到)
# 保存关闭编辑器后开始rebase
$ git checkout master
$ git merge new-feature

git reflog
git用名为reflog的机制追踪每一个分支最前端的更新状态,所以每当HEAD指针的状态有变化(好比切换分支,pull变更,重写历史,增长commit)就有新的条目被加到reflog中。在重写历史之后,reflog包含了关于分支的旧状态,让你在须要的时候回到这些状态。注意的是git reflog只是8追踪状态变动操做。

$ git reflog # 查看本地库的reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2 # 最近的活动
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2
$ git reflog --relative-date # 增长显示reflog的相对日期信息(好比2 days ago)
$ git reset --hard 0254ea7 # 使用git reset命令就能够回到以前的commit状态

到这里为止就是一些在git上创建项目,对项目进行基本的开发操做所需的最多见的命令。下一步就须要讲git进行项目协做时所须要的一些经常使用命令。

相关文章
相关标签/搜索