1 | /** |
你好,新战友!
前年就想着入一台Macbook pro了,16年发布的Touch-bar还是很让我惊艳和心动的。两年来自己的人生经历了很多大事,并无过多结余,遂作罢。
也是今年幸运,到手年终奖之后略有结余,就一咬牙入了一台,一来奖励自己,二来真正想用Macbook做一些东西出来。打开屏幕的那一刹那,感觉自己做的决定真是没有错。
你好,新战友,愿你陪伴我乘风破浪!
纪念《英雄志》断更十年
重读《英雄志》,像做了一个很长的梦。
两年的零碎时间,重新把高中大学时期看过的武侠小说看了一个遍,前前后后大概有二十部,从头到尾看下来,还是《英雄志》里的人物和故事最打动我。
要说文字功底,孙晓和金庸不可同日而语,再看意境刻画,孙晓又差了古龙几个档次。可现在,反而是孙晓笔下的诸多人物和画面深深刻在了脑海中,反复涌现。
很多小说描绘芸芸众生,大千世界不同的人物性格迥异,主角作为其中之一,纵使会有成长和历练,但是一般主角的个性相对变化不大。可是《英雄志》讲的却是变化,跨越十年的变化。印象很深刻的是杨肃观回答银川公主的一句话:那年臣屡遭变故,从此挥别轻狂,步入中年。“那年”是十年前,杨肃观二十六岁。
于是就想起自己,跟十年前相比,有没有变化。想来想去,中二的性格似乎一成不变,但是年少的热情与叛逆,却在与日俱减。
孙晓曾说,杨肃观与秦仲海这两位主角已经完全化,修罗一怒和怒王冲冠,都狰狞恐怖。卢云也将在第二十三卷完全化,最晚完全化的是伍定远,因为他太强,也太压抑。那么自己呢,没有十年水瀑洗礼,也没有煮酒论英雄的经历,自己还是个傻小子,什么时候完全化呢?
书中记忆特别深的一段剧情,也许现在我都还不理解。那就是卢云在布庄看到暌违十年的顾倩兮,他站在阴影中注视着自己的未婚妻,突然发现顾倩兮留着指甲尖,不知不觉中感到无比茫然,脑子里明明白白记着,银川有指甲尖,琼芳也有,甚至连艳婷他都记得有,可就是想不起来顾倩兮以前留不留指甲尖。什么都似曾相识,却什么都记不起来了,卢云泪流满面。正统十年元宵节深夜,杨夫人就在眼前,而顾小姐却远在天涯,再也找不到了。
这一章的名字叫做章台柳,是我看过的武侠小说里边写重逢写的最好的,吊打神雕的十六年后。
《英雄志》断更十年,好在结局呼之欲出,让自己有所期待。
闭上眼,眼前依旧是白水河的瀑布、金水桥畔的细雨、红螺寺的小雪以及仲海身穿白衣白甲手持“怒”字旗,帅军奔驰在苍茫的草原上……
闭包不完全指西
理解闭包是前端开发工程师的基本。 ——鲁迅
1. 闭包是什么
要讲清楚闭包,首先要先从js的作用域说起。
js在运行之前会建立起一条作用域链:
1 | var glob = 'Global'; |
上述代码的作用域链为Global => Outer => Inner
,作用域链越靠近末端,可以访问的变量范围越大。任何一个函数可以访问其自己所在作用域中的变量以及父作用域中的变量,比如在Inner
函数内部可以访问到glob
、outer
、inner
三个变量。
函数无法访问自己子作用域中的任何变量,除非定义为全局的(不推荐):
1 | var glob = 'Global'; |
js中函数是一等公民,也就是说函数可以作为任何值被传递,所以看上边的例子,虽然在全局环境无法访问outer
变量,但由于Inner
函数可以访问,如果在全局环境中获取Inner
函数的话,就可以访问outer
变量了:
1 | var glob = 'Global'; |
上述的func
接受了Outer
函数返回的Inner
函数,所以func
即可访问Outer
函数中定义的outer
变量了。举个简单的例子:
1 | function Outer() { |
这就是外部读取内部变量的经典方式。这个Inner
的内部函数,就叫做闭包。闭包
简单来说就是函数内部的函数,如果一旦该内部函数被返回到别的作用域,那么这个内部函数就形成一个闭包。因为引用了函数内部的一些变量,所以可以在别的作用域中进行访问。
同一个闭包可以同时存在多个,例如:
1 | function Outer() { |
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
2. 闭包的副作用
首先由于闭包会携带包含它的作用域,所以闭包会比其他函数占用更多的内存。过度使用可能会导致内存泄漏。
另一方面,闭包还有一个副作用,就是闭包只能取得包含函数中任何变量的最后一个值。
1 | function createFunctions() { |
你会发现,result
这个数组其实是一串闭包的数组,因为都引用了i
这个变量。
表面上看,数组里的每一项运行后都能返回它们各自的索引值,从0-9,但其实,每次运行的结果都是10。因为这10个函数中都引用了createFunctions
里边的i
变量,这个变量在函数执行完成的时候为10,所以所有的结果都是10。如果想要改成预期的结果,则可以这样做来避免上述情况:
1 | function createFunctions() { |
另外,关于this
对象,闭包有时候也会有一些需要注意的地方,举个例子:
1 | var name = 'The window'; |
上述三个例子,object1
是正常情况,object2
调用getName()()
后发现this
的指向是全局的,因为object2.getName()
执行后返回的是匿名函数function(){return this.name}
,因为匿名函数的执行环境具有全局性,所以这个this
指向全局环境中的name
。如果想要返回的函数返回对象里边的name
,那么就要像object3
一样,构建一个闭包,引用内部的this
,将值放到that
中,然后返回调用即可。
3. 闭包的例子
1 实现私有变量
1 | var app = (function() { |
2 knockout中的observable
1 | var far = ko.observable('LinWei'); |
3 未完待续…
4. 参考文献
- 学习Javascript闭包(Closure) 阮一峰
- Javascript高级程序设计(第五版) 第七章-函数表达式
React Quick Start - 5. State和生命周期
State和生命周期
考虑之前时钟的例子,目前为止我们只学习了一种更新UI的方式,那就是通过调用ReactDOM.render()
来渲染新的页面:
1 | function tick() { |
这一章,我们将学习如何让Clock
组件变成可复用的和可封装的,它将会自己设置计时器并且每一秒更新自己。
首先先对clock进行组件封装:
1 | function Clock(props) { |
但是,这个组件忽略了一个关键的需求:应该由Clock
自己来设置一个计时器并且每秒更新UI,这是Clock
的实现细节。
理想状态下,我们想要的效果是只写一次Clock
,并让其自己进行更新:
1 | ReactDOM.render( |
要实现这个效果,我们需要给Clock
组件添加”state”。
State和props很像,但是State是私有的,并且完全由组件自己来控制。
之前我们曾谈到过,组件以类的方式来定义的话有一些额外的特性。本地的state就属于这种特性,只有类方式定义的组件才有。
将函数式组件改写为类方式
将一个函数式组件改写为类组件需要五步:
- 用相同的名字创建一个
ES6
的class
,并且继承自React.Component
。 - 添加一个空的方法
render()
。 - 将函数组件的内部代码转移到
render()
方法内部。 - 替换
render()
方法内部的props
属性为this.props
。 - 删除空的函数声明。
1 | class Clock extends React.Component { |
如上所示,Clock
组件现在已经从函数式定义转变为了类定义。这样一来,我们就可以使用一些额外的特性了,比如state和生命周期钩子。
添加本地State
将date
从props变为state需要三步:
1 将render()
方法中的this.props.date
替换为this.state.date
:
1 | class Clock extends React.Component { |
2 为类添加一个构造函数constructor
,并且声明和初始化this.state
:
1 | class Clock extends React.Component { |
注意,我们传递了props
给基类构造函数:
1 | constructor(props) { |
类组件应该在任何时候都传递props
参数给基类构造函数并且调用。
3 移除<Clock />
元素里边的date
属性:
1 | ReactDOM.render( |
我们稍后添加回来计时器相关的代码。
目前的组件如下:
1 | class Clock extends React.Component { |
下一步,我们将给Clock
逐渐设置计时器,并且让它每一秒都进行更新。
添加生命周期函数
在拥有很多组件的应用中,有一点非常重要,那就是当组件摧毁的时候一定要释放其占用的资源。
我们想在Clock
第一次渲染在DOM树中的时候设置一个定时器,这在react中叫做”挂载(monting)”。
同样的,我们也想在Clock
在DOM树中移除的时候清除掉定时器,这在react中叫做”卸载(unmounting)”。
我们可以在组件类中声明一些特殊的方法,这些方法可以在组件挂载或者卸载的时候运行:
1 | class Clock extends React.Component { |
这些方法统称为”生命周期挂钩”。
componentDidMount()
挂钩函数会在组件渲染到DOM中之后执行,将计时器放到这里非常合适:
1 | componentDidMount() { |
注意这里我们将计时器的ID附在了this
上边。
this.props
、由React来设置,this.state
相比而言有着特殊的意义,你可以任意添加额外的字段到class中如果你需要存储一些额外信息,并且不需要用来作为显示输出。
如果不需要在render()
方法中使用一些变量,那么不就应该放置到state中。
此外,我们还需要在componentWillUnmount()
生命周期挂钩中清除掉计时器:
1 | componentWillUnmount() { |
最后,我们来实现tich()
方法,用来每秒更新时间。
这个方法将会使用this.setState()
来更新组件的本地state状态:
1 | class Clock extends React.Component { |
到现在,这个时钟就可以正常工作了。
现在来快速回顾一下发生了什么以及这些方法调用的顺序:
- 当
<Clock />
传递给ReactDOM.render()
的时候,React会调用Clock
组件的构造函数。因为Clock
需要显示当前时间,所以它用一个包含当前时间的对象来初始化this.state
。之后我们还会更新这个state。 - 之后React调用
Clock
组件的render()
方法。这一步React才知道需要在屏幕上展示什么内容。React接着更新DOM去匹配Clock
的渲染输出。 - 当
Clock
组件的输出插入到DOM中的时候,React调用componentDidMount()
方法。在这个方法中,Clock
组件请求浏览器来设置一个定时器,每一秒钟调用一次tick()
方法。 - 每一秒钟,浏览器都会调用
tick()
方法。这个方法中,Clock
组件将一个包含当前时间的对象传递给setState()
,以此来更新UI。通过setState()
调用,React会知道state已经改变,然后再次调用render()
方法去了解下一步要在屏幕上展示什么样的内容。这个时候,render()
方法中的this.state.date
会变化,所以渲染出来的结果将会包含更新过的时间。相应地,React更新DOM。 - 如果
Clock
组件从DOM中移除的话,React会调用componentWillUnmount()
方法,这个时候计时器停止。
正确使用State
关于setState()
,以下三点你必须知道。
不要直接去更改State
举个例子,直接去修改state的话不会重新渲染组件:
1 | // Wrong |
如果要修改state,请使用setState()
:
1 | // Correct |
唯一可以直接对this.state
直接赋值的地方是构造函数。
State的更新可能是异步的
React可能会在一次更新操作中批处理多个setState()
调用。
因为this.props
和this.state
可能异步更新,所以你不应该依赖他们当前的值去计算将来的状态(state)。
举个例子,下边的代码更新计数器可能会失败:
1 | // Wrong |
为了针对这种情况,有另一种调用setState()
方法的方式,那就是不再传递一个对象给它,而是传递一个函数。这个函数接受前一个状态值作为第一个参数,第二个参数是执行更新操作的时候的props属性:
1 | // Correct |
上边使用的是箭头函数,当然使用常规的函数也可以:
1 | // Correct |
State的更新会被合并
当调用setState()
的时候,React会合并你提供给当前state的所有对象。
举个例子,你的state可能包含若干相互独立的变量:
1 | constructor(props) { |
之后你可能会分别调用setState()
来互不影响地更新它们:
1 | componentDidMount() { |
合并只是表面的,所以this.setState({comments})
保证了this.state.posts
的独立完整,但是完整替换了this.state.comments
。
数据流自上而下流动
无论是父组件还是子组件都无法知道一个确定的组件是有状态的还是无状态的,而且它们也不应该关注组件是以何种方式定义。
正因为如此,所以说state经常被认为是本地的或者封装的。对于任何组件,无论谁拥有它或设置它,都无法访问它的内部。
一个组件可以将其state以props的方式向下传递给其子组件:
1 | <h2>It is {this.state.date.toLocaleTimeString()}.</h2> |
这种方式也适用于自定义组件:
1 | <FormattedDate date={this.state.date} /> |
FormattedDate
组件可以接受date
参数在其props中,并且不需要知道是否来自Clock
组件的state或者props或者仅仅是被手动输入的:
1 | function FormattedDate(props) { |
这通常被称为”自顶向下”或者”单向”的数据流。任何state都属于一些特定的组件,任何组件中的数据或者源于state的UI元素都只能影响该组件下游的子组件。
如果将一棵组件树想象成为props的瀑布,每一个组件的state都是一个额外的水源,这个水源可以并入瀑布任意一点,并且只能向下流动。
为了表示所有组件确实是相互隔离的,我们可以创建一个App
组件来渲染三个Clock
:
1 | function App() { |
每一个时钟都会设置自己的计时器,并且互不影响地进行更新。
在React应用中,无论一个组件是有状态的还是无状态的都被认为是一个组件的实现细节,都可能随着时间而改变。你可以在有状态的组件中使用无状态的组件,反过来也一样。
React Quick Start - 4. 组件与Props
组件与Props
组件可以将UI页面分割成为许多相互独立的、可复用的片段,从而使得考虑每一部分的设计时可以独立考虑。
概念上讲,组件类似于Javascript中的函数。组件接受任意的输入(称为props
)并最终返回在屏幕上显示的React元素。
函数式与类形式的组件
最简单定义一个组件的方式是用Javascript的function:
1 | function Welcome(props) { |
上边这个函数是一个合法的React组件,它接受一个”props”对象作为参数,并返回一个React元素。我们称这种组件为”函数式的组件”,因为它就是字面上的Javascrit函数。
另一种定义组件的方法是使用ES6中的class
:
1 | class Welcome extends React.Component { |
以上两种组件在React里是完全等价的。利用class
定义的组件会有一些额外的特性,下一章会重点讨论,目前为止都用函数式的组件来举例,因为简明一点。
渲染组件
之前我们只遇到过React元素代表DOM标签的情况:
1 | const element = <div />; |
其实,元素也可以表示用户自定义的组件:
1 | const element = <Welcome name="Sara" />; |
当React检测到一个元素代表的是一个用户自定义的组件的时候,它会将JSX的属性当做一个单对象传递给组件。我们称这个对象叫做”props”。
举个例子,下面的例子在页面上展示”Hello, Sara”:
1 | function Welcome(props) { |
现在重点来看下在这个例子中发生了什么:
- 通过调用
ReactDOM.render()
来渲染<Welcome name="Sara" />
元素。 - React 调用
Welcome
组件,并且将{name: 'Sara'}
作为props对象传入。 Welcome
组件返回<h1>Hello, Sara</h1>
元素作为组件输出结果。- ReactDOM最终更新DOM,将
<h1>Hello, Sara</h1>
元素显示。
警告:通常情况下,组件名称都以大写开头。举个例子,
<div />
代表了一个普通的DOM元素标签,但是<Welcome />
代表的是一个组件,并且需要Welcome
在作用域内。
编写组件
组件在输出的时候也可以引用其他的组件。这可以让我们用组件抽象出各种层级的细节。一个按钮,一个表单,一个对话框等:在React应用中,所有的这些都是通过组件来表达的。
举个例子,我们可以通过创建一个APP
组件来渲染多次Welcome
组件:
1 | function Welcome(props) { |
通常,React的应用拥有一个顶级的App
组件。不过如果是将React集成进一个已经存在的应用里边,可能一开始是自底向上构建,比如从一个按钮一样的小组件开始,然后逐步向顶层构建。
警告:组件必须返回一个单结点的根元素。这也是为什么上边将所有
<Welcome />
放置在一个<div>
里边的原因。
提取组件
不必害怕将一个大的组件分解成为小的组件。
举个例子,考虑将下边的Comment
组件抽取成小的组件:
1 | function Comment(props) { |
这个组件接受anthor
(一个对象),text
(一个字符串)和date
(一个日期类型)作为props,在一个社交媒体网站上描述一段评论信息。
这个组件很难去更改,因为所有的内容糅杂在一起了。这导致很难去复用组件里边的每一部分。让我们把这个组件里边的一些小的组件提取出来。
首先可以提取Avatar
组件来表示头像:
1 | function Avatar(props) { |
Avatar
组件并不关心Comment
组件中渲染什么样的内容,这也是为什么传递给Avatar
一个更加通用的名字:user
而不是author
。
推荐给props命名从这个组件自身作用的视角前去考虑,而不是这个组件将被用于什么样的场合。
现在可以将Comment
组件稍微简化一点:
1 | function Comment(props) { |
下一步提取一个UserInfo
组件来渲染用户的姓名,这个组件包含用户的头像和姓名:
1 | function UserInfo(props) { |
这样可以让Comment
组件进一步简化:
1 | function Comment(props) { |
提取组件可能是一件比较繁琐的工作,但是在大的应用中,提取各种各样可复用的组件回报很大!
一个好的实践就是如果你的页面中某一部分被多次使用(比如Button
,Panel
,Avatar
),或者这个组件自己很复杂(App
,FeedStory
,Comment
),那么将其提取成各种可复用的组件会是一个不错的选择。
Props是只读的
当以函数式或者类形式声明一个组件的时候,这个组件就无法改变它自己的props。看下边这个sum
函数:
1 | function sum(a, b) { |
这种函数被称为纯函数,因为它并不改变输入的值,而且相同的输入下总是返回相同的结果。
相应的,还有一种非纯函数,这种函数会改变它输入的值:
1 | function withdraw(account, amount) { |
React非常灵活,但是有一条很严格的规则:
所有的React组件必须表现得像纯函数一样,不更改它们的props。
当然,所有的应用都是动态的,跟随时间所变化。下一节,我们会介绍一个新的概念state
。
在不违背上述规则的前提下,State允许React组件随着时间根据用户的动作、网络响应或者其他事件来做出变动。
React Quick Start - 3. 渲染元素
渲染元素
元素(Elements)
是构建React应用的最小单元。
一个元素来描述你希望屏幕上展现的UI元素:
1 | const element = <h1>Hello, world</h1>; |
与DOM元素不同的是,React元素是普通的对象,创建简单。React DOM更加关注更新与React元素匹配的DOM树。
Tips: 一个常见的误区是将元素与组件(Components)弄混淆。元素只是构成组件的零部件。
渲染一个元素到DOM树中
首先假定在html文档中有一个<div>
:
1 | <div id="root"></div> |
我们把这个称为”root”DOM结点,其中嵌套的所有东西都将被React DOM来接管。
通过React构建的应用通常只有一个单root结点。如果要将React集成在一个已存在的应用里,那么可以拥有多个相互独立的DOM根节点。
将一个React元素渲染到根DOM结点中,需要借助ReactDOM.render()
函数:
1 | const element = <h1>Hello, world</h1>; |
这会在页面上展示”Hello World”。
更新已渲染的元素
React的元素是不可变
的。一旦创建了一个元素后就无法改变它的子节点或者属性。
一个元素就像是电影里边的一帧:它代表了一个UI元素在某一刻的展现。
就我们现在了解到的,唯一可以更新UI元素的方式就是创建一个新的元素,并通过ReactDOM.render()
函数来渲染。
下边是一个时钟的例子:
1 | function tick() { |
这个例子中,setInterval()
函数每过一秒就会重新调用ReactDOM.render()
来渲染一个新的元素到页面上。
注意:实践中,大多数情况下React APP只调用
ReactDOM.render()
一次,在后边的章节中会学习如何利用具有状态的组件来封装代码。
React只在必要的时候才会更新
React DOM会将当前元素以及其子元素和之前的状态进行比对,只有在状态确实改变的情况下才会做出必要的更新。
React Quick Start - 2. JSX 简介
JSX 简介
先看下这句声明:1
const element = <h1>Hello, world</h1>;
以上这种标签语法既不是一个字符串也不是HTML。
这就是JSX
,一种Javascript语法的一种扩展。官方推荐使用JSX
和React
搭配来描述UI组件。
JSX
产出React
的元素
。下边主要介绍JSX
的基本使用。
将表达式嵌入JSX
在JSX
中可以使用{}
来包含任何javascript表达式,比如说2+2
,user.name
或者方法调用formatName(user)
等都是合法的表达式:
1 | function formatName(user) { |
将JSX放入表达式中
经过编译,JSX
表达式会变为普通的Javascript对象。
这意味着你可以在if语句中和for循环中插入JSX
语法,也可以将JSX
赋值给变量、接受其为参数或者从函数中返回JSX
表达式。1
2
3
4
5
6function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
为JSX设定属性
可以通过双引号包括的字符串字面量来作为JSX
的属性:
1 | const element = <div tabIndex="0"></div>; |
当然也可以通过{}
包裹的Javascript表达式来作为属性:
1 | const element = <img src={user.avatarUrl}></img>; |
为JSX设定子元素
如果一个标签是空标签,可以使用闭合标签/>
来结束标签,像XML一样:
1 | const element = <img src={user.avatarUrl} />; |
JSX
标签可以包含子元素:
1 | const element = ( |
注意:由于JSX语法较之HTML更加接近于Javascript,所以React DOM使用驼峰命名的方式来命名属性名,以此来替换HTML中的属性。
举个例子比如class
在JSX中必须写成className
,tabindex
必须写成tabIndex
。
JSX可以阻止注入攻击
将用户输入嵌入JSX是安全的:
1 | const title = response.potentiallyMaliciousInput; |
默认情况下,对于任何嵌入到JSX中的值,React DOM在渲染之前都会进行编码。因此,它确保了在你的应用中你无法注入任何没有明确写明的代码,因为任何东西都会在渲染前被转化成字符串。
这种方式可以阻止XSS(跨域站点攻击)。
JSX代表了一种对象
Babel通过转码会将JSX编译为React.createElement()
的调用:
以下两种方式是等价的:
1 | const element = ( |
1 | const element = React.createElement( |
React.createElement()
可以确保你写出bug更少的代码,它创造出的对象像这样:
1 | // Note: this structure is simplified |
这种对象称为React元素
,你可以理解为他们就是页面上UI元素的描述信息。React解析这些对象,并通过他们来构建和更新DOM树。
React Quick Start - 1. 安装
主要翻译的是React的官方文档Quick Start部分
安装
安装React
官方推荐使用Yarn
或者npm
来管理前端依赖。
如果使用Yarn
,可以这样安装:1
yarn add react react-dom
如果使用npm
,可以这样安装:1
npm install --save react react-dom
使用ES6和JSX
官方推荐使用Babel
转义器来编译React代码,这样可以在React中使用ES6
和JSX
的语法。ES6
是最新一代的Javascript,它拥有诸多全新的特性与语法,能够使开发更加便捷轻松。JSX
是Javascript语言的一种扩展,可以与React友好地进行协作。
在使用babel
之前确保已经安装了babel-preset-react
和babel-preset-es2015
,并且在.bablerc
中做好了配置。
Hello World
官方推荐使用webpack
或者Browserify
等打包器来管理代码,一来可以让代码模块化,二来可以打包加载以减少加载时间。
下边是最小的一个React应用Hello World:1
2
3
4
5
6
7import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
这段代码构造了一个包含Hello, world!
字样的h1
Dom元素,并且将这个元素渲染进了页面上一个id为root的元素里。
Learn Git
记录一些常用的git命令
基本配置
1 | git config --global #全局配置 |
基础
1 | git init #初始化仓库 |
.gitignore 文件规则使用glob模式,所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(
*
)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。
日志
1 | git log #查看日志 |
git log 选项 说明
-p 按补丁格式显示每个更新之间的差异。
–stat 显示每次更新的文件修改统计信息。
–shortstat 只显示 –stat 中最后的行数修改添加移除统计。
–name-only 仅在提交信息后显示已修改的文件清单。
–name-status 显示新增、修改、删除的文件清单。
–abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
–relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
–graph 显示 ASCII 图形表示的分支合并历史。
–pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
format 选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
远程仓库
1 | git remote [-v] #查看远程仓库 |
标签
1 |
|
分支
1 |
|
git会维护一个HEAD的指针,指向当前分支的顶端。
切换分支最好保留一个干净的工作区域。
远程分支在本地无法移动,只是一个标记位。远程分支使用[origin]/[master]这种方式展现。