<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Controle-De-Versao | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/controle-de-versao/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Mon, 19 Oct 2026 23:20:48 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/controle-de-versao/index.xml" rel="self" type="application/rss+xml"/><item><title>Criei um CLI pra Checar Pendências no Meu GitHub: ghpending</title><link>https://akitaonrails.github.io/2026/05/23/criei-cli-pra-checar-pendencias-github-ghpending/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/05/23/criei-cli-pra-checar-pendencias-github-ghpending/</guid><pubDate>Sat, 23 May 2026 22:00:00 -0300</pubDate><description>&lt;p&gt;Depois da minha &lt;a href="https://akitaonrails.github.io/2026/05/14/terminando-maratona-ia-sucesso-ou-fracasso/"&gt;maratona de IA&lt;/a&gt;, fiquei com dezenas de repositórios públicos no GitHub pra cuidar. Vários deles têm contribuidores postando issues e mandando pull requests. O problema é que abrir o navegador, entrar em cada repo, ver se tem alguma coisa nova esperando, vira um ritual cansativo que eu acabo evitando. E quando evito, fico semanas sem responder os colaboradores.&lt;/p&gt;
&lt;p&gt;Sei que tem várias ferramentas que resolvem isso. Mas eu queria algo bem simples e rápido pra rodar no terminal e ver pendências de todos os meus projetos de uma vez só. Então fiz: o &lt;strong&gt;&lt;a href="https://github.com/akitaonrails/ghpending"target="_blank" rel="noopener"&gt;ghpending&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Depois da minha <a href="/2026/05/14/terminando-maratona-ia-sucesso-ou-fracasso/">maratona de IA</a>, fiquei com dezenas de repositórios públicos no GitHub pra cuidar. Vários deles têm contribuidores postando issues e mandando pull requests. O problema é que abrir o navegador, entrar em cada repo, ver se tem alguma coisa nova esperando, vira um ritual cansativo que eu acabo evitando. E quando evito, fico semanas sem responder os colaboradores.</p>
<p>Sei que tem várias ferramentas que resolvem isso. Mas eu queria algo bem simples e rápido pra rodar no terminal e ver pendências de todos os meus projetos de uma vez só. Então fiz: o <strong><a href="https://github.com/akitaonrails/ghpending"target="_blank" rel="noopener">ghpending</a></strong>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/23/ghpending/ghpending-screenshot.png" alt="Saída do ghpending no terminal: tabela compacta listando cada repo monitorado com contagem de issues e pull requests abertos, autor da última atividade e há quanto tempo."  loading="lazy" /></p>
<p>Roda em Linux e macOS. Eu testei em Linux (Arch + Omarchy) mas ainda não rodei no macOS. Se você for testar lá e topar com algum bug, abre uma issue lá: <a href="https://github.com/akitaonrails/ghpending/issues"target="_blank" rel="noopener">github.com/akitaonrails/ghpending/issues</a>.</p>
<h2>Instalação<span class="hx:absolute hx:-mt-20" id="instalação"></span>
    <a href="#instala%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Três caminhos, escolhe o que mais combina com seu setup.</p>
<p>Via Homebrew (macOS ou Linux):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">brew tap akitaonrails/tap <span class="o">&amp;&amp;</span> brew install ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Via Cargo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">cargo install ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Via mise:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mise use -g github:akitaonrails/ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Atualizando<span class="hx:absolute hx:-mt-20" id="atualizando"></span>
    <a href="#atualizando" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># Homebrew</span>
</span></span><span class="line"><span class="cl">brew upgrade ghpending
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Cargo</span>
</span></span><span class="line"><span class="cl">cargo install ghpending --force
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># mise</span>
</span></span><span class="line"><span class="cl">mise upgrade ghpending
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># From source</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ghpending <span class="o">&amp;&amp;</span> git pull <span class="o">&amp;&amp;</span> cargo install --path .</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Uso<span class="hx:absolute hx:-mt-20" id="uso"></span>
    <a href="#uso" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quatro comandos. O fluxo normal é rodar <code>ghpending add</code> uma vez pra configurar quais repos você quer monitorar, depois é só <code>ghpending</code> quando quiser ver o digest.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">ghpending add    <span class="c1"># pick os repos de um usuário ou org pra monitorar</span>
</span></span><span class="line"><span class="cl">ghpending        <span class="c1"># imprime o digest de issues e PRs abertos</span>
</span></span><span class="line"><span class="cl">ghpending list   <span class="c1"># mostra os repos monitorados</span>
</span></span><span class="line"><span class="cl">ghpending rm     <span class="c1"># tira repos do monitoramento via menu interativo</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <code>add</code> pergunta o usuário ou organização, lista os repos públicos, e deixa você marcar os que quer acompanhar. O username fica salvo, então rodar <code>add</code> de novo já pula essa pergunta.</p>
<p>O comando principal (<code>ghpending</code> sem argumentos) busca todos os repos monitorados em paralelo e mostra a tabela com issues e PRs abertos.</p>
<h2>Token do GitHub (opcional)<span class="hx:absolute hx:-mt-20" id="token-do-github-opcional"></span>
    <a href="#token-do-github-opcional" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Sem autenticação funciona, mas o GitHub limita em 60 requisições por hora pra anônimo. Pra subir esse teto pra 5.000 req/h, exporta um token:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nv">GITHUB_TOKEN</span><span class="o">=</span><span class="k">$(</span>gh auth token<span class="k">)</span> ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ele é lido silenciosamente na inicialização, não precisa configurar nada.</p>
<h2>Onde fica o config<span class="hx:absolute hx:-mt-20" id="onde-fica-o-config"></span>
    <a href="#onde-fica-o-config" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>Linux: <code>~/.config/ghpending/config.toml</code></li>
<li>macOS: <code>~/Library/Application Support/ghpending/config.toml</code></li>
</ul>
<p>Você pode editar direto se quiser reordenar ou trocar o <code>user</code>. Estrutura:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">user</span> <span class="p">=</span> <span class="s2">&#34;akitaonrails&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">repos</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;akitaonrails/ai-memory&#34;</span><span class="p">,</span> <span class="s2">&#34;akitaonrails/ai-jail&#34;</span><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Por que esse CLI existe<span class="hx:absolute hx:-mt-20" id="por-que-esse-cli-existe"></span>
    <a href="#por-que-esse-cli-existe" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A real é simples: ferramenta que eu fizesse pra mim mesmo, eu uso. Ferramenta de outro autor, sempre tem fricção (login web, opinião arquitetural diferente, feature que faltou ou sobrou). Como o objetivo era reduzir o atrito de checar 30 repos por semana, melhor 200 linhas de Rust escritas num sábado à tarde do que aprender um stack novo.</p>
<p>Agora espero conseguir responder as pendências dos colaboradores mais rápido. Se você achar útil, é MIT, PRs são bem-vindos.</p>
]]></content:encoded><category>ghpending</category><category>automacao</category><category>ferramentas-de-desenvolvimento</category><category>controle-de-versao</category></item><item><title>[Akitando] #147 - Criptografia na Prática - Certificados, BitTorrent, Git, Bitcoin</title><link>https://akitaonrails.github.io/2023/11/10/akitando-147-criptografia-na-pratica-certificados-bittorrent-git-bitcoin/</link><guid isPermaLink="true">https://akitaonrails.github.io/2023/11/10/akitando-147-criptografia-na-pratica-certificados-bittorrent-git-bitcoin/</guid><pubDate>Fri, 10 Nov 2023 10:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/iAA8NrfQtHo"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Como que se geram chaves secretas pra encriptação? Pra que servem pares de chaves? Como certificados funcionam? Qual a diferença de impressão digital e assinatura digital? Como tudo isso funciona junto e chega em coisas como BitTorrent, Git ou Bitcoin?&lt;/p&gt;
&lt;p&gt;Já tinha feito 2 videos falando do básico de criptografia, hoje é dia de ver como essas coisas funcionam na prática e são usados em aplicações de verdade.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/iAA8NrfQtHo"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Como que se geram chaves secretas pra encriptação? Pra que servem pares de chaves? Como certificados funcionam? Qual a diferença de impressão digital e assinatura digital? Como tudo isso funciona junto e chega em coisas como BitTorrent, Git ou Bitcoin?</p>
<p>Já tinha feito 2 videos falando do básico de criptografia, hoje é dia de ver como essas coisas funcionam na prática e são usados em aplicações de verdade.</p>
<h2>Capítulos<span class="hx:absolute hx:-mt-20" id="capítulos"></span>
    <a href="#cap%c3%adtulos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>00:00:00 - Intro</li>
<li>00:01:28 - CAP 1 - Criptografia Simétrica - OpenSSL</li>
<li>00:10:11 - CAP 2 - Criptografia Assimétrica - Chaves RSA</li>
<li>00:17:06 - CAP 3 - Certificados Digitais - Let&rsquo;s Encrypt</li>
<li>00:29:18 - CAP 4 - Impressões e Assinaturas Digitais - Hashing</li>
<li>00:38:54 - CAP 5 - Picotando Arquivos - Introdução a BitTorrent</li>
<li>00:46:03 - CAP 6 - Entendendo Links Magnéticos - BitTorrent</li>
<li>00:53:52 - CAP 7 - Árvores de Merkle - Escalando BitTorrent</li>
<li>01:01:13 - CAP 8 - Git e Merkle - Reescrevendo a História</li>
<li>01:06:10 - CAP 9 - Juntando as Peças - BitTorrent e NoSQL</li>
<li>01:09:56 - CAP 10 - As Últimas Peças - De ZFS a Bitcoin</li>
<li>01:13:03 - Bloopers</li>
</ul>
<h2>Links<span class="hx:absolute hx:-mt-20" id="links"></span>
    <a href="#links" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/advanced-encryption-standard-instructions-aes-ni.html"target="_blank" rel="noopener">Intel® Advanced Encryption Standard Instructions (AES-NI)</a></li>
<li><a href="https://www.veracrypt.fr/en/Downloads.html"target="_blank" rel="noopener">VeraCrypt - Free Open source disk encryption with strong security for the Paranoid</a></li>
<li><a href="https://wiki.archlinux.org/title/Rng-tools"target="_blank" rel="noopener">Rng-tools - ArchWiki (archlinux.org)</a></li>
<li><a href="https://wiki.gentoo.org/wiki/Trusted_Platform_Module"target="_blank" rel="noopener">Trusted Platform Module - Gentoo wiki</a></li>
<li><a href="https://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb"target="_blank" rel="noopener">How to choose an AES encryption mode (CBC ECB CTR OCB CFB)? - Stack Overflow</a></li>
<li><a href="https://crypto.stackexchange.com/questions/12823/ecdsa-vs-ecies-vs-ecdh"target="_blank" rel="noopener">encryption - ECDSA vs ECIES vs ECDH - Cryptography Stack Exchange</a></li>
<li><a href="https://www.kb.cert.org/vuls/id/836068"target="_blank" rel="noopener">VU#836068 - MD5 vulnerable to collision attacks (cert.org)</a></li>
<li><a href="https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html"target="_blank" rel="noopener">Google Online Security Blog: Announcing the first SHA1 collision (googleblog.com)</a></li>
<li><a href="https://www.gnupg.org/"target="_blank" rel="noopener">The GNU Privacy Guard (gnupg.org)</a></li>
<li><a href="https://wiki.manjaro.org/index.php/How-to_verify_GPG_key_of_official_.ISO_images"target="_blank" rel="noopener">How-to verify GPG key of official .ISO images - Manjaro</a></li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Criptografia é um assunto super extenso, hoje quero mostrar um pouco mais na prática alguns componentes comuns que vocês vão esbarrar mais cedo ou mais tarde. Já tinha feito 2 videos sobre a teoria, depois assistam.</p>
<p>Eu não sou especialista em segurança, sou um desenvolvedor que se interessou pelo assunto, só. Por isso não considerem que tudo que vou falar é o único jeito ou é completo. É apenas o que eu considero o mínimo, de desenvolvedor pra desenvolvedor. Quem for especialista na área, por favor, complementem nos comentários abaixo.</p>
<p>Quem evitou o assunto até agora, hoje é o dia de cair de cabeça. Quero mostrar como encriptação de dados funciona, como certificados digitais funcionam, e como tudo isso é aplicado no mundo real, passo a passo. Na segunda metade vou explicar como um BitTorrent funciona e quais tecnologias tornam ela similar a um Git. Vamos lá.</p>
<p>(&hellip;.)</p>
<p>Este é um video pra quem já saiu da faculdade mas ainda não brincou o suficiente com criptografia, infelizmente você vai precisar ter um mínimo de base pra prosseguir e desta vez não vou segurar muito na mão. Prestem bastante atenção, façam anotações se precisar. Se não entenderem este começo, não vão conseguir seguir o raciocínio depois. Pausem, voltem e revejam, até entender. A primeira metade, vale até abrir o terminal e tentar os comandos vocês mesmos pra sentirem na mão como funcionam. Videos densos como de hoje não é pra assistir só uma vez.</p>
<p>Toda CPU moderna tem instruções em hardware pra encriptar dados. Se você tem Mac e habilitou File Vault, se tem Windows e habilitou BitLocker, ou se usa Linux com LUKS, todos usam o algoritmo de encriptação AES, provavelmente a variante de 256 bits. Em CPUs novas é muito raro sentir que o sistema ficou mais lento por causa disso, por isso recomendo manter ativado, especialmente em notebooks. Caso sejam roubados, a chance de alguém acessar seus dados é perto de zero. Se quiser aumentar performance, o certo não é desligar encriptação e sim comprar um SSD melhor.</p>
<p>Há quem ainda ache que só porque precisa fazer login na sua conta quando liga a máquina, está seguro. Mas não. Se remover o HD ou SSD do seu notebook e ligar numa outra máquina, vou ter acesso a todos os arquivos se não estiverem encriptados. Seu login e senha não fazem nenhuma diferença. Login em sistema operacional só evita que um usuário tenha acesso aos arquivos de outro usuários enquanto a máquina está ligada e ninguém tem acesso físico ao hardware. Se tiver acesso físico, não faz diferença. Isso vale pra qualquer sistema operacional.</p>
<p>No episódio de Sua Segurança é uma Droga eu demonstrei o uso do Veracrypt. Diferente de BitLocker do Windows, por exemplo, que só dá suporte a AES porque é o mais performático, o Veracrypt permite escolher outros algoritmos. Pra coisas muito grandes vai ser mais lento, mas por outro é o mais seguro. Um HD com BitLocker, pode ser difícil de quebrar, mas sabendo que é AES podemos focar só nisso. Mas uma imagem encriptada com Veracrypt, não sabemos nem o algoritmo usado, e isso aumenta a segurança exponencialmente. Por isso que pra coisas absolutamente críticas, é melhor usar Veracrypt. Pro dia a dia AES de BitLocker ou FileVault é mais que suficiente.</p>
<p>Vou repetir: se você tem dados importantes, num notebook que carrega embaixo do braço, pra trabalhar de um starbucks da vida, espero que não seja burro de andar por aí sem encriptação. Mas como que funciona esse tipo de encriptação? Vamos dar alguns exemplos.</p>
<p>Um dos pacotes de segurança e criptografia mais conhecidos e utilizados do mundo é o famoso OpenSSL. É um pacote gigante, com praticamente tudo que se precisa pra lidar com criptografia. Existem outras alternativas como LibreSSL, BoringSSL do Google, NSS da Mozilla e outros, mas vou focar no OpenSSL porque ainda é um dos mais usados. De um terminal num Linux, é assim podemos gerar uma chave aleatória. Vamos chamar de &ldquo;key.bin&rdquo;</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>openssl rand -out key.bin 32
hexdump key.bin</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Geramos uma chave binária de 32 bytes ou 256 bits. Essa chave é segura o suficiente pra maioria dos usos hoje. Pra ser mais seguro deveríamos adicionar entropia externa, por exemplo, com um hardware externo ou o chip de TPM que vem em máquinas modernas usando algo como TPM2-TSS. Entropia inicial é extremamente importante pra definir a força de uma chave. Mas só pra usar de exemplo, isso vai servir.</p>
<p>Por ser importante, vale mais uma tangente. Tudo em criptografia depende de um PRNG ou Pseudo Random Number Generator. Não existe número verdadeiramente aleatório, existem funções que, dado um número inicial, consegue ir &ldquo;cuspindo&rdquo; números que parecem aleatórios. Se colocarmos numa distribuição estatística, seria uniforme, onde todos os números tem mais ou menos as mesmas chances de aparecer, sem nenhum viés.</p>
<p>Mais do que isso, os números gerados não devem parecer diferentes de aleatoriedade verdadeira, devem ser resistentes a backtracking. Não deveria ser possível reconstruir números anteriores só vendo uma sequência de números gerados, ou seja, devem ser resistentes à previsão, não podendo ser fácil prever os próximos. Devem ser difíceis ou impossíveis de serem reproduzidos.</p>
<p>Bons números aleatórios são fornecidos pelo sistema operacional. No caso de Linux, existe um dispositivo em &ldquo;/dev/random&rdquo; que é de onde pacotes como OpenSSL se conectam. Podemos aumentar a entropia dele usando um serviço como &ldquo;rngd&rdquo;. Num Arch é só instalar com &ldquo;pacman -S rngd&rdquo;, habilitar e iniciar com &ldquo;systemctl enable&rdquo; e &ldquo;systemctl start&rdquo;. Esse serviço vai tentar puxar entropia de dispositivos de hardware ou do chip TPM do seu PC, se ele for moderno.</p>
<p>Chip TPM, de Trusted Platform Module, é o que gerou controvérsia quando o Windows 11 inicialmente ia obrigar todo mundo a ter. No fim voltaram atrás porque muita gente não ia ter e reclamaram muito. Mas o certo é realmente ter esse chip. Toda placa mãe tem um slot pra esse chip, mas em modelos baratos ele vem vazio, pra você comprar separadamente. Dentre as várias funções de segurança que ele fornece, está entropia via hardware. Esse serviço &ldquo;rngd&rdquo; vai alimentar o &ldquo;/dev/random&rdquo; que, por sua vez, é usado pelo OpenSSL.</p>
<p>Várias classes de malware e vírus são bloqueados ao ter um chip TPM devidamente configurado, em particular rootkits, além de melhorar a encriptação com BitLocker. Cheque se sua máquina tem, ou se tem suporte de instalar. Se tiver, compre e instale. Todo Mac e iPhone tem um chip similar instalado de fábrica, o chip T2, que é uma das razões de um Mac ser mais seguro que um PC mais barato. Nada disso torna você 100% seguro, mas certamente melhora sua defesa.</p>
<p>Voltando ao exemplo, agora vou criar um arquivo idiota chamado &ldquo;test.txt&rdquo; com a boa e velha frase &ldquo;Hello World&rdquo;. E vamos encriptar usando nossa chave. Esta linha de comando de OpenSSL vai usar AES 256 em modo CBC, com salt, passando o test.txt e devolvendo test.txt.enc, usando a chave key.bin que acabamos de gerar. Pronto. Se dermos um &ldquo;cat&rdquo; no novo arquivo, vai devolver um lixo binário. Podemos ver com hexdump e não tem nada a ver mais com o arquivo original. Esse é o resultado esperado.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>openssl enc -aes-256-cbc -salt -in test.txt -out test.txt.enc -pass file:./key.bin

cat test.txt.enc
Salted__����z�F�ğQw8��#�- ���4

hexdump test.txt.enc
0000000 6153 746c 6465 5f5f 1ae2 88f0 d67a cc46
0000010 9fc4 7751 fc38 23b4 2da6 f020 aaf5 0a34
0000020

hexdump test.txt
0000000 6548 6c6c 206f 6f57 6c72 0a64          
000000c</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mais um adendo aqui. Só pra simplificar o exemplo, escolhi AES 256 em modo CBC ou Cipher Block Chaining. Ele é melhor que o anterior ECB mas é sensitivo a erros de ciphertext e é vulnerável a ataques de padding oracles se não for implementado corretamente.
Além de CBC existe Cipher Feedback ou CFB, Output Feedback ou OFB, Counter ou CTR e Galois/Counter Mode ou GCM. Se precisar escolher, use GCM.</p>
<p>Estou usando CBC com um salt, que já é melhor que só CBC sozinho, mas se for usar GCM ele vai gerar um tag de autenticação e anexar no arquivo encriptado. Mas o certo é se preocupar com o tráfego desse tag, porque ele precisa ser transportado de forma segura e não num anexo de email, aberto, por exemplo. Além disso tem que se preocupar com Initialization Vectors, IVs. Por isso a maioria dos tutoriais por aí, assim como meu exemplo, usam CBC: porque é mais simples de explicar.</p>
<p>Entenda isso: pareceu que falei uma sopa de letrinhas a toa só pra dificultar, mas foi pra demonstrar que a maioria dos tutoriais vai escolher a opção mais simples de explicar, que costuma ser a mais insegura. Por isso eu falo que se você não estudou criptografia a fundo, não deveria tomar decisões de criptografia: vai usar errado e vai tornar a encriptação do seu sistema mais inseguro.</p>
<p>Era uma das minhas críticas quando JWT, o JSON Web Authentication, por exemplo, foi lançado lá atrás: ele exigia que o desenvolvedor soubesse tomar essas decisões de algoritmos e modos, mas quase ninguém sabe e nem vai aprender certo. Ele não deveria permitir as combinações obviamente mais inseguras. Só que ao fazer isso, tornaria a usabilidade mais difícil. É sempre um trade-off: quanto mais usável tentar fazer alguma coisa, mais inseguro vai ser. Segurança é complicado mesmo.</p>
<p>Agora eu poderia enviar o arquivo encriptado pra outra pessoa pela internet e, se ela tiver a minha chave, conseguiria decriptar com uma linha de comando parecida com esta. OpenSSL modo AES 256 CBC recebendo o test.txt.enc encriptado e descriptografando em test.txt.dec passando a chave key.bin. Com um comando &ldquo;cat&rdquo; vemos que foi com sucesso.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>openssl enc -aes-256-cbc -d -in test.txt.enc -out test.txt.dec -pass file:./key.bin

cat test.txt.dec
Hello World</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Bacana, de forma simplificada, é assim que um BitLocker ou FileVault ou Veracrypt da vida estaria lidando seus dados. Eu digo simplificando porque aqui dei exemplo lidando com arquivos inteiros, mas sistemas de arquivos lidam com stream de blocos. Enfim, no HD físico só teria o equivalente àquele arquivo &ldquo;test.txt.enc&rdquo;, a versão encriptada. E precisa ter essa chave em algum lugar pra conseguir ler o conteúdo original de volta. Isso normalmente seria pesado de computar, mas a encriptação e decriptação não é feita via software e sim via instruções direto da CPU, em hardware, por isso o processo é mais acelerado e quase não sentimos, na prática.</p>
<p>Mas no exemplo anterior temos um problema gigantesco. Se o objetivo de encriptar algum arquivo for pra poder enviar de forma segura pra outra pessoa, como fazemos pra enviar a chave de forma segura também? A única forma seria essa pessoa vir até minha casa, trazendo um pendrive, e eu gravar a chave direto no pendrive, sem nunca trafegar pela internet. Mas isso não é prático. Como consigo passar uma chave pra outra pessoa de forma que ninguém na internet consiga interceptar ou adulterar?</p>
<p>Aqui entra o conceito de chaves assimétricas. Novamente, explico sobre isso em detalhes no 2o video de criptografia. Mas deixa eu dar um exemplo prático. De volta ao terminal, vamos usar OpenSSL pra gerar um par de chaves. Literalmente são dois arquivos, um chamado private.pem e outro chamado public.pem.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private.pem -out public.pem</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Enfim, escolhi a opção de algoritmo RSA de 2048 bits. E eu sei que já deve ter gente indo nos comentários reclamar &ldquo;porra Akita, você devia ter usado curva elíptica em vez de RSA&rdquo;. Eu expliquei o que são esses algoritmos e as diferenças no meu video antigo, mas resumindo, sim, Elliptic Curve Cryptograph ou ECC é mais forte que RSA. Uma chave ECC de 256 bits é mais forte que uma chave RSA de 3072 bits, mais de 10 vezes o tamanho.</p>
<p>O problema é que OpenSSL não oferece comandos simples pra encriptar dados arbitrários com chaves ECC porque ECC é normalmente usado pra coisas como tráfego de chaves ou ECDH, assinatura digital que é ECDSA ou com mecanismos de encapsulamento de chave em ECIES. Ou seja, não é simples pra sair encriptando qualquer dado arbitrário. Eu poderia usar ECDH mas a ideia desse video não é entrar nas minúcias de ECC e sim dar uma noção geral de criptografia e, pra esse fim, RSA vai ser mais que suficiente. RSA é mais simples pra usar e ensinar, mas se estiverem fazendo algo sério, estudem tudo que acabei de falar sobre ECC.</p>
<p>E o que são esses arquivos RSA que foram gerados? Podemos usar o comando &ldquo;cat&rdquo; e vai listar o conteúdo. É a versão em Base64 da chave binária. Base64 é como encodamos um binário em caracteres ASCII que dá pra mostrar na tela. Convertemos 8 bits em 7 bits, por isso ele fica um pouco maior. O private.pem é mais longo e o public.pem é mais curto. Olhem só.</p>
<p>❯ cat private.pem
&mdash;&ndash;BEGIN PRIVATE KEY&mdash;&ndash;
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5cMQZZd85emyr
oL9cvrR522tmBBlcwpF7QyobGwAmk8smL2HoMf207LJl3wi7JE736qqWdme/Rwr8
Zsz8ZOAeiud4SIk/NbfezMoLVkUxQGOg0GprpYciUVUp9e/3HKl5PS9Yr9lU6QN8
Bs7gY96DohUrGz/hQR7G550O+/ITV+qm3VZytPiH8tQAXrueqC8lf/Ly97UnlfLr
bauftw6QIw1ldI8NjxPIcwbNot+BfXcN6kiJifttdLi7/kEaAboecjYxcoipntjb
RdAntX/Y6xluwKtKRqrTs1XxHAPtEEQrVvfc6CjM2kLiFe//r3oZCLhHAAyZhJ6X
+J3zt0gXAgMBAAECggEAArvbsDzVEc2xEhtVvDd1hVcLmh6VBHiRLijbKI4j2O2/
MMoXCUDKFY49wmG2M71rypzeNJynQpB4uX2HJ8YRmxxUyuxVgFCO7DfHAhbHVga1
0Uub811RL4YqbsAY8GLZnsIc/hONl9ey555ZaMEgKSmiYpciwHPg0RwzWmKy0BUq
bFRLIHhfJfxzIpHxw3LExW///jyVw56sK9IE260pYqf8MxX/mhxPllj5VLNbctzj
YO1W+EjeUlolI19xSMJSlqW2bGeoXIalddDR0552QrmZAXEpRgI9V+NoQdSKnrXr
gHJS2uav2ORqoxDp3+7np0dnt06dtD/rzevrDtoDzQKBgQD0IiKU9L0QbZDWz8SH
b+SF5IymsMKRykyAXkghEjyzMXbbgnOHo7Eesc4iU07k1kzOKgtCcwt0glnEwKei
qlzpDOZoW+UdloWVUXSuLFcW1Oy67B8w/YLp6PFBce7YMjij1hOvJSQS5unEfmt2
8PZ01JUA9m49DGfXTUrnzX6mRQKBgQDCdEnZLkIyo9RKBjsrd57+Yp4KtJuAqn8o
fuBh33qxnrImkA/RMwrcUQIoLJly3DUk8UhJRIIqCLOp4BHc9WDkxa+7bqLl7OXG
qWfyvwNLVY1M98oMbU7ESJPZb2BHLNrWgnD/tGYLrRQq+WbVkh2B7b+31SP63UZz
sWG3W8TYqwKBgQDAZSZbvYRCLha3M4XuNoAiBLvTofm0a3lesO6nIXg0wAard2MU
wiwTxIxvALhsH3boArpc481jRFb6d9mkmB4gi8oI9GLbroQX6NnLaaY4mBT4Op1a
Pm+tZ6a5rNBOEUYR1+QrY1nKi7Au6nJGih+wctf6qjq2YdB0HLWreIcXAQKBgAFj
ezOF1QsviFCPAtUooYYHhkFlUMb2cLoFTezk/6dNDtCfrr07tBQmr/FBgEcFs4tz
/aQoTRWnwkCwTPBbADoh/0NDYv5pVqq4gvmEy606CyxFiLfV//3+1q9y0Ckx9M4b
WFs7Go9yyPYVmdpklLlczUWuy/b6pYol9iCtExbJAoGAWgDbmrwo6Pg2ro+f98i6
K761tCW43rB2Trfl0XyB38OT/8geW14/BLBJdRp3XChg60b2UaVVwLhp3WvfKs9u
FDFmsSSdnSs+X+JNtfnixbwXJl5I3z+s3Uz9wxRY/82RHM2f1FgQ22JFV7Es2H1U
XENsUtHZ/bgOggoZTP3uEl0=
&mdash;&ndash;END PRIVATE KEY&mdash;&ndash;</p>
<p>/tmp
❯ cat public.pem
&mdash;&ndash;BEGIN PUBLIC KEY&mdash;&ndash;
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuXDEGWXfOXpsq6C/XL60
edtrZgQZXMKRe0MqGxsAJpPLJi9h6DH9tOyyZd8IuyRO9+qqlnZnv0cK/GbM/GTg
HorneEiJPzW33szKC1ZFMUBjoNBqa6WHIlFVKfXv9xypeT0vWK/ZVOkDfAbO4GPe
g6IVKxs/4UEexuedDvvyE1fqpt1WcrT4h/LUAF67nqgvJX/y8ve1J5Xy622rn7cO
kCMNZXSPDY8TyHMGzaLfgX13DepIiYn7bXS4u/5BGgG6HnI2MXKIqZ7Y20XQJ7V/
2OsZbsCrSkaq07NV8RwD7RBEK1b33OgozNpC4hXv/696GQi4RwAMmYSel/id87dI
FwIDAQAB
&mdash;&ndash;END PUBLIC KEY&mdash;&ndash;</p>
<p>Arquivos ponto &ldquo;pem&rdquo; significa Privacy Enhanced Mail que é um formato de arquivo texto exatamente como dessas chaves que acabei de mostrar. É isso, um arquivo que começa com esses hifens, BEGIN, lá embaixo termina com END, e no meio é algum binário formatado com Base64. Se usa SSH, você já gerou chaves nesse formato.</p>
<p>As chaves pública e privada geradas com os comandos <code>openssl genpkey</code> e <code>openssl rsa</code> estão relacionadas por meio do algoritmo de criptografia RSA. O processo começa gerando dois números primos enormes e aleatórios. Esses números primos são mantidos em segredo e são a base da segurança do sistema. Em seguida, esses números primos são multiplicados entre si, resultando em um número chamado &lsquo;módulo&rsquo;. Este módulo é usado na criação de ambas as chaves.</p>
<p>A chave pública é derivada do módulo e de um expoente público, que é um número pré-definido e geralmente pequeno. A chave pública pode ser compartilhada com qualquer pessoa e é usada para criptografar mensagens que só podem ser descriptografadas pela chave privada correspondente.</p>
<p>Por outro lado, a chave privada é gerada usando os números primos originais e um expoente privado calculado a partir deles. A chave privada é mantida em segredo e é usada para descriptografar as mensagens criptografadas pela chave pública.</p>
<p>Embora as chaves pública e privada não sejam os números primos em si, elas são matematicamente relacionadas aos números primos utilizados durante a geração. Esse relacionamento matemático é fundamental para o funcionamento da criptografia RSA e é o que permite a comunicação segura, onde informações criptografadas com a chave pública só podem ser lidas pela chave privada correspondente, e vice-versa. Fodeu, não entendeu nada né. Vamos simplificar com um exemplo.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>openssl pkeyutl -encrypt -pubin -inkey public.pem -in key.bin -out key.bin.enc</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Digito o comando openssl pedindo pra encriptar minha chave key.bin usando a chave pública e resultando num novo arquivo key.bin.enc. Pronto, agora temos a chave simétrica original, encriptada usando a nova chave pública. Se listarmos o conteúdo com hexdump, veja como os binários são diferentes.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">❯ hexdump key.bin.enc
</span></span><span class="line"><span class="cl"><span class="m">0000000</span> 84b4 f414 <span class="m">6144</span> 087d 079a <span class="m">3036</span> 4eb7 <span class="m">5798</span>
</span></span><span class="line"><span class="cl"><span class="m">0000010</span> 28de <span class="m">7505</span> e9fe fc0e 851c 877e be59 4b97
</span></span><span class="line"><span class="cl"><span class="m">0000020</span> a5b6 05de e379 c30f 974b 9f31 a24a f567
</span></span><span class="line"><span class="cl"><span class="m">0000030</span> 68ad 0af9 d39b f770 3e20 0be8 <span class="m">3116</span> c52f
</span></span><span class="line"><span class="cl"><span class="m">0000040</span> <span class="m">7437</span> <span class="m">4224</span> 54f4 d071 b28e a5ff d29e 7a85
</span></span><span class="line"><span class="cl"><span class="m">0000050</span> e78a 8ec2 b125 38ea 204a c071 82d0 <span class="m">5526</span>
</span></span><span class="line"><span class="cl"><span class="m">0000060</span> <span class="m">8923</span> 4a84 23d6 72ea da81 434a 5f05 9e87
</span></span><span class="line"><span class="cl"><span class="m">0000070</span> 3b8d f2c1 <span class="m">5331</span> fc16 e92a 4e3b d695 <span class="m">4759</span>
</span></span><span class="line"><span class="cl"><span class="m">0000080</span> <span class="m">2472</span> <span class="m">0453</span> 34cc db67 6dbf 8fbb 92ec e52f
</span></span><span class="line"><span class="cl"><span class="m">0000090</span> be0c f295 3b5e 2aac 7fdb 0e82 bf0a 0cf8
</span></span><span class="line"><span class="cl">00000a0 f158 <span class="m">0014</span> f93e 33f0 fe77 ca3a <span class="m">3144</span> 25f8
</span></span><span class="line"><span class="cl">00000b0 877c a25f <span class="m">4012</span> <span class="m">4953</span> be15 e06e <span class="m">9894</span> a4ef
</span></span><span class="line"><span class="cl">00000c0 a399 3b4a 1efe dc57 1e85 <span class="m">5410</span> 5df3 7f95
</span></span><span class="line"><span class="cl">00000d0 c957 <span class="m">9182</span> c426 55a6 ea7e 5e0d <span class="m">1270</span> 9ab2
</span></span><span class="line"><span class="cl">00000e0 <span class="m">4781</span> 44ae <span class="m">3437</span> 886b ef0c 82d6 5d66 5c5b
</span></span><span class="line"><span class="cl">00000f0 e072 <span class="m">0888</span> bccd 55ba f217 100c <span class="m">4906</span> f45e
</span></span><span class="line"><span class="cl"><span class="m">0000100</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/tmp 
</span></span><span class="line"><span class="cl">❯ hexdump key.bin
</span></span><span class="line"><span class="cl"><span class="m">0000000</span> ccab 139c 27f3 c206 76af 952d 0fb1 2c17
</span></span><span class="line"><span class="cl"><span class="m">0000010</span> 51a5 0a43 731b 5bf4 9f3c 72b5 1e57 <span class="m">2503</span>
</span></span><span class="line"><span class="cl"><span class="m">0000020</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pra não ficar confuso vamos recapitular. Eu gerei uma chave simétrica AES chamada &ldquo;key.bin&rdquo;. Quero usar essa chave pra criptografar arquivos ou mensagens pra mandar pra uma segunda pessoa em algum lugar na internet, vamos chamar de John. Porém, não adianta criptografar a mensagem e mandar pro John porque ele precisa ter a chave. Mas não posso mandar a chave via email ou whatsapp porque aí não tem segurança nenhuma. Precisamos garantir que nada no meio do caminho possa interceptar ou adulterar minhas mensagens pro John.</p>
<p>O problema de mandar a chave direto por email é se alguém na internet, vamos chamar de Skynet, intercepte essa comunicação e fique com minha chave, sem passar pra frente. Daí ela manda uma outra chave falsificada pro John. O John não sabe que recebeu uma chave falsa. Agora eu, Fabio, vou mandar uma mensagem criptografada com minha chave que diz &ldquo;John, descobri a localização da Skynet, ele tá nas coordenadas X Y&rdquo;.</p>
<p>Skynet intercepta essa comunicação, e como já tinha interceptado minha chave, consegue facilmente abrir a mensagem. Agora ela forja uma segunda mensagem dizendo &ldquo;John, achei a Skynet, clique neste link pra ver a localização!&rdquo;, criptografa com a chave falsa e manda pro John. O John abre achando que veio de mim. Daí, na confiança, clica no link e é isso, acabou a raça humana. Game over. Eu sempre digo: nunca saia clicando em links que recebe por email ou whatsapp.</p>
<p>Mas eu sei que isso poderia acontecer. Em vez disso já tinha ensinado: &ldquo;John, cria um par de chaves RSA, e manda a chave pública pra mim, mas nunca, jamais, transmita a chave privada pra fora da sua máquina&rdquo;. Ele me manda a chave pública, por email aberto mesmo, e com essa chave eu encripto aquele arquivo &ldquo;key.bin&rdquo;. Quando o John receber a mensagem criptografada, pode rodar o seguinte comando. Openssl decriptar o arquivo key.bin.enc com a chave privada.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>openssl pkeyutl -decrypt -inkey private.pem -in key.bin.enc -out key.bin.dec</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Entenda, quando encripto alguma coisa com a chave pública do John, o único que vai conseguir abrir é o John com sua chave privada. Se eu conseguir passar minha chave simétrica key.bin pra ele, de forma segura, garantidamente só nós dois temos o segredo e podemos começar a trocar comunicação encriptada com AES e a Skynet não vai ter como interceptar nem ficar no meio do caminho tentando forjar mensagens.</p>
<p>Mas tem um pequeno defeito nesse processo. Como eu sei que a chave pública que o John me mandou veio realmente do John e não é de novo a porra da Skynet tentando me enganar? Esse é o mesmo problema que temos hoje em websites e ecommerces da vida. Como eu sei que a chave pública que recebi da Amazon é realmente da Amazon? Como eu sei que não é um site de um hacker, uma Skynet, tentando se passar pela Amazon? Vamos entender a última peça que falta: a autoridade certificadora.</p>
<p>Esse é o princípio por trás de SSL ou, mais corretamente, TLS, que todo navegador utiliza. O que acontece por trás dos panos sem você ver é o seguinte: primeiro, abrimos o navegador e digitamos &ldquo;<a href="https://amazon.com"target="_blank" rel="noopener">https://amazon.com</a>&rdquo; da vida. Importante que seja um site &ldquo;https&rdquo;. Então o navegador pede uma conexão segura pro servidor web da amazon que, por sua vez, devolve um certificado digital.</p>
<p>Um certificado digital é um pacote que contém a chave pública, a metade do par de chaves de uma Amazon. A chave privada nunca sai do servidor. Lembrem-se desse conceito: chaves privadas nunca podem tocar a internet, jamais. Mas, como eu posso garantir que não é a Skynet, minha conexão foi interceptada e recebi um certificado falso? Pra isso que existem CAs ou Certificate Authorities. Vamos ilustrar com um exemplo, porque é importante ter esse processo na cabeça.</p>
<p>De novo, o objetivo do video não é ser um tutorial. Mas se nunca viu, vou criar um novo certificado digital usando o serviço Letsencrypt associado ao meu domínio válido &ldquo;akitando.me&rdquo;. Pra criar um certificado precisa ter um domínio registrado válido. O jeito tradicional é um passo a passo super chato. É um processo já bem documentado, que tem em dezenas de lugares, só procurar no Google. Mas Letsencrypt é mais fácil porque simplifica vários passos. Pra começar precisa ter instalado o programa &ldquo;certbot&rdquo; na sua máquina.</p>
<p>Iniciamos o processo pedindo pra gerar um certificado pro meu domínio usando o método de checar um registro no DNS pra garantir que sou mesmo o dono desse domínio. Só quem é dono do domínio consegue configurar o nameserver. No caso ele gera o que chamamos de um challenge, um desafio, que é esse número aleatório aqui. Eu tenho que registrar um campo TXT no DNS do domínio com exatamente esse número. Isso não é uma senha, fica aberto mesmo. É só pra garantir que a pessoa pedindo pra gerar o certificado realmente é o dono do domínio.</p>
<p>No caso, meu DNS ou nameserver tá na AWS então abro o Route53 pra editar os registros do meu domínio &ldquo;akitando.me&rdquo;. Dependendo de onde tá seu domínio, vai ser um tela diferente, mas o princípio é o mesmo. Daí tem que esperar alguns minutos pra essa informação replicar pela internet. Se não sabe como isso de DNS funciona, não deixe de ver minha série sobre redes. Já expliquei isso lá.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo certbot certonly --manual --preferred-challenges dns -d akitando.me


Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for akitando.me

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.akitando.me.

with the following value:

m_6yRys7mMCh-jg_R8kpwCl3mx5EVK3tMp9jeIR9RFI

Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.akitando.me.
Look for one or more bolded line(s) below the line &#39;;ANSWER&#39;. It should show the
value(s) you&#39;ve just added.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue


Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/akitando.me/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/akitando.me/privkey.pem
This certificate expires on 2023-11-22.
These files will be updated when the certificate renews.

NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate&#39;s expiry date.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let&#39;s Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(screenshot route53)</p>
<p>Uma vez validado, vamos ganhar dois arquivos gravados em &ldquo;/etc/letsencrypt/live/akitando.me&rdquo;, um chamado &ldquo;fullchain.pem&rdquo;, o certificado digital propriamente dito, que vai ser servido pra todo navegador que conectar no meu servidor web, e o &ldquo;privkey.pem&rdquo;, que é a chave privada que jamais deve sair do meu servidor, nem ser transmitido pela internet de nenhuma forma.</p>
<p>Estou repetindo pra vocês nunca esquecerem: chave privada se chama &ldquo;privada&rdquo; por uma razão. Se perder essa chave privada, qualquer um pode se passar por você. E se alguém ficou pensando &ldquo;porra, mas e se realmente eu perder essa chave?&rdquo;. A solução é imediatamente gerar um novo certificado e substituir.</p>
<p>Eu não instalei de verdade essa chave porque não tenho site nesse domínio, mas se tivesse, no seu navegador iria aparecer o famoso cadeado. As informações que aparecem quando clica no cadeado é exatamente o conteúdo desse arquivo &ldquo;fullchain.pem&rdquo;. Ele contém a chave pública e está assinado por uma CA, a Central Authority, no caso, a LetsEncrypt.</p>
<p>Eis a resposta de como podemos garantir que o certificado que o site me deu é confiável. Todo navegador, Firefox, Safari, Edge, Chrome, Opera, já vem pré-instalados com vários certificados raíz, seja da Certsign, Digicert, GlobalSign. São arquivos que foram instalados juntos com seu navegador ou que seu navegador puxa do próprio sistema operacional. Um dos dois vai ter esses certificados raiz.</p>
<p>Se baixou seu navegador fora do site oficial, num site genérico de downloads, não existe garantia nenhuma. La garantia soy yo. Mas se estiver usando o Safari que já veio no seu Mac ou o Edge que já veio no seu Windows, a probabilidade é que sejam legítimos. De novo, se comprou sua máquina usada de um terceiro, e nunca formatou, não dá pra garantir nada. Nenhuma máquina que não foi você quem instalou, ou que não comprou numa loja de verdade, não tem nenhuma garantia, ponto final.</p>
<p>Podemos abrir diretamente o arquivo &ldquo;fullchain.pem&rdquo; e veja só, temos uma cadeia de certificados. Primeiro o do meu domínio. Em seguida, vemos que foi assinada pela Letsencrypt que é essa R3. E ela mesma, por sua vez, é assinada por outra autoridade, a Digital Signature Trust Co.</p>
<p>Certificados raíz podem ser instalados com o sistema operacional, ou podem vir com os navegadores. Google Chrome e outros navegadores baseados em Chromium costumam usar o que tem no sistema operacional. Firefox prefere usar certificados próprios. Num Arch Linux, como o Manjaro que eu uso, vem no pacote &ldquo;ca-certificates&rdquo; e costumam ficar no diretório &ldquo;/etc/ssl/certs&rdquo;. Olha só um trecho da listagem dos certificados. E se olharmos mais pra baixo, temos o certificado &ldquo;ISRG Root X1&rdquo; que é a raiz que vimos no certificado digital gerado pela Letsencrypt.</p>
<p>Meu navegador acha o ISRG Root X1 instalado na máquina. Ele parte do pressuposto que pode confiar em todo certificado que foi assinado por qualquer autorizada abaixo dela. É uma cadeia de assinaturas. O certificado do meu domínio akitando.me foi assinado pela chave privada da Letsencrypt que, por sua vez, tem um certificado assinado por essa ISRG que é verificado pela Digital Signature Trust que, em última instância, foi instalado no meu sistema operacional. Em última instância, confiamos que o sistema operacional não foi adulterado, porque instalamos com o ISO oficial ou já veio pré-instalado de fábrica e a máquina é nova.</p>
<p>Quando fizemos o processo do &ldquo;Certbot&rdquo;. Ele gerou aquele &ldquo;fullchain.pem&rdquo;. Começou gerando o par de chaves pública e privada. Daí empacotou no certificado, que é uma estrutura de dados com informações como o nome do domínio, email, a chave pública, etc. É só um arquivo, como se fosse um JSON da vida, por exemplo, mas num formato específico de certificado. No final, ele gera a impressão digital desse arquivo, que é um hash, usando um algoritmo como um SHA256 da vida.</p>
<p>Impressão digital é outro nome pra um hash. Quando passamos um arquivo ou algum conteúdo por um algoritmo de hashing, o resultado é uma impressão digital. Porque se alterarmos 1 bit que seja do arquivo original e refizermos o hashing, o resultado vai ser completamente diferente. Daqui a pouco vou voltar no tema de hashing. Por enquanto entenda que hashing serve pra identificar exatamente o conteúdo original de qualquer coisa. E o que chamamos de assinatura digital é o hash via chave privada de alguma autoridade.</p>
<p>Vamos em outro exemplo. YouTube.com. Olha o cadeado aqui no navegador. Se abrirmos vemos que a raíz é um tal de GTS Root R1. Voltando lá pro diretório &ldquo;/etc/ssl/certs&rdquo; achamos o arquivo do certificado e abrindo podemos ver que se trata do Google Trust Services LLC. Uma empresa do próprio Google dedicado só a gerenciar esse certificado raíz. O Google sendo gigante, é do interesse dela ter controle sobre uma autoridade certificadora também. Sim, seu computador automaticamente acredita em tudo do Google. Existem várias implicações éticas nesse tipo de coisa mas não é escopo desse video.</p>
<p>Com isso o navegador sabe que o certificado do site é &ldquo;válido&rdquo;. Daí ele pode pegar a chave pública de dentro do certificado, e usar pra encriptar uma chave simétrica. Isso estabelece o que se chama de &ldquo;conexão segura&rdquo;. Falando assim dá impressão que passamos a usar, sei lá, um cabo de rede especial que é mais seguro ou algo assim né? Mas não, nada mudou. Estamos na mesma rede física que antes. A diferença é que conseguimos comunicar de forma segura, encriptando todos os pacotes. E a partir daqui, tudo que o navegador mandar pro servidor e vice versa, vai ser encriptado com essa chave.</p>
<p>Tudo isso é feito pra garantir que ninguém no meio do caminho, nem a Skynet, nem o provedor, nem ninguém, consiga ver o que está sendo trafegado, porque vai tudo encriptado. Esse imbróglio todo de cadeias de certificados, assinaturas digitais, pares de chaves assimétricas, é a implementação de um &ldquo;triângulo de confiança&rdquo;. Segurança é quando eu e o John temos confiança num mesmo terceiro, que é uma autoridade reconhecida por ambos. Pra haver confiança, sempre precisa haver um terceiro elemento que ambos os lados confiam.</p>
<p>Voltando pra pergunta original, como eu posso confiar na chave pública do John? Como eu sei que não é a Skynet me mandando uma chave falsificada, se passando pelo John? E a resposta é porque tanto minha máquina, quanto a do John, foram configuradas pela primeira vez pela mãe dele. Mãezona Sara Connor se certificou de instalar um certificado raíz dela em ambas as máquinas. Quando o John me mandar o certificado dele, basta ver se tá assinado pelo certificado da Sara. E vou saber isso porque eu também tenho o certificado de CA dela na minha máquina, igual o exemplo do Google que mostrei. E com isso a humanidade ainda tem chances contra a Skynet.</p>
<p>Pra finalizar esta parte, já vimos como usar o certbot pra gerar um novo certificado digital, assinado pela Letsencrypt, pronto pra colocar no meu servidor web. Mas posso já usar aqui mesmo no terminal. Pra isso podemos usar openssl pra extrair só a chave pública que está dentro desse certificado, pelo terminal como estou mostrando. Openssl passando x509 que é o formato de um certificado, passando o arquivo do certificado e mandando extrair a chave pública pra esse arquivo pubkey.pem. Podemos dar cat nesse arquivo e voilá, eis o base64 da chave.</p>
<p>openssl x509 -pubkey -noout -in /etc/letsencrypt/live/akitando.me/fullchain.pem &gt; pubkey.pem</p>
<p>cat pubkey.pem
&mdash;&ndash;BEGIN PUBLIC KEY&mdash;&ndash;
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+3a8eg0k/O7Sny46alWDx64kA8FK
SvBs1GxEJA+1ay/6XLg2cF8xEjlDrLbkDrZlUPEr4KxVtFxKAS6uSaCtUA==
&mdash;&ndash;END PUBLIC KEY&mdash;&ndash;</p>
<p>Tendo a chave pública, pra encriptar uma mensagem qualquer ou a chave simétrica, como meu arquivo &ldquo;key.bin&rdquo; do começo do video, é só usar openssl pra encriptar, como já mostrei antes. Mas tem um porém. Não vai dar pra mostrar isso porque essa chave pública que extraí do certificado gerado pelo certbot é uma curva elíptica ECC. Lembram porque eu usei RSA no exemplo lá atrás? Porque OpenSSL não consegue encriptar uma mensagem qualquer com ECC, lembra? Foi o motivo todo de porque eu fiz um exemplo com RSA.</p>
<p>Diferente do exemplo de encriptar uma chave simétrica diretamente usando a chave pública RSA; com curva elíptica, num navegador o que acontece é outro processo chamado de &ldquo;key exchange&rdquo; ou troca de chaves. Um método popular é usando o método de Diffie-Hellman que expliquei no meu video antigo de criptografia, ou ECDH que é a variante pra curva elíptica. Está fora do escopo deste vídeo explicar os detalhes, mas pesquise sobre ECDH.</p>
<p>Em resumo, o navegador já tem implementado um processo só pra trocar chaves simétricas usando essas chaves assimétricas, não precisa fazer na mão como estou mostrando no terminal. Curva elíptica é mais eficiente e mais sofisticado do que o RSA antigo. Mas RSA é suficiente e mais fácil pra demonstrar passo a passo no terminal. Objetivo do vídeo não é tornar ninguém aqui especialista em criptografia, só molhar os dedos pra vocês mesmos depois irem procurar as formas mais avançadas.</p>
<p>Também não é objetivo do video explicar arquitetura de segurança de aplicações web. Só usei o exemplo de TLS pra explicar a utilidade de chaves assimétricas. Muita gente ainda tem na cabeça a imagem de que criptografia é meramente uma única chave secreta super longa. Ou até como nos filmes mostra uma chave física mesmo. Quem assistiu o último Missão Impossível? Eles passam o filme todo literalmente atrás de uma chave dividida em duas &hellip; não é assim que funciona par de chaves. Mas enfim.</p>
<p>Agora precisamos estar na mesma página sobre outro conceito importantíssimo: impressões digitais ou fingerprints e assinatura digital. Novamente, não vou detalhar o que já expliquei no primeiro video de criptografia. Entenda assim: existem diversas funções de hashing como SHA512 que é um dos mais usados. Existem algumas que já são sabidamente defeituosas e fracas, como o antigo MD5. O MD5 não deve ser usado pra assinatura digital porque não é difícil criar colisões, mas não quer dizer que seja inútil. Dependendo do caso de uso, ela ainda é usada. SHA1 é a mesma coisa, deve ser evitado.</p>
<p>A função SHA 512 tem esse nome porque independente do que  passarmos pra ele, o resultado sempre vai ser um número binário de 512 bits, representado como uma string de 128 caracteres, mostrando o binário em formato hexadecimal pra ficar mais fácil de conseguirmos copiar e colar o texto. Pra demonstração, precisamos do programa &ldquo;sha512sum&rdquo;. Acho que na maioria das distros Linux já vem pré-instalado. No caso de distros derivadas de Arch, costuma ficar no pacote &ldquo;coreutils&rdquo;.</p>
<p>Vamos pegar aquele arquivo &ldquo;test.txt&rdquo; que usamos lá atrás e que tinha o conteúdo &ldquo;Hello World&rdquo;. Se fizermos &ldquo;sha512sum test.txt&rdquo; vai devolver esta string:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sha512sum test.txt
e1c112ff908febc3b98b1693a6cd3564eaf8e5e6ca629d084d9f0eba99247cacdd72e369ff8941397c2807409ff66be64be908da17ad7b8a49a2a26c0e8086aa  test.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Olha só. &ldquo;Hello World&rdquo; é super curto, mas mesmo assim o resultado do hash é sempre fixo: um número de 512 bits. Vamos editar esse arquivo e fazer uma modificação de nada. Só adicionar um ponto no final do arquivo. Rodamos o mesmo comando &ldquo;sha512sum&rdquo; e olha o resultado, é um novo hash completamente diferente. É essa propriedade que tornam hashes importantes.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sha512sum test.txt
cf260f19ff6ac4d64990e2173a7d51252b3bd054cbd1ea5df0841035140592832a2ebc64f17c3dd48e3d8f1682143087217b979de7568c8e1836b008d1a5e463  test.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Comparem o hash de antes e o novo. Apesar de ter só adicionado um mísero ponto no final, os hashes são completamente diferentes. Não tem como dizer que vieram de arquivos tão parecidos. E este é o objetivo. Por isso eles servem de impressão digital. Um ser humano lendo um texto mais longo, poderia passar batido numa pontuação fora do lugar. Num documento importante, digamos um contrato, um zero fora do lugar pode significar a diferença entre se comprometer a pagar 100 mil ou 1 milhão de dólares.</p>
<p>Se o contrato tiver 50 páginas, a maioria de nós, seres humanos, íamos passar batido. Mas se o documento original tem uma impressão digital. Basta passarmos o novo documento pelo &ldquo;sha512sum&rdquo; e ver se o hash continua batendo. Se bater, temos certeza que o documento está intacto e não foi adulterado. Porém, se o número for diferente, sabemos com certeza que foi adulterado e não pode ser confiado. Não sabemos onde foi adulterado, mas sabemos que foi. Lembram do episódio anterior sobre recuperação de dados? É assim que um sistema de arquivos como ZFS ou BTRFS sabe que um arquivo pode estar corrompido.</p>
<p>Outro exemplo. Vou no site da minha distro favorita, Manjaro e vou baixar a ISO pra instalar. É um arquivão de 3.7 gigabytes. Digamos que eu fiz o download numa rede pública, tipo num aeroporto ou num Starbucks. Como posso garantir que ninguém interceptou minha requisição e me mandou um arquivão falso? Daí quando eu for instalar, na verdade vai instalar um malware?</p>
<p>Chegando numa rede confiável, como minha casa ou escritório, posso baixar esse arquivo de checksum, com extensão .iso.sha512. Agora posso rodar o seguinte comando. Sha512sum passando esse arquivo de SHA e o ISO baixado.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sha512sum -c manjaro-gnome-22.1.3-230529-linux61.iso.sha512
manjaro-gnome-22.1.3-230529-linux61.iso: OK</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se devolver &ldquo;OK&rdquo; é porque tá tudo bem, mas o que ele fez? Vamos abrir o arquivo .iso.sha512. É um numerozão no formado da saída do sha512sum. A empresa que faz o Manjaro, que criou o arquivo de instalação original, gerou o sha512 e gravou nesse arquivo pra gente.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cat manjaro-gnome-22.1.3-230529-linux61.iso.sha512
78b8fa6a5222bc10a2e767485e76f82f3c7b2b59f4a70501b4a9fb846c5ad94987cf8b8ed630a701a8a3386debc0a39fc7363a743df8f13f41d3d8324d74e227  manjaro-gnome-22.1.3-230529-linux61.iso</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Podemos simplesmente calcular o sha512 em cima do ISO de 3.7 gigabytes com o comando &ldquo;sha512sum&rdquo;, igual fizemos com o arquivo do Hello World e olha só, gerou exatamente o mesmo hash. Com isso sabemos que nenhum bit dos 3.7 gigabytes veio adulterado. Se um único bit tivesse sido corrompido no download, o hash gerado seria completamente diferente. Com isso sabemos que o ISO é válido e podemos instalar.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sha512sum manjaro-gnome-22.1.3-230529-linux61.iso
78b8fa6a5222bc10a2e767485e76f82f3c7b2b59f4a70501b4a9fb846c5ad94987cf8b8ed630a701a8a3386debc0a39fc7363a743df8f13f41d3d8324d74e227  manjaro-gnome-22.1.3-230529-linux61.iso</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Lembra dos meus videos sobre Git? Esse stringzão não parece com o identificador de um commit? Só que no caso de commit é mais curto. Isso porque Git usa SHA1 em vez de SHA256. SHA1, assim como MD5, também não é mais pra ser usado, mas é difícil migrar pra SHA256 sem quebrar compatibilidade e gerar confusão na cabeça de todo mundo, por isso não usamos SHA256 ainda, mas uma hora Git também deve mudar.</p>
<p>A razão de não usarmos mais MD5 é porque descobriram um bug no seu processo que torna razoavelmente fácil gerar colisões. Ou seja, se modificarmos um determinado documento, cuidadosamente, nos bits certos, temos como mudar o conteúdo e no final o MD5 vai gerar exatamente o mesmo hash. Aí perde o propósito. Descobriram que dá pra fazer isso com SHA1 também, mas não é tão trivial, mesmo assim não é impossível. Não é prático, mas é estatisticamente possível, e isso é suficiente pra considerarmos parar de usar.</p>
<p>Dois conteúdos diferentes que geram o mesmo hash, chamamos de &ldquo;colisão&rdquo; e mais cedo ou mais tarde, colisões acontecem. É estatisticamente inevitável, mas é muito difícil de controlar a colisão pra acontecer exatamente quando queremos num SHA256 ou outra função melhor. Mas MD5 e SHA1 já sabemos como controlar as colisões, adicionar bits extras que dá pra ignorar, só pro arquivo no final conseguir chegar no mesmo hash.</p>
<p>Eu explico no video de criptografia sobre hashes usados pra não guardar senhas abertas num banco de dados, assistam depois porque é importante saber disso. Mas caso não saiba, é considerado certificado de estupidez gravar senha dos usuários aberto numa tabela no seu banco de dados. Também é uma idiotice achar que é esperto e gerar o hash da senha e gravar numa tabela. Precisa de salt. E tem algoritmos melhores do que outros. Assista depois pra entender.</p>
<p>O principal é o seguinte: quando um determinado conteúdo, um arquivo ou documento, tem conteúdo secreto, o certo é encriptar o arquivo inteiro usando um algoritmo como AES com uma chave bem longa e forte. Mas pra diversos usos, só pra identificar se foi ou não adulterado, como um contrato, não precisa encriptar o arquivo todo, basta encriptar a impressão digital, que vai ser mais curta. Um exemplo disso é o download da ISO do site da Manjaro.</p>
<p>É um desperdício encriptar uma ISO inteira, não tem nada secreto dentro. É um arquivo público que qualquer um pode baixar e instalar de graça. O problema é saber se a ISO baixada não foi adulterada no meio do caminho. Pra isso tem coisas como o arquivo &ldquo;.iso.sha512&rdquo;. Mas tem um outro arquivo no site deles também, olha só, eles chamam de assinatura e é um arquivo &ldquo;.iso.sig&rdquo;. Não dá pra listar porque é um arquivo binário, mas é a assinatura do hash da ISO.</p>
<p>O problema de um arquivo .sha512 é que se baixamos os dois ao mesmo tempo, se o ISO foi adulterado no caminho, certamente o arquivo .sha512 também foi adulterado. Por isso, no exemplo, de propósito eu disse que o cenário é que baixei a ISO do aeroporto mas depois baixei o arquivo .sha512 quando cheguei em casa, numa rede confiável. Mas não precisamos confiar na rede pra checar essas coisas.</p>
<p>Antes de sair de casa, o certo é já ter instalado o pacote &ldquo;GNUPG&rdquo; o famoso GPG, que é implementação do padrão OpenPGP, de Pretty Good Privacy. É outro pacote que faz uso do conceito de chaves assimétricas, como no exemplo de TLS com RSA ou curva elíptica, lembra? Chaves pública e privada? Só que no exemplo anterior estávamos lidando com certificados .pem pra servidores web. GPG é similar, mas pra identificar pessoas, e não servidores.</p>
<p>Tendo o GPG instalado, no meu caso, que uso Manjaro, eu já teria que ter baixado as chaves GPG da equipe do Manjaro. Só fazer o download do arquivo com &ldquo;wget&rdquo; e instalar com &ldquo;gpg&rdquo; assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>wget gitlab.manjaro.org/packages/core/manjaro-keyring/-/raw/master/manjaro.gpg

gpg --import manjaro.gpg</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Lembra o conceito que o sistema operacional ou o navegador já trazem certificados raiz de autoridades certificadoras? Aqueles arquivos em &ldquo;/etc/ssl/certs&rdquo;? O conceito é parecido. Esse arquivo &ldquo;manjaro.gpg&rdquo; tem as chaves confiáveis da equipe do Manjaro. Tudo que a equipe de lá assinar, temos como checar agora.</p>
<p>Eu fiz isso em casa, agora estou no aeroporto e resolvi baixar a ISO do Manjaro. Uma rede pública é o oposto de confiável. Por isso recomendamos sempre usar VPN. Mas digamos que não estou numa VPN e ainda assim quero me arriscar a baixar a ISO. Como eu sei que não veio adulterado? Algum hacker esperto subiu um servidor do note dele, adulterou o DNS do roteador do aeroporto, e agora todo download que eu faço na verdade é um malware?</p>
<p>Baixamos também o arquivo &ldquo;.iso.sig&rdquo; e podemos checar com o seguinte comando. GPG verify o arquivo .sig e o arquivo ISO.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gpg --verify manjaro-gnome-22.1.3-230529-linux61.iso.sig manjaro-gnome-22.1.3-230529-linux61.iso
gpg: Signature made seg 29 mai 2023 06:38:26 -03
gpg:                using RSA key 3B794DE6D4320FCE594F4171279E7CF5D8D56EC8
gpg: Good signature from &#34;Manjaro Build Server &lt;build@manjaro.org&gt;&#34; [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 3B79 4DE6 D432 0FCE 594F  4171 279E 7CF5 D8D5 6EC8</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Dentro desse arquivo de assinatura tem o hashing do ISO, assinado pelo Manjaro Build Server. Ele checa o hashing e diz &ldquo;Good Signature&rdquo;, então o binário do ISO pelo menos bate com o hashing dentro da assinatura. Mas aí tem um bugzinho que pelo jeito existe faz alguns anos no Manjaro: a chave desse servidor de build não pôde ser confirmado. Sei lá porque não vem já pré-instalado como deveria e quando vai checar dá esse aviso. O certo era ter o certificado raiz desse <a href="mailto:build@manjaro.org">build@manjaro.org</a> já no meu sistema, mas pelo visto não tem. Bug do Manjaro que até hoje não foi corrigido. Se isso mudou, mandem nos comentários abaixo.</p>
<p>O ponto todo desse exercício foi pra vocês terem na cabeça o que é um hash, uma impressão digital e uma assinatura digital. Deixa eu expandir o exemplo do download da ISO pra algo que muitos de vocês já devem ter usado pelo menos uma vez na vida: BitTorrent. De novo, objetivo do video não é ser uma explicação detalhada sobre BitTorrent, então procurem a documentação depois. Eu quero que vocês consigam entender os conceitos de criptografia que ele usa e como eles resolvem o problema de arquivos distribuídos.</p>
<p>Vamos lá, no próprio site do Manjaro, posso baixar um arquivo .torrent. Um download normal, que é o link do lado, vai puxar os 3.7 gigabytes direto do servidor da empresa do Manjaro. Mas com esse arquivo .torrent, a expectativa é conseguir baixar até mais rápido ou com mais estabilidade, porque vou baixar pedaços da ISO de diversos peers diferentes pela internet. Todo mundo que baixou a ISO e deixou o programa de BitTorrent ativo, está compartilhando o mesmo arquivo. Eu posso baixar pedaços em paralelo de várias pessoas diferentes. Essa é a idéia básica de BitTorrent.</p>
<p>Tangente de história. Nos anos 90 a internet era muito mais lenta que hoje, conexões de modem discado. No mundo dos servidores era tudo super imaturo. Ninguém sabia como manter servidores de download decentes, além de banda ser caro. Era terrível, e no meio disso surgiram os primeiros serviços de compartilhamento de arquivos, vulgo pirataria, como o lendário Napster, seguidos de copycats nos anos 2000 como Limewire, Kazaa, eDonkey e outros.</p>
<p>Mas o princípio na época era só um jeito de dizer pra outras pessoas quais arquivos tem na sua máquina pra compartilhar e deixar todo mundo baixar livremente. É o modelo mais simples de compartilhamento de arquivos. Foram anos de controvérsia, diversos processos da indústria de música, até culminar no surgimento dos primeiros serviços comerciais de venda de música e depois de assinatura como iTunes e hoje Spotify.</p>
<p>O grande defeito desse modelo simples de compartilhamento, é que era muito fácil identificar quem tinha os arquivos e ir processar direto essa pessoa, derrubando o compartilhamento. Uma vez que desliga a máquina do cara, somem os arquivos e ninguém baixa mais. Esse era o problema a ser resolvido. O problema ainda não foi totalmente resolvido, mas vamos ver como BitTorrent melhorou radicalmente nossa situação.</p>
<p>Primeiro de tudo, precisamos acabar com o conceito de um servidor único, centralizado de arquivos, de onde todo mundo puxa. É o mesmo problema do servidor de ISOs da Manjaro. Se todo mundo vai no mesmo lugar pra baixar, quanto mais gente descobrir o servidor, mais lento vai ficar pra todo mundo, e maiores os riscos desse servidor cair ou ser derrubado e todo mundo ficar sem nada.</p>
<p>Lembra da minha série sobre armazenamento, sistemas de arquivos e tudo mais? O que é um arquivo? É só um linguição de bits. Por baixo dos panos, eles são organizados como blocos de bits. Podemos picotar um arquivo como quiser e depois remontar. Inclusive é assim que eles são gravados em HDs e SSDs: fora de ordem. Vamos simular esse exercício num terminal de Linux só pra terem essa imagem na cabeça.</p>
<p>Podemos fazer isso com qualquer arquivo, seja texto ou binário, já expliquei que pro computador não faz diferença nenhuma, por baixo é tudo um linguição de bits. Só pra nós humanos que faz diferença porque arquivos texto conseguimos abrir e ler. De exemplo, peguei o script do episódio 142 que foi a explicação sobre ChatGPT. Esse script tem 94 kilobytes, ou seja, 94 mil letras. Quero conseguir dividir isso em arquivos iguais de exatamente 10 kilobytes cada, pra simular blocos de 10 kilobytes.</p>
<p>Pra isso usamos o comando &ldquo;split&rdquo; que tem em Linux. Só passar o tamanho de cada pedaço, o arquivo original e o prefixo do nome dos pedaços, que vou chamar de chunk.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>split -b 10K subtitle_142.txt chunk_

❯ ls -la
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_aa
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_ab
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_ac
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_ad
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_ae
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_af
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_ag
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_ah
.rw-r--r--  10k akitaonrails 18 jul 13:22  chunk_ai
.rw-r--r-- 1,5k akitaonrails 18 jul 13:22  chunk_aj
.rw-r-----  94k akitaonrails 18 jul 13:21  subtitle_142.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto, obviamente, só o último arquivo vai ser menos que 10 kilobytes, mas vejam como consegui dividir direitinho. Posso tentar dar cat em qualquer pedaço e vai vir o trecho do script. Quebrar arquivos grandes em pedaços menores era algo que fazíamos o tempo todo nos anos 90 porque era muito arriscado tentar fazer download de um arquivo gigante. O modem podia desligar no meio do caminho, algoritmos de checagem de corrupção ainda não eram difundidos, então podia vir sujeira na linha e o arquivo ser corrompido. Se viesse corrompido, precisava mandar baixar tudo de novo do zero.</p>
<p>Então dividíamos o risco. Era melhor deixar agendado pra ir baixando um pedaço de cada vez. Se alguma coisa desse errado, só um pedaço ia ser corrompido e dava pra baixar só aquele pedaço separado depois, em vez de ter que recomeçar do zero. Hoje em dia o próprio protocolo HTTP suporta resumir um download pela metade e o protocolo TCP garante que os pacotes não vão vir corrompidos, como já expliquei na série de redes. Mas estou falando de uma era antes dessas facilidades.</p>
<p>E agora vocês já sabem. Uma forma de manualmente checar se meus pedaços vieram corretamente seria ter um arquivo com o hash de cada pedaço. Assim podemos comparar o hash do download e ver se nada foi corrompido. Lembra? Como estou falando de anos 90, poderíamos fazer o SHA1 de cada pedaço. Por exemplo, eis o hash do primeiro pedaço, com o comando sha1sum:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ sha1sum chunk_aa
c3bb4df901fb79af2421cc274e9db87309bf6424  chunk_aa</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Com um pouco de mágica de script de shell, podemos fazer a mesma coisa pra todos os pedaços e colocar num arquivo &ldquo;index.txt&rdquo; da seguinte forma, fazendo, pra cada file, ou arquivo, que começa com &ldquo;chunk&rdquo;, gere o sha1 e vá adicionando nesse arquivo index.txt.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>for file in chunk_*; do
    sha1=$(sha1sum &#34;$file&#34; | awk &#39;{print $1}&#39;)
    echo &#34;$file $sha1&#34; &gt;&gt; index.txt
done


/tmp/subtitle 
❯ cat index.txt 
chunk_aa c3bb4df901fb79af2421cc274e9db87309bf6424
chunk_ab 79a5c1fb9a9708982a92009d874bd9ee3db65a31
chunk_ac 84de6f8eb80fc64628514500b8d6b09ff930bda5
chunk_ad 2f68d9f1caffdfca69b28ae33348cdd0ca467474
chunk_ae c644daae2adcc6c87b8b4ee91052535c50edbe4f
chunk_af de3471b65337f2ffb2d97bbd6ef45b1e95d6ba88
chunk_ag 454093fda498e7f67cacb0919e3a5ffa579d0cb6
chunk_ah a0d040c39213188382dc681d7a74da283bfb07c0
chunk_ai 0469b41afb556f10c01cca5f099a81e6b5e17a31
chunk_aj 45fa07b4fdfea4a0a5c02abcad6520b652264a25</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Por isso é interessante aprender sobre scripts de shell. Abrindo o arquivo &ldquo;index.txt&rdquo; com &ldquo;cat&rdquo; temos o nome do arquivo de cada pedaço seguido do seu hash SHA1. Olhem como o sha1 do primeiro arquivo bate com o que geramos na mão antes. Então está correto. Além disso, esse arquivo me diz exatamente a totalidade dos pedaços que preciso baixar. Poderíamos fazer outro script pra baixar cada pedaço, checar o SHA1 e se vier errado, mandar refazer o download só daquele pedaço. Fica de exercício pra vocês fazerem isso.</p>
<p>Ok, fizemos o download de todos os pedaços. E agora? Como  remontamos o arquivo original? É simples, bastar dar &ldquo;cat&rdquo; de todos os pedaços e redirecionar pra um arquivo final, assim, cat chunk asterisco maior pra restored.txt:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cat chunk_* &gt; restored.txt

❯ sha512sum restored.txt   
bfec6c9efe398146b8197b7b0258860563f6057dac7f6d1931ec5d4118c730d47007678ddd2cf9387b5b02903c7dd35a1911a2da4707d624ce4e996514a766c6  restored.txt

/tmp/subtitle 
❯ sha512sum subtitle_142.txt 
bfec6c9efe398146b8197b7b0258860563f6057dac7f6d1931ec5d4118c730d47007678ddd2cf9387b5b02903c7dd35a1911a2da4707d624ce4e996514a766c6  subtitle_142.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso funciona em shells modernos porque a maioria sempre lista os arquivos em ordem alfabética. Caso o seu não faça isso, precisaria primeiro usar o comando &ldquo;sort&rdquo;, de novo, fica de exercício. Mas assumindo que o cat pegou cada arquivo chunk na ordem certa, como sei que o arquivo restored.txt realmente é idêntico ao original? Vocês já sabem né? Só comparar o hash dos dois. E como podem ver, é exatamente igual. Se um único bit tivesse vindo errado, os hashes seriam diferentes.</p>
<p>Estão entendendo como podemos manipular qualquer tipo de dados e garantir a integridade deles via ferramentas criptográficas? Sabendo desses conceitos, vou fazer uma explicação super de alto nível pra explicar como BitTorrent tira proveito disso que acabei de explicar.</p>
<p>Existem duas formas de baixar um arquivo via Torrent. Um é baixando um arquivo .torrent, como aquele do site do Manjaro. Podemos usar o programa &ldquo;transmission-show&rdquo; que vem no pacote &ldquo;transmission-cli&rdquo; pra abrir e ver o conteúdo desse arquivo.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ transmission-show manjaro-gnome-22.1.3-230529-linux61.iso.torrent 
Name: manjaro-gnome-22.1.3-230529-linux61.iso
File: manjaro-gnome-22.1.3-230529-linux61.iso.torrent

GENERAL

  Name: manjaro-gnome-22.1.3-230529-linux61.iso
  Hash v1: 68617f6ffc9f95bbca41733556f666739f647f9c
  Created by: mktorrent 1.1
  Created on: Mon May 29 06:38:30 2023

  Piece Count: 1769
  Piece Size: 2.00 MiB
  Total Size: 3.71 GB
  Privacy: Public torrent

TRACKERS

  Tier #1
  udp://tracker.opentrackr.org:1337

WEBSEEDS

  https://download.manjaro.org/gnome/22.1.3/manjaro-gnome-22.1.3-230529-linux61.iso

FILES

  manjaro-gnome-22.1.3-230529-linux61.iso (3.71 GB)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O importante desse arquivo é que ele identifica o que vamos baixar, no caso o ISO, e onde podemos achar esse arquivo. Primeiro temos esse tal de tracker. Um tracker é um servidor que contém listas de peers que estão online e que tem o arquivo com este hash. Se tiver pelo menos 1 peer ativo que seja um seeder, vai começar a baixar. Peer é o nome que damos pra qualquer máquina, PC, servidor, participando dessa rede. Um tracker não necessariamente tem o arquivo ou seus pedaços, mas sabe quem pode ter, que são os peers.</p>
<p>Você já deve ter visto essa nomenclatura em programas de torrent como QBitTorrent ou Transmission. Quanto mais seeders tiver ativo, melhor será o download. Seeder são computadores online que também estão compartilhando o mesmo arquivo ou pedaços. Leechers são computadores online fazendo download mas não contribuindo em nada. Parasitando mesmo, leeching. O algoritmo do BitTorrent tenta ser justo e deixar download mais rápido pra quem contribui e download mais lento pra quem é só parasita.</p>
<p>Trackers só compartilham e sincronizam a lista de peers, peers podem ser tanto seeders quanto leechers. Quanto mais seeders, mais rápido o download, quanto mais leechers e menos seeders, mais lento o download. O mundo ideal é todo mundo ser seeder. Alguns são chatos e desligam mesmo, outros realmente não conseguem contribuir porque a rede do provedor bloqueia e eles não sabem como furar firewall. Eu falo de furar firewall na série de redes. Depois procurem saber mais como habilitar portas, usar VPN pra torrent e coisas assim, não vou falar disso hoje.</p>
<p>Vamos voltar pro arquivo .torrent. Olhem outras informações que ele nos dá. Sabemos que o arquivo ISO tem 3.7 gigabytes, como já repeti várias vezes, e sabemos que este torrent está organizado em mil setecentos e sessenta e nove pedaços de 2 megabytes cada. Lembra os chunks que fiz usando o comando split? É a mesma coisa. Ele não mostra aqui, mas dentro desse torrent vai ter uma lista de pedaços seguido do hash de cada pedaço, parecido com aquele arquivo &ldquo;index.txt&rdquo; que fiz no exemplo.</p>
<p>Cada seeder que o tracker conhece, vai ter ou o arquivo inteiro ou o arquivo parcial, porque ainda não terminou o download. Agora eu, que comecei o download agora, começo a baixar cada um desses pedaços de cada um dos seeders disponíveis, em paralelo. Por exemplo, digamos que na rede tenha o seeder John, a seeder Sara e o seeder Kyle. E eu vou baixar aquele meu arquivo de script. Começo a baixar o chunk_aa do John, o chunk_ab da Sara, o chunk_ac do Kyle e assim por diante.</p>
<p>Toda vez que um chunk termina de baixar, consigo checar o hash e sei que tá correto, daí peço o chunk seguinte, distribuindo a carga entre todos os seeders. Se outra pessoa aparecer pedindo o mesmo arquivo, talvez ele pegue um chunk de mim. Entenderam? O seeder não precisa ter o arquivo completo pra começar a contribuir, basta ter pelo menos um chunk completo. E por isso, quanto mais gente tiver, menos pesado fica pra todo mundo, especialmente pro servidor principal da Manjaro. É a base de computação distribuída.</p>
<p>Se não tiver nenhum seeder ativo, o arquivo .torrent aponta pra um Webseed, que é o link pro arquivo original no servidor da Manjaro. Daí seria como um download direto normal mesmo. Entenderam até aqui: com uma lista de pedaços e seus respectivos hashes, distribuídos entre diversos computadores num swarm, ou enxame, e cada um baixando um pedaço do computador do outro, até todo mundo ter o arquivo completo, essa é a arquitetura básica por trás do BitTorrent.</p>
<p>BitTorrent é &ldquo;decentralizado&rdquo; no sentido que, diferente de um iTunes da Apple, não tem uma única empresa nem um único servidor que centraliza os downloads. Eis alguns mecanismos que tornam o BitTorrent resiliente, embora não totalmente invulnerável.</p>
<p>De cara, poderíamos pensar que se derrubarmos quem mantém trackers, poderíamos parar um enxame, não? Primeiro, não existe somente um tracker, existem diversos trackers servindo um de backup pro outro. Precisaria derrubar todos os trackers em atividade no mundo. Mas isso não seria suficiente.</p>
<p>Toda vez que você abre um programa como Transmission ou Nano Torrent da vida, ele faz &ldquo;bootstrapping&rdquo; usando uma lista de &ldquo;bootstrapping nodes&rdquo;, uma lista de nós bem conhecidos na rede que acreditamos que costumam estar online, vários deles. São esses nós que nos ajudam a achar outros nós. Assim como trackers, eles também não são pontos únicos de falha.</p>
<p>Quando seu programa se conecta e acha um tracker ou um bootstrapping node, ele se junta à rede DHT ou Hash Table distribuído. Cada nó que entra na rede armazena informação sobre a rede toda ou um pedaço da rede. Então, quando você pergunta ao DHT por alguma coisa, seu cliente de BitTorrent manda uma requisição pra um nó que ele conhece. Esse nó ou vai ter a informação e te retornar ou vai te direcionar pra outro nó que pode saber essa informação. A partir desse ponto você, como nó da rede, passa a ter essa informação e pode responder a alguém que te perguntar também. Todo mundo se torna backup e contribui pra outras pessoas.</p>
<p>O conceito de trackers é um pouco obsoleto na realidade, das primeiras versões de BitTorrent. Ele veio evoluindo e agora temos o conceito de DHT e PEX que é Peer Exchange. Mesmo derrubando todos os trackers, cada pessoa conectada na rede contribui pra distribuir a informação do resto da rede. Hoje em dia os programas de BitTorrent implementam as duas maneiras.</p>
<p>Eu falei do arquivo .torrent que baixamos pra começar um download via BitTorrent, mas nem precisamos desse arquivo. Vocês já devem ter visto o que se chama de link magnético, que é tipo uma URL que usa o protocolo &ldquo;magnet&rdquo;. Lembram o arquivo .torrent da ISO do Manjaro que baixamos? Podemos gerar um link a partir dele usando o programa &ldquo;transmission-show&rdquo; com a opção &ldquo;-m&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ transmission-show -m /mnt/terachad/Downloads/manjaro-gnome-22.1.3-230529-linux61.iso.torrent
magnet:?xt=urn:btih:68617f6ffc9f95bbca41733556f666739f647f9c&amp;dn=manjaro-gnome-22.1.3-230529-linux61.iso&amp;tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&amp;ws=https%3A%2F%2Fdownload.manjaro.org%2Fgnome%2F22.1.3%2Fmanjaro-gnome-22.1.3-230529-linux61.iso</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É esse link que encontramos em diversos sites que listam torrents pra baixar. Veja num Piratebay da vida, onde tem esse ícone de ímã. Quando clica, automaticamente pede pra abrir seu programa de Torrent instalado. A parte mais importante é esse &ldquo;btih&rdquo; logo no começo que é o BitTorrent InfoHash, o hash que identifica unicamente esse arquivo de ISO. O resto ajuda mas é opcional. Por exemplo, ele tem o link pro webseed caso ninguém esteja oferecendo o arquivo na rede e também o link pra um tracker.</p>
<p>Vamos fazer um html idiota com o link pra esse magnet. Um link com a URL inteira que foi gerado e um segundo link só com o infohash.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&lt;html&gt;
 &lt;body&gt;
  &lt;p&gt;&lt;a href=&#34;magnet:?xt=urn:btih:68617f6ffc9f95bbca41733556f666739f647f9c&amp;dn=manjaro-gnome-22.1.3-230529-linux61.iso&amp;tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&amp;ws=https%3A%2F%2Fdownload.manjaro.org%2Fgnome%2F22.1.3%2Fmanjaro-gnome-22.1.3-230529-linux61.iso&#34;&gt;Magnet pro ISO&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;&lt;a href=&#34;magnet:?xt=urn:btih:68617f6ffc9f95bbca41733556f666739f647f9c&#34;&gt;Magnet pro ISO (só infohash)&lt;/a&gt;&lt;/p&gt;
 &lt;/body&gt;
&lt;/html&gt;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se abrir num navegador e clicar no link, o media type está associado ao QBitTorrent instalado no meu Manjaro. Ele abre e oferece pra começar a baixar. Se eu abrir pelo segundo link, mesmo sem informação de webseed e nem de tracker, só com o infohash, olha só, abre normalmente e oferece pra baixar igual. Isso porque o QBitTorrent na minha máquina se uniu à rede, via DHT e PEX e conseguiu achar os seeders e já sabe como começar a baixar.</p>
<p>Entenderam? Pra parar um arquivo de ser compartilhado, não basta derrubar todos os trackers, precisa parar 100% de todo mundo que está compartilhando o arquivo, de todas as partes do mundo, ao mesmo tempo. Se tiver só um nó na rede funcionando, dá pra continuar compartilhando, porque via DHT e PEX, essa informação vai voltar a ser distribuída. Computação distribuída é fascinante por causa disso: ninguém consegue parar somente com uma canetada sem comprometer a internet inteira de funcionar direito. Por isso eu gosto desse conceito.</p>
<p>DHT e PEX são assuntos fascinantes por si só, pra garantir a descentralização da rede, então espero que vocês pesquisem mais sobre isso depois. Ótimo tema pra TCC. Mas tem um assunto menos comentado que eu acho ainda mais interessante. É o conceito de Árvores de Hashes ou Merkle Trees, nome dado por causa do criador do conceito, Ralph Merkle, que inventou isso no fim dos anos 70.</p>
<p>Vamos lá. O que temos até agora é um arquivo &ldquo;index.txt&rdquo; com uma lista de chunks ou pedaços daquele meu arquivo de script. 10 pedaços com o hash SHA1 do lado de cada um, lembram? Numa versão rudimentar de BitTorrent, eu poderia ter baixado esse arquivo e perguntado na tabela distribuída de hashes, o tal DHT, de onde baixar cada pedaço, identificado pelo SHA1, que serve como um ID único pra cada pedaço.</p>
<p>Depois que fizer o download de cada pedaço, basta rodar o comando &ldquo;sha1sum&rdquo; e comparar o hash com o que tem no arquivo &ldquo;index.txt&rdquo;. Se bater é porque veio correto, sem corromper nem nada. No final, basta concatenar os pedaços como fiz antes, gerar o hash do arquivo todo, comparar com o hash mestre do arquivo original, pra garantir que tenho o mesmo arquivo no final. Parece simples e parece que só isso é o suficiente.</p>
<p>Mas no mundo real tem várias razões de porque isso não é suficiente. Pra começar, via BitTorrent podemos trafegar arquivos de qualquer tamanho gigante, posso compartilhar diretórios inteiros com milhares de arquivos. Eu poderia fazer um torrent do C: inteiro do meu Windows se quisesse. E posso puxar pedaços de diversos nós diferentes no enxame de computadores participando na rede. Pra validar o hash de tudo, dessa forma, não seria prático. Imagine ter que computar o hashzão do seu HD inteiro pra saber se o que fiz download tá correto. Seria muito pesado, em alguns casos até inviável.</p>
<p>Em programação, você sempre vai bater nesse caso: pra um arquivo pequeno, com poucos kilobytes ou megabytes de tamanho, o esquema simples de um arquivo de hashes é mais que suficiente. Mas pra escala de internet, onde podemos querer lidar com gigabytes ou terabytes, milhares de arquivos num mesmo torrent, não vai escalar.</p>
<p>Pense assim, temos 10 hashes, um pra cada pedaço, e temos 1 hash mestre, que valida o arquivo completo inteiro. Eu recebo esses hashes e começo a fazer download de algum nó. Veio esse primeiro pedaço chunk_aa, o hash começa com c3bb. Eu rodo o comando sha1sum e vejo que, sim, o hash começa com c3bb mesmo. Bacana. Isso só garante que o pedaço é quem ele diz que é, mas como eu sei que de fato esse pedaço faz parte do arquivão inteiro?</p>
<p>A única forma é baixar todos os 10 pedaços, concatenar tudo, rodar o &ldquo;sha1sum&rdquo; pro arquivo gerado e comparar com o hash mestre. Só assim vou saber que todos os pedaços são realmente válidos pra esse arquivo, entenderam? Os hashes individuais não me dizem nada sobre o arquivo inteiro original. Eu tenho que confiar que o nó da rede, que me deu essa informação, não mentiu pra mim. Imagina se fosse um torrent como falei antes, com vários arquivos e muitos gigabytes. Eu só ia ter certeza que todos são válidos depois que baixar tudo.</p>
<p>Em BitTorrent, vocês já devem ter visto que podemos escolher baixar somente alguns dos arquivos e não todos. Então, de cara, esse esquema de só ter um hash mestre pra tudo não funciona. Eu preciso de hashes parciais, no mínimo um pra cada arquivo separado, não? E mesmo assim, preciso garantir que esse arquivo, uma vez que baixei os pedaços e montei, de fato faz parte do conjunto todo, sem ter que baixar o conjunto todo. Como faz?</p>
<p>Pra simplificar, vamos partir de novo do exemplo de 10 pedaços. Imagine que isso poderia ser um arquivo de milhares de pedaços e dezenas de arquivos em sub-diretórios. É uma simplificação, ok? Pois bem, começo organizando esses pedaços em pares. chunk_aa com chunk_ab, depois chunk_ac com chunk_ad e assim por diante. Pares de pedaços.</p>
<p>Agora pegamos o hash de cada par e concatenamos só eles. Por exemplo, o hash c3bb do chunk_aa com o hash 79a4 do chunk_ab. E gero o hash dessa combinação. Vai dar um hash que começa com &ldquo;0285&rdquo;. Faço a mesma coisa com os hashes do próximo par, vai dar um novo hash que começa com &ldquo;84de&rdquo; e continuo fazendo isso pros outros pares.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/tmp/subtitle 
❯ echo &#34;c3bb4df901fb79af2421cc274e9db87309bf642479a5c1fb9a9708982a92009d874bd9ee3db65a31&#34; | sha1sum
0285f4ec89c694112c330ec20a571457a4e159f2  -

/tmp/subtitle 
❯ echo &#34;84de6f8eb80fc64628514500b8d6b09ff930bda52f68d9f1caffdfca69b28ae33348cdd0ca467474&#34; | sha1sum
05e420886ad96877bd78051df8a01c4a9414cc33  -

/tmp/subtitle 
❯ echo &#34;c644daae2adcc6c87b8b4ee91052535c50edbe4fde3471b65337f2ffb2d97bbd6ef45b1e95d6ba88&#34; | sha1sum
d9e3a713ccd57d87adb4ea04aede0cebdcc8fd8e  -

/tmp/subtitle 
❯ echo &#34;454093fda498e7f67cacb0919e3a5ffa579d0cb6a0d040c39213188382dc681d7a74da283bfb07c0&#34; | sha1sum
4c6b2637222b6a7cc9e52a3d5441dedcdf00b4e6  -

/tmp/subtitle 
❯ echo &#34;0469b41afb556f10c01cca5f099a81e6b5e17a3145fa07b4fdfea4a0a5c02abcad6520b652264a25&#34; | sha1sum
65c704fefd21e50f92df28de813ef2d3b4d7cff4  -</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>0285f4ec89c694112c330ec20a571457a4e159f2
05e420886ad96877bd78051df8a01c4a9414cc33
d9e3a713ccd57d87adb4ea04aede0cebdcc8fd8e
4c6b2637222b6a7cc9e52a3d5441dedcdf00b4e6
65c704fefd21e50f92df28de813ef2d3b4d7cff4</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No final vou ter 5 novos hashes. Agora repito o mesmo processo. Organizo pares de hashes, concateno e gero um novo hash pra cada par. Pego esse hash que começa com &ldquo;0285&rdquo; com o hash que começa com &ldquo;05e4&rdquo; e gero o hash que começa com &ldquo;9361&rdquo;. Faço isso pro segundo par. Só no terceiro par que vou ter problemas porque o último hash vai ficar sozinho. Não tem problema, gero o hash dele também, concatenado com vazio mesmo.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/tmp/subtitle 
❯ echo &#34;0285f4ec89c694112c330ec20a571457a4e159f205e420886ad96877bd78051df8a01c4a9414cc33&#34; | sha1sum
9361c518dbcc3374c2d9a6c343dfa903f5226219  -

/tmp/subtitle 
❯ echo &#34;d9e3a713ccd57d87adb4ea04aede0cebdcc8fd8e4c6b2637222b6a7cc9e52a3d5441dedcdf00b4e6&#34; | sha1sum
910ec073793aafe29790941efe004c17debdc2d9  -

/tmp/subtitle 
❯ echo &#34;65c704fefd21e50f92df28de813ef2d3b4d7cff4&#34; | sha1sum
2924e53ff12eae61d88da7a0cca2967150881598  -</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vamos repetir mais uma vez. Par de hashes &ldquo;9361&rdquo; com &ldquo;910e&rdquo; vai dar &ldquo;bdf3&rdquo; e o último hash é &ldquo;2354&rdquo;. E pronto. Chegamos ao final, temos só dois hashes resultantes. Geramos o hash da concatenação desses dois também. Vai dar um hash final que começa com &ldquo;f2fb&rdquo;. Esse hash é especial, é o que chamamos de Merkle Root ou Raiz Merkle. É o hash que engloba todos os outros hashes.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/tmp/subtitle 
❯ echo &#34;9361c518dbcc3374c2d9a6c343dfa903f5226219910ec073793aafe29790941efe004c17debdc2d9&#34; | sha1sum
bdf3037c43dbe2cb7bf318a38948b3a504bea5d7  -

/tmp/subtitle 
❯ echo &#34;2924e53ff12eae61d88da7a0cca2967150881598&#34; | sha1sum
2354ddf1e76303140524dad59d5c2c3f418d87b7  -



/tmp/subtitle 
❯ echo &#34;bdf3037c43dbe2cb7bf318a38948b3a504bea5d72354ddf1e76303140524dad59d5c2c3f418d87b7&#34; | sha1sum
f2fb982d0f416376d64a7b451fa2d2176bf45ca8  -</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>chunk_aa c3bb
chunk_ab 79a5  0285
chunk_ac 84de
chunk_ad 2f68  05e4  9361
chunk_ae c644
chunk_af de34  d9e3
chunk_ag 4540
chunk_ah a0d0  4c6b  910e  bdf3
chunk_ai 0469
chunk_aj 45fa   65c7  2924  2354  f2fb</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Antes, se eu só tivesse o hash mestre, precisaria de todos os 10 hashes de cada pedaço pra saber se tá tudo correto. Mas nesse esquema da árvore de Merkle, digamos que eu queira saber se o pedaço &ldquo;aa&rdquo; que acabei de baixar realmente faz parte do arquivo final, sem baixar os outros pedaços, como eu faço? Peço pro servidor somente os hashes do irmão &ldquo;ab&rdquo;, com isso consigo calcular &ldquo;aa&rdquo; e &ldquo;ab&rdquo; como fiz antes. Daí preciso que o servidor me mande os 4 hashes &ldquo;acad&rdquo;, &ldquo;aeaf&rdquo;, &ldquo;agah&rdquo;, &ldquo;aiaj&rdquo;.</p>
<p>Tendo somente esses 4 hashes, mais o hash do pedaço &ldquo;aa&rdquo;, consigo recalcular a árvore e achar a raiz Merkle e comparar com o que o servidor me indicou. Se bater, quer dizer que esse pedaço que baixei faz parte do arquivo final. Repetindo: eu diminuí a necessidade de precisar de todos os 10 hashes pra somente 4 hashes, sacaram?</p>
<p>De novo, é um exemplo super pequeno e simples só pra caber aqui na tela. Mas veja aquele exemplo da ISO do Manjaro, lembram? Eram quase mil e oitocentos pedaços. Se eu baixar só um pedaço, pra garantir que ele faz realmente parte do ISO original, precisaria calcular o hash mestre usando todos os quase mil e oitocentos hashes. Mas organizado numa árvore, precisaria só de uns log2(2048) que dá 11.</p>
<p>A árvore precisa ser balanceada, e o próximo número acima de mil e oitocentos que balanceia uma árvore binária é 2048. Enfim, eu preciso que o servidor me mande a raiz Merkle que ele tem pré-calculado e 11 hashes dessa árvore, mais ou menos. Não é um número exato esse log. Mas entenda que 11 é uma ordem de grandeza menos hashes do que mil e oitocentos. Mesmo nesse exemplo simples de só uma ISO de Manjaro, já temos um ganho significativo.</p>
<p>BitTorrent não foi criado com árvores Merkle, isso é uma coisa mais recente. Da mesma forma que hoje não precisaria de trackers por causa de DHT e PEX. É uma tecnologia ainda em evolução. Lembram do que eu falei no video sobre bancos de dados: árvores são as estruturas de dados mais importantes da computação, tudo são árvores. Nesse caso, árvores binárias, mas já falei de B-trees ou B+Trees em sistemas de arquivos. Assistam meu video de Árvores depois.</p>
<p>Sabe que outro programa se beneficia de árvores Merkle? Git. O que são commits? São diffs com trechos de arquivos de código fonte, mais metadados, como nome do arquivo, autor da modificação, datas. Uma característica importante de um commit é que ele é identificado por um hash, um SHA1 do seu conteúdo. É como sabemos que o conteúdo não foi adulterado, da mesma forma como fizemos pra checar os chunks do download via BitTorrent.</p>
<p>Mas um commit de Git tem outra característica importante: ele contém o SHA1 do commit pai anterior. Por isso commits formam uma cadeia, mais especificamente, uma árvore. Objetos de árvore de diretórios também tem SHA1 dos objetos filho. Tudo isso forma um DAG, um direct acyclic graph ou grafo direcionado não cíclico, mais especificamente um Merkle DAG.</p>
<p>Uma característica importante é que como todo commit contém o SHA1 do commit anterior, é impossível modificar só um commit no meio da árvore. Precisaria reescrever todos os commits na frente e regerar SHA1 novos pra todos. É o que chamamos de &ldquo;reescrever a história&rdquo;. Vocês já devem ter ouvido falar disso em discussões se é melhor fazer merge ou rebase e todo mundo é contra fazer rebases na master porque isso &ldquo;reescreve a história&rdquo;. É isso que significa reescrever a história: tem que regerar o SHA1 de todos os commits pra frente.</p>
<p>Deixa eu mostrar rapidamente com um exemplo. Vamos iniciar um novo repositório com &ldquo;git init&rdquo; e criar um arquivo de teste idiota só pra criar o primeiro commit.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ git init
hint: Using &#39;master&#39; as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:  git config --global init.defaultBranch &lt;name&gt;
hint: 
hint: Names commonly chosen instead of &#39;master&#39; are &#39;main&#39;, &#39;trunk&#39; and
hint: &#39;development&#39;. The just-created branch can be renamed via this command:
hint: 
hint:  git branch -m &lt;name&gt;
Initialized empty Git repository in /tmp/teste/.git/



teste on  master 
❯ echo &#34;hello world&#34; &gt; hello.txt

teste on  master [?] 
❯ git a .

teste on  master [&#43;] 
❯ git c &#34;initial commit&#34;
[master (root-commit) 308ba8d] initial commit
 1 file changed, 1 insertion(&#43;)
 create mode 100644 hello.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fazendo &ldquo;git log&rdquo; vemos o commit identificado pelo hash que começa com &ldquo;4b4c&rdquo;. Agora vamos criar um novo branch chamado &ldquo;teste&rdquo;, modificar o arquivo e criar um novo commit.
No caso criou um novo commit identificado pelo hash que começa com &ldquo;65c2&rdquo;. (no exemplo abaixo está com outro hash porque refiz o exemplo e a mudança de data gera hashes diferentes)</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>commit 1eb601ac03c713abaa08fd54e002eec04ee66922 (HEAD -&gt; teste)
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;
Date:   Thu Aug 24 11:54:32 2023 -0300

    adding hello again

commit 308ba8d7ee9ac84c6b318393614203e2b905f0f4 (master)
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;
Date:   Thu Aug 24 11:53:28 2023 -0300

    initial commit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora vou voltar pro branch master, criar um novo arquivo qualquer e gravar em outro commit. Veja só, criou esse commit com hash que começa com &ldquo;9431&rdquo;. Agora, em vez de merge vou fazer rebase do branch teste em cima do branch master com &ldquo;git rebase teste&rdquo;. Como não fiz nada demais, vai terminar sem nenhum conflito.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>commit 921bb4796a2545fabc87324b5a8fa069d53201f2 (HEAD -&gt; master)
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;
Date:   Thu Aug 24 11:55:16 2023 -0300

    adding foo

commit 1eb601ac03c713abaa08fd54e002eec04ee66922 (teste)
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;
Date:   Thu Aug 24 11:54:32 2023 -0300

    adding hello again

commit 308ba8d7ee9ac84c6b318393614203e2b905f0f4
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;
Date:   Thu Aug 24 11:53:28 2023 -0300

    initial commit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fazemos &ldquo;git log&rdquo; e olha só, o commit que eu tinha acabado de fazer, que tinha o hash começando em &ldquo;9431&rdquo; agora começa com &ldquo;ee00&rdquo;. Isso porque antes, o commit anterior era o &ldquo;initial commit&rdquo; com hash &ldquo;4b4c&rdquo;. Como fiz rebase, ele trouxe o commit &ldquo;adding hello again&rdquo; de hash &ldquo;65c2&rdquo;, que passa a ser o novo pai do commit &ldquo;adding teste&rdquo;, portanto o hash desse commit tem que ser recalculado e por isso mudou pra &ldquo;ee00&rdquo;, ou seja, acabamos de reescrever a história.</p>
<p>Esse conceito é muito importante. Ao colocar o SHA1 do commit anterior no commit atual, criamos uma dependência entre esses dois objetos. Se o objeto anterior mudar, se seu hash mudar, o hash do objeto seguinte vai ser invalidado e precisa ser recalculado também. Não dá pra simplesmente entuchar um commit no meio da cadeia sem consequências e é isso que garante a integridade do código fonte armazenado nesse repositório.</p>
<p>Se alguém tentar adulterar um commit lá atrás no passado, ele vai ser obrigado a fazer rebase de tudo pra frente, ou seja, recalcular o SHA1 de todo mundo. Daí todo mundo vai imediatamente saber que o repositório foi modificado, porque os IDs de tudo vão estar diferentes. É onde temos que lidar com conflitos depois. Se você nunca parou pra entender como o Git realmente funciona, recomendo que assista meu video de &ldquo;Entendendo Git&rdquo;.</p>
<p>Entenda, ter um Merkle Tree não impede que um commit seja adulterado. Só torna mais fácil de identificar quando for. E torna mais barato fazer os cálculos pra chegar nessa conclusão, do que ter que checar o SHA1 de 100% de tudo. No caso de Git, que é um repositório decentralizado, digamos que era uma equipe de 5 pessoas. Uma delas faz pull do GitHub e nota que alguém fez merda e deu &ldquo;push &ndash;force&rdquo; de um rebase gigante que reescreveu centenas de commits.</p>
<p>Você consegue ver que isso aconteceu e consegue ligar pra um dos outros colegas que ainda não fez &ldquo;pull&rdquo; e pedir pra ele dar um novo &ldquo;push &ndash;force&rdquo; pra recuperar o histórico como era antes e eliminar a cagada. O fato de termos várias cópias do mesmo repositório nas máquinas de todo mundo é um tipo de backup. Se o repositório elegido como principal ficou zoado, tem formas de recuperar. E não precisa sobrescrever tudo, só a partir do commit onde deu rebase, entenderam?</p>
<p>Existem várias discussões sobre centralizadores de Git como GitHub ou GitLab. Eles centralizaram a confiança de Git neles, mas Git originalmente não era pra ter um ponto único de falha. Você pode fazer git clone do repositório de alguém localmente, num pendrive, como mostrei no episódio anterior. Não precisa obrigatoriamente ter que fazer git clone de um repositório do GitHub. Isso é outra discussão, só pra vocês saberem que Git também foi feito pra ser distribuído, mas hoje em dia usamos centralizado num GitHub da vida.</p>
<p>Voltando pro exemplo de BitTorrent. Como qualquer computador na internet pode participar do compartilhamento de arquivos, sim, algum computador malicioso poderia começar a tentar espalhar binários adulterados. Mas o principal é o arquivo .torrent ou link magnético que aponta pra algum nó que normalmente confiamos, que vai nos fornecer a árvore Merkle com os hashes. Tendo os hashes, conseguimos checar se os pedaços que baixamos são válidos ou não.</p>
<p>Esses hashes são organizados numa tabelona, mais especificamente numa tabela distribuída, ou Distributed Hash Table, o DHT que eu fico falando. Pra quem é desenvolvedor web, é parecido com um DynamoDB. Cada item nessa tabelona ganha um ID longo de 160 bits. E os possíveis IDs são particionados entre os diversos nós da rede, de tal forma que cada nó só é responsável por um pedaço do espaço de IDs possíveis.</p>
<p>Toda vez que algum nó precisa registrar alguma coisa, ele gera uma chave e com isso determina qual nó que é responsável pelo espaço de IDs que essa nova chave faz parte, e manda pra esse nó. Mesma coisa pra pesquisar dados na tabela, tendo o ID pra consultar, ele pergunta pro nó responsável por esse intervalo de IDs.</p>
<p>Uma rede de BitTorrent é gigantesca, milhares ou milhões de nós ao mesmo tempo. É impossível meu computador saber quais são todos os nós ativos neste exato momento no mundo. Em vez disso, cada nó mantém um tipo de tabela de roteamento com informações sobre um sub-conjunto dos nós na rede, preferencialmente os mais &ldquo;próximos&rdquo; do espaço de IDs. Quando alguém pede pro seu nó gravar ou ler um certo ID mas você não é o responsável, dá pra usar essa tabela de roteamento pra redirecionar pro nó certo.</p>
<p>E claro, um nó pode simplesmente desaparecer do nada. Como cada nó pode ser um computador caseiro, alguém pode desligar o PC e sair da rede. Por isso essas informações são redundantes entre vários nós. Os algoritmos de DHT e roteamento vão se balanceando e atualizando dinamicamente com nós entrando e nós saindo.</p>
<p>Se isso pareceu similar a um banco de dados NoSQL distribuído como Amazon DynamoDB ou Cassandra, é porque é. Uns três ou quatro episódios atrás eu expliquei como funcionam chaves distribuídas, depois assistam. BitTorrent não deixa de ser um grande banco de dados NoSQL distribuído. São tecnologias similares. Toda vez que você abre um programa como Transmission ou QBitTorrent pra baixar alguma coisa, está participando dessa coordenação toda e se tornando um nó réplica de um grande banco de dados NoSQL distribuído. Você só não sabia disso.</p>
<p>Diferente de um banco de dados, eu não preciso que todos os dados que já foram gravados num DHT, permaneçam lá pra sempre. Arquivos novos aparecem, arquivos que ninguém mais quer desaparecem. Se não tem ninguém compartilhando algum determinado arquivo, não preciso ter informações dele trafegando. Seria ineficiente. Só interessa arquivos que alguém está compartilhando.</p>
<p>Num Git não, eu preciso que tudo que os desenvolvedores do projeto fizeram, seja adicionar novos arquivos, modificar existentes, ou até apagar, esteja devidamente registrado no log de commits do repositório, e que seja possível voltar pra qualquer momento no passado e ter tudo intacto, exatamente como era. Não pode ser parcial. Por outro lado, não pode ficar no caminho do desenvolvedor e adicionar obstáculos. Se o desenvolvedor achar que quer reescrever a história, precisa deixar.</p>
<p>A resiliência do Git aparece numa equipe, com mais de uma pessoa. Basta ter mais um desenvolvedor que tem como comparar os repositórios nas máquinas de cada um e ver que houve problemas. Basta checar os hashes dos commits. Se tiver 3 desenvolvedores já temos como fazer um tipo rudimentar de consenso: o que tiver o histórico mais diferente tá errado.</p>
<p>Eu só expliquei os conceitos bem básicos, tem bem mais detalhes por trás pra vocês pesquisarem depois. Árvores Merkle são úteis pra BitTorrent e pra Git, mas também são usados em outros lugares. O sistema de arquivos ZFS, que é usado no TrueNAS pra grandes servidores de armazenamento também usa árvores Merkle pra detectar corrupção de dados e ter a funcionalidade de snapshots com pouco overhead.</p>
<p>Outras redes ponto a ponto como o IPFS, que é o InterPlanetary File System, um sistema de arquivos distribuídos global, usa árvores Merkle, mais especificamente Merkle DAGs como Git. Não só sistemas de arquivos, mas bancos de dados distribuídos como Cassandra ou Riak usam árvores de Merkle pra minimizar a quantidade de dados que precisam ser transmitidos pra sincronização de dados.</p>
<p>No começo do video eu mostrei a cadeia de certificados e como o certificado do meu domínio é assinado pela Letsencrypt que, por sua vez é assinado pela ISRG. O protocolo TLS usa uma variante de árvore de Merkle chamada Merkle Hash Tree, no framework de Certificado de Transparência pra fornecer um log auditável de certificados gerados. E essa característica é importante: qualquer coisa que precise ser auditável pode usar árvores Merkle pra oferecer &ldquo;prova de inclusão&rdquo;, de forma que é possível provar que um pedaço de dados faz parte de um determinado conjunto, sem revelar o conjunto inteiro.</p>
<p>E se você já parou pra estudar o básico de Bitcoin e outras criptomoedas, já deve ter se deparado com Árvores Merkle e Merkle Root. É o que está na raíz do bloco de transações da tal de blockchain. Um blockchain e um repositório Git ou a árvore de merkle de Bit Torrent são muito parecidos: eles garantem que não dá pra adulterar um bloco sozinho, sem precisar re-gerar todos os blocos à frente dele. Com um ingrediente a mais: o tal processo de mineração. Em Git, um rebase é uma operação razoavelmente rápida.</p>
<p>Mas num blockchain de Bitcoin, o equivalente de rebase de Git custa absurdamente caro, astronomicamente caro na real, o que garante que é perto de impossível fazer um rebase. É isso que torna o blockchain interessante. Um blockchain onde é barato fazer rebase não é diferente de um Git e, como reserva de valor, não vale nada. Tudo que expliquei neste episódio são as tecnologias que estão na fundação do Bitcoin.</p>
<p>Se conseguiram chegar até aqui, nós vimos na prática como encriptação com chaves simétrica funciona. Aprendemos como funciona troca de chaves usando criptografia assimétrica. Vimos como quebrar arquivos e trafegar pedaços de uma forma que conseguimos checar se os pedaços vieram corretamente e vimos como BitTorrent e Git funcionam com árvores Merkle. Tudo em computação moderna usa esses conceitos, em particular certificados digitais TLS, bancos de dados NoSQL e até Bitcoin, por isso é importante estudarem tudo. Se ficaram com dúvida deixem nos comentários abaixo, se curitram o video mandem um joinha, assinem o canal e não deixem de compartilhar com seus amigos. A gente se vê, até mais!</p>
]]></content:encoded><category>seguranca</category><category>bitcoin-e-criptomoedas</category><category>controle-de-versao</category><category>akitando</category></item><item><title>[Akitando] #146 - Protegendo e Recuperando Dados Perdidos - Git, Backup, BTRFS</title><link>https://akitaonrails.github.io/2023/10/19/akitando-146-protegendo-e-recuperando-dados-perdidos-git-backup-btrfs/</link><guid isPermaLink="true">https://akitaonrails.github.io/2023/10/19/akitando-146-protegendo-e-recuperando-dados-perdidos-git-backup-btrfs/</guid><pubDate>Thu, 19 Oct 2023 10:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/yfEhz9poqW8"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Fazia tempo que queria fazer um último episódio à playlist de armazenamento trazendo um pouco mais de BTRFS. Aproveito pra adicionar mais sobre Git, Docker tudo voltado ao tema de como não perder dados nunca mais e até como recuperar dados numa emergência.&lt;/p&gt;
&lt;p&gt;Tudo que você nem sabia que queria saber sobre armazenamento e gerenciamento de dados no seu HD.&lt;/p&gt;
&lt;h2&gt;Conteúdo&lt;span class="hx:absolute hx:-mt-20" id="conteúdo"&gt;&lt;/span&gt;
&lt;a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;00:00:00 - Intro&lt;/li&gt;
&lt;li&gt;00:00:51 - CAP 1 - Recapitulando Sistemas de Arquivos - Journaling&lt;/li&gt;
&lt;li&gt;00:02:42 - CAP 2 - Caminho pro Desastre! Git e GitHub&lt;/li&gt;
&lt;li&gt;00:07:21 - CAP 3 - Git SEM GitHub - Git Raíz&lt;/li&gt;
&lt;li&gt;00:12:25 - CAP 4 - Partição Perdida - Sem Superbloco&lt;/li&gt;
&lt;li&gt;00:16:09 - CAP 5 - Não Caia em Golpes - Aliexpress&lt;/li&gt;
&lt;li&gt;00:19:46 - CAP 6 - Tentando Consertar - Backup do Superbloco&lt;/li&gt;
&lt;li&gt;00:24:51 - CAP 7 - Tentando Consertar - Última Instância&lt;/li&gt;
&lt;li&gt;00:29:36 - CAP 8 - Checksums - Sistemas de Arquivos Modernos&lt;/li&gt;
&lt;li&gt;00:34:18 - CAP 9 - Deduplicação - Sistemas de Arquivos Modernos&lt;/li&gt;
&lt;li&gt;00:36:28 - CAP 10 - Copy On Write - Sistemas de Arquivos Modernos&lt;/li&gt;
&lt;li&gt;00:40:21 - CAP 11 - BTRFS no Ubuntu - Por que Manjaro?&lt;/li&gt;
&lt;li&gt;00:45:51 - CAP 12 - Snapshots e Timeshift - BTRFS&lt;/li&gt;
&lt;li&gt;00:53:56 - CAP 13 - Snapshots e Docker - Não Perca Espaço&lt;/li&gt;
&lt;li&gt;01:02:59 - CAP 14 - Meu NAS Anti Ransomware - Synology&lt;/li&gt;
&lt;li&gt;01:07:37 - CAP 15 - Pacotes, Docker, Flatpak, QEMU - Sistema Estável&lt;/li&gt;
&lt;li&gt;01:10:03 - Bloopers&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;span class="hx:absolute hx:-mt-20" id="links"&gt;&lt;/span&gt;
&lt;a href="#links" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;A Quick and Hacky Way to Serve a Git Repo over HTTP (&lt;a href="https://theartofmachinery.com/2016/07/02/git_over_http.html"target="_blank" rel="noopener"&gt;https://theartofmachinery.com/2016/07/02/git_over_http.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;NGrok (&lt;a href="https://ngrok.com/docs/getting-started/"target="_blank" rel="noopener"&gt;https://ngrok.com/docs/getting-started/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;TestDisk LiveCD (&lt;a href="https://www.cgsecurity.org/wiki/TestDisk_Livecd"target="_blank" rel="noopener"&gt;https://www.cgsecurity.org/wiki/TestDisk_Livecd&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;TestDisk Docs (&lt;a href="https://www.cgsecurity.org/testdisk_doc/presentation.html#testdisk-partition-recovery"target="_blank" rel="noopener"&gt;https://www.cgsecurity.org/testdisk_doc/presentation.html#testdisk-partition-recovery&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;ArchWiki: BTRFS (&lt;a href="https://wiki.archlinux.org/title/Btrfs"target="_blank" rel="noopener"&gt;https://wiki.archlinux.org/title/Btrfs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Timeshift and grub-btrfs in Ubuntu (&lt;a href="https://www.lorenzobettini.it/2022/10/timeshift-and-grub-btrfs-in-ubuntu/"target="_blank" rel="noopener"&gt;https://www.lorenzobettini.it/2022/10/timeshift-and-grub-btrfs-in-ubuntu/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;timeshift-autosnap-apt (&lt;a href="https://github.com/wmutschl/timeshift-autosnap-apt"target="_blank" rel="noopener"&gt;https://github.com/wmutschl/timeshift-autosnap-apt&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Scheduled snapshots not working (&lt;a href="https://github.com/teejee2008/timeshift/issues/135"target="_blank" rel="noopener"&gt;https://github.com/teejee2008/timeshift/issues/135&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Why is Grub menu not shown when starting my computer? (&lt;a href="https://askubuntu.com/a/1187104"target="_blank" rel="noopener"&gt;https://askubuntu.com/a/1187104&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Full-disk encryption with Btrfs on Ubuntu Xenial (&lt;a href="https://albertodonato.net/blog/posts/full-disk-encryption-with-btrfs-on-ubuntu-xenial.html"target="_blank" rel="noopener"&gt;https://albertodonato.net/blog/posts/full-disk-encryption-with-btrfs-on-ubuntu-xenial.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;How To Add Swap Space on Ubuntu 20.04 (&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-20-04"target="_blank" rel="noopener"&gt;https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-20-04&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Swapfile Swapon invalid argument (&lt;a href="https://unix.stackexchange.com/a/713929"target="_blank" rel="noopener"&gt;https://unix.stackexchange.com/a/713929&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Thoughts on swap partition vs swap file in subvolume with Btrfs? (&lt;a href="https://www.reddit.com/r/archlinux/comments/ymq7ve/thoughts_on_swap_partition_vs_swap_file_in/?onetap_auto=true"target="_blank" rel="noopener"&gt;https://www.reddit.com/r/archlinux/comments/ymq7ve/thoughts_on_swap_partition_vs_swap_file_in/?onetap_auto=true&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;How do I use Timeshift/btrfs to permanently restore to a previous snapshot? (&lt;a href="https://forum.manjaro.org/t/how-do-i-use-timeshift-btrfs-to-permanently-restore-to-a-previous-snapshot/124683"target="_blank" rel="noopener"&gt;https://forum.manjaro.org/t/how-do-i-use-timeshift-btrfs-to-permanently-restore-to-a-previous-snapshot/124683&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SCRIPT&lt;span class="hx:absolute hx:-mt-20" id="script"&gt;&lt;/span&gt;
&lt;a href="#script" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Olá pessoal, Fabio Akita&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/yfEhz9poqW8"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Fazia tempo que queria fazer um último episódio à playlist de armazenamento trazendo um pouco mais de BTRFS. Aproveito pra adicionar mais sobre Git, Docker tudo voltado ao tema de como não perder dados nunca mais e até como recuperar dados numa emergência.</p>
<p>Tudo que você nem sabia que queria saber sobre armazenamento e gerenciamento de dados no seu HD.</p>
<h2>Conteúdo<span class="hx:absolute hx:-mt-20" id="conteúdo"></span>
    <a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>00:00:00 - Intro</li>
<li>00:00:51 - CAP 1 - Recapitulando Sistemas de Arquivos - Journaling</li>
<li>00:02:42 - CAP 2 - Caminho pro Desastre! Git e GitHub</li>
<li>00:07:21 - CAP 3 - Git SEM GitHub - Git Raíz</li>
<li>00:12:25 - CAP 4 - Partição Perdida - Sem Superbloco</li>
<li>00:16:09 - CAP 5 - Não Caia em Golpes - Aliexpress</li>
<li>00:19:46 - CAP 6 - Tentando Consertar - Backup do Superbloco</li>
<li>00:24:51 - CAP 7 - Tentando Consertar - Última Instância</li>
<li>00:29:36 - CAP 8 - Checksums - Sistemas de Arquivos Modernos</li>
<li>00:34:18 - CAP 9 - Deduplicação - Sistemas de Arquivos Modernos</li>
<li>00:36:28 - CAP 10 - Copy On Write - Sistemas de Arquivos Modernos</li>
<li>00:40:21 - CAP 11 - BTRFS no Ubuntu - Por que Manjaro?</li>
<li>00:45:51 - CAP 12 - Snapshots e Timeshift - BTRFS</li>
<li>00:53:56 - CAP 13 - Snapshots e Docker - Não Perca Espaço</li>
<li>01:02:59 - CAP 14 - Meu NAS Anti Ransomware - Synology</li>
<li>01:07:37 - CAP 15 - Pacotes, Docker, Flatpak, QEMU - Sistema Estável</li>
<li>01:10:03 - Bloopers</li>
</ul>
<h2>Links<span class="hx:absolute hx:-mt-20" id="links"></span>
    <a href="#links" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>A Quick and Hacky Way to Serve a Git Repo over HTTP (<a href="https://theartofmachinery.com/2016/07/02/git_over_http.html"target="_blank" rel="noopener">https://theartofmachinery.com/2016/07/02/git_over_http.html</a>)</li>
<li>NGrok (<a href="https://ngrok.com/docs/getting-started/"target="_blank" rel="noopener">https://ngrok.com/docs/getting-started/</a>)</li>
<li>TestDisk LiveCD (<a href="https://www.cgsecurity.org/wiki/TestDisk_Livecd"target="_blank" rel="noopener">https://www.cgsecurity.org/wiki/TestDisk_Livecd</a>)</li>
<li>TestDisk Docs (<a href="https://www.cgsecurity.org/testdisk_doc/presentation.html#testdisk-partition-recovery"target="_blank" rel="noopener">https://www.cgsecurity.org/testdisk_doc/presentation.html#testdisk-partition-recovery</a>)</li>
<li>ArchWiki: BTRFS (<a href="https://wiki.archlinux.org/title/Btrfs"target="_blank" rel="noopener">https://wiki.archlinux.org/title/Btrfs</a>)</li>
<li>Timeshift and grub-btrfs in Ubuntu (<a href="https://www.lorenzobettini.it/2022/10/timeshift-and-grub-btrfs-in-ubuntu/"target="_blank" rel="noopener">https://www.lorenzobettini.it/2022/10/timeshift-and-grub-btrfs-in-ubuntu/</a>)</li>
<li>timeshift-autosnap-apt (<a href="https://github.com/wmutschl/timeshift-autosnap-apt"target="_blank" rel="noopener">https://github.com/wmutschl/timeshift-autosnap-apt</a>)</li>
<li>Scheduled snapshots not working (<a href="https://github.com/teejee2008/timeshift/issues/135"target="_blank" rel="noopener">https://github.com/teejee2008/timeshift/issues/135</a>)</li>
<li>Why is Grub menu not shown when starting my computer? (<a href="https://askubuntu.com/a/1187104"target="_blank" rel="noopener">https://askubuntu.com/a/1187104</a>)</li>
<li>Full-disk encryption with Btrfs on Ubuntu Xenial (<a href="https://albertodonato.net/blog/posts/full-disk-encryption-with-btrfs-on-ubuntu-xenial.html"target="_blank" rel="noopener">https://albertodonato.net/blog/posts/full-disk-encryption-with-btrfs-on-ubuntu-xenial.html</a>)</li>
<li>How To Add Swap Space on Ubuntu 20.04 (<a href="https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-20-04"target="_blank" rel="noopener">https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-20-04</a>)</li>
<li>Swapfile Swapon invalid argument (<a href="https://unix.stackexchange.com/a/713929"target="_blank" rel="noopener">https://unix.stackexchange.com/a/713929</a>)</li>
<li>Thoughts on swap partition vs swap file in subvolume with Btrfs? (<a href="https://www.reddit.com/r/archlinux/comments/ymq7ve/thoughts_on_swap_partition_vs_swap_file_in/?onetap_auto=true"target="_blank" rel="noopener">https://www.reddit.com/r/archlinux/comments/ymq7ve/thoughts_on_swap_partition_vs_swap_file_in/?onetap_auto=true</a>)</li>
<li>How do I use Timeshift/btrfs to permanently restore to a previous snapshot? (<a href="https://forum.manjaro.org/t/how-do-i-use-timeshift-btrfs-to-permanently-restore-to-a-previous-snapshot/124683"target="_blank" rel="noopener">https://forum.manjaro.org/t/how-do-i-use-timeshift-btrfs-to-permanently-restore-to-a-previous-snapshot/124683</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Estou bem atrasado com a segunda parte do video da Rinha, e vai demorar mais um pouco. Honestamente, o tema me deu burn out, então resolvi fazer um video antes que não deixa de ser um pré-requisito porque até o final vou explicar um pouco mais sobre Docker e Docker Compose. Mas antes, quero falar de um problema que vira e mexe vejo desenvolvedores tendo: perder dados.</p>
<p>Sim, acidentes acontecem, e tanto o hardware quanto sistemas operacionais de hoje em dia, ajudam muito a não perder dados. Mas sério, a maioria de vocês não ia conseguir sobreviver com a tecnologia rudimentar que a gente tinha nos anos 80 e 90. Eu mesmo não lembro mais a última vez que perdi dados. Acho um absurdo que programadores ainda percam tempo com esse tipo de problema, então quero discutir diversas soluções pra isso. Vamos lá.</p>
<p>(&hellip;)</p>
<p>Um bom programador é mestre da sua máquina, nunca o contrário. Quando seu hardware perde seus dados e te força a passar perrengue, ele se tornou seu mestre, e você não é mais que um mero peão de um objeto inanimado. Vocês tem que mostrar quem manda. Vamos a alguns fatos. Em termos de sistema operacional, tanto faz se usa Mac, ou Windows ou Linux. Nenhum deles é muito melhor que o outro em termos de garantia de dados. Não é porque escolheu usar Mac que está magicamente mais protegido do que Windows. Isso não é verdade faz anos.</p>
<p>Vejam minha série sobre armazenamento. O NTFS do Windows tá longe de ser perfeito, cheio de problemas ainda, mas pro dia a dia, você não vai ter mais problemas do que teria com um ext4 no Linux ou APFS no Mac. Todos tem journaling, por exemplo. Significa que, mesmo que acabe a luz no meio de uma operação de disco, seu sistema de arquivos não vai ficar corrompido a toa. Vira e mexe alguns blocos podem se perder mesmo, mas as rotinas de manutenção padrão, são suficientes pra resolver a maioria dos problemas.</p>
<p>Quando alguém perde dados só existem duas razões: porque comprou um HD muito do porcaria ou porque cometeu erro humano mesmo. Aliás, vira e mexe alguém faz aquele meme de digitar &ldquo;rm -rf /&rdquo; e diz que iniciante não sabe o que isso faz. Mas parece que quem faz o meme também não sabe. Já faz algum tempo que o comando &ldquo;rm&rdquo; não apaga a raíz assim do nada. Precisa ou digitar &ldquo;/*&rdquo; com asterisco no final ou usar a opção &ldquo;&ndash;no-preserve-root&rdquo; pra realmente apagar a raíz. The more you know.</p>
<p>Enfim, nenhum sistema operacional vai resolver qualquer um desses problemas. Hardware porcaria só tem uma solução: jogar fora e comprar um de verdade. Toda vez que estiver tendo problemas frequentes, a probabilidade maior é hardware porcaria. Já a parte do erro humano, vamos tentar mitigar um pouco disso hoje.</p>
<p>Alguns meses atrás, um conhecido foi tentar instalar Linux como dual boot com Windows no notebook dele. Não sei qual era a marca, nem nada. Mas ele se ferrou: a partição Linux não bootava mais. Agora vem o problema: as últimas coisas que ele fez no projeto dele, não tinha dado push pro GitHub ainda. E, como praticamente todo mundo aqui assistindo, não tinha backup.</p>
<p>Tudo que vou narrar a partir daqui poderia ter sido evitado. Hoje em dia, todo programador usa Git. Isso é obrigatório. Mesmo se trabalha numa empresa, o mais porcaria possível, que por alguma razão do além, ainda não está usando Git, não existe desculpa: o projeto não precisa adotar Git pra você usar Git. GitHub é opcional, Git não. Marrete esse conceito na sua cabeça. Não importa se é um projetinho pessoal só na sua máquina. Faça &ldquo;git init&rdquo; e comece a fazer &ldquo;git commit&rdquo;.</p>
<p>Git não precisa de um servidor externo. Digamos que essa empresa hipotética super tosca pra onde está trabalhando não queira usar Git pra não ter que pagar GitHub ou alguma bobagem desse tipo. Ou usa alguma alternativa obsoleta de 20 anos atrás como a enorme porcaria de um SourceSafe da vida. Aí são dois grandes erros imperdoáveis. E digamos que você também não queira investir numa conta paga de GitHub, portanto não tem como fazer repositórios privados. E agora? Como faz?</p>
<p>Vou repetir: não precisa existir projeto no GitHub pra usar Git. Muitos iniciantes confundem isso. Git é uma ferramenta, GitHub é um serviço. GitHub precisa de Git, mas Git não precisa de GitHub. Qualquer diretório do seu computador pode virar um repositório Git, basta digitar &ldquo;git init&rdquo; dentro e pronto, ele cria o subdiretório &ldquo;.git&rdquo; que é o repositório propriamente dito. Agora basta fazer &ldquo;git add .&rdquo; pra adicionar tudo nesse diretório e &ldquo;git commit -m&rdquo; com uma mensagem pra criar um commit e já era. Só isso.</p>
<p>Eu fiz dois videos onde conto tudo que você deveria saber sobre Git e recomendo que assistam. Inclusive eu conto como lá nos primórdios de Git, quando realmente ninguém nem sabia que existia, eu trabalhei em projetos de clientes que usavam coisas como Subversion, mas só eu trabalhava com Git e usava um plugin chamado git-svn pra integrar os dois. Se lá em 2007 eu conseguia usar, sem nenhum suporte, em 2023, não existe desculpa aceitável pra não usar.</p>
<p>Isso resolve o problema de acidentalmente apagar um arquivo que não deveria, ou mudar de ideia num código que acabou de fazer, e conseguir voltar pra como estava no último commit. Digamos que você apagou um arquivo. Puts, apagou o arquivo errado? E agora? Se você fez a coisa certa, que é ficar fazendo pequenos commits, o tempo todo, ainda deve ter a versão anterior no último commit. Faça &ldquo;git status&rdquo;, olha ele marcado pra deleção. Basta fazer &ldquo;git checkout&rdquo; e o nome do arquivo, e pronto, listamos e olha só o arquivo de volta aqui.</p>
<p>Qualquer programador que use de desculpa &ldquo;puts, apaguei o arquivo sem querer e perdi tudo&rdquo;, ainda é um iniciante que precisa aprender a usar git. Ah, mas eu gosto de primeiro terminar tudo, tudo e depois fazer commit. E eu digo que você é um idiota. Vou dar um exemplo. Digamos que fiz um código aqui num arquivo, mas não terminei. E acabou o dia. Como sou disciplinado, já vou fazer um &ldquo;git commit -m &lsquo;codigo no meio&rsquo;&rdquo;. Puts, mas que commit feio. Melhor um commit feio agora do que ter um acidente e perder o arquivo. Agora posso desligar o computador e ir dormir.</p>
<p>Acordo no dia seguinte, vou continuar editando esse arquivo e aí sim, finalmente termino. Agora posso fazer um &ldquo;git commit -m &lsquo;codigo final&rsquo; &ndash;amend&rdquo;. Essa opção amend, vai desfazer o commit anterior e trocar por esse novo commit, com a nova mensagem mais bonita e descritiva. Rode &ldquo;git log&rdquo; olha só, aquela mensagem feia com código parcial sumiu e estou com o commit bonito. Agora posso fazer push pro servidor se quiser.</p>
<p>Dentro de um branch, faça quanto commits quiser, dê push de commits feios, e no final, faça um squash do branch antes de criar um pull request, se fizer muita questão. É uma das formas de lidar com isso. O histórico de commits nunca deve ser reescrito no branch master ou main, mas no seu branch, onde só você está trabalhando, foda-se, faça cinquenta commits. Daqui uma semana quando terminar, faça squash, se a quantidade de commits incomodar. Daí manda merge pra master.</p>
<p>Existem várias nuances no que falei, não é uma regra escrita em pedra, essas convenções vão variar de equipe pra equipe. Mas voltando ao meu ponto inicial: digamos que você não tenha um repositório remoto no GitHub. Estou trabalhando sozinho na minha máquina local. E não tenho pra onde dar push. Se der alguma pane catastrófica na minha máquina, eu perco tudo. Como evitar isso?</p>
<p>Se o projeto que estiver trabalhando for de cliente, e você for freelancer, por favor, pague uma conta no GitHub ou GitLab e faça push pra lá. Não desperdice o tempo de todo mundo. Isso tem nome: se chama economia porca. Porque na hora que alguma coisa der pane, não tem mais como recuperar. Isso dito, você é mão de vaca bagarai. Ok. Vou te dar a solução mais barata de todos os tempos. Espete um pendrive ou HD externo no seu PC.</p>
<p>Do diretório do seu projetinho faça &ldquo;git clone &ndash;bare . /media/pendrive/projeto.git&rdquo;. Isso vai criar um clone do seu projeto nesse pendrive. Lógico o caminho de onde o pendrive montou vai variar de distro pra distro. Adicione o pendrive como origin com &ldquo;git remote add origin /media/pendrive/projeto.git&rdquo;. Pronto. Vamos continuar trabalhando, criando um novo arquivo, adicionando, fazendo commit. Mas agora posso fazer o bom e velho &ldquo;git push origin master&rdquo; e ele vai mandar praquele clone no pendrive. O pendrive está fazendo o papel de GitHub pra você, sacou?</p>
<p>E podemos ir mais longe. Digamos que é aquela sexta feira no escritório, você está finalizando uma nova funcionalidade que precisa entregar hoje. O Tech lead tá lá esperando pra finalizar a parte dele. Mas aí dá uma daquelas panes no GitHub e ele fica fora do ar algumas horas. Bem no fim da sexta feira. Fodeu né? Fica pra segunda então? Não. Faça git push no pendrive, passa o pendrive pro tech lead, ele consegue adicionar como remote e dar pull no repo dele. E vai conseguir continuar o trabalho na máquina dele, mesmo sem ter GitHub.</p>
<p>GitHub é uma conveniência, mas não é mandatório. Qualquer coisa pode substituir. O que você perde enquanto ele tá fora do ar são as conveniências, a integração com o continuous integration, os pull requests que tavam em aberto e você não puxou, lista de issues e coisas assim, mas o código em si, dá pra manipular de vários jeitos diferentes.</p>
<p>Só pra concluir essa parte de Git, vou mais longe no exemplo do GitHub saindo fora do ar. Meu exemplo com pendrive assume todo mundo trabalhando presencialmente no mesmo escritório. Mas que cabeça a minha, todo mundo tá home office hoje né? Então fodeu mesmo, sem GitHub pra centralizar, se cair, todo mundo fica sem conseguir colaborar né? Só que não.</p>
<p>Eu terminei a minha parte na minha máquina, o tech lead tá lá na casa dele, precisando puxar o que acabei de fazer pra continuar. Não temos GitHub. Como faz? Simples. Sabe o clone &ldquo;bare&rdquo; que eu fiz no tal pendrive? Vamos modificar ele. Basta entrar no diretório &ldquo;.git&rdquo; e começar fazendo &ldquo;git &ndash;bare update-server-info&rdquo; e depois &ldquo;mv hooks/post-update.sample hooks/post-update&rdquo; e pronto, nosso repositório está pronto pra ser integrado a um servidor web qualquer. O mais simples que consigo imaginar é o http server que vem no Python. Com uma única linha de comando &ldquo;python -m http.server 8000&rdquo; e já era, temos um servidor web git.</p>
<p>Pra testar, basta ir no &ldquo;/tmp&rdquo; ou qualquer lugar e fazer &ldquo;git clone <a href="http://localhost:8000"target="_blank" rel="noopener">http://localhost:8000</a> teste&rdquo; e voilá, consigo fazer clone do meu repositório local. Quem trabalha com VPNs como TailScale ou ZeroTier já sabe o que fazer: só trocar localhost pelo IP da VPN que todo mundo da equipe enxerga e fazer o mesmo que descrevi antes: o tech lead adiciona um remote apontando pra esse servidor, consegue dar &ldquo;git fetch&rdquo; do seu branch e pode continuar a trabalhar tranquilamente enquanto povo do GitHub fica lá brincando de restartar servidor até voltar.</p>
<p>Outra solução rápida é instalar ngrok na sua máquina. Ngrok é o jeito mais simples de criar um túnel de fora pra dentro da sua máquina. Muita gente usa pra mostrar uma aplicação web rodando na sua máquina local que ainda não tem servidor de testes ou staging. Assim um cliente que não está presente consegue visualizar o andamento do trabalho olhando o que tá rodando no localhost da sua máquina. Entenderam?</p>
<p>Olha só, posso rodar &ldquo;ngrok http 8000&rdquo;. Isso vai abrir um tunel no serviço ngrok apontando pro nosso servidorzinho python local. Ele nos dá essa URL gigante de &ldquo;Forwarding&rdquo;. Podemos passar ela pro nosso querido tech lead. Na máquina dele, ele pode fazer &ldquo;git clone &quot; e essa URL-zona e &hellip; voilá, olha só. Da casa dele, tá conseguindo puxar os commits da minha máquina. Entenderam o que aconteceu aqui?</p>
<p>Num dia normal, o trabalho iria parar por horas até o GitHub voltar. Mas sabendo dessas coisas simples e triviais, conseguimos continuar trabalhando como se nada tivesse acontecido. Esse é o poder do Git e de saber como juntar componentes simples. Criar um clone &ldquo;bare&rdquo;, habilitar  pra ser servido via http, subir um servidor http simples com python e abrir um túnel seguro usando ngrok.</p>
<p>Mas claro, tudo isso assume que você, no mínimo, usa Git minimamente direito. Fazendo commits frequentes. Fazendo backups frequentes. Só quem já perdeu dados sabe da importância disso. Todos que dizem que é desnecessário só provam que são virgens e ainda precisam perder alguma coisa importante pra aprender. Vocês assistindo podem aproveitar o que falei e evitar ter que passar por esse estresse.</p>
<p>Digamos que não fez nada disso. Deu na telha que quer experimentar montar um dual boot na sua máquina pela primeira vez, e gosta de viver perigosamente, nem deu push dos projetos pro GitHub, nem copiou num pendrive, nem sequer fez commits, tá tudo parcial na sua máquina. Mas tá seguro, você assistiu um tutorial no YouTube, seus parças tão te chamando pra jogar Genshin a noite. Não tem tempo a perder, bora redimensionar o disco, criar uma segunda partição de Windows e passar a noite jogando.</p>
<p>Só que aí dá tudo errado. Tudo na primeira vez, sempre vai dar errado. Aceitem essa máxima. Se for a primeira vez de qualquer coisa, já comecem sabendo que não vai funcionar de primeira. Não custa nada espetar um HD externo e copiar as coisas mais importantes lá. Mas alas, a pressa é inimiga da perfeição mesmo. Não lembro onde guardei o HD externo, não quero perder tempo procurando. Foda-se, vai dar certo, confia, cola no pai.</p>
<p>É muito difícil saber exatamente o que acontece em panes. Se foi algum procedimento errado ou se foi mesmo algum defeito de hardware. Pra tentar demonstrar algo parecido, fiz uma máquina virtual com um Ubuntu e um disco virtual. Esse disco aparece como dispositivo &ldquo;/dev/vdb&rdquo; de virtual device B, já que o virtual device A é disco primário. Está formatado como ext4, dentro coloquei meu projeto da rinha em rails, um ISO de Ubuntu. Faça de conta que é um HD normal do seu notebook.</p>
<p>Agora vou desmontar com &ldquo;umount&rdquo;. Um dos jeito de simular um erro de hardware é escrever por cima dos primeiros bytes no começo do dispositivo. Lembram dos meus videos sobre sistemas de arquivo? O que é uma partição? É uma estrutura de dados que guarda coisas como em qual endereço começa e onde termina uma partição, o tipo de sistema de arquivo, localização dos blocos. Esses primeiros bytes formam o que chamamos de Super Bloco.</p>
<p>Olha só, se eu tentar montar esse dispositivo de novo, vai dar erro. O Linux não sabe o que fazer, porque não tem mais o superbloco. Os dados em si ainda estão na partição, no linguição de bytes, mas sem o superbloco não dá pra saber onde começa e onde termina o que. Pense assim, seus arquivos são como casas montados com lego. Cada lego está enterrado num campo de futebol, o seu HD. Mas o superbloco tem a localização de cada bloco, e as instruções de como remontar sua casa de lego. Sem esse superbloco, mesmo achando as peças, não sabemos se ainda falta mais blocos ou como encaixar esses blocos.</p>
<p>Se acontecer um erro como esse, onde a partição se recusa a bootar ou montar e aparecem erros como esse, filesystem errado, ou opção errada, ou superbloco errado, é um péssimo sinal. A primeira coisa que recomendo fazer é evitar insistir. Se usar comandos errados que sobrescrevem em cima do disco, se fodeu, vai perder dados. Muito cuidado com comandos como fdisk ou mkfs, que é o equivalente de format de Linux.</p>
<p>A forma de diagnosticar algo assim é achar outro computador, outro notebook, baixar a ISO de algum Linux como o próprio Ubuntu, gravar num pendrive, daí passar a fazer boot pelo pendrive. A maioria das distros Linux tem um ambiente de Live CD que boota um ambiente operacional inteiro, que inclusive se conecta no seu Wifi e permite instalar qualquer pacote. Isso permite baixar quaisquer ferramentas de diagnóstico e recuperação.</p>
<p>A partir desse ambiente, ele vai achar seu dispositivo como um &ldquo;/dev/sdb&rdquo; ou &ldquo;/dev/nvme2&rdquo; ou qualquer coisa assim. Assistam meus videos sobre Linux, o de Gentoo principalmente, porque a instalação dele exige manualmente fazer exatamente esse tipo de operação. Por isso fiz aquele video: qualquer um que já instalou um Gentoo ou Arch do zero, já sabe como fazer isso.</p>
<p>Antes de continuar deixa eu comentar uma coisa: porque esse tipo de problema acontece? Digamos que não foi erro humano. SSDs quebram assim do nada mesmo? E sim, quebram. Se for no Aliexpress e pesquisar por SSDs e ordenar a partir do menor preço, sim, as primeiras dez páginas, no mínimo, é quase 100% de chance de vir um tijolo. Aliás, se vier um tijolo pelo menos vai ser mais seguro. Problema é quando vem um drive mesmo e você confia com seus dados.</p>
<p>Primeiro, tem os golpes mais óbvios. Quando desparafusa e abre, dentro não tem uma placa controladora e chips de memória Flash. Não, tem um adaptador USB barato pra micro SD Card. Chips de SD Card sempre usam componentes mais baratos que SSD. Eles foram feitos pra serem quase descartáveis, justamente pra uso em câmeras fotográficas ou celulares. Não foram pensados pra uso como um HD de notebook. Não tem banda pra isso, não tem controlador pra isso, não tem memória RAM de cache, a qualidade da memória NAND flash é muito inferior. Por isso são muito mais baratos.</p>
<p>Já disse isso antes e vou repetir: nunca deixe nada importante num SD Card. É comum eles corromperem. SD Card deve ser usado única e exclusivamente como armazenamento temporário, tempo suficiente pra chegar em casa da sua viagem, e descarregar os cartões no seu notebook. Mas os tais SSDs baratos de marcas chinesas genéricas contam com sua burrice. Ninguém nunca abre a carcaça de um SSD pra ver o que tem dentro, e eles contam com isso.</p>
<p>Mesmo quando vem com componentes de SSD de verdade, eles vendem chips de 500MB como se fosse 1TB. Por isso sai pela metade do preço, mas o controlador tá gambiarrado e mente pro seu computador dizendo que tem 1TB. Se você tentar mesmo gravar mais que 500MB, ele vai começar a sobrescrever os dados do começo. Como a maioria das pessoas vai só gravando mas não checa na hora se tudo gravou mesmo, só vai descobrir da pior maneira: quando precisar do arquivo, tentar acessar, e descobrir que os arquivos não estão mais lá ou estão incompletos e corrompidos.</p>
<p>Como evitar isso? Nunca, jamais compre marcas chinesas genéricas desconhecidas. Compre Samsung, Crucial, Sandisk, Western Digital, só marcas reconhecidas de longa data. E de lojas com alguma reputação e não um revendedor aleatório desconhecido. Muitos vão pegar a carcaça de um Samsung quebrado, colocar o tal adaptador USB com SD card e te revender como se fosse original Samsung. Só compre em loja de verdade e só compre item novo e lacrado. Pense em SSD como escova de dente: você não quer um usado.</p>
<p>Em quais situações você pode arriscar um SSD mais barato genérico? Se for pra ser sua biblioteca de Steam, por exemplo. HD pra games. Aí foda-se se perder dados. Se o HD for mesmo um golpe, pega outro HD barato, e baixa tudo da Steam de novo. Mas nunca, jamais, sob nenhuma circunstância use um HD desses pra trabalho, ou mesmo pra guardar coisas importantes pra você, como as fotos da sua família, dos seus filhos, das suas viagens. Você vai perder esses dados pra sempre.</p>
<p>Obviamente, todo mundo concordou com o que falei, mas tá fazendo exatamente o que eu disse pra não fazer. Não usou Git. Não fez uma cópia dos seus projetos num pendrive ou hd externo. Foi querer usar o mesmo HD de trabalho pra games com dual boot. E chegamos na situação que ele não boota mais e nem conseguimos mais montar a partição. O próximo passo é arranjar um outro HD, seja interno, seja externo, que tenha mais espaço livre do que o total do HD quebrado. Vamos fazer uma cópia bit a bit, pra garantir que não vamos perder mais nada.</p>
<p>Usando o comando &ldquo;lsblk&rdquo; podemos ver os dispositivos de armazenamento conectados. Aqui neste exemplo, tenho o &ldquo;/dev/vba&rdquo; que é a partição funcionando do meu Ubuntu, e o &ldquo;/dev/vdb&rdquo; que é a partição corrompida. Antes de tentar fazer qualquer coisa, o certo é usar o comando &ldquo;dd&rdquo; e fazer uma cópia, assim &ldquo;sudo dd if=/dev/vdb of=~/Downloads/backup.hd bs=4M&rdquo;. O nome de arquivo e extensão não importam, qualquer coisa serve. Dependendo do tamanho do seu HD quebrado e velocidade do seu PC, isso pode levar horas. Vai parecer que está travado. Deixa quieto lá, vai jantar e volta amanhã. O LOLzinho com os parça essa noite já miou, nem insiste.</p>
<p>Pronto, agora temos uma cópia bit a bit do HD nesse arquivo. Garanta que não vai perder ele, porque se fizer mais alguma coisa errada na tentativa de consertar, temos uma segunda chance com esse backup. Coloque num outro HD externo se quiser garantir. Mantenha longe de você por enquanto, você já causou problemas demais. Um profissional de recuperação de dados tem muito mais opções do que vou mostrar agora. Só vou falar o mais óbvio, porque eu também não sei tudo.</p>
<p>Felizmente muita gente já passou por situação similar, e a partição costuma ter backups desse superbloco. Mais de um na real. Faça o teste no seu próprio sistema. Num Arch linux, primeiro precisamos instalar o pacote &ldquo;e2fsprogs&rdquo;, que contém programas pra lidar com sistema de arquivos como ext2 ou ext4. Usando o comando &ldquo;lsblk&rdquo; podemos listar os dispositivos e com o comando &ldquo;dumpe2fs /dev/vdb | grep -i superblock&rdquo; podemos listar os tais superblocos e backups.</p>
<p>Como esperado, o superbloco primário está no endereço zero, mas veja quantos backups temos. Um no endereço 32 mil e tantos, outro no endereço 98 mil e tantos, oito backups. Um erro catastrófico é quando alguma coisa acontece que corrompe não só os primeiros bytes, mas todos os bytes até o último backup. É raro, mas pode acontecer, especialmente se você usar errado uma ferramenta como fdisk e sobrescrever uma nova partição em cima da antiga.</p>
<p>O comando &ldquo;dumpe2fs&rdquo; talvez não consiga listar esses superblocos caso a partição esteja corrompida. Tentando no dispositivo &ldquo;/dev/vdb&rdquo; olha só, ele dá erro e não lista nada. Podemos usar o comando &ldquo;mke2fs&rdquo; que normalmente serve pra formatar uma partição, mas passando a opção &ldquo;-n&rdquo; de &ldquo;dry-run&rdquo; vai só simular a execução do comando sem de fato executar nada. Lembra o que falei antes: não queremos que nada escreva nesse disco quebrado. O programa não está consultando a partição com problema, só dizendo que, se não fosse um dry-run, escreveria os superblocos nesses endereços que listou.</p>
<p>Pra facilitar recuperação é importante saber qual é o tipo de sistema de arquivos que escolhemos na instalação. No caso de Windows é fácil, acho que ninguém escolhe nada diferente de NTFS. Mac já complica porque antigamente tinha o HPFS+ que mudou pra APFS a partir do final de 2017 com o macOS 10.13, mas muita gente continuou usando o antigo até depois de 2018. Já Linux, maioria costuma escolher ext4. Tem gente que escolhe ReiserFS ou os novos ZFS ou BTRFS. Cada um deles tem ferramental diferente. O que estou mostrando até agora é assumindo que é ext4.</p>
<p>Agora podemos tentar recuperar o superbloco a partir dos backups. Temos que tentar um a um até conseguir. Talvez nenhum dos backups estejam bons, talvez já no primeiro resolva. Pra fazer essa recuperação usamos o comando &ldquo;fsck&rdquo; com opção &ldquo;-b&rdquo; e o número do backup. Agora é ir confirmando &ldquo;yes&rdquo; até o fim. E se continuar assim, yes, yes, olha só, terminou. Será que funcionou?</p>
<p>Pra saber, podemos tentar montar de novo. E sucesso, montou de primeira, sem nenhum erro. Se listarmos dentro, sim, aqui estão nossos arquivos. À primeira vista tudo intacto. Nem sempre as coisas acontecem assim bonitinho, lembrem-se que eu só simulei um problema num ambiente controlado. Sob condições ideais de temperatura e pressão, é assim que conseguimos recuperar.</p>
<p>O certo agora é não confiar que só porque parece tudo ok, parar aqui. O certo é plugar outro HD externo, e usar um programa como rsync pra fazer uma cópia dos arquivos pra outro lugar. Você quis economizar com HD barato, mas tá percebendo que pra recuperar agora vai precisar de vários outros HDs pra fazer o backup que não quis fazer antes? Depois de tudo copiado, o certo é formatar essa partição do zero e copiar os arquivos de volta. Mas o mais certo mesmo é, depois de copiar os arquivos pra um lugar seguro, se livrar desse HD.</p>
<p>Se foi problema de hardware e não erro humano, é provável que vai voltar a dar pau de novo. Nunca confie num hardware que já te deixou na mão uma vez. É lixo, joga fora. Se o cavalo te derruba, você sacrifica, é o que eu sempre digo. No mínimo troca por um SSD novo, de verdade, de uma Crucial ou Samsung da vida, e usa esse HD que deu pau num estojo externo pra ser HD USB só pra coisas não importantes como games da Steam.</p>
<p>Mas, digamos que o comando &ldquo;fsck&rdquo; não funcionou, nenhum dos backups estava bom e não deu pra recuperar o superbloco. A partir daqui depende da importância dos dados no seu HD. Faz de conta que é daqueles casos excepcionais onde sua carteira de Bitcoin com milhões de dólares está só nesse HD. Um puta erro de amador, mas já vimos isso acontecer antes. Nesse caso o melhor é procurar um profissional de recuperação de dados. Pra recuperações mais hardcore, ele vai desmontar o SSD, colocar num hardware especializado pra esse tipo de coisa e vai fazer magia negra. Dependendo da pane, talvez consiga recuperar.</p>
<p>Se não for esse caso, aí recomendo que instale a ferramenta TestDisk. Eu não tenho tanta experiência nisso, então recomendo que procure outros videos como este daqui. Vou deixar o link abaixo. O TestDisk propriamente dito tem algumas ferramentas de análise pra tentar recuperar as informações da partição, reconstruir o que conseguir, mas não tem nenhuma garantia que vai funcionar. Ele não é muito intuitivo de usar, por isso recomendo ler bem a documentação e assistir alguns videos de exemplo.</p>
<p>Seja por ter corrompido a partição ou seja porque você deletou arquivos acidentalmente, o caso é o mesmo: os bytes que compõe seu arquivo nunca são deletados de verdade. Eles ainda estão fisicamente no HD. Acontece o seguinte: o tal sistema de arquivos, como ext4, mantém um índice dizendo: arquivo chamado &ldquo;hello.txt&rdquo; tem 20 blocos, e os endereços desses blocos são X e Y. Quando pedimos esse arquivo, ele vai buscar esses blocos e nos devolve na ordem certa. Blocos são conjuntos de bytes, depende do sistema de arquivos dizer quantos bytes tem num bloco. Por isso falo que é tudo um linguição de bytes.</p>
<p>Quando mandamos apagar um arquivo, não vai bloco a bloco e escreve zeros em cima. Isso até existe, alguns sistemas chamam de &ldquo;Deleção segura&rdquo;. Só apaga o registro do arquivo &ldquo;hello.txt&rdquo; desse índice, daí perdemos a localização dos blocos. Mas os blocos em si continuam no HD. Lembram a metáfora dos legos enterrados no campo de futebol. Eles continuam lá enterrados, só jogamos fora o mapa da localização de cada peça.</p>
<p>Os blocos só vão se perder se algum novo arquivo escrever por cima deles. Por isso falamos que, se apagou algo por engano, a primeiríssima coisa a se fazer é desmontar esse HD e tirar da máquina, pra nenhum processo acabar gravando nada em cima por acidente. E por isso a segunda coisa é fazer um backup bit a bit com a ferramenta DD, porque isso vai preservar todos os blocos, exatamente como eles estavam até aquele momento. Daí podemos fuçar sem perigo de perder alguma coisa, porque tem o backup, a imagem exata.</p>
<p>No pacote do TestDisk existe a ferramenta Photorec. Também já expliquei em outro video a diferença de arquivos texto e arquivos binários, e expliquei como o sistema operacional sabe que um determinado binário é um executável. Todo arquivo binário, tem nos primeiros bytes, um cabeçalho que indica que tipo de arquivo é. Um JPEG, um PNG, um video MP4, um executável ELF de Linux, um documento de Word, todos tem cabeçalhos padrão.</p>
<p>Na metáfora dos Legos, é como ir desenterrando peça a peça, sabendo que um JPEG começa com um bloco vermelho, um Word começa com bloco azul, e aí tentar montar na tentativa e erro. Uma ferramenta como Photorec vai tentar, por tentativa e erro, localizar o começo dos arquivos e os blocos que fazem sentido vir depois.</p>
<p>Eu simulei isso nesse dispositivo &ldquo;/dev/vdb&rdquo;, fazendo de conta que o fsck não conseguiu recuperar. Rodando o photorec, ele vai pedir pra apontar um diretório em outra partição boa, pra onde queremos que vá gravando os arquivos que conseguir encontrar. Dependendo da quantidade de arquivos que tinha, vai demorar um bocado. No final vai surgir um diretório com um monte de arquivos com nomes esquisitos. É o máximo que podemos fazer se não tiver como recuperar o superbloco.</p>
<p>Olha só, tentando abrir alguns desses arquivos, de fato o conteúdo faz algum sentido. Mas claramente alguns estão incompletos. E vai vir muito lixo junto. Nesse ponto assuma que você se fodeu, perdeu a maior parte, e qualquer resquício que conseguir recuperar já é lucro. Entendeu? Se você perder o superbloco, perdeu os metadados, diretórios, nomes de arquivos, tudo. Nesse estágio estamos escovando bit mesmo.</p>
<p>Como não chegar nesse ponto? Só existe uma maneira: faça backup. É a recomendação mais velha da computação que ninguém segue, e eu nem tenho esperanças que vocês assistindo vão seguir, mas a gente tenta. E é a economia mais porca de todas não fazer backup. Um SSD Sandisk de 1TB, no MercadoLivre, custa uns 300 reais. Sandisk não é e melhor marca, mas é mil vezes melhor que um chinês genérico no fim do Aliexpress. Um case USB que transforma esse SSD num drive externo, não custa 30 reais. Uma balada paga isso.</p>
<p>Todo sistema operacional já vem com software de backup. Nem precisa instalar nada. Use o Time Machine do MacOS, use o app de Backup que já vem no Windows, use um app como Deja Vu que já vem nos Ubuntu. Não seja preguiçoso. E backup não é só pro caso de dar pane de hardware no seu SSD barato, tem caso pior. Já ouviu falar de ransomware? Vou contar como eu sou imune a isso.</p>
<p>Se num extremo está a pessoa que confia todos os seus dados importantes num SSD genérico chinês do fundo do Aliexpress, sem nenhum backup, do outro lado estou eu. Já mostrei minha solução em outros videos e no Insta, mas eu tenho um NAS de 60 terabytes. São 6 HDs IronWolf de nível industrial de 12 terabytes cada, em sistema de redundância, onde não perco nada mesmo se um HD morrer agora. Eu uso não só pra backup, mas faço coisas como edição dos meus videos direto dele.</p>
<p>Claro que não dá pra recomendar isso, é uma solução de mais de 30 mil reais de investimento, na época. Como preços pioraram, agora, é inviável. Minha recomendação é um HD externo USB, coisa de menos de 400 reais, e que vai evitar muita dor de cabeça. Qualquer coisa acima é luxo. O mínimo é suficiente, faça o que puder, só não faça nada.</p>
<p>Quero aproveitar pra falar de outra coisa que não falei na série de armazenamento: sistemas de arquivos modernos. Lembrando, sistemas de arquivos como os antigos FAT16 e seus derivados como FAT32, ou mesmo ext2 nos primeiros Linux, foram pensados numa época onde espaço em disco era caro. RAM era caro. CPU era caro. Não havia recursos pra desperdiçar. Era a época onde falo que a gente escovava bit, no sentido que cada bit extra fazia diferença.</p>
<p>Segurança e performance são trade offs. Quanto mais segurança quiser, menos conveniente as coisas vão ser e mais recursos vamos precisar. Quando gravamos um arquivo num HD, o sistema de arquivos divide os bytes em blocos e grava nos setores do disco. Esse arquivo fica lá, semanas, meses, às vezes anos, sem ser acessado. Um belo dia você resolve abrir esse arquivo. Digamos que é uma planilha. Como eu sei que um dos setores físicos do disco magnético não falhou, flipou um bit, e algum dos valores na planilha não estão errados?</p>
<p>Num sistema antigo como FAT16, não tem como saber. Se corromper, corrompeu e não vamos saber. Num sistema moderno existe checksums. Num APFS de Mac ou NTFS de Windows, existe checksum no nível do arquivo. Quando o arquivo é gravado, o sistema gera um checksum e grava junto. Quando pedimos pra ler, ele pega os blocos e recalcula o checksum pra comparar com o que estava guardado. Se os checksums forem diferentes, significa que o arquivo está corrompido. Não é isso, mas se não sabe o que é um checksum, pense num dígito verificador.</p>
<p>Essa informação é super importante, porque numa planilha com milhares de valores, podemos estar tomando decisões importantes de forma errada por causa de um único bit errado. Um único bit pode mudar um valor de 100 reais pra milhões, por exemplo. Mas se o sistema avisar que o checksum está errado, sabemos que não podemos confiar nesse arquivo. O que fazer? Se você seguiu a recomendação e fez a porcaria de um backup, podemos ir nesse backup recuperar esse arquivo.</p>
<p>Em sistemas de arquivos mais modernos, pensados pra situações mais parrudas como servidores de arquivos, pense um Dropbox ou Google Drive da vida, com dados de milhões de pessoas, existem opções como o lendário ZFS da antiga Sun e o novo BTRFS do Linux. Eles tem checksum por blocos. Então conseguimos saber qual bloco dentro do arquivo está corrompido. Mas eles vão mais longe.</p>
<p>Meu NAS Synology está em configuração de RAID, com múltiplos HDs. Significa que o mesmo bloco tem cópias redundantes em mais de um HD. E BTRFS tem uma rotina que recomenda rodar periodicamente, digamos de 3 em 3 meses ou de 6 em 6 meses, chamado &ldquo;scrubbing&rdquo;. É um processo pesado que vai rodar em background, sem interromper seu dia a dia, e rechecar checksum a checksum de todos os blocos. Se notar que uma falhou, e estivermos em RAID, ele vai procurar uma cópia redundante desse bloco. A chance dos dois estarem corrompidos ao mesmo tempo é baixa, então podemos usar o bloco intacto pra consertar o que corrompeu.</p>
<p>Todo HD falha. Não importa se é HD mecânico com discos ou chips de Flash NAND de SSDs ou NVMEs. Todo chip Flash tem uma vida útil de quantidade de operações de escrita. Depois que passar desse limite, estraga e é isso. As checagens que falei, em muitos casos, só vão indicar mais cedo que algo falhou, mas sem redundância, sem uma segunda cópia intacta, não tem como adivinhar o que era o dado original que corrompeu, só dá pra saber que corrompeu.</p>
<p>Checagens de checksum, recuperação de blocos corrompidos, gastam RAM, gastam CPU. Não sai de graça. Servidores de arquivo parrudos como ZFS precisam de gigabytes de RAM pra serem eficientes, e vão usar toda RAM possível. Claro que tem mais vantagens que só isso. NTFS acho que não tem, mas APFS de Mac, assim como ZFS e BTRFS de Linux tem deduplication.</p>
<p>Faz de conta que você é desenvolvedor web e tem diversos projetos de Node.js na sua máquina. Sabe o maldito &ldquo;npm install&rdquo; que gera aqueles diretórios node_modules em todo projeto? Concorda que na maioria dos projetos vai ter as mesmas bibliotecas e os mesmos arquivos? Então estamos desperdiçando espaço com tanta duplicata? A resposta é sim, está desperdiçando.</p>
<p>Pra combater isso existem processos de deduplicação. Num NTFS de Windows, se estiver em Windows Server, ele oferece ferramenta pra que, caso dois arquivos tenham exatamente o mesmo conteúdo binário, ele consiga fazer o metadado dos dois apontarem pros mesmos conjuntos de blocos. Se forem dois arquivos de 200MB, originalmente iriam ocupar 400MB, mas com deduplicação, vai ocupar só 200MB. Só volta a ocupar mais espaço se decidirmos modificar o segundo arquivo, por exemplo.</p>
<p>Isso é muito útil no cenário de servidor de arquivos da empresa. O chefe manda um email com um PDF de 100MB pra 10 pessoas. Cada uma grava no seu diretório pessoal no servidor. Iria ocupar 1 giga. Mas se rodar a ferramenta de deduplicação, ele faz as 10 cópias apontarem pros mesmos blocos de um arquivo só, economizando 10 vezes o espaço. É o mesmo caso de termos 10 projetos de Node, cada um com node_modules repetidos. Rodando algum processo de deduplicação, no caso uma ferramenta como Opendedup no Linux, dá pra fazer algo semelhante no ext4.</p>
<p>Mas nenhuma dessas soluções é ideal. Os sistemas de arquivos NTFS e ext4 não foram feitos pra suportar esse tipo de coisa. O sistema de arquivos em si não suporta nada disso, daí dependemos de uma ferramenta externa pra tentar adicionar essas funcionalidades. O ideal é que o próprio sistema de arquivos implemente Copy on Write, independente de uma ferramenta externa.</p>
<p>Copy on Write ou CoW é super importante. É literalmente os conceitos de um Git só que em nível do sistema de arquivos inteiro, funcionando de forma transparente pro usuário. Num sistema copy on write, quando editamos um arquivo que já existe, os novos blocos não são gravados por cima dos antigos, ele faz o equivalente a um branch de Git e os novos blocos são gravados nesse branch, parecido com commits. Isso mantém o arquivo original intacto.</p>
<p>De cara, isso também evita mais corrupção de dados. Antigamente, num FAT16 da vida, onde estávamos na situação onde não tínhamos espaço sobrando, não tinha outra opção: toda modificação de arquivos precisava gravar os blocos novos em cima dos velhos. Mas e se acabasse a luz no meio? Ia ficar um arquivo corrompido. E isso era comum. Pra mitigar esse problema criou-se o conceito de journaling, que NTFS no WIndows, ext4 no Linux. HPFS+ no Mac passaram a suportar no fim dos anos 90.</p>
<p>Super resumido é assim. Digamos que eu tenha um arquivo, aquele Powerpoint, e ele ocupa 10 blocos no disco. No sistema de arquivos temos o nome do arquivo e um mapa apontando pra esses 10 blocos. Isso faz parte dos tais metadados de um arquivo. Agora editamos por cima e essa mudança afetaria os dois últimos blocos. Esses dois blocos serão gravados no disco e a localização será gravada no journal. O arquivo original ainda tem os 10 blocos originais até este ponto.</p>
<p>Ao terminar de gravar os dois novos blocos, só aí o NTFS vai atualizar o mapa do arquivo e mudar a referência dos dois últimos blocos pra apontar pra localização dos dois novos. Mas digamos que ele começou a mudar a referência do penúltimo bloco e antes de mudar a referência do último, acaba a luz e o sistema reboota. Quando reiniciar, o NTFS checa o journal e vê que faltou mudar a localização do último bloco.</p>
<p>Como ele tem essa informação no journal, vai conseguir terminar a operação e o Powerpoint não fica corrompido, como ficaria antes. Se ele ficasse em dúvida, faria rollback, e manteria o arquivo intacto. Os novos blocos não sobrescreveram os antigos ainda. Esse processo gasta um pouco mais de espaço, mas evita que arquivos fiquem corrompidos. Espaço hoje é mais barato, então podemos fazer isso.</p>
<p>Copy on Write vai um passo além. Depois que os novos blocos são gravados e registrados no journal, é feito tipo um fork no nível dos metadados. Imagine o mundo antes de Git. Se você copiasse um arquivo em cima do outro, o que acontece com o original? Ele se perderia pra sempre, certo? É assim que todo sistema de arquivos funciona. Mas o que acontece quando habilita Git num diretório e copia um arquivo em cima do outro? Vamos testar.</p>
<p>Aqui temos um diretório com Git habilitado e os arquivos já comitados. Se listar o que tem neste hello.txt, veja, &ldquo;hello world&rdquo;. Vou criar um novo arquivo fora, lá no /tmp com conteúdo de &ldquo;bla bla bla&rdquo;. Copio o arquivo por cima do meu original. Normal, meu arquivo original com &ldquo;hello world&rdquo; foi pro saco como podem ver. E se eu mudar de idéia?</p>
<p>Agora entra o Git pra te salvar. Fazendo &ldquo;git status&rdquo; podemos ver que ele sabe que esse arquivo foi modificado mas ainda não comitamos, não nos comprometemos com ele. Então podemos fazer &ldquo;git checkout&rdquo; desse arquivo, olha o conteúdo, &ldquo;hello world&rdquo; de volta. Git nos permite mudar de idéia e não perder dados ao custo de manter uma cópia do original em algum lugar ocupando espaço extra.</p>
<p>É por isso que todo projeto deve usar Git. Podemos mudar de idéia sem preocupação. Quando estivermos satisfeitos fazemos commits, e mesmo assim podemos reverter e até recuperar commits apagados. Copy on Write em sistema de arquivos é a mesma coisa. E em Linux, que eu saiba, só existem dois sistemas de arquivos modernos que suportam isso: o  ZFS e o BTRFS.</p>
<p>Quem usa Ubuntu tem na instalação a opção de usar ZFS. E aqui já aviso: evite. É muito fácil fazer besteira em ZFS e perder tudo sem querer. ZFS é excelente pra quem está montando um servidor de arquivos, usando distros com o TrueNAS, que é o melhor. Só quem estudou bastante a documentação de ZFS deve tentar usar. Não basta só instalar e acabou, precisa de constante manutenção e monitoramento pra saber se está realmente tudo funcionando como deveria. Pra usar no dia a dia, no seu notebook, acho que vai mais atrapalhar do que ajudar.</p>
<p>Isso não quer dizer que ZFS é ruim, pelo contrário, é provavelmente o sistema de arquivos mais avançado que existe. Porém, pros propósitos de máquina do dia a dia de programador é como matar uma mosca com uma bazuca e arriscar atirar a bazuca no seu pé. Outra coisa: todo mundo me pergunta qual distro eu mais gosto. Eu uso Manjaro GNOME no meu dia a dia. Primeiro porque é um Arch Linux mais estável. Ele segura um pouco os pacotes mais novos, pra testar e garantir que nada vai quebrar. Mesmo assim alguns updates ainda quebram de vez em quando, mas é um bom compromisso entre ter coisas novas mais rápido e não ficar parado a cada novo update.</p>
<p>Mais importante: Manjaro já vem pré-configurado pra BTRFS logo na instalação. É só instalar, escolher BTRFS e ele vai se virar e te dar um ambiente estável. Em vez de mostrar Manjaro, vou mostrar Ubuntu 23, que acabou de lançar. Todo o trabalho de configuração que vou mostrar agora não precisa fazer fazer no Manjaro. Começando na instalação, todo mundo escolhe aqui a primeira opção, que vai instalar EXT4 igual toda outra distro. Mas agora ela tem essa segunda opção com ZFS, que é experimental ainda. De novo: não teste numa máquina de trabalho. Teste numa máquina virtual pra aprender tudo primeiro caso queira mesmo adotar ZFS.</p>
<p>Em vez disso vamos nessa terceira opção e particionar o disco manualmente. Não vou explicar cada detalhe. Vamos assumir que você está num PC moderno, dos últimos 10 anos. Provavelmente vem com modo legado na BIOS desligado, já suportando boot com EFI. Falando nisso, não deixe de assistir meu video que explica como o Boot de Linux funciona.</p>
<p>Enfim, vamos primeiro criar uma partição EFI que a BIOS vai procurar primeiro. Uns 200 megabytes costuma ser suficiente, deixe 500 se quiser garantir. Depois precisamos de uma segunda partição com um sistema de arquivos legado fácil da BIOS ler. Em Windows costuma ser FAT32, mas aqui podemos escolher o antigo EXT2 mesmo. Eu deixo 500 mega, mas de novo, pra garantir pro futuro caso a kernel cresça de tamanho, deixa 1 gigabyte. Espaço é barato.</p>
<p>Agora usamos o resto do espaço pra partição primária com BTRFS e montando o root nela. Se for um notebook, sempre recomendo criar uma partição encriptada com LUKS, ou no mínimo ter o subvolume de HOME encriptado com LUKS. Não vou explicar nada disso aqui porque é um saco de configurar, então pesquisem no Wiki do Arch sobre isso. BTRFS funciona mais ou menos como um LVM, um gerenciador de volumes lógico. Ele começa como um pool de subvolumes e por default vai criar um subvolume chamado &ldquo;arroba&rdquo; onde vai montar o &ldquo;/&rdquo; root e outro  &ldquo;@home&rdquo; onde vai montar o &ldquo;/home&rdquo;.</p>
<p>Novamente, pesquise sobre LVM e volumes lógicos. Antigamente uma boa prática era separar duas partições: uma pra root onde fica o sistema operacional e outra pra home, daí montamos essa partição como &ldquo;/home&rdquo;. O problema de usar partições é que eles tem tamanho fixo. Uma vez definido não tem como redimensionar fácil. É como no Windows quando você particiona C: e D:. Mas com volumes lógicos, podemos redimensionar on the fly, sem qualquer problema. E BTRFS vai um passo além, subvolumes servem propósito parecido de volumes lógicos mas nem precisamos nos preocupar em redimensionar, eles são dinâmicos.</p>
<p>Enfim, quando terminar de instalar podemos bootar. Vamos acelerar aqui, três, dois, um e pronto. Ubuntu instalado. É muito fácil instalar distros Linux hoje em dia. Só não instala quem tem muita preguiça ou tem um hardware muito, mas muito ruim. Quando Linux não identifica hardware só tem duas opções: ou ele é muito ruim, ou é muito novo ou recém lançado. Se você não comprou um note que saiu este ano, não tem desculpa, é o hardware que é ruim, porque drivers pra Linux são atualizados constantemente.</p>
<p>Se estiver em dúvida num notebook, e tiver a oportunidade de experimentar antes de comprar, leve um pendrive com um Manjaro ou Ubuntu da vida e faça boot pelo pendrive. Veja se ele consegue identificar tudo: wifi, bluetooth, áudio, placa de video. É o melhor teste que pode fazer. Se alguma coisa não for detectada, desista desse modelo. Vai te dar mais trabalho do que compensa.</p>
<p>Pronto, agora vamos listar o arquivo &ldquo;/etc/fstab&rdquo; que o Ubuntu criou. Super simples. Dispositivos de armazenamento são identificados por um UUID. Abrindo o terminal, basta digitar &ldquo;lsblk -f&rdquo; e vai ter uma coluna com UUID. O arquivo &ldquo;/etc/fstab&rdquo; define quais dispositivos vão montar onde durante o boot. Por exemplo, o dispositivo de UUID que começa com ed022 vai montar como root &ldquo;/&rdquo;. Olhem na listagem, é o dispositivo /dev/vda3. O /dev/vda2 de UUID que começa com 294c8 monta como /boot.</p>
<p>A graça começa quando vemos o ponto de montagem de &ldquo;/home&rdquo;. O Ubuntu, pelo menos, separou corretamente um subvolume de BTRFS chamado &ldquo;@home&rdquo;. É uma boa prática. Mas temos alguns problemas. Do jeito que está aqui, não podemos ativar snapshots, porque seu espaço em disco vai desaparecer rapidamente.</p>
<p>Snapshots são possíveis por causa da funcionalidade de copy on write do BTRFS que expliquei antes. Copy on write funciona mais ou menos como branches num Git. Lembra que mostrei que o Git consegue manter o arquivo original, sem perder o conteúdo, mesmo eu gravando outro por cima? Pra fazer isso, obviamente, precisa manter o conteúdo original gravado em algum lugar, certo? Está gastando espaço extra. Diretórios do Linux como /var/cache, /var/log, /var/tmp estão em operação o tempo todo. Mesma coisa /tmp ou arquivo de swap. Se ficar mantendo versão velha deles em snapshots, o espaço livre do seu sistema vai acabar muito rápido sem você entender porque.</p>
<p>Pra resolver é assim: abre o terminal e faça &ldquo;sudo chattr +C /var/log&rdquo;. Isso é change attribute, estamos mudando o atributo do diretório pra dizer pro BTRFS não considerar arquivos nesse diretório pra snapshot. Esse change attribute só vai afetar novos arquivos criados, não os antigos que já existem, sempre se lembrem disso, mas como acabamos de instalar, acho que só isso deve ser suficiente.</p>
<p>Agora vamos instalar o Timeshift. É um programa gráfico pra fazer snapshots periódicos do sistema. Pra iniciantes, vai ser mais fácil do que lembrar os comandos pro terminal. Basta fazer &ldquo;apt install timeshift&rdquo; ou &ldquo;yay -S timeshift&rdquo; no Arch ou Manjaro. Logo que abrir ele vai oferecer pra configurar pra você. Mesmo se não estiver usando BTRFS, a alternativa é usar Rsync, caso esteja usando ext4.</p>
<p>Rsync, é um programa pra sincronizar dois diretórios. Equivalente no Windows é o novo robocopy ou o antigo xcopy. O jeito força bruta, sem a ajuda do sistema de arquivos, pra ter um backup local, é fazendo sincronização de diretórios. O problema é óbvio: vai ocupar espaço pra cacete no seu sistema, porque periodicamente vai duplicar tudo. Sim, se algo der errado e apagar algum arquivo que não devia, vai ter uma cópia. Com a funcionalidade de copy on write e snapshots do Btrfs temos a mesma coisa, mas sem duplicar nenhum arquivo e sem gastar mais espaço do que realmente precisa no mesmo HD.</p>
<p>Tá complicado de entender? Olha só, vou baixar um arquivo qualquer aqui, escolhi a ISO do Ubuntu. Acelerando e &hellip; pronto, baixado. Agora vou no Timeshift e crio um snapshot manualmente. Com o timeshift configurado posso escolher pra  fazer snapshots de hora em hora, e guardar X snapshots por hora, Y snapshots por dia, Z por semana e assim por diante. Depois dêem uma fuçada, mas criei um pra mostrar agora. Snapshots são baratos, pode fazer toda hora. Não importa se você tem mil arquivos ou um milhão de arquivos, diferente de rsync, o tempo de criação de snapshots é quase instantâneo, porque ele não está duplicando nenhum byte, só metadados.</p>
<p>Simulando um acidente, oops, apaguei essa ISO que acabei de baixar. Que droga, vou ter que esperar um tempão pra baixar de novo. Ou não. Vamos no Timeshift e posso navegar pelos arquivos do snapshot. Olha só, se formos na pasta de downloads, a ISO está lá. Pronto, só copiar de volta pra pasta de downloads corrente e tá na mão: arquivo recuperado.</p>
<p>Não ficou claro? Vou dar outro exemplo. Mas pra isso precisamos terminar de configurar o Timeshift. De novo, siga o artigo que deixei linkado abaixo. Diferente do Manjaro que já me dá isso automaticamente, no Ubuntu precisamos manualmente instalar o grub-btrfs a partir do código fonte. Primeiro precisa instalar os pacotes build-essential pra termos compilador, e o git. Daí fazemos clone do repositório e &ldquo;make install&rdquo;.</p>
<p>Também precisamos instalar o pacote inotify-tools e editar a configuração do grub-btrfs com &ldquo;systemctl edit &ndash;full grub-btrfsd&rdquo; e trocar a opção &ldquo;/.snapshots&rdquo; por &ldquo;&ndash;timeshift-auto&rdquo;. Pronto, agora só iniciar o serviço e habilitar pra iniciar automaticamente depois do boot. E finalmente, mais um programa que precisamos instalar a partir do código fonte, o timeshift autosnap. Mesma coisa, só fazer clone do repositório e rodar &ldquo;make install&rdquo; dentro.</p>
<p>O que tudo isso faz? Se ainda não passou por isso, é só uma questão de tempo. Sabe quando estamos no meio do dia e pensamos, ah, vou fazer uma atualização no sistema. &ldquo;apt update&rdquo; daí &ldquo;apt upgrade&rdquo;, reboota o computador e pau, não boota mais. E vai ser justo naquele dia que você tá sem nenhum pendrive por perto, daí não consegue bootar no LiveCD. Você tá de home office e decidiu ir trabalhar do Starbucks, não tem nenhum amigo com computador por perto pra baixar a ISO pra você. Acabou sua tarde. Vai ter que voltar pra casa, achar um pendrive, baixar a ISO e gastar um tempo no stackoverflow tentando voltar um kernel antigo pra bootar de volta na sua máquina. Quem nunca?</p>
<p>E se eu disser que nunca mais vai precisar fazer isso? Vamos simular de novo. O Timeshift já está configurado pra fazer snapshots periódicos. Com isso, caso um dia lembre, puts, eu queria aquele arquivo que apaguei mês passado e achei que não fosse precisar. Meu HD de backup tá em casa, e agora? Sem problemas, só pegar o snapshot do mês passado, que está local na sua máquina. Mas, e na situação quando fizer um &ldquo;apt upgrade&rdquo; e isso quebrar seu sistema? Sem problemas também, aquele pacote timeshift autosnap vai criar um snapshot automático antes de todo &ldquo;apt upgrade&rdquo;.</p>
<p>Daí aquele pacote grub-btrfs vai listar esses snapshots no menu de boot do grub. Oops, Ubuntu por padrão não mostra o menu do Grub. Mas isso é fácil, só editar o arquivo &ldquo;/etc/default/grub&rdquo; e mudar a opção GRUB_TIMEOUT_STYLE de &ldquo;hidden&rdquo; pra &ldquo;menu&rdquo; e a opção GRUB_TIMEOUT de zero pra uns cinco ou dez segundos. Pra atualizar, só rodar &ldquo;update-grub&rdquo;. Vamos ver como funciona.</p>
<p>Pra simular, vamos criar mais um snapshot temporário chamado &ldquo;teste&rdquo; no Timeshift. Como é um snapshot manual, vou rodar &ldquo;update-grub&rdquo; manualmente, mas num &ldquo;apt upgrade&rdquo; não precisaria. Agora vou abrir a loja do Ubuntu e instalar alguns programas. Vamos instalar aqui o Gimp, vai demorar um bocado. Acelerando &hellip; Agora instalamos o Discord, de novo, vamos acelerar. Pronto. Podemos checar no menu, e estão aqui, Gimp, e Discord. Faz de conta que instalei um monte de coisas mas no final mudei de idéia, queria desinstalar tudo e voltar como estava antes. Como faz?</p>
<p>Fácil, só rebootar. Agora aparece o menu do Grub aqui no boot e tem esta nova opção e olha só os snapshots que estávamos vendo no timeshift. Estão vendo a descrição &ldquo;teste&rdquo;? Basta escolher esse ou um snapshot anterior. Entenderam? Posso bootar direto em um snapshot. Esperamos iniciar, e olha só. Cadê o Gimp? Cadê o Discord? Como o nome diz, timeshift, ele fez um shift no tempo, voltamos ao passado, exatamente como estava antes de instalar aqueles aplicativos. Imagine que em vez de Gimp ou Discord fosse um &ldquo;apt upgrade&rdquo; e veio uma kernel quebrada que fode o boot? Basta rebootar, escolher o snapshot anterior e pronto, vai ser como se nada tivesse acontecido.</p>
<p>Abrindo o Timeshift, pra tornar isso permanente, posso fazer um Restore do snapshot antigo pra passar a ser meu sistema corrente. Só isso. Entenderam? Com o Timeshift temos um backup local que ocupa muito pouco espaço e com isso qualquer acidente pode ser facilmente recuperado sem precisar de HD externo, sem precisar esperar copiar tudo de backup nem nada, muito menos reinstalar tudo do zero. É instantâneo.</p>
<p>Claro, isso é uma conveniência, pense como sendo sua última barreira de defesa. Isso não substitui um backup externo. O Timeshift e BTRFS, pra funcionar, depende que seu SSD esteja funcionando perfeitamente. Se o SSD em si der pau, como mostrei no começo do video, isso não vai te salvar. Aí só um backup externo. O que estou recomendando é ter as três coisas que mostrei até agora: se disciplinar a usar Git em todo projeto, pessoal ou de trabalho. Daí ter BTRFS e Timeshift pra consertar acidentes rápidos. E um backup externo pra evitar catástrofes mais sérias. São três níveis de segurança de dados pra ter dar paz de espírito.</p>
<p>E pra finalizar, tem um detalhe importante no caso de BTRFS. Aqui tem a ver um pouco com a Rinha de Backend de novo. Durante toda minha exploração dos projetos dos participantes, fiquei rodando Docker Compose de cada projeto. Eu já fiz um video explicando containers e Docker em particular, não deixem de assistir. Mas vamos entender o problema.</p>
<p>De dentro do projeto de qualquer um dos participantes, vamos rodar &ldquo;docker compose up&rdquo;. Lendo o arquivo docker-compose.yml, vai estar configurado várias aplicações, por exemplo, nginx, postgres. De onde vem esses aplicativos? O Docker vai baixar a imagem do docker.io. É isso que significa esses &ldquo;pulls&rdquo;. Uma imagem não é um único arquivo, é um conjunto de arquivos binários, que chamamos de overlays. Pense como se fossem commits de Git, mas commits binários. Esses overlays são unificados num único sistema de arquivos usando o Union File System ou UFS.</p>
<p>Se rodar o comando &ldquo;mount&rdquo; olha só aqui no fim, vários pontos de montagem usando overlays. Rode o comando &ldquo;docker image ls&rdquo; e temos a listagem das imagens baixadas e geradas localmente. A imagem do projeto em Node.js tá custando 220MB, o nginx tá ocupando quase 200MB, o postgres mais de 400MB. Só esse projeto, pra rodar, precisou gastar quase 1 Giga aqui no meu HD.</p>
<p>Onde ficam esses arquivos de overlays? Dentro do diretório &ldquo;/var/lib/docker/overlay2&rdquo;. Podemos listar como sudo e temos vários diretórios com o SHA256 de cada camada da imagem. Agora experimente testar docker compose de uns 50 projetos. Coisas como postgres e nginx, se forem da mesma versão, vão ser reusados, mas cada imagem de aplicação custa na faixa de 200 MB pra cima. Então 50 vezes 200 vai dar quase 10 Giga.</p>
<p>De tempos em tempos é recomendado rodar um &ldquo;docker system prune&rdquo; pra apagar imagens que não estão sendo usadas. Mas cuidado, se tiver containers, mesmo desligados, associados a uma imagem, ela não vai ser apagada e vai ficar sobrando ocupando espaço a toa. Use o comando &ldquo;docker container ls -a&rdquo; pra listar e apague os que não precisa. No caso de docker compose, quando acabar de testar o projeto, faça &ldquo;docker compose down rmi &ndash;all&rdquo; pra limpar containers, imagens, volumes e network que não for usar mais.</p>
<p>Mas beleza, 10Gb pra 50 projetos parece pouca coisa. O problema é que eu não excluí o diretório do docker da solução de snapshot. E o timeshift vai ficar criando snapshots periodicamente. Pense assim, eu fui baixando docker compose up, um monte de imagens, ao longo de mais de uma semana. Todo dia o timeshift foi fazendo snapshots. Num dia eu fazia docker compose up. No dia seguinte fazia docker compose down, achando que tava apagando tudo, mas na verdade eles estavam sendo guardados nos snapshots.</p>
<p>Depois eu resolvo testar o projeto de novo, faço um novo docker compose up. Vai baixar 1 giga de imagens outra vez. Só que agora tem 1 giga das imagens antigas no snapshot de ontem, e mais 1 giga duplicado no snapshot de hoje. BTRFS tem opção de deduplicação, mas ele não roda  automaticamente, por padrão vai duplicar tudo. Repita isso em diversos projetos ao longo de duas semanas.</p>
<p>Quando terminei de testar tudo, escrevi o script do último video, gravei, editei, e meu sistema começou a ficar estranho. Tudo foi ficando mais lento. No começo achei que era pau do Microsoft Edge, porque ele começou a crashear do nada. Tentei reinstalar e nada. Ué, que diabos tá acontecendo com meu sistema. Eis que comandos de instalar pacotes como yay começaram a dar problema e aí caiu a ficha. Fui checar quanto de espaço eu tinha com &ldquo;df -h&rdquo; e tava dizendo que tava com 100% ocupado na partição primária.</p>
<p>Puts, as malditas imagens de docker! Tentei dar um docker system prune. Mas nada. Continua ocupando 100% do disco. Puts, os snapshots! As malditas imagens ficaram presas em snapshots do passado. Abri o timeshift e apaguei todos os snapshots do passado. Voltei pra verificar o espaço livre, e nada, continuava ocupando. Ué, que diabos.</p>
<p>BTRFS é conservador, ele não vai apagar nada a menos que você realmente diga pra apagar. Apagar snapshots não libera o espaço, pra isso precisa reclamar esse espaço de volta. Deixa eu demonstrar. Podemos rodar o comando &ldquo;btrfs filesystem df /&rdquo;. Uma das pequenas desvantagens de usar btrfs é que ferramentas normais de Linux como o comando &ldquo;df&rdquo; propriamente dito, podem informar valores incorretos.</p>
<p>No nosso Ubuntu de exemplo, estamos usando um total de 25GB. Isso é somando coisas como metadados do sistema de arquivos e tudo mais. Os bytes dos arquivos em si é esse &ldquo;used&rdquo;, pouco mais de 23GB. Ou seja, pelo menos 2GB são só metadados. Você precisa entender essa diferença. Tamanho de um arquivo não são só os bytes dentro desse arquivo. O nome do arquivo ocupa espaço, a informação de data de criação também, atributos como read only, localização no diretório também. Tudo ocupa espaço. E quanto mais avançado é o sistema de arquivos, mais espaço vai ocupar de metadados.</p>
<p>Enfim, se você não tem noção de quanto um sistema recém instalado ocupa, pode pensar &ldquo;ah, 25 giga, ok, faz sentido&rdquo;. Mas lembra que eu fiz alguns testes de snapshots pra vocês verem na prática? E aquele Gimp, e aquele Discord? Eles desapareceram, mas os bytes deles continuam no snapshot antigo. Vamos abrir o Timeshift e apagar todos os snapshots.</p>
<p>Rodando de novo o comando &ldquo;df&rdquo; do btrfs, não mudou nada. Continua indicando 25GB. Foi o que aconteceu na minha máquina quando acabou espaço. Mesmo apagando os snapshots, continuava indicando falta de espaço. Precisa manualmente reclamar o espaço de volta, pra isso usamos o comando &ldquo;btrfs filesystem balance start /&rdquo;. Esse comando é bem demorado. Pode levar mais de hora numa máquina como a minha, mas pelo menos podemos continuar usando o sistema em paralelo.</p>
<p>Enfim, esperamos acabar e pronto. Vamos ver de novo o comando &ldquo;df&rdquo; e olha só, caiu de 25GB pra 10GB! Tinha 15 GB sendo reservado pelo btrfs seja pros snapshots antigos e outras coisas dele. E isso numa instalação nova, praticamente vazia, onde só instalamos 2 programas. Agora imagina no meu caso, com dezenas de imagens de docker em snapshots. Ocupou meu HD inteiro de 1 terabyte. Pois é, foi muito fácil encher 1 tera em poucos dias só de não ter tomado cuidado.</p>
<p>E como faz pra isso não acontecer? Tem que fazer &ldquo;sudo chattr +C /var/lib/docker&rdquo; ou também &ldquo;sudo chattr +C /var/lib/libvirt&rdquo; no caso de estar usando Libvirt pra máquinas virtuais. E de novo, isso só vai se aplicar a arquivos novos. O correto é primeiro limpar esses diretórios, daí mudar esse atributo. Acho que o certo não é aplicar esse atributo no diretório todo, algumas coisas talvez você queira manter snapshots, como volumes de Docker. Fique só atento com isso.</p>
<p>Essa dica é muito importante, porque se você não entender o funcionamento de snapshots no btrfs e começar a usar como máquina de desenvolvimento, com certeza vai ter imagens de docker, e isso com certeza vai destruir o espaço livre em poucas semanas. Você não vai saber que é, nenhum tutorial de como liberar espaço vai funcionar, você não saberia que precisa rebalancear o sistema, e no final iria desistir e reinstalar tudo do zero de novo.</p>
<p>No momento em que estou escrevendo este script, no meu Manjaro, se rodar &ldquo;df&rdquo; ele me diz que está usando 775 giga do meu NVME de 1TB de novo. De arquivos mesmo são só 164 GB. Bora rodar &ldquo;balance start&rdquo; e ir tomar café. Deixei 1 hora rodando e olha só, agora diz que está usando um total de só 166 giga, que faz mais sentido. Ainda tinha uns snapshots velhos, provavelmente com imagens de docker velhas.</p>
<p>Logo que instalei o Timeshift, mostrei que tem como configurar pra guardar snapshots só de hoje, ou da semana, ou até de meses. Se você tem um bom backup externo, isso não é necessário, só o do dia ou até o da semana deve ser suficiente. Quando ver que está acabando o espaço, sabendo que tem tudo no backup externo, pode apagar todos os snapshots do timeshift, menos o último. Daí rodar o rebalanceamento e pronto.</p>
<p>Segurança é um trade off com performance e espaço. Gastamos mais processamento e gastamos mais espaço, pra garantir paz de espírito, essa é a filosofia. E felizmente chegamos num ponto onde o sistema operacional realmente nos ajuda a não perder nada. A única coisa que nenhum software consegue fazer, é consertar um hardware barato porcaria. Contanto que o hardware seja bom, é quase impossível perder dados hoje em dia. Tão importante quanto não perder dados importante é não perder horas ou dias tentando recuperar seu sistema por causa de uma atualização bugada. Basta não ter preguiça, porque as ferramentas estão à nossa disposição e, como demonstrei, são super fáceis de usar.</p>
<p>O meu NAS Synology usa BTRFS também, mas em configuração de RAID. Ele roda scrubbing a cada 6 meses e faz checagens de checksum pra garantir que nenhum erro físico nos HDs me faça perder dados. Isso é só pra quem escolher ter um NAS: lembre-se de configurar pra rodar scrubbing, é essencial, mas isso vem desligado por padrão. Não basta só instalar e achar que vai tudo funcionar por mágica.</p>
<p>Numa distro Linux como Ubuntu ou Manjaro, tenho o Timeshift. Mas no sistema operacional da Synology, ele tem o Snapshot Replication, que também me permite navegar por snapshots antigos e recuperar coisas rapidamente. Olha só, se der qualquer problema, consigo ver como eram meus arquivos de 4 meses atrás. E não sei se perceberam, mas esse sistema de snapshots me dá outra coisa de graça: proteção contra ransomware.</p>
<p>O que é um ransomware? É um malware que você instala ao abrir um powerpoint ou PDF qualquer que veio de um email desconhecido e na verdade era um programa malicioso. É assim que YouTuber perde o canal deles: clicando em PDF falso que não era um PDF. Parem com essa mania de dar duplo clique em tudo. O malware se instala silenciosamente na sua máquina e começa a encriptar seus arquivos. Pode levar dias e você não vai perceber até precisar abrir um desses arquivos e notar que não abre mais.</p>
<p>No final vai aparecer uma janela parecida com essa dizendo algo como &ldquo;Você se fodeu, encriptamos todos os seus arquivos, podemos te dar a chave pra desencriptar mas você vai nos pagar X bitcoins pra isso. Mande pra este endereço e daí te damos a chave&rdquo;. Isso realmente acontece, e não existe garantia nenhuma de receber essa tal chave, mesmo pagando. Se isso acontecer com você, nunca pague, só aceite que você se fodeu. Reze pra ter seguido minhas recomendações.</p>
<p>Ah, mas eu tenho backup externo, então tô seguro, não? Não. Eu disse que leva dias pra esse processo conseguir encriptar todos os seus arquivos. Durante esse período o que você fez? Plugou o HD externo e deixou fazendo backup dos arquivos encriptados por cima dos arquivos bons do backup. Agora seu backup também foi contaminado, parte dele tá encriptado. Perdeu playboy.</p>
<p>É menos ruim se estiver usando Google Drive ou Dropbox ou OneDrive da vida, porque eles tem suporte de snapshot. Mas eu não confiaria muito. Nunca precisei usar o deles então não fucei o suficiente, mas veja esta minha pasta do Obsidian no Dropbox, que é onde edito meus scripts. Tem essa função de Rewind, que é voltar pra trás. Escolho o dia que quero voltar, e ele me mostra as últimas atividades. Daqui posso escolher voltar pra algum ponto do passado, mais ou menos como commits também. É meio um saco porque eu não vi uma interface que permite voltar meu Dropbox inteiro, acho que tem que ir diretório a diretório, não tenho certeza. Enfim, não achei muito intuitivo de usar, mas em última instância, se não tiver mais pra onde recorrer, talvez isso ajude.</p>
<p>Se um ransomware encriptar todos os seus arquivos, mesmo no Dropbox vai estar tudo encriptado, mas se tiver o Rewind de alguns dias pra trás,  pode tentar recuperar. Isso é ok, mas eu não gosto de depender de um serviço que não tenho controle. Por isso essa mesma pasta de Dropbox está em BTRFS no meu NAS, sendo sincronizado com o aplicativo Cloud Sync da Synology. Eu uso os três, Google Drive, Microsoft OneDrive e Dropbox como backups secundários offsite de alguns dados. E o meu NAS tem snapshots de BTRFS ativado pra até 4 meses pra trás.</p>
<p>Ou seja, isso nunca vai acontecer, mas se por ventura algum malware conseguir passar por mim e encriptar meus arquivos, eu só dou risada, volto alguns snapshots pra trás e mando esse hackerzinho de merda tomar no cú. Não pego malware faz pelo menos uns 20 anos, e sempre tenho múltiplos backups de tudo, tanto local quanto offsite, fora daqui. E os arquivos mais antigos que realmente não quero perder já queimei tudo em Blurays Millenium Disk, feitos de material inorgânico que nunca oxidam. Todos guardados no meu cofre. Vem tentar encriptar meus dados seus hackerzinhos de merda.</p>
<p>Segurança é uma questão de oportunidades. Quanto mais você pensar que nunca vai acontecer com você, maiores as chances de realmente acontecer. Eu penso o contrário, sempre penso que estou 100% inseguro 100% do tempo e faço toda a estratégia em cima disso. Segurança funciona em camadas. Se o plano A falhar, tem o plano B. Se o plano B falhar tem o plano C. Isso se chama contingência. Quem não prepara contingências, sempre vai viver dando desculpas. E eu odeio desculpas. Se desculpas funcionassem, não precisávamos de polícia.</p>
<p>Por fim, um comentário que não coube antes no video é sobre como mantenho meu Manjaro. Uma recomendação que dou é usar muito Docker pros seus projetos. Não precisa instalar coisas como MySQL, Postgres, Redis nem nada local. Use tudo com Docker Compose. Primeiro porque isso garante que estamos rodando a versão certa de cada coisa pra cada projeto. Segundo, porque é menos pacotes instalados na sua máquina que o Manjaro vai precisar ficar atualizando. Quanto mais pacotes instalar, maiores as chances de algum upgrade quebrar alguma coisa.</p>
<p>Pelo mesmo motivo, tudo que é aplicativo como Discord, Zoom, o próprio browser Edge que eu uso, Firefox, e muito mais, eu prefiro instalar via Flatpak. Flatpaks são containers, tipo um Docker light. E dá pra usar coisas como FlatSeal pra garantir que nenhum aplicativo tem mais permissões e acessos do que deveria. Podemos fechar um Discord pra não conseguir enxer nada dos seus arquivos, só deixar ele ver a pasta de Downloads, por exemplo. Depois veja este video explicando sobre Flat Seal.</p>
<p>Eu sei que vai aparecer o povo do NixOS nos comentários, mas eu não pretendo usar e nem preciso. Meu sistema é super estável. Raramente tenho problemas, porque eu instalo o mínimo possível. Pra projetos tá tudo em Docker. Aplicativos tudo em Flatpak ou Snap. E coisas mais arriscadas, como games, ou meus experimentos com LLMs e IAs, estão tudo em máquinas virtuais com QEMU/KVM como mostrei neste outro video. Fazendo tudo que mostrei neste video, não precisei reinstalar meu sistema nenhuma vez e nunca perdi nem dados e nem tempo. Qualquer besteira que eu faço, o Timeshift me salva, qualquer besteira maior e os backups no meu NAS me salvam.</p>
<p>Recapitulando: sempre, sempre, inicie um repositório git local no seu projeto, independente de ter no GitHub ou não. Sempre, sempre, tenha no mínimo um HD externo USB da vida, um pouco maior do que o HD do seu PC, e configure o app de Backup que já vem no seu sistema operacional. Não precisa de nada super fancy nem nada. Só habilita. Qualquer backup é melhor que nenhum backup. Finalmente, se quiser aprender mais de Linux e usar o que há de mais novo, instale Manjaro com suporte a BTRFS e configure o Timeshift como eu expliquei. Isso vai tornar você quase invencível, é praticamente um super poder. Se ficaram com dúvidas, mandem nos comentários abaixo, se curtiram o video deixem um joinha, assinem o canal e compartilhem o video com seus amigos. A gente se vê, até mais!</p>
]]></content:encoded><category>armazenamento-e-backup</category><category>controle-de-versao</category><category>seguranca</category><category>akitando</category></item><item><title>[Akitando] #71 - Usando Git Direito | Limpando seus Commits!</title><link>https://akitaonrails.github.io/2020/02/12/akitando-71-usando-git-direito-limpando-seus-commits/</link><guid isPermaLink="true">https://akitaonrails.github.io/2020/02/12/akitando-71-usando-git-direito-limpando-seus-commits/</guid><pubDate>Wed, 12 Feb 2020 10:30:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/6OokP-NE49k"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIPTION&lt;span class="hx:absolute hx:-mt-20" id="description"&gt;&lt;/span&gt;
&lt;a href="#description" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Continuando o episódio sobre Git, vamos encerrar o assunto hoje. Desta vez vai ser mais ou menos um tutorial mas nada do básico. Vamos ver o que a maioria dos tutoriais de iniciantes não cobre pra você conseguir manter um repositório limpo e bem organizado. Como manipular commits. Como reescrever o histórico.&lt;/p&gt;
&lt;p&gt;E no final quero discutir rapidamente sobre o assunto de monorepos que o Google ou Facebook usam. E finalmente explicar porque o desenvolvimento da kernel do Linux, que foi de onde originou o Git, dificilmente poderia funcionar em plataformas como GitHub.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/6OokP-NE49k"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIPTION<span class="hx:absolute hx:-mt-20" id="description"></span>
    <a href="#description" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Continuando o episódio sobre Git, vamos encerrar o assunto hoje. Desta vez vai ser mais ou menos um tutorial mas nada do básico. Vamos ver o que a maioria dos tutoriais de iniciantes não cobre pra você conseguir manter um repositório limpo e bem organizado. Como manipular commits. Como reescrever o histórico.</p>
<p>E no final quero discutir rapidamente sobre o assunto de monorepos que o Google ou Facebook usam. E finalmente explicar porque o desenvolvimento da kernel do Linux, que foi de onde originou o Git, dificilmente poderia funcionar em plataformas como GitHub.</p>
<p>Links:</p>
<ul>
<li>Git: Squash your latests commits into one (<a href="https://www.devroom.io/2011/07/05/git-squash-your-latests-commits-into-one/"target="_blank" rel="noopener">https://www.devroom.io/2011/07/05/git-squash-your-latests-commits-into-one/</a>)</li>
<li>Git and Other Systems - Migrating to Git (<a href="https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git"target="_blank" rel="noopener">https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git</a>)</li>
<li>Effectively Using Git With Subversion (<a href="https://www.viget.com/articles/effectively-using-git-with-subversion/"target="_blank" rel="noopener">https://www.viget.com/articles/effectively-using-git-with-subversion/</a>)</li>
<li>Git LFS (<a href="https://www.atlassian.com/git/tutorials/git-lfs#how-git-lfs-works"target="_blank" rel="noopener">https://www.atlassian.com/git/tutorials/git-lfs#how-git-lfs-works</a>)</li>
<li>BFG Repo Cleaner (<a href="https://rtyley.github.io/bfg-repo-cleaner/"target="_blank" rel="noopener">https://rtyley.github.io/bfg-repo-cleaner/</a>)</li>
<li>Why Github can&rsquo;t host the Linux Kernel Community (<a href="https://blog.ffwll.ch/2017/08/github-why-cant-host-the-kernel.html"target="_blank" rel="noopener">https://blog.ffwll.ch/2017/08/github-why-cant-host-the-kernel.html</a>)</li>
<li>Why don’t you have a monorepo? (<a href="https://hub.packtpub.com/why-dont-you-have-a-monorepo/"target="_blank" rel="noopener">https://hub.packtpub.com/why-dont-you-have-a-monorepo/</a>)</li>
<li>Monolithic Repos Are Evil (<a href="https://www.yegor256.com/2018/09/05/monolithic-repositories.html"target="_blank" rel="noopener">https://www.yegor256.com/2018/09/05/monolithic-repositories.html</a>)</li>
<li>Monorepos: Please don’t! (<a href="https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b"target="_blank" rel="noopener">https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Fiquei bem contente que vocês gostaram do vídeo de Git da semana passada. Como eu tinha dito o objetivo não era fazer um tutorial e sim dar contexto de como as coisas começaram e porque usamos Git hoje em dia pra tudo. Porém, vocês devem ter notado que o vídeo ficou bem longo passando de 1h, então obviamente eu não consegui dizer tudo que eu queria.</p>
<p>Mas como parece que vocês gostaram do assunto, resolvi fazer este logo na sequência pra encerrar. Neste episódio eu vou de fato fazer um formato mini tutorial no começo. Só que não vai ser os comandos básicos. Como eu já disse, a essa altura você já devia ter ido no site oficial do Git e lido a Documentação Oficial que faz um excelente trabalho de explicar não só o básico, mas tem bastante coisa avançada pra estudar também.</p>
<p>Na segunda metade - que começa neste tempo aqui embaixo se você quiser pular direto - eu quero dar mais contexto. Em particular rapidamente explicar porque apesar do desenvolvimento de um dos projetos open source mais importantes de todos os tempos, a kernel do Linux, ter sido a origem do Git, ninguém por lá tem muita intenção de migrar pra plataformas como GitHub. E também dar meus dois centavos sobre um assunto que talvez você tenha ouvido falar, chamado monorepos.</p>
<p>(&hellip;)</p>
<p>Pra começar eu quero explicar algumas coisas que eu acho que é importante qualquer desenvolvedor saber hoje em dia. A principal função de um repositório Git é gravar um registro preciso da história. Em qualquer momento você pode voltar no tempo e ver como seu código era exatamente uma hora atrás, ou um dia atrás, ou um ano atrás. E ele consegue fazer isso garantindo que os dados colocados lá estão íntegros, ou seja, você nunca vai puxar um dado corrompido sem o Git te avisar. E pra guardar tanta história, ao mesmo tempo ele faz o possível pra economizar espaço.</p>
<p>Apesar do Git sozinho fazer bastante coisa sem você saber, ainda assim tem maneiras de você estragar seu repositório, então o tema do mini tutorial dessa primeira metade do vídeo é um pouco sobre como zelar um pouco mais pelo seu repositório.</p>
<p>E eu acho que a primeira coisa que você deveria tentar fazer é: não escreve porcaria nas mensagens de commit. Tá ligado, mensagens do tipo &ldquo;correção&rdquo; ou &ldquo;fixing&rdquo; ou &ldquo;hotfix&rdquo; ou &ldquo;atualizando&rdquo; ou qualquer lixo assim. Sendo mais exato, você pode fazer mensagens porcaria de commit. Mas no seu próprio branch. Antes de mergear seu trabalho na master, por favor, escreve que nem gente. Vai, o tico e o teco não vão reclamar tanto assim.</p>
<p>Mas tudo bem, eu entendo, você começou a aprender Git semana passada, só sabe os primeiros comandos que todos os dez mil tutoriais por aí já repetiram milhares de vezes. Digamos que você fez um git commit -m e escreveu uma palavra errada. Mas agora já era, tá comitado. Já foi, deixa pra lá.
NÃO, sempre conserte as coisas no minuto que notou o erro. E corrigir uma mensagem de commit é super fácil, nenhum inconveniente.</p>
<p>== tutorial - begin
Vamos lá, primeiro vamos adicionar alguns arquivos no stage.
Agora vamos fazer git commit com um erro na mensagem.
Puts e agora? Se eu puxar o git log, olha que feio.
Mas não tem problema, vamos fazer git commit &ndash;amend com a mensagem certa e
boom, olha só que simples. Completamente indolor, e não te custou 2 segundos.</p>
<p>Mas vamos mudar o exemplo. Digamos que você tá programando, feliz e contente. Só pra simplificar vou só criar um arquivo vazio aqui e adicionar um commit.
Só que aí você lembra, puts, esqueci que precisava fazer outra coisa. Então vou criar outro arquivo aqui e comitar com a feliz mensagem de &ldquo;fixing&rdquo;.
Mas como sempre, nunca acaba quando você pensa, daí dá algum outro pau e você lembra que precisava adicionar outro arquivo, então vamos fazer isso e comitar com a criativa mensagem de &ldquo;fixing&rdquo; de novo.
E você pode continuar fazendo isso e de repente você achava que ia terminar meia dúzia de commits atrás e em vez disso ficou com um monte de commitzinhos com mensagens toscas de fixing.</p>
<p>== tutorial - end</p>
<p>Tudo bem, não tem nenhum problema fazer isso. Porque se você está desenvolvendo no seu branch e ainda não deu push pro servidor, você ainda tem uma chance de não passar vergonha. Tem duas formas de fazer isso. Uma delas é a forma que eu normalmente faço porque eu sou preguiçoso pra relembrar muitos comandos. O git tem um comando que faz diversas coisas, esse é o reset. O último commit que você tá trabalhando normalmente é etiquetado como HEAD ou literalmente cabeça. Eu já não falei que o Git é representado por um grafo? Mas no caso mais simples você tá lidando com uma simples lista ligada de commits e o último se chama HEAD. A importância disso é que podemos navegar por essa lista ligada fazendo HEAD (til) 3 pra apontar 3 commits pra trás por exemplo.</p>
<p>O comando git reset tem duas opções que eu uso mais. &ndash;hard e &ndash;reset. O &ndash;hard normalmente eu uso quando baguncei o código e quero simplesmente apagar tudo que eu fiz e voltar como era antes de eu mexer. É uma opção de tipo “undo” sem recuperação, então cuidado. Por outro lado eu posso fazer &ndash;soft que vai remover os commits da tal lista ligada mas não vai jogar fora as modificações desses commits.</p>
<p>=== tutorial - ini</p>
<p>É isso que eu quero, eu quero apagar os commits fixing mas não quero perder o que eu fiz. Então vamos fazer isso. git reset &ndash;soft HEAD~2 que foram os dois commits de fixing.</p>
<p>Pronto, matei os commits e agora posso fazer um novo git commit com uma mensagem decente. Se eu errar a mensagem, posso fazer &ndash;amend como falei antes. Mas, esse não é o jeito considerado oficial. Tem outro jeito. Então vamos refazer os commits feios de fixing de novo &hellip; (tempo) &hellip; pronto.
Agora podemos usar git rebase -i que significa interativo porque o git vai ficar me perguntando o que ele quer que eu faça. Se eu passar git rebase -i HEAD~2 ele vai abrir a seguinte tela.</p>
<p>Olha que fácil, ele lista os últimos commits, normalmente eu quero manter o primeiro como pick e os seguintes eu posso mudar de pick pra squash. Squash é o que você pensou mesmo, amassar tudo junto. Não precisa ser todos, eu posso inclusive pular um no meio se não fizer sentido mesclar tudo junto.
Embaixo ele te ajuda ainda dizendo quais opções você tem. Eu recomendo brincar com todas as opções num repositório local seu.</p>
<p>Vamos mudar tudo pra squash. Esse editor que ele abriu pra mim é o vim que é o que eu prefiro mas você pode mudar qual editor quer usar com a variável de ambiente EDITOR ou usando o comando git config. De novo, veja a documentação oficial do git config pra saber mais. Mas enfim, quando eu gravar e sair, ele vai abrir o editor de novo, mostrando embaixo o que esse novo commit vai conter e me pedindo pra eu editar uma mensagem decente. Então vamos tirar a mensagem padrão que ele montou e digitar uma mensagem decente.
Pronto, agora se dermos um git log, veja que bonito que ficou.</p>
<p>=== tutorial - end</p>
<p>Pro próximo exemplo, uma coisa muito comum é você começar a trabalhar numa coisa, aí você dá uma divagada, resolve codar outra coisa não relacionada, só que agora você ficou com duas coisas não relacionadas que precisa comitar. Vamos pro caso mais simples. Onde você criou arquivos relacionados a uma coisa e criou outros arquivos que são de outra coisa diferente.</p>
<p>A maioria das pessoas faz um git add . (ponto) e comita tudo junto. Tá errado! Commits devem ter só uma &ldquo;coisa&rdquo; de cada vez. Um commit pra uma pequena funcionalidade. Um commit pra correção de bugs. Um commit pra documentação. Meio difícil definir o que é essa &ldquo;coisa&rdquo; mas usa o bom senso, pra variar. Não faz sentido você misturar um arquivo CSS de uma tela com uma linha nova de README descrevendo infra, por exemplo.</p>
<p>=== tutorial - ini</p>
<p>Vamos criar uns arquivos css qualquer. E vamos criar um arquivo README por exemplo. E vamos criar mais algumas coisa só pra ficar mais realista.
A coisa mais simples é dar git add e o nome do css e fazer o commit só do css.
Depois fazer git add separado do readme e um commit só pra ele.
Quando você tem poucos arquivos esse é o jeito mais simples. Mas normalmente você se empolga, ou tá usando algum gerador de código de algum framework e fica com um tanto de arquivo.</p>
<p>Eu sei, é um saco digitar git add pra caaaada arquivo pra organizar o commit. Digamos que você começou fazendo a besteira de fazer git add . (ponto) agora todos os arquivos estão misturados no stage e se você der git commit vai tudo pro mesmo commit.
Ainda tem como consertar. Se for só um arquivo que você quer tirar do stage basta fazer git reset &ndash; (traço traço duas vezes) e o nome do arquivo. Pronto, se der git status, veja que ele saiu do stage.</p>
<p>Mas como eu falei, faz de conta que são uma dúzia de arquivos e você deu git add . de tudo. Novamente, a preguiça reina e você tá quase ligando o foda-se e fazendo commit de tudo. Calma! Ainda dá pra consertar fácil. Digite git add -i que é de novo opção interativa.
O git vai te abrir esse menuzinho e você pode usar a opção 3 que é revert. Ele vai listar os arquivos que estão no stage e você pode só digitar o número dos arquivos que não quer comitar agora.
No final dá enter pra voltar pro menu anterior.</p>
<p>Digamos que você olha e fala, puuuts, tinha um arquivo que eu tirei que era pra estar lá mesmo na real. Então você tem a opção 2 de update.
É o oposto, agora você digita o número do arquivo que quer colocar no stage.
Dá enter no final, e no menu principal digita q pra dar quit e sair.
Pronto. Vamos dar git status e olha só, é como se tivéssemos dado git add manualmente arquivo por arquivo.
Agora sim podemos fazer o commit correto e fazer git add -i de novo pra ir adicionando arquivos pro próximo commit. Super simples.</p>
<p>Mas, como eu falei, esse é o caso simples. E se no mesmo arquivo temos modificações que um é pra ir num commit e outro é pra ir só em outro commit? Vamos fazer um arquivo README qualquer com um conteúdo aleatório pra servir de base. (&hellip;)
Pronto. Agora vamos fazer 3 modificações.
Agora eu queria que  a primeira modificação e a terceira fossem um commit só. E a segunda modificação fosse um commit separado. Vamos digitar git diff pra ver o patch completo.
Olha só, se eu der git add e comitar vai tudo no mesmo commit. E agora?</p>
<p>Não se preocupem, pra isso tem git add -p. A idéia é que o git vai perguntar um hunk de modificação de cada vez.
Veja só, como as modificações estão próximas ele me pergunta se quer adicionar elas juntas pro stage. Se eu quisesse bastaria digitar y de yes, ou se eu quiser pular eu digito n pra não e esse tanto de opção aqui embaixo se eu der enter ou interrogação, ele vai explicar todas as opções que eu tenho.
Vale a pena explorar cada uma delas depois, num repositório local. Mas no meu caso eu não quero adicionar tudo junto, quero dividir. Pra isso eu digito s de split, e veja só ele dividiu o hunk mais uma vez e me deu só a primeira parte! E é isso que eu quero. Então vamos dar yes. O próximo hunk é o que eu quero separado, então digito “no” pra pular, e a última eu também quero no mesmo commit, então dou yes. Se eu quisesse parar no meio, só digitar q de quit.</p>
<p>Se eu der git status vemos que parte do arquivo README está no stage e parte não está.
Se eu der git diff &ndash;staged pra ver só o que vai no próximo commit, veja que está só o hunk que eu escolhi.
Vamos criar o commit. Pronto, e agora só sobrou a segunda modificação que eu posso adicionar no stage e criar o segundo commit. (&hellip;) Entenderam?
Essa parte pode ser meio complicada da primeira vez então recomendo assistir de novo. Mas resumindo, eu tinha modificações num mesmo arquivo que eu queria separar em commits diferentes. Pra isso usamos git add -p. Aliás esse p é de patch.</p>
<p>== tutorial - end</p>
<p>Trabalhar com o git tem sempre 3 estágios. O primeiro estágio acontece onde chamamos de working tree que é o diretório normal de trabalho com seus arquivos pra você editar e tudo mais. Pra criar um commit você precisa ir estacionando as modificações num lugar intermediário, esse lugar chamamos de stage. O Git não rastreia arquivos, ele rastreia conteúdo. Hunks e patches, lembra que eu falei no episódio passado? Podemos adicionar todas as modificações de um ou mais arquivos com os comandos git add ou git add interativo -i ou adicionar um hunk de cada arquivo de cada vez usando git add -p.</p>
<p>Aliás, como o Git rastreia conteúdo e não diretórios, por isso que diretórios vazios nunca entram num repositório git e por isso muitos frameworks reservam diretórios vazios colocando um arquivo vazio escondido como .gitkeep só pro diretório ter alguma coisa e com isso conseguir adicionar no Git. Uma vez que eu tenho tudo que eu quero no stage, posso empacotar com o comando git commit, onde você cadastra uma mensagem e o git vai terminar adicionando o resto dos metadados pra grudar seu commit como a nova cabeça do grafo, com a etiqueta de HEAD.</p>
<p>Mas mesmo depois que você já comitou, ainda dá pra voltar atrás. Você pode fazer git reset tanto hard pra apagar tudo sem dó nem piedade. Ou git reset soft pra tirar os commits do grafo e jogar as modificações de volta pro stage. Vamos fazer isso pra você ver.</p>
<p>=== tutorial - ini</p>
<p>Digamos que temos alguns commits.
Vamos fazer git reset &ndash;soft HEAD~2 pra apagar 2 commits. Essa sintaxe de HEAD e tilzinho pode ser mais complicado se você tiver merges no meio do caminho, então recomendo pesquisar e estudar como isso funciona na documentação oficial, mas pra maioria dos casos serve associar que esse número é o número de commits pra trás.
=== tutorial - end</p>
<p>Por exemplo, se você quiser reverter um commit de merge que aponta pra outros 10 commits, ele é virar um galho paralelo no histórico. Mas basta usar HEAD~1 que ele vai matar só o commit do merge que é quem liga os outros commits e você some com os 10. Isso não é tão intuitivo então é melhor você praticar.</p>
<p>=== tutorial - ini</p>
<p>De qualquer forma, demos reset soft. Se dermos git log veja que os commits desapareceram, mas você não perdeu nada.
Se der git status veja que está tudo no stage, como se você tivesse acabado de dar git add. Nesse ponto, como já fizemos antes, podemos dar git commit e empacotar tudo num commit. Mas e se alguns desses arquivos eu não quero no próximo commit?
Pra isso podemos fazer git reset &ndash; (traço traço) e o nome do arquivo. Ou só git reset &ndash; e ele vai tirar tudo do stage. Olha no git status como mudou e eles saíram do stage. A partir desse ponto podemos fazer git add -i ou git add -p como já expliquei antes.</p>
<p>Mas digamos que tô estressado e quero só marretar tudo pro lixo. Então eu vou fazer git reset &ndash;hard porque eu sei que só tem lixo aqui mesmo &hellip; e puta que pariu! tinha um arquivo que eu não podia perder!! e agora???</p>
<p>Calma!! Se você tiver o SHA-1 do último commit tá tudo certo. E por sorte demos um git log aqui em cima, então temos o SHA-1 e podemos fazer o equivalente a um undelete. Mesmo se eu não tivesse ainda ia dar, só ia dar um pouquinho mais de trabalho pra achar o commit certo. Como eu disse antes o Git é um banco de dados de grafos. Quando demos o reset só mudamos a etiqueta, ou referência do branch master dois commits pra trás. Os commits em si não foram apagados, eles foram só marcados pra serem apagados. Aliás, pro tipo: sempre evite apagar dados, melhor sempre marcar como apagado. De temos em tempos o Git roda um garbage collector, que você pode executar manualmente com o comando git gc, que seria o equivalente a esvaziar a lixeira no Windows.</p>
<p>Mas não queremos rodar o GC agora. Vamos primeiro tirar nossos commits da lixeira. Pra isso podemos rodar o comando git reflog -b (pra criar um novo branch) teste xxxxx (com o SHA-1). Aproveitando, note que normalmente usamos só um pedaço do começo do SHA-1 em vez do SHA-1 inteiro porque como raramente eles colidem, só a versão curta é suficiente pra achar o commit.
E pronto! Se dermos git log veja que o master continua apontando 2 commits pra trás, mas acabamos de anexar a etiqueta de teste pro commit antigo.</p>
<p>Se eu quiser recuperar de volta pro master, basta dar git checkout master pra voltar pro master, e como estamos trabalhando só localmente sem dar push ou pull de servidor, podemos fazer um git rebase teste.
E se dermos git log de novo veja que o branch master está apontando pro mesmo commit que o branch teste. Se listarmos os arquivos, pronto, tá tudo recuperado! Agora podemos respirar fundo e reescrever esses commits sem perder algo importante. Ah sim, e pode apagar esse branch temporário com git branch -d teste. Pronto, tudo limpo.</p>
<p>=== tutorial - end</p>
<p>Tudo que eu mostrei até agora são comandos que você pode e deve usar no seu dia a dia pra manter o histórico dos seus commits limpos. Muita gente, especialmente iniciantes e amadores, ligam o foda-se pra isso. Eu mesmo sou culpado de já ter dado push de muito commit ruim então não posso julgar tanto. O correto é deixar limpo. Repositório limpo, código limpo, tudo isso faz parte de você se tornar um profissional. É que nem sua casa ou seu escritório. Você pode jogar lixo no chão. Pode deixar as coisas sujas. Isso diz muita coisa sobre você. Se for um espaço compartilhado, demonstra sua falta de respeito com as pessoas ao redor. E nesse caso você também não pode reclamar se achar uma casca de banana em cima do seu teclado. Se você não se importa com os outros, porque os outros tem que se importar com você?</p>
<p>E se você é um tech lead ou seja lá que posição que é responsável por mergear pull requests dos outros, é parte do seu trabalho rejeitar pull requests se eles estiverem sujos e ruim. Senão pra que você serve? Rejeite sumariamente pull requests que estão quebrados, incompletos, não tem testes, estão bagunçados com coisas que não deviam estar misturados, com commits sem mensagens decentes e descritivas. Tudo isso faz parte do protocolo de boa vizinhança.</p>
<p>“Ah mas estamos com pressa, os prazos tão apertados, foda-se se tá um pouco bagunçado ou um pouco quebrado, depois a gente arruma”. Não, depois que alguma coisa é mergeada na master, ninguém nunca mais vai arrumar. Faz direito da primeira vez. Sujeira se acumula, e ninguém nunca vai limpar. O que vai acontecer, como sempre acontece, é que lá na frente essa sujeira vai aparecer. E quando a produtividade cair, número de bugs aumentar, mais pessoas novas entrarem na equipe com todo mundo copiando os maus maus hábitos, é tarde demais. É muito difícil, se não impossível, mudar maus hábitos depois. Então enforce bons hábitos no começo.</p>
<p>Dez anos atrás tinha um excelente programador na minha equipe. E ele gostava de mentorar os outros desenvolvedores. Eu não tenho certeza mas eu acho que todo mundo podia dar push direto na master, ainda não era tão comum usar Pull Requests como fluxo de trabalho. Ele basicamente apagava o commit da master se o novo código estivesse sem testes. A regra dele era muito simples &ldquo;código sem teste não tem lugar no repositório&rdquo;. E ele tinha razão. No começo todo mundo reclama, depois sabe o que acontece? Todo o código está com cobertura de teste. E a produtividade não cai. Mais do que isso, ela aumenta, porque no futuro não acumulou débito técnico.</p>
<p>Débito técnico daria um outro episódio inteiro, mas resumindo é qualquer coisa que você sabe que devia fazer, mas acha que vai te deixar mais devagar, então você escolhe pular. Você cria uma dívida. Só que como o nome diz, uma hora você tem que pagar essa dívida. E quanto mais demora mais os juros vão aumentando. Vai ficando cada vez mais caro, mais e mais rápido. É uma escolha, e às vezes realmente compensa assumir uma pequena dívida, basta ter consciência de pagar os juros sem reclamar depois.</p>
<p>Mudando de assunto, falando em projetos dos outros, vai uma outra dica. Tô parecendo vitrola quebrada, mas na documentação no site oficial do Git tem um capítulo sobre migração de projetos pra Git. Eu mencionei que mesmo se você estiver num projeto subversion, ainda assim pode usar Git. Pra fazer isso primeiro você vai instalar o suporte a subversion do Git.</p>
<p>=== tutorial - ini</p>
<p>Vamos voltar pro Ubuntu e instalar o pacote apt install git-svn. Todas as distros tem algum pacote similar. Feito isso vamos clonar o projeto do próprio subversion, que obviamente está em subversion.</p>
<p>git svn clone &hellip;.</p>
<p>Isso vai demorar um tempão, então vai almoçar. Quando voltar você vai ter um clone do repositório em Git. A partir daqui, você pode simplesmente pode dar git remote pra adicionar um repositório vazio do Github por exemplo, e dar push pra lá. E a partir desse ponto, pode jogar fora o repositório subversion. Todo o histórico está salvo.</p>
<p>=== tutorial - end</p>
<p>Mas, digamos que você não possa fazer isso porque sei lá porque motivo a empresa exige que precisa comitar no servidor de subversion mesmo. Não tem problema. Localmente tudo que eu expliquei no tutorial até agora vai funcionar igualzinho. É um repositório Git normal. De tempos em tempos você pode fazer git svn rebase pra puxar os últimos commits do subversion. E quando você tiver terminado basta agora fazer um git svn dcommit (observe o d antes da palavra commit). Me repetindo de novo, diferente de um commit de Git que é só local commit de subversion é pra mandar pro servidor. Aqui o git svn vai pegar os commits que você trabalhou localmente no Git e criar commits de subversion.</p>
<p>A graça disso é que outros desenvolvedores de subversion, quando derem svn update, vão baixar seus commits como se você nunca tivesse usado Git. A diferença é que você vai trabalhar muito melhor que todo mundo, porque você pode fazer branches locais pra experimentar coisas e trabalhar aos poucos fazendo commits pequenos, e só no final pode fazer squash dos seus branchs e criar commits limpos pra mandar de volta pro servidor legado. Era o que eu fazia em 2007. E digamos que em vez de subversion você tá usando Mercurial, Bazaar, Perforce ou mesmo o antigo TFS. Tem opções similares pra todos eles. Olhem a documentação no site oficial.</p>
<p>Uma última dica importante que muitos iniciantes fazem errado. Digamos que você é um desenvolvedor front-end. Digamos que você receba arquivos de Photoshop dos designers. E como é de costume; vira e mexe os designers ficam mandando novas versões dos photoshops. Uma hora vocês perdem noção de qual era a versão mais nova. E ninguém chega num consenso de qual era a versão do design certo pra mexer. Pra facilitar, alguém tem a grande idéia de criar uma pasta chamada photoshop na raíz do projeto e vocês ensinam o designer a usar Git também. Daí sempre que tiver versões novas, todo mundo recebe no próximo git pull e ninguém mais se confunde. Ótima idéia ….</p>
<p>Só que isso tá errado se você não souber o que é LFS. Lembrem do que eu ensinei no episódio anterior. Ferramentas como diff, patch e o próprio Git tem algoritmos muito bons pra lidar com hunks e patches de texto. Porém, photoshop é um arquivo binário. Não existe nenhuma forma eficiente de se fazer patches de arquivos binários. Se você tentar mergear binários sem saber a estrutura interna delas, a chance de dar problemas é praticamente garantido. Portanto o Git não faz deltas de binários, ele grava o binário inteiro. Aliás, ele grava o arquivo inteiro pra cada modificação.</p>
<p>Pra entender o problema pensa assim. Digamos que o photoshop original tinha 200 Mb. Você adiciona ele no Git. O designer vai mexendo, e todo dia ele cria um novo commit. E ele faz isso por 5 dias só. Em cada commit vai os 200Mb inteiro de cada versão, porque como eu disse, não tem como tirar delta de binário. Ao final de 5 dias seu repositório tem 5 vezes 200 Mb ou 1 Gb. Agora todo novo desenvolvedor que fizer um git clone vai ter que puxar 1 Gb, sendo que o código mesmo, no mesmo repositório, são poucos megas. E normalmente você só precisaria do último photoshop do designer.</p>
<p>Se for arquivos pequenos como jpgs ou pngs tudo bem, mas qualquer coisa muito maior que isso precisa ser tratado diferente. Isso é uma das arestas que ainda não é exatamente a melhor solução, mas pra casos assim, onde você precisa armazenar grandes binários, como photoshops, videos, audio, instaladores, você tem que lembrar de fazer da seguinte maneira:</p>
<p>=== tutorial - ini</p>
<p>Em Ubuntu, primeiro você precisa instalar apt install git-lfs. Pronto, agora no diretório do projeto você inicializa com git lfs install. Finalmente você tem que dizer pro Git que tipos de arquivos você quer jogar no LFS. Pra isso você faz git lfs track &ldquo;*.psd&rdquo; por exemplo. Isso vai criar um arquivo chamado .gitattributes com essa condição.</p>
<p>Agora você pode começar a dar git add de arquivos com extensão .psd. Vamos adicionar alguns psds, dar git add, criar um commit e até aqui, tudo igualzinho com era antes. Do seu ponto de vista de desenvolvedor, não parece que mudou nada.</p>
<p>=== tutorial - end</p>
<p>O que está acontecendo agora é que no commit não vai mais anexado o arquivo binário inteiro. Em vez de jogar o binário no diretório .git/objects, que eu ensinei no episódio anterior, ele vai jogar em outro diretório, o .git/lfs. E no commit em si, em vez de ir armazenado o binário inteiro, vai só um texto indicando onde tá o arquivo. Pense como se fosse um link simbólico num Linux. Assim o commit fica super leve, sem o bináriozão pesando nele. A vantagem disso é que agora quando você fizer um novo git clone do zero, não vai mais vir todo o 1 GB com todas as versões de photoshop que o designer comitou. Vai vir só o último e você pode pedir versões antigas só se precisar.</p>
<p>A desvantagem disso é que instalar e configurar o suporte de LFS num servidor de Git seu pode ser um pouco chatinho de gerenciar. Se você estiver usando GitHub ou GitLab ou outra plataforma web, esse trabalho deve ser mais transparente. Na dúvida, sempre use git lfs se tiver arquivos binários grandes no seu projeto.</p>
<p>Porém, no caso do seu projeto ser basicamente só binários grandes e pouco ou nenhum texto, eu diria que o Git não é a melhor solução. Não só git, nenhum versionador feito pra código. Imagina uma agência de design ou uma produtora de vídeo. Ninguém usa Git num lugar desses. Se você precisa lidar com binário, o fluxo de trabalho é completamente diferente. Não tem como dois designers trabalharem ao mesmo tempo, em máquinas separadas, no mesmo arquivo de Illustrator. Não existe pull request de edição de vídeo de Premiere Pro. Não tem como fazer patches num arquivo de áudio de LogicPro. Trabalho de mídia, pela própria natureza, sempre vai ser um fluxo em cascata, onde a próxima etapa só pode ser iniciada quando a anterior já terminou. E sempre que precisa voltar passos é uma dor de cabeça mesmo.</p>
<p>Por isso mesmo, não é incomum ver servidores de arquivos em rede de alta velocidade, rede de 10 gigabit, com servidores mais parrudos, com gigabytes de RAM pra fazer buffer, e soluções bem caras como os famosos Jellyfish da LumaForge, cujos modelos mais baratos como o de 32TB móvel, começa em mais de 9 mil dólares. E 32TB é bem pouco espaço na real. O modelo servidor, que começa com 80TB não sai por menos de 30 mil dólares. Tá vendo porque um Mac Pro de 10 mil dólares não é caro pra lugares como esses?</p>
<p>Voltando ao assunto. Pra poucos binários como designs de photoshop, video em mp4 de tutorial do seu produto, manuais em PDF e coisas assim, use o git lfs. Mas, digamos que você não sabia disso, e já tem um repositório com um monte de binário, e já tá sentindo que tá lento, todo mundo que faz git clone fica esperando um tempão baixar tudo pela internet. E na cidade que você mora sequer tem suporte a internet de fibra com mais banda. E agora?</p>
<p>O problema de um repositório de versionamento é que por princípio, o binário agora está anexado num commit e esse binário é usado também pra gerar o SHA-1 que identifica o commit. Lembra? Se eu tirar o binário vai necessariamente mudar o SHA-1. Qual o problema disso? Lembra que o Git é um DAG, ou seja um grafo, onde os nós são cada commit apontando pro commit anterior? E como um commit aponta pro anterior? Fácil, armazenando nele o SHA-1 desse commit anterior. Ou seja, se o SHA-1 do commit anterior mudar, o SHA-1 de Todos os outros commits que apontam pra ele mudam também.</p>
<p>Portanto, sim, é possível arrancar fora um arquivo do Git, mas pra fazer isso precisamos literalmente reescrever toda a história até o ponto onde o binário foi adicionado pela primeira vez. Se fizer isso, todo mundo que trabalhava no repositório antigo vai precisar dar um git clone novo tudo outra vez. Se você tá no ponto onde seu repositório tem gigas de tamanho e tá ficando cada vez pior, só adicionar o git lfs não vai adiantar porque o lfs vai ficar ativo só pra novos commits. Mas os antigos vão continuar no repositório.</p>
<p>Entendido isso, vamos aos finalmentes. A situação tá feia, a equipe concorda que vale a pena todo mundo parar de trabalhar um pouco pra limpar o repositório. Lembre de avisar todo mundo pra ver se deram push de tudo. Uma solução é usar o recurso de filter-branch do Git mas ele é muito lento. Não, em vez disso existe uma ferramenta feita em java chamada BFG. Eu gosto dele só pelo nome. Eu não sei se foi essa a inspiração mas qualquer um que tenha jogado Doom sabe o que é um Big Fucking Gun.</p>
<p>Vou deixar o link nas descrições abaixo. Mas resumindo, primeiro você instala Java na sua distro. Como cada um tem seu jeito, veja nas documentações pra cada sistema operacional. Num ubuntu seria um apt install openjdk. Agora você baixa o .jar neste link. Pra executar um java, se você não sabe é rodar o comando java -jar com o caminho pra onde você baixou o .jar.</p>
<p>Agora você precisa fazer um git clone &ndash;mirror do repositório gigante. Tem que ser com &ndash;mirror porque quando você acabar e der git push ele vai reescrever todo o repositório. Mais ou menos como se você fizesse um git push com force.</p>
<p>Com o BFG você pode escolher remover arquivos específicos pelo próprio nome. Mas se forem muitos, você pode escolher remover todos os blobs maiores que um certo tamanho por exemplo. Digamos que todos os seus videos tenham 100mb ou mais, então use esta opção pra remover por esse critério.</p>
<p>Tem um outro caso de uso pro BFG, se por acaso em vez de arquivos grandes na realidade você fez a besteira de iniciante que todo mundo faz, e comitou arquivos de configuração com senhas hardcoded de coisas de produção. Se você não sabia disso, deixa eu explicar: JAMAIS comite coisas com senha num repositório. Mesmo se você apagar do arquivo depois o histórico vai continuar lá. Se você fez a monumental besteira de comitar senhas no Git, agora você precisa obrigatoriamente atirar o BFG pra reescrever a história também.</p>
<p>Lembram que eu já expliquei que o git não apaga pra sempre seus commits? O BFG vai gerar novos commits e reapontar todas as referências de branchs e tudo mais corretamente, mas os commits antigos vão continuar na lixeira. Pra apagar definitivamente você vai usar o comando git gc. O procedimento completo é primeiro rodar um git reflog pra expirar tudo que você não vai precisar e depois rodar o git gc assim. (imagem)</p>
<p>Feito isso, é só dar um git push digamos pro repositório principal no Github. Agora você pode apagar essa cópia local onde rodou o BFG e todo mundo pode dar git clone de novo do novo histórico. E sim, o procedimento não é exatamente rápido nem trivial, mas também a idéia é que se você fizer certo como eu expliquei até agora, você não devia nunca precisar rodar algo assim. O BFG é pros casos raros onde você fez merda e agora temos que limpar a merda. Se isso fosse Pulp Fiction, o BFG seria Winston The Wolf e você é o Vincent ou o Jules tendo que limpar a merda dentro do carro.</p>
<p>E falando em sujeira, vamos falar rapidamente sobre monorepos. Esse é um daqueles assuntos que arrisca ser controverso, mas vou tentar simplificar pra vocês. Se você nunca ouviu falar dessa palavra, se não me engano começou com o Twitter. Mas o que interessa é o seguinte. Digamos que você é uma empresa de tecnologia gigante, mais especificamente uma com poucos produtos muito populares e centenas de desenvolvedores internos. Como num Twitter. Pior ainda, digamos que esse produto é composto por dezenas de bibliotecas e microserviços. Imagine o pesadelo logístico.</p>
<p>Você tem dezenas de pull requests aparecendo toda hora em dezenas de repositórios de código. Alguns deles podem acabar quebrando as integrações entre os diversos componentes. Num exemplo hipotético simples, digamos que num dos repositórios você atualize uma biblioteca de &hellip; parsing de JSON por conta de uma melhoria de performance. Mas digamos que em outro repositório você precise voltar uma versão pra trás dessa biblioteca porque a versão nova causa um erro de compatibilidade com seu código. E agora imagine um terceiro repositório que importa o código desses dois repositórios, cada um dependendo da mesma biblioteca em versões diferentes. Fodeu.</p>
<p>Agora imagine situações como essa acontecendo na mão de centenas de desenvolvedores, toda hora, todos os dias. Imagine a dificuldade que é no final empacotar tudo junto pra gerar um deploy coerente. Milhões de linhas de código, divididos em dezenas ou centenas de repositórios, recebendo modificações o dia inteiro pela mão de centenas de desenvolvedores, sendo vários desses times distribuídos geograficamente e separados até por fusos horários diferentes, e no final precisa empacotar uma versão de produção sem bugs. Ah, e isso tem que acontecer com bastante frequência, tipo uma vez por semana ou mesmo uma vez por dia. Ah sim, e o deploy acontece em centenas de máquinas espalhadas em dezenas de data centers pelo mundo.</p>
<p>E pra piorar imagine 20 anos de histórico de código acumulado no caso de alguém como Google. Falando em Google, no caso dele estamos falando de mais de um bilhão de linhas de código. Mais de 25 mil engenheiros. E quase 20 anos de histórico num mono repositório gigante com todo o código da empresa que hoje já passou dos 80 terabytes de tamanho. Só de código! Esse é o tipo de desafio que empresas com Twitter, Google, Facebook, Netflix, e outros gigantes enfrentam todos os dias.</p>
<p>Nesse contexto, é muito fácil de imaginar que uma das técnicas que pode ajudar é reunir todos os projetos que compõe o produto num único repositório. Mais do que isso, vendorizar, ou seja, baixar as dependências de terceiros e também comitar junto com o código; em vez de instalar bibliotecas só durante o processo de deployment em produção. Por que você acha que o Go, que é a linguagem criada pelo Google, não tem um gerenciador de dependência tradicional como um NPM de Javascript? Porque o Google prefere manter todas as dependências estáticas junto com o código do projeto. Eles também preferem compilar todas as dependências estaticamente no mesmo binariozão, de forma que não precise depender de quase nada instalado no sistema operacional. Essa é a necessidade deles, e um Go da vida foi feito primariamente pra resolver os problemas específicos deles, não necessariamente os seus.</p>
<p>Casos como Google, Twitter ou Facebook são obviamente pontos fora da curva. Você pode contar nos dedos quantas empresas tem o mesmo nível de problemas. Agora vem o ponto principal: você não é o Google! Desculpa, as chances da sua empresa ser o próximo Twitter ou o próximo Facebook é uma fração de uma fração de uma fração de um porcento, o que em matemática nós diríamos que é o limite tendendo a zero.</p>
<p>Quando um engenheiro de uma dessas empresas apresenta em palestras e eventos ou posts de blog sobre o que eles estão fazendo, esses insights servem na maior parte das vezes só pra outras empresas de porte similar. Portanto se você é uma empresa com uma equipe de 10 ou 20 pessoas, com 10 ou 20 repositórios, e só meras dezenas de milhares de linhas de código, fazendo deployment em produção uma vez por quinzena; então quase nada da escalabilidade de um Google se aplica a você. Se você é menor que isso, pode pular esse assunto por um bom tempo, ele não te interessa.</p>
<p>Uma das coisas mais prejudiciais que alguém pode fazer é adotar uma tecnologia ou uma técnica baseado na premissa errada de “se o Google usa, deve ser bom e devemos usar também”. Não, essa premissa não faz o menor sentido, porque o correto deveria ser “se o Google usa, qualquer empresa de mesmo porte e com os mesmos problemas, talvez se beneficiem e talvez valha a pena tentar”. Você tem o porte do Google? Mais importante, você tem o dinheiro que o Google investe? Lógico que não. Então não, você nunca, jamais, deve usar alguma coisa meramente porque você é fanboy de uma marca. Fanboyismo é o oposto de engenharia. Engenharia é encontrar a solução do tamanho certo pro problema certo em mãos, dentro das restrições do seu contexto. Nem mais, nem menos.</p>
<p>Agora tem um problema similar que vale a pena colocar em perspectiva. Eu mencionei no episódio anterior que o desenvolvimento da kernel do Linux não está no GitHub. Um dos motivos é que o Linus Torvalds jamais vai confiar num terceiro pra manter a cópia mestra do kernel.</p>
<p>(torvalds - google)</p>
<p>Mas tirando isso, seria bem difícil usar o fluxo de trabalho e interface gráfica ou mesmo as apis do GitHub. Assim como no caso do monorepo, daria pra ficar horas listando item a item todos os prós e contras e explicar tecnicamente os efeitos pra cada caso particular. Em vez disso quero que você leiam os links que vou deixar nas descrições abaixo e por hoje quero listar só os pontos que eu acho importantes.</p>
<p>A kernel do Linux é um dos projetos open source mais antigos em atividade hoje, e com maior quantidade de código, e com a maior quantidade de voluntários colaborando ao redor do mundo. Dezenas de empresas, de dezenas de setores, dependem do Linux. A kernel em si é tecnicamente um monorepo. Mais corretamente ela é um monotree. Diferente de projetos dentro de um Google, onde a maior parte dos repositórios é centralizada e todo mundo clona de um único lugar. No caso da kernel, existe o repositório do Linus, só que o único desenvolvedor que trabalha nesse repositório é o próprio Linus.</p>
<p>Cada distro como Ubuntu, Fedora ou outros tem seus próprios repositórios principais, que não necessariamente clonam direto do Linus. Cada fabricante de hardware que desenvolve drivers tem repositórios diferentes. Mais importante, diversos produtos estão trabalhando em versões diferentes da kernel. Por exemplo, na própria Canonical que faz o Ubuntu, você tem o Ubuntu LTS que é o Long Term Service e o Ubuntu com versões mais novas dos componentes. A idéia do LTS é que ele atualize o menos possível pra não ficar quebrando compatibilidade o tempo todo.</p>
<p>Porém, digamos que seja descoberto um problema grave de segurança num driver na kernel nova. E esse bug afeta várias versões da kernel pra trás. O bug é descoberto no Fedora primeiro. Então a correção é feita no repositório deles. O patch da correção começa a ser distribuída em mailing lists. Cada empresa ou comunidade com um repositório tem seu mailing list. A forma de distribuir pra tanta gente é literalmente usar o recurso de CC ou carbon copy de e-mail, e assim distribuir o patch pra diversos lugares diferentes.</p>
<p>Pense por um momento num GitHub. Se cada empresa tem uma organização e cada um tem um fork. É como se você tivesse um jeito de postar o mesmo pull request pra diferentes repositórios em diferentes organizações. Coisa que na interface gráfica atual, não dá pra fazer. Na interface web você só devolve pull request pro repositório de quem fez fork. Mas o problema não acaba aí. Não quer dizer que esse patch vai funcionar em todas as versões e diferentes variações de cada organização. Agora os responsáveis precisam testar, muita discussão pode acontecer. Numa mailing list essa discussão é um Reply All pra todo mundo. Num GitHub seriam as Issues em cada repositório, só que no GitHub as issues são atreladas a um repositório. Como que o cara da RedHat que tem repositório próprio vai ficar respondendo nas Issues dele e nas Issues de outras organizações como da Canonical, sobre o mesmo problema? De novo, a interface gráfica atual não tem como resolver esse problema.</p>
<p>Mais do que isso, cada área de interesse dentro do kernel, driver de rede, o X, o systemd e assim por diante tem uma lista de mantenedores. Eles que em última instância vão aprovar ou reprovar uma modificação em seu subsistema. No caso do Linux, se eu quiser achar quem é o responsável pra receber o tal patch de segurança, eu procuro no arquivo chamado MAINTAINERS que tem na raíz do projeto. No caso de um GitHub, cada mantenedor talvez teria um fork do projeto na sua conta do GitHub. Mas não existe uma forma fácil de achar quais dos forks são de mantenedores ativos e quais dos forks são de colaboradores. De novo, a interface gráfica atual não resolve esse problema.</p>
<p>Em última instância, até depois dos outros repositórios já terem aplicado o patch e deployado, a versão mestra pode acabar aplicando o patch depois dos outros, porque o Linus vai ser mais cuidadoso em aceitar os patches, talvez até rejeite e mande modificar. Mas enquanto isso a RedHat ou Canonical precisam fechar o bug pros seus clientes, independente do Linus ter aprovado o patch ou não. Vai ter muitas idas e vindas em cada patch, e não existe a obrigação de que os forks vão esperar pra dar git fetch do Linus só depois dele ter dado merge no repositório dele.</p>
<p>O fluxo de trabalho num GitHub envolve fazer forks dos repositórios, discutir Issues na interface gráfica do repositório, e receber colaborações via pull requests vindos de outros forks. O fluxo de trabalho da kernel do Linux envolve dezenas de repositórios distribuídos que são forks do repositório do Linux, com discussões acontecendo simultaneamente em dezenas de mailing lists, e esses e-mails servindo como canal de distribuição de dezenas de patches de código que são automatizados de diversas maneiras diferentes por cada empresa ou mantenedor. Uma das primeiras coisas que é diferente no fluxo é que no GitHub só tem como fazer pull request de volta pro repositório de onde se fez fork. Na kernel você quer distribuir os patches pra múltiplos repositórios de uma só vez. Além de pode discutir o assunto em paralelo entre diversos repositórios ao mesmo tempo.</p>
<p>É por essas e várias outras razões que, hoje, não tem como suportar o fluxo de trabalho da kernel no GitHub ou GitLab ou qualquer um que siga esse mesmo modelo e tenha essa escala. O fluxo deles segue uma árvore, onde o principal é a raíz e ele vai expandindo numa única direção pra cada galho. No caso do Linux é uma rede, um mesh, onde não existe direção definida, cada nó da rede pode se comunicar com qualquer outro nó da rede, sem um que coordena ou bloqueia essa comunicação. O Git foi originalmente feito com essa estrutura totalmente distribuída em mente. Hoje em dia nós simplificamos de uma forma mais centralizada, como num GitHub, pra ficar mais usável pra maioria dos mortais.</p>
<p>Em resumo, sim, se o GitHub ou GitLab resolverem criar uma estrutura de projeto de múltiplas árvores do mesmo repositório separados pra mantenedores diferentes com uma forma mais simples de unificar a cooperação entre repositórios, tanto do ponto de vista de Issues e Pull Requests distribuídos, talvez, bem talvez, um projeto como Linux poderia viver nessas plataformas. Mas seria um tanto de funcionalidade extra pra atender um nicho muito pequeno de projetos. Pouquíssimos projetos tem os requerimentos da kernel do Linux, por isso não vejo isso acontecendo tão cedo.</p>
<p>Pros colaboradores do kernel, tirando alguns subgrupos que podem trabalhar mais isoladamente; digamos, equipes de drivers ou algo assim, a grande maioria ainda vai ser mais produtiva com simples mailing lists e distribuindo patches mesmo, sem ficar na dependência de nenhuma plataforma controlando a comunicação.</p>
<p>Em ambos os casos, os monorepos do Google e Facebook ou o monotree da kernel do Linux. Nenhum é o nosso caso comum, meu ou seu. Pra maioria esmagadora dos projetos, o GitFlow do GitHub, com um repositório principal, diversos forks, e pull requests que voltam pro repositório principal, e um único lugar pra registrar Issues, ainda é o caso de uso mais produtivo.</p>
<p>Você como desenvolvedor, antes de perder muito tempo tentando achar que deveria usar monorepos porque é fã do Google ou que deveria usar fluxos de monotrees porque é fã do Linus, primeiro pára de fazer commits sujos com mensagens de “fixing” pra lá e pra cá e escreve código decente. Eu posso estar sendo injusto em generalizar, mas vou fazer isso mesmo assim: normalmente quem é fanboy demais e fica só falando em “deveríamos, deveríamos” é o primeiro que faz o trabalho mal feito, pela metade, sujo. Antes de pensar em utopias, primeiro limpa seu quadrado aí, acaba as coisas que começa, e escreve código limpo pra variar. Depois sai cagando regra por aí.</p>
<p>E por hoje é isso aí! Pelo menos pelo futuro próximo eu acho que falei o grosso do que eu tinha pra falar sobre Git. E como eu disse no primeiro episódio de Conhecimentos Gerais, Git é obrigatório saber. E Git é uma ferramenta que à medida que você vai usando e explorando, mais surpresas ele vai revelando. Não é uma coisa que você aprende num workshop de poucas horas e já acha que sabe tudo. Eu diria que se você realmente gosta de programação, Git vai te manter fascinado por um bom tempo. Comece no site oficial, leia a documentação oficial, é lá que se começa. Pare de perder tempo com divagações desnecessárias, ponha a mão na massa e escreva código limpo, pra variar. Se ficaram com dúvidas, não deixem de mandar nos comentários abaixo, se curtiram o vídeo deixem um joinha, assinem o canal e não deixem de clicar no sininho pra não perder os próximos episódios. A gente se vê, até mais.</p>
]]></content:encoded><category>controle-de-versao</category><category>tutoriais</category><category>akitando</category></item><item><title>[Akitando] #70 - Entendendo GIT | (não é um tutorial!)</title><link>https://akitaonrails.github.io/2020/02/05/akitando-70-entendendo-git-nao-e-um-tutorial/</link><guid isPermaLink="true">https://akitaonrails.github.io/2020/02/05/akitando-70-entendendo-git-nao-e-um-tutorial/</guid><pubDate>Wed, 05 Feb 2020 10:30:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/6Czd1Yetaac"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIPTION&lt;span class="hx:absolute hx:-mt-20" id="description"&gt;&lt;/span&gt;
&lt;a href="#description" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Finalmente estou de volta depois de uns 2 meses de férias!&lt;/p&gt;
&lt;p&gt;Pegando o gancho que eu deixei no primeiro episódio da série Começando aos 40, em Conhecimentos Gerais eu falei que era obrigatório saber sobre Git. Pois bem, finalmente consegui fazer um episódio pra cobrir esse assunto.&lt;/p&gt;
&lt;p&gt;Porém, diferente de qualquer outro vídeo que daria um tutorial dos comandos básicos, eu não vou ensinar nenhum comando nem fluxo de trabalho. Em vez disso quero contar algumas histórias de como eu comecei a usar Git e o que me convenceu a insistir em usar mais de 10 anos atrás quando basicamente ninguém fora do projeto de desenvolvimento do kernel do Linux estava interessado.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/6Czd1Yetaac"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIPTION<span class="hx:absolute hx:-mt-20" id="description"></span>
    <a href="#description" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Finalmente estou de volta depois de uns 2 meses de férias!</p>
<p>Pegando o gancho que eu deixei no primeiro episódio da série Começando aos 40, em Conhecimentos Gerais eu falei que era obrigatório saber sobre Git. Pois bem, finalmente consegui fazer um episódio pra cobrir esse assunto.</p>
<p>Porém, diferente de qualquer outro vídeo que daria um tutorial dos comandos básicos, eu não vou ensinar nenhum comando nem fluxo de trabalho. Em vez disso quero contar algumas histórias de como eu comecei a usar Git e o que me convenceu a insistir em usar mais de 10 anos atrás quando basicamente ninguém fora do projeto de desenvolvimento do kernel do Linux estava interessado.</p>
<p>Vamos mergulhar fundo em entender o funcionamento e a estrutura básica do Git, e sabendo isso eu acho que você vai começar a se interessar muito mais em entender mais dessa ferramenta e talvez até olhar para o que você faz de maneira muito diferente!</p>
<p>Links:</p>
<ul>
<li>FLOSS Weekly 19 (<a href="https://twit.tv/shows/floss-weekly/episodes/19"target="_blank" rel="noopener">https://twit.tv/shows/floss-weekly/episodes/19</a>)</li>
<li>Git Internals (<a href="https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain"target="_blank" rel="noopener">https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain</a>)</li>
<li>Three-Way Merging: A Look Under the Hood (<a href="https://www.drdobbs.com/tools/three-way-merging-a-look-under-the-hood/240164902"target="_blank" rel="noopener">https://www.drdobbs.com/tools/three-way-merging-a-look-under-the-hood/240164902</a>)</li>
<li>Tech Talk: Linus Torvalds on Git - Transcript (<a href="https://gist.github.com/dukeofgaming/2150263"target="_blank" rel="noopener">https://gist.github.com/dukeofgaming/2150263</a>)</li>
<li>Longest Common Subsequence (2 Strings) (<a href="https://www.youtube.com/watch?v=ASoaQq66foQ"target="_blank" rel="noopener">https://www.youtube.com/watch?v=ASoaQq66foQ</a>)</li>
<li>TortoiseSVN mailing list 2004 (<a href="https://markmail.org/message/uwi6ld7jqwoyau6z"target="_blank" rel="noopener">https://markmail.org/message/uwi6ld7jqwoyau6z</a>)</li>
<li>Posts de Git em AkitaOnRails.com (<a href="https://www.akitaonrails.com/git"target="_blank" rel="noopener">https://www.akitaonrails.com/git</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal Fabio Akita</p>
<p>Finalmente tô de volta! Fiquei uns dois meses fora meio que de férias de fim de ano enquanto eu tava fazendo obras e me mudando pro meu novo apartamento. Ainda não tá 100% mas vocês já podem ver que o cenário mudou e este é o piloto de teste da nova configuração de equipamentos. Ainda vou refinar mais um pouco e em breve faço um vídeo sobre como tô gravando a partir de agora.</p>
<p>Como já estamos em fevereiro nem vou falar de retrospectiva nem nada disso. Vamos já pular direto pra um assunto técnico. Na real, eu tô postergando esse tema desde um ano atrás quando comecei a série Começando aos 40 e no primeiro vídeo de conhecimentos gerais eu disse que era obrigatório conhecer Git e Linux. Linux eu já fiz um episódio onde falei sobre como instalar e configurar um ambiente de desenvolvimento em Ubuntu. Então resta falar sobre Git.</p>
<p>Porém, já vou avisar que isso Não é um tutorial de Git. Uma coisa que eu não tô a fim de fazer é tutoriais que você pode facilmente encontrar em qualquer outro canal, ou curso online ou posts de blog. Aprender a meia dúzia de comandos básicos de Git é super simples, nenhum inconveniente. Em poucas horas você já aprendeu e com poucos dias praticando você já devia ser capaz de participar de um projeto. Em vez disso, eu quero dar alguns insights que talvez vocês não saibam mesmo se já usam Git faz tempo. Essa história começa uns 12 anos atrás.</p>
<p>(&hellip;)</p>
<p>Lembro bem que era um dia de semana por volta de 2007, eu tava voltando de algum cliente ou algo assim no meu carro e naquela época eu ouvia alguns podcasts e audiobooks porque eu gastava muitas horas no carro indo e voltando do trabalho. E um dos programas que eu ouvia naquela época era o FLOSS Weekly do Leo Laporte, episódio 19 do dia 1o de setembro de 2007. Uma entrevista com Junio Hamano, entrevistado por ninguém menos que Randal Schwartz que era bem conhecido na comunidade Perl. Vou deixar o link desse episódio nas descrições abaixo. Eu mesmo preciso ouvir de novo porque eu só ouvi uma única vez naquele dia.</p>
<p>Pra dar um contexto, nessa época eu tava deixando de ser consultor SAP e programador Java pra experimentar ser freelancer em Ruby on Rails. Eu contei um pouco disso no episódio sobre SAP e sobre Ruby on Rails então dêem uma olhada. Mas o comum naquela época na maioria das empresas era, ou não usar nenhum tipo de controle de versão de código fonte, ou no máximo algum produto comercial Bem porcaria como Microsoft SourceSafe ou Rational ClearCase. Mundo open source ainda não tinha tanta força, muitos projetos ainda usavam CVS, alguns poucos tavam indo pra Subversion. A primeira década do século XXI ainda tava muito atrasado em relação a qualidade de vida pra desenvolvedores.</p>
<p>Eu era um dos rebeldes chatos que ficava insistindo em tentar usar pelo menos Subversion no mundo corporativo, porque eu já tinha cicatrizes demais por conta do SourceSafe. Ninguém merece aquele lixo. Eu sofri com SourceSafe desde o ano 2000 e já tava de saco cheio. E mesmo Subversion não era nenhuma grande maravilha, era mais a filosofia do “menos pior” mesmo. E CVS, quem teve que lidar com essa outra porcaria nos anos 90 lembra o sofrimento. Mundo open source e mundo corporativo estavam bem mal servidos.</p>
<p>A entrevista do podcast era com Junio Hamano que era, e acho que ainda é, o mantenedor do Git. E ele tava justamente explicando que diabos era isso de Git. Em 2007 literalmente ninguém fora dos colaboradores da kernel do Linux tinham muita idéia sobre isso. Como eu tinha essa péssima experiência com CVS, subversion, SourceSafe e tudo mais, quando eu ouvi a explicação fiquei com vontade de experimentar o quanto antes.</p>
<p>Então cheguei em casa e fui logo pesquisar. Obviamente tinha pouca coisa online a respeito. Foi meio um dejavu, foi a mesma sensação quando pesquisei sobre Ruby on Rails 2 anos antes e também não achava ninguém muito interessado. Mas aí esbarrei numa palestra do próprio Linus Torvalds de poucos meses antes. Foi uma tech talk dentro do Google. Tem um monte de coisas do Google que eu pessoalmente não gosto, mas não se pode negar que eles sempre tentam estar um passo à frente nas tendências. E se tem um video sobre Git que eu diria que deveria ser obrigatório assistir é essa tech talk de Maio de 2007 do Torvalds. A seguinte passagem foi o que vendeu Git pra mim:</p>
<p>(Linus xinga CVS e Subversion)
(anybody who disagree is ugly)</p>
<p>Eu Adoro quando o Torvalds não só pisa no calo, mas puxa o martelo e marreta com força. Em resumo, antes do Git a manutenção do projeto da kernel do Linux era feita literalmente com patches e tarballs. Vou voltar pra explicar isso já já. Mas aí eles encontraram a ferramenta comercial BitKeeper que foi um dos primeiros versionadores de código distribuídos e bem feitos. E assim como o GitHub, era uma ferramenta fechada mas que eles deixaram projetos open source usar. Só que o problema de confiar numa ferramenta comercial é que por um tempo eles podem ser legais e deixar usar de grátis, mas um belo dia podem proibir de usar. E foi o que aconteceu, porque alguém tentou fazer engenharia reversa do protocolo deles, se não me engano, e eles não gostaram nada disso e mudaram de idéia.</p>
<p>Daí o Linux ia ficar sem o BitKeeper. Obviamente outras porcarias como SourceSafe jamais foram cogitados como alternativa. E nem voltar pra coisas como CVS. A outra opção era voltar ao procedimento anterior de patches e tarballs. Depois de lançar a kernel 2.6 o Torvalds queria resolver esse problema de uma vez. Ele olhou as opções e pensou assim.</p>
<p>(eu faço melhor em duas semanas e tava certo)</p>
<p>Eu não sou contra falar grande se a pessoa entrega grande. E Torvalds como todo mundo sabe, entrega. E o resultado disso foi o Git, que ele desenvolveu por volta de 2015. Antes de continuar, vale eu voltar rapidamente na história. Pensa assim, um programador iniciante hoje ou um programador nos anos 70 ou 80, não importa. Você tá nos primeiros passos, você quer escrever código. Então você abre um editor de textos qualquer e começa a criar arquivos e vai salvando em algum diretório.</p>
<p>Muito rapidamente você vai cair nos mesmos erros que todo programador sempre caiu não importa em qual época. Você vai escrever em cima do seu código de ontem, salvar e depois descobrir que fez errado e agora perdeu a versão de ontem. Você vai renomear arquivos, sair apagando, e uma hora vai apagar um arquivo que não devia. Quem nunca? Depois de apagar coisas por acidente algumas vezes, você vai começar a duplicar os arquivos antes de editar. Daí vai começar a ficar com versões diferentes do mesmo arquivo no diretório. Eu cansei de fazer isso quando era novo e cansei de ver projetos dos outros cheio de arquivos velhos que não deviam estar lá.</p>
<p>Daí vem o segundo problema, você precisa que outro desenvolvedor trabalhe no mesmo projeto. Uma das maneiras de fazer isso é colocar o diretório do projeto em alguma pasta compartilhada na rede local. Daí acontece de os dois abrirem o mesmo arquivo pra editar. Você edita e salva primeiro. Daí o segundo cara edita a parte dele e salva por cima! E pronto, você acabou de perder o que tava fazendo.</p>
<p>Por isso que muitos produtos comerciais daquela época como SourceSafe ou ClearCase, que eu já xinguei várias vezes, tem como princípio ser uma camada por cima do sistema de arquivos de rede e te dar a opção de bloquear um arquivo ou diretório. Daí antes de abrir um arquivo, primeiro você bloqueia ou faz checkin. Daí se outro programador tentar abrir o mesmo arquivo o sistema vai negar. E ele tem que esperar você terminar, salvar e dar checkout pra desbloquear o arquivo.</p>
<p>Isso ajuda um pouquinho, mas não muito. Eu lembro que no ano 2000 eu trabalhava no Brasil mas na empresa tinha grupos de programadores em Miami, em Buenos Aires e outros lugares da América Latina. Daí um infeliz em Miami bloqueava um arquivo e ia embora sem desbloquear. E eu precisava editar aqui e não conseguia. Daí tinha que ligar pra alguém que fosse administrador do servidor pra desbloquear forçado.</p>
<p>Esse sistema de bloquear e desbloquear era um lixo na prática. Só servia pra casos muito simples. E isso não resolve o problema de alguém ou você mesmo salvar o que não devia e perder o que tinha antes. Pra isso precisava ter versionamento. Hoje em dia os próprios sistemas operacionais e mesmo produtos como Dropbox já oferecem um mínimo de versionamento. Mesmo que você apague o conteúdo de um arquivo e grave, você pode optar em pedir pro sistema fazer tipo um backup da versão anterior. Daí você tem a opção de tentar recuperar algumas versões pra trás. Veja um Time Machine da Apple por exemplo. Hoje em dia a maioria das pessoas tem no mínimo 128GB de HD mas é muito comum ter meio tera ou mais que um terabyte. O computador que eu uso pra editar meus vídeos tem 2 Samsung EVO nvme2 de 3TB cada, sem contar os backups externos.</p>
<p>Mas no ano 2000 era luxo ter 10 GB de HD. Máquina corporativa podia ter menos que isso ainda. E projeto Java naquela época era pesado como é hoje mesmo. Nos anos 90 pra trás, espaço em disco era mais inacessível ainda. O jeito tosco que eu falei acima de duplicar o arquivo antes de editar, pro caso de você precisar da versão anterior é super ineficiente não só porque vai sujando seu diretório de arquivos desnecessários. Mas vira um enorme desperdício de espaço caro em disco. Por isso acho que foi por volta dos anos 70, na época dos UNIX que surgiram duas ferramentas que existem até hoje nos Linux e devia ser ensinado como parte da matéria de algoritmos e estruturas de dados.</p>
<p>A primeira ferramenta se chama Diff que é usado pra tirar a diferença entre dois arquivos. E aqui é um exemplo de onde estudo de algoritmos faz diferença. Se você como iniciante quiser fazer uma ferramenta que abre dois arquivos e tira a diferença em um terceiro arquivo, como você faria? É um problema exponencial que fica lento muito rápido se implementar desse jeito tosco. Mas existe um algoritmo bem conhecido pra encontrar a subsequência comum mais longa entre dois strings. No pior caso a complexidade vai ser de 2 elevado a m, sendo m o tamanho de um dos arquivos. Mas no melhor caso a complexidade cai pra m vezes n, sendo m e n o tamanho dos dois arquivos. Se fizer da forma ingênua vai ficar exponencialmente pior que isso.</p>
<p>Aliás, se vocês estão estudando algoritmos e estruturas de dados na faculdade, recomendo estudar esse algoritmo também. O professor de vocês pode ajudar. Ou procurem vídeos no YouTube mesmo com a explicação passo a passo pra entender como você consegue achar essa subsequência. A partir dessa subsequência, se um conteúdo existe no arquivo original mas não existe na subsequência, podemos marcar com um caracter como &ldquo;menos&rdquo;. Se não existe no arquivo original mas existe na subsequência, podemos marcar com um caracter como &ldquo;mais&rdquo;. Adicione a essa subsequência alguns metadados como em qual linha do arquivo aquela subsequência se localiza e chegamos no que chamamos do formato de um patch.</p>
<p>Então a ferramenta diff que todo Linux tem consegue achar a subsequência comum mais longa entre dois arquivos, adiciona metadados como o número da linha no arquivo e gera esse arquivo de patch. Patch que em inglês é basicamente um remendo. Alguns dizem que essa é a origem do nome Apache, o servidor web. Era A-Patch, por isso Apache.</p>
<p>Enfim, dentro de um arquivo de patch você tem chunks ou hunks de código, que é literalmente nacos de código. Vários nacos de código, ou hunks, formam um patch, que é um remendo. Daí digamos que você queira mandar as modificações que você fez num arquivo pra um amigo seu que tem o mesmo arquivo. Você envia só o arquivo de patch. E no Linux tem a segunda ferramenta que eu falei, que se chama patch e é feito pra aplicar um arquivo de patch no arquivo original, pra ter a versão nova.</p>
<p>Se você já pelo menos andou fuçando um projeto num GitHub da vida, já deve ter visto o formato de patches só que colorizado. Mas é basicamente a mesma coisa. A ferramenta diff nasceu por volta de 1976 então ele é pouca coisa mais velho do que eu &hellip; e graças a isso ficou mais econômico guardar só as modificações feitas sobre o arquivo original em vez de duplicar o arquivo toda vez só por causa de Uma linha modificada. Então economizamos tanto espaço em disco quanto banda de rede já que se fosse transferir as modificações pela rede, antes precisava ir o arquivo modificado inteiro, agora pode ir só o patch que vai ser muito menor. E de novo, lembre-se que hoje você tem banda larga com 200Mb por segundo ou mais, mas antigamente estamos falando de linhas discadas ou redes lentas que trafegavam alguns kilobytes por segundo só.</p>
<p>Agora, se você tiver outro programador que tá colaborando no mesmo projeto, não basta só dar o patch pra ele, ele precisa ter alguma versão do projeto baixado pra poder aplicar esse patch. E como ele baixa o projeto? Pensando de novo nos primórdios da internet, uma forma de fazer isso seria baixar de um servidor de arquivos. Só que eu já expliquei que temos essa limitação de baixa velocidade. Dependendo do tamanho do projeto pode ficar bem lento de baixar.</p>
<p>Pense que antigamente, anos 70 pra trás, disco rígido era uma coisa bem cara. A forma mais comum nos mainframes e minicomputadores era armazenar dados em fitas. Rolos de fitas. E pra gravar em fita você precisava tentar desperdiçar menos espaço quanto possível e daí no fim dos anos 70 nasce o formato de gravação de arquivos em fita ou tape archive, que você conhece hoje como TAR. A estrutura de dados de um TAR é gravar uma série de objetos um atrás do outro. Tar é traduzido literalmente como alcatrão, um poluente de petróleo, mas pense mais como um piche. Então se você tem uma bola de piche onde objetos vão grudando, você tem um tarball.</p>
<p>Daí que vem esse termo que todo mundo de Unix e Linux sempre usa mas você nunca soube o que era. Na prática pense que em vez de você ter 100 arquivos separados, você combine os 100 um atrás do outro num único arquivão. A grosso modo isso é um tar. E pra ser eficiente o comando tar dos Unix e Linux ganhou capacidade de adicionar compressão sem perda a partir dos anos 90.</p>
<p>Aliás, compressão existem dois tipos, lossy e lossless, ou seja, compressão que perde dados e compressão que não perde dados. Exemplos de compressão lossy que perde dados é JPEG pra imagens ou MP3 pra música. Exemplos de compressão que não perde dados é Zip ou 7z. A compressão mais simples que você mesmo pode aprender sozinho e deveria ser ensinado na faculdade na matéria de algoritmos e estruturas de dados é o algoritmo de Huffman pra compressão de texto.</p>
<p>O formato gzip que você vê até hoje em Linux usa o algoritmo deflate que é uma combinação de uma variação do Lempel-Ziv e do Huffman. Aliás, algoritmos de compressão é outra área que os russos dominavam. Se você já usou ou ouviu falar de RAR o nome vem de Roshal Archive, que é o nome do inventor russo Eugene Roshal. O zip e o 7zip vem de outro russo, Igor Pavlov. No fim dos anos 80 um dos compressores mais famosos na época do MS-DOS era o PKZIP que eu usei bastante. Mas nesse caso era do americano Phil Katz. E foi Katz que inventou o deflate dos Unix. Compressão é outro tema complicado de explicar a história, mas até hoje você usa alguma variação disso. Qualquer página na web que seu navegador baixa é entregue comprimido pelos servidores web, normalmente por gzip.</p>
<p>De qualquer forma, essa tangente foi só pra explicar que com as ferramentas que eu descrevi, você já consegue montar um fluxo de trabalho pra um projeto como a kernel do Linux. O Linus Torvalds faz um tarball da versão 1.0 da kernel e coloca num servidor FTP público. Um outro desenvolvedor como um Alan Cox ou Andrew Morton baixa do FTP e começa a desenvolver alguma nova funcionalidade. Quando termina, faz um diff dos arquivos e envia anexado num e-mail de volta pro Linus. No Unix e Linux existem comandos de terminal pra conectar em servidores POP3 ou IMAP hoje em dia, então o Linus consegue automatizar baixar o e-mail, separar o anexo, e usar o comando patch do Linux pra aplicar os patches na versão na sua máquina. Se tudo estiver ok pra ele, ele faz um novo tarball versão 1.1 e coloca no servidor. Se você já fuçou servidores de FTP já deve ter visto diretórios de projetos com tarballs de diversas versões.</p>
<p>Não é um fluxo eficiente em 2020, mas nos anos 90, estamos falando no que havia de mais avançado em fluxo de trabalho distribuído. Então a internet, somado com ferramental Linux como diff, patch, tar, gzip, FTP, e-mail, permitiram um fluxo de trabalho distribuído e o nascimento do mundo open source como conhecemos hoje. Alguma variação disso já existia no mundo Unix nos anos 80 mas foi com o Linux que esse modelo de trabalho começou a ganhar chão.</p>
<p>Mas isso é no mundo pós-internet comercial e Linux. No começo dos anos 70 já havia o conceito de versionamento de código fonte. Em resumo em 1972 havia o SCSS ou Source Code Control System desenvolvido na linguagem SNOBOL na Bell Labs, obviamente, e usado em sistemas como IBM System 370 até o PDP-11. Nos anos 80 foi substituído por uma evolução, que foi o RCS ou Revision Control System. O RCS ainda é mantido até hoje. Finalmente por volta de 1990 surgiu o Concurrent Version System ou CVS pra substituir o RCS.</p>
<p>O modelo sempre foi o que se chama de centralizado, ou cliente-servidor, que era a arquitetura da moda nos anos 80 e 90. Você configurava um servidor onde ficava o projeto. Na máquina de cada desenvolvedor você instala um cliente que consegue se conectar nesse servidor. O servidor era responsável por coisas como autenticar o login de cada desenvolvedor pra dar permissão de acesso ao código. Daí o desenvolvedor podia fazer checkout pra baixar todo o código; fazer checkin pra subir as modificações e de tempos em tempos dar um update pra baixar as últimas modificações do servidor. O servidor ia guardando os deltas de modificação, possível graças à idéia de patches. Novas versões adicionaram coisas como delta compression pra economizar espaço no disco guardando os patches zipados. E novas funcionalidades foram aparecendo como a idéia de fazer branches.</p>
<p>Branch é um conceito simples com uma operação complicada. Fazer um branch, da forma mais grosseira, é duplicar o diretório do projeto. Daí você trabalha num diretório separado. Quando termina gera os patches e aplica no diretório principal e pode apagar o diretório duplicado. É uma forma de trabalhar numa funcionalidade que pode demorar; ou testar correção de bugs, sem atrapalhar o diretório principal. Isso funciona bem se você trabalha sozinho, ou com poucas pessoas, ou com pessoas que estão muito bem coordenadas entre si. Caso contrário isso se torna um puta pesadelo muito rápido. Nos dias de hoje você tem Pull Requests no GitHub que é basicamente um branch do projeto.</p>
<p>(Linux branch true guru)</p>
<p>Pra adiantar essa história, o que foi o Subversion? Subversion de fato nasceu com o conceito de CVS feito direito. Na prática ele adota um fluxo muito parecido com o CVS mas com uma implementação menos puxadinho e um pouco mais estável. O básico funciona igual, checkout, checkin ou commit, update. Mas branches sempre foi o calcanhar de aquiles. O Subversion consertou branches tarde demais. Só na versão 1.5 que saiu em junho de 2008, que ele ganhou a funcionalidade de merge tracking. 8 anos depois da primeira versão. Aí foi muito pouco muito tarde. Em 2008 foi quando o GitHub começou a bombar e aí o resto é história.</p>
<p>Eu já expliquei porque a gente odeia coisas como SourceSafe, porque o conceito de bloquear arquivos pra só uma pessoa usar de cada vez é uma desgraça. Pior ainda pra gerenciar releases, ou fechar versões. Era tão complicado essas porcarias que nos anos 90 e começo dos anos 2000 havia a figura de um gestor de configuração. Um cara pago pra fazer o equivalente a commit e tagear versões. Pensa no absurdo de precisar de alguém tempo integral pra não corromper muito o código fonte.</p>
<p>O outro problema eram as branches. E é a razão principal de porque CVS e Subversion tiveram reputação tão ruim. A gente era meio que obrigado a usar porque não tinha opção melhor. Ninguém usava porque gostava. Faça um exercício: tenta instalar um servidor CSV em qualquer Linux hoje. Você vai ver que é super chato. Eu mesmo perco a paciência e desisto no meio de tão chato que é configurar um CVS. Subversion é um pouco mais fácil de configurar e mesmo assim é um saco.</p>
<p>Um dos grandes defeitos estruturais do CVS é que ele guardava revisões arquivo por arquivo, ele não entendia o conceito de um único arquivão de patch de uma funcionalidade inteira, como um Pull Request hoje por exemplo, que é um conjunto de commits. De forma simplória um commit é nada mais que um arquivo de patch com um cabeçalho com metadados como o autor do commit e data e hora. Mas enfim, era difícil de gerenciar versões no CVS. Cada desenvolvedor precisava trabalhar num branch ou num fork que, pra todos os efeitos e propósitos, é mais ou menos a mesma coisa, uma duplicata do projeto original. Só que pra combinar as modificações de dezenas de desenvolvedores numa única versão, era um cornojob desgraçado, podia levar um dia inteiro ou mais pra conseguir fazer o merge.</p>
<p>Eis que na virada do século surge a BitMover e seu produto BitKeeper. Muita gente pensa que a principal diferença era ser descentralizado. Mas na realidade a principal novidade foi tornar branches, forks e principalmente merges, operações razoavelmente triviais. Um trabalho que podia levar horas ou dias reduzido pra minutos, e com muito menos margem de erros. Por isso que apesar de ser um produto comercial de código fechado, por pragmatismo, o Linus resolveu migrar o desenvolvimento do kernel pro BitKeeper. Isso foi em 2002, mas em 2 ou 3 anos teve o problema que já falei que a BitMover resolveu botar restrições demais ao ponto que o Linus se encheu.</p>
<p>Com o CVS e Subversion, que eram as alternativas open source, muito atrasados em relação ao BitKeeper, as outras alternativas comerciais sendo as porcarias que sempre foram, muitos começaram a pensar em novas soluções. Assim em 2003 surgiu o Darcs e em 2005 surgiu o Mercurial e a própria Canonical que mantém o Ubuntu fez o Bazaar. E também em 2005 o próprio Linus resolveu fazer o Git. No papel todos eles são muito parecidos. Sistemas de versionamento descentralizados com foco em resolver o problema de branches e merges. A diferença é que todos eles tinham problema de performance, menos o Git.</p>
<p>Mercurial e Bazaar eram feitos em Python, então eles não tem como escalar em performance até certo ponto. Darcs se não me engano era feito em Haskell e também era criticado por problemas de performance. O Git, por outro lado, feito pelo Linus sempre foi hiper rápido, porém difícil de usar. Mas em casos como esse, performance e, principalmente segurança, tem prioridade.</p>
<p>Antes desses novos sistemas, a estrutura de dados que ferramentas como Subversion entendiam era uma árvore. Pense diretórios e arquivos. É uma árvore. Um branch era mais ou menos duplicar a árvore. E um merge era comparar duas árvores. Pra ilustrar um problema óbvio. Pense que no diretório original você renomeia um arquivo. E no diretório duplicado você deleta o mesmo arquivo. Agora tenta fazer o merge disso no Subversion ou CVS, você vai tomar um conflito de merge. Isso é o tipo de coisa que um Git faz merge automaticamente e corretamente, BitKeeper e os outros novos também.</p>
<p>Conflitos de merge é o pesadelo de todo desenvolvedor nesses sistemas antigos e incompetentes. Num Git minimamente bem usado você raramente encontra conflitos. E mesmo quando encontra eles costumam ser fáceis de resolver. Hoje em dia trabalhar em branches é super comum, a gente faz isso o tempo todo. Fazer merge também. Idealmente você faz merges com frequência e evita deixar código não mergeado esquecido num branch por semanas ou meses. Mas em sistemas antigos fazer branches custava meio caro, e com merges eram complicados, você evitava ao máximo fazer com frequência. Só que quanto mais você espera também mais caro vai ficando, então era uma faca de dois gumes. Em vez de ter que passar por todo esse pesadelo a maioria simplesmente desistia de fazer branches e todo mundo trabalhava na mesma versão, que em Subversion se chama trunk, em Git é o que você conhece como master.</p>
<p>Por isso antigamente se tinha tanta discussão sobre quem podia ou quem não podia ter acesso de escrever, ou seja acesso de commit no trunk. O Linus explica isso melhor:</p>
<p>(Linus morons commit access)</p>
<p>Agora, por que branches e merges num sistema como Git é tão infinitamente melhor do que num Subversion antes da versão 1.5? Pra simplificar vamos pensar o que você vê hoje em dia num projeto num GitHub da vida, que usa Git por baixo. Aliás, se você não sabia, você não precisa de GitHub pra usar Git, o GitHub é só um facilitador mas ele é opcional. De qualquer forma, se você já fez qualquer tutorial de Git e já tentou usar um pouco deve ter notado alguns termos que a gente usa. Vamos recapitular, você faz git clone pra puxar uma cópia do projeto pra sua máquina. Você vai fazendo modificações, edita um arquivo, cria um novo, renomeia outro e quando termina uma parte do trabalho, faz um git add pra marcar essas alterações e faz um git commit pra empacotar essas modificações. Se você não sabia disso, o git commit por baixo faz algo equivalente ao diff que falei antes. Um commit é o patch de modificações desde o commit anterior.</p>
<p>Eu fiz o episódio de Criptografia básica porque eu precisava ensinar um conceito lá que vou usar agora. Uma das coisas que o Linus queria garantir é que os dados no repositório Git sejam sempre confiáveis, ou seja, que eu possa confiar que o texto que eu baixo dele está correto. Pra garantir isso a próxima coisa depois do tal diff que o git commit faz é passar o patch por um algoritmo de hashing no caso o SHA-1. Esse é aquele numerozão longo e estranho que todo commit tem. É o hash do patch, a assinatura daquele delta. Lembra do conceito de hash? Se eu modificar um bit dentro desse patch o SHA-1 vai mudar completamente e não vai mais bater com o SHA-1 original então eu sei que o patch está corrompido.</p>
<p>Aqui vale mais uma curiosidade. Eu não entendo quando as pessoas me perguntam &ldquo;Akita, que tutorial de Git eu devo estudar?&rdquo; Qual é a dificuldade de ler a documentação no site oficial das coisas como o primeiro passo? Eu sempre achei óbvio ir no site oficial de tudo que eu quero aprender. Se você tivesse ido no site do Git e aberto o link “Book” que é o livro, e lido o capítulo Git Internals, já saberia muito do que estou dizendo aqui. Só pra não perder tempo, eu vou justamente usar um trecho desse capítulo.</p>
<p>Eu disse que não vou fazer um tutorial, mas eu acho interessante demonstrar algumas coisas neste ponto. Eu fico bastante irritado de ver programadores usando ferramentas que geram arquivos e modificam coisas no seu projeto e ele não saber o que está acontecendo. Deixa eu fazer outra tangente. Quando eu aprendi CVS uma coisa que era necessário pra ferramenta rastrear os arquivos é que ele sujava meu projeto inteiro criando um diretório .cvs em todos os subdiretórios do projeto. Era um saco isso. Esse subdiretório começa com ponto porque todo mundo sabe que em Linux um arquivo que começa com ponto significa escondido. Se você mexesse nesses diretórios tinha muita chance de estragar tudo e ter que dar um checkout de novo pra reconstruir certo.</p>
<p>Subversion fazia a mesma coisa, só que agora eram subdiretórios .svn. Até aí tudo bem. Só que em 2003 eu desenvolvia em Windows e tinha projetos .NET que eu precisava usar Visual Studio. Se você usasse alguma coisa como a porcaria do SourceSafe, tecnicamente tudo funcionava integrado. Mas obviamente eu era rebelde e queria usar Subversion pra tudo. Pois bem, me fodi, Visual Studio crasheava quando tentava abrir um projeto vindo do Subversion. Porque em 2003 nem Windows XP, nem Windows Server 2003 entendiam direito arquivos que começam com ponto. Era um dos motivos de porque quase ninguém usava Subversion com projetos de Visual Studio no começo dos anos 2000.</p>
<p>O que eu fiz? A ferramenta que normalmente se usava pra dar checkout de repositórios Subversion se chamava TortoiseSVN. Era uma ferramentinha feita em Visual C++. Então eu fiz um patch pra esse projeto, eu fiz o Tortoise criar esses subdiretórios de rastreamento como underline &ldquo;_svn&rdquo; em vez de ponto &ldquo;.svn&rdquo; e aí o Visual Studio conseguia abrir o diretório agora sem crashear. Pra fazer isso primeiro eu precisava saber que não tinha importância renomear esse diretório, porque ele só é importante pro cliente de subversion, não pro servidor.</p>
<p>Hoje em dia felizmente o Windows já entende arquivos e diretórios que começa com ponto, porque o Git faz a mesma coisa, ele cria um diretório chamado ponto &ldquo;.git&rdquo;. Mas ele é diferente do CVS e Subversion, porque na realidade agora os arquivos e diretórios do projeto que você vê e edita é que não tem importância. O conteúdo de verdade dos arquivos fica no repositório inteiro dentro desse ponto .git e por isso só tem um na raíz e não em todos os subdiretórios.</p>
<p>Entenda. Se você cria um projeto qualquer, digamos de de Node, de C#, tanto faz, e dá git init pra iniciar, depois git add de tudo e o primeiro git commit, agora você pode apagar os arquivos originais, menos o diretório .git. Mesmo se você apagar qualquer arquivo, qualquer um pode ser recuperado com um simples git checkout. Ele vai procurar o arquivo dentro desse .git, sem precisar perguntar nada pra nenhum servidor. É por isso que você consegue ficar mudando de branches no mesmo diretório, o conteúdo inteiro muda. Arquivos somem e reaparecem. Num Mercurial toda nova branch gera um novo diretório ao lado do diretório original do projeto.</p>
<p>Só que se você listar o que tem dentro desse diretório, você não vai achar os arquivos do seu projeto. Em vez disso vai ter alguns subdiretórios meio estranhos e um maior que é o .git / objects. E nele vai achar um monte de arquivos com nomes que parecem hashes de SHA-1. Ficou complicado? Deixa eu ilustrar. Nesta parte eu vou seguir igualzinho a primeira página do capítulo de Git Internals do livro gratuito que está no site oficial, que é uma coisa que você já devia ter feito também. Vamos lá, primeiro criar um novo repositório local de git com o comando git init test.</p>
<p>Agora vamos listar o tal diretório .git / objects. Vazio. Agora vamos usar um comando que você nunca viu o git hash-object. Vou dar echo de uma string e passar pra esse comando. Agora se listar de novo o diretório você vai ver que apareceu um arquivo lá com o nome sendo o SHA-1 desse string. Agora, pra pegar o conteúdo desse arquivo podemos usar outro comando que você nunca viu e nem nunca vai usar o git cat-file. E pronto, é o conteúdo que passamos antes.</p>
<p>O repositório git é um banco de dados de objetos. Vamos dar echo de um string qualquer pra dentro de um arquivo. Isso é uma operação do sistema operacional, nada a ver com git. Agora vamos usar o git pra gerar o hash do conteúdo do arquivo com o git hash-object de novo. Faltou nesse livro apagar o arquivo original antes de dar echo de novo, senão o próximo comando vai falhar. Então vamos apagar e agora dar echo de um novo conteúdo com o mesmo nome de arquivo. Rodamos hash-object e obviamente o SHA-1 tem que ser diferente pra um conteúdo diferente.</p>
<p>Listando os objetos agora temos mais arquivos. Nesse ponto, podemos apagar de novo o arquivo e recuperar o conteúdo que está no arquivo hasheado. E assim sabemos que é possível recuperar conteúdo que você apagou a partir do versionamento do repositório. É assim que comandos como o git checkout funcionam.</p>
<p>Só que até aqui estamos gravando meramente os conteúdos dos arquivos mas não os metadados como o nome do arquivo, estrutura de diretórios ou atributos. Os objetos que criamos até aqui são meros blobs, um punhado de bytes. Pra organizar, precisamos criar esses metadados que também são objetos, mas em vez de tipo blob eles são tipo tree ou árvore. Pra isso precisamos usar uma área intermediária do Git onde é possível manipular esses metadados. Se você já usou o comando git add é nessa área intermediária que o git estaciona suas modificações.</p>
<p>No nosso caso queremos adicionar os metadados na primeira versão do arquivo text.txt que criamos no começo. Então vamos usar outro comando que você nunca viu o git update-index. E esse número 100644 é o mode do arquivo, que se você já usou Linux antes deve saber do comando chmod que é change mode. 644 é um arquivo normal, 755 é um arquivo executável. Ou sendo mais exato, o primeiro número é o modo pro usuário dono do arquivo, o segundo número é o modo pro grupo e o terceiro número é o modo pra qualquer outro usuário. 7 é ler, escrever e executar, 5 é ler e executar, 4 é só leitura e assim por diante. Estude sobre chmod depois pra entender isso melhor. É coisa básica de Linux.</p>
<p>De qualquer forma, nada foi gravado ainda, pra gravar precisamos agora tirar da área estacionada ou stage com o comando git write-tree. E agora criamos um novo objeto de tipo tree com os metadados e podemos usar o git cat-file pra ver. Agora vamos criar um novo objeto de tree só que apontando pra dois outros objetos, um novo arquivo e um que já existe. Primeiro criamos o arquivo new.txt. Agora damos update-index na versão mais recente do test.txt que criamos antes e também um update-index no arquivo new.txt. De novo, estamos trabalhando na área intermediária de stage, então quando terminamos de adicionar os metadados, agora podemos fazer git write-tree pra gerar o objeto de tree com esses metadados. Vamos listar com cat-file e agora ele mostra os dois blobs que esse novo objeto de tree está apontando.</p>
<p>Entenderam o que estamos fazendo? Estamos manipulando o repositório Git local que é basicamente um banco de dados de objetos, criando os objetos e apontando novos objetos de metadados pra eles. Até agora nossa estrutura está mais ou menos como nessa ilustração.</p>
<p>Finalmente, vamos pro terceiro tipo de objeto interessante aqui, os commits. Vamos criar um commit apontando pro último objeto de tree que criamos. Quando tentamos criar um commit o git vai reclamar que eu ainda não configurei email nem nome pra etiquetar no commit. Pra gerar os mesmos SHA-1 do exemplo eu vou usar o email e nome do Scott Chacon que é o autor desse livro e um dos sócios fundadores do GitHub. Mas na sua máquina você deve usar seu próprio nome e e-mail, claro.</p>
<p>Pronto, agora geramos o commit de novo e novamente ele nos devolve um hash. Veja como todo objeto, seja um blob, um tree ou um commit ou qualquer outro tipo sempre gera um SHA-1 que ao mesmo tempo serve como ID desse objeto e também como validador da integridade do dado. Podemos usar o mesmo cat-file pra mostrar esse commit também usando o hash que acabamos de gerar. E se você já usou git antes, esse formato deve ser familiar pra você.</p>
<p>Vamos gerar mais dois objetos de commit, cada um referenciando o commit anterior. Então criamos o segundo commit apontando pro primeiro. E o terceiro apontando pro segundo. E agora, se usarmos o comando git log, vamos ver tudo organizado.</p>
<p>Entenda o que aconteceu até aqui. Usamos comandos de baixo nível do Git que são conhecidos como plumbing, ou encanamento mesmo, como cat-file, write-tree, update-index, read-tree, pra fazer as mesmas coisas que outros dois comandos que você talvez já conheça, o git add e git commit. Add e Commit são comandos conhecidos como Porcelain, de alto nível. Todo tutorial e curso de Git só ensina os comandos Porcelain, mas entenda que por baixo dos panos na realidade esses comandos fazem uso do encanamento, dos comandos plumbing, que você raramente vai usar. O Linus desenvolveu primeiro os comandos plumbing, foi depois que o Junio Hamano entrou como novo mantenedor que ele e outros colaboradores foram melhorando a usabilidade e criando comandos mais simples pra meros mortais.</p>
<p>Agora vamos terminar essa parte mostrando por outra perspectiva. Vamos abrir o console de Ruby puro. Começamos criando uma variável com um string. Agora vamos criar uma variável chamada header que tem como conteúdo a palavra blog e o tamanho da variável anterior. Finalmente vamos criar uma terceira variável chamada store que é o header mais o conteúdo.</p>
<p>Vamos importar a biblioteca de sha1 do Ruby e gerar a versão hexadecimal de 40 caracteres desse store. Agora vamos abrir um outro terminal e usar o primeiro comando plumbing do Git que vimos, o hash-object. E veja que o SHA-1 é exatamente igual. É assim que o Git estrutura o conteúdo e gera o hash. Se você foi atento deve ter notado que usamos o comando git cat-file pra ver o conteúdo do objeto. Mas porque não usamos o comando cat padrão do Linux? Vamos fazer isso no terminal. E veja que apareceu só um monte de sujeira que não dá pra ler. É um binário, não um texto. Mas o que é esse binário?</p>
<p>Voltando pro Ruby, vamos importar outra biblioteca, a zlib pra ter acesso ao método Deflate. Lembram que eu falei de deflate antes? Que é a compressão do gzip? Então vamos aplicar deflate na variável store e esse é o binário comprimido da string original. Parece maior porque é a representação do binário no terminal, mas o conteúdo real é menor. E pra simular o que o git hash-object faz, vamos gravar o arquivo. Primeiro vamos gerar o caminho completo do diretório e arquivo.</p>
<p>De curiosidade você vai notar que quebramos os primeiros dois caracteres desse SHA-1 pra fazer um subdiretório e colocamos o restante como arquivo dentro dele. Se você é um pouco experiente entende porque. Entenda que diretórios tem limite de arquivos. Eu não lembro se eram uns 64 mil arquivos. Pra evitar ficar tudo num diretório só, criando subdiretórios a partir do SHA-1 vai ajudar a balancear os arquivos, agora você pode ter uns 64 mil subdiretórios cada um com uns 64 mil arquivos cada ou seja, aumentamos a capacidade máxima pra uns 4 bilhões de arquivos… deve ser suficiente.</p>
<p>Agora vamos importar a biblioteca fileutils pra criar os subdiretórios caso não existam. No Linux usamos o comando mkdir com o parâmetro traço -p pra isso, por isso o método no fileutils do Ruby se chama mkdir_p. E finalmente gravamos a versão comprimida do conteúdo no novo arquivo. Pra ver se funcionou, podemos sair do console do Ruby e usar o mesmo comando git cat-file pra abrir esse objeto blob que acabamos de criar. E funciona!</p>
<p>Repetindo, quando você clona um projeto do GitHub pra sua máquina, os diretórios e arquivos do projeto que você vê no seu editor, são só um espelho do que está dentro do diretório .git / objects. O banco de dados de verdade são esses objects. A estrutura de diretórios e arquivos pode ser inteira recriada a partir desses objetos. Mais do que isso, perceberam que quando criamos o SHA-1 ele só considera o blob. Os metadados como nome do arquivo e diretório ficam num objeto separado, do tipo tree.</p>
<p>É uma das razões de porque o Git é mais eficiente pra comparar versões diferentes. Lembram quando eu falei da situação de você renomear um arquivo num branch e apagar o arquivo num outro branch e o conflito que isso dá num Subversion ou CVS? No Git esse conflito não existe, o Git rastreia primeiro o conteúdo, depois os metadados.</p>
<p>Mas essa é só a primeira vantagem. E com esse exemplo de Ruby você sabe como as bibliotecas de Git em qualquer outra linguagem se integram com repositórios Git. É isso que permite um GitHub ou GitLab. A estrutura de dados do Git é aberta e razoavelmente simples de trabalhar.</p>
<p>Eu recomendo que vocês leiam o livro no site oficial do Git pra entender como o resto do Git funciona. Mas agora eu posso voltar pro problema principal: branches e merge. Como eu disse antes, imagine que em sistemas antigos tipo CVS um branch é mais ou menos internamente como duplicar o diretório inteiro do projeto. E o merge é fazer diff no diretório duplicado, gerar os patches de cada arquivo e aplicar de volta no diretório original. Parece simples, mas é muito fácil de ver como isso vai fracassar homericamente.</p>
<p>Esse tipo de merge é o que se chama de 2-way. O Git prioriza não a estrutura de diretórios, mas um grafo, mais especificamente um DAG ou um Direct Acyclic Graph. Acíclico porque os nós desse grafo não apontam de volta um pro outro causando um loop infinito. Vocês viram como manualmente criamos um commit novo apontando pro commit anterior. É isso que o comando git commit faz sempre: cria um commit ligado ao anterior, criando uma cadeia. O DAG é esse grafo de commits. Um branch é nada mais, nada menos que um desvio nesse grafo com uma etiqueta com o nome do branch.</p>
<p>A vantagem disso é que todo branch tem a informação do ponto de Origem. A partir de qual commit começa o branch. Um CVS e o Subversion antes da versão 1.5, não tem essa informação. Depois que você faz merge uma vez, fodeu, você não pode mais continuar trabalhando no branch, porque o próximo merge vai ser um inferno na Terra de conflitos. Até aqui deve estar meio confuso pra vocês visualizarem, então vamos fazer um exemplo simples. Na verdade eu adaptei o exemplo de um artigo da revista Dr. Dobbs que eu li em 2013 pra entender isso e ainda é um bom exemplo, então deixo o link nas descrições abaixo.</p>
<p>Pra variar vamos chamar nossos desenvolvedores fictícios de Alice e Bob. Cada um tem uma cópia do projeto. Em um determinado momento precisamos mergear o trabalho dos dois. Você abre o arquivo da Alice e encontra o comando Print hello na linha 30. Daí abre o mesmo arquivo do Bob e vê o comando um pouco diferente, Print bye na mesma linha 30. E agora? Um CVS olha pra esses dois arquivos e tem que interromper o processo de merge porque ele não tem como decidir qual das duas linhas é a válida. Agora a Alice e o Bob precisam ser chamados pra ver se eles lembram o que era pra ser o correto. Isso é um pequeno exemplo, agora imagina isso acontecendo em centenas de linhas de dezenas de arquivos no mesmo projeto.</p>
<p>Isso é o que chamamos de um merge 2-way, ou seja com duas versões sendo comparadas. A diferença no Git é que quando fazemos um branch ele marca a commit de origem do branch. Agora Alice continua trabalhando no branch original master e Bob começa a trabalhar no novo branch, digamos hotfix, que acabamos de criar. Mesma situação, no final precisamos mesclar o trabalho dele e chegamos no mesmo problema de antes na tal linha 30 do arquivo.</p>
<p>A diferença é que o Git pode voltar no passado até o ponto onde o branch foi criado e olhar como era o arquivo original. Agora temos 3 bases de comparação, a versão da Alice, a versão do Bob e a versão original. E se olharmos a versão original vamos descobrir que originalmente a linha 30 tinha o comando Print bye. Então quem modificou o arquivo foi a Alice. E o Git pode escolher automaticamente usar a versão mais recente que é certamente a da Alice. E o processo de merge não dá mais esse conflito e pode seguir automaticamente.</p>
<p>(Linus 22,000 files)</p>
<p>Agora você começa a entender porque o algoritmo de merge 3-way é absurdamente superior a 2-way. Por causa disso o Git consegue eliminar praticamente todo conflito que daria num CVS. Esse foi o ponto que pra mim mais me deixou impressionado em 2007. Mais do que isso, depois que o Git conclui o merge, ele cria um commit de merge. Agora Alice e Bob podem continuar trabalhando cada um em seu respectivo branch e quando forem fazer um novo merge, o novo ancestral de comparação vai ser esse último merge e não o commit de quando o branch foi criado. Num CVS e no Subversion sem o merge tracking da versão 1.5 que eu falei, eles não tem como saber quando aconteceu o último merge e ele sempre vai comparar tudo de um branch contra tudo do outro branch, então se da primeira vez deu um monte de conflito, vai dar tudo de novo no próximo merge e só vai piorando.</p>
<p>== cortado - começo
Outra coisa que parece confundir muita gente até hoje é a diferença entre rebase e merge. A regra é simples, você nunca faz rebase num branch que espelha um branch num repositório oficial, por exemplo na sua conta no GitHub. Por convenção Isso normalmente é o branch chamado de master, que aponta pra remote/origin/master do servidor. Agora, qualquer branch de nova funcionalidade, ou branch experimental que você cria na sua própria máquina, você sempre deve fazer rebase da master nele. E quando terminar você faz merge da sua branch pra master. Essa é a regra: rebase a partir do master pra sua branch e merge da sua branch pro master.</p>
<p>Agora o que diabos é um rebase na prática. Vamos voltar pro exemplo da Alice e do Bob. Pra facilitar digamos que o Bob faz um branch chamado hotfix a partir do commit 5 do branch master onde a Alice tá trabalhando. Agora a Alice continua trabalhando e faz os commits 6 e 7. E o Bob continua trabalhando no branch dele e faz um commit 8. No histórico do Bob o commit 5 pula direto pro commit 8. Mas pro Bob estar atualizado com o que a Alice anda trabalhando, de tempos em tempos ele pode fazer rebase da master</p>
<p>Quando ele faz rebase, o que o Git faz é mover os commits dele uma área temporária, então é como se ele rebobinasse o branch do Bob até onde ele foi criado, que é o commit 5. Daí o Git pega os commits 6 e 7 da Alice e adiciona no branch do Bob. Agora ele tem a sequência mais atualizada dos commits 5, 6 e 7. E finalmente o Git pega os commits que ele moveu pra área temporária, no caso o commit 8, e aplica a partir do 7. Agora o histórico do Bob está limpo e linear, 5, 6, 7 e 8. E quando ele terminar agora ele faz um merge, que vai gerar um commit 9 ligando com o commit 8 do Bob com o 7 da Alice. E pronto, ele pode apagar o branch temporário dele. Recapitulando, rebase é rebobinar seu branch de volta pro começo, aplicar os commits mais novos do branch original, e depois aplicar seus commits de volta por cima. Só isso.
== cortado - fim</p>
<p>Hoje em dia, se você só usou Git e ocasionalmente fez alguma besteira e deu alguns conflitos num merge, é só isso, um pequeno inconveniente, mas antes de ferramentas como Git, era o que eu falei antes, um verdadeiro inferno na Terra. Como anedota, quando eu era consultor Java e SAP, eu estava num cliente com minha equipe. Um dos desenvolvedores amigo meu fazia faculdade a noite. E ele era do tipo que se irritava com as coisas mesmo. Ele tava atrasado e deu um tanto de conflito quando ele deu um update ou commit. Ele tentava resolver os conflitos e não ia. No fim ele saiu atrasado pra faculdade a noite, puto da vida, e bateu o carro. Então, Subversion já fez alguém bater o carro. Não tô exagerando, era assim ruim.</p>
<p>Então, quando eu ouvi o Torvalds xingando o povo do Subversion, eu sabia exatamente do que ele tava falando. Sério, no marketing do Subversion onde eles falavam que era CVS feito direito, também tinha bastante propaganda de como criar branches era uma coisa simples no Subversion. Isso era totalmente irrelevante. De fato, criar branches num CVS era mais custoso. Mas o problema nunca foi criar branches, o problema sempre foi mergear os branches. E isso o Subversion levou 8 anos pra remendar. Depois dessa tech talk do Torvalds ainda levou mais de um ano pra adicionarem o maldito merge tracking. Mas nesse ponto já era tarde demais, eu já tinha abandonado Subversion e comecei a usar Git.</p>
<p>Mas tinha um problema. Ao meu redor, só eu queria usar Git, pra variar. Quando eu deixei de ser consultor SAP e virei freelancer de Ruby on Rails, os primeiros projetos que peguei ainda eram em Subversion. Felizmente tanto Mercurial quanto Git tinham tipo um adaptador. Era possível fazer checkout, baixar o código de um repositório Subversion, converter localmente em um repositório Git e trabalhar normalmente com Git. Depois que eu tivesse terminado de mergear meus branches e estivesse tudo pronto, dava pra dar commit de volta pro repositório Subversion. Então era possível começar a migrar minha forma de trabalhar mesmo se o cliente não quisesse mudar pra Git.</p>
<p>Essa é a outra vantagem de sistemas distribuídos. Num sistema centralizado, qualquer coisa que você precisa fazer, precisa pedir pro servidor. Então se eu estivesse offline, digamos num aeroporto, e eu quisesse fazer um commit pra não perder acidentalmente o que eu tava fazendo, não tinha como, porque em Subversion commit é o equivalente a commit e push do Git. Ele precisa enviar pro servidor. Mas offline, já era, meu fluxo de trabalho era interrompido. Compare com hoje em dia, 2 anos atrás eu realmente fui codando um projeto durante meu vôo pra Las Vegas, fiquei as 8 horas do vôo codando, comitando e fazendo tudo que eu precisava. Quando cheguei no aeroporto em Las Vegas eu conectei meu notebook no 4G e fiz um push pro repositório no meu GitLab.</p>
<p>Aliás, vale explicar outra coisa. Eu já disse antes que GitHub ou GitLab são opcionais pra usar Git. Quando você faz um git init como eu já mostrei, o diretório .git tem tudo que você precisa. Digamos que estamos na situação do aeroporto ou na situação de eu estar em alguma conferência sem internet. Daí um amigo meu se oferece pra me ajudar. Como ele clona o projeto de mim? De várias maneiras. Ele pode se conectar via SSH no meu notebook. Ele pode simplesmente pegar uma cópia do diretório pela rede local. Ou eu posso copiar o repositório num pendrive e dar pra ele clonar.</p>
<p>Muita gente não sabe disso mas quando você faz um git clone de um GitHub, a diferença pra um repositório criado localmente é só algumas linhas no arquivo de config no diretório .git apontando um remote chamado origin pra um endereço na internet, que pode ser um servidor https ou até mesmo um servidor SSH, ou nem precisa ser um servidor, pode ser um outro diretório local na mesma máquina. Isso mesmo, você pode fazer git clone de um diretório. Deixa eu mostrar isso, vamos pegar o repositório que criamos antes chamado test. Agora vamos sair do diretório. Agora podemos fazer git clone desse repositório e chamar o clone de test2. Pronto, clonado. Se olharmos o arquivo de config do test2 vemos uma configuração de remote origin apontando pro diretório em vez do GitHub por exemplo. E como remote é só um texto de configuração, podemos apontar pra onde a gente quiser depois.</p>
<p>Git e outros sistemas distribuídos tem como característica não precisar de um servidor central que controla tudo. Como hoje já tá comprido demais este episódio um dia eu retomo o assunto, mas basta dizer que a forma como todo mundo usa Git via GitHub hoje é só uma das formas de se trabalhar, que provavelmente funciona melhor pra maioria das pessoas, mas não é adequada pro fluxo de trabalho dos desenvolvedores do projeto da kernel do Linux, por exemplo, por isso eles não estão no GitHub apesar de usarem Git. Eu mesmo prefiro que terceiros não tenham acesso ao meu código fonte e nem dos meus clientes, por isso eu mantenho uma instalação privada de GitLab. Tudo funciona igualzinho ao GitHub se não melhor em muitos casos. E mesmo sem GitLab, pra casos menores, bastaria fazer o que eu já fazia dez anos atrás: dar git init num servidor de SSH e todos os meus amigos podem clonar de lá e a gente já pode trabalhar.</p>
<p>Uma das grandes barreiras que o Git enfrentou desde o começo, principalmente comparado com a concorrência, é que ele foi feito pra ser usado num terminal Linux. O Git é um conjunto de programas de linha de comando, que assume que está num ambiente POSIX. Portanto em Windows sempre foi bem chatinho de usar porque o command prompt sempre foi uma grande porcaria e o suporte POSIX sempre foi meia boca. O que uma década não faz? Se você assistiu meu vídeo sobre o Windows Subsystem for Linux, ou WSL, agora o Windows oferece um terminal decente e agora tá super fácil usar programas de linha de comando de Linux nativamente no Windows. Fora isso diversas IDEs e editores de texto incluindo o Visual Studio Code tem suporte decente de Git também. Então mesmo no Windows você consegue usar Git sem grandes problemas hoje.</p>
<p>Em 2008 quando eu fui trabalhar na Locaweb, assim como muitas outras empresas, eles usavam o que? O famigerado e odiado Microsoft SourceSafe. Eu, do meu jeito delicado de chegar chutando a porta, forcei a migração de todos os projetos e códigos fonte da empresa pra ficar num servidor Git. Na época GitHub ainda tava só começando, GitLab não existia, mas havia um projeto open source chamado Gitorious que dava suporte somente a hospedar projetos open source. Como o código era aberto, eu adicionei a funcionalidade de administração e gerenciamento de usuários e permissões. E foi isso que usamos por muito tempo. Depois que eu saí eles migraram pro GitLab que tinha acabado de lançar e é bem melhor.</p>
<p>Eu lembro que a rejeição ao Git era muito grande, especialmente por conta de suporte ruim a usar em Windows pelo command prompt. Eu literalmente fui sentando na mesa de cada desenvolvedor pra ensinar a usar os comandos e fluxos básicos. Foi uma puta dor de cabeça na época, só um louco pra me deixar fazer isso em 2008. Levou meses até todo mundo se acostumar, mas eu acho que valeu a pena.</p>
<p>Por causa de todas as limitações e a filosofia de servidor centralizado, em corporações antiquadas o mais comum ainda é esconder o máximo do código fonte quanto possível. Normalmente desenvolvedores de departamentos diferentes não tem acesso ao código de outros departamentos. Hoje em dia em ambientes modernos isso pode parecer um absurdo, mas esse era o normal. Como era fácil fazer merda, era melhor limitar o acesso o máximo possível.</p>
<p>Além de forçar a migração do SourceSafe pra Gitorious em 2008 eu também injetei o conceito de todo mundo ver tudo, todo código fonte fica aberto a todo funcionário e qualquer um pode criar branches e forks e colaborar via o equivalente a Pull Requests da época. É o modelo que eu acho que é o mais correto até hoje. Esconder código garante que código ruim sobreviva mais do que deveria. Além de criar politicagem desnecessária.</p>
<p>E estamos chegando ao fim deste episódio! Como eu falei no começo do vídeo, o objetivo de hoje não era fazer tutorial. O próprio Linus tem a mesma resposta que eu usaria:</p>
<p>(Linus feeling lucky)</p>
<p>O objetivo hoje era tentar dar um panorama de porque o Git existe, quais eram as alternativas, porque nenhuma outra foi bem sucedida e o que torna o Git tão diferente do resto. O próprio BitKeeper ainda existe e hoje em dia e integra com Git. A Microsoft ainda tem o Team Foundation Server ou TFS que pra todos os efeitos e propósitos é o sucessor espiritual do SourceSafe e eu espero nunca esbarrar em um. E o TFS também se não me engano tem alguma forma de interfacear com repositórios Git. Graças ao GitHub, rapidamente todo mundo migrou pra Git. Hoje em dia ninguém mais questiona.</p>
<p>Se você é da comunidade Ruby on Rails daquela época e se lembrar da primeira grande conferência no Brasil, a Rails Summit Latin America, vai lembrar que um dos palestrantes que eu trouxe foi o Chris Wanstrath, um dos fundadores do GitHub. Eu tinha entrevistado ele pro meu blog no ano anterior e conheci ele, o Tom e o Scott Chacon na Railsconf um ano antes. O Scott que eu também trouxe se não me engano na Rubyconf Brasil de 2010. Hoje todo mundo conhece o GitHub como a compra de mais de 7 bilhões de dólares pela Microsoft. Eu lembro do GitHub na época em que eles estavam codando o GitHub de algum Starbucks. Bons tempos.</p>
<p>Espero que vocês tenham aprendido um pouco mais sobre essa que é provavelmente a ferramenta mais básica pra qualquer desenvolvedor hoje em dia. Minha expectativa de vida como desenvolvedor melhorou substancialmente depois que eu migrei pra Git em 2007. Se tiverem interesse de ver o que eu falava sobre o assunto vou deixar linkado os posts do meu blog que postei naquela época também. Se curtiram o video deixem um joinha, assinem o canal e não deixem de clicar no sininho pra não perder os próximos episódios. Agora eu acho que devo voltar a publicar toda semana se nada der errado, então a gente se vê, até mais!</p>
]]></content:encoded><category>controle-de-versao</category><category>ciencia-da-computacao</category><category>akitando</category></item><item><title>[Akitando] #38 - Conhecimentos Básicos para Iniciantes em Programação | Série "Começando aos 40"</title><link>https://akitaonrails.github.io/2019/02/06/akitando-38-conhecimentos-basicos-para-iniciantes-em-programacao-serie-comecando-aos-40/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/02/06/akitando-38-conhecimentos-basicos-para-iniciantes-em-programacao-serie-comecando-aos-40/</guid><pubDate>Wed, 06 Feb 2019 17:00:00 -0200</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/sx4hAHhO9CY"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Neste terceiro episódio da série &amp;ldquo;Começando aos 40&amp;rdquo; vou finalmente começar a tocar em assuntos mais práticos, pra começar o que eu considero que é o absolutamente básico que você precisa começar a dominar o quanto antes.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/sx4hAHhO9CY"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Neste terceiro episódio da série &ldquo;Começando aos 40&rdquo; vou finalmente começar a tocar em assuntos mais práticos, pra começar o que eu considero que é o absolutamente básico que você precisa começar a dominar o quanto antes.</p>
<p>Neste e nos próximos 2 episódios, pelo menos, vou dar uma visão mais estruturada do que considero que a carreira de &ldquo;desenvolvedor Web&rdquo; envolve para que você tenha uma idéia &lsquo;macro&rsquo; do que deve planejar para estudar e treinar.</p>
<p>Agora é a hora de arregaçar as mangas e treinar!</p>
<p>Links:</p>
<ul>
<li>Developer Roadmap (<a href="https://github.com/kamranahmedse/developer-roadmap"target="_blank" rel="noopener">https://github.com/kamranahmedse/developer-roadmap</a>)</li>
<li>git-remote-dropbox (<a href="https://github.com/anishathalye/git-remote-dropbox"target="_blank" rel="noopener">https://github.com/anishathalye/git-remote-dropbox</a>)</li>
<li>Building a Continuous Delivery Pipeline with Git &amp; Jenkins (<a href="https://stackify.com/continuous-delivery-git-jenkins/"target="_blank" rel="noopener">https://stackify.com/continuous-delivery-git-jenkins/</a>)</li>
<li>A beginner&rsquo;s guide to continuous integration (<a href="https://about.gitlab.com/2018/01/22/a-beginners-guide-to-continuous-integration/"target="_blank" rel="noopener">https://about.gitlab.com/2018/01/22/a-beginners-guide-to-continuous-integration/</a>)</li>
<li>TodoMVC (<a href="http://todomvc.com/"target="_blank" rel="noopener">http://todomvc.com/</a>)</li>
<li>Arch Linux (<a href="https://www.archlinux.org/"target="_blank" rel="noopener">https://www.archlinux.org/</a>)</li>
</ul>
<p>AkitaOnRails Blog:</p>
<ul>
<li>Jogar Pedra em Gato Morto: por que Subversion não presta (<a href="https://www.akitaonrails.com/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta"target="_blank" rel="noopener">https://www.akitaonrails.com/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta</a>)</li>
<li>GIT: Muito Promissor (<a href="https://www.akitaonrails.com/2007/09/22/git-muito-promissor"target="_blank" rel="noopener">https://www.akitaonrails.com/2007/09/22/git-muito-promissor</a>)</li>
<li>Micro Tutorial de Git (<a href="https://www.akitaonrails.com/2008/04/02/micro-tutorial-de-git"target="_blank" rel="noopener">https://www.akitaonrails.com/2008/04/02/micro-tutorial-de-git</a>)</li>
<li>Dicas de Git (<a href="https://www.akitaonrails.com/2009/07/05/dicas-de-git"target="_blank" rel="noopener">https://www.akitaonrails.com/2009/07/05/dicas-de-git</a>)</li>
<li>[Screencast] Começando com Git (<a href="https://www.akitaonrails.com/2010/08/17/screencast-comecando-com-git"target="_blank" rel="noopener">https://www.akitaonrails.com/2010/08/17/screencast-comecando-com-git</a>)</li>
<li>Chatting with Chris Wanstrath (Err the Blog/Github) (<a href="http://www.akitaonrails.com/2008/04/21/conversando-com-chris-wanstrath-err-the-blog-github"target="_blank" rel="noopener">http://www.akitaonrails.com/2008/04/21/conversando-com-chris-wanstrath-err-the-blog-github</a>)</li>
<li>Arch Linux - Best distro ever? (<a href="http://www.akitaonrails.com/2017/01/10/arch-linux-a-melhor-distro-de-todas"target="_blank" rel="noopener">http://www.akitaonrails.com/2017/01/10/arch-linux-a-melhor-distro-de-todas</a>)</li>
<li>Optimizing Linux for Slow Computers (<a href="https://www.akitaonrails.com/2017/01/17/otimizando-o-linux-para-computadores-lentos"target="_blank" rel="noopener">https://www.akitaonrails.com/2017/01/17/otimizando-o-linux-para-computadores-lentos</a>)</li>
<li>From the Macbook Pro to the Dell XPS. Arch Linux for Creative Pro Users (<a href="http://www.akitaonrails.com/2017/01/31/do-macbook-pro-para-o-dell-xps-arch-linux-para-usuarios-criativos"target="_blank" rel="noopener">http://www.akitaonrails.com/2017/01/31/do-macbook-pro-para-o-dell-xps-arch-linux-para-usuarios-criativos</a>)</li>
<li>Running Arch Linux over Windows 10! (<a href="http://www.akitaonrails.com/2018/04/29/rodando-arch-linux-sobre-o-windows-10"target="_blank" rel="noopener">http://www.akitaonrails.com/2018/04/29/rodando-arch-linux-sobre-o-windows-10</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Neste terceiro episódio da série “Começando aos 40” vamos finalmente começar a falar sobre o currículo básico pra quem quer se tornar um desenvolvedor Web. Veja que não existe somente Web, você pode escolher desenvolvimento mobile para fazer apps para smartphones ou mesmo algo mais tradicional como aplicativos nativos Desktop</p>
<p>Todo mundo que tá iniciando tem que começar do zero, um pouco de HTML, um pouco de CSS, um pouco de Javascript. Quem está fazendo algum curso de tecnólogo começa com um pouco de Java, talvez um pouco de C# ou PHP.</p>
<p>Mas praticamente todos esses cursos falham em mostrar TUDO que você realmente vai precisar pra montar uma carreira. Claro, como eu já disse antes, sabendo um pouco do básico, você já começa a ser capaz de começar a trabalhar. E a importância de trabalhar numa empresa com uma equipe é porque justamente você ainda não vai ser capaz de fazer um projeto do começo ao fim sozinho, simplesmente porque em 1 ou 2 anos é impossível saber tudo que se precisa.</p>
<p>E o que exatamente é esse TUDO que se precisa? Eu disse que essa série vai ser bem longa. No episódio de HOJE vamos começar com os conhecimentos mais básicos que você vai precisar. Vamos lá.</p>
<p>(&hellip;)</p>
<p>Um ou dois anos atrás eu esbarrei num repositório no GitHub chamado Developer Roadmap, ele tem sido atualizado uma vez por ano e a versão 2019 está lá. O autor por acaso é de uma empresa de cursos chamada Hackr.io que eu nunca parei pra ver então não sei dizer se os cursos são bons ou não. Esse roadmap é um mindmap, que é uma representação gráfica de idéias em conceitos, na prática é um monte de caixinhas ligadas num gráfico. O que eu achei interessante é que ele lista caixinhas obrigatórias pra cada carreira e caixinhas opcionais mas desejáveis e, no geral, eu concordo com as divisões e prioridades que ele colocou, então é uma boa referência pra vocês.</p>
<p>E note que eu disse que ele atualiza todo ano. Nos últimos 2 anos não mudou tanto, mas se ele tivesse começado 5 ou 10 anos atrás, vocês iriam ver que a carreira mudou bastante nesse período. Lembra no episódio anterior que eu falo sobre o tempo? Pois é, considere que esse roadmap pode mudar muito ainda daqui 2 anos ou mais. Se você for aprender TUDO que está nesse roadmap, se estudar e treinar com afinco, vai precisar de uns 3 a 5 anos, no mínimo. E eu diria que esse roadmap é básico, tem muito mais além disso ainda. Mas não se assuste, você não precisa saber tudo de uma vez só, mas tenha na cabeça que nossa carreira vai exigir que você esteja estudando constantemente. Não existe fazer um curso de 6 meses e achar que já sai tudo.</p>
<p>Este roadmap se divide em QUATRO partes. Primeiro, e o tema deste episódio, são conhecimentos gerais que todo mundo precisa saber. Daí ele separa em carreira de desenvolvedor Front-end que se preocupa mais com a parte visual que as pessoas normalmente vêem, o HTML e CSS final, Javascript e tudo que envolve usabilidade. Então você tem Back-end que é a carreira mais complexa em termos de coisas pra aprender e se preocupa com o software que roda por trás, as regras de negócio, as integrações, os bancos de dados. Finalmente uma carreira nova que chamamos erroneamente de Devops, vou explicar porque em outro episódio. E como eu disse antes, essas não são as únicas carreiras, só as primeiras TRES dentro do mundo de Web.</p>
<p>A idéia é você usar esse Roadmap como um checklist. Cada caixinha amarela é um corpo de conhecimentos grande. Você vai encontrar cursos inteiros pra cada caixinha. Mas lembre-se do que eu já falei no meu episódio sobre Faculdade: você não deve tentar aprender 100% de cada assunto de uma só vez. Aprenda no máximo 20%.
Os primeiros 20% que vai te permitir resolver 80% dos problemas. Vou dar um exemplo. Na primeira parte, de conhecimentos gerais, a primeira caixinha é GIT.</p>
<p>GIT é um versionador de arquivos. Essa ferramenta foi desenvolvida inicialmente pelo criador do kernel do Linux, o famoso Linus Torvalds e o atual mantenedor é Junio Hamano. Pense assim: como centenas de desenvolvedores podem editar os mesmos códigos num projeto como o Linux e não virar uma bagunça colossal? É por causa de ferramentas como o GIT.</p>
<p>Nos anos 90 e começo dos 2000 haviam várias, como CVS que era uma grande porcaria mas era o que tinha, substituído pelo Subversion ou SVN que era menos porcaria e eu usei muito e inclusive fiz um patch pra ferramenta TortoiseSVN pra ele funcionar em Windows em projetos com Visual Studio que não suportava diretórios que começa com ponto. Haviam ferramentas comerciais bem porcaria como Rational Clearcase ou a droga do Microsoft SourceSafe. Eram todos versionadores centralizados, e daí nos anos 2000 começamos a ir para versionadores distribuídos como BitKeeper, Perforce, Darcs, Bazaar que era usado pela Canonical, Mercurial.</p>
<p>Eu comecei a evangelizar Git no Brasil por volta de 2007 como você pode ver em meus blog posts mais antigos linkados na descrição abaixo. E nós da comunidade Ruby on Rails ajudamos muito o GitHub - que é feito em Ruby on Rails - a crescer rapidamente. Por causa disso GIT se tornou o padrão embora em ambientes comerciais alguns ainda fiquem presos a ferramentas como o Microsoft Team Services. Mas esquece, 99% do mundo de desenvolvimento usa GIT pelo simples fato que a maior parte dos melhores software open source do mundo estarem lá no GITHUB.</p>
<p>O GIT em si possui centenas de comandos e permutações. Você usa GIT pra baixar código dos repositórios preservando um histórico de “commits” que são trechos de alteração de código. Você pode navegar pra trás no tempo num sistema desses e recuperar o estado do código como ele era ontem ou semana passada ou ano passado. E cada modificação que você faz você empacota nesses “commits” e empurra (ou dá push) pra um repositório elegido como central que todos usam, embora GIT em si não precise de um servidor central. O fluxo de trabalho com GIT que a maioria dos desenvolvedores Web usam é derivado do que nós da comunidade Rails desenvolvemos junto com o GitHub, o mecanismo que chamamos de Pull Requests e Forks. A comunidade do Kernel do Linux usa um fluxo de trabalho diferente por exemplo.</p>
<p>Agora, os tais 20% que eu falei antes? Você precisa saber talvez uma dúzia só de comandos como “git init” pra iniciar um repositório, “git add” pra acumular as modificações que fez agora, “git commit” pra empacotar essas modificações, “git push” pra enviar as modificações pra outro repositório de origem, “git pull” pra puxar as últimas modificações desse repositório.
Se você dominar esses comandos já consegue participar de um projeto. Só que isso é o ultra básico, porque existem vários coisas importantes que você vai precisar aprender como o que são branches e como trocar de branches, como fazer cherrypicking, como reescrever commits, as diferenças entre rebasing e merge, o que é stage, pra que serve squash de commits, pra que serve bisect, pra que serve reflog, diferenças entre reset e clean, e assim por diante.</p>
<p>Mas você pode ir aprendendo um comando de cada vez. O Git é praticamente um sistema de arquivos avançado, sua estrutura de dados é baseado num DAG que é um Direct Acyclic Graph e se você entender como ele funciona internamente você pode usar o GIT pra fazer coisas avançadas. Por exemplo, você poderia criar um produto que é um mini-clone do Dropbox usando GIT. Você pode integrar o GIT com sistemas de deployment, ou seja, que toda vez que você dá push no repositório ele toma uma ação como atualizar o servidor ou rodar os testes do repositório num sistema de Continuous Integration. As opções são infinitas.</p>
<p>Mas atenha-se em aprender os primeiros 20%, a primeira uma dúzia de comandos. Não se preocupe, se você for curioso, você vai aprender tudo do GIT eventualmente. Mas se tentar aprender TUDO de uma só vez, você vai ficar meses estudando a teoria e não vai entender tudo. Gaste uma semana em tutoriais iniciais do GIT. O próprio GitHub tem o site Github Guides que vai te dar o básico. E obviamente treine em dezenas de arquivos na sua própria máquina. Erre dezenas de vezes pra ver o comportamento de cada comando, teste os limites de cada comando. Mas de onde vamos arranjar esses arquivos e projetos pra você treinar?</p>
<p>O Roadmap está correto em colocar GIT em primeiro lugar porque uma das coisas que você logo no começo PRECISA fazer é fuçar MUITO o GitHub. Agora, entenda uma GRANDE verdade de qualquer curso ou tutorial: TODOS os passos que eles mostram SEMPRE funcionam, porque a demonstração foi preparada pra funcionar. Começa com algum exercício do tipo, “vamos fazer um aplicativo de lista de tarefas” daí ele vai te dando passo a passo tudo na ordem certa pra no final você ter um pequeno aplicativo. Mas os passos só funcionam dentro de um ambiente muito controlado, quase de laboratório, se divergir um pouquinho vai dar errado e você não vai saber o que tem que fazer.</p>
<p>Pra quem só está iniciando, claro que é importante ver as coisas sendo construídas de forma ordenada. Só entenda que isso é irreal. Todo tutorial e curso não tem absolutamente nada a ver com a vida real. Na vida real você quase sempre vai participar de um projeto que já existe, ou começar com outras pessoas, e vai baixar código que já existe e ter que participar do código dos outros. E esse código não vai estar perfeito, aliás, normalmente vai estar MUITO longe de perfeito, as coisas vão quebrar, as instruções não vão funcionar, e você vai ficar em dúvida se é o código do projeto que tá quebrado ou você que não tá entendendo alguma coisa trivial.</p>
<p>Muito do processo de programação é tentativa e erro. Um dos grandes defeitos de cursos e tutoriais é que eles mostram só o passo a passo perfeito. Tutoriais não ensinam a lidar com erros e isso é um enorme problema. Eu mesmo cansei de receber mensagens de pessoas que falam “Akita, tava seguindo esse tutorial e deu um erro, você sabe como resolve?”
Daí eu falo “me manda a mensagem de erro que tá aparecendo aí”. Daí eu pego essa mensagem, jogo no Google, e mando o primeiro link que aparece e pergunto “e aí, era isso?”, aí a pessoa responde “puuuuuta Akita você é foda mesmo, resolveu!”  …. Aí eu fico …
“filho da mãe, eu virei proxy do Google”!!</p>
<p>Sério, se você é iniciante, assuma que 99% de todos os erros que você encontrar, você certamente não é o primeiro. Muita gente já passou pelo mesmo erro que você tá passando e eles já foram documentados nas Issues do GitHub, ou em sites como Stackoverflow, Stackexchange, fóruns como Reddit. Antigamente, nos anos 80 e começo dos 90, a gente simplesmente não tinha onde procurar online, porque nem tinha internet!! Então quando tinha um erro, a gente precisava aprender a descobrir tudo sozinho. Por isso o povo dependia de empresas como Microsoft, BEA, IBM ou outros que costumavam ter uma comunidade interna somente pra quem pagava a assinatura e você tinha acesso a bancos de conhecimento que vinham em CDs como o MSDN. Mas hoje em dia, o Google vai te responder 90% das coisas que você vai precisar.</p>
<p>Isso tudo dito, em vez de ficar só fazendo passo a passo de vários tutoriais, vá no GitHub e procure alguma coisa como “clone instagram javascript”, vai aparecer vários. Alguns muito parecidos, alguns uma porcaria. Não importa. E agora que você já aprendeu o básico de Git, clone o projeto na sua máquina e tente rodar. Você vai sofrer um bocado da primeira vez e vai cair na segunda caixinha amarela do Roadmap que diz “Basic Terminal Usage”.</p>
<p>Essa caixinha junto com outras como “SSH” vai te dar MUITO trabalho. Entenda outra verdade: quando você for subir seu primeiro projeto de verdade para o público, ele não vai estar na sua máquina, vai estar num servidor. Seja você um programador front-end ou back-end você PRECISA o quanto antes aprender mais sobre sistemas operacionais.</p>
<p>A maioria de vocês provavelmente está usando Windows. E não tem nenhum problema. Eu uso Windows também. Porém você obrigatoriamente deve aprender sobre distribuições baseadas em Linux. Você pode fazer isso do jeito difícil ou do jeito ultra difícil. O jeito difícil, se sua máquina aguentar é baixar e usar o programa Virtualbox ou versões pagas como VMWare ou Parallels.</p>
<p>Hoje em dia se tornou muito comum rodar Linux em máquinas virtuais. Todo novo CPU suporta o que chamamos de instruções VT-X no caso da Intel ou AMD-V no caso da AMD que são funções da CPU para fornecer o máximo de performance de acesso ao hardware quanto possível em ambientes virtualizados. Significa que você consegue rodar uma distribuição de Linux como Ubuntu, Fedora ou Arch inteira dentro de um Virtualbox.</p>
<p>A maioria das pessoas nunca instala seu próprio sistema operacional, nem configura nada. Simplesmente compra a máquina e usa do jeito que veio. Isso é horrível. Um programador escreve software. Software é código que diz à máquina o que fazer. A máquina é controlada por um sistema operacional. Se você não conhece o sistema operacional, sua programação sempre vai ter muito achismo e chutes. Você acaba se tornando supersticioso e em vez de resolver os problemas procura alguma mandinga.</p>
<p>É impossível aprender tudo só em poucos dias ou semanas, por isso o melhor caminho pra começar é de cara tirar a rodinha da bicicleta e começar a apanhar. Não comece com uma distribuição fácil como Mint ou Ubuntu. Comece instalando um sistema operacional manualmente do zero e pra isso não há nada melhor do que uma distribuição Linux como o Arch Linux, que me lembra um pouco minha época de Slackware 1.0 no meio dos anos 90.</p>
<p>Se você ainda não sabe disso, Linux é o nome do kernel, o binário responsável por iniciar a máquina, configurar os dispositivos e oferecer serviços pros aplicativos podem falar com a máquina. Existem dezenas de distribuições que usam o kernel Linux. O que varia em cada uma é a seleção de aplicativos, formas de configuração, gerenciamento de pacotes e ecossistema. Na dúvida você vai acabar escolhendo algo como Ubuntu, Mint, Elementary, Fedora, CentOS, openSUSE, Manjaro, dentre outros.</p>
<p>Um Ubuntu ou mesmo Fedora são muito simples, eles já fazem tudo pra você e no final é como se tivesse instalado um Windows ou MacOS, você vai só apertando “próximo”, “próximo” e no final não aprendeu nada porque os instaladores estão bons o suficiente pra fazer quase tudo sozinhos. Mas o Arch Linux não, ele vai exigir que você realmente preste atenção, estude e vá configurando cada componente sozinho. A cada passo você não vai saber o que precisa fazer e pra ajudar existe o site ArchWiki que tem páginas inteiras detalhando cada pequeno componente do sistema em detalhes.</p>
<p>O Arch vai te forçar a ficar na linha de comando e digitar comandos que, mesmo que você não entenda exatamente o que está fazendo, vai te dar a primeira sensação de que você está colocando as mãos de verdade no computador. Vai ver dezenas de mensagens de erro e vai perder horas em tentativa e erro a cada passo, e quando chegar ao final vai ter aquela satisfação de que você fez alguma coisa funcionar sozinho.</p>
<p>Quando eu falo que as pessoas ficam supersticiosas imagina quando alguma coisa não funciona, e o que é a primeira coisa que todo mundo pensa? Ahh reboota a máquina que volta a funcionar. Nãaaao porque você vai rebootar? Primeiro tenta entender se precisa rebootar. Normalmente é quando você muda alguma configuração que algum serviço precisa, um daemon por exemplo.</p>
<p>Falando em daemons você vai precisar entender que você sempre tem pelo menos 2 níveis de permissão no sistema, o acesso geral de administrador ou root e o mais limitado, do seu usuário. Um Spotify roda sob os privilégios do seu usuário e não tem privilégios pra afetar coisas importantes do núcleo do sistema, como rebootar a máquina. Mas existem serviços ou daemons - e nao DA-E-MONS daemons, que aliás, não tem nada a ver com diabo mas com antigos demônios que performam tarefas em background -, que o sistema operacional começa a iniciar depois do boot e rodam sob privilégios maiores pra poder controlar coisas como sua rede, seu sistema de arquivos geral, tudo em background sem você perceber.</p>
<p>Um programa que você manualmente abriu como seu editor de textos você pode matar a qualquer momento. Mas daemons, precisam de privilégios pra reiniciar, e aí você começa a aprender sobre ferramentas como o “sudo” que você vai ver em vários tutoriais, que permitem que seu usuário temporariamente tenha privilégios de administrador. No Windows isso é aquela janela de confirmação que escurece sua tela toda pedindo uma confirmação e que obviamente você nunca lê e só dá ok.</p>
<p>Mais do que isso, pra rodar linguagens como Javascript, Ruby, Python, PHP, Java, você precisa saber quais versões instalar porque códigos antigos podem precisar de versões mais antigas de cada linguagem. Se tentar rodar na mais nova provavelmente vai falhar. Se você tem o Python mais novo instalado na sua máquina como a versão 3, mas você entra numa empresa que precisa dar manutenção num projeto mais antigo que ainda não foi atualizado, usando digamos Python 2.6, como você faz pra ter múltiplas versões rodando na máquina ao mesmo tempo? Você vai ter que aprender que quando digita o comando “python” na linha de comando ele vai olhar uma coisa chamada PATH, uma variável de ambiente, que diz em quais diretórios procurar pelo executável. Se quiser ter múltiplas versões, basta instalar algum gerenciador dessas variáveis VIRTUALENV ou o mais poderoso ASDF que vai gerenciar o PATH e outras variáveis do ambiente pra você. Mas pra isso você precisa ter essa noção que cada linguagem é um conjunto de executáveis, coisas como interpretador, compilador, debugger, bibliotecas. Onde eles ficam na sua máquina? Como você, como programador, não sabe onde fica cada componente da linguagem que está usando? Você PRECISA saber, então corre atrás disso.</p>
<p>Portanto, as duas coisas mais importantes que você PRECISA aprender e treinar logo de cara é como gerenciar código usando ferramentas como GIT e serviços como GitHub e onde fica cada ferramenta que você vai usar no sistema operacional. Você precisa aprender a ter controle sobre sua própria máquina, porque a principal função de um programador é controlar as máquinas para que façam exatamente o que você quer, e a forma de dar essas ordems é através de programas gerados com o código que você escreve. Se você não consegue controlar seu sistema operacional e sequer sabe onde estão suas ferramentas e porque elas funcionam, é a máquina que vai controlar você e não o contrário. Sem isso você sempre vai fazer algum código, muitas vezes vai chutar e se realmente funciona você continua sem saber porque, e desse jeito nunca vai evoluir como programador. Coloque isso na sua cabeça, a máquina serve a você e não o contrário. Domine suas ferramentas e faça com que elas sirvam a você.</p>
<p>E como podem ver, hoje falamos somente das duas primeiras caixinhas. Durante a série algumas serão mais curtas, algumas serão mais longas, mas o assunto claramente é longo. Aproveitem pra dar uma olhada no Developer Roadmap e já joguem dúvidas nos comentários abaixo, talvez eu possa aproveitar algumas nos próximos episódios. Se curtiram o vídeo deixem um joinha, compartilhem com seus amigos, assinem o canal e cliquem no sininho. A gente se vê semana que vem, até mais!</p>
]]></content:encoded><category>aprendizado</category><category>controle-de-versao</category><category>linux</category><category>akitando</category></item><item><title>3 Meses de GitLab. A Era do SaaS Híbrido</title><link>https://akitaonrails.github.io/2016/11/16/3-meses-de-gitlab-a-era-do-saas-hibrido/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/11/16/3-meses-de-gitlab-a-era-do-saas-hibrido/</guid><pubDate>Wed, 16 Nov 2016 16:44:00 -0200</pubDate><description>&lt;p&gt;Se você ainda não leu, talvez seja uma boa ideia dar uma olhada no meu post anterior &lt;a href="https://about.gitlab.com/2016/08/04/moving-to-gitlab-yes-its-worth-it/"target="_blank" rel="noopener"&gt;Moving to GitLab! Yes, it&amp;rsquo;s worth it!&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Minhas impressões vêm de circunstâncias bem específicas, então isso aqui não deve ser lido como recomendação geral para qualquer situação. Por exemplo, eu não avaliei as opções hospedadas do GitLab, que podem ser mais compatíveis com cenários diferentes do meu.&lt;/p&gt;
&lt;p&gt;No meu caso, a mudança para o GitLab fez parte de uma estratégia interna da empresa para ter mais controle sobre nossos próprios dados. Nessa mesma estratégia também migramos do Slack para o Mattermost, e do Pivotal Tracker para uma alternativa open source que eu mesmo mantenho, chamada &lt;a href="https://github.com/Codeminer42/cm42-central"target="_blank" rel="noopener"&gt;Central&lt;/a&gt;, entre outras coisas.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Se você ainda não leu, talvez seja uma boa ideia dar uma olhada no meu post anterior <a href="https://about.gitlab.com/2016/08/04/moving-to-gitlab-yes-its-worth-it/"target="_blank" rel="noopener">Moving to GitLab! Yes, it&rsquo;s worth it!</a>.</p>
<p>Minhas impressões vêm de circunstâncias bem específicas, então isso aqui não deve ser lido como recomendação geral para qualquer situação. Por exemplo, eu não avaliei as opções hospedadas do GitLab, que podem ser mais compatíveis com cenários diferentes do meu.</p>
<p>No meu caso, a mudança para o GitLab fez parte de uma estratégia interna da empresa para ter mais controle sobre nossos próprios dados. Nessa mesma estratégia também migramos do Slack para o Mattermost, e do Pivotal Tracker para uma alternativa open source que eu mesmo mantenho, chamada <a href="https://github.com/Codeminer42/cm42-central"target="_blank" rel="noopener">Central</a>, entre outras coisas.</p>
<p>Antes da migração, eu tinha dezenas de projetos ativos no GitHub e uma porção de projetos arquivados no Bitbucket. Sou do tipo que odeia perder dado, então mantenho backups redundantes e tento nunca apagar nada.</p>
<p>Movi praticamente tudo para meu próprio servidor GitLab e isso dá quase <strong>200 repositórios</strong>, espalhados em 4 grupos, com cerca de 80 usuários ativos. Isso representa mais de 13.500 notas, mais de 5.200 Merge Requests, mais de 2.500 builds no CI, e o Sidekiq interno que controla tudo isso já processou mais de 51.000 jobs.</p>
<p>Nos últimos 3 meses desde a migração, venho pagando uma conta de Digital Ocean em torno de $140 por mês pela infraestrutura exata que descrevi no artigo anterior, com máquinas separadas para o GitLab Core, o CI Runner, o CI Docker Registry Mirror, o servidor de cache do CI e máquinas descartáveis para builds paralelos, controladas pelo Runner.</p>
<p>A redução de custo é bem-vinda, claro, mas não foi por causa dela que fui atrás dessa empreitada toda. Eu pagaria tranquilamente o dobro ou o triplo só pela conveniência, só que a estratégia por trás é o que mais importa nesse caso.</p>
<p>Minha empresa tem mais de 60 desenvolvedores, trabalhando a partir dos nossos próprios escritórios em 6 cidades diferentes no Brasil (sem freelancers e sem home-office), se comunicando diariamente pelo Mattermost, fazendo Code Review via Merge Requests do GitLab, recebendo feedback automatizado imediato dos testes pelo GitLab CI, e gerenciando projetos através da nossa própria aplicação Central.</p>
<p>A única peça que ainda está faltando é uma boa ferramenta de análise estática de código. A gente depende bastante do Code Climate, que na nossa experiência é uma das melhores opções que existem hoje.</p>
<p>Eu estava prestes a começar uma empreitada para construir uma versão OSS simplificada para integrar com o GitLab CI, mas o <a href="http://blog.codeclimate.com/blog/2016/10/06/series-a-and-community-edition/"target="_blank" rel="noopener">Bryan Helmkamp anunciou recentemente</a> que eles vão lançar a própria &ldquo;Community Edition&rdquo; do Code Climate nos próximos meses.</p>
<p>Essa é exatamente a peça que faltava no meu stack, e vai acelerar minha estratégia para o ano que vem.</p>
<h3>A Era do SaaS Híbrido<span class="hx:absolute hx:-mt-20" id="a-era-do-saas-híbrido"></span>
    <a href="#a-era-do-saas-h%c3%adbrido" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Nos últimos 5 a 10 anos houve um movimento rápido em direção a uma estrutura de &ldquo;micro-serviços&rdquo; de terceiros. E isso é realmente ótimo, porque permitiu que muitas empresas pequenas ou até desenvolvedores independentes tivessem acesso a tecnologias que os fizeram andar mais rápido e com cada vez mais qualidade na entrega.</p>
<p>Hoje é super fácil ter um serviço de upload e storage de primeira linha com ferramentas como Cloudinary.</p>
<p>Também é muito fácil ter um banco relacional de ponta, com replicação e escalabilidade, como Heroku Postgres ou AWS RDS.</p>
<p>Gerenciamento de projetos como serviço com Pivotal Tracker, Trello.</p>
<p>Comunicação como serviço com Slack, Hangout, etc.</p>
<p>Gestão de conhecimento como serviço com GitHub, Bitbucket, etc.</p>
<p>Então hoje muitas empresas de tecnologia são um mashup de vários serviços de terceiros diferentes.</p>
<p>Minha pequena implicância é que todo o nosso conhecimento, experiência, portfólio, fica espalhado por uma dúzia ou mais de serviços opacos, completamente fora do nosso controle e dependendo do capricho de cada empresa (ou dos investidores delas).</p>
<p>Não me entenda mal, sou a favor de usar esses serviços, pelo contrário. Uso vários e vou continuar usando muitos deles no futuro previsível. Algumas empreitadas nem seriam possíveis sem a eficiência desse pedaço de um mercado livre baseado em tecnologia.</p>
<p>Só que de vez em quando você chega num ponto de virada onde passa a ser importante ter mais controle sobre seus próprios dados, sua própria identidade. Tanto pela posse em si quanto pela possibilidade de usar esses dados com mais intenção.</p>
<p>E acredito que, chegado esse ponto, a gente deveria ter uma opção que não custasse um rim e um pulmão pra replicar.</p>
<p>Antes de uma opção como o GitLab, estávamos limitados a escolher entre jardins murados como GitHub ou Bitbucket, ou então investir uma tonelada de recursos tentando juntar pequenos componentes open source para construir o seu próprio. Com o GitLab agora temos a opção de fazer uma transição suave para a versão hospedada, sem precisar encarar manutenção de infraestrutura, ou podemos optar por ter controle total. E os usuários não sofrem no processo.</p>
<p>Por isso acredito que o melhor SaaS para sobreviver à próxima década vai começar a entrar no <strong>modo Híbrido</strong>: ter uma opção comercial, hospedada, geralmente &ldquo;mais barata&rdquo;, e uma versão OSS DIY (do-it-yourself).</p>
<p>GitLab é assim. Mattermost é assim. Agora o Code Climate está ficando assim.</p>
<p>Nessa opção, empresas como o GitLab ficam numa situação ganha-ganha. Muito mais gente e empresas podem contribuir para fazer uma plataforma madura e robusta que todo mundo gosta de usar, e ao mesmo tempo cada participante pode estender a ferramenta para seus próprios planos particulares. O GitLab consegue ter um modelo de negócio sustentável atendendo a cauda longa de empresas e desenvolvedores que querem um serviço hospedado razoavelmente acessível, enquanto os 20% do topo podem perseguir empreitadas mais específicas usando a mesma tecnologia.</p>
<p>Dessa forma tiramos a discussão de confidencialidade, do jeito como as empresas tratam nossos dados, e avançamos para ações mais produtivas como construir uma ferramenta que beneficia tanto o público externo quanto os planos internos de cada um.</p>
<p>Jardins murados vieram para ficar. Liberar integração - ainda que opaca - via APIs públicas não é suficiente. É muito animador ver essas novas opções surgindo para preencher essa lacuna. O ambiente OSS junto de um modelo de negócio sustentável faz sentido. E espero continuar vendo cada vez mais concorrentes de serviços fechados seguindo o modelo Híbrido num futuro próximo.</p>
<p>Por que dar o trabalho de instalar, manter e ajustar alternativas open source para serviços hospedados (ainda que opacos) bem estabelecidos? Acredito que seja uma discussão maior do que conveniência, manutenção e corte de custos. A gente deveria começar pequeno, com menos risco, mas à medida que cresce deveria conseguir retomar o controle. Só que normalmente não dá para sair de um jardim murado sem um investimento significativo - e às vezes impossível - em reinventar a roda. Sempre tivemos os pequenos componentes open source sobre os quais esses serviços são construídos, mas ir desses pequenos componentes até um sistema completo é um caminho irrealista.</p>
<p>Com essa tendência possível de pós-SaaS, ou SaaS Híbrido como eu chamo, talvez a gente tenha acabado de ganhar o elo que faltava para ir da mera conveniência ao controle total sem o abismo inerente de custos e riscos.</p>
]]></content:encoded><category>controle-de-versao</category><category>open-source</category><category>negocios</category></item><item><title>Migrando para o GitLab! Sim, vale a pena!</title><link>https://akitaonrails.github.io/2016/08/03/migrando-para-o-gitlab-sim-vale-a-pena/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/08/03/migrando-para-o-gitlab-sim-vale-a-pena/</guid><pubDate>Wed, 03 Aug 2016 14:49:00 -0300</pubDate><description>&lt;p&gt;Eu &lt;a href="https://akitaonrails.github.io/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta/"&gt;comecei a evangelizar Git em 2007&lt;/a&gt;. Foi uma venda dificílima de fazer naquela época.&lt;/p&gt;
&lt;p&gt;Fora do desenvolvimento do kernel quase ninguém queria aprender e tínhamos concorrentes bem dignos, de Subversion, Mercurial, Bazaar, Darcs, Perforce, e por aí vai. Mas quem cavou mais fundo sabia que o Git tinha a vantagem e era só uma questão de tempo.&lt;/p&gt;
&lt;p&gt;Aí o GitHub apareceu em 2008 e o resto é história. Por muitos anos era só &amp;ldquo;bacana&amp;rdquo; estar no GitHub. A comunidade Ruby impulsionou o GitHub lá pra cima. No fim virou o status quo e o único monopólio real em repositórios de informação, não só código-fonte, mas tudo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eu <a href="/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta/">comecei a evangelizar Git em 2007</a>. Foi uma venda dificílima de fazer naquela época.</p>
<p>Fora do desenvolvimento do kernel quase ninguém queria aprender e tínhamos concorrentes bem dignos, de Subversion, Mercurial, Bazaar, Darcs, Perforce, e por aí vai. Mas quem cavou mais fundo sabia que o Git tinha a vantagem e era só uma questão de tempo.</p>
<p>Aí o GitHub apareceu em 2008 e o resto é história. Por muitos anos era só &ldquo;bacana&rdquo; estar no GitHub. A comunidade Ruby impulsionou o GitHub lá pra cima. No fim virou o status quo e o único monopólio real em repositórios de informação, não só código-fonte, mas tudo.</p>
<p>Sempre soube que deveríamos ter uma opção &ldquo;local&rdquo;, que é por isso que <a href="https://gitorious.org/gitorious/oboxodo-gitorious?p=gitorious:oboxodo-gitorious.git;a=search;h=9f6bdf5887c65a440bc3fdc43a14652f42ddf103;s=Fabio&#43;Akita;st=committer"target="_blank" rel="noopener">tentei contribuir com o Gitorious</a> lá em 2009. Outras opções apareceram, mas eventualmente o GitLab surgiu por volta de 2011 e pegou tração nos últimos dois anos.</p>
<p>O próprio GitHub levantou <a href="https://www.crunchbase.com/organization/github#/entity"target="_blank" rel="noopener">USD 350 milhões em financiamento</a> e uma das metas obrigatórias é arrasar na Enterprise Edition para grandes corporações que não querem seus dados fora dos jardins fechados. Embora o GitHub hospede cada projeto open source que existe, eles próprios são closed-source.</p>
<p>A <a href="http://gitlab.com"target="_blank" rel="noopener">GitLab Inc.</a> começou diferente, com uma abordagem open source-first com a Community Edition (CE) e tendo tanto uma opção hospedada estilo GitHub quanto uma Enterprise Edition suportada para as corporações temerosas. Já levantaram <a href="https://www.crunchbase.com/organization/gitlab-com#/entity"target="_blank" rel="noopener">USD 5.62 milhões em financiamento</a>, e são a alternativa mais promissora ao GitHub até agora.</p>
<p>Claro, existem outras plataformas como o Bitbucket da Atlassian. Mas acredito que a estratégia da Atlassian é mais lenta e eles têm uma suíte maior de produtos enterprise pra vender primeiro, como o Confluence e o Jira. Acho que eles nunca chegaram a representar muita competição contra o GitHub.</p>
<p>O GitLab realmente começou a acelerar em 2015 como mostra este <a href="https://github.com/gitlabhq/gitlabhq/graphs/contributors?from=2015-03-14&amp;to=2016-08-02&amp;type=c"target="_blank" rel="noopener">gráfico de commits</a>:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/545/big_Contributors_to_gitlabhq_gitlabhq.png" alt="contributions"  loading="lazy" /></p>
<p>Vem crescendo de forma constante desde 2011, mas parece que cruzaram o primeiro ponto de virada lá por final de 2014, saindo dos early adopters pra early majority. Isso ficou mais importante quando o GitHub <a href="https://github.com/blog/2164-introducing-unlimited-private-repositories"target="_blank" rel="noopener">anunciou as mudanças de preço</a> em maio.</p>
<p>Disseram que não se comprometeram com um prazo pra forçar a mudança, então as organizações podem optar por ficar fora do novo formato por enquanto. Eles estão mudando de &ldquo;repositórios limitados e usuários ilimitados&rdquo; pra &ldquo;repositórios ilimitados e usuários limitados&rdquo;.</p>
<h2>O Dilema do Custo-Benefício<span class="hx:absolute hx:-mt-20" id="o-dilema-do-custo-benefício"></span>
    <a href="#o-dilema-do-custo-benef%c3%adcio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Por exemplo, se você tem até 8 desenvolvedores no plano de USD 50/mês (20 repositórios privados), a mudança não vai te afetar, já que você vai pagar USD 25/mês por 5 usuários e USD 9 por usuários adicionais (total de USD 52/mês).</p>
<p>Agora, se você tem uma equipe grande de 100 desenvolvedores atualmente no Diamond Plan de USD 450/mês (300 repositórios privados), teria que pagar USD 25/mês + 95 vezes USD 9, o que totaliza impressionantes USD 880/mês! <strong>O dobro do valor!</strong></p>
<p>Isso são <strong>USD 10.560 a mais por ano</strong>!</p>
<p>E o que o GitLab te oferece em troca?</p>
<p>Você pode ter muito mais usuários e mais repositórios numa máquina virtual de <strong>USD 40/mês</strong> (4GB de RAM, 60GB SSD, 4TB de transferência).</p>
<p>E não para por aí. O GitLab também tem o funcionalíssimo <a href="https://gitlab.com/gitlab-org/gitlab-ci-multi-runner"target="_blank" rel="noopener">GitLab Multi Runner</a> que você pode instalar numa máquina separada (na verdade, pelo menos 3 máquinas, mais sobre isso abaixo).</p>
<p>Você pode facilmente conectar esse runner ao sistema de build pelo GitLab de modo que cada novo git push dispara o runner pra rodar a suíte de testes automatizados numa imagem Docker à sua escolha. Então é um sistema de Integração Contínua completo e totalmente funcional integrado na interface do projeto GitLab:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/546/big_Pipelines___cm42-archived___PremiosOnline___GitLab.png" alt="Pipeline"  loading="lazy" /></p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/547/big_test___144____Builds___cm42-archived___PremiosOnline___GitLab.png" alt="CI Runner"  loading="lazy" /></p>
<p>Lembra de alguma coisa? Sim, é uma alternativa totalmente funcional ao Travis-CI, Semaphore, CircleCI ou qualquer outro CI que você esteja usando com um procedimento de instalação bem fácil. Digamos que você está pagando USD 489/mês ao Travis-CI pra ter 10 jobs concorrentes.</p>
<p>Você pode instalar o GitLab Runner em 3 máquinas de USD 10/mês (1GB RAM, 1 Core, 30GB SSD) e ter muito mais jobs concorrentes (20? 50? Auto-Scale!?) que rodam <strong>mais rápido</strong> (num teste simples, um build que demorou 15 minutos no Travis demorou menos de 8 minutos no Digital Ocean).</p>
<p>Então vamos fazer a conta pra um ano de serviço. Primeiro considerando nenhuma mudança no plano do GitHub:</p>
<blockquote>
  <p>USD 5.400 (GitHub) + USD 5.868 (Travis) = USD 11.268 por ano.</p>

</blockquote>
<p>Agora, GitLab + GitLab Runner + Digital Ocean pelas mesmas features e usuários ilimitados, repositórios ilimitados, builds concorrentes ilimitados:</p>
<blockquote>
  <p>USD 480 (GitLab) + USD 840 (máquina do Runner) = USD 1.320 por ano.</p>

</blockquote>
<p>Isso já é quase 8,5x mais barato com quase nenhuma mudança de qualidade.</p>
<p>No pior cenário, compare quando o GitHub decidir forçar os novos planos:</p>
<blockquote>
  <p>USD 10.560 (novos planos GitHub) + USD 5.868 (Travis) = USD 16.428</p>

</blockquote>
<p>Agora a opção GitLab é 11x mais barata! Você economiza quase USD 15.000 por ano! Isso é algo que você não pode ignorar na sua planilha de custos.</p>
<p>Como eu disse, os cálculos acima só são significativos num cenário de 100 desenvolvedores. Você tem que fazer sua própria conta considerando o tamanho do seu time e número de projetos ativos (você sempre pode arquivar projetos que não estão sendo usados).</p>
<p>Mesmo que você não tenha 100 desenvolvedores. Vamos considerar o cenário pra <strong>30 desenvolvedores</strong> nos novos planos por usuário do GitHub e uma configuração menor de Travis pra 5 jobs concorrentes:</p>
<blockquote>
  <p>USD 3.000 (novo plano GitHub) + USD 3.000 (Travis) = USD 6.000</p>

</blockquote>
<p>É 4,5x mais barato na opção suite Digital Ocean + GitLab.</p>
<p>Olha, vamos considerar o plano <strong>Atual</strong> do GitHub (o Platinum, pra até 125 repositórios):</p>
<blockquote>
  <p>USD 2.400 (plano atual GitHub) + USD 3.000 (Travis) = USD 5.400</p>

</blockquote>
<p>Ainda é pelo menos <strong>4x mais caro</strong> que uma solução baseada em GitLab!</p>
<p>E quanto tempo leva pra um único desenvolvedor descobrir o setup e migrar tudo do GitHub pra nova instalação do GitLab? Vou dizer que você pode reservar 1 semana de trabalho pra um programador mediano fazer isso seguindo a documentação oficial e minhas dicas e truques abaixo.</p>
<h2>Instalando o GitLab CE<span class="hx:absolute hx:-mt-20" id="instalando-o-gitlab-ce"></span>
    <a href="#instalando-o-gitlab-ce" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Não vou te encher com o que você encontra facilmente pela Web. Recomendo fortemente que você comece com a solução mais fácil primeiro: <a href="https://www.digitalocean.com/features/one-click-apps/gitlab/"target="_blank" rel="noopener">One-Click Automatic Install do Digital Ocean</a>. Instale numa máquina com pelo menos 4GB de RAM (você vai querer mantê-la se gostar).</p>
<p>Claro, existem várias opções diferentes de instalação, de imagens AMI da AWS a pacotes Ubuntu que você pode instalar manualmente. Estude a <a href="https://about.gitlab.com/installation/"target="_blank" rel="noopener">documentação</a>.</p>
<p>Vai te custar USD 40 por um mês de teste. Se você quiser economizar dezenas de milhares de dólares, isso é uma pechincha.</p>
<p>O GitLab tem muitas opções de customização. Você pode trancar seu GitLab privado pra permitir só usuários com e-mail oficial do seu domínio, por exemplo. Você pode configurar <a href="http://docs.gitlab.com/ee/integration/omniauth.html"target="_blank" rel="noopener">provedores OAuth2</a> pros seus usuários entrarem rapidamente usando contas do GitHub, Facebook, Google ou outras.</p>
<h4>Alguns Detalhes<span class="hx:absolute hx:-mt-20" id="alguns-detalhes"></span>
    <a href="#alguns-detalhes" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Tropecei em alguns caveats na configuração. É por isso que recomendo planejar com antecedência, estude este artigo inteiro antes! Faça uma instalação rápida que você pode jogar fora, pra você &ldquo;sentir&rdquo; o ambiente antes de tentar migrar todos os seus repositórios pro seu GitLab novinho. Como referência, esta é uma parte do meu <code>/etc/gitlab/gitlab.rb</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># registre um domínio pro seu servidor e coloque aqui:</span>
</span></span><span class="line"><span class="cl"><span class="n">external_url</span> <span class="s2">&#34;http://my-gitlab-server.com/&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># você vai querer habilitar [LFS](https://git-lfs.github.com)</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;lfs_enabled&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># registre seus emails</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;gitlab_email_from&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;no-reply@my-gitlab-server.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;gitlab_support_email&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;contact@my-gitlab-server.com&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># adicione sua configuração de email (template pra gmail)</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_enable&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_address&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;smtp.gmail.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_port&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="mi">587</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_user_name&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;-- algum email no-reply ---&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_password&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;-- a senha ---&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_domain&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;my-gitlab-server.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_authentication&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;login&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_enable_starttls_auto&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;smtp_openssl_verify_mode&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;peer&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># aqui é onde você habilita a integração oauth2</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;omniauth_enabled&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># CUIDADO!</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Isso permite usuários logarem sem ter uma conta de usuário primeiro. Defina os providers permitidos</span>
</span></span><span class="line"><span class="cl"><span class="c1"># usando um array, ex. [&#34;saml&#34;, &#34;twitter&#34;], ou como true/false pra permitir todos os providers ou nenhum.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Contas de usuário serão criadas automaticamente quando a autenticação for bem-sucedida.</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;omniauth_allow_single_sign_on&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;github&#39;</span><span class="p">,</span> <span class="s1">&#39;google_oauth2&#39;</span><span class="p">,</span> <span class="s1">&#39;bitbucket&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;omniauth_block_auto_created_users&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;omniauth_providers&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;github&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;app_id&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;-- github app id --&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;app_secret&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;-- github secret --&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;url&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;https://github.com/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;args&#34;</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">&#34;scope&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;user:email&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;google_oauth2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;app_id&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;-- google app id --&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;app_secret&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;-- google secret --&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;args&#34;</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="s2">&#34;access_type&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;offline&#34;</span><span class="p">,</span> <span class="s2">&#34;approval_prompt&#34;</span> <span class="o">=&gt;</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="n">hd</span> <span class="o">=&gt;</span> <span class="s1">&#39;codeminer42.com&#39;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;bitbucket&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;app_id&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;-- bitbucket app id --&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;app_secret&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;-- bitbucket secret id --&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;url&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;https://bitbucket.org/&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># se você está importando repos do GitHub, os workers do Sidekiq podem crescer até 2.5GB de RAM e a config padrão do [Sidekiq Killer](http://docs.gitlab.com/ee/operations/sidekiq_memory_killer.html) vai limitar em 1GB, então você vai querer ou desabilitar adicionando &#39;0&#39; ou adicionar um limite maior</span>
</span></span><span class="line"><span class="cl"><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;env&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">&#39;SIDEKIQ_MEMORY_KILLER_MAX_RSS&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;3000000&#39;</span> <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Existem <a href="https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb#L57"target="_blank" rel="noopener">dezenas de variáveis padrão</a> que você pode <a href="https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/environment-variables.md"target="_blank" rel="noopener">sobrescrever</a>, só tome cuidado nos seus testes.</p>
<p>Toda vez que você muda uma configuração, basta rodar os seguintes comandos:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você pode abrir um console Rails pra inspecionar objetos de produção assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gitlab-rails console</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tive muita dor de cabeça importando repos grandes do GitHub, mas depois de alguns dias debugando o problema com os desenvolvedores do GitLab Core Team <a href="https://gitlab.com/u/dbalexandre"target="_blank" rel="noopener">Douglas Alexandre</a>, <a href="https://gitlab.com/u/brodock"target="_blank" rel="noopener">Gabriel Mazetto</a>, alguns Merge Requests e algum patching local finalmente consegui importar projetos relativamente grandes (mais de 5.000 commits, mais de 1.000 issues, mais de 1.200 pull requests com várias discussões de comentários). Um projeto desse tamanho pode levar algumas horas pra completar, principalmente porque <strong>é lento demais usar as APIs públicas do GitHub</strong> (são lentas e têm rate limits e detecção de abuso, então você não consegue baixar tudo tão rápido quanto sua banda permitiria).</p>
<p>(A propósito, não perca o GitLab na <a href="http://www.rubyconf.com.br/pt-BR/speakers#Gabriel%20Gon%c3%a7alves%20Nunes%20Mazetto"target="_blank" rel="noopener">Rubyconf Brasil 2016</a>, nos dias 23 e 24 de setembro)</p>
<p>Migrar todos os meus projetos do GitHub levou alguns dias, mas todos passaram sem problemas e meu time não teve nenhuma dificuldade, só ajustar os git remote URLs e pronto.</p>
<p>O procedimento de importação do GitHub é bem completo, traz não só o repositório git em si, mas também todos os metadados, de labels a comentários e histórico de pull request, que é justamente o que normalmente leva mais tempo.</p>
<p>Mas eu recomendaria esperar pelo menos a versão 8.11 (atualmente está na 8.10.3) antes de tentar importar projetos grandes do GitHub.</p>
<p>Se você está no Bitbucket, infelizmente tem menos features no importador. Ele vai trazer principalmente só o código-fonte. Então fique atento se você depende bastante do sistema de pull request deles e quer preservar esse histórico. Mais features virão e você até pode ajudá-los, eles são bem desenvoltos e dispostos a melhorar o GitLab.</p>
<h3>Desvio: Customizações pra cada máquina Digital Ocean<span class="hx:absolute hx:-mt-20" id="desvio-customizações-pra-cada-máquina-digital-ocean"></span>
    <a href="#desvio-customiza%c3%a7%c3%b5es-pra-cada-m%c3%a1quina-digital-ocean" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Assuma que você deve rodar o que está nesta seção pra todas as novas máquinas que você criar no Digital Ocean.</p>
<p>Primeiro, elas vêm sem arquivo de swap. Não importa quanta RAM você tem, o Linux foi feito pra funcionar melhor combinado com um arquivo de swap. Você pode <a href="https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04"target="_blank" rel="noopener">ler mais sobre isso</a> depois, por enquanto só rode o seguinte como root:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

sysctl vm.swappiness=10
sysctl vm.vfs_cache_pressure=50</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Edite o arquivo <code>/etc/fstab</code> e adicione esta linha:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/swapfile   none    swap    sw    0   0</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finalmente, edite o arquivo <code>/etc/sysctl.conf</code> e adicione estas linhas:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>vm.swappiness=10
vm.vfs_cache_pressure = 50</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Não esqueça de configurar o <a href="http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue"target="_blank" rel="noopener">locale padrão</a> da sua máquina. Comece editando o arquivo <code>/etc/environment</code> e adicionando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>LC_ALL=en_US.UTF-8
LANG=en_US.UTF-8</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então rode:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo locale-gen en_US en_US.UTF-8
sudo dpkg-reconfigure locales</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finalmente, você deveria ter o Ubuntu instalando patches de segurança estáveis automaticamente pra você. Você não quer esquecer máquinas online sem as correções de segurança mais atuais, então só rode isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo dpkg-reconfigure --priority=low unattended-upgrades</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Escolha &ldquo;yes&rdquo; e pronto. E claro, pra toda instalação nova, é sempre bom rodar o velho e bom:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get update &amp;&amp; sudo apt-get upgrade</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso é o básico do básico, acho que é mais fácil ter uma imagem com tudo isso pronto, mas se você usa as imagens padrão do Digital Ocean, estas configurações devem dar conta por enquanto.</p>
<h2>Instalando o CI Runner<span class="hx:absolute hx:-mt-20" id="instalando-o-ci-runner"></span>
    <a href="#instalando-o-ci-runner" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Assim que você terminar sua instalação do GitLab, é <a href="https://about.gitlab.com/2016/04/19/how-to-set-up-gitlab-runner-on-digitalocean/"target="_blank" rel="noopener">super fácil</a> subir o GitLab Runner. Você pode usar a mesma máquina, mas recomendo instalar numa máquina separada.</p>
<p>Se você não sabe o que é um runner, imagina assim: basicamente é um servidor conectado à instalação do GitLab. Quando está disponível e online, sempre que alguém dá push num novo commit, merge request, num repositório que tem um arquivo <code>gitlab-ci-yml</code> presente, o GitLab vai empurrar um comando pro runner.</p>
<p>Dependendo de como você configurou o runner, ele vai receber esse comando e criar um novo container Docker. Dentro do container ele vai executar o que você definiu no arquivo <code>gitlab-ci.yml</code> do projeto. Normalmente é buscar arquivos em cache (dependências, por exemplo) e rodar sua suíte de testes.</p>
<p>No setup mais básico, você só vai ter um Runner e quaisquer builds subsequentes de outros usuários vão esperar na fila até terminarem. Se você já usou serviços externos de CI como Travis-CI ou CircleCI, sabe que eles cobram por um certo número de builds concorrentes. E é <strong>muito caro</strong>.</p>
<p>Quanto menos builds concorrentes disponíveis, mais seus usuários vão ter que esperar por feedback nas mudanças deles, e menos produtivos vocês ficam. As pessoas podem até começar a evitar adicionar novos testes, ou ignorar completamente os testes, o que vai machucar muito a qualidade do seu projeto com o tempo. Se tem uma coisa que você <strong>não pode</strong> deixar de fazer é ter boas suítes de testes automatizados.</p>
<p>O Gabriel Mazetto me apontou pra uma feature muito importante do GitLab CI Runner: <a href="https://about.gitlab.com/2016/03/29/gitlab-runner-1-1-released/"target="_blank" rel="noopener"><strong>auto-scaling</strong></a>. É isso que eles usam na oferta hospedada deles lá no GitLab.com.</p>
<p>Você pode <strong>facilmente</strong> configurar um runner que pode usar &ldquo;docker-machine&rdquo; e as APIs do seu provedor IaaS pra subir máquinas na hora pra rodar quantos builds concorrentes você quiser, e vai ser super barato!</p>
<p>Por exemplo, no Digital Ocean você pode ser cobrado USD 0,06 (6 centavos) por hora de uso numa máquina de 4GB. Na AWS EC2 você pode ser cobrado USD 0,041 por hora numa máquina m3.medium.</p>
<p>Tem documentação extensiva mas vou tentar resumir o que você tem que fazer. Pra mais detalhes recomendo fortemente que você estude a <a href="https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/install/autoscaling.md#prepare-the-docker-registry-and-cache-server"target="_blank" rel="noopener">documentação oficial</a>.</p>
<p>Comece criando 3 novas máquinas no Digital Ocean, todas na mesma Região com rede privada habilitada! Vou listar um endereço IP privado fake só pra avançar nos exemplos de configuração:</p>
<ul>
<li>uma máquina de 1GB chamada &ldquo;docker-registry-mirror&rdquo;, (ex 10.0.0.1)</li>
<li>uma máquina de 1GB chamada &ldquo;ci-cache&rdquo;, (ex 10.0.0.2)</li>
<li>uma máquina de 1GB chamada &ldquo;ci-runner&rdquo;, (ex 10.0.0.3)</li>
</ul>
<p>Sim, podem ser pequenas já que muito pouco vai rodar nelas. Você pode ser conservador e escolher as de 2GB RAM só pra ficar no seguro (e o preço ainda vai ser super barato).</p>
<p>Não esqueça de executar a configuração básica que mencionei acima pra habilitar swapfile, auto security update e regeneração de locale.</p>
<p>Faça SSH na &ldquo;docker-registry-mirror&rdquo; e rode:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>docker run -d -p 6000:5000 \
    -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
    --restart always \
    --name registry registry:2</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora você vai ter um registry proxy e cache local de imagens Docker em <code>10.0.0.1:6000</code> (anote o IP privado real).</p>
<p>Faça SSH na &ldquo;ci-cache&rdquo; e rode:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mkdir -p /export/runner

docker run -it --restart always -p 9005:9000 \
        -v /.minio:/root/.minio -v /export:/export \
        --name minio \
        minio/minio:latest /export</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora você vai ter um clone do AWS S3 chamado <a href="https://github.com/minio/minio"target="_blank" rel="noopener">Minio</a> rodando. Eu nem sabia que esse projeto existia, mas é um serviçozinho legal escrito em Go pra clonar o comportamento e APIs do AWS S3. Então agora você pode ter seu próprio S3 dentro da sua infraestrutura!</p>
<p>Depois que o Docker sobe, ele vai imprimir a Access Key e Secret key, anote. E este serviço vai estar rodando em <code>10.0.0.2:9005</code>.</p>
<p>Você até pode abrir um browser e ver a interface web em <code>http://10.0.0.2:9005</code> e usar as access e secret keys pra logar. Certifique-se de ter um bucket chamado &ldquo;runner&rdquo;. Os arquivos serão armazenados no diretório <code>/export/runner</code>.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/549/big_Minio_Browser.png" alt="Minio Dashboard"  loading="lazy" /></p>
<p>Certifique-se de que o <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html"target="_blank" rel="noopener">nome do bucket é válido</a> (tem que ser um nome DNS válido, por exemplo, NÃO use underlines).</p>
<p>Abra esta URL do seu GitLab-CE recém instalado: <code>http://yourgitlab.com/admin/runners</code> e anote o Registration Token. Digamos que seja <code>1aaaa_Z1AbB2CdefGhij</code></p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/550/big_Admin_Area___GitLab.png" alt="Admin Area for Runner Registration Token"  loading="lazy" /></p>
<p>Finalmente, faça SSH na &ldquo;ci-runner&rdquo; e rode:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>curl -L https://github.com/docker/machine/releases/download/v0.7.0/docker-machine-`uname -s`-`uname -m` &gt; /usr/local/bin/docker-machine

chmod &#43;x /usr/local/bin/docker-machine

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash

sudo apt-get install gitlab-ci-multi-runner

rm -Rf ~/.docker # só pra garantir</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora você pode registrar este novo runner com sua instalação do GitLab, você vai precisar do Registration Token mencionado acima.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo gitlab-ci-multi-runner register</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você vai ser perguntado algumas coisas, e isto é o que você pode responder:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/ci )
https://yourgitlab.com/ci
Please enter the gitlab-ci token for this runner
1aaaa_Z1AbB2CdefGhij # como no exemplo acima
Please enter the gitlab-ci description for this runner
my-autoscale-runner
INFO[0034] fcf5c619 Registering runner... succeeded
Please enter the executor: shell, docker, docker-ssh, docker&#43;machine, docker-ssh&#43;machine, ssh?
docker&#43;machine
Please enter the Docker image (eg. ruby:2.1):
codeminer42/ci-ruby:2.3
INFO[0037] Runner registered successfully. Feel free to start it, but if it&#39;s
running already the config should be automatically reloaded!</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vamos fazer uma cópia da configuração original, só pra garantir:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cp /etc/gitlab-runner/config.toml /etc/gitlab-runner/config.bak</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Copie as primeiras linhas deste arquivo (você quer o token), vai parecer com isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>concurrent = 1
check_interval = 0

[[runners]]
  name = &#34;my-autoscale-runner&#34;
  url = &#34;http://yourgitlab.com/ci&#34;
  token = &#34;--- generated runner token ---&#34;
  executor = &#34;docker&#43;machine&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A parte importante aqui é o &ldquo;token&rdquo;. Você vai querer anotar. E agora você também vai querer criar um <a href="https://cloud.digitalocean.com/settings/api/tokens"target="_blank" rel="noopener">novo API Token no Digital Ocean</a>. Só Gere um Novo Token e anote.</p>
<p>Agora você pode substituir o arquivo <code>config.toml</code> inteiro por isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>concurrent = 20
check_interval = 0

[[runners]]
  name = &#34;my-autoscale-runner&#34;
  url = &#34;http://yourgitlab.com/ci&#34;
  token = &#34;--- generated runner token ---&#34;
  executor = &#34;docker&#43;machine&#34;
  limit = 15
  [runners.docker]
    tls_verify = false
    image = &#34;codeminer42/ci-ruby:2.3&#34;
    privileged = false
  [runners.machine]
    IdleCount = 2                   # Tem que ter 2 máquinas em estado Idle
    IdleTime = 1800                 # Cada máquina pode ficar em estado Idle até 30 minutos (depois disso será removida)
    MaxBuilds = 100                 # Cada máquina pode lidar com até 100 builds em sequência (depois disso será removida)
    MachineName = &#34;ci-auto-scale-%s&#34;   # Cada máquina vai ter um nome único (&#39;%s&#39; é obrigatório)
    MachineDriver = &#34;digitalocean&#34;  # Docker Machine está usando o driver &#39;digitalocean&#39;
    MachineOptions = [
        &#34;digitalocean-image=coreos-beta&#34;,
        &#34;digitalocean-ssh-user=core&#34;,
        &#34;digitalocean-access-token=-- seu novo Digital Ocean API Token --&#34;,
        &#34;digitalocean-region=nyc1&#34;,
        &#34;digitalocean-size=4gb&#34;,
        &#34;digitalocean-private-networking&#34;,
        &#34;engine-registry-mirror=http://10.0.0.1:6000&#34;
    ]
  [runners.cache]
    Type = &#34;s3&#34;   # O Runner está usando um cache distribuído com o serviço Amazon S3
    ServerAddress = &#34;10.0.0.2:9005&#34;  # minio
    AccessKey = &#34;-- sua minio access key --&#34;
    SecretKey = &#34;-- sua minio secret key&#34;
    BucketName = &#34;runner&#34;
    Insecure = true # Use Insecure só quando usar com Minio, sem o certificado TLS habilitado</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E você pode reiniciar o runner pra pegar a nova configuração assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gitlab-ci-multi-runner restart</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Como eu disse antes, você vai querer ler a <a href="https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/configuration/autoscale.md"target="_blank" rel="noopener">documentação oficial</a> extensiva (e todos os links dentro).</p>
<p>Se você fez tudo certo, trocando os IPs privados corretos pro docker registry e cache, os tokens corretos, e assim por diante, pode logar no seu dashboard do Digital Ocean e vai ver algo assim:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/548/big_DigitalOcean_-_Droplets.png" alt="Digital Ocean CI Setup"  loading="lazy" /></p>
<p>E da máquina <code>ci-runner</code>, você pode listá-las assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># docker-machine ls

NAME                                                ACTIVE   DRIVER         STATE     URL                         SWARM   DOCKER    ERRORS
runner-xxxx-ci-auto-scale-xxxx-xxxx   -        digitalocean   Running   tcp://191.168.0.1:2376            v1.10.3
runner-xxxx-ci-auto-scale-xxxx-xxxx   -        digitalocean   Running   tcp://192.169.0.2:2376           v1.10.3</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Elas não devem listar nenhum erro, significando que estão de pé e rodando, esperando novos builds começarem.</p>
<p>Vai ter 2 novas máquinas listadas no seu dashboard do Digital Ocean, chamadas &ldquo;runner-xxxxx-ci-auto-scale-xxxxx&rdquo;. Isso é o que o <code>IdleCount = 2</code> faz. Se ficarem paradas por mais de 30 minutos (<code>IdleTime = 1800</code>) serão desligadas pra você não ser cobrado.</p>
<p>Você pode ter várias definições de &ldquo;runner&rdquo;, cada uma com um <code>limit</code> de builds/máquinas que podem ser criadas no Digital Ocean. Você pode ter outras definições de runner pra outros provedores, por exemplo. Mas neste exemplo estamos limitados a no máximo 15 máquinas, então 15 builds concorrentes.</p>
<p>O limite <code>concurrent</code> é uma configuração global. Então se eu tivesse 3 definições de runner, cada uma com um <code>limit</code> de 15, elas ainda seriam globalmente limitadas a 20 como definido na variável global <code>concurrent</code>.</p>
<p>Você pode usar provedores diferentes pra necessidades específicas, por exemplo, pra rodar builds OS X ou builds Raspberry PI ou outros tipos exóticos de build. No exemplo estou mantendo simples e configurando vários builds no mesmo provedor (Digital Ocean).</p>
<p>E não se preocupe com a mensalidade de cada máquina. Quando usada dessa forma, você vai pagar por hora.</p>
<p>Além disso, certifique-se de ter subido todas as suas máquinas (docker-registry, minio cache, CI runner) todas com <strong>rede privada habilitada</strong> (pra que conversem pela VLAN interna em vez de ter que atravessar toda a internet pública) e que estão todas na mesma região/data center (NYC1 é New York 1, New York tem 3 sub-regiões, por exemplo). Não comece máquinas em regiões diferentes.</p>
<p>Porque temos proxy/cache Docker e cache Minio/S3, seus builds vão demorar mais na primeira vez (digamos, 5 minutos), e aí builds subsequentes vão buscar tudo do cache (levando, digamos, 1:30 minuto). É rápido e é conveniente.</p>
<h3>Configurando cada Projeto pro Runner<span class="hx:absolute hx:-mt-20" id="configurando-cada-projeto-pro-runner"></span>
    <a href="#configurando-cada-projeto-pro-runner" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Runner é uma das peças mais novas do ecossistema GitLab então você pode ter algum trabalho no começo pra descobrir uma configuração decente. Mas uma vez que você tem a infraestrutura toda figurada como descrito na seção anterior, agora é tão fácil quanto adicionar um arquivo <code>.gitlab-ci.yml</code> no seu diretório raiz. Algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># Este arquivo é um template, e pode precisar de edição antes de funcionar no seu projeto.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">codeminer42/ci-ruby:2.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Escolha zero ou mais serviços pra serem usados em todos os builds.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Só necessário quando usar um container docker pra rodar seus testes.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Confira: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">postgres:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">redis:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">cache</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">your-project-name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">untracked</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">.ci_cache/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">RAILS_ENV</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;test&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">DATABASE_URL</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql://postgres:@postgres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">CODECLIMATE_REPO_TOKEN</span><span class="p">:</span><span class="w"> </span>-- <span class="l">seu token de projeto codeclimate --</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">before_script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">bundle install --without development production -j $(nproc) --path .ci_cache</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">cp .env.sample .env</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">cp config/database.yml.example config/database.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">bundle exec rake db:create db:migrate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">xvfb-run bundle exec rspec</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Meu time na <a href="http://www.codeminer42.com"target="_blank" rel="noopener">Codeminer 42</a> preparou uma <a href="https://hub.docker.com/r/codeminer42/ci-ruby/"target="_blank" rel="noopener">imagem Docker simples</a> com coisas úteis pré-instaladas (como o phantomjs mais novo, xvfb, etc), então agora é super fácil habilitar builds automatizados dentro do GitLab só adicionando este arquivo aos repositórios. (Obrigado ao Carlos Lopes, Danilo Resende e Paulo Diovanni, que vai estar falando <a href="http://www.rubyconf.com.br/pt-BR/speakers#Paulo%20Diovani%20Gon%c3%a7alves"target="_blank" rel="noopener">sobre Docker na Rubyconf Brasil 2016</a>, a propósito)</p>
<p>O GitLab-CI até suporta buildar um Merge Request pendente, e você pode forçar o request pra que ele só possa ser merjado se os builds passarem, assim como no GitHub + Travis. E como o Code Climate é agnóstico a host de repositório ou CI runner, você pode integrá-lo facilmente também.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/551/big_Settings___Codeminer42___CM-Fulcrum___GitLab.png" alt="Project Force Successful Build to Merge"  loading="lazy" /></p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A matemática é difícil de contestar: o combo GitLab + GitLab-CI + Digital Ocean é uma grande vitória. A interface do GitLab é bem familiar, então usuários vindos do GitHub ou Bitbucket vão se sentir em casa rapidinho.</p>
<p>Podemos usar todos os <a href="https://about.gitlab.com/2014/09/29/gitlab-flow/"target="_blank" rel="noopener">Git flows</a> que estamos acostumados.</p>
<p>O GitLab-CE ainda é um trabalho em andamento, o time está aumentando o ritmo mas atualmente tem mais de <a href="https://gitlab.com/gitlab-org/gitlab-ce/issues"target="_blank" rel="noopener">4.200 issues abertas</a>. Mas como tudo isto é Ruby on Rails e tooling Ruby, você pode facilmente entrar e contribuir. Nenhuma contribuição é pequena demais. Só reportar como reproduzir um bug já é ajuda suficiente pra assistir os desenvolvedores a descobrirem como melhorar mais rápido.</p>
<p>Mas não se assuste com as issues abertas, está totalmente funcional agora e eu não achei nenhum bug que poderia ser considerado show stopper.</p>
<p>Eles têm muita coisa certa. Primeiro de tudo, é um projeto Ruby on Rails &ldquo;simples&rdquo;. É um front-end sem firulas com jQuery puro. A escolha de HAML pras views é questionável mas não atrapalha. Eles usam o bom e velho Sidekiq+Redis pra jobs assíncronos. Sem mágica negra aqui. Um monólito puro que não é difícil de entender e contribuir.</p>
<p>As APIs são todas escritas usando Grape. Eles têm o projeto <a href="https://gitlab.com/gitlab-org/gitlab-ce"target="_blank" rel="noopener">GitLab CE</a> separado de outros componentes, como o <a href="https://gitlab.com/gitlab-org/gitlab-shell"target="_blank" rel="noopener">GitLab-Shell</a> e o <a href="https://gitlab.com/gitlab-org/gitlab-ci-multi-runner"target="_blank" rel="noopener">GitLab-CI-Multi-Runner</a>.</p>
<p>Eles também forkaram o <a href="https://gitlab.com/gitlab-org/omnibus-gitlab"target="_blank" rel="noopener">Omnibus</a> pra conseguir empacotar o projeto Rails CE como um &ldquo;.deb&rdquo;. Tudo é orquestrado com Docker. E quando uma nova versão está disponível, você só precisa rodar <code>apt-get update &amp;&amp; apt-get upgrade</code> e ele vai fazer todo o trabalho de backup e migração do Postgresql, atualizar o código, incluir novas dependências, reiniciar os serviços e por aí vai. É super conveniente e você deveria dar uma olhada neste projeto se você tem deploys Rails complicados na sua própria infraestrutura (fora do Heroku, por exemplo).</p>
<p>Estou quase terminando de mover centenas de repositórios tanto do BitBucket quanto do GitHub pro GitLab agora e os desenvolvedores da minha empresa já estão usando no dia a dia sem nenhum problema. Estamos quase no ponto onde podemos nos desengajar do BitBucket, GitHub e CIs externos.</p>
<p>Você vai se surpreender com o quão fácil sua empresa também pode fazer isso e economizar alguns milhares de dólares no processo, enquanto se diverte fazendo!</p>
]]></content:encoded><category>controle-de-versao</category><category>devops</category></item><item><title>[Screencast] Começando com Git</title><link>https://akitaonrails.github.io/2010/08/17/screencast-comecando-com-git/</link><guid isPermaLink="true">https://akitaonrails.github.io/2010/08/17/screencast-comecando-com-git/</guid><pubDate>Tue, 17 Aug 2010 00:21:00 -0300</pubDate><description>&lt;p&gt;&lt;strong&gt;Atualização 09/04/12:&lt;/strong&gt; Este screencast foi liberado para visualização gratuita. Acesse este &lt;a href="http://akitaonrails.com/2012/04/09/screencasts-liberados-gratuitamente"target="_blank" rel="noopener"&gt;post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Atualização 17/08:&lt;/strong&gt; Tem uma pequena correção para quem usa Windows que não coloquei no screencast. Veja no fim do artigo.&lt;/p&gt;
&lt;p&gt;Como prometido, continuo com minha iniciativa de publicar screencasts, vídeo-aulas completas para que você possa aprender sobre as novas tecnologias.&lt;/p&gt;
&lt;p&gt;Todos devem saber que Git cresceu muito em popularidade desde 2007, quando publiquei meu &lt;a href="http://www.akitaonrails.com/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta"target="_blank" rel="noopener"&gt;primeiro artigo evangelizando Git&lt;/a&gt;. Depois disso publiquei outros artigos incluindo o famoso &lt;a href="http://www.akitaonrails.com/2008/04/02/micro-tutorial-de-git"target="_blank" rel="noopener"&gt;Micro Tutorial de Git&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p><strong>Atualização 09/04/12:</strong> Este screencast foi liberado para visualização gratuita. Acesse este <a href="http://akitaonrails.com/2012/04/09/screencasts-liberados-gratuitamente"target="_blank" rel="noopener">post</a></p>
<p><strong>Atualização 17/08:</strong> Tem uma pequena correção para quem usa Windows que não coloquei no screencast. Veja no fim do artigo.</p>
<p>Como prometido, continuo com minha iniciativa de publicar screencasts, vídeo-aulas completas para que você possa aprender sobre as novas tecnologias.</p>
<p>Todos devem saber que Git cresceu muito em popularidade desde 2007, quando publiquei meu <a href="http://www.akitaonrails.com/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta"target="_blank" rel="noopener">primeiro artigo evangelizando Git</a>. Depois disso publiquei outros artigos incluindo o famoso <a href="http://www.akitaonrails.com/2008/04/02/micro-tutorial-de-git"target="_blank" rel="noopener">Micro Tutorial de Git</a></p>
<p>Graças ao <a href="http://www.github.com"target="_blank" rel="noopener">Github</a> todo bom desenvolvedor que se preza tem o Git na sua caixa de ferramentas. Você, sua equipe e sua empresa só tem a ganhar aprendendo esta ferramenta.</p>
<ul>
<li>
<p>Nível: Iniciante, Intermediário</p>
</li>
<li>
<p>Tema: Começando a usar a ferramenta de controle de versionamento de código, Git</p>
</li>
<li>
<p>Duração: <strong>3:17h</strong></p>
</li>
<li>
<p>Formatos:</p>
<ul>
<li><a href="http://bit.ly/Iv8XfH"target="_blank" rel="noopener">Maior</a> 800×600 – 510Mb (zip, Quicktime/H.264)</li>
<li><a href="http://bit.ly/1b7CwLc"target="_blank" rel="noopener">iPhone</a> 480×360 – 471Mb (zip, Quicktime/H.264)</li>
</ul>
</li>
<li>
<p>Plataformas: Linux (Ubuntu), Mac OS X, Windows 7</p>
</li>
<li>
<p>Acompanha PDF com os slides usados na apresentação</p>
</li>
<li>
<p>Tem organização em Capítulos</p>
</li>
</ul>
<p>E assistam na íntegra aqui:</p>
<video controls>
<source src="https://screencasts2010.s3.us-east-2.amazonaws.com/Comecando_com_Git/Come%C3%A7ando+com+Git.mov">
Your browser does not support the video tag. [Direct Link](https://screencasts2010.s3.us-east-2.amazonaws.com/Comecando_com_Git/Come%C3%A7ando+com+Git.mov)
</source></video>
<p>E se está interessado em aprender mais, leia todos os meus artigos já publicados neste blog sobre Git:</p>
<ul>
<li><a href="/2010/03/14/git-com-smart-http-transport">Git com Smart HTTP Transport</a></li>
<li><a href="/2010/02/13/deploy-com-git-push">Deploy com Git Push</a></li>
<li><a href="/2009/09/04/a-controv-rsia-do-ticket-2033-ruby-on-git">A Controvérsia do Ticket #2033 – Ruby on Git</a></li>
<li><a href="/2009/07/05/dicas-de-git/">Dicas de Git</a></li>
<li><a href="/2009/02/23/pequena-dica-de-git-para-windows">Pequena dica de Git para Windows</a></li>
<li><a href="/2008/12/02/tradu-o-por-que-git-melhor-que-x">Por que Git é melhor que X</a></li>
<li><a href="/2008/10/02/entendendo-git-e-instalando-gitorious-git-via-web">Entendendo Git e Instalando Gitorious – Git via Web</a></li>
<li><a href="/2008/09/21/colaborando-no-github">Colaborando no Github</a></li>
<li><a href="/2008/04/03/git-com-sake">Git com Sake</a></li>
<li><a href="/2008/04/02/finalmente-rails-mudando-de-svn-para-git/">Finalmente, Rails mudando de SVN para Git</a></li>
<li><a href="/2008/04/02/micro-tutorial-de-git/">Micro-Tutorial de Git</a></li>
<li><a href="/2008/02/13/git-on-cygwin-on-windows">Git on Cygwin on Windows</a></li>
<li><a href="/2008/02/12/git-para-cientistas-da-computa-o">Git para Cientistas da Computação</a></li>
<li><a href="/2008/02/04/ruby-on-git">Ruby on Git</a></li>
<li><a href="/2007/10/26/aprenda-git-pelo-peepcode">Aprenda Git pelo Peepcode</a></li>
<li><a href="/2007/09/22/git-muito-promissor/">Git, muito Promissor</a></li>
<li><a href="/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta/">Jogar Pedra em Gato Morto: Por que Subversion não Presta</a></li>
</ul>
<h2>Atualização: Git – Cloning e pushing via https no Windows<span class="hx:absolute hx:-mt-20" id="atualização-git--cloning-e-pushing-via-https-no-windows"></span>
    <a href="#atualiza%c3%a7%c3%a3o-git--cloning-e-pushing-via-https-no-windows" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Li este post do <a href="http://www.vogella.de/blog/2010/08/09/git-https/"target="_blank" rel="noopener">Lars Vogel</a> hoje com uma dica para quem usa Windows. Vou traduzir para facilitar. Isto é quando você quiser fazer clone ou push para repositórios Git via HTTPS (como o Github passou a suportar recentemente), por exemplo:</p>
<hr>
<h2>git clone <a href="https://vogella@github.com/vogella/de.vogella.rcp.example.git"target="_blank" rel="noopener">https://vogella@github.com/vogella/de.vogella.rcp.example.git</a><br>
…<br>
git push <a href="https://vogella@github.com/vogella/de.vogella.rcp.example.git"target="_blank" rel="noopener">https://vogella@github.com/vogella/de.vogella.rcp.example.git</a><span class="hx:absolute hx:-mt-20" id="git-push"></span>
    <a href="#git-push" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Você pode receber este erro no Windows:</p>
<hr>
<h2>error: error setting certificate verify locations:<br>
CAfile: /bin/curl-ca-bundle.crt<br>
CApath: none<span class="hx:absolute hx:-mt-20" id="capath-none"></span>
    <a href="#capath-none" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se ver este erro para resolver é só desabilitar a verificação de SSL:</p>
<hr>
<h2>git config -global http.sslverify “false”<span class="hx:absolute hx:-mt-20" id="git-config--global-httpsslverify-false"></span>
    <a href="#git-config--global-httpsslverify-false" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Depois desta configuração, clone e push via https vai funcionar no Windows.</p>
<p>Outra alternativa que o Vogel não testou é a seguinte:</p>
<hr>
<h2>mkdir c:\bin\<br>
copy “C:\Program Files\Git\bin\curl-ca-bundle.crt” c:\bin\curl-ca-bundle.crt<span class="hx:absolute hx:-mt-20" id="copy-cprogram-filesgitbincurl-ca-bundlecrt-cbincurl-ca-bundlecrt"></span>
    <a href="#copy-cprogram-filesgitbincurl-ca-bundlecrt-cbincurl-ca-bundlecrt" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Ou:</p>
<hr>
<h2>git config -system http.sslcainfo \bin/curl-ca-bundle.crt<span class="hx:absolute hx:-mt-20" id="git-config--system-httpsslcainfo-bincurl-ca-bundlecrt"></span>
    <a href="#git-config--system-httpsslcainfo-bincurl-ca-bundlecrt" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A discussão completa sobre esse problema do msysGit pode ser encontrada <a href="http://github.com/blog/642-smart-http-support"target="_blank" rel="noopener">aqui</a>.</p>
<p>E se estiver atrás de um proxy HTTP você pode configurar assim:</p>
<hr>
<h2>git config -global http.proxy http://proxy:8080<span class="hx:absolute hx:-mt-20" id="git-config--global-httpproxy-httpproxy8080"></span>
    <a href="#git-config--global-httpproxy-httpproxy8080" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Para checar a configuração:</p>
<hr>
<h2>git config -get http.proxy<span class="hx:absolute hx:-mt-20" id="git-config--get-httpproxy"></span>
    <a href="#git-config--get-httpproxy" class="subheading-anchor" aria-label="Permalink for this section"></a></h2>]]></content:encoded><category>controle-de-versao</category><category>ferramentas-de-desenvolvimento</category><category>tutoriais</category></item><item><title>Dicas de Git</title><link>https://akitaonrails.github.io/2009/07/05/dicas-de-git/</link><guid isPermaLink="true">https://akitaonrails.github.io/2009/07/05/dicas-de-git/</guid><pubDate>Sun, 05 Jul 2009 14:56:08 -0300</pubDate><description>&lt;p&gt;Se você ainda não aprendeu Git, um bom lugar para começar é meu &lt;a href="https://akitaonrails.github.io/2008/4/3/micro-tutorial-de-git"&gt;Micro Tutorial de Git&lt;/a&gt;. Para complementar, existem algumas dicas úteis para usar no dia-a-dia que vou explicar agora. Mesmo assim, Git é um mundo rico e complexo, existem infinitas possibilidades e você nunca vai se cansar de aprender coisas novas com ele. Recomendo também acompanhar o site &lt;a href="http://www.gitready.com/"target="_blank" rel="noopener"&gt;GitReady&lt;/a&gt; para continuar aprendendo truques novos.&lt;/p&gt;
&lt;p&gt;Para começar, vamos criar um novo repositório para exemplo:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Se você ainda não aprendeu Git, um bom lugar para começar é meu <a href="/2008/4/3/micro-tutorial-de-git">Micro Tutorial de Git</a>. Para complementar, existem algumas dicas úteis para usar no dia-a-dia que vou explicar agora. Mesmo assim, Git é um mundo rico e complexo, existem infinitas possibilidades e você nunca vai se cansar de aprender coisas novas com ele. Recomendo também acompanhar o site <a href="http://www.gitready.com/"target="_blank" rel="noopener">GitReady</a> para continuar aprendendo truques novos.</p>
<p>Para começar, vamos criar um novo repositório para exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rails teste  
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> teste  
</span></span><span class="line"><span class="cl">git init  
</span></span><span class="line"><span class="cl">git add .  
</span></span><span class="line"><span class="cl">git commit m <span class="s2">&#34;initial commit&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora vamos falar sobre como desfazer modificações, manipular commits, e muito mais.</p>
<h3>Undo<span class="hx:absolute hx:-mt-20" id="undo"></span>
    <a href="#undo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Uma coisa que muitos perguntam e podem se confundir devido a comandos do git como ‘reset’ e ‘revert’. Por exemplo, digamos que você editou alguns arquivos e quer retornar apenas um deles ao estado original do último commit:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">(</span>master<span class="o">)</span> $ git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>1. On branch master
2. Changed but not updated:
3. (use &#34;git add &lt;file&gt;…&#34; to update what will be committed)&lt;/file&gt;
4. (use &#34;git checkout — &lt;file&gt;…&#34; to discard changes in working directory)&lt;br&gt;
# &lt;/file&gt;
5. modified: README
6. modified: Rakefile  

# 

no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No exemplo, modificamos os arquivos ‘README’ e ‘Rakefile’. Agora digamos que você desistiu das mudanças no arquivo Rakefile e quer que ele volte ao estado original. Para isso apenas faça:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout Rakefile</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Porém, digamos que você queira realmente desfazer todas as modificações que você fez, nesse caso você faz:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset -hard </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso retornará todos os arquivos modificados de volta ao estado original do último commit. Porém esse comando apenas afeta arquivos que já fazem parte do repositório. Se você criou diversos arquivos novos e que se livrar deles também, faça isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clean d -f  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Adicionando arquivos ao repositório<span class="hx:absolute hx:-mt-20" id="adicionando-arquivos-ao-repositório"></span>
    <a href="#adicionando-arquivos-ao-reposit%c3%b3rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Uma coisa que muitos aprendem, decoram e fazem da forma errada é este comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit a -m <span class="s2">&#34;mensagem do commit&#34;</span>  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A opção “-a” essencialmente significa o equivalente a fazer o seguinte: ‘adicione todos os arquivos modificados ao próximo commit’. Normalmente é isso mesmo que queremos fazer, mas nem sempre. Por exemplo, isso não adiciona arquivos que já não estavam no repositório, para esses você precisa adicionar manualmente antes de fazer o commit, por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add novo_arquivo.txt  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Digamos que você tem esta situação:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./script/generate scaffold Post title:string</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso nos dá o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">(</span>master<span class="o">)</span> $ git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>1. On branch master
2. Changed but not updated:
3. (use &#34;git add &lt;file&gt;…&#34; to update what will be committed)&lt;/file&gt;
4. (use &#34;git checkout — &lt;file&gt;…&#34; to discard changes in working directory)&lt;br&gt;

# &lt;/file&gt;

5. modified: config/routes.rb  

#

6. Untracked files:
7. (use &#34;git add &lt;file&gt;…&#34; to include in what will be committed)&lt;br&gt;

# &lt;/file&gt;

8. app/controllers/posts_controller.rb
9. app/helpers/posts_helper.rb
10. app/models/
11. app/views/
12. db/
13. public/stylesheets/
14. test/fixtures/
15. test/functional/
16. test/unit/  
no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Preste sempre atenção – as pessoas se acostumam a “não ler” as coisas. Vejam que temos dois grupos de modificações:</p>
<ul>
<li>Changed but not updated – significa arquivos que já estavam no repositório e foram modificados</li>
<li>Untracked files – significa arquivos novos, que ainda não foram adicionados ao repositório</li>
</ul>
<p>Agora, façamos algumas operações:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add app/models/  
</span></span><span class="line"><span class="cl">git add config/routes.rb  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vejam o resultado:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">(</span>master<span class="o">)</span> $ git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>1. On branch master
2. Changes to be committed:
3. (use &#34;git reset HEAD &lt;file&gt;…&#34; to unstage)&lt;br&gt;

# &lt;/file&gt;

4. new file: app/models/post.rb
5. modified: config/routes.rb  

#

6. Untracked files:
7. (use &#34;git add &lt;file&gt;…&#34; to include in what will be committed)&lt;br&gt;

# &lt;/file&gt;

8. app/controllers/posts_controller.rb
9. app/helpers/posts_helper.rb
10. app/views/
11. db/
12. public/stylesheets/
13. test/fixtures/
14. test/functional/
15. test/unit/  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora eu tenho um novo grupo de arquivos:</p>
<ul>
<li>Changes to be committed – estes são arquivos modificados, que eu manualmente indiquei que serão inclusos no próximo commit, ou seja, eles estão numa região chamada “index”. Somente arquivos marcados no “index” entram no commit. Por exemplo:</li>
</ul>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit m <span class="s2">&#34;teste&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[master bd69909] teste  
 2 files changed, 4 insertions(&#43;), 0 deletions()  
 create mode 100644 app/models/post.rb  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notem que eu não precisei usar a opção “-a”. Na verdade, eu recomendaria não usar essa opção a menos que você saiba exatamente o que ela faz. Nesse último commit, apenas os dois arquivos que estavam no index entraram.</p>
<p>Como exemplo, digamos que esse último commit foi um engano, justamente porque “esquecemos” de adicionar os outros arquivos que estavam como “Untracked files”. Podemos desfazer um commit, sem perder as modificações que ela carrega fazendo isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset -soft HEAD~1  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Note a opção “—soft”, ela está indicando para desfazer o commit anterior (HEAD~1) mas sem perder seu conteúdo. Veja:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">(</span>master<span class="o">)</span> $ git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>1. On branch master
2. Changes to be committed:
3. (use &#34;git reset HEAD &lt;file&gt;…&#34; to unstage)&lt;br&gt;

# &lt;/file&gt;

4. new file: app/models/post.rb
5. modified: config/routes.rb  
…  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora, como adicionar os “Untracked files”? Uma forma que eu acabo fazendo é simplesmente copiando os paths indicados para um editor de texto, adicionar ‘git add’ e simplesmente colar os comandos no terminal. Outra forma é adicionar arquivo a arquivo de forma interativa, assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add -i </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code> staged unstaged path  
 1: &#43;2/-0 nothing app/models/post.rb  
 2: &#43;2/-0 nothing config/routes.rb

- Commands *****  
 1: [s]tatus 2: [u]pdate 3:®evert 4: [a]dd untracked  
 5: [p]atch 6: [d]iff 7: [q]uit 8: [h]elp  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ele nos dá um menu de comandos. Você pode usar a opção “4” para adicionar os novos arquivos. Ele nos levará à seguinte tela:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>What now\&gt; 4  
 1: app/controllers/posts_controller.rb  
 2: app/helpers/posts_helper.rb  
 3: app/views/layouts/posts.html.erb  
 4: app/views/posts/edit.html.erb  
 5: app/views/posts/index.html.erb  
 6: app/views/posts/new.html.erb  
 7: app/views/posts/show.html.erb  
 8: db/migrate/20090705175333_create_posts.rb  
 9: public/stylesheets/scaffold.css  
 10: test/fixtures/posts.yml  
 11: test/functional/posts_controller_test.rb  
 12: test/unit/helpers/posts_helper_test.rb  
 13: test/unit/post_test.rb  
Add untracked\&gt;\&gt;  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora você pode digitar o número do arquivo que quer adicionar e pressionar ‘enter’. Por exemplo, para adicionar o arquivo ‘test/unit/post_test.rb’ apenas digite ‘13[enter]’. Para adicionar vários de uma só vez, você pode usar um ‘range’ ou seja, se digitar “1-13” ele adicionará do arquivo 1 ao 13. Arquivos adicionados ficam marcados com “*” do lado.</p>
<p>Isso terminado, digite apenas ‘enter’ para retornar ao menu anterior e digite ‘q’ para sair do modo interativo. Agora, com os arquivos novos adicionados, temos isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>1. On branch master
2. Changes to be committed:
3. (use &#34;git reset HEAD &lt;file&gt;…&#34; to unstage)&lt;br&gt;

# &lt;/file&gt;

4. new file: app/controllers/posts_controller.rb
5. new file: app/helpers/posts_helper.rb
6. new file: app/models/post.rb
7. new file: app/views/layouts/posts.html.erb
8. new file: app/views/posts/edit.html.erb
9. new file: app/views/posts/index.html.erb
10. new file: app/views/posts/new.html.erb
11. new file: app/views/posts/show.html.erb
12. modified: config/routes.rb
13. new file: db/migrate/20090705175333_create_posts.rb
14. new file: public/stylesheets/scaffold.css
15. new file: test/fixtures/posts.yml
16. new file: test/functional/posts_controller_test.rb
17. new file: test/unit/helpers/posts_helper_test.rb
18. new file: test/unit/post_test.rb  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto, agora todos os arquivos que queremos estão no “index”, marcados como “Changes to be committed”. A descrição é bastante explicativa: arquivos marcados como “new file” são os arquivos novos que adicionamos, e os “modified” são aqueles que já estavam no repositório e colocamos no index. Basta fazer o commit agora:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit m <span class="s2">&#34;adicionando scaffold de Post&#34;</span>  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Revertendo commits<span class="hx:absolute hx:-mt-20" id="revertendo-commits"></span>
    <a href="#revertendo-commits" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>No <a href="/2008/4/3/micro-tutorial-de-git">Micro Tutorial de Git</a> expliquei sobre repositórios locais e remotos. Neste exemplo estamos apenas no repositório local. Se tivéssemos um remoto usaríamos ‘git remote’ para adicioná-lo e usaríamos os comandos ‘git push’ para enviar os commits locais para o repositório e ‘git pull’ para trazer os commits do repositório remoto para o local.</p>
<p>A regra básica é a seguinte:</p>
<blockquote>
  <p>Commits que você já enviou ao repositório remoto, ou commits que recebeu de lá, nunca devem ser modificados.</p>

</blockquote>
<p>Ou seja, não use comandos como ‘git reset —hard HEAD~1’ para apagar um commit, ou faça ‘rebase’ em cima de um branch que já exista no lado remoto. Se precisarmos reverter o que foi feito em um commit podemos primeiro fazer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>commit 89b53e7d0bfc4fdb4b5c389f5481dab5ddb2b83d  
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;  
Date: Sun Jul 5 15:06:44 2009 -0300

adicionando scaffold de Post

commit d394bee7ec01b2d90f00f20fc698364e9d943352  
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;  
Date: Sun Jul 5 14:41:39 2009 -0300

initial commit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<hr>
<p>Vamos reverter o commit mais recente. Para isso tomamos nota do seu identificados SHA1 e podemos executar este comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git revert -no-edit 89b53e7d0bfc4fdb4b5c389f5481dab5ddb2b83d  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>Agora teremos o seguinte log:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>commit 15bb972393f7794892dbf5d6a3097c533a68fbea  
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;  
Date: Sun Jul 5 15:13:03 2009 -0300

Revert &#34;adicionando scaffold de Post&#34; This reverts commit 89b53e7d0bfc4fdb4b5c389f5481dab5ddb2b83d.

commit 89b53e7d0bfc4fdb4b5c389f5481dab5ddb2b83d  
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;  
Date: Sun Jul 5 15:06:44 2009 -0300

adicionando scaffold de Post

commit d394bee7ec01b2d90f00f20fc698364e9d943352  
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;  
Date: Sun Jul 5 14:41:39 2009 -0300

initial commit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<hr>
<p>Entendeu? Isso criou um novo commit com a reversão do que fizemos antes. Por exemplo, se no commit original nós criamos um arquivo, neste novo commit esse arquivo será deletado, e assim por diante. Essa é a forma correta de reverter modificações que já estão no repositório local. Mas se você quiser reverter commits locais que ainda não enviou ao servidor, o ‘git reset —soft’ é uma das opções.</p>
<h3>Forçando modificações remotas<span class="hx:absolute hx:-mt-20" id="forçando-modificações-remotas"></span>
    <a href="#for%c3%a7ando-modifica%c3%a7%c3%b5es-remotas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Dissemos que commits remotos não devem ser modificados. Porém, digamos que você está numa situação onde precisa reorganizar seus commits. Existem algumas razões excepcionais, você saberá quando cair nelas :-) Estou sem um bom exemplo. Mas digamos que você precisou destruir ou reescrever commits e precisa modificar isso no servidor.</p>
<p>Antes de mais nada, considere o cenário de um repositório remoto controlado, onde você conhece todas as pessoas da equipe envolvidas nela. Ou seja, não é um projeto open source no Github. Nesse caso faça as modificações locais que precisa e no final faça isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git push -force</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>A opção “—force” irá reescrever os commits no repositório remoto para refletir o que você fez localmente. Agora o truque: avise todos os membros da sua equipe para baixar as modificações da seguinte forma:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git pull -rebase</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>Isto irá reescrever o histórico local de cada um dos membros da equipe. Não é uma operação que você quer fazer o tempo todo, por isso faça somente quando realmente precisar, simule isso em repositórios de teste para se sentir confortável antes de fazer isso no repositório do seu projeto, garanta que seus backups estão em dia. Reescrever o histórico no Git não é problemático, fazer sem saber o que está fazendo, é.</p>
<h3>Undelete<span class="hx:absolute hx:-mt-20" id="undelete"></span>
    <a href="#undelete" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Depois de tanto manipular commits, digamos que você tenha feito alguma besteira. Vamos simular uma “besteira”, digamos que você tenha apagado um commit que não queria, por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>commit 15bb972393f7794892dbf5d6a3097c533a68fbea  
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;  
Date: Sun Jul 5 15:13:03 2009 -0300

Revert &#34;adicionando scaffold de Post&#34; This reverts commit 89b53e7d0bfc4fdb4b5c389f5481dab5ddb2b83d.

…</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ul>
<li></li>
</ul>
<p>Para apagar “por engano” este commit mais recente, digamos que você “acidentalmente” tenha feito:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset -hard HEAD~1  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>Agora seu log ficará assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>commit 89b53e7d0bfc4fdb4b5c389f5481dab5ddb2b83d  
Author: AkitaOnRails &lt;fabioakita@gmail.com&gt;  
Date: Sun Jul 5 15:06:44 2009 -0300

adicionando scaffold de Post

…</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto, besteira feita, e agora? Faça de conta que esse commit representa todo o trabalho que você fez durante o dia todo. Será que agora você terá que fazer tudo de novo? Claro que não, o Git prevê esse tipo de coisa. Para começar, o ideal é que você tente recuperar um erro imediatamente quando o fez, não deixe para depois. Agora faça assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reflog</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>89b53e7 HEAD@{0}: HEAD~1: updating HEAD  
15bb972 HEAD@{1}: commit: Revert &#34;adicionando scaffold de Post&#34;  
89b53e7 HEAD@{2}: HEAD~1: updating HEAD  
4a41f10 HEAD@{3}: commit: Revert &#34;adicionando scaffold de Post&#34;  
89b53e7 HEAD@{4}: commit: adicionando scaffold de Post  
d394bee HEAD@{5}: HEAD~1: updating HEAD  
bd69909 HEAD@{6}: commit: teste  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O comando ‘git reflog’ listará os commits inacessíveis. Podemos ver na segunda linha o commit que acabamos de ‘apagar’. Para recuperá-lo, faça o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git merge 15bb972</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto, isso trás o commit perdido de volta ao histórico oficial e será como se nada tivesse acontecido. Qualquer coisa que já está no repositório pode ser recuperado, porém se você apagou algum novo arquivo que ainda não estava no commit daí não há o que fazer. Por isso mesmo sempre recomendamos que se faça commits o tempo todo.</p>
<h3>Branches no passado<span class="hx:absolute hx:-mt-20" id="branches-no-passado"></span>
    <a href="#branches-no-passado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Toda vez que queremos um novo branch usamos o seguinte comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout b novo_branch  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>Isso indica que criaremos o ‘novo_branch’ tendo como pai o branch onde estamos neste momento. Porém, digamos que queremos criar um branch a partir de um commit no passado. Podemos fazer desta forma:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout b novo_branch d394bee7ec01b2d90f00f20fc698364e9d943352  </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>Use o comando ‘git log’ para identificar o SHA1 do commit que quer. Visualmente ficaremos com o seguinte:</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2009/7/5/Picture_2_original.png" alt=""  loading="lazy" /></p>
<h3>Quem mexeu no meu Queixo?<span class="hx:absolute hx:-mt-20" id="quem-mexeu-no-meu-queixo"></span>
    <a href="#quem-mexeu-no-meu-queixo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Às vezes queremos saber quem mexeu em determinado trecho de um arquivo, às vezes porque faltou algum comentário e queremos perguntar a quem fez a modificação para saber se podemos mexer, refatorar ou coisa parecida.</p>
<p>Isso é simples, use o seguinte comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git blame base.rb
</span></span><span class="line"><span class="cl">…  
</span></span><span class="line"><span class="cl">^db045db <span class="o">(</span>David Heinemeier … 2004-11-24 … 15<span class="o">)</span> module ActiveRecord <span class="c1">#:nodoc:  </span>
</span></span><span class="line"><span class="cl">98dc5827 <span class="o">(</span>Pratik Naik … 2008-05-25 … 16<span class="o">)</span> <span class="c1"># Generic Active Record ex  </span>
</span></span><span class="line"><span class="cl"><span class="m">73673256</span> <span class="o">(</span>Jeremy Kemper … 2007-12-10 … 17<span class="o">)</span> class ActiveRecordError <span class="se">\&lt;</span>
</span></span><span class="line"><span class="cl">^db045db <span class="o">(</span>David Heinemeier … 2004-11-24 … 18<span class="o">)</span> end
</span></span><span class="line"><span class="cl"><span class="m">73673256</span> <span class="o">(</span>Jeremy Kemper … 2007-12-10 … 19<span class="o">)</span>
</span></span><span class="line"><span class="cl">0432d151 <span class="o">(</span>Pratik Naik … 2008-07-16 … 20<span class="o">)</span> <span class="c1"># Raised when the single-t</span>
</span></span><span class="line"><span class="cl"><span class="m">73673256</span> <span class="o">(</span>Jeremy Kemper … 2007-12-10 … 21<span class="o">)</span> <span class="c1"># (for example due to impr</span>
</span></span><span class="line"><span class="cl">605bc775 <span class="o">(</span>David Heinemeier … 2004-12-14 … 22<span class="o">)</span> class SubclassNotFound <span class="se">\&lt;</span> A  
</span></span><span class="line"><span class="cl">605bc775 <span class="o">(</span>David Heinemeier … 2004-12-14 … 23<span class="o">)</span> end  
</span></span><span class="line"><span class="cl">…</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(O trecho acima está encurtado)</p>
<p>Com isso podemos saber quem mexeu em cada linha, quando mexeu e em que commit está essa modificação. Assim podemos inclusive rastrear o commit que contém a modificação.</p>
<h3>Reescrevendo a última mensagem de commit<span class="hx:absolute hx:-mt-20" id="reescrevendo-a-última-mensagem-de-commit"></span>
    <a href="#reescrevendo-a-%c3%baltima-mensagem-de-commit" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Quantas vezes não fazemos um commit e nos damos conta que escrevemos a mensagem de commit com algum erro ortográfico, ou mesmo esquecemos de detalhar alguma coisa importante? Mas o Git nos ajuda nisso também. Em vez de desfazer o commit e refazê-lo, podemos usar este comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit -amend</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>Isso abrirá seu editor padrão (ex. Vim) e lhe dará a oportunidade de editar a mensagem de commit.</p>
<h3>Obtendo mais ajuda<span class="hx:absolute hx:-mt-20" id="obtendo-mais-ajuda"></span>
    <a href="#obtendo-mais-ajuda" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Não se esqueça, para conhecer alguns dos principais comandos do Git use este comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git -help</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>&ndash;</p>
<p>E para obter detalhes e instruções sobre como usar cada comando, faça assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git <span class="nb">help</span> commit</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso vale para todos os comandos. É a melhor forma de explorar e aprender a usar os comandos.</p>
<h3>Aprendendo mais<span class="hx:absolute hx:-mt-20" id="aprendendo-mais"></span>
    <a href="#aprendendo-mais" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Por hoje acho que isso já é suficiente. Continue sempre explorando mais e mais as possibilidades do Git. Acompanhe o trabalho do Scott Chacon, do Github, para saber mais. Outro site interessante é o <a href="http://book.git-scm.com/"target="_blank" rel="noopener">Git Community Book</a> com mais documentação. Se quiser um livro impresso, recomendo o <a href="http://www.amazon.com/Pro-Git-Scott-Chacon/dp/1430218339"target="_blank" rel="noopener">Pro Git</a>.</p>
<p>E, finalmente, não deixe de ajudar a tradução do site <a href="http://pt-br.gitready.com/"target="_blank" rel="noopener">GitReady</a> para português seguindo as instruções no post do <a href="http://www.tailorfontela.com.br/2009/03/07/gitready/"target="_blank" rel="noopener">Tailor Fontela</a>. Esse site tem mais dicas importantes para sua rotina com Git.</p>
]]></content:encoded><category>controle-de-versao</category><category>tutoriais</category><category>aprendizado</category></item><item><title>GitHub Oficialmente Lançado!!</title><link>https://akitaonrails.github.io/2008/04/10/github-oficialmente-lan-ado/</link><guid isPermaLink="true">https://akitaonrails.github.io/2008/04/10/github-oficialmente-lan-ado/</guid><pubDate>Thu, 10 Apr 2008 18:02:00 -0300</pubDate><description>&lt;p&gt;&lt;a href="http://brainspl.at/articles/2008/04/10/fork-you"target="_blank" rel="noopener"&gt;Ezra&lt;/a&gt; e &lt;a href="http://www.rubyinside.com/github-officially-launches-git-hosting-a-go-go-853.html"target="_blank" rel="noopener"&gt;Peter&lt;/a&gt; acabaram de anunciar: &lt;a href="http://github.com/"target="_blank" rel="noopener"&gt;GitHub&lt;/a&gt; não está mais em Beta. Ou seja: &lt;strong&gt;não precisa mais de convite para abrir uma conta!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Aproveitem! Criem suas contas e comecem a fazer forks! :-)&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="http://brainspl.at/articles/2008/04/10/fork-you"target="_blank" rel="noopener">Ezra</a> e <a href="http://www.rubyinside.com/github-officially-launches-git-hosting-a-go-go-853.html"target="_blank" rel="noopener">Peter</a> acabaram de anunciar: <a href="http://github.com/"target="_blank" rel="noopener">GitHub</a> não está mais em Beta. Ou seja: <strong>não precisa mais de convite para abrir uma conta!</strong></p>
<p>Aproveitem! Criem suas contas e comecem a fazer forks! :-)</p>
]]></content:encoded><category>open-source</category><category>controle-de-versao</category></item><item><title>Micro Tutorial de Git</title><link>https://akitaonrails.github.io/2008/04/02/micro-tutorial-de-git/</link><guid isPermaLink="true">https://akitaonrails.github.io/2008/04/02/micro-tutorial-de-git/</guid><pubDate>Wed, 02 Apr 2008 23:57:00 -0300</pubDate><description>&lt;p&gt;Como &lt;a href="http://www.akitaonrails.com/2008/4/3/finalmente-rails-mudando-de-svn-para-git"target="_blank" rel="noopener"&gt;acabei de postar&lt;/a&gt;, todo o repositório do Ruby on Rails vai migrar do antiquado Subversion para Git, em particular ficará no excelente serviço &lt;a href="http://github.com/"target="_blank" rel="noopener"&gt;Github&lt;/a&gt;. O David recomenda ler o tutorial &lt;a href="http://git.or.cz/course/svn.html"target="_blank" rel="noopener"&gt;Git – SVN Crash Course&lt;/a&gt; que é um guia para quem é usuário de SVN se acostumar com os comandos de Git. Mas eu resolvi fazer o meu próprio para tentar convencê-los de porque Git é importante.&lt;/p&gt;
&lt;h1&gt;Clone do Github&lt;/h1&gt;&lt;p&gt;Pra começar, o Github é quase um ‘social networking para programadores’. Ele torna fazer forks e contribuir para projetos algo trivial. Porém, ele ainda está fechado em Beta e é preciso convite para se cadastrar.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Como <a href="http://www.akitaonrails.com/2008/4/3/finalmente-rails-mudando-de-svn-para-git"target="_blank" rel="noopener">acabei de postar</a>, todo o repositório do Ruby on Rails vai migrar do antiquado Subversion para Git, em particular ficará no excelente serviço <a href="http://github.com/"target="_blank" rel="noopener">Github</a>. O David recomenda ler o tutorial <a href="http://git.or.cz/course/svn.html"target="_blank" rel="noopener">Git – SVN Crash Course</a> que é um guia para quem é usuário de SVN se acostumar com os comandos de Git. Mas eu resolvi fazer o meu próprio para tentar convencê-los de porque Git é importante.</p>
<h1>Clone do Github</h1><p>Pra começar, o Github é quase um ‘social networking para programadores’. Ele torna fazer forks e contribuir para projetos algo trivial. Porém, ele ainda está fechado em Beta e é preciso convite para se cadastrar.</p>
<p>Mas isso não significa que você precise disso. Para começar, a URL do repositório Git de cada projeto está acessível para quem quiser. Você precisa, sim, se quiser contribuir patches de volta para o repositório original de forma automatizada. Senão, mãos-à-obra:</p>
<p>Não vou reexplicar como se <a href="http://git.or.cz/gitwiki/WindowsInstall"target="_blank" rel="noopener">instala Git</a>. Por favor abram o <a href="http://www.google.com/search?client=safari&amp;rls=en-us&amp;q=how&#43;install&#43;git&#43;windows&amp;ie=UTF-8&amp;oe=UTF-8"target="_blank" rel="noopener">Google</a> e procurem as dezenas de receitas que já existem. Para usuários de Windows, uma dica, prefiram o método que usa Cygwin. Sem isso vocês não terão a outra excelente razão para se usar Git: o git-svn que permite usar Git com repositórios Subversion.</p>
<p>Por exemplo, digamos que você goste do projeto Merb. Mas quer fazer modificações pessoais que só servem para você.</p>
<p>O código do Merb fica no Github também, nesta URL: <a href="http://github.com/wycats/merb-core/tree/master"target="_blank" rel="noopener">http://github.com/wycats/merb-core/tree/master</a></p>
<p>Em cada projeto lá, você encontra o “Clone URL</span>” que é tudo que você precisa. Com o git instalado, vá ao seu diretório de desenvolvimento e faça:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone git://github.com/wycats/merb-core.git merb-core</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto, quando ele terminar você terá um clone do repositório original. Literalmente: porque de brinde você ainda ganha todo o histórico desde o primeiro commit! Esse é o equivalente a se fazer um ‘svn checkout’.</p>
<p>Feito isso, entre no diretório ‘merb-core’. Digite:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch -a</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>* master
  origin/HEAD
  origin/master</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O asterisco indica em qual branch você está. Os outros branches eu não mexeria se fosse você, eles servem para puxar novas atualizações do repositório original.</p>
<h2>Manipulando Branches Locais<span class="hx:absolute hx:-mt-20" id="manipulando-branches-locais"></span>
    <a href="#manipulando-branches-locais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A primeira coisa que recomendo é criar um novo branch. Pode ser qualquer nome, no meu caso eu sempre crio um chamado ‘working’.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git checkout -b working</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to a new branch &#34;working&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>  master

* working</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse comando cria o novo branch e já te muda para lá. Note como o asterisco mudou de branch. Agora você pode fazer as loucuras que quiser. Por exemplo, vou editar alguns arquivos aleatoriamente. Como sei o que mudou? Simples:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># On branch working

# Changed but not updated:

# (use &#34;git add &lt;file&gt;…&#34; to update what will be committed)

# 

# modified: CONFIG

# modified: README

# modified: TODO

# 

# Untracked files:

# (use &#34;git add &lt;file&gt;…&#34; to include in what will be committed)

# 

# akita.txt

no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Os arquivos que já existiam no repositório e que você mexeu, aparecem como ‘modified’, ou ‘deleted’. Se você criou alguma arquivo novo, ele aparece em ‘Untracked files’.</p>
<p>Você precisa adicionar ele ao repositório antes de dar commit. Da mesma forma como você precisaria fazer ‘svn add’.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git add akita.txt </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Outra forma, caso você tenha vários novos arquivos é usar a seguinte linha:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git add -i</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>staged unstaged path
  1: unchanged &#43;3/-1 CONFIG
  2: unchanged &#43;2/-0 README
  3: unchanged &#43;1/-0 TODO

&lt;b&gt;*Commands*&lt;/b&gt;
  1: status 2: update 3: revert 4: add untracked
  5: patch 6: diff 7: quit 8: help
What now&gt;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A opção “-i” significa ‘interativo’. Ele abre um sisteminha onde você pode digitar a opção “4”. Daí ele mostrará uma lista de arquivos cada um com um número na frente. Para adicioná-lo, digite o número de cada arquivo que quiser. No final dê enter sem digitar nada e ele volta para o menu acima. Quando acabar digite ‘7’ para voltar ao shell.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">What now&gt; <span class="m">4</span>
</span></span><span class="line"><span class="cl">  1: akita.txt
</span></span><span class="line"><span class="cl">Add untracked&gt;&gt; <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">* 1: akita.txt
</span></span><span class="line"><span class="cl">Add untracked&gt;&gt; 
</span></span><span class="line"><span class="cl">added one path</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto. Com os arquivos adicionados, basta fazer o commit:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git commit -a -m <span class="s2">&#34;meu primeiro commit&#34;</span>
</span></span><span class="line"><span class="cl">Created commit 457f597: meu primeiro commit
</span></span><span class="line"><span class="cl"> <span class="m">4</span> files changed, <span class="m">7</span> insertions<span class="o">(</span>+<span class="o">)</span>, <span class="m">1</span> deletions
</span></span><span class="line"><span class="cl"> create mode <span class="m">100644</span> akita.txt</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O git commit funciona parecido com svn commit, mas você tem o -a para que ele adicione automaticamente todo arquivo que foi modificado. O -m é como no svn, para colocar mensagens de commit.</p>
<p>Vejamos como isso se parece:</p>
<p style="text-align: center; margin: 3px"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/3/Picture_3.png" alt=""></p>
<p>Note como o branch master ficou para trás e o nosso ‘working’ subiu: isso porque agora ele tem modificações que não existem no master.</p>
<p>Vamos brincar mais um pouco. Digamos que você descubra um bug no master que precisa corrigir rapidamente e colocar em produção mas não quer colocar também o que você acabou de fazer no ‘working’ porque ainda não está pronto. O que fazer?? Não se preocupe, é aqui que o Git começa a brilhar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git checkout master</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to branch &#34;master&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git checkout -b meufix</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to branch &#34;meufix&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; ls akita.txt</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ls: akita.txt: No such file or directory</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vamos lá! Na primeira linha eu voltei ao branch master. De lá vou criar meu novo branch, com git checkout novamente, como já fizemos antes. Nesse ponto, o branch ‘meufix’ virou uma cópia exata do master, sem o código que eu estava trabalhando, que ficou no branch separado ‘working’. Como disse antes, o git checkout -b cria o novo branch e já me coloca nele.</p>
<p>O último comando é um ‘ls’ (o equivalente de ‘dir’, para quem é de Windows). Lembram que no branch ‘working’ eu criei um arquivinho ‘akita.txt’? Como esse arquivo só existe no branch ‘working’, quando criei o branch ‘meufix’ a partir do ‘master’ ele não veio, claro. Estou com uma cópia exata do branch principal.</p>
<p>Agora, vou fazer as correções que preciso.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git commit -a -m <span class="s2">&#34;minha correcao&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Created commit 760873a: minha correcao
 3 files changed, 6 insertions(&#43;), 1 deletions(-)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto! Fiz minhas correções, fiz o commit e agora posso jogar em produção. Meu chefe me deixa em paz e eu posso voltar ao que estava fazendo antes.</p>
<h2>Merges Triviais<span class="hx:absolute hx:-mt-20" id="merges-triviais"></span>
    <a href="#merges-triviais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Espere um instante, e agora? Em que situação estou. Vamos ver:</p>
<p style="text-align: center; margin: 3px"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/3/Picture_4.png" alt=""></p>
<p>Que bagunça! Agora eu tenho o branch ‘master’, que ainda é a cópia exata do repositório no Github. Tenho o branch ‘working’ onde eu acrescentei meu código novo. E tenho o branch ‘meufix’ com a correção sobre o master.</p>
<p>Ambos ‘working’ e ‘master’ tem um ancestral em comum, o ‘master’. O que vou fazer agora não é o comum, mas faça de conta que você tenha permissão de escrita no repositório central do Github e quer jogar essa correção que fizemos para lá. Ou seja, preciso pegar a correção, jogar no master e dali empurrar para o servidor online:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch
</span></span><span class="line"><span class="cl">  master
</span></span><span class="line"><span class="cl">* meufix
</span></span><span class="line"><span class="cl">  working</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git checkout master</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to branch &#34;master&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git merge meufix</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Updating a83054c..dee9e1f
Fast forward
 CHANGELOG | 1 &#43;
 CONFIG| 3 &#43;-
 TODO | 1 &#43;
 3 files changed, 4 insertions, 1 deletions(-)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch -d meufix</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Deleted branch meufix.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Apenas para ter certeza, na primeira linha usamos o comando git branch para ver quais branches existem e em qual estou. Feito isso, mudo de volta para o branch master.</p>
<p>O comando git merge faz o que esperamos: traz as correções que fizemos no branch ‘meufix’ de volta para o master. Finalmente o último comando, git branch -d apaga o branch ‘meufix’, já que não vamos mais precisar dele. Veja como é tudo muito rápido!! E sem precisa mexer em mais nada.</p>
<p>Bom, vamos voltar para nosso branch ‘working’ e continuar trabalhando nele.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># On branch working

# Changed but not updated:

# (use &#34;git add &lt;file&gt;…&#34; to update what will be committed)

# 

# modified: LICENSE

# modified: akita.txt

# 

no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto, estamos em ‘working’ e continuamos trabalhando. Veja como eu mexi em dois arquivos. Eis que me dou conta: <em>“As correções que acabei de dar merge no master!!”</em> Pois é, eu preciso trazer as correções do master para o working. Mas agora estou no meio de uma codificação e ainda não estou a fim de fazer commit. O que fazer? Eis a resposta:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git stash</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Saved &#34;WIP on working: 457f597… meu primeiro commit&#34;
HEAD is now at 457f597… meu primeiro commit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pense em ‘stash’ como uma pilha temporária de coisas. Eu não quis fazer commit dos dois arquivos me mexi acima, então simplesmente joguei para o stash e ele vai ficar lá por um momento. Agora vamos pegar as correções que estão no master:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git rebase master</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>First, rewinding head to replay your work on top of it…
HEAD is now at dee9e1f… minha correcao

Applying meu primeiro commit

error: patch failed: CONFIG :1
error: CONFIG: patch does not apply
error: patch failed: TODO:0
error: TODO: patch does not apply
Using index info to reconstruct a base tree…
Falling back to patching base and 3-way merge…
Auto-merged CONFIG
CONFLICT (content): Merge conflict in CONFIG
Auto-merged TODO
CONFLICT (content): Merge conflict in TODO
Failed to merge in the changes.
Patch failed at 0001.

When you have resolved this problem run &#34;git rebase —continue&#34;.
If you would prefer to skip this patch, instead run &#34;git rebase —skip&#34;.
To restore the original branch and stop rebasing run &#34;git rebase —abort&#34;.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Uau! Quanta coisa! Dois conflitos! <em>“Droga, mas não disseram que o Git faz merges inteligentes?”</em> Exatamente, ele fará o melhor merge possível. Já explico isso melhor. Mas de qualquer forma, ocasionalmente sempre haverá o caso onde o Git não tem como decidir, por exemplo, quando dois desenvolvedores modificam exatamente a mesma linha: qual dos dois tem prioridade? Então, vamos corrigir os erros na mão. Você pode usar qualquer ferramenta GUI</span> que manipula Diffs, é a mesma coisa:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>teste
&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD:
CONFIG
&gt;&gt;&gt;&gt;&gt;&gt;&gt; meu primeiro commit: CONFIG</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No primeiro arquivo, CONFIG</span>, felizmente era apenas uma linha branca a mais. Então vamos apenas retirar as marcações do diff de lá e salvar.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD: TODO
TESTE
===
&gt;&gt;&gt;&gt;&gt;&gt;&gt; meu primeiro commit:TODO</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O segundo arquivo, TODO</span>, tinha dois conteúdos diferentes para a mesma linha. Vou escolher a primeira e tirar as marcações de diff, e salvar o arquivo.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># On branch working

# Changes to be committed:

# (use &#34;git reset HEAD &lt;file&gt;…&#34; to unstage)

# 

# modified: README

# new file: akita.txt

# 

# Changed but not updated:

# (use &#34;git add &lt;file&gt;…&#34; to update what will be committed)

# 

# unmerged: CONFIG

# modified: CONFIG

# unmerged: TODO

# modified: TODO

# 

# Untracked files:

# (use &#34;git add &lt;file&gt;…&#34; to include in what will be committed)

# 

# .dotest/</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto. Os dois arquivos conflitados aparecem na lista ‘Changed but not updated’. Para continuar, precisamos usar git add para indicar ao Git que já resolvemos os conflitos:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add CONFIG</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git add TODO
</span></span><span class="line"><span class="cl">&gt;&gt; git rebase —continue</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Applying meu primeiro commit

Wrote tree ba231cbcbb1c61e67b4871e09be67b744559d28d
Committed: d5d5def2e451acc55fbcceeb634efffb0164b431</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora sim, terminou sem nenhum erro! O que ele fez? Ele continuou a operação de ‘rebase’ que começamos lá em cima e foi até o fim. Quando os conflitos apareceram vocês devem ter notado que no git status que dei antes apareceu um cara estranho chamado .dotest/. Ele guardou o que faltava aplicar no nosso branch e o comando git rebase —continue segue a partir de onde parou usando o que ficou gravado em .dotest/. Agora que o rebase terminou, esse diretório também some.</p>
<h2>Merge vs Rebase<span class="hx:absolute hx:-mt-20" id="merge-vs-rebase"></span>
    <a href="#merge-vs-rebase" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>E por que antes eu fiz ‘merge’ e agora eu usei esse tal de ‘rebase’?</p>
<p>Isso é importante! Prestem atenção. Rebase é o seguinte: ele dará rollback até o commit onde eu fiz o branch. Ou seja, o primeiro commit que eu fiz no começo do artigo será guardado e o branch voltará ao estado inicial. Sobre ele, ele pegará os commits novos que apareceram no branch original, o ‘master’, e o aplicará sobre meu branch ‘working’.</p>
<p>Dessa forma o ‘working’ ficará sincronizado com o principal. Agora, sobre isso, ele reaplicará todos os commits que eu fiz exclusivamente dentro do branch ‘working’.</p>
<p>Agora, o merge é o contrário: você trabalhou no branch e agora quer trazer as modificações de volta ao ‘master’, então você faz merge. Eu decorei assim: do master para meu branch, rebase. Do meu branch para o master, merge.</p>
<p>Fazendo isso, você normalmente terá mais conflitos quando fizer o rebase no seu branch temporário, mas resolvendo os conflitos ali, quando fizer o merge para o master, ele será automático e sem erros.</p>
<p>Agora, faltou uma coisa: o stash!! Lembram dos dois arquivos que eu guardei no stash, que eu ainda não havia feito commit? Vamos buscá-lo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git stash apply</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># On branch working

# Changed but not updated:

# (use &#34;git add &lt;file&gt;…&#34; to update what will be committed)

# 

# modified: LICENSE

# modified: akita.txt

# 

no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git stash clear</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto. O comando git stash apply trará nossas modificações de volta. Também poderia haver conflitos, o procedimento é o mesmo de antes. O último comando é para limpar o espaço do stash, é bom fazer isso sempre que aplicar as modificações de volta. O stash é muito útil e você pode ter quantos stashes quantos quiser, inclusive pode dar nome a eles.</p>
<p style="text-align: center; margin: 3px"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/3/Picture_6.png" alt=""></p>
<p>Vejamos mais sobre o rebase na prática. Antes disso, vamos fazer o commit desses 2 arquivos que recuperamos do stash:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add .</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git commit -a -m <span class="s2">&#34;mais um commit&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Created commit 69bc9a8: mais um commit
 2 files changed, 3 insertions(&#43;), 1 deletions</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O git add . (ponto) significa <em>“adicione todo novo arquivo que apareceu agora e que não estava no repositório antes.”</em> O segundo comando vocês já conhecem. Cuidado ao adicionar tudo de uma vez só, você pode acabar adicionando arquivos que não queria como logs e outras sujeiras. Use git status para se certificar do que está adicionando.</p>
<h2>Rebaseando seu Branch<span class="hx:absolute hx:-mt-20" id="rebaseando-seu-branch"></span>
    <a href="#rebaseando-seu-branch" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git checkout master</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to branch &#34;master&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git pull</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Merge made by recursive.
 lib/merb-core/bootloader.rb | 18 &#43;&#43;&#43;&#43;&#43;—
 lib/merb-core/controller/abstract_controller.rb | 10 
 lib/merb-core/controller/exceptions.rb | 8 &#43;
 lib/merb-core/controller/mixins/controller.rb | 10 
 lib/merb-core/controller/mixins/render.rb | 49 &#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;—
 lib/merb-core/core_ext/string.rb | 11 &#43;&#43;&#43;&#43;-
 lib/merb-core/dispatch/router/route.rb | 2 
 lib/merb-core/rack/handler/mongrel.rb | 2 &#43;-
 …/abstract_controller/controllers/render.rb | 12 &#43;
 spec/public/abstract_controller/render_spec.rb | 12 &#43;&#43;&#43;&#43;&#43;
 10 files changed, 105 insertions(&#43;), 29 deletions(-)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Na primeira linha, voltamos ao branch master. Acho que não precisa, mas eu prefiro seguir esse fluxo. Em seguida damos simplesmente git pull, que irá buscar novidades no repositório online do Github. Veja que vieram novas modificações!</p>
<p>Como eu ainda não terminei minha nova funcionalidade no branch ‘working’, quero que essas novidades estejam lá também para que eu possa testar contra meu código. Vamos repetir o que eu expliquei acima:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git checkout working</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to branch &#34;working&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git rebase master</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>First, rewinding head to replay your work on top of it…

HEAD is now at 76969b2… Merge branch &#39;master&#39; of git://github.com/wycats/merb-core

Applying meu primeiro commit

Wrote tree 0f9f0e2b72f45f14a7b941b8e064de8dba678566
Committed: 9c35dc1cead3f50c8b96494b01cf2b7a29fdb2fa

Applying mais um commit

Wrote tree 104dd17852e815cfc4e23345ae123ea390c997e3
Committed: c9a4f27905d47d57798ee2b3239eec1e162cb452</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto. Agora meu branch ‘working’ está sincronizado com o ‘master’.</p>
<h2>Reusando e Resincronizando seus Repositórios<span class="hx:absolute hx:-mt-20" id="reusando-e-resincronizando-seus-repositórios"></span>
    <a href="#reusando-e-resincronizando-seus-reposit%c3%b3rios" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Façamos de conta que eu acabei o código que estava trabalhando no ‘working’. Agora gostaria de utilizá-lo em outro projeto meu. Ou seja, em vez de usar o Merb original, quero usar o  customizado. Como fazer isso?</p>
<p>Antes de mais nada, ‘working’ não é um nome legal, quero mudar de nome para refletir o código que acabei de terminar. Para isso faça:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch -m working meu_merb</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>  master

* meu_merb</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Veja, como o branch ‘working’ sumiu e apareceu o ‘meu_merb’. Esse comando é mais ou menos um rename simples.</p>
<p>Agora, vou sair desse diretório, porque quero reusar esse branch de outro lugar, em outro projeto meu:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; <span class="nb">cd</span> ..</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git clone merb-core projeto-merb</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Initialized empty Git repository in /Users/akitaonrails/rails/sandbox/merb/projeto-merb/.git/</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então, primeiro dou um cd .. para voltar para meu diretório-raíz. De lá faço o clone do <em>branch atual onde eu estava, no caso meu_merb</em>.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; <span class="nb">cd</span> projeto-merb</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>* meu_merb</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora, entro novamente em meu projeto. Veja como só temos o branch meu_merb lá, com todas as customizações que eu tinha feito.</p>
<p>Vamos modificar e fazer um commit nesse novo projeto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> vi README</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&gt;&gt; git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># On branch meu_merb

# Changed but not updated

# (use &#34;git add &lt;file&gt;…&#34; to update what will be committed)

#

# modified: README

#

no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git commit -a -m <span class="s2">&#34;modificacao no projeto&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Created commit a730a96: modificacao no projeto
 1 files changed, 1 insertions(&#43;), 1 deletions</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git push</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>updating &#39;refs/heads/meu_merb&#39;
  from c9a4f27905d47d57798ee2b3239eec1e162cb452
  to a730a9698905fd6f9a933feca6777198bde2eb9e
 Also local refs/remotes/origin/meu_merb
Generating pack…
Done counting 3 objects.
Deltifying 3 objects…
 100% (3/3) done
Writing 3 objects…
 100% (3/3) done
Total 3 (delta 0), reused 0 (delta 0)
Unpacking 3 objects…
 100% (3/3) done
refs/heads/meu_merb: c9a4f27905d47d57798ee2b3239eec1e162cb452 -&gt; a30a9698905fd6f9a933feca6777198bde2eb9e</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ok, até o git commit todo mundo deve ter entendido. Mas e esse git push? Bom, se git pull trás tudo do repositório original, git push empurra as modificações de volta, se você tiver permissão de escrita nele, claro. Vamos voltar ao projeto original e checar isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; <span class="nb">cd</span> ../merb-core/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p style="text-align: center; margin: 3px"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/3/Picture_7.png" alt=""></p>
<p>Está vendo? Veja a descrição do commit que eu fiz fora do projeto e que agora está no meu branch!</p>
<p>Sabe o que isso significa? Digamos que você gosta de algum projeto open source, como Merb, Mephisto, Rubinius e qualquer outro que está em Git. Agora digamos que você quer mudar ele para melhor servir suas necessidades pessoais, ou seja, coisas que não é interessante contribuir de volta porque só serve para você.</p>
<p>Antigamente você tinha um problema: você fazia um svn checkout mas não tinha como fazer commits para lugar algum. Pior, toda vez que viesse uma mudança a partir do servidor original, era aquela bagunça.</p>
<p>Agora não só você pode fazer modificações num branch separado, fazer commits, merges e rebases locais, como também ainda fazer um clone do seu clone!! E tudo funciona perfeitamente.</p>
<h1>Limpando a Casa</h1>
<p>Finalmente, mais algumas dicas. Digamos que me arrependi de tudo que fiz! Quero que meu projeto Merb volte a ser como era antes. Bom, sempre podemos apagar o diretório original e fazer um clone do servidor online de novo, mas isso não seria esperto, façamos do jeito certo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt; git checkout master</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Already on branch &#34;master&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch -d meu_merb</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>error: The branch &#39;meu_merb&#39; is not a strict subset of your current HEAD.
If you are sure you want to delete it, run &#39;git branch -D meu_merb&#39;.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git branch -D meu_merb</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Deleted branch meu_merb.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git reset —hard HEAD</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>HEAD is now at dee9e1f… minha correcao</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git reset -hard HEAD^1</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>HEAD is now at a83054c… Added Time#to_json to Core Extensions, making the default JSON formatted output for Time objects ISO 8601 compatible.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vamos entender. Primeiro, eu me certifico que estou no branch ‘master’. Em seguida tento apagar o branch ‘meu_merb’. Ele se recusa porque eu fiz modificações nele que ainda não joguei de volta ao master, mas eu quero mesmo perder tudo então forço com a opção -D (“D” maiúsculo).</p>
<p>Agora, um comando novo, git reset —hard HEAD</span>^1. HEAD</span> é o topo da lista de commits. <span class="caps">HEAD</span>^1 significa, um commit para trás. <span class="caps">HEAD</span>^2 significa dois commits para trás, e assim por diante. Você também pode usar o nome do commit (que é um identificador <span class="caps">SHA</span>-1 meio longo). Cada vez que você dá reset para apagar um commit que você fez, ele mostra a descrição do commit que virou o novo <span class="caps">HEAD</span>. Eu fiz uma vez, e na segunda vez ele mostrou que o <span class="caps">HEAD</span> é um commit que não fui eu quem fez. É onde eu páro.</p>
<p>Esse comando serve para outra coisa também, digamos que fiz várias coisas bizarras como apagar o que não devia e tal. Se você se der conta disso antes de fazer um commit, basta digitar somente git reset —hard. Assim ele vai retornar tudo o que você fez até o último commit.</p>
<p>Prestem atenção! Ele vai desfazer <strong>tudo</strong> que você fez! Inclusive, se você apagou diretórios e renomeou um monte de coisas, ele vai recuperar <strong>tudo</strong> para você. E isso sem precisar ir online uma única vez!</p>
<h2>Golpe de Mestre!!<span class="hx:absolute hx:-mt-20" id="golpe-de-mestre"></span>
    <a href="#golpe-de-mestre" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora, apenas para demonstrar algumas das inteligências do Git, vou criar um novo branch a partir do master e fazer uma alteração que o Subversion provavelmente choraria:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git checkout -b working</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to a new branch &#34;working&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; vi LICENSE
</span></span><span class="line"><span class="cl">&gt;&gt; mv LICENSE licenca.txt</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git status</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># On branch working

# Changed but not updated

# (use &#34;git add/rm &lt;file&gt;…&#34; to update what will be committed)

#

# deleted: LICENSE

#

# Untracked files

# (use &#34;git add &lt;file&gt;…&#34; to include in what will be committed)

#

# licenca.txt

no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vejam: eu alterei o conteúdo do arquivo LICENSE</span> e ainda por cima renomeei para licenca.txt. No git status ele viu que eu <strong>apaguei</strong> o LICENSE</span> e que apareceu um novo arquivo licenca.txt. Só de curiosidade, eis o trecho que adicionei no topo do arquivo, agora licenca.txt:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Fabio Akita
&lt;www.akitaonrails.com&gt;

Copyright © 2008 Ezra Zygmuntowicz
…</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vou fazer o commit (não esquecer da opção -a !):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt; git add licenca.txt</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git commit -a -m <span class="s2">&#34;Novo arquivo&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Created commit a2e52ec: Novo arquivo
 2 files changed, 23 insertions(&#43;), 20 deletions(-)
 delete mode 100644 LICENSE
 create mode 100644 licenca.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora o golpe de misericórdia. Vou retornar ao branch master e modificar o <strong>mesmo arquivo</strong> LICENSE</span> em um lugar diferente – faça de conta que outro desenvolvedor fez modificações e eu dei git pull para trazê-las para o master.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt; git checkout master</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Switched to branch &#34;master&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; vi LICENSE
</span></span><span class="line"><span class="cl">&gt;&gt; git commit -a -m <span class="s2">&#34;modificando LICENSE&#34;</span>
</span></span><span class="line"><span class="cl">Created commit 6c74941: modificando LICENSE
</span></span><span class="line"><span class="cl"> <span class="m">1</span> files changed, <span class="m">2</span> insertions<span class="o">(</span>+<span class="o">)</span>, <span class="m">8</span> deletions</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O que aconteceu aqui? Vamos recordar: no branch ‘working’ eu modifiquei o conteúdo de LICENSE</span> e renomei para licenca.txt. No branch ‘master’ eu apenas modifiquei o conteúdo de LICENSE</span> e, de curiosidade eis o que eu fiz:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Copyright © 2008 Ezra Zygmuntowicz

Artigo sobre Git

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
…</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu acrescentei uma linha e apaguei um parágrafo. Agora é a hora da verdade. Farei o merge do branch ‘working’ sobre o ‘master’. Se fosse o Subversion aconteceria o seguinte: o arquivo LICENSE</span> do master ia continuar lá e apareceria um novo arquivo licenca.txt, mas os dois conteúdos não seria misturados, que é o que gostaríamos que acontecesse. <em>“Lógico, nenhum sistema pode saber isso!”</em> é o que alguém diria. Tolinho:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; git merge working</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Renamed LICENSE =&gt; licenca.txt
Auto-merged licenca.txt
Merge made by recursive.
 LICENSE =&gt; licenca.txt | 3 &#43;
 1 files changed, 3 insertions(&#43;), 0 deletions(-)
 rename LICENSE =&gt; licenca.txt (94%)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Prestem atenção ao que o comando git merge disse. Primeiro, vejamos se o arquivo LICENSE</span> ainda existe:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt; ls LICENSE</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ls: LICENSE: No such file or directory</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora, vejamos o que tem no arquvo licenca.txt.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Fabio Akita
&lt;www.akitaonrails.com&gt;

Copyright © 2008 Ezra Zygmuntowicz

Artigo sobre Git

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
…</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Estou vendo cara de espanto de alguém aí na platéia?? Entenderam o que aconteceu? Eu não fiz um svn delete nem um svn rename, nem dei nenhuma dica ao Git sobre o que eu fiz, mas ele entendeu que o conteúdo de licenca.txt tem como antecessor o LICENSE</span>, e ele entendeu que alguém mexeu no LICENSE</span> em outro branch, portanto, ambos deveriam ser mesclados e, claro, ele inteligentemente fez isso!!</p>
<p>Estão vendo? São coisas como essas que fazem o Git tão popular.</p>
<h2>Conclusão Arrebatadora<span class="hx:absolute hx:-mt-20" id="conclusão-arrebatadora"></span>
    <a href="#conclus%c3%a3o-arrebatadora" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Percebam outra coisa: eu criei branches, apaguei commits, apaguei os branches e tudo isso sem sair do mesmo diretório. Nada de sub-diretórios bagunçando tudo. Posso trabalhar em branches paralelos sem nenhum problema. Mais do que isso: tudo muito rápido! Posso até apagar as coisas e recuperar com um reset ou de outro branch sem precisar tocar a minha rede! Está tudo dentro do único diretório de sistema que ele precisa, chamado .git.</p>
<p>Melhor ainda, nada de .cvs ou .svn em <strong>todo</strong> sub-diretório do meu projeto. Apenas um pequeníssimo .git na raíz e mais nada. Querem ver quão pequeno isso é?</p>
<p>Lembrem-se que o .git guarda <strong>TODO</span></strong> o histórico do projeto desde o primeiro commit. Bom, com o .git dentro o projeto merb-core tem 2.7Mb.</p>
<p>Se retirar o .git, o projeto fica com 1.5Mb. Novamente <strong>TODO</span></strong> o histórico do repositório. 800kb, pouco mais da metade do código-fonte, contendo todas as modificações feitas por todos os desenvolvedores do projeto desde o primeiro dia, e tudo off-line na sua máquina. Por isso que mesmo o clone inicial, que buscou tudo do repositório Github, é tão rápido.</p>
<p>Espero que isso tenha dado uma luz sobre como usar o Git no dia a dia. Em <a href="/2008/2/4/akitaonrails-on-mephisto-0-8-on-git">outro artigo</a> eu demonstrei como fazer um clone de um repositório Subversion em vez de Git. Uma vez feito isso, localmente são todos os mesmos fluxos que mostrei acima.</p>
<p>Branches são instantâneos, merges são instantâneos, clones são muitos rápidos, clones locais são instantâneos. É tudo absurdamente rápido. E não é porque mexi em poucos arquivos, o Linus Torvalds faz mais de 20 mil merges de arquivos por dia, tudo com velocidade – e ele é exigente, vocês sabem disso.</p>
<p>Agora com o Edge Rails migrando para Github, qualquer um pode fazer um clone de lá, criar uma versão customizada para seus projetos, e continuar sincronizando com o repositório original, mantendo seu repositório local sempre em dia, coisa que era impossível de fazer com Subversion sem fazer muito malabarismo.</p>
<p>Eu escrevi esse artigo ao mesmo tempo em que ia digitando os exemplos acima, não travei um único minuto preso em algum beco sem saída. O Git é absurdamente flexível e performático (pois é escrito em C, não dá para ser mais rápido do que isso). Definitivamente o SCM</span> da nova geração.</p>
<p>Como aprender mais? Procure <a href="http://www.google.com/search?hl=en&client=safari&rls=en-us&q=site%3Awww.akitaonrails.com+git&btnG=Search">no meu site</a> os artigos que falei sobre Git (são vários!). Compre o <a href="/2007/10/26/aprenda-git-pelo-peepcode">screencast do Peepcode</a> ensinando Git, é a forma mais rápida! E procure os vários artigos e tutoriais pela internet, tem muito material.</p>
]]></content:encoded><category>tutoriais</category><category>controle-de-versao</category></item><item><title>Finalmente! Rails mudando de SVN para Git!!</title><link>https://akitaonrails.github.io/2008/04/02/finalmente-rails-mudando-de-svn-para-git/</link><guid isPermaLink="true">https://akitaonrails.github.io/2008/04/02/finalmente-rails-mudando-de-svn-para-git/</guid><pubDate>Wed, 02 Apr 2008 21:29:00 -0300</pubDate><description>&lt;p&gt;O David Hansson &lt;a href="http://weblog.rubyonrails.com/2008/4/2/rails-is-moving-from-svn-to-git"target="_blank" rel="noopener"&gt;acabou de anunciar&lt;/a&gt; que todo o repositório do Rails irá migrar do atual Subversion para Git, no &lt;a href="http://github.com/"target="_blank" rel="noopener"&gt;Github&lt;/a&gt; !!&lt;/p&gt;
&lt;p&gt;É uma excelente notícia que deve ajudar o Git ainda mais a se estabelecer como o &lt;em&gt;de-facto&lt;/em&gt; gerenciador de código fonte para a comunidade Rails.&lt;/p&gt;
&lt;p&gt;Além disso, o gerenciamento de ticket tracking vai sair do Trac para &lt;a href="http://www.lighthouseapp.com/"target="_blank" rel="noopener"&gt;Lighthouse&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;O repositório atual em SVN e o Trac serão congelados quando a migração acontecer para que nenhum svn:externals quebre, mas se você quiser o Edge Rails, terá que migrar para Git também (o que, convenhamos, não é nenhum sacrifício).&lt;/p&gt;</description><content:encoded><![CDATA[<p>O David Hansson <a href="http://weblog.rubyonrails.com/2008/4/2/rails-is-moving-from-svn-to-git"target="_blank" rel="noopener">acabou de anunciar</a> que todo o repositório do Rails irá migrar do atual Subversion para Git, no <a href="http://github.com/"target="_blank" rel="noopener">Github</a> !!</p>
<p>É uma excelente notícia que deve ajudar o Git ainda mais a se estabelecer como o <em>de-facto</em> gerenciador de código fonte para a comunidade Rails.</p>
<p>Além disso, o gerenciamento de ticket tracking vai sair do Trac para <a href="http://www.lighthouseapp.com/"target="_blank" rel="noopener">Lighthouse</a>.</p>
<p>O repositório atual em SVN e o Trac serão congelados quando a migração acontecer para que nenhum svn:externals quebre, mas se você quiser o Edge Rails, terá que migrar para Git também (o que, convenhamos, não é nenhum sacrifício).</p>
<p>Parabéns ao Rails Core!</p>
]]></content:encoded><category>rails</category><category>controle-de-versao</category><category>open-source</category></item><item><title>GIT: Muito Promissor</title><link>https://akitaonrails.github.io/2007/09/22/git-muito-promissor/</link><guid isPermaLink="true">https://akitaonrails.github.io/2007/09/22/git-muito-promissor/</guid><pubDate>Sat, 22 Sep 2007 05:44:00 -0300</pubDate><description>&lt;p&gt;No &lt;a href="https://akitaonrails.github.io/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta/"&gt;artigo anterior&lt;/a&gt; falei um pouco sobre &lt;a href="http://git.or.cz/"target="_blank" rel="noopener"&gt;&lt;strong&gt;GIT&lt;/strong&gt;&lt;/a&gt; e ele parece mesmo promissor. Meu objetivo é não fazer uma migração big-bang. Não me interessa neste momento ter todo o trabalho de retirar meu Subversion do ar, instalar um servidor GIT, etc. Meu objetivo é ter um repositório local que seja: rápido, estável, confiável, que faça o arroz-com-feijão com competência. Só o tempo dirá. Mas os primeiros testes foram muito satisfatórios!&lt;/p&gt;
&lt;p&gt;Instalar o GIT varia. Em Windows: não faço idéia. Em Linux: Google – ao que parece existem tarballs, pacotes para Yum, pacotes para Apt-get. Deve ser bem simples, façam sua lição de casa. Para Mac: MacPorts. Foi um pouco estranho, porque o &lt;strong&gt;git-svn&lt;/strong&gt; parece que usa Perl5, e meu SVK que já estava instalado também. Acabei desinstalando o SVK e suas dependências e instalando novamente – deve ser o sono, já é bem tarde. Resumindo, acredito que a receita (para começar em um MacPorts limpo), seja algo assim:&lt;/p&gt;</description><content:encoded><![CDATA[<p>No <a href="/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta/">artigo anterior</a> falei um pouco sobre <a href="http://git.or.cz/"target="_blank" rel="noopener"><strong>GIT</strong></a> e ele parece mesmo promissor. Meu objetivo é não fazer uma migração big-bang. Não me interessa neste momento ter todo o trabalho de retirar meu Subversion do ar, instalar um servidor GIT, etc. Meu objetivo é ter um repositório local que seja: rápido, estável, confiável, que faça o arroz-com-feijão com competência. Só o tempo dirá. Mas os primeiros testes foram muito satisfatórios!</p>
<p>Instalar o GIT varia. Em Windows: não faço idéia. Em Linux: Google – ao que parece existem tarballs, pacotes para Yum, pacotes para Apt-get. Deve ser bem simples, façam sua lição de casa. Para Mac: MacPorts. Foi um pouco estranho, porque o <strong>git-svn</strong> parece que usa Perl5, e meu SVK que já estava instalado também. Acabei desinstalando o SVK e suas dependências e instalando novamente – deve ser o sono, já é bem tarde. Resumindo, acredito que a receita (para começar em um MacPorts limpo), seja algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo port install git-core +doc +svn</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Considerando que todos conseguiram instalar, agora deve haver, junto com o bom e velho script svn, os scripts <strong>git</strong> e <strong>git-svn</strong>. O que eu quero fazer: usar o git para gerenciar um repositório local – de tal forma que eu possa trabalhar (fazer commits, branches, merges) no meu micro sem depender de uma conexão ao servidor e, ao final do dia, poder “empurrar” todos os changesets (não apenas a última modificação, mas todo o histórico do dia) de volta ao servidor Subversion. Este <a href="https://wiki.bnl.gov/dayabay/index.php?title=Synchronizing_Repositories#GIT_and_SVN">link</a> foi muito útil.</p>
<p>Eu fiz um micro teste (micro mesmo, ultra-simples) usando o subversion de um projeto no <span class="caps">SVN</span> do Google Code.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir <span class="o">[</span>diretorio_local_do_projeto<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">[</span>diretorio_local_do_projeto<span class="o">]</span>
</span></span><span class="line"><span class="cl">git-svn init -username <span class="o">[</span>seu_username<span class="o">]</span> <span class="o">[</span>url_do_seu_svn<span class="o">]</span>/trunk</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Isso inicia o repositório local do git e associa ao <span class="caps">SVN</span> remoto. A partir daqui existem várias opções, leiam <a href="http://utsl.gen.nz/talks/git-svn/intro.html#howto-fetch">neste artigo</a> para entender as alternativas. Dentre as principais está fazer uma cópia <strong>completa</strong> do repositório – o que significa <strong>todas</strong> as revisões -, ou fazer um download apenas do <span class="caps">HEAD</span> do seu trunk. Se o projeto for pequeno, tanto faz. Se o histórico for importante, será necessário replicar tudo, ou replicar a partir de uma determinada revisão.<br>
<br>
Um aviso: puxar muitas revisões (sei lá, 500, 1000, 10 mil?) pode demorar <strong><span class="caps">MUITAS</span></strong> horas. Mesmo meu projetinho, com menos de 100 revisões, sob uma conexão de 8Mbit levou alguns minutos. Felizmente é possível pegar a partir de uma revisão até o <span class="caps">HEAD</span>, assim:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git-svn fetch</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Onde “<span class="caps">XXX</span>” é o número da revisào a partir da qual você quer pegar. Se quiser apenas o <span class="caps">HEAD</span>, pegue apenas a última. Não sabe qual o número? Oras “svn log —limit XX” para puxar o log das últimas XX revisões do seu <span class="caps">SVN</span>.<br>
<br>
Uma recomendação do artigo é criar um novo branch – questões cosméticas? Aliás, gerenciar branches – que é algo que fazemos com muita cerimônia no <span class="caps">SVN</span> – deve ser rotina no <span class="caps">GIT</span>: faça quantas quiser, quando quiser. Faça um por arquivo se for maluco. Diz a propaganda que o <span class="caps">GIT</span> aguenta. Na prática existem muitos recursos do <span class="caps">SVN</span> que usamos raramente porque sabemos que pode trazer dores-de-cabeça. E se pensarmos melhor, oras, se precisamos planejar tanto para usar um recurso é porque ele é muito mal feito. Todo bom recurso deveria ser à prova de noobies. Veremos nos próximos dias se o <span class="caps">GIT</span> sobrevive à propaganda (espero que sim). Enfim, criar branches:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout -track -b <span class="o">[</span>qualquer_nome<span class="o">]</span> git-svn</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
“git-svn” é o nome do branch que o script “git-svn” criou automaticamente. Fora ele, por default, todo repositório tem pelo menos um branch, chamado “master”. Ao criar um branch, ele automaticamente se torna o novo default. Para ver todos os branches disponíveis use:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git branch -a</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Já podemos brincar. Posso alterar meu código localmente neste novo working copy. Toda vez que precisar fazer um commit, basta fazer:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit -a -m <span class="s2">&#34;[seu comentário]&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Note que esses commits não estão indo ao servidor. Está tudo local. Para ver as revisões, o comando é parecido com o <span class="caps">SVN</span>:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Finalmente, depois de alterar meu working copy como quiser, digamos que estou pronto para publicar as modificações de volta ao <span class="caps">SVN</span>:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git-svn dcommit</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Isso deve fazer um replay do histórico deste o último fetch de volta ao servidor, teoricamente mantendo o histórico intacto, com o autor, os comentários dos commits e tudo mais.<br>
<br>
Agora, no meu antigo working copy do <span class="caps">SVN</span>, fiz algumas pequenas modificações e depois svn commit. No novo working copy do <span class="caps">GIT</span>, para fazer o equivalente ao antigo “svn up”, faço o seguinte:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git-svn fetch
</span></span><span class="line"><span class="cl">git-svn rebase</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Isso trará todas as revisões. O interessante é que parece que ele faz um “rollback” local e dá um “replay” nas minhas próprias revisões sobre as que vieram do <span class="caps">SVN</span>. Ainda não entendi bem o algoritmo, mas isso parece mais eficiente do que um merge bidimensional apenas do último snapshot local (working copy) contra os deltas do <span class="caps">HEAD</span> do servidor. Um conceito que parece importante é que o <span class="caps">GIT</span> – ao contrário de todos os outros – não rastreia arquivos, ele <strong>rastreia conteúdo</strong>. Segundo Linus Torvalds, isso é muito mais importante. Por exemplo, você consegue rastrear de onde veio uma determina linha, ou trecho: de qual arquivo ele veio, depois quem fez o “copy and paste” e para que arquivo ele foi parar. Todo o histórico. Fazer um blame nunca foi tão fácil.<br>
<br>
Enfim. Ao final fiz um teste mais interessante: no working copy do <span class="caps">SVN</span>, eu movi um arquivo de lugar, um básico “mv”. Isso significa:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">svn delete <span class="o">[</span>arquivo antigo<span class="o">]</span>
</span></span><span class="line"><span class="cl">svn add <span class="o">[</span>arquivo novo<span class="o">]</span>
</span></span><span class="line"><span class="cl">svn commit -m <span class="s2">&#34;&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
No working copy do <span class="caps">GIT</span>, antes de fazer o fetch, eu peguei o <strong>mesmo arquivo</strong>, que ainda está com o nome antigo, e alterei seu conteúdo. Se fosse no <span class="caps">SVN</span>, aconteceria o seguinte: o svn up traria a ordem de deleção. Como o arquivo foi modificado ele não seria deletado. O novo arquivo viria e você acabaria com o novo e com o antigo, agora fora do repositório (marcado como “?”). <br>
<br>
E no <span class="caps">GIT</span>? Bem, depois to fetch/rebase ele foi inteligente o suficiente para saber que o arquivo antigo foi renomeado! Ele trouxe a já aplicou a mudança que havia feito localmente no <span class="caps">GIT</span>!! E não me deixou com sujeira para rastrear, copiar do antigo, colar no novo e apagar o arquivo velho, como o <span class="caps">SVN</span> faria. Isso foi interessante. Só esse recurso já me convenceu. <br>
<br>
E isso porque ainda nem comecei a raspar na casca. Como agora tenho um histórico local, digamos que estou na praia, sem internet, trabalhando desconectado mas mesmo assim fazendo commits :-) (wishful thinking, eu sei) mas suponha que eu pense <em>“Putz, não era isso que eu queria comitar”</em> mas agora eu posso reverter commits até a revisão anterior que eu queira. Vejam neste <a href="http://www.kernel.org/pub/software/scm/git/docs/howto/revert-branch-rebase.html">tutorial</a> sobre como trabalhar com o recurso de reversão.<br>
<br>
O pessoal do <a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Samba</a> e do <a href="http://live.gnome.org/GitForGnomeDevelopers">Gnome</a> tem tutoriais parecidos. No primeiro caso, parece que o Samba agora fica gerenciado em <span class="caps">GIT</span>, então dá para ter uma idéia de como é trabalhar num ambiente puramente <span class="caps">GIT</span>. No caso do Gnome eles ensinam como colaborar com <span class="caps">GIT</span> mas interfaceando com a infraestrutura <span class="caps">SVN</span> (gerar patches e tudo mais). E aí mais um truque: em vez de usar “init” como fiz, eles usam “clone” e ao final há um passo extra: mudar de volta ao branch “master”, fazer um “merge” com seu branch atual e só depois “dcommit”. Parece que eles preferem fazer merge no master antes de enviar os commits ao <span class="caps">SVN</span>. No fundo acho que é a mesma coisa do que falei aqui.<br>
<br>
E falando em merge, veja o que acontece se fizer o merge entre o branch “working”, que modifiquei nos passos acima, e o “master”:<br>
<br>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout master
</span></span><span class="line"><span class="cl">git merge -squash working</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Updating 780040f..00e988e&lt;br&gt;
Fast forward&lt;br&gt;
Squash commit  not updating
 init.rb | 4 
 lib/{fixes.rb =&gt; activerecord_fixes.rb} | 66 &#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;&#43;—————
 2 files changed, 47 insertions(&#43;), 23 deletions(-)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<br>
Notem como ele realmente entendeu que aconteceu um rename de arquivos. Também mostra quantas linhas mudaram em cada um. Ainda estou surpreso que a modificação que fiz no “fixes.rb” – do working copy do <span class="caps">GIT</span> – foi parar corretamente e automaticamente dentro do “activerecord_fixes.rb”, que foi o arquivo renomeado vindo do <span class="caps">SVN</span>. E pelo que li, se deletar um arquivo usando “rm” ele automaticamente entende que é para tirar do repositório, diferente do svn onde precisamos explicitamente usar “svn delete”, “svn mv”, etc. Para adicionar novos arquivos, é igual: “git add [arquivo]”.<br>
<br>
Aliás, o comando “git checkout” é semelhante ao “svn switch”. Não sei como ele faz, mas este comando foi instantâneo! Tecnicamente significa que agora meu working copy é o “master” e não tem as modificações que fiz no “working”. Pra isso ele teve que remontar minha estrutura local. No <span class="caps">SVN</span> não é nem de perto tão rápido: para começar que ele precisa se comunicar com o servidor, só no tempo que leva para conectar ao servidor para puxar o update, o <span class="caps">GIT</span> já acabou o trabalho. Aliás, switch é outra coisa que pode dar dor de cabeça. Nunca confiei muito nesse comando no <span class="caps">SVN</span>. Pra começar que você precisa ter feito commit de tudo antes do switch. No <span class="caps">GIT</span>, ele transfere as modificações que ainda não sofreram commit entre branches, durante o switch! Muito esperto. Ou seja, posso alterar arquivos, não fazer commit, ir tocando de branches e só comitar quando eu escolher o branch que eu queria!<br>
<br>
No fim, este ultra-micro-teste foi muito interessante, bem sucedido e me deixou muito satisfeito. Não deu nenhum problema inesperado, as mensagens do <span class="caps">GIT</span> foram muito solícitas e fáceis de entender e ele fez muito melhor do que eu esperava. Significa que vou usá-lo por mais algum tempo.
]]></content:encoded><category>controle-de-versao</category><category>ferramentas-de-desenvolvimento</category></item><item><title>Jogar Pedra em Gato Morto: por que Subversion não presta</title><link>https://akitaonrails.github.io/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta/</link><guid isPermaLink="true">https://akitaonrails.github.io/2007/09/22/jogar-pedra-em-gato-morto-por-que-subversion-no-presta/</guid><pubDate>Sat, 22 Sep 2007 01:03:00 -0300</pubDate><description>&lt;p&gt;Adiantando o final do artigo para os impacientes: está procurando um sistema de versionamento (VCS)? Tente &lt;a href="http://git.or.cz/"target="_blank" rel="noopener"&gt;&lt;strong&gt;GIT&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hoje eu estava ouvindo um podcast muito interessante no FLOSS Weekly com &lt;a href="http://www.twit.tv/floss19"target="_blank" rel="noopener"&gt;Junio Hamano&lt;/a&gt;, mantenedor do sistema de versionamento &lt;a href="http://en.wikipedia.org/wiki/Git_%28software%29"target="_blank" rel="noopener"&gt;GIT&lt;/a&gt;, que é um sistema &lt;strong&gt;DISTRIBUÍDO&lt;/strong&gt; de controle de código fonte.&lt;/p&gt;
&lt;p&gt;Nunca ouvi falar.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://s3.amazonaws.com/akitaonrails/assets/2007/9/22/stress_one.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://darcs.net/"target="_blank" rel="noopener"&gt;Darcs&lt;/a&gt;, &lt;a href="http://www.selenic.com/mercurial/"title="hg"target="_blank" rel="noopener"&gt;Mercurial&lt;/a&gt;, &lt;a href="http://bazaar-vcs.org/"title="bzr"target="_blank" rel="noopener"&gt;Bazaar-NG&lt;/a&gt;, &lt;a href="http://www.perforce.com/"target="_blank" rel="noopener"&gt;Perforce&lt;/a&gt; por alguma razão me dão uma sensação de serem mais famosos – Google usa Perforce – ou pelo menos aparecem mais nas notícias. Não sei.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Adiantando o final do artigo para os impacientes: está procurando um sistema de versionamento (VCS)? Tente <a href="http://git.or.cz/"target="_blank" rel="noopener"><strong>GIT</strong></a>.</p>
<p>Hoje eu estava ouvindo um podcast muito interessante no FLOSS Weekly com <a href="http://www.twit.tv/floss19"target="_blank" rel="noopener">Junio Hamano</a>, mantenedor do sistema de versionamento <a href="http://en.wikipedia.org/wiki/Git_%28software%29"target="_blank" rel="noopener">GIT</a>, que é um sistema <strong>DISTRIBUÍDO</strong> de controle de código fonte.</p>
<p>Nunca ouvi falar.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/9/22/stress_one.png" alt=""  loading="lazy" /></p>
<p><a href="http://darcs.net/"target="_blank" rel="noopener">Darcs</a>, <a href="http://www.selenic.com/mercurial/"title="hg"target="_blank" rel="noopener">Mercurial</a>, <a href="http://bazaar-vcs.org/"title="bzr"target="_blank" rel="noopener">Bazaar-NG</a>, <a href="http://www.perforce.com/"target="_blank" rel="noopener">Perforce</a> por alguma razão me dão uma sensação de serem mais famosos – Google usa Perforce – ou pelo menos aparecem mais nas notícias. Não sei.</p>
<p>Uso Subversion por razões históricas. Na época que comecei a usar, as outras opções mais famosas eram CVS ou ClearCase – eu sei, haviam outros, mas eu era ignorante, o que posso dizer. E ser mais estável que CVS, parecendo CVS e sem ser CVS, para mim fechou a venda. Enfim, não gosto de nenhum dos dois. Falar que CVS é ruim é um eufemismo hiperbólico (isso existe?). Ele nasceu torto, vai morrer torto, daí a primeira metade do título do artigo.</p>
<p>ClearCase, é uma ferramenta comercial e não é barata (mudou?). Se fosse barata até pensaria em começar a considerar mas também não gosto da <em>pose</em> dos “especialistas da Rational”. Qual a vantagem de ser um especialista de commits? O dia que eu precisar de certificação pra fazer commits … o mundo acabou! <a href="http://www.bitkeeper.com/"target="_blank" rel="noopener">BitKeeper</a> também é comercial, na época em que Linus resolveu usar BitKeeper houve muita controvérsia mas depois todo mundo esqueceu. Um fator que pode não parecer muito, é o formato do repositório, leiam este <a href="http://64.233.169.104/search?q=cache:b6n7lvK2lGEJ:keithp.com/blog/Repository_Formats_Matter.html&#43;repository&#43;formats&#43;matter&amp;hl=en&amp;ct=clnk&amp;cd=1&amp;client=safari"target="_blank" rel="noopener">artigo</a></p>
<p>Darcs, Bazaar e Mercurial são excelentes projetos. Não me agrada muito no Darcs o fato dele ser feito em Haskell. Nada contra Haskell, eu mesmo acho que é uma excelente linguagem, mas como meu dia-a-dia não envolve Haskell – e o de muita gente – se eu precisar de suporte, alguma coisa extra, vai ser mais difícil conseguir.</p>
<p>Mercurial e, acho, que Bazaar são feitos em Python. Mesmo assim parece que suas performances são muito boas. Bazaar parece que segue a linha do antigo Arch – acho que um dos primeiros a sair da linha centralizada do CVS. Por alguma razão nunca me senti muito atraído por elas. Pelo menos todos nasceram distribuídos. Conclusão: se quiser outro controlador de versão, escolha um distribuído.</p>
<p>Falar bem de GIT não significa falar mal desses outros. Mas podemos falar muito mal de CVS, então falaremos: gerenciar branches é uma piada de mal gosto. Arquivos corrompidos nem se fala. Tags? Pff. Procurem no Google “cvs sucks” e acharão uma lista de motivos de porque CVS é ruim.</p>
<p>Subversion? Como disse, uso por motivos históricos. O slogan é <em>“um CVS bem feito”</em>. De fato, ele corrompe muito menos, branches são mais fáceis. No geral ele é melhor. Toneladas de ferramentas, livros, hostings, bibliotecas, etc. Gostando ou não se tornou o “padrão”. Quando implementei a versão beta num projeto, acho que em 2003, minha equipe quase cortou minha cabeça. Mas uso desde então em todos os meus projetos, com variados níveis de satisfação. Mas não vamos cuspir no prato: Subversion serviu seu papel muito bem, apesar de não ser perfeito. Mas acho que passou da hora de mudar.</p>
<p>Interessado no GIT, acabei achando este vídeo com o próprio Linus Torvalds, criador do GIT, dando uma palestra no Google:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/4XpnKHJAok8"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>É o que eu digo: todo bom projeto, open source ou não, precisa de caras como Linus. Ele vai direto ao ponto: <em>“os criadores do Subversion são idiotas!”</em> E repete isso diversas vezes em mais de uma hora de palestra. <em>“É o projeto mais sem sentido que eu já vi”</em>. Hilário! Fugindo um pouco do ponto acho que todo bom projeto precisa de pessoas carismáticas na liderança. Não importa o que eles falam, são eles que vendem o produto. O Linus começou o GIT, é LÓGICO que ele acha os outros ruins – e não fará nenhuma tentativa de ser <em>policamente correto</em> ou conciliador, o que eu acho ótimo. E mudando de assunto de novo: é por isso que eu gosto tanto da série <a href="http://en.wikipedia.org/wiki/House_%28TV_series%29"target="_blank" rel="noopener">House M.D.</a>. Recomendo.</p>
<p>Voltando ao assunto, <strong>ele tem razão</strong>! Oras, Subversion é uma versão <em>um pouco</em> melhorada do CVS. Ele faz branches muito mais rápido: mas quem se importa? Fazer branches só é importante se o Merge depois for excepcionalmente melhor do que no CVS. E convenhamos: gerenciar merges em Subversion é outra piada de mal gosto (tenho um amigo que já bateu o carro de nervoso por causa disso hehe). Mesmo assim, eis a segunda metade do título do artigo.</p>
<p>Aham. Não quer dizer que não pode ser feito: claro que pode, mas não chega nem perto de receber o título de “trivial”. Ainda não usei GIT, mas é o que Linus argumenta: nenhuma outra alternativa que existe supera GIT. Eu argumentaria que talvez Darcs e Mercurial sejam bons competidores, mas o discurso de Linus foi muito persuasivo.</p>
<p>Os problemas mais comuns? Eu gosto de fazer commits o tempo todo. Mudei uma única função em um único arquivo? Commit. Mudei uma única linha de comentário? Commit. Isso funciona bem se eu trabalho sozinho. Coloque mais um desenvolvedor no jogo e a coisa começa a ficar complicada muito fácil: eu já não posso fazer mais isso com frequência porque eu posso interromper o trabalho do outro.</p>
<p>Ok, vamos fazer branches. Criar o branch é trivial. Switch. Posso fazer commits à vontade agora. Chegou a hora do merge. Vamos ver, qual foi mesmo a revisão em que o branch foi criado? Log. Finalmente, merge e … conflicts. Vamos gastar algum tempo fazendo conflict resolutions (nem sei porque algumas coisas aparecem como conflito). Agora faça isso por dois ou três dias seguidos fazendo merges mais de uma vez por dia. A coisa fica tediosa muito rápido. Esse é o problema que sistemas distribuídos prometem eliminar. Veremos se GIT ajuda.</p>
<p>Então encontrei um sub-projeto chamado <a href="http://git.or.cz/course/svn.html"target="_blank" rel="noopener">git-svn</a> (<a href="http://utsl.gen.nz/talks/git-svn/intro.html"target="_blank" rel="noopener">estes</a> <a href="http://tsunanet.blogspot.com/2007/07/learning-git-svn-in-5min.html"target="_blank" rel="noopener">outros</a> também são interessantes), cuja intenção é usar GIT em ambientes onde Subversion seja mandatório: seja numa empresa, seja em projetos como no Google Code ou outro hosting. Você mantém um repositório centralizado em Subversion mas faz todo seu trabalho em um repositório descentralizado do GIT e no final envia os changesets de volta.</p>
<p>Inicialmente (algumas horas atrás, na verdade) pensei nisso porque migrar 100% para GIT significaria sacrificar algumas ferramentas. Capistrano, por exemplo, depende de Subversion. Mas olhando um pouco melhor parece que <a href="http://scie.nti.st/2007/9/4/capistrano-2-0-with-git-shared-repository"target="_blank" rel="noopener">suportar GIT no Capistrano</a> é próximo de trivial também.</p>
<p>Ao que parece temos um vencedor. Ainda é um trabalho em andamento – já que a moda hoje em dia é estar sempre em Beta :-) No Windows, esqueçam: precisa de Cygwin e mesmo assim tem alguns problemas. Ferramentas gráficas? Acho que só em Linux e mesmo assim são poucas – e meio feinhas, vamos concordar. Mas, como Windows não me interessa e o próprio Subversion eu só uso via shell, então tanto faz. Como diria DHH: <em>“pra quê fazer parte do mainstream?”</em></p>
<p>Estes artigos tem algumas comparações interessantes: <a href="http://bryan-murdock.blogspot.com/2007/03/cutting-edge-revision-control.html"target="_blank" rel="noopener">Cutting Edge Revision Control</a>, <a href="http://jaredrobinson.com/blog/?p=62"target="_blank" rel="noopener">What’s Happening with Version Control Systems</a>. Ainda preciso ler este <a href="http://git.or.cz/gitwiki/GitFaq?highlight=%28merge%29#head-f7dc61b87eab4db58fe90ce48cc1d47fd50e6bea"target="_blank" rel="noopener">FAQ</a> do GIT. Aliás, o <a href="http://git.or.cz/gitwiki/FrontPage"target="_blank" rel="noopener">GitWiki</a> tem muitos artigos e links muito interessantes.</p>
<p>Talvez o melhor artigo para todo aspirante a deserdor de Subversion, que queira entender a arquitetura geral do GIT e porque ele é melhor que os outros, seja este <a href="http://utsl.gen.nz/talks/git-svn/intro.html"target="_blank" rel="noopener">An introduction to git-svn for Subversion/SVK users and deserters</a>. Assim que tiver mais tempo vou testar GIT nos casos mais comuns do dia-a-dia de trabalho.</p>
<p>E assistam ao vídeo do Linus é <strong>MUITO HILÁRIO</strong>!! Kudos a Linus pela apresentação, todo palestrante deveria ter o mesmo senso de humor. Aliás: ponto negativo a todo palestrante que lê seus próprios slides (eu incluso, porque acabo caindo nessa armadilha de vez em quanto). Não preciso do palestrante se for só pra ler os slides: eu mesmo posso baixar e ler. Abaixo os bullet points!!</p>
]]></content:encoded><category>controle-de-versao</category><category>ferramentas-de-desenvolvimento</category></item></channel></rss>