diff --git a/OUTLINE.md b/OUTLINE.md new file mode 100644 index 0000000..140d47c --- /dev/null +++ b/OUTLINE.md @@ -0,0 +1,230 @@ +# Pro Git, 3rd Edition — Outline & Punch List + +The canonical section-by-section punch list for the 3rd-edition restructure. +Content-level staleness and Git 3.0 details live in +[`REVISION_PLAN.md`](REVISION_PLAN.md); this file tracks the new shape of the book and +execution. Each chapter heading notes which current files the chapter is built from. + +Chapter order (Git at Scale is second-to-last, before Internals): + +> Preface · 1 Getting Started · 2 Git Basics · 3 Git Branching · 4 Distributed Git · +> 5 Git Toolkit · 6 Customizing Git · 7 Git and Agents · 8 Git Servers · +> 9 Git at Scale · 10 Git Internals · Appendix A: Git Commands + +**Effort** is the writing/editing size for that section (Minimal = mechanical sweeps +only; Small = targeted edits; Medium = significant additions or restructuring; +Large = new or mostly-new material). Update **Status** as work proceeds +(`not started` → `in progress` → `drafted` → `done`). + +## Preface + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| P | Preface | The Git 3.0 moment (SHA-256, reftable, `main`, Rust); new-defaults-first stance with dual-path coverage; what's new this edition (scale, agents, worktrees); experimental-command caveats; decide fate of `introduction.asc` (recommend folding in) | Large | not started | + +## Chapter 1 — Getting Started + +*From `book/ch01/`, all seven sections retained.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 1.0 | Chapter intro & Summary | Wrapper prose and chapter summary refresh | Minimal | not started | +| 1.1 | About Version Control | Local vs. centralized vs. distributed version control, why DVCS won | Minimal | not started | +| 1.2 | A Short History of Git | BitKeeper origins and the kernel's needs; extended with the 2.x→3.0/Rust arc | Small | not started | +| 1.3 | What is Git? | Snapshots, three states, integrity; hash passage rewritten around "SHA-256 by default, SHA-1 in older repos" | Medium | not started | +| 1.4 | The Command Line | Why the book teaches the CLI | Minimal | not started | +| 1.5 | Installing Git | Per-platform install refresh; building from source now requires the Rust toolchain | Medium | not started | +| 1.6 | First-Time Git Setup | Identity, editor, config levels; default-branch subsection flips to "`main` is the default; here's how to override" | Medium | not started | +| 1.7 | Getting Help | `git help`/`-h`; community channels refreshed (Stack Overflow, Discussions, Discord; IRC demoted) | Small | not started | + +## Chapter 2 — Git Basics + +*From `book/ch02/`; 2.7 expands `ch02/aliases.asc` to deliver the "basic config" promise.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 2.0 | Chapter intro & Summary | Wrapper prose and chapter summary refresh | Minimal | not started | +| 2.1 | Getting a Git Repository | `git init` (what 3.0 creates: `main`, SHA-256, reftable) and `git clone`; `git://` demoted to legacy | Medium | not started | +| 2.2 | Recording Changes to the Repository | Status/add/diff/commit/rm/mv lifecycle; keeps ignoring-files basics (depth moves to 6.2) | Small | not started | +| 2.3 | Viewing the Commit History | `git log`, formatting, filtering; `.mailmap` note | Small | not started | +| 2.4 | Undoing Things | Reframed around `restore`/`switch` as primary verbs; `checkout`/`reset` as legacy forms; amend | Medium | not started | +| 2.5 | Working with Remotes | Remote add/fetch/pull/push/inspect/rename; `pull.rebase` normalized as baseline | Small | not started | +| 2.6 | Tagging | Annotated vs. lightweight tags, sharing and checking out tags | Minimal | not started | +| 2.7 | Basic Configuration and Aliases | Everyday config in one place (identity recap, editor, useful knobs) plus aliases; pointer to Ch 6 for depth | Medium | not started | + +## Chapter 3 — Git Branching + +*From `book/ch03/` plus one net-new section (3.5 Worktrees — zero existing coverage +anywhere in the book; placeholder at `ch03/worktrees.asc`).* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 3.0 | Chapter intro & Summary | Wrapper prose; summary gains worktrees | Small | not started | +| 3.1 | Branches in a Nutshell | Commit/pointer model, HEAD; `git switch` taught first, `checkout` legacy; all diagrams regenerated for `main` | Medium | not started | +| 3.2 | Basic Branching and Merging | Hotfix/topic walk-through, merge conflicts; strategy is `ort` | Small | not started | +| 3.3 | Branch Management | Listing, merged/unmerged filters; renaming discussion revisited for a main-default world | Small | not started | +| 3.4 | Branching Workflows | Long-running and topic branches; adds GitHub Flow, trunk-based development, stacked branches/PRs | Medium | not started | +| 3.5 | Worktrees | New section: `worktree add`/`list`/`remove`/`lock`; hotfix-mid-feature, parallel builds, one-checkout-per-agent (pointer to Ch 7); new diagrams | Large | not started | +| 3.6 | Remote Branches | Remote-tracking branches, pushing, tracking setup, deleting | Small | not started | +| 3.7 | Rebasing | Basic rebase, `--onto`, the perils, rebase-vs-merge | Small | not started | + +## Chapter 4 — Distributed Git + +*Was `book/ch05/`, now `book/ch04/`, plus material rescued from the dropped GitHub +chapter into 4.3 (placeholder at `ch04/pull-requests.asc`). Rescued prose (from the +removed `ch06/2-contributing.asc` and `ch06/3-maintaining.asc`; recover via git +history) needs human rewriting to be forge-neutral, not just moving.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 4.0 | Chapter intro & Summary | Wrapper prose; the "next chapter: GitHub" handoff rewritten | Small | not started | +| 4.1 | Distributed Workflows | Centralized, integration-manager, dictator-lieutenants; adds trunk-based development and situates forge/PR collaboration | Medium | not started | +| 4.2 | Contributing to a Project | Commit guidelines; private small/managed teams; email workflow reframed as the kernel/git.git niche; `range-diff`; IMAP-password fix | Medium | not started | +| 4.3 | Pull Requests and Forges | New section: fork → branch → PR → review → merge, forge-neutral; fork syncing and `refs/pull/*` rescued from the GitHub chapter; merge strategies; `gh` CLI | Large | not started | +| 4.4 | Maintaining a Project | `apply`/`am`, contributor branches, determining what's introduced, integrating, tagging releases; adds PR-based maintenance counterpart | Medium | not started | + +## Chapter 5 — Git Toolkit + +*Was `book/ch07/`, now `book/ch05/`, renamed from "Git Tools." +(`subtree-merges.asc` has no section row of its own: it is included from within +`advanced-merging.asc` as a subsection and stays there.)* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 5.0 | Chapter intro & Summary | Wrapper prose and chapter summary refresh (renamed from "Git Tools") | Minimal | not started | +| 5.1 | Revision Selection | Single revisions, short SHAs (64-char), ancestry, ranges, reflog | Minimal | not started | +| 5.2 | Interactive Staging | `add -i`/`-p`, staging hunks | Minimal | not started | +| 5.3 | Stashing and Cleaning | Stash/apply/pop/branch, `clean`; `stash save` → `stash push`; `show -u` | Small | not started | +| 5.4 | Signing Your Work | Adds SSH signing (`gpg.format=ssh`) as the mainstream path alongside GPG; modern keys; verification; forge display | Large | not started | +| 5.5 | Searching | `git grep`, `log -S`/`-G`, line-log `-L` | Minimal | not started | +| 5.6 | Rewriting History | Amend, rebase -i, autosquash; adds `git history` (reword/split/fixup/drop) as the everyday tool; `filter-repo` leads, `filter-branch` demoted | Large | not started | +| 5.7 | Reset Demystified | The three trees, reset vs. checkout, path forms; cross-referenced with `restore` | Minimal | not started | +| 5.8 | Advanced Merging | Conflict tooling, ours/theirs, criss-cross; `ort` naming; `merge-tree` note | Small | not started | +| 5.9 | Rerere | Reuse recorded conflict resolutions | Minimal | not started | +| 5.10 | Debugging with Git | `blame`, bisect; adds `git bisect run` | Small | not started | +| 5.11 | Submodules | Adding, updating, publishing, nesting; "newer Git" framing normalized (heaviest mechanical file: 35 renames) | Small | not started | +| 5.12 | Bundling | `git bundle` for offline transfer | Minimal | not started | +| 5.13 | Replace | Reworked: grafts removed in 3.0; taught around `replace` incl. `--graft` | Medium | not started | +| 5.14 | Credential Storage | Cache/store/osxkeychain; Git Credential Manager as the cross-platform standard; custom helpers | Medium | not started | + +## Chapter 6 — Customizing Git + +*Was `book/ch08/`, now `book/ch06/`; 6.2 (placeholder at `ch06/ignoring.asc`) is +assembled from `ch02/recording-changes.asc` (`_ignoring`) and `config.asc` excludes +material.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 6.0 | Chapter intro & Summary | Wrapper prose and chapter summary refresh | Minimal | not started | +| 6.1 | Git Configuration | Config levels + XDG path; adds `includeIf`, `core.hooksPath`, `safe.directory`/`safe.bareRepository`, `init.defaultBranch`, 3.0 extension knobs | Large | not started | +| 6.2 | Ignoring Files | Promoted section: pattern rules in depth, nested `.gitignore`, global excludes, `check-ignore`; assembled from Ch 2 + config material | Medium | not started | +| 6.3 | Git Attributes | Diff/merge drivers, smudge/clean filters, `export-ignore`; LFS cross-ref to 9.6 | Small | not started | +| 6.4 | Git Hooks | Hook catalog refreshed with post-2014 hooks; shared hooks via `core.hooksPath`; hook managers note | Small | not started | +| 6.5 | An Example Git-Enforced Policy | Server-side enforcement capstone (update hook + client mirror); open decision: keep (recommended) or cut | Minimal | not started | + +## Chapter 7 — Git and Agents + +*Entirely new chapter (placeholders at `book/ch07-git-and-agents.asc` + `book/ch07/`); +7.3 is a condensed reflow of the dropped Appendix B (`book/B/`, removed — recover via +git history) plus gitoxide and per-library SHA-256 notes.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 7.0 | Chapter intro & Summary | New chapter: why AI coding agents change repo usage — many actors, high commit volume, review as bottleneck | Medium | not started | +| 7.1 | Workflows for Agents | Branch-per-agent, worktree-per-agent (builds on 3.5), sandboxed checkouts, commit/push cadence, draft PRs as hand-off | Large | not started | +| 7.2 | Tools and Interfaces | How agents drive Git: the CLI as universal interface, `gh`, MCP servers, forge APIs and webhooks | Large | not started | +| 7.3 | Libraries | Programmatic Git: libgit2 (+ bindings), JGit, go-git, Dulwich, gitoxide; SHA-256 support per library (condensed reflow of old Appendix B) | Medium | not started | +| 7.4 | Guiding Agents | Repo-level agent instructions (AGENTS.md-style), skills, commit/branch conventions for machine contributors, ignoring agent artifacts | Large | not started | +| 7.5 | Tips and Pitfalls | Reviewing agent-authored history, attribution trailers, safety rails (protected branches, force-push, `safe.*`), secrets hygiene | Medium | not started | + +## Chapter 8 — Git Servers + +*Was `book/ch04/`, now `book/ch08/`; 8.8 is reworked from `gitlab.asc`, 8.9 from +`hosted.asc` (currently a GitHub/Bitbucket-era stub that deferred to the dropped +GitHub chapter).* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 8.0 | Chapter intro & Summary | Wrapper prose and chapter summary refresh | Small | not started | +| 8.1 | The Protocols | Local, SSH, smart HTTP (protocol v2) as the living protocols; dumb HTTP cut to history; `git://` legacy | Medium | not started | +| 8.2 | Getting Git on a Server | Bare repositories, `--shared`, putting a repo on a box | Minimal | not started | +| 8.3 | Generating Your SSH Public Key | Ed25519 as the recommended type; DSA-era advice removed | Small | not started | +| 8.4 | Setting Up the Server | The `git` user, `authorized_keys`, `git-shell`; xinetd/sysvinit dropped | Small | not started | +| 8.5 | Git Daemon | Trimmed to a short treatment (systemd unit), framed as niche | Small | not started | +| 8.6 | Smart HTTP | `git-http-backend` behind current Apache/nginx config; token/OIDC auth notes | Medium | not started | +| 8.7 | GitWeb | Open decision: cut to a sidebar mention (recommended) or keep trimmed | Small | not started | +| 8.8 | Self-Hosted Forges | Reworked from GitLab-only: Gitea/Forgejo as the lightweight lead, GitLab heavyweight; install sketch + day-one basics; new screenshots | Large | not started | +| 8.9 | Hosted Options | The hosting landscape (GitHub, GitLab.com, Codeberg, Sourcehut, Azure DevOps) and choosing hosted vs. self-hosted | Medium | not started | + +## Chapter 9 — Git at Scale + +*Entirely new chapter (placeholders at `book/ch09-git-at-scale.asc` + `book/ch09/`) — +no existing coverage of shallow/partial clones, sparse checkout, Scalar, LFS, or +monorepos anywhere in the current book. 9.5 keeps the ops-level performance story; +on-disk format detail stays in Chapter 10.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 9.0 | Chapter intro & Summary | New chapter: why big repos and teams strain Git; map of the scaling toolbox | Medium | not started | +| 9.1 | Shallow Clones | `--depth`, `--shallow-since`, deepening/unshallowing, CI patterns, limitations | Medium | not started | +| 9.2 | Partial Clone | Blob/tree filters, promisor remotes, lazy on-demand fetch, `git backfill`; when partial beats shallow | Large | not started | +| 9.3 | Sparse Checkout | `git sparse-checkout`, cone mode, sparse index; working in a directory subset | Medium | not started | +| 9.4 | Scalar | `scalar clone`/`register` as the batteries-included front door; what it enables | Medium | not started | +| 9.5 | Keeping Large Repositories Fast | `git maintenance` schedules, commit-graph, multi-pack-index, FSMonitor (ops story; formats stay in Ch 10) | Medium | not started | +| 9.6 | Large Files | Git LFS basics; when to prefer it vs. partial clone; keeping binaries out of history | Medium | not started | +| 9.7 | Monorepos | Putting it together: trade-offs, layout/ownership practices, real-world Scalar deployments, forge constraints | Large | not started | + +## Chapter 10 — Git Internals + +*From `book/ch10/`, all eight sections retained.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| 10.0 | Chapter intro & Summary | Wrapper prose ("last chapter" framing still true) and summary refresh | Minimal | not started | +| 10.1 | Plumbing and Porcelain | The `.git` directory tour, updated for a 3.0 repo (reftable dirs, no legacy `branches/`) | Small | not started | +| 10.2 | Git Objects | Blobs/trees/commits retaught with object-format framing: SHA-256 default + SHA-1 interop; walk-throughs regenerated | Large | not started | +| 10.3 | Git References | Refs/HEAD/tags/remotes plus a substantial new reftable subsection (format and why); loose/packed-refs reframed as legacy | Large | not started | +| 10.4 | Packfiles | Pack/idx formats and deltification; adds multi-pack-index and commit-graph format detail | Medium | not started | +| 10.5 | The Refspec | Fetch/push refspec syntax; PR-refs cross-reference retargeted to 4.3 | Small | not started | +| 10.6 | Transfer Protocols | Rebuilt around protocol v2 (ref filtering, negotiation); dumb protocol shrunk to a historical aside | Large | not started | +| 10.7 | Maintenance and Data Recovery | `git maintenance` as the modern face of `gc`; reflog/`fsck` recovery; big-file removal redone with `filter-repo` | Medium | not started | +| 10.8 | Environment Variables | The environment-variable survey | Minimal | not started | + +## Appendix A — Git Commands + +*Was `C-git-commands.asc`, now `A-git-commands.asc` — the only appendix kept.* + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| A | Git Commands | Command index retargeted to the new chapter map; adds `switch`, `restore`, `worktree`, `sparse-checkout`, `scalar`, `maintenance`, `range-diff`, `history`; notes 3.0 removals; External Systems category trimmed; broken xrefs fixed | Medium | not started | + +## Dropped outright + +Numbering below is the *2nd edition's*; section numbers in the right column are the new outline's. + +| Dropped (2nd ed.) | Where its survivors go | +|---|---| +| Ch 6 GitHub (`book/ch06/`) | PR flow, fork syncing, PR refs → **4.3/4.4**; account setup, org management, Markdown, scripting/API → cut | +| Ch 9 Git and Other Systems (`book/ch09/`) | Cut entirely (SVN/Hg/P4 bridges, importers); command-index entries trimmed to match | +| Appendix A Git in Other Environments (`book/A/`) | Cut entirely (GUIs, IDEs, shell prompts) | +| Appendix B Embedding Git (`book/B/`) | Library survey reflows, condensed, into **7.3** | +| `introduction.asc` | Open decision: fold into the Preface or Ch 1 opener; recommend folding into the Preface | + +## Open decisions + +1. `policy.asc` (6.5) — keep (recommended) or cut. +2. GitWeb (8.7) — cut to a sidebar mention (recommended) or keep trimmed. +3. `introduction.asc` — fold into the Preface (recommended) or keep as a standalone opener. +4. Large Files (9.6) — lives in Git at Scale (recommended) vs. Git Servers. +5. Performance split — ops story in 9.5, format detail in 10.4/10.7 (recommended), or consolidate in one place. + +## Cross-cutting & mechanical + +| Section | Title | Description | Effort | Status | +|---|---|---|---|---| +| X1 | master → main sweep | ~602 renames book-wide per `book_master_to_main_inventory.md`; coordinate with diagram regeneration | Large | not started | +| X2 | SHA-256 example hashes | Regenerate example hashes/transcripts book-wide at 64 chars; settle the legacy-SHA-1-example policy | Large | not started | +| X3 | Version reframing | "Written for Git 2" → 3.x; normalize "since 2.x" framing to baseline behavior | Medium | not started | +| X4 | Restructure plumbing | New include map in `progit.asc`; wrapper files for Ch 7 and Ch 9; delete `ch06/`, `ch09/`, `A/`, `B/` and their images; renumber chapter dirs | Medium | done | +| X5 | Cross-reference repair | Retarget xrefs into dropped chapters (`refspec.asc`, `A-git-commands.asc`, `contributing.asc`, `hosted.asc`) — done via anchors preserved in the 4.3 placeholder; Ch 2's `_ignoring` anchor moves with the 6.2 content work | Small | done | +| X6 | Diagram regeneration | `diagram-source/` regenerated for `main`; new diagrams for 3.5 (worktrees), Ch 7, and Ch 9 | Large | not started | diff --git a/book/C-git-commands.asc b/book/A-git-commands.asc similarity index 60% rename from book/C-git-commands.asc rename to book/A-git-commands.asc index 3e3523e..924f89d 100644 --- a/book/C-git-commands.asc +++ b/book/A-git-commands.asc @@ -1,4 +1,4 @@ -[[C-git-commands]] +[[A-git-commands]] [appendix] == Git Commands @@ -29,22 +29,22 @@ There are several files this command will read from and write to so you can set The `git config` command has been used in nearly every chapter of the book. -In <> we used it to specify our name, email address and editor preference before we even got started using Git. +In <<_first_time,Ch 1, Getting Started>> we used it to specify our name, email address and editor preference before we even got started using Git. -In <> we showed how you could use it to create shorthand commands that expand to long option sequences so you don't have to type them every time. +In <<_git_aliases,Ch 2, Git Basics>> we showed how you could use it to create shorthand commands that expand to long option sequences so you don't have to type them every time. -In <> we used it to make `--rebase` the default when you run `git pull`. +In <<_rebasing,Ch 3, Git Branching>> we used it to make `--rebase` the default when you run `git pull`. -In <> we used it to set up a default store for your HTTP passwords. +In <<_credential_caching,Ch 5, Git Toolkit>> we used it to set up a default store for your HTTP passwords. -In <> we showed how to set up smudge and clean filters on content coming in and out of Git. +In <<_keyword_expansion,Ch 6, Customizing Git>> we showed how to set up smudge and clean filters on content coming in and out of Git. -Finally, basically the entirety of <> is dedicated to the command. +Finally, basically the entirety of <<_git_config,Ch 6, Customizing Git>> is dedicated to the command. [[ch_core_editor]] ==== git config core.editor commands -Accompanying the configuration instructions in <>, many editors can be set as follows: +Accompanying the configuration instructions in <<_editor,Ch 1, Getting Started>>, many editors can be set as follows: .Exhaustive list of `core.editor` configuration commands [cols="1,2",options="header"] @@ -84,7 +84,7 @@ If you have a 32-bit editor on a Windows 64-bit system, the program will be inst The `git help` command is used to show you all the documentation shipped with Git about any command. While we're giving a rough overview of most of the more popular ones in this appendix, for a full listing of all of the possible options and flags for every command, you can always run `git help `. -We introduced the `git help` command in <> and showed you how to use it to find more information about the `git shell` in <>. +We introduced the `git help` command in <<_git_help,Ch 1, Getting Started>> and showed you how to use it to find more information about the `git shell` in <<_setting_up_server,Ch 8, Git Servers>>. === Getting and Creating Projects @@ -95,13 +95,13 @@ One is to copy it from an existing repository on the network or elsewhere and th To take a directory and turn it into a new Git repository so you can start version controlling it, you can simply run `git init`. -We first introduce this in <>, where we show creating a brand new repository to start working with. +We first introduce this in <<_getting_a_repo,Ch 2, Git Basics>>, where we show creating a brand new repository to start working with. -We talk briefly about how you can change the default branch name from "`master`" in <>. +We talk briefly about how you can change the default branch name from "`master`" in <<_remote_branches,Ch 3, Git Branching>>. -We use this command to create an empty bare repository for a server in <>. +We use this command to create an empty bare repository for a server in <<_bare_repo,Ch 8, Git Servers>>. -Finally, we go through some of the details of what it actually does behind the scenes in <>. +Finally, we go through some of the details of what it actually does behind the scenes in <<_plumbing_porcelain,Ch 10, Git Internals>>. ==== git clone @@ -110,13 +110,13 @@ It creates a new directory, goes into it and runs `git init` to make it an empty The `git clone` command is used in dozens of places throughout the book, but we'll just list a few interesting places. -It's basically introduced and explained in <>, where we go through a few examples. +It's basically introduced and explained in <<_git_cloning,Ch 2, Git Basics>>, where we go through a few examples. -In <> we look at using the `--bare` option to create a copy of a Git repository with no working directory. +In <<_getting_git_on_a_server,Ch 8, Git Servers>> we look at using the `--bare` option to create a copy of a Git repository with no working directory. -In <> we use it to unbundle a bundled Git repository. +In <<_bundling,Ch 5, Git Toolkit>> we use it to unbundle a bundled Git repository. -Finally, in <> we learn the `--recurse-submodules` option to make cloning a repository with submodules a little simpler. +Finally, in <<_cloning_submodules,Ch 5, Git Toolkit>> we learn the `--recurse-submodules` option to make cloning a repository with submodules a little simpler. Though it's used in many other places through the book, these are the ones that are somewhat unique or where it is used in ways that are a little different. @@ -132,13 +132,13 @@ When the `git commit` command is run, by default it only looks at this staging a This command is an incredibly important command in Git and is mentioned or used dozens of times in this book. We'll quickly cover some of the unique uses that can be found. -We first introduce and explain `git add` in detail in <>. +We first introduce and explain `git add` in detail in <<_tracking_files,Ch 2, Git Basics>>. -We mention how to use it to resolve merge conflicts in <>. +We mention how to use it to resolve merge conflicts in <<_basic_merge_conflicts,Ch 3, Git Branching>>. -We go over using it to interactively stage only specific parts of a modified file in <>. +We go over using it to interactively stage only specific parts of a modified file in <<_interactive_staging,Ch 5, Git Toolkit>>. -Finally, we emulate it at a low level in <>, so you can get an idea of what it's doing behind the scenes. +Finally, we emulate it at a low level in <<_tree_objects,Ch 10, Git Internals>>, so you can get an idea of what it's doing behind the scenes. ==== git status @@ -146,7 +146,7 @@ The `git status` command will show you the different states of files in your wor Which files are modified and unstaged and which are staged but not yet committed. In its normal form, it also will show you some basic hints on how to move files between these stages. -We first cover `status` in <>, both in its basic and simplified forms. +We first cover `status` in <<_checking_status,Ch 2, Git Basics>>, both in its basic and simplified forms. While we use it throughout the book, pretty much everything you can do with the `git status` command is covered there. ==== git diff @@ -154,36 +154,36 @@ While we use it throughout the book, pretty much everything you can do with the The `git diff` command is used when you want to see differences between any two trees. This could be the difference between your working environment and your staging area (`git diff` by itself), between your staging area and your last commit (`git diff --staged`), or between two commits (`git diff master branchB`). -We first look at the basic uses of `git diff` in <>, where we show how to see what changes are staged and which are not yet staged. +We first look at the basic uses of `git diff` in <<_git_diff_staged,Ch 2, Git Basics>>, where we show how to see what changes are staged and which are not yet staged. -We use it to look for possible whitespace issues before committing with the `--check` option in <>. +We use it to look for possible whitespace issues before committing with the `--check` option in <<_commit_guidelines,Ch 4, Distributed Git>>. -We see how to check the differences between branches more effectively with the `git diff A...B` syntax in <>. +We see how to check the differences between branches more effectively with the `git diff A...B` syntax in <<_what_is_introduced,Ch 4, Distributed Git>>. -We use it to filter out whitespace differences with `-b` and how to compare different stages of conflicted files with `--theirs`, `--ours` and `--base` in <>. +We use it to filter out whitespace differences with `-b` and how to compare different stages of conflicted files with `--theirs`, `--ours` and `--base` in <<_advanced_merging,Ch 5, Git Toolkit>>. -Finally, we use it to effectively compare submodule changes with `--submodule` in <>. +Finally, we use it to effectively compare submodule changes with `--submodule` in <<_starting_submodules,Ch 5, Git Toolkit>>. ==== git difftool The `git difftool` command simply launches an external tool to show you the difference between two trees in case you want to use something other than the built in `git diff` command. -We only briefly mention this in <>. +We only briefly mention this in <<_git_diff_staged,Ch 2, Git Basics>>. ==== git commit The `git commit` command takes all the file contents that have been staged with `git add` and records a new permanent snapshot in the database and then moves the branch pointer on the current branch up to it. -We first cover the basics of committing in <>. +We first cover the basics of committing in <<_committing_changes,Ch 2, Git Basics>>. There we also demonstrate how to use the `-a` flag to skip the `git add` step in daily workflows and how to use the `-m` flag to pass a commit message in on the command line instead of firing up an editor. -In <> we cover using the `--amend` option to redo the most recent commit. +In <<_undoing,Ch 2, Git Basics>> we cover using the `--amend` option to redo the most recent commit. -In <>, we go into much more detail about what `git commit` does and why it does it like that. +In <<_git_branches_overview,Ch 3, Git Branching>>, we go into much more detail about what `git commit` does and why it does it like that. -We looked at how to sign commits cryptographically with the `-S` flag in <>. +We looked at how to sign commits cryptographically with the `-S` flag in <<_signing_commits,Ch 5, Git Toolkit>>. -Finally, we take a look at what the `git commit` command does in the background and how it's actually implemented in <>. +Finally, we take a look at what the `git commit` command does in the background and how it's actually implemented in <<_git_commit_objects,Ch 10, Git Internals>>. ==== git reset @@ -191,34 +191,34 @@ The `git reset` command is primarily used to undo things, as you can possibly te It moves around the `HEAD` pointer and optionally changes the `index` or staging area and can also optionally change the working directory if you use `--hard`. This final option makes it possible for this command to lose your work if used incorrectly, so make sure you understand it before using it. -We first effectively cover the simplest use of `git reset` in <>, where we use it to unstage a file we had run `git add` on. +We first effectively cover the simplest use of `git reset` in <<_unstaging,Ch 2, Git Basics>>, where we use it to unstage a file we had run `git add` on. -We then cover it in quite some detail in <>, which is entirely devoted to explaining this command. +We then cover it in quite some detail in <<_git_reset,Ch 5, Git Toolkit>>, which is entirely devoted to explaining this command. -We use `git reset --hard` to abort a merge in <>, where we also use `git merge --abort`, which is a bit of a wrapper for the `git reset` command. +We use `git reset --hard` to abort a merge in <<_abort_merge,Ch 5, Git Toolkit>>, where we also use `git merge --abort`, which is a bit of a wrapper for the `git reset` command. ==== git rm The `git rm` command is used to remove files from the staging area and working directory for Git. It is similar to `git add` in that it stages a removal of a file for the next commit. -We cover the `git rm` command in some detail in <>, including recursively removing files and only removing files from the staging area but leaving them in the working directory with `--cached`. +We cover the `git rm` command in some detail in <<_removing_files,Ch 2, Git Basics>>, including recursively removing files and only removing files from the staging area but leaving them in the working directory with `--cached`. -The only other differing use of `git rm` in the book is in <> where we briefly use and explain the `--ignore-unmatch` when running `git filter-branch`, which simply makes it not error out when the file we are trying to remove doesn't exist. +The only other differing use of `git rm` in the book is in <<_removing_objects,Ch 10, Git Internals>> where we briefly use and explain the `--ignore-unmatch` when running `git filter-branch`, which simply makes it not error out when the file we are trying to remove doesn't exist. This can be useful for scripting purposes. ==== git mv The `git mv` command is a thin convenience command to move a file and then run `git add` on the new file and `git rm` on the old file. -We only briefly mention this command in <>. +We only briefly mention this command in <<_git_mv,Ch 2, Git Basics>>. ==== git clean The `git clean` command is used to remove unwanted files from your working directory. This could include removing temporary build artifacts or merge conflict files. -We cover many of the options and scenarios in which you might used the clean command in <>. +We cover many of the options and scenarios in which you might used the clean command in <<_git_clean,Ch 5, Git Toolkit>>. === Branching and Merging @@ -229,48 +229,48 @@ There are just a handful of commands that implement most of the branching and me The `git branch` command is actually something of a branch management tool. It can list the branches you have, create a new branch, delete branches and rename branches. -Most of <> is dedicated to the `branch` command and it's used throughout the entire chapter. -We first introduce it in <> and we go through most of its other features (listing and deleting) in <>. +Most of <> is dedicated to the `branch` command and it's used throughout the entire chapter. +We first introduce it in <<_create_new_branch,Ch 3, Git Branching>> and we go through most of its other features (listing and deleting) in <<_branch_management,Ch 3, Git Branching>>. -In <> we use the `git branch -u` option to set up a tracking branch. +In <<_tracking_branches,Ch 3, Git Branching>> we use the `git branch -u` option to set up a tracking branch. -Finally, we go through some of what it does in the background in <>. +Finally, we go through some of what it does in the background in <<_git_refs,Ch 10, Git Internals>>. ==== git checkout The `git checkout` command is used to switch branches and check content out into your working directory. -We first encounter the command in <> along with the `git branch` command. +We first encounter the command in <<_switching_branches,Ch 3, Git Branching>> along with the `git branch` command. -We see how to use it to start tracking branches with the `--track` flag in <>. +We see how to use it to start tracking branches with the `--track` flag in <<_tracking_branches,Ch 3, Git Branching>>. -We use it to reintroduce file conflicts with `--conflict=diff3` in <>. +We use it to reintroduce file conflicts with `--conflict=diff3` in <<_checking_out_conflicts,Ch 5, Git Toolkit>>. -We go into closer detail on its relationship with `git reset` in <>. +We go into closer detail on its relationship with `git reset` in <<_git_reset,Ch 5, Git Toolkit>>. -Finally, we go into some implementation detail in <>. +Finally, we go into some implementation detail in <>. ==== git merge The `git merge` tool is used to merge one or more branches into the branch you have checked out. It will then advance the current branch to the result of the merge. -The `git merge` command was first introduced in <>. +The `git merge` command was first introduced in <<_basic_branching,Ch 3, Git Branching>>. Though it is used in various places in the book, there are very few variations of the `merge` command -- generally just `git merge ` with the name of the single branch you want to merge in. -We covered how to do a squashed merge (where Git merges the work but pretends like it's just a new commit without recording the history of the branch you're merging in) at the very end of <>. +We covered how to do a squashed merge (where Git merges the work but pretends like it's just a new commit without recording the history of the branch you're merging in) at the very end of <<_public_project,Ch 4, Distributed Git>>. -We went over a lot about the merge process and command, including the `-Xignore-space-change` command and the `--abort` flag to abort a problem merge in <>. +We went over a lot about the merge process and command, including the `-Xignore-space-change` command and the `--abort` flag to abort a problem merge in <<_advanced_merging,Ch 5, Git Toolkit>>. -We learned how to verify signatures before merging if your project is using GPG signing in <>. +We learned how to verify signatures before merging if your project is using GPG signing in <<_signing_commits,Ch 5, Git Toolkit>>. -Finally, we learned about Subtree merging in <>. +Finally, we learned about Subtree merging in <<_subtree_merge,Ch 5, Git Toolkit>>. ==== git mergetool The `git mergetool` command simply launches an external merge helper in case you have issues with a merge in Git. -We mention it quickly in <> and go into detail on how to implement your own external merge tool in <>. +We mention it quickly in <<_basic_merge_conflicts,Ch 3, Git Branching>> and go into detail on how to implement your own external merge tool in <<_external_merge_tools,Ch 6, Customizing Git>>. ==== git log @@ -280,37 +280,37 @@ It is also often used to show differences between two or more branches at the co This command is used in nearly every chapter of the book to demonstrate the history of a project. -We introduce the command and cover it in some depth in <>. +We introduce the command and cover it in some depth in <<_viewing_history,Ch 2, Git Basics>>. There we look at the `-p` and `--stat` option to get an idea of what was introduced in each commit and the `--pretty` and `--oneline` options to view the history more concisely, along with some simple date and author filtering options. -In <> we use it with the `--decorate` option to easily visualize where our branch pointers are located and we also use the `--graph` option to see what divergent histories look like. +In <<_create_new_branch,Ch 3, Git Branching>> we use it with the `--decorate` option to easily visualize where our branch pointers are located and we also use the `--graph` option to see what divergent histories look like. -In <> and <> we cover the `branchA..branchB` syntax to use the `git log` command to see what commits are unique to a branch relative to another branch. -In <> we go through this fairly extensively. +In <<_private_team,Ch 4, Distributed Git>> and <<_commit_ranges,Ch 5, Git Toolkit>> we cover the `branchA..branchB` syntax to use the `git log` command to see what commits are unique to a branch relative to another branch. +In <<_commit_ranges,Ch 5, Git Toolkit>> we go through this fairly extensively. -In <> and <> we cover using the `branchA...branchB` format and the `--left-right` syntax to see what is in one branch or the other but not in both. -In <> we also look at how to use the `--merge` option to help with merge conflict debugging as well as using the `--cc` option to look at merge commit conflicts in your history. +In <<_merge_log,Ch 5, Git Toolkit>> and <<_triple_dot,Ch 5, Git Toolkit>> we cover using the `branchA...branchB` format and the `--left-right` syntax to see what is in one branch or the other but not in both. +In <<_merge_log,Ch 5, Git Toolkit>> we also look at how to use the `--merge` option to help with merge conflict debugging as well as using the `--cc` option to look at merge commit conflicts in your history. -In <> we use the `-g` option to view the Git reflog through this tool instead of doing branch traversal. +In <<_git_reflog,Ch 5, Git Toolkit>> we use the `-g` option to view the Git reflog through this tool instead of doing branch traversal. -In <> we look at using the `-S` and `-L` options to do fairly sophisticated searches for something that happened historically in the code such as seeing the history of a function. +In <<_searching,Ch 5, Git Toolkit>> we look at using the `-S` and `-L` options to do fairly sophisticated searches for something that happened historically in the code such as seeing the history of a function. -In <> we see how to use `--show-signature` to add a validation string to each commit in the `git log` output based on if it was validly signed or not. +In <<_signing_commits,Ch 5, Git Toolkit>> we see how to use `--show-signature` to add a validation string to each commit in the `git log` output based on if it was validly signed or not. ==== git stash The `git stash` command is used to temporarily store uncommitted work in order to clean out your working directory without having to commit unfinished work on a branch. -This is basically entirely covered in <>. +This is basically entirely covered in <<_git_stashing,Ch 5, Git Toolkit>>. ==== git tag The `git tag` command is used to give a permanent bookmark to a specific point in the code history. Generally this is used for things like releases. -This command is introduced and covered in detail in <> and we use it in practice in <>. +This command is introduced and covered in detail in <<_git_tagging,Ch 2, Git Basics>> and we use it in practice in <<_tagging_releases,Ch 4, Distributed Git>>. -We also cover how to create a GPG signed tag with the `-s` flag and verify one with the `-v` flag in <>. +We also cover how to create a GPG signed tag with the `-s` flag and verify one with the `-v` flag in <<_signing,Ch 5, Git Toolkit>>. === Sharing and Updating Projects @@ -321,45 +321,45 @@ When you are ready to share your work or pull changes from elsewhere, there are The `git fetch` command communicates with a remote repository and fetches down all the information that is in that repository that is not in your current one and stores it in your local database. -We first look at this command in <> and we continue to see examples of its use in <>. +We first look at this command in <<_fetching_and_pulling,Ch 2, Git Basics>> and we continue to see examples of its use in <<_remote_branches,Ch 3, Git Branching>>. -We also use it in several of the examples in <>. +We also use it in several of the examples in <<_contributing_project,Ch 4, Distributed Git>>. -We use it to fetch a single specific reference that is outside of the default space in <> and we see how to fetch from a bundle in <>. +We use it to fetch a single specific reference that is outside of the default space in <<_pr_refs,Ch 4, Distributed Git>> and we see how to fetch from a bundle in <<_bundling,Ch 5, Git Toolkit>>. -We set up highly custom refspecs in order to make `git fetch` do something a little different than the default in <>. +We set up highly custom refspecs in order to make `git fetch` do something a little different than the default in <<_refspec,Ch 10, Git Internals>>. ==== git pull The `git pull` command is basically a combination of the `git fetch` and `git merge` commands, where Git will fetch from the remote you specify and then immediately try to merge it into the branch you're on. -We introduce it quickly in <> and show how to see what it will merge if you run it in <>. +We introduce it quickly in <<_fetching_and_pulling,Ch 2, Git Basics>> and show how to see what it will merge if you run it in <<_inspecting_remote,Ch 2, Git Basics>>. -We also see how to use it to help with rebasing difficulties in <>. +We also see how to use it to help with rebasing difficulties in <<_rebase_rebase,Ch 3, Git Branching>>. -We show how to use it with a URL to pull in changes in a one-off fashion in <>. +We show how to use it with a URL to pull in changes in a one-off fashion in <<_checking_out_remotes,Ch 4, Distributed Git>>. -Finally, we very quickly mention that you can use the `--verify-signatures` option to it in order to verify that commits you are pulling have been GPG signed in <>. +Finally, we very quickly mention that you can use the `--verify-signatures` option to it in order to verify that commits you are pulling have been GPG signed in <<_signing_commits,Ch 5, Git Toolkit>>. ==== git push The `git push` command is used to communicate with another repository, calculate what your local database has that the remote one does not, and then pushes the difference into the other repository. It requires write access to the other repository and so normally is authenticated somehow. -We first look at the `git push` command in <>. +We first look at the `git push` command in <<_pushing_remotes,Ch 2, Git Basics>>. Here we cover the basics of pushing a branch to a remote repository. -In <> we go a little deeper into pushing specific branches and in <> we see how to set up tracking branches to automatically push to. -In <> we use the `--delete` flag to delete a branch on the server with `git push`. +In <<_pushing_branches,Ch 3, Git Branching>> we go a little deeper into pushing specific branches and in <<_tracking_branches,Ch 3, Git Branching>> we see how to set up tracking branches to automatically push to. +In <<_delete_branches,Ch 3, Git Branching>> we use the `--delete` flag to delete a branch on the server with `git push`. -Throughout <> we see several examples of using `git push` to share work on branches through multiple remotes. +Throughout <<_contributing_project,Ch 4, Distributed Git>> we see several examples of using `git push` to share work on branches through multiple remotes. -We see how to use it to share tags that you have made with the `--tags` option in <>. +We see how to use it to share tags that you have made with the `--tags` option in <<_sharing_tags,Ch 2, Git Basics>>. -In <> we use the `--recurse-submodules` option to check that all of our submodules work has been published before pushing the superproject, which can be really helpful when using submodules. +In <<_publishing_submodules,Ch 5, Git Toolkit>> we use the `--recurse-submodules` option to check that all of our submodules work has been published before pushing the superproject, which can be really helpful when using submodules. -In <> we talk briefly about the `pre-push` hook, which is a script we can setup to run before a push completes to verify that it should be allowed to push. +In <<_other_client_hooks,Ch 6, Customizing Git>> we talk briefly about the `pre-push` hook, which is a script we can setup to run before a push completes to verify that it should be allowed to push. -Finally, in <> we look at pushing with a full refspec instead of the general shortcuts that are normally used. +Finally, in <<_pushing_refspecs,Ch 10, Git Internals>> we look at pushing with a full refspec instead of the general shortcuts that are normally used. This can help you be very specific about what work you wish to share. ==== git remote @@ -368,7 +368,7 @@ The `git remote` command is a management tool for your record of remote reposito It allows you to save long URLs as short handles, such as "`origin`" so you don't have to type them out all the time. You can have several of these and the `git remote` command is used to add, change and delete them. -This command is covered in detail in <>, including listing, adding, removing and renaming them. +This command is covered in detail in <<_remote_repos,Ch 2, Git Basics>>, including listing, adding, removing and renaming them. It is used in nearly every subsequent chapter in the book too, but always in the standard `git remote add ` format. @@ -376,7 +376,7 @@ It is used in nearly every subsequent chapter in the book too, but always in the The `git archive` command is used to create an archive file of a specific snapshot of the project. -We use `git archive` to create a tarball of a project for sharing in <>. +We use `git archive` to create a tarball of a project for sharing in <<_preparing_release,Ch 4, Distributed Git>>. ==== git submodule @@ -384,7 +384,7 @@ The `git submodule` command is used to manage external repositories within a nor This could be for libraries or other types of shared resources. The `submodule` command has several sub-commands (`add`, `update`, `sync`, etc) for managing these resources. -This command is only mentioned and entirely covered in <>. +This command is only mentioned and entirely covered in <<_git_submodules,Ch 5, Git Toolkit>>. === Inspection and Comparison @@ -393,25 +393,25 @@ This command is only mentioned and entirely covered in <>. +We first use it to show annotated tag information in <<_annotated_tags,Ch 2, Git Basics>>. -Later we use it quite a bit in <> to show the commits that our various revision selections resolve to. +Later we use it quite a bit in <<_revision_selection,Ch 5, Git Toolkit>> to show the commits that our various revision selections resolve to. -One of the more interesting things we do with `git show` is in <> to extract specific file contents of various stages during a merge conflict. +One of the more interesting things we do with `git show` is in <<_manual_remerge,Ch 5, Git Toolkit>> to extract specific file contents of various stages during a merge conflict. ==== git shortlog The `git shortlog` command is used to summarize the output of `git log`. It will take many of the same options that the `git log` command will but instead of listing out all of the commits it will present a summary of the commits grouped by author. -We showed how to use it to create a nice changelog in <>. +We showed how to use it to create a nice changelog in <<_the_shortlog,Ch 4, Distributed Git>>. ==== git describe The `git describe` command is used to take anything that resolves to a commit and produces a string that is somewhat human-readable and will not change. It's a way to get a description of a commit that is as unambiguous as a commit SHA-1 but more understandable. -We use `git describe` in <> and <> to get a string to name our release file after. +We use `git describe` in <<_build_number,Ch 4, Distributed Git>> and <<_preparing_release,Ch 4, Distributed Git>> to get a string to name our release file after. === Debugging @@ -422,20 +422,20 @@ This ranges from figuring out where something was introduced to figuring out who The `git bisect` tool is an incredibly helpful debugging tool used to find which specific commit was the first one to introduce a bug or problem by doing an automatic binary search. -It is fully covered in <> and is only mentioned in that section. +It is fully covered in <<_binary_search,Ch 5, Git Toolkit>> and is only mentioned in that section. ==== git blame The `git blame` command annotates the lines of any file with which commit was the last one to introduce a change to each line of the file and what person authored that commit. This is helpful in order to find the person to ask for more information about a specific section of your code. -It is covered in <> and is only mentioned in that section. +It is covered in <<_file_annotation,Ch 5, Git Toolkit>> and is only mentioned in that section. ==== git grep The `git grep` command can help you find any string or regular expression in any of the files in your source code, even older versions of your project. -It is covered in <> and is only mentioned in that section. +It is covered in <<_git_grep,Ch 5, Git Toolkit>> and is only mentioned in that section. === Patching @@ -447,27 +447,27 @@ These commands help you manage your branches in this manner. The `git cherry-pick` command is used to take the change introduced in a single Git commit and try to re-introduce it as a new commit on the branch you're currently on. This can be useful to only take one or two commits from a branch individually rather than merging in the branch which takes all the changes. -Cherry picking is described and demonstrated in <>. +Cherry picking is described and demonstrated in <<_rebase_cherry_pick,Ch 4, Distributed Git>>. ==== git rebase The `git rebase` command is basically an automated `cherry-pick`. It determines a series of commits and then cherry-picks them one by one in the same order somewhere else. -Rebasing is covered in detail in <>, including covering the collaborative issues involved with rebasing branches that are already public. +Rebasing is covered in detail in <<_rebasing,Ch 3, Git Branching>>, including covering the collaborative issues involved with rebasing branches that are already public. -We use it in practice during an example of splitting your history into two separate repositories in <>, using the `--onto` flag as well. +We use it in practice during an example of splitting your history into two separate repositories in <<_replace,Ch 5, Git Toolkit>>, using the `--onto` flag as well. -We go through running into a merge conflict during rebasing in <>. +We go through running into a merge conflict during rebasing in <>. -We also use it in an interactive scripting mode with the `-i` option in <>. +We also use it in an interactive scripting mode with the `-i` option in <<_changing_multiple,Ch 5, Git Toolkit>>. ==== git revert The `git revert` command is essentially a reverse `git cherry-pick`. It creates a new commit that applies the exact opposite of the change introduced in the commit you're targeting, essentially undoing or reverting it. -We use this in <> to undo a merge commit. +We use this in <<_reverse_commit,Ch 5, Git Toolkit>> to undo a merge commit. === Email @@ -479,43 +479,49 @@ Git has a number of tools built into it that help make this process easier, from The `git apply` command applies a patch created with the `git diff` or even GNU diff command. It is similar to what the `patch` command might do with a few small differences. -We demonstrate using it and the circumstances in which you might do so in <>. +We demonstrate using it and the circumstances in which you might do so in <<_patches_from_email,Ch 4, Distributed Git>>. ==== git am The `git am` command is used to apply patches from an email inbox, specifically one that is mbox formatted. This is useful for receiving patches over email and applying them to your project easily. -We covered usage and workflow around `git am` in <> including using the `--resolved`, `-i` and `-3` options. +We covered usage and workflow around `git am` in <<_git_am,Ch 4, Distributed Git>> including using the `--resolved`, `-i` and `-3` options. -There are also a number of hooks you can use to help with the workflow around `git am` and they are all covered in <>. +There are also a number of hooks you can use to help with the workflow around `git am` and they are all covered in <<_email_hooks,Ch 6, Customizing Git>>. -We also use it to apply patch formatted GitHub Pull Request changes in <>. +We also use it to apply patch formatted GitHub Pull Request changes in <<_email_notifications,Ch 4, Distributed Git>>. ==== git format-patch The `git format-patch` command is used to generate a series of patches in mbox format that you can use to send to a mailing list properly formatted. -We go through an example of contributing to a project using the `git format-patch` tool in <>. +We go through an example of contributing to a project using the `git format-patch` tool in <<_project_over_email,Ch 4, Distributed Git>>. ==== git imap-send The `git imap-send` command uploads a mailbox generated with `git format-patch` into an IMAP drafts folder. -We go through an example of contributing to a project by sending patches with the `git imap-send` tool in <>. +We go through an example of contributing to a project by sending patches with the `git imap-send` tool in <<_project_over_email,Ch 4, Distributed Git>>. ==== git send-email The `git send-email` command is used to send patches that are generated with `git format-patch` over email. -We go through an example of contributing to a project by sending patches with the `git send-email` tool in <>. +We go through an example of contributing to a project by sending patches with the `git send-email` tool in <<_project_over_email,Ch 4, Distributed Git>>. ==== git request-pull The `git request-pull` command is simply used to generate an example message body to email to someone. If you have a branch on a public server and want to let someone know how to integrate those changes without sending the patches over email, you can run this command and send the output to the person you want to pull the changes in. -We demonstrate how to use `git request-pull` to generate a pull message in <>. +We demonstrate how to use `git request-pull` to generate a pull message in <<_public_project,Ch 4, Distributed Git>>. + +//// +AUTHOR TODO (restructure): the "Git and Other Systems" chapter was dropped in the +3rd edition, so these entries have no backing chapter to point to. Decide whether the +External Systems category survives as reference-only text or is removed entirely +(see OUTLINE.md, Appendix A row). === External Systems @@ -533,6 +539,7 @@ This command is covered in depth in <>. For other version control systems or importing from nearly any format, you can use `git fast-import` to quickly map the other format to something Git can easily record. This command is covered in depth in <>. +//// === Administration @@ -543,39 +550,41 @@ If you're administering a Git repository or need to fix something in a big way, The `git gc` command runs "`garbage collection`" on your repository, removing unnecessary files in your database and packing up the remaining files into a more efficient format. This command normally runs in the background for you, though you can manually run it if you wish. -We go over some examples of this in <>. +We go over some examples of this in <<_git_gc,Ch 10, Git Internals>>. ==== git fsck The `git fsck` command is used to check the internal database for problems or inconsistencies. -We only quickly use this once in <> to search for dangling objects. +We only quickly use this once in <<_data_recovery,Ch 10, Git Internals>> to search for dangling objects. ==== git reflog The `git reflog` command goes through a log of where all the heads of your branches have been as you work to find commits you may have lost through rewriting histories. -We cover this command mainly in <>, where we show normal usage to and how to use `git log -g` to view the same information with `git log` output. +We cover this command mainly in <<_git_reflog,Ch 5, Git Toolkit>>, where we show normal usage to and how to use `git log -g` to view the same information with `git log` output. -We also go through a practical example of recovering such a lost branch in <>. +We also go through a practical example of recovering such a lost branch in <<_data_recovery,Ch 10, Git Internals>>. ==== git filter-branch The `git filter-branch` command is used to rewrite loads of commits according to certain patterns, like removing a file everywhere or filtering the entire repository down to a single subdirectory for extracting a project. -In <> we explain the command and explore several different options such as `--commit-filter`, `--subdirectory-filter` and `--tree-filter`. +In <<_removing_file_every_commit,Ch 5, Git Toolkit>> we explain the command and explore several different options such as `--commit-filter`, `--subdirectory-filter` and `--tree-filter`. -In <> we use it to fix up imported external repositories. +// AUTHOR TODO (restructure): the "Git and Other Systems" chapter was dropped in the +// 3rd edition, so this cross-reference has no target anymore. +// In <> we use it to fix up imported external repositories. === Plumbing Commands There were also quite a number of lower level plumbing commands that we encountered in the book. -The first one we encounter is `ls-remote` in <> which we use to look at the raw references on the server. +The first one we encounter is `ls-remote` in <<_pr_refs,Ch 4, Distributed Git>> which we use to look at the raw references on the server. -We use `ls-files` in <>, <> and <> to take a more raw look at what your staging area looks like. +We use `ls-files` in <<_manual_remerge,Ch 5, Git Toolkit>>, <> and <<_the_index,Ch 5, Git Toolkit>> to take a more raw look at what your staging area looks like. -We also mention `rev-parse` in <> to take just about any string and turn it into an object SHA-1. +We also mention `rev-parse` in <<_branch_references,Ch 5, Git Toolkit>> to take just about any string and turn it into an object SHA-1. -However, most of the low level plumbing commands we cover are in <>, which is more or less what the chapter is focused on. +However, most of the low level plumbing commands we cover are in <>, which is more or less what the chapter is focused on. We tried to avoid use of them throughout most of the rest of the book. diff --git a/book/A-git-in-other-environments.asc b/book/A-git-in-other-environments.asc deleted file mode 100644 index 10acab6..0000000 --- a/book/A-git-in-other-environments.asc +++ /dev/null @@ -1,28 +0,0 @@ -[[A-git-in-other-environments]] -[appendix] -== Git in Other Environments - -If you read through the whole book, you've learned a lot about how to use Git at the command line. -You can work with local files, connect your repository to others over a network, and work effectively with others. -But the story doesn't end there; Git is usually used as part of a larger ecosystem, and the terminal isn't always the best way to work with it. -Now we'll take a look at some of the other kinds of environments where Git can be useful, and how other applications (including yours) work alongside Git. - -include::A/guis.asc[] - -include::A/visualstudio.asc[] - -include::A/visualstudiocode.asc[] - -include::A/jetbrainsides.asc[] - -include::A/sublimetext.asc[] - -include::A/bash.asc[] - -include::A/zsh.asc[] - -include::A/powershell.asc[] - -=== Summary - -You've learned how to harness Git's power from inside the tools that you use during your everyday work, and also how to access Git repositories from your own programs. diff --git a/book/A/bash.asc b/book/A/bash.asc deleted file mode 100644 index 629d294..0000000 --- a/book/A/bash.asc +++ /dev/null @@ -1,43 +0,0 @@ -=== Git in Bash - -(((bash)))(((tab completion, bash)))(((shell prompts, bash))) -If you're a Bash user, you can tap into some of your shell's features to make your experience with Git a lot friendlier. -Git actually ships with plugins for several shells, but it's not turned on by default. - -First, you need to get a copy of the completions file from the source code of the Git release you're using. -Check your version by typing `git version`, then use `git checkout tags/vX.Y.Z`, where `vX.Y.Z` corresponds to the version of Git you are using. -Copy the `contrib/completion/git-completion.bash` file somewhere handy, like your home directory, and add this to your `.bashrc`: - -[source,console] ----- -. ~/git-completion.bash ----- - -Once that's done, change your directory to a Git repository, and type: - -[source,console] ----- -$ git chec ----- - -…and Bash will auto-complete to `git checkout`. -This works with all of Git's subcommands, command-line parameters, and remotes and ref names where appropriate. - -It's also useful to customize your prompt to show information about the current directory's Git repository. -This can be as simple or complex as you want, but there are generally a few key pieces of information that most people want, like the current branch, and the status of the working directory. -To add these to your prompt, just copy the `contrib/completion/git-prompt.sh` file from Git's source repository to your home directory, add something like this to your `.bashrc`: - -[source,console] ----- -. ~/git-prompt.sh -export GIT_PS1_SHOWDIRTYSTATE=1 -export PS1='\w$(__git_ps1 " (%s)")\$ ' ----- - -The `\w` means print the current working directory, the `\$` prints the `$` part of the prompt, and `__git_ps1 " (%s)"` calls the function provided by `git-prompt.sh` with a formatting argument. -Now your bash prompt will look like this when you're anywhere inside a Git-controlled project: - -.Customized `bash` prompt -image::images/git-bash.png[Customized `bash` prompt] - -Both of these scripts come with helpful documentation; take a look at the contents of `git-completion.bash` and `git-prompt.sh` for more information. diff --git a/book/A/guis.asc b/book/A/guis.asc deleted file mode 100644 index 3c2b629..0000000 --- a/book/A/guis.asc +++ /dev/null @@ -1,151 +0,0 @@ -=== Graphical Interfaces - -(((GUIs)))(((Graphical tools))) -Git's native environment is in the terminal. -New features show up there first, and only at the command line is the full power of Git completely at your disposal. -But plain text isn't the best choice for all tasks; sometimes a visual representation is what you need, and some users are much more comfortable with a point-and-click interface. - -It's important to note that different interfaces are tailored for different workflows. -Some clients expose only a carefully curated subset of Git functionality, in order to support a specific way of working that the author considers effective. -When viewed in this light, none of these tools can be called "`better`" than any of the others, they're simply more fit for their intended purpose. -Also note that there's nothing these graphical clients can do that the command-line client can't; the command-line is still where you'll have the most power and control when working with your repositories. - -==== `gitk` and `git-gui` - -(((git commands, gitk)))(((git commands, gui)))(((gitk))) -When you install Git, you also get its visual tools, `gitk` and `git-gui`. - -`gitk` is a graphical history viewer. -Think of it like a powerful GUI shell over `git log` and `git grep`. -This is the tool to use when you're trying to find something that happened in the past, or visualize your project's history. - -Gitk is easiest to invoke from the command-line. -Just `cd` into a Git repository, and type: - -[source,console] ----- -$ gitk [git log options] ----- - -Gitk accepts many command-line options, most of which are passed through to the underlying `git log` action. -Probably one of the most useful is the `--all` flag, which tells `gitk` to show commits reachable from _any_ ref, not just HEAD. -Gitk's interface looks like this: - -.The `gitk` history viewer -image::images/gitk.png[The `gitk` history viewer] - -On the top is something that looks a bit like the output of `git log --graph`; each dot represents a commit, the lines represent parent relationships, and refs are shown as colored boxes. -The yellow dot represents HEAD, and the red dot represents changes that are yet to become a commit. -At the bottom is a view of the selected commit; the comments and patch on the left, and a summary view on the right. -In between is a collection of controls used for searching history. - -`git-gui`, on the other hand, is primarily a tool for crafting commits. -It, too, is easiest to invoke from the command line: - -[source,console] ----- -$ git gui ----- - -And it looks something like this: - -.The `git-gui` commit tool -image::images/git-gui.png[The `git-gui` commit tool] - -On the left is the index; unstaged changes are on top, staged changes on the bottom. -You can move entire files between the two states by clicking on their icons, or you can select a file for viewing by clicking on its name. - -At top right is the diff view, which shows the changes for the currently-selected file. -You can stage individual hunks (or individual lines) by right-clicking in this area. - -At the bottom right is the message and action area. -Type your message into the text box and click "`Commit`" to do something similar to `git commit`. -You can also choose to amend the last commit by choosing the "`Amend`" radio button, which will update the "`Staged Changes`" area with the contents of the last commit. -Then you can simply stage or unstage some changes, alter the commit message, and click "`Commit`" again to replace the old commit with a new one. - -`gitk` and `git-gui` are examples of task-oriented tools. -Each of them is tailored for a specific purpose (viewing history and creating commits, respectively), and omit the features not necessary for that task. - -==== GitHub for macOS and Windows - -(((GitHub for macOS)))(((GitHub for Windows))) -GitHub has created two workflow-oriented Git clients: one for Windows, and one for macOS. -These clients are a good example of workflow-oriented tools – rather than expose _all_ of Git's functionality, they instead focus on a curated set of commonly-used features that work well together. -They look like this: - -.GitHub for macOS -image::images/github_mac.png[GitHub for macOS] - -.GitHub for Windows -image::images/github_win.png[GitHub for Windows] - -They are designed to look and work very much alike, so we'll treat them like a single product in this chapter. -We won't be doing a detailed rundown of these tools (they have their own documentation), but a quick tour of the "`changes`" view (which is where you'll spend most of your time) is in order. - -* On the left is the list of repositories the client is tracking; you can add a repository (either by cloning or attaching locally) by clicking the "`+`" icon at the top of this area. -* In the center is a commit-input area, which lets you input a commit message, and select which files should be included. - On Windows, the commit history is displayed directly below this; on macOS, it's on a separate tab. -* On the right is a diff view, which shows what's changed in your working directory, or which changes were included in the selected commit. -* The last thing to notice is the "`Sync`" button at the top-right, which is the primary way you interact over the network. - -[NOTE] -==== -You don't need a GitHub account to use these tools. -While they're designed to highlight GitHub's service and recommended workflow, they will happily work with any repository, and do network operations with any Git host. -==== - -===== Installation - -GitHub for Windows and macOS can be downloaded from https://desktop.github.com/[^]. -When the applications are first run, they walk you through all the first-time Git setup, such as configuring your name and email address, and both set up sane defaults for many common configuration options, such as credential caches and CRLF behavior. - -Both are "`evergreen`" – updates are downloaded and installed in the background while the applications are open. -This helpfully includes a bundled version of Git, which means you probably won't have to worry about manually updating it again. -On Windows, the client includes a shortcut to launch PowerShell with Posh-git, which we'll talk more about later in this chapter. - -The next step is to give the tool some repositories to work with. -The client shows you a list of the repositories you have access to on GitHub, and can clone them in one step. -If you already have a local repository, just drag its directory from the Finder or Windows Explorer into the GitHub client window, and it will be included in the list of repositories on the left. - -===== Recommended Workflow - -Once it's installed and configured, you can use the GitHub client for many common Git tasks. -The intended workflow for this tool is sometimes called the "`GitHub Flow.`" -We cover this in more detail in <>, but the general gist is that (a) you'll be committing to a branch, and (b) you'll be syncing up with a remote repository fairly regularly. - -Branch management is one of the areas where the two tools diverge. -On macOS, there's a button at the top of the window for creating a new branch: - -."`Create Branch`" button on macOS -image::images/branch_widget_mac.png[“Create Branch” button on macOS] - -On Windows, this is done by typing the new branch's name in the branch-switching widget: - -.Creating a branch on Windows -image::images/branch_widget_win.png[Creating a branch on Windows] - -Once your branch is created, making new commits is fairly straightforward. -Make some changes in your working directory, and when you switch to the GitHub client window, it will show you which files changed. -Enter a commit message, select the files you'd like to include, and click the "`Commit`" button (ctrl-enter or ⌘-enter). - -The main way you interact with other repositories over the network is through the "`Sync`" feature. -Git internally has separate operations for pushing, fetching, merging, and rebasing, but the GitHub clients collapse all of these into one multi-step feature. -Here's what happens when you click the Sync button: - -. `git pull --rebase`. - If this fails because of a merge conflict, fall back to `git pull --no-rebase`. -. `git push`. - -This is the most common sequence of network commands when working in this style, so squashing them into one command saves a lot of time. - -===== Summary - -These tools are very well-suited for the workflow they're designed for. -Developers and non-developers alike can be collaborating on a project within minutes, and many of the best practices for this kind of workflow are baked into the tools. -However, if your workflow is different, or you want more control over how and when network operations are done, we recommend you use another client or the command line. - -==== Other GUIs - -There are a number of other graphical Git clients, and they run the gamut from specialized, single-purpose tools all the way to apps that try to expose everything Git can do. -The official Git website has a curated list of the most popular clients at https://git-scm.com/downloads/guis[^]. -A more comprehensive list is available on the Git wiki site, at https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/Interfaces,_frontends,_and_tools.html#Graphical_Interfaces[^]. diff --git a/book/A/jetbrainsides.asc b/book/A/jetbrainsides.asc deleted file mode 100644 index 1def4a3..0000000 --- a/book/A/jetbrainsides.asc +++ /dev/null @@ -1,11 +0,0 @@ -=== Git in IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine - -(((JetBrains))) -JetBrains IDEs (such as IntelliJ IDEA, PyCharm, WebStorm, PhpStorm, RubyMine, and others) ship with a Git Integration plugin. -It provides a dedicated view in the IDE to work with Git and GitHub Pull Requests. - -.Version Control ToolWindow in JetBrains IDEs -image::images/jb.png[Version Control ToolWindow in JetBrains IDEs] - -The integration relies on the command-line Git client, and requires one to be installed. -The official documentation is available at https://www.jetbrains.com/help/idea/using-git-integration.html[^]. diff --git a/book/A/powershell.asc b/book/A/powershell.asc deleted file mode 100644 index e51fe0d..0000000 --- a/book/A/powershell.asc +++ /dev/null @@ -1,86 +0,0 @@ -[[_git_powershell]] -=== Git in PowerShell - -(((PowerShell)))(((tab completion, PowerShell)))(((shell prompts, PowerShell))) -(((posh-git))) -The legacy command-line terminal on Windows (`cmd.exe`) isn't really capable of a customized Git experience, but if you're using PowerShell, you're in luck. -This also works if you're running PowerShell Core on Linux or macOS. -A package called posh-git (https://github.com/dahlbyk/posh-git[^]) provides powerful tab-completion facilities, as well as an enhanced prompt to help you stay on top of your repository status. -It looks like this: - -.PowerShell with Posh-git -image::images/posh-git.png[PowerShell with Posh-git] - -==== Installation - -===== Prerequisites (Windows only) - -Before you're able to run PowerShell scripts on your machine, you need to set your local `ExecutionPolicy` to `RemoteSigned` (basically, anything except `Undefined` and `Restricted`). -If you choose `AllSigned` instead of `RemoteSigned`, also local scripts (your own) need to be digitally signed in order to be executed. -With `RemoteSigned`, only scripts having the `ZoneIdentifier` set to `Internet` (were downloaded from the web) need to be signed, others not. -If you're an administrator and want to set it for all users on that machine, use `-Scope LocalMachine`. -If you're a normal user, without administrative rights, you can use `-Scope CurrentUser` to set it only for you. - -More about PowerShell Scopes: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes[^]. - -More about PowerShell ExecutionPolicy: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy[^]. - -To set the value of `ExecutionPolicy` to `RemoteSigned` for all users use the next command: - -[source,powershell] ----- -> Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -Force ----- - -===== PowerShell Gallery - -If you have at least PowerShell 5 or PowerShell 4 with PackageManagement installed, you can use the package manager to install posh-git for you. - -More information about PowerShell Gallery: https://learn.microsoft.com/en-us/powershell/scripting/gallery/overview[^]. - -[source,powershell] ----- -> Install-Module posh-git -Scope CurrentUser -Force -> Install-Module posh-git -Scope CurrentUser -AllowPrerelease -Force # Newer beta version with PowerShell Core support ----- - -If you want to install posh-git for all users, use `-Scope AllUsers` instead and execute the command from an elevated PowerShell console. -If the second command fails with an error like `Module 'PowerShellGet' was not installed by using Install-Module`, you'll need to run another command first: - -[source,powershell] ----- -> Install-Module PowerShellGet -Force -SkipPublisherCheck ----- - -Then you can go back and try again. -This happens, because the modules that ship with Windows PowerShell are signed with a different publishment certificate. - -===== Update PowerShell Prompt - -To include Git information in your prompt, the posh-git module needs to be imported. -To have posh-git imported every time PowerShell starts, execute the `Add-PoshGitToProfile` command which will add the import statement into your `$profile` script. -This script is executed everytime you open a new PowerShell console. -Keep in mind, that there are multiple `$profile` scripts. -E.g. one for the console and a separate one for the ISE. - -[source,powershell] ----- -> Import-Module posh-git -> Add-PoshGitToProfile -AllHosts ----- - -===== From Source - -Just download a posh-git release from https://github.com/dahlbyk/posh-git/releases[^], and uncompress it. -Then import the module using the full path to the `posh-git.psd1` file: - -[source,powershell] ----- -> Import-Module \src\posh-git.psd1 -> Add-PoshGitToProfile -AllHosts ----- - -This will add the proper line to your `profile.ps1` file, and posh-git will be active the next time you open PowerShell. - -For a description of the Git status summary information displayed in the prompt see: https://github.com/dahlbyk/posh-git/blob/master/README.md#git-status-summary-information[^] -For more details on how to customize your posh-git prompt see: https://github.com/dahlbyk/posh-git/blob/master/README.md#customization-variables[^]. diff --git a/book/A/sublimetext.asc b/book/A/sublimetext.asc deleted file mode 100644 index d62eff4..0000000 --- a/book/A/sublimetext.asc +++ /dev/null @@ -1,16 +0,0 @@ -=== Git in Sublime Text - -(((Sublime Text))) -From version 3.2 onwards, Sublime Text has Git integration in the editor. - -The features are: - -* The sidebar will show the `git status` of files and folders with a badge/icon. -* Files and folders that are in your `.gitignore` file will be faded out in the sidebar. -* In the status bar, you can see the current Git branch and how many modifications you have made. -* All changes to a file are now visible via markers in the gutter. -* You can use part of the Sublime Merge Git client functionality from within Sublime Text. - This requires that Sublime Merge is installed. - See: https://www.sublimemerge.com/[^]. - -The official documentation for Sublime Text can be found here: https://www.sublimetext.com/docs/git_integration.html[^]. diff --git a/book/A/visualstudio.asc b/book/A/visualstudio.asc deleted file mode 100644 index f46d575..0000000 --- a/book/A/visualstudio.asc +++ /dev/null @@ -1,18 +0,0 @@ -=== Git in Visual Studio - -(((Visual Studio))) -Visual Studio has Git tooling built directly into the IDE, starting with Visual Studio 2019 version 16.8. - -The tooling supports the following Git functionality: - -* Create or clone a repository. -* Open and browse history of a repository. -* Create and checkout branches and tags. -* Stash, stage, and commit changes. -* Fetch, pull, push, or sync commits. -* Merge and rebase branches. -* Resolve merge conflicts. -* View diffs. -* ... and more! - -Read the https://learn.microsoft.com/en-us/visualstudio/version-control/[official documentation^] to learn more. diff --git a/book/A/visualstudiocode.asc b/book/A/visualstudiocode.asc deleted file mode 100644 index 7a646c3..0000000 --- a/book/A/visualstudiocode.asc +++ /dev/null @@ -1,22 +0,0 @@ -=== Git in Visual Studio Code - -(((Visual Studio Code))) -Visual Studio Code has Git support built in. -You will need to have Git version 2.0.0 (or newer) installed. - -The main features are: - -* See the diff of the file you are editing in the gutter. -* The Git Status Bar (lower left) shows the current branch, dirty indicators, incoming and outgoing commits. -* You can do the most common git operations from within the editor: -** Initialize a repository. -** Clone a repository. -** Create branches and tags. -** Stage and commit changes. -** Push/pull/sync with a remote branch. -** Resolve merge conflicts. -** View diffs. -* With an extension, you can also handle GitHub Pull Requests: - https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github[^]. - -The official documentation can be found here: https://code.visualstudio.com/docs/sourcecontrol/overview[^]. diff --git a/book/A/zsh.asc b/book/A/zsh.asc deleted file mode 100644 index c0578a9..0000000 --- a/book/A/zsh.asc +++ /dev/null @@ -1,55 +0,0 @@ -=== Git in Zsh - -(((zsh)))(((tab completion, zsh)))(((shell prompts, zsh))) -Zsh also ships with a tab-completion library for Git. -To use it, simply run `autoload -Uz compinit && compinit` in your `.zshrc`. -Zsh's interface is a bit more powerful than Bash's: - -[source,console] ----- -$ git che -check-attr -- display gitattributes information -check-ref-format -- ensure that a reference name is well formed -checkout -- checkout branch or paths to working tree -checkout-index -- copy files from index to working directory -cherry -- find commits not merged upstream -cherry-pick -- apply changes introduced by some existing commits ----- - -Ambiguous tab-completions aren't just listed; they have helpful descriptions, and you can graphically navigate the list by repeatedly hitting tab. -This works with Git commands, their arguments, and names of things inside the repository (like refs and remotes), as well as filenames and all the other things Zsh knows how to tab-complete. - -Zsh ships with a framework for getting information from version control systems, called `vcs_info`. -To include the branch name in the prompt on the right side, add these lines to your `~/.zshrc` file: - -[source,console] ----- -autoload -Uz vcs_info -precmd_vcs_info() { vcs_info } -precmd_functions+=( precmd_vcs_info ) -setopt prompt_subst -RPROMPT='${vcs_info_msg_0_}' -# PROMPT='${vcs_info_msg_0_}%# ' -zstyle ':vcs_info:git:*' formats '%b' ----- - -This results in a display of the current branch on the right-hand side of the terminal window, whenever your shell is inside a Git repository. -The left side is supported as well, of course; just uncomment the assignment to `PROMPT`. -It looks a bit like this: - -.Customized `zsh` prompt -image::images/zsh-prompt.png[Customized `zsh` prompt] - -For more information on `vcs_info`, check out its documentation in the `zshcontrib(1)` manual page, or online at https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#Version-Control-Information[^]. - -Instead of `vcs_info`, you might prefer the prompt customization script that ships with Git, called `git-prompt.sh`; see https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh[^] for details. -`git-prompt.sh` is compatible with both Bash and Zsh. - -Zsh is powerful enough that there are entire frameworks dedicated to making it better. -One of them is called "oh-my-zsh", and it can be found at https://github.com/ohmyzsh/ohmyzsh[^]. -oh-my-zsh's plugin system comes with powerful Git tab-completion, and it has a variety of prompt "themes", many of which display version-control data. -<> is just one example of what can be done with this system. - -[[oh_my_zsh_git]] -.An example of an oh-my-zsh theme -image::images/zsh-oh-my.png[An example of an oh-my-zsh theme] diff --git a/book/B-embedding-git-in-your-applications.asc b/book/B-embedding-git-in-your-applications.asc deleted file mode 100644 index 7467ff4..0000000 --- a/book/B-embedding-git-in-your-applications.asc +++ /dev/null @@ -1,19 +0,0 @@ -[[B-embedding-git-in-your-applications]] -[appendix] -== Embedding Git in your Applications - -If your application is for developers, chances are good that it could benefit from integration with source control. -Even non-developer applications, such as document editors, could potentially benefit from version-control features, and Git's model works very well for many different scenarios. - -If you need to integrate Git with your application, you have essentially two options: spawn a shell and call the `git` command-line program, or embed a Git library into your application. -Here we'll cover command-line integration and several of the most popular embeddable Git libraries. - -include::B/command-line.asc[] - -include::B/libgit2.asc[] - -include::B/jgit.asc[] - -include::B/go-git.asc[] - -include::B/dulwich.asc[] diff --git a/book/B/callouts/1.png b/book/B/callouts/1.png deleted file mode 100644 index 76af924..0000000 Binary files a/book/B/callouts/1.png and /dev/null differ diff --git a/book/B/callouts/10.png b/book/B/callouts/10.png deleted file mode 100644 index 7377a83..0000000 Binary files a/book/B/callouts/10.png and /dev/null differ diff --git a/book/B/callouts/2.png b/book/B/callouts/2.png deleted file mode 100644 index 51ddd05..0000000 Binary files a/book/B/callouts/2.png and /dev/null differ diff --git a/book/B/callouts/3.png b/book/B/callouts/3.png deleted file mode 100644 index 3bda852..0000000 Binary files a/book/B/callouts/3.png and /dev/null differ diff --git a/book/B/callouts/4.png b/book/B/callouts/4.png deleted file mode 100644 index 4ff2806..0000000 Binary files a/book/B/callouts/4.png and /dev/null differ diff --git a/book/B/callouts/5.png b/book/B/callouts/5.png deleted file mode 100644 index bfc4b60..0000000 Binary files a/book/B/callouts/5.png and /dev/null differ diff --git a/book/B/callouts/6.png b/book/B/callouts/6.png deleted file mode 100644 index 67d3f27..0000000 Binary files a/book/B/callouts/6.png and /dev/null differ diff --git a/book/B/callouts/7.png b/book/B/callouts/7.png deleted file mode 100644 index 9a11f7a..0000000 Binary files a/book/B/callouts/7.png and /dev/null differ diff --git a/book/B/callouts/8.png b/book/B/callouts/8.png deleted file mode 100644 index fef709e..0000000 Binary files a/book/B/callouts/8.png and /dev/null differ diff --git a/book/B/callouts/9.png b/book/B/callouts/9.png deleted file mode 100644 index 9f4063d..0000000 Binary files a/book/B/callouts/9.png and /dev/null differ diff --git a/book/B/command-line.asc b/book/B/command-line.asc deleted file mode 100644 index 01d5369..0000000 --- a/book/B/command-line.asc +++ /dev/null @@ -1,16 +0,0 @@ -=== Command-line Git - -One option is to spawn a shell process and use the Git command-line tool to do the work. -This has the benefit of being canonical, and all of Git's features are supported. -This also happens to be fairly easy, as most runtime environments have a relatively simple facility for invoking a process with command-line arguments. -However, this approach does have some downsides. - -One is that all the output is in plain text. -This means that you'll have to parse Git's occasionally-changing output format to read progress and result information, which can be inefficient and error-prone. - -Another is the lack of error recovery. -If a repository is corrupted somehow, or the user has a malformed configuration value, Git will simply refuse to perform many operations. - -Yet another is process management. -Git requires you to maintain a shell environment on a separate process, which can add unwanted complexity. -Trying to coordinate many of these processes (especially when potentially accessing the same repository from several processes) can be quite a challenge. diff --git a/book/B/dulwich.asc b/book/B/dulwich.asc deleted file mode 100644 index 62cab95..0000000 --- a/book/B/dulwich.asc +++ /dev/null @@ -1,42 +0,0 @@ -=== Dulwich - -(((Dulwich)))(((Python))) -There is also a pure-Python Git implementation - Dulwich. -The project is hosted under https://www.dulwich.io/[^]. -It aims to provide an interface to Git repositories (both local and remote) that doesn't call out to Git directly but instead uses pure Python. -It has an optional C extensions though, that significantly improve the performance. - -Dulwich follows Git design and separate two basic levels of API: plumbing and porcelain. - -Here is an example of using the lower level API to access the commit message of the last commit: - -[source, python] ----- -from dulwich.repo import Repo -r = Repo('.') -r.head() -# '57fbe010446356833a6ad1600059d80b1e731e15' - -c = r[r.head()] -c -# - -c.message -# 'Add note about encoding.\n' ----- - -To print a commit log using high-level porcelain API, one can use: - -[source, python] ----- -from dulwich import porcelain -porcelain.log('.', max_entries=1) - -#commit: 57fbe010446356833a6ad1600059d80b1e731e15 -#Author: Jelmer Vernooij -#Date: Sat Apr 29 2017 23:57:34 +0000 ----- - -==== Further Reading - -The API documentation, tutorial, and many examples of how to do specific tasks with Dulwich are available on the official website https://www.dulwich.io[^]. diff --git a/book/B/go-git.asc b/book/B/go-git.asc deleted file mode 100644 index a477cf1..0000000 --- a/book/B/go-git.asc +++ /dev/null @@ -1,83 +0,0 @@ -=== go-git - -(((go-git)))(((Go))) -In case you want to integrate Git into a service written in Golang, there also is a pure Go library implementation. -This implementation does not have any native dependencies and thus is not prone to manual memory management errors. -It is also transparent for the standard Golang performance analysis tooling like CPU, Memory profilers, race detector, etc. - -go-git is focused on extensibility, compatibility and supports most of the plumbing APIs, which is documented at https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md[^]. - -Here is a basic example of using Go APIs: - -[source, go] ----- -import "github.com/go-git/go-git/v5" - -r, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{ - URL: "https://github.com/go-git/go-git", - Progress: os.Stdout, -}) ----- - -As soon as you have a `Repository` instance, you can access information and perform mutations on it: - -[source, go] ----- -// retrieves the branch pointed by HEAD -ref, err := r.Head() - -// get the commit object, pointed by ref -commit, err := r.CommitObject(ref.Hash()) - -// retrieves the commit history -history, err := commit.History() - -// iterates over the commits and print each -for _, c := range history { - fmt.Println(c) -} ----- - -==== Advanced Functionality - -go-git has few notable advanced features, one of which is a pluggable storage system, which is similar to Libgit2 backends. -The default implementation is in-memory storage, which is very fast. - -[source, go] ----- -r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ - URL: "https://github.com/go-git/go-git", -}) ----- - -Pluggable storage provides many interesting options. -For instance, https://github.com/go-git/go-git/tree/master/_examples/storage[^] allows you to store references, objects, and configuration in an Aerospike database. - -Another feature is a flexible filesystem abstraction. -Using https://pkg.go.dev/github.com/go-git/go-billy/v5?tab=doc#Filesystem[^] it is easy to store all the files in different way i.e by packing all of them to a single archive on disk or by keeping them all in-memory. - -Another advanced use-case includes a fine-tunable HTTP client, such as the one found at https://github.com/go-git/go-git/blob/master/_examples/custom_http/main.go[^]. - -[source, go] ----- -customClient := &http.Client{ - Transport: &http.Transport{ // accept any certificate (might be useful for testing) - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - Timeout: 15 * time.Second, // 15 second timeout - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse // don't follow redirect - }, -} - -// Override http(s) default protocol to use our custom client -client.InstallProtocol("https", githttp.NewClient(customClient)) - -// Clone repository using the new client if the protocol is https:// -r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{URL: url}) ----- - -==== Further Reading - -A full treatment of go-git's capabilities is outside the scope of this book. -If you want more information on go-git, there's API documentation at https://pkg.go.dev/github.com/go-git/go-git/v5[^], and a set of usage examples at https://github.com/go-git/go-git/tree/master/_examples[^]. diff --git a/book/B/jgit.asc b/book/B/jgit.asc deleted file mode 100644 index 74ae05a..0000000 --- a/book/B/jgit.asc +++ /dev/null @@ -1,160 +0,0 @@ -=== JGit - -(((jgit)))(((Java))) -If you want to use Git from within a Java program, there is a fully featured Git library called JGit. -JGit is a relatively full-featured implementation of Git written natively in Java, and is widely used in the Java community. -The JGit project is under the Eclipse umbrella, and its home can be found at https://projects.eclipse.org/projects/technology.jgit[^]. - -==== Getting Set Up - -There are a number of ways to connect your project with JGit and start writing code against it. -Probably the easiest is to use Maven – the integration is accomplished by adding the following snippet to the `` tag in your `pom.xml` file: - -[source,xml] ----- - - org.eclipse.jgit - org.eclipse.jgit - 3.5.0.201409260305-r - ----- - -The `version` will most likely have advanced by the time you read this; check https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit[^] for updated repository information. -Once this step is done, Maven will automatically acquire and use the JGit libraries that you'll need. - -If you would rather manage the binary dependencies yourself, pre-built JGit binaries are available from https://projects.eclipse.org/projects/technology.jgit/downloads[^]. -You can build them into your project by running a command like this: - -[source,console] ----- -javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java -java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App ----- - -==== Plumbing - -JGit has two basic levels of API: plumbing and porcelain. -The terminology for these comes from Git itself, and JGit is divided into roughly the same kinds of areas: porcelain APIs are a friendly front-end for common user-level actions (the sorts of things a normal user would use the Git command-line tool for), while the plumbing APIs are for interacting with low-level repository objects directly. - -The starting point for most JGit sessions is the `Repository` class, and the first thing you'll want to do is create an instance of it. -For a filesystem-based repository (yes, JGit allows for other storage models), this is accomplished using `FileRepositoryBuilder`: - -[source,java] ----- -// Create a new repository -Repository newlyCreatedRepo = FileRepositoryBuilder.create( - new File("/tmp/new_repo/.git")); -newlyCreatedRepo.create(); - -// Open an existing repository -Repository existingRepo = new FileRepositoryBuilder() - .setGitDir(new File("my_repo/.git")) - .build(); ----- - -The builder has a fluent API for providing all the things it needs to find a Git repository, whether or not your program knows exactly where it's located. -It can use environment variables (`.readEnvironment()`), start from a place in the working directory and search (`.setWorkTree(…).findGitDir()`), or just open a known `.git` directory as above. - -Once you have a `Repository` instance, you can do all sorts of things with it. -Here's a quick sampling: - -[source,java] ----- -// Get a reference -Ref master = repo.getRef("master"); - -// Get the object the reference points to -ObjectId masterTip = master.getObjectId(); - -// Rev-parse -ObjectId obj = repo.resolve("HEAD^{tree}"); - -// Load raw object contents -ObjectLoader loader = repo.open(masterTip); -loader.copyTo(System.out); - -// Create a branch -RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1"); -createBranch1.setNewObjectId(masterTip); -createBranch1.update(); - -// Delete a branch -RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1"); -deleteBranch1.setForceUpdate(true); -deleteBranch1.delete(); - -// Config -Config cfg = repo.getConfig(); -String name = cfg.getString("user", null, "name"); ----- - -There's quite a bit going on here, so let's go through it one section at a time. - -The first line gets a pointer to the `master` reference. -JGit automatically grabs the _actual_ `master` ref, which lives at `refs/heads/master`, and returns an object that lets you fetch information about the reference. -You can get the name (`.getName()`), and either the target object of a direct reference (`.getObjectId()`) or the reference pointed to by a symbolic ref (`.getTarget()`). -Ref objects are also used to represent tag refs and objects, so you can ask if the tag is "`peeled,`" meaning that it points to the final target of a (potentially long) string of tag objects. - -The second line gets the target of the `master` reference, which is returned as an ObjectId instance. -ObjectId represents the SHA-1 hash of an object, which might or might not exist in Git's object database. -The third line is similar, but shows how JGit handles the rev-parse syntax (for more on this, see <>); you can pass any object specifier that Git understands, and JGit will return either a valid ObjectId for that object, or `null`. - -The next two lines show how to load the raw contents of an object. -In this example, we call `ObjectLoader.copyTo()` to stream the contents of the object directly to stdout, but ObjectLoader also has methods to read the type and size of an object, as well as return it as a byte array. -For large objects (where `.isLarge()` returns `true`), you can call `.openStream()` to get an InputStream-like object that can read the raw object data without pulling it all into memory at once. - -The next few lines show what it takes to create a new branch. -We create a RefUpdate instance, configure some parameters, and call `.update()` to trigger the change. -Directly following this is the code to delete that same branch. -Note that `.setForceUpdate(true)` is required for this to work; otherwise the `.delete()` call will return `REJECTED`, and nothing will happen. - -The last example shows how to fetch the `user.name` value from the Git configuration files. -This Config instance uses the repository we opened earlier for local configuration, but will automatically detect the global and system configuration files and read values from them as well. - -This is only a small sampling of the full plumbing API; there are many more methods and classes available. -Also not shown here is the way JGit handles errors, which is through the use of exceptions. -JGit APIs sometimes throw standard Java exceptions (such as `IOException`), but there are a host of JGit-specific exception types that are provided as well (such as `NoRemoteRepositoryException`, `CorruptObjectException`, and `NoMergeBaseException`). - -==== Porcelain - -The plumbing APIs are rather complete, but it can be cumbersome to string them together to achieve common goals, like adding a file to the index, or making a new commit. -JGit provides a higher-level set of APIs to help out with this, and the entry point to these APIs is the `Git` class: - -[source,java] ----- -Repository repo; -// construct repo... -Git git = new Git(repo); ----- - -The Git class has a nice set of high-level _builder_-style methods that can be used to construct some pretty complex behavior. -Let's take a look at an example -- doing something like `git ls-remote`: - -[source,java] ----- -CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd"); -Collection remoteRefs = git.lsRemote() - .setCredentialsProvider(cp) - .setRemote("origin") - .setTags(true) - .setHeads(false) - .call(); -for (Ref ref : remoteRefs) { - System.out.println(ref.getName() + " -> " + ref.getObjectId().name()); -} ----- - -This is a common pattern with the Git class; the methods return a command object that lets you chain method calls to set parameters, which are executed when you call `.call()`. -In this case, we're asking the `origin` remote for tags, but not heads. -Also notice the use of a `CredentialsProvider` object for authentication. - -Many other commands are available through the Git class, including but not limited to `add`, `blame`, `commit`, `clean`, `push`, `rebase`, `revert`, and `reset`. - -==== Further Reading - -This is only a small sampling of JGit's full capabilities. -If you're interested and want to learn more, here's where to look for information and inspiration: - -* The official JGit API documentation can be found at https://help.eclipse.org/latest/topic/org.eclipse.egit.doc/help/JGit/User_Guide/User-Guide.html[^]. - These are standard Javadoc, so your favorite JVM IDE will be able to install them locally, as well. -* The JGit Cookbook at https://github.com/centic9/jgit-cookbook[^] has many examples of how to do specific tasks with JGit. diff --git a/book/B/libgit2.asc b/book/B/libgit2.asc deleted file mode 100644 index 2e006fd..0000000 --- a/book/B/libgit2.asc +++ /dev/null @@ -1,237 +0,0 @@ -=== Libgit2 - -(((libgit2)))((("C"))) -Another option at your disposal is to use Libgit2. -Libgit2 is a dependency-free implementation of Git, with a focus on having a nice API for use within other programs. -You can find it at https://libgit2.org[^]. - -First, let's take a look at what the C API looks like. -Here's a whirlwind tour: - -[source,c] ----- -// Open a repository -git_repository *repo; -int error = git_repository_open(&repo, "/path/to/repository"); - -// Dereference HEAD to a commit -git_object *head_commit; -error = git_revparse_single(&head_commit, repo, "HEAD^{commit}"); -git_commit *commit = (git_commit*)head_commit; - -// Print some of the commit's properties -printf("%s", git_commit_message(commit)); -const git_signature *author = git_commit_author(commit); -printf("%s <%s>\n", author->name, author->email); -const git_oid *tree_id = git_commit_tree_id(commit); - -// Cleanup -git_commit_free(commit); -git_repository_free(repo); ----- - -The first couple of lines open a Git repository. -The `git_repository` type represents a handle to a repository with a cache in memory. -This is the simplest method, for when you know the exact path to a repository's working directory or `.git` folder. -There's also the `git_repository_open_ext` which includes options for searching, `git_clone` and friends for making a local clone of a remote repository, and `git_repository_init` for creating an entirely new repository. - -The second chunk of code uses rev-parse syntax (see <> for more on this) to get the commit that HEAD eventually points to. -The type returned is a `git_object` pointer, which represents something that exists in the Git object database for a repository. -`git_object` is actually a "`parent`" type for several different kinds of objects; the memory layout for each of the "`child`" types is the same as for `git_object`, so you can safely cast to the right one. -In this case, `git_object_type(commit)` would return `GIT_OBJ_COMMIT`, so it's safe to cast to a `git_commit` pointer. - -The next chunk shows how to access the commit's properties. -The last line here uses a `git_oid` type; this is Libgit2's representation for a SHA-1 hash. - -From this sample, a couple of patterns have started to emerge: - -* If you declare a pointer and pass a reference to it into a Libgit2 call, that call will probably return an integer error code. - A `0` value indicates success; anything less is an error. -* If Libgit2 populates a pointer for you, you're responsible for freeing it. -* If Libgit2 returns a `const` pointer from a call, you don't have to free it, but it will become invalid when the object it belongs to is freed. -* Writing C is a bit painful. - -(((Ruby))) -That last one means it isn't very probable that you'll be writing C when using Libgit2. -Fortunately, there are a number of language-specific bindings available that make it fairly easy to work with Git repositories from your specific language and environment. -Let's take a look at the above example written using the Ruby bindings for Libgit2, which are named Rugged, and can be found at https://github.com/libgit2/rugged[^]. - -[source,ruby] ----- -repo = Rugged::Repository.new('path/to/repository') -commit = repo.head.target -puts commit.message -puts "#{commit.author[:name]} <#{commit.author[:email]}>" -tree = commit.tree ----- - -As you can see, the code is much less cluttered. -Firstly, Rugged uses exceptions; it can raise things like `ConfigError` or `ObjectError` to signal error conditions. -Secondly, there's no explicit freeing of resources, since Ruby is garbage-collected. -Let's take a look at a slightly more complicated example: crafting a commit from scratch - -[source,ruby] ----- -blob_id = repo.write("Blob contents", :blob) # <1> - -index = repo.index -index.read_tree(repo.head.target.tree) -index.add(:path => 'newfile.txt', :oid => blob_id) # <2> - -sig = { - :email => "bob@example.com", - :name => "Bob User", - :time => Time.now, -} - -commit_id = Rugged::Commit.create(repo, - :tree => index.write_tree(repo), # <3> - :author => sig, - :committer => sig, # <4> - :message => "Add newfile.txt", # <5> - :parents => repo.empty? ? [] : [ repo.head.target ].compact, # <6> - :update_ref => 'HEAD', # <7> -) -commit = repo.lookup(commit_id) # <8> ----- - -<1> Create a new blob, which contains the contents of a new file. -<2> Populate the index with the head commit's tree, and add the new file at the path `newfile.txt`. -<3> This creates a new tree in the ODB, and uses it for the new commit. -<4> We use the same signature for both the author and committer fields. -<5> The commit message. -<6> When creating a commit, you have to specify the new commit's parents. - This uses the tip of HEAD for the single parent. -<7> Rugged (and Libgit2) can optionally update a reference when making a commit. -<8> The return value is the SHA-1 hash of a new commit object, which you can then use to get a `Commit` object. - -The Ruby code is nice and clean, but since Libgit2 is doing the heavy lifting, this code will run pretty fast, too. -If you're not a rubyist, we touch on some other bindings in <<_libgit2_bindings>>. - -==== Advanced Functionality - -Libgit2 has a couple of capabilities that are outside the scope of core Git. -One example is pluggability: Libgit2 allows you to provide custom "`backends`" for several types of operation, so you can store things in a different way than stock Git does. -Libgit2 allows custom backends for configuration, ref storage, and the object database, among other things. - -Let's take a look at how this works. -The code below is borrowed from the set of backend examples provided by the Libgit2 team (which can be found at https://github.com/libgit2/libgit2-backends[^]). -Here's how a custom backend for the object database is set up: - -[source,c] ----- -git_odb *odb; -int error = git_odb_new(&odb); // <1> - -git_odb_backend *my_backend; -error = git_odb_backend_mine(&my_backend, /*…*/); // <2> - -error = git_odb_add_backend(odb, my_backend, 1); // <3> - -git_repository *repo; -error = git_repository_open(&repo, "some-path"); -error = git_repository_set_odb(repo, odb); // <4> ----- - -_Note that errors are captured, but not handled. We hope your code is better than ours._ - -<1> Initialize an empty object database (ODB) "`frontend,`" which will act as a container for the "`backends`" which are the ones doing the real work. -<2> Initialize a custom ODB backend. -<3> Add the backend to the frontend. -<4> Open a repository, and set it to use our ODB to look up objects. - -But what is this `git_odb_backend_mine` thing? -Well, that's the constructor for your own ODB implementation, and you can do whatever you want in there, so long as you fill in the `git_odb_backend` structure properly. -Here's what it _could_ look like: - -[source,c] ----- -typedef struct { - git_odb_backend parent; - - // Some other stuff - void *custom_context; -} my_backend_struct; - -int git_odb_backend_mine(git_odb_backend **backend_out, /*…*/) -{ - my_backend_struct *backend; - - backend = calloc(1, sizeof (my_backend_struct)); - - backend->custom_context = …; - - backend->parent.read = &my_backend__read; - backend->parent.read_prefix = &my_backend__read_prefix; - backend->parent.read_header = &my_backend__read_header; - // … - - *backend_out = (git_odb_backend *) backend; - - return GIT_SUCCESS; -} ----- - -The subtlest constraint here is that ``my_backend_struct```'s first member must be a ``git_odb_backend`` structure; this ensures that the memory layout is what the Libgit2 code expects it to be. -The rest of it is arbitrary; this structure can be as large or small as you need it to be. - -The initialization function allocates some memory for the structure, sets up the custom context, and then fills in the members of the `parent` structure that it supports. -Take a look at the `include/git2/sys/odb_backend.h` file in the Libgit2 source for a complete set of call signatures; your particular use case will help determine which of these you'll want to support. - -[[_libgit2_bindings]] -==== Other Bindings - -Libgit2 has bindings for many languages. -Here we show a small example using a few of the more complete bindings packages as of this writing; libraries exist for many other languages, including C++, Go, Node.js, Erlang, and the JVM, all in various stages of maturity. -The official collection of bindings can be found by browsing the repositories at https://github.com/libgit2[^]. -The code we'll write will return the commit message from the commit eventually pointed to by HEAD (sort of like `git log -1`). - -===== LibGit2Sharp - -(((.NET)))(((C#)))(((Mono))) -If you're writing a .NET or Mono application, LibGit2Sharp (https://github.com/libgit2/libgit2sharp[^]) is what you're looking for. -The bindings are written in C#, and great care has been taken to wrap the raw Libgit2 calls with native-feeling CLR APIs. -Here's what our example program looks like: - -[source,csharp] ----- -new Repository(@"C:\path\to\repo").Head.Tip.Message; ----- - -For desktop Windows applications, there's even a NuGet package that will help you get started quickly. - -===== objective-git - -(((Apple)))(((Objective-C)))(((Cocoa))) -If your application is running on an Apple platform, you're likely using Objective-C as your implementation language. -Objective-Git (https://github.com/libgit2/objective-git[^]) is the name of the Libgit2 bindings for that environment. -The example program looks like this: - -[source,objc] ----- -GTRepository *repo = - [[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL]; -NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message]; ----- - -Objective-git is fully interoperable with Swift, so don't fear if you've left Objective-C behind. - -===== pygit2 - -(((Python))) -The bindings for Libgit2 in Python are called Pygit2, and can be found at https://www.pygit2.org[^]. -Our example program: - -[source,python] ----- -pygit2.Repository("/path/to/repo") # open repository - .head # get the current branch - .peel(pygit2.Commit) # walk down to the commit - .message # read the message ----- - -==== Further Reading - -Of course, a full treatment of Libgit2's capabilities is outside the scope of this book. -If you want more information on Libgit2 itself, there's API documentation at https://libgit2.github.com/libgit2[^], and a set of guides at https://libgit2.github.com/docs[^]. -For the other bindings, check the bundled README and tests; there are often small tutorials and pointers to further reading there. diff --git a/book/ch01/first-time-setup.asc b/book/ch01/first-time-setup.asc index 10b7049..7cdfc22 100644 --- a/book/ch01/first-time-setup.asc +++ b/book/ch01/first-time-setup.asc @@ -74,7 +74,7 @@ $ git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' -m [NOTE] ==== Vim, Emacs and Notepad++ are popular text editors often used by developers on Unix-based systems like Linux and macOS or a Windows system. -If you are using another editor, or a 32-bit version, please find specific instructions for how to set up your favorite editor with Git in <>. +If you are using another editor, or a 32-bit version, please find specific instructions for how to set up your favorite editor with Git in <>. ==== [WARNING] diff --git a/book/ch01/history.asc b/book/ch01/history.asc index 7ab05fd..c369153 100644 --- a/book/ch01/history.asc +++ b/book/ch01/history.asc @@ -17,4 +17,4 @@ Some of the goals of the new system were as follows: * Able to handle large projects like the Linux kernel efficiently (speed and data size) Since its birth in 2005, Git has evolved and matured to be easy to use and yet retain these initial qualities. -It's amazingly fast, it's very efficient with large projects, and it has an incredible branching system for non-linear development (see <>). +It's amazingly fast, it's very efficient with large projects, and it has an incredible branching system for non-linear development (see <>). diff --git a/book/ch01/what-is-git.asc b/book/ch01/what-is-git.asc index 466201b..a4b9fc5 100644 --- a/book/ch01/what-is-git.asc +++ b/book/ch01/what-is-git.asc @@ -27,7 +27,7 @@ image::images/snapshots.png[Git stores data as snapshots of the project over tim This is an important distinction between Git and nearly all other VCSs. It makes Git reconsider almost every aspect of version control that most other systems copied from the previous generation. This makes Git more like a mini filesystem with some incredibly powerful tools built on top of it, rather than simply a VCS. -We'll explore some of the benefits you gain by thinking of your data this way when we cover Git branching in <>. +We'll explore some of the benefits you gain by thinking of your data this way when we cover Git branching in <>. ==== Nearly Every Operation Is Local @@ -72,7 +72,7 @@ It is hard to get the system to do anything that is not undoable or to make it e As with any VCS, you can lose or mess up changes you haven't committed yet, but after you commit a snapshot into Git, it is very difficult to lose, especially if you regularly push your database to another repository. This makes using Git a joy because we know we can experiment without the danger of severely screwing things up. -For a more in-depth look at how Git stores its data and how you can recover data that seems lost, see <>. +For a more in-depth look at how Git stores its data and how you can recover data that seems lost, see <<_undoing,Ch 2, Git Basics>>. ==== The Three States @@ -106,4 +106,4 @@ The basic Git workflow goes something like this: If a particular version of a file is in the Git directory, it's considered _committed_. If it has been modified and was added to the staging area, it is _staged_. And if it was changed since it was checked out but has not been staged, it is _modified_. -In <>, you'll learn more about these states and how you can either take advantage of them or skip the staged part entirely. +In <>, you'll learn more about these states and how you can either take advantage of them or skip the staged part entirely. diff --git a/book/ch02/getting-a-repository.asc b/book/ch02/getting-a-repository.asc index 3b69efd..7efc70f 100644 --- a/book/ch02/getting-a-repository.asc +++ b/book/ch02/getting-a-repository.asc @@ -38,7 +38,7 @@ $ git init This creates a new subdirectory named `.git` that contains all of your necessary repository files -- a Git repository skeleton. At this point, nothing in your project is tracked yet. -See <> for more information about exactly what files are contained in the `.git` directory you just created.(((git commands, init))) +See <> for more information about exactly what files are contained in the `.git` directory you just created.(((git commands, init))) If you want to start version-controlling existing files (as opposed to an empty directory), you should probably begin tracking those files and do an initial commit. You can accomplish that with a few `git add` commands that specify the files you want to track, followed by a `git commit`: @@ -60,7 +60,7 @@ If you want to get a copy of an existing Git repository -- for example, a projec If you're familiar with other VCSs such as Subversion, you'll notice that the command is "clone" and not "checkout". This is an important distinction -- instead of getting just a working copy, Git receives a full copy of nearly all data that the server has. Every version of every file for the history of the project is pulled down by default when you run `git clone`. -In fact, if your server disk gets corrupted, you can often use nearly any of the clones on any client to set the server back to the state it was in when it was cloned (you may lose some server-side hooks and such, but all the versioned data would be there -- see <> for more details). +In fact, if your server disk gets corrupted, you can often use nearly any of the clones on any client to set the server back to the state it was in when it was cloned (you may lose some server-side hooks and such, but all the versioned data would be there -- see <<_getting_git_on_a_server,Ch 8, Git Servers>> for more details). You clone a repository with `git clone `.(((git commands, clone))) For example, if you want to clone the Git linkable library called `libgit2`, you can do so like this: @@ -84,4 +84,4 @@ That command does the same thing as the previous one, but the target directory i Git has a number of different transfer protocols you can use. The previous example uses the `https://` protocol, but you may also see `git://` or `user@server:path/to/repo.git`, which uses the SSH transfer protocol. -<> will introduce all of the available options the server can set up to access your Git repository and the pros and cons of each. +<<_getting_git_on_a_server,Ch 8, Git Servers>> will introduce all of the available options the server can set up to access your Git repository and the pros and cons of each. diff --git a/book/ch02/recording-changes.asc b/book/ch02/recording-changes.asc index 8bcd785..34a0998 100644 --- a/book/ch02/recording-changes.asc +++ b/book/ch02/recording-changes.asc @@ -34,13 +34,13 @@ This means you have a clean working directory; in other words, none of your trac Git also doesn't see any untracked files, or they would be listed here. Finally, the command tells you which branch you're on and informs you that it has not diverged from the same branch on the server. For now, that branch is always `master`, which is the default; you won't worry about it here. -<> will go over branches and references in detail. +<> will go over branches and references in detail. [NOTE] ==== GitHub changed the default branch name from `master` to `main` in mid-2020, and other Git hosts followed suit. So you may find that the default branch name in some newly created repositories is `main` and not `master`. -In addition, the default branch name can be changed (as you have seen in <>), so you may see a different name for the default branch. +In addition, the default branch name can be changed (as you have seen in <<_new_default_branch,Ch 1, Getting Started>>), so you may see a different name for the default branch. However, Git itself still uses `master` as the default, so we will use it throughout the book. ==== @@ -432,7 +432,7 @@ Doing so launches your editor of choice. [NOTE] ==== -This is set by your shell's `EDITOR` environment variable -- usually vim or emacs, although you can configure it with whatever you want using the `git config --global core.editor` command as you saw in <>.(((editor, changing default)))(((git commands, config))) +This is set by your shell's `EDITOR` environment variable -- usually vim or emacs, although you can configure it with whatever you want using the `git config --global core.editor` command as you saw in <>.(((editor, changing default)))(((git commands, config))) ==== The editor displays the following text (this example is a Vim screen): diff --git a/book/ch02/remotes.asc b/book/ch02/remotes.asc index 80e9825..3dabe4b 100644 --- a/book/ch02/remotes.asc +++ b/book/ch02/remotes.asc @@ -67,7 +67,7 @@ origin git@github.com:mojombo/grit.git (push) This means we can pull contributions from any of these users pretty easily. We may additionally have permission to push to one or more of these, though we can't tell that here. -Notice that these remotes use a variety of protocols; we'll cover more about this in <>. +Notice that these remotes use a variety of protocols; we'll cover more about this in <<_getting_git_on_a_server,Ch 8, Git Servers>>. ==== Adding Remote Repositories @@ -103,7 +103,7 @@ From https://github.com/paulboone/ticgit ---- Paul's `master` branch is now accessible locally as `pb/master` -- you can merge it into one of your branches, or you can check out a local branch at that point if you want to inspect it. -We'll go over what branches are and how to use them in much more detail in <>. +We'll go over what branches are and how to use them in much more detail in <>. [[_fetching_and_pulling]] ==== Fetching and Pulling from Your Remotes @@ -123,7 +123,7 @@ So, `git fetch origin` fetches any new work that has been pushed to that server It's important to note that the `git fetch` command only downloads the data to your local repository -- it doesn't automatically merge it with any of your work or modify what you're currently working on. You have to merge it manually into your work when you're ready. -If your current branch is set up to track a remote branch (see the next section and <> for more information), you can use the `git pull` command to automatically fetch and then merge that remote branch into your current branch.(((git commands, pull))) +If your current branch is set up to track a remote branch (see the next section and <> for more information), you can use the `git pull` command to automatically fetch and then merge that remote branch into your current branch.(((git commands, pull))) This may be an easier or more comfortable workflow for you; and by default, the `git clone` command automatically sets up your local `master` branch to track the remote `master` branch (or whatever the default branch is called) on the server you cloned from. Running `git pull` generally fetches data from the server you originally cloned from and automatically tries to merge it into the code you're currently working on. @@ -154,7 +154,7 @@ $ git push origin master This command works only if you cloned from a server to which you have write access and if nobody has pushed in the meantime. If you and someone else clone at the same time and they push upstream and then you push upstream, your push will rightly be rejected. You'll have to fetch their work first and incorporate it into yours before you'll be allowed to push. -See <> for more detailed information on how to push to remote servers. +See <> for more detailed information on how to push to remote servers. [[_inspecting_remote]] ==== Inspecting a Remote diff --git a/book/ch02/undoing.asc b/book/ch02/undoing.asc index 0c815dd..194a418 100644 --- a/book/ch02/undoing.asc +++ b/book/ch02/undoing.asc @@ -99,7 +99,7 @@ However, in the scenario described above, the file in your working directory is ===== For now this magic invocation is all you need to know about the `git reset` command. -We'll go into much more detail about what `reset` does and how to master it to do really interesting things in <>. +We'll go into much more detail about what `reset` does and how to master it to do really interesting things in <<_git_reset,Ch 5, Git Toolkit>>. ==== Unmodifying a Modified File @@ -141,10 +141,10 @@ Any local changes you made to that file are gone -- Git just replaced that file Don't ever use this command unless you absolutely know that you don't want those unsaved local changes. ===== -If you would like to keep the changes you've made to that file but still need to get it out of the way for now, we'll go over stashing and branching in <>; these are generally better ways to go. +If you would like to keep the changes you've made to that file but still need to get it out of the way for now, we'll go over stashing and branching in <>; these are generally better ways to go. Remember, anything that is _committed_ in Git can almost always be recovered. -Even commits that were on branches that were deleted or commits that were overwritten with an `--amend` commit can be recovered (see <> for data recovery). +Even commits that were on branches that were deleted or commits that were overwritten with an `--amend` commit can be recovered (see <<_data_recovery,Ch 10, Git Internals>> for data recovery). However, anything you lose that was never committed is likely never to be seen again. [[undoing_git_restore]] diff --git a/book/ch02/viewing-history.asc b/book/ch02/viewing-history.asc index 440c7f2..5e6c712 100644 --- a/book/ch02/viewing-history.asc +++ b/book/ch02/viewing-history.asc @@ -182,7 +182,7 @@ a11bef0 - Scott Chacon, 6 years ago : Initial commit You may be wondering what the difference is between _author_ and _committer_. The author is the person who originally wrote the work, whereas the committer is the person who last applied the work. So, if you send in a patch to a project and one of the core members applies the patch, both of you get credit -- you as the author, and the core member as the committer. -We'll cover this distinction a bit more in <>. +We'll cover this distinction a bit more in <>. The `oneline` and `format` option values are particularly useful with another `log` option called `--graph`. This option adds a nice little ASCII graph showing your branch and merge history: diff --git a/book/ch03-git-branching.asc b/book/ch03-git-branching.asc index cebef89..5713a6e 100644 --- a/book/ch03-git-branching.asc +++ b/book/ch03-git-branching.asc @@ -20,6 +20,8 @@ include::ch03/branch-management.asc[] include::ch03/workflows.asc[] +include::ch03/worktrees.asc[] + include::ch03/remote-branches.asc[] include::ch03/rebasing.asc[] diff --git a/book/ch03/basic-branching-and-merging.asc b/book/ch03/basic-branching-and-merging.asc index a894b67..902006a 100644 --- a/book/ch03/basic-branching-and-merging.asc +++ b/book/ch03/basic-branching-and-merging.asc @@ -62,7 +62,7 @@ All you have to do is switch back to your `master` branch. However, before you do that, note that if your working directory or staging area has uncommitted changes that conflict with the branch you're checking out, Git won't let you switch branches. It's best to have a clean working state when you switch branches. -There are ways to get around this (namely, stashing and commit amending) that we'll cover later on, in <>. +There are ways to get around this (namely, stashing and commit amending) that we'll cover later on, in <<_git_stashing,Ch 5, Git Toolkit>>. For now, let's assume you've committed all your changes, so you can switch back to your `master` branch: [source,console] @@ -270,7 +270,7 @@ Just type the name of the tool you'd rather use. [NOTE] ==== -If you need more advanced tools for resolving tricky merge conflicts, we cover more on merging in <>. +If you need more advanced tools for resolving tricky merge conflicts, we cover more on merging in <<_advanced_merging,Ch 5, Git Toolkit>>. ==== After you exit the merge tool, Git asks you if the merge was successful. diff --git a/book/ch03/nutshell.asc b/book/ch03/nutshell.asc index 2bffd5d..c7321f6 100644 --- a/book/ch03/nutshell.asc +++ b/book/ch03/nutshell.asc @@ -3,13 +3,13 @@ To really understand the way Git does branching, we need to take a step back and examine how Git stores its data. -As you may remember from <>, Git doesn't store data as a series of changesets or differences, but instead as a series of _snapshots_. +As you may remember from <>, Git doesn't store data as a series of changesets or differences, but instead as a series of _snapshots_. When you make a commit, Git stores a commit object that contains a pointer to the snapshot of the content you staged. This object also contains the author's name and email address, the message that you typed, and pointers to the commit or commits that directly came before this commit (its parent or parents): zero parents for the initial commit, one parent for a normal commit, and multiple parents for a commit that results from a merge of two or more branches. To visualize this, let's assume that you have a directory containing three files, and you stage them all and commit. -Staging the files computes a checksum for each one (the SHA-1 hash we mentioned in <>), stores that version of the file in the Git repository (Git refers to them as _blobs_), and adds that checksum to the staging area: +Staging the files computes a checksum for each one (the SHA-1 hash we mentioned in <>), stores that version of the file in the Git repository (Git refers to them as _blobs_), and adds that checksum to the staging area: [source,console] ---- diff --git a/book/ch03/remote-branches.asc b/book/ch03/remote-branches.asc index adbb873..c8de8e1 100644 --- a/book/ch03/remote-branches.asc +++ b/book/ch03/remote-branches.asc @@ -44,7 +44,7 @@ image::images/remote-branches-3.png[`git fetch` updates your remote-tracking bra To demonstrate having multiple remote servers and what remote branches for those remote projects look like, let's assume you have another internal Git server that is used only for development by one of your sprint teams. This server is at `git.team1.ourcompany.com`. -You can add it as a new remote reference to the project you're currently working on by running the `git remote add` command as we covered in <>. +You can add it as a new remote reference to the project you're currently working on by running the `git remote add` command as we covered in <>. Name this remote `teamone`, which will be your shortname for that whole URL. .Adding another server as a remote @@ -81,7 +81,7 @@ To https://github.com/schacon/simplegit This is a bit of a shortcut. Git automatically expands the `serverfix` branchname out to `refs/heads/serverfix:refs/heads/serverfix`, which means, "`Take my `serverfix` local branch and push it to update the remote's `serverfix` branch.`" -We'll go over the `refs/heads/` part in detail in <>, but you can generally leave it off. +We'll go over the `refs/heads/` part in detail in <>, but you can generally leave it off. You can also do `git push origin serverfix:serverfix`, which does the same thing -- it says, "`Take my serverfix and make it the remote's serverfix.`" You can use this format to push a local branch into a remote branch that is named differently. If you didn't want it to be called `serverfix` on the remote, you could instead run `git push origin serverfix:awesomebranch` to push your local `serverfix` branch to the `awesomebranch` branch on the remote project. @@ -95,7 +95,7 @@ By default it will prompt you on the terminal for this information so the server If you don't want to type it every single time you push, you can set up a "`credential cache`". The simplest is just to keep it in memory for a few minutes, which you can easily set up by running `git config --global credential.helper cache`. -For more information on the various credential caching options available, see <>. +For more information on the various credential caching options available, see <<_credential_caching,Ch 5, Git Toolkit>>. ==== The next time one of your collaborators fetches from the server, they will get a reference to where the server's version of `serverfix` is under the remote branch `origin/serverfix`: diff --git a/book/ch03/workflows.asc b/book/ch03/workflows.asc index 9e6f392..e38eafc 100644 --- a/book/ch03/workflows.asc +++ b/book/ch03/workflows.asc @@ -57,7 +57,7 @@ Your history then looks like this: .History after merging `dumbidea` and `iss91v2` image::images/topic-branches-2.png[History after merging `dumbidea` and `iss91v2`] -We will go into more detail about the various possible workflows for your Git project in <>, so before you decide which branching scheme your next project will use, be sure to read that chapter. +We will go into more detail about the various possible workflows for your Git project in <>, so before you decide which branching scheme your next project will use, be sure to read that chapter. It's important to remember when you're doing all this that these branches are completely local. When you're branching and merging, everything is being done only in your Git repository -- there is no communication with the server. diff --git a/book/ch03/worktrees.asc b/book/ch03/worktrees.asc new file mode 100644 index 0000000..2e09e0c --- /dev/null +++ b/book/ch03/worktrees.asc @@ -0,0 +1,28 @@ +[[_worktrees]] +=== Worktrees + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 3.5. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* What a worktree is: multiple working trees sharing a single repository + (one object database, one set of refs, many checkouts) +* `git worktree add`, `list`, `move`, `remove`, `lock`/`unlock`, `prune`; + `git worktree add --orphan` for empty starting points +* Use cases: +** fixing a bug on another branch without stashing or disturbing a work in progress +** running builds/tests on one branch while working on another +** one checkout per coding agent for parallel agent work + (forward pointer to <>) +* Rules and housekeeping: a branch can only be checked out in one worktree at a + time; where the linked worktree metadata lives; cleaning up with `prune` +* New diagrams needed in `diagram-source/` (shared repository, multiple working trees) diff --git a/book/ch05-distributed-git.asc b/book/ch04-distributed-git.asc similarity index 62% rename from book/ch05-distributed-git.asc rename to book/ch04-distributed-git.asc index 3ce517a..03126c6 100644 --- a/book/ch05-distributed-git.asc +++ b/book/ch04-distributed-git.asc @@ -1,4 +1,4 @@ -[[ch05-distributed-git]] +[[ch04-distributed-git]] == Distributed Git (((distributed git))) @@ -7,14 +7,19 @@ Now that you have a remote Git repository set up as a focal point for all the de In this chapter, you'll see how to work with Git in a distributed environment as a contributor and an integrator. That is, you'll learn how to contribute code successfully to a project and make it as easy on you and the project maintainer as possible, and also how to maintain a project successfully with a number of developers contributing. -include::ch05/distributed-workflows.asc[] +include::ch04/distributed-workflows.asc[] -include::ch05/contributing.asc[] +include::ch04/contributing.asc[] -include::ch05/maintaining.asc[] +include::ch04/pull-requests.asc[] + +include::ch04/maintaining.asc[] === Summary You should feel fairly comfortable contributing to a project in Git as well as maintaining your own project or integrating other users' contributions. Congratulations on being an effective Git developer! -In the next chapter, you'll learn about how to use the largest and most popular Git hosting service, GitHub. +// AUTHOR TODO (restructure): the dedicated GitHub chapter was dropped in the 3rd +// edition (forge/PR material now lives in this chapter) and the next chapter is now +// Git Toolkit. Write a new closing sentence for the new chapter order. +// In the next chapter, you'll learn about how to use the largest and most popular Git hosting service, GitHub. diff --git a/book/ch05/contributing.asc b/book/ch04/contributing.asc similarity index 97% rename from book/ch05/contributing.asc rename to book/ch04/contributing.asc index 2c6b085..05e3b89 100644 --- a/book/ch05/contributing.asc +++ b/book/ch04/contributing.asc @@ -49,11 +49,11 @@ If you run that command before committing, you can tell if you're about to commi Next, try to make each commit a logically separate changeset. If you can, try to make your changes digestible -- don't code for a whole weekend on five different issues and then submit them all as one massive commit on Monday. Even if you don't commit during the weekend, use the staging area on Monday to split your work into at least one commit per issue, with a useful message per commit. -If some of the changes modify the same file, try to use `git add --patch` to partially stage files (covered in detail in <>). +If some of the changes modify the same file, try to use `git add --patch` to partially stage files (covered in detail in <<_interactive_staging,Ch 5, Git Toolkit>>). The project snapshot at the tip of the branch is identical whether you do one commit or five, as long as all the changes are added at some point, so try to make things easier on your fellow developers when they have to review your changes. This approach also makes it easier to pull out or revert one of the changesets if you need to later. -<> describes a number of useful Git tricks for rewriting history and interactively staging files -- use these tools to help craft a clean and understandable history before sending the work to someone else. +<<_rewriting_history,Ch 5, Git Toolkit>> describes a number of useful Git tricks for rewriting history and interactively staging files -- use these tools to help craft a clean and understandable history before sending the work to someone else. The last thing to keep in mind is the commit message. Getting in the habit of creating quality commit messages makes using and collaborating with Git a lot easier. @@ -254,7 +254,7 @@ Date: Fri May 29 16:01:27 2009 -0700 ---- The `issue54..origin/master` syntax is a log filter that asks Git to display only those commits that are on the latter branch (in this case `origin/master`) and that are not on the first branch (in this case `issue54`). -We'll go over this syntax in detail in <>. +We'll go over this syntax in detail in <<_commit_ranges,Ch 5, Git Toolkit>>. From the above output, we can see that there is a single commit that John has made that Jessica has not merged into her local work. If she merges `origin/master`, that is the single commit that will modify her local work. @@ -426,7 +426,7 @@ To jessica@githost:simplegit.git ---- This is called a _refspec_. -See <> for a more detailed discussion of Git refspecs and different things you can do with them. +See <<_refspec,Ch 10, Git Internals>> for a more detailed discussion of Git refspecs and different things you can do with them. Also notice the `-u` flag; this is short for `--set-upstream`, which configures the branches for easier pushing and pulling later. Suddenly, Jessica gets email from John, who tells her he's pushed some changes to the `featureA` branch on which they are collaborating, and he asks Jessica to take a look at them. @@ -522,7 +522,7 @@ $ git commit [NOTE] ==== -You may want to use `rebase -i` to squash your work down to a single commit, or rearrange the work in the commits to make the patch easier for the maintainer to review -- see <> for more information about interactive rebasing. +You may want to use `rebase -i` to squash your work down to a single commit, or rearrange the work in the commits to make the patch easier for the maintainer to review -- see <<_rewriting_history,Ch 5, Git Toolkit>> for more information about interactive rebasing. ==== When your branch work is finished and you're ready to contribute it back to the maintainers, go to the original project page and click the "`Fork`" button, creating your own writable fork of the project. @@ -535,7 +535,7 @@ $ git remote add myfork You then need to push your new work to this repository. It's easiest to push the topic branch you're working on to your forked repository, rather than merging that work into your `master` branch and pushing that. -The reason is that if your work isn't accepted or is cherry-picked, you don't have to rewind your `master` branch (the Git `cherry-pick` operation is covered in more detail in <>). +The reason is that if your work isn't accepted or is cherry-picked, you don't have to rewind your `master` branch (the Git `cherry-pick` operation is covered in more detail in <<_rebase_cherry_pick,Ch 4, Distributed Git>>). If the maintainers `merge`, `rebase`, or `cherry-pick` your work, you'll eventually get it back via pulling from their repository anyhow. In any event, you can push your work with: @@ -547,7 +547,7 @@ $ git push -u myfork featureA (((git commands, request-pull))) Once your work has been pushed to your fork of the repository, you need to notify the maintainers of the original project that you have work you'd like them to merge. -This is often called a _pull request_, and you typically generate such a request either via the website -- GitHub has its own "`Pull Request`" mechanism that we'll go over in <> -- or you can run the `git request-pull` command and email the subsequent output to the project maintainer manually. +This is often called a _pull request_, and you typically generate such a request either via the website -- GitHub has its own "`Pull Request`" mechanism that we'll go over in <<_pull_requests_and_forges,Ch 4, Distributed Git>> -- or you can run the `git request-pull` command and email the subsequent output to the project maintainer manually. The `git request-pull` command takes the base branch into which you want your topic branch pulled and the Git repository URL you want them to pull from, and produces a summary of all the changes you're asking to be pulled. For instance, if Jessica wants to send John a pull request, and she's done two commits on the topic branch she just pushed, she can run this: diff --git a/book/ch05/distributed-workflows.asc b/book/ch04/distributed-workflows.asc similarity index 100% rename from book/ch05/distributed-workflows.asc rename to book/ch04/distributed-workflows.asc diff --git a/book/ch05/maintaining.asc b/book/ch04/maintaining.asc similarity index 99% rename from book/ch05/maintaining.asc rename to book/ch04/maintaining.asc index c377bb6..edac1f0 100644 --- a/book/ch05/maintaining.asc +++ b/book/ch04/maintaining.asc @@ -423,14 +423,14 @@ Now, whenever you do a merge that resolves conflicts, the resolution will be rec If you need to, you can interact with the rerere cache using the `git rerere` command. When it's invoked alone, Git checks its database of resolutions and tries to find a match with any current merge conflicts and resolve them (although this is done automatically if `rerere.enabled` is set to `true`). There are also subcommands to see what will be recorded, to erase specific resolution from the cache, and to clear the entire cache. -We will cover rerere in more detail in <>. +We will cover rerere in more detail in <>. [[_tagging_releases]] ==== Tagging Your Releases (((tags)))(((tags, signing))) When you've decided to cut a release, you'll probably want to assign a tag so you can re-create that release at any point going forward. -You can create a new tag as discussed in <>. +You can create a new tag as discussed in <>. If you decide to sign the tag as the maintainer, the tagging may look something like this: [source,console] diff --git a/book/ch04/pull-requests.asc b/book/ch04/pull-requests.asc new file mode 100644 index 0000000..2e56308 --- /dev/null +++ b/book/ch04/pull-requests.asc @@ -0,0 +1,47 @@ +[[_pull_requests_and_forges]] +=== Pull Requests and Forges + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 4.3. + +This section absorbs the load-bearing material from the 2nd edition's GitHub +chapter (ch06/2-contributing.asc and ch06/3-maintaining.asc, removed in the +restructure — recover via git history). That prose must be rewritten forge-neutral +by a human, not just moved. The anchors below are preserved from the old chapter so +existing cross-references keep resolving; keep them attached to the equivalent +content when writing. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover (forge-neutral): + +* The fork → branch → pull request → review → merge loop +* Opening a PR, iterating on review feedback, updating the PR branch +* Merge strategies on forges: merge commit, squash, rebase +* The `gh` CLI for creating and managing PRs from the terminal +* How this relates to the branching workflows in + <<_branching_workflows,Ch 3, Git Branching>> (GitHub Flow, trunk-based, stacked) + +[[_fetch_and_push_on_different_repositories]] +Keeping a fork in sync — fetching from the upstream repository while pushing to +your fork (triangular workflows): + +* upstream/origin remote setup, `remote.pushDefault`, `branch..pushRemote` + +[[_pr_refs]] +Pull request refs (`refs/pull/*`): + +* how forges advertise PR heads as refs; fetching a specific PR by ref; + inspecting raw references with `ls-remote` + +[[_email_notifications]] +PR notifications and patch-formatted changes: + +* applying PR changes as email patches with `git am` + (referenced from <>) diff --git a/book/ch07-git-tools.asc b/book/ch05-git-toolkit.asc similarity index 66% rename from book/ch07-git-tools.asc rename to book/ch05-git-toolkit.asc index 40f2b04..c26aa4d 100644 --- a/book/ch07-git-tools.asc +++ b/book/ch05-git-toolkit.asc @@ -1,38 +1,38 @@ -[[ch07-git-tools]] -== Git Tools +[[ch05-git-toolkit]] +== Git Toolkit By now, you've learned most of the day-to-day commands and workflows that you need to manage or maintain a Git repository for your source code control. You've accomplished the basic tasks of tracking and committing files, and you've harnessed the power of the staging area and lightweight topic branching and merging. Now you'll explore a number of very powerful things that Git can do that you may not necessarily use on a day-to-day basis but that you may need at some point. -include::ch07/revision-selection.asc[] +include::ch05/revision-selection.asc[] -include::ch07/interactive-staging.asc[] +include::ch05/interactive-staging.asc[] -include::ch07/stashing-cleaning.asc[] +include::ch05/stashing-cleaning.asc[] -include::ch07/signing.asc[] +include::ch05/signing.asc[] -include::ch07/searching.asc[] +include::ch05/searching.asc[] -include::ch07/rewriting-history.asc[] +include::ch05/rewriting-history.asc[] -include::ch07/reset.asc[] +include::ch05/reset.asc[] -include::ch07/advanced-merging.asc[] +include::ch05/advanced-merging.asc[] -include::ch07/rerere.asc[] +include::ch05/rerere.asc[] -include::ch07/debugging.asc[] +include::ch05/debugging.asc[] -include::ch07/submodules.asc[] +include::ch05/submodules.asc[] -include::ch07/bundling.asc[] +include::ch05/bundling.asc[] -include::ch07/replace.asc[] +include::ch05/replace.asc[] -include::ch07/credentials.asc[] +include::ch05/credentials.asc[] === Summary diff --git a/book/ch07/advanced-merging.asc b/book/ch05/advanced-merging.asc similarity index 97% rename from book/ch07/advanced-merging.asc rename to book/ch05/advanced-merging.asc index 7ace27b..c8f834a 100644 --- a/book/ch07/advanced-merging.asc +++ b/book/ch05/advanced-merging.asc @@ -14,7 +14,7 @@ We'll also cover some of the different, non-standard types of merges you can do, ==== Merge Conflicts -While we covered some basics on resolving merge conflicts in <>, for more complex conflicts, Git provides a few tools to help you figure out what's going on and how to better deal with the conflict. +While we covered some basics on resolving merge conflicts in <<_basic_merge_conflicts,Ch 3, Git Branching>>, for more complex conflicts, Git provides a few tools to help you figure out what's going on and how to better deal with the conflict. First of all, if at all possible, try to make sure your working directory is clean before doing a merge that may have conflicts. If you have work in progress, either commit it to a temporary branch or stash it. @@ -406,7 +406,7 @@ Another useful tool when resolving merge conflicts is `git log`. This can help you get context on what may have contributed to the conflicts. Reviewing a little bit of history to remember why two lines of development were touching the same area of code can be really helpful sometimes. -To get a full list of all of the unique commits that were included in either branch involved in this merge, we can use the "`triple dot`" syntax that we learned in <>. +To get a full list of all of the unique commits that were included in either branch involved in this merge, we can use the "`triple dot`" syntax that we learned in <<_triple_dot,Ch 5, Git Toolkit>>. [source,console] ---- @@ -547,7 +547,7 @@ In most cases, if you follow the errant `git merge` with `git reset --hard HEAD~ .History after `git reset --hard HEAD~` image::images/undomerge-reset.png[History after `git reset --hard HEAD~`] -We covered `reset` back in <>, so it shouldn't be too hard to figure out what's going on here. +We covered `reset` back in <<_git_reset,Ch 5, Git Toolkit>>, so it shouldn't be too hard to figure out what's going on here. Here's a quick refresher: `reset --hard` usually goes through three steps: . Move the branch HEAD points to. @@ -556,7 +556,7 @@ Here's a quick refresher: `reset --hard` usually goes through three steps: . Make the working directory look like the index. The downside of this approach is that it's rewriting history, which can be problematic with a shared repository. -Check out <> for more on what can happen; the short version is that if other people have the commits you're rewriting, you should probably avoid `reset`. +Check out <<_rebase_peril,Ch 3, Git Branching>> for more on what can happen; the short version is that if other people have the commits you're rewriting, you should probably avoid `reset`. This approach also won't work if any other commits have been created since the merge; moving the refs would effectively lose those changes. [[_reverse_commit]] diff --git a/book/ch07/bundling.asc b/book/ch05/bundling.asc similarity index 98% rename from book/ch07/bundling.asc rename to book/ch05/bundling.asc index 79cb1b8..06e6b34 100644 --- a/book/ch07/bundling.asc +++ b/book/ch05/bundling.asc @@ -83,7 +83,7 @@ Unlike the network protocols which figure out the minimum set of data to transfe Now, you could just do the same thing and bundle the entire repository, which will work, but it's better to just bundle up the difference - just the three commits we just made locally. In order to do that, you'll have to calculate the difference. -As we described in <>, you can specify a range of commits in a number of ways. +As we described in <<_commit_ranges,Ch 5, Git Toolkit>>, you can specify a range of commits in a number of ways. To get the three commits that we have in our `master` branch that weren't in the branch we originally cloned, we can use something like `origin/master..master` or `master ^origin/master`. You can test that with the `log` command. diff --git a/book/B/callouts/1.pdf b/book/ch05/callouts/1.pdf similarity index 100% rename from book/B/callouts/1.pdf rename to book/ch05/callouts/1.pdf diff --git a/book/ch06/callouts/1.png b/book/ch05/callouts/1.png similarity index 100% rename from book/ch06/callouts/1.png rename to book/ch05/callouts/1.png diff --git a/book/B/callouts/10.pdf b/book/ch05/callouts/10.pdf similarity index 100% rename from book/B/callouts/10.pdf rename to book/ch05/callouts/10.pdf diff --git a/book/ch06/callouts/10.png b/book/ch05/callouts/10.png similarity index 100% rename from book/ch06/callouts/10.png rename to book/ch05/callouts/10.png diff --git a/book/B/callouts/2.pdf b/book/ch05/callouts/2.pdf similarity index 100% rename from book/B/callouts/2.pdf rename to book/ch05/callouts/2.pdf diff --git a/book/ch06/callouts/2.png b/book/ch05/callouts/2.png similarity index 100% rename from book/ch06/callouts/2.png rename to book/ch05/callouts/2.png diff --git a/book/B/callouts/3.pdf b/book/ch05/callouts/3.pdf similarity index 100% rename from book/B/callouts/3.pdf rename to book/ch05/callouts/3.pdf diff --git a/book/ch06/callouts/3.png b/book/ch05/callouts/3.png similarity index 100% rename from book/ch06/callouts/3.png rename to book/ch05/callouts/3.png diff --git a/book/B/callouts/4.pdf b/book/ch05/callouts/4.pdf similarity index 100% rename from book/B/callouts/4.pdf rename to book/ch05/callouts/4.pdf diff --git a/book/ch06/callouts/4.png b/book/ch05/callouts/4.png similarity index 100% rename from book/ch06/callouts/4.png rename to book/ch05/callouts/4.png diff --git a/book/B/callouts/5.pdf b/book/ch05/callouts/5.pdf similarity index 100% rename from book/B/callouts/5.pdf rename to book/ch05/callouts/5.pdf diff --git a/book/ch06/callouts/5.png b/book/ch05/callouts/5.png similarity index 100% rename from book/ch06/callouts/5.png rename to book/ch05/callouts/5.png diff --git a/book/B/callouts/6.pdf b/book/ch05/callouts/6.pdf similarity index 100% rename from book/B/callouts/6.pdf rename to book/ch05/callouts/6.pdf diff --git a/book/ch06/callouts/6.png b/book/ch05/callouts/6.png similarity index 100% rename from book/ch06/callouts/6.png rename to book/ch05/callouts/6.png diff --git a/book/B/callouts/7.pdf b/book/ch05/callouts/7.pdf similarity index 100% rename from book/B/callouts/7.pdf rename to book/ch05/callouts/7.pdf diff --git a/book/ch06/callouts/7.png b/book/ch05/callouts/7.png similarity index 100% rename from book/ch06/callouts/7.png rename to book/ch05/callouts/7.png diff --git a/book/B/callouts/8.pdf b/book/ch05/callouts/8.pdf similarity index 100% rename from book/B/callouts/8.pdf rename to book/ch05/callouts/8.pdf diff --git a/book/ch06/callouts/8.png b/book/ch05/callouts/8.png similarity index 100% rename from book/ch06/callouts/8.png rename to book/ch05/callouts/8.png diff --git a/book/B/callouts/9.pdf b/book/ch05/callouts/9.pdf similarity index 100% rename from book/B/callouts/9.pdf rename to book/ch05/callouts/9.pdf diff --git a/book/ch06/callouts/9.png b/book/ch05/callouts/9.png similarity index 100% rename from book/ch06/callouts/9.png rename to book/ch05/callouts/9.png diff --git a/book/ch07/credentials.asc b/book/ch05/credentials.asc similarity index 100% rename from book/ch07/credentials.asc rename to book/ch05/credentials.asc diff --git a/book/ch07/debugging.asc b/book/ch05/debugging.asc similarity index 100% rename from book/ch07/debugging.asc rename to book/ch05/debugging.asc diff --git a/book/ch07/git-credential-read-only b/book/ch05/git-credential-read-only similarity index 100% rename from book/ch07/git-credential-read-only rename to book/ch05/git-credential-read-only diff --git a/book/ch07/interactive-staging.asc b/book/ch05/interactive-staging.asc similarity index 100% rename from book/ch07/interactive-staging.asc rename to book/ch05/interactive-staging.asc diff --git a/book/ch07/replace.asc b/book/ch05/replace.asc similarity index 99% rename from book/ch07/replace.asc rename to book/ch05/replace.asc index fd7c8ec..06d7ca2 100644 --- a/book/ch07/replace.asc +++ b/book/ch05/replace.asc @@ -96,7 +96,7 @@ $ echo 'Get history from blah blah blah' | git commit-tree 9c68fdc^{tree} The `commit-tree` command is one of a set of commands that are commonly referred to as 'plumbing' commands. These are commands that are not generally meant to be used directly, but instead are used by *other* Git commands to do smaller jobs. On occasions when we're doing weirder things like this, they allow us to do really low-level things but are not meant for daily use. -You can read more about plumbing commands in <>. +You can read more about plumbing commands in <<_plumbing_porcelain,Ch 10, Git Internals>>. ===== .Creating a base commit using `commit-tree` diff --git a/book/ch07/rerere.asc b/book/ch05/rerere.asc similarity index 98% rename from book/ch07/rerere.asc rename to book/ch05/rerere.asc index dd3e60e..ed4310d 100644 --- a/book/ch07/rerere.asc +++ b/book/ch05/rerere.asc @@ -148,7 +148,7 @@ You can see that it "Recorded resolution for FILE". image::images/rerere2.png[Recorded resolution for FILE] Now, let's undo that merge and then rebase it on top of our `master` branch instead. -We can move our branch back by using `git reset` as we saw in <>. +We can move our branch back by using `git reset` as we saw in <<_git_reset,Ch 5, Git Toolkit>>. [source,console] ---- @@ -227,7 +227,7 @@ def hello end ---- -We saw an example of this in <>. +We saw an example of this in <<_advanced_merging,Ch 5, Git Toolkit>>. For now though, let's re-resolve it by just running `git rerere` again: [source,console] diff --git a/book/ch07/reset.asc b/book/ch05/reset.asc similarity index 99% rename from book/ch07/reset.asc rename to book/ch05/reset.asc index f5b9534..a738d36 100644 --- a/book/ch07/reset.asc +++ b/book/ch05/reset.asc @@ -233,7 +233,7 @@ If we look at the diagram for that command and think about what `git add` does, .Staging file to index image::images/reset-path2.png[Staging file to index] -This is why the output of the `git status` command suggests that you run this to unstage a file (see <> for more on this). +This is why the output of the `git status` command suggests that you run this to unstage a file (see <<_unstaging,Ch 2, Git Basics>> for more on this). We could just as easily not let Git assume we meant "`pull the data from HEAD`" by specifying a specific commit to pull that file version from. We would just run something like `git reset eb43bf file.txt`. diff --git a/book/ch07/revision-selection.asc b/book/ch05/revision-selection.asc similarity index 98% rename from book/ch07/revision-selection.asc rename to book/ch05/revision-selection.asc index 95e8e83..b63bfa0 100644 --- a/book/ch07/revision-selection.asc +++ b/book/ch05/revision-selection.asc @@ -99,7 +99,7 @@ $ git show topic1 ---- If you want to see which specific SHA-1 a branch points to, or if you want to see what any of these examples boils down to in terms of SHA-1s, you can use a Git plumbing tool called `rev-parse`. -You can see <> for more information about plumbing tools; basically, `rev-parse` exists for lower-level operations and isn't designed to be used in day-to-day operations. +You can see <> for more information about plumbing tools; basically, `rev-parse` exists for lower-level operations and isn't designed to be used in day-to-day operations. However, it can be helpful sometimes when you need to see what's really going on. Here you can run `rev-parse` on your branch. diff --git a/book/ch07/rewriting-history.asc b/book/ch05/rewriting-history.asc similarity index 99% rename from book/ch07/rewriting-history.asc rename to book/ch05/rewriting-history.asc index 7fbb06d..930e95b 100644 --- a/book/ch07/rewriting-history.asc +++ b/book/ch05/rewriting-history.asc @@ -318,7 +318,7 @@ If you get partway through a rebase like this and decide it's not a good idea, y Type `git rebase --abort`, and your repo will be returned to the state it was in before you started the rebase. If you finish a rebase and decide it's not what you want, you can use `git reflog` to recover an earlier version of your branch. -See <> for more information on the `reflog` command. +See <<_data_recovery,Ch 10, Git Internals>> for more information on the `reflog` command. [NOTE] ==== diff --git a/book/ch07/searching.asc b/book/ch05/searching.asc similarity index 100% rename from book/ch07/searching.asc rename to book/ch05/searching.asc diff --git a/book/ch07/signing.asc b/book/ch05/signing.asc similarity index 100% rename from book/ch07/signing.asc rename to book/ch05/signing.asc diff --git a/book/ch07/stashing-cleaning.asc b/book/ch05/stashing-cleaning.asc similarity index 100% rename from book/ch07/stashing-cleaning.asc rename to book/ch05/stashing-cleaning.asc diff --git a/book/ch07/submodules.asc b/book/ch05/submodules.asc similarity index 99% rename from book/ch07/submodules.asc rename to book/ch05/submodules.asc index 188ff64..6a81046 100644 --- a/book/ch07/submodules.asc +++ b/book/ch05/submodules.asc @@ -862,7 +862,7 @@ This is obviously a simplified example, but hopefully it gives you an idea of ho ===== Useful Aliases You may want to set up some aliases for some of these commands as they can be quite long and you can't set configuration options for most of them to make them defaults. -We covered setting up Git aliases in <>, but here is an example of what you may want to set up if you plan on working with submodules in Git a lot. +We covered setting up Git aliases in <<_git_aliases,Ch 2, Git Basics>>, but here is an example of what you may want to set up if you plan on working with submodules in Git a lot. [source,console] ---- diff --git a/book/ch07/subtree-merges.asc b/book/ch05/subtree-merges.asc similarity index 96% rename from book/ch07/subtree-merges.asc rename to book/ch05/subtree-merges.asc index 24bfa3a..fd0862b 100644 --- a/book/ch07/subtree-merges.asc +++ b/book/ch05/subtree-merges.asc @@ -49,7 +49,7 @@ It's not common, because it's rarely helpful, but it's fairly easy to have branc In this case, we want to pull the Rack project into our `master` project as a subdirectory. We can do that in Git with `git read-tree`. -You'll learn more about `read-tree` and its friends in <>, but for now know that it reads the root tree of one branch into your current staging area and working directory. +You'll learn more about `read-tree` and its friends in <>, but for now know that it reads the root tree of one branch into your current staging area and working directory. We just switched back to your `master` branch, and we pull the `rack_branch` branch into the `rack` subdirectory of our `master` branch of our main project: [source,console] @@ -82,7 +82,7 @@ Automatic merge went well; stopped before committing as requested All the changes from the Rack project are merged in and ready to be committed locally. You can also do the opposite -- make changes in the `rack` subdirectory of your `master` branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. -This gives us a way to have a workflow somewhat similar to the submodule workflow without using submodules (which we will cover in <>). +This gives us a way to have a workflow somewhat similar to the submodule workflow without using submodules (which we will cover in <<_git_submodules,Ch 5, Git Toolkit>>). We can keep branches with other related projects in our repository and subtree merge them into our project occasionally. It is nice in some ways, for example all the code is committed to a single place. However, it has other drawbacks in that it's a bit more complex and easier to make mistakes in reintegrating changes or accidentally pushing a branch into an unrelated repository. diff --git a/book/ch08-customizing-git.asc b/book/ch06-customizing-git.asc similarity index 83% rename from book/ch08-customizing-git.asc rename to book/ch06-customizing-git.asc index c747fa9..fe055db 100644 --- a/book/ch08-customizing-git.asc +++ b/book/ch06-customizing-git.asc @@ -1,17 +1,19 @@ -[[ch08-customizing-git]] +[[ch06-customizing-git]] == Customizing Git So far, we've covered the basics of how Git works and how to use it, and we've introduced a number of tools that Git provides to help you use it easily and efficiently. In this chapter, we'll see how you can make Git operate in a more customized fashion, by introducing several important configuration settings and the hooks system. With these tools, it's easy to get Git to work exactly the way you, your company, or your group needs it to. -include::ch08/config.asc[] +include::ch06/config.asc[] -include::ch08/attributes.asc[] +include::ch06/ignoring.asc[] -include::ch08/hooks.asc[] +include::ch06/attributes.asc[] -include::ch08/policy.asc[] +include::ch06/hooks.asc[] + +include::ch06/policy.asc[] === Summary diff --git a/book/ch06-github.asc b/book/ch06-github.asc deleted file mode 100644 index 92205b7..0000000 --- a/book/ch06-github.asc +++ /dev/null @@ -1,35 +0,0 @@ -[[ch06-github]] -== GitHub - -(((GitHub))) -GitHub is the single largest host for Git repositories, and is the central point of collaboration for millions of developers and projects. -A large percentage of all Git repositories are hosted on GitHub, and many open-source projects use it for Git hosting, issue tracking, code review, and other things. -So while it's not a direct part of the Git open source project, there's a good chance that you'll want or need to interact with GitHub at some point while using Git professionally. - -This chapter is about using GitHub effectively. -We'll cover signing up for and managing an account, creating and using Git repositories, common workflows to contribute to projects and to accept contributions to yours, GitHub's programmatic interface and lots of little tips to make your life easier in general. - -If you are not interested in using GitHub to host your own projects or to collaborate with other projects that are hosted on GitHub, you can safely skip to <>. - -[WARNING] -.Interfaces Change -==== -It's important to note that like many active websites, the UI elements in these screenshots are bound to change over time. -Hopefully the general idea of what we're trying to accomplish here will still be there, but if you want more up to date versions of these screens, the online versions of this book may have newer screenshots. -==== - -include::ch06/1-setting-up-account.asc[] - -include::ch06/2-contributing.asc[] - -include::ch06/3-maintaining.asc[] - -include::ch06/4-managing-organization.asc[] - -include::ch06/5-scripting.asc[] - -=== Summary - -Now you're a GitHub user. -You know how to create an account, manage an organization, create and push to repositories, contribute to other people's projects and accept contributions from others. -In the next chapter, you'll learn more powerful tools and tips for dealing with complex situations, which will truly make you a Git master. diff --git a/book/ch06/1-setting-up-account.asc b/book/ch06/1-setting-up-account.asc deleted file mode 100644 index 2ab023e..0000000 --- a/book/ch06/1-setting-up-account.asc +++ /dev/null @@ -1,97 +0,0 @@ -=== Account Setup and Configuration - -(((GitHub, user accounts))) -The first thing you need to do is set up a free user account. -Simply visit https://github.com[^], choose a user name that isn't already taken, provide an email address and a password, and click the big green "`Sign up for GitHub`" button. - -.The GitHub sign-up form -image::images/signup.png[The GitHub sign-up form] - -The next thing you'll see is the pricing page for upgraded plans, but it's safe to ignore this for now. -GitHub will send you an email to verify the address you provided. -Go ahead and do this; it's pretty important (as we'll see later). - -[NOTE] -==== -GitHub provides almost all of its functionality with free accounts, except some advanced features. - -GitHub's paid plans include advanced tools and features as well as increased limits for free services, but we won't be covering those in this book. -To get more information about available plans and their comparison, visit https://github.com/pricing[^]. -==== - -Clicking the Octocat logo at the top-left of the screen will take you to your dashboard page. -You're now ready to use GitHub. - -==== SSH Access - -(((SSH keys, with GitHub))) -As of right now, you're fully able to connect with Git repositories using the `https://` protocol, authenticating with the username and password you just set up. -However, to simply clone public projects, you don't even need to sign up - the account we just created comes into play when we fork projects and push to our forks a bit later. - -If you'd like to use SSH remotes, you'll need to configure a public key. -If you don't already have one, see <>. -Open up your account settings using the link at the top-right of the window: - -.The "`Account settings`" link -image::images/account-settings.png[The “Account settings” link] - -Then select the "`SSH keys`" section along the left-hand side. - -.The "`SSH keys`" link -image::images/ssh-keys.png[The “SSH keys” link] - -From there, click the "`Add an SSH key`" button, give your key a name, paste the contents of your `~/.ssh/id_rsa.pub` (or whatever you named it) public-key file into the text area, and click "`Add key`". - -[NOTE] -==== -Be sure to name your SSH key something you can remember. -You can name each of your keys (e.g. "My Laptop" or "Work Account") so that if you need to revoke a key later, you can easily tell which one you're looking for. -==== - -[[_personal_avatar]] -==== Your Avatar - -Next, if you wish, you can replace the avatar that is generated for you with an image of your choosing. -First go to the "`Profile`" tab (above the SSH Keys tab) and click "`Upload new picture`". - -.The "`Profile`" link -image::images/your-profile.png[The “Profile” link] - -We'll choose a copy of the Git logo that is on our hard drive and then we get a chance to crop it. - -.Crop your uploaded avatar -image::images/avatar-crop.png[Crop your uploaded avatar] - -Now anywhere you interact on the site, people will see your avatar next to your username. - -If you happen to have uploaded an avatar to the popular Gravatar service (often used for WordPress accounts), that avatar will be used by default and you don't need to do this step. - -==== Your Email Addresses - -The way that GitHub maps your Git commits to your user is by email address. -If you use multiple email addresses in your commits and you want GitHub to link them up properly, you need to add all the email addresses you have used to the Emails section of the admin section. - -[[_add_email_addresses]] -.Add all your email addresses -image::images/email-settings.png[Add all your email addresses] - -In <<_add_email_addresses>> we can see some of the different states that are possible. -The top address is verified and set as the primary address, meaning that is where you'll get any notifications and receipts. -The second address is verified and so can be set as the primary if you wish to switch them. -The final address is unverified, meaning that you can't make it your primary address. -If GitHub sees any of these in commit messages in any repository on the site, it will be linked to your user now. - -==== Two Factor Authentication - -Finally, for extra security, you should definitely set up Two-factor Authentication or "`2FA`". -Two-factor Authentication is an authentication mechanism that is becoming more and more popular recently to mitigate the risk of your account being compromised if your password is stolen somehow. -Turning it on will make GitHub ask you for two different methods of authentication, so that if one of them is compromised, an attacker will not be able to access your account. - -You can find the Two-factor Authentication setup under the Security tab of your Account settings. - -.2FA in the Security Tab -image::images/2fa-1.png[2FA in the Security Tab] - -If you click on the "`Set up two-factor authentication`" button, it will take you to a configuration page where you can choose to use a phone app to generate your secondary code (a "`time based one-time password`"), or you can have GitHub send you a code via SMS each time you need to log in. - -After you choose which method you prefer and follow the instructions for setting up 2FA, your account will then be a little more secure and you will have to provide a code in addition to your password whenever you log into GitHub. diff --git a/book/ch06/2-contributing.asc b/book/ch06/2-contributing.asc deleted file mode 100644 index 1dffbd9..0000000 --- a/book/ch06/2-contributing.asc +++ /dev/null @@ -1,548 +0,0 @@ -=== Contributing to a Project - -Now that our account is set up, let's walk through some details that could be useful in helping you contribute to an existing project. - -==== Forking Projects - -(((forking))) -If you want to contribute to an existing project to which you don't have push access, you can "`fork`" the project. -When you "`fork`" a project, GitHub will make a copy of the project that is entirely yours; it lives in your namespace, and you can push to it. - -[NOTE] -==== -Historically, the term "`fork`" has been somewhat negative in context, meaning that someone took an open source project in a different direction, sometimes creating a competing project and splitting the contributors. -In GitHub, a "`fork`" is simply the same project in your own namespace, allowing you to make changes to a project publicly as a way to contribute in a more open manner. -==== - -This way, projects don't have to worry about adding users as collaborators to give them push access. -People can fork a project, push to it, and contribute their changes back to the original repository by creating what's called a Pull Request, which we'll cover next. -This opens up a discussion thread with code review, and the owner and the contributor can then communicate about the change until the owner is happy with it, at which point the owner can merge it in. - -To fork a project, visit the project page and click the "`Fork`" button at the top-right of the page. - -.The "`Fork`" button -image::images/forkbutton.png[The “Fork” button] - -After a few seconds, you'll be taken to your new project page, with your own writeable copy of the code. - -[[ch06-github_flow]] -==== The GitHub Flow - -(((GitHub, Flow))) -GitHub is designed around a particular collaboration workflow, centered on Pull Requests. -This flow works whether you're collaborating with a tightly-knit team in a single shared repository, or a globally-distributed company or network of strangers contributing to a project through dozens of forks. -It is centered on the <> workflow covered in <>. - -Here's how it generally works: - -1. Fork the project. -2. Create a topic branch from `master`. -3. Make some commits to improve the project. -4. Push this branch to your GitHub project. -5. Open a Pull Request on GitHub. -6. Discuss, and optionally continue committing. -7. The project owner merges or closes the Pull Request. -8. Sync the updated `master` back to your fork. - -This is basically the Integration Manager workflow covered in <>, but instead of using email to communicate and review changes, teams use GitHub's web based tools. - -Let's walk through an example of proposing a change to an open source project hosted on GitHub using this flow. - -[TIP] -==== -You can use the official *GitHub CLI* tool instead of the GitHub web interface for most things. -The tool can be used on Windows, macOS, and Linux systems. -Go to the https://cli.github.com/[GitHub CLI homepage^] for installation instructions and the manual. -==== - -===== Creating a Pull Request - -Tony is looking for code to run on his Arduino programmable microcontroller and has found a great program file on GitHub at https://github.com/schacon/blink[^]. - -.The project we want to contribute to -image::images/blink-01-start.png[The project we want to contribute to] - -The only problem is that the blinking rate is too fast. -We think it's much nicer to wait 3 seconds instead of 1 in between each state change. -So let's improve the program and submit it back to the project as a proposed change. - -First, we click the 'Fork' button as mentioned earlier to get our own copy of the project. -Our user name here is "`tonychacon`" so our copy of this project is at `https://github.com/tonychacon/blink` and that's where we can edit it. -We will clone it locally, create a topic branch, make the code change and finally push that change back up to GitHub. - -[source,console] ----- -$ git clone https://github.com/tonychacon/blink <1> -Cloning into 'blink'... - -$ cd blink -$ git checkout -b slow-blink <2> -Switched to a new branch 'slow-blink' - -$ sed -i '' 's/1000/3000/' blink.ino (macOS) <3> -# If you're on a Linux system, do this instead: -# $ sed -i 's/1000/3000/' blink.ino <3> - -$ git diff --word-diff <4> -diff --git a/blink.ino b/blink.ino -index 15b9911..a6cc5a5 100644 ---- a/blink.ino -+++ b/blink.ino -@@ -18,7 +18,7 @@ void setup() { -// the loop routine runs over and over again forever: -void loop() { - digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) - [-delay(1000);-]{+delay(3000);+} // wait for a second - digitalWrite(led, LOW); // turn the LED off by making the voltage LOW - [-delay(1000);-]{+delay(3000);+} // wait for a second -} - -$ git commit -a -m 'Change delay to 3 seconds' <5> -[slow-blink 5ca509d] Change delay to 3 seconds - 1 file changed, 2 insertions(+), 2 deletions(-) - -$ git push origin slow-blink <6> -Username for 'https://github.com': tonychacon -Password for 'https://tonychacon@github.com': -Counting objects: 5, done. -Delta compression using up to 8 threads. -Compressing objects: 100% (3/3), done. -Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done. -Total 3 (delta 1), reused 0 (delta 0) -To https://github.com/tonychacon/blink - * [new branch] slow-blink -> slow-blink ----- - -<1> Clone our fork of the project locally. -<2> Create a descriptive topic branch. -<3> Make our change to the code. -<4> Check that the change is good. -<5> Commit our change to the topic branch. -<6> Push our new topic branch back up to our GitHub fork. - -Now if we go back to our fork on GitHub, we can see that GitHub noticed that we pushed a new topic branch up and presents us with a big green button to check out our changes and open a Pull Request to the original project. - -You can alternatively go to the "`Branches`" page at `\https://github.com///branches` to locate your branch and open a new Pull Request from there. - -.Pull Request button -image::images/blink-02-pr.png[Pull Request button] - -(((GitHub, pull requests))) -If we click that green button, we'll see a screen that asks us to give our Pull Request a title and description. -It is almost always worthwhile to put some effort into this, since a good description helps the owner of the original project determine what you were trying to do, whether your proposed changes are correct, and whether accepting the changes would improve the original project. - -We also see a list of the commits in our topic branch that are "`ahead`" of the `master` branch (in this case, just the one) and a unified diff of all the changes that will be made should this branch get merged by the project owner. - -.Pull Request creation page -image::images/blink-03-pull-request-open.png[Pull Request creation page] - -When you hit the 'Create pull request' button on this screen, the owner of the project you forked will get a notification that someone is suggesting a change and will link to a page that has all of this information on it. - -[NOTE] -==== -Though Pull Requests are used commonly for public projects like this when the contributor has a complete change ready to be made, it's also often used in internal projects _at the beginning_ of the development cycle. -Since you can keep pushing to the topic branch even *after* the Pull Request is opened, it's often opened early and used as a way to iterate on work as a team within a context, rather than opened at the very end of the process. -==== - -===== Iterating on a Pull Request - -At this point, the project owner can look at the suggested change and merge it, reject it or comment on it. -Let's say that he likes the idea, but would prefer a slightly longer time for the light to be off than on. - -Where this conversation may take place over email in the workflows presented in <>, on GitHub this happens online. -The project owner can review the unified diff and leave a comment by clicking on any of the lines. - -.Comment on a specific line of code in a Pull Request -image::images/blink-04-pr-comment.png[Comment on a specific line of code in a Pull Request] - -Once the maintainer makes this comment, the person who opened the Pull Request (and indeed, anyone else watching the repository) will get a notification. -We'll go over customizing this later, but if he had email notifications turned on, Tony would get an email like this: - -[[_email_notification]] -.Comments sent as email notifications -image::images/blink-04-email.png[Comments sent as email notifications] - -Anyone can also leave general comments on the Pull Request. -In <<_pr_discussion>> we can see an example of the project owner both commenting on a line of code and then leaving a general comment in the discussion section. -You can see that the code comments are brought into the conversation as well. - -[[_pr_discussion]] -.Pull Request discussion page -image::images/blink-05-general-comment.png[Pull Request discussion page] - -Now the contributor can see what they need to do in order to get their change accepted. -Luckily this is very straightforward. -Where over email you may have to re-roll your series and resubmit it to the mailing list, with GitHub you simply commit to the topic branch again and push, which will automatically update the Pull Request. -In <<_pr_final>> you can also see that the old code comment has been collapsed in the updated Pull Request, since it was made on a line that has since been changed. - -Adding commits to an existing Pull Request doesn't trigger a notification, so once Tony has pushed his corrections he decides to leave a comment to inform the project owner that he made the requested change. - -[[_pr_final]] -.Pull Request final -image::images/blink-06-final.png[Pull Request final] - -An interesting thing to notice is that if you click on the "`Files Changed`" tab on this Pull Request, you'll get the "`unified`" diff -- that is, the total aggregate difference that would be introduced to your main branch if this topic branch was merged in. -In `git diff` terms, it basically automatically shows you `git diff master...` for the branch this Pull Request is based on. -See <> for more about this type of diff. - -The other thing you'll notice is that GitHub checks to see if the Pull Request merges cleanly and provides a button to do the merge for you on the server. -This button only shows up if you have write access to the repository and a trivial merge is possible. -If you click it GitHub will perform a "`non-fast-forward`" merge, meaning that even if the merge *could* be a fast-forward, it will still create a merge commit. - -If you would prefer, you can simply pull the branch down and merge it locally. -If you merge this branch into the `master` branch and push it to GitHub, the Pull Request will automatically be closed. - -This is the basic workflow that most GitHub projects use. -Topic branches are created, Pull Requests are opened on them, a discussion ensues, possibly more work is done on the branch and eventually the request is either closed or merged. - -[NOTE] -.Not Only Forks -==== -It's important to note that you can also open a Pull Request between two branches in the same repository. -If you're working on a feature with someone and you both have write access to the project, you can push a topic branch to the repository and open a Pull Request on it to the `master` branch of that same project to initiate the code review and discussion process. -No forking necessary. -==== - -==== Advanced Pull Requests - -Now that we've covered the basics of contributing to a project on GitHub, let's cover a few interesting tips and tricks about Pull Requests so you can be more effective in using them. - -===== Pull Requests as Patches - -It's important to understand that many projects don't really think of Pull Requests as queues of perfect patches that should apply cleanly in order, as most mailing list-based projects think of patch series contributions. -Most GitHub projects think about Pull Request branches as iterative conversations around a proposed change, culminating in a unified diff that is applied by merging. - -This is an important distinction, because generally the change is suggested before the code is thought to be perfect, which is far more rare with mailing list based patch series contributions. -This enables an earlier conversation with the maintainers so that arriving at the proper solution is more of a community effort. -When code is proposed with a Pull Request and the maintainers or community suggest a change, the patch series is generally not re-rolled, but instead the difference is pushed as a new commit to the branch, moving the conversation forward with the context of the previous work intact. - -For instance, if you go back and look again at <<_pr_final>>, you'll notice that the contributor did not rebase his commit and send another Pull Request. -Instead they added new commits and pushed them to the existing branch. -This way if you go back and look at this Pull Request in the future, you can easily find all of the context of why decisions were made. -Pushing the "`Merge`" button on the site purposefully creates a merge commit that references the Pull Request so that it's easy to go back and research the original conversation if necessary. - -===== Keeping up with Upstream - -If your Pull Request becomes out of date or otherwise doesn't merge cleanly, you will want to fix it so the maintainer can easily merge it. -GitHub will test this for you and let you know at the bottom of every Pull Request if the merge is trivial or not. - -[[_pr_fail]] -.Pull Request does not merge cleanly -image::images/pr-01-fail.png[Pull Request does not merge cleanly] - -If you see something like <<_pr_fail>>, you'll want to fix your branch so that it turns green and the maintainer doesn't have to do extra work. - -You have two main options in order to do this. -You can either rebase your branch on top of whatever the target branch is (normally the `master` branch of the repository you forked), or you can merge the target branch into your branch. - -Most developers on GitHub will choose to do the latter, for the same reasons we just went over in the previous section. -What matters is the history and the final merge, so rebasing isn't getting you much other than a slightly cleaner history and in return is *far* more difficult and error prone. - -If you want to merge in the target branch to make your Pull Request mergeable, you would add the original repository as a new remote, fetch from it, merge the main branch of that repository into your topic branch, fix any issues and finally push it back up to the same branch you opened the Pull Request on. - -For example, let's say that in the "`tonychacon`" example we were using before, the original author made a change that would create a conflict in the Pull Request. -Let's go through those steps. - -[source,console] ----- -$ git remote add upstream https://github.com/schacon/blink <1> - -$ git fetch upstream <2> -remote: Counting objects: 3, done. -remote: Compressing objects: 100% (3/3), done. -Unpacking objects: 100% (3/3), done. -remote: Total 3 (delta 0), reused 0 (delta 0) -From https://github.com/schacon/blink - * [new branch] master -> upstream/master - -$ git merge upstream/master <3> -Auto-merging blink.ino -CONFLICT (content): Merge conflict in blink.ino -Automatic merge failed; fix conflicts and then commit the result. - -$ vim blink.ino <4> -$ git add blink.ino -$ git commit -[slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \ - into slower-blink - -$ git push origin slow-blink <5> -Counting objects: 6, done. -Delta compression using up to 8 threads. -Compressing objects: 100% (6/6), done. -Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done. -Total 6 (delta 2), reused 0 (delta 0) -To https://github.com/tonychacon/blink - ef4725c..3c8d735 slower-blink -> slow-blink ----- - -<1> Add the original repository as a remote named `upstream`. -<2> Fetch the newest work from that remote. -<3> Merge the main branch of that repository into your topic branch. -<4> Fix the conflict that occurred. -<5> Push back up to the same topic branch. - -Once you do that, the Pull Request will be automatically updated and re-checked to see if it merges cleanly. - -[[_pr_merge_fix]] -.Pull Request now merges cleanly -image::images/pr-02-merge-fix.png[Pull Request now merges cleanly] - -One of the great things about Git is that you can do that continuously. -If you have a very long-running project, you can easily merge from the target branch over and over again and only have to deal with conflicts that have arisen since the last time that you merged, making the process very manageable. - -If you absolutely wish to rebase the branch to clean it up, you can certainly do so, but it is highly encouraged to not force push over the branch that the Pull Request is already opened on. -If other people have pulled it down and done more work on it, you run into all of the issues outlined in <>. -Instead, push the rebased branch to a new branch on GitHub and open a brand new Pull Request referencing the old one, then close the original. - -===== References - -Your next question may be "`How do I reference the old Pull Request?`". -It turns out there are many, many ways to reference other things almost anywhere you can write in GitHub. - -Let's start with how to cross-reference another Pull Request or an Issue. -All Pull Requests and Issues are assigned numbers and they are unique within the project. -For example, you can't have Pull Request +#3+ _and_ Issue +#3+. -If you want to reference any Pull Request or Issue from any other one, you can simply put `+#+` in any comment or description. -You can also be more specific if the Issue or Pull request lives somewhere else; write `username#` if you're referring to an Issue or Pull Request in a fork of the repository you're in, or `username/repo#` to reference something in another repository. - -Let's look at an example. -Say we rebased the branch in the previous example, created a new pull request for it, and now we want to reference the old pull request from the new one. -We also want to reference an issue in the fork of the repository and an issue in a completely different project. -We can fill out the description just like <<_pr_references>>. - -[[_pr_references]] -.Cross references in a Pull Request -image::images/mentions-01-syntax.png[Cross references in a Pull Request] - -When we submit this pull request, we'll see all of that rendered like <<_pr_references_render>>. - -[[_pr_references_render]] -.Cross references rendered in a Pull Request -image::images/mentions-02-render.png[Cross references rendered in a Pull Request] - -Notice that the full GitHub URL we put in there was shortened to just the information needed. - -Now if Tony goes back and closes out the original Pull Request, we can see that by mentioning it in the new one, GitHub has automatically created a trackback event in the Pull Request timeline. -This means that anyone who visits this Pull Request and sees that it is closed can easily link back to the one that superseded it. -The link will look something like <<_pr_closed>>. - -[[_pr_closed]] -.Link back to the new Pull Request in the closed Pull Request timeline -image::images/mentions-03-closed.png[Link back to the new Pull Request in the closed Pull Request timeline] - -In addition to issue numbers, you can also reference a specific commit by SHA-1. -You have to specify a full 40 character SHA-1, but if GitHub sees that in a comment, it will link directly to the commit. -Again, you can reference commits in forks or other repositories in the same way you did with issues. - -==== GitHub Flavored Markdown - -Linking to other Issues is just the beginning of interesting things you can do with almost any text box on GitHub. -In Issue and Pull Request descriptions, comments, code comments and more, you can use what is called "`GitHub Flavored Markdown`". -Markdown is like writing in plain text but which is rendered richly. - -See <<_example_markdown>> for an example of how comments or text can be written and then rendered using Markdown. - -[[_example_markdown]] -.An example of GitHub Flavored Markdown as written and as rendered -image::images/markdown-01-example.png[An example of GitHub Flavored Markdown as written and as rendered] - -The GitHub flavor of Markdown adds more things you can do beyond the basic Markdown syntax. -These can all be really useful when creating useful Pull Request or Issue comments or descriptions. - -===== Task Lists - -The first really useful GitHub specific Markdown feature, especially for use in Pull Requests, is the Task List. -A task list is a list of checkboxes of things you want to get done. -Putting them into an Issue or Pull Request normally indicates things that you want to get done before you consider the item complete. - -You can create a task list like this: - -[source,text] ----- -- [X] Write the code -- [ ] Write all the tests -- [ ] Document the code ----- - -If we include this in the description of our Pull Request or Issue, we'll see it rendered like <<_eg_task_lists>>. - -[[_eg_task_lists]] -.Task lists rendered in a Markdown comment -image::images/markdown-02-tasks.png[Task lists rendered in a Markdown comment] - -This is often used in Pull Requests to indicate what all you would like to get done on the branch before the Pull Request will be ready to merge. -The really cool part is that you can simply click the checkboxes to update the comment -- you don't have to edit the Markdown directly to check tasks off. - -What's more, GitHub will look for task lists in your Issues and Pull Requests and show them as metadata on the pages that list them out. -For example, if you have a Pull Request with tasks and you look at the overview page of all Pull Requests, you can see how far done it is. -This helps people break down Pull Requests into subtasks and helps other people track the progress of the branch. -You can see an example of this in <<_task_list_progress>>. - -[[_task_list_progress]] -.Task list summary in the Pull Request list -image::images/markdown-03-task-summary.png[Task list summary in the Pull Request list] - -These are incredibly useful when you open a Pull Request early and use it to track your progress through the implementation of the feature. - -===== Code Snippets - -You can also add code snippets to comments. -This is especially useful if you want to present something that you _could_ try to do before actually implementing it as a commit on your branch. -This is also often used to add example code of what is not working or what this Pull Request could implement. - -To add a snippet of code you have to "`fence`" it in backticks. - -[source,text] ----- -```java -for(int i=0 ; i < 5 ; i++) -{ - System.out.println("i is : " + i); -} -``` ----- - -If you add a language name like we did there with 'java', GitHub will also try to syntax highlight the snippet. -In the case of the above example, it would end up rendering like <<_md_code>>. - -[[_md_code]] -.Rendered fenced code example -image::images/markdown-04-fenced-code.png[Rendered fenced code example] - -===== Quoting - -If you're responding to a small part of a long comment, you can selectively quote out of the other comment by preceding the lines with the `>` character. -In fact, this is so common and so useful that there is a keyboard shortcut for it. -If you highlight text in a comment that you want to directly reply to and hit the `r` key, it will quote that text in the comment box for you. - -The quotes look something like this: - -[source,text] ----- -> Whether 'tis Nobler in the mind to suffer -> The Slings and Arrows of outrageous Fortune, - -How big are these slings and in particular, these arrows? ----- - -Once rendered, the comment will look like <<_md_quote>>. - -[[_md_quote]] -.Rendered quoting example -image::images/markdown-05-quote.png[Rendered quoting example] - -===== Emoji - -Finally, you can also use emoji in your comments. -This is actually used quite extensively in comments you see on many GitHub Issues and Pull Requests. -There is even an emoji helper in GitHub. -If you are typing a comment and you start with a `:` character, an autocompleter will help you find what you're looking for. - -[[_md_emoji_auto]] -.Emoji autocompleter in action -image::images/markdown-06-emoji-complete.png[Emoji autocompleter in action] - -Emojis take the form of `::` anywhere in the comment. -For instance, you could write something like this: - -[source,text] ----- -I :eyes: that :bug: and I :cold_sweat:. - -:trophy: for :microscope: it. - -:+1: and :sparkles: on this :ship:, it's :fire::poop:! - -:clap::tada::panda_face: ----- - -When rendered, it would look something like <<_md_emoji>>. - -[[_md_emoji]] -.Heavy emoji commenting -image::images/markdown-07-emoji.png[Heavy emoji commenting] - -Not that this is incredibly useful, but it does add an element of fun and emotion to a medium that is otherwise hard to convey emotion in. - -[NOTE] -==== -There are actually quite a number of web services that make use of emoji characters these days. -A great cheat sheet to reference to find emoji that expresses what you want to say can be found at: - -https://www.webfx.com/tools/emoji-cheat-sheet/[^] -==== - -===== Images - -This isn't technically GitHub Flavored Markdown, but it is incredibly useful. -In addition to adding Markdown image links to comments, which can be difficult to find and embed URLs for, GitHub allows you to drag and drop images into text areas to embed them. - -[[_md_drag]] -.Drag and drop images to upload them and auto-embed them -image::images/markdown-08-drag-drop.png[Drag and drop images to upload them and auto-embed them] - -If you look at <<_md_drag>>, you can see a small "`Parsed as Markdown`" hint above the text area. -Clicking on that will give you a full cheat sheet of everything you can do with Markdown on GitHub. - -[[_fetch_and_push_on_different_repositories]] -==== Keep your GitHub public repository up-to-date - -Once you've forked a GitHub repository, your repository (your "fork") exists independently from the original. -In particular, when the original repository has new commits, GitHub informs you by a message like: - -[source,text] ----- -This branch is 5 commits behind progit:master. ----- - -But your GitHub repository will never be automatically updated by GitHub; this is something that you must do yourself. -Fortunately, this is very easy to do. - -One possibility to do this requires no configuration. -For example, if you forked from `https://github.com/progit/progit2.git`, you can keep your `master` branch up-to-date like this: - -[source,console] ----- -$ git checkout master <1> -$ git pull https://github.com/progit/progit2.git <2> -$ git push origin master <3> ----- - -<1> If you were on another branch, return to `master`. -<2> Fetch changes from `https://github.com/progit/progit2.git` and merge them into `master`. -<3> Push your `master` branch to `origin`. - -This works, but it is a little tedious having to spell out the fetch URL every time. -You can automate this work with a bit of configuration: - -[source,console] ----- -$ git remote add progit https://github.com/progit/progit2.git <1> -$ git fetch progit <2> -$ git branch --set-upstream-to=progit/master master <3> -$ git config --local remote.pushDefault origin <4> ----- - -<1> Add the source repository and give it a name. - Here, I have chosen to call it `progit`. -<2> Get a reference on progit's branches, in particular `master`. -<3> Set your `master` branch to fetch from the `progit` remote. -<4> Define the default push repository to `origin`. - -Once this is done, the workflow becomes much simpler: - -[source,console] ----- -$ git checkout master <1> -$ git pull <2> -$ git push <3> ----- - -<1> If you were on another branch, return to `master`. -<2> Fetch changes from `progit` and merge changes into `master`. -<3> Push your `master` branch to `origin`. - -This approach can be useful, but it's not without downsides. -Git will happily do this work for you silently, but it won't warn you if you make a commit to `master`, pull from `progit`, then push to `origin` -- all of those operations are valid with this setup. -So you'll have to take care never to commit directly to `master`, since that branch effectively belongs to the upstream repository. diff --git a/book/ch06/3-maintaining.asc b/book/ch06/3-maintaining.asc deleted file mode 100644 index 2505e86..0000000 --- a/book/ch06/3-maintaining.asc +++ /dev/null @@ -1,377 +0,0 @@ -[[_maintaining_gh_project]] -=== Maintaining a Project - -Now that we're comfortable contributing to a project, let's look at the other side: creating, maintaining and administering your own project. - -==== Creating a New Repository - -Let's create a new repository to share our project code with. -Start by clicking the "`New repository`" button on the right-hand side of the dashboard, or from the `+` button in the top toolbar next to your username as seen in <<_new_repo_dropdown>>. - -.The "`Your repositories`" area -image::images/newrepo.png[The “Your repositories” area] - -[[_new_repo_dropdown]] -.The "`New repository`" dropdown -image::images/new-repo.png[The “New repository” dropdown] - -This takes you to the "`new repository`" form: - -.The "`new repository`" form -image::images/newrepoform.png[The “new repository” form] - -All you really have to do here is provide a project name; the rest of the fields are completely optional. -For now, just click the "`Create Repository`" button, and boom -- you have a new repository on GitHub, named `/`. - -Since you have no code there yet, GitHub will show you instructions for how to create a brand-new Git repository, or connect an existing Git project. -We won't belabor this here; if you need a refresher, check out <>. - -Now that your project is hosted on GitHub, you can give the URL to anyone you want to share your project with. -Every project on GitHub is accessible over HTTPS as `\https://github.com//`, and over SSH as `\git@github.com:/`. -Git can fetch from and push to both of these URLs, but they are access-controlled based on the credentials of the user connecting to them. - -[NOTE] -==== -It is often preferable to share the HTTPS based URL for a public project, since the user does not have to have a GitHub account to access it for cloning. -Users will have to have an account and an uploaded SSH key to access your project if you give them the SSH URL. -The HTTPS one is also exactly the same URL they would paste into a browser to view the project there. -==== - -==== Adding Collaborators - -If you're working with other people who you want to give commit access to, you need to add them as "`collaborators`". -If Ben, Jeff, and Louise all sign up for accounts on GitHub, and you want to give them push access to your repository, you can add them to your project. -Doing so will give them "`push`" access, which means they have both read and write access to the project and Git repository. - -Click the "`Settings`" link at the bottom of the right-hand sidebar. - -.The repository settings link -image::images/reposettingslink.png[The repository settings link] - -Then select "`Collaborators`" from the menu on the left-hand side. -Then, just type a username into the box, and click "`Add collaborator.`" -You can repeat this as many times as you like to grant access to everyone you like. -If you need to revoke access, just click the "`X`" on the right-hand side of their row. - -.The repository collaborators box -image::images/collaborators.png[The repository collaborators box] - -==== Managing Pull Requests - -Now that you have a project with some code in it and maybe even a few collaborators who also have push access, let's go over what to do when you get a Pull Request yourself. - -Pull Requests can either come from a branch in a fork of your repository or they can come from another branch in the same repository. -The only difference is that the ones in a fork are often from people where you can't push to their branch and they can't push to yours, whereas with internal Pull Requests generally both parties can access the branch. - -For these examples, let's assume you are "`tonychacon`" and you've created a new Arduino code project named "`fade`". - -[[_email_notifications]] -===== Email Notifications - -Someone comes along and makes a change to your code and sends you a Pull Request. -You should get an email notifying you about the new Pull Request and it should look something like <<_email_pr>>. - -[[_email_pr]] -.Email notification of a new Pull Request -image::images/maint-01-email.png[Email notification of a new Pull Request] - -There are a few things to notice about this email. -It will give you a small diffstat -- a list of files that have changed in the Pull Request and by how much. -It gives you a link to the Pull Request on GitHub. -It also gives you a few URLs that you can use from the command line. - -If you notice the line that says `git pull patch-1`, this is a simple way to merge in a remote branch without having to add a remote. -We went over this quickly in <>. -If you wish, you can create and switch to a topic branch and then run this command to merge in the Pull Request changes. - -The other interesting URLs are the `.diff` and `.patch` URLs, which as you may guess, provide unified diff and patch versions of the Pull Request. -You could technically merge in the Pull Request work with something like this: - -[source,console] ----- -$ curl https://github.com/tonychacon/fade/pull/1.patch | git am ----- - -===== Collaborating on the Pull Request - -As we covered in <>, you can now have a conversation with the person who opened the Pull Request. -You can comment on specific lines of code, comment on whole commits or comment on the entire Pull Request itself, using GitHub Flavored Markdown everywhere. - -Every time someone else comments on the Pull Request you will continue to get email notifications so you know there is activity happening. -They will each have a link to the Pull Request where the activity is happening and you can also directly respond to the email to comment on the Pull Request thread. - -.Responses to emails are included in the thread -image::images/maint-03-email-resp.png[Responses to emails are included in the thread] - -Once the code is in a place you like and want to merge it in, you can either pull the code down and merge it locally, either with the `git pull ` syntax we saw earlier, or by adding the fork as a remote and fetching and merging. - -If the merge is trivial, you can also just hit the "`Merge`" button on the GitHub site. -This will do a "`non-fast-forward`" merge, creating a merge commit even if a fast-forward merge was possible. -This means that no matter what, every time you hit the merge button, a merge commit is created. -As you can see in <<_merge_button>>, GitHub gives you all of this information if you click the hint link. - -[[_merge_button]] -.Merge button and instructions for merging a Pull Request manually -image::images/maint-02-merge.png[Merge button and instructions for merging a Pull Request manually] - -If you decide you don't want to merge it, you can also just close the Pull Request and the person who opened it will be notified. - -[[_pr_refs]] -===== Pull Request Refs - -If you're dealing with a *lot* of Pull Requests and don't want to add a bunch of remotes or do one time pulls every time, there is a neat trick that GitHub allows you to do. -This is a bit of an advanced trick and we'll go over the details of this a bit more in <>, but it can be pretty useful. - -GitHub actually advertises the Pull Request branches for a repository as sort of pseudo-branches on the server. -By default you don't get them when you clone, but they are there in an obscured way and you can access them pretty easily. - -To demonstrate this, we're going to use a low-level command (often referred to as a "`plumbing`" command, which we'll read about more in <>) called `ls-remote`. -This command is generally not used in day-to-day Git operations but it's useful to show us what references are present on the server. - -If we run this command against the "`blink`" repository we were using earlier, we will get a list of all the branches and tags and other references in the repository. - -[source,console] ----- -$ git ls-remote https://github.com/schacon/blink -10d539600d86723087810ec636870a504f4fee4d HEAD -10d539600d86723087810ec636870a504f4fee4d refs/heads/master -6a83107c62950be9453aac297bb0193fd743cd6e refs/pull/1/head -afe83c2d1a70674c9505cc1d8b7d380d5e076ed3 refs/pull/1/merge -3c8d735ee16296c242be7a9742ebfbc2665adec1 refs/pull/2/head -15c9f4f80973a2758462ab2066b6ad9fe8dcf03d refs/pull/2/merge -a5a7751a33b7e86c5e9bb07b26001bb17d775d1a refs/pull/4/head -31a45fc257e8433c8d8804e3e848cf61c9d3166c refs/pull/4/merge ----- - -Of course, if you're in your repository and you run `git ls-remote origin` or whatever remote you want to check, it will show you something similar to this. - -If the repository is on GitHub and you have any Pull Requests that have been opened, you'll get these references that are prefixed with `refs/pull/`. -These are basically branches, but since they're not under `refs/heads/` you don't get them normally when you clone or fetch from the server -- the process of fetching ignores them normally. - -There are two references per Pull Request - the one that ends in `/head` points to exactly the same commit as the last commit in the Pull Request branch. -So if someone opens a Pull Request in our repository and their branch is named `bug-fix` and it points to commit `a5a775`, then in *our* repository we will not have a `bug-fix` branch (since that's in their fork), but we _will_ have `pull//head` that points to `a5a775`. -This means that we can pretty easily pull down every Pull Request branch in one go without having to add a bunch of remotes. - -Now, you could do something like fetching the reference directly. - -[source,console] ----- -$ git fetch origin refs/pull/958/head -From https://github.com/libgit2/libgit2 - * branch refs/pull/958/head -> FETCH_HEAD ----- - -This tells Git, "`Connect to the `origin` remote, and download the ref named `refs/pull/958/head`.`" -Git happily obeys, and downloads everything you need to construct that ref, and puts a pointer to the commit you want under `.git/FETCH_HEAD`. -You can follow that up with `git merge FETCH_HEAD` into a branch you want to test it in, but that merge commit message looks a bit weird. -Also, if you're reviewing a *lot* of pull requests, this gets tedious. - -There's also a way to fetch _all_ of the pull requests, and keep them up to date whenever you connect to the remote. -Open up `.git/config` in your favorite editor, and look for the `origin` remote. -It should look a bit like this: - -[source,ini] ----- -[remote "origin"] - url = https://github.com/libgit2/libgit2 - fetch = +refs/heads/*:refs/remotes/origin/* ----- - -That line that begins with `fetch =` is a "`refspec.`" -It's a way of mapping names on the remote with names in your local `.git` directory. -This particular one tells Git, "the things on the remote that are under `refs/heads` should go in my local repository under `refs/remotes/origin`." -You can modify this section to add another refspec: - -[source,ini] ----- -[remote "origin"] - url = https://github.com/libgit2/libgit2.git - fetch = +refs/heads/*:refs/remotes/origin/* - fetch = +refs/pull/*/head:refs/remotes/origin/pr/* ----- - -That last line tells Git, "`All the refs that look like `refs/pull/123/head` should be stored locally like `refs/remotes/origin/pr/123`.`" -Now, if you save that file, and do a `git fetch`: - -[source,console] ----- -$ git fetch -# … - * [new ref] refs/pull/1/head -> origin/pr/1 - * [new ref] refs/pull/2/head -> origin/pr/2 - * [new ref] refs/pull/4/head -> origin/pr/4 -# … ----- - -Now all of the remote pull requests are represented locally with refs that act much like tracking branches; they're read-only, and they update when you do a fetch. -This makes it super easy to try the code from a pull request locally: - -[source,console] ----- -$ git checkout pr/2 -Checking out files: 100% (3769/3769), done. -Branch pr/2 set up to track remote branch pr/2 from origin. -Switched to a new branch 'pr/2' ----- - -The eagle-eyed among you would note the `head` on the end of the remote portion of the refspec. -There's also a `refs/pull/#/merge` ref on the GitHub side, which represents the commit that would result if you push the "`merge`" button on the site. -This can allow you to test the merge before even hitting the button. - -===== Pull Requests on Pull Requests - -Not only can you open Pull Requests that target the main or `master` branch, you can actually open a Pull Request targeting any branch in the network. -In fact, you can even target another Pull Request. - -If you see a Pull Request that is moving in the right direction and you have an idea for a change that depends on it or you're not sure is a good idea, or you just don't have push access to the target branch, you can open a Pull Request directly to it. - -When you go to open a Pull Request, there is a box at the top of the page that specifies which branch you're requesting to pull to and which you're requesting to pull from. -If you hit the "`Edit`" button at the right of that box you can change not only the branches but also which fork. - -[[_pr_targets]] -.Manually change the Pull Request target fork and branch -image::images/maint-04-target.png[Manually change the Pull Request target fork and branch] - -Here you can fairly easily specify to merge your new branch into another Pull Request or another fork of the project. - -==== Mentions and Notifications - -GitHub also has a pretty nice notifications system built in that can come in handy when you have questions or need feedback from specific individuals or teams. - -In any comment you can start typing a `@` character and it will begin to autocomplete with the names and usernames of people who are collaborators or contributors in the project. - -.Start typing @ to mention someone -image::images/maint-05-mentions.png[Start typing @ to mention someone] - -You can also mention a user who is not in that dropdown, but often the autocompleter can make it faster. - -Once you post a comment with a user mention, that user will be notified. -This means that this can be a really effective way of pulling people into conversations rather than making them poll. -Very often in Pull Requests on GitHub people will pull in other people on their teams or in their company to review an Issue or Pull Request. - -If someone gets mentioned on a Pull Request or Issue, they will be "`subscribed`" to it and will continue getting notifications any time some activity occurs on it. -You will also be subscribed to something if you opened it, if you're watching the repository or if you comment on something. -If you no longer wish to receive notifications, there is an "`Unsubscribe`" button on the page you can click to stop receiving updates on it. - -.Unsubscribe from an Issue or Pull Request -image::images/maint-06-unsubscribe.png[Unsubscribe from an Issue or Pull Request] - -===== The Notifications Page - -When we mention "`notifications`" here with respect to GitHub, we mean a specific way that GitHub tries to get in touch with you when events happen and there are a few different ways you can configure them. -If you go to the "`Notification center`" tab from the settings page, you can see some of the options you have. - -.Notification center options -image::images/maint-07-notifications.png[Notification center options] - -The two choices are to get notifications over "`Email`" and over "`Web`" and you can choose either, neither or both for when you actively participate in things and for activity on repositories you are watching. - -====== Web Notifications - -Web notifications only exist on GitHub and you can only check them on GitHub. -If you have this option selected in your preferences and a notification is triggered for you, you will see a small blue dot over your notifications icon at the top of your screen as seen in <<_not_center>>. - -[[_not_center]] -.Notification center -image::images/maint-08-notifications-page.png[Notification center] - -If you click on that, you will see a list of all the items you have been notified about, grouped by project. -You can filter to the notifications of a specific project by clicking on its name in the left hand sidebar. -You can also acknowledge the notification by clicking the checkmark icon next to any notification, or acknowledge _all_ of the notifications in a project by clicking the checkmark at the top of the group. -There is also a mute button next to each checkmark that you can click to not receive any further notifications on that item. - -All of these tools are very useful for handling large numbers of notifications. -Many GitHub power users will simply turn off email notifications entirely and manage all of their notifications through this screen. - -====== Email Notifications - -Email notifications are the other way you can handle notifications through GitHub. -If you have this turned on you will get emails for each notification. -We saw examples of this in <<_email_notification>> and <<_email_pr>>. -The emails will also be threaded properly, which is nice if you're using a threading email client. - -There is also a fair amount of metadata embedded in the headers of the emails that GitHub sends you, which can be really helpful for setting up custom filters and rules. - -For instance, if we look at the actual email headers sent to Tony in the email shown in <<_email_pr>>, we will see the following among the information sent: - -[source,mbox] ----- -To: tonychacon/fade -Message-ID: -Subject: [fade] Wait longer to see the dimming effect better (#1) -X-GitHub-Recipient: tonychacon -List-ID: tonychacon/fade -List-Archive: https://github.com/tonychacon/fade -List-Post: -List-Unsubscribe: ,... -X-GitHub-Recipient-Address: tchacon@example.com ----- - -There are a couple of interesting things here. -If you want to highlight or re-route emails to this particular project or even Pull Request, the information in `Message-ID` gives you all the data in `///` format. -If this was an issue, for example, the `` field would have been "`issues`" rather than "`pull`". - -The `List-Post` and `List-Unsubscribe` fields mean that if you have a mail client that understands those, you can easily post to the list or "`Unsubscribe`" from the thread. -That would be essentially the same as clicking the "`mute`" button on the web version of the notification or "`Unsubscribe`" on the Issue or Pull Request page itself. - -It's also worth noting that if you have both email and web notifications enabled and you read the email version of the notification, the web version will be marked as read as well if you have images allowed in your mail client. - -==== Special Files - -There are a couple of special files that GitHub will notice if they are present in your repository. - -==== README - -The first is the `README` file, which can be of nearly any format that GitHub recognizes as prose. -For example, it could be `README`, `README.md`, `README.asciidoc`, etc. -If GitHub sees a `README` file in your source, it will render it on the landing page of the project. - -Many teams use this file to hold all the relevant project information for someone who might be new to the repository or project. -This generally includes things like: - -* What the project is for -* How to configure and install it -* An example of how to use it or get it running -* The license that the project is offered under -* How to contribute to it - -Since GitHub will render this file, you can embed images or links in it for added ease of understanding. - -==== CONTRIBUTING - -The other special file that GitHub recognizes is the `CONTRIBUTING` file. -If you have a file named `CONTRIBUTING` with any file extension, GitHub will show <<_contrib_file>> when anyone starts opening a Pull Request. - -[[_contrib_file]] -.Opening a Pull Request when a CONTRIBUTING file exists -image::images/maint-09-contrib.png[Opening a Pull Request when a CONTRIBUTING file exists] - -The idea here is that you can specify specific things you want or don't want in a Pull Request sent to your project. -This way people may actually read the guidelines before opening the Pull Request. - -==== Project Administration - -Generally there are not a lot of administrative things you can do with a single project, but there are a couple of items that might be of interest. - -===== Changing the Default Branch - -If you are using a branch other than "`master`" as your default branch that you want people to open Pull Requests on or see by default, you can change that in your repository's settings page under the "`Options`" tab. - -[[_default_branch]] -.Change the default branch for a project -image::images/maint-10-default-branch.png[Change the default branch for a project] - -Simply change the default branch in the dropdown and that will be the default for all major operations from then on, including which branch is checked out by default when someone clones the repository. - -===== Transferring a Project - -If you would like to transfer a project to another user or an organization in GitHub, there is a "`Transfer ownership`" option at the bottom of the same "`Options`" tab of your repository settings page that allows you to do this. - -[[_transfer_project]] -.Transfer a project to another GitHub user or Organization -image::images/maint-11-transfer.png[Transfer a project to another GitHub user or Organization] - -This is helpful if you are abandoning a project and someone wants to take it over, or if your project is getting bigger and want to move it into an organization. - -Not only does this move the repository along with all its watchers and stars to another place, it also sets up a redirect from your URL to the new place. -It will also redirect clones and fetches from Git, not just web requests. diff --git a/book/ch06/4-managing-organization.asc b/book/ch06/4-managing-organization.asc deleted file mode 100644 index cdb2447..0000000 --- a/book/ch06/4-managing-organization.asc +++ /dev/null @@ -1,72 +0,0 @@ -[[ch06-github_orgs]] -=== Managing an organization - -(((GitHub, organizations))) -In addition to single-user accounts, GitHub has what are called Organizations. -Like personal accounts, Organizational accounts have a namespace where all their projects exist, but many other things are different. -These accounts represent a group of people with shared ownership of projects, and there are many tools to manage subgroups of those people. -Normally these accounts are used for Open Source groups (such as "`perl`" or "`rails`") or companies (such as "`google`" or "`twitter`"). - -==== Organization Basics - -An organization is pretty easy to create; just click on the "`+`" icon at the top-right of any GitHub page, and select "`New organization`" from the menu. - -.The "`New organization`" menu item -image::images/neworg.png[The “New organization” menu item] - -First you'll need to name your organization and provide an email address for a main point of contact for the group. -Then you can invite other users to be co-owners of the account if you want to. - -Follow these steps and you'll soon be the owner of a brand-new organization. -Like personal accounts, organizations are free if everything you plan to store there will be open source. - -As an owner in an organization, when you fork a repository, you'll have the choice of forking it to your organization's namespace. -When you create new repositories you can create them either under your personal account or under any of the organizations that you are an owner in. -You also automatically "`watch`" any new repository created under these organizations. - -Just like in <<_personal_avatar>>, you can upload an avatar for your organization to personalize it a bit. -Also just like personal accounts, you have a landing page for the organization that lists all of your repositories and can be viewed by other people. - -Now let's cover some of the things that are a bit different with an organizational account. - -==== Teams - -Organizations are associated with individual people by way of teams, which are simply a grouping of individual user accounts and repositories within the organization and what kind of access those people have in those repositories. - -For example, say your company has three repositories: `frontend`, `backend`, and `deployscripts`. -You'd want your HTML/CSS/JavaScript developers to have access to `frontend` and maybe `backend`, and your Operations people to have access to `backend` and `deployscripts`. -Teams make this easy, without having to manage the collaborators for every individual repository. - -The Organization page shows you a simple dashboard of all the repositories, users and teams that are under this organization. - -[[_org_page]] -.The Organization page -image::images/orgs-01-page.png[The Organization page] - -To manage your Teams, you can click on the Teams sidebar on the right hand side of the page in <<_org_page>>. -This will bring you to a page you can use to add members to the team, add repositories to the team or manage the settings and access control levels for the team. -Each team can have read only, read/write or administrative access to the repositories. -You can change that level by clicking the "`Settings`" button in <<_team_page>>. - -[[_team_page]] -.The Team page -image::images/orgs-02-teams.png[The Team page] - -When you invite someone to a team, they will get an email letting them know they've been invited. - -Additionally, team `@mentions` (such as `@acmecorp/frontend`) work much the same as they do with individual users, except that *all* members of the team are then subscribed to the thread. -This is useful if you want the attention from someone on a team, but you don't know exactly who to ask. - -A user can belong to any number of teams, so don't limit yourself to only access-control teams. -Special-interest teams like `ux`, `css`, or `refactoring` are useful for certain kinds of questions, and others like `legal` and `colorblind` for an entirely different kind. - -==== Audit Log - -Organizations also give owners access to all the information about what went on under the organization. -You can go to the 'Audit Log' tab and see what events have happened at an organization level, who did them and where in the world they were done. - -[[_the_audit_log]] -.The Audit log -image::images/orgs-03-audit.png[The Audit log] - -You can also filter down to specific types of events, specific places or specific people. diff --git a/book/ch06/5-scripting.asc b/book/ch06/5-scripting.asc deleted file mode 100644 index c755afa..0000000 --- a/book/ch06/5-scripting.asc +++ /dev/null @@ -1,301 +0,0 @@ -=== Scripting GitHub - -So now we've covered all of the major features and workflows of GitHub, but any large group or project will have customizations they may want to make or external services they may want to integrate. - -Luckily for us, GitHub is really quite hackable in many ways. -In this section we'll cover how to use the GitHub hooks system and its API to make GitHub work how we want it to. - -==== Services and Hooks - -The Hooks and Services section of GitHub repository administration is the easiest way to have GitHub interact with external systems. - -===== Services - -First we'll take a look at Services. -Both the Hooks and Services integrations can be found in the Settings section of your repository, where we previously looked at adding Collaborators and changing the default branch of your project. -Under the "`Webhooks and Services`" tab you will see something like <<_services_hooks>>. - -[[_services_hooks]] -.Services and Hooks configuration section -image::images/scripting-01-services.png[Services and Hooks configuration section] - -There are dozens of services you can choose from, most of them integrations into other commercial and open source systems. -Most of them are for Continuous Integration services, bug and issue trackers, chat room systems and documentation systems. -We'll walk through setting up a very simple one, the Email hook. -If you choose "`email`" from the "`Add Service`" dropdown, you'll get a configuration screen like <<_service_config>>. - -[[_service_config]] -.Email service configuration -image::images/scripting-02-email-service.png[Email service configuration] - -In this case, if we hit the "`Add service`" button, the email address we specified will get an email every time someone pushes to the repository. -Services can listen for lots of different types of events, but most only listen for push events and then do something with that data. - -If there is a system you are using that you would like to integrate with GitHub, you should check here to see if there is an existing service integration available. -For example, if you're using Jenkins to run tests on your codebase, you can enable the Jenkins builtin service integration to kick off a test run every time someone pushes to your repository. - -===== Hooks - -If you need something more specific or you want to integrate with a service or site that is not included in this list, you can instead use the more generic hooks system. -GitHub repository hooks are pretty simple. -You specify a URL and GitHub will post an HTTP payload to that URL on any event you want. - -Generally the way this works is you can setup a small web service to listen for a GitHub hook payload and then do something with the data when it is received. - -To enable a hook, you click the "`Add webhook`" button in <<_services_hooks>>. -This will bring you to a page that looks like <<_web_hook>>. - -[[_web_hook]] -.Web hook configuration -image::images/scripting-03-webhook.png[Web hook configuration] - -The configuration for a web hook is pretty simple. -In most cases you simply enter a URL and a secret key and hit "`Add webhook`". -There are a few options for which events you want GitHub to send you a payload for -- the default is to only get a payload for the `push` event, when someone pushes new code to any branch of your repository. - -Let's see a small example of a web service you may set up to handle a web hook. -We'll use the Ruby web framework Sinatra since it's fairly concise and you should be able to easily see what we're doing. - -Let's say we want to get an email if a specific person pushes to a specific branch of our project modifying a specific file. -We could fairly easily do that with code like this: - -[source,ruby] ----- -require 'sinatra' -require 'json' -require 'mail' - -post '/payload' do - push = JSON.parse(request.body.read) # parse the JSON - - # gather the data we're looking for - pusher = push["pusher"]["name"] - branch = push["ref"] - - # get a list of all the files touched - files = push["commits"].map do |commit| - commit['added'] + commit['modified'] + commit['removed'] - end - files = files.flatten.uniq - - # check for our criteria - if pusher == 'schacon' && - branch == 'ref/heads/special-branch' && - files.include?('special-file.txt') - - Mail.deliver do - from 'tchacon@example.com' - to 'tchacon@example.com' - subject 'Scott Changed the File' - body "ALARM" - end - end -end ----- - -Here we're taking the JSON payload that GitHub delivers us and looking up who pushed it, what branch they pushed to and what files were touched in all the commits that were pushed. -Then we check that against our criteria and send an email if it matches. - -In order to develop and test something like this, you have a nice developer console in the same screen where you set the hook up. -You can see the last few deliveries that GitHub has tried to make for that webhook. -For each hook you can dig down into when it was delivered, if it was successful and the body and headers for both the request and the response. -This makes it incredibly easy to test and debug your hooks. - -[[_web_hook_debug]] -.Web hook debugging information -image::images/scripting-04-webhook-debug.png[Web hook debugging information] - -The other great feature of this is that you can redeliver any of the payloads to test your service easily. - -For more information on how to write webhooks and all the different event types you can listen for, go to the GitHub Developer documentation at https://docs.github.com/en/webhooks-and-events/webhooks/about-webhooks[^]. - -==== The GitHub API - -(((GitHub, API))) -Services and hooks give you a way to receive push notifications about events that happen on your repositories, but what if you need more information about these events? -What if you need to automate something like adding collaborators or labeling issues? - -This is where the GitHub API comes in handy. -GitHub has tons of API endpoints for doing nearly anything you can do on the website in an automated fashion. -In this section we'll learn how to authenticate and connect to the API, how to comment on an issue and how to change the status of a Pull Request through the API. - -==== Basic Usage - -The most basic thing you can do is a simple GET request on an endpoint that doesn't require authentication. -This could be a user or read-only information on an open source project. -For example, if we want to know more about a user named "`schacon`", we can run something like this: - -[source,javascript] ----- -$ curl https://api.github.com/users/schacon -{ - "login": "schacon", - "id": 70, - "avatar_url": "https://avatars.githubusercontent.com/u/70", -# … - "name": "Scott Chacon", - "company": "GitHub", - "following": 19, - "created_at": "2008-01-27T17:19:28Z", - "updated_at": "2014-06-10T02:37:23Z" -} ----- - -There are tons of endpoints like this to get information about organizations, projects, issues, commits -- just about anything you can publicly see on GitHub. -You can even use the API to render arbitrary Markdown or find a `.gitignore` template. - -[source,javascript] ----- -$ curl https://api.github.com/gitignore/templates/Java -{ - "name": "Java", - "source": "*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear - -# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -" -} ----- - -==== Commenting on an Issue - -However, if you want to do an action on the website such as comment on an Issue or Pull Request or if you want to view or interact with private content, you'll need to authenticate. - -There are several ways to authenticate. -You can use basic authentication with just your username and password, but generally it's a better idea to use a personal access token. -You can generate this from the "`Applications`" tab of your settings page. - -[[_access_token]] -.Generate your access token from the "`Applications`" tab of your settings page -image::images/scripting-05-access-token.png[Generate your access token from the “Applications” tab of your settings page] - -It will ask you which scopes you want for this token and a description. -Make sure to use a good description so you feel comfortable removing the token when your script or application is no longer used. - -GitHub will only show you the token once, so be sure to copy it. -You can now use this to authenticate in your script instead of using a username and password. -This is nice because you can limit the scope of what you want to do and the token is revocable. - -This also has the added advantage of increasing your rate limit. -Without authenticating, you will be limited to 60 requests per hour. -If you authenticate you can make up to 5,000 requests per hour. - -So let's use it to make a comment on one of our issues. -Let's say we want to leave a comment on a specific issue, Issue #6. -To do so we have to do an HTTP POST request to `repos///issues//comments` with the token we just generated as an Authorization header. - -[source,javascript] ----- -$ curl -H "Content-Type: application/json" \ - -H "Authorization: token TOKEN" \ - --data '{"body":"A new comment, :+1:"}' \ - https://api.github.com/repos/schacon/blink/issues/6/comments -{ - "id": 58322100, - "html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100", - ... - "user": { - "login": "tonychacon", - "id": 7874698, - "avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2", - "type": "User", - }, - "created_at": "2014-10-08T07:48:19Z", - "updated_at": "2014-10-08T07:48:19Z", - "body": "A new comment, :+1:" -} ----- - -Now if you go to that issue, you can see the comment that we just successfully posted as in <<_api_comment>>. - -[[_api_comment]] -.A comment posted from the GitHub API -image::images/scripting-06-comment.png[A comment posted from the GitHub API] - -You can use the API to do just about anything you can do on the website -- creating and setting milestones, assigning people to Issues and Pull Requests, creating and changing labels, accessing commit data, creating new commits and branches, opening, closing or merging Pull Requests, creating and editing teams, commenting on lines of code in a Pull Request, searching the site and on and on. - -==== Changing the Status of a Pull Request - -There is one final example we'll look at since it's really useful if you're working with Pull Requests. -Each commit can have one or more statuses associated with it and there is an API to add and query that status. - -Most of the Continuous Integration and testing services make use of this API to react to pushes by testing the code that was pushed, and then report back if that commit has passed all the tests. -You could also use this to check if the commit message is properly formatted, if the submitter followed all your contribution guidelines, if the commit was validly signed -- any number of things. - -Let's say you set up a webhook on your repository that hits a small web service that checks for a `Signed-off-by` string in the commit message. - -[source,ruby] ----- -require 'httparty' -require 'sinatra' -require 'json' - -post '/payload' do - push = JSON.parse(request.body.read) # parse the JSON - repo_name = push['repository']['full_name'] - - # look through each commit message - push["commits"].each do |commit| - - # look for a Signed-off-by string - if /Signed-off-by/.match commit['message'] - state = 'success' - description = 'Successfully signed off!' - else - state = 'failure' - description = 'No signoff found.' - end - - # post status to GitHub - sha = commit["id"] - status_url = "https://api.github.com/repos/#{repo_name}/statuses/#{sha}" - - status = { - "state" => state, - "description" => description, - "target_url" => "http://example.com/how-to-signoff", - "context" => "validate/signoff" - } - HTTParty.post(status_url, - :body => status.to_json, - :headers => { - 'Content-Type' => 'application/json', - 'User-Agent' => 'tonychacon/signoff', - 'Authorization' => "token #{ENV['TOKEN']}" } - ) - end -end ----- - -Hopefully this is fairly simple to follow. -In this web hook handler we look through each commit that was just pushed, we look for the string 'Signed-off-by' in the commit message and finally we POST via HTTP to the `/repos///statuses/` API endpoint with the status. - -In this case you can send a state ('success', 'failure', 'error'), a description of what happened, a target URL the user can go to for more information and a "`context`" in case there are multiple statuses for a single commit. -For example, a testing service may provide a status and a validation service like this may also provide a status -- the "`context`" field is how they're differentiated. - -If someone opens a new Pull Request on GitHub and this hook is set up, you may see something like <<_commit_status>>. - -[[_commit_status]] -.Commit status via the API -image::images/scripting-07-status.png[Commit status via the API] - -You can now see a little green check mark next to the commit that has a "`Signed-off-by`" string in the message and a red cross through the one where the author forgot to sign off. -You can also see that the Pull Request takes the status of the last commit on the branch and warns you if it is a failure. -This is really useful if you're using this API for test results so you don't accidentally merge something where the last commit is failing tests. - -==== Octokit - -Though we've been doing nearly everything through `curl` and simple HTTP requests in these examples, several open-source libraries exist that make this API available in a more idiomatic way. -At the time of this writing, the supported languages include Go, Objective-C, Ruby, and .NET. -Check out https://github.com/octokit[^] for more information on these, as they handle much of the HTTP for you. - -Hopefully these tools can help you customize and modify GitHub to work better for your specific workflows. -For complete documentation on the entire API as well as guides for common tasks, check out https://docs.github.com/[^]. diff --git a/book/ch08/attributes.asc b/book/ch06/attributes.asc similarity index 100% rename from book/ch08/attributes.asc rename to book/ch06/attributes.asc diff --git a/book/ch06/callouts/1.pdf b/book/ch06/callouts/1.pdf deleted file mode 100644 index e2e678f..0000000 Binary files a/book/ch06/callouts/1.pdf and /dev/null differ diff --git a/book/ch06/callouts/10.pdf b/book/ch06/callouts/10.pdf deleted file mode 100644 index 4fa51ce..0000000 Binary files a/book/ch06/callouts/10.pdf and /dev/null differ diff --git a/book/ch06/callouts/2.pdf b/book/ch06/callouts/2.pdf deleted file mode 100644 index 32dd285..0000000 Binary files a/book/ch06/callouts/2.pdf and /dev/null differ diff --git a/book/ch06/callouts/3.pdf b/book/ch06/callouts/3.pdf deleted file mode 100644 index c5f4f06..0000000 Binary files a/book/ch06/callouts/3.pdf and /dev/null differ diff --git a/book/ch06/callouts/4.pdf b/book/ch06/callouts/4.pdf deleted file mode 100644 index 5df555d..0000000 Binary files a/book/ch06/callouts/4.pdf and /dev/null differ diff --git a/book/ch06/callouts/5.pdf b/book/ch06/callouts/5.pdf deleted file mode 100644 index 96c1ba2..0000000 Binary files a/book/ch06/callouts/5.pdf and /dev/null differ diff --git a/book/ch06/callouts/6.pdf b/book/ch06/callouts/6.pdf deleted file mode 100644 index 99b454b..0000000 Binary files a/book/ch06/callouts/6.pdf and /dev/null differ diff --git a/book/ch06/callouts/7.pdf b/book/ch06/callouts/7.pdf deleted file mode 100644 index 2e6827f..0000000 Binary files a/book/ch06/callouts/7.pdf and /dev/null differ diff --git a/book/ch06/callouts/8.pdf b/book/ch06/callouts/8.pdf deleted file mode 100644 index 159ac76..0000000 Binary files a/book/ch06/callouts/8.pdf and /dev/null differ diff --git a/book/ch06/callouts/9.pdf b/book/ch06/callouts/9.pdf deleted file mode 100644 index 60a2e70..0000000 Binary files a/book/ch06/callouts/9.pdf and /dev/null differ diff --git a/book/ch08/config.asc b/book/ch06/config.asc similarity index 97% rename from book/ch08/config.asc rename to book/ch06/config.asc index 1673969..c0ebe5d 100644 --- a/book/ch08/config.asc +++ b/book/ch06/config.asc @@ -2,7 +2,7 @@ === Git Configuration (((git commands, config))) -As you read briefly in <>, you can specify Git configuration settings with the `git config` command. +As you read briefly in <>, you can specify Git configuration settings with the `git config` command. One of the first things you did was set up your name and email address: [source,console] @@ -134,7 +134,7 @@ If you run that, Git will print the entire output of all commands, no matter how ===== `user.signingkey` (((GPG))) -If you're making signed annotated tags (as discussed in <>), setting your GPG signing key as a configuration setting makes things easier. +If you're making signed annotated tags (as discussed in <<_signing,Ch 5, Git Toolkit>>), setting your GPG signing key as a configuration setting makes things easier. Set your key ID like so: [source,console] @@ -152,7 +152,7 @@ $ git tag -s ===== `core.excludesfile` (((excludes)))(((.gitignore))) -You can put patterns in your project's `.gitignore` file to have Git not see them as untracked files or try to stage them when you run `git add` on them, as discussed in <>. +You can put patterns in your project's `.gitignore` file to have Git not see them as untracked files or try to stage them when you run `git add` on them, as discussed in <<_ignoring,Ch 2, Git Basics>>. But sometimes you want to ignore certain files for all repositories that you work with. If your computer is running macOS, you're probably familiar with `.DS_Store` files. @@ -514,4 +514,4 @@ $ git config --system receive.denyDeletes true This denies any deletion of branches or tags -- no user can do it. To remove remote branches, you must remove the ref files from the server manually. -There are also more interesting ways to do this on a per-user basis via ACLs, as you'll learn in <>. +There are also more interesting ways to do this on a per-user basis via ACLs, as you'll learn in <<_an_example_git_enforced_policy,Ch 6, Customizing Git>>. diff --git a/book/ch08/hooks.asc b/book/ch06/hooks.asc similarity index 99% rename from book/ch08/hooks.asc rename to book/ch06/hooks.asc index 285ba4f..d06f5a5 100644 --- a/book/ch08/hooks.asc +++ b/book/ch06/hooks.asc @@ -27,7 +27,7 @@ This section splits them into committing-workflow hooks, email-workflow scripts, [NOTE] ==== It's important to note that client-side hooks are *not* copied when you clone a repository. -If your intent with these scripts is to enforce a policy, you'll probably want to do that on the server side; see the example in <>. +If your intent with these scripts is to enforce a policy, you'll probably want to do that on the server side; see the example in <<_an_example_git_enforced_policy,Ch 6, Customizing Git>>. ==== ===== Committing-Workflow Hooks diff --git a/book/ch06/ignoring.asc b/book/ch06/ignoring.asc new file mode 100644 index 0000000..dac8b37 --- /dev/null +++ b/book/ch06/ignoring.asc @@ -0,0 +1,28 @@ +[[_ignoring_files]] +=== Ignoring Files + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 6.2. + +This section is assembled mostly from existing prose: the in-depth ignore material +from ch02/recording-changes.asc (the `_ignoring` subsection keeps only the +quick-start basics there and points here) and the `core.excludesFile` discussion in +ch06/config.asc. When the move happens, keep the ch02 `_ignoring` anchor valid. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* `.gitignore` pattern rules in depth (globs, negation, directory patterns, + double-asterisk) +* Nested `.gitignore` files and their scoping +* Global excludes: `core.excludesFile` and per-repo `.git/info/exclude` +* Debugging why a file is ignored with `git check-ignore --verbose` +* Ignoring build artifacts, editor droppings, and agent-generated files + (cross-reference <>) diff --git a/book/ch08/policy.asc b/book/ch06/policy.asc similarity index 98% rename from book/ch08/policy.asc rename to book/ch06/policy.asc index 852a0c0..95fce47 100644 --- a/book/ch08/policy.asc +++ b/book/ch06/policy.asc @@ -62,7 +62,7 @@ You can take that output, loop through each of those commit SHA-1s, grab the mes You have to figure out how to get the commit message from each of these commits to test. To get the raw commit data, you can use another plumbing command called `git cat-file`. -We'll go over all these plumbing commands in detail in <>; but for now, here's what that command gives you: +We'll go over all these plumbing commands in detail in <>; but for now, here's what that command gives you: [source,console] ---- @@ -167,7 +167,7 @@ On the ACL file you looked at earlier, this `get_acl_access_data` method returns Now that you have the permissions sorted out, you need to determine what paths the commits being pushed have modified, so you can make sure the user who's pushing has access to all of them. -You can pretty easily see what files have been modified in a single commit with the `--name-only` option to the `git log` command (mentioned briefly in <>): +You can pretty easily see what files have been modified in a single commit with the `--name-only` option to the `git log` command (mentioned briefly in <>): [source,console] ---- @@ -428,7 +428,7 @@ target_shas.each do |sha| end ---- -This script uses a syntax that wasn't covered in <>. +This script uses a syntax that wasn't covered in <<_revision_selection,Ch 5, Git Toolkit>>. You get a list of commits that have already been pushed up by running this: [source,ruby] diff --git a/book/ch07-git-and-agents.asc b/book/ch07-git-and-agents.asc new file mode 100644 index 0000000..01b9c93 --- /dev/null +++ b/book/ch07-git-and-agents.asc @@ -0,0 +1,38 @@ +[[ch07-git-and-agents]] +== Git and Agents + +//// +AUTHOR TODO: outline notes only — this is a new chapter for the 3rd edition and all +of its prose must be written by a human author before publication (see AGENTS.md). +Replace the placeholder notes with real prose. See OUTLINE.md, Chapter 7. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this chapter's introduction needs to make: + +* Why AI coding agents change how repositories are used: many concurrent actors, + high commit volume, review becoming the bottleneck +* The repository as the agent's workspace and the commit/PR as its unit of hand-off +* How this chapter builds on worktrees (<<_worktrees,Ch 3, Git Branching>>) and the + collaboration workflows of <> + +include::ch07/workflows-for-agents.asc[] + +include::ch07/tools-and-interfaces.asc[] + +include::ch07/libraries.asc[] + +include::ch07/guiding-agents.asc[] + +include::ch07/tips-and-pitfalls.asc[] + +=== Summary + +[NOTE] +==== +*Draft placeholder — summary to be written by a human author along with the chapter.* +==== diff --git a/book/ch07/callouts/1.pdf b/book/ch07/callouts/1.pdf deleted file mode 100644 index e2e678f..0000000 Binary files a/book/ch07/callouts/1.pdf and /dev/null differ diff --git a/book/ch07/callouts/1.png b/book/ch07/callouts/1.png deleted file mode 100644 index b6cbbc5..0000000 Binary files a/book/ch07/callouts/1.png and /dev/null differ diff --git a/book/ch07/callouts/10.pdf b/book/ch07/callouts/10.pdf deleted file mode 100644 index 4fa51ce..0000000 Binary files a/book/ch07/callouts/10.pdf and /dev/null differ diff --git a/book/ch07/callouts/10.png b/book/ch07/callouts/10.png deleted file mode 100644 index 821ef4c..0000000 Binary files a/book/ch07/callouts/10.png and /dev/null differ diff --git a/book/ch07/callouts/2.pdf b/book/ch07/callouts/2.pdf deleted file mode 100644 index 32dd285..0000000 Binary files a/book/ch07/callouts/2.pdf and /dev/null differ diff --git a/book/ch07/callouts/2.png b/book/ch07/callouts/2.png deleted file mode 100644 index e881df1..0000000 Binary files a/book/ch07/callouts/2.png and /dev/null differ diff --git a/book/ch07/callouts/3.pdf b/book/ch07/callouts/3.pdf deleted file mode 100644 index c5f4f06..0000000 Binary files a/book/ch07/callouts/3.pdf and /dev/null differ diff --git a/book/ch07/callouts/3.png b/book/ch07/callouts/3.png deleted file mode 100644 index 200d582..0000000 Binary files a/book/ch07/callouts/3.png and /dev/null differ diff --git a/book/ch07/callouts/4.pdf b/book/ch07/callouts/4.pdf deleted file mode 100644 index 5df555d..0000000 Binary files a/book/ch07/callouts/4.pdf and /dev/null differ diff --git a/book/ch07/callouts/4.png b/book/ch07/callouts/4.png deleted file mode 100644 index 3bc4c36..0000000 Binary files a/book/ch07/callouts/4.png and /dev/null differ diff --git a/book/ch07/callouts/5.pdf b/book/ch07/callouts/5.pdf deleted file mode 100644 index 96c1ba2..0000000 Binary files a/book/ch07/callouts/5.pdf and /dev/null differ diff --git a/book/ch07/callouts/5.png b/book/ch07/callouts/5.png deleted file mode 100644 index e52d3a8..0000000 Binary files a/book/ch07/callouts/5.png and /dev/null differ diff --git a/book/ch07/callouts/6.pdf b/book/ch07/callouts/6.pdf deleted file mode 100644 index 99b454b..0000000 Binary files a/book/ch07/callouts/6.pdf and /dev/null differ diff --git a/book/ch07/callouts/6.png b/book/ch07/callouts/6.png deleted file mode 100644 index a4465d3..0000000 Binary files a/book/ch07/callouts/6.png and /dev/null differ diff --git a/book/ch07/callouts/7.pdf b/book/ch07/callouts/7.pdf deleted file mode 100644 index 2e6827f..0000000 Binary files a/book/ch07/callouts/7.pdf and /dev/null differ diff --git a/book/ch07/callouts/7.png b/book/ch07/callouts/7.png deleted file mode 100644 index f07d991..0000000 Binary files a/book/ch07/callouts/7.png and /dev/null differ diff --git a/book/ch07/callouts/8.pdf b/book/ch07/callouts/8.pdf deleted file mode 100644 index 159ac76..0000000 Binary files a/book/ch07/callouts/8.pdf and /dev/null differ diff --git a/book/ch07/callouts/8.png b/book/ch07/callouts/8.png deleted file mode 100644 index c99ae9e..0000000 Binary files a/book/ch07/callouts/8.png and /dev/null differ diff --git a/book/ch07/callouts/9.pdf b/book/ch07/callouts/9.pdf deleted file mode 100644 index 60a2e70..0000000 Binary files a/book/ch07/callouts/9.pdf and /dev/null differ diff --git a/book/ch07/callouts/9.png b/book/ch07/callouts/9.png deleted file mode 100644 index b4cbcac..0000000 Binary files a/book/ch07/callouts/9.png and /dev/null differ diff --git a/book/ch07/guiding-agents.asc b/book/ch07/guiding-agents.asc new file mode 100644 index 0000000..dc49d22 --- /dev/null +++ b/book/ch07/guiding-agents.asc @@ -0,0 +1,22 @@ +[[_guiding_agents]] +=== Guiding Agents + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 7.4. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* Repository-level agent instructions (AGENTS.md-style files): what belongs in + them, and keeping them accurate as the repo evolves +* Skills and reusable task recipes for common Git operations +* Commit-message and branch-naming conventions for machine contributors +* Ignoring agent artifacts (scratch files, logs) — cross-reference + <<_ignoring_files,Ch 6, Customizing Git>> diff --git a/book/ch07/libraries.asc b/book/ch07/libraries.asc new file mode 100644 index 0000000..f608078 --- /dev/null +++ b/book/ch07/libraries.asc @@ -0,0 +1,27 @@ +[[_git_libraries]] +=== Libraries + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 7.3. + +This section is a condensed reflow of the 2nd edition's Appendix B "Embedding Git +in your Applications" (book/B/, removed in the restructure — recover via git +history), updated and extended with gitoxide and per-library SHA-256 support notes. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* When to use a library instead of shelling out to the `git` binary +* libgit2 and its language bindings (Rust `git2-rs`, Python `pygit2`, Go `git2go`, + Node `nodegit`, Ruby Rugged) +* JGit (Java), go-git (pure Go), Dulwich (pure Python) +* gitoxide (`gix`, Rust) — new since the 2nd edition +* Per-library SHA-256 object-format support status — a real concern during the + 3.0 transition diff --git a/book/ch07/tips-and-pitfalls.asc b/book/ch07/tips-and-pitfalls.asc new file mode 100644 index 0000000..d8577f3 --- /dev/null +++ b/book/ch07/tips-and-pitfalls.asc @@ -0,0 +1,23 @@ +[[_agent_tips_and_pitfalls]] +=== Tips and Pitfalls + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 7.5. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* Reviewing agent-authored history: what to look for, keeping diffs reviewable +* Attribution: co-author trailers and identifying machine contributions +* Safety rails: protected branches, force-push protection, `safe.directory` and + `safe.bareRepository` in sandboxes +* Secrets hygiene: keeping credentials out of commits agents create; credential + helpers in agent environments (cross-reference + <<_credential_caching,Ch 5, Git Toolkit>>) diff --git a/book/ch07/tools-and-interfaces.asc b/book/ch07/tools-and-interfaces.asc new file mode 100644 index 0000000..1c4673f --- /dev/null +++ b/book/ch07/tools-and-interfaces.asc @@ -0,0 +1,22 @@ +[[_tools_and_interfaces]] +=== Tools and Interfaces + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 7.2. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* The Git CLI as the universal interface agents already know how to drive +* The `gh` CLI for forge operations (PRs, issues, CI status) from agent sessions +* MCP (Model Context Protocol) servers exposing Git and forge capabilities as tools +* Forge APIs and webhooks for agent-driven automation +* Plumbing commands as stable, parseable output for tooling + (cross-reference <>) diff --git a/book/ch07/workflows-for-agents.asc b/book/ch07/workflows-for-agents.asc new file mode 100644 index 0000000..bdb9294 --- /dev/null +++ b/book/ch07/workflows-for-agents.asc @@ -0,0 +1,22 @@ +[[_workflows_for_agents]] +=== Workflows for Agents + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 7.1. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* Branch-per-agent: giving each agent its own topic branch and remote namespace +* Worktree-per-agent for parallel local work (builds on + <<_worktrees,Ch 3, Git Branching>>) +* Sandboxed and disposable checkouts; cloning strategies for agent VMs +* Commit and push cadence: small commits, frequent pushes, recoverable state +* Draft pull requests as the agent hand-off unit; humans review and land diff --git a/book/ch04-git-on-the-server.asc b/book/ch08-git-servers.asc similarity index 82% rename from book/ch04-git-on-the-server.asc rename to book/ch08-git-servers.asc index 87d682b..2d2e6f7 100644 --- a/book/ch04-git-on-the-server.asc +++ b/book/ch08-git-servers.asc @@ -1,5 +1,5 @@ -[[ch04-git-on-the-server]] -== Git on the Server +[[ch08-git-servers]] +== Git Servers (((serving repositories))) At this point, you should be able to do most of the day-to-day tasks for which you'll be using Git. @@ -14,29 +14,32 @@ The first section of this chapter will cover the available protocols and the pro The next sections will explain some typical setups using those protocols and how to get your server running with them. Last, we'll go over a few hosted options, if you don't mind hosting your code on someone else's server and don't want to go through the hassle of setting up and maintaining your own server. +// AUTHOR TODO (restructure): "the next chapter" no longer discusses distributed +// workflows — Distributed Git is now Chapter 4 (before this chapter) and the next +// chapter is Git at Scale. Reword this sentence for the new chapter order. If you have no interest in running your own server, you can skip to the last section of the chapter to see some options for setting up a hosted account and then move on to the next chapter, where we discuss the various ins and outs of working in a distributed source control environment. A remote repository is generally a _bare repository_ -- a Git repository that has no working directory. Because the repository is only used as a collaboration point, there is no reason to have a snapshot checked out on disk; it's just the Git data. In the simplest terms, a bare repository is the contents of your project's `.git` directory and nothing else. -include::ch04/protocols.asc[] +include::ch08/protocols.asc[] -include::ch04/git-on-a-server.asc[] +include::ch08/git-on-a-server.asc[] -include::ch04/generating-ssh-key.asc[] +include::ch08/generating-ssh-key.asc[] -include::ch04/setting-up-server.asc[] +include::ch08/setting-up-server.asc[] -include::ch04/git-daemon.asc[] +include::ch08/git-daemon.asc[] -include::ch04/smart-http.asc[] +include::ch08/smart-http.asc[] -include::ch04/gitweb.asc[] +include::ch08/gitweb.asc[] -include::ch04/gitlab.asc[] +include::ch08/gitlab.asc[] -include::ch04/hosted.asc[] +include::ch08/hosted.asc[] === Summary diff --git a/book/ch04/generating-ssh-key.asc b/book/ch08/generating-ssh-key.asc similarity index 100% rename from book/ch04/generating-ssh-key.asc rename to book/ch08/generating-ssh-key.asc diff --git a/book/ch04/git-daemon.asc b/book/ch08/git-daemon.asc similarity index 100% rename from book/ch04/git-daemon.asc rename to book/ch08/git-daemon.asc diff --git a/book/ch04/git-on-a-server.asc b/book/ch08/git-on-a-server.asc similarity index 100% rename from book/ch04/git-on-a-server.asc rename to book/ch08/git-on-a-server.asc diff --git a/book/ch04/gitlab.asc b/book/ch08/gitlab.asc similarity index 100% rename from book/ch04/gitlab.asc rename to book/ch08/gitlab.asc diff --git a/book/ch04/gitweb.asc b/book/ch08/gitweb.asc similarity index 100% rename from book/ch04/gitweb.asc rename to book/ch08/gitweb.asc diff --git a/book/ch04/hosted.asc b/book/ch08/hosted.asc similarity index 57% rename from book/ch04/hosted.asc rename to book/ch08/hosted.asc index 54eb793..dbc584e 100644 --- a/book/ch04/hosted.asc +++ b/book/ch08/hosted.asc @@ -7,4 +7,8 @@ Even if you set up and run your own server internally, you may still want to use These days, you have a huge number of hosting options to choose from, each with different advantages and disadvantages. To see an up-to-date list, check out the GitHosting page on the main Git wiki at https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitHosting.html[^]. -We'll cover using GitHub in detail in <>, as it is the largest Git host out there and you may need to interact with projects hosted on it in any case, but there are dozens more to choose from should you not want to set up your own Git server. +// AUTHOR TODO (restructure): the dedicated GitHub chapter was dropped in the 3rd +// edition; forge-neutral PR coverage now lives in <<_pull_requests_and_forges,Ch 4, Distributed Git>>. +// Reword this sentence — GitHub is no longer "covered in detail" anywhere, and this +// section is slated to become the hosting-landscape survey (see OUTLINE.md 8.9). +We'll cover using GitHub in detail in <<_pull_requests_and_forges,Ch 4, Distributed Git>>, as it is the largest Git host out there and you may need to interact with projects hosted on it in any case, but there are dozens more to choose from should you not want to set up your own Git server. diff --git a/book/ch04/protocols.asc b/book/ch08/protocols.asc similarity index 97% rename from book/ch04/protocols.asc rename to book/ch08/protocols.asc index ff226d1..3851f92 100644 --- a/book/ch04/protocols.asc +++ b/book/ch08/protocols.asc @@ -29,7 +29,7 @@ $ git clone file:///srv/git/project.git Git operates slightly differently if you explicitly specify `file://` at the beginning of the URL. If you just specify the path, Git tries to use hardlinks or directly copy the files it needs. If you specify `file://`, Git fires up the processes that it normally uses to transfer data over a network, which is generally much less efficient. -The main reason to specify the `file://` prefix is if you want a clean copy of the repository with extraneous references or objects left out -- generally after an import from another VCS or something similar (see <> for maintenance tasks). +The main reason to specify the `file://` prefix is if you want a clean copy of the repository with extraneous references or objects left out -- generally after an import from another VCS or something similar (see <> for maintenance tasks). We'll use the normal path here because doing so is almost always faster. To add a local repository to an existing Git project, you can run something like this: @@ -46,7 +46,7 @@ Then, you can push to and pull from that remote via your new remote name `local_ The pros of file-based repositories are that they're simple and they use existing file permissions and network access. If you already have a shared filesystem to which your whole team has access, setting up a repository is very easy. You stick the bare repository copy somewhere everyone has shared access to and set the read/write permissions as you would for any other shared directory. -We'll discuss how to export a bare repository copy for this purpose in <>. +We'll discuss how to export a bare repository copy for this purpose in <<_getting_git_on_a_server,Ch 8, Git Servers>>. This is also a nice option for quickly grabbing work from someone else's working repository. If you and a co-worker are working on the same project and they want you to check something out, running a command like `git pull /home/john/project` is often easier than them pushing to a remote server and you subsequently fetching from it. @@ -90,7 +90,7 @@ In fact, for services like GitHub, the URL you use to view the repository online If the server does not respond with a Git HTTP smart service, the Git client will try to fall back to the simpler _Dumb_ HTTP protocol. The Dumb protocol expects the bare Git repository to be served like normal files from the web server. The beauty of Dumb HTTP is the simplicity of setting it up. -Basically, all you have to do is put a bare Git repository under your HTTP document root and set up a specific `post-update` hook, and you're done (see <>). +Basically, all you have to do is put a bare Git repository under your HTTP document root and set up a specific `post-update` hook, and you're done (see <<_git_hooks,Ch 6, Customizing Git>>). At that point, anyone who can access the web server under which you put the repository can also clone your repository. To allow read access to your repository over HTTP, do something like this: @@ -113,7 +113,7 @@ $ git clone https://example.com/gitproject.git ---- In this particular case, we're using the `/var/www/htdocs` path that is common for Apache setups, but you can use any static web server -- just put the bare repository in its path. -The Git data is served as basic static files (see the <> chapter for details about exactly how it's served). +The Git data is served as basic static files (see the <> chapter for details about exactly how it's served). Generally you would either choose to run a read/write Smart HTTP server or simply have the files accessible as read-only in the Dumb manner. It's rare to run a mix of the two services. @@ -138,7 +138,7 @@ Other than that, there is very little advantage that other protocols have over S If you're using HTTP for authenticated pushing, providing your credentials is sometimes more complicated than using keys over SSH. There are, however, several credential caching tools you can use, including Keychain access on macOS and Credential Manager on Windows, to make this pretty painless. -Read <> to see how to set up secure HTTP password caching on your system. +Read <<_credential_caching,Ch 5, Git Toolkit>> to see how to set up secure HTTP password caching on your system. ==== The SSH Protocol diff --git a/book/ch04/setting-up-server.asc b/book/ch08/setting-up-server.asc similarity index 100% rename from book/ch04/setting-up-server.asc rename to book/ch08/setting-up-server.asc diff --git a/book/ch04/smart-http.asc b/book/ch08/smart-http.asc similarity index 100% rename from book/ch04/smart-http.asc rename to book/ch08/smart-http.asc diff --git a/book/ch09-git-and-other-systems.asc b/book/ch09-git-and-other-systems.asc deleted file mode 100644 index 42c49f5..0000000 --- a/book/ch09-git-and-other-systems.asc +++ /dev/null @@ -1,44 +0,0 @@ -[[ch09-git-and-other-systems]] -== Git and Other Systems - -The world isn't perfect. -Usually, you can't immediately switch every project you come in contact with to Git. -Sometimes you're stuck on a project using another VCS, and wish it was Git. -We'll spend the first part of this chapter learning about ways to use Git as a client when the project you're working on is hosted in a different system. - -At some point, you may want to convert your existing project to Git. -The second part of this chapter covers how to migrate your project into Git from several specific systems, as well as a method that will work if no pre-built import tool exists. - -=== Git as a Client - -(((Git as a client))) -Git provides such a nice experience for developers that many people have figured out how to use it on their workstation, even if the rest of their team is using an entirely different VCS. -There are a number of these adapters, called "`bridges,`" available. -Here we'll cover the ones you're most likely to run into in the wild. - -include::ch09/client-svn.asc[] - -include::ch09/client-hg.asc[] - -include::ch09/client-p4.asc[] - -[[_migrating]] -=== Migrating to Git - -(((Migrating to Git))) -If you have an existing codebase in another VCS but you've decided to start using Git, you must migrate your project one way or another. -This section goes over some importers for common systems, and then demonstrates how to develop your own custom importer. -You'll learn how to import data from several of the bigger professionally used SCM systems, because they make up the majority of users who are switching, and because high-quality tools for them are easy to come by. - -include::ch09/import-svn.asc[] - -include::ch09/import-hg.asc[] - -include::ch09/import-p4.asc[] - -include::ch09/import-custom.asc[] - -=== Summary - -You should feel comfortable using Git as a client for other version-control systems, or importing nearly any existing repository into Git without losing data. -In the next chapter, we'll cover the raw internals of Git so you can craft every single byte, if need be. diff --git a/book/ch09-git-at-scale.asc b/book/ch09-git-at-scale.asc new file mode 100644 index 0000000..edd2e9c --- /dev/null +++ b/book/ch09-git-at-scale.asc @@ -0,0 +1,45 @@ +[[ch09-git-at-scale]] +== Git at Scale + +//// +AUTHOR TODO: outline notes only — this is a new chapter for the 3rd edition and all +of its prose must be written by a human author before publication (see AGENTS.md). +Replace the placeholder notes with real prose. See OUTLINE.md, Chapter 9. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this chapter's introduction needs to make: + +* Why big repositories and big teams strain Git: history size, object count, + working-tree size, ref count +* A map of the scaling toolbox and how the pieces relate: shallow clones, + partial clones, sparse checkout, Scalar, background maintenance, large-file + storage, and monorepo practices +* Where the on-disk format details live + (<>) versus the day-to-day operations + covered here + +include::ch09/shallow-clones.asc[] + +include::ch09/partial-clone.asc[] + +include::ch09/sparse-checkout.asc[] + +include::ch09/scalar.asc[] + +include::ch09/keeping-large-repos-fast.asc[] + +include::ch09/large-files.asc[] + +include::ch09/monorepos.asc[] + +=== Summary + +[NOTE] +==== +*Draft placeholder — summary to be written by a human author along with the chapter.* +==== diff --git a/book/ch09/client-hg.asc b/book/ch09/client-hg.asc deleted file mode 100644 index 4f79456..0000000 --- a/book/ch09/client-hg.asc +++ /dev/null @@ -1,396 +0,0 @@ -==== Git and Mercurial - -(((Interoperation with other VCSs, Mercurial))) -(((Mercurial))) -The DVCS universe is larger than just Git. -In fact, there are many other systems in this space, each with their own angle on how to do distributed version control correctly. -Apart from Git, the most popular is Mercurial, and the two are very similar in many respects. - -The good news, if you prefer Git's client-side behavior but are working with a project whose source code is controlled with Mercurial, is that there's a way to use Git as a client for a Mercurial-hosted repository. -Since the way Git talks to server repositories is through remotes, it should come as no surprise that this bridge is implemented as a remote helper. -The project's name is git-remote-hg, and it can be found at https://github.com/felipec/git-remote-hg[^]. - -===== git-remote-hg - -First, you need to install git-remote-hg. -This basically entails dropping its file somewhere in your path, like so: - -[source,console] ----- -$ curl -o ~/bin/git-remote-hg \ - https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg -$ chmod +x ~/bin/git-remote-hg ----- - -…assuming `~/bin` is in your `$PATH`. -Git-remote-hg has one other dependency: the `mercurial` library for Python. -If you have Python installed, this is as simple as: - -[source,console] ----- -$ pip install mercurial ----- - -If you don't have Python installed, visit https://www.python.org/[^] and get it first. - -The last thing you'll need is the Mercurial client. -Go to https://www.mercurial-scm.org/[^] and install it if you haven't already. - -Now you're ready to rock. -All you need is a Mercurial repository you can push to. -Fortunately, every Mercurial repository can act this way, so we'll just use the "hello world" repository everyone uses to learn Mercurial: - -[source,console] ----- -$ hg clone http://selenic.com/repo/hello /tmp/hello ----- - -===== Getting Started - -Now that we have a suitable "`server-side`" repository, we can go through a typical workflow. -As you'll see, these two systems are similar enough that there isn't much friction. - -As always with Git, first we clone: - -[source,console] ----- -$ git clone hg::/tmp/hello /tmp/hello-git -$ cd /tmp/hello-git -$ git log --oneline --graph --decorate -* ac7955c (HEAD, origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master, master) Create a makefile -* 65bb417 Create a standard 'hello, world' program ----- - -You'll notice that working with a Mercurial repository uses the standard `git clone` command. -That's because git-remote-hg is working at a fairly low level, using a similar mechanism to how Git's HTTP/S protocol is implemented (remote helpers). -Since Git and Mercurial are both designed for every client to have a full copy of the repository history, this command makes a full clone, including all the project's history, and does it fairly quickly. - -The `log` command shows two commits, the latest of which is pointed to by a whole slew of refs. -It turns out some of these aren't actually there. -Let's take a look at what's actually in the `.git` directory: - -[source,console] ----- -$ tree .git/refs -.git/refs -├── heads -│ └── master -├── hg -│ └── origin -│ ├── bookmarks -│ │ └── master -│ └── branches -│ └── default -├── notes -│ └── hg -├── remotes -│ └── origin -│ └── HEAD -└── tags - -9 directories, 5 files ----- - -Git-remote-hg is trying to make things more idiomatically Git-esque, but under the hood it's managing the conceptual mapping between two slightly different systems. -The `refs/hg` directory is where the actual remote refs are stored. -For example, the `refs/hg/origin/branches/default` is a Git ref file that contains the SHA-1 starting with "`ac7955c`", which is the commit that `master` points to. -So the `refs/hg` directory is kind of like a fake `refs/remotes/origin`, but it has the added distinction between bookmarks and branches. - -The `notes/hg` file is the starting point for how git-remote-hg maps Git commit hashes to Mercurial changeset IDs. -Let's explore a bit: - -[source,console] ----- -$ cat notes/hg -d4c10386... - -$ git cat-file -p d4c10386... -tree 1781c96... -author remote-hg <> 1408066400 -0800 -committer remote-hg <> 1408066400 -0800 - -Notes for master - -$ git ls-tree 1781c96... -100644 blob ac9117f... 65bb417... -100644 blob 485e178... ac7955c... - -$ git cat-file -p ac9117f -0a04b987be5ae354b710cefeba0e2d9de7ad41a9 ----- - -So `refs/notes/hg` points to a tree, which in the Git object database is a list of other objects with names. -`git ls-tree` outputs the mode, type, object hash, and filename for items inside a tree. -Once we dig down to one of the tree items, we find that inside it is a blob named "`ac9117f`" (the SHA-1 hash of the commit pointed to by `master`), with contents "`0a04b98`" (which is the ID of the Mercurial changeset at the tip of the `default` branch). - -The good news is that we mostly don't have to worry about all of this. -The typical workflow won't be very different from working with a Git remote. - -There's one more thing we should attend to before we continue: ignores. -Mercurial and Git use a very similar mechanism for this, but it's likely you don't want to actually commit a `.gitignore` file into a Mercurial repository. -Fortunately, Git has a way to ignore files that's local to an on-disk repository, and the Mercurial format is compatible with Git, so you just have to copy it over: - -[source,console] ----- -$ cp .hgignore .git/info/exclude ----- - -The `.git/info/exclude` file acts just like a `.gitignore`, but isn't included in commits. - -===== Workflow - -Let's assume we've done some work and made some commits on the `master` branch, and you're ready to push it to the remote repository. -Here's what our repository looks like right now: - -[source,console] ----- -$ git log --oneline --graph --decorate -* ba04a2a (HEAD, master) Update makefile -* d25d16f Goodbye -* ac7955c (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Create a makefile -* 65bb417 Create a standard 'hello, world' program ----- - -Our `master` branch is two commits ahead of `origin/master`, but those two commits exist only on our local machine. -Let's see if anyone else has been doing important work at the same time: - -[source,console] ----- -$ git fetch -From hg::/tmp/hello - ac7955c..df85e87 master -> origin/master - ac7955c..df85e87 branches/default -> origin/branches/default -$ git log --oneline --graph --decorate --all -* 7b07969 (refs/notes/hg) Notes for default -* d4c1038 Notes for master -* df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation -| * ba04a2a (HEAD, master) Update makefile -| * d25d16f Goodbye -|/ -* ac7955c Create a makefile -* 65bb417 Create a standard 'hello, world' program ----- - -Since we used the `--all` flag, we see the "`notes`" refs that are used internally by git-remote-hg, but we can ignore them. -The rest is what we expected; `origin/master` has advanced by one commit, and our history has now diverged. -Unlike the other systems we work with in this chapter, Mercurial is capable of handling merges, so we're not going to do anything fancy. - -[source,console] ----- -$ git merge origin/master -Auto-merging hello.c -Merge made by the 'recursive' strategy. - hello.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) -$ git log --oneline --graph --decorate -* 0c64627 (HEAD, master) Merge remote-tracking branch 'origin/master' -|\ -| * df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation -* | ba04a2a Update makefile -* | d25d16f Goodbye -|/ -* ac7955c Create a makefile -* 65bb417 Create a standard 'hello, world' program ----- - -Perfect. -We run the tests and everything passes, so we're ready to share our work with the rest of the team: - -[source,console] ----- -$ git push -To hg::/tmp/hello - df85e87..0c64627 master -> master ----- - -That's it! -If you take a look at the Mercurial repository, you'll see that this did what we'd expect: - -[source,console] ----- -$ hg log -G --style compact -o 5[tip]:4,2 dc8fa4f932b8 2014-08-14 19:33 -0700 ben -|\ Merge remote-tracking branch 'origin/master' -| | -| o 4 64f27bcefc35 2014-08-14 19:27 -0700 ben -| | Update makefile -| | -| o 3:1 4256fc29598f 2014-08-14 19:27 -0700 ben -| | Goodbye -| | -@ | 2 7db0b4848b3c 2014-08-14 19:30 -0700 ben -|/ Add some documentation -| -o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm -| Create a makefile -| -o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm - Create a standard 'hello, world' program ----- - -The changeset numbered _2_ was made by Mercurial, and the changesets numbered _3_ and _4_ were made by git-remote-hg, by pushing commits made with Git. - -===== Branches and Bookmarks - -Git has only one kind of branch: a reference that moves when commits are made. -In Mercurial, this kind of a reference is called a "`bookmark,`" and it behaves in much the same way as a Git branch. - -Mercurial's concept of a "`branch`" is more heavyweight. -The branch that a changeset is made on is recorded _with the changeset_, which means it will always be in the repository history. -Here's an example of a commit that was made on the `develop` branch: - -[source,console] ----- -$ hg log -l 1 -changeset: 6:8f65e5e02793 -branch: develop -tag: tip -user: Ben Straub -date: Thu Aug 14 20:06:38 2014 -0700 -summary: More documentation ----- - -Note the line that begins with "`branch`". -Git can't really replicate this (and doesn't need to; both types of branch can be represented as a Git ref), but git-remote-hg needs to understand the difference, because Mercurial cares. - -Creating Mercurial bookmarks is as easy as creating Git branches. -On the Git side: - -[source,console] ----- -$ git checkout -b featureA -Switched to a new branch 'featureA' -$ git push origin featureA -To hg::/tmp/hello - * [new branch] featureA -> featureA ----- - -That's all there is to it. -On the Mercurial side, it looks like this: - -[source,console] ----- -$ hg bookmarks - featureA 5:bd5ac26f11f9 -$ hg log --style compact -G -@ 6[tip] 8f65e5e02793 2014-08-14 20:06 -0700 ben -| More documentation -| -o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben -|\ Merge remote-tracking branch 'origin/master' -| | -| o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben -| | update makefile -| | -| o 3:1 318914536c86 2014-08-14 20:00 -0700 ben -| | goodbye -| | -o | 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben -|/ Add some documentation -| -o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm -| Create a makefile -| -o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm - Create a standard 'hello, world' program ----- - -Note the new `[featureA]` tag on revision 5. -These act exactly like Git branches on the Git side, with one exception: you can't delete a bookmark from the Git side (this is a limitation of remote helpers). - -You can work on a "`heavyweight`" Mercurial branch also: just put a branch in the `branches` namespace: - -[source,console] ----- -$ git checkout -b branches/permanent -Switched to a new branch 'branches/permanent' -$ vi Makefile -$ git commit -am 'A permanent change' -$ git push origin branches/permanent -To hg::/tmp/hello - * [new branch] branches/permanent -> branches/permanent ----- - -Here's what that looks like on the Mercurial side: - -[source,console] ----- -$ hg branches -permanent 7:a4529d07aad4 -develop 6:8f65e5e02793 -default 5:bd5ac26f11f9 (inactive) -$ hg log -G -o changeset: 7:a4529d07aad4 -| branch: permanent -| tag: tip -| parent: 5:bd5ac26f11f9 -| user: Ben Straub -| date: Thu Aug 14 20:21:09 2014 -0700 -| summary: A permanent change -| -| @ changeset: 6:8f65e5e02793 -|/ branch: develop -| user: Ben Straub -| date: Thu Aug 14 20:06:38 2014 -0700 -| summary: More documentation -| -o changeset: 5:bd5ac26f11f9 -|\ bookmark: featureA -| | parent: 4:0434aaa6b91f -| | parent: 2:f098c7f45c4f -| | user: Ben Straub -| | date: Thu Aug 14 20:02:21 2014 -0700 -| | summary: Merge remote-tracking branch 'origin/master' -[...] ----- - -The branch name "`permanent`" was recorded with the changeset marked _7_. - -From the Git side, working with either of these branch styles is the same: just checkout, commit, fetch, merge, pull, and push as you normally would. -One thing you should know is that Mercurial doesn't support rewriting history, only adding to it. -Here's what our Mercurial repository looks like after an interactive rebase and a force-push: - -[source,console] ----- -$ hg log --style compact -G -o 10[tip] 99611176cbc9 2014-08-14 20:21 -0700 ben -| A permanent change -| -o 9 f23e12f939c3 2014-08-14 20:01 -0700 ben -| Add some documentation -| -o 8:1 c16971d33922 2014-08-14 20:00 -0700 ben -| goodbye -| -| o 7:5 a4529d07aad4 2014-08-14 20:21 -0700 ben -| | A permanent change -| | -| | @ 6 8f65e5e02793 2014-08-14 20:06 -0700 ben -| |/ More documentation -| | -| o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben -| |\ Merge remote-tracking branch 'origin/master' -| | | -| | o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben -| | | update makefile -| | | -+---o 3:1 318914536c86 2014-08-14 20:00 -0700 ben -| | goodbye -| | -| o 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben -|/ Add some documentation -| -o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm -| Create a makefile -| -o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm - Create a standard "hello, world" program ----- - -Changesets _8_, _9_, and _10_ have been created and belong to the `permanent` branch, but the old changesets are still there. -This can be *very* confusing for your teammates who are using Mercurial, so try to avoid it. - -===== Mercurial Summary - -Git and Mercurial are similar enough that working across the boundary is fairly painless. -If you avoid changing history that's left your machine (as is generally recommended), you may not even be aware that the other end is Mercurial. diff --git a/book/ch09/client-p4.asc b/book/ch09/client-p4.asc deleted file mode 100644 index 16708c1..0000000 --- a/book/ch09/client-p4.asc +++ /dev/null @@ -1,671 +0,0 @@ -==== Git and Perforce - -(((Interoperation with other VCSs, Perforce))) -(((Perforce))) -Perforce is a very popular version-control system in corporate environments. -It's been around since 1995, which makes it the oldest system covered in this chapter. -As such, it's designed with the constraints of its day; it assumes you're always connected to a single central server, and only one version is kept on the local disk. -To be sure, its features and constraints are well-suited to several specific problems, but there are lots of projects using Perforce where Git would actually work better. - -There are two options if you'd like to mix your use of Perforce and Git. -The first one we'll cover is the "`Git Fusion`" bridge from the makers of Perforce, which lets you expose subtrees of your Perforce depot as read-write Git repositories. -The second is git-p4, a client-side bridge that lets you use Git as a Perforce client, without requiring any reconfiguration of the Perforce server. - -[[_p4_git_fusion]] -===== Git Fusion - -(((Perforce, Git Fusion))) -Perforce provides a product called Git Fusion (available at https://www.perforce.com/manuals/git-fusion/[^]), which synchronizes a Perforce server with Git repositories on the server side. - -====== Setting Up - -For our examples, we'll be using the easiest installation method for Git Fusion, which is downloading a virtual machine that runs the Perforce daemon and Git Fusion. -You can get the virtual machine image from https://www.perforce.com/downloads[^], and once it's finished downloading, import it into your favorite virtualization software (we'll use VirtualBox). - -Upon first starting the machine, it asks you to customize the password for three Linux users (`root`, `perforce`, and `git`), and provide an instance name, which can be used to distinguish this installation from others on the same network. -When that has all completed, you'll see this: - -.The Git Fusion virtual machine boot screen -image::images/git-fusion-boot.png[The Git Fusion virtual machine boot screen] - -You should take note of the IP address that's shown here, we'll be using it later on. -Next, we'll create a Perforce user. -Select the "`Login`" option at the bottom and press enter (or SSH to the machine), and log in as `root`. -Then use these commands to create a user: - -[source,console] ----- -$ p4 -p localhost:1666 -u super user -f john -$ p4 -p localhost:1666 -u john passwd -$ exit ----- - -The first one will open a VI editor to customize the user, but you can accept the defaults by typing `:wq` and hitting enter. -The second one will prompt you to enter a password twice. -That's all we need to do with a shell prompt, so exit out of the session. - -The next thing you'll need to do to follow along is to tell Git not to verify SSL certificates. -The Git Fusion image comes with a certificate, but it's for a domain that won't match your virtual machine's IP address, so Git will reject the HTTPS connection. -If this is going to be a permanent installation, consult the Perforce Git Fusion manual to install a different certificate; for our example purposes, this will suffice: - -[source,console] ----- -$ export GIT_SSL_NO_VERIFY=true ----- - -Now we can test that everything is working. - -[source,console] ----- -$ git clone https://10.0.1.254/Talkhouse -Cloning into 'Talkhouse'... -Username for 'https://10.0.1.254': john -Password for 'https://john@10.0.1.254': -remote: Counting objects: 630, done. -remote: Compressing objects: 100% (581/581), done. -remote: Total 630 (delta 172), reused 0 (delta 0) -Receiving objects: 100% (630/630), 1.22 MiB | 0 bytes/s, done. -Resolving deltas: 100% (172/172), done. -Checking connectivity... done. ----- - -The virtual-machine image comes equipped with a sample project that you can clone. -Here we're cloning over HTTPS, with the `john` user that we created above; Git asks for credentials for this connection, but the credential cache will allow us to skip this step for any subsequent requests. - -====== Fusion Configuration - -Once you've got Git Fusion installed, you'll want to tweak the configuration. -This is actually fairly easy to do using your favorite Perforce client; just map the `//.git-fusion` directory on the Perforce server into your workspace. -The file structure looks like this: - -[source,console] ----- -$ tree -. -├── objects -│ ├── repos -│ │ └── [...] -│ └── trees -│ └── [...] -│ -├── p4gf_config -├── repos -│ └── Talkhouse -│ └── p4gf_config -└── users - └── p4gf_usermap - -498 directories, 287 files ----- - -The `objects` directory is used internally by Git Fusion to map Perforce objects to Git and vice versa, you won't have to mess with anything in there. -There's a global `p4gf_config` file in this directory, as well as one for each repository – these are the configuration files that determine how Git Fusion behaves. -Let's take a look at the file in the root: - -[source,ini] ----- -[repo-creation] -charset = utf8 - -[git-to-perforce] -change-owner = author -enable-git-branch-creation = yes -enable-swarm-reviews = yes -enable-git-merge-commits = yes -enable-git-submodules = yes -preflight-commit = none -ignore-author-permissions = no -read-permission-check = none -git-merge-avoidance-after-change-num = 12107 - -[perforce-to-git] -http-url = none -ssh-url = none - -[@features] -imports = False -chunked-push = False -matrix2 = False -parallel-push = False - -[authentication] -email-case-sensitivity = no ----- - -We won't go into the meanings of these flags here, but note that this is just an INI-formatted text file, much like Git uses for configuration. -This file specifies the global options, which can then be overridden by repository-specific configuration files, like `repos/Talkhouse/p4gf_config`. -If you open this file, you'll see a `[@repo]` section with some settings that are different from the global defaults. -You'll also see sections that look like this: - -[source,ini] ----- -[Talkhouse-master] -git-branch-name = master -view = //depot/Talkhouse/main-dev/... ... ----- - -This is a mapping between a Perforce branch and a Git branch. -The section can be named whatever you like, so long as the name is unique. -`git-branch-name` lets you convert a depot path that would be cumbersome under Git to a more friendly name. -The `view` setting controls how Perforce files are mapped into the Git repository, using the standard view mapping syntax. -More than one mapping can be specified, like in this example: - -[source,ini] ----- -[multi-project-mapping] -git-branch-name = master -view = //depot/project1/main/... project1/... - //depot/project2/mainline/... project2/... ----- - -This way, if your normal workspace mapping includes changes in the structure of the directories, you can replicate that with a Git repository. - -The last file we'll discuss is `users/p4gf_usermap`, which maps Perforce users to Git users, and which you may not even need. -When converting from a Perforce changeset to a Git commit, Git Fusion's default behavior is to look up the Perforce user, and use the email address and full name stored there for the author/committer field in Git. -When converting the other way, the default is to look up the Perforce user with the email address stored in the Git commit's author field, and submit the changeset as that user (with permissions applying). -In most cases, this behavior will do just fine, but consider the following mapping file: - -[source] ----- -john john@example.com "John Doe" -john johnny@appleseed.net "John Doe" -bob employeeX@example.com "Anon X. Mouse" -joe employeeY@example.com "Anon Y. Mouse" ----- - -Each line is of the format ` ""`, and creates a single user mapping. -The first two lines map two distinct email addresses to the same Perforce user account. -This is useful if you've created Git commits under several different email addresses (or change email addresses), but want them to be mapped to the same Perforce user. -When creating a Git commit from a Perforce changeset, the first line matching the Perforce user is used for Git authorship information. - -The last two lines mask Bob and Joe's actual names and email addresses from the Git commits that are created. -This is nice if you want to open-source an internal project, but don't want to publish your employee directory to the entire world. -Note that the email addresses and full names should be unique, unless you want all the Git commits to be attributed to a single fictional author. - -====== Workflow - -Perforce Git Fusion is a two-way bridge between Perforce and Git version control. -Let's have a look at how it feels to work from the Git side. -We'll assume we've mapped in the "`Jam`" project using a configuration file as shown above, which we can clone like this: - -[source,console] ----- -$ git clone https://10.0.1.254/Jam -Cloning into 'Jam'... -Username for 'https://10.0.1.254': john -Password for 'https://john@10.0.1.254': -remote: Counting objects: 2070, done. -remote: Compressing objects: 100% (1704/1704), done. -Receiving objects: 100% (2070/2070), 1.21 MiB | 0 bytes/s, done. -remote: Total 2070 (delta 1242), reused 0 (delta 0) -Resolving deltas: 100% (1242/1242), done. -Checking connectivity... done. -$ git branch -a -* master - remotes/origin/HEAD -> origin/master - remotes/origin/master - remotes/origin/rel2.1 -$ git log --oneline --decorate --graph --all -* 0a38c33 (origin/rel2.1) Create Jam 2.1 release branch. -| * d254865 (HEAD, origin/master, origin/HEAD, master) Upgrade to latest metrowerks on Beos -- the Intel one. -| * bd2f54a Put in fix for jam's NT handle leak. -| * c0f29e7 Fix URL in a jam doc -| * cc644ac Radstone's lynx port. -[...] ----- - -The first time you do this, it may take some time. -What's happening is that Git Fusion is converting all the applicable changesets in the Perforce history into Git commits. -This happens locally on the server, so it's relatively fast, but if you have a lot of history, it can still take some time. -Subsequent fetches do incremental conversion, so it'll feel more like Git's native speed. - -As you can see, our repository looks exactly like any other Git repository you might work with. -There are three branches, and Git has helpfully created a local `master` branch that tracks `origin/master`. -Let's do a bit of work, and create a couple of new commits: - -[source,console] ----- -# ... -$ git log --oneline --decorate --graph --all -* cfd46ab (HEAD, master) Add documentation for new feature -* a730d77 Whitespace -* d254865 (origin/master, origin/HEAD) Upgrade to latest metrowerks on Beos -- the Intel one. -* bd2f54a Put in fix for jam's NT handle leak. -[...] ----- - -We have two new commits. -Now let's check if anyone else has been working: - -[source,console] ----- -$ git fetch -remote: Counting objects: 5, done. -remote: Compressing objects: 100% (3/3), done. -remote: Total 3 (delta 2), reused 0 (delta 0) -Unpacking objects: 100% (3/3), done. -From https://10.0.1.254/Jam - d254865..6afeb15 master -> origin/master -$ git log --oneline --decorate --graph --all -* 6afeb15 (origin/master, origin/HEAD) Update copyright -| * cfd46ab (HEAD, master) Add documentation for new feature -| * a730d77 Whitespace -|/ -* d254865 Upgrade to latest metrowerks on Beos -- the Intel one. -* bd2f54a Put in fix for jam's NT handle leak. -[...] ----- - -It looks like someone was! -You wouldn't know it from this view, but the `6afeb15` commit was actually created using a Perforce client. -It just looks like another commit from Git's point of view, which is exactly the point. -Let's see how the Perforce server deals with a merge commit: - -[source,console] ----- -$ git merge origin/master -Auto-merging README -Merge made by the 'recursive' strategy. - README | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) -$ git push -Counting objects: 9, done. -Delta compression using up to 8 threads. -Compressing objects: 100% (9/9), done. -Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done. -Total 9 (delta 6), reused 0 (delta 0) -remote: Perforce: 100% (3/3) Loading commit tree into memory... -remote: Perforce: 100% (5/5) Finding child commits... -remote: Perforce: Running git fast-export... -remote: Perforce: 100% (3/3) Checking commits... -remote: Processing will continue even if connection is closed. -remote: Perforce: 100% (3/3) Copying changelists... -remote: Perforce: Submitting new Git commit objects to Perforce: 4 -To https://10.0.1.254/Jam - 6afeb15..89cba2b master -> master ----- - -Git thinks it worked. -Let's take a look at the history of the `README` file from Perforce's point of view, using the revision graph feature of `p4v`: - -.Perforce revision graph resulting from Git push -image::images/git-fusion-perforce-graph.png[Perforce revision graph resulting from Git push] - -If you've never seen this view before, it may seem confusing, but it shows the same concepts as a graphical viewer for Git history. -We're looking at the history of the `README` file, so the directory tree at top left only shows that file as it surfaces in various branches. -At top right, we have a visual graph of how different revisions of the file are related, and the big-picture view of this graph is at bottom right. -The rest of the view is given to the details view for the selected revision (`2` in this case). - -One thing to notice is that the graph looks exactly like the one in Git's history. -Perforce didn't have a named branch to store the `1` and `2` commits, so it made an "`anonymous`" branch in the `.git-fusion` directory to hold it. -This will also happen for named Git branches that don't correspond to a named Perforce branch (and you can later map them to a Perforce branch using the configuration file). - -Most of this happens behind the scenes, but the end result is that one person on a team can be using Git, another can be using Perforce, and neither of them will know about the other's choice. - -====== Git-Fusion Summary - -If you have (or can get) access to your Perforce server, Git Fusion is a great way to make Git and Perforce talk to each other. -There's a bit of configuration involved, but the learning curve isn't very steep. -This is one of the few sections in this chapter where cautions about using Git's full power will not appear. -That's not to say that Perforce will be happy with everything you throw at it – if you try to rewrite history that's already been pushed, Git Fusion will reject it – but Git Fusion tries very hard to feel native. -You can even use Git submodules (though they'll look strange to Perforce users), and merge branches (this will be recorded as an integration on the Perforce side). - -If you can't convince the administrator of your server to set up Git Fusion, there is still a way to use these tools together. - -[[_git_p4_client]] -===== Git-p4 - -(((git commands, p4))) -Git-p4 is a two-way bridge between Git and Perforce. -It runs entirely inside your Git repository, so you won't need any kind of access to the Perforce server (other than user credentials, of course). -Git-p4 isn't as flexible or complete a solution as Git Fusion, but it does allow you to do most of what you'd want to do without being invasive to the server environment. - -[NOTE] -====== -You'll need the `p4` tool somewhere in your `PATH` to work with git-p4. -As of this writing, it is freely available at https://www.perforce.com/downloads/helix-command-line-client-p4[^]. -====== - -====== Setting Up - -For example purposes, we'll be running the Perforce server from the Git Fusion OVA as shown above, but we'll bypass the Git Fusion server and go directly to the Perforce version control. - -In order to use the `p4` command-line client (which git-p4 depends on), you'll need to set a couple of environment variables: - -[source,console] ----- -$ export P4PORT=10.0.1.254:1666 -$ export P4USER=john ----- - -====== Getting Started - -As with anything in Git, the first command is to clone: - -[source,console] ----- -$ git p4 clone //depot/www/live www-shallow -Importing from //depot/www/live into www-shallow -Initialized empty Git repository in /private/tmp/www-shallow/.git/ -Doing initial import of //depot/www/live/ from revision #head into refs/remotes/p4/master ----- - -This creates what in Git terms is a "`shallow`" clone; only the very latest Perforce revision is imported into Git; remember, Perforce isn't designed to give every revision to every user. -This is enough to use Git as a Perforce client, but for other purposes it's not enough. - -Once it's finished, we have a fully-functional Git repository: - -[source,console] ----- -$ cd myproject -$ git log --oneline --all --graph --decorate -* 70eaf78 (HEAD, p4/master, p4/HEAD, master) Initial import of //depot/www/live/ from the state at revision #head ----- - -Note how there's a "`p4`" remote for the Perforce server, but everything else looks like a standard clone. -Actually, that's a bit misleading; there isn't actually a remote there. - -[source,console] ----- -$ git remote -v ----- - -No remotes exist in this repository at all. -Git-p4 has created some refs to represent the state of the server, and they look like remote refs to `git log`, but they're not managed by Git itself, and you can't push to them. - -====== Workflow - -Okay, let's do some work. -Let's assume you've made some progress on a very important feature, and you're ready to show it to the rest of your team. - -[source,console] ----- -$ git log --oneline --all --graph --decorate -* 018467c (HEAD, master) Change page title -* c0fb617 Update link -* 70eaf78 (p4/master, p4/HEAD) Initial import of //depot/www/live/ from the state at revision #head ----- - -We've made two new commits that we're ready to submit to the Perforce server. -Let's check if anyone else was working today: - -[source,console] ----- -$ git p4 sync -git p4 sync -Performing incremental import into refs/remotes/p4/master git branch -Depot paths: //depot/www/live/ -Import destination: refs/remotes/p4/master -Importing revision 12142 (100%) -$ git log --oneline --all --graph --decorate -* 75cd059 (p4/master, p4/HEAD) Update copyright -| * 018467c (HEAD, master) Change page title -| * c0fb617 Update link -|/ -* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head ----- - -Looks like they were, and `master` and `p4/master` have diverged. -Perforce's branching system is _nothing_ like Git's, so submitting merge commits doesn't make any sense. -Git-p4 recommends that you rebase your commits, and even comes with a shortcut to do so: - -[source,console] ----- -$ git p4 rebase -Performing incremental import into refs/remotes/p4/master git branch -Depot paths: //depot/www/live/ -No changes to import! -Rebasing the current branch onto remotes/p4/master -First, rewinding head to replay your work on top of it... -Applying: Update link -Applying: Change page title - index.html | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) ----- - -You can probably tell from the output, but `git p4 rebase` is a shortcut for `git p4 sync` followed by `git rebase p4/master`. -It's a bit smarter than that, especially when working with multiple branches, but this is a good approximation. - -Now our history is linear again, and we're ready to contribute our changes back to Perforce. -The `git p4 submit` command will try to create a new Perforce revision for every Git commit between `p4/master` and `master`. -Running it drops us into our favorite editor, and the contents of the file look something like this: - -[source,console] ----- -# A Perforce Change Specification. -# -# Change: The change number. 'new' on a new changelist. -# Date: The date this specification was last modified. -# Client: The client on which the changelist was created. Read-only. -# User: The user who created the changelist. -# Status: Either 'pending' or 'submitted'. Read-only. -# Type: Either 'public' or 'restricted'. Default is 'public'. -# Description: Comments about the changelist. Required. -# Jobs: What opened jobs are to be closed by this changelist. -# You may delete jobs from this list. (New changelists only.) -# Files: What opened files from the default changelist are to be added -# to this changelist. You may delete files from this list. -# (New changelists only.) - -Change: new - -Client: john_bens-mbp_8487 - -User: john - -Status: new - -Description: - Update link - -Files: - //depot/www/live/index.html # edit - - -######## git author ben@straub.cc does not match your p4 account. -######## Use option --preserve-user to modify authorship. -######## Variable git-p4.skipUserNameCheck hides this message. -######## everything below this line is just the diff ####### ---- //depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000 -+++ /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000 -@@ -60,7 +60,7 @@ - - - Source and documentation for -- -+ - Jam/MR, - a software build tool. - ----- - -This is mostly the same content you'd see by running `p4 submit`, except the stuff at the end which git-p4 has helpfully included. -Git-p4 tries to honor your Git and Perforce settings individually when it has to provide a name for a commit or changeset, but in some cases you want to override it. -For example, if the Git commit you're importing was written by a contributor who doesn't have a Perforce user account, you may still want the resulting changeset to look like they wrote it (and not you). - -Git-p4 has helpfully imported the message from the Git commit as the content for this Perforce changeset, so all we have to do is save and quit, twice (once for each commit). -The resulting shell output will look something like this: - -[source,console] ----- -$ git p4 submit -Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/ -Synchronizing p4 checkout... -... - file(s) up-to-date. -Applying dbac45b Update link -//depot/www/live/index.html#4 - opened for edit -Change 12143 created with 1 open file(s). -Submitting change 12143. -Locking 1 files ... -edit //depot/www/live/index.html#5 -Change 12143 submitted. -Applying 905ec6a Change page title -//depot/www/live/index.html#5 - opened for edit -Change 12144 created with 1 open file(s). -Submitting change 12144. -Locking 1 files ... -edit //depot/www/live/index.html#6 -Change 12144 submitted. -All commits applied! -Performing incremental import into refs/remotes/p4/master git branch -Depot paths: //depot/www/live/ -Import destination: refs/remotes/p4/master -Importing revision 12144 (100%) -Rebasing the current branch onto remotes/p4/master -First, rewinding head to replay your work on top of it... -$ git log --oneline --all --graph --decorate -* 775a46f (HEAD, p4/master, p4/HEAD, master) Change page title -* 05f1ade Update link -* 75cd059 Update copyright -* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head ----- - -The result is as though we just did a `git push`, which is the closest analogy to what actually did happen. - -Note that during this process every Git commit is turned into a Perforce changeset; if you want to squash them down into a single changeset, you can do that with an interactive rebase before running `git p4 submit`. -Also note that the SHA-1 hashes of all the commits that were submitted as changesets have changed; this is because git-p4 adds a line to the end of each commit it converts: - -[source,console] ----- -$ git log -1 -commit 775a46f630d8b46535fc9983cf3ebe6b9aa53145 -Author: John Doe -Date: Sun Aug 31 10:31:44 2014 -0800 - - Change page title - - [git-p4: depot-paths = "//depot/www/live/": change = 12144] ----- - -What happens if you try to submit a merge commit? -Let's give it a try. -Here's the situation we've gotten ourselves into: - -[source,console] ----- -$ git log --oneline --all --graph --decorate -* 3be6fd8 (HEAD, master) Correct email address -* 1dcbf21 Merge remote-tracking branch 'p4/master' -|\ -| * c4689fc (p4/master, p4/HEAD) Grammar fix -* | cbacd0a Table borders: yes please -* | b4959b6 Trademark -|/ -* 775a46f Change page title -* 05f1ade Update link -* 75cd059 Update copyright -* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head ----- - -The Git and Perforce history diverge after `775a46f`. -The Git side has two commits, then a merge commit with the Perforce head, then another commit. -We're going to try to submit these on top of a single changeset on the Perforce side. -Let's see what would happen if we tried to submit now: - -[source,console] ----- -$ git p4 submit -n -Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/ -Would synchronize p4 checkout in /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/ -Would apply - b4959b6 Trademark - cbacd0a Table borders: yes please - 3be6fd8 Correct email address ----- - -The `-n` flag is short for `--dry-run`, which tries to report what would happen if the submit command were run for real. -In this case, it looks like we'd be creating three Perforce changesets, which correspond to the three non-merge commits that don't yet exist on the Perforce server. -That sounds like exactly what we want, let's see how it turns out: - -[source,console] ----- -$ git p4 submit -[…] -$ git log --oneline --all --graph --decorate -* dadbd89 (HEAD, p4/master, p4/HEAD, master) Correct email address -* 1b79a80 Table borders: yes please -* 0097235 Trademark -* c4689fc Grammar fix -* 775a46f Change page title -* 05f1ade Update link -* 75cd059 Update copyright -* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head ----- - -Our history became linear, just as though we had rebased before submitting (which is in fact exactly what happened). -This means you can be free to create, work on, throw away, and merge branches on the Git side without fear that your history will somehow become incompatible with Perforce. -If you can rebase it, you can contribute it to a Perforce server. - -[[_git_p4_branches]] -====== Branching - -If your Perforce project has multiple branches, you're not out of luck; git-p4 can handle that in a way that makes it feel like Git. -Let's say your Perforce depot is laid out like this: - -[source] ----- -//depot - └── project - ├── main - └── dev ----- - -And let's say you have a `dev` branch, which has a view spec that looks like this: - -[source] ----- -//depot/project/main/... //depot/project/dev/... ----- - -Git-p4 can automatically detect that situation and do the right thing: - -[source,console] ----- -$ git p4 clone --detect-branches //depot/project@all -Importing from //depot/project@all into project -Initialized empty Git repository in /private/tmp/project/.git/ -Importing revision 20 (50%) - Importing new branch project/dev - - Resuming with change 20 -Importing revision 22 (100%) -Updated branches: main dev -$ cd project; git log --oneline --all --graph --decorate -* eae77ae (HEAD, p4/master, p4/HEAD, master) main -| * 10d55fb (p4/project/dev) dev -| * a43cfae Populate //depot/project/main/... //depot/project/dev/.... -|/ -* 2b83451 Project init ----- - -Note the "`@all`" specifier in the depot path; that tells git-p4 to clone not just the latest changeset for that subtree, but all changesets that have ever touched those paths. -This is closer to Git's concept of a clone, but if you're working on a project with a long history, it could take a while. - -The `--detect-branches` flag tells git-p4 to use Perforce's branch specs to map the branches to Git refs. -If these mappings aren't present on the Perforce server (which is a perfectly valid way to use Perforce), you can tell git-p4 what the branch mappings are, and you get the same result: - -[source,console] ----- -$ git init project -Initialized empty Git repository in /tmp/project/.git/ -$ cd project -$ git config git-p4.branchList main:dev -$ git clone --detect-branches //depot/project@all . ----- - -Setting the `git-p4.branchList` configuration variable to `main:dev` tells git-p4 that "`main`" and "`dev`" are both branches, and the second one is a child of the first one. - -If we now `git checkout -b dev p4/project/dev` and make some commits, git-p4 is smart enough to target the right branch when we do `git p4 submit`. -Unfortunately, git-p4 can't mix shallow clones and multiple branches; if you have a huge project and want to work on more than one branch, you'll have to `git p4 clone` once for each branch you want to submit to. - -For creating or integrating branches, you'll have to use a Perforce client. -Git-p4 can only sync and submit to existing branches, and it can only do it one linear changeset at a time. -If you merge two branches in Git and try to submit the new changeset, all that will be recorded is a bunch of file changes; the metadata about which branches are involved in the integration will be lost. - -===== Git and Perforce Summary - -Git-p4 makes it possible to use a Git workflow with a Perforce server, and it's pretty good at it. -However, it's important to remember that Perforce is in charge of the source, and you're only using Git to work locally. -Just be really careful about sharing Git commits; if you have a remote that other people use, don't push any commits that haven't already been submitted to the Perforce server. - -If you want to freely mix the use of Perforce and Git as clients for source control, and you can convince the server administrator to install it, Git Fusion makes using Git a first-class version-control client for a Perforce server. diff --git a/book/ch09/client-svn.asc b/book/ch09/client-svn.asc deleted file mode 100644 index de330a1..0000000 --- a/book/ch09/client-svn.asc +++ /dev/null @@ -1,490 +0,0 @@ -[[_git_svn]] -==== Git and Subversion - -(((Subversion)))(((Interoperation with other VCSs, Subversion))) -A large fraction of open source development projects and a good number of corporate projects use Subversion to manage their source code. -It's been around for more than a decade, and for most of that time was the _de facto_ VCS choice for open-source projects. -It's also very similar in many ways to CVS, which was the big boy of the source-control world before that. - -(((git commands, svn)))(((git-svn))) -One of Git's great features is a bidirectional bridge to Subversion called `git svn`. -This tool allows you to use Git as a valid client to a Subversion server, so you can use all the local features of Git and then push to a Subversion server as if you were using Subversion locally. -This means you can do local branching and merging, use the staging area, use rebasing and cherry-picking, and so on, while your collaborators continue to work in their dark and ancient ways. -It's a good way to sneak Git into the corporate environment and help your fellow developers become more efficient while you lobby to get the infrastructure changed to support Git fully. -The Subversion bridge is the gateway drug to the DVCS world. - -===== `git svn` - -The base command in Git for all the Subversion bridging commands is `git svn`. -It takes quite a few commands, so we'll show the most common while going through a few simple workflows. - -It's important to note that when you're using `git svn`, you're interacting with Subversion, which is a system that works very differently from Git. -Although you *can* do local branching and merging, it's generally best to keep your history as linear as possible by rebasing your work, and avoiding doing things like simultaneously interacting with a Git remote repository. - -Don't rewrite your history and try to push again, and don't push to a parallel Git repository to collaborate with fellow Git developers at the same time. -Subversion can have only a single linear history, and confusing it is very easy. -If you're working with a team, and some are using SVN and others are using Git, make sure everyone is using the SVN server to collaborate – doing so will make your life easier. - -===== Setting Up - -To demonstrate this functionality, you need a typical SVN repository that you have write access to. -If you want to copy these examples, you'll have to make a writeable copy of an SVN test repository. -In order to do that easily, you can use a tool called `svnsync` that comes with Subversion. - -To follow along, you first need to create a new local Subversion repository: - -[source,console] ----- -$ mkdir /tmp/test-svn -$ svnadmin create /tmp/test-svn ----- - -Then, enable all users to change revprops – the easy way is to add a `pre-revprop-change` script that always exits 0: - -[source,console] ----- -$ cat /tmp/test-svn/hooks/pre-revprop-change -#!/bin/sh -exit 0; -$ chmod +x /tmp/test-svn/hooks/pre-revprop-change ----- - -You can now sync this project to your local machine by calling `svnsync init` with the to and from repositories. - -[source,console] ----- -$ svnsync init file:///tmp/test-svn \ - http://your-svn-server.example.org/svn/ ----- - -This sets up the properties to run the sync. -You can then clone the code by running: - -[source,console] ----- -$ svnsync sync file:///tmp/test-svn -Committed revision 1. -Copied properties for revision 1. -Transmitting file data .............................[...] -Committed revision 2. -Copied properties for revision 2. -[…] ----- - -Although this operation may take only a few minutes, if you try to copy the original repository to another remote repository instead of a local one, the process will take nearly an hour, even though there are fewer than 100 commits. -Subversion has to clone one revision at a time and then push it back into another repository – it's ridiculously inefficient, but it's the only easy way to do this. - -===== Getting Started - -Now that you have a Subversion repository to which you have write access, you can go through a typical workflow. -You'll start with the `git svn clone` command, which imports an entire Subversion repository into a local Git repository. -Remember that if you're importing from a real hosted Subversion repository, you should replace the `\file:///tmp/test-svn` here with the URL of your Subversion repository: - -[source,console] ----- -$ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags -Initialized empty Git repository in /private/tmp/progit/test-svn/.git/ -r1 = dcbfb5891860124cc2e8cc616cded42624897125 (refs/remotes/origin/trunk) - A m4/acx_pthread.m4 - A m4/stl_hash.m4 - A java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java - A java/src/test/java/com/google/protobuf/WireFormatTest.java -… -r75 = 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae (refs/remotes/origin/trunk) -Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/my-calc-branch, 75 -Found branch parent: (refs/remotes/origin/my-calc-branch) 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae -Following parent with do_switch -Successfully followed parent -r76 = 0fb585761df569eaecd8146c71e58d70147460a2 (refs/remotes/origin/my-calc-branch) -Checked out HEAD: - file:///tmp/test-svn/trunk r75 ----- - -This runs the equivalent of two commands – `git svn init` followed by `git svn fetch` – on the URL you provide. -This can take a while. -If, for example, the test project has only about 75 commits and the codebase isn't that big, Git nevertheless must check out each version, one at a time, and commit it individually. -For a project with hundreds or thousands of commits, this can literally take hours or even days to finish. - -The `-T trunk -b branches -t tags` part tells Git that this Subversion repository follows the basic branching and tagging conventions. -If you name your trunk, branches, or tags differently, you can change these options. -Because this is so common, you can replace this entire part with `-s`, which means standard layout and implies all those options. -The following command is equivalent: - -[source,console] ----- -$ git svn clone file:///tmp/test-svn -s ----- - -At this point, you should have a valid Git repository that has imported your branches and tags: - -[source,console] ----- -$ git branch -a -* master - remotes/origin/my-calc-branch - remotes/origin/tags/2.0.2 - remotes/origin/tags/release-2.0.1 - remotes/origin/tags/release-2.0.2 - remotes/origin/tags/release-2.0.2rc1 - remotes/origin/trunk ----- - -Note how this tool manages Subversion tags as remote refs. -(((git commands, show-ref))) -Let's take a closer look with the Git plumbing command `show-ref`: - -[source,console] ----- -$ git show-ref -556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/heads/master -0fb585761df569eaecd8146c71e58d70147460a2 refs/remotes/origin/my-calc-branch -bfd2d79303166789fc73af4046651a4b35c12f0b refs/remotes/origin/tags/2.0.2 -285c2b2e36e467dd4d91c8e3c0c0e1750b3fe8ca refs/remotes/origin/tags/release-2.0.1 -cbda99cb45d9abcb9793db1d4f70ae562a969f1e refs/remotes/origin/tags/release-2.0.2 -a9f074aa89e826d6f9d30808ce5ae3ffe711feda refs/remotes/origin/tags/release-2.0.2rc1 -556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/remotes/origin/trunk ----- - -Git doesn't do this when it clones from a Git server; here's what a repository with tags looks like after a fresh clone: - -[source,console] ----- -$ git show-ref -c3dcbe8488c6240392e8a5d7553bbffcb0f94ef0 refs/remotes/origin/master -32ef1d1c7cc8c603ab78416262cc421b80a8c2df refs/remotes/origin/branch-1 -75f703a3580a9b81ead89fe1138e6da858c5ba18 refs/remotes/origin/branch-2 -23f8588dde934e8f33c263c6d8359b2ae095f863 refs/tags/v0.1.0 -7064938bd5e7ef47bfd79a685a62c1e2649e2ce7 refs/tags/v0.2.0 -6dcb09b5b57875f334f61aebed695e2e4193db5e refs/tags/v1.0.0 ----- - -Git fetches the tags directly into `refs/tags`, rather than treating them as remote branches. - -===== Committing Back to Subversion - -Now that you have a working directory, you can do some work on the project and push your commits back upstream, using Git effectively as an SVN client. -If you edit one of the files and commit it, you have a commit that exists in Git locally that doesn't exist on the Subversion server: - -[source,console] ----- -$ git commit -am 'Adding git-svn instructions to the README' -[master 4af61fd] Adding git-svn instructions to the README - 1 file changed, 5 insertions(+) ----- - -Next, you need to push your change upstream. -Notice how this changes the way you work with Subversion – you can do several commits offline and then push them all at once to the Subversion server. -To push to a Subversion server, you run the `git svn dcommit` command: - -[source,console] ----- -$ git svn dcommit -Committing to file:///tmp/test-svn/trunk ... - M README.txt -Committed r77 - M README.txt -r77 = 95e0222ba6399739834380eb10afcd73e0670bc5 (refs/remotes/origin/trunk) -No changes between 4af61fd05045e07598c553167e0f31c84fd6ffe1 and refs/remotes/origin/trunk -Resetting to the latest refs/remotes/origin/trunk ----- - -This takes all the commits you've made on top of the Subversion server code, does a Subversion commit for each, and then rewrites your local Git commit to include a unique identifier. -This is important because it means that all the SHA-1 checksums for your commits change. -Partly for this reason, working with Git-based remote versions of your projects concurrently with a Subversion server isn't a good idea. -If you look at the last commit, you can see the new `git-svn-id` that was added: - -[source,console] ----- -$ git log -1 -commit 95e0222ba6399739834380eb10afcd73e0670bc5 -Author: ben -Date: Thu Jul 24 03:08:36 2014 +0000 - - Adding git-svn instructions to the README - - git-svn-id: file:///tmp/test-svn/trunk@77 0b684db3-b064-4277-89d1-21af03df0a68 ----- - -Notice that the SHA-1 checksum that originally started with `4af61fd` when you committed now begins with `95e0222`. -If you want to push to both a Git server and a Subversion server, you have to push (`dcommit`) to the Subversion server first, because that action changes your commit data. - -===== Pulling in New Changes - -If you're working with other developers, then at some point one of you will push, and then the other one will try to push a change that conflicts. -That change will be rejected until you merge in their work. -In `git svn`, it looks like this: - -[source,console] ----- -$ git svn dcommit -Committing to file:///tmp/test-svn/trunk ... - -ERROR from SVN: -Transaction is out of date: File '/trunk/README.txt' is out of date -W: d5837c4b461b7c0e018b49d12398769d2bfc240a and refs/remotes/origin/trunk differ, using rebase: -:100644 100644 f414c433af0fd6734428cf9d2a9fd8ba00ada145 c80b6127dd04f5fcda218730ddf3a2da4eb39138 M README.txt -Current branch master is up to date. -ERROR: Not all changes have been committed into SVN, however the committed -ones (if any) seem to be successfully integrated into the working tree. -Please see the above messages for details. ----- - -To resolve this situation, you can run `git svn rebase`, which pulls down any changes on the server that you don't have yet and rebases any work you have on top of what is on the server: - -[source,console] ----- -$ git svn rebase -Committing to file:///tmp/test-svn/trunk ... - -ERROR from SVN: -Transaction is out of date: File '/trunk/README.txt' is out of date -W: eaa029d99f87c5c822c5c29039d19111ff32ef46 and refs/remotes/origin/trunk differ, using rebase: -:100644 100644 65536c6e30d263495c17d781962cfff12422693a b34372b25ccf4945fe5658fa381b075045e7702a M README.txt -First, rewinding head to replay your work on top of it... -Applying: update foo -Using index info to reconstruct a base tree... -M README.txt -Falling back to patching base and 3-way merge... -Auto-merging README.txt -ERROR: Not all changes have been committed into SVN, however the committed -ones (if any) seem to be successfully integrated into the working tree. -Please see the above messages for details. ----- - -Now, all your work is on top of what is on the Subversion server, so you can successfully `dcommit`: - -[source,console] ----- -$ git svn dcommit -Committing to file:///tmp/test-svn/trunk ... - M README.txt -Committed r85 - M README.txt -r85 = 9c29704cc0bbbed7bd58160cfb66cb9191835cd8 (refs/remotes/origin/trunk) -No changes between 5762f56732a958d6cfda681b661d2a239cc53ef5 and refs/remotes/origin/trunk -Resetting to the latest refs/remotes/origin/trunk ----- - -Note that unlike Git, which requires you to merge upstream work you don't yet have locally before you can push, `git svn` makes you do that only if the changes conflict (much like how Subversion works). -If someone else pushes a change to one file and then you push a change to another file, your `dcommit` will work fine: - -[source,console] ----- -$ git svn dcommit -Committing to file:///tmp/test-svn/trunk ... - M configure.ac -Committed r87 - M autogen.sh -r86 = d8450bab8a77228a644b7dc0e95977ffc61adff7 (refs/remotes/origin/trunk) - M configure.ac -r87 = f3653ea40cb4e26b6281cec102e35dcba1fe17c4 (refs/remotes/origin/trunk) -W: a0253d06732169107aa020390d9fefd2b1d92806 and refs/remotes/origin/trunk differ, using rebase: -:100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18 e757b59a9439312d80d5d43bb65d4a7d0389ed6d M autogen.sh -First, rewinding head to replay your work on top of it... ----- - -This is important to remember, because the outcome is a project state that didn't exist on either of your computers when you pushed. -If the changes are incompatible but don't conflict, you may get issues that are difficult to diagnose. -This is different than using a Git server – in Git, you can fully test the state on your client system before publishing it, whereas in SVN, you can't ever be certain that the states immediately before commit and after commit are identical. - -You should also run this command to pull in changes from the Subversion server, even if you're not ready to commit yourself. -You can run `git svn fetch` to grab the new data, but `git svn rebase` does the fetch and then updates your local commits. - -[source,console] ----- -$ git svn rebase - M autogen.sh -r88 = c9c5f83c64bd755368784b444bc7a0216cc1e17b (refs/remotes/origin/trunk) -First, rewinding head to replay your work on top of it... -Fast-forwarded master to refs/remotes/origin/trunk. ----- - -Running `git svn rebase` every once in a while makes sure your code is always up to date. -You need to be sure your working directory is clean when you run this, though. -If you have local changes, you must either stash your work or temporarily commit it before running `git svn rebase` – otherwise, the command will stop if it sees that the rebase will result in a merge conflict. - -===== Git Branching Issues - -When you've become comfortable with a Git workflow, you'll likely create topic branches, do work on them, and then merge them in. -If you're pushing to a Subversion server via `git svn`, you may want to rebase your work onto a single branch each time instead of merging branches together. -The reason to prefer rebasing is that Subversion has a linear history and doesn't deal with merges like Git does, so `git svn` follows only the first parent when converting the snapshots into Subversion commits. - -Suppose your history looks like the following: you created an `experiment` branch, did two commits, and then merged them back into `master`. -When you `dcommit`, you see output like this: - -[source,console] ----- -$ git svn dcommit -Committing to file:///tmp/test-svn/trunk ... - M CHANGES.txt -Committed r89 - M CHANGES.txt -r89 = 89d492c884ea7c834353563d5d913c6adf933981 (refs/remotes/origin/trunk) - M COPYING.txt - M INSTALL.txt -Committed r90 - M INSTALL.txt - M COPYING.txt -r90 = cb522197870e61467473391799148f6721bcf9a0 (refs/remotes/origin/trunk) -No changes between 71af502c214ba13123992338569f4669877f55fd and refs/remotes/origin/trunk -Resetting to the latest refs/remotes/origin/trunk ----- - -Running `dcommit` on a branch with merged history works fine, except that when you look at your Git project history, it hasn't rewritten either of the commits you made on the `experiment` branch – instead, all those changes appear in the SVN version of the single merge commit. - -When someone else clones that work, all they see is the merge commit with all the work squashed into it, as though you ran `git merge --squash`; they don't see the commit data about where it came from or when it was committed. - -===== Subversion Branching - -Branching in Subversion isn't the same as branching in Git; if you can avoid using it much, that's probably best. -However, you can create and commit to branches in Subversion using `git svn`. - -===== Creating a New SVN Branch - -To create a new branch in Subversion, you run `git svn branch [new-branch]`: - -[source,console] ----- -$ git svn branch opera -Copying file:///tmp/test-svn/trunk at r90 to file:///tmp/test-svn/branches/opera... -Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/opera, 90 -Found branch parent: (refs/remotes/origin/opera) cb522197870e61467473391799148f6721bcf9a0 -Following parent with do_switch -Successfully followed parent -r91 = f1b64a3855d3c8dd84ee0ef10fa89d27f1584302 (refs/remotes/origin/opera) ----- - -This does the equivalent of the `svn copy trunk branches/opera` command in Subversion and operates on the Subversion server. -It's important to note that it doesn't check you out into that branch; if you commit at this point, that commit will go to `trunk` on the server, not `opera`. - -===== Switching Active Branches - -Git figures out what branch your dcommits go to by looking for the tip of any of your Subversion branches in your history – you should have only one, and it should be the last one with a `git-svn-id` in your current branch history. - -If you want to work on more than one branch simultaneously, you can set up local branches to `dcommit` to specific Subversion branches by starting them at the imported Subversion commit for that branch. -If you want an `opera` branch that you can work on separately, you can run: - -[source,console] ----- -$ git branch opera remotes/origin/opera ----- - -Now, if you want to merge your `opera` branch into `trunk` (your `master` branch), you can do so with a normal `git merge`. -But you need to provide a descriptive commit message (via `-m`), or the merge will say "`Merge branch opera`" instead of something useful. - -Remember that although you're using `git merge` to do this operation, and the merge likely will be much easier than it would be in Subversion (because Git will automatically detect the appropriate merge base for you), this isn't a normal Git merge commit. -You have to push this data back to a Subversion server that can't handle a commit that tracks more than one parent; so, after you push it up, it will look like a single commit that squashed in all the work of another branch under a single commit. -After you merge one branch into another, you can't easily go back and continue working on that branch, as you normally can in Git. -The `dcommit` command that you run erases any information that says what branch was merged in, so subsequent merge-base calculations will be wrong – the `dcommit` makes your `git merge` result look like you ran `git merge --squash`. -Unfortunately, there's no good way to avoid this situation – Subversion can't store this information, so you'll always be crippled by its limitations while you're using it as your server. -To avoid issues, you should delete the local branch (in this case, `opera`) after you merge it into trunk. - -===== Subversion Commands - -The `git svn` toolset provides a number of commands to help ease the transition to Git by providing some functionality that's similar to what you had in Subversion. -Here are a few commands that give you what Subversion used to. - -====== SVN Style History - -If you're used to Subversion and want to see your history in SVN output style, you can run `git svn log` to view your commit history in SVN formatting: - -[source,console] ----- -$ git svn log ------------------------------------------------------------------------- -r87 | schacon | 2014-05-02 16:07:37 -0700 (Sat, 02 May 2014) | 2 lines - -autogen change - ------------------------------------------------------------------------- -r86 | schacon | 2014-05-02 16:00:21 -0700 (Sat, 02 May 2014) | 2 lines - -Merge branch 'experiment' - ------------------------------------------------------------------------- -r85 | schacon | 2014-05-02 16:00:09 -0700 (Sat, 02 May 2014) | 2 lines - -updated the changelog ----- - -You should know two important things about `git svn log`. -First, it works offline, unlike the real `svn log` command, which asks the Subversion server for the data. -Second, it only shows you commits that have been committed up to the Subversion server. -Local Git commits that you haven't dcommited don't show up; neither do commits that people have made to the Subversion server in the meantime. -It's more like the last known state of the commits on the Subversion server. - -====== SVN Annotation - -Much as the `git svn log` command simulates the `svn log` command offline, you can get the equivalent of `svn annotate` by running `git svn blame [FILE]`. -The output looks like this: - -[source,console] ----- -$ git svn blame README.txt - 2 temporal Protocol Buffers - Google's data interchange format - 2 temporal Copyright 2008 Google Inc. - 2 temporal http://code.google.com/apis/protocolbuffers/ - 2 temporal -22 temporal C++ Installation - Unix -22 temporal ======================= - 2 temporal -79 schacon Committing in git-svn. -78 schacon - 2 temporal To build and install the C++ Protocol Buffer runtime and the Protocol - 2 temporal Buffer compiler (protoc) execute the following: - 2 temporal ----- - -Again, it doesn't show commits that you did locally in Git or that have been pushed to Subversion in the meantime. - -====== SVN Server Information - -You can also get the same sort of information that `svn info` gives you by running `git svn info`: - -[source,console] ----- -$ git svn info -Path: . -URL: https://schacon-test.googlecode.com/svn/trunk -Repository Root: https://schacon-test.googlecode.com/svn -Repository UUID: 4c93b258-373f-11de-be05-5f7a86268029 -Revision: 87 -Node Kind: directory -Schedule: normal -Last Changed Author: schacon -Last Changed Rev: 87 -Last Changed Date: 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009) ----- - -This is like `blame` and `log` in that it runs offline and is up to date only as of the last time you communicated with the Subversion server. - -====== Ignoring What Subversion Ignores - -If you clone a Subversion repository that has `svn:ignore` properties set anywhere, you'll likely want to set corresponding `.gitignore` files so you don't accidentally commit files that you shouldn't. -`git svn` has two commands to help with this issue. -The first is `git svn create-ignore`, which automatically creates corresponding `.gitignore` files for you so your next commit can include them. - -The second command is `git svn show-ignore`, which prints to stdout the lines you need to put in a `.gitignore` file so you can redirect the output into your project exclude file: - -[source,console] ----- -$ git svn show-ignore > .git/info/exclude ----- - -That way, you don't litter the project with `.gitignore` files. -This is a good option if you're the only Git user on a Subversion team, and your teammates don't want `.gitignore` files in the project. - -===== Git-Svn Summary - -The `git svn` tools are useful if you're stuck with a Subversion server, or are otherwise in a development environment that necessitates running a Subversion server. -You should consider it crippled Git, however, or you'll hit issues in translation that may confuse you and your collaborators. -To stay out of trouble, try to follow these guidelines: - -* Keep a linear Git history that doesn't contain merge commits made by `git merge`. - Rebase any work you do outside of your mainline branch back onto it; don't merge it in. -* Don't set up and collaborate on a separate Git server. - Possibly have one to speed up clones for new developers, but don't push anything to it that doesn't have a `git-svn-id` entry. - You may even want to add a `pre-receive` hook that checks each commit message for a `git-svn-id` and rejects pushes that contain commits without it. - -If you follow those guidelines, working with a Subversion server can be more bearable. -However, if it's possible to move to a real Git server, doing so can gain your team a lot more. diff --git a/book/ch09/import-custom.asc b/book/ch09/import-custom.asc deleted file mode 100644 index 726f3d8..0000000 --- a/book/ch09/import-custom.asc +++ /dev/null @@ -1,371 +0,0 @@ -[[_custom_importer]] -==== A Custom Importer - -(((git commands, fast-import))) -(((Importing, from others))) -If your system isn't one of the above, you should look for an importer online – quality importers are available for many other systems, including CVS, Clear Case, Visual Source Safe, even a directory of archives. -If none of these tools works for you, you have a more obscure tool, or you otherwise need a more custom importing process, you should use `git fast-import`. -This command reads simple instructions from stdin to write specific Git data. -It's much easier to create Git objects this way than to run the raw Git commands or try to write the raw objects (see <> for more information). -This way, you can write an import script that reads the necessary information out of the system you're importing from and prints straightforward instructions to stdout. -You can then run this program and pipe its output through `git fast-import`. - -To quickly demonstrate, you'll write a simple importer. -Suppose you work in `current`, you back up your project by occasionally copying the directory into a time-stamped `back_YYYY_MM_DD` backup directory, and you want to import this into Git. -Your directory structure looks like this: - -[source,console] ----- -$ ls /opt/import_from -back_2014_01_02 -back_2014_01_04 -back_2014_01_14 -back_2014_02_03 -current ----- - -In order to import a Git directory, you need to review how Git stores its data. -As you may remember, Git is fundamentally a linked list of commit objects that point to a snapshot of content. -All you have to do is tell `fast-import` what the content snapshots are, what commit data points to them, and the order they go in. -Your strategy will be to go through the snapshots one at a time and create commits with the contents of each directory, linking each commit back to the previous one. - -As we did in <>, we'll write this in Ruby, because it's what we generally work with and it tends to be easy to read. -You can write this example pretty easily in anything you're familiar with – it just needs to print the appropriate information to `stdout`. -And, if you are running on Windows, this means you'll need to take special care to not introduce carriage returns at the end your lines – `git fast-import` is very particular about just wanting line feeds (LF) not the carriage return line feeds (CRLF) that Windows uses. - -To begin, you'll change into the target directory and identify every subdirectory, each of which is a snapshot that you want to import as a commit. -You'll change into each subdirectory and print the commands necessary to export it. -Your basic main loop looks like this: - -[source,ruby] ----- -last_mark = nil - -# loop through the directories -Dir.chdir(ARGV[0]) do - Dir.glob("*").each do |dir| - next if File.file?(dir) - - # move into the target directory - Dir.chdir(dir) do - last_mark = print_export(dir, last_mark) - end - end -end ----- - -You run `print_export` inside each directory, which takes the manifest and mark of the previous snapshot and returns the manifest and mark of this one; that way, you can link them properly. -"`Mark`" is the `fast-import` term for an identifier you give to a commit; as you create commits, you give each one a mark that you can use to link to it from other commits. -So, the first thing to do in your `print_export` method is generate a mark from the directory name: - -[source,ruby] ----- -mark = convert_dir_to_mark(dir) ----- - -You'll do this by creating an array of directories and using the index value as the mark, because a mark must be an integer. -Your method looks like this: - -[source,ruby] ----- -$marks = [] -def convert_dir_to_mark(dir) - if !$marks.include?(dir) - $marks << dir - end - ($marks.index(dir) + 1).to_s -end ----- - -Now that you have an integer representation of your commit, you need a date for the commit metadata. -Because the date is expressed in the name of the directory, you'll parse it out. -The next line in your `print_export` file is: - -[source,ruby] ----- -date = convert_dir_to_date(dir) ----- - -where `convert_dir_to_date` is defined as: - -[source,ruby] ----- -def convert_dir_to_date(dir) - if dir == 'current' - return Time.now().to_i - else - dir = dir.gsub('back_', '') - (year, month, day) = dir.split('_') - return Time.local(year, month, day).to_i - end -end ----- - -That returns an integer value for the date of each directory. -The last piece of meta-information you need for each commit is the committer data, which you hardcode in a global variable: - -[source,ruby] ----- -$author = 'John Doe ' ----- - -Now you're ready to begin printing out the commit data for your importer. -The initial information states that you're defining a commit object and what branch it's on, followed by the mark you've generated, the committer information and commit message, and then the previous commit, if any. -The code looks like this: - -[source,ruby] ----- -# print the import information -puts 'commit refs/heads/master' -puts 'mark :' + mark -puts "committer #{$author} #{date} -0700" -export_data('imported from ' + dir) -puts 'from :' + last_mark if last_mark ----- - -You hardcode the time zone (-0700) because doing so is easy. -If you're importing from another system, you must specify the time zone as an offset. -The commit message must be expressed in a special format: - -[source] ----- -data (size)\n(contents) ----- - -The format consists of the word data, the size of the data to be read, a newline, and finally the data. -Because you need to use the same format to specify the file contents later, you create a helper method, `export_data`: - -[source,ruby] ----- -def export_data(string) - print "data #{string.size}\n#{string}" -end ----- - -All that's left is to specify the file contents for each snapshot. -This is easy, because you have each one in a directory – you can print out the `deleteall` command followed by the contents of each file in the directory. -Git will then record each snapshot appropriately: - -[source,ruby] ----- -puts 'deleteall' -Dir.glob("**/*").each do |file| - next if !File.file?(file) - inline_data(file) -end ----- - -Note: Because many systems think of their revisions as changes from one commit to another, fast-import can also take commands with each commit to specify which files have been added, removed, or modified and what the new contents are. -You could calculate the differences between snapshots and provide only this data, but doing so is more complex – you may as well give Git all the data and let it figure it out. -If this is better suited to your data, check the `fast-import` man page for details about how to provide your data in this manner. - -The format for listing the new file contents or specifying a modified file with the new contents is as follows: - -[source] ----- -M 644 inline path/to/file -data (size) -(file contents) ----- - -Here, 644 is the mode (if you have executable files, you need to detect and specify 755 instead), and inline says you'll list the contents immediately after this line. -Your `inline_data` method looks like this: - -[source,ruby] ----- -def inline_data(file, code = 'M', mode = '644') - content = File.read(file) - puts "#{code} #{mode} inline #{file}" - export_data(content) -end ----- - -You reuse the `export_data` method you defined earlier, because it's the same as the way you specified your commit message data. - -The last thing you need to do is to return the current mark so it can be passed to the next iteration: - -[source,ruby] ----- -return mark ----- - -[NOTE] -==== -If you are running on Windows you'll need to make sure that you add one extra step. -As mentioned before, Windows uses CRLF for new line characters while `git fast-import` expects only LF. -To get around this problem and make `git fast-import` happy, you need to tell ruby to use LF instead of CRLF: - -[source,ruby] ----- -$stdout.binmode ----- -==== - -That's it. -Here's the script in its entirety: - -[source,ruby] ----- -#!/usr/bin/env ruby - -$stdout.binmode -$author = "John Doe " - -$marks = [] -def convert_dir_to_mark(dir) - if !$marks.include?(dir) - $marks << dir - end - ($marks.index(dir)+1).to_s -end - -def convert_dir_to_date(dir) - if dir == 'current' - return Time.now().to_i - else - dir = dir.gsub('back_', '') - (year, month, day) = dir.split('_') - return Time.local(year, month, day).to_i - end -end - -def export_data(string) - print "data #{string.size}\n#{string}" -end - -def inline_data(file, code='M', mode='644') - content = File.read(file) - puts "#{code} #{mode} inline #{file}" - export_data(content) -end - -def print_export(dir, last_mark) - date = convert_dir_to_date(dir) - mark = convert_dir_to_mark(dir) - - puts 'commit refs/heads/master' - puts "mark :#{mark}" - puts "committer #{$author} #{date} -0700" - export_data("imported from #{dir}") - puts "from :#{last_mark}" if last_mark - - puts 'deleteall' - Dir.glob("**/*").each do |file| - next if !File.file?(file) - inline_data(file) - end - mark -end - -# Loop through the directories -last_mark = nil -Dir.chdir(ARGV[0]) do - Dir.glob("*").each do |dir| - next if File.file?(dir) - - # move into the target directory - Dir.chdir(dir) do - last_mark = print_export(dir, last_mark) - end - end -end ----- - -If you run this script, you'll get content that looks something like this: - -[source,console] ----- -$ ruby import.rb /opt/import_from -commit refs/heads/master -mark :1 -committer John Doe 1388649600 -0700 -data 29 -imported from back_2014_01_02deleteall -M 644 inline README.md -data 28 -# Hello - -This is my readme. -commit refs/heads/master -mark :2 -committer John Doe 1388822400 -0700 -data 29 -imported from back_2014_01_04from :1 -deleteall -M 644 inline main.rb -data 34 -#!/bin/env ruby - -puts "Hey there" -M 644 inline README.md -(...) ----- - -To run the importer, pipe this output through `git fast-import` while in the Git directory you want to import into. -You can create a new directory and then run `git init` in it for a starting point, and then run your script: - -[source,console] ----- -$ git init -Initialized empty Git repository in /opt/import_to/.git/ -$ ruby import.rb /opt/import_from | git fast-import -git-fast-import statistics: ---------------------------------------------------------------------- -Alloc'd objects: 5000 -Total objects: 13 ( 6 duplicates ) - blobs : 5 ( 4 duplicates 3 deltas of 5 attempts) - trees : 4 ( 1 duplicates 0 deltas of 4 attempts) - commits: 4 ( 1 duplicates 0 deltas of 0 attempts) - tags : 0 ( 0 duplicates 0 deltas of 0 attempts) -Total branches: 1 ( 1 loads ) - marks: 1024 ( 5 unique ) - atoms: 2 -Memory total: 2344 KiB - pools: 2110 KiB - objects: 234 KiB ---------------------------------------------------------------------- -pack_report: getpagesize() = 4096 -pack_report: core.packedGitWindowSize = 1073741824 -pack_report: core.packedGitLimit = 8589934592 -pack_report: pack_used_ctr = 10 -pack_report: pack_mmap_calls = 5 -pack_report: pack_open_windows = 2 / 2 -pack_report: pack_mapped = 1457 / 1457 ---------------------------------------------------------------------- ----- - -As you can see, when it completes successfully, it gives you a bunch of statistics about what it accomplished. -In this case, you imported 13 objects total for 4 commits into 1 branch. -Now, you can run `git log` to see your new history: - -[source,console] ----- -$ git log -2 -commit 3caa046d4aac682a55867132ccdfbe0d3fdee498 -Author: John Doe -Date: Tue Jul 29 19:39:04 2014 -0700 - - imported from current - -commit 4afc2b945d0d3c8cd00556fbe2e8224569dc9def -Author: John Doe -Date: Mon Feb 3 01:00:00 2014 -0700 - - imported from back_2014_02_03 ----- - -There you go – a nice, clean Git repository. -It's important to note that nothing is checked out – you don't have any files in your working directory at first. -To get them, you must reset your branch to where `master` is now: - -[source,console] ----- -$ ls -$ git reset --hard master -HEAD is now at 3caa046 imported from current -$ ls -README.md main.rb ----- - -You can do a lot more with the `fast-import` tool – handle different modes, binary data, multiple branches and merging, tags, progress indicators, and more. -A number of examples of more complex scenarios are available in the `contrib/fast-import` directory of the Git source code. diff --git a/book/ch09/import-hg.asc b/book/ch09/import-hg.asc deleted file mode 100644 index b6d1e19..0000000 --- a/book/ch09/import-hg.asc +++ /dev/null @@ -1,124 +0,0 @@ -==== Mercurial - -(((Mercurial)))(((Importing, from Mercurial))) -Since Mercurial and Git have fairly similar models for representing versions, and since Git is a bit more flexible, converting a repository from Mercurial to Git is fairly straightforward, using a tool called "hg-fast-export", which you'll need a copy of: - -[source,console] ----- -$ git clone https://github.com/frej/fast-export.git ----- - -The first step in the conversion is to get a full clone of the Mercurial repository you want to convert: - -[source,console] ----- -$ hg clone /tmp/hg-repo ----- - -The next step is to create an author mapping file. -Mercurial is a bit more forgiving than Git for what it will put in the author field for changesets, so this is a good time to clean house. -Generating this is a one-line command in a `bash` shell: - -[source,console] ----- -$ cd /tmp/hg-repo -$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors ----- - -This will take a few seconds, depending on how long your project's history is, and afterwards the `/tmp/authors` file will look something like this: - -[source] ----- -bob -bob@localhost -bob -bob jones company com> -Bob Jones -Joe Smith ----- - -In this example, the same person (Bob) has created changesets under four different names, one of which actually looks correct, and one of which would be completely invalid for a Git commit. -Hg-fast-export lets us fix this by turning each line into a rule: `""=""`, mapping an `` to an ``. -Inside the `` and `` strings, all escape sequences understood by the Python `string_escape` encoding are supported. -If the author mapping file does not contain a matching ``, that author will be sent on to Git unmodified. -If all the usernames look fine, we won't need this file at all. -In this example, we want our file to look like this: - -[source] ----- -"bob"="Bob Jones " -"bob@localhost"="Bob Jones " -"bob "="Bob Jones " -"bob jones company com>"="Bob Jones " ----- - -The same kind of mapping file can be used to rename branches and tags when the Mercurial name is not allowed by Git. - -The next step is to create our new Git repository, and run the export script: - -[source,console] ----- -$ git init /tmp/converted -$ cd /tmp/converted -$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors ----- - -The `-r` flag tells hg-fast-export where to find the Mercurial repository we want to convert, and the `-A` flag tells it where to find the author-mapping file (branch and tag mapping files are specified by the `-B` and `-T` flags respectively). -The script parses Mercurial changesets and converts them into a script for Git's "fast-import" feature (which we'll discuss in detail a bit later on). -This takes a bit (though it's _much_ faster than it would be over the network), and the output is fairly verbose: - -[source,console] ----- -$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors -Loaded 4 authors -master: Exporting full revision 1/22208 with 13/0/0 added/changed/removed files -master: Exporting simple delta revision 2/22208 with 1/1/0 added/changed/removed files -master: Exporting simple delta revision 3/22208 with 0/1/0 added/changed/removed files -[…] -master: Exporting simple delta revision 22206/22208 with 0/4/0 added/changed/removed files -master: Exporting simple delta revision 22207/22208 with 0/2/0 added/changed/removed files -master: Exporting thorough delta revision 22208/22208 with 3/213/0 added/changed/removed files -Exporting tag [0.4c] at [hg r9] [git :10] -Exporting tag [0.4d] at [hg r16] [git :17] -[…] -Exporting tag [3.1-rc] at [hg r21926] [git :21927] -Exporting tag [3.1] at [hg r21973] [git :21974] -Issued 22315 commands -git-fast-import statistics: ---------------------------------------------------------------------- -Alloc'd objects: 120000 -Total objects: 115032 ( 208171 duplicates ) - blobs : 40504 ( 205320 duplicates 26117 deltas of 39602 attempts) - trees : 52320 ( 2851 duplicates 47467 deltas of 47599 attempts) - commits: 22208 ( 0 duplicates 0 deltas of 0 attempts) - tags : 0 ( 0 duplicates 0 deltas of 0 attempts) -Total branches: 109 ( 2 loads ) - marks: 1048576 ( 22208 unique ) - atoms: 1952 -Memory total: 7860 KiB - pools: 2235 KiB - objects: 5625 KiB ---------------------------------------------------------------------- -pack_report: getpagesize() = 4096 -pack_report: core.packedGitWindowSize = 1073741824 -pack_report: core.packedGitLimit = 8589934592 -pack_report: pack_used_ctr = 90430 -pack_report: pack_mmap_calls = 46771 -pack_report: pack_open_windows = 1 / 1 -pack_report: pack_mapped = 340852700 / 340852700 ---------------------------------------------------------------------- - -$ git shortlog -sn - 369 Bob Jones - 365 Joe Smith ----- - -That's pretty much all there is to it. -All of the Mercurial tags have been converted to Git tags, and Mercurial branches and bookmarks have been converted to Git branches. -Now you're ready to push the repository up to its new server-side home: - -[source,console] ----- -$ git remote add origin git@my-git-server:myrepository.git -$ git push origin --all ----- diff --git a/book/ch09/import-p4.asc b/book/ch09/import-p4.asc deleted file mode 100644 index a8501fb..0000000 --- a/book/ch09/import-p4.asc +++ /dev/null @@ -1,102 +0,0 @@ -[[_perforce_import]] -==== Perforce - -(((Perforce)))(((Importing, from Perforce))) -The next system you'll look at importing from is Perforce. -As we discussed above, there are two ways to let Git and Perforce talk to each other: git-p4 and Perforce Git Fusion. - -===== Perforce Git Fusion - -Git Fusion makes this process fairly painless. -Just configure your project settings, user mappings, and branches using a configuration file (as discussed in <<_p4_git_fusion>>), and clone the repository. -Git Fusion leaves you with what looks like a native Git repository, which is then ready to push to a native Git host if you desire. -You could even use Perforce as your Git host if you like. - -[[_git_p4]] -===== Git-p4 - -Git-p4 can also act as an import tool. -As an example, we'll import the Jam project from the Perforce Public Depot. -To set up your client, you must export the P4PORT environment variable to point to the Perforce depot: - -[source,console] ----- -$ export P4PORT=public.perforce.com:1666 ----- - -[NOTE] -==== -In order to follow along, you'll need a Perforce depot to connect with. -We'll be using the public depot at public.perforce.com for our examples, but you can use any depot you have access to. -==== - -(((git commands, p4))) -Run the `git p4 clone` command to import the Jam project from the Perforce server, supplying the depot and project path and the path into which you want to import the project: - -[source,console] ----- -$ git-p4 clone //guest/perforce_software/jam@all p4import -Importing from //guest/perforce_software/jam@all into p4import -Initialized empty Git repository in /private/tmp/p4import/.git/ -Import destination: refs/remotes/p4/master -Importing revision 9957 (100%) ----- - -This particular project has only one branch, but if you have branches that are configured with branch views (or just a set of directories), you can use the `--detect-branches` flag to `git p4 clone` to import all the project's branches as well. -See <<_git_p4_branches>> for a bit more detail on this. - -At this point you're almost done. -If you go to the `p4import` directory and run `git log`, you can see your imported work: - -[source,console] ----- -$ git log -2 -commit e5da1c909e5db3036475419f6379f2c73710c4e6 -Author: giles -Date: Wed Feb 8 03:13:27 2012 -0800 - - Correction to line 355; change to . - - [git-p4: depot-paths = "//public/jam/src/": change = 8068] - -commit aa21359a0a135dda85c50a7f7cf249e4f7b8fd98 -Author: kwirth -Date: Tue Jul 7 01:35:51 2009 -0800 - - Fix spelling error on Jam doc page (cummulative -> cumulative). - - [git-p4: depot-paths = "//public/jam/src/": change = 7304] ----- - -You can see that `git-p4` has left an identifier in each commit message. -It's fine to keep that identifier there, in case you need to reference the Perforce change number later. -However, if you'd like to remove the identifier, now is the time to do so – before you start doing work on the new repository. -(((git commands, filter-branch))) -You can use `git filter-branch` to remove the identifier strings en masse: - -[source,console] ----- -$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"' -Rewrite e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125) -Ref 'refs/heads/master' was rewritten ----- - -If you run `git log`, you can see that all the SHA-1 checksums for the commits have changed, but the `git-p4` strings are no longer in the commit messages: - -[source,console] ----- -$ git log -2 -commit b17341801ed838d97f7800a54a6f9b95750839b7 -Author: giles -Date: Wed Feb 8 03:13:27 2012 -0800 - - Correction to line 355; change to . - -commit 3e68c2e26cd89cb983eb52c024ecdfba1d6b3fff -Author: kwirth -Date: Tue Jul 7 01:35:51 2009 -0800 - - Fix spelling error on Jam doc page (cummulative -> cumulative). ----- - -Your import is ready to push up to your new Git server. diff --git a/book/ch09/import-svn.asc b/book/ch09/import-svn.asc deleted file mode 100644 index 5ed4e8f..0000000 --- a/book/ch09/import-svn.asc +++ /dev/null @@ -1,144 +0,0 @@ -==== Subversion - -(((Subversion))) -(((Importing, from Subversion))) -If you read the previous section about using `git svn`, you can easily use those instructions to `git svn clone` a repository; then, stop using the Subversion server, push to a new Git server, and start using that. -If you want the history, you can accomplish that as quickly as you can pull the data out of the Subversion server (which may take a while). - -However, the import isn't perfect; and because it will take so long, you may as well do it right. -The first problem is the author information. -In Subversion, each person committing has a user on the system who is recorded in the commit information. -The examples in the previous section show `schacon` in some places, such as the `blame` output and the `git svn log`. -If you want to map this to better Git author data, you need a mapping from the Subversion users to the Git authors. -Create a file called `users.txt` that has this mapping in a format like this: - -[source] ----- -schacon = Scott Chacon -selse = Someo Nelse ----- - -To get a list of the author names that SVN uses, you can run this: - -[source,console] ----- -$ svn log --xml --quiet | grep author | sort -u | \ - perl -pe 's/.*>(.*?)<.*/$1 = /' ----- - -That generates the log output in XML format, then keeps only the lines with author information, discards duplicates, strips out the XML tags. -Obviously this only works on a machine with `grep`, `sort`, and `perl` installed. -Then, redirect that output into your `users.txt` file so you can add the equivalent Git user data next to each entry. - -[NOTE] -==== -If you're trying this on a Windows machine, this is the point where you'll run into trouble. -Microsoft have provided some good advice and samples at https://learn.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git[^]. -==== - -You can provide this file to `git svn` to help it map the author data more accurately. -You can also tell `git svn` not to include the metadata that Subversion normally imports, by passing `--no-metadata` to the `clone` or `init` command. -The metadata includes a `git-svn-id` inside each commit message that Git will generate during import. -This can bloat your Git log and might make it a bit unclear. - -[NOTE] -==== -You need to keep the metadata when you want to mirror commits made in the Git repository back into the original SVN repository. -If you don't want the synchronization in your commit log, feel free to omit the `--no-metadata` parameter. -==== - -This makes your `import` command look like this: - -[source,console] ----- -$ git svn clone http://my-project.googlecode.com/svn/ \ - --authors-file=users.txt --no-metadata --prefix "" -s my_project -$ cd my_project ----- - -Now you should have a nicer Subversion import in your `my_project` directory. -Instead of commits that look like this: - -[source] ----- -commit 37efa680e8473b615de980fa935944215428a35a -Author: schacon -Date: Sun May 3 00:12:22 2009 +0000 - - fixed install - go to trunk - - git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de- - be05-5f7a86268029 ----- - -they look like this: - -[source] ----- -commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2 -Author: Scott Chacon -Date: Sun May 3 00:12:22 2009 +0000 - - fixed install - go to trunk ----- - -Not only does the Author field look a lot better, but the `git-svn-id` is no longer there, either. - -You should also do a bit of post-import cleanup. -For one thing, you should clean up the weird references that `git svn` set up. -First you'll move the tags so they're actual tags rather than strange remote branches, and then you'll move the rest of the branches so they're local. - -To move the tags to be proper Git tags, run: - -[source,console] ----- -$ for t in $(git for-each-ref --format='%(refname:short)' refs/remotes/tags); do git tag ${t/tags\//} $t && git branch -D -r $t; done ----- - -This takes the references that were remote branches that started with `refs/remotes/tags/` and makes them real (lightweight) tags. - -Next, move the rest of the references under `refs/remotes` to be local branches: - -[source,console] ----- -$ for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); do git branch $b refs/remotes/$b && git branch -D -r $b; done ----- - -It may happen that you'll see some extra branches which are suffixed by `@xxx` (where xxx is a number), while in Subversion you only see one branch. -This is actually a Subversion feature called "`peg-revisions`", which is something that Git simply has no syntactical counterpart for. -Hence, `git svn` simply adds the SVN version number to the branch name just in the same way as you would have written it in SVN to address the peg-revision of that branch. -If you do not care anymore about the peg-revisions, simply remove them: - -[source,console] ----- -$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done ----- - -Now all the old branches are real Git branches and all the old tags are real Git tags. - -There's one last thing to clean up. -Unfortunately, `git svn` creates an extra branch named `trunk`, which maps to Subversion's default branch, but the `trunk` ref points to the same place as `master`. -Since `master` is more idiomatically Git, here's how to remove the extra branch: - -[source,console] ----- -$ git branch -d trunk ----- - -The last thing to do is add your new Git server as a remote and push to it. -Here is an example of adding your server as a remote: - -[source,console] ----- -$ git remote add origin git@my-git-server:myrepository.git ----- - -Because you want all your branches and tags to go up, you can now run this: - -[source,console] ----- -$ git push origin --all -$ git push origin --tags ----- - -All your branches and tags should be on your new Git server in a nice, clean import. diff --git a/book/ch09/keeping-large-repos-fast.asc b/book/ch09/keeping-large-repos-fast.asc new file mode 100644 index 0000000..6b3d3e9 --- /dev/null +++ b/book/ch09/keeping-large-repos-fast.asc @@ -0,0 +1,23 @@ +[[_keeping_large_repositories_fast]] +=== Keeping Large Repositories Fast + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 9.5. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* `git maintenance start` and the scheduled task model (prefetch, commit-graph, + loose-object packing, incremental repack) +* The commit-graph and what it speeds up (log, merge-base, ahead/behind) +* The multi-pack-index +* FSMonitor and the untracked cache for fast `git status` in huge worktrees +* This is the operations story; the on-disk formats are covered in + <> diff --git a/book/ch09/large-files.asc b/book/ch09/large-files.asc new file mode 100644 index 0000000..1baa4cd --- /dev/null +++ b/book/ch09/large-files.asc @@ -0,0 +1,22 @@ +[[_large_files]] +=== Large Files + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 9.6. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* Why large binaries hurt: every clone carries every version forever +* Git LFS: pointer files, smudge/clean filters + (cross-reference <<_git_attributes,Ch 6, Customizing Git>>), server support +* When to prefer partial clone over LFS, and vice versa +* Getting large files back *out* of history + (cross-reference <<_rewriting_history,Ch 5, Git Toolkit>> / `git filter-repo`) diff --git a/book/ch09/monorepos.asc b/book/ch09/monorepos.asc new file mode 100644 index 0000000..140b324 --- /dev/null +++ b/book/ch09/monorepos.asc @@ -0,0 +1,23 @@ +[[_monorepos]] +=== Monorepos + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 9.7. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* Monorepo trade-offs versus many-repo setups (with submodules as the many-repo + glue — cross-reference <<_git_submodules,Ch 5, Git Toolkit>>) +* Putting the chapter together: partial clone + sparse checkout + Scalar + + background maintenance as the standard monorepo client setup +* Repository layout and ownership practices +* Real-world deployments (e.g. Microsoft Office/Windows on Scalar) +* Forge support constraints and server-side considerations diff --git a/book/ch09/partial-clone.asc b/book/ch09/partial-clone.asc new file mode 100644 index 0000000..a8f2690 --- /dev/null +++ b/book/ch09/partial-clone.asc @@ -0,0 +1,24 @@ +[[_partial_clone]] +=== Partial Clone + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 9.2. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* Object filters: `git clone --filter=blob:none` (blobless) and + `--filter=tree:0` (treeless); when to use each +* Promisor remotes and lazy, on-demand object fetching +* `git backfill` (experimental) for batch-downloading missing blobs +* Full history for `log`; which operations trigger network fetches +* Why partial clone usually beats shallow clone for day-to-day work +* Pointer to the internals story (promised objects) in + <> diff --git a/book/ch09/scalar.asc b/book/ch09/scalar.asc new file mode 100644 index 0000000..46eecff --- /dev/null +++ b/book/ch09/scalar.asc @@ -0,0 +1,22 @@ +[[_scalar]] +=== Scalar + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 9.4. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* `scalar clone` and `scalar register` as the batteries-included front door to + the features in this chapter +* What Scalar turns on: partial clone, cone-mode sparse checkout, background + maintenance, FSMonitor +* `scalar diagnose` for troubleshooting +* History: shipped with Git since 2.38; grew out of VFS for Git diff --git a/book/ch09/shallow-clones.asc b/book/ch09/shallow-clones.asc new file mode 100644 index 0000000..0f98e8d --- /dev/null +++ b/book/ch09/shallow-clones.asc @@ -0,0 +1,22 @@ +[[_shallow_clones]] +=== Shallow Clones + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 9.1. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* `git clone --depth`, `--shallow-since`, `--shallow-exclude` +* Deepening and unshallowing: `git fetch --deepen`, `--unshallow` +* `--no-tags` to avoid tag bloat +* CI patterns: fast single-revision checkouts +* Limitations: no full history for `log`/`blame`/`bisect`, server cost of + shallow negotiation — why partial clone is often the better tool diff --git a/book/ch09/sparse-checkout.asc b/book/ch09/sparse-checkout.asc new file mode 100644 index 0000000..7311a35 --- /dev/null +++ b/book/ch09/sparse-checkout.asc @@ -0,0 +1,21 @@ +[[_sparse_checkout]] +=== Sparse Checkout + +//// +AUTHOR TODO: outline notes only — the actual section prose must be written by a +human author before publication (see AGENTS.md). Replace everything below with +real prose. See OUTLINE.md, section 9.3. +//// + +[NOTE] +==== +*Draft placeholder — outline notes only; prose not yet written.* +==== + +Points this section needs to cover: + +* Working in a directory subset of a huge repository: + `git sparse-checkout set`/`add`/`list`/`disable` +* Cone mode and why it is the recommended (and default) mode +* The sparse index for fast `status`/`add` in sparse worktrees +* Combining sparse checkout with partial clone diff --git a/book/ch10/environment.asc b/book/ch10/environment.asc index 9b01781..bd0c290 100644 --- a/book/ch10/environment.asc +++ b/book/ch10/environment.asc @@ -216,7 +216,7 @@ To support extra command-line parameters, you can use *`GIT_SSH_COMMAND`*, write The command is interpreted by the shell, and extra command-line arguments can be used with `ssh`, such as `GIT_SSH_COMMAND="ssh -i ~/.ssh/my_key" git clone git@example.com:my/repo`. *`GIT_ASKPASS`* is an override for the `core.askpass` configuration value. -This is the program invoked whenever Git needs to ask the user for credentials, which can expect a text prompt as a command-line argument, and should return the answer on `stdout` (see <> for more on this subsystem). +This is the program invoked whenever Git needs to ask the user for credentials, which can expect a text prompt as a command-line argument, and should return the answer on `stdout` (see <<_credential_caching,Ch 5, Git Toolkit>> for more on this subsystem). *`GIT_NAMESPACE`* controls access to namespaced refs, and is equivalent to the `--namespace` flag. This is mostly useful on the server side, where you may want to store multiple forks of a single repository in one repository, only keeping the refs separate. diff --git a/book/ch10/maintenance.asc b/book/ch10/maintenance.asc index d4e5308..533cd92 100644 --- a/book/ch10/maintenance.asc +++ b/book/ch10/maintenance.asc @@ -94,7 +94,7 @@ The trick is finding that latest commit SHA-1 – it's not like you've memorized Often, the quickest way is to use a tool called `git reflog`. As you're working, Git silently records what your HEAD is every time you change it. Each time you commit or change branches, the reflog is updated. -The reflog is also updated by the `git update-ref` command, which is another reason to use it instead of just writing the SHA-1 value to your ref files, as we covered in <>. +The reflog is also updated by the `git update-ref` command, which is another reason to use it instead of just writing the SHA-1 value to your ref files, as we covered in <<_git_refs,Ch 10, Git Internals>>. You can see where you've been at any time by running `git reflog`: [source,console] @@ -262,7 +262,7 @@ dadf7258d699da2c8d89b09ef6670edb7d5f91b4 commit 229 159 12 ---- The big object is at the bottom: 5MB. -To find out what file it is, you'll use the `rev-list` command, which you used briefly in <>. +To find out what file it is, you'll use the `rev-list` command, which you used briefly in <<_enforcing_commit_message_format,Ch 6, Customizing Git>>. If you pass `--objects` to `rev-list`, it lists all the commit SHA-1s and also the blob SHA-1s with the file paths associated with them. You can use this to find your blob's name: @@ -283,7 +283,7 @@ dadf725 Oops - remove large tarball ---- You must rewrite all the commits downstream from `7b30847` to fully remove this file from your Git history. -To do so, you use `filter-branch`, which you used in <>: +To do so, you use `filter-branch`, which you used in <<_rewriting_history,Ch 5, Git Toolkit>>: [source,console] ---- @@ -294,7 +294,7 @@ Rewrite dadf7258d699da2c8d89b09ef6670edb7d5f91b4 (2/2) Ref 'refs/heads/master' was rewritten ---- -The `--index-filter` option is similar to the `--tree-filter` option used in <>, except that instead of passing a command that modifies files checked out on disk, you're modifying your staging area or index each time. +The `--index-filter` option is similar to the `--tree-filter` option used in <<_rewriting_history,Ch 5, Git Toolkit>>, except that instead of passing a command that modifies files checked out on disk, you're modifying your staging area or index each time. Rather than remove a specific file with something like `rm file`, you have to remove it with `git rm --cached` – you must remove it from the index, not from disk. The reason to do it this way is speed – because Git doesn't have to check out each revision to disk before running your filter, the process can be much, much faster. diff --git a/book/ch10/plumbing-porcelain.asc b/book/ch10/plumbing-porcelain.asc index 53b4095..eea708f 100644 --- a/book/ch10/plumbing-porcelain.asc +++ b/book/ch10/plumbing-porcelain.asc @@ -29,7 +29,7 @@ refs/ Depending on your version of Git, you may see some additional content there, but this is a fresh `git init` repository -- it's what you see by default. The `description` file is used only by the GitWeb program, so don't worry about it. The `config` file contains your project-specific configuration options, and the `info` directory keeps a global exclude file (((excludes))) for ignored patterns that you don't want to track in a `.gitignore` file. -The `hooks` directory contains your client- or server-side hook scripts, which are discussed in detail in <>. +The `hooks` directory contains your client- or server-side hook scripts, which are discussed in detail in <<_git_hooks,Ch 6, Customizing Git>>. This leaves four important entries: the `HEAD` and (yet to be created) `index` files, and the `objects` and `refs` directories. These are the core parts of Git. diff --git a/book/ch10/refs.asc b/book/ch10/refs.asc index c831908..be6bf06 100644 --- a/book/ch10/refs.asc +++ b/book/ch10/refs.asc @@ -127,7 +127,7 @@ The _tag_ object is very much like a commit object -- it contains a tagger, a da The main difference is that a tag object generally points to a commit rather than a tree. It's like a branch reference, but it never moves -- it always points to the same commit but gives it a friendlier name. -As discussed in <>, there are two types of tags: annotated and lightweight. +As discussed in <>, there are two types of tags: annotated and lightweight. You can make a lightweight tag by running something like this: [source,console] diff --git a/book/ch10/refspec.asc b/book/ch10/refspec.asc index f8157ce..dfc5fa0 100644 --- a/book/ch10/refspec.asc +++ b/book/ch10/refspec.asc @@ -123,7 +123,7 @@ Again, this will cause a `git push origin` to push the local `master` branch to [NOTE] ==== You cannot use the refspec to fetch from one repository and push to another one. -For an example to do so, refer to <>. +For an example to do so, refer to <<_fetch_and_push_on_different_repositories,Ch 4, Distributed Git>>. ==== ==== Deleting References diff --git a/images/2fa-1.png b/images/2fa-1.png deleted file mode 100644 index 02725b9..0000000 Binary files a/images/2fa-1.png and /dev/null differ diff --git a/images/account-settings.png b/images/account-settings.png deleted file mode 100644 index ea9ef1b..0000000 Binary files a/images/account-settings.png and /dev/null differ diff --git a/images/avatar-crop.png b/images/avatar-crop.png deleted file mode 100644 index 622e05d..0000000 Binary files a/images/avatar-crop.png and /dev/null differ diff --git a/images/blink-01-start.png b/images/blink-01-start.png deleted file mode 100644 index ccf06e7..0000000 Binary files a/images/blink-01-start.png and /dev/null differ diff --git a/images/blink-02-pr.png b/images/blink-02-pr.png deleted file mode 100644 index ec9f08c..0000000 Binary files a/images/blink-02-pr.png and /dev/null differ diff --git a/images/blink-03-pull-request-open.png b/images/blink-03-pull-request-open.png deleted file mode 100644 index 442a329..0000000 Binary files a/images/blink-03-pull-request-open.png and /dev/null differ diff --git a/images/blink-04-email.png b/images/blink-04-email.png deleted file mode 100644 index 52b6137..0000000 Binary files a/images/blink-04-email.png and /dev/null differ diff --git a/images/blink-04-pr-comment.png b/images/blink-04-pr-comment.png deleted file mode 100644 index 46402d5..0000000 Binary files a/images/blink-04-pr-comment.png and /dev/null differ diff --git a/images/blink-05-general-comment.png b/images/blink-05-general-comment.png deleted file mode 100644 index 7cf1205..0000000 Binary files a/images/blink-05-general-comment.png and /dev/null differ diff --git a/images/blink-06-final.png b/images/blink-06-final.png deleted file mode 100644 index 155a5ef..0000000 Binary files a/images/blink-06-final.png and /dev/null differ diff --git a/images/branch_widget_mac.png b/images/branch_widget_mac.png deleted file mode 100644 index 3361dca..0000000 Binary files a/images/branch_widget_mac.png and /dev/null differ diff --git a/images/branch_widget_win.png b/images/branch_widget_win.png deleted file mode 100644 index 11e2f1c..0000000 Binary files a/images/branch_widget_win.png and /dev/null differ diff --git a/images/collaborators.png b/images/collaborators.png deleted file mode 100644 index 01a480d..0000000 Binary files a/images/collaborators.png and /dev/null differ diff --git a/images/email-settings.png b/images/email-settings.png deleted file mode 100644 index b073ded..0000000 Binary files a/images/email-settings.png and /dev/null differ diff --git a/images/forkbutton.png b/images/forkbutton.png deleted file mode 100644 index 8ce7ff2..0000000 Binary files a/images/forkbutton.png and /dev/null differ diff --git a/images/git-bash.png b/images/git-bash.png deleted file mode 100644 index a0bbfb2..0000000 Binary files a/images/git-bash.png and /dev/null differ diff --git a/images/git-fusion-boot.png b/images/git-fusion-boot.png deleted file mode 100644 index 79272af..0000000 Binary files a/images/git-fusion-boot.png and /dev/null differ diff --git a/images/git-fusion-perforce-graph.png b/images/git-fusion-perforce-graph.png deleted file mode 100644 index 022293c..0000000 Binary files a/images/git-fusion-perforce-graph.png and /dev/null differ diff --git a/images/git-gui.png b/images/git-gui.png deleted file mode 100644 index 6d05a94..0000000 Binary files a/images/git-gui.png and /dev/null differ diff --git a/images/github_mac.png b/images/github_mac.png deleted file mode 100644 index 5e3aea0..0000000 Binary files a/images/github_mac.png and /dev/null differ diff --git a/images/github_win.png b/images/github_win.png deleted file mode 100644 index 3313456..0000000 Binary files a/images/github_win.png and /dev/null differ diff --git a/images/gitk.png b/images/gitk.png deleted file mode 100644 index 16ee0e2..0000000 Binary files a/images/gitk.png and /dev/null differ diff --git a/images/jb.png b/images/jb.png deleted file mode 100644 index 2ffe74a..0000000 Binary files a/images/jb.png and /dev/null differ diff --git a/images/maint-01-email.png b/images/maint-01-email.png deleted file mode 100644 index 6de4698..0000000 Binary files a/images/maint-01-email.png and /dev/null differ diff --git a/images/maint-02-merge.png b/images/maint-02-merge.png deleted file mode 100644 index 62ba7f7..0000000 Binary files a/images/maint-02-merge.png and /dev/null differ diff --git a/images/maint-03-email-resp.png b/images/maint-03-email-resp.png deleted file mode 100644 index 0d2a096..0000000 Binary files a/images/maint-03-email-resp.png and /dev/null differ diff --git a/images/maint-04-target.png b/images/maint-04-target.png deleted file mode 100644 index 1a6476f..0000000 Binary files a/images/maint-04-target.png and /dev/null differ diff --git a/images/maint-05-mentions.png b/images/maint-05-mentions.png deleted file mode 100644 index 4660bbc..0000000 Binary files a/images/maint-05-mentions.png and /dev/null differ diff --git a/images/maint-06-unsubscribe.png b/images/maint-06-unsubscribe.png deleted file mode 100644 index e068945..0000000 Binary files a/images/maint-06-unsubscribe.png and /dev/null differ diff --git a/images/maint-07-notifications.png b/images/maint-07-notifications.png deleted file mode 100644 index a866509..0000000 Binary files a/images/maint-07-notifications.png and /dev/null differ diff --git a/images/maint-08-notifications-page.png b/images/maint-08-notifications-page.png deleted file mode 100644 index 3008539..0000000 Binary files a/images/maint-08-notifications-page.png and /dev/null differ diff --git a/images/maint-09-contrib.png b/images/maint-09-contrib.png deleted file mode 100644 index bb2a172..0000000 Binary files a/images/maint-09-contrib.png and /dev/null differ diff --git a/images/maint-10-default-branch.png b/images/maint-10-default-branch.png deleted file mode 100644 index 4fed697..0000000 Binary files a/images/maint-10-default-branch.png and /dev/null differ diff --git a/images/maint-11-transfer.png b/images/maint-11-transfer.png deleted file mode 100644 index a55e385..0000000 Binary files a/images/maint-11-transfer.png and /dev/null differ diff --git a/images/markdown-01-example.png b/images/markdown-01-example.png deleted file mode 100644 index 455fe32..0000000 Binary files a/images/markdown-01-example.png and /dev/null differ diff --git a/images/markdown-02-tasks.png b/images/markdown-02-tasks.png deleted file mode 100644 index 00cf536..0000000 Binary files a/images/markdown-02-tasks.png and /dev/null differ diff --git a/images/markdown-03-task-summary.png b/images/markdown-03-task-summary.png deleted file mode 100644 index 8168ed1..0000000 Binary files a/images/markdown-03-task-summary.png and /dev/null differ diff --git a/images/markdown-04-fenced-code.png b/images/markdown-04-fenced-code.png deleted file mode 100644 index c9335f5..0000000 Binary files a/images/markdown-04-fenced-code.png and /dev/null differ diff --git a/images/markdown-05-quote.png b/images/markdown-05-quote.png deleted file mode 100644 index 83f62d3..0000000 Binary files a/images/markdown-05-quote.png and /dev/null differ diff --git a/images/markdown-06-emoji-complete.png b/images/markdown-06-emoji-complete.png deleted file mode 100644 index 840d701..0000000 Binary files a/images/markdown-06-emoji-complete.png and /dev/null differ diff --git a/images/markdown-07-emoji.png b/images/markdown-07-emoji.png deleted file mode 100644 index e94cc13..0000000 Binary files a/images/markdown-07-emoji.png and /dev/null differ diff --git a/images/markdown-08-drag-drop.png b/images/markdown-08-drag-drop.png deleted file mode 100644 index 983b9f9..0000000 Binary files a/images/markdown-08-drag-drop.png and /dev/null differ diff --git a/images/mentions-01-syntax.png b/images/mentions-01-syntax.png deleted file mode 100644 index 87a22a0..0000000 Binary files a/images/mentions-01-syntax.png and /dev/null differ diff --git a/images/mentions-02-render.png b/images/mentions-02-render.png deleted file mode 100644 index 9cbd2b7..0000000 Binary files a/images/mentions-02-render.png and /dev/null differ diff --git a/images/mentions-03-closed.png b/images/mentions-03-closed.png deleted file mode 100644 index 202563b..0000000 Binary files a/images/mentions-03-closed.png and /dev/null differ diff --git a/images/new-repo.png b/images/new-repo.png deleted file mode 100644 index 4e4f162..0000000 Binary files a/images/new-repo.png and /dev/null differ diff --git a/images/neworg.png b/images/neworg.png deleted file mode 100644 index a4491c4..0000000 Binary files a/images/neworg.png and /dev/null differ diff --git a/images/newrepo.png b/images/newrepo.png deleted file mode 100644 index 952cb2f..0000000 Binary files a/images/newrepo.png and /dev/null differ diff --git a/images/newrepoform.png b/images/newrepoform.png deleted file mode 100644 index dc49a50..0000000 Binary files a/images/newrepoform.png and /dev/null differ diff --git a/images/orgs-01-page.png b/images/orgs-01-page.png deleted file mode 100644 index 82006b7..0000000 Binary files a/images/orgs-01-page.png and /dev/null differ diff --git a/images/orgs-02-teams.png b/images/orgs-02-teams.png deleted file mode 100644 index 7183746..0000000 Binary files a/images/orgs-02-teams.png and /dev/null differ diff --git a/images/orgs-03-audit.png b/images/orgs-03-audit.png deleted file mode 100644 index 3a353af..0000000 Binary files a/images/orgs-03-audit.png and /dev/null differ diff --git a/images/posh-git.png b/images/posh-git.png deleted file mode 100644 index d403b6d..0000000 Binary files a/images/posh-git.png and /dev/null differ diff --git a/images/pr-01-fail.png b/images/pr-01-fail.png deleted file mode 100644 index 2337656..0000000 Binary files a/images/pr-01-fail.png and /dev/null differ diff --git a/images/pr-02-merge-fix.png b/images/pr-02-merge-fix.png deleted file mode 100644 index cc2bafd..0000000 Binary files a/images/pr-02-merge-fix.png and /dev/null differ diff --git a/images/reposettingslink.png b/images/reposettingslink.png deleted file mode 100644 index a3f0db4..0000000 Binary files a/images/reposettingslink.png and /dev/null differ diff --git a/images/scripting-01-services.png b/images/scripting-01-services.png deleted file mode 100644 index 3af65a1..0000000 Binary files a/images/scripting-01-services.png and /dev/null differ diff --git a/images/scripting-02-email-service.png b/images/scripting-02-email-service.png deleted file mode 100644 index b1e5949..0000000 Binary files a/images/scripting-02-email-service.png and /dev/null differ diff --git a/images/scripting-03-webhook.png b/images/scripting-03-webhook.png deleted file mode 100644 index bf90bcb..0000000 Binary files a/images/scripting-03-webhook.png and /dev/null differ diff --git a/images/scripting-04-webhook-debug.png b/images/scripting-04-webhook-debug.png deleted file mode 100644 index e91c9e2..0000000 Binary files a/images/scripting-04-webhook-debug.png and /dev/null differ diff --git a/images/scripting-05-access-token.png b/images/scripting-05-access-token.png deleted file mode 100644 index f404142..0000000 Binary files a/images/scripting-05-access-token.png and /dev/null differ diff --git a/images/scripting-06-comment.png b/images/scripting-06-comment.png deleted file mode 100644 index 7dd2791..0000000 Binary files a/images/scripting-06-comment.png and /dev/null differ diff --git a/images/scripting-07-status.png b/images/scripting-07-status.png deleted file mode 100644 index b0dad61..0000000 Binary files a/images/scripting-07-status.png and /dev/null differ diff --git a/images/signup.png b/images/signup.png deleted file mode 100644 index 73e8d9a..0000000 Binary files a/images/signup.png and /dev/null differ diff --git a/images/ssh-keys.png b/images/ssh-keys.png deleted file mode 100644 index 34c0ff8..0000000 Binary files a/images/ssh-keys.png and /dev/null differ diff --git a/images/your-profile.png b/images/your-profile.png deleted file mode 100644 index 01373f6..0000000 Binary files a/images/your-profile.png and /dev/null differ diff --git a/images/zsh-oh-my.png b/images/zsh-oh-my.png deleted file mode 100644 index dd1d4e0..0000000 Binary files a/images/zsh-oh-my.png and /dev/null differ diff --git a/images/zsh-prompt.png b/images/zsh-prompt.png deleted file mode 100644 index 634a8c5..0000000 Binary files a/images/zsh-prompt.png and /dev/null differ diff --git a/progit.asc b/progit.asc index 947c6d1..d13b5e2 100644 --- a/progit.asc +++ b/progit.asc @@ -20,24 +20,20 @@ include::book/ch02-git-basics-chapter.asc[] include::book/ch03-git-branching.asc[] -include::book/ch04-git-on-the-server.asc[] +include::book/ch04-distributed-git.asc[] -include::book/ch05-distributed-git.asc[] +include::book/ch05-git-toolkit.asc[] -include::book/ch06-github.asc[] +include::book/ch06-customizing-git.asc[] -include::book/ch07-git-tools.asc[] +include::book/ch07-git-and-agents.asc[] -include::book/ch08-customizing-git.asc[] +include::book/ch08-git-servers.asc[] -include::book/ch09-git-and-other-systems.asc[] +include::book/ch09-git-at-scale.asc[] include::book/ch10-git-internals.asc[] -include::book/A-git-in-other-environments.asc[] - -include::book/B-embedding-git-in-your-applications.asc[] - -include::book/C-git-commands.asc[] +include::book/A-git-commands.asc[] ifdef::backend-pdf[include::book/index.asc[]]