mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-30 15:16:43 +08:00
732 lines
51 KiB
HTML
732 lines
51 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>031 Git协同工作流,你该怎样选.md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li><a href="../">上一级</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/000 开篇词 洞悉技术的本质,享受科技的乐趣.md.html">000 开篇词 洞悉技术的本质,享受科技的乐趣.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/001 程序员如何用技术变现(上).md.html">001 程序员如何用技术变现(上).md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/002 程序员如何用技术变现(下).md.html">002 程序员如何用技术变现(下).md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/003 Equifax信息泄露始末.md.html">003 Equifax信息泄露始末.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/004 从Equifax信息泄露看数据安全.md.html">004 从Equifax信息泄露看数据安全.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/005 何为技术领导力.md.html">005 何为技术领导力.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/006 如何拥有技术领导力.md.html">006 如何拥有技术领导力.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/007 推荐阅读:每个程序员都该知道的事.md.html">007 推荐阅读:每个程序员都该知道的事.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/008 Go语言,Docker和新技术.md.html">008 Go语言,Docker和新技术.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/009 答疑解惑:渴望、热情和选择.md.html">009 答疑解惑:渴望、热情和选择.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/010 如何成为一个大家愿意追随的Leader?.md.html">010 如何成为一个大家愿意追随的Leader?.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/011 程序中的错误处理:错误返回码和异常捕捉.md.html">011 程序中的错误处理:错误返回码和异常捕捉.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/012 程序中的错误处理:异步编程和最佳实践.md.html">012 程序中的错误处理:异步编程和最佳实践.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/013 魔数 0x5f3759df.md.html">013 魔数 0x5f3759df.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/014 推荐阅读:机器学习101.md.html">014 推荐阅读:机器学习101.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/015 时间管理:同扭曲时间的事儿抗争.md.html">015 时间管理:同扭曲时间的事儿抗争.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/016 时间管理:投资赚取时间.md.html">016 时间管理:投资赚取时间.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/017 故障处理最佳实践:应对故障.md.html">017 故障处理最佳实践:应对故障.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/018 故障处理最佳实践:故障改进.md.html">018 故障处理最佳实践:故障改进.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/019 答疑解惑:我们应该能够识别的表象和本质.md.html">019 答疑解惑:我们应该能够识别的表象和本质.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/020 分布式系统架构的冰与火.md.html">020 分布式系统架构的冰与火.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/021 从亚马逊的实践,谈分布式系统的难点.md.html">021 从亚马逊的实践,谈分布式系统的难点.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/022 分布式系统的技术栈.md.html">022 分布式系统的技术栈.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/023 分布式系统关键技术:全栈监控.md.html">023 分布式系统关键技术:全栈监控.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/024 分布式系统关键技术:服务调度.md.html">024 分布式系统关键技术:服务调度.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/025 分布式系统关键技术:流量与数据调度.md.html">025 分布式系统关键技术:流量与数据调度.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/026 洞悉PaaS平台的本质.md.html">026 洞悉PaaS平台的本质.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/027 推荐阅读:分布式系统架构经典资料.md.html">027 推荐阅读:分布式系统架构经典资料.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/028 编程范式游记(1)- 起源.md.html">028 编程范式游记(1)- 起源.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/029 编程范式游记(2)- 泛型编程.md.html">029 编程范式游记(2)- 泛型编程.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/030 编程范式游记(3) - 类型系统和泛型的本质.md.html">030 编程范式游记(3) - 类型系统和泛型的本质.md.html</a>
|
||
</li>
|
||
<li>
|
||
<a class="current-tab" href="/专栏/左耳听风/031 Git协同工作流,你该怎样选.md.html">031 Git协同工作流,你该怎样选.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/032 推荐阅读:分布式数据调度相关论文.md.html">032 推荐阅读:分布式数据调度相关论文.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/033 编程范式游记(4)- 函数式编程.md.html">033 编程范式游记(4)- 函数式编程.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/034 编程范式游记(5)- 修饰器模式.md.html">034 编程范式游记(5)- 修饰器模式.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/035 编程范式游记(6)- 面向对象编程.md.html">035 编程范式游记(6)- 面向对象编程.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/036 编程范式游记(7)- 基于原型的编程范式.md.html">036 编程范式游记(7)- 基于原型的编程范式.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/037 编程范式游记(8)- Go 语言的委托模式.md.html">037 编程范式游记(8)- Go 语言的委托模式.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/038 编程范式游记(9)- 编程的本质.md.html">038 编程范式游记(9)- 编程的本质.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/039 编程范式游记(10)- 逻辑编程范式.md.html">039 编程范式游记(10)- 逻辑编程范式.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/040 编程范式游记(11)- 程序世界里的编程范式.md.html">040 编程范式游记(11)- 程序世界里的编程范式.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/041 弹力设计篇之“认识故障和弹力设计”.md.html">041 弹力设计篇之“认识故障和弹力设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/042 弹力设计篇之“隔离设计”.md.html">042 弹力设计篇之“隔离设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/043 弹力设计篇之“异步通讯设计”.md.html">043 弹力设计篇之“异步通讯设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/044 弹力设计篇之“幂等性设计”.md.html">044 弹力设计篇之“幂等性设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/045 弹力设计篇之“服务的状态”.md.html">045 弹力设计篇之“服务的状态”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/046 弹力设计篇之“补偿事务”.md.html">046 弹力设计篇之“补偿事务”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/047 弹力设计篇之“重试设计”.md.html">047 弹力设计篇之“重试设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/048 弹力设计篇之“熔断设计”.md.html">048 弹力设计篇之“熔断设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/049 弹力设计篇之“限流设计”.md.html">049 弹力设计篇之“限流设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/050 弹力设计篇之“降级设计”.md.html">050 弹力设计篇之“降级设计”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/051 弹力设计篇之“弹力设计总结”.md.html">051 弹力设计篇之“弹力设计总结”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/052 区块链技术 - 区块链的革命性及技术概要.md.html">052 区块链技术 - 区块链的革命性及技术概要.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/053 区块链技术 - 区块链技术细节 - 哈希算法.md.html">053 区块链技术 - 区块链技术细节 - 哈希算法.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/054 区块链技术 - 区块链技术细节 - 加密和挖矿.md.html">054 区块链技术 - 区块链技术细节 - 加密和挖矿.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/055 区块链技术 - 去中心化的共识机制.md.html">055 区块链技术 - 去中心化的共识机制.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/056 区块链技术 - 智能合约.md.html">056 区块链技术 - 智能合约.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/057 区块链技术 - 传统金融和虚拟货币.md.html">057 区块链技术 - 传统金融和虚拟货币.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/058 管理设计篇之分布式锁.md.html">058 管理设计篇之分布式锁.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/059 管理设计篇之配置中心.md.html">059 管理设计篇之配置中心.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/060 管理设计篇之边车模式.md.html">060 管理设计篇之边车模式.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/061 管理设计篇之服务网格.md.html">061 管理设计篇之服务网格.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/062 管理设计篇之网关模式.md.html">062 管理设计篇之网关模式.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/063 管理设计篇之部署升级策略.md.html">063 管理设计篇之部署升级策略.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/064 性能设计篇之缓存.md.html">064 性能设计篇之缓存.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/065 性能设计篇之异步处理.md.html">065 性能设计篇之异步处理.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/066 性能设计篇之数据库扩展.md.html">066 性能设计篇之数据库扩展.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/067 性能设计篇之秒杀.md.html">067 性能设计篇之秒杀.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/068 性能设计篇之边缘计算.md.html">068 性能设计篇之边缘计算.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/069 程序员练级攻略(2018):开篇词.md.html">069 程序员练级攻略(2018):开篇词.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/070 程序员练级攻略(2018):零基础启蒙.md.html">070 程序员练级攻略(2018):零基础启蒙.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/071 程序员练级攻略(2018):正式入门.md.html">071 程序员练级攻略(2018):正式入门.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/072 程序员练级攻略(2018):程序员修养.md.html">072 程序员练级攻略(2018):程序员修养.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/073 程序员练级攻略(2018):编程语言.md.html">073 程序员练级攻略(2018):编程语言.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/074 程序员练级攻略:理论学科.md.html">074 程序员练级攻略:理论学科.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/075 程序员练级攻略(2018):系统知识.md.html">075 程序员练级攻略(2018):系统知识.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/076 程序员练级攻略(2018):软件设计.md.html">076 程序员练级攻略(2018):软件设计.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/077 程序员练级攻略(2018):Linux系统、内存和网络.md.html">077 程序员练级攻略(2018):Linux系统、内存和网络.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/078 程序员练级攻略(2018):异步IO模型和Lock-Free编程.md.html">078 程序员练级攻略(2018):异步IO模型和Lock-Free编程.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/079 程序员练级攻略(2018):Java底层知识.md.html">079 程序员练级攻略(2018):Java底层知识.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/080 程序员练级攻略(2018):数据库.md.html">080 程序员练级攻略(2018):数据库.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/081 程序员练级攻略(2018):分布式架构入门.md.html">081 程序员练级攻略(2018):分布式架构入门.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/082 程序员练级攻略(2018):分布式架构经典图书和论文.md.html">082 程序员练级攻略(2018):分布式架构经典图书和论文.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/083 程序员练级攻略(2018):分布式架构工程设计.md.html">083 程序员练级攻略(2018):分布式架构工程设计.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/084 程序员练级攻略(2018):微服务.md.html">084 程序员练级攻略(2018):微服务.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/085 程序员练级攻略(2018):容器化和自动化运维.md.html">085 程序员练级攻略(2018):容器化和自动化运维.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/086 程序员练级攻略(2018):机器学习和人工智能.md.html">086 程序员练级攻略(2018):机器学习和人工智能.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/087 程序员练级攻略(2018):前端基础和底层原理.md.html">087 程序员练级攻略(2018):前端基础和底层原理.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/088 程序员练级攻略(2018):前端性能优化和框架.md.html">088 程序员练级攻略(2018):前端性能优化和框架.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/089 程序员练级攻略(2018):UIUX设计.md.html">089 程序员练级攻略(2018):UIUX设计.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/090 程序员练级攻略(2018):技术资源集散地.md.html">090 程序员练级攻略(2018):技术资源集散地.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/091 程序员面试攻略:面试前的准备.md.html">091 程序员面试攻略:面试前的准备.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/092 程序员面试攻略:面试中的技巧.md.html">092 程序员面试攻略:面试中的技巧.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/093 程序员面试攻略:面试风格.md.html">093 程序员面试攻略:面试风格.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/094 程序员面试攻略:实力才是王中王.md.html">094 程序员面试攻略:实力才是王中王.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/095 高效学习:端正学习态度.md.html">095 高效学习:端正学习态度.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/096 高效学习:源头、原理和知识地图.md.html">096 高效学习:源头、原理和知识地图.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/097 高效学习:深度,归纳和坚持实践.md.html">097 高效学习:深度,归纳和坚持实践.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/098 高效学习:如何学习和阅读代码.md.html">098 高效学习:如何学习和阅读代码.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/099 高效学习:面对枯燥和量大的知识.md.html">099 高效学习:面对枯燥和量大的知识.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/100 高效沟通:Talk和Code同等重要.md.html">100 高效沟通:Talk和Code同等重要.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/101 高效沟通:沟通阻碍和应对方法.md.html">101 高效沟通:沟通阻碍和应对方法.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/102 高效沟通:沟通方式及技巧.md.html">102 高效沟通:沟通方式及技巧.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/103 高效沟通:沟通技术.md.html">103 高效沟通:沟通技术.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/104 高效沟通:好老板要善于提问.md.html">104 高效沟通:好老板要善于提问.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/105 高效沟通:好好说话的艺术.md.html">105 高效沟通:好好说话的艺术.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/106 加餐 谈谈我的“三观”.md.html">106 加餐 谈谈我的“三观”.md.html</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/左耳听风/107 结束语 业精于勤,行成于思.md.html">107 结束语 业精于勤,行成于思.md.html</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
<div class="sidebar-toggle-inner"></div>
|
||
</div>
|
||
<script>
|
||
function add_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>031 Git协同工作流,你该怎样选</h1>
|
||
<p><img src="assets/0011a6d80f3f9d9cc36bcef5ad1d7f77.jpg" alt="img" /></p>
|
||
<p>与传统的代码版本管理工具相比,Git 有很多的优势,因而越来越成为程序员喜欢的版本管理工具。Git 这个代码版本管理工具最大的优势有以下几个。</p>
|
||
<ul>
|
||
<li>Git 是一个分布式的版本管理工具,而且可以是单机版的,所以,你在没有网络的时候同样可以提交(commit)代码。对于我们来说,这意味着在出差途中或是没有网络的环境中依然可以工作写代码。</li>
|
||
</ul>
|
||
<p>这是不是听起来有点不对?一方面,以后再也不能以将没有网络作为不工作的借口了。另一方面,没有网络意味着没有 Google 和 StackOverflow,光有个本地的 Git 我也一样不能写代码啊……</p>
|
||
<p>(哈哈。好吧,这已经超出了 Git 这个技术的范畴了,这里就不讨论了。)</p>
|
||
<ul>
|
||
<li>Git 从一个分支向另一个分支合并代码的时候,会把要合并的分支上的所有提交一个一个应用到被合并的分支上,合并后也能看得到整个代码的变更记录。而其他的版本管理工具则不能。</li>
|
||
<li>Git 切换分支的时候通常很快。不像其他版本管理器,每个分支一份拷贝。</li>
|
||
<li>Git 有很多非常有用的命令,让你可以很方便地工作。</li>
|
||
</ul>
|
||
<p>比如我很喜欢的<code>git stash</code>命令,可以把当前没有完成的事先暂存一下,然后去忙别的事。<code>git cherry-pick</code>命令可以让你有选择地合并提交。<code>git add -p</code>可以让你挑选改动提交,<code>git grep $regexp $(git rev-list --all)</code>可以用来在所有的提交中找代码。因为都是本地操作,所以你会觉得飞快。</p>
|
||
<p>除此之外,由 Git 衍生出来的 GitHub/GitLab 可以帮你很好地管理编程工作,比如 wiki、fork、pull request、issue……集成了与编程相关的工作,让人觉得这不是一个冷冰冰的工具,而真正和我们的日常工作发生了很好的交互。</p>
|
||
<p>GitHub/GitLab 这样工具的出现,让我们的工作可以呈现在一个工作平台上,并以此来规范整个团队的工作,这才正是 Git 这个版本管理工具成功的原因。</p>
|
||
<p>今天,我们不讲 Git 是怎么用的,因为互联网上有太多的文章和书了。而且,如果你还不会用 Git 的话,那么你已经严重落后于这个时代了。在这篇文章中,我想讲一下 Git 的协同工作流,因为我看到很多团队在使用 Git 时,并没有用好。</p>
|
||
<p>注意,因为 Git 是一个分布式的代码管理器,所以,是分布式就会出现数据不一致的情况,因此,我们需要一个协同工作流来让工作变得高效,并可以有效地让代码具有更好的一致性。</p>
|
||
<p>说到一致性,就是每个人手里的开发代码,还有测试和生产线上的代码,要有一个比较好的一致性的管理和协同方法。这就是 Git 协同工作流所需要解决的问题。</p>
|
||
<p>目前来说,你可能以为我想说的是 GitFlow 工作流。恭喜你猜对了。但是,我想说的是,GitFlow 工作流太过复杂,我并不觉得 GitFlow 工作流是一个好的工作流。如果你的团队在用这种工作流开发软件,我相信你的感觉一定是糟透了。</p>
|
||
<p>所以,我的这篇文章会对比一些比较主流的协同工作流,然后,再抨击一下 GitFlow 工作流。</p>
|
||
<h1>中心式协同工作流</h1>
|
||
<p>首先,我们先说明一下,Git 是可以像 SVN 这样的中心工作流一样工作的。我相信很多程序员都是在采用这样的工作方式。</p>
|
||
<p>这个过程一般是下面这个样子的。</p>
|
||
<ol>
|
||
<li>从服务器上做<code>git pull origin master</code>把代码同步下来。</li>
|
||
<li>改完后,<code>git commit</code>到本地仓库中。</li>
|
||
<li>然后<code>git push origin master</code>到远程仓库中,这样其他同学就可以得到你的代码了。</li>
|
||
</ol>
|
||
<p>如果在第 3 步发现 push 失败,因为别人已经提交了,那么你需要先把服务器上的代码给 pull 下来,为了避免有 merge 动作,你可以使用 <code>git pull --rebase</code> 。这样就可以把服务器上的提交直接合并到你的代码中,对此,Git 的操作是这样的。</p>
|
||
<ol>
|
||
<li>先把你本地提交的代码放到一边。</li>
|
||
<li>然后把服务器上的改动下载下来。</li>
|
||
<li>然后在本地把你之前的改动再重新一个一个地做 commit,直到全部成功。</li>
|
||
</ol>
|
||
<p>如下图所示。Git 会把 Origin/Master 的远程分支下载下来(紫色的),然后把本地的 Master 分支上的改动一个一个地提交上去(蓝色的)。</p>
|
||
<p><img src="assets/5974a4026acca1000cd21772c4c52a6b.png" alt="img" /></p>
|
||
<p>如果有冲突,那么你要先解决冲突,然后做 <code>git rebase --continue</code> 。如下图所示,git 在做 pull --rebase 时,会一个一个地应用(apply)本地提交的代码,如果有冲突就会停下来,等你解决冲突。</p>
|
||
<p><img src="assets/75b3fea18fa91b837f4f3ae6db6ab6e7.png" alt="img" /></p>
|
||
<h1>功能分支协同工作流</h1>
|
||
<p>上面的那种方式有一个问题,就是大家都在一个主干上开发程序,对于小团队或是小项目你可以这么干,但是对比较大的项目或是人比较多的团队,这么干就会有很多问题。</p>
|
||
<p>最大的问题就是代码可能干扰太严重。尤其是,我们想安安静静地开发一个功能时,我们想把各个功能的代码变动隔离开来,同时各个功能又会有多个开发人员在开发。</p>
|
||
<p>这时,我们不想让各个功能的开发人员都在 Master 分支上共享他们的代码。我们想要的协同方式是这样的:同时开发一个功能的开发人员可以分享各自的代码,但是不会把代码分享给开发其他功能的开发人员,直到整个功能开发完毕后,才会分享给其他的开发人员(也就是进入主干分支)。</p>
|
||
<p>因此,我们引入“功能分支”。这个协同工作流的开发过程如下。</p>
|
||
<ol>
|
||
<li>首先使用 <code>git checkout -b new-feature</code> 创建 “new-feature”分支。</li>
|
||
<li>然后共同开发这个功能的程序员就在这个分支上工作,进行 add、commit 等操作。</li>
|
||
<li>然后通过 <code>git push -u origin new-feature</code> 把分支代码 push 到服务器上。</li>
|
||
<li>其他程序员可以通过<code>git pull --rebase</code>来拿到最新的这个分支的代码。</li>
|
||
<li>最后通过 Pull Request 的方式做完 Code Review 后合并到 Master 分支上。</li>
|
||
</ol>
|
||
<p><img src="assets/455b921b2d178c87fe66714910301aec.png" alt="img" /></p>
|
||
<p>就像上面这个图显示的一样,紫色的分支就是功能分支,合并后就会像上面这个样子。</p>
|
||
<p>我们可以看到,其实,这种开发也是以服务器为中心的开发,还不是 Git 分布式开发,它只不过是用分支来完成代码改动的隔离。</p>
|
||
<p>另外,我想提醒一下,为什么会叫“功能分支”,而不是“项目分支”?因为 Git 的最佳实践希望大家在开发的过程中,快速提交,快速合并,快速完成。这样可以少很多冲突的事,所以叫功能分支。</p>
|
||
<p>传统的项目分支开得太久,时间越长就越合不回去。这种玩法其实就是让我们把一个大项目切分成若干个小项目来执行(最好是一个小功能一个项目)。这样才是互联网式的快速迭代式的开发流程。</p>
|
||
<h1>GitFlow 协同工作流</h1>
|
||
<p>在真实的生产过程中,前面的协同工作流还是不能满足工作的要求。这主要因为我们的生产过程是比较复杂的,软件生产中会有各式各样的问题,并要面对不同的环境。我们要在不停地开发新代码的同时,维护线上的代码,于是,就有了下面这些需求。</p>
|
||
<ol>
|
||
<li>希望有一个分支是非常干净的,上面是可以发布的代码,上面的改动永远都是可以发布到生产环境中的。这个分支上不能有中间开发过程中不可以上生产线的代码提交。</li>
|
||
<li>希望当代码达到可以上线的状态时,也就是在 alpha/beta release 时,在测试和交付的过程中,依然可以开发下一个版本的代码。</li>
|
||
<li>最后,对于已经发布的代码,也会有一些 Bug-fix 的改动,不会将正在开发的代码提交到生产线上去。</li>
|
||
</ol>
|
||
<p>你看,面对这些需求,前面的那些协同方式就都不行了。因为我们不仅是要在整个团队中共享代码,我们要的更是管理好不同环境下的代码不互相干扰。说得技术一点儿就是,要管理好代码与环境的一致性。</p>
|
||
<p>为了解决这些问题,GitFlow 协同工作流就出来了。</p>
|
||
<p>GitFlow 协同工作流是由 Vincent Driessen 于 2010 年在 A successful Git branching model 这篇文章介绍给世人的。</p>
|
||
<p>这个协同工作流的核心思想如下图所示。</p>
|
||
<p><img src="assets/9cf4c9bc17bf11aa07d47f61d2137fca.png" alt="img" /></p>
|
||
<p>整个代码库中一共有五种分支。</p>
|
||
<ul>
|
||
<li>Master 分支。也就是主干分支,用作发布环境,上面的每一次提交都是可以发布的。</li>
|
||
<li>Feature 分支。也就是功能分支,用于开发功能,其对应的是开发环境。</li>
|
||
<li>Developer 分支。是开发分支,一旦功能开发完成,就向 Developer 分支合并,合并完成后,删除功能分支。这个分支对应的是集成测试环境。</li>
|
||
<li>Release 分支。当 Developer 分支测试达到可以发布状态时,开出一个 Release 分支来,然后做发布前的准备工作。这个分支对应的是预发环境。之所以需要这个 Release 分支,是我们的开发可以继续向前,不会因为要发布而被 block 住而不能提交。</li>
|
||
</ul>
|
||
<p>一旦 Release 分支上的代码达到可以上线的状态,那么需要把 Release 分支向 Master 分支和 Developer 分支同时合并,以保证代码的一致性。然后再把 Release 分支删除掉。</p>
|
||
<ul>
|
||
<li>Hotfix 分支。是用于处理生产线上代码的 Bug-fix,每个线上代码的 Bug-fix 都需要开一个 Hotfix 分支,完成后,向 Developer 分支和 Master 分支上合并。合并完成后,删除 Hotfix 分支。</li>
|
||
</ul>
|
||
<p>这就是整个 GitFlow 协同工作流的工作过程。我们可以看到:</p>
|
||
<ol>
|
||
<li>我们需要长期维护 Master 和 Developer 两个分支。</li>
|
||
<li>这其中的方式还是有一定复杂度的,尤其是 Release 和 Hotfix 分支需要同时向两个分支作合并。所以,如果没有一个好的工具来支撑的话,这会因为我们可能会忘了做一些操作而导致代码不一致。</li>
|
||
<li>GitFlow 协同虽然工作流比较重。但是它几乎可以应对所有公司的各种开发流程,包括瀑布模型,或是快速迭代模型。</li>
|
||
</ol>
|
||
<h1>GitHub/GitLab 协同工作流</h1>
|
||
<h2>GitFlow 的问题</h2>
|
||
<p>对于 GitFlow 来说,虽然可以解决我们的问题,但是也有很多问题。在 GitFlow 流行了一段时间后,圈内出现了一些不同的声音。参看下面两篇吐槽文章。</p>
|
||
<ul>
|
||
<li><a href="http://endoflineblog.com/gitflow-considered-harmful">GitFlow considered harmful</a></li>
|
||
<li><a href="http://luci.criosweb.ro/a-real-life-git-workflow-why-git-flow-does-not-work-for-us/">Why git flow does not work for us</a></li>
|
||
</ul>
|
||
<p>其中有个问题就是因为分支太多,所以会出现 git log 混乱的局面。具体来说,主要是 git-flow 使用<code>git merge --no-ff</code>来合并分支,在 git-flow 这样多个分支的环境下会让你的分支管理的 log 变得很难看。如下所示,左边是使用–no-ff 参数在多个分支下的问题。</p>
|
||
<p><img src="assets/13a78e9d493ba2737c3d6b8431be47b8.png" alt="img" /></p>
|
||
<p>所谓–no-ff 参数的意思是——no fast forward 的意思。也就是说,合并的方法不要把这个分支的提交以前置合并的方式,而是留下一个 merge 的提交。这是把双刃剑,我们希望我们的–no-ff 能像右边那样,而不是像左边那样。</p>
|
||
<p>对此的建议是:只有 feature 合并到 developer 分支时,使用–no-ff 参数,其他的合并都不使用–no-ff 参数来做合并。</p>
|
||
<p>另外,还有一个问题就是,在开发得足够快的时候,你会觉得同时维护 Master 和 Developer 两个分支是一件很无聊的事,因为这两个分支在大多数情况下都是一样的。包括 Release 分支,你会觉得创建的这些分支太无聊。</p>
|
||
<p>而你的整个开发过程也会因为这么复杂的管理变得非常复杂。尤其当你想回滚某些人的提交时,你就会发现这事似乎有点儿不好干了。而且在工作过程中,你会来来回回地切换工作的分支,有时候一不小心没有切换,就提交到了不正确的分支上,你还要回滚和重新提交,等等。</p>
|
||
<p>GitLab 一开始是 GitFlow 的坚定支持者,后来因为这些吐槽,以及 Hacker News 和 Reddit 上大量的讨论,GitLab 也开始不玩了。他们写了<a href="https://about.gitlab.com/2014/09/29/gitlab-flow/">一篇 blog</a>来创造了一个新的 Workflow——GitLab Flow,这个 GitLab Flow 是基于 GitHub Flow 来做的(参看:<a href="http://scottchacon.com/2011/08/31/github-flow.html"> GitHub Flow</a> )。</p>
|
||
<h2>GitHub Flow</h2>
|
||
<p>所谓 GitHub Flow,其实也叫 Forking flow,也就是 GitHub 上的那个开发方式。</p>
|
||
<ol>
|
||
<li>每个开发人员都把“官方库”的代码 fork 到自己的代码仓库中。</li>
|
||
<li>然后,开发人员在自己的代码仓库中做开发,想干啥干啥。</li>
|
||
<li>因此,开发人员的代码库中,需要配两个远程仓库,一个是自己的库,一个是官方库(用户的库用于提交代码改动,官方库用于同步代码)。</li>
|
||
<li>然后在本地建“功能分支”,在这个分支上做代码开发。</li>
|
||
<li>这个功能分支被 push 到开发人员自己的代码仓库中。</li>
|
||
<li>然后,向“官方库”发起 pull request,并做 Code Review。</li>
|
||
<li>一旦通过,就向官方库进行合并。</li>
|
||
</ol>
|
||
<p>这就是 GitHub 的工作流程。</p>
|
||
<p>如果你有“官方库”的权限,那么就可以直接在“官方库”中建功能分支开发,然后提交 pull request。通过 Code Review 后,合并进 Master 分支,而 Master 一旦有代码被合并就可以马上 release。</p>
|
||
<p>这是一种非常 Geek 的玩法。这需要一个自动化的 CI/CD 工具做辅助。是的,CI/CD 应该是开发中的标配了。</p>
|
||
<h2>GitLab Flow</h2>
|
||
<p>然而,GitHub Flow 这种玩法依然会有好多问题,因为其虽然变得很简单,但是没有把我们的代码和我们的运行环境给联系在一起。所以,GitLab 提出了几个优化点。</p>
|
||
<p>其中一个是引入环境分支,如下图所示,其包含了预发布(Pre-Production)和生产(Production)分支。</p>
|
||
<p><img src="assets/c9cf817612cc9d474cd253d26344e184.png" alt="img" /></p>
|
||
<p>而有些时候,我们还会有不同版本的发布,所以,还需要有各种 release 的分支。如下图所示。Master 分支是一个 roadmap 分支,然后,一旦稳定了就建稳定版的分支,如 2.3.stable 分支和 2.4.stable 分支,其中可以 cherry-pick master 分支上的一些改动过去。</p>
|
||
<p><img src="assets/ed94b250461ca2bf6d7faa2d0aaa1a96.png" alt="img" /></p>
|
||
<p>这样也就解决了两个问题:</p>
|
||
<ul>
|
||
<li>环境和代码分支对应的问题;</li>
|
||
<li>版本和代码分支对应的问题。</li>
|
||
</ul>
|
||
<p>老实说,对于互联网公司来说,环境和代码分支对应这个事,只要有个比较好的 CI/CD 生产线,这种环境分支应该也是没有必要的。而对于版本和代码分支的问题,我觉得这应该是有意义的,但是,最好不要维护太多的版本,版本应该是短暂的,等新的版本发布时,老的版本就应该删除掉了。</p>
|
||
<h1>协同工作流的本质</h1>
|
||
<p>对于上面这些各式各样的工作流的比较和思考,虽然,我个人非常喜欢 GitHub Flow,在必要的时候使用上 GitLab 中的版本或环境分支。不过,我们现实生活中,还是有一些开发工作不是以功能为主要,而是以项目为主的。也就是说,项目的改动量可能比较大,时间和周期可能也比较长。</p>
|
||
<p>我在想,是否有一种工作流,可以面对我们现实工作中的各种情况。但是,我想这个世界太复杂了,应该不存在一种一招鲜吃遍天的放之四海皆准的银弹方案。所以,我们还要根据自己的实际情况来挑选适合我们的协同工作的方式。</p>
|
||
<p>而代码的协同工作流属于 SCM(Software Configuration Management)的范畴,要挑选好适合自己的方式,我们需要知道软件工程配置管理的本质。根据这么多年来我在各个公司的经历,有互联网的,有金融的,有项目的,有快速迭代的等,我认为团队协同工作的本质不外乎这么几个事儿。</p>
|
||
<ol>
|
||
<li>不同的团队能够尽大可能地并行开发。</li>
|
||
<li>不同软件版本和代码的一致性。</li>
|
||
<li>不同环境和代码的一致性。</li>
|
||
<li>代码总是会在稳定和不稳定间交替。我们希望生产线上的代码总是能对应到稳定的代码上来。</li>
|
||
</ol>
|
||
<p>基本上述的四个事儿,上述的工作流大都是在以建立不同的分支,来做到开发并行、代码和环境版本一致,以及稳定的代码。</p>
|
||
<p>要选择适合自己的协同工作流,我们就不得不谈一下软件开发的工作模式。</p>
|
||
<p>首先,我们知道软件开发的趋势一定是下面这个样子的。</p>
|
||
<ul>
|
||
<li><strong>以微服务或是 SOA 为架构的方式</strong>。一个大型软件会被拆分成若干个服务,那么,我们的代码应该也会跟着服务拆解成若干个代码仓库。这样一来,我们的每个代码仓库都会变小,于是我们的协同工作流程就会变简单。</li>
|
||
</ul>
|
||
<p>对于每个服务的代码仓库,我们的开发和迭代速度也会变得很快,开发团队也会跟服务一样被拆分成多个小团队。这样一来, Gitflow 这种协同工作流程就非常重了,而 GitHub 这种方式或是功能分支这种方式会更适合我们的开发。</p>
|
||
<ul>
|
||
<li><strong>以 DevOps 为主的开发流程</strong>。DevOps 关注于 CI/CD,需要我们有自动化的集成测试和持续部署的工具。这样一来,我们的代码发布速度就会大大加快,每一次提交都能很快地被完整地集成测试,并很快地发布到生产线上。</li>
|
||
</ul>
|
||
<p>于是,我们就可以使用更简单的协同工作流程,不需要维护多个版本,也不需要关注不同的运行环境,只需要一套代码,就可以了。GitHub Flow 或是功能分支这种方式也更适应这种开发。</p>
|
||
<p>你看,如果我们将软件开发升级并简化到 SOA 服务化以及 DevOps 上来,那么协同工作流就会变得非常简单。所以,协同工作流的本质,并不是怎么玩好代码仓库的分支策略,而是玩好我们的软件架构和软件开发流程。</p>
|
||
<p>当然,服务化和 DevOps 是每个开发团队需要去努力的目标,但就算是这样,也有某些情况我们需要用重的协同工作的模式。比如,整个公司在做一个大的升级项目,这其中会对代码做一个大的调整(很有可能是一次重大的重构)。</p>
|
||
<p>这个时候,可能还有一些并行的开发需要做,如一些小功能的优化,一些线上 Bug 的处理,我们可能还需要在生产线上做新旧两个版本的 A/B 测试。在这样的情况下,我们可能会或多或少地使用 GitFlow 协同工作流。</p>
|
||
<p>但是,这样的方式不会是常态,是特殊时期,我们不可能隔三差五地对系统做架构或是对代码做大规模的重构。所以,在大多数情况下,我们还是应该选择一个比较轻量的协同工作流,而在特殊时期特例特办。</p>
|
||
<p>最后,让我用一句话来结束这篇文章——<strong>与其花时间在 Git 协同工作流上,还不如把时间花在调整软件架构和自动化软件生产和运维流程上来,这才是真正简化协同工作流程的根本</strong>。</p>
|
||
<p>(这篇文章中有大量的 Git 命令,很难用音频体现出来,所以没有录制音频,还望谅解。)</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/左耳听风/030 编程范式游记(3) - 类型系统和泛型的本质.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/左耳听风/032 推荐阅读:分布式数据调度相关论文.md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099780fda413cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
</script>
|
||
</html>
|